diff options
1132 files changed, 49814 insertions, 30041 deletions
diff --git a/.clang-tidy b/.clang-tidy index 6776d86d21..b7888a1fcf 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -14,6 +14,7 @@ Checks: > section for it, -bugprone-assignment-in-if-condition, -bugprone-implicit-widening-of-multiplication-result, + -bugprone-multi-level-implicit-pointer-conversion, -bugprone-not-null-terminated-result, -bugprone-suspicious-memory-comparison, -bugprone-switch-missing-default-case, @@ -32,6 +33,7 @@ Checks: > -hicpp-signed-bitwise, -misc-unused-parameters, -modernize-macro-to-enum, + -readability-avoid-nested-conditional-operator, -readability-else-after-return, -readability-function-size, -readability-isolate-declaration, @@ -40,6 +42,7 @@ Checks: > -altera-*, Checks related to OpenCL programming for FPGAs. Not relevant, -android-*, -bugprone-easily-swappable-parameters, + -bugprone-inc-dec-in-conditions, -bugprone-swapped-arguments, -clang-analyzer-*, Already covered by the cmake target "clang-analyzer", -cppcoreguidelines-avoid-non-const-global-variables, @@ -65,6 +68,9 @@ Checks: > -cert-msc24-c, -cert-msc33-c, -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-macro-to-enum, + -google-readability-braces-around-statements, -google-readability-function-size, + -hicpp-braces-around-statements, -hicpp-function-size, -llvm-else-after-return, diff --git a/.github/scripts/build_universal_macos.sh b/.github/scripts/build_universal_macos.sh deleted file mode 100755 index aeda93ee03..0000000000 --- a/.github/scripts/build_universal_macos.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -e - -MACOSX_DEPLOYMENT_TARGET=11.0 -export MACOSX_DEPLOYMENT_TARGET -cmake -S cmake.deps -B .deps -G Ninja \ - -D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -D CMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \ - -D CMAKE_OSX_ARCHITECTURES=arm64\;x86_64 \ - -D CMAKE_FIND_FRAMEWORK=NEVER -cmake --build .deps -cmake -B build -G Ninja \ - -D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -D CMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \ - -D CMAKE_OSX_ARCHITECTURES=arm64\;x86_64 \ - -D ENABLE_LIBINTL=OFF \ - -D CMAKE_FIND_FRAMEWORK=NEVER -cmake --build build -# Make sure we build everything for M1 as well -for macho in build/bin/* build/lib/nvim/parser/*.so; do - lipo -info "$macho" | grep -q arm64 || exit 1 -done -cpack --config build/CPackConfig.cmake diff --git a/.github/scripts/close_unresponsive.js b/.github/scripts/close_unresponsive.js index b16c19b653..5fd231423c 100644 --- a/.github/scripts/close_unresponsive.js +++ b/.github/scripts/close_unresponsive.js @@ -26,7 +26,7 @@ module.exports = async ({ github, context }) => { repo: repo, issue_number: number, }, - (response) => response.data.filter(labeledEvent) + (response) => response.data.filter(labeledEvent), ); const latest_response_label = events[events.length - 1]; diff --git a/.github/scripts/install_deps.sh b/.github/scripts/install_deps.sh index ad81e053f9..66f418eb10 100755 --- a/.github/scripts/install_deps.sh +++ b/.github/scripts/install_deps.sh @@ -16,7 +16,7 @@ if [[ $os == Linux ]]; then if [[ $CC == clang ]]; then DEFAULT_CLANG_VERSION=$(echo | clang -dM -E - | grep __clang_major | awk '{print $3}') - CLANG_VERSION=17 + CLANG_VERSION=18 if ((DEFAULT_CLANG_VERSION >= CLANG_VERSION)); then echo "Default clang version is $DEFAULT_CLANG_VERSION, which equal or larger than wanted version $CLANG_VERSION. Aborting!" exit 1 @@ -31,11 +31,17 @@ if [[ $os == Linux ]]; then if [[ -n $TEST ]]; then sudo apt-get install -y locales-all cpanminus attr libattr1-dev gdb fswatch + + # Use default CC to avoid compilation problems when installing Python modules + CC=cc python3 -m pip -q install --user --upgrade pynvim fi elif [[ $os == Darwin ]]; then brew update --quiet brew install ninja if [[ -n $TEST ]]; then brew install cpanminus fswatch + + # Use default CC to avoid compilation problems when installing Python modules + CC=cc python3 -m pip -q install --user --upgrade --break-system-packages pynvim fi fi diff --git a/.github/scripts/labeler_configuration.yml b/.github/scripts/labeler_configuration.yml index 3db8444fde..ea670d1dd0 100644 --- a/.github/scripts/labeler_configuration.yml +++ b/.github/scripts/labeler_configuration.yml @@ -1,71 +1,71 @@ -lsp: +build: - changed-files: - - any-glob-to-any-file: [ runtime/lua/vim/lsp.lua, runtime/lua/vim/lsp/* ] + - any-glob-to-any-file: [ CMakeLists.txt, "**/CMakeLists.txt", "**/Makefile", "**/*.cmake", cmake.deps/**/* ] -tui: +ci: - changed-files: - - any-glob-to-any-file: [ src/nvim/tui/tui.* ] + - any-glob-to-any-file: [ .github/actions/**, .github/workflows/**, .github/scripts/** ] -treesitter: +clipboard: - changed-files: - - any-glob-to-any-file: [ src/nvim/lua/treesitter.*, runtime/lua/vim/treesitter.lua, runtime/lua/vim/treesitter/*, runtime/queries/**/* ] + - any-glob-to-any-file: [ runtime/autoload/provider/clipboard.vim ] + +column: + - changed-files: + - any-glob-to-any-file: [ src/nvim/sign* ] diagnostic: - changed-files: - any-glob-to-any-file: [ runtime/lua/vim/diagnostic.lua ] -dependencies: +diff: - changed-files: - - any-glob-to-any-file: [ cmake.deps/**/* ] + - any-glob-to-any-file: [ src/nvim/diff.* ] -spell: +documentation: - changed-files: - - any-glob-to-any-file: [ src/nvim/spell* ] + - any-glob-to-all-files: [ runtime/doc/*, "**/*.md" ] -terminal: +editorconfig: - changed-files: - - any-glob-to-any-file: [ src/nvim/terminal.* ] + - any-glob-to-any-file: [ .editorconfig, runtime/lua/editorconfig.lua, runtime/plugin/editorconfig.lua ] -column: +filetype: - changed-files: - - any-glob-to-any-file: [ src/nvim/sign* ] + - any-glob-to-any-file: [ runtime/lua/vim/filetype.lua, runtime/lua/vim/filetype/detect.lua ] folds: - changed-files: - any-glob-to-any-file: [ src/nvim/fold* ] -mouse: +lsp: - changed-files: - - any-glob-to-any-file: [ src/nvim/mouse* ] + - any-glob-to-any-file: [ runtime/lua/vim/lsp.lua, runtime/lua/vim/lsp/* ] -documentation: +mouse: - changed-files: - - any-glob-to-all-files: [ runtime/doc/*, "**/*.md" ] + - any-glob-to-any-file: [ src/nvim/mouse* ] -clipboard: +snippet: - changed-files: - - any-glob-to-any-file: [ runtime/autoload/provider/clipboard.vim ] + - any-glob-to-any-file: [ runtime/lua/vim/snippet.lua ] -diff: +spell: - changed-files: - - any-glob-to-any-file: [ src/nvim/diff.* ] + - any-glob-to-any-file: [ src/nvim/spell* ] -build: +terminal: - changed-files: - - any-glob-to-any-file: [ CMakeLists.txt, "**/CMakeLists.txt", "**/Makefile", "**/*.cmake" ] + - any-glob-to-any-file: [ src/nvim/terminal.* ] test: - changed-files: - any-glob-to-all-files: [test/**/*] -ci: - - changed-files: - - any-glob-to-any-file: [ .github/actions/**, .github/workflows/**, .github/scripts/** ] - -filetype: +treesitter: - changed-files: - - any-glob-to-any-file: [ runtime/lua/vim/filetype.lua, runtime/lua/vim/filetype/detect.lua ] + - any-glob-to-any-file: [ src/nvim/lua/treesitter.*, runtime/lua/vim/treesitter.lua, runtime/lua/vim/treesitter/*, runtime/queries/**/* ] -platform:nix: +tui: - changed-files: - - any-glob-to-any-file: [ contrib/flake.lock, contrib/flake.nix ] + - any-glob-to-any-file: [ src/nvim/tui/tui.* ] diff --git a/.github/scripts/reviewers_add.js b/.github/scripts/reviewers_add.js index c6a878ec8e..a7e0d2e47b 100644 --- a/.github/scripts/reviewers_add.js +++ b/.github/scripts/reviewers_add.js @@ -7,11 +7,6 @@ module.exports = async ({ github, context }) => { const labels = pr_data.data.labels.map((e) => e.name); const reviewers = new Set(); - if (labels.includes("api")) { - reviewers.add("bfredl"); - reviewers.add("famiu"); - } - if (labels.includes("build")) { reviewers.add("dundargoc"); reviewers.add("jamessan"); @@ -32,10 +27,6 @@ module.exports = async ({ github, context }) => { reviewers.add("gpanders"); } - if (labels.includes("dependencies")) { - reviewers.add("jamessan"); - } - if (labels.includes("diagnostic")) { reviewers.add("gpanders"); } @@ -44,14 +35,14 @@ module.exports = async ({ github, context }) => { reviewers.add("lewis6991"); } - if (labels.includes("distribution")) { - reviewers.add("jamessan"); - } - if (labels.includes("documentation")) { reviewers.add("clason"); } + if (labels.includes("editorconfig")) { + reviewers.add("gpanders"); + } + if (labels.includes("extmarks")) { reviewers.add("bfredl"); } @@ -59,11 +50,9 @@ module.exports = async ({ github, context }) => { if (labels.includes("filetype")) { reviewers.add("clason"); reviewers.add("gpanders"); - reviewers.add("smjonas"); } if (labels.includes("lsp")) { - reviewers.add("folke"); reviewers.add("MariaSolOs"); reviewers.add("mfussenegger"); } @@ -81,6 +70,10 @@ module.exports = async ({ github, context }) => { reviewers.add("justinmk"); } + if (labels.includes("snippet")) { + reviewers.add("MariaSolOs"); + } + if (labels.includes("statusline")) { reviewers.add("famiu"); } @@ -93,6 +86,7 @@ module.exports = async ({ github, context }) => { reviewers.add("bfredl"); reviewers.add("clason"); reviewers.add("lewis6991"); + reviewers.add("wookayin"); } if (labels.includes("tui")) { @@ -103,11 +97,6 @@ module.exports = async ({ github, context }) => { reviewers.add("dundargoc"); } - if (labels.includes("ui")) { - reviewers.add("bfredl"); - reviewers.add("famiu"); - } - if (labels.includes("vim-patch")) { reviewers.add("seandewar"); reviewers.add("zeertzjq"); diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 88cddcded4..9fbe837106 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -12,11 +12,25 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Create backport PRs + - name: Create backport PR + id: backport uses: korthout/backport-action@v2 with: + pull_title: "${pull_title}" + label_pattern: "^ci:backport ([^ ]+)$" # https://github.com/korthout/backport-action/pull/399 experimental: > { "detect_merge_method": true } + + - if: ${{steps.backport.outputs.was_successful == 'true'}} + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.addLabels({ + issue_number: ${{steps.backport.outputs.created_pull_numbers}}, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['target:release'] + }) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 13fa692be1..a2316f3f0f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,30 +10,20 @@ on: - '**/CMakePresets.json' - 'cmake.*/**' - '.github/**' + workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: BIN_DIR: ${{ github.workspace }}/bin INSTALL_PREFIX: ${{ github.workspace }}/nvim-install jobs: - macos-universal: - runs-on: macos-14 - timeout-minutes: 20 - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup - - run: printf 'CMAKE_BUILD_TYPE=Release\n' >> $GITHUB_ENV - - - name: Build universal binary - run: ./.github/scripts/build_universal_macos.sh - old-cmake: name: Test oldest supported cmake - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest timeout-minutes: 15 env: CMAKE_URL: 'https://cmake.org/files/v3.13/cmake-3.13.0-Linux-x86_64.sh' @@ -66,7 +56,7 @@ jobs: use-existing-src: name: Test USE_EXISTING_SRC_DIR=ON builds with no network access - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c2db78dd25..9c85d606da 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,8 +1,8 @@ name: "codeql" concurrency: - group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} on: push: @@ -10,6 +10,8 @@ on: pull_request: # The branches below must be a subset of the branches above branches: [ "master" ] + workflow_dispatch: + jobs: analyze: name: Analyze diff --git a/.github/workflows/notes.md b/.github/workflows/notes.md index 294d92ebb5..f67a098687 100644 --- a/.github/workflows/notes.md +++ b/.github/workflows/notes.md @@ -18,12 +18,19 @@ ${NVIM_VERSION} 2. Run the MSI 3. Run `nvim.exe` on your CLI of choice -### macOS +### macOS (x86_64) -1. Download **nvim-macos.tar.gz** -2. Run `xattr -c ./nvim-macos.tar.gz` (to avoid "unknown developer" warning) -3. Extract: `tar xzvf nvim-macos.tar.gz` -4. Run `./nvim-macos/bin/nvim` +1. Download **nvim-macos-x86_64.tar.gz** +2. Run `xattr -c ./nvim-macos-x86_64.tar.gz` (to avoid "unknown developer" warning) +3. Extract: `tar xzvf nvim-macos-x86_64.tar.gz` +4. Run `./nvim-macos-x86_64/bin/nvim` + +### macOS (arm64) + +1. Download **nvim-macos-arm64.tar.gz** +2. Run `xattr -c ./nvim-macos-arm64.tar.gz` (to avoid "unknown developer" warning) +3. Extract: `tar xzvf nvim-macos-arm64.tar.gz` +4. Run `./nvim-macos-arm64/bin/nvim` ### Linux (x64) @@ -56,7 +63,8 @@ https://github.com/neovim/neovim-releases. ${SHA_LINUX_64_TAR} ${SHA_APP_IMAGE} ${SHA_APP_IMAGE_ZSYNC} -${SHA_MACOS} +${SHA_MACOS_X86_64} +${SHA_MACOS_ARM64} ${SHA_WIN_64_ZIP} ${SHA_WIN_64_MSI} ``` diff --git a/.github/workflows/optional.yml b/.github/workflows/optional.yml index c9a8205f87..742d51377f 100644 --- a/.github/workflows/optional.yml +++ b/.github/workflows/optional.yml @@ -5,8 +5,8 @@ on: workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: INSTALL_PREFIX: ${{ github.workspace }}/nvim-install @@ -46,3 +46,10 @@ jobs: runuser -u qemuci -- cmake -B build -G Ninja -D CI_BUILD=ON -D PREFER_LUA=ON && runuser -u qemuci -- make ${{ matrix.test }} " + + windows-asan: + if: contains(github.event.pull_request.labels.*.name, 'ci:windows-asan') || github.event_name == 'workflow_dispatch' + uses: ./.github/workflows/test_windows.yml + with: + build_flags: "-D ENABLE_ASAN_UBSAN=ON" + functionaltest_timeout: 40 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 48a3901955..6abdd588ba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,8 +15,32 @@ on: # Build on the oldest supported images, so we have broader compatibility # Build with gcc-10 to prevent triggering #14150 (default is still gcc-9 on 20.04) jobs: + setup: + runs-on: ubuntu-latest + outputs: + build_type: ${{ steps.build.outputs.build_type }} + appimage_tag: ${{ steps.build.outputs.appimage_tag }} + steps: + # Nightly uses RelWithDebInfo while stable uses Release (which disables + # asserts). This helps get better debug info from people brave enough to + # use the nightly builds. + - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly') + run: | + echo 'CMAKE_BUILD_TYPE=Release' >> $GITHUB_ENV + echo 'APPIMAGE_TAG=latest' >> $GITHUB_ENV + - if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly') + run: | + echo 'CMAKE_BUILD_TYPE=RelWithDebInfo' >> $GITHUB_ENV + echo 'APPIMAGE_TAG=nightly' >> $GITHUB_ENV + - name: Export build information + id: build + run: | + printf "build_type=${CMAKE_BUILD_TYPE}\n" >> $GITHUB_OUTPUT + printf "appimage_tag=${APPIMAGE_TAG}\n" >> $GITHUB_OUTPUT + linux: runs-on: ubuntu-20.04 + needs: setup env: CC: gcc-10 outputs: @@ -27,16 +51,9 @@ jobs: # Perform a full checkout #13471 fetch-depth: 0 - run: ./.github/scripts/install_deps.sh - - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly') - run: | - echo 'CMAKE_BUILD_TYPE=Release' >> $GITHUB_ENV - echo 'APPIMAGE_TAG=latest' >> $GITHUB_ENV - - if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly') - run: | - echo 'CMAKE_BUILD_TYPE=RelWithDebInfo' >> $GITHUB_ENV - echo 'APPIMAGE_TAG=nightly' >> $GITHUB_ENV + - run: echo "CMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }}" >> $GITHUB_ENV - name: appimage - run: ./scripts/genappimage.sh ${APPIMAGE_TAG} + run: ./scripts/genappimage.sh ${{ needs.setup.outputs.appimage_tag }} - name: tar.gz run: cpack --config build/CPackConfig.cmake -G TGZ - uses: actions/upload-artifact@v4 @@ -59,8 +76,20 @@ jobs: ./build/bin/nvim --version | head -n 3 >> $GITHUB_OUTPUT printf 'END\n' >> $GITHUB_OUTPUT - macOS: - runs-on: macos-14 + macos: + needs: setup + strategy: + fail-fast: false + matrix: + runner: [ macos-12, macos-14 ] + include: + - runner: macos-12 + arch: x86_64 + - runner: macos-14 + arch: arm64 + runs-on: ${{ matrix.runner }} + env: + MACOSX_DEPLOYMENT_TARGET: 11.0 steps: - uses: actions/checkout@v4 with: @@ -68,21 +97,34 @@ jobs: fetch-depth: 0 - name: Install dependencies run: ./.github/scripts/install_deps.sh - - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly') - run: printf 'CMAKE_BUILD_TYPE=Release\n' >> $GITHUB_ENV - - if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly') - run: printf 'CMAKE_BUILD_TYPE=RelWithDebInfo\n' >> $GITHUB_ENV - - name: Build universal binary - run: ./.github/scripts/build_universal_macos.sh + + - name: Build deps + run: | + cmake -S cmake.deps -B .deps -G Ninja \ + -D CMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }} \ + -D CMAKE_FIND_FRAMEWORK=NEVER + cmake --build .deps + + - name: Build neovim + run: | + cmake -B build -G Ninja \ + -D CMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }} \ + -D ENABLE_LIBINTL=OFF \ + -D CMAKE_FIND_FRAMEWORK=NEVER + cmake --build build + + - name: Package + run: cpack --config build/CPackConfig.cmake + - uses: actions/upload-artifact@v4 with: - name: nvim-macos - path: build/nvim-macos.tar.gz + name: nvim-macos-${{ matrix.arch }} + path: build/nvim-macos-${{ matrix.arch }}.tar.gz retention-days: 1 windows: + needs: setup runs-on: windows-2019 - name: windows (MSVC_64) steps: - uses: actions/checkout@v4 with: @@ -91,11 +133,11 @@ jobs: - run: .github/scripts/env.ps1 - name: Build deps run: | - cmake -S cmake.deps -B .deps -G Ninja -DCMAKE_BUILD_TYPE='RelWithDebInfo' + cmake -S cmake.deps -B .deps -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }} cmake --build .deps - name: build package run: | - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE='RelWithDebInfo' + cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ needs.setup.outputs.build_type }} cmake --build build --target package - uses: actions/upload-artifact@v4 with: @@ -106,7 +148,7 @@ jobs: retention-days: 1 publish: - needs: [linux, macOS, windows] + needs: [linux, macos, windows] runs-on: ubuntu-latest env: GH_REPO: ${{ github.repository }} @@ -160,11 +202,16 @@ jobs: cd ./appimage sha256sum nvim.appimage.zsync > nvim.appimage.zsync.sha256sum echo "SHA_APP_IMAGE_ZSYNC=$(cat nvim.appimage.zsync.sha256sum)" >> $GITHUB_ENV - - name: Generate macOS SHA256 checksums + - name: Generate macos x86_64 SHA256 checksums + run: | + cd ./nvim-macos-x86_64 + sha256sum nvim-macos-x86_64.tar.gz > nvim-macos-x86_64.tar.gz.sha256sum + echo "SHA_MACOS_X86_64=$(cat nvim-macos-x86_64.tar.gz.sha256sum)" >> $GITHUB_ENV + - name: Generate macos arm64 SHA256 checksums run: | - cd ./nvim-macos - sha256sum nvim-macos.tar.gz > nvim-macos.tar.gz.sha256sum - echo "SHA_MACOS=$(cat nvim-macos.tar.gz.sha256sum)" >> $GITHUB_ENV + cd ./nvim-macos-arm64 + sha256sum nvim-macos-arm64.tar.gz > nvim-macos-arm64.tar.gz.sha256sum + echo "SHA_MACOS_ARM64=$(cat nvim-macos-arm64.tar.gz.sha256sum)" >> $GITHUB_ENV - name: Generate Win64 SHA256 checksums run: | cd ./nvim-win64 @@ -179,6 +226,6 @@ jobs: run: | envsubst < "$GITHUB_WORKSPACE/.github/workflows/notes.md" > "$RUNNER_TEMP/notes.md" if [ "$TAG_NAME" != "nightly" ]; then - gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos/* nvim-linux64/* appimage/* nvim-win64/* + gh release create stable $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux64/* appimage/* nvim-win64/* fi - gh release create $TAG_NAME $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos/* nvim-linux64/* appimage/* nvim-win64/* + gh release create $TAG_NAME $PRERELEASE --notes-file "$RUNNER_TEMP/notes.md" --title "$SUBJECT" --target $GITHUB_SHA nvim-macos-x86_64/* nvim-macos-arm64/* nvim-linux64/* appimage/* nvim-win64/* diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6bd40b8561..d0ee18ab73 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,10 +10,11 @@ on: - 'release-[0-9]+.[0-9]+' paths-ignore: - 'contrib/**' + workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: ASAN_OPTIONS: detect_leaks=1:check_initialization_order=1:log_path=${{ github.workspace }}/build/log/asan:intercept_tls_get_addr=0 @@ -113,8 +114,6 @@ jobs: build: { flavor: tsan } - test: unittest build: { flavor: puc-lua } - - test: unittest - build: { runner: macos-14 } # unittests don't work on M1 #26145 - test: oldtest build: { flavor: tsan } runs-on: ${{ matrix.build.runner }} @@ -133,10 +132,6 @@ jobs: - if: ${{ matrix.test != 'unittest' }} name: Set up interpreter packages run: | - # Use default CC to avoid compilation problems when installing Python modules. - echo "Install neovim module for Python." - CC=cc python3 -m pip -q install --user --upgrade pynvim - echo "Install neovim RubyGem." gem install --no-document --bindir "$BIN_DIR" --user-install --pre neovim @@ -193,65 +188,7 @@ jobs: run: cat $(find "$LOG_DIR" -type f) windows: - runs-on: windows-2022 - timeout-minutes: 45 - strategy: - fail-fast: false - matrix: - test: [functional, old] - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup - - - name: Build deps - run: | - cmake -S cmake.deps -B .deps -G Ninja -D CMAKE_BUILD_TYPE='RelWithDebInfo' - cmake --build .deps - - - name: Build - run: | - cmake --preset ci -D CMAKE_BUILD_TYPE='RelWithDebInfo' - cmake --build build - - - name: Install test deps - run: | - $PSNativeCommandArgumentPassing = 'Legacy' - - & build\bin\nvim.exe "--version" - - # Ensure that the "win32" feature is set. - & build\bin\nvim -u NONE --headless -c 'exe !has(\"win32\").\"cq\"' - - python -m pip install pynvim - # Sanity check - python -c "import pynvim; print(str(pynvim))" - - node --version - npm.cmd --version - - npm.cmd install -g neovim - Get-Command -CommandType Application neovim-node-host.cmd - npm.cmd link neovim - - - if: ${{ matrix.test == 'functional' }} - name: functionaltest - timeout-minutes: 20 - run: cmake --build build --target functionaltest - - - if: ${{ matrix.test == 'old' }} - uses: msys2/setup-msys2@v2 - with: - update: true - pacboy: >- - make:p gcc:p diffutils:p - release: false - - - if: ${{ matrix.test == 'old' }} - name: oldtest - shell: msys2 {0} - run: | - cd test/old/testdir - mingw32-make VERBOSE=1 + uses: ./.github/workflows/test_windows.yml # This job tests the following things: # - Check if Release, MinSizeRel and RelWithDebInfo compiles correctly. diff --git a/.github/workflows/test_windows.yml b/.github/workflows/test_windows.yml new file mode 100644 index 0000000000..d92993a08c --- /dev/null +++ b/.github/workflows/test_windows.yml @@ -0,0 +1,72 @@ +name: windows +on: + workflow_call: + inputs: + build_flags: + type: string + functionaltest_timeout: + default: 20 + type: number + workflow_dispatch: + +jobs: + windows: + runs-on: windows-2022 + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + test: [functional, old] + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + + - name: Build deps + run: | + cmake -S cmake.deps -B .deps -G Ninja -D CMAKE_BUILD_TYPE='RelWithDebInfo' + cmake --build .deps + + - name: Build + run: | + cmake --preset ci -D CMAKE_BUILD_TYPE='RelWithDebInfo' ${{ inputs.build_flags }} + cmake --build build + + - name: Install test deps + run: | + $PSNativeCommandArgumentPassing = 'Legacy' + + & build\bin\nvim.exe "--version" + + # Ensure that the "win32" feature is set. + & build\bin\nvim -u NONE --headless -c 'exe !has(\"win32\").\"cq\"' + + python -m pip install pynvim + # Sanity check + python -c "import pynvim; print(str(pynvim))" + + node --version + npm.cmd --version + + npm.cmd install -g neovim + Get-Command -CommandType Application neovim-node-host.cmd + npm.cmd link neovim + + - if: ${{ matrix.test == 'functional' }} + name: functionaltest + timeout-minutes: ${{ inputs.functionaltest_timeout }} + run: cmake --build build --target functionaltest + + - if: ${{ matrix.test == 'old' }} + uses: msys2/setup-msys2@v2 + with: + update: true + pacboy: >- + make:p gcc:p diffutils:p + release: false + + - if: ${{ matrix.test == 'old' }} + name: oldtest + shell: msys2 {0} + run: | + cd test/old/testdir + mingw32-make VERBOSE=1 diff --git a/.github/workflows/vim_patches.yml b/.github/workflows/vim_patches.yml index 711ddae815..f4251336c7 100644 --- a/.github/workflows/vim_patches.yml +++ b/.github/workflows/vim_patches.yml @@ -50,6 +50,6 @@ jobs: if: ${{ steps.update-version.outputs.NEW_PATCHES != 0 }} run: | git add -u - git commit -m 'version.c: update [skip ci]' + git commit -m 'docs: update version.c [skip ci]' git push --force https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY} ${VERSION_BRANCH} gh pr create --draft --fill --label vim-patch --base ${GITHUB_REF#refs/heads/} --head ${VERSION_BRANCH} || true @@ -8,15 +8,12 @@ Anmol Sethi <hi@nhooyr.io> <nhooyr@users.noreply.github.com> BK1603 <chouhan.shreyansh2702@gmail.com> Shreyansh Chouhan Billy Su <g4691821@gmail.com> Billy SU Billy Vong <billyvg@gmail.com> <billyvg@users.noreply.github.com> -bfredl <bjorn.linse@gmail.com> Carlos Hernandez <carlos@techbyte.ca> <hurricanehrndz@users.noreply.github.com> Chris Kipp <ckipp@pm.me> ckipp01 Christian Clason <c.clason@uni-graz.at> <christian.clason@uni-due.de> Cédric Barreteau <> <cbarrete@users.noreply.github.com> Dan Aloni <alonid@gmail.com> <dan@kernelim.com> Daniel Hahler <git@thequod.de> <github@thequod.de> -dundargoc <gocdundar@gmail.com> <33953936+dundargoc@users.noreply.github.com> -dundargoc <gocdundar@gmail.com> Dundar Goc Eisuke Kawashima <e-kwsm@users.noreply.github.com> E Kawashima ElPiloto <luis.r.piloto@gmail.com> Luis Piloto Eliseo Martínez <eliseomarmol@gmail.com> Eliseo Martínez @@ -38,6 +35,7 @@ J Phani Mahesh <phanimahesh@gmail.com> <github@phanimahesh.me> Jack Bracewell <FriedSock@users.noreply.github.com> <jack.bracewell@unboxedconsulting.com> Jack Bracewell <FriedSock@users.noreply.github.com> <jbtwentythree@gmail.com> Jacques Germishuys <jacquesg@striata.com> <jacquesg@users.noreply.github.com> +Jaehwang Jung <tomtomjhj@gmail.com> Jaehwang Jerry Jung Jakub Łuczyński <doubleloop@o2.pl> <doubleloop@users.noreply.github.com> James McCoy <jamessan@jamessan.com> <vega.james@gmail.com> Jan Edmund Lazo <jan.lazo@mail.utoronto.ca> <janedmundlazo@hotmail.com> @@ -57,6 +55,8 @@ Kwon-Young Choi <kwon-young.choi@hotmail.fr> Kwon-Young Lewis Russell <lewis6991@gmail.com> <me@lewisr.dev> Lucas Hoffmann <l-m-h@web.de> <lucc@posteo.de> Lucas Hoffmann <l-m-h@web.de> <lucc@users.noreply.github.com> +Luuk van Baal <luukvbaal@gmail.com> <31730729+luukvbaal@users.noreply.github.com> +Luuk van Baal <luukvbaal@gmail.com> luukvbaal Marco Hinz <mh.codebro@gmail.com> <mh.codebro+github@gmail.com> Marvim the Paranoid Android <marvim@users.noreply.github.com> marvim Mateusz Czapliński <czapkofan@gmail.com> Mateusz Czaplinski @@ -111,14 +111,20 @@ Yorick Peterse <git@yorickpeterse.com> <yorick@yorickpeterse.com> ZyX <kp-pav@yandex.ru> <kp-pav@ya.ru> ZyX <kp-pav@yandex.ru> Nikolai Aleksandrovich Pavlov aph <a.hewson@gmail.com> Ashley Hewson +bfredl <bjorn.linse@gmail.com> butwerenotthereyet <58348703+butwerenotthereyet@users.noreply.github.com> We're Yet chemzqm <chemzqm@gmail.com> Qiming zhao chentau <tchen1998@gmail.com> Tony Chen dedmass <carlo.abelli@gmail.com> Carlo Abelli +dundargoc <gocdundar@gmail.com> <33953936+dundargoc@users.noreply.github.com> +dundargoc <gocdundar@gmail.com> Dundar Goc equal-l2 <eng.equall2@gmail.com> <equal-l2@users.noreply.github.com> francisco souza <fsouza@users.noreply.github.com> <108725+fsouza@users.noreply.github.com> glacambre <code@lacamb.re> <me@r4> glacambre <code@lacamb.re> Ghjuvan Lacambre +glepnir <glephunter@gmail.com> Raphael +glepnir <glepnir@gopherhub.org> Raphael +glepnir <glepnir@neovim.pro> Raphael ii14 <ii14@users.noreply.github.com> <59243201+ii14@users.noreply.github.com> jdrouhard <john@jmdtech.org> <github@jmdtech.org> kuuote <znmxodq1@gmail.com> <36663503+kuuote@users.noreply.github.com> @@ -16,7 +16,7 @@ **Notes**: - From the repository's root directory, running `make` will download and build all the needed dependencies and put the `nvim` executable in `build/bin`. -- Third-party dependencies (libuv, LuaJIT, etc.) are downloaded automatically to `.deps/`. See the [FAQ](FAQ#build-issues) if you have issues. +- Third-party dependencies (libuv, LuaJIT, etc.) are downloaded automatically to `.deps/`. See the [FAQ](https://neovim.io/doc/user/faq.html#faq-build) if you have issues. - After building, you can run the `nvim` executable without installing it by running `VIMRUNTIME=runtime ./build/bin/nvim`. - If you plan to develop Neovim, install [Ninja](https://ninja-build.org/) for faster builds. It will automatically be used. - Install [ccache](https://ccache.dev/) for faster rebuilds of Neovim. It's used by default. To disable it, use `CCACHE_DISABLE=true make`. @@ -240,7 +240,7 @@ cmake --build build ### How to build without "bundled" dependencies 1. Manually install the dependencies: - - libuv libluv libtermkey luajit lua-lpeg lua-mpack msgpack-c tree-sitter unibilium + - libuv libluv libvterm luajit lua-lpeg lua-mpack msgpack-c tree-sitter tree-sitter-bash tree-sitter-c tree-sitter-lua tree-sitter-markdown tree-sitter-python tree-sitter-query tree-sitter-vim tree-sitter-vimdoc unibilium 2. Run CMake: ```sh cmake -B build -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo @@ -255,11 +255,12 @@ cmake --build build ``` 3. Run `make`, `ninja`, or whatever build tool you told CMake to generate. - Using `ninja` is strongly recommended. +4. If treesitter parsers are not bundled, they need to be available in a `parser/` runtime directory (e.g. `/usr/share/nvim/runtime/parser/`). #### Debian 10 (Buster) example: ```sh -sudo apt install luajit libluajit-5.1-dev lua-mpack lua-lpeg libunibilium-dev libmsgpack-dev libtermkey-dev +sudo apt install luajit libluajit-5.1-dev lua-mpack lua-lpeg libunibilium-dev libmsgpack-dev cmake -S cmake.deps -B .deps -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_BUNDLED=OFF -DUSE_BUNDLED_LIBUV=ON -DUSE_BUNDLED_LUV=ON -DUSE_BUNDLED_LIBVTERM=ON -DUSE_BUNDLED_TS=ON cmake --build .deps cmake -B build -G Ninja -D CMAKE_BUILD_TYPE=RelWithDebInfo @@ -290,13 +291,13 @@ Platform-specific requirements are listed below. ### Ubuntu / Debian ```sh -sudo apt-get install ninja-build gettext cmake unzip curl +sudo apt-get install ninja-build gettext cmake unzip curl build-essential ``` -### CentOS / RHEL / Fedora +### RHEL / Fedora ``` -sudo dnf -y install ninja-build cmake gcc make unzip gettext curl +sudo dnf -y install ninja-build cmake gcc make unzip gettext curl glibc-gconv-extra ``` ### openSUSE @@ -345,7 +346,7 @@ buildPhase ``` Tests are not available by default, because of some unfixed failures. You can enable them via adding this package in your overlay: -``` +``` neovim-dev = (super.pkgs.neovim-unwrapped.override { doCheck=true; }).overrideAttrs(oa:{ diff --git a/CMakeLists.txt b/CMakeLists.txt index 009b562953..7c0f8483ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,9 @@ # CMAKE REFERENCE -# intro: https://codingnest.com/basic-cmake/ -# best practices (3.0+): https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1 -# pitfalls: https://izzys.casa/2019/02/everything-you-never-wanted-to-know-about-cmake/ +# - intro: https://codingnest.com/basic-cmake/ +# - best practices (3.0+): https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1 +# - pitfalls: https://izzys.casa/2019/02/everything-you-never-wanted-to-know-about-cmake/ +# - troubleshooting: +# - variable_watch https://cmake.org/cmake/help/latest/command/variable_watch.html # Version should match the tested CMAKE_URL in .github/workflows/build.yml. cmake_minimum_required(VERSION 3.13) @@ -18,7 +20,7 @@ if(POLICY CMP0135) endif() if(XCODE) - message(FATAL_ERROR [[Xcode generator is not supported. Use "Ninja" or "Unix Makefiles" instead.]]) + message(FATAL_ERROR [[Xcode generator is not supported. Use "Ninja" or "Unix Makefiles" instead]]) endif() # Point CMake at any custom modules we may ship @@ -48,11 +50,6 @@ file(GLOB DOCFILES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/runtime/doc/*.txt) set_directory_properties(PROPERTIES EP_PREFIX "${DEPS_BUILD_DIR}") -find_program(CCACHE_PRG ccache) -if(CCACHE_PRG) - set(CMAKE_C_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env CCACHE_SLOPPINESS=pch_defines,time_macros ${CCACHE_PRG}) -endif() - if(NOT CI_BUILD) set(CMAKE_INSTALL_MESSAGE NEVER) endif() @@ -144,14 +141,14 @@ endif() # If not in a git repo (e.g., a tarball) these tokens define the complete # version string, else they are combined with the result of `git describe`. set(NVIM_VERSION_MAJOR 0) -set(NVIM_VERSION_MINOR 10) +set(NVIM_VERSION_MINOR 11) set(NVIM_VERSION_PATCH 0) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers # API level set(NVIM_API_LEVEL 12) # Bump this after any API change. set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change. -set(NVIM_API_PRERELEASE true) +set(NVIM_API_PRERELEASE false) # Build-type: RelWithDebInfo # /Og means something different in MSVC @@ -167,6 +164,7 @@ endif() option(ENABLE_ASAN_UBSAN "Enable Clang address & undefined behavior sanitizer for nvim binary." OFF) option(ENABLE_MSAN "Enable Clang memory sanitizer for nvim binary." OFF) +# TSAN exists to test Luv threads. option(ENABLE_TSAN "Enable Clang thread sanitizer for nvim binary." OFF) if((ENABLE_ASAN_UBSAN AND ENABLE_MSAN) @@ -269,7 +267,7 @@ add_custom_target(lintcommit add_dependencies(lintcommit nvim_bin) add_custom_target(lint) -add_dependencies(lint lintc lintlua lintsh lintcommit lintdoc) +add_dependencies(lint lintc lintlua lintsh lintcommit) # Format add_glob_target( @@ -303,13 +301,12 @@ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) endif() ExternalProject_Add(uncrustify - URL https://github.com/uncrustify/uncrustify/archive/uncrustify-0.78.1.tar.gz - URL_HASH SHA256=ecaf4c0adca14c36dfffa30bc28e69865115ecd602c90eb16a8cddccb41caad2 + URL https://github.com/uncrustify/uncrustify/archive/uncrustify-0.79.0.tar.gz + URL_HASH SHA256=e7afaeabf636b7f0ce4e3e9747b95f7bd939613a8db49579755dddf44fedca5f DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/uncrustify CMAKE_ARGS ${DEPS_CMAKE_ARGS} - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} EXCLUDE_FROM_ALL TRUE - ${EXTERNALPROJECT_OPTIONS}) + DOWNLOAD_NO_PROGRESS TRUE) option(USE_BUNDLED_BUSTED "Use bundled busted" ON) if(USE_BUNDLED_BUSTED) @@ -322,7 +319,7 @@ if(USE_BUNDLED_BUSTED) BUILD_COMMAND "" INSTALL_COMMAND "" EXCLUDE_FROM_ALL TRUE - ${EXTERNALPROJECT_OPTIONS}) + DOWNLOAD_NO_PROGRESS TRUE) else() add_custom_target(lua-dev-deps) endif() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2f5c2cce5a..d080f3079e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,10 +8,11 @@ If you want to help but don't know where to start, here are some low-risk/isolated tasks: - Try a [complexity:low] issue. -- Fix bugs found by [Clang](#clang-scan-build) or [Coverity](#coverity). +- Fix bugs found by [Coverity](#coverity). - [Merge a Vim patch] (requires strong familiarity with Vim) - NOTE: read the above link before sending improvements to "runtime files" (anything in `runtime/`). - - Vimscript and documentation files are (mostly) maintained by [Vim](https://github.com/vim/vim), not Nvim. + - Vimscript and documentation files are (mostly) maintained by [Vim], not Nvim. + - Nvim's [filetype detection](https://github.com/neovim/neovim/blob/master/runtime/lua/vim/filetype.lua) behavior matches Vim, so changes to filetype detection should be submitted to [Vim] first. - Lua files are maintained by Nvim. Reporting problems @@ -82,38 +83,36 @@ a comment. ### Commit messages Follow the [conventional commits guidelines][conventional_commits] to *make reviews easier* and to make -the VCS/git logs more valuable. The general structure of a commit message is: - -``` -<type>([optional scope]): <description> - -[optional body] - -[optional footer(s)] -``` +the VCS/git logs more valuable. The structure of a commit message is: + + type(scope): subject + + Problem: + ... + + Solution: + ... + +- Commit message **subject** (you can **ignore this for "fixup" commits** or any commits you expect to be squashed): + - Prefix with a [_type_](https://github.com/commitizen/conventional-commit-types/blob/master/index.json): + - `build ci docs feat fix perf refactor revert test vim-patch` + - Append an optional `(scope)` such as `(lsp)`, `(treesitter)`, `(float)`, … + - Use the _imperative voice_: "Fix bug" rather than "Fixed bug" or "Fixes bug." + - Keep it short (under 72 characters). +- Commit message **body** (detail): + - Concisely describe the Problem/Solution in the commit **body**. [Describing the problem](https://lamport.azurewebsites.net/pubs/state-the-problem.pdf) + _independently of the solution_ often leads to a better understanding for you, reviewers, and future readers. + ``` + Problem: + + Solution: + ``` +- Indicate breaking API changes with "!" after the type, and a "BREAKING CHANGE" footer. Example: + ``` + refactor(provider)!: drop support for Python 2 -- Prefix the commit subject with one of these [_types_](https://github.com/commitizen/conventional-commit-types/blob/master/index.json): - - `build`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `test`, `vim-patch` - - You can **ignore this for "fixup" commits** or any commits you expect to be squashed. -- Append optional scope to _type_ such as `(lsp)`, `(treesitter)`, `(float)`, … -- _Description_ shouldn't start with a capital letter or end in a period. -- Use the _imperative voice_: "Fix bug" rather than "Fixed bug" or "Fixes bug." -- Try to keep the first line under 72 characters. -- A blank line must follow the subject. -- Breaking API changes must be indicated by - 1. "!" after the type/scope, and - 2. a "BREAKING CHANGE" footer describing the change. - Example: - ``` - refactor(provider)!: drop support for Python 2 - - BREAKING CHANGE: refactor to use Python 3 features since Python 2 is no longer supported. - ``` - -### News - -High level release notes are maintained in [news.txt](runtime/doc/news.txt). A PR is not required to add a news item -but is generally recommended. + BREAKING CHANGE: refactor to use Python 3 features since Python 2 is no longer supported. + ``` ### Automated builds (CI) @@ -132,21 +131,6 @@ Each pull request must pass the automated builds on [Cirrus CI] and [GitHub Acti - To see CI results faster in your PR, you can temporarily set `TEST_FILE` in [test.yml](https://github.com/neovim/neovim/blob/e35b9020b16985eee26e942f9a3f6b045bc3809b/.github/workflows/test.yml#L29). -### Clang scan-build - -View the [Clang report] to see potential bugs found by the Clang -[scan-build](https://clang-analyzer.llvm.org/scan-build.html) analyzer. - -- Search the Neovim commit history to find examples: - ```bash - git log --oneline --no-merges --grep clang - ``` -- To verify a fix locally, run `scan-build` like this: - ```bash - rm -rf build/ - scan-build --use-analyzer=/usr/bin/clang make - ``` - ### Coverity Coverity runs against the master build. To view the defects you must @@ -318,6 +302,29 @@ types, etc. See [:help dev-lua-doc][dev-lua-doc]. - Private functions usually should be underscore-prefixed (named "_foo", not "foo"). - Mark deprecated functions with `@deprecated`. +Third-party dependencies +------------------------ + +To build Nvim using a different commit of a dependency change the appropriate +URL in `cmake.deps/deps.txt`. For example, to use a different version of luajit +replace the value in `LUAJIT_URL` with the wanted commit hash: + +```bash +LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/<sha>.tar.gz +``` + +Set `DEPS_IGNORE_SHA` to `TRUE` in `cmake.deps/CMakeLists.txt` to skip hash +check from cmake. + +Alternatively, you may point the URL as a local path where the repository is. +This is convenient when bisecting a problem in a dependency with `git bisect`. +This may require running `make distclean` between each build. Hash checking is +always skipped in this case regardless of `DEPS_IGNORE_SHA`. + +```bash +LUAJIT_URL /home/user/luajit +``` + Reviewing --------- @@ -341,6 +348,7 @@ as context, use the `-W` argument as well. [Cirrus CI]: https://cirrus-ci.com/github/neovim/neovim [Clang report]: https://neovim.io/doc/reports/clang/ [GitHub Actions]: https://github.com/neovim/neovim/actions +[Vim]: https://github.com/vim/vim [clangd]: https://clangd.llvm.org [Merge a Vim patch]: https://neovim.io/doc/user/dev_vimpatch.html [complexity:low]: https://github.com/neovim/neovim/issues?q=is%3Aopen+is%3Aissue+label%3Acomplexity%3Alow diff --git a/INSTALL.md b/INSTALL.md index 9bba1f0a8c..056a5acb04 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -15,7 +15,8 @@ Install from download Downloads are available on the [Releases](https://github.com/neovim/neovim/releases) page. * Latest [stable release](https://github.com/neovim/neovim/releases/latest) - * [macOS](https://github.com/neovim/neovim/releases/latest/download/nvim-macos.tar.gz) + * [macOS x86](https://github.com/neovim/neovim/releases/latest/download/nvim-macos-x86_64.tar.gz) + * [macOS arm](https://github.com/neovim/neovim/releases/latest/download/nvim-macos-arm64.tar.gz) * [Linux](https://github.com/neovim/neovim/releases/latest/download/nvim-linux64.tar.gz) * [Windows](https://github.com/neovim/neovim/releases/latest/download/nvim-win64.msi) * Latest [development prerelease](https://github.com/neovim/neovim/releases/nightly) @@ -36,7 +37,7 @@ Windows 8+ is required. Windows 7 or older is not supported. ### [Chocolatey](https://chocolatey.org) -- **Release (v0.7):** `choco install neovim` (use -y for automatically skipping confirmation messages) +- **Latest Release:** `choco install neovim` (use -y for automatically skipping confirmation messages) - **Development (pre-release):** `choco install neovim --pre` ### [Scoop](https://scoop.sh/) @@ -60,9 +61,9 @@ Several Neovim GUIs are available from scoop (extras): [scoop.sh/#/apps?q=neovim - Add the `bin` folder (e.g. `C:\Program Files\nvim\bin`) to your PATH. - This makes it easy to run `nvim` and `nvim-qt` from anywhere. -- If `:set spell` does not work, create the `C:/Users/foo/AppData/Local/nvim/site/spell` folder. - You can then copy your spell files over (for English, located - [here](https://github.com/vim/vim/blob/master/runtime/spell/en.utf-8.spl) and +- If `:set spell` does not work, create the `C:/Users/foo/AppData/Local/nvim/site/spell` folder. + You can then copy your spell files over (for English, located + [here](https://github.com/vim/vim/blob/master/runtime/spell/en.utf-8.spl) and [here](https://github.com/vim/vim/blob/master/runtime/spell/en.utf-8.sug)); - For Python plugins you need the `pynvim` module. "Virtual envs" are recommended. After activating the virtual env do `pip install pynvim` (in *both*). Edit your `init.vim` so that it contains the path to the env's Python executable: ```vim @@ -78,9 +79,17 @@ Several Neovim GUIs are available from scoop (extras): [scoop.sh/#/apps?q=neovim The [Releases](https://github.com/neovim/neovim/releases) page provides pre-built binaries for macOS 10.15+. - curl -LO https://github.com/neovim/neovim/releases/download/nightly/nvim-macos.tar.gz - tar xzf nvim-macos.tar.gz - ./nvim-macos/bin/nvim +For x86_64: + + curl -LO https://github.com/neovim/neovim/releases/download/nightly/nvim-macos-x86_64.tar.gz + tar xzf nvim-macos-x86_64.tar.gz + ./nvim-macos-x86_64/bin/nvim + +For arm64: + + curl -LO https://github.com/neovim/neovim/releases/download/nightly/nvim-macos-arm64.tar.gz + tar xzf nvim-macos-arm64.tar.gz + ./nvim-macos-arm64/bin/nvim ### [Homebrew](https://brew.sh) on macOS or Linux @@ -122,7 +131,7 @@ To expose nvim globally: And the following line to `~/.bashrc`: - export PATH="$PATH:/opt/nvim/" + export PATH="$PATH:/opt/nvim/" If the `./nvim.appimage` command fails, try: ```sh @@ -206,7 +215,7 @@ You can find Neovim on [Flathub](https://flathub.org/apps/details/io.neovim.nvim You can add `/var/lib/flatpak/exports/bin` (or `~/.local/share/flatpak/exports/bin` if you used `--user`) to the `$PATH` and run it with `io.neovim.nvim`. -Note that Flatpak'ed Neovim will look for `init.vim` in `~/.var/app/io.neovim.nvim/config/nvim` instead of `~/.config/nvim`. +Note that Flatpak'ed Neovim will look for `init.vim` in `~/.var/app/io.neovim.nvim/config/nvim` instead of `~/.config/nvim`. ### Gentoo Linux @@ -259,7 +268,7 @@ Neovim can be installed with: sudo zypper in neovim To install the Python modules: - + sudo zypper in python-neovim python3-neovim ### PLD Linux diff --git a/MAINTAIN.md b/MAINTAIN.md index 3c21b13276..f7f0c5769c 100644 --- a/MAINTAIN.md +++ b/MAINTAIN.md @@ -22,7 +22,7 @@ In practice we haven't found a way to forecast more precisely than "next" and * Next feature-release (1.x.0) The forecasting problem might be solved with an explicit priority system (like -Bram's todo.txt). Meanwhile the Neovim priority system is defined by: +Vim's todo.txt). Meanwhile the Neovim priority system is defined by: * PRs nearing completion. * Issue labels. E.g. the `has:plan` label increases the ticket's priority merely @@ -50,15 +50,14 @@ has a major bug: 1. Fix the bug on `master`. 2. Cherry-pick the fix to `release-x.y`. 3. Cut a release from `release-x.y`. - * Run `./scripts/release.sh` - * Update (force-push) the remote `stable` tag. + * Run `./scripts/release.sh` (requires [git cliff](https://github.com/orhun/git-cliff)) * The [CI job](https://github.com/neovim/neovim/blob/3d45706478cd030c3ee05b4f336164bb96138095/.github/workflows/release.yml#L11-L13) - will update the release assets and force-push to the `stable` tag. + will update the release assets and [force-push to the "stable" tag](https://github.com/neovim/neovim/blob/cdd87222c86c5b2274a13d36f23de0637462e317/.github/workflows/release.yml#L229). ### Release automation -Neovim automation includes a [backport bot](https://github.com/zeebe-io/backport-action). -Trigger the action by labeling a PR with `backport release-X.Y`. See `.github/workflows/backport.yml`. +Neovim automation includes a [backport bot](https://github.com/korthout/backport-action). +Trigger the action by labeling a PR with `ci:backport release-x.y`. See `.github/workflows/backport.yml`. Deprecating and removing features --------------------------------- @@ -72,39 +71,37 @@ inform users of the change. When a (non-experimental) feature is slated to be removed it should: 1. Be _soft_ deprecated in the _next_ release - - Use of the deprecated feature will still work. - - This means deprecating via documentation and annotation (`@deprecated`). - - Include a note in `news.txt` under `DEPRECATIONS`. - - For Lua features, use `vim.deprecate()`. The specified version is the - current minor version + 2. For example, if the current version is - `v0.10.0-dev-1957+gd676746c33` then use `0.12`. - - For Vimscript features, use `v:lua.vim.deprecate()`. Use the same version - as described for Lua features. + - Use of the deprecated feature will still work. + - This means deprecating via documentation and annotation (`@deprecated`). + - Include a note in `deprecated.txt`. + - For Lua features, use `vim.deprecate()`. The specified version is the + current minor version + 2. For example, if the current version is + `v0.10.0-dev-1957+gd676746c33` then use `0.12`. + - For Vimscript features, use `v:lua.vim.deprecate()`. Use the same version + as described for Lua features. 2. Be _hard_ deprecated in a following a release in which it was soft deprecated. - - Use of the deprecated feature will still work but should issue a warning. - - Features implemented in C will need bespoke implementations to communicate - to users that the feature is deprecated. + - Use of the deprecated feature will still work but should issue a warning. + - Features implemented in C will need bespoke implementations to communicate + to users that the feature is deprecated. 3. Be removed in a release following the release in which it was hard deprecated - - Usually this will be the next release, but it may be a later release if a - longer deprecation cycle is desired - - If possible, keep the feature as a stub (e.g. function API) and issue an error - when it is accessed. + - Usually this will be the next release, but it may be a later release if + a longer deprecation cycle is desired + - If possible, keep the feature as a stub (e.g. function API) and issue an + error when it is accessed. Example: -``` - Deprecation Removal - ┆ ┆ ┆ - ┆ Soft ┆ Hard ┆ - ┆ Deprecation ┆ Deprecation ┆ - ┆ Period ┆ Period ┆ - ──────────────────────────────────────────────────────────── -Version: 0.10 0.11 0.12 - ──────────────────────────────────────────────────────────── - Old code Old code Old code - + + - New code New code New code -``` + Deprecation Removal + ┆ ┆ ┆ + ┆ Soft ┆ Hard ┆ + ┆ Deprecation ┆ Deprecation ┆ + ┆ Period ┆ Period ┆ + ──────────────────────────────────────────────────────────── + Version: 0.10 0.11 0.12 + ──────────────────────────────────────────────────────────── + Old code Old code Old code + + + + New code New code New code Feature removals which may benefit from community input or further discussion should also have a tracking issue (which should be linked to in the release @@ -132,12 +129,12 @@ Some can be auto-bumped by `scripts/bump_deps.lua`. * [gettext](https://ftp.gnu.org/pub/gnu/gettext/) * [libiconv](https://ftp.gnu.org/pub/gnu/libiconv) * [libuv](https://github.com/libuv/libuv) -* [libvterm](http://www.leonerd.org.uk/code/libvterm/) - * Downloading from the original source is unreliable, so we use our [mirror](https://github.com/neovim/libvterm) instead. +* [libvterm](https://www.leonerd.org.uk/code/libvterm/) + * Downloading from the original source is unreliable, so we use our [mirror](https://github.com/neovim/libvterm) instead. * [lua-compat](https://github.com/keplerproject/lua-compat-5.3) * [tree-sitter](https://github.com/tree-sitter/tree-sitter) * [unibilium](https://github.com/neovim/unibilium) - * The original project [was abandoned](https://github.com/neovim/neovim/issues/10302), so the [neovim/unibilium](https://github.com/neovim/unibilium) fork is considered "upstream" and is maintained on the `master` branch. + * The original project [was abandoned](https://github.com/neovim/neovim/issues/10302), so the [neovim/unibilium](https://github.com/neovim/unibilium) fork is considered "upstream" and is maintained on the `master` branch. * [treesitter parsers](https://github.com/neovim/neovim/blob/7e97c773e3ba78fcddbb2a0b9b0d572c8210c83e/cmake.deps/deps.txt#L47-L62) ### Vendored dependencies @@ -165,12 +162,14 @@ These dependencies are "vendored" (inlined), we must update the sources manually * `runtime/lua/coxpcall.lua`: coxpcall (only needed for PUC lua, builtin to luajit) * `src/termkey`: [libtermkey](https://github.com/neovim/libtermkey) -Non-technical dependencies +Other dependencies -------------------------- * GitHub users: * https://github.com/marvim * https://github.com/nvim-winget +* Org secrets/tokens: + * `CODECOV_TOKEN` * Domain names (held in https://namecheap.com): * neovim.org * neovim.io @@ -208,18 +207,16 @@ https://github.com/neovim/neovim-backup * Avoid macOS if an Ubuntu or a Windows runner can be used instead. This is because macOS runners have [tighter restrictions on the number of concurrent jobs](https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration#usage-limits). - * Runner versions: * For special-purpose jobs where the runner version doesn't really matter, prefer `-latest` tags so we don't need to manually bump the versions. An - example of a special-purpose workflow is `labeler.yml`. - * For our testing jobs, which are in `test.yml` and `build.yml`, prefer to - use the latest stable (i.e. non-beta) version explicitly. Avoid using the - `-latest` tags here as it makes it difficult to determine from an - unrelated PR if a failure is due to the PR itself or due to GitHub bumping - the `-latest` tag without our knowledge. There's also a high risk that - automatically bumping the CI versions will fail due to manual work being - required from experience. + example of a special-purpose workflow is `labeler_pr.yml`. + * For our testing job `test.yml`, prefer to use the latest stable (i.e. + non-beta) version explicitly. Avoid using the `-latest` tags here as it + makes it difficult to determine from an unrelated PR if a failure is due + to the PR itself or due to GitHub bumping the `-latest` tag without our + knowledge. There's also a high risk that automatically bumping the CI + versions will fail due to manual work being required from experience. * For our release job, which is `release.yml`, prefer to use the oldest stable (i.e. non-deprecated) versions available. The reason is that we're trying to produce images that work in the broadest number of environments, @@ -229,9 +226,10 @@ https://github.com/neovim/neovim-backup Some github labels are used to trigger certain jobs: -* `backport release-x.y` - backport to release branch +* `ci:backport release-x.y` - backport to branch `release-x.y` * `ci:s390x` - enable s390x CI * `ci:skip-news` - skip news.yml workflows +* `ci:windows-asan` - test windows with ASAN enabled * `needs:response` - close PR after a certain amount of time if author doesn't respond @@ -6,7 +6,6 @@ </h1> [](https://scan.coverity.com/projects/2227) -[](https://neovim.io/doc/reports/clang) [](https://repology.org/metapackage/neovim) [](https://buildd.debian.org/neovim) [](https://github.com/neovim/neovim/releases/) diff --git a/cmake.config/CMakeLists.txt b/cmake.config/CMakeLists.txt index f3cc803cf5..3171f9e88c 100644 --- a/cmake.config/CMakeLists.txt +++ b/cmake.config/CMakeLists.txt @@ -79,6 +79,17 @@ int main(void) } " HAVE_PWD_FUNCS) +check_c_source_compiles(" +#include <intrin.h> + +int main(void) +{ + unsigned long index; + unsigned char mask = 0x8000; + _BitScanForward64(&index, mask); + return 0; +} +" HAVE_BITSCANFORWARD64) if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") check_c_source_compiles(" @@ -183,6 +194,6 @@ file(GENERATE INPUT "${PROJECT_BINARY_DIR}/cmake.config/auto/versiondef.h.gen") configure_file ( - "${PROJECT_SOURCE_DIR}/cmake.config/pathdef.c.in" - "${PROJECT_BINARY_DIR}/cmake.config/auto/pathdef.c" + "${PROJECT_SOURCE_DIR}/cmake.config/pathdef.h.in" + "${PROJECT_BINARY_DIR}/cmake.config/auto/pathdef.h" ESCAPE_QUOTES) diff --git a/cmake.config/config.h.in b/cmake.config/config.h.in index 47fbb5bafe..af4e13fc11 100644 --- a/cmake.config/config.h.in +++ b/cmake.config/config.h.in @@ -52,3 +52,4 @@ #cmakedefine HAVE_EXECINFO_BACKTRACE #cmakedefine HAVE_BUILTIN_ADD_OVERFLOW #cmakedefine HAVE_WIMPLICIT_FALLTHROUGH_FLAG +#cmakedefine HAVE_BITSCANFORWARD64 diff --git a/cmake.config/pathdef.c.in b/cmake.config/pathdef.h.in index 773fa1d592..bc09549fb7 100644 --- a/cmake.config/pathdef.c.in +++ b/cmake.config/pathdef.h.in @@ -1,4 +1,3 @@ -#include "${PROJECT_SOURCE_DIR}/src/nvim/vim_defs.h" char *default_vim_dir = "${CMAKE_INSTALL_FULL_DATAROOTDIR}/nvim"; char *default_vimruntime_dir = ""; char *default_lib_dir = "${CMAKE_INSTALL_FULL_LIBDIR}/nvim"; diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt index e37b606bd9..4853a1ab14 100644 --- a/cmake.deps/CMakeLists.txt +++ b/cmake.deps/CMakeLists.txt @@ -17,21 +17,13 @@ include(Deps) include(Find) include(Util) -set_default_buildtype(Release) -get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(NOT isMultiConfig) - list(APPEND DEPS_CMAKE_ARGS -D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) -endif() +#------------------------------------------------------------------------------- +# User settings +#------------------------------------------------------------------------------- -set(DEFAULT_MAKE_CFLAGS CFLAGS+=-g) - -check_c_compiler_flag(-Og HAS_OG_FLAG) -if(HAS_OG_FLAG) - set(DEFAULT_MAKE_CFLAGS CFLAGS+=-Og ${DEFAULT_MAKE_CFLAGS}) -endif() - -set(DEPS_INCLUDE_FLAGS "-I${DEPS_INSTALL_DIR}/include -I${DEPS_INSTALL_DIR}/include/luajit-2.1") +set(DEPS_IGNORE_SHA FALSE) +# Options option(USE_BUNDLED "Use bundled dependencies." ON) option(USE_BUNDLED_LIBUV "Use the bundled libuv." ${USE_BUNDLED}) @@ -45,7 +37,6 @@ option(USE_BUNDLED_MSGPACK "Use the bundled msgpack." ${USE_BUNDLED}) option(USE_BUNDLED_TS "Use the bundled treesitter runtime." ${USE_BUNDLED}) option(USE_BUNDLED_TS_PARSERS "Use the bundled treesitter parsers." ${USE_BUNDLED}) option(USE_BUNDLED_UNIBILIUM "Use the bundled unibilium." ${USE_BUNDLED}) - if(USE_BUNDLED AND MSVC) option(USE_BUNDLED_GETTEXT "Use the bundled version of gettext." ON) option(USE_BUNDLED_LIBICONV "Use the bundled version of libiconv." ON) @@ -56,6 +47,21 @@ endif() option(USE_EXISTING_SRC_DIR "Skip download of deps sources in case of existing source directory." OFF) +set_default_buildtype(Release) +get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(NOT isMultiConfig) + list(APPEND DEPS_CMAKE_ARGS -D CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) +endif() + +set(DEFAULT_MAKE_CFLAGS CFLAGS+=-g) + +check_c_compiler_flag(-Og HAS_OG_FLAG) +if(HAS_OG_FLAG) + set(DEFAULT_MAKE_CFLAGS CFLAGS+=-Og ${DEFAULT_MAKE_CFLAGS}) +endif() + +set(DEPS_INCLUDE_FLAGS "-I${DEPS_INSTALL_DIR}/include -I${DEPS_INSTALL_DIR}/include/luajit-2.1") + # If the macOS deployment target is not set manually (via $MACOSX_DEPLOYMENT_TARGET), # fall back to local system version. Needs to be done here and in top-level CMakeLists.txt. if(APPLE) diff --git a/cmake.deps/cmake/BuildGettext.cmake b/cmake.deps/cmake/BuildGettext.cmake index 33cfbe1a22..29127dc811 100644 --- a/cmake.deps/cmake/BuildGettext.cmake +++ b/cmake.deps/cmake/BuildGettext.cmake @@ -1,7 +1,6 @@ if(MSVC) + get_externalproject_options(gettext ${DEPS_IGNORE_SHA}) ExternalProject_Add(gettext - URL ${GETTEXT_URL} - URL_HASH SHA256=${GETTEXT_SHA256} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/gettext PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/cmake/GettextCMakeLists.txt @@ -9,7 +8,6 @@ if(MSVC) CMAKE_ARGS ${DEPS_CMAKE_ARGS} -D LIBICONV_INCLUDE_DIRS=${DEPS_INSTALL_DIR}/include -D LIBICONV_LIBRARIES=${DEPS_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}libcharset${CMAKE_STATIC_LIBRARY_SUFFIX}$<SEMICOLON>${DEPS_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}libiconv${CMAKE_STATIC_LIBRARY_SUFFIX} - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} ${EXTERNALPROJECT_OPTIONS}) else() message(FATAL_ERROR "Trying to build gettext in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}") diff --git a/cmake.deps/cmake/BuildLibiconv.cmake b/cmake.deps/cmake/BuildLibiconv.cmake index 362e9b4609..13126dc916 100644 --- a/cmake.deps/cmake/BuildLibiconv.cmake +++ b/cmake.deps/cmake/BuildLibiconv.cmake @@ -1,13 +1,11 @@ if(MSVC) + get_externalproject_options(libiconv ${DEPS_IGNORE_SHA}) ExternalProject_Add(libiconv - URL ${LIBICONV_URL} - URL_HASH SHA256=${LIBICONV_SHA256} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libiconv PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibiconvCMakeLists.txt ${DEPS_BUILD_DIR}/src/libiconv/CMakeLists.txt CMAKE_ARGS ${DEPS_CMAKE_ARGS} - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} ${EXTERNALPROJECT_OPTIONS}) else() message(FATAL_ERROR "Trying to build libiconv in an unsupported system ${CMAKE_SYSTEM_NAME}/${CMAKE_C_COMPILER_ID}") diff --git a/cmake.deps/cmake/BuildLibuv.cmake b/cmake.deps/cmake/BuildLibuv.cmake index e7f7fdf253..6f241f63f6 100644 --- a/cmake.deps/cmake/BuildLibuv.cmake +++ b/cmake.deps/cmake/BuildLibuv.cmake @@ -1,11 +1,9 @@ +get_externalproject_options(libuv ${DEPS_IGNORE_SHA}) ExternalProject_Add(libuv - URL ${LIBUV_URL} - URL_HASH SHA256=${LIBUV_SHA256} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libuv CMAKE_ARGS ${DEPS_CMAKE_ARGS} -D CMAKE_INSTALL_LIBDIR=lib -D BUILD_TESTING=OFF -D LIBUV_BUILD_SHARED=OFF -D UV_LINT_W4=OFF - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} ${EXTERNALPROJECT_OPTIONS}) diff --git a/cmake.deps/cmake/BuildLibvterm.cmake b/cmake.deps/cmake/BuildLibvterm.cmake index 63f5872cb2..3415d8debe 100644 --- a/cmake.deps/cmake/BuildLibvterm.cmake +++ b/cmake.deps/cmake/BuildLibvterm.cmake @@ -1,10 +1,8 @@ +get_externalproject_options(libvterm ${DEPS_IGNORE_SHA}) ExternalProject_Add(libvterm - URL ${LIBVTERM_URL} - URL_HASH SHA256=${LIBVTERM_SHA256} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/libvterm PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LibvtermCMakeLists.txt ${DEPS_BUILD_DIR}/src/libvterm/CMakeLists.txt CMAKE_ARGS ${DEPS_CMAKE_ARGS} - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} ${EXTERNALPROJECT_OPTIONS}) diff --git a/cmake.deps/cmake/BuildLpeg.cmake b/cmake.deps/cmake/BuildLpeg.cmake index b65baf4c0f..2dd9d62968 100644 --- a/cmake.deps/cmake/BuildLpeg.cmake +++ b/cmake.deps/cmake/BuildLpeg.cmake @@ -1,12 +1,10 @@ +get_externalproject_options(lpeg ${DEPS_IGNORE_SHA}) ExternalProject_Add(lpeg - URL ${LPEG_URL} - URL_HASH SHA256=${LPEG_SHA256} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lpeg PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/cmake/LpegCMakeLists.txt ${DEPS_BUILD_DIR}/src/lpeg/CMakeLists.txt CMAKE_ARGS ${DEPS_CMAKE_ARGS} -DCMAKE_C_FLAGS=${DEPS_INCLUDE_FLAGS} - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} ${EXTERNALPROJECT_OPTIONS}) if(USE_BUNDLED_LUAJIT) diff --git a/cmake.deps/cmake/BuildLua.cmake b/cmake.deps/cmake/BuildLua.cmake index 96b7a903f9..9a9f90db31 100644 --- a/cmake.deps/cmake/BuildLua.cmake +++ b/cmake.deps/cmake/BuildLua.cmake @@ -40,9 +40,8 @@ set(LUA_CONFIGURE_COMMAND -i ${DEPS_BUILD_DIR}/src/lua/src/luaconf.h) set(LUA_INSTALL_TOP_ARG "INSTALL_TOP=${DEPS_INSTALL_DIR}") +get_externalproject_options(lua ${DEPS_IGNORE_SHA}) ExternalProject_Add(lua - URL ${LUA_URL} - URL_HASH SHA256=${LUA_SHA256} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua CONFIGURE_COMMAND "${LUA_CONFIGURE_COMMAND}" BUILD_IN_SOURCE 1 diff --git a/cmake.deps/cmake/BuildLuajit.cmake b/cmake.deps/cmake/BuildLuajit.cmake index 81fa6446c4..db37d79099 100644 --- a/cmake.deps/cmake/BuildLuajit.cmake +++ b/cmake.deps/cmake/BuildLuajit.cmake @@ -1,20 +1,13 @@ -# BuildLuajit(TARGET targetname CONFIGURE_COMMAND ... BUILD_COMMAND ... INSTALL_COMMAND ...) -# Reusable function to build luajit, wraps ExternalProject_Add. -# Failing to pass a command argument will result in no command being run function(BuildLuajit) cmake_parse_arguments(_luajit "" - "TARGET" + "" "CONFIGURE_COMMAND;BUILD_COMMAND;INSTALL_COMMAND;DEPENDS" ${ARGN}) - if(NOT _luajit_TARGET) - set(_luajit_TARGET "luajit") - endif() - ExternalProject_Add(${_luajit_TARGET} - URL ${LUAJIT_URL} - URL_HASH SHA256=${LUAJIT_SHA256} - DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/${_luajit_TARGET} + get_externalproject_options(luajit ${DEPS_IGNORE_SHA}) + ExternalProject_Add(luajit + DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/luajit CONFIGURE_COMMAND "${_luajit_CONFIGURE_COMMAND}" BUILD_IN_SOURCE 1 BUILD_COMMAND "${_luajit_BUILD_COMMAND}" @@ -48,49 +41,11 @@ if(APPLE) set(DEPLOYMENT_TARGET "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") endif() -if((UNIX AND NOT APPLE) OR (APPLE AND NOT CMAKE_OSX_ARCHITECTURES)) +if(UNIX) BuildLuaJit(INSTALL_COMMAND ${BUILDCMD_UNIX} CC=${DEPS_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR} ${DEPLOYMENT_TARGET} install) -elseif(CMAKE_OSX_ARCHITECTURES AND APPLE) - - set(LUAJIT_C_COMPILER "${CMAKE_C_COMPILER}") - if(CMAKE_OSX_SYSROOT) - set(LUAJIT_C_COMPILER "${LUAJIT_C_COMPILER} -isysroot${CMAKE_OSX_SYSROOT}") - endif() - - # Passing multiple `-arch` flags to the LuaJIT build will cause it to fail. - # To get a working universal build, we build each requested architecture slice - # individually then `lipo` them all up. - set(LUAJIT_SRC_DIR "${DEPS_BUILD_DIR}/src/luajit") - foreach(ARCH IN LISTS CMAKE_OSX_ARCHITECTURES) - set(STATIC_CC "${LUAJIT_C_COMPILER} -arch ${ARCH}") - set(DYNAMIC_CC "${LUAJIT_C_COMPILER} -arch ${ARCH} -fPIC") - set(TARGET_LD "${LUAJIT_C_COMPILER} -arch ${ARCH}") - list(APPEND LUAJIT_THIN_EXECUTABLES "${LUAJIT_SRC_DIR}-${ARCH}/src/luajit") - list(APPEND LUAJIT_THIN_STATIC_LIBS "${LUAJIT_SRC_DIR}-${ARCH}/src/libluajit.a") - list(APPEND LUAJIT_THIN_DYLIBS "${LUAJIT_SRC_DIR}-${ARCH}/src/libluajit.so") - list(APPEND LUAJIT_THIN_TARGETS "luajit-${ARCH}") - - # See https://luajit.org/install.html#cross. - BuildLuaJit(TARGET "luajit-${ARCH}" - BUILD_COMMAND ${BUILDCMD_UNIX} - CC=${LUAJIT_C_COMPILER} STATIC_CC=${STATIC_CC} - DYNAMIC_CC=${DYNAMIC_CC} TARGET_LD=${TARGET_LD} - PREFIX=${DEPS_INSTALL_DIR} - ${DEPLOYMENT_TARGET}) - endforeach() - BuildLuaJit( - CONFIGURE_COMMAND ${BUILDCMD_UNIX} CC=${LUAJIT_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR} ${DEPLOYMENT_TARGET} - COMMAND ${CMAKE_COMMAND} -E rm -f ${LUAJIT_SRC_DIR}/src/luajit ${LUAJIT_SRC_DIR}/src/libluajit.so ${LUAJIT_SRC_DIR}/src/libluajit.a - BUILD_COMMAND lipo ${LUAJIT_THIN_EXECUTABLES} -create -output ${LUAJIT_SRC_DIR}/src/luajit - COMMAND lipo ${LUAJIT_THIN_STATIC_LIBS} -create -output ${LUAJIT_SRC_DIR}/src/libluajit.a - COMMAND lipo ${LUAJIT_THIN_DYLIBS} -create -output ${LUAJIT_SRC_DIR}/src/libluajit.so - INSTALL_COMMAND ${BUILDCMD_UNIX} CC=${LUAJIT_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR} ${DEPLOYMENT_TARGET} install - DEPENDS ${LUAJIT_THIN_TARGETS} - ) - elseif(MINGW) if(CMAKE_GENERATOR MATCHES "Ninja") diff --git a/cmake.deps/cmake/BuildLuv.cmake b/cmake.deps/cmake/BuildLuv.cmake index 9830ea717a..2e07f9e337 100644 --- a/cmake.deps/cmake/BuildLuv.cmake +++ b/cmake.deps/cmake/BuildLuv.cmake @@ -1,6 +1,6 @@ set(LUV_CMAKE_ARGS -D LUA_BUILD_TYPE=System - -D LUA_COMPAT53_DIR=${DEPS_BUILD_DIR}/src/lua-compat-5.3 + -D LUA_COMPAT53_DIR=${DEPS_BUILD_DIR}/src/lua_compat53 -D WITH_SHARED_LIBUV=ON -D BUILD_STATIC_LIBS=ON -D BUILD_MODULE=OFF) @@ -17,23 +17,20 @@ if(CMAKE_GENERATOR MATCHES "Unix Makefiles" AND list(APPEND LUV_CMAKE_ARGS -D CMAKE_MAKE_PROGRAM=gmake) endif() -ExternalProject_Add(lua-compat-5.3 - URL ${LUA_COMPAT53_URL} - URL_HASH SHA256=${LUA_COMPAT53_SHA256} - DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua-compat-5.3 +get_externalproject_options(lua_compat53 ${DEPS_IGNORE_SHA}) +ExternalProject_Add(lua_compat53 + DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/lua_compat53 CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" ${EXTERNALPROJECT_OPTIONS}) +get_externalproject_options(luv ${DEPS_IGNORE_SHA}) ExternalProject_Add(luv - DEPENDS lua-compat-5.3 - URL ${LUV_URL} - URL_HASH SHA256=${LUV_SHA256} + DEPENDS lua_compat53 DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/luv SOURCE_DIR ${DEPS_BUILD_DIR}/src/luv CMAKE_ARGS ${DEPS_CMAKE_ARGS} ${LUV_CMAKE_ARGS} - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} ${EXTERNALPROJECT_OPTIONS}) if(USE_BUNDLED_LUAJIT) diff --git a/cmake.deps/cmake/BuildMsgpack.cmake b/cmake.deps/cmake/BuildMsgpack.cmake index f60bdad5c9..8f82dab140 100644 --- a/cmake.deps/cmake/BuildMsgpack.cmake +++ b/cmake.deps/cmake/BuildMsgpack.cmake @@ -1,9 +1,7 @@ +get_externalproject_options(msgpack ${DEPS_IGNORE_SHA}) ExternalProject_Add(msgpack - URL ${MSGPACK_URL} - URL_HASH SHA256=${MSGPACK_SHA256} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/msgpack CMAKE_ARGS ${DEPS_CMAKE_ARGS} -D MSGPACK_BUILD_TESTS=OFF -D MSGPACK_BUILD_EXAMPLES=OFF - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} ${EXTERNALPROJECT_OPTIONS}) diff --git a/cmake.deps/cmake/BuildTreesitter.cmake b/cmake.deps/cmake/BuildTreesitter.cmake index cd27a13ee3..7eb98163b9 100644 --- a/cmake.deps/cmake/BuildTreesitter.cmake +++ b/cmake.deps/cmake/BuildTreesitter.cmake @@ -1,10 +1,8 @@ +get_externalproject_options(treesitter ${DEPS_IGNORE_SHA}) ExternalProject_Add(treesitter - URL ${TREESITTER_URL} - URL_HASH SHA256=${TREESITTER_SHA256} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/treesitter PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/cmake/TreesitterCMakeLists.txt ${DEPS_BUILD_DIR}/src/treesitter/CMakeLists.txt CMAKE_ARGS ${DEPS_CMAKE_ARGS} - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} ${EXTERNALPROJECT_OPTIONS}) diff --git a/cmake.deps/cmake/BuildTreesitterParsers.cmake b/cmake.deps/cmake/BuildTreesitterParsers.cmake index eddc896e69..837d075d20 100644 --- a/cmake.deps/cmake/BuildTreesitterParsers.cmake +++ b/cmake.deps/cmake/BuildTreesitterParsers.cmake @@ -15,22 +15,16 @@ function(BuildTSParser) set(TS_CMAKE_FILE TreesitterParserCMakeLists.txt) endif() - set(NAME treesitter-${TS_LANG}) - string(TOUPPER "TREESITTER_${TS_LANG}_URL" URL_VARNAME) - set(URL ${${URL_VARNAME}}) - string(TOUPPER "TREESITTER_${TS_LANG}_SHA256" HASH_VARNAME) - set(HASH ${${HASH_VARNAME}}) + set(NAME treesitter_${TS_LANG}) + get_externalproject_options(${NAME} ${DEPS_IGNORE_SHA}) ExternalProject_Add(${NAME} - URL ${URL} - URL_HASH SHA256=${HASH} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/${NAME} PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${TS_CMAKE_FILE} ${DEPS_BUILD_DIR}/src/${NAME}/CMakeLists.txt CMAKE_ARGS ${DEPS_CMAKE_ARGS} -D PARSERLANG=${TS_LANG} - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} ${EXTERNALPROJECT_OPTIONS}) endfunction() diff --git a/cmake.deps/cmake/BuildUnibilium.cmake b/cmake.deps/cmake/BuildUnibilium.cmake index 9f1871aaf5..7ce4b5fbf2 100644 --- a/cmake.deps/cmake/BuildUnibilium.cmake +++ b/cmake.deps/cmake/BuildUnibilium.cmake @@ -1,7 +1,5 @@ +get_externalproject_options(unibilium ${DEPS_IGNORE_SHA}) ExternalProject_Add(unibilium - URL ${UNIBILIUM_URL} - URL_HASH SHA256=${UNIBILIUM_SHA256} DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/unibilium CMAKE_ARGS ${DEPS_CMAKE_ARGS} - CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS} ${EXTERNALPROJECT_OPTIONS}) diff --git a/cmake.deps/cmake/GetBinaryDeps.cmake b/cmake.deps/cmake/GetBinaryDeps.cmake index 2f1e237588..6d3ce48e4f 100644 --- a/cmake.deps/cmake/GetBinaryDeps.cmake +++ b/cmake.deps/cmake/GetBinaryDeps.cmake @@ -24,7 +24,7 @@ function(GetBinaryDep) BUILD_COMMAND "" INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_BIN_DIR} COMMAND "${_gettool_INSTALL_COMMAND}" - ${EXTERNALPROJECT_OPTIONS}) + DOWNLOAD_NO_PROGRESS TRUE) endfunction() # Download executable and move it to DEPS_BIN_DIR @@ -49,5 +49,5 @@ function(GetExecutable) BUILD_COMMAND "" INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${DEPS_BIN_DIR} COMMAND ${CMAKE_COMMAND} -E copy <DOWNLOADED_FILE> ${DEPS_BIN_DIR} - ${EXTERNALPROJECT_OPTIONS}) + DOWNLOAD_NO_PROGRESS TRUE) endfunction() diff --git a/cmake.deps/cmake/GettextCMakeLists.txt b/cmake.deps/cmake/GettextCMakeLists.txt index 16fce4485f..c36f3aada5 100644 --- a/cmake.deps/cmake/GettextCMakeLists.txt +++ b/cmake.deps/cmake/GettextCMakeLists.txt @@ -1,9 +1,4 @@ cmake_minimum_required(VERSION 3.13) - -# Can be removed once minimum version is at least 3.15 -if(POLICY CMP0092) - cmake_policy(SET CMP0092 NEW) -endif() project(gettext C) add_compile_options(-w) diff --git a/cmake.deps/cmake/LibiconvCMakeLists.txt b/cmake.deps/cmake/LibiconvCMakeLists.txt index 220e3e07cb..e62b479b6b 100644 --- a/cmake.deps/cmake/LibiconvCMakeLists.txt +++ b/cmake.deps/cmake/LibiconvCMakeLists.txt @@ -1,8 +1,4 @@ cmake_minimum_required(VERSION 3.13) -# Can be removed once minimum version is at least 3.15 -if(POLICY CMP0092) - cmake_policy(SET CMP0092 NEW) -endif() project(libiconv C) add_compile_options(-w) diff --git a/cmake.deps/cmake/LibvtermCMakeLists.txt b/cmake.deps/cmake/LibvtermCMakeLists.txt index 41c79a915f..c197523786 100644 --- a/cmake.deps/cmake/LibvtermCMakeLists.txt +++ b/cmake.deps/cmake/LibvtermCMakeLists.txt @@ -1,67 +1,10 @@ cmake_minimum_required(VERSION 3.13) -# Can be removed once minimum version is at least 3.15 -if(POLICY CMP0092) - cmake_policy(SET CMP0092 NEW) -endif() project(libvterm C) add_compile_options(-w) include(GNUInstallDirs) -set(DECDRAWING [[ - static const struct StaticTableEncoding encoding_DECdrawing = { - { .decode = &decode_table }, - { - [0x60] = 0x25C6, - [0x61] = 0x2592, - [0x62] = 0x2409, - [0x63] = 0x240C, - [0x64] = 0x240D, - [0x65] = 0x240A, - [0x66] = 0x00B0, - [0x67] = 0x00B1, - [0x68] = 0x2424, - [0x69] = 0x240B, - [0x6a] = 0x2518, - [0x6b] = 0x2510, - [0x6c] = 0x250C, - [0x6d] = 0x2514, - [0x6e] = 0x253C, - [0x6f] = 0x23BA, - [0x70] = 0x23BB, - [0x71] = 0x2500, - [0x72] = 0x23BC, - [0x73] = 0x23BD, - [0x74] = 0x251C, - [0x75] = 0x2524, - [0x76] = 0x2534, - [0x77] = 0x252C, - [0x78] = 0x2502, - [0x79] = 0x2A7D, - [0x7a] = 0x2A7E, - [0x7b] = 0x03C0, - [0x7c] = 0x2260, - [0x7d] = 0x00A3, - [0x7e] = 0x00B7, - } - }; -]] -) - -set(UK [[ - static const struct StaticTableEncoding encoding_uk = { - { .decode = &decode_table }, - { - [0x23] = 0x00a3, - } - }; -]] -) - -file(WRITE src/encoding/DECdrawing.inc "${DECDRAWING}") -file(WRITE src/encoding/uk.inc "${UK}") - include_directories(${CMAKE_SOURCE_DIR}/include) include_directories(${CMAKE_BINARY_DIR}) diff --git a/cmake.deps/cmake/MarkdownParserCMakeLists.txt b/cmake.deps/cmake/MarkdownParserCMakeLists.txt index 8ee149e774..981bf4dfd7 100644 --- a/cmake.deps/cmake/MarkdownParserCMakeLists.txt +++ b/cmake.deps/cmake/MarkdownParserCMakeLists.txt @@ -1,8 +1,4 @@ cmake_minimum_required(VERSION 3.13) -# Can be removed once minimum version is at least 3.15 -if(POLICY CMP0092) - cmake_policy(SET CMP0092 NEW) -endif() project(${PARSERLANG} C) add_compile_options(-w) diff --git a/cmake.deps/cmake/TreesitterCMakeLists.txt b/cmake.deps/cmake/TreesitterCMakeLists.txt index f1e0d4e575..71174bfe5b 100644 --- a/cmake.deps/cmake/TreesitterCMakeLists.txt +++ b/cmake.deps/cmake/TreesitterCMakeLists.txt @@ -1,8 +1,4 @@ cmake_minimum_required(VERSION 3.13) -# Can be removed once minimum version is at least 3.15 -if(POLICY CMP0092) - cmake_policy(SET CMP0092 NEW) -endif() project(treesitter C) add_compile_options(-w) diff --git a/cmake.deps/cmake/TreesitterParserCMakeLists.txt b/cmake.deps/cmake/TreesitterParserCMakeLists.txt index c71a0a8f9b..0d4bbf0508 100644 --- a/cmake.deps/cmake/TreesitterParserCMakeLists.txt +++ b/cmake.deps/cmake/TreesitterParserCMakeLists.txt @@ -1,8 +1,4 @@ cmake_minimum_required(VERSION 3.13) -# Can be removed once minimum version is at least 3.15 -if(POLICY CMP0092) - cmake_policy(SET CMP0092 NEW) -endif() project(parser C) add_compile_options(-w) diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt index de212460c7..6b72ed5b52 100644 --- a/cmake.deps/deps.txt +++ b/cmake.deps/deps.txt @@ -1,11 +1,11 @@ LIBUV_URL https://github.com/libuv/libuv/archive/v1.48.0.tar.gz LIBUV_SHA256 8c253adb0f800926a6cbd1c6576abae0bc8eb86a4f891049b72f9e5b7dc58f33 -MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/c-6.0.0.tar.gz -MSGPACK_SHA256 af6f3cf25edb220aa2140b09bb5bdd73ddf00938194bd94ebe5c92090cccb466 +MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/c-6.0.1.tar.gz +MSGPACK_SHA256 58d5fe49d0ee2b374d60a61aabf8028b2c92004e6f11bff04e74b639fc8ad541 -LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/0d313b243194a0b8d2399d8b549ca5a0ff234db5.tar.gz -LUAJIT_SHA256 53731880dbc4adbbf82ba69a85b5dbe15266032b8b94a077c0835bc10ec75f12 +LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/75e92777988017fe47c5eb290998021bbf972d1f.tar.gz +LUAJIT_SHA256 0f69288190024d732c67645e40ed5b137d67aa950fedf0f44a9ad0f3dba6d5d2 LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333 @@ -13,8 +13,8 @@ LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333 UNIBILIUM_URL https://github.com/neovim/unibilium/archive/d72c3598e7ac5d1ebf86ee268b8b4ed95c0fa628.tar.gz UNIBILIUM_SHA256 9c4747c862ab5e3076dcf8fa8f0ea7a6b50f20ec5905618b9536655596797487 -LIBVTERM_URL https://github.com/neovim/libvterm/archive/v0.3.3.tar.gz -LIBVTERM_SHA256 0babe3ab42c354925dadede90d352f054aa9c4ae6842ea803a20c9741e172e56 +LIBVTERM_URL https://github.com/neovim/libvterm/archive/0a15c6e983b0db7ef8276e0792414a805d01bdaf.tar.gz +LIBVTERM_SHA256 c4683e7a2d71c04781fd0ab7719a94202800e97a9e091514c16983bb732b0fa7 LUV_URL https://github.com/luvit/luv/releases/download/1.48.0-2/luv-1.48.0-2.tar.gz LUV_SHA256 2c3a1ddfebb4f6550293a40ee789f7122e97647eede51511f57203de48c03b7a @@ -41,21 +41,21 @@ GETTEXT_SHA256 66415634c6e8c3fa8b71362879ec7575e27da43da562c798a8a2f223e6e47f5c LIBICONV_URL https://github.com/neovim/deps/raw/b9bf36eb31f27e8136d907da38fa23518927737e/opt/libiconv-1.17.tar.gz LIBICONV_SHA256 8f74213b56238c85a50a5329f77e06198771e70dd9a739779f4c02f65d971313 -TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.8.tar.gz -TREESITTER_C_SHA256 50f8aa6a84e2ad532cc3384e84e9f9f91fe3ec41c74f58e0c1e1013df5bf3a88 -TREESITTER_LUA_URL https://github.com/tree-sitter-grammars/tree-sitter-lua/archive/v0.0.19.tar.gz -TREESITTER_LUA_SHA256 974230f212d0049fce8e881b88b18eebbd05f1fd0edd16fe4ba5bdd2bcd78d08 -TREESITTER_VIM_URL https://github.com/neovim/tree-sitter-vim/archive/v0.3.0.tar.gz -TREESITTER_VIM_SHA256 403acec3efb7cdb18ff3d68640fc823502a4ffcdfbb71cec3f98aa786c21cbe2 -TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v2.2.0.tar.gz -TREESITTER_VIMDOC_SHA256 2cd898245d28bb606b05c022f04077031381d998faa3c6825c5ca01b7c89e2ae -TREESITTER_QUERY_URL https://github.com/tree-sitter-grammars/tree-sitter-query/archive/v0.1.0.tar.gz -TREESITTER_QUERY_SHA256 e2b806f80e8bf1c4f4e5a96248393fe6622fc1fc6189d6896d269658f67f914c -TREESITTER_PYTHON_URL https://github.com/tree-sitter/tree-sitter-python/archive/v0.20.4.tar.gz -TREESITTER_PYTHON_SHA256 1e38c991832f461c0da8ca222fbe5be3b82b868fe34025f0295206b5e5789d7a -TREESITTER_BASH_URL https://github.com/tree-sitter/tree-sitter-bash/archive/v0.20.5.tar.gz -TREESITTER_BASH_SHA256 7bba80ac64a18ec1b3f47e738e6a168f065c3cb4244234eff1b773816008f5a7 -TREESITTER_MARKDOWN_URL https://github.com/MDeiml/tree-sitter-markdown/archive/v0.1.7.tar.gz -TREESITTER_MARKDOWN_SHA256 7d0e7f7ed4516ed0816f9c304e2e7fa93b2c16f9280416c2fb64dc4efd9c5f83 -TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.21.0.tar.gz -TREESITTER_SHA256 6bb60e5b63c1dc18aba57a9e7b3ea775b4f9ceec44cc35dac4634d26db4eb69c +TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.21.0.tar.gz +TREESITTER_C_SHA256 6f0f5d1b71cf8ffd8a37fb638c6022fa1245bd630150b538547d52128ce0ea7e +TREESITTER_LUA_URL https://github.com/tree-sitter-grammars/tree-sitter-lua/archive/v0.1.0.tar.gz +TREESITTER_LUA_SHA256 230cfcbfa74ed1f7b8149e9a1f34c2efc4c589a71fe0f5dc8560622f8020d722 +TREESITTER_VIM_URL https://github.com/neovim/tree-sitter-vim/archive/v0.4.0.tar.gz +TREESITTER_VIM_SHA256 9f856f8b4a10ab43348550fa2d3cb2846ae3d8e60f45887200549c051c66f9d5 +TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v2.5.1.tar.gz +TREESITTER_VIMDOC_SHA256 063645096504b21603585507c41c6d8718ff3c11b2150c5bfc31e8f3ee9afea3 +TREESITTER_QUERY_URL https://github.com/tree-sitter-grammars/tree-sitter-query/archive/v0.3.0.tar.gz +TREESITTER_QUERY_SHA256 f878ff37abcb83250e31a6569e997546f3dbab74dcb26683cb2d613f7568cfc0 +TREESITTER_PYTHON_URL https://github.com/tree-sitter/tree-sitter-python/archive/v0.21.0.tar.gz +TREESITTER_PYTHON_SHA256 720304a603271fa89e4430a14d6a81a023d6d7d1171b1533e49c0ab44f1e1c13 +TREESITTER_BASH_URL https://github.com/tree-sitter/tree-sitter-bash/archive/v0.21.0.tar.gz +TREESITTER_BASH_SHA256 f0515efda839cfede851adb24ac154227fbc0dfb60c6c11595ecfa9087d43ceb +TREESITTER_MARKDOWN_URL https://github.com/MDeiml/tree-sitter-markdown/archive/v0.2.3.tar.gz +TREESITTER_MARKDOWN_SHA256 4909d6023643f1afc3ab219585d4035b7403f3a17849782ab803c5f73c8a31d5 +TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.22.6.tar.gz +TREESITTER_SHA256 e2b687f74358ab6404730b7fb1a1ced7ddb3780202d37595ecd7b20a8f41861f diff --git a/cmake.packaging/CMakeLists.txt b/cmake.packaging/CMakeLists.txt index fa815da7f9..645215ec92 100644 --- a/cmake.packaging/CMakeLists.txt +++ b/cmake.packaging/CMakeLists.txt @@ -44,7 +44,7 @@ if(WIN32) list(APPEND CPACK_WIX_EXTENSIONS WixUtilExtension) list(APPEND CPACK_WIX_PATCH_FILE ${CMAKE_CURRENT_LIST_DIR}/WixPatch.xml) elseif(APPLE) - set(CPACK_PACKAGE_FILE_NAME "nvim-macos") + set(CPACK_PACKAGE_FILE_NAME "nvim-macos-${CMAKE_SYSTEM_PROCESSOR}") set(CPACK_GENERATOR TGZ) set(CPACK_PACKAGE_ICON ${CMAKE_CURRENT_LIST_DIR}/neovim.icns) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") diff --git a/cmake/Deps.cmake b/cmake/Deps.cmake index 9966e42084..413e3a08a9 100644 --- a/cmake/Deps.cmake +++ b/cmake/Deps.cmake @@ -18,8 +18,16 @@ if(APPLE) list(APPEND DEPS_CMAKE_ARGS -D CMAKE_FIND_FRAMEWORK=${CMAKE_FIND_FRAMEWORK}) endif() -set(DEPS_CMAKE_CACHE_ARGS -DCMAKE_OSX_ARCHITECTURES:STRING=${CMAKE_OSX_ARCHITECTURES}) -set(EXTERNALPROJECT_OPTIONS DOWNLOAD_NO_PROGRESS TRUE) +# Can be removed once minimum version is at least 3.15 +if(POLICY CMP0092) + list(APPEND DEPS_CMAKE_ARGS -D CMAKE_POLICY_DEFAULT_CMP0092=NEW) +endif() + +find_program(CACHE_PRG NAMES ccache sccache) +if(CACHE_PRG) + set(CMAKE_C_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env CCACHE_SLOPPINESS=pch_defines,time_macros ${CACHE_PRG}) + list(APPEND DEPS_CMAKE_CACHE_ARGS -DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER}) +endif() # MAKE_PRG if(UNIX) @@ -49,8 +57,19 @@ set(DEPS_C_COMPILER "${CMAKE_C_COMPILER}") if(CMAKE_OSX_SYSROOT) set(DEPS_C_COMPILER "${DEPS_C_COMPILER} -isysroot${CMAKE_OSX_SYSROOT}") endif() -if(CMAKE_OSX_ARCHITECTURES) - foreach(ARCH IN LISTS CMAKE_OSX_ARCHITECTURES) - set(DEPS_C_COMPILER "${DEPS_C_COMPILER} -arch ${ARCH}") - endforeach() -endif() + +function(get_externalproject_options name DEPS_IGNORE_SHA) + string(TOUPPER ${name} name_allcaps) + set(url ${${name_allcaps}_URL}) + + set(EXTERNALPROJECT_OPTIONS + DOWNLOAD_NO_PROGRESS TRUE + EXTERNALPROJECT_OPTIONS URL ${${name_allcaps}_URL} + CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS}) + + if(NOT ${DEPS_IGNORE_SHA}) + list(APPEND EXTERNALPROJECT_OPTIONS URL_HASH SHA256=${${name_allcaps}_SHA256}) + endif() + + set(EXTERNALPROJECT_OPTIONS ${EXTERNALPROJECT_OPTIONS} PARENT_SCOPE) +endfunction() diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake index 6f93f73bc0..add83bc9cb 100644 --- a/cmake/RunTests.cmake +++ b/cmake/RunTests.cmake @@ -4,6 +4,7 @@ set(ENV{VIMRUNTIME} ${WORKING_DIR}/runtime) set(ENV{NVIM_RPLUGIN_MANIFEST} ${BUILD_DIR}/Xtest_rplugin_manifest) set(ENV{XDG_CONFIG_HOME} ${BUILD_DIR}/Xtest_xdg/config) set(ENV{XDG_DATA_HOME} ${BUILD_DIR}/Xtest_xdg/share) +set(ENV{XDG_STATE_HOME} ${BUILD_DIR}/Xtest_xdg/state) unset(ENV{XDG_DATA_DIRS}) unset(ENV{NVIM}) # Clear $NVIM in case tests are running from Nvim. #11009 @@ -46,7 +47,7 @@ if(DEFINED ENV{TEST_FILTER_OUT} AND NOT "$ENV{TEST_FILTER_OUT}" STREQUAL "") list(APPEND BUSTED_ARGS --filter-out $ENV{TEST_FILTER_OUT}) endif() -# TMPDIR: for helpers.tmpname() and Nvim tempname(). +# TMPDIR: for testutil.tmpname() and Nvim tempname(). set(ENV{TMPDIR} "${BUILD_DIR}/Xtest_tmpdir") execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory $ENV{TMPDIR}) @@ -57,7 +58,7 @@ if(NOT DEFINED ENV{TEST_TIMEOUT} OR "$ENV{TEST_TIMEOUT}" STREQUAL "") set(ENV{TEST_TIMEOUT} 1200) endif() -set(ENV{SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_NAME}) # used by test/helpers.lua. +set(ENV{SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_NAME}) # used by test/testutil.lua. if(NOT WIN32) # Tests assume POSIX "sh" and may fail if SHELL=fish. #24941 #6172 diff --git a/cmake/Util.cmake b/cmake/Util.cmake index 80f81fd0b4..f09de78668 100644 --- a/cmake/Util.cmake +++ b/cmake/Util.cmake @@ -148,6 +148,33 @@ function(add_glob_target) add_custom_target(${ARG_TARGET} DEPENDS ${touch_list}) endfunction() +# A wrapper function that combines add_custom_command and add_custom_target. It +# essentially models the "make" dependency where a target is only rebuilt if +# any dependencies have been changed. +# +# Important to note is that `DEPENDS` is a bit misleading; it should not only +# specify dependencies but also the files that are being generated/output +# files in order to work correctly. +function(add_target) + cmake_parse_arguments(ARG + "" + "" + "COMMAND;DEPENDS;CUSTOM_COMMAND_ARGS" + ${ARGN} + ) + set(target ${ARGV0}) + + set(touch_file ${TOUCHES_DIR}/${target}) + add_custom_command( + OUTPUT ${touch_file} + COMMAND ${CMAKE_COMMAND} -E touch ${touch_file} + COMMAND ${CMAKE_COMMAND} -E env "VIMRUNTIME=${NVIM_RUNTIME_DIR}" ${ARG_COMMAND} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + DEPENDS ${ARG_DEPENDS} + ${ARG_CUSTOM_COMMAND_ARGS}) + add_custom_target(${target} DEPENDS ${touch_file}) +endfunction() + # Set default build type to BUILD_TYPE. Also limit the list of allowable build # types to the ones defined in variable allowableBuildTypes. # diff --git a/contrib/flake.lock b/contrib/flake.lock deleted file mode 100644 index 0259dd4c8c..0000000000 --- a/contrib/flake.lock +++ /dev/null @@ -1,61 +0,0 @@ -{ - "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1703013332, - "narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/contrib/flake.nix b/contrib/flake.nix deleted file mode 100644 index 6355f3a68a..0000000000 --- a/contrib/flake.nix +++ /dev/null @@ -1,165 +0,0 @@ -{ - description = "Neovim flake"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - }; - - outputs = { self, nixpkgs, flake-utils }: - let - inherit (builtins) - elemAt - foldl' - mapAttrs - match - readFile - ; - inherit (nixpkgs.lib) - const - flip - pipe - remove - splitString - toLower - ; - in - { - overlay = final: prev: { - - neovim = (final.neovim-unwrapped.override { - treesitter-parsers = pipe ../cmake.deps/deps.txt [ - readFile - (splitString "\n") - (map (match "TREESITTER_([A-Z_]+)_(URL|SHA256)[[:space:]]+([^[:space:]]+)[[:space:]]*")) - (remove null) - (flip foldl' { } - (acc: matches: - let - lang = toLower (elemAt matches 0); - type = toLower (elemAt matches 1); - value = elemAt matches 2; - in - acc // { - ${lang} = acc.${lang} or { } // { - ${type} = value; - }; - })) - (mapAttrs (const final.fetchurl)) - (self: self // { - markdown = final.stdenv.mkDerivation { - inherit (self.markdown) name; - src = self.markdown; - installPhase = '' - mv tree-sitter-markdown $out - ''; - }; - }) - ]; - }).overrideAttrs (oa: rec { - version = self.shortRev or "dirty"; - src = ../.; - preConfigure = oa.preConfigure or "" + '' - sed -i cmake.config/versiondef.h.in -e 's/@NVIM_VERSION_PRERELEASE@/-dev-${version}/' - ''; - nativeBuildInputs = oa.nativeBuildInputs ++ [ - final.libiconv - ]; - }); - - # a development binary to help debug issues - neovim-debug = let - stdenv = if final.stdenv.isLinux then - final.llvmPackages_latest.stdenv - else - final.stdenv; - in (final.neovim.override { - lua = final.luajit; - inherit stdenv; - }).overrideAttrs (oa: { - - dontStrip = true; - NIX_CFLAGS_COMPILE = " -ggdb -Og"; - - cmakeBuildType = "Debug"; - - disallowedReferences = [ ]; - }); - - # for neovim developers, beware of the slow binary - neovim-developer = let inherit (final.luaPackages) luacheck; - in (final.neovim-debug.override { - doCheck = final.stdenv.isLinux; - }).overrideAttrs (oa: { - cmakeFlags = oa.cmakeFlags ++ [ - "-DLUACHECK_PRG=${luacheck}/bin/luacheck" - "-DENABLE_LTO=OFF" - ] ++ final.lib.optionals final.stdenv.isLinux [ - # https://github.com/google/sanitizers/wiki/AddressSanitizerFlags - # https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports - "-DENABLE_ASAN_UBSAN=ON" - ]; - }); - }; - } // flake-utils.lib.eachDefaultSystem (system: - let - pkgs = import nixpkgs { - overlays = [ self.overlay ]; - inherit system; - }; - - lua = pkgs.lua5_1; - - pythonEnv = pkgs.python3.withPackages (ps: [ - ps.msgpack - ]); - in { - packages = with pkgs; { - default = neovim; - inherit neovim neovim-debug neovim-developer; - }; - - checks = { - shlint = pkgs.runCommand "shlint" { - nativeBuildInputs = [ pkgs.shellcheck ]; - preferLocalBuild = true; - } "make -C ${./..} shlint > $out"; - }; - - # kept for backwards-compatibility - defaultPackage = pkgs.neovim; - - devShells = { - default = pkgs.neovim-developer.overrideAttrs (oa: { - - buildInputs = with pkgs; - oa.buildInputs ++ [ - lua.pkgs.luacheck - sumneko-lua-language-server - pythonEnv - include-what-you-use # for scripts/check-includes.py - jq # jq for scripts/vim-patch.sh -r - shellcheck # for `make shlint` - ]; - - nativeBuildInputs = with pkgs; - oa.nativeBuildInputs ++ [ - clang-tools # for clangd to find the correct headers - ]; - - shellHook = oa.shellHook + '' - export NVIM_PYTHON_LOG_LEVEL=DEBUG - export NVIM_LOG_FILE=/tmp/nvim.log - export ASAN_SYMBOLIZER_PATH=${pkgs.llvm_11}/bin/llvm-symbolizer - - # ASAN_OPTIONS=detect_leaks=1 - export ASAN_OPTIONS="log_path=./test.log:abort_on_error=1" - - # for treesitter functionaltests - mkdir -p runtime/parser - cp -f ${pkgs.vimPlugins.nvim-treesitter.builtGrammars.c}/parser runtime/parser/c.so - ''; - }); - }; - }); -} diff --git a/contrib/local.mk.example b/contrib/local.mk.example index 4e8a510f2d..58474a3750 100644 --- a/contrib/local.mk.example +++ b/contrib/local.mk.example @@ -42,13 +42,16 @@ # them. # # DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_BUSTED=OFF -# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LIBTERMKEY=OFF +# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_GETTEXT=OFF +# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LIBICONV=OFF # DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LIBUV=OFF # DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LIBVTERM=OFF # DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LUAJIT=OFF +# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LUV=OFF # DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_MSGPACK=OFF +# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_TS=OFF +# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_TS_PARSERS=OFF # DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_UNIBILIUM=OFF -# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_UTF8PROC=OFF # # Or disable all bundled dependencies at once. # diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim deleted file mode 100644 index a7dbedab08..0000000000 --- a/runtime/autoload/health.vim +++ /dev/null @@ -1,40 +0,0 @@ -function! s:deprecate(type) abort - let deprecate = v:lua.vim.deprecate('health#report_' . a:type, 'vim.health.' . a:type, '0.11') - redraw | echo 'Running healthchecks...' - if deprecate isnot v:null - call v:lua.vim.health.warn(deprecate) - endif -endfunction - -function! health#report_start(name) abort - call v:lua.vim.health.start(a:name) - call s:deprecate('start') -endfunction - -function! health#report_info(msg) abort - call v:lua.vim.health.info(a:msg) - call s:deprecate('info') -endfunction - -function! health#report_ok(msg) abort - call v:lua.vim.health.ok(a:msg) - call s:deprecate('ok') -endfunction - -function! health#report_warn(msg, ...) abort - if a:0 > 0 - call v:lua.vim.health.warn(a:msg, a:1) - else - call v:lua.vim.health.warn(a:msg) - endif - call s:deprecate('warn') -endfunction - -function! health#report_error(msg, ...) abort - if a:0 > 0 - call v:lua.vim.health.error(a:msg, a:1) - else - call v:lua.vim.health.error(a:msg) - endif - call s:deprecate('error') -endfunction diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index fa08bb3848..ae602c5be6 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -8,6 +8,10 @@ " 2023 Nov 22 by Vim Project: fix handling of very long filename on longlist style (v173a) " 2024 Feb 19 by Vim Project: (announce adoption) " 2024 Feb 29 by Vim Project: handle symlinks in tree mode correctly +" 2024 Apr 03 by Vim Project: detect filetypes for remote edited files +" 2024 May 08 by Vim Project: cleanup legacy Win9X checks +" 2024 May 09 by Vim Project: remove hard-coded private.ppk +" 2024 May 10 by Vim Project: recursively delete directories by default " Former Maintainer: Charles E Campbell " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 @@ -280,20 +284,15 @@ if !exists("g:netrw_scp_cmd") if executable("scp") call s:NetrwInit("g:netrw_scp_cmd" , "scp -q") elseif executable("pscp") - if (has("win32") || has("win95") || has("win64") || has("win16")) && filereadable('c:\private.ppk') - call s:NetrwInit("g:netrw_scp_cmd", 'pscp -i c:\private.ppk') - else - call s:NetrwInit("g:netrw_scp_cmd", 'pscp -q') - endif + call s:NetrwInit("g:netrw_scp_cmd", 'pscp -q') else call s:NetrwInit("g:netrw_scp_cmd" , "scp -q") endif endif - call s:NetrwInit("g:netrw_sftp_cmd" , "sftp") call s:NetrwInit("g:netrw_ssh_cmd" , "ssh") -if (has("win32") || has("win95") || has("win64") || has("win16")) +if has("win32") \ && exists("g:netrw_use_nt_rcp") \ && g:netrw_use_nt_rcp \ && executable( $SystemRoot .'/system32/rcp.exe') @@ -308,12 +307,8 @@ endif " Default values for netrw's global variables {{{2 " Cygwin Detection ------- {{{3 if !exists("g:netrw_cygwin") - if has("win32") || has("win95") || has("win64") || has("win16") - if has("win32unix") && &shell =~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$' - let g:netrw_cygwin= 1 - else - let g:netrw_cygwin= 0 - endif + if has("win32unix") && &shell =~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$' + let g:netrw_cygwin= 1 else let g:netrw_cygwin= 0 endif @@ -369,10 +364,6 @@ endif call s:NetrwInit("g:netrw_keepdir",1) if !exists("g:netrw_list_cmd") if g:netrw_scp_cmd =~ '^pscp' && executable("pscp") - if (has("win32") || has("win95") || has("win64") || has("win16")) && filereadable("c:\\private.ppk") - " provide a pscp-based listing command - let g:netrw_scp_cmd ="pscp -i C:\\private.ppk" - endif if exists("g:netrw_list_cmd_options") let g:netrw_list_cmd= g:netrw_scp_cmd." -ls USEPORT HOSTNAME: ".g:netrw_list_cmd_options else @@ -400,7 +391,7 @@ if !exists("g:netrw_localcmdshell") let g:netrw_localcmdshell= "" endif if !exists("g:netrw_localcopycmd") - if has("win32") || has("win95") || has("win64") || has("win16") + if has("win32") if g:netrw_cygwin let g:netrw_localcopycmd= "cp" else @@ -414,7 +405,7 @@ if !exists("g:netrw_localcopycmd") endif endif if !exists("g:netrw_localcopydircmd") - if has("win32") || has("win95") || has("win64") || has("win16") + if has("win32") if g:netrw_cygwin let g:netrw_localcopydircmd = "cp" let g:netrw_localcopydircmdopt= " -R" @@ -436,7 +427,7 @@ if exists("g:netrw_local_mkdir") let g:netrw_localmkdir= g:netrw_local_mkdir call netrw#ErrorMsg(s:NOTE,"g:netrw_local_mkdir is deprecated in favor of g:netrw_localmkdir",87) endif -if has("win32") || has("win95") || has("win64") || has("win16") +if has("win32") if g:netrw_cygwin call s:NetrwInit("g:netrw_localmkdir","mkdir") else @@ -452,7 +443,7 @@ if exists("g:netrw_local_movecmd") call netrw#ErrorMsg(s:NOTE,"g:netrw_local_movecmd is deprecated in favor of g:netrw_localmovecmd",88) endif if !exists("g:netrw_localmovecmd") - if has("win32") || has("win95") || has("win64") || has("win16") + if has("win32") if g:netrw_cygwin let g:netrw_localmovecmd= "mv" else @@ -485,7 +476,7 @@ call s:NetrwInit("g:netrw_mousemaps" , (exists("+mouse") && &mouse =~# '[anh call s:NetrwInit("g:netrw_retmap" , 0) if has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin) call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME") -elseif has("win32") || has("win95") || has("win64") || has("win16") +elseif has("win32") call s:NetrwInit("g:netrw_chgperm" , "cacls FILENAME /e /p PERM") else call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME") @@ -544,14 +535,13 @@ if !exists("g:netrw_xstrlen") endif endif call s:NetrwInit("g:NetrwTopLvlMenu","Netrw.") -call s:NetrwInit("g:netrw_win95ftp",1) call s:NetrwInit("g:netrw_winsize",50) call s:NetrwInit("g:netrw_wiw",1) if g:netrw_winsize > 100|let g:netrw_winsize= 100|endif " --------------------------------------------------------------------- " Default values for netrw's script variables: {{{2 call s:NetrwInit("g:netrw_fname_escape",' ?&;%') -if has("win32") || has("win95") || has("win64") || has("win16") +if has("win32") call s:NetrwInit("g:netrw_glob_escape",'*?`{[]$') else call s:NetrwInit("g:netrw_glob_escape",'*[]?`{~$\') @@ -683,7 +673,7 @@ fun! netrw#Explore(indx,dosplit,style,...) " record current directory let curdir = simplify(b:netrw_curdir) let curfiledir = substitute(expand("%:p"),'^\(.*[/\\]\)[^/\\]*$','\1','e') - if !exists("g:netrw_cygwin") && (has("win32") || has("win95") || has("win64") || has("win16")) + if !exists("g:netrw_cygwin") && has("win32") let curdir= substitute(curdir,'\','/','g') endif " call Decho("curdir<".curdir."> curfiledir<".curfiledir.">",'~'.expand("<slnum>")) @@ -836,7 +826,7 @@ fun! netrw#Explore(indx,dosplit,style,...) " handle .../**/.../filepat " call Decho("case starpat=4: Explore .../**/.../filepat",'~'.expand("<slnum>")) let prefixdir= substitute(dirname,'^\(.\{-}\)\*\*.*$','\1','') - if prefixdir =~ '^/' || (prefixdir =~ '^\a:/' && (has("win32") || has("win95") || has("win64") || has("win16"))) + if prefixdir =~ '^/' || (prefixdir =~ '^\a:/' && has("win32")) let b:netrw_curdir = prefixdir else let b:netrw_curdir= getcwd().'/'.prefixdir @@ -873,7 +863,7 @@ fun! netrw#Explore(indx,dosplit,style,...) else if dirname == "" let dirname= getcwd() - elseif (has("win32") || has("win95") || has("win64") || has("win16")) && !g:netrw_cygwin + elseif has("win32") && !g:netrw_cygwin " Windows : check for a drive specifier, or else for a remote share name ('\\Foo' or '//Foo', " depending on whether backslashes have been converted to forward slashes by earlier code). if dirname !~ '^[a-zA-Z]:' && dirname !~ '^\\\\\w\+' && dirname !~ '^//\w\+' @@ -1382,7 +1372,7 @@ fun! netrw#Obtain(islocal,fname,...) " call Decho("obtain a file from local ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>")) if exists("b:netrw_curdir") && getcwd() != b:netrw_curdir let topath= s:ComposePath(tgtdir,"") - if (has("win32") || has("win95") || has("win64") || has("win16")) + if has("win32") " transfer files one at time " call Decho("transfer files one at a time",'~'.expand("<slnum>")) for fname in fnamelist @@ -1761,7 +1751,9 @@ fun! s:NetrwOptionsRestore(vt) " call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) if !exists("{a:vt}netrw_optionsave") " call Decho("case ".a:vt."netrw_optionsave : doesn't exist",'~'.expand("<slnum>")) - if filereadable(expand("%")) + + " filereadable() returns zero for remote files (e.g. scp://localhost//etc/fstab) + if filereadable(expand("%")) || expand("%") =~# '^\w\+://\f\+/' " call Decho("..doing filetype detect anyway") filetype detect " call Decho("..settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) @@ -2251,7 +2243,7 @@ fun! netrw#NetRead(mode,...) endif " 'C' in 'C:\path\to\file' is handled as hostname on windows. " This is workaround to avoid mis-handle windows local-path: - if g:netrw_scp_cmd =~ '^scp' && (has("win32") || has("win95") || has("win64") || has("win16")) + if g:netrw_scp_cmd =~ '^scp' && has("win32") let tmpfile_get = substitute(tr(tmpfile, '\', '/'), '^\(\a\):[/\\]\(.*\)$', '/\1/\2', '') else let tmpfile_get = tmpfile @@ -2680,7 +2672,7 @@ fun! netrw#NetWrite(...) range let url= g:netrw_choice call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_put_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(url,1) ) elseif !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,"can't write to http using <".g:netrw_http_put_cmd.">".",16) + call netrw#ErrorMsg(s:ERROR,"can't write to http using <".g:netrw_http_put_cmd.">",16) endif "......................................... @@ -3171,7 +3163,7 @@ fun! s:NetrwMethod(choice) if exists("s:netrw_hup[host]") call NetUserPass("ftp:".host) - elseif (has("win32") || has("win95") || has("win64") || has("win16")) && s:netrw_ftp_cmd =~# '-[sS]:' + elseif has("win32") && s:netrw_ftp_cmd =~# '-[sS]:' " call Decho("has -s: : s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">",'~'.expand("<slnum>")) " call Decho(" g:netrw_ftp_cmd<".g:netrw_ftp_cmd.">",'~'.expand("<slnum>")) if g:netrw_ftp_cmd =~# '-[sS]:\S*MACHINE\>' @@ -3286,38 +3278,6 @@ fun! s:NetrwMethod(choice) " call Dret("s:NetrwMethod : b:netrw_method=".b:netrw_method." g:netrw_port=".g:netrw_port) endfun -" ------------------------------------------------------------------------ -" NetReadFixup: this sort of function is typically written by the user {{{2 -" to handle extra junk that their system's ftp dumps -" into the transfer. This function is provided as an -" example and as a fix for a Windows 95 problem: in my -" experience, win95's ftp always dumped four blank lines -" at the end of the transfer. -if has("win95") && exists("g:netrw_win95ftp") && g:netrw_win95ftp - fun! NetReadFixup(method, line1, line2) -" call Dfunc("NetReadFixup(method<".a:method."> line1=".a:line1." line2=".a:line2.")") - - " sanity checks -- attempt to convert inputs to integers - let method = a:method + 0 - let line1 = a:line1 + 0 - let line2 = a:line2 + 0 - if type(method) != 0 || type(line1) != 0 || type(line2) != 0 || method < 0 || line1 <= 0 || line2 <= 0 -" call Dret("NetReadFixup") - return - endif - - if method == 3 " ftp (no <.netrc>) - let fourblanklines= line2 - 3 - if fourblanklines >= line1 - exe "sil NetrwKeepj ".fourblanklines.",".line2."g/^\s*$/d" - call histdel("/",-1) - endif - endif - -" call Dret("NetReadFixup") - endfun -endif - " --------------------------------------------------------------------- " NetUserPass: set username and password for subsequent ftp transfer {{{2 " Usage: :call NetUserPass() -- will prompt for userid and password @@ -3940,7 +3900,7 @@ fun! s:NetrwBrowse(islocal,dirname) if b:netrw_curdir =~ '[/\\]$' let b:netrw_curdir= substitute(b:netrw_curdir,'[/\\]$','','e') endif - if b:netrw_curdir =~ '\a:$' && (has("win32") || has("win95") || has("win64") || has("win16")) + if b:netrw_curdir =~ '\a:$' && has("win32") let b:netrw_curdir= b:netrw_curdir."/" endif if b:netrw_curdir == '' @@ -4094,7 +4054,7 @@ fun! s:NetrwFile(fname) let b:netrw_curdir= getcwd() endif - if !exists("g:netrw_cygwin") && (has("win32") || has("win95") || has("win64") || has("win16")) + if !exists("g:netrw_cygwin") && has("win32") if fname =~ '^\' || fname =~ '^\a:\' " windows, but full path given let ret= fname @@ -4814,7 +4774,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) call s:SavePosn(s:netrw_posn) NetrwKeepj call s:NetrwOptionsSave("s:") NetrwKeepj call s:NetrwOptionsSafe(a:islocal) - if (has("win32") || has("win95") || has("win64") || has("win16")) + if has("win32") let dirname = substitute(b:netrw_curdir,'\\','/','ge') else let dirname = b:netrw_curdir @@ -5061,7 +5021,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) endif " call Decho("go-up: amiga: dirname<".dirname."> (go up one dir)",'~'.expand("<slnum>")) - elseif !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) + elseif !g:netrw_cygwin && has("win32") " windows if a:islocal let dirname= substitute(dirname,'^\(.*\)/\([^/]\+\)/$','\1','') @@ -5346,7 +5306,7 @@ fun! netrw#BrowseX(fname,remote) " set up the filename " (lower case the extension, make a local copy of a remote file) let exten= substitute(a:fname,'.*\.\(.\{-}\)','\1','e') - if has("win32") || has("win95") || has("win64") || has("win16") + if has("win32") let exten= substitute(exten,'^.*$','\L&\E','') endif if exten =~ "[\\/]" @@ -5393,12 +5353,12 @@ fun! netrw#BrowseX(fname,remote) " by default, g:netrw_suppress_gx_mesg is true if g:netrw_suppress_gx_mesg if &srr =~ "%s" - if (has("win32") || has("win95") || has("win64") || has("win16")) + if has("win32") let redir= substitute(&srr,"%s","nul","") else let redir= substitute(&srr,"%s","/dev/null","") endif - elseif (has("win32") || has("win95") || has("win64") || has("win16")) + elseif has("win32") let redir= &srr . "nul" else let redir= &srr . "/dev/null" @@ -5441,7 +5401,7 @@ fun! netrw#BrowseX(fname,remote) call s:NetrwExe("sil !".viewer." ".viewopt.s:ShellEscape(fname,1).redir) let ret= v:shell_error - elseif has("win32") || has("win64") + elseif has("win32") " call Decho("(netrw#BrowseX) win".(has("win32")? "32" : "64"),'~'.expand("<slnum>")) if executable("start") call s:NetrwExe('sil! !start rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(fname,1)) @@ -7164,7 +7124,7 @@ fun! s:NetrwMarkFileCopy(islocal,...) let args= join(map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),"s:ShellEscape(b:netrw_curdir.\"/\".v:val)")) let tgt = s:ShellEscape(s:netrwmftgt) endif - if !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) + if !g:netrw_cygwin && has("win32") let args= substitute(args,'/','\\','g') let tgt = substitute(tgt, '/','\\','g') endif @@ -7178,7 +7138,7 @@ fun! s:NetrwMarkFileCopy(islocal,...) " call Decho("args<".args."> is a directory",'~'.expand("<slnum>")) let copycmd= g:netrw_localcopydircmd " call Decho("using copydircmd<".copycmd.">",'~'.expand("<slnum>")) - if !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) + if !g:netrw_cygwin && has("win32") " window's xcopy doesn't copy a directory to a target properly. Instead, it copies a directory's " contents to a target. One must append the source directory name to the target to get xcopy to " do the right thing. @@ -7796,7 +7756,7 @@ fun! s:NetrwMarkFileMove(islocal) endif let tgt = s:ShellEscape(s:netrwmftgt) " call Decho("tgt<".tgt.">",'~'.expand("<slnum>")) - if !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) + if !g:netrw_cygwin && has("win32") let tgt= substitute(tgt, '/','\\','g') " call Decho("windows exception: tgt<".tgt.">",'~'.expand("<slnum>")) if g:netrw_localmovecmd =~ '\s' @@ -7817,7 +7777,7 @@ fun! s:NetrwMarkFileMove(islocal) " Jul 19, 2022: fixing file move when g:netrw_keepdir is 1 let fname= b:netrw_curdir."/".fname endif - if !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) + if !g:netrw_cygwin && has("win32") let fname= substitute(fname,'/','\\','g') endif " call Decho("system(".movecmd." ".s:ShellEscape(fname)." ".tgt.")",'~'.expand("<slnum>")) @@ -10236,7 +10196,7 @@ fun! s:NetrwRemoteFtpCmd(path,listcmd) endif " cleanup for Windows " {{{3 - if has("win32") || has("win95") || has("win64") || has("win16") + if has("win32") sil! NetrwKeepj %s/\r$//e NetrwKeepj call histdel("/",-1) endif @@ -10744,7 +10704,7 @@ fun! netrw#FileUrlEdit(fname) let fname= substitute(fname,'^file://localhost/','file:///','') " call Decho("fname<".fname.">",'~'.expand("<slnum>")) endif - if (has("win32") || has("win95") || has("win64") || has("win16")) + if has("win32") if fname =~ '^file:///\=\a[|:]/' " call Decho('converting file:///\a|/ -to- file://\a:/','~'.expand("<slnum>")) let fname = substitute(fname,'^file:///\=\(\a\)[|:]/','file://\1:/','') @@ -10754,7 +10714,7 @@ fun! netrw#FileUrlEdit(fname) let fname2396 = netrw#RFC2396(fname) let fname2396e= fnameescape(fname2396) let plainfname= substitute(fname2396,'file://\(.*\)','\1',"") - if (has("win32") || has("win95") || has("win64") || has("win16")) + if has("win32") " call Decho("windows exception for plainfname",'~'.expand("<slnum>")) if plainfname =~ '^/\+\a:' " call Decho('removing leading "/"s','~'.expand("<slnum>")) @@ -10966,7 +10926,7 @@ fun! s:LocalFastBrowser() let s:netrw_events= 1 augroup AuNetrwEvent au! - if (has("win32") || has("win95") || has("win64") || has("win16")) + if has("win32") " call Decho("installing autocmd: ShellCmdPost",'~'.expand("<slnum>")) au ShellCmdPost * call s:LocalBrowseRefresh() else @@ -11008,7 +10968,7 @@ fun! s:LocalListing() let filelist = filelist + s:NetrwGlob(dirname,".*",0) " call Decho("filelist=".string(filelist),'~'.expand("<slnum>")) - if g:netrw_cygwin == 0 && (has("win32") || has("win95") || has("win64") || has("win16")) + if g:netrw_cygwin == 0 && has("win32") " call Decho("filelist=".string(filelist),'~'.expand("<slnum>")) elseif index(filelist,'..') == -1 && b:netrw_curdir !~ '/' " include ../ in the glob() entry if its missing @@ -11054,7 +11014,7 @@ fun! s:LocalListing() let pfile= filename."/" elseif exists("b:netrw_curdir") && b:netrw_curdir !~ '^.*://' && !isdirectory(s:NetrwFile(filename)) - if (has("win32") || has("win95") || has("win64") || has("win16")) + if has("win32") if filename =~ '\.[eE][xX][eE]$' || filename =~ '\.[cC][oO][mM]$' || filename =~ '\.[bB][aA][tT]$' " indicate an executable " call Decho("indicate <".filename."> is executable with trailing *",'~'.expand("<slnum>")) @@ -11388,7 +11348,7 @@ fun! s:NetrwLocalRmFile(path,fname,all) if !all echohl Statement call inputsave() - let ok= input("Confirm deletion of directory<".rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") + let ok= input("Confirm *recursive* deletion of directory<".rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") call inputrestore() let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') if ok == "" @@ -11401,7 +11361,7 @@ fun! s:NetrwLocalRmFile(path,fname,all) let rmfile= substitute(rmfile,'[\/]$','','e') if all || ok =~# 'y\%[es]' || ok == "" - if delete(rmfile,"d") + if delete(rmfile,"rf") call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".rmfile.">!",103) endif endif @@ -11531,7 +11491,7 @@ endfun " netrw#WinPath: tries to insure that the path is windows-acceptable, whether cygwin is used or not {{{2 fun! netrw#WinPath(path) " call Dfunc("netrw#WinPath(path<".a:path.">)") - if (!g:netrw_cygwin || &shell !~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$') && (has("win32") || has("win95") || has("win64") || has("win16")) + if (!g:netrw_cygwin || &shell !~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$') && has("win32") " remove cygdrive prefix, if present let path = substitute(a:path,g:netrw_cygdrive.'/\(.\)','\1:','') " remove trailing slash (Win95) @@ -11591,11 +11551,11 @@ fun! s:ComposePath(base,subdir) endif " COMBAK: test on windows with changing to root directory: :e C:/ - elseif a:subdir =~ '^\a:[/\\]\([^/\\]\|$\)' && (has("win32") || has("win95") || has("win64") || has("win16")) + elseif a:subdir =~ '^\a:[/\\]\([^/\\]\|$\)' && has("win32") " call Decho("windows",'~'.expand("<slnum>")) let ret= a:subdir - elseif a:base =~ '^\a:[/\\]\([^/\\]\|$\)' && (has("win32") || has("win95") || has("win64") || has("win16")) + elseif a:base =~ '^\a:[/\\]\([^/\\]\|$\)' && has("win32") " call Decho("windows",'~'.expand("<slnum>")) if a:base =~ '[/\\]$' let ret= a:base.a:subdir @@ -11707,7 +11667,7 @@ fun! s:GetTempfile(fname) " o/s dependencies if g:netrw_cygwin != 0 let tmpfile = substitute(tmpfile,'^\(\a\):',g:netrw_cygdrive.'/\1','e') - elseif has("win32") || has("win95") || has("win64") || has("win16") + elseif has("win32") if !exists("+shellslash") || !&ssl let tmpfile = substitute(tmpfile,'/','\','g') endif @@ -11923,7 +11883,7 @@ fun! s:NetrwDelete(path) " call Dfunc("s:NetrwDelete(path<".a:path.">)") let path = netrw#WinPath(a:path) - if !g:netrw_cygwin && (has("win32") || has("win95") || has("win64") || has("win16")) + if !g:netrw_cygwin && has("win32") if exists("+shellslash") let sskeep= &shellslash setl noshellslash @@ -12114,7 +12074,7 @@ fun! s:NetrwLcd(newdir) " 'root' (ie. '\'). The share name may start with either backslashes ('\\Foo') or " forward slashes ('//Foo'), depending on whether backslashes have been converted to " forward slashes by earlier code; so check for both. - if (has("win32") || has("win95") || has("win64") || has("win16")) && !g:netrw_cygwin + if has("win32") && !g:netrw_cygwin if a:newdir =~ '^\\\\\w\+' || a:newdir =~ '^//\w\+' let dirname = '\' exe 'NetrwKeepj sil lcd '.fnameescape(dirname) @@ -12587,7 +12547,7 @@ endfun " --------------------------------------------------------------------- " s:ShellEscape: shellescape(), or special windows handling {{{2 fun! s:ShellEscape(s, ...) - if (has('win32') || has('win64')) && $SHELL == '' && &shellslash + if has('win32') && $SHELL == '' && &shellslash return printf('"%s"', substitute(a:s, '"', '""', 'g')) endif let f = a:0 > 0 ? a:1 : 0 diff --git a/runtime/autoload/netrwSettings.vim b/runtime/autoload/netrwSettings.vim index 5525c0d035..3452602272 100644 --- a/runtime/autoload/netrwSettings.vim +++ b/runtime/autoload/netrwSettings.vim @@ -3,6 +3,8 @@ " Maintainer: This runtime file is looking for a new maintainer. " Former Maintainer: Charles E Campbell " Version: 18 +" Last Change: +" 2024 May 08 by Vim Project: cleanup legacy Win9X checks " Copyright: Copyright (C) 1999-2007 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -91,7 +93,6 @@ fun! netrwSettings#NetrwSettings() put = 'let g:netrw_sshport = '.g:netrw_sshport put = 'let g:netrw_silent = '.g:netrw_silent put = 'let g:netrw_use_nt_rcp = '.g:netrw_use_nt_rcp - put = 'let g:netrw_win95ftp = '.g:netrw_win95ftp let s:netrw_xfer_stop= line(".") put ='' put ='+ Netrw Messages' diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 05f6bdb871..82e0953196 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -67,7 +67,8 @@ function! provider#clipboard#Error() abort endfunction function! provider#clipboard#Executable() abort - if exists('g:clipboard') + " Setting g:clipboard to v:false explicitly opts-in to using the "builtin" clipboard providers below + if exists('g:clipboard') && g:clipboard isnot# v:false if type({}) isnot# type(g:clipboard) \ || type({}) isnot# type(get(g:clipboard, 'copy', v:null)) \ || type({}) isnot# type(get(g:clipboard, 'paste', v:null)) diff --git a/runtime/autoload/zig/fmt.vim b/runtime/autoload/zig/fmt.vim deleted file mode 100644 index b78c1994dd..0000000000 --- a/runtime/autoload/zig/fmt.vim +++ /dev/null @@ -1,100 +0,0 @@ -" Adapted from fatih/vim-go: autoload/go/fmt.vim -" -" Copyright 2011 The Go Authors. All rights reserved. -" Use of this source code is governed by a BSD-style -" license that can be found in the LICENSE file. -" -" Upstream: https://github.com/ziglang/zig.vim - -function! zig#fmt#Format() abort - " Save cursor position and many other things. - let view = winsaveview() - - if !executable('zig') - echohl Error | echomsg "no zig binary found in PATH" | echohl None - return - endif - - let cmdline = 'zig fmt --stdin --ast-check' - let current_buf = bufnr('') - - " The formatted code is output on stdout, the errors go on stderr. - if exists('*systemlist') - silent let out = systemlist(cmdline, current_buf) - else - silent let out = split(system(cmdline, current_buf)) - endif - if len(out) == 1 - if out[0] == "error: unrecognized parameter: '--ast-check'" - let cmdline = 'zig fmt --stdin' - if exists('*systemlist') - silent let out = systemlist(cmdline, current_buf) - else - silent let out = split(system(cmdline, current_buf)) - endif - endif - endif - let err = v:shell_error - - - if err == 0 - " remove undo point caused via BufWritePre. - try | silent undojoin | catch | endtry - - " Replace the file content with the formatted version. - if exists('*deletebufline') - call deletebufline(current_buf, len(out), line('$')) - else - silent execute ':' . len(out) . ',' . line('$') . ' delete _' - endif - call setline(1, out) - - " No errors detected, close the loclist. - call setloclist(0, [], 'r') - lclose - elseif get(g:, 'zig_fmt_parse_errors', 1) - let errors = s:parse_errors(expand('%'), out) - - call setloclist(0, [], 'r', { - \ 'title': 'Errors', - \ 'items': errors, - \ }) - - let max_win_height = get(g:, 'zig_fmt_max_window_height', 5) - " Prevent the loclist from becoming too long. - let win_height = min([max_win_height, len(errors)]) - " Open the loclist, but only if there's at least one error to show. - execute 'silent! lwindow ' . win_height - endif - - call winrestview(view) - - if err != 0 - echohl Error | echomsg "zig fmt returned error" | echohl None - return - endif - - " Run the syntax highlighter on the updated content and recompute the folds if - " needed. - syntax sync fromstart -endfunction - -" parse_errors parses the given errors and returns a list of parsed errors -function! s:parse_errors(filename, lines) abort - " list of errors to be put into location list - let errors = [] - for line in a:lines - let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)') - if !empty(tokens) - call add(errors,{ - \"filename": a:filename, - \"lnum": tokens[2], - \"col": tokens[3], - \"text": tokens[4], - \ }) - endif - endfor - - return errors -endfunction -" vim: sw=2 ts=2 et diff --git a/runtime/colors/vim.lua b/runtime/colors/vim.lua index 5a29ba0ec6..7231418f5f 100644 --- a/runtime/colors/vim.lua +++ b/runtime/colors/vim.lua @@ -158,18 +158,22 @@ hi('@boolean', { link = 'Boolean' }) hi('@number.float', { link = 'Float' }) -- Functions -hi('@function', { link = 'Function' }) -hi('@function.builtin', { link = 'Special' }) -hi('@function.macro', { link = 'Macro' }) -hi('@variable.parameter', { link = 'Identifier' }) -hi('@function.method', { link = 'Function' }) -hi('@variable.member', { link = 'Identifier' }) -hi('@property', { link = 'Identifier' }) -hi('@constructor', { link = 'Special' }) +hi('@function', { link = 'Function' }) +hi('@function.builtin', { link = 'Special' }) +hi('@function.macro', { link = 'Macro' }) +hi('@function.method', { link = 'Function' }) +hi('@variable.parameter', { link = 'Identifier' }) +hi('@variable.parameter.builtin', { link = 'Special' }) +hi('@variable.member', { link = 'Identifier' }) +hi('@property', { link = 'Identifier' }) +hi('@attribute', { link = 'Macro' }) +hi('@attribute.builtin', { link = 'Special' }) +hi('@constructor', { link = 'Special' }) -- Keywords hi('@keyword.conditional', { link = 'Conditional' }) hi('@keyword.repeat', { link = 'Repeat' }) +hi('@keyword.type', { link = 'Structure' }) hi('@label', { link = 'Label' }) hi('@operator', { link = 'Operator' }) hi('@keyword', { link = 'Keyword' }) @@ -178,12 +182,12 @@ hi('@keyword.exception', { link = 'Exception' }) hi('@variable', { link = 'Identifier' }) hi('@type', { link = 'Type' }) hi('@type.definition', { link = 'Typedef' }) -hi('@keyword.storage', { link = 'StorageClass' }) hi('@module', { link = 'Identifier' }) hi('@keyword.import', { link = 'Include' }) hi('@keyword.directive', { link = 'PreProc' }) hi('@keyword.debug', { link = 'Debug' }) hi('@tag', { link = 'Tag' }) +hi('@tag.builtin', { link = 'Special' }) -- LSP semantic tokens hi('@lsp.type.class', { link = 'Structure' }) diff --git a/runtime/compiler/ant.vim b/runtime/compiler/ant.vim index 0605c69fab..6219b01c36 100644 --- a/runtime/compiler/ant.vim +++ b/runtime/compiler/ant.vim @@ -2,16 +2,13 @@ " Compiler: ant " Maintainer: Johannes Zellner <johannes@zellner.org> " Last Change: Mi, 13 Apr 2005 22:50:07 CEST +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "ant" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/bcc.vim b/runtime/compiler/bcc.vim index 5bd284a581..2d9d4fbb36 100644 --- a/runtime/compiler/bcc.vim +++ b/runtime/compiler/bcc.vim @@ -2,16 +2,13 @@ " Compiler: bcc - Borland C " Maintainer: Emile van Raaij (eraaij@xs4all.nl) " Last Change: 2004 Mar 27 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "bcc" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - " A workable errorformat for Borland C CompilerSet errorformat=%*[^0-9]%n\ %f\ %l:\ %m diff --git a/runtime/compiler/bdf.vim b/runtime/compiler/bdf.vim index b062e847aa..2aaa93bc27 100644 --- a/runtime/compiler/bdf.vim +++ b/runtime/compiler/bdf.vim @@ -1,7 +1,9 @@ " Vim compiler file " Compiler: BDF to PCF Conversion " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2006-04-19 +" Contributors: Enno Nagel +" Last Change: 2024 Mar 29 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -11,9 +13,8 @@ let current_compiler = "bdf" let s:cpo_save = &cpo set cpo-=C -setlocal makeprg=bdftopcf\ $* - -setlocal errorformat=%ABDF\ %trror\ on\ line\ %l:\ %m, +CompilerSet makeprg=bdftopcf\ $* +CompilerSet errorformat=%ABDF\ %trror\ on\ line\ %l:\ %m, \%-Z%p^, \%Cbdftopcf:\ bdf\ input\\,\ %f\\,\ corrupt, \%-G%.%# diff --git a/runtime/compiler/cargo.vim b/runtime/compiler/cargo.vim index aa9b01e93c..bbea45dee7 100644 --- a/runtime/compiler/cargo.vim +++ b/runtime/compiler/cargo.vim @@ -2,6 +2,7 @@ " Compiler: Cargo Compiler " Maintainer: Damien Radtke <damienradtke@gmail.com> " Latest Revision: 2023-09-11 +" 2024 Apr 05 by The Vim Project (removed :CompilerSet definition) " For bugs, patches and license go to https://github.com/rust-lang/rust.vim if exists('current_compiler') @@ -15,10 +16,6 @@ let s:save_cpo = &cpo set cpo&vim " vint: +ProhibitAbbreviationOption -if exists(':CompilerSet') != 2 - command -nargs=* CompilerSet setlocal <args> -endif - if exists('g:cargo_makeprg_params') execute 'CompilerSet makeprg=cargo\ '.escape(g:cargo_makeprg_params, ' \|"').'\ $*' else diff --git a/runtime/compiler/checkstyle.vim b/runtime/compiler/checkstyle.vim index 4ebd9e15b5..3c17ee45bf 100644 --- a/runtime/compiler/checkstyle.vim +++ b/runtime/compiler/checkstyle.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Checkstyle " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Aug 2 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "checkstyle" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/cm3.vim b/runtime/compiler/cm3.vim index 3de51282e6..0ec360855c 100644 --- a/runtime/compiler/cm3.vim +++ b/runtime/compiler/cm3.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Critical Mass Modula-3 Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2021 Apr 08 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "cm3" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/context.vim b/runtime/compiler/context.vim index cb78c96df0..3d626ffa60 100644 --- a/runtime/compiler/context.vim +++ b/runtime/compiler/context.vim @@ -9,10 +9,6 @@ endif let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - " If makefile exists and we are not asked to ignore it, we use standard make " (do not redefine makeprg) if get(b:, 'context_ignore_makefile', get(g:, 'context_ignore_makefile', 0)) || @@ -20,11 +16,12 @@ if get(b:, 'context_ignore_makefile', get(g:, 'context_ignore_makefile', 0)) || let current_compiler = 'context' " The following assumes that the current working directory is set to the " directory of the file to be typeset - let &l:makeprg = get(b:, 'context_mtxrun', get(g:, 'context_mtxrun', 'mtxrun')) + let s:makeprg = get(b:, 'context_mtxrun', get(g:, 'context_mtxrun', 'mtxrun')) \ . ' --script context --autogenerate --nonstopmode --synctex=' \ . (get(b:, 'context_synctex', get(g:, 'context_synctex', 0)) ? '1' : '0') \ . ' ' . get(b:, 'context_extra_options', get(g:, 'context_extra_options', '')) \ . ' ' . shellescape(expand('%:p:t')) + execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' ') else let current_compiler = 'make' endif diff --git a/runtime/compiler/cs.vim b/runtime/compiler/cs.vim index 4f6dd3cdfd..123dff854f 100644 --- a/runtime/compiler/cs.vim +++ b/runtime/compiler/cs.vim @@ -3,6 +3,7 @@ " Maintainer: Yichao Zhou (broken.zhou@gmail.com) " Previous Maintainer: Joseph H. Yao (hyao@sina.com) " Last Change: Jul 22, 2019 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -11,10 +12,6 @@ let current_compiler = "cs" let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat& CompilerSet errorformat+=%f(%l\\,%v):\ %t%*[^:]:\ %m, \%trror%*[^:]:\ %m, diff --git a/runtime/compiler/csslint.vim b/runtime/compiler/csslint.vim index 14c4289a24..608cc74864 100644 --- a/runtime/compiler/csslint.vim +++ b/runtime/compiler/csslint.vim @@ -1,16 +1,13 @@ " Vim compiler file " Compiler: csslint for CSS -" Maintainer: Daniel Moch <daniel@danielmoch.com> -" Last Change: 2016 May 21 +" Maintainer: Daniel Moch <daniel@danielmoch.com> +" Last Change: 2016 May 21 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "csslint" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=csslint\ --format=compact CompilerSet errorformat=%-G,%-G%f:\ lint\ free!,%f:\ line\ %l\\,\ col\ %c\\,\ %trror\ -\ %m,%f:\ line\ %l\\,\ col\ %c\\,\ %tarning\ -\ %m,%f:\ line\ %l\\,\ col\ %c\\,\ %m diff --git a/runtime/compiler/cucumber.vim b/runtime/compiler/cucumber.vim index 17ce3627c1..8089a5ee5c 100644 --- a/runtime/compiler/cucumber.vim +++ b/runtime/compiler/cucumber.vim @@ -2,16 +2,13 @@ " Compiler: Cucumber " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " Last Change: 2016 Aug 29 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "cucumber" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/dart.vim b/runtime/compiler/dart.vim index f9ebfe1b27..9577236bee 100644 --- a/runtime/compiler/dart.vim +++ b/runtime/compiler/dart.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Dart VM " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 May 08 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "dart" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/dart2js.vim b/runtime/compiler/dart2js.vim index b6a4e4d530..a05dead2a9 100644 --- a/runtime/compiler/dart2js.vim +++ b/runtime/compiler/dart2js.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Dart to JavaScript Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 May 08 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "dart2js" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/dart2native.vim b/runtime/compiler/dart2native.vim index 51bdab0cc3..6604fc4bd4 100644 --- a/runtime/compiler/dart2native.vim +++ b/runtime/compiler/dart2native.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Dart to Native Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 May 08 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "dart2native" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/dartanalyser.vim b/runtime/compiler/dartanalyser.vim index e691d80b61..48a976a016 100644 --- a/runtime/compiler/dartanalyser.vim +++ b/runtime/compiler/dartanalyser.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Dart Analyzer " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 May 08 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "dartanalyzer" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/dartdevc.vim b/runtime/compiler/dartdevc.vim index e8d1988ed6..16e4ed7ced 100644 --- a/runtime/compiler/dartdevc.vim +++ b/runtime/compiler/dartdevc.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Dart Development Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 May 08 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "dartdevc" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/dartdoc.vim b/runtime/compiler/dartdoc.vim index 26b38d8dd4..78f0d241a4 100644 --- a/runtime/compiler/dartdoc.vim +++ b/runtime/compiler/dartdoc.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Dart Documentation Generator " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 May 08 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "dartdoc" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/dartfmt.vim b/runtime/compiler/dartfmt.vim index 2e4ab02ad3..d204df8a73 100644 --- a/runtime/compiler/dartfmt.vim +++ b/runtime/compiler/dartfmt.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Dart Formatter " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 May 08 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "dartfmt" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/decada.vim b/runtime/compiler/decada.vim index 0bf5487c89..11b0cb412a 100644 --- a/runtime/compiler/decada.vim +++ b/runtime/compiler/decada.vim @@ -14,6 +14,7 @@ " 08.09.2006 MK Correct double load protection. " Help Page: compiler-decada "------------------------------------------------------------------------------ +" Last Change: 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if (exists("current_compiler") && current_compiler == "decada") || version < 700 finish @@ -34,13 +35,6 @@ if !exists("g:decada") call g:decada.Set_Session () endif -if exists(":CompilerSet") != 2 - " - " plugin loaded by other means then the "compiler" command - " - command -nargs=* CompilerSet setlocal <args> -endif - execute "CompilerSet makeprg=" . escape (g:decada.Make_Command, ' ') execute "CompilerSet errorformat=" . escape (g:decada.Error_Format, ' ') diff --git a/runtime/compiler/dot.vim b/runtime/compiler/dot.vim index 0327739aae..145411ebd3 100644 --- a/runtime/compiler/dot.vim +++ b/runtime/compiler/dot.vim @@ -1,15 +1,15 @@ " Vim compiler file " Compiler: ATT dot " Maintainer: Marcos Macedo <bar4ka@bol.com.br> -" Last Change: 2004 May 16 +" Last Change: 2024 March 21 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "dot" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=dot\ -T$*\ \"%:p\"\ -o\ \"%:p:r.$*\" +" matches error messages as below skipping final part after line number +" Error: ./file.dot: syntax error in line 1 near 'rankdir' +CompilerSet errorformat=%trror:\ %f:\ %m\ in\ line\ %l%.%# diff --git a/runtime/compiler/dotnet.vim b/runtime/compiler/dotnet.vim index ac64084663..1290357221 100644 --- a/runtime/compiler/dotnet.vim +++ b/runtime/compiler/dotnet.vim @@ -2,6 +2,7 @@ " Compiler: dotnet build (.NET CLI) " Maintainer: Nick Jensen <nickspoon@gmail.com> " Last Change: 2022-12-06 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " License: Vim (see :h license) " Repository: https://github.com/nickspoons/vim-cs @@ -10,10 +11,6 @@ if exists("current_compiler") endif let current_compiler = "dotnet" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/erlang.vim b/runtime/compiler/erlang.vim index e22887e05e..b30b5c11e7 100644 --- a/runtime/compiler/erlang.vim +++ b/runtime/compiler/erlang.vim @@ -2,6 +2,7 @@ " Compiler: Erlang " Maintainer: Dmitry Vasiliev <dima at hlabs dot org> " Last Change: 2019 Jul 23 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish diff --git a/runtime/compiler/eruby.vim b/runtime/compiler/eruby.vim index a81a3f3b77..815d082f86 100644 --- a/runtime/compiler/eruby.vim +++ b/runtime/compiler/eruby.vim @@ -3,17 +3,13 @@ " Maintainer: Doug Kearns <dougkearns@gmail.com> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2018 Jan 25 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "eruby" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/eslint.vim b/runtime/compiler/eslint.vim index 7d57acbdf4..db7a665991 100644 --- a/runtime/compiler/eslint.vim +++ b/runtime/compiler/eslint.vim @@ -2,15 +2,12 @@ " Compiler: ESLint for JavaScript " Maintainer: Romain Lafourcade <romainlafourcade@gmail.com> " Last Change: 2020 August 20 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "eslint" -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=npx\ eslint\ --format\ compact CompilerSet errorformat=%f:\ line\ %l\\,\ col\ %c\\,\ %m,%-G%.%# diff --git a/runtime/compiler/fbc.vim b/runtime/compiler/fbc.vim index 1c29392da8..afedc32dff 100644 --- a/runtime/compiler/fbc.vim +++ b/runtime/compiler/fbc.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: FreeBASIC Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2015 Jan 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "fbc" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/fortran_F.vim b/runtime/compiler/fortran_F.vim index d9cb47cc1f..7f6ec818e0 100644 --- a/runtime/compiler/fortran_F.vim +++ b/runtime/compiler/fortran_F.vim @@ -3,17 +3,14 @@ " URL: http://www.unb.ca/chem/ajit/compiler/fortran_F.vim " Maintainer: Ajit J. Thakkar (ajit AT unb.ca); <http://www.unb.ca/chem/ajit/> " Version: 0.2 -" Last Change: 2004 Mar 27 +" Last Change: 2004 Mar 27 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "fortran_F" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cposet=&cpoptions set cpoptions-=C diff --git a/runtime/compiler/fortran_cv.vim b/runtime/compiler/fortran_cv.vim index bc217bdc66..ea46d99af4 100644 --- a/runtime/compiler/fortran_cv.vim +++ b/runtime/compiler/fortran_cv.vim @@ -2,16 +2,13 @@ " Compiler: Compaq Visual Fortran " Maintainer: Joh.-G. Simon (johann-guenter.simon@linde-le.com) " Last Change: 11/05/2002 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "fortran_cv" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cposet = &cpoptions set cpoptions-=C diff --git a/runtime/compiler/fortran_elf90.vim b/runtime/compiler/fortran_elf90.vim index 34c33cfaf8..903701e8e6 100644 --- a/runtime/compiler/fortran_elf90.vim +++ b/runtime/compiler/fortran_elf90.vim @@ -4,17 +4,14 @@ " URL: http://www.unb.ca/chem/ajit/compiler/fortran_elf90.vim " Maintainer: Ajit J. Thakkar (ajit AT unb.ca); <http://www.unb.ca/chem/ajit/> " Version: 0.2 -" Last Change: 2004 Mar 27 +" Last Change: 2004 Mar 27 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "fortran_elf90" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cposet=&cpoptions set cpoptions-=C diff --git a/runtime/compiler/fortran_g77.vim b/runtime/compiler/fortran_g77.vim index 744ce6a334..dcac2a7c9a 100644 --- a/runtime/compiler/fortran_g77.vim +++ b/runtime/compiler/fortran_g77.vim @@ -2,6 +2,7 @@ " Compiler: g77 (GNU Fortran) " Maintainer: Ralf Wildenhues <Ralf.Wildenhues@gmx.de> " Last Change: $Date: 2004/06/13 18:17:36 $ +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " $Revision: 1.1 $ if exists("current_compiler") @@ -9,10 +10,6 @@ if exists("current_compiler") endif let current_compiler = "fortran_g77" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/fortran_lf95.vim b/runtime/compiler/fortran_lf95.vim index 685fee7b4a..2cb71589b2 100644 --- a/runtime/compiler/fortran_lf95.vim +++ b/runtime/compiler/fortran_lf95.vim @@ -3,17 +3,14 @@ " URL: http://www.unb.ca/chem/ajit/compiler/fortran_lf95.vim " Maintainer: Ajit J. Thakkar (ajit AT unb.ca); <http://www.unb.ca/chem/ajit/> " Version: 0.2 -" Last Change: 2004 Mar 27 +" Last Change: 2004 Mar 27 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "fortran_lf95" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cposet=&cpoptions set cpoptions-=C diff --git a/runtime/compiler/fpc.vim b/runtime/compiler/fpc.vim index de8e2fe3dc..0d321ab4c7 100644 --- a/runtime/compiler/fpc.vim +++ b/runtime/compiler/fpc.vim @@ -2,16 +2,13 @@ " Compiler: FPC 2.1 " Maintainer: Jaroslaw Blasiok <jaro3000@o2.pl> " Last Change: 2005 October 07 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "fpc" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - " NOTE: compiler must be run with -vb to write whole source path, not only file " name. CompilerSet errorformat=%f(%l\\,%c)\ %m diff --git a/runtime/compiler/g95.vim b/runtime/compiler/g95.vim index ecb3212cbe..e0b3a688f2 100644 --- a/runtime/compiler/g95.vim +++ b/runtime/compiler/g95.vim @@ -2,6 +2,7 @@ " Maintainer: H Xu <xuhdev@gmail.com> " Version: 0.1.3 " Last Change: 2012 Apr 30 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " Homepage: http://www.vim.org/scripts/script.php?script_id=3492 " https://bitbucket.org/xuhdev/compiler-g95.vim " License: Same as Vim @@ -13,10 +14,6 @@ let current_compiler = 'g95' let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat= \%AIn\ file\ %f:%l, \%-C%p1, diff --git a/runtime/compiler/gawk.vim b/runtime/compiler/gawk.vim index a109eb8642..f2a360d5f3 100644 --- a/runtime/compiler/gawk.vim +++ b/runtime/compiler/gawk.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: GNU Awk " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Feb 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "gawk" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/gcc.vim b/runtime/compiler/gcc.vim index 30e5149f9f..7b6ebb98f4 100644 --- a/runtime/compiler/gcc.vim +++ b/runtime/compiler/gcc.vim @@ -1,10 +1,11 @@ " Vim compiler file -" Compiler: GNU C Compiler -" Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2010-10-14 -" changed pattern for entering/leaving directories -" by Daniel Hahler, 2019 Jul 12 -" added line suggested by Anton Lindqvist 2016 Mar 31 +" Compiler: GNU C Compiler +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Last Change: 2010 Oct 14 +" changed pattern for entering/leaving directories +" by Daniel Hahler, 2019 Jul 12 +" added line suggested by Anton Lindqvist 2016 Mar 31 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish diff --git a/runtime/compiler/gfortran.vim b/runtime/compiler/gfortran.vim index 1e4e5cf4f4..1fea3bcb18 100644 --- a/runtime/compiler/gfortran.vim +++ b/runtime/compiler/gfortran.vim @@ -2,6 +2,7 @@ " Maintainer: H Xu <xuhdev@gmail.com> " Version: 0.1.3 " Last Change: 2012 Apr 30 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " Homepage: http://www.vim.org/scripts/script.php?script_id=3496 " https://bitbucket.org/xuhdev/compiler-gfortran.vim " License: Same as Vim @@ -13,10 +14,6 @@ let current_compiler = 'gfortran' let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat= \%A%f:%l.%c:, \%-Z%trror:\ %m, diff --git a/runtime/compiler/gjs.vim b/runtime/compiler/gjs.vim index e033764030..b1be8cf3e6 100644 --- a/runtime/compiler/gjs.vim +++ b/runtime/compiler/gjs.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: GJS (Gnome JavaScript Bindings) " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Jul 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "gjs" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/gm2.vim b/runtime/compiler/gm2.vim index 5053912200..954da9d0e7 100644 --- a/runtime/compiler/gm2.vim +++ b/runtime/compiler/gm2.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: GNU Modula-2 Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2024 Jan 04 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "gm2" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/gnat.vim b/runtime/compiler/gnat.vim index 3ad1eb4e3f..086edbede3 100644 --- a/runtime/compiler/gnat.vim +++ b/runtime/compiler/gnat.vim @@ -52,13 +52,6 @@ if !exists("g:gnat") call g:gnat.Set_Session () endif -if exists(":CompilerSet") != 2 - " - " plugin loaded by other means then the "compiler" command - " - command -nargs=* CompilerSet setlocal <args> -endif - execute "CompilerSet makeprg=" . escape (g:gnat.Get_Command('Make'), ' ') execute "CompilerSet errorformat=" . escape (g:gnat.Error_Format, ' ') diff --git a/runtime/compiler/go.vim b/runtime/compiler/go.vim index cf638f23d6..9c1a6f3119 100644 --- a/runtime/compiler/go.vim +++ b/runtime/compiler/go.vim @@ -2,16 +2,13 @@ " Compiler: Go " Maintainer: David Barnett (https://github.com/google/vim-ft-go) " Last Change: 2014 Aug 16 +" 2024 Apr 05 by The Vim Project (removed :CompilerSet definition) if exists('current_compiler') finish endif let current_compiler = 'go' -if exists(':CompilerSet') != 2 - command -nargs=* CompilerSet setlocal <args> -endif - let s:save_cpo = &cpo set cpo-=C diff --git a/runtime/compiler/haml.vim b/runtime/compiler/haml.vim index 9464c3dc85..99c171d76c 100644 --- a/runtime/compiler/haml.vim +++ b/runtime/compiler/haml.vim @@ -2,16 +2,13 @@ " Compiler: Haml " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " Last Change: 2016 Aug 29 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "haml" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/hare.vim b/runtime/compiler/hare.vim index c0fa68cc00..c98bbb9c63 100644 --- a/runtime/compiler/hare.vim +++ b/runtime/compiler/hare.vim @@ -2,6 +2,7 @@ " Compiler: Hare Compiler " Maintainer: Amelia Clarke <me@rsaihe.dev> " Last Change: 2022-09-21 +" 2024 Apr 05 by The Vim Project (removed :CompilerSet definition) if exists("g:current_compiler") finish @@ -11,10 +12,6 @@ let g:current_compiler = "hare" let s:cpo_save = &cpo set cpo&vim -if exists(':CompilerSet') != 2 - command -nargs=* CompilerSet setlocal <args> -endif - if filereadable("Makefile") || filereadable("makefile") CompilerSet makeprg=make else diff --git a/runtime/compiler/hp_acc.vim b/runtime/compiler/hp_acc.vim index 15d5ce5f19..f6ccc57c73 100644 --- a/runtime/compiler/hp_acc.vim +++ b/runtime/compiler/hp_acc.vim @@ -3,6 +3,7 @@ " Maintainer: Matthias Ulrich <matthias-ulrich@web.de> " URL: http://www.subhome.de/vim/hp_acc.vim " Last Change: 2012 Apr 30 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " " aCC --version says: "HP ANSI C++ B3910B A.03.13" " This compiler has been tested on: @@ -22,10 +23,6 @@ let current_compiler = "hp_acc" let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat=%A%trror\ %n\:\ \"%f\"\\,\ line\ %l\ \#\ %m, \%A%tarning\ (suggestion)\ %n\:\ \"%f\"\\,\ line\ %l\ \#\ %m\ %#, \%A%tarning\ %n\:\ \"%f\"\\,\ line\ %l\ \#\ %m\ %#, diff --git a/runtime/compiler/icc.vim b/runtime/compiler/icc.vim index 751fba65c7..37958f1c94 100644 --- a/runtime/compiler/icc.vim +++ b/runtime/compiler/icc.vim @@ -2,16 +2,13 @@ " Compiler: icc - Intel C++ " Maintainer: Peter Puck <PtrPck@netscape.net> " Last Change: 2004 Mar 27 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "icc" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - " I think that Intel is calling the compiler icl under Windows CompilerSet errorformat=%-Z%p^,%f(%l):\ remark\ #%n:%m,%f(%l)\ :\ (col.\ %c)\ remark:\ %m,%E%f(%l):\ error:\ %m,%E%f(%l):\ error:\ #%n:\ %m,%W%f(%l):\ warning\ #%n:\ %m,%W%f(%l):\ warning:\ %m,%-C%.%# diff --git a/runtime/compiler/icon.vim b/runtime/compiler/icon.vim index 40f67930f7..8b46af2a13 100644 --- a/runtime/compiler/icon.vim +++ b/runtime/compiler/icon.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Icon Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2022 Jun 16 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "icont" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/ifort.vim b/runtime/compiler/ifort.vim index 3a60d0cd12..8c58ba685d 100644 --- a/runtime/compiler/ifort.vim +++ b/runtime/compiler/ifort.vim @@ -2,6 +2,7 @@ " Maintainer: H Xu <xuhdev@gmail.com> " Version: 0.1.1 " Last Change: 2012 Apr 30 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " Homepage: http://www.vim.org/scripts/script.php?script_id=3497 " https://bitbucket.org/xuhdev/compiler-ifort.vim " License: Same as Vim @@ -13,10 +14,6 @@ let current_compiler = 'ifort' let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat= \%A%f(%l):\ %trror\ \#%n:\ %m, \%A%f(%l):\ %tarning\ \#%n:\ %m, diff --git a/runtime/compiler/intel.vim b/runtime/compiler/intel.vim index 254d13b6d8..cde62b1c48 100644 --- a/runtime/compiler/intel.vim +++ b/runtime/compiler/intel.vim @@ -2,6 +2,7 @@ " Compiler: Intel C++ 7.1 " Maintainer: David Harrison <david_jr@users.sourceforge.net> " Last Change: 2012 Apr 30 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -10,10 +11,6 @@ let current_compiler = "intel" let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat=%E%f(%l):\ error:\ %m, \%W%f(%l):\ warning:\ %m, \%I%f(%l):\ remark\ #%n:\ %m, diff --git a/runtime/compiler/irix5_c.vim b/runtime/compiler/irix5_c.vim index f440e74f05..f70bfb1ea7 100644 --- a/runtime/compiler/irix5_c.vim +++ b/runtime/compiler/irix5_c.vim @@ -2,6 +2,7 @@ " Compiler: SGI IRIX 5.3 cc " Maintainer: David Harrison <david_jr@users.sourceforge.net> " Last Change: 2012 Apr 30 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -10,10 +11,6 @@ let current_compiler = "irix5_c" let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat=\%Ecfe:\ Error:\ %f\\,\ line\ %l:\ %m, \%Wcfe:\ Warning:\ %n:\ %f\\,\ line\ %l:\ %m, \%Wcfe:\ Warning\ %n:\ %f\\,\ line\ %l:\ %m, diff --git a/runtime/compiler/irix5_cpp.vim b/runtime/compiler/irix5_cpp.vim index 0112dc1cbc..85ab8c67f2 100644 --- a/runtime/compiler/irix5_cpp.vim +++ b/runtime/compiler/irix5_cpp.vim @@ -2,6 +2,7 @@ " Compiler: SGI IRIX 5.3 CC or NCC " Maintainer: David Harrison <david_jr@users.sourceforge.net> " Last Change: 2012 Apr 30 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -10,10 +11,6 @@ let current_compiler = "irix5_cpp" let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat=%E\"%f\"\\,\ line\ %l:\ error(%n):\ , \%E\"%f\"\\,\ line\ %l:\ error(%n):\ %m, \%W\"%f\"\\,\ line\ %l:\ warning(%n):\ %m, diff --git a/runtime/compiler/javac.vim b/runtime/compiler/javac.vim index 8507efd67c..f5fe84124f 100644 --- a/runtime/compiler/javac.vim +++ b/runtime/compiler/javac.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Java Development Kit Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Oct 21 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "javac" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/jest.vim b/runtime/compiler/jest.vim index a4bb549de1..663123c30f 100644 --- a/runtime/compiler/jest.vim +++ b/runtime/compiler/jest.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Jest " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2021 Nov 20 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "jest" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/jikes.vim b/runtime/compiler/jikes.vim index 2d4500e894..f12c2cf5f1 100644 --- a/runtime/compiler/jikes.vim +++ b/runtime/compiler/jikes.vim @@ -2,6 +2,7 @@ " Compiler: Jikes " Maintainer: Dan Sharp <dwsharp at hotmail dot com> " Last Change: 2019 Jul 23 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " URL: http://dwsharp.users.sourceforge.net/vim/compiler if exists("current_compiler") @@ -9,10 +10,6 @@ if exists("current_compiler") endif let current_compiler = "jikes" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - " Jikes defaults to printing output on stderr CompilerSet makeprg=jikes\ -Xstdout\ +E\ \"%:S\" CompilerSet errorformat=%f:%l:%v:%*\\d:%*\\d:%*\\s%m diff --git a/runtime/compiler/jjs.vim b/runtime/compiler/jjs.vim index 09a973683a..543d9ca3d4 100644 --- a/runtime/compiler/jjs.vim +++ b/runtime/compiler/jjs.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Nashorn Shell " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2018 Jan 9 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "jjs" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/jq.vim b/runtime/compiler/jq.vim new file mode 100644 index 0000000000..a656223e51 --- /dev/null +++ b/runtime/compiler/jq.vim @@ -0,0 +1,25 @@ +" Vim compiler file +" Compiler: jq +" Maintainer: Vito <vito.blog@gmail.com> +" Last Change: 2024 Apr 17 +" Upstream: https://github.com/vito-c/jq.vim + +if exists('b:current_compiler') + finish +endif +let b:current_compiler = 'jq' + +let s:save_cpoptions = &cpoptions +set cpoptions&vim + +if has('unix') + CompilerSet makeprg=jq\ -f\ %:S\ /dev/null +else + CompilerSet makeprg=jq\ -f\ %:S\ nul +endif +CompilerSet errorformat=%E%m\ at\ \\<%o\\>\\,\ line\ %l:, + \%Z, + \%-G%.%# + +let &cpoptions = s:save_cpoptions +unlet s:save_cpoptions diff --git a/runtime/compiler/jshint.vim b/runtime/compiler/jshint.vim index 865591cd2b..9879ef49e5 100644 --- a/runtime/compiler/jshint.vim +++ b/runtime/compiler/jshint.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: JSHint " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Jul 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "jshint" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/jsonlint.vim b/runtime/compiler/jsonlint.vim index 5466dcc7f4..6283b1b01f 100644 --- a/runtime/compiler/jsonlint.vim +++ b/runtime/compiler/jsonlint.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: JSON Lint " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Jul 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "jsonlint" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/mcs.vim b/runtime/compiler/mcs.vim index c606586870..0c265b3de5 100644 --- a/runtime/compiler/mcs.vim +++ b/runtime/compiler/mcs.vim @@ -1,8 +1,9 @@ " Vim compiler file -" Compiler: Mono C# Compiler -" Maintainer: Jarek Sobiecki <harijari@go2.pl> -" Last Updated By: Peter Collingbourne -" Latest Revision: 2012 Jul 19 +" Compiler: Mono C# Compiler +" Maintainer: Jarek Sobiecki <harijari@go2.pl> +" Contributors: Peter Collingbourne and Enno Nagel +" Last Change: 2024 Mar 29 +" 2024 Apr 05 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -12,7 +13,8 @@ let current_compiler = "mcs" let s:cpo_save = &cpo set cpo-=C -setlocal errorformat= +CompilerSet makeprg=mcs +CompilerSet errorformat= \%D%.%#Project\ \"%f/%[%^/\"]%#\"%.%#, \%X%.%#Done\ building\ project\ \"%f/%[%^/\"]%#\"%.%#, \%-G%\\s%.%#, diff --git a/runtime/compiler/mips_c.vim b/runtime/compiler/mips_c.vim index a0ebfe7ba2..544f314a1a 100644 --- a/runtime/compiler/mips_c.vim +++ b/runtime/compiler/mips_c.vim @@ -2,6 +2,7 @@ " Compiler: SGI IRIX 6.5 MIPS C (cc) " Maintainer: David Harrison <david_jr@users.sourceforge.net> " Last Change: 2012 Apr 30 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -10,10 +11,6 @@ let current_compiler = "mips_c" let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat=%Ecc\-%n\ %.%#:\ ERROR\ File\ =\ %f\%\\,\ Line\ =\ %l, \%Wcc\-%n\ %.%#:\ WARNING\ File\ =\ %f\%\\,\ Line\ =\ %l, \%Icc\-%n\ %.%#:\ REMARK\ File\ =\ %f\%\\,\ Line\ =\ %l, diff --git a/runtime/compiler/mipspro_c89.vim b/runtime/compiler/mipspro_c89.vim index 20eb70d3fe..4d6bdabeef 100644 --- a/runtime/compiler/mipspro_c89.vim +++ b/runtime/compiler/mipspro_c89.vim @@ -2,6 +2,7 @@ " Compiler: SGI IRIX 6.5 MIPSPro C (c89) " Maintainer: David Harrison <david_jr@users.sourceforge.net> " Last Change: 2012 Apr 30 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -10,10 +11,6 @@ let current_compiler = "mipspro_c89" let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat=%Ecc\-%n\ %.%#:\ ERROR\ File\ =\ %f\%\\,\ Line\ =\ %l, \%Wcc\-%n\ %.%#:\ WARNING\ File\ =\ %f\%\\,\ Line\ =\ %l, \%Icc\-%n\ %.%#:\ REMARK\ File\ =\ %f\%\\,\ Line\ =\ %l, diff --git a/runtime/compiler/mipspro_cpp.vim b/runtime/compiler/mipspro_cpp.vim index 2f43af8455..9aac2a6564 100644 --- a/runtime/compiler/mipspro_cpp.vim +++ b/runtime/compiler/mipspro_cpp.vim @@ -2,6 +2,7 @@ " Compiler: SGI IRIX 6.5 MIPSPro C++ (CC) " Maintainer: David Harrison <david_jr@users.sourceforge.net> " Last Change: 2012 Apr 30 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -10,10 +11,6 @@ let current_compiler = "mipspro_cpp" let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat=%Ecc\-%n\ %.%#:\ ERROR\ File\ =\ %f\%\\,\ Line\ =\ %l, \%Wcc\-%n\ %.%#:\ WARNING\ File\ =\ %f\%\\,\ Line\ =\ %l, \%Icc\-%n\ %.%#:\ REMARK\ File\ =\ %f\%\\,\ Line\ =\ %l, diff --git a/runtime/compiler/modelsim_vcom.vim b/runtime/compiler/modelsim_vcom.vim index 6aa1bde0b3..8c6b8063ad 100644 --- a/runtime/compiler/modelsim_vcom.vim +++ b/runtime/compiler/modelsim_vcom.vim @@ -1,7 +1,9 @@ " Vim Compiler File " Compiler: Modelsim Vcom " Maintainer: Paul Baleme <pbaleme@mail.com> -" Last Change: September 8, 2003 +" Contributors: Enno Nagel +" Last Change: 2024 Mar 29 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " Thanks to: allanherriman@hotmail.com if exists("current_compiler") @@ -9,12 +11,9 @@ if exists("current_compiler") endif let current_compiler = "modelsim_vcom" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif +CompilerSet makeprg=vcom "setlocal errorformat=\*\*\ %tRROR:\ %f(%l):\ %m,%tRROR:\ %f(%l):\ %m,%tARNING\[%*[0-9]\]:\ %f(%l):\ %m,\*\*\ %tRROR:\ %m,%tRROR:\ %m,%tARNING\[%*[0-9]\]:\ %m - "setlocal errorformat=%tRROR:\ %f(%l):\ %m,%tARNING\[%*[0-9]\]:\ %m CompilerSet errorformat=\*\*\ %tRROR:\ %f(%l):\ %m,\*\*\ %tRROR:\ %m,\*\*\ %tARNING:\ %m,\*\*\ %tOTE:\ %m,%tRROR:\ %f(%l):\ %m,%tARNING\[%*[0-9]\]:\ %f(%l):\ %m,%tRROR:\ %m,%tARNING\[%*[0-9]\]:\ %m diff --git a/runtime/compiler/msbuild.vim b/runtime/compiler/msbuild.vim index 3652ca0e04..c871efaf3d 100644 --- a/runtime/compiler/msbuild.vim +++ b/runtime/compiler/msbuild.vim @@ -2,6 +2,7 @@ " Compiler: Microsoft Visual Studio C# " Maintainer: Chiel ten Brinke (ctje92@gmail.com) " Last Change: 2013 May 13 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -10,10 +11,6 @@ let current_compiler = "msbuild" let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat=\ %#%f(%l\\\,%c):\ %m CompilerSet makeprg=msbuild\ /nologo\ /v:q\ /property:GenerateFullPaths=true diff --git a/runtime/compiler/msvc.vim b/runtime/compiler/msvc.vim index 0d5660c103..3d40de5b7f 100644 --- a/runtime/compiler/msvc.vim +++ b/runtime/compiler/msvc.vim @@ -2,6 +2,7 @@ " Compiler: Microsoft Visual C " Maintainer: The Vim Project <https://github.com/vim/vim> " Last Change: 2023 Aug 10 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " Former Maintainer: Bram Moolenaar <Bram@vim.org> if exists("current_compiler") diff --git a/runtime/compiler/neato.vim b/runtime/compiler/neato.vim index bd184b7f6e..0f36734130 100644 --- a/runtime/compiler/neato.vim +++ b/runtime/compiler/neato.vim @@ -1,15 +1,15 @@ " Vim compiler file " Compiler: ATT neato " Maintainer: Marcos Macedo <bar4ka@bol.com.br> -" Last Change: 2004 May 16 +" Last Change: 2024 March 21 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "neato" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=neato\ -T$*\ \"%:p\"\ -o\ \"%:p:r.$*\" +" matches error messages as below skipping final part after line number +" Error: ./file.dot: syntax error in line 1 near 'rankdir' +CompilerSet errorformat=%trror:\ %f:\ %m\ in\ line\ %l%.%# diff --git a/runtime/compiler/ocaml.vim b/runtime/compiler/ocaml.vim index faa8af1f5f..754289fc14 100644 --- a/runtime/compiler/ocaml.vim +++ b/runtime/compiler/ocaml.vim @@ -3,6 +3,7 @@ " Maintainer: Markus Mottl <markus.mottl@gmail.com> " URL: https://github.com/ocaml/vim-ocaml " Last Change: +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " 2020 Mar 28 - Improved error format (Thomas Leonard) " 2017 Nov 26 - Improved error format (Markus Mottl) " 2013 Aug 27 - Added a new OCaml error format (Markus Mottl) diff --git a/runtime/compiler/onsgmls.vim b/runtime/compiler/onsgmls.vim index 68925ef459..4306f99de5 100644 --- a/runtime/compiler/onsgmls.vim +++ b/runtime/compiler/onsgmls.vim @@ -2,16 +2,13 @@ " Compiler: onsgmls " Maintainer: Robert Rowsome <rowsome@wam.umd.edu> " Last Change: 2019 Jul 23 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "onsgmls" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/pandoc.vim b/runtime/compiler/pandoc.vim new file mode 100644 index 0000000000..6c151930c5 --- /dev/null +++ b/runtime/compiler/pandoc.vim @@ -0,0 +1,55 @@ +" Vim compiler file +" Compiler: Pandoc +" Maintainer: Konfekt +" +" Expects output file extension, say `:make html` or `:make pdf`. +" Passes additional arguments to pandoc, say `:make html --self-contained`. + +if exists("current_compiler") + finish +endif + +let s:keepcpo = &cpo +set cpo&vim + +let current_compiler = 'pandoc' + +" As of 2024-04-08 pandoc supports the following text input formats with +" an ftplugin on Github: +let s:supported_filetypes = + \ [ 'bibtex', 'markdown', 'creole', 'json', 'csv', 'tsv', 'docbook', + \ 'xml', 'fb2', 'html', 'jira', 'tex', 'mediawiki', 'nroff', 'org', + \ 'rtf', 'rst', 't2t', 'textile', 'twiki', 'typst', 'vimwiki' ] +" .. and out of those the following are included in Vim's runtime: +" 'xml', 'tex', 'html', 'rst', 'json', 'nroff', 'markdown' + +silent! function s:PandocFiletype(filetype) abort + let ft = a:filetype + if ft ==# 'pandoc' + return 'markdown' + elseif ft ==# 'tex' + return 'latex' + elseif ft ==# 'xml' + " Pandoc does not support XML as a generic input format, but it does support + " EndNote XML and Jats XML out of which the latter seems more universal. + return 'jats' + elseif ft ==# 'text' || empty(ft) + return 'markdown' + elseif index(s:supported_filetypes, &ft) >= 0 + return ft + else + echomsg 'Unsupported filetype: ' . ft . ', falling back to Markdown as input format!' + return 'markdown' + endif +endfunction +execute 'CompilerSet makeprg=pandoc\ --standalone' . + \ '\ --metadata\ title=%:t:r:S' . + \ '\ --metadata\ lang=' . matchstr(&spelllang, '^\a\a') . + \ '\ --from=' . s:PandocFiletype(&filetype) . + \ '\ ' . escape(get(b:, 'pandoc_compiler_args', get(g:, 'pandoc_compiler_args', '')), ' ') . + \ '\ --output\ %:r:S.$*\ %:S' + +CompilerSet errorformat="%f",\ line\ %l:\ %m + +let &cpo = s:keepcpo +unlet s:keepcpo diff --git a/runtime/compiler/pbx.vim b/runtime/compiler/pbx.vim index 9e81ea7355..5a45f7ffe0 100644 --- a/runtime/compiler/pbx.vim +++ b/runtime/compiler/pbx.vim @@ -2,16 +2,13 @@ " Compiler: Apple Project Builder " Maintainer: Alexander von Below (public@vonBelow.Com) " Last Change: 2004 Mar 27 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "pbx" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - " The compiler actually is gcc, so the errorformat is unchanged CompilerSet errorformat& diff --git a/runtime/compiler/perl.vim b/runtime/compiler/perl.vim index 21e384fb85..6aeaac3fa1 100644 --- a/runtime/compiler/perl.vim +++ b/runtime/compiler/perl.vim @@ -6,16 +6,13 @@ " Bugs/requests: https://github.com/vim-perl/vim-perl/issues " License: Vim License (see :help license) " Last Change: 2021 Nov 2 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "perl" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:savecpo = &cpo set cpo&vim diff --git a/runtime/compiler/perlcritic.vim b/runtime/compiler/perlcritic.vim index cf0678eeb2..4b5f34dd2e 100644 --- a/runtime/compiler/perlcritic.vim +++ b/runtime/compiler/perlcritic.vim @@ -6,16 +6,13 @@ " Bugs/requests: https://github.com/vim-perl/vim-perl/issues " License: Vim License (see :help license) " Last Change: 2021 Oct 20 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "perlcritic" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/php.vim b/runtime/compiler/php.vim index 92f93b89fc..f5d521098b 100644 --- a/runtime/compiler/php.vim +++ b/runtime/compiler/php.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: PHP CLI " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2013 Jun 25 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "php" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/pip_compile.vim b/runtime/compiler/pip_compile.vim new file mode 100644 index 0000000000..ca4d509660 --- /dev/null +++ b/runtime/compiler/pip_compile.vim @@ -0,0 +1,43 @@ +" the Requirements File Format syntax support for Vim +" Version: 1.8.0 +" Author: raimon <raimon49@hotmail.com> +" Upstream: https://github.com/raimon49/requirements.txt.vim +" License: MIT LICENSE +" The MIT License (MIT) +" +" Copyright (c) 2015 raimon +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to deal +" in the Software without restriction, including without limitation the rights +" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +" copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in all +" copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +" SOFTWARE. + +if exists('b:current_compiler') + finish +endif +let b:current_compiler = 'pip_compile' + +let s:save_cpoptions = &cpoptions +set cpoptions&vim + +CompilerSet makeprg=pip-compile\ %:S +CompilerSet errorformat=%ECould\ not\ find\ a\ version\ that\ matches\ %o\ (from\ -r\ %f\ (line\ %l)), + \%C%m, + \%Z, + \%-G%.%# +let &cpoptions = s:save_cpoptions +unlet s:save_cpoptions +" vim: et sw=4 ts=4 sts=4: diff --git a/runtime/compiler/podchecker.vim b/runtime/compiler/podchecker.vim index 2cd4e13660..20faaa4bcc 100644 --- a/runtime/compiler/podchecker.vim +++ b/runtime/compiler/podchecker.vim @@ -6,16 +6,13 @@ " Bugs/requests: https://github.com/vim-perl/vim-perl/issues " License: Vim License (see :help license) " Last Change: 2021 Oct 20 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "podchecker" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/powershell.vim b/runtime/compiler/powershell.vim index 45d5ec2191..821fea4085 100644 --- a/runtime/compiler/powershell.vim +++ b/runtime/compiler/powershell.vim @@ -1,17 +1,16 @@ " Vim compiler file " Compiler: powershell " URL: https://github.com/PProvost/vim-ps1 -" Last Change: 2020 Mar 30 +" Contributors: Enno Nagel +" Last Change: 2024 Mar 29 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) +" 2024 Apr 05 by The Vim Project (avoid leaving behind g:makeprg) if exists("current_compiler") finish endif let current_compiler = "powershell" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C @@ -37,7 +36,7 @@ let g:ps1_efm_show_error_categories = get(g:, 'ps1_efm_show_error_categories', 0 " Use absolute path because powershell requires explicit relative paths " (./file.ps1 is okay, but # expands to file.ps1) -let &l:makeprg = g:ps1_makeprg_cmd .' %:p:S' +let s:makeprg = g:ps1_makeprg_cmd .. ' %:p:S' " Parse file, line, char from callstacks: " Write-Ouput : The term 'Write-Ouput' is not recognized as the name of a @@ -50,6 +49,8 @@ let &l:makeprg = g:ps1_makeprg_cmd .' %:p:S' " + CategoryInfo : ObjectNotFound: (Write-Ouput:String) [], CommandNotFoundException " + FullyQualifiedErrorId : CommandNotFoundException +execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' ') + " Showing error in context with underlining. CompilerSet errorformat=%+G+%m " Error summary. diff --git a/runtime/compiler/pylint.vim b/runtime/compiler/pylint.vim index 93079ce61d..14e9696dbb 100644 --- a/runtime/compiler/pylint.vim +++ b/runtime/compiler/pylint.vim @@ -2,15 +2,12 @@ " Compiler: Pylint for Python " Maintainer: Daniel Moch <daniel@danielmoch.com> " Last Change: 2016 May 20 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "pylint" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=pylint\ --output-format=text\ --msg-template=\"{path}:{line}:{column}:{C}:\ [{symbol}]\ {msg}\"\ --reports=no CompilerSet errorformat=%A%f:%l:%c:%t:\ %m,%A%f:%l:\ %m,%A%f:(%l):\ %m,%-Z%p^%.%#,%-G%.%# diff --git a/runtime/compiler/pyunit.vim b/runtime/compiler/pyunit.vim index 2a4ea92b68..2368346dd2 100644 --- a/runtime/compiler/pyunit.vim +++ b/runtime/compiler/pyunit.vim @@ -2,15 +2,12 @@ " Compiler: Unit testing tool for Python " Maintainer: Max Ischenko <mfi@ukr.net> " Last Change: 2004 Mar 27 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "pyunit" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet efm=%C\ %.%#,%A\ \ File\ \"%f\"\\,\ line\ %l%.%#,%Z%[%^\ ]%\\@=%m diff --git a/runtime/compiler/raco.vim b/runtime/compiler/raco.vim index bd10859aa9..d35d86b8c2 100644 --- a/runtime/compiler/raco.vim +++ b/runtime/compiler/raco.vim @@ -3,12 +3,9 @@ " Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com> " URL: https://github.com/benknoble/vim-racket " Last Change: 2022 Aug 12 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) let current_compiler = 'raco' -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=raco CompilerSet errorformat=%f:%l:%c:%m diff --git a/runtime/compiler/racomake.vim b/runtime/compiler/racomake.vim index dae95fec42..bd2c327037 100644 --- a/runtime/compiler/racomake.vim +++ b/runtime/compiler/racomake.vim @@ -3,12 +3,9 @@ " Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com> " URL: https://github.com/benknoble/vim-racket " Last Change: 2022 Aug 12 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) let current_compiler = 'racomake' -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=raco\ make\ --\ % CompilerSet errorformat=%f:%l:%c:%m diff --git a/runtime/compiler/racosetup.vim b/runtime/compiler/racosetup.vim index 1efe8a15a2..e16faf081a 100644 --- a/runtime/compiler/racosetup.vim +++ b/runtime/compiler/racosetup.vim @@ -3,12 +3,9 @@ " Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com> " URL: https://github.com/benknoble/vim-racket " Last Change: 2022 Aug 12 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) let current_compiler = 'racosetup' -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=raco\ setup CompilerSet errorformat=%f:%l:%c:%m diff --git a/runtime/compiler/racotest.vim b/runtime/compiler/racotest.vim index d2a1a3c0f3..3ac11737d2 100644 --- a/runtime/compiler/racotest.vim +++ b/runtime/compiler/racotest.vim @@ -3,12 +3,9 @@ " Maintainer: D. Ben Knoble <ben.knoble+github@gmail.com> " URL: https://github.com/benknoble/vim-racket " Last Change: 2022 Aug 12 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) let current_compiler = 'racotest' -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=raco\ test\ % CompilerSet errorformat=location:%f:%l:%c diff --git a/runtime/compiler/rake.vim b/runtime/compiler/rake.vim index 3d11a31f89..5b3c7f4092 100644 --- a/runtime/compiler/rake.vim +++ b/runtime/compiler/rake.vim @@ -4,16 +4,13 @@ " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> " Last Change: 2018 Mar 02 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "rake" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/rhino.vim b/runtime/compiler/rhino.vim index 30f3dd3252..b6fc9c8d8c 100644 --- a/runtime/compiler/rhino.vim +++ b/runtime/compiler/rhino.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Rhino Shell (JavaScript in Java) " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Jul 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "rhino" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/rime_deployer.vim b/runtime/compiler/rime_deployer.vim new file mode 100644 index 0000000000..e0c8daef6e --- /dev/null +++ b/runtime/compiler/rime_deployer.vim @@ -0,0 +1,30 @@ +" Vim Compiler File +" Language: rime_deployer +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" URL: https://rime.im +" Latest Revision: 2024-04-09 + +if exists('b:current_compiler') + finish +endif +let b:current_compiler = 'rime_deployer' + +let s:save_cpoptions = &cpoptions +set cpoptions&vim + +" Android Termux +let s:prefix = getenv('PREFIX') +if s:prefix == v:null + let s:prefix = '/usr' +endif +" Android, NixOS, GNU/Linux, BSD +for s:shared_data_dir in ['/sdcard/rime-data', '/run/current-system/sw/share/rime-data', '/usr/local/share/rime-data', s:prefix . '/share/rime-data'] + if isdirectory(s:shared_data_dir) + break + endif +endfor +execute 'CompilerSet makeprg=rime_deployer\ --build\ %:p:h:S\' s:shared_data_dir +unlet s:prefix s:shared_data_dir + +let &cpoptions = s:save_cpoptions +unlet s:save_cpoptions diff --git a/runtime/compiler/rspec.vim b/runtime/compiler/rspec.vim index 0cfce04572..ee848f703a 100644 --- a/runtime/compiler/rspec.vim +++ b/runtime/compiler/rspec.vim @@ -4,16 +4,13 @@ " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> " Last Change: 2018 Aug 07 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "rspec" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/rst.vim b/runtime/compiler/rst.vim index 392bea6ae0..72b6a58b67 100644 --- a/runtime/compiler/rst.vim +++ b/runtime/compiler/rst.vim @@ -2,7 +2,8 @@ " Compiler: sphinx >= 1.0.8, http://www.sphinx-doc.org " Description: reStructuredText Documentation Format " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2017-03-31 +" Last Change: 2017 Mar 31 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -12,10 +13,6 @@ let current_compiler = "rst" let s:cpo_save = &cpo set cpo&vim -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat= \%f\\:%l:\ %tEBUG:\ %m, \%f\\:%l:\ %tNFO:\ %m, diff --git a/runtime/compiler/rubocop.vim b/runtime/compiler/rubocop.vim index 7764b4ad20..9d2f01499f 100644 --- a/runtime/compiler/rubocop.vim +++ b/runtime/compiler/rubocop.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: RuboCop " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Jul 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "rubocop" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/ruby.vim b/runtime/compiler/ruby.vim index 82d4d1c876..70dff5bf24 100644 --- a/runtime/compiler/ruby.vim +++ b/runtime/compiler/ruby.vim @@ -5,16 +5,13 @@ " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> " Last Change: 2019 Jan 06 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "ruby" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/rubyunit.vim b/runtime/compiler/rubyunit.vim index 48e8fa41ab..e59873fa21 100644 --- a/runtime/compiler/rubyunit.vim +++ b/runtime/compiler/rubyunit.vim @@ -4,16 +4,13 @@ " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> " Last Change: 2014 Mar 23 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "rubyunit" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/rustc.vim b/runtime/compiler/rustc.vim index efcf24ed80..b3c8091987 100644 --- a/runtime/compiler/rustc.vim +++ b/runtime/compiler/rustc.vim @@ -14,10 +14,6 @@ let s:save_cpo = &cpo set cpo&vim " vint: +ProhibitAbbreviationOption -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - if get(g:, 'rustc_makeprg_no_percent', 0) CompilerSet makeprg=rustc else diff --git a/runtime/compiler/sass.vim b/runtime/compiler/sass.vim index 9c540ac443..d0ed65cfa5 100644 --- a/runtime/compiler/sass.vim +++ b/runtime/compiler/sass.vim @@ -2,16 +2,13 @@ " Compiler: Sass " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " Last Change: 2016 Aug 29 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "sass" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/scdoc.vim b/runtime/compiler/scdoc.vim index c37969f89c..364ddc179c 100644 --- a/runtime/compiler/scdoc.vim +++ b/runtime/compiler/scdoc.vim @@ -2,6 +2,7 @@ " Compiler: scdoc " Maintainer: Gregory Anders <contact@gpanders.com> " Last Updated: 2019-10-24 +" 2024 Apr 05 by The Vim Project (removed :CompilerSet definition) " Upstream: https://github.com/gpanders/vim-scdoc if exists('current_compiler') @@ -9,9 +10,5 @@ if exists('current_compiler') endif let current_compiler = 'scdoc' -if exists(':CompilerSet') != 2 - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=scdoc\ <\ %\ 2>&1 CompilerSet errorformat=Error\ at\ %l:%c:\ %m,%-G%.%# diff --git a/runtime/compiler/se.vim b/runtime/compiler/se.vim index 38fd10c131..c9332277e5 100644 --- a/runtime/compiler/se.vim +++ b/runtime/compiler/se.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: se (Liberty Eiffel Compiler) " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2013 Jun 29 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "se" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/shellcheck.vim b/runtime/compiler/shellcheck.vim index 7550439aa2..5eeddc69c1 100644 --- a/runtime/compiler/shellcheck.vim +++ b/runtime/compiler/shellcheck.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: ShellCheck " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Sep 4 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "shellcheck" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/sml.vim b/runtime/compiler/sml.vim index a0b13b6c8a..e829e20e8c 100644 --- a/runtime/compiler/sml.vim +++ b/runtime/compiler/sml.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: SML/NJ Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2022 Feb 09 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "sml" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/spectral.vim b/runtime/compiler/spectral.vim index bd13c51f43..f1ea8653e5 100644 --- a/runtime/compiler/spectral.vim +++ b/runtime/compiler/spectral.vim @@ -2,16 +2,13 @@ " Compiler: Spectral for YAML " Maintainer: Romain Lafourcade <romainlafourcade@gmail.com> " Last Change: 2021 July 21 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "spectral" -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=spectral\ lint\ %\ -f\ text CompilerSet errorformat=%f:%l:%c\ %t%.%\\{-}\ %m diff --git a/runtime/compiler/splint.vim b/runtime/compiler/splint.vim index 3d7ada6d1d..e00460149c 100644 --- a/runtime/compiler/splint.vim +++ b/runtime/compiler/splint.vim @@ -3,6 +3,7 @@ " Maintainer: Ralf Wildenhues <Ralf.Wildenhues@gmx.de> " Splint Home: http://www.splint.org/ " Last Change: 2019 Jul 23 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) " $Revision: 1.3 $ if exists("current_compiler") @@ -10,10 +11,6 @@ if exists("current_compiler") endif let current_compiler = "splint" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo-=C diff --git a/runtime/compiler/standard.vim b/runtime/compiler/standard.vim index 50b7c97ca7..fdc79ae2ba 100644 --- a/runtime/compiler/standard.vim +++ b/runtime/compiler/standard.vim @@ -2,15 +2,12 @@ " Compiler: Standard for JavaScript " Maintainer: Romain Lafourcade <romainlafourcade@gmail.com> " Last Change: 2020 August 20 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "standard" -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=npx\ standard CompilerSet errorformat=%f:%l:%c:\ %m,%-G%.%# diff --git a/runtime/compiler/stylelint.vim b/runtime/compiler/stylelint.vim index 784a61d46a..22acea9eb0 100644 --- a/runtime/compiler/stylelint.vim +++ b/runtime/compiler/stylelint.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Stylelint " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Jun 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "stylelint" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/tcl.vim b/runtime/compiler/tcl.vim index 81af185f5e..1e6d1f39e1 100644 --- a/runtime/compiler/tcl.vim +++ b/runtime/compiler/tcl.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: tcl " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2004 Nov 27 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "tcl" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=tcl CompilerSet errorformat=%EError:\ %m,%+Z\ %\\{4}(file\ \"%f\"\ line\ %l),%-G%.%# diff --git a/runtime/compiler/tex.vim b/runtime/compiler/tex.vim index 65e15cf6e2..282b3a0588 100644 --- a/runtime/compiler/tex.vim +++ b/runtime/compiler/tex.vim @@ -1,7 +1,10 @@ " Vim compiler file " Compiler: TeX " Maintainer: Artem Chuprina <ran@ran.pp.ru> -" Last Change: 2012 Apr 30 +" Contributors: Enno Nagel +" Last Change: 2024 Mar 29 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) +" 2024 Apr 05 by The Vim Project (avoid leaving behind g:makeprg) if exists("current_compiler") finish @@ -9,10 +12,6 @@ endif let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - " If makefile exists and we are not asked to ignore it, we use standard make " (do not redefine makeprg) if exists('b:tex_ignore_makefile') || exists('g:tex_ignore_makefile') || @@ -27,7 +26,8 @@ if exists('b:tex_ignore_makefile') || exists('g:tex_ignore_makefile') || else let current_compiler = "latex" endif - let &l:makeprg=current_compiler.' -interaction=nonstopmode' + let s:makeprg=current_compiler .. ' -interaction=nonstopmode' + execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' ') else let current_compiler = 'make' endif diff --git a/runtime/compiler/tidy.vim b/runtime/compiler/tidy.vim index 3f8e68ebf2..9e72207862 100644 --- a/runtime/compiler/tidy.vim +++ b/runtime/compiler/tidy.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: HTML Tidy " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Sep 4 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "tidy" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/ts-node.vim b/runtime/compiler/ts-node.vim index 14f0ea790c..1b46d3bf5a 100644 --- a/runtime/compiler/ts-node.vim +++ b/runtime/compiler/ts-node.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: TypeScript Runner " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Feb 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "node" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/tsc.vim b/runtime/compiler/tsc.vim index a246fc7751..76f525baa6 100644 --- a/runtime/compiler/tsc.vim +++ b/runtime/compiler/tsc.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: TypeScript Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Feb 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "tsc" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/typedoc.vim b/runtime/compiler/typedoc.vim index 3be06f3d4e..7a152e85fd 100644 --- a/runtime/compiler/typedoc.vim +++ b/runtime/compiler/typedoc.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: TypeDoc " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Feb 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "typedoc" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/vimdoc.vim b/runtime/compiler/vimdoc.vim new file mode 100644 index 0000000000..a30355f855 --- /dev/null +++ b/runtime/compiler/vimdoc.vim @@ -0,0 +1,20 @@ +" Vim Compiler File +" Language: vimdoc +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Latest Revision: 2024-04-13 +" +" If you can not find 'vimdoc' in the package manager of your distribution e.g +" 'pip', then you may need to build it from its source. + +if exists('b:current_compiler') + finish +endif +let b:current_compiler = 'vimdoc' + +let s:save_cpoptions = &cpoptions +set cpoptions&vim + +CompilerSet makeprg=vimdoc + +let &cpoptions = s:save_cpoptions +unlet s:save_cpoptions diff --git a/runtime/compiler/xbuild.vim b/runtime/compiler/xbuild.vim index b508a4616a..42c7c65f53 100644 --- a/runtime/compiler/xbuild.vim +++ b/runtime/compiler/xbuild.vim @@ -2,6 +2,7 @@ " Compiler: Mono C# " Maintainer: Chiel ten Brinke (ctje92@gmail.com) " Last Change: 2013 May 13 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -11,10 +12,6 @@ let current_compiler = "xbuild" let s:keepcpo= &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet errorformat=\ %#%f(%l\\\,%c):\ %m CompilerSet makeprg=xbuild\ /nologo\ /v:q\ /property:GenerateFullPaths=true diff --git a/runtime/compiler/xmllint.vim b/runtime/compiler/xmllint.vim index 79d38b4d14..16eebb45ce 100644 --- a/runtime/compiler/xmllint.vim +++ b/runtime/compiler/xmllint.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Libxml2 Command-Line Tool " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Jul 30 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "xmllint" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/xmlwf.vim b/runtime/compiler/xmlwf.vim index 3de9d08d55..2e38ca4aba 100644 --- a/runtime/compiler/xmlwf.vim +++ b/runtime/compiler/xmlwf.vim @@ -2,6 +2,7 @@ " Compiler: xmlwf " Maintainer: Robert Rowsome <rowsome@wam.umd.edu> " Last Change: 2019 Jul 23 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish @@ -11,10 +12,6 @@ let current_compiler = "xmlwf" let s:cpo_save = &cpo set cpo&vim -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=xmlwf\ %:S CompilerSet errorformat=%f:%l%c:%m diff --git a/runtime/compiler/xo.vim b/runtime/compiler/xo.vim index 525657d4bb..74804ca6d0 100644 --- a/runtime/compiler/xo.vim +++ b/runtime/compiler/xo.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: XO " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Jul 10 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "xo" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/compiler/yamllint.vim b/runtime/compiler/yamllint.vim index 889b04b63c..88e2efb27a 100644 --- a/runtime/compiler/yamllint.vim +++ b/runtime/compiler/yamllint.vim @@ -2,15 +2,12 @@ " Compiler: Yamllint for YAML " Maintainer: Romain Lafourcade <romainlafourcade@gmail.com> " Last Change: 2021 July 21 +" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) if exists("current_compiler") finish endif let current_compiler = "yamllint" -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - CompilerSet makeprg=yamllint\ -f\ parsable diff --git a/runtime/compiler/zig.vim b/runtime/compiler/zig.vim index 2cc6831329..44014a3775 100644 --- a/runtime/compiler/zig.vim +++ b/runtime/compiler/zig.vim @@ -10,10 +10,6 @@ let current_compiler = "zig" let s:save_cpo = &cpo set cpo&vim -if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> -endif - " a subcommand must be provided for the this compiler (test, build-exe, etc) if has('patch-7.4.191') CompilerSet makeprg=zig\ \$*\ \%:S diff --git a/runtime/compiler/zig_build.vim b/runtime/compiler/zig_build.vim index 0441267b64..5a61c9f423 100644 --- a/runtime/compiler/zig_build.vim +++ b/runtime/compiler/zig_build.vim @@ -1,6 +1,7 @@ " Vim compiler file " Compiler: Zig Compiler (zig build) " Upstream: https://github.com/ziglang/zig.vim +" Last Change: 2024 Apr 05 by The Vim Project (removed :CompilerSet definition) if exists('current_compiler') finish @@ -11,11 +12,6 @@ let current_compiler = 'zig_build' let s:save_cpo = &cpo set cpo&vim - -if exists(':CompilerSet') != 2 - command -nargs=* CompilerSet setlocal <args> -endif - if exists('g:zig_build_makeprg_params') execute 'CompilerSet makeprg=zig\ build\ '.escape(g:zig_build_makeprg_params, ' \|"').'\ $*' else diff --git a/runtime/compiler/zig_build_exe.vim b/runtime/compiler/zig_build_exe.vim index 20f0bb3366..259d0e267b 100644 --- a/runtime/compiler/zig_build_exe.vim +++ b/runtime/compiler/zig_build_exe.vim @@ -1,6 +1,7 @@ " Vim compiler file " Compiler: Zig Compiler (zig build-exe) " Upstream: https://github.com/ziglang/zig.vim +" Last Change: 2024 Apr 05 by The Vim Project (removed :CompilerSet definition) if exists('current_compiler') finish @@ -11,11 +12,6 @@ let current_compiler = 'zig_build_exe' let s:save_cpo = &cpo set cpo&vim - -if exists(':CompilerSet') != 2 - command -nargs=* CompilerSet setlocal <args> -endif - if has('patch-7.4.191') CompilerSet makeprg=zig\ build-exe\ \%:S\ \$* else diff --git a/runtime/compiler/zig_test.vim b/runtime/compiler/zig_test.vim index a82d2a6378..dafeb6f1e3 100644 --- a/runtime/compiler/zig_test.vim +++ b/runtime/compiler/zig_test.vim @@ -1,6 +1,7 @@ " Vim compiler file " Compiler: Zig Compiler (zig test) " Upstream: https://github.com/ziglang/zig.vim +" Last Change: 2024 Apr 05 by The Vim Project (removed :CompilerSet definition) if exists('current_compiler') finish @@ -11,11 +12,6 @@ let current_compiler = 'zig_test' let s:save_cpo = &cpo set cpo&vim - -if exists(':CompilerSet') != 2 - command -nargs=* CompilerSet setlocal <args> -endif - if has('patch-7.4.191') CompilerSet makeprg=zig\ test\ \%:S\ \$* else diff --git a/runtime/compiler/zsh.vim b/runtime/compiler/zsh.vim index 5703c1fc44..bd195582c1 100644 --- a/runtime/compiler/zsh.vim +++ b/runtime/compiler/zsh.vim @@ -1,17 +1,13 @@ " Vim compiler file " Compiler: Zsh " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Sep 6 +" Last Change: 2024 Apr 03 if exists("current_compiler") finish endif let current_compiler = "zsh" -if exists(":CompilerSet") != 2 " older Vim always used :setlocal - command -nargs=* CompilerSet setlocal <args> -endif - let s:cpo_save = &cpo set cpo&vim diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 87269ad147..2aa147770d 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -112,7 +112,11 @@ Basic types ~ Dictionary (msgpack: map) Object < - Note: empty Array is accepted as a valid argument for Dictionary parameter. + Note: + - Empty Array is accepted as a valid Dictionary parameter. + - Functions cannot cross RPC boundaries. But API functions (e.g. + |nvim_create_autocmd()|) may support Lua function parameters for non-RPC + invocations. Special types (msgpack EXT) ~ @@ -504,8 +508,9 @@ Extended marks (extmarks) represent buffer annotations that track text changes in the buffer. They can represent cursors, folds, misspelled words, anything that needs to track a logical location in the buffer over time. |api-indexing| -Extmark position works like "bar" cursor: it exists between characters. Thus, -the maximum extmark index on a line is 1 more than the character index: > +Extmark position works like a "vertical bar" cursor: it exists between +characters. Thus, the maximum extmark index on a line is 1 more than the +character index: > f o o b a r line contents 0 1 2 3 4 5 character positions (0-based) @@ -579,109 +584,6 @@ created for extmark changes. ============================================================================== Global Functions *api-global* -nvim__get_runtime({pat}, {all}, {opts}) *nvim__get_runtime()* - Find files in runtime directories - - Attributes: ~ - |api-fast| - - Parameters: ~ - • {pat} pattern of files to search for - • {all} whether to return all matches or only the first - • {opts} is_lua: only search Lua subdirs - - Return: ~ - list of absolute paths to the found files - -nvim__id({obj}) *nvim__id()* - Returns object given as argument. - - This API function is used for testing. One should not rely on its presence - in plugins. - - Parameters: ~ - • {obj} Object to return. - - Return: ~ - its argument. - -nvim__id_array({arr}) *nvim__id_array()* - Returns array given as argument. - - This API function is used for testing. One should not rely on its presence - in plugins. - - Parameters: ~ - • {arr} Array to return. - - Return: ~ - its argument. - -nvim__id_dictionary({dct}) *nvim__id_dictionary()* - Returns dictionary given as argument. - - This API function is used for testing. One should not rely on its presence - in plugins. - - Parameters: ~ - • {dct} Dictionary to return. - - Return: ~ - its argument. - -nvim__id_float({flt}) *nvim__id_float()* - Returns floating-point value given as argument. - - This API function is used for testing. One should not rely on its presence - in plugins. - - Parameters: ~ - • {flt} Value to return. - - Return: ~ - its argument. - -nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* - NB: if your UI doesn't use hlstate, this will not return hlstate first - time. - -nvim__invalidate_glyph_cache() *nvim__invalidate_glyph_cache()* - For testing. The condition in schar_cache_clear_if_full is hard to reach, - so this function can be used to force a cache clear in a test. - -nvim__stats() *nvim__stats()* - Gets internal stats. - - Return: ~ - Map of various internal stats. - -nvim_call_atomic({calls}) *nvim_call_atomic()* - Calls many API methods atomically. - - This has two main usages: - 1. To perform several requests from an async context atomically, i.e. - without interleaving redraws, RPC requests from other clients, or user - interactions (however API methods may trigger autocommands or event - processing which have such side effects, e.g. |:sleep| may wake - timers). - 2. To minimize RPC overhead (roundtrips) of a sequence of many requests. - - Attributes: ~ - |RPC| only - - Parameters: ~ - • {calls} an array of calls, where each call is described by an array - with two elements: the request name, and an array of - arguments. - - Return: ~ - Array of two elements. The first is an array of return values. The - second is NIL if all calls succeeded. If a call resulted in an error, - it is a three-element array with the zero-based index of the call - which resulted in an error, the error type and the error message. If - an error occurred, the values from all preceding calls will still be - returned. - nvim_chan_send({chan}, {data}) *nvim_chan_send()* Send data to channel `id`. For a job, it writes it to the stdin of the process. For the stdio channel |channel-stdio|, it writes to Nvim's @@ -700,21 +602,6 @@ nvim_chan_send({chan}, {data}) *nvim_chan_send()* • {chan} id of the channel • {data} data to write. 8-bit clean: can contain NUL bytes. -nvim_complete_set({index}, {opts}) *nvim_complete_set()* - Set info for the completion candidate index. if the info was shown in a - window, then the window and buffer ids are returned for further - customization. If the text was not shown, an empty dict is returned. - - Parameters: ~ - • {index} the completion candidate index - • {opts} Optional parameters. - • info: (string) info text. - - Return: ~ - Dictionary containing these keys: - • winid: (number) floating window id - • bufnr: (number) buffer id in floating window - nvim_create_buf({listed}, {scratch}) *nvim_create_buf()* Creates a new, empty, unnamed buffer. @@ -1574,23 +1461,127 @@ nvim_strwidth({text}) *nvim_strwidth()* Return: ~ Number of cells -nvim_subscribe({event}) *nvim_subscribe()* - Subscribes to event broadcasts. +nvim__complete_set({index}, {opts}) *nvim__complete_set()* + EXPERIMENTAL: this API may change in the future. - Attributes: ~ - |RPC| only + Sets info for the completion item at the given index. If the info text was + shown in a window, returns the window and buffer ids, or empty dict if not + shown. Parameters: ~ - • {event} Event type string + • {index} Completion candidate index + • {opts} Optional parameters. + • info: (string) info text. + + Return: ~ + Dictionary containing these keys: + • winid: (number) floating window id + • bufnr: (number) buffer id in floating window -nvim_unsubscribe({event}) *nvim_unsubscribe()* - Unsubscribes to event broadcasts. +nvim__get_runtime({pat}, {all}, {opts}) *nvim__get_runtime()* + Find files in runtime directories Attributes: ~ - |RPC| only + |api-fast| + + Parameters: ~ + • {pat} pattern of files to search for + • {all} whether to return all matches or only the first + • {opts} is_lua: only search Lua subdirs + + Return: ~ + list of absolute paths to the found files + +nvim__id({obj}) *nvim__id()* + Returns object given as argument. + + This API function is used for testing. One should not rely on its presence + in plugins. + + Parameters: ~ + • {obj} Object to return. + + Return: ~ + its argument. + +nvim__id_array({arr}) *nvim__id_array()* + Returns array given as argument. + + This API function is used for testing. One should not rely on its presence + in plugins. + + Parameters: ~ + • {arr} Array to return. + + Return: ~ + its argument. + +nvim__id_dictionary({dct}) *nvim__id_dictionary()* + Returns dictionary given as argument. + + This API function is used for testing. One should not rely on its presence + in plugins. + + Parameters: ~ + • {dct} Dictionary to return. + + Return: ~ + its argument. + +nvim__id_float({flt}) *nvim__id_float()* + Returns floating-point value given as argument. + + This API function is used for testing. One should not rely on its presence + in plugins. + + Parameters: ~ + • {flt} Value to return. + + Return: ~ + its argument. + +nvim__inspect_cell({grid}, {row}, {col}) *nvim__inspect_cell()* + NB: if your UI doesn't use hlstate, this will not return hlstate first + time. + +nvim__invalidate_glyph_cache() *nvim__invalidate_glyph_cache()* + For testing. The condition in schar_cache_clear_if_full is hard to reach, + so this function can be used to force a cache clear in a test. + +nvim__redraw({opts}) *nvim__redraw()* + EXPERIMENTAL: this API may change in the future. + + Instruct Nvim to redraw various components. Parameters: ~ - • {event} Event type string + • {opts} Optional parameters. + • win: Target a specific |window-ID| as described below. + • buf: Target a specific buffer number as described below. + • flush: Update the screen with pending updates. + • valid: When present mark `win`, `buf`, or all windows for + redraw. When `true`, only redraw changed lines (useful for + decoration providers). When `false`, forcefully redraw. + • range: Redraw a range in `buf`, the buffer in `win` or the + current buffer (useful for decoration providers). Expects a + tuple `[first, last]` with the first and last line number of + the range, 0-based end-exclusive |api-indexing|. + • cursor: Immediately update cursor position on the screen in + `win` or the current window. + • statuscolumn: Redraw the 'statuscolumn' in `buf`, `win` or + all windows. + • statusline: Redraw the 'statusline' in `buf`, `win` or all + windows. + • winbar: Redraw the 'winbar' in `buf`, `win` or all windows. + • tabline: Redraw the 'tabline'. + + See also: ~ + • |:redraw| + +nvim__stats() *nvim__stats()* + Gets internal stats. + + Return: ~ + Map of various internal stats. ============================================================================== @@ -2447,7 +2438,7 @@ nvim_buf_set_mark({buffer}, {name}, {line}, {col}, {opts}) • |nvim_buf_get_mark()| nvim_buf_set_name({buffer}, {name}) *nvim_buf_set_name()* - Sets the full file name for a buffer + Sets the full file name for a buffer, like |:file_f| Parameters: ~ • {buffer} Buffer handle, or 0 for current buffer @@ -2767,8 +2758,8 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) • url: A URL to associate with this extmark. In the TUI, the OSC 8 control sequence is used to generate a clickable hyperlink to this URL. - • scoped: boolean that indicates that the extmark should - only be displayed in the namespace scope. (experimental) + • scoped: boolean (EXPERIMENTAL) enables "scoping" for the + extmark. See |nvim__win_add_ns()| Return: ~ Id of the created/updated extmark @@ -2850,34 +2841,41 @@ nvim_set_decoration_provider({ns_id}, {opts}) ["end", tick] < -nvim_win_add_ns({window}, {ns_id}) *nvim_win_add_ns()* - Adds the namespace scope to the window. +nvim__win_add_ns({window}, {ns_id}) *nvim__win_add_ns()* + EXPERIMENTAL: this API will change in the future. + + Scopes a namespace to the a window, so extmarks in the namespace will be + active only in the given window. Parameters: ~ • {window} Window handle, or 0 for current window - • {ns_id} the namespace to add + • {ns_id} Namespace Return: ~ true if the namespace was added, else false -nvim_win_get_ns({window}) *nvim_win_get_ns()* - Gets all the namespaces scopes associated with a window. +nvim__win_del_ns({window}, {ns_id}) *nvim__win_del_ns()* + EXPERIMENTAL: this API will change in the future. + + Unscopes a namespace (un-binds it from the given scope). Parameters: ~ • {window} Window handle, or 0 for current window + • {ns_id} the namespace to remove Return: ~ - a list of namespaces ids + true if the namespace was removed, else false + +nvim__win_get_ns({window}) *nvim__win_get_ns()* + EXPERIMENTAL: this API will change in the future. -nvim_win_remove_ns({window}, {ns_id}) *nvim_win_remove_ns()* - Removes the namespace scope from the window. + Gets the namespace scopes for a given window. Parameters: ~ • {window} Window handle, or 0 for current window - • {ns_id} the namespace to remove Return: ~ - true if the namespace was removed, else false + a list of namespaces ids ============================================================================== @@ -3271,9 +3269,8 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* • footer_pos: Footer position. Must be set with `footer` option. Value can be one of "left", "center", or "right". Default is `"left"`. - • noautocmd: If true then no buffer-related autocommand - events such as |BufEnter|, |BufLeave| or |BufWinEnter| may - fire from calling this function. + • noautocmd: If true then all autocommands are blocked for + the duration of the call. • fixed: If true when anchor is NW or SW, the float window would be kept fixed even if the window would be truncated. • hide: If true the floating window will be hidden. @@ -3297,11 +3294,11 @@ nvim_win_get_config({window}) *nvim_win_get_config()* Map defining the window configuration, see |nvim_open_win()| nvim_win_set_config({window}, {config}) *nvim_win_set_config()* - Configures window layout. Currently only for floating and external windows - (including changing a split window to those layouts). + Configures window layout. Cannot be used to move the last window in a + tabpage to a different one. - When reconfiguring a floating window, absent option keys will not be - changed. `row`/`col` and `relative` must be reconfigured together. + When reconfiguring a window, absent option keys will not be changed. + `row`/`col` and `relative` must be reconfigured together. Parameters: ~ • {window} Window handle, or 0 for current window @@ -3475,7 +3472,8 @@ nvim_create_autocmd({event}, {opts}) *nvim_create_autocmd()* Vimscript function name, if string) called when the event(s) is triggered. Lua callback can return a truthy value (not `false` or `nil`) to delete the autocommand. - Receives a table argument with these keys: + Receives one argument, a table with these keys: + *event-args* • id: (number) autocommand id • event: (string) name of the triggered event |autocmd-events| @@ -3484,7 +3482,7 @@ nvim_create_autocmd({event}, {opts}) *nvim_create_autocmd()* • buf: (number) expanded value of <abuf> • file: (string) expanded value of <afile> • data: (any) arbitrary data passed from - |nvim_exec_autocmds()| + |nvim_exec_autocmds()| *event-data* • command (string) optional: Vim command to execute on event. Cannot be used with {callback} • once (boolean) optional: defaults to false. Run the diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 8890872e1a..ca816851dd 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -466,6 +466,16 @@ CompleteDone After Insert mode completion is done. Either CompleteDonePre if you need it. |v:completed_item| gives the completed item. + Sets these |v:event| keys: + reason Reason for completion being + done. Can be one of: + - "accept": completion was + accepted using |complete_CTRL-Y|. + - "cancel": completion was cancelled + using |complete_CTRL-E|, pressing + a non-keyword character, or + triggering a new completion. + *CursorHold* CursorHold When the user doesn't press a key for the time specified with 'updatetime'. Not triggered @@ -884,6 +894,9 @@ SafeState When nothing is pending, going to wait for the *SessionLoadPost* SessionLoadPost After loading the session file created using the |:mksession| command. + *SessionWritePost* +SessionWritePost After writing a session file by calling + the |:mksession| command. *ShellCmdPost* ShellCmdPost After executing a shell command with |:!cmd|, |:make| and |:grep|. Can be used to check for @@ -900,18 +913,18 @@ ShellFilterPost After executing a shell command with ":{range}!cmd", ":w !cmd" or ":r !cmd". Can be used to check for any changed files. *SourcePre* -SourcePre Before sourcing a vim/lua file. |:source| +SourcePre Before sourcing a Vimscript/Lua file. |:source| <afile> is the name of the file being sourced. *SourcePost* -SourcePost After sourcing a vim/lua file. |:source| +SourcePost After sourcing a Vimscript/Lua file. |:source| <afile> is the name of the file being sourced. Not triggered when sourcing was interrupted. Also triggered after a SourceCmd autocommand was triggered. *SourceCmd* -SourceCmd When sourcing a vim/lua file. |:source| +SourceCmd When sourcing a Vimscript/Lua file. |:source| <afile> is the name of the file being sourced. - The autocommand must source this file. + The autocommand must source that file. |Cmd-event| *SpellFileMissing* SpellFileMissing When trying to load a spell checking file and @@ -986,18 +999,16 @@ TermClose When a |terminal| job ends. Sets these |v:event| keys: status *TermRequest* -TermRequest When a |terminal| job emits an OSC or DCS - sequence. Sets |v:termrequest|. When used from - Lua, the request string is included in the - "data" field of the autocommand callback. +TermRequest When a |:terminal| child process emits an OSC + or DCS sequence. Sets |v:termrequest|. The + |event-data| is the request string. *TermResponse* TermResponse When Nvim receives an OSC or DCS response from - the terminal. Sets |v:termresponse|. When used - from Lua, the response string is included in - the "data" field of the autocommand callback. - May be triggered halfway through another event - (file I/O, a shell command, or anything else - that takes time). Example: >lua + the host terminal. Sets |v:termresponse|. The + |event-data| is the response string. May be + triggered during another event (file I/O, + a shell command, or anything else that takes + time). Example: >lua -- Query the terminal palette for the RGB value of color 1 -- (red) using OSC 4 diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index c5f3946871..ff7d5f9ce8 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1268,7 +1268,6 @@ executable({expr}) *executable()* The result is a Number: 1 exists 0 does not exist - -1 not implemented on this system |exepath()| can be used to get the full path of an executable. execute({command} [, {silent}]) *execute()* @@ -1588,6 +1587,7 @@ feedkeys({string} [, {mode}]) *feedkeys()* 't' Handle keys as if typed; otherwise they are handled as if coming from a mapping. This matters for undo, opening folds, etc. + 'L' Lowlevel input. Other flags are not used. 'i' Insert the string instead of appending (see above). 'x' Execute commands until typeahead is empty. This is similar to using ":normal!". You can call feedkeys() @@ -2196,16 +2196,16 @@ getchangelist([{buf}]) *getchangelist()* position refers to the position in the list. For other buffers, it is set to the length of the list. -getchar([expr]) *getchar()* +getchar([{expr}]) *getchar()* Get a single character from the user or input stream. - If [expr] is omitted, wait until a character is available. - If [expr] is 0, only get a character when one is available. + If {expr} is omitted, wait until a character is available. + If {expr} is 0, only get a character when one is available. Return zero otherwise. - If [expr] is 1, only check if a character is available, it is + If {expr} is 1, only check if a character is available, it is not consumed. Return zero if no character available. If you prefer always getting a string use |getcharstr()|. - Without [expr] and when [expr] is 0 a whole character or + Without {expr} and when {expr} is 0 a whole character or special key is returned. If it is a single character, the result is a Number. Use |nr2char()| to convert it to a String. Otherwise a String is returned with the encoded character. @@ -2215,11 +2215,11 @@ getchar([expr]) *getchar()* also a String when a modifier (shift, control, alt) was used that is not included in the character. - When [expr] is 0 and Esc is typed, there will be a short delay + When {expr} is 0 and Esc is typed, there will be a short delay while Vim waits to see if this is the start of an escape sequence. - When [expr] is 1 only the first byte is returned. For a + When {expr} is 1 only the first byte is returned. For a one-byte character it is the character itself as a number. Use nr2char() to convert it to a String. @@ -2311,13 +2311,13 @@ getcharsearch() *getcharsearch()* nnoremap <expr> , getcharsearch().forward ? ',' : ';' < Also see |setcharsearch()|. -getcharstr([expr]) *getcharstr()* +getcharstr([{expr}]) *getcharstr()* Get a single character from the user or input stream as a string. - If [expr] is omitted, wait until a character is available. - If [expr] is 0 or false, only get a character when one is + If {expr} is omitted, wait until a character is available. + If {expr} is 0 or false, only get a character when one is available. Return an empty string otherwise. - If [expr] is 1 or true, only check if a character is + If {expr} is 1 or true, only check if a character is available, it is not consumed. Return an empty string if no character is available. Otherwise this works like |getchar()|, except that a number @@ -2969,6 +2969,38 @@ getregion({pos1}, {pos2} [, {opts}]) *getregion()* \ getpos('v'), getpos('.'), #{ type: mode() })<CR> < +getregionpos({pos1}, {pos2} [, {opts}]) *getregionpos()* + Same as |getregion()|, but returns a list of positions + describing the buffer text segments bound by {pos1} and + {pos2}. + The segments are a pair of positions for every line: > + [[{start_pos}, {end_pos}], ...] +< + The position is a |List| with four numbers: + [bufnum, lnum, col, off] + "bufnum" is the buffer number. + "lnum" and "col" are the position in the buffer. The first + column is 1. + If the "off" number of a starting position is non-zero, it is + the offset in screen columns from the start of the character. + E.g., a position within a <Tab> or after the last character. + If the "off" number of an ending position is non-zero, it is + the offset of the character's first cell not included in the + selection, otherwise all its cells are included. + + Apart from the options supported by |getregion()|, {opts} also + supports the following: + + eol If |TRUE|, indicate positions beyond + the end of a line with "col" values + one more than the length of the line. + If |FALSE|, positions are limited + within their lines, and if a line is + empty or the selection is entirely + beyond the end of a line, a "col" + value of 0 is used for both positions. + (default: |FALSE|) + getregtype([{regname}]) *getregtype()* The result is a String, which is type of register {regname}. The value will be one of: @@ -3014,7 +3046,7 @@ getscriptinfo([{opts}]) *getscriptinfo()* Examples: >vim echo getscriptinfo({'name': 'myscript'}) - echo getscriptinfo({'sid': 15}).variables + echo getscriptinfo({'sid': 15})[0].variables < gettabinfo([{tabnr}]) *gettabinfo()* @@ -4981,9 +5013,9 @@ mkdir({name} [, {flags} [, {prot}]]) *mkdir()* *E73 successful or FALSE if the directory creation failed or partly failed. -mode([expr]) *mode()* +mode([{expr}]) *mode()* Return a string that indicates the current mode. - If [expr] is supplied and it evaluates to a non-zero Number or + If {expr} is supplied and it evaluates to a non-zero Number or a non-empty String (|non-zero-arg|), then the full mode is returned, otherwise only the first letter is returned. Also see |state()|. @@ -5486,6 +5518,9 @@ printf({fmt}, {expr1} ...) *printf()* echo printf("%1$*2$.*3$f", 1.4142135, 6, 2) < 1.41 + You will get an overflow error |E1510|, when the field-width + or precision will result in a string longer than 6400 chars. + *E1500* You cannot mix positional and non-positional arguments: >vim echo printf("%s%1$s", "One", "Two") @@ -6054,6 +6089,7 @@ search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]]) *search()* When a match has been found its line number is returned. If there is no match a 0 is returned and the cursor doesn't move. No error message is given. + To get the matched string, use |matchbufline()|. {flags} is a String, which can contain these character flags: 'b' search Backward instead of forward @@ -6958,10 +6994,11 @@ shellescape({string} [, {special}]) *shellescape()* Otherwise encloses {string} in single-quotes and replaces all "'" with "'\''". - If {special} is a |non-zero-arg|: - - Special items such as "!", "%", "#" and "<cword>" will be - preceded by a backslash. The backslash will be removed again - by the |:!| command. + The {special} argument adds additional escaping of keywords + used in Vim commands. If it is a |non-zero-arg|: + - Special items such as "!", "%", "#" and "<cword>" (as listed + in |expand()|) will be preceded by a backslash. + The backslash will be removed again by the |:!| command. - The <NL> character is escaped. If 'shell' contains "csh" in the tail: @@ -7407,7 +7444,8 @@ slice({expr}, {start} [, {end}]) *slice()* Similar to using a |slice| "expr[start : end]", but "end" is used exclusive. And for a string the indexes are used as character indexes instead of byte indexes. - Also, composing characters are not counted. + Also, composing characters are treated as a part of the + preceding base character. When {end} is omitted the slice continues to the last item. When {end} is -1 the last item is omitted. Returns an empty value if {start} or {end} are invalid. @@ -7754,8 +7792,8 @@ strcharpart({src}, {start} [, {len} [, {skipcc}]]) *strcharpart()* of byte index and length. When {skipcc} is omitted or zero, composing characters are counted separately. - When {skipcc} set to 1, Composing characters are ignored, - similar to |slice()|. + When {skipcc} set to 1, composing characters are treated as a + part of the preceding base character, similar to |slice()|. When a character index is used where a character does not exist it is omitted and counted as one character. For example: >vim @@ -7769,7 +7807,7 @@ strchars({string} [, {skipcc}]) *strchars()* in String {string}. When {skipcc} is omitted or zero, composing characters are counted separately. - When {skipcc} set to 1, Composing characters are ignored. + When {skipcc} set to 1, composing characters are ignored. |strcharlen()| always does this. Returns zero on error. @@ -7871,10 +7909,10 @@ string({expr}) *string()* for infinite and NaN floating-point values representations which use |str2float()|. Strings are also dumped literally, only single quote is escaped, which does not allow using YAML - for parsing back binary strings. |eval()| should always work for - strings and floats though and this is the only official - method, use |msgpackdump()| or |json_encode()| if you need to - share data with other application. + for parsing back binary strings. |eval()| should always work + for strings and floats though, and this is the only official + method. Use |msgpackdump()| or |json_encode()| if you need to + share data with other applications. strlen({string}) *strlen()* The result is a Number, which is the length of the String @@ -8203,6 +8241,10 @@ synconcealed({lnum}, {col}) *synconcealed()* synconcealed(lnum, 5) [1, 'X', 2] synconcealed(lnum, 6) [0, '', 0] + Note: Doesn't consider |matchadd()| highlighting items, + since syntax and matching highlighting are two different + mechanisms |syntax-vs-match|. + synstack({lnum}, {col}) *synstack()* Return a |List|, which is the stack of syntax items at the position {lnum} and {col} in the current window. {lnum} is @@ -8897,14 +8939,13 @@ win_screenpos({nr}) *win_screenpos()* [1, 1], unless there is a tabline, then it is [2, 1]. {nr} can be the window number or the |window-ID|. Use zero for the current window. - Returns [0, 0] if the window cannot be found in the current - tabpage. + Returns [0, 0] if the window cannot be found. win_splitmove({nr}, {target} [, {options}]) *win_splitmove()* - Move the window {nr} to a new split of the window {target}. - This is similar to moving to {target}, creating a new window - using |:split| but having the same contents as window {nr}, and - then closing {nr}. + Temporarily switch to window {target}, then move window {nr} + to a new split adjacent to {target}. + Unlike commands such as |:split|, no new windows are created + (the |window-ID| of window {nr} is unchanged after the move). Both {nr} and {target} can be window numbers or |window-ID|s. Both must be in the current tab page. @@ -9007,7 +9048,9 @@ winnr([{arg}]) *winnr()* # the number of the last accessed window (where |CTRL-W_p| goes to). If there is no previous window or it is in another tab page 0 is - returned. + returned. May refer to the current window in + some cases (e.g. when evaluating 'statusline' + expressions). {N}j the number of the Nth window below the current window (where |CTRL-W_j| goes to). {N}k the number of the Nth window above the current diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 703addf51a..9ff16165d7 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -281,6 +281,13 @@ gr{char} Replace the virtual characters under the cursor with that have a special meaning in Insert mode, such as most CTRL-keys, cannot be used. + *gr-default* + Nvim creates default mappings with "gr" as a prefix, + which may inhibit the behavior of |gr|. Use the + following to restore the builtin behavior: > + nnoremap <nowait> gr gr +< + *digraph-arg* The argument for Normal mode commands like |r| and |t| is a single character. When 'cpo' doesn't contain the 'D' flag, this character can also be entered @@ -1291,8 +1298,8 @@ expression (like with the "/" command). The expression must evaluate to a String. A Number is always automatically converted to a String. For the "p" and ":put" command, if the result is a Float it's converted into a String. If the result is a List each element is -turned into a String and used as a line. A Dictionary or FuncRef results in -an error message (use string() to convert). +turned into a String and used as a line. A Dictionary is converted into a +String. A Funcref results in an error message (use string() to convert). If the "= register is used for the "p" command, the String is split up at <NL> characters. If the String ends in a <NL>, it is regarded as a linewise diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 57a4223c53..f306067a9c 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -274,14 +274,6 @@ CTRL-P command-line completion (see |cmdline-completion|) CTRL-A command-line completion (see |cmdline-completion|) CTRL-L command-line completion (see |cmdline-completion|) - *c_CTRL-_* -CTRL-_ Switch between Hebrew and English keyboard mode, which is - private to the command-line and not related to hkmap. - This is useful when Hebrew text entry is required in the - command-line, searches, abbreviations, etc. Applies only if - the 'allowrevins' option is set. - See |rileft.txt|. - *c_CTRL-^* CTRL-^ Toggle the use of language |:lmap| mappings and/or Input Method. diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 5ac4ad4ce2..646ba72bd8 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -12,21 +12,112 @@ They should not be used in new scripts, and old scripts should be updated. ============================================================================== Deprecated features +------------------------------------------------------------------------------ +DEPRECATED IN 0.11 *deprecated-0.11* + +API +- nvim_subscribe() Plugins must maintain their own "multicast" channels list. +- nvim_unsubscribe() Plugins must maintain their own "multicast" channels list. + + +------------------------------------------------------------------------------ +DEPRECATED IN 0.10 *deprecated-0.10* + +API +• *nvim_buf_get_option()* Use |nvim_get_option_value()| instead. +• *nvim_buf_set_option()* Use |nvim_set_option_value()| instead. +• *nvim_call_atomic()* Use |nvim_exec_lua()| instead. +• *nvim_get_option()* Use |nvim_get_option_value()| instead. +• *nvim_set_option()* Use |nvim_set_option_value()| instead. +• *nvim_win_get_option()* Use |nvim_get_option_value()| instead. +• *nvim_win_set_option()* Use |nvim_set_option_value()| instead. + +CHECKHEALTH +• *health#report_error* *vim.health.report_error()* Use |vim.health.error()| instead. +• *health#report_info* *vim.health.report_info()* Use |vim.health.info()| instead. +• *health#report_ok* *vim.health.report_ok()* Use |vim.health.ok()| instead. +• *health#report_start* *vim.health.report_start()* Use |vim.health.start()| instead. +• *health#report_warn* *vim.health.report_warn()* Use |vim.health.warn()| instead. + +DIAGNOSTICS +• Configuring |diagnostic-signs| using |:sign-define| or |sign_define()|. Use + the "signs" key of |vim.diagnostic.config()| instead. +• vim.diagnostic functions: + • *vim.diagnostic.disable()* Use |vim.diagnostic.enable()| + • *vim.diagnostic.is_disabled()* Use |vim.diagnostic.is_enabled()| + • Legacy signature: `vim.diagnostic.enable(buf:number, namespace:number)` + +LSP +• *vim.lsp.util.get_progress_messages()* Use |vim.lsp.status()| instead. +• *vim.lsp.get_active_clients()* Use |vim.lsp.get_clients()| instead. +• *vim.lsp.for_each_buffer_client()* Use |vim.lsp.get_clients()| instead. +• *vim.lsp.util.trim_empty_lines()* Use |vim.split()| with `trimempty` instead. +• *vim.lsp.util.try_trim_markdown_code_blocks()* +• *vim.lsp.util.set_lines()* +• *vim.lsp.util.extract_completion_items()* +• *vim.lsp.util.parse_snippet()* +• *vim.lsp.util.text_document_completion_list_to_complete_items()* +• *vim.lsp.util.lookup_section()* Use |vim.tbl_get()| instead: > + local keys = vim.split(section, '.', { plain = true }) + local vim.tbl_get(table, unpack(keys)) + +LUA +• *vim.loop* Use |vim.uv| instead. +• *vim.tbl_add_reverse_lookup()* +• *vim.tbl_flatten()* Use |Iter:flatten()| instead. +• *vim.tbl_islist()* Use |vim.islist()| instead. + +OPTIONS +• The "term_background" UI option |ui-ext-options| is deprecated and no longer + populated. Background color detection is now performed in Lua by the Nvim + core, not the TUI. + +TREESITTER +• *LanguageTree:for_each_child()* Use |LanguageTree:children()| (non-recursive) instead. + + +------------------------------------------------------------------------------ +DEPRECATED IN 0.9 *deprecated-0.9* + +API +- *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead. +- *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead. + +TREESITTER +- *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()| instead. +- *vim.treesitter.get_node_at_pos()* Use |vim.treesitter.get_node()| instead. +- *vim.treesitter.get_node_at_cursor()* Use |vim.treesitter.get_node()| + and |TSNode:type()| instead. +• The following top level Treesitter functions have been moved: + - *vim.treesitter.inspect_language()* -> |vim.treesitter.language.inspect()| + - *vim.treesitter.get_query_files()* -> |vim.treesitter.query.get_files()| + - *vim.treesitter.set_query()* -> |vim.treesitter.query.set()| + - *vim.treesitter.query.set_query()* -> |vim.treesitter.query.set()| + - *vim.treesitter.get_query()* -> |vim.treesitter.query.get()| + - *vim.treesitter.query.get_query()* -> |vim.treesitter.query.get()| + - *vim.treesitter.parse_query()* -> |vim.treesitter.query.parse()| + - *vim.treesitter.query.parse_query()* -> |vim.treesitter.query.parse()| + - *vim.treesitter.add_predicate()* -> |vim.treesitter.query.add_predicate()| + - *vim.treesitter.add_directive()* -> |vim.treesitter.query.add_directive()| + - *vim.treesitter.list_predicates()* -> |vim.treesitter.query.list_predicates()| + - *vim.treesitter.list_directives()* -> |vim.treesitter.query.list_directives()| + - *vim.treesitter.query.get_range()* -> |vim.treesitter.get_range()| + - *vim.treesitter.query.get_node_text()* -> |vim.treesitter.get_node_text()| + +LUA + - *nvim_exec()* Use |nvim_exec2()| instead. + - *vim.pretty_print()* Use |vim.print()| instead. + + +------------------------------------------------------------------------------ +DEPRECATED IN 0.8 OR EARLIER + API - *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead. - *nvim_buf_set_virtual_text()* Use |nvim_buf_set_extmark()| instead. - *nvim_command_output()* Use |nvim_exec2()| instead. - *nvim_execute_lua()* Use |nvim_exec_lua()| instead. -- *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead. -- *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead. -- *nvim_exec()* Use |nvim_exec2()| instead. - *nvim_get_option_info()* Use |nvim_get_option_info2()| instead. -- *nvim_buf_get_option()* Use |nvim_get_option_value()| instead. -- *nvim_buf_set_option()* Use |nvim_set_option_value()| instead. -- *nvim_get_option()* Use |nvim_get_option_value()| instead. -- *nvim_set_option()* Use |nvim_set_option_value()| instead. -- *nvim_win_get_option()* Use |nvim_get_option_value()| instead. -- *nvim_win_set_option()* Use |nvim_set_option_value()| instead. COMMANDS - *:rv* *:rviminfo* Deprecated alias to |:rshada| command. @@ -60,11 +151,6 @@ FUNCTIONS - *buffer_name()* Obsolete name for |bufname()|. - *buffer_number()* Obsolete name for |bufnr()|. - *file_readable()* Obsolete name for |filereadable()|. -- *health#report_error* *vim.health.report_error()* Use |vim.health.error()| instead. -- *health#report_info* *vim.health.report_info()* Use |vim.health.info()| instead. -- *health#report_ok* *vim.health.report_ok()* Use |vim.health.ok()| instead. -- *health#report_start* *vim.health.report_start()* Use |vim.health.start()| instead. -- *health#report_warn* *vim.health.report_warn()* Use |vim.health.warn()| instead. - *highlight_exists()* Obsolete name for |hlexists()|. - *highlightID()* Obsolete name for |hlID()|. - *inputdialog()* Use |input()| instead. @@ -85,7 +171,7 @@ For each of the functions below, use the corresponding function in |vim.diagnostic.get()| instead of |vim.lsp.diagnostic.get()|. - *vim.lsp.diagnostic.clear()* Use |vim.diagnostic.hide()| instead. -- *vim.lsp.diagnostic.disable()* +- *vim.lsp.diagnostic.disable()* Use |vim.diagnostic.enable()| instead. - *vim.lsp.diagnostic.display()* Use |vim.diagnostic.show()| instead. - *vim.lsp.diagnostic.enable()* - *vim.lsp.diagnostic.get()* @@ -140,36 +226,9 @@ LSP FUNCTIONS {async=false} instead. - *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()| or |vim.lsp.buf.format()| instead. -- *vim.lsp.util.get_progress_messages()* Use |vim.lsp.status()| or access - `progress` of |vim.lsp.Client| -- *vim.lsp.get_active_clients()* Use |vim.lsp.get_clients()| -- *vim.lsp.for_each_buffer_client()* Use |vim.lsp.get_clients()| -- *vim.lsp.util.lookup_section()* Use |vim.tbl_get()| and - |vim.split()| with {plain=true} instead. -- *vim.lsp.util.trim_empty_lines()* Use |vim.split()| with `trimempty` instead. -- *vim.lsp.util.try_trim_markdown_code_blocks()* -- *vim.lsp.util.set_lines()* -- *vim.lsp.util.extract_completion_items()* -- *vim.lsp.util.parse_snippet()* -- *vim.lsp.util.text_document_completion_list_to_complete_items()* - -TREESITTER FUNCTIONS -- *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()| - instead. -- *vim.treesitter.get_node_at_pos()* Use |vim.treesitter.get_node()| - instead. -- *vim.treesitter.get_node_at_cursor()* Use |vim.treesitter.get_node()| - and |TSNode:type()| instead. -- *vim.treesitter.query.get_query()* Use |vim.treesitter.query.get()| - instead. -- *LanguageTree:for_each_child()* Use |LanguageTree:children()| - (non-recursive) instead. LUA - vim.register_keystroke_callback() Use |vim.on_key()| instead. -- *vim.pretty_print()* Use |vim.print()| instead. -- *vim.loop* Use |vim.uv| instead. -- *vim.tbl_add_reverse_lookup()* NORMAL COMMANDS - *]f* *[f* Same as "gf". @@ -211,8 +270,10 @@ UI EXTENSIONS by the Nvim core directly instead of the TUI. VARIABLES -- *b:terminal_job_pid* PID of the top-level process in a |:terminal|. - Use `jobpid(&channel)` instead. +- *b:terminal_job_pid* Use `jobpid(&channel)` instead. +- *b:terminal_job_id* Use `&channel` instead. To access in non-current buffer: + - Lua: `vim.bo[bufnr].channel` + - Vimscript: `getbufvar(bufnr, '&channel')` vim:noet:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/dev_style.txt b/runtime/doc/dev_style.txt index 85aeddd425..6c805963a9 100644 --- a/runtime/doc/dev_style.txt +++ b/runtime/doc/dev_style.txt @@ -6,7 +6,7 @@ Nvim style guide *dev-style* -This is style guide for developers working on Nvim's source code. +Style guidelines for developers working Nvim's source code. License: CC-By 3.0 https://creativecommons.org/licenses/by/3.0/ diff --git a/runtime/doc/dev_theme.txt b/runtime/doc/dev_theme.txt index 29a2da0d90..532506abcb 100644 --- a/runtime/doc/dev_theme.txt +++ b/runtime/doc/dev_theme.txt @@ -4,9 +4,9 @@ NVIM REFERENCE MANUAL -Nvim theme style guide *dev-theme* +Nvim colorscheme guidelines *dev-theme* -This is style guide for developers working on Nvim's default color scheme. +Style guidelines for developing Nvim's default colorscheme. License: CC-By 3.0 https://creativecommons.org/licenses/by/3.0/ @@ -17,19 +17,15 @@ Design - Be "Neovim branded", i.e. have mostly "green-blue" feel plus one or two colors reserved for very occasional user attention. - - Be oriented for 'termguicolors' (true colors) while being extra minimal for 'notermguicolors' (16 colors) as fallback. - - Be accessible, i.e. have high enough contrast ratio (as defined in https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef). This means to have value at least 7 for |hl-Normal| and 4.5 for some common cases (|hl-Visual|, `Comment` with set 'cursorline', colored syntax, `Diff*`, |hl-Search|). - - Be suitable for dark and light backgrounds via exchange of dark and light palettes. - - Be usable, i.e. provide enough visual feedback for common objects. @@ -38,39 +34,29 @@ Palettes - There are two separate palettes: dark and light. They all contain the same set of colors exported as `NvimDark*` and `NvimLight*` colors respectively. - - The dark palette is used for background in the dark color scheme and for foreground in the light color scheme; and vice versa. This introduces recognizable visual system without too standing out. - - Actual computation of palettes should be done in a perceptually uniform color space. Oklch is a good choice. - - Each palette has the following colors (descriptions are for dark background; reverse for light one): - - Four shades of colored "cold" greys for general UI. - - Dark ones (from darkest to lightest) are reserved as background for |hl-NormalFloat| (considered as "black"), |hl-Normal| (background), |hl-CursorLine|, |hl-Visual|. - - Light ones (also from darkest to lightest) are reserved for `Comment`, |hl-StatusLine|/|hl-TabLine|, |hl-Normal| (foreground), and color considered as "white". - - Six colors to provide enough terminal colors: red, yellow, green, cyan, blue, magenta. They should have (reasonably) similar lightness and chroma to make them visually coherent. Lightness should be as equal to the palette's basic grey (which is used for |hl-Normal|) as possible. They should have (reasonably) different hues to make them visually separable. - - For 16 colors: - - Greys are not used and are replaced with the foreground and background colors of the terminal emulator. - - Non-grey colors fall back to terminal colors as ordered in ANSI codes (https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit), that is red (1, 9), green (2, 10), yellow (3, 11), blue (4, 12), @@ -78,33 +64,25 @@ Palettes To increase contrast, colors 1-6 are used for light background and 9-14 for dark background. - ============================================================================== Highlight groups Use: - Grey shades for general UI according to their design. - - Bold text for keywords (`Statement` highlight group). This is an important choice to increase accessibility for people with color deficiencies, as it doesn't rely on actual color. - - Green for strings, |hl-DiffAdd| (as background), |hl-DiagnosticOk|, and some minor text UI elements. - - Cyan as main syntax color, i.e. for function usage (`Function` highlight group), |hl-DiffText|, |hl-DiagnosticInfo|, and some minor text UI elements. - - Red to generally mean high user attention, i.e. errors; in particular for |hl-ErrorMsg|, |hl-DiffDelete|, |hl-DiagnosticError|. - - Yellow very sparingly to mean mild user attention, i.e. warnings. That is, |hl-DiagnosticWarn| and |hl-WarningMsg|. - - Blue very sparingly as |hl-DiagnosticHint| and some additional important syntax group (like `Identifier`). - - Magenta very carefully (if at all). In case of 16 colors: @@ -113,9 +91,7 @@ In case of 16 colors: colors can be used as foreground". This means that in any foreground/background combination there should be background and one non-background color. - - Use 0 (black) or 15 (bright white) as foreground for non-grey background, depending on whether normal background is light or dark. - vim:tw=78:ts=8:et:ft=help:norl: diff --git a/runtime/doc/dev_tools.txt b/runtime/doc/dev_tools.txt index 3ee48bec90..52513db31d 100644 --- a/runtime/doc/dev_tools.txt +++ b/runtime/doc/dev_tools.txt @@ -14,10 +14,10 @@ itself. See also |debug.txt| for advice that applies to Vim. ============================================================================== Backtraces *dev-tools-backtrace* -LINUX ~ +LINUX -Core dumps are disabled by default on Ubuntu -https://stackoverflow.com/a/18368068, CentOS and others. To enable core dumps: +Core dumps are disabled by default on Ubuntu, CentOS and others. +To enable core dumps: >bash ulimit -c unlimited < @@ -25,21 +25,29 @@ On systemd-based systems getting a backtrace is as easy as: >bash coredumpctl -1 gdb < -It's an optional tool, so you may need to install it: +`coredumpctl` is an optional tool, so you may need to install it: >bash sudo apt install systemd-coredump < -The full backtrace is most useful, send us the `bt.txt` file: +The full backtrace is most useful; please send us the `backtrace.txt` file +when reporting a bug related to a crash: >bash - 2>&1 coredumpctl -1 gdb | tee -a bt.txt - thread apply all bt full + 2>&1 coredumpctl -1 gdb | tee -a backtrace.txt + (gdb) thread apply all bt full < -On older systems a `core` file will appear in the current directory. To get -a backtrace from the `core` file: + +On systems without `coredumpctl`, you may find a `core` dump file appearing +in the current directory or in other locations. On Linux systems where +`apport` is installed (such as Ubuntu), the directory where core dump files +are saved can be `/var/lib/apport/coredump` or elsewhere, depending on the +system configuration (see `/proc/sys/kernel/core_pattern`). See also: +https://stackoverflow.com/a/18368068 + +To get a backtrace from the `./core` dump file: >bash - gdb build/bin/nvim core 2>&1 | tee backtrace.txt - thread apply all bt full + gdb build/bin/nvim ./core 2>&1 | tee backtrace.txt + (gdb) thread apply all bt full < MACOS @@ -76,7 +84,7 @@ core dumps with `/etc/launchd.conf`). ============================================================================== Gdb *dev-tools-gdb* -USING GDB TO STEP THROUGH FUNCTIONAL TESTS ~ +USING GDB TO STEP THROUGH FUNCTIONAL TESTS Use `TEST_TAG` to run tests matching busted tags (of the form `#foo` e.g. `it("test #foo ...", ...)`): @@ -86,20 +94,19 @@ Use `TEST_TAG` to run tests matching busted tags (of the form `#foo` e.g. Then, in another terminal: >bash gdb build/bin/nvim - target remote localhost:7777 -< -- See also test/functional/helpers.lua https://github.com/neovim/neovim/blob/3098b18a2b63a841351f6d5e3697cb69db3035ef/test/functional/helpers.lua#L38-L44. + (gdb) target remote localhost:7777 +-- See `nvim_argv` in https://github.com/neovim/neovim/blob/master/test/functional/testnvim.lua. -USING LLDB TO STEP THROUGH UNIT TESTS ~ +USING LLDB TO STEP THROUGH UNIT TESTS ->bash +> lldb .deps/usr/bin/luajit -- .deps/usr/bin/busted --lpath="./build/?.lua" test/unit/ < +USING GDB -USING GDB ~ - -To attach to a running `nvim` process with a pid of 1234: +To attach to a running `nvim` process with a pid of 1234 (Tip: the pid of a +running Nvim instance can be obtained by calling |getpid()|), for instance: >bash gdb -tui -p 1234 build/bin/nvim < @@ -118,8 +125,7 @@ The `gdb` interactive prompt will appear. At any time you can: need for a gdb "frontend". - `<up>` and `<down>` to scroll the source file view - -GDB "REVERSE DEBUGGING" ~ +GDB REVERSE DEBUGGING - `set record full insn-number-max unlimited` - `continue` for a bit (at least until `main()` is executed @@ -127,8 +133,7 @@ GDB "REVERSE DEBUGGING" ~ - provoke the bug, then use `revert-next`, `reverse-step`, etc. to rewind the debugger - -USING GDBSERVER ~ +USING GDBSERVER You may want to connect multiple `gdb` clients to the same running `nvim` process, or you may want to connect to a remote `nvim` process with a local @@ -146,12 +151,12 @@ debugging session in another terminal: < Once you've entered `gdb`, you need to attach to the remote session: > - target remote localhost:6666 + (gdb) target remote localhost:6666 < In case gdbserver puts the TUI as a background process, the TUI can become unable to read input from pty (and receives SIGTTIN signal) and/or output data (SIGTTOU signal). To force the TUI as the foreground process, you can add -> +>c signal (SIGTTOU, SIG_IGN); if (!tcsetpgrp(data->input.in_fd, getpid())) { perror("tcsetpgrp failed"); @@ -159,8 +164,7 @@ unable to read input from pty (and receives SIGTTIN signal) and/or output data < to `tui.c:terminfo_start`. - -USING GDBSERVER IN TMUX ~ +USING GDBSERVER IN TMUX Consider using a custom makefile https://github.com/neovim/neovim/blob/master/BUILD.md#custom-makefile to @@ -185,8 +189,8 @@ Here `gdb_start.sh` includes `gdb` commands to be called when the debugger starts. It needs to attach to the server started by the `dbg-start` rule. For example: > - target remote localhost:6666 - br main + (gdb) target remote localhost:6666 + (gdb) br main < vim:tw=78:ts=8:et:ft=help:norl: diff --git a/runtime/doc/dev_vimpatch.txt b/runtime/doc/dev_vimpatch.txt index 96307dc7df..d6e4ced054 100644 --- a/runtime/doc/dev_vimpatch.txt +++ b/runtime/doc/dev_vimpatch.txt @@ -185,7 +185,7 @@ information. mch_memmove memmove vim_memset copy_chars copy_spaces memset vim_strbyte strchr - vim_strncpy strncpy xstrlcpy + vim_strncpy strncpy xstrlcpy/xmemcpyz vim_strcat strncat xstrlcat VIM_ISWHITE ascii_iswhite IS_WHITE_OR_NUL ascii_iswhite_or_nul @@ -209,6 +209,8 @@ information. utf_off2cells grid_off2cells ml_get_curline get_cursor_line_ptr ml_get_cursor get_cursor_pos_ptr + ml_get_curline_len get_cursor_line_len + ml_get_cursor_len get_cursor_pos_len screen_char ui_line screen_line grid_put_linebuf screen_* (most functions) grid_* diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index 767f46ad1e..d7837dc2fe 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -4,7 +4,7 @@ NVIM REFERENCE MANUAL -Development of Nvim *development* *dev* +Development of Nvim *development* *dev* This reference describes design constraints and guidelines, for developing Nvim applications or Nvim itself. @@ -16,13 +16,13 @@ Nvim is free and open source. Everybody is encouraged to contribute. Type |gO| to see the table of contents. ============================================================================== -Design goals *design-goals* +Design goals *design-goals* Most important things come first (roughly). Some items conflict; this is intentional. A balance must be found. -NVIM IS... IMPROVED *design-improved* +NVIM IS... IMPROVED *design-improved* The Neo bits of Nvim should make it a better Vim, without becoming a completely different editor. @@ -35,7 +35,7 @@ completely different editor. never break. -NVIM IS... WELL DOCUMENTED *design-documented* +NVIM IS... WELL DOCUMENTED *design-documented* - A feature that isn't documented is a useless feature. A patch for a new feature must include the documentation. @@ -44,7 +44,7 @@ NVIM IS... WELL DOCUMENTED *design-documented* item is easier to find. -NVIM IS... FAST AND SMALL *design-speed-size* +NVIM IS... FAST AND SMALL *design-speed-size* Keep Nvim small and fast. This directly affects versatility and usability. - Computers are becoming faster and bigger each year. Vim can grow too, but @@ -59,7 +59,7 @@ Keep Nvim small and fast. This directly affects versatility and usability. ("composability"). -NVIM IS... MAINTAINABLE *design-maintain* +NVIM IS... MAINTAINABLE *design-maintain* - The source code should not become a mess. It should be reliable code. - Use comments in a useful way! Quoting the function name and argument names @@ -70,7 +70,7 @@ NVIM IS... MAINTAINABLE *design-maintain* knowledge spread to other parts of the code. -NVIM IS... NOT *design-not* +NVIM IS... NOT *design-not* Nvim is not an operating system; instead it should be composed with other tools or hosted as a component. Marvim once said: "Unlike Emacs, Nvim does not @@ -78,10 +78,10 @@ include the kitchen sink... but it's good for plumbing." ============================================================================== -Developer guidelines *dev-guidelines* +Developer guidelines *dev-guidelines* -PROVIDERS *dev-provider* +PROVIDERS *dev-provider* A primary goal of Nvim is to allow extension of the editor without special knowledge in the core. Some core functions are delegated to "providers" @@ -115,7 +115,7 @@ For example, the Python provider is implemented by the to 2 only if a valid external Python host is found. Then `has("python")` reflects whether Python support is working. - *provider-reload* + *provider-reload* Sometimes a GUI or other application may want to force a provider to "reload". To reload a provider, undefine its "loaded" flag, then use |:runtime| to reload it: >vim @@ -124,7 +124,7 @@ Sometimes a GUI or other application may want to force a provider to :runtime autoload/provider/clipboard.vim -DOCUMENTATION *dev-doc* +DOCUMENTATION *dev-doc* - "Just say it". Avoid mushy, colloquial phrasing in all documentation (docstrings, user manual, website materials, newsletters, …). Don't mince @@ -159,6 +159,12 @@ DOCUMENTATION *dev-doc* not "the user host terminal". - Use "tui-" to prefix help tags related to the host terminal, and "TUI" in prose if possible. +- Rough guidelines on where Lua documentation should end up: + - Nvim API functions `vim.api.nvim_*` should be in `api.txt`. + - If the module is big and not relevant to generic and lower-level Lua + functionality, then it's a strong candidate for separation. Example: + `treesitter.txt` + - Otherwise, add them to `lua.txt` Documentation format ~ @@ -230,7 +236,7 @@ in src/nvim/api/win_config.c like this: > Lua docstrings ~ - *dev-lua-doc* + *dev-lua-doc* Lua documentation lives in the source code, as docstrings on the function definitions. The |lua-vim| :help is generated from the docstrings. @@ -289,7 +295,7 @@ vim.paste in runtime/lua/vim/_editor.lua like this: > --- @returns false if client should cancel the paste. -LUA STDLIB DESIGN GUIDELINES *dev-lua* +LUA STDLIB DESIGN GUIDELINES *dev-lua* See also |dev-naming|. @@ -304,14 +310,22 @@ See also |dev-naming|. - accept iterable instead of table - exception: in some cases iterable doesn't make sense, e.g. spair() sorts the input by definition, so there is no reason for it to accept an - iterable, because the input needs to be "hydrated", it can't operate on + iterable, because the input needs to be "reified"; it can't operate on a "stream". - return iterable instead of table - mimic the pairs() or ipairs() interface if the function is intended to be used in a "for" loop. + - when a result-or-error interface is needed, return `result|nil, nil|errmsg`: > + ---@return Foo|nil # Result object, or nil if not found. + ---@return nil|string # Error message on failure, or nil on success. +< + - Examples: |vim.ui.open()| |io.open()| |luv-error-handling| + *dev-patterns* Interface conventions ~ +Where possible, these patterns apply to _both_ Lua and the API: + - When accepting a buffer id, etc., 0 means "current buffer", nil means "all buffers". Likewise for window id, tabpage id, etc. - Examples: |vim.lsp.codelens.clear()| |vim.diagnostic.enable()| @@ -322,8 +336,19 @@ Interface conventions ~ filter(table, opts, function() … end) BAD: filter(function() … end, table, opts) - -API DESIGN GUIDELINES *dev-api* +- "Enable" ("toggle") interface and behavior: + - `enable(…, nil)` and `enable(…, {buf=nil})` are synonyms and control the + the "global" enablement of a feature. + - `is_enabled(nil)` and `is_enabled({buf=nil})`, likewise, query the + global state of the feature. + - `enable(…, {buf: number})` sets a buffer-local "enable" flag. + - `is_enabled({buf: number})`, likewise, queries the buffer-local state of + the feature. + - See |vim.lsp.inlay_hint.enable()| and |vim.lsp.inlay_hint.is_enabled()| + for a reference implementation of these "best practices". + - NOTE: open questions: https://github.com/neovim/neovim/issues/28603 + +API DESIGN GUIDELINES *dev-api* See also |dev-naming|. @@ -363,32 +388,45 @@ Naming conventions ~ In general, look for precedent when choosing a name, that is, look at existing (non-deprecated) functions. In particular, see below... - *dev-name-common* + *dev-name-common* Use existing common {verb} names (actions) if possible: - add: Appends or inserts into a collection - attach: Listens to something to get events from it (TODO: rename to "on"?) - call: Calls a function + - cancel: Cancels or dismisses an event or interaction, typically + user-initiated and without error. (Compare "abort", which + cancels and signals error/failure.) - clear: Clears state but does not destroy the container - create: Creates a new (non-trivial) thing (TODO: rename to "def"?) - del: Deletes a thing (or group of things) - detach: Dispose attached listener (TODO: rename to "un"?) + - enable: Enables/disables functionality. Signature should be + `enable(enable?:boolean, filter?:table)`. - eval: Evaluates an expression - - exec: Executes code + - exec: Executes code, may return a result - fmt: Formats - get: Gets things (often by a query) - inspect: Presents a high-level, often interactive, view + - is_enabled: Checks if functionality is enabled. - open: Opens something (a buffer, window, …) - parse: Parses something into a structured form - set: Sets a thing (or group of things) + - start: Spin up a long-lived process. Prefer "enable" except when + "start" is obviously more appropriate. + - stop: Inverse of "start". Teardown a long-lived process. - try_{verb}: Best-effort operation, failure returns null or error obj Do NOT use these deprecated verbs: + - disable: Prefer `enable(enable: boolean)`. + - exit: Prefer "cancel" (or "stop" if appropriate). + - is_disabled: Prefer `is_enabled()`. - list: Redundant with "get" - - show: Redundant with "print", "echo" - notify: Redundant with "print", "echo" + - show: Redundant with "print", "echo" + - toggle: Prefer `enable(not is_enabled())`. -Use consistent names for {noun} (nouns) in API functions: buffer is called -"buf" everywhere, not "buffer" in some places and "buf" in others. +Use consistent names for {topic} in API functions: buffer is called "buf" +everywhere, not "buffer" in some places and "buf" in others. - buf: Buffer - chan: |channel| - cmd: Command @@ -401,12 +439,12 @@ Use consistent names for {noun} (nouns) in API functions: buffer is called - win: Window Do NOT use these deprecated nouns: - - buffer + - buffer Use "buf" instead - callback Use on_foo instead - - command - - window + - command Use "cmd" instead + - window Use "win" instead - *dev-name-events* + *dev-name-events* Use the "on_" prefix to name event-handling callbacks and also the interface for "registering" such handlers (on_key). The dual nature is acceptable to avoid a confused collection of naming conventions for these related concepts. @@ -420,14 +458,17 @@ Use this format to name API (RPC) events: > Example: > nvim_buf_changedtick_event < - *dev-name-api* + *dev-api-name* Use this format to name new RPC |API| functions: > - nvim_{noun}_{verb}_{arbitrary-qualifiers} + nvim_{topic}_{verb}_{arbitrary-qualifiers} -If the function acts on an object then {noun} is the name of that object -(e.g. "buf" or "win"). If the function operates in a "global" context then -{noun} is usually omitted (but consider "namespacing" your global operations -with a {noun} that groups functions under a common concept). +Do not add new nvim_buf/nvim_win/nvim_tabpage APIs, unless you are certain the +concept will NEVER be applied to more than one "scope". That is, {topic} +should be the TOPIC ("ns", "extmark", "option", …) that acts on the scope(s) +(buf/win/tabpage/global), it should NOT be the scope. Instead the scope should +be a parameter (typically manifest as mutually-exclusive buf/win/… flags like +|nvim_get_option_value()|, or less commonly as a `scope: string` field like +|nvim_get_option_info2()|). - Example: `nvim_get_keymap('v')` operates in a global context (first parameter is not a Buffer). The "get" verb indicates that it gets anything @@ -437,9 +478,61 @@ with a {noun} that groups functions under a common concept). and uses the "del" {verb}. -API-CLIENT *dev-api-client* +INTERFACE PATTERNS *dev-api-patterns* + +Prefer adding a single `nvim_{topic}_{verb}_…` interface for a given topic. + +Example: > - *api-client* + nvim_ns_add( + ns_id: int, + filter: { + handle: integer (buf/win/tabpage id) + scope: "global" | "win" | "buf" | "tabpage" + } + ): { ok: boolean } + + nvim_ns_get( + ns_id: int, + filter: { + handle: integer (buf/win/tabpage id) + scope: "global" | "win" | "buf" | "tabpage" + } + ): { ids: int[] } + + nvim_ns_del( + ns_id: int, + filter: { + handle: integer (buf/win/tabpage id) + scope: "global" | "win" | "buf" | "tabpage" + } + ): { ok: boolean } + + +Anti-Example: + +Creating separate `nvim_xx`, `nvim_buf_xx`, `nvim_win_xx`, and +`nvim_tabpage_xx`, functions all for the same `xx` topic, requires 4x the +amount of documentation, tests, boilerplate, and interfaces, which users must +comprehend, maintainers must maintain, etc. Thus the following is NOT +recommended (compare these 12(!) functions to the above 3 functions): > + + nvim_add_ns(…) + nvim_buf_add_ns(…) + nvim_win_add_ns(…) + nvim_tabpage_add_ns(…) + nvim_del_ns(…) + nvim_buf_del_ns(…) + nvim_win_del_ns(…) + nvim_tabpage_del_ns(…) + nvim_get_ns(…) + nvim_buf_get_ns(…) + nvim_win_get_ns(…) + nvim_tabpage_get_ns(…) + +API-CLIENT *dev-api-client* + + *api-client* API clients wrap the Nvim |API| to provide idiomatic "SDKs" for their respective platforms (see |jargon|). You can build a new API client for your favorite platform or programming language. @@ -447,9 +540,10 @@ favorite platform or programming language. List of API clients: https://github.com/neovim/neovim/wiki/Related-projects#api-clients - *pynvim* -The Python client is the reference implementation for API clients. - https://github.com/neovim/pynvim + *node-client* *pynvim* +These clients can be considered the "reference implementation" for API clients: +- https://github.com/neovim/node-client +- https://github.com/neovim/pynvim Standard Features ~ @@ -500,7 +594,7 @@ API client implementation guidelines ~ https://github.com/msgpack-rpc/msgpack-rpc -EXTERNAL UI *dev-ui* +EXTERNAL UI *dev-ui* External UIs should be aware of the |api-contract|. In particular, future versions of Nvim may add new items to existing events. The API is strongly @@ -526,6 +620,8 @@ External UIs are expected to implement these common features: published in this event. See also "mouse_on", "mouse_off". - UIs generally should NOT set |$NVIM_APPNAME| (unless explicitly requested by the user). +- Support the text decorations/attributes given by |ui-event-hl_attr_define|. + The "url" attr should be presented as a clickable hyperlink. vim:tw=78:ts=8:sw=4:et:ft=help:norl: diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index c9e783ca62..36616b9a0d 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -371,8 +371,10 @@ Lua module: vim.diagnostic *diagnostic-api* A table with the following keys: Fields: ~ - • {namespace}? (`integer`) Limit diagnostics to the given namespace. - • {lnum}? (`integer`) Limit diagnostics to the given line number. + • {namespace}? (`integer[]|integer`) Limit diagnostics to one or more + namespaces. + • {lnum}? (`integer`) Limit diagnostics to those spanning the + specified line number. • {severity}? (`vim.diagnostic.SeverityFilter`) See |diagnostic-severity|. @@ -387,7 +389,7 @@ Lua module: vim.diagnostic *diagnostic-api* |nvim_win_get_cursor()|. • {wrap}? (`boolean`, default: `true`) Whether to loop around file or not. Similar to 'wrapscan'. - • {severity} (`vim.diagnostic.Severity`) See + • {severity}? (`vim.diagnostic.SeverityFilter`) See |diagnostic-severity|. • {float}? (`boolean|vim.diagnostic.Opts.Float`, default: `true`) If `true`, call @@ -432,7 +434,7 @@ Lua module: vim.diagnostic *diagnostic-api* • {update_in_insert}? (`boolean`, default: `false`) Update diagnostics in Insert mode (if `false`, diagnostics are updated on |InsertLeave|) - • {severity_sort}? (`boolean|{reverse?:boolean}`, default: `false) + • {severity_sort}? (`boolean|{reverse?:boolean}`, default: `false`) Sort diagnostics by severity. This affects the order in which signs and virtual text are displayed. When true, higher severities are @@ -496,6 +498,7 @@ Lua module: vim.diagnostic *diagnostic-api* diagnostic instead of prepending it. Overrides the setting from |vim.diagnostic.config()|. • {focus_id}? (`string`) + • {border}? (`string`) see |nvim_open_win()|. *vim.diagnostic.Opts.Signs* @@ -615,23 +618,20 @@ count({bufnr}, {opts}) *vim.diagnostic.count()* (`table`) Table with actually present severity values as keys (see |diagnostic-severity|) and integer counts as values. -disable({bufnr}, {namespace}) *vim.diagnostic.disable()* - Disable diagnostics in the given buffer. +enable({enable}, {filter}) *vim.diagnostic.enable()* + Enables or disables diagnostics. - Parameters: ~ - • {bufnr} (`integer?`) Buffer number, or 0 for current buffer. When - omitted, disable diagnostics in all buffers. - • {namespace} (`integer?`) Only disable diagnostics for the given - namespace. - -enable({bufnr}, {namespace}) *vim.diagnostic.enable()* - Enable diagnostics in the given buffer. + To "toggle", pass the inverse of `is_enabled()`: >lua + vim.diagnostic.enable(not vim.diagnostic.is_enabled()) +< Parameters: ~ - • {bufnr} (`integer?`) Buffer number, or 0 for current buffer. When - omitted, enable diagnostics in all buffers. - • {namespace} (`integer?`) Only enable diagnostics for the given - namespace. + • {enable} (`boolean?`) true/nil to enable, false to disable + • {filter} (`table?`) Optional filters |kwargs|, or `nil` for all. + • {ns_id}? (`integer`) Diagnostic namespace, or `nil` for + all. + • {bufnr}? (`integer`) Buffer number, or 0 for current + buffer, or `nil` for all buffers. fromqflist({list}) *vim.diagnostic.fromqflist()* Convert a list of quickfix items to a list of diagnostics. @@ -732,7 +732,7 @@ hide({namespace}, {bufnr}) *vim.diagnostic.hide()* diagnostics, use |vim.diagnostic.reset()|. To hide diagnostics and prevent them from re-displaying, use - |vim.diagnostic.disable()|. + |vim.diagnostic.enable()|. Parameters: ~ • {namespace} (`integer?`) Diagnostic namespace. When omitted, hide @@ -740,14 +740,15 @@ hide({namespace}, {bufnr}) *vim.diagnostic.hide()* • {bufnr} (`integer?`) Buffer number, or 0 for current buffer. When omitted, hide diagnostics in all buffers. -is_disabled({bufnr}, {namespace}) *vim.diagnostic.is_disabled()* - Check whether diagnostics are disabled in a given buffer. +is_enabled({filter}) *vim.diagnostic.is_enabled()* + Check whether diagnostics are enabled. Parameters: ~ - • {bufnr} (`integer?`) Buffer number, or 0 for current buffer. - • {namespace} (`integer?`) Diagnostic namespace. When omitted, checks - if all diagnostics are disabled in {bufnr}. Otherwise, - only checks if diagnostics from {namespace} are disabled. + • {filter} (`table?`) Optional filters |kwargs|, or `nil` for all. + • {ns_id}? (`integer`) Diagnostic namespace, or `nil` for + all. + • {bufnr}? (`integer`) Buffer number, or 0 for current + buffer, or `nil` for all buffers. Return: ~ (`boolean`) diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 1a4572e94a..662d89895d 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -632,9 +632,12 @@ list of the current window. :[count]arge[dit][!] [++opt] [+cmd] {name} .. *:arge* *:argedit* Add {name}s to the argument list and edit it. - When {name} already exists in the argument list, this - entry is edited. - This is like using |:argadd| and then |:edit|. + There is no check for duplicates, it is possible to + add a file to the argument list twice |:argded|. + This is like using |:argadd| and then |:edit| (with + the small exception that |:edit| does not change the + argument list, so the argument list pointer isn't + changed). Spaces in filenames have to be escaped with "\". [count] is used like with |:argadd|. If the current file cannot be |abandon|ed {name}s will @@ -653,12 +656,12 @@ list of the current window. If the argument list is "a b c", and "b" is the current argument, then these commands result in: command new argument list ~ - :argadd x a b x c - :0argadd x x a b c - :1argadd x a x b c - :$argadd x a b c x + :argadd x a [b] x c + :0argadd x x a [b] c + :1argadd x a x [b] c + :$argadd x a [b] c x And after the last one: - :+2argadd y a b c x y + :+2argadd y a [b] c x y There is no check for duplicates, it is possible to add a file to the argument list twice. You can use |:argdedupe| to fix it afterwards: > diff --git a/runtime/doc/editorconfig.txt b/runtime/doc/editorconfig.txt index a2281a7b7c..0b20c77801 100644 --- a/runtime/doc/editorconfig.txt +++ b/runtime/doc/editorconfig.txt @@ -3,79 +3,35 @@ NVIM REFERENCE MANUAL + Type |gO| to see the table of contents. +============================================================================== EditorConfig integration *editorconfig* -Nvim supports EditorConfig. When a file is opened, Nvim searches all parent -directories of that file for ".editorconfig" files, parses them, and applies -any properties that match the opened file. Think of it like 'modeline' for an -entire (recursive) directory. For more information see -https://editorconfig.org/. +Nvim supports EditorConfig. When a file is opened, after running |ftplugin|s +and |FileType| autocommands, Nvim searches all parent directories of that file +for ".editorconfig" files, parses them, and applies any properties that match +the opened file. Think of it like 'modeline' for an entire (recursive) +directory. For more information see https://editorconfig.org/. *g:editorconfig* *b:editorconfig* -EditorConfig is enabled by default. To disable it, add to your config: >lua +EditorConfig is enabled by default. To disable it, add to your config: >lua vim.g.editorconfig = false < + (Vimscript: `let g:editorconfig = v:false`). It can also be disabled per-buffer by setting the |b:editorconfig| buffer-local variable to `false`. Nvim stores the applied properties in |b:editorconfig| if it is not `false`. - *editorconfig-properties* -The following properties are supported by default: - - *editorconfig_root* -root If "true", then stop searching for .editorconfig files - in parent directories. This property must be at the - top-level of the .editorconfig file (i.e. it must not - be within a glob section). - - *editorconfig_charset* -charset One of "utf-8", "utf-8-bom", "latin1", "utf-16be", or - "utf-16le". Sets the 'fileencoding' and 'bomb' - options. - - *editorconfig_end_of_line* -end_of_line One of "lf", "crlf", or "cr". These correspond to - setting 'fileformat' to "unix", "dos", or "mac", - respectively. - - *editorconfig_indent_style* -indent_style One of "tab" or "space". Sets the 'expandtab' option. - - *editorconfig_indent_size* -indent_size A number indicating the size of a single indent. - Alternatively, use the value "tab" to use the value of - the tab_width property. Sets the 'shiftwidth' and - 'softtabstop' options. - If this value is not "tab" and the tab_width property - is not set, 'tabstop' is also set to this value. - - *editorconfig_insert_final_newline* -insert_final_newline "true" or "false" to ensure the file always has a - trailing newline as its last byte. Sets the - 'fixendofline' and 'endofline' options. - - *editorconfig_max_line_length* -max_line_length A number indicating the maximum length of a single - line. Sets the 'textwidth' option. - - *editorconfig_tab_width* -tab_width The display size of a single tab character. Sets the - 'tabstop' option. - - *editorconfig_trim_trailing_whitespace* -trim_trailing_whitespace - When "true", trailing whitespace is automatically - removed when the buffer is written. - *editorconfig-custom-properties* + New properties can be added by adding a new entry to the "properties" table. The table key is a property name and the value is a callback function which -accepts the number of the buffer to be modified, the value of the property -in the .editorconfig file, and (optionally) a table containing all of the -other properties and their values (useful for properties which depend on other +accepts the number of the buffer to be modified, the value of the property in +the `.editorconfig` file, and (optionally) a table containing all of the other +properties and their values (useful for properties which depend on other properties). The value is always a string and must be coerced if necessary. Example: >lua @@ -86,4 +42,48 @@ Example: >lua vim.b[bufnr].foo = val end < - vim:tw=78:ts=8:et:sw=4:ft=help:norl: + + *editorconfig-properties* + +The following properties are supported by default: + + +charset *editorconfig.charset* + One of `"utf-8"`, `"utf-8-bom"`, `"latin1"`, `"utf-16be"`, or + `"utf-16le"`. Sets the 'fileencoding' and 'bomb' options. + +end_of_line *editorconfig.end_of_line* + One of `"lf"`, `"crlf"`, or `"cr"`. These correspond to setting + 'fileformat' to "unix", "dos", or "mac", respectively. + +indent_size *editorconfig.indent_size* + A number indicating the size of a single indent. Alternatively, use the + value "tab" to use the value of the tab_width property. Sets the + 'shiftwidth' and 'softtabstop' options. If this value is not "tab" and the + tab_width property is not set, 'tabstop' is also set to this value. + +indent_style *editorconfig.indent_style* + One of `"tab"` or `"space"`. Sets the 'expandtab' option. + +insert_final_newline *editorconfig.insert_final_newline* + `"true"` or `"false"` to ensure the file always has a trailing newline as + its last byte. Sets the 'fixendofline' and 'endofline' options. + +max_line_length *editorconfig.max_line_length* + A number indicating the maximum length of a single line. Sets the + 'textwidth' option. + +root *editorconfig.root* + If "true", then stop searching for `.editorconfig` files in parent + directories. This property must be at the top-level of the `.editorconfig` + file (i.e. it must not be within a glob section). + +tab_width *editorconfig.tab_width* + The display size of a single tab character. Sets the 'tabstop' option. + +trim_trailing_whitespace *editorconfig.trim_trailing_whitespace* + When `"true"`, trailing whitespace is automatically removed when the + buffer is written. + + + vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl: diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index ef416fe56f..7b4dba5a50 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2144,6 +2144,10 @@ text... :cons[t] {var-name} = {expr1} :cons[t] [{name1}, {name2}, ...] = {expr1} :cons[t] [{name}, ..., ; {lastname}] = {expr1} +:cons[t] {var-name} =<< [trim] [eval] {marker} +text... +text... +{marker} Similar to |:let|, but additionally lock the variable after setting the value. This is the same as locking the variable with |:lockvar| just after |:let|, thus: > diff --git a/runtime/doc/faq.txt b/runtime/doc/faq.txt index 09bf829512..7d2774c191 100644 --- a/runtime/doc/faq.txt +++ b/runtime/doc/faq.txt @@ -29,13 +29,6 @@ Use the stable (release) https://github.com/neovim/neovim/releases/latest version for a more predictable experience. -CAN I USE RUBY-BASED VIM PLUGINS (E.G. LUSTYEXPLORER)? ~ - -Yes, starting with Nvim 0.1.5 PR #4980 -https://github.com/neovim/neovim/pull/4980 the legacy Vim `if_ruby` interface -is supported. - - CAN I USE LUA-BASED VIM PLUGINS (E.G. NEOCOMPLETE)? ~ No. Starting with Nvim 0.2 PR #4411 @@ -288,19 +281,17 @@ Run the following from the command line: >bash locale | grep -E '(LANG|LC_CTYPE|LC_ALL)=(.*\.)?(UTF|utf)-?8' < -If there's no results, then you might not be using a UTF-8 locale. See the -following issues: -#1601 https://github.com/neovim/neovim/issues/1601 -#1858 https://github.com/neovim/neovim/issues/1858 -#2386 https://github.com/neovim/neovim/issues/2386 +If there's no results, you might not be using a UTF-8 locale. See these issues: +- https://github.com/neovim/neovim/issues/1601 +- https://github.com/neovim/neovim/issues/1858 +- https://github.com/neovim/neovim/issues/2386 ESC IN TMUX OR GNU SCREEN IS DELAYED ~ This is a common problem https://www.google.com/?q=tmux%20vim%20escape%20delay in `tmux` / `screen` -(see also tmux/#131 -https://github.com/tmux/tmux/issues/131#issuecomment-145853211). The +(see also https://github.com/tmux/tmux/issues/131#issuecomment-145853211). The corresponding timeout needs to be tweaked to a low value (10-20ms). `.tmux.conf`: @@ -317,11 +308,11 @@ corresponding timeout needs to be tweaked to a low value (10-20ms). "WHY DOESN'T THIS HAPPEN IN VIM?" It does happen (try `vim -N -u NONE`), but if you hit a key quickly after -ESC_ then Vim interprets the ESC as ESC instead of ALT (META). You won't +ESC then Vim interprets the ESC as ESC instead of ALT (META). You won't notice the delay unless you closely observe the cursor. The tradeoff is that Vim won't understand ALT (META) key-chords, so for example `nnoremap <M-a>` -won't work. ALT (META) key-chords always work in Nvim. See also `:help -xterm-cursor-keys` in Vim. +won't work. ALT (META) key-chords always work in Nvim. +See also `:help xterm-cursor-keys` in Vim. Nvim 0.3 mimics the Vim behavior while still fully supporting ALT mappings. See |i_ALT|. @@ -329,7 +320,7 @@ Nvim 0.3 mimics the Vim behavior while still fully supporting ALT mappings. See ESC IN GNU SCREEN IS LOST WHEN MOUSE MODE IS ENABLED ~ -This happens because of a bug in screen https://savannah.gnu.org/bugs/?60196: +This happens because of a bug in screen https://savannah.gnu.org/bugs/?60196 : in mouse mode, screen assumes that `ESC` is part of a mouse sequence and will wait an unlimited time for the rest of the sequence, regardless of `maptimeout`. Until it's fixed in screen, there's no known workaround for @@ -339,10 +330,11 @@ passed through to Nvim. CALLING INPUTLIST(), ECHOMSG, ... IN FILETYPE PLUGINS AND AUTOCMD DOES NOT WORK ~ -#10008 https://github.com/neovim/neovim/issues/10008, -#10116 https://github.com/neovim/neovim/issues/10116, -#12288 https://github.com/neovim/neovim/issues/12288, -# vim/vim#4379 https://github.com/vim/vim/issues/4379. +- https://github.com/neovim/neovim/issues/10008 +- https://github.com/neovim/neovim/issues/10116 +- https://github.com/neovim/neovim/issues/12288 +- https://github.com/vim/vim/issues/4379 + This is because Nvim sets `shortmess+=F` by default. Vim behaves the same way with `set shortmes+=F`. There are plans to improve this, but meanwhile as a workaround, use `set shortmess-=F` or use `unsilent` as follows. @@ -400,8 +392,8 @@ CMAKE ERRORS ~ `configure_file Problem configuring file` This is probably a permissions issue, which can happen if you run `make` as the -root user, then later run an unprivileged `make`. To fix this, run `rm -rf -build` and try again. +root user, then later run an unprivileged `make`. To fix this, run +`rm -rf build` and try again. GENERATING HELPTAGS FAILED ~ @@ -434,7 +426,7 @@ WHY EMBED LUA INSTEAD OF X? ~ - Nvim also uses Lua internally as an alternative to C. Extra performance is useful there, as opposed to a slow language like Python or Vim9script. - LuaJIT is one of the fastest runtimes on the planet, 10x faster than Python - and "Vim9script" https://vimhelp.org/vim9.txt.html, 100x faster than + and "Vim9script" https://vimhelp.org/vim9.txt.html , 100x faster than Vimscript. - Python/JS cost more than Lua in terms of size and portability, and there are already numerous Python/JS-based editors. So Python/JS would make Nvim diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 071a45c9c3..5eae78744c 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -155,6 +155,8 @@ variables can be used to overrule the filetype used for certain extensions: `*.inc` g:filetype_inc `*.lsl` g:filetype_lsl `*.m` g:filetype_m |ft-mathematica-syntax| + `*.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md` + g:filetype_md |ft-pandoc-syntax| `*.mod` g:filetype_mod `*.p` g:filetype_p |ft-pascal-syntax| `*.pl` g:filetype_pl @@ -400,6 +402,31 @@ So to enable this only for ruby, set the following variable: > If both, the global `plugin_exec` and the `<filetype>_exec` specific variable are set, the filetype specific variable should have precedent. + +ASCIIDOC *ft-asciidoc-plugin* + +To enable |folding| use this: > + let g:asciidoc_folding = 1 + +To disable nesting of folded headers use this: > + let g:asciidoc_foldnested = 0 + +To disable folding everything under the title use this: > + let asciidoc_fold_under_title = 0 + + +ARDUINO *ft-arduino-plugin* + +By default the following options are set, in accordance with the default +settings of Arduino IDE: > + + setlocal expandtab tabstop=2 softtabstop=2 shiftwidth=2 + +To disable this behavior, set the following variable in your vimrc: > + + let g:arduino_recommended_style = 0 + + AWK *ft-awk-plugin* Support for features specific to GNU Awk, like @include, can be enabled by @@ -527,18 +554,6 @@ under it. If not found, a new entry and item is prepended to the beginning of the Changelog. -ASCIIDOC *ft-asciidoc-plugin* - -To enable |folding| use this: > - let g:asciidoc_folding = 1 - -To disable nesting of folded headers use this: > - let g:asciidoc_foldnested = 0 - -To disable folding everything under the title use this: > - let asciidoc_fold_under_title = 0 - - FORTRAN *ft-fortran-plugin* Options: @@ -587,6 +602,37 @@ The mapping can be disabled with: > let g:no_gprof_maps = 1 +JAVA *ft-java-plugin* + +Whenever the variable "g:ftplugin_java_source_path" is defined and its value +is a filename whose extension is either ".jar" or ".zip", e.g.: > + let g:ftplugin_java_source_path = '/path/to/src.jar' + let g:ftplugin_java_source_path = '/path/to/src.zip' +< +and the |zip| plugin has already been sourced, the |gf| command can be used to +open the archive and the |n| command can be used to look for the selected type +and the <Return> key can be used to load a listed file. + +Note that the effect of using the "gf" command WITHIN a buffer loaded with the +Zip plugin depends on the version of the Zip plugin. For the Zip plugin +versions that do not support Jar type archives, consider creating symbolic +links with the ".zip" extension for each Jar archive of interest and assigning +any such file to the variable from now on. + +Otherwise, for the defined variable "g:ftplugin_java_source_path", the local +value of the 'path' option will be further modified by prefixing the value of +the variable, e.g.: > + let g:ftplugin_java_source_path = $JDK_SRC_PATH + let &l:path = g:ftplugin_java_source_path . ',' . &l:path +< +and the "gf" command can be used on a fully-qualified type to look for a file +in the "path" and to try to load it. + +Remember to manually trigger the |FileType| event from a buffer with a Java +file loaded in it each time after assigning a new value to the variable: > + doautocmd FileType +< + MAIL *ft-mail-plugin* Options: @@ -626,8 +672,10 @@ Man Open the manpage for the <cWORD> (man buffers) or <cword> (non-man buffers) under the cursor. Man! Display the current buffer contents as a manpage. -|:Man| accepts command modifiers. For example, to use a vertical split: > +|:Man| accepts command modifiers. For example, to use a vertical split: >vim :vertical Man printf +To reuse the current window: >vim + :hide Man printf Local mappings: K or CTRL-] Jump to the manpage for the <cWORD> under the @@ -647,14 +695,14 @@ Variables: empty. Enabled by default. Set |FALSE| to enable soft wrapping. -To use Nvim as a manpager: > +To use Nvim as a manpager: >sh export MANPAGER='nvim +Man!' Note that when running `man` from the shell and with that `MANPAGER` in your environment, `man` will pre-format the manpage using `groff`. Thus, Nvim will inevitably display the manual page as it was passed to it from stdin. One of the caveats of this is that the width will _always_ be hard-wrapped and not -soft wrapped as with `g:man_hardwrap=0`. You can set in your environment: > +soft wrapped as with `g:man_hardwrap=0`. You can set in your environment: >sh export MANWIDTH=999 So `groff`'s pre-formatting output will be the same as with `g:man_hardwrap=0` i.e soft-wrapped. @@ -662,6 +710,10 @@ So `groff`'s pre-formatting output will be the same as with `g:man_hardwrap=0` i To disable bold highlighting: > :highlight link manBold Normal +An alternative to using `MANPAGER` in shell can be redefined `man`, for example: >sh + man() { + nvim "+hide Man $1" + } MARKDOWN *ft-markdown-plugin* @@ -942,6 +994,31 @@ The mappings can be disabled with: > let g:no_vim_maps = 1 +ZIG *ft-zig-plugin* + + *g:zig_recommended_style* +By default the following indentation options are set, in accordance with Zig's +recommended style (https://ziglang.org/documentation/master/): > + + setlocal expandtab shiftwidth=4 softtabstop=4 tabstop=8 +< +To disable this behavior, set |g:zig_recommended_style| to 0: > + + let g:zig_recommended_style = 0 +< + *g:zig_std_dir* +The path to the Zig standard library. The Zig |ftplugin| reads |g:zig_std_dir| +and appends it to the 'path' for Zig files. Where the Zig standard library +is located is system and installation method dependent. + +One can automatically set |g:zig_std_dir| using `zig env`: > + + let g:zig_std_dir = json_decode(system('zig env'))['std_dir'] +< +This can, for example, be put in a FileType |:autocmd| or user |ftplugin| to +only load when a Zig file is opened. + + ZIMBU *ft-zimbu-plugin* The Zimbu filetype plugin defines mappings to move to the start and end of diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index bf62dba539..a64d722177 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -443,18 +443,18 @@ You can define the special menu "PopUp". This is the menu that is displayed when the right mouse button is pressed, if 'mousemodel' is set to popup or popup_setpos. -The default "PopUp" menu is: > - aunmenu PopUp - vnoremenu PopUp.Cut "+x - vnoremenu PopUp.Copy "+y - anoremenu PopUp.Paste "+gP - vnoremenu PopUp.Paste "+P - vnoremenu PopUp.Delete "_x - nnoremenu PopUp.Select\ All ggVG - vnoremenu PopUp.Select\ All gg0oG$ - inoremenu PopUp.Select\ All <C-Home><C-O>VG - anoremenu PopUp.-1- <Nop> - anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR> +The default "PopUp" menu is: >vim + vnoremenu PopUp.Cut "+x + vnoremenu PopUp.Copy "+y + anoremenu PopUp.Paste "+gP + vnoremenu PopUp.Paste "+P + vnoremenu PopUp.Delete "_x + nnoremenu PopUp.Select\ All ggVG + vnoremenu PopUp.Select\ All gg0oG$ + inoremenu PopUp.Select\ All <C-Home><C-O>VG + anoremenu PopUp.Inspect <Cmd>Inspect<CR> + anoremenu PopUp.-1- <Nop> + anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR> < Showing What Menus Are Mapped To *showing-menus* diff --git a/runtime/doc/pi_health.txt b/runtime/doc/health.txt index bcc933d8b2..e879f11351 100644 --- a/runtime/doc/pi_health.txt +++ b/runtime/doc/health.txt @@ -1,11 +1,14 @@ -*pi_health.txt* Healthcheck framework +*health.txt* Nvim -Author: TJ DeVries <devries.timothyj@gmail.com> - Type |gO| to see the table of contents. + NVIM REFERENCE MANUAL + + + Type |gO| to see the table of contents. ============================================================================== -Introduction *health* +Checkhealth *health* + health.vim is a minimal framework to help users troubleshoot configuration and any other environment conditions that a plugin might care about. Nvim ships @@ -18,12 +21,11 @@ To run all healthchecks, use: >vim < Plugin authors are encouraged to write new healthchecks. |health-dev| -============================================================================== -Commands *health-commands* +Commands *health-commands* - *:che* *:checkhealth* + *:che* *:checkhealth* :che[ckhealth] Run all healthchecks. - *E5009* + *E5009* Nvim depends on |$VIMRUNTIME|, 'runtimepath' and 'packpath' to find the standard "runtime files" for syntax highlighting, filetype-specific behavior, and standard plugins (including @@ -33,7 +35,7 @@ Commands *health-commands* :che[ckhealth] {plugins} Run healthcheck(s) for one or more plugins. E.g. to run only the standard Nvim healthcheck: >vim - :checkhealth nvim + :checkhealth vim.health < To run the healthchecks for the "foo" and "bar" plugins (assuming they are on 'runtimepath' and they have implemented @@ -46,33 +48,8 @@ Commands *health-commands* :checkhealth vim.lsp vim.treesitter :checkhealth vim* < -============================================================================== -Functions *health-functions* *vim.health* - -The Lua "health" module can be used to create new healthchecks. To get started -see |health-dev|. -vim.health.start({name}) *vim.health.start()* - Starts a new report. Most plugins should call this only once, but if - you want different sections to appear in your report, call this once - per section. - -vim.health.info({msg}) *vim.health.info()* - Reports an informational message. - -vim.health.ok({msg}) *vim.health.ok()* - Reports a "success" message. - -vim.health.warn({msg} [, {advice}]) *vim.health.warn()* - Reports a warning. {advice} is an optional list of suggestions to - present to the user. - -vim.health.error({msg} [, {advice}]) *vim.health.error()* - Reports an error. {advice} is an optional list of suggestions to - present to the user. - -============================================================================== -Create a healthcheck *health-dev* +Create a healthcheck *health-dev* *vim.health* Healthchecks are functions that check the user environment, configuration, or any other prerequisites that a plugin cares about. Nvim ships with @@ -119,4 +96,39 @@ with your plugin name: >lua return M -vim:et:tw=78:ts=8:ft=help:fdm=marker +error({msg}, {...}) *vim.health.error()* + Reports an error. + + Parameters: ~ + • {msg} (`string`) + • {...} (`string|string[]`) Optional advice + +info({msg}) *vim.health.info()* + Reports an informational message. + + Parameters: ~ + • {msg} (`string`) + +ok({msg}) *vim.health.ok()* + Reports a "success" message. + + Parameters: ~ + • {msg} (`string`) + +start({name}) *vim.health.start()* + Starts a new report. Most plugins should call this only once, but if you + want different sections to appear in your report, call this once per + section. + + Parameters: ~ + • {name} (`string`) + +warn({msg}, {...}) *vim.health.warn()* + Reports a warning. + + Parameters: ~ + • {msg} (`string`) + • {...} (`string|string[]`) Optional advice + + + vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl: diff --git a/runtime/doc/hebrew.txt b/runtime/doc/hebrew.txt index e38385e13a..c19b4b9835 100644 --- a/runtime/doc/hebrew.txt +++ b/runtime/doc/hebrew.txt @@ -14,7 +14,7 @@ currently helping support these features. ------------------------------------------------------------------------------ Introduction -Hebrew-specific options are 'hkmap', 'hkmapp' 'keymap'=hebrew and 'aleph'. +Hebrew-specific 'keymap' values are "hebrew" and "hebrewp". Hebrew-useful options are 'delcombine', 'allowrevins', 'revins', 'rightleft' and 'rightleftcmd'. @@ -30,7 +30,7 @@ Details + 'rightleft' ('rl') sets window orientation to right-to-left. This means that the logical text 'ABC' will be displayed as 'CBA', and will start drawing at the right edge of the window, not the left edge. - + 'keymap' ('kmp') sets keyboard mapping. use values "hebrew" or "heprewp" + + 'keymap' ('kmp') sets keyboard mapping. use values "hebrew" or "hebrewp" (the latter option enables phonetic mapping) + 'delcombine' ('deco'), boolean, allows one to remove the niqud or te`amim by pressing 'x' on a character (with associated niqud). @@ -41,35 +41,23 @@ Details + Encoding: + Under Unix, ISO 8859-8 encoding (Hebrew letters codes: 224-250). + Under MS DOS, PC encoding (Hebrew letters codes: 128-154). - These are defaults, that can be overridden using the 'aleph' option. + You should prefer using UTF8, as it supports the combining-characters ('deco' does nothing if UTF8 encoding is not active). + Vim arguments: - + `vim -H file` starts editing a Hebrew file, i.e. 'rightleft' and 'hkmap' - are set. + + `vim -H file` starts editing a Hebrew file, i.e. 'rightleft' is set and + 'keymap' is set to "hebrew". + Keyboard: - + The 'allowrevins' option enables the CTRL-_ command in Insert mode and - in Command-line mode. + + The 'allowrevins' option enables the CTRL-_ command in Insert mode. - + CTRL-_ in insert/replace modes toggles 'revins' and 'hkmap' as follows: - - When in rightleft window, 'revins' is toggled, since - English will likely be inserted in this case. - - When in norightleft window, 'revins' is toggled, since Hebrew - will likely be inserted in this case. + + CTRL-_ in Insert mode toggles 'revins'. CTRL-_ moves the cursor to the end of the typed text. - + CTRL-_ in command mode only toggles keyboard mapping (see Bugs below). - This setting is independent of 'hkmap' option, which only applies to - insert/replace mode. - Note: On some keyboards, CTRL-_ is mapped to CTRL-?. - + Keyboard mapping while 'hkmap' is set (standard Israeli keyboard): + + Keyboard mapping while 'keymap' is "hebrew" (standard Israeli keyboard): q w e r t y u i o p / ' ק ר א ט ו ן ם פ @@ -80,11 +68,8 @@ Details z x c v b n m , . / ז ס ב ה נ מ צ ת ץ . - This is also the keymap when 'keymap=hebrew' is set. The advantage of - 'keymap' is that it works properly when using UTF8, e.g. it inserts the - correct characters; 'hkmap' does not. The 'keymap' keyboard can also - insert niqud and te`amim. To see what those mappings are, look at the - keymap file 'hebrew.vim' etc. + The 'keymap' keyboard can also insert niqud and te`amim. To see what + those mappings are, look at the keymap file hebrew.vim etc. Typing backwards @@ -102,13 +87,7 @@ If the 'showmode' option is set, "-- REVERSE INSERT --" will be shown in the status line when reverse Insert mode is active. When the 'allowrevins' option is set, reverse Insert mode can be also entered -via CTRL-_, which has some extra functionality: First, keyboard mapping is -changed according to the window orientation -- if in a left-to-right window, -'revins' is used to enter Hebrew text, so the keyboard changes to Hebrew -('hkmap' is set); if in a right-to-left window, 'revins' is used to enter -English text, so the keyboard changes to English ('hkmap' is reset). Second, -when exiting 'revins' via CTRL-_, the cursor moves to the end of the typed -text (if possible). +and exited via CTRL-_. ------------------------------------------------------------------------------ diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index 7545d2c621..43f80101ed 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -114,6 +114,7 @@ API (EXTENSIBILITY/SCRIPTING/PLUGINS) |vimscript-functions| Vimscript functions |testing.txt| Vimscript testing functions |remote-plugin| Nvim remote plugins +|health| Health checking ------------------------------------------------------------------------------ PROGRAMMING LANGUAGE SUPPORT @@ -175,7 +176,6 @@ DEVELOPING NVIM Standard plugins ~ *standard-plugin-list* |pi_gzip.txt| Reading and writing compressed files -|pi_health.txt| Healthcheck framework |pi_msgpack.txt| msgpack utilities |pi_netrw.txt| Reading and writing files over a network |pi_paren.txt| Highlight matching parens diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index efcc6afae9..46b3ab507d 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -305,7 +305,7 @@ Hints for translators: using the "tag@en" notation. - Make a package with all the files and the tags file available for download. Users can drop it in one of the "doc" directories and start use it. - Report this to Bram, so that he can add a link on www.vim.org. + Report to the development team, so they can add a link on www.vim.org. - Use the |:helptags| command to generate the tags files. It will find all languages in the specified directory. diff --git a/runtime/doc/indent.txt b/runtime/doc/indent.txt index c20143bc6e..a890d531ac 100644 --- a/runtime/doc/indent.txt +++ b/runtime/doc/indent.txt @@ -1232,5 +1232,5 @@ By default, the yaml indent script does not try to detect multiline scalars. If you want to enable this, set the following variable: > let g:yaml_indent_multiline_scalar = 1 - +< vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index c1a8aec40e..79f10b33f1 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -92,8 +92,7 @@ tag char action in Insert mode ~ CTRL-\ others not used |i_CTRL-]| CTRL-] trigger abbreviation |i_CTRL-^| CTRL-^ toggle use of |:lmap| mappings -|i_CTRL-_| CTRL-_ When 'allowrevins' set: change language - (Hebrew) +|i_CTRL-_| CTRL-_ When 'allowrevins' set: toggle 'revins' <Space> to '~' not used, except '0' and '^' followed by CTRL-D @@ -699,7 +698,6 @@ tag char note action in Normal mode ~ tag char note action in Normal mode ~ ------------------------------------------------------------------------------ ~ -g_CTRL-A g CTRL-A dump a memory profile |g_CTRL-G| g CTRL-G show information about current cursor position |g_CTRL-H| g CTRL-H start Select block mode @@ -1073,8 +1071,6 @@ tag command action in Command-line editing mode ~ CTRL-\ others not used |c_CTRL-]| CTRL-] trigger abbreviation |c_CTRL-^| CTRL-^ toggle use of |:lmap| mappings -|c_CTRL-_| CTRL-_ when 'allowrevins' set: change language - (Hebrew) |c_<Del>| <Del> delete the character under the cursor |c_<Left>| <Left> cursor left diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 34affb22e9..91b0d41f1c 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -225,11 +225,7 @@ CTRL-Y Insert the character which is above the cursor. able to copy characters from a long line. *i_CTRL-_* -CTRL-_ Switch between insert direction, by toggling 'revins', as follows: - - When in a rightleft window, 'revins' is toggled, - since English will likely be inserted in this case. - - When in a norightleft window, 'revins' is toggled, - since a rightleft language will likely be inserted in this case. +CTRL-_ Switch between insert direction, by toggling 'revins'. CTRL-_ moves the cursor to the end of the typed text. diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index c9211291d0..4c757cc1f6 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -69,11 +69,11 @@ If Nvim crashes, try to get a backtrace. See |debug.txt|. Sponsor Vim/Nvim development *sponsor* *register* Fixing bugs and adding new features takes a lot of time and effort. To show -your appreciation for the work and motivate Bram and others to continue -working on Vim please send a donation. +your appreciation for the work and motivate developers to continue working on +Vim please send a donation. -Since Bram is back to a paid job the money will now be used to help children -in Uganda. See |uganda|. But at the same time donations increase Bram's +The money you donated will be mainly used to help children in Uganda. See +|uganda|. But at the same time donations increase the development team motivation to keep working on Vim! For the most recent information about sponsoring look on the Vim web site: diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index eb37cb2a6f..50fffca497 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -12,7 +12,7 @@ enhanced LSP tools. https://microsoft.github.io/language-server-protocol/ -LSP facilitates features like go-to-definition, find-references, hover, +LSP facilitates features like go-to-definition, find references, hover, completion, rename, format, refactor, etc., using semantic whole-project analysis (unlike |ctags|). @@ -25,26 +25,36 @@ Nvim provides an LSP client, but the servers are provided by third parties. Follow these steps to get LSP features: 1. Install language servers using your package manager or by following the - upstream installation instruction. You can find language servers here: + upstream installation instructions. You can find language servers here: https://microsoft.github.io/language-server-protocol/implementors/servers/ -2. Configure the LSP client per language server. See |vim.lsp.start()| or use - this minimal example as a guide: >lua - - vim.lsp.start({ - name = 'my-server-name', - cmd = {'name-of-language-server-executable'}, - root_dir = vim.fs.dirname(vim.fs.find({'setup.py', 'pyproject.toml'}, { upward = true })[1]), +2. Use |vim.lsp.start()| to start the LSP server (or attach to an existing + one) when a file is opened. Example: >lua + -- Create an event handler for the FileType autocommand + vim.api.nvim_create_autocmd('FileType', { + -- This handler will fire when the buffer's 'filetype' is "python" + pattern = 'python', + callback = function(ev) + vim.lsp.start({ + name = 'my-server-name', + cmd = {'name-of-language-server-executable', '--option', 'arg1', 'arg2'}, + + -- Set the "root directory" to the parent directory of the file in the + -- current buffer (`ev.buf`) that contains either a "setup.py" or a + -- "pyproject.toml" file. Files that share a root directory will reuse + -- the connection to the same LSP server. + root_dir = vim.fs.root(ev.buf, {'setup.py', 'pyproject.toml'}), + }) + end, }) < -3. Check that the server attached to the buffer: > - :lua =vim.lsp.get_clients() +3. Check that the buffer is attached to the server: >vim + :checkhealth lsp -4. Configure keymaps and autocmds to use LSP features. See |lsp-config|. +4. (Optional) Configure keymaps and autocommands to use LSP features. |lsp-config| - *lsp-config* *lsp-defaults* -When the LSP client starts it enables diagnostics |vim.diagnostic| (see +When the Nvim LSP client starts it enables diagnostics |vim.diagnostic| (see |vim.diagnostic.config()| to customize). It also sets various default options, listed below, if (1) the language server supports the functionality and (2) the options are empty or were set by the builtin runtime (ftplugin) files. The @@ -57,51 +67,47 @@ options are not restored when the LSP client is stopped or detached. |CTRL-W_}| to utilize the language server. - 'formatexpr' is set to |vim.lsp.formatexpr()|, so you can format lines via |gq| if the language server supports it. - - To opt out of this use |gw| instead of gq, or set 'formatexpr' on LspAttach. + - To opt out of this use |gw| instead of gq, or clear 'formatexpr' on |LspAttach|. - |K| is mapped to |vim.lsp.buf.hover()| unless |'keywordprg'| is customized or a custom keymap for `K` exists. + *grr* *gra* *grn* *i_CTRL-S* +Some keymaps are created unconditionally when Nvim starts: +- "grn" is mapped in Normal mode to |vim.lsp.buf.rename()| +- "gra" is mapped in Normal and Visual mode to |vim.lsp.buf.code_action()| +- "grr" is mapped in Normal mode to |vim.lsp.buf.references()| +- CTRL-S is mapped in Insert mode to |vim.lsp.buf.signature_help()| + +If not wanted, these keymaps can be removed at any time using +|vim.keymap.del()| or |:unmap| (see also |gr-default|). + *lsp-defaults-disable* -To override the above defaults, set or unset the options on |LspAttach|: >lua +To override or delete any of the above defaults, set or unset the options on +|LspAttach|: >lua + vim.api.nvim_create_autocmd('LspAttach', { callback = function(ev) vim.bo[ev.buf].formatexpr = nil vim.bo[ev.buf].omnifunc = nil - vim.keymap.del("n", "K", { buffer = ev.buf }) - end, - }) - -To use other LSP features like hover, rename, etc. you can set other keymaps -on |LspAttach|. Example: >lua - vim.api.nvim_create_autocmd('LspAttach', { - callback = function(args) - vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = args.buf }) + vim.keymap.del('n', 'K', { buffer = ev.buf }) end, }) +< + *lsp-config* +To use other LSP features, set keymaps on |LspAttach|. Not all language +servers provide the same capabilities. To ensure you only set keymaps if the +language server supports a feature, guard keymaps behind capability checks. +Example: >lua -The most common functions are: - -- |vim.lsp.buf.hover()| -- |vim.lsp.buf.format()| -- |vim.lsp.buf.references()| -- |vim.lsp.buf.implementation()| -- |vim.lsp.buf.code_action()| - - -Not all language servers provide the same capabilities. To ensure you only set -keymaps if the language server supports a feature, you can guard the keymap -calls behind capability checks: ->lua vim.api.nvim_create_autocmd('LspAttach', { callback = function(args) local client = vim.lsp.get_client_by_id(args.data.client_id) - if client.server_capabilities.hoverProvider then - vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = args.buf }) + if client.supports_method('textDocument/implementation') then + -- Create a keymap for vim.lsp.buf.implementation end end, }) < - To learn what capabilities are available you can run the following command in a buffer with a started LSP client: >vim @@ -182,6 +188,7 @@ won't run if your server doesn't support them. - textDocument/hover - textDocument/implementation* - textDocument/inlayHint +- textDocument/prepareTypeHierarchy - textDocument/publishDiagnostics - textDocument/rangeFormatting - textDocument/references @@ -190,6 +197,8 @@ won't run if your server doesn't support them. - textDocument/semanticTokens/full/delta - textDocument/signatureHelp - textDocument/typeDefinition* +- typeHierarchy/subtypes +- typeHierarchy/supertypes - window/logMessage - window/showMessage - window/showDocument @@ -454,23 +463,44 @@ You probably want these inside a |ColorScheme| autocommand. Use |LspTokenUpdate| and |vim.lsp.semantic_tokens.highlight_token()| for more complex highlighting. -The following groups are linked by default to standard |group-name|s: - -@lsp.type.class Structure -@lsp.type.decorator Function -@lsp.type.enum Structure -@lsp.type.enumMember Constant -@lsp.type.function Function -@lsp.type.interface Structure -@lsp.type.macro Macro -@lsp.type.method Function -@lsp.type.namespace Structure -@lsp.type.parameter Identifier -@lsp.type.property Identifier -@lsp.type.struct Structure -@lsp.type.type Type -@lsp.type.typeParameter TypeDef -@lsp.type.variable Identifier +The following is a list of standard captures used in queries for Nvim, +highlighted according to the current colorscheme (use |:Inspect| on one to see +the exact definition): + +@lsp.type.class Identifiers that declare or reference a class type +@lsp.type.comment Tokens that represent a comment +@lsp.type.decorator Identifiers that declare or reference decorators and annotations +@lsp.type.enum Identifiers that declare or reference an enumeration type +@lsp.type.enumMember Identifiers that declare or reference an enumeration property, constant, or member +@lsp.type.event Identifiers that declare an event property +@lsp.type.function Identifiers that declare a function +@lsp.type.interface Identifiers that declare or reference an interface type +@lsp.type.keyword Tokens that represent a language keyword +@lsp.type.macro Identifiers that declare a macro +@lsp.type.method Identifiers that declare a member function or method +@lsp.type.modifier Tokens that represent a modifier +@lsp.type.namespace Identifiers that declare or reference a namespace, module, or package +@lsp.type.number Tokens that represent a number literal +@lsp.type.operator Tokens that represent an operator +@lsp.type.parameter Identifiers that declare or reference a function or method parameters +@lsp.type.property Identifiers that declare or reference a member property, member field, or member variable +@lsp.type.regexp Tokens that represent a regular expression literal +@lsp.type.string Tokens that represent a string literal +@lsp.type.struct Identifiers that declare or reference a struct type +@lsp.type.type Identifiers that declare or reference a type that is not covered above +@lsp.type.typeParameter Identifiers that declare or reference a type parameter +@lsp.type.variable Identifiers that declare or reference a local or global variable + +@lsp.mod.abstract Types and member functions that are abstract +@lsp.mod.async Functions that are marked async +@lsp.mod.declaration Declarations of symbols +@lsp.mod.defaultLibrary Symbols that are part of the standard library +@lsp.mod.definition Definitions of symbols, for example, in header files +@lsp.mod.deprecated Symbols that should no longer be used +@lsp.mod.documentation Occurrences of symbols in documentation +@lsp.mod.modification Variable references where the variable is assigned to +@lsp.mod.readonly Readonly variables and member fields (constants) +@lsp.mod.static Class members (static members) ============================================================================== EVENTS *lsp-events* @@ -533,14 +563,14 @@ LspNotify *LspNotify* LspProgress *LspProgress* Upon receipt of a progress notification from the server. Notifications can be polled from a `progress` ring buffer of a |vim.lsp.Client| or use - |vim.lsp.status()| to get an aggregate message + |vim.lsp.status()| to get an aggregate message. If the server sends a "work done progress", the `pattern` is set to `kind` (one of `begin`, `report` or `end`). When used from Lua, the event contains a `data` table with `client_id` and - `result` properties. `result` will contain the request params sent by the - server. + `params` properties. `params` will contain the request params sent by the + server (see `lsp.ProgressParams`). Example: >vim autocmd LspProgress * redrawstatus @@ -683,8 +713,8 @@ buf_request_sync({bufnr}, {method}, {params}, {timeout_ms}) milliseconds to wait for a result. Return (multiple): ~ - (`table<integer, {err: lsp.ResponseError, result: any}>?`) result Map - of client_id:request_result. + (`table<integer, {error: lsp.ResponseError?, result: any}>?`) result + Map of client_id:request_result. (`string?`) err On timeout, cancel, or error, `err` is a string describing the failure reason, and `result` is nil. @@ -812,7 +842,7 @@ start({config}, {opts}) *vim.lsp.start()* vim.lsp.start({ name = 'my-server-name', cmd = {'name-of-language-server-executable'}, - root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]), + root_dir = vim.fs.root(0, {'pyproject.toml', 'setup.py'}), }) < @@ -824,7 +854,7 @@ start({config}, {opts}) *vim.lsp.start()* |vim.lsp.start_client()|. • `root_dir` path to the project root. By default this is used to decide if an existing client should be re-used. The example above uses - |vim.fs.find()| and |vim.fs.dirname()| to detect the root by traversing + |vim.fs.root()| and |vim.fs.dirname()| to detect the root by traversing the file system upwards starting from the current directory until either a `pyproject.toml` or `setup.py` file is found. • `workspace_folders` list of `{ uri:string, name: string }` tables @@ -845,12 +875,14 @@ start({config}, {opts}) *vim.lsp.start()* |vim.lsp.ClientConfig|. • {opts} (`table?`) Optional keyword arguments • {reuse_client} - (`fun(client: vim.lsp.Client, config: table): boolean`) + (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`) Predicate used to decide if a client should be re-used. Used on all running clients. The default implementation re-uses a client if name and root_dir matches. • {bufnr} (`integer`) Buffer handle to attach to if starting or re-using a client (0 for current). + • {silent}? (`boolean`) Suppress error reporting if the LSP + server fails to start (default false). Return: ~ (`integer?`) client_id @@ -862,10 +894,11 @@ start_client({config}) *vim.lsp.start_client()* • {config} (`vim.lsp.ClientConfig`) Configuration for the server. See |vim.lsp.ClientConfig|. - Return: ~ + Return (multiple): ~ (`integer?`) client_id |vim.lsp.get_client_by_id()| Note: client may not be fully initialized. Use `on_init` to do any actions once the client has been initialized. + (`string?`) Error message, if any status() *vim.lsp.status()* Consumes the latest progress messages from all clients and formats them as @@ -886,8 +919,8 @@ stop_client({client_id}, {force}) *vim.lsp.stop_client()* for this client, then force-shutdown is attempted. Parameters: ~ - • {client_id} (`integer|vim.lsp.Client`) id or |vim.lsp.Client| object, - or list thereof + • {client_id} (`integer|integer[]|vim.lsp.Client[]`) id, list of id's, + or list of |vim.lsp.Client| objects • {force} (`boolean?`) shutdown forcefully tagfunc({pattern}, {flags}) *vim.lsp.tagfunc()* @@ -978,9 +1011,9 @@ Lua module: vim.lsp.client *lsp-client* case-sensitive. • {flags} (`table`) A table with flags for the client. The current (experimental) flags are: - • {allow_incremental_sync}? (`boolean`) Allow - using incremental sync for buffer edits - (defailt: `true`) + • {allow_incremental_sync}? (`boolean`, + default: `true`) Allow using incremental + sync for buffer edits • {debounce_text_changes} (`integer`, default: `150`) Debounce `didChange` notifications to the server by the given number in @@ -995,7 +1028,7 @@ Lua module: vim.lsp.client *lsp-client* • {capabilities} (`lsp.ClientCapabilities`) The capabilities provided by the client (editor or tool) • {dynamic_capabilities} (`lsp.DynamicCapabilities`) - • {request} (`fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer): boolean, integer?`) + • {request} (`fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`) Sends a request to the server. This is a thin wrapper around {client.rpc.request} with some additional checking. If {handler} is not @@ -1049,7 +1082,7 @@ Lua module: vim.lsp.client *lsp-client* *vim.lsp.ClientConfig* Fields: ~ - • {cmd} (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient?`) + • {cmd} (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`) command string[] that launches the language server (treated as in |jobstart()|, must be absolute or on `$PATH`, shell constructs like @@ -1149,9 +1182,9 @@ Lua module: vim.lsp.client *lsp-client* initialize request. Invalid/empty values will • {flags}? (`table`) A table with flags for the client. The current (experimental) flags are: - • {allow_incremental_sync}? (`boolean`) Allow - using incremental sync for buffer edits - (defailt: `true`) + • {allow_incremental_sync}? (`boolean`, default: + `true`) Allow using incremental sync for + buffer edits • {debounce_text_changes} (`integer`, default: `150`) Debounce `didChange` notifications to the server by the given number in @@ -1187,12 +1220,11 @@ Lua module: vim.lsp.buf *lsp-buf* vim.lsp.buf.references(nil, { on_list = on_list }) < - If you prefer loclist do something like this: >lua - local function on_list(options) - vim.fn.setloclist(0, {}, ' ', options) - vim.cmd.lopen() - end + If you prefer loclist instead of qflist: >lua + vim.lsp.buf.definition({ loclist = true }) + vim.lsp.buf.references(nil, { loclist = true }) < + • {loclist}? (`boolean`) *vim.lsp.LocationOpts* Extends: |vim.lsp.ListOpts| @@ -1221,30 +1253,30 @@ add_workspace_folder({workspace_folder}) clear_references() *vim.lsp.buf.clear_references()* Removes document highlights from current buffer. -code_action({options}) *vim.lsp.buf.code_action()* +code_action({opts}) *vim.lsp.buf.code_action()* Selects a code action available at the current cursor position. Parameters: ~ - • {options} (`table?`) A table with the following fields: - • {context}? (`lsp.CodeActionContext`) Corresponds to - `CodeActionContext` of the LSP specification: - • {diagnostics}? (`table`) LSP `Diagnostic[]`. Inferred - from the current position if not provided. - • {only}? (`table`) List of LSP `CodeActionKind`s used to - filter the code actions. Most language servers support - values like `refactor` or `quickfix`. - • {triggerKind}? (`integer`) The reason why code actions - were requested. - • {filter}? (`fun(x: lsp.CodeAction|lsp.Command):boolean`) - Predicate taking an `CodeAction` and returning a boolean. - • {apply}? (`boolean`) When set to `true`, and there is - just one remaining action (after filtering), the action - is applied without user query. - • {range}? (`{start: integer[], end: integer[]}`) Range for - which code actions should be requested. If in visual mode - this defaults to the active selection. Table must contain - `start` and `end` keys with {row,col} tuples using - mark-like indexing. See |api-indexing| + • {opts} (`table?`) A table with the following fields: + • {context}? (`lsp.CodeActionContext`) Corresponds to + `CodeActionContext` of the LSP specification: + • {diagnostics}? (`table`) LSP `Diagnostic[]`. Inferred from + the current position if not provided. + • {only}? (`table`) List of LSP `CodeActionKind`s used to + filter the code actions. Most language servers support + values like `refactor` or `quickfix`. + • {triggerKind}? (`integer`) The reason why code actions + were requested. + • {filter}? (`fun(x: lsp.CodeAction|lsp.Command):boolean`) + Predicate taking an `CodeAction` and returning a boolean. + • {apply}? (`boolean`) When set to `true`, and there is just + one remaining action (after filtering), the action is + applied without user query. + • {range}? (`{start: integer[], end: integer[]}`) Range for + which code actions should be requested. If in visual mode + this defaults to the active selection. Table must contain + `start` and `end` keys with {row,col} tuples using mark-like + indexing. See |api-indexing| See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction @@ -1263,7 +1295,7 @@ completion({context}) *vim.lsp.buf.completion()* See also: ~ • vim.lsp.protocol.CompletionTriggerKind -declaration({options}) *vim.lsp.buf.declaration()* +declaration({opts}) *vim.lsp.buf.declaration()* Jumps to the declaration of the symbol under the cursor. Note: ~ @@ -1271,13 +1303,13 @@ declaration({options}) *vim.lsp.buf.declaration()* |vim.lsp.buf.definition()| instead. Parameters: ~ - • {options} (`vim.lsp.LocationOpts?`) See |vim.lsp.LocationOpts|. + • {opts} (`vim.lsp.LocationOpts?`) See |vim.lsp.LocationOpts|. -definition({options}) *vim.lsp.buf.definition()* +definition({opts}) *vim.lsp.buf.definition()* Jumps to the definition of the symbol under the cursor. Parameters: ~ - • {options} (`vim.lsp.LocationOpts?`) See |vim.lsp.LocationOpts|. + • {opts} (`vim.lsp.LocationOpts?`) See |vim.lsp.LocationOpts|. document_highlight() *vim.lsp.buf.document_highlight()* Send request to the server to resolve document highlights for the current @@ -1293,11 +1325,11 @@ document_highlight() *vim.lsp.buf.document_highlight()* highlights. |hl-LspReferenceText| |hl-LspReferenceRead| |hl-LspReferenceWrite| -document_symbol({options}) *vim.lsp.buf.document_symbol()* +document_symbol({opts}) *vim.lsp.buf.document_symbol()* Lists all symbols in the current buffer in the quickfix window. Parameters: ~ - • {options} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|. + • {opts} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|. execute_command({command_params}) *vim.lsp.buf.execute_command()* Executes an LSP server command. @@ -1308,53 +1340,53 @@ execute_command({command_params}) *vim.lsp.buf.execute_command()* See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand -format({options}) *vim.lsp.buf.format()* +format({opts}) *vim.lsp.buf.format()* Formats a buffer using the attached (and optionally filtered) language server clients. Parameters: ~ - • {options} (`table?`) A table with the following fields: - • {formatting_options}? (`table`) Can be used to specify - FormattingOptions. Some unspecified options will be - automatically derived from the current Nvim options. See - https://microsoft.github.io/language-server-protocol/specification/#formattingOptions - • {timeout_ms}? (`integer`, default: `1000`) Time in - milliseconds to block for formatting requests. No effect - if async=true. - • {bufnr}? (`integer`, default: current buffer) Restrict - formatting to the clients attached to the given buffer. - • {filter}? (`fun(client: vim.lsp.Client): boolean?`) - Predicate used to filter clients. Receives a client as - argument and must return a boolean. Clients matching the - predicate are included. Example: >lua - -- Never request typescript-language-server for formatting - vim.lsp.buf.format { - filter = function(client) return client.name ~= "tsserver" end - } + • {opts} (`table?`) A table with the following fields: + • {formatting_options}? (`table`) Can be used to specify + FormattingOptions. Some unspecified options will be + automatically derived from the current Nvim options. See + https://microsoft.github.io/language-server-protocol/specification/#formattingOptions + • {timeout_ms}? (`integer`, default: `1000`) Time in + milliseconds to block for formatting requests. No effect if + async=true. + • {bufnr}? (`integer`, default: current buffer) Restrict + formatting to the clients attached to the given buffer. + • {filter}? (`fun(client: vim.lsp.Client): boolean?`) + Predicate used to filter clients. Receives a client as + argument and must return a boolean. Clients matching the + predicate are included. Example: >lua + -- Never request typescript-language-server for formatting + vim.lsp.buf.format { + filter = function(client) return client.name ~= "tsserver" end + } < - • {async}? (`boolean`, default: false) If true the method - won't block. Editing the buffer while formatting - asynchronous can lead to unexpected changes. - • {id}? (`integer`) Restrict formatting to the client with - ID (client.id) matching this field. - • {name}? (`string`) Restrict formatting to the client with - name (client.name) matching this field. - • {range}? (`{start:integer[],end:integer[]}`, default: - current selection in visual mode, `nil` in other modes, - formatting the full buffer) Range to format. Table must - contain `start` and `end` keys with {row,col} tuples - using (1,0) indexing. + • {async}? (`boolean`, default: false) If true the method + won't block. Editing the buffer while formatting + asynchronous can lead to unexpected changes. + • {id}? (`integer`) Restrict formatting to the client with ID + (client.id) matching this field. + • {name}? (`string`) Restrict formatting to the client with + name (client.name) matching this field. + • {range}? (`{start:integer[],end:integer[]}`, default: + current selection in visual mode, `nil` in other modes, + formatting the full buffer) Range to format. Table must + contain `start` and `end` keys with {row,col} tuples using + (1,0) indexing. hover() *vim.lsp.buf.hover()* Displays hover information about the symbol under the cursor in a floating window. Calling the function twice will jump into the floating window. -implementation({options}) *vim.lsp.buf.implementation()* +implementation({opts}) *vim.lsp.buf.implementation()* Lists all the implementations for the symbol under the cursor in the quickfix window. Parameters: ~ - • {options} (`vim.lsp.LocationOpts?`) See |vim.lsp.LocationOpts|. + • {opts} (`vim.lsp.LocationOpts?`) See |vim.lsp.LocationOpts|. incoming_calls() *vim.lsp.buf.incoming_calls()* Lists all the call sites of the symbol under the cursor in the |quickfix| @@ -1369,13 +1401,13 @@ outgoing_calls() *vim.lsp.buf.outgoing_calls()* |quickfix| window. If the symbol can resolve to multiple items, the user can pick one in the |inputlist()|. -references({context}, {options}) *vim.lsp.buf.references()* +references({context}, {opts}) *vim.lsp.buf.references()* Lists all the references to the symbol under the cursor in the quickfix window. Parameters: ~ • {context} (`table?`) Context for the request - • {options} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|. + • {opts} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|. See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references @@ -1388,13 +1420,13 @@ remove_workspace_folder({workspace_folder}) Parameters: ~ • {workspace_folder} (`string?`) -rename({new_name}, {options}) *vim.lsp.buf.rename()* +rename({new_name}, {opts}) *vim.lsp.buf.rename()* Renames all references to the symbol under the cursor. Parameters: ~ • {new_name} (`string?`) If not provided, the user will be prompted for a new name using |vim.ui.input()|. - • {options} (`table?`) Additional options: + • {opts} (`table?`) Additional options: • {filter}? (`fun(client: vim.lsp.Client): boolean?`) Predicate used to filter clients. Receives a client as argument and must return a boolean. Clients matching the @@ -1407,13 +1439,21 @@ signature_help() *vim.lsp.buf.signature_help()* Displays signature information about the symbol under the cursor in a floating window. -type_definition({options}) *vim.lsp.buf.type_definition()* +type_definition({opts}) *vim.lsp.buf.type_definition()* Jumps to the definition of the type of the symbol under the cursor. Parameters: ~ - • {options} (`vim.lsp.LocationOpts?`) See |vim.lsp.LocationOpts|. + • {opts} (`vim.lsp.LocationOpts?`) See |vim.lsp.LocationOpts|. -workspace_symbol({query}, {options}) *vim.lsp.buf.workspace_symbol()* +typehierarchy({kind}) *vim.lsp.buf.typehierarchy()* + Lists all the subtypes or supertypes of the symbol under the cursor in the + |quickfix| window. If the symbol can resolve to multiple items, the user + can pick one using |vim.ui.select()|. + + Parameters: ~ + • {kind} (`"subtypes"|"supertypes"`) + +workspace_symbol({query}, {opts}) *vim.lsp.buf.workspace_symbol()* Lists all symbols in the current workspace in the quickfix window. The list is filtered against {query}; if the argument is omitted from the @@ -1421,8 +1461,8 @@ workspace_symbol({query}, {options}) *vim.lsp.buf.workspace_symbol()* string means no filtering is done. Parameters: ~ - • {query} (`string?`) optional - • {options} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|. + • {query} (`string?`) optional + • {opts} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|. ============================================================================== @@ -1567,19 +1607,18 @@ save({lenses}, {bufnr}, {client_id}) *vim.lsp.codelens.save()* ============================================================================== Lua module: vim.lsp.inlay_hint *lsp-inlay_hint* -enable({bufnr}, {enable}) *vim.lsp.inlay_hint.enable()* - Enables or disables inlay hints for a buffer. +enable({enable}, {filter}) *vim.lsp.inlay_hint.enable()* + Enables or disables inlay hints for the {filter}ed scope. To "toggle", pass the inverse of `is_enabled()`: >lua - vim.lsp.inlay_hint.enable(0, not vim.lsp.inlay_hint.is_enabled()) + vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled()) < - Note: ~ - • This API is pre-release (unstable). - Parameters: ~ - • {bufnr} (`integer?`) Buffer handle, or 0 or nil for current • {enable} (`boolean?`) true/nil to enable, false to disable + • {filter} (`table?`) Optional filters |kwargs|, or `nil` for all. + • {bufnr} (`integer?`) Buffer number, or 0 for current + buffer, or nil for all. get({filter}) *vim.lsp.inlay_hint.get()* Get the list of inlay hints, (optionally) restricted by buffer or range. @@ -1588,7 +1627,8 @@ get({filter}) *vim.lsp.inlay_hint.get()* local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer local client = vim.lsp.get_client_by_id(hint.client_id) - resolved_hint = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0).result + local resp = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) + local resolved_hint = assert(resp and resp.result, resp.err) vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding) location = resolved_hint.label[1].location @@ -1598,9 +1638,6 @@ get({filter}) *vim.lsp.inlay_hint.get()* }) < - Note: ~ - • This API is pre-release (unstable). - Parameters: ~ • {filter} (`table?`) Optional filters |kwargs|: • {bufnr} (`integer?`) @@ -1612,13 +1649,13 @@ get({filter}) *vim.lsp.inlay_hint.get()* • {client_id} (`integer`) • {inlay_hint} (`lsp.InlayHint`) -is_enabled({bufnr}) *vim.lsp.inlay_hint.is_enabled()* - - Note: ~ - • This API is pre-release (unstable). +is_enabled({filter}) *vim.lsp.inlay_hint.is_enabled()* + Query whether inlay hint is enabled in the {filter}ed scope Parameters: ~ - • {bufnr} (`integer?`) Buffer handle, or 0 or nil for current + • {filter} (`table`) Optional filters |kwargs|, or `nil` for all. + • {bufnr} (`integer?`) Buffer number, or 0 for current + buffer, or nil for all. Return: ~ (`boolean`) @@ -1668,9 +1705,10 @@ highlight_token({token}, {bufnr}, {client_id}, {hl_group}, {opts}) use inside |LspTokenUpdate| callbacks. Parameters: ~ - • {token} (`table`) a semantic token, found as `args.data.token` in - |LspTokenUpdate|. - • {bufnr} (`integer`) the buffer to highlight + • {token} (`table`) A semantic token, found as `args.data.token` in + |LspTokenUpdate| + • {bufnr} (`integer`) The buffer to highlight, or `0` for current + buffer • {client_id} (`integer`) The ID of the |vim.lsp.Client| • {hl_group} (`string`) Highlight group name • {opts} (`table?`) Optional parameters: @@ -1691,8 +1729,8 @@ start({bufnr}, {client_id}, {opts}) *vim.lsp.semantic_tokens.start()* < Parameters: ~ - • {bufnr} (`integer`) - • {client_id} (`integer`) + • {bufnr} (`integer`) Buffer number, or `0` for current buffer + • {client_id} (`integer`) The ID of the |vim.lsp.Client| • {opts} (`table?`) Optional keyword arguments • debounce (integer, default: 200): Debounce token requests to the server by the given number in @@ -1708,8 +1746,8 @@ stop({bufnr}, {client_id}) *vim.lsp.semantic_tokens.stop()* from the buffer. Parameters: ~ - • {bufnr} (`integer`) - • {client_id} (`integer`) + • {bufnr} (`integer`) Buffer number, or `0` for current buffer + • {client_id} (`integer`) The ID of the |vim.lsp.Client| ============================================================================== @@ -2096,7 +2134,7 @@ rename({old_fname}, {new_fname}, {opts}) *vim.lsp.util.rename()* It deletes existing buffers that conflict with the renamed file name only when • `opts` requests overwriting; or - • the conflicting buffers are not loaded, so that deleting thme does not + • the conflicting buffers are not loaded, so that deleting them does not result in data loss. Parameters: ~ @@ -2208,32 +2246,20 @@ Lua module: vim.lsp.rpc *lsp-rpc* • {terminate} (`fun()`) -connect({host}, {port}) *vim.lsp.rpc.connect()* - Create a LSP RPC client factory that connects via TCP to the given host - and port. - - Return a function that can be passed to the `cmd` field for - |vim.lsp.start_client()| or |vim.lsp.start()|. - - Parameters: ~ - • {host} (`string`) host to connect to - • {port} (`integer`) port to connect to - - Return: ~ - (`fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`) - - *vim.lsp.rpc.domain_socket_connect()* -domain_socket_connect({pipe_path}) - Create a LSP RPC client factory that connects via named pipes (Windows) or - unix domain sockets (Unix) to the given pipe_path (file path on Unix and - name on Windows). +connect({host_or_path}, {port}) *vim.lsp.rpc.connect()* + Create a LSP RPC client factory that connects to either: + • a named pipe (windows) + • a domain socket (unix) + • a host and port via TCP Return a function that can be passed to the `cmd` field for |vim.lsp.start_client()| or |vim.lsp.start()|. Parameters: ~ - • {pipe_path} (`string`) file path of the domain socket (Unix) or name - of the named pipe (Windows) to connect to + • {host_or_path} (`string`) host to connect to or path to a pipe/domain + socket + • {port} (`integer?`) TCP port to connect to. If absent the + first argument must be a pipe Return: ~ (`fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`) @@ -2318,7 +2344,7 @@ start({cmd}, {dispatchers}, {extra_spawn_params}) *vim.lsp.rpc.start()* See |vim.system()| Return: ~ - (`vim.lsp.rpc.PublicClient?`) Client RPC object, with these methods: + (`vim.lsp.rpc.PublicClient`) Client RPC object, with these methods: • `notify()` |vim.lsp.rpc.notify()| • `request()` |vim.lsp.rpc.request()| • `is_closing()` returns a boolean indicating if the RPC is closing. diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 367b5c36d2..3d8453c5a2 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -34,10 +34,11 @@ Nvim ever ships with Lua 5.4+, a Lua 5.1 compatibility shim will be provided so that old plugins continue to work transparently. *lua-luajit* -Nvim is built with luajit on platforms which support it, which provides -extra functionality. Lua code in |init.lua| and plugins can assume its presence -on installations on common platforms. For maximum compatibility with less -common platforms, availability can be checked using the `jit` global variable: >lua +On supported platforms, Nvim is built with LuaJIT, which provides extra +functionality (compared to PUC Lua) such as "bit" and various utilities (see +|lua-profile|). Lua code in |init.lua| and plugins can assume its presence on +many platforms, but for maximum compatibility should check the `jit` global +variable: >lua if jit then -- code for luajit else @@ -45,9 +46,21 @@ common platforms, availability can be checked using the `jit` global variable: > end < *lua-bit* -In particular, the luajit "bit" extension module is _always_ available. -A fallback implementation is included when nvim is built with PUC Lua 5.1, -and will be transparently used when `require("bit")` is invoked. +The LuaJIT "bit" extension module is _always_ available: when built with PUC +Lua, Nvim includes a fallback implementation which provides `require("bit")`. + + *lua-profile* +To profile Lua code (with LuaJIT-enabled Nvim), the basic steps are: >lua + -- Start a profiling session: + require('jit.p').start('ri1', '/tmp/profile') + + -- Perform arbitrary tasks (use plugins, scripts, etc.) ... + + -- Stop the session. Profile is written to /tmp/profile. + require('jit.p').stop() + +See https://luajit.org/ext_profiler.html or the "p.lua" source for details: > + :lua vim.cmd.edit(package.searchpath('jit.p', package.path)) ============================================================================== LUA CONCEPTS AND IDIOMS *lua-concepts* @@ -244,8 +257,8 @@ arguments separated by " " (space) instead of "\t" (tab). *:lua=* *:lua* :lua {chunk} Executes Lua chunk {chunk}. If {chunk} starts with "=" the rest of the - chunk is evaluated as an expression and printed. `:lua =expr` or `:=expr` is - equivalent to `:lua print(vim.inspect(expr))`. + chunk is evaluated as an expression and printed. `:lua =expr` and `:=expr` + are equivalent to `:lua vim.print(expr)`. Examples: >vim :lua vim.api.nvim_command('echo "Hello, Nvim!"') @@ -530,6 +543,16 @@ Example: File-change detection *watch-file* vim.api.nvim_command( "command! -nargs=1 Watch call luaeval('watch_file(_A)', expand('<args>'))") < + *fswatch-limitations* +When on Linux and using fswatch, you may need to increase the maximum number +of `inotify` watches and queued events as the default limit can be too low. To +increase the limit, run: >sh + sysctl fs.inotify.max_user_watches=100000 + sysctl fs.inotify.max_queued_events=100000 +< +This will increase the limit to 100000 watches and queued events. These lines +can be added to `/etc/sysctl.conf` to make the changes persistent. + Example: TCP echo-server *tcp-server* 1. Save this code to a file. 2. Execute it with ":luafile %". @@ -582,24 +605,13 @@ A subset of the `vim.*` API is available in threads. This includes: ============================================================================== VIM.HIGHLIGHT *vim.highlight* -Nvim includes a function for highlighting a selection on yank. - -To enable it, add the following to your `init.vim`: >vim - au TextYankPost * silent! lua vim.highlight.on_yank() -< - -You can customize the highlight group and the duration of the highlight via: >vim - au TextYankPost * silent! lua vim.highlight.on_yank {higroup="IncSearch", timeout=150} -< +vim.highlight.on_yank({opts}) *vim.highlight.on_yank()* + Highlight the yanked text during a |TextYankPost| event. -If you want to exclude visual selections from highlighting on yank, use: >vim - au TextYankPost * silent! lua vim.highlight.on_yank {on_visual=false} + Add the following to your `init.vim`: >vim + autocmd TextYankPost * silent! lua vim.highlight.on_yank {higroup='Visual', timeout=300} < - -vim.highlight.on_yank({opts}) *vim.highlight.on_yank()* - Highlight the yanked text - Parameters: ~ • {opts} (`table?`) Optional parameters • higroup highlight group for yanked region (default @@ -633,12 +645,14 @@ vim.highlight.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {opts}) tuple or string accepted by |getpos()| • {finish} (`integer[]|string`) End of region as a (line, column) tuple or string accepted by |getpos()| - • {opts} (`table?`) Optional parameters - • regtype type of range (see |setreg()|, default charwise) - • inclusive boolean indicating whether the range is - end-inclusive (default false) - • priority number indicating priority of highlight (default - priorities.user) + • {opts} (`table?`) A table with the following fields: + • {regtype}? (`string`, default: `'charwise'`) Type of + range. See |setreg()| + • {inclusive}? (`boolean`, default: `false`) Indicates + whether the range is end-inclusive + • {priority}? (`integer`, default: + `vim.highlight.priorities.user`) Indicates priority of + highlight ============================================================================== @@ -666,44 +680,47 @@ vim.diff({a}, {b}, {opts}) *vim.diff()* Parameters: ~ • {a} (`string`) First string to compare • {b} (`string`) Second string to compare - • {opts} (`table<string,any>`) Optional parameters: - • `on_hunk` (callback): Invoked for each hunk in the diff. - Return a negative number to cancel the callback for any - remaining hunks. Args: - • `start_a` (integer): Start line of hunk in {a}. - • `count_a` (integer): Hunk size in {a}. - • `start_b` (integer): Start line of hunk in {b}. - • `count_b` (integer): Hunk size in {b}. - • `result_type` (string): Form of the returned diff: - • "unified": (default) String in unified format. - • "indices": Array of hunk locations. Note: This option is + • {opts} (`table`) Optional parameters: + • {on_hunk} + (`fun(start_a: integer, count_a: integer, start_b: integer, count_b: integer): integer`) + Invoked for each hunk in the diff. Return a negative number + to cancel the callback for any remaining hunks. Arguments: + • `start_a` (`integer`): Start line of hunk in {a}. + • `count_a` (`integer`): Hunk size in {a}. + • `start_b` (`integer`): Start line of hunk in {b}. + • `count_b` (`integer`): Hunk size in {b}. + • {result_type} (`'unified'|'indices'`, default: `'unified'`) + Form of the returned diff: + • `unified`: String in unified format. + • `indices`: Array of hunk locations. Note: This option is ignored if `on_hunk` is used. - • `linematch` (boolean|integer): Run linematch on the + • {linematch} (`boolean|integer`) Run linematch on the resulting hunks from xdiff. When integer, only hunks upto this size in lines are run through linematch. Requires `result_type = indices`, ignored otherwise. - • `algorithm` (string): Diff algorithm to use. Values: - • "myers" the default algorithm - • "minimal" spend extra time to generate the smallest + • {algorithm} (`'myers'|'minimal'|'patience'|'histogram'`, + default: `'myers'`) Diff algorithm to use. Values: + • `myers`: the default algorithm + • `minimal`: spend extra time to generate the smallest possible diff - • "patience" patience diff algorithm - • "histogram" histogram diff algorithm - • `ctxlen` (integer): Context length - • `interhunkctxlen` (integer): Inter hunk context length - • `ignore_whitespace` (boolean): Ignore whitespace - • `ignore_whitespace_change` (boolean): Ignore whitespace + • `patience`: patience diff algorithm + • `histogram`: histogram diff algorithm + • {ctxlen} (`integer`) Context length + • {interhunkctxlen} (`integer`) Inter hunk context length + • {ignore_whitespace} (`boolean`) Ignore whitespace + • {ignore_whitespace_change} (`boolean`) Ignore whitespace change - • `ignore_whitespace_change_at_eol` (boolean) Ignore + • {ignore_whitespace_change_at_eol} (`boolean`) Ignore whitespace change at end-of-line. - • `ignore_cr_at_eol` (boolean) Ignore carriage return at + • {ignore_cr_at_eol} (`boolean`) Ignore carriage return at end-of-line - • `ignore_blank_lines` (boolean) Ignore blank lines - • `indent_heuristic` (boolean): Use the indent heuristic for + • {ignore_blank_lines} (`boolean`) Ignore blank lines + • {indent_heuristic} (`boolean`) Use the indent heuristic for the internal diff library. Return: ~ - (`string|table?`) See {opts.result_type}. `nil` if {opts.on_hunk} is - given. + (`string|integer[]`) See {opts.result_type}. `nil` if {opts.on_hunk} + is given. ============================================================================== @@ -817,8 +834,8 @@ vim.spell.check({str}) *vim.spell.check()* • {str} (`string`) Return: ~ - (`{[1]: string, [2]: string, [3]: string}[]`) List of tuples with - three items: + (`{[1]: string, [2]: 'bad'|'rare'|'local'|'caps', [3]: integer}[]`) + List of tuples with three items: • The badly spelled word. • The type of the spelling error: "bad" spelling mistake "rare" rare word "local" word only valid in another region "caps" word should @@ -899,7 +916,7 @@ vim.empty_dict() *vim.empty_dict()* Return: ~ (`table`) -vim.iconv({str}, {from}, {to}, {opts}) *vim.iconv()* +vim.iconv({str}, {from}, {to}) *vim.iconv()* The result is a String, which is the text {str} converted from encoding {from} to encoding {to}. When the conversion fails `nil` is returned. When some characters could not be converted they are replaced with "?". The @@ -908,9 +925,8 @@ vim.iconv({str}, {from}, {to}, {opts}) *vim.iconv()* Parameters: ~ • {str} (`string`) Text to convert - • {from} (`number`) Encoding of {str} - • {to} (`number`) Target encoding - • {opts} (`table<string,any>?`) + • {from} (`string`) Encoding of {str} + • {to} (`string`) Target encoding Return: ~ (`string?`) Converted string if conversion succeeds, `nil` otherwise. @@ -950,20 +966,20 @@ vim.schedule({fn}) *vim.schedule()* |textlock| or other temporary restrictions. Parameters: ~ - • {fn} (`function`) + • {fn} (`fun()`) vim.str_byteindex({str}, {index}, {use_utf16}) *vim.str_byteindex()* Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not supplied, it defaults to false (use UTF-32). Returns the byte index. - Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. An {index} + Invalid UTF-8 and NUL is treated like in |vim.str_utfindex()|. An {index} in the middle of a UTF-16 sequence is rounded upwards to the end of that sequence. Parameters: ~ • {str} (`string`) - • {index} (`number`) - • {use_utf16} (`any?`) + • {index} (`integer`) + • {use_utf16} (`boolean?`) vim.str_utf_end({str}, {index}) *vim.str_utf_end()* Gets the distance (in bytes) from the last byte of the codepoint @@ -981,10 +997,10 @@ vim.str_utf_end({str}, {index}) *vim.str_utf_end()* Parameters: ~ • {str} (`string`) - • {index} (`number`) + • {index} (`integer`) Return: ~ - (`number`) + (`integer`) vim.str_utf_pos({str}) *vim.str_utf_pos()* Gets a list of the starting byte positions of each UTF-8 codepoint in the @@ -996,7 +1012,7 @@ vim.str_utf_pos({str}) *vim.str_utf_pos()* • {str} (`string`) Return: ~ - (`table`) + (`integer[]`) vim.str_utf_start({str}, {index}) *vim.str_utf_start()* Gets the distance (in bytes) from the starting byte of the codepoint @@ -1017,10 +1033,10 @@ vim.str_utf_start({str}, {index}) *vim.str_utf_start()* Parameters: ~ • {str} (`string`) - • {index} (`number`) + • {index} (`integer`) Return: ~ - (`number`) + (`integer`) vim.str_utfindex({str}, {index}) *vim.str_utfindex()* Convert byte index to UTF-32 and UTF-16 indices. If {index} is not @@ -1033,7 +1049,7 @@ vim.str_utfindex({str}, {index}) *vim.str_utfindex()* Parameters: ~ • {str} (`string`) - • {index} (`number?`) + • {index} (`integer?`) Return (multiple): ~ (`integer`) UTF-32 index @@ -1161,6 +1177,7 @@ Lua list copies the list object to Vimscript and does NOT modify the Lua list: > vim.print(list) --> "{ 1, 2, 3 }" < + vim.call({func}, {...}) *vim.call()* Invokes |vim-function| or |user-function| {func} with arguments {...}. See also |vim.fn|. @@ -1239,6 +1256,7 @@ vim.v *vim.v* |v:| variables. Invalid or unset key returns `nil`. + *lua-options* *lua-vim-options* *lua-vim-set* @@ -1262,6 +1280,7 @@ window-scoped options. Note that this must NOT be confused with |local-options| and |:setlocal|. There is also |vim.go| that only accesses the global value of a |global-local| option, see |:setglobal|. + *vim.opt_local* *vim.opt_global* *vim.opt* @@ -1406,7 +1425,7 @@ Option:remove({value}) *vim.opt:remove()* Parameters: ~ • {value} (`string`) Value to remove -vim.bo *vim.bo* +vim.bo[{bufnr}] *vim.bo* Get or set buffer-scoped |options| for the buffer with number {bufnr}. If {bufnr} is omitted then the current buffer is used. Invalid {bufnr} or key is an error. @@ -1456,7 +1475,7 @@ vim.o *vim.o* print(vim.o.foo) -- error: invalid key < -vim.wo *vim.wo* +vim.wo[{winid}][{bufnr}] *vim.wo* Get or set window-scoped |options| for the window with handle {winid} and buffer with number {bufnr}. Like `:setlocal` if setting a |global-local| option or if {bufnr} is provided, like `:set` otherwise. If {winid} is @@ -1625,12 +1644,14 @@ vim.on_key({fn}, {ns_id}) *vim.on_key()* Note: ~ • {fn} will be removed on error. • {fn} will not be cleared by |nvim_buf_clear_namespace()| - • {fn} will receive the keys after mappings have been evaluated Parameters: ~ - • {fn} (`fun(key: string)?`) Function invoked on every key press. - |i_CTRL-V| Passing in nil when {ns_id} is specified removes - the callback associated with namespace {ns_id}. + • {fn} (`fun(key: string, typed: string)?`) Function invoked on + every key press. |i_CTRL-V| {key} is the key after mappings + have been applied, and {typed} is the key(s) before mappings + are applied, which may be empty if {key} is produced by + non-typed keys. When {fn} is nil and {ns_id} is specified, + the callback associated with namespace {ns_id} is removed. • {ns_id} (`integer?`) Namespace ID. If nil or 0, generates and returns a new |nvim_create_namespace()| id. @@ -1795,6 +1816,7 @@ vim.system({cmd}, {opts}, {on_exit}) *vim.system()* Return: ~ (`vim.SystemObj`) Object with the fields: + • cmd (string[]) Command name and args • pid (integer) Process ID • wait (fun(timeout: integer|nil): SystemCompleted) Wait for the process to complete. Upon timeout the process is sent the KILL @@ -2011,6 +2033,43 @@ vim.is_callable({f}) *vim.is_callable()* Return: ~ (`boolean`) `true` if `f` is callable, else `false` +vim.isarray({t}) *vim.isarray()* + Tests if `t` is an "array": a table indexed only by integers (potentially + non-contiguous). + + If the indexes start from 1 and are contiguous then the array is also a + list. |vim.islist()| + + Empty table `{}` is an array, unless it was created by |vim.empty_dict()| + or returned as a dict-like |API| or Vimscript result, for example from + |rpcrequest()| or |vim.fn|. + + Parameters: ~ + • {t} (`table?`) + + Return: ~ + (`boolean`) `true` if array-like table, else `false`. + + See also: ~ + • https://github.com/openresty/luajit2#tableisarray + +vim.islist({t}) *vim.islist()* + Tests if `t` is a "list": a table indexed only by contiguous integers + starting from 1 (what |lua-length| calls a "regular array"). + + Empty table `{}` is a list, unless it was created by |vim.empty_dict()| or + returned as a dict-like |API| or Vimscript result, for example from + |rpcrequest()| or |vim.fn|. + + Parameters: ~ + • {t} (`table?`) + + Return: ~ + (`boolean`) `true` if list-like table, else `false`. + + See also: ~ + • |vim.isarray()| + vim.list_contains({t}, {value}) *vim.list_contains()* Checks if a list-like table (integer keys without gaps) contains `value`. @@ -2190,8 +2249,8 @@ vim.tbl_deep_extend({behavior}, {...}) *vim.tbl_deep_extend()* Merges recursively two or more tables. Parameters: ~ - • {behavior} (`"error"|"keep"|"force"`) (string) Decides what to do if - a key is found in more than one map: + • {behavior} (`'error'|'keep'|'force'`) Decides what to do if a key is + found in more than one map: • "error": raise an error • "keep": use value from the leftmost map • "force": use value from the rightmost map @@ -2207,8 +2266,8 @@ vim.tbl_extend({behavior}, {...}) *vim.tbl_extend()* Merges two or more tables. Parameters: ~ - • {behavior} (`string`) Decides what to do if a key is found in more - than one map: + • {behavior} (`'error'|'keep'|'force'`) Decides what to do if a key is + found in more than one map: • "error": raise an error • "keep": use value from the leftmost map • "force": use value from the rightmost map @@ -2230,20 +2289,6 @@ vim.tbl_filter({func}, {t}) *vim.tbl_filter()* Return: ~ (`any[]`) Table of filtered values -vim.tbl_flatten({t}) *vim.tbl_flatten()* - Creates a copy of a list-like table such that any nested tables are - "unrolled" and appended to the result. - - Parameters: ~ - • {t} (`table`) List-like table - - Return: ~ - (`table`) Flattened copy of the given list-like table - - See also: ~ - • From - https://github.com/premake/premake-core/blob/master/src/base/table.lua - vim.tbl_get({o}, {...}) *vim.tbl_get()* Index into a table (first argument) via string keys passed as subsequent arguments. Return `nil` if the key does not exist. @@ -2261,26 +2306,6 @@ vim.tbl_get({o}, {...}) *vim.tbl_get()* Return: ~ (`any`) Nested value indexed by key (if it exists), else nil -vim.tbl_isarray({t}) *vim.tbl_isarray()* - Tests if `t` is an "array": a table indexed only by integers (potentially - non-contiguous). - - If the indexes start from 1 and are contiguous then the array is also a - list. |vim.tbl_islist()| - - Empty table `{}` is an array, unless it was created by |vim.empty_dict()| - or returned as a dict-like |API| or Vimscript result, for example from - |rpcrequest()| or |vim.fn|. - - Parameters: ~ - • {t} (`table`) - - Return: ~ - (`boolean`) `true` if array-like table, else `false`. - - See also: ~ - • https://github.com/openresty/luajit2#tableisarray - vim.tbl_isempty({t}) *vim.tbl_isempty()* Checks if a table is empty. @@ -2293,23 +2318,6 @@ vim.tbl_isempty({t}) *vim.tbl_isempty()* See also: ~ • https://github.com/premake/premake-core/blob/master/src/base/table.lua -vim.tbl_islist({t}) *vim.tbl_islist()* - Tests if `t` is a "list": a table indexed only by contiguous integers - starting from 1 (what |lua-length| calls a "regular array"). - - Empty table `{}` is a list, unless it was created by |vim.empty_dict()| or - returned as a dict-like |API| or Vimscript result, for example from - |rpcrequest()| or |vim.fn|. - - Parameters: ~ - • {t} (`table`) - - Return: ~ - (`boolean`) `true` if list-like table, else `false`. - - See also: ~ - • |vim.tbl_isarray()| - vim.tbl_keys({t}) *vim.tbl_keys()* Return a list of all keys used in a table. However, the order of the return table of keys is not guaranteed. @@ -2358,7 +2366,8 @@ vim.trim({s}) *vim.trim()* • https://www.lua.org/pil/20.2.html vim.validate({opt}) *vim.validate()* - Validates a parameter specification (types and values). + Validates a parameter specification (types and values). Specs are + evaluated in alphanumeric order, until the first failure. Usage example: >lua function user.new(name, age, hobbies) @@ -2551,17 +2560,22 @@ vim.ui.open({path}) *vim.ui.open()* Expands "~/" and environment variables in filesystem paths. Examples: >lua + -- Asynchronous. vim.ui.open("https://neovim.io/") vim.ui.open("~/path/to/file") - vim.ui.open("$VIMRUNTIME") + -- Synchronous (wait until the process exits). + local cmd, err = vim.ui.open("$VIMRUNTIME") + if cmd then + cmd:wait() + end < Parameters: ~ • {path} (`string`) Path or URL to open Return (multiple): ~ - (`vim.SystemCompleted?`) Command result, or nil if not found. - (`string?`) Error message on failure + (`vim.SystemObj?`) Command object, or nil if not found. + (`string?`) Error message on failure, or nil on success. See also: ~ • |vim.system()| @@ -2672,7 +2686,6 @@ vim.filetype.add({filetypes}) *vim.filetype.add()* vim.filetype.add { pattern = { ['.*'] = { - priority = -math.huge, function(path, bufnr) local content = vim.api.nvim_buf_get_lines(bufnr, 0, 1, false)[1] or '' if vim.regex([[^#!.*\\<mine\\>]]):match_str(content) ~= nil then @@ -2681,6 +2694,7 @@ vim.filetype.add({filetypes}) *vim.filetype.add()* return 'drawing' end end, + { priority = -math.huge }, }, }, } @@ -2781,9 +2795,9 @@ vim.keymap.del({modes}, {lhs}, {opts}) *vim.keymap.del()* Parameters: ~ • {modes} (`string|string[]`) • {lhs} (`string`) - • {opts} (`table?`) A table of optional arguments: - • "buffer": (integer|boolean) Remove a mapping from the given - buffer. When `0` or `true`, use the current buffer. + • {opts} (`table?`) A table with the following fields: + • {buffer}? (`integer|boolean`) Remove a mapping from the + given buffer. When `0` or `true`, use the current buffer. See also: ~ • |vim.keymap.set()| @@ -2805,20 +2819,20 @@ vim.keymap.set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()* < Parameters: ~ - • {mode} (`string|table`) Mode short-name, see |nvim_set_keymap()|. Can - also be list of modes to create mapping on multiple modes. + • {mode} (`string|string[]`) Mode short-name, see |nvim_set_keymap()|. + Can also be list of modes to create mapping on multiple modes. • {lhs} (`string`) Left-hand side |{lhs}| of the mapping. • {rhs} (`string|function`) Right-hand side |{rhs}| of the mapping, can be a Lua function. - • {opts} (`table?`) Table of |:map-arguments|. - • Same as |nvim_set_keymap()| {opts}, except: - • "replace_keycodes" defaults to `true` if "expr" is `true`. - • "noremap": inverse of "remap" (see below). - • Also accepts: - • "buffer": (integer|boolean) Creates buffer-local mapping, - `0` or `true` for current buffer. - • "remap": (boolean) Make the mapping recursive. Inverse of - "noremap". Defaults to `false`. + • {opts} (`table?`) Table of |:map-arguments|. Same as + |nvim_set_keymap()| {opts}, except: + • {replace_keycodes} defaults to `true` if "expr" is `true`. + + Also accepts: + • {buffer}? (`integer|boolean`) Creates buffer-local mapping, + `0` or `true` for current buffer. + • {remap}? (`boolean`, default: `false`) Make the mapping + recursive. Inverse of {noremap}. See also: ~ • |nvim_set_keymap()| @@ -2881,13 +2895,6 @@ vim.fs.find({names}, {opts}) *vim.fs.find()* narrow the search to find only that type. Examples: >lua - -- location of Cargo.toml from the current buffer's path - local cargo = vim.fs.find('Cargo.toml', { - upward = true, - stop = vim.uv.os_homedir(), - path = vim.fs.dirname(vim.api.nvim_buf_get_name(0)), - }) - -- list all test directories under the runtime directory local test_dirs = vim.fs.find( {'test', 'tst', 'testdir'}, @@ -2939,26 +2946,41 @@ vim.fs.joinpath({...}) *vim.fs.joinpath()* vim.fs.normalize({path}, {opts}) *vim.fs.normalize()* Normalize a path to a standard format. A tilde (~) character at the - beginning of the path is expanded to the user's home directory and any - backslash (\) characters are converted to forward slashes (/). Environment - variables are also expanded. + beginning of the path is expanded to the user's home directory and + environment variables are also expanded. "." and ".." components are also + resolved, except when the path is relative and trying to resolve it would + result in an absolute path. + • "." as the only part in a relative path: + • "." => "." + • "././" => "." + • ".." when it leads outside the current directory + • "foo/../../bar" => "../bar" + • "../../foo" => "../../foo" + • ".." in the root directory returns the root directory. + • "/../../" => "/" + + On Windows, backslash (\) characters are converted to forward slashes (/). Examples: >lua - vim.fs.normalize('C:\\\\Users\\\\jdoe') - -- 'C:/Users/jdoe' - - vim.fs.normalize('~/src/neovim') - -- '/home/jdoe/src/neovim' - - vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim') - -- '/Users/jdoe/.config/nvim/init.vim' + [[C:\Users\jdoe]] => "C:/Users/jdoe" + "~/src/neovim" => "/home/jdoe/src/neovim" + "$XDG_CONFIG_HOME/nvim/init.vim" => "/Users/jdoe/.config/nvim/init.vim" + "~/src/nvim/api/../tui/./tui.c" => "/home/jdoe/src/nvim/tui/tui.c" + "./foo/bar" => "foo/bar" + "foo/../../../bar" => "../../bar" + "/home/jdoe/../../../bar" => "/bar" + "C:foo/../../baz" => "C:../baz" + "C:/foo/../../baz" => "C:/baz" + [[\\?\UNC\server\share\foo\..\..\..\bar]] => "//?/UNC/server/share/bar" < Parameters: ~ • {path} (`string`) Path to normalize • {opts} (`table?`) A table with the following fields: - • {expand_env} (`boolean`, default: `true`) Expand environment - variables. + • {expand_env}? (`boolean`, default: `true`) Expand + environment variables. + • {win}? (`boolean`, default: `true` in Windows, `false` + otherwise) Path is a Windows path. Return: ~ (`string`) Normalized path @@ -2988,6 +3010,39 @@ vim.fs.parents({start}) *vim.fs.parents()* (`nil`) (`string?`) +vim.fs.root({source}, {marker}) *vim.fs.root()* + Find the first parent directory containing a specific "marker", relative + to a file path or buffer. + + If the buffer is unnamed (has no backing file) or has a non-empty + 'buftype' then the search begins from Nvim's |current-directory|. + + Example: >lua + -- Find the root of a Python project, starting from file 'main.py' + vim.fs.root(vim.fs.joinpath(vim.env.PWD, 'main.py'), {'pyproject.toml', 'setup.py' }) + + -- Find the root of a git repository + vim.fs.root(0, '.git') + + -- Find the parent directory containing any file with a .csproj extension + vim.fs.root(0, function(name, path) + return name:match('%.csproj$') ~= nil + end) +< + + Parameters: ~ + • {source} (`integer|string`) Buffer number (0 for current buffer) or + file path (absolute or relative to the |current-directory|) + to begin the search from. + • {marker} (`string|string[]|fun(name: string, path: string): boolean`) + A marker, or list of markers, to search for. If a function, + the function is called for each evaluated item and should + return true if {name} and {path} are a match. + + Return: ~ + (`string?`) Directory path containing one of the given markers, or nil + if no directory was found. + ============================================================================== Lua module: vim.glob *vim.glob* @@ -3022,11 +3077,10 @@ vim.glob.to_lpeg({pattern}) *vim.glob.to_lpeg()* VIM.LPEG *vim.lpeg* -LPeg is a pattern-matching library for Lua, based on -Parsing Expression Grammars (https://bford.info/packrat/) (PEGs). +LPeg is a pattern-matching library for Lua, based on Parsing Expression +Grammars (PEGs). https://bford.info/packrat/ - *lua-lpeg* - *vim.lpeg.Pattern* + *lua-lpeg* *vim.lpeg.Pattern* The LPeg library for parsing expression grammars is included as `vim.lpeg` (https://www.inf.puc-rio.br/~roberto/lpeg/). @@ -3419,10 +3473,11 @@ vim.lpeg.version() *vim.lpeg.version()* VIM.RE *vim.re* The `vim.re` module provides a conventional regex-like syntax for pattern -usage within LPeg |vim.lpeg|. +usage within LPeg |vim.lpeg|. (Unrelated to |vim.regex| which provides Vim +|regexp| from Lua.) See https://www.inf.puc-rio.br/~roberto/lpeg/re.html for the original -documentation including regex syntax and more concrete examples. +documentation including regex syntax and examples. vim.re.compile({string}, {defs}) *vim.re.compile()* @@ -3553,7 +3608,7 @@ vim.secure.trust({opts}) *vim.secure.trust()* The trust database is located at |$XDG_STATE_HOME|/nvim/trust. Parameters: ~ - • {opts} (`table?`) A table with the following fields: + • {opts} (`table`) A table with the following fields: • {action} (`'allow'|'deny'|'remove'`) - `'allow'` to add a file to the trust database and trust it, • `'deny'` to add a file to the trust database and deny it, @@ -3764,7 +3819,6 @@ vim.version.range({spec}) *vim.version.range()* (`table?`) A table with the following fields: • {from} (`vim.Version`) • {to}? (`vim.Version`) - • {has} (`fun(self: vim.VersionRangeversion: string|vim.Version)`) See also: ~ • https://github.com/npm/node-semver#ranges @@ -3780,6 +3834,7 @@ chained to create iterator "pipelines": the output of each pipeline stage is input to the next stage. The first stage depends on the type passed to `vim.iter()`: • List tables (arrays, |lua-list|) yield only the value of each element. + • Holes (nil values) are allowed. • Use |Iter:enumerate()| to also pass the index to the next stage. • Or initialize with ipairs(): `vim.iter(ipairs(…))`. • Non-list tables (|lua-dict|) yield both the key and value of each element. @@ -3833,27 +3888,6 @@ Examples: >lua -- { "a", "b" } < -In addition to the |vim.iter()| function, the |vim.iter| module provides -convenience functions like |vim.iter.filter()| and |vim.iter.totable()|. - - -filter({f}, {src}) *vim.iter.filter()* - Filters a table or other |iterable|. >lua - -- Equivalent to: - vim.iter(src):filter(f):totable() -< - - Parameters: ~ - • {f} (`fun(...):boolean`) Filter function. Accepts the current - iterator or table values as arguments and returns true if those - values should be kept in the final table - • {src} (`table|function`) Table or iterator function to filter - - Return: ~ - (`table`) - - See also: ~ - • |Iter:filter()| Iter:all({pred}) *Iter:all()* Returns true if all items in the iterator match the given predicate. @@ -3897,6 +3931,7 @@ Iter:enumerate() *Iter:enumerate()* < Example: >lua + local it = vim.iter(vim.gsplit('abc', '')):enumerate() it:next() -- 1 'a' @@ -3931,6 +3966,7 @@ Iter:find({f}) *Iter:find()* found. Examples: >lua + local it = vim.iter({ 3, 6, 9, 12 }) it:find(12) -- 12 @@ -3973,18 +4009,23 @@ Iter:flatten({depth}) *Iter:flatten()* (`Iter`) Iter:fold({init}, {f}) *Iter:fold()* - Folds ("reduces") an iterator into a single value. + Folds ("reduces") an iterator into a single value. *Iter:reduce()* Examples: >lua -- Create a new table with only even values - local t = { a = 1, b = 2, c = 3, d = 4 } - local it = vim.iter(t) - it:filter(function(k, v) return v % 2 == 0 end) - it:fold({}, function(t, k, v) - t[k] = v - return t - end) - -- { b = 2, d = 4 } + vim.iter({ a = 1, b = 2, c = 3, d = 4 }) + :filter(function(k, v) return v % 2 == 0 end) + :fold({}, function(acc, k, v) + acc[k] = v + return acc + end) --> { b = 2, d = 4 } + + -- Get the "maximum" item of an iterable. + vim.iter({ -99, -4, 3, 42, 0, 0, 7 }) + :fold({}, function(acc, v) + acc.max = math.max(v, acc.max or v) + return acc + end) --> { max = 42 } < Parameters: ~ @@ -4011,6 +4052,7 @@ Iter:last() *Iter:last()* Drains the iterator and returns the last item. Example: >lua + local it = vim.iter(vim.gsplit('abcdefg', '')) it:last() -- 'g' @@ -4023,6 +4065,9 @@ Iter:last() *Iter:last()* Return: ~ (`any`) + See also: ~ + • Iter.rpeek + Iter:map({f}) *Iter:map()* Maps the items of an iterator pipeline to the values returned by `f`. @@ -4051,6 +4096,7 @@ Iter:next() *Iter:next()* Gets the next value from the iterator. Example: >lua + local it = vim.iter(string.gmatch('1 2 3', '%d+')):map(tonumber) it:next() -- 1 @@ -4063,51 +4109,28 @@ Iter:next() *Iter:next()* Return: ~ (`any`) -Iter:nextback() *Iter:nextback()* - "Pops" a value from a |list-iterator| (gets the last value and decrements - the tail). - - Example: >lua - local it = vim.iter({1, 2, 3, 4}) - it:nextback() - -- 4 - it:nextback() - -- 3 -< - - Return: ~ - (`any`) - Iter:nth({n}) *Iter:nth()* Gets the nth value of an iterator (and advances to it). + If `n` is negative, offsets from the end of a |list-iterator|. + Example: >lua local it = vim.iter({ 3, 6, 9, 12 }) it:nth(2) -- 6 it:nth(2) -- 12 -< - - Parameters: ~ - • {n} (`number`) The index of the value to return. - - Return: ~ - (`any`) - -Iter:nthback({n}) *Iter:nthback()* - Gets the nth value from the end of a |list-iterator| (and advances to it). - Example: >lua - local it = vim.iter({ 3, 6, 9, 12 }) - it:nthback(2) + local it2 = vim.iter({ 3, 6, 9, 12 }) + it2:nth(-2) -- 9 - it:nthback(2) + it2:nth(-2) -- 3 < Parameters: ~ - • {n} (`number`) The index of the value to return. + • {n} (`number`) Index of the value to return. May be negative if the + source is a |list-iterator|. Return: ~ (`any`) @@ -4116,6 +4139,7 @@ Iter:peek() *Iter:peek()* Gets the next value in a |list-iterator| without consuming it. Example: >lua + local it = vim.iter({ 3, 6, 9, 12 }) it:peek() -- 3 @@ -4128,19 +4152,16 @@ Iter:peek() *Iter:peek()* Return: ~ (`any`) -Iter:peekback() *Iter:peekback()* - Gets the last value of a |list-iterator| without consuming it. - - See also |Iter:last()|. +Iter:pop() *Iter:pop()* + "Pops" a value from a |list-iterator| (gets the last value and decrements + the tail). Example: >lua local it = vim.iter({1, 2, 3, 4}) - it:peekback() - -- 4 - it:peekback() - -- 4 - it:nextback() + it:pop() -- 4 + it:pop() + -- 3 < Return: ~ @@ -4150,6 +4171,7 @@ Iter:rev() *Iter:rev()* Reverses a |list-iterator| pipeline. Example: >lua + local it = vim.iter({ 3, 6, 9, 12 }):rev() it:totable() -- { 12, 9, 6, 3 } @@ -4159,13 +4181,14 @@ Iter:rev() *Iter:rev()* (`Iter`) Iter:rfind({f}) *Iter:rfind()* - Gets the first value in a |list-iterator| that satisfies a predicate, - starting from the end. + Gets the first value satisfying a predicate, from the end of a + |list-iterator|. Advances the iterator. Returns nil and drains the iterator if no value is found. Examples: >lua + local it = vim.iter({ 1, 2, 3, 2, 1 }):enumerate() it:rfind(1) -- 5 1 @@ -4182,13 +4205,34 @@ Iter:rfind({f}) *Iter:rfind()* See also: ~ • Iter.find -Iter:skip({n}) *Iter:skip()* - Skips `n` values of an iterator pipeline. +Iter:rpeek() *Iter:rpeek()* + Gets the last value of a |list-iterator| without consuming it. Example: >lua - local it = vim.iter({ 3, 6, 9, 12 }):skip(2) + local it = vim.iter({1, 2, 3, 4}) + it:rpeek() + -- 4 + it:rpeek() + -- 4 + it:pop() + -- 4 +< + + Return: ~ + (`any`) + + See also: ~ + • Iter.last + +Iter:rskip({n}) *Iter:rskip()* + Discards `n` values from the end of a |list-iterator| pipeline. + + Example: >lua + local it = vim.iter({ 1, 2, 3, 4, 5 }):rskip(2) it:next() - -- 9 + -- 1 + it:pop() + -- 3 < Parameters: ~ @@ -4197,15 +4241,14 @@ Iter:skip({n}) *Iter:skip()* Return: ~ (`Iter`) -Iter:skipback({n}) *Iter:skipback()* - Skips `n` values backwards from the end of a |list-iterator| pipeline. +Iter:skip({n}) *Iter:skip()* + Skips `n` values of an iterator pipeline. Example: >lua - local it = vim.iter({ 1, 2, 3, 4, 5 }):skipback(2) + + local it = vim.iter({ 3, 6, 9, 12 }):skip(2) it:next() - -- 1 - it:nextback() - -- 3 + -- 9 < Parameters: ~ @@ -4217,7 +4260,7 @@ Iter:skipback({n}) *Iter:skipback()* Iter:slice({first}, {last}) *Iter:slice()* Sets the start and end of a |list-iterator| pipeline. - Equivalent to `:skip(first - 1):skipback(len - last + 1)`. + Equivalent to `:skip(first - 1):rskip(len - last + 1)`. Parameters: ~ • {first} (`number`) @@ -4249,9 +4292,9 @@ Iter:totable() *Iter:totable()* Collect the iterator into a table. The resulting table depends on the initial source in the iterator - pipeline. List-like tables and function iterators will be collected into a - list-like table. If multiple values are returned from the final stage in - the iterator pipeline, each value will be included in a table. + pipeline. Array-like tables and function iterators will be collected into + an array-like table. If multiple values are returned from the final stage + in the iterator pipeline, each value will be included in a table. Examples: >lua vim.iter(string.gmatch('100 20 50', '%d+')):map(tonumber):totable() @@ -4264,90 +4307,65 @@ Iter:totable() *Iter:totable()* -- { { 'a', 1 }, { 'c', 3 } } < - The generated table is a list-like table with consecutive, numeric + The generated table is an array-like table with consecutive, numeric indices. To create a map-like table with arbitrary keys, use |Iter:fold()|. Return: ~ (`table`) -map({f}, {src}) *vim.iter.map()* - Maps a table or other |iterable|. >lua - -- Equivalent to: - vim.iter(src):map(f):totable() -< - - Parameters: ~ - • {f} (`fun(...): any?`) Map function. Accepts the current iterator - or table values as arguments and returns one or more new - values. Nil values are removed from the final table. - • {src} (`table|function`) Table or iterator function to filter - Return: ~ - (`table`) - - See also: ~ - • |Iter:map()| +============================================================================== +Lua module: vim.snippet *vim.snippet* -totable({f}) *vim.iter.totable()* - Collects an |iterable| into a table. >lua - -- Equivalent to: - vim.iter(f):totable() -< +*vim.snippet.ActiveFilter* - Parameters: ~ - • {f} (`function`) Iterator function + Fields: ~ + • {direction} (`vim.snippet.Direction`) Navigation direction. -1 for + previous, 1 for next. - Return: ~ - (`table`) +vim.snippet.active({filter}) *vim.snippet.active()* + Returns `true` if there's an active snippet in the current buffer, + applying the given filter if provided. -============================================================================== -Lua module: vim.snippet *vim.snippet* + You can use this function to navigate a snippet as follows: >lua + vim.keymap.set({ 'i', 's' }, '<Tab>', function() + if vim.snippet.active({ direction = 1 }) then + return '<cmd>lua vim.snippet.jump(1)<cr>' + else + return '<Tab>' + end + end, { expr = true }) +< -vim.snippet.active() *vim.snippet.active()* - Returns `true` if there's an active snippet in the current buffer. + Parameters: ~ + • {filter} (`vim.snippet.ActiveFilter?`) Filter to constrain the search + with: + • `direction` (vim.snippet.Direction): Navigation direction. + Will return `true` if the snippet can be jumped in the + given direction. See |vim.snippet.ActiveFilter|. Return: ~ (`boolean`) -vim.snippet.exit() *vim.snippet.exit()* - Exits the current snippet. - vim.snippet.expand({input}) *vim.snippet.expand()* Expands the given snippet text. Refer to https://microsoft.github.io/language-server-protocol/specification/#snippet_syntax for the specification of valid input. - Tabstops are highlighted with hl-SnippetTabstop. + Tabstops are highlighted with |hl-SnippetTabstop|. Parameters: ~ • {input} (`string`) vim.snippet.jump({direction}) *vim.snippet.jump()* - Jumps within the active snippet in the given direction. If the jump isn't - possible, the function call does nothing. - - You can use this function to navigate a snippet as follows: >lua - vim.keymap.set({ 'i', 's' }, '<Tab>', function() - if vim.snippet.jumpable(1) then - return '<cmd>lua vim.snippet.jump(1)<cr>' - else - return '<Tab>' - end - end, { expr = true }) -< + Jumps to the next (or previous) placeholder in the current snippet, if + possible. - Parameters: ~ - • {direction} (`vim.snippet.Direction`) Navigation direction. -1 for - previous, 1 for next. - -vim.snippet.jumpable({direction}) *vim.snippet.jumpable()* - Returns `true` if there is an active snippet which can be jumped in the - given direction. You can use this function to navigate a snippet as - follows: >lua + For example, map `<Tab>` to jump while a snippet is active: >lua vim.keymap.set({ 'i', 's' }, '<Tab>', function() - if vim.snippet.jumpable(1) then + if vim.snippet.active({ direction = 1 }) then return '<cmd>lua vim.snippet.jump(1)<cr>' else return '<Tab>' @@ -4359,8 +4377,8 @@ vim.snippet.jumpable({direction}) *vim.snippet.jumpable()* • {direction} (`vim.snippet.Direction`) Navigation direction. -1 for previous, 1 for next. - Return: ~ - (`boolean`) +vim.snippet.stop() *vim.snippet.stop()* + Exits the current snippet. ============================================================================== diff --git a/runtime/doc/luaref.txt b/runtime/doc/luaref.txt index e7b62f4c6c..cd0b648560 100644 --- a/runtime/doc/luaref.txt +++ b/runtime/doc/luaref.txt @@ -932,7 +932,7 @@ implicit extra parameter `self`. Thus, the statement is syntactic sugar for - `t.a.b.c:f = function (self, (` `params` `)` `body` `end` + `t.a.b.c:f = function (` `self`, `params` `)` `body` `end` ============================================================================== 2.6 Visibility Rules *lua-visibility* diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 68206dd494..9ec34d5d52 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -604,7 +604,7 @@ instead. Example: > map <Leader>A oanother line<Esc> Works like: > map \A oanother line<Esc> -But after: +But after: > let mapleader = "," It works like: > map ,A oanother line<Esc> diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index c3154fc372..afe64300e7 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -114,6 +114,13 @@ wiped out a buffer which contains a mark or is referenced in another way. You cannot have two buffers with exactly the same name. This includes the path leading to the file. + *E1513* > + Cannot switch buffer. 'winfixbuf' is enabled + +If a window has 'winfixbuf' enabled, you cannot change that window's current +buffer. You need to set 'nowinfixbuf' before continuing. You may use [!] to +force the window to switch buffers, if your command supports it. + *E72* > Close error on swap file diff --git a/runtime/doc/news-0.10.txt b/runtime/doc/news-0.10.txt new file mode 100644 index 0000000000..8a0e7e92e7 --- /dev/null +++ b/runtime/doc/news-0.10.txt @@ -0,0 +1,473 @@ +*news-10.txt* Nvim + + + NVIM REFERENCE MANUAL + + +Notable changes since Nvim 0.9 *news-0.10* + +For changes in the previous release, see |news-0.9|. + + Type |gO| to see the table of contents. + +============================================================================== +BREAKING CHANGES + +These changes may require adaptations in your config or plugins. + +• API: + • |nvim_open_win()| now blocks all autocommands when `noautocmd` is set, + rather than just those from setting the `buffer` to display in the window. + +• Defaults: + • Default color scheme has been updated to be "Nvim branded" and accessible. + Use `:colorscheme vim` to revert to the old legacy color scheme. + • These Nvim specific highlight groups are now defined in a meaningfully + different way and might need an update: + • |hl-FloatBorder| is linked to |hl-NormalFloat| instead of |hl-WinSeparator|. + • |hl-NormalFloat| is not linked to |hl-Pmenu|. + • |hl-WinBar| has different background. + • |hl-WinBarNC| is similar to |hl-WinBar| but not bold. + • |hl-WinSeparator| is linked to |hl-Normal| instead of |hl-VertSplit|. + • This also might result into some color schemes looking differently due + to them relying on implicit assumptions about how highlight groups are + defined by default. To account for this, define all attributes of + highlight groups explicitly. Alternatively, use `:colorscheme vim` or + `:source $VIMRUNTIME/colors/vim.lua` to restore previous definitions. + • 'termguicolors' is enabled by default when Nvim is able to determine that + the host terminal emulator supports 24-bit color. + +• Editor: + • When switching windows, |CursorMoved| autocommands trigger when Nvim is + back on the main loop rather than immediately. This is more compatible + with Vim. + • "#" followed by a digit no longer stands for a function key at the start + of the lhs of a mapping. + • |shm-q| now fully hides macro recording message instead of only shortening it. + • Signs placed through the legacy |sign-commands| are now stored and + displayed as |extmarks| internally. Along with the following changes: + • A sign placed twice in the same group with the same identifier will be + moved. + • Legacy signs are always deleted along with the line it is placed on. + • Legacy and extmark signs will show up in both |:sign-place-list| and + |nvim_buf_get_extmarks()|. + • Legacy and extmark signs are displayed and listed with the same priority: + line number -> priority -> sign id -> recently placed + • `:behave` was removed. + • If you used `:behave xterm`, the following is equivalent: >vim + + set mousemodel=extend +< + • If you used `:behave mswin`, the following is equivalent: >vim + + set selection=exclusive + set selectmode=mouse,key + set mousemodel=popup + set keymodel=startsel,stopsel +< +• Events: + • Returning any truthy value from a callback passed to + |nvim_create_autocmd()| (rather than just `true`) will delete the + autocommand. + +• LSP: + • |LanguageTree:parse()| will no longer parse injections by default and now + requires an explicit range argument to be passed. If injections are + required, provide an explicit range via `parser:parse({ start_row, end_row })`. + • |vim.lsp.util.parse_snippet()| will now strictly follow the snippet + grammar defined by LSP, and hence previously parsed snippets might now be + considered invalid input. + • |vim.lsp.codelens.refresh()| now takes an `opts` argument. With this + change, the default behavior of just refreshing the current buffer has + been replaced by refreshing all buffers. + • |vim.lsp.util.extract_completion_items()| will no longer return reliable + results, since it does not apply `itemDefaults` when its input is + a `CompletionList`. Moreover, since support for LSP + `completionList.itemDefaults` was added, some third party plugins might be + negatively impacted in case the language servers support the feature but + the plugin does not. If necessary, the respective capability can be + removed when calling |vim.lsp.protocol.make_client_capabilities()|. + • |LspRequest| and LspProgressUpdate (renamed to |LspProgress|) autocmds + were promoted from |User| autocmds to first class citizens. + +• Lua: + • |-l| ensures output ends with a newline if the script prints messages and + doesn't cause Nvim to exit. + • Removed functions from the |vim.json| module: + • Unnecessary, undocumented functions which caused global side-effects. + • `vim.json.null` is redundant with `vim.NIL`. + • `vim.json.array_mt` (and related) is redundant with `vim.empty_dict()`. + • |vim.islist()| now checks whether a table is actually list-like (i.e., has + integer keys without gaps and starting from 1). For the previous behavior + (only check for integer keys, allow gaps or not starting with 1), use + |vim.isarray()|. + • Renamed `vim.treesitter.playground` to `vim.treesitter.dev`. + +• Options: + • Removed some Vim 5.0<= option compatibilities: + • 'backspace' no longer supports number values. Instead: + • for `backspace=0` set `backspace=` (empty) + • for `backspace=1` set `backspace=indent,eol` + • for `backspace=2` set `backspace=indent,eol,start` (default behavior in Nvim) + • for `backspace=3` set `backspace=indent,eol,nostop` + • 'backupdir' and 'directory' will no longer remove a `>` at the start of + the option. + • |OptionSet| autocommand args |v:option_new|, |v:option_old|, + |v:option_oldlocal|, |v:option_oldglobal| now have the type of the option + instead of always being strings. |v:option_old| is now the old global + value for all global-local options, instead of just string global-local + options. + • Local value for a global-local number/boolean option is now unset when the + option is set (e.g. using |:set| or |nvim_set_option_value()|) without + a scope, which means they now behave the same way as string options. + +• Plugins: + • |:TOhtml| has been rewritten in Lua to support Nvim-specific decorations, + and many options have been removed. + +• Treesitter: + • Treesitter highlight groups have been renamed to be more in line with + upstream tree-sitter and Helix to make it easier to share queries. The + full list is documented in |treesitter-highlight-groups|. + +• TUI: + • In some cases, the cursor in the Nvim |TUI| used to blink even without + configuring 'guicursor' as mentioned in |cursor-blinking|. This was a bug + that has now been fixed. If your cursor has stopped blinking, add the + following (or similar, adapted to user preference) to your |config| file: >vim + set guicursor+=n-v-c:blinkon500-blinkoff500 +< + +============================================================================== +NEW FEATURES + +The following new features were added. + +• API: + • Passing 0 to |nvim_get_chan_info()| gets info about the current channel. + • |nvim_buf_set_extmark()| supports inline virtual text. + • |nvim_win_text_height()| computes the number of screen lines occupied + by a range of text in a given window. + • New RPC client type `msgpack-rpc` is added for |nvim_set_client_info()| to + support fully MessagePack-RPC compliant clients. + • Floating windows can now be hidden by setting `hide` in |nvim_open_win()| or + |nvim_win_set_config()|. + • |nvim_input_mouse()| supports mouse buttons "x1" and "x2". + • Added "force_crlf" option field in |nvim_open_term()|. + • Added |nvim_tabpage_set_win()| to set the current window of a tabpage. + • |nvim__win_add_ns()| can bind a |namespace| to a window-local scope(s). + • Extmarks opt-in to this scoping via the `scoped` flag of |nvim_buf_set_extmark()|. + • Mapping APIs now support abbreviations when mode short-name has suffix "a". + • Floating windows can now show footer with new `footer` and `footer_pos` + config fields. Uses |hl-FloatFooter| by default. + • |extmarks| can set a "url" highlight attribute, so the text region can + become a clickable hyperlink (assuming UI support). The TUI renders URLs + using the OSC 8 control sequence, enabling clickable text in supporting + terminals. + • |nvim_open_win()| and |nvim_win_set_config()| now support opening normal + (split) windows, moving floating windows into split windows, and opening + windows in non-current tabpages. + • Flags added to |nvim_buf_set_extmark()|: + • "undo_restore": opt-out extmarks of precise undo tracking. + • "invalidate": automatically hide or delete extmarks. + • "virt_text_repeat_linebreak": repeat virtual text on wrapped lines. + • Extmarks now fully support multi-line ranges, and a single extmark can be + used to highlight a range of arbitrary length. The |nvim_buf_set_extmark()| + API function already allowed you to define such ranges, but highlight + regions were not rendered consistently for a range that covers more than + one line break. This has now been fixed. Signs defined as part of + a multi-line extmark also apply to every line in the range, not just the + first. In addition, |nvim_buf_get_extmarks()| has gained an "overlap" + option to return such ranges even if they started before the specified + position. + +• Defaults: + • The `workspace/didChangeWatchedFiles` LSP client capability is now enabled + by default on Mac and Windows. Disabled on Linux since there currently + isn't a viable backend for watching files that scales well for large + directories. + • On Windows 'isfname' does not include ":". Drive letters are handled + correctly without it. (Use |gF| for filepaths suffixed with ":line:col"). + • 'comments' includes "fb:•". + • 'shortmess' includes the "C" flag. + • 'grepprg' uses the -H and -I flags for grep by default, + and defaults to using ripgrep if available. + • "]d" and "[d" in Normal mode map to |vim.diagnostic.goto_next()| and + |vim.diagnostic.goto_prev()|, respectively. |]d-default| |[d-default| + • <C-W>d (and <C-W><C-D>) map to |vim.diagnostic.open_float()| + |CTRL-W_d-default| + • |vim.lsp.start()| sets the following default keymaps (assuming server + support): + • |K| in Normal mode maps to |vim.lsp.buf.hover()|, unless 'keywordprg' + was customized before calling |vim.lsp.start()|. + • Automatic linting of treesitter query files (see |ft-query-plugin|). + Can be disabled via: >lua + vim.g.query_lint_on = {} +< + • Enabled treesitter highlighting for: + • Treesitter query files + • Vim help files + • Lua files + +• Editor: + • Better cmdline completion for string option value. |complete-set-option| + • Try it with `:set listchars=<Tab>` + • By default, the swapfile "ATTENTION" |E325| dialog is skipped if the + swapfile is owned by a running Nvim process, instead of prompting. If you + always want the swapfile dialog, delete the default SwapExists handler: + `autocmd! nvim_swapfile`. |default-autocmds| + • Navigating the |jumplist| with CTRL+O, CTRL+I behaves more intuitively + when deleting buffers, and avoids "invalid buffer" cases. #25461 + • |:fclose| command. + • |v_Q-default| and |v_@-default| repeat a register for each line of a linewise + visual selection. + • Clicking on a tabpage in the tabline with the middle mouse button closes it. + • |:checkhealth| buffer can be opened in a split window using modifiers like + |:vertical|, |:horizontal| and |:botright|. + +• Events: + • |vim.on_key()| callbacks receive a second argument for keys typed before + mappings are applied. + +• LSP: + • LSP method names are available in |vim.lsp.protocol.Methods|. + • Implemented LSP inlay hints: |lsp-inlay_hint| + https://microsoft.github.io/language-server-protocol/specification/#textDocument_inlayHint + • Implemented pull diagnostic textDocument/diagnostic: |vim.lsp.diagnostic.on_diagnostic()| + https://microsoft.github.io/language-server-protocol/specification/#textDocument_diagnostic + • Implemented LSP type hierarchy: |vim.lsp.buf.typehierarchy()| + https://microsoft.github.io/language-server-protocol/specification/#textDocument_prepareTypeHierarchy + • |vim.lsp.status()| consumes the last progress messages as a string. + • LSP client now always saves and restores named buffer marks when applying + text edits. + • LSP client now supports the `positionEncoding` server capability. If a server + responds with the `positionEncoding` capability in its initialization + response, Nvim automatically sets the client's `offset_encoding` field. + • Dynamic registration of LSP capabilities. An implication of this change is + that checking a client's `server_capabilities` is no longer a sufficient + indicator to see if a server supports a feature. Instead use + `client.supports_method(<method>)`. It considers both the dynamic + capabilities and static `server_capabilities`. + • `anchor_bias` option to |lsp-handlers| aids in positioning of floating + windows. + • |vim.lsp.util.locations_to_items()| sets the `user_data` of each item to + the original LSP `Location` or `LocationLink`. + • Added support for connecting to servers using named pipes (Windows) or + Unix domain sockets (Unix) via |vim.lsp.rpc.connect()|. + • Added support for `completionList.itemDefaults`, reducing overhead when + computing completion items where properties often share the same value + (e.g. `commitCharacters`). Note that this might affect plugins and + language servers that don't support the feature, and in such cases the + respective capability can be unset. + • |vim.lsp.start()| accepts a "silent" option for suppressing messages + if an LSP server failed to start. + • |vim.lsp.buf.definition()|, |vim.lsp.buf.declaration()|, + |vim.lsp.buf.type_definition()|, and |vim.lsp.buf.implementation()| now + support the `loclist` field of |vim.lsp.ListOpts|. + +• Lua: + • |:lua| with a |[range]| executes that range as Lua code, in any buffer. + • |:source| without arguments treats a buffer with 'filetype' of "lua" as + Lua code regardless of its extension. + • Vimscript function |exists()| supports checking |v:lua| functions. + • |vim.iter()| is a generic interface for all |iterable| objects (tables, + |iterator|s). + • |vim.snippet| provides a mode for expanding and navigating snippets. + • |vim.ringbuf()| is a generic ring buffer (data structure). + • |vim.deepcopy()| gained a `noref` argument to avoid hashing table values. + • |vim.keycode()| translates keycodes in a string. + • |vim.system()| runs commands / starts processes. + • |vim.lpeg| and |vim.re| expose the bundled Lpeg expression grammar parser + and its regex interface. + • |vim.base64.encode()| and |vim.base64.decode()| encode and decode strings + using Base64 encoding. + • |vim.text.hexencode()| and |vim.text.hexdecode()| convert strings to and + from byte representations. + • |vim.ui.open()| opens URIs using the system default handler (macOS `open`, + Windows `explorer`, Linux `xdg-open`, etc.) + • |vim.wo| can now be double indexed for |:setlocal| behaviour. Currently + only `0` for the buffer index is supported. + • Improved messages for type errors in `vim.api.*` calls (including `opts` params). + • Lua type annotations for: + • `vim.*` + • `vim.fn.*` + • `vim.api.*` + • `vim.v.*` + • Functions that take a severity as an optional parameter (e.g. + |vim.diagnostic.get()|) now also accept a list of severities |vim.diagnostic.severity| + • |vim.diagnostic.count()| returns the number of diagnostics for a given + buffer and/or namespace, by severity. This is a faster alternative to + |vim.diagnostic.get()| when only the number of diagnostics is needed, but + not the diagnostics themselves. + • |vim.diagnostic.is_enabled()| + • |vim.version.le()|, |vim.version.ge()| + • |vim.fs.root()| finds project root directories from a list of "root + markers". + • |vim.tbl_contains()| now works for general tables and allows specifying + a predicate function that is checked for each value. (Use + |vim.list_contains()| for checking list-like tables (integer keys without + gaps) for literal values.) + • |vim.region()| can use a string accepted by |getpos()| as position. + +• Options: + • 'winfixbuf' keeps a window focused onto a specific buffer + • 'smoothscroll' option to scroll by screen line rather than by text line + when 'wrap' is set. + • 'foldtext' now supports virtual text format. |fold-foldtext| + • 'foldtext' can be set to an empty string to disable and render the line: + as normal with regular highlighting and no line wrapping. + • 'complete' option supports "f" flag for completing buffer names. + • 'completeopt' option supports "popup" flag to show extra information in + a floating window. + • 'errorfile' (|-q|) accepts `-` as an alias for stdin. + +• Performance: + • 'diffopt' "linematch" scoring algorithm now favours larger and less groups + https://github.com/neovim/neovim/pull/23611 + • Treesitter highlighting now parses injections incrementally during + screen redraws only for the line range being rendered. This significantly + improves performance in large files with many injections. + • 'breakindent' performance is significantly improved for wrapped lines. + • Cursor movement, insertion with [count] and |screenpos()| are now faster. + +• Plugins: + • Nvim now includes |commenting| support. + • |:Man| supports the `:hide` modifier to open page in the current window. + • |:Man| respects 'wrapmargin' + +• Startup: + • |$NVIM_APPNAME| can be set to a relative path instead of only a name. + • |--startuptime| reports startup times for both processes (TUI + server) as + separate sections. + +• Terminal: + • |:terminal| accepts some |:command-modifiers| (specifically |:horizontal| + and those that affect splitting a window). + • Terminal buffers emit a |TermRequest| autocommand event when the child + process emits an OSC or DCS control sequence. + • Terminal buffers respond to OSC background and foreground requests. + |default-autocmds| + +• Treesitter: + • Bundled parsers and queries (highlight, folds) for Markdown, Python, and + Bash. + • |:InspectTree| shows root nodes. + • |:InspectTree| now supports |folding|. + • |:InspectTree| shows node ranges in 0-based instead of 1-based indexing. + • |vim.treesitter.foldexpr()| now recognizes folds captured using a + quantified query pattern. + • |vim.treesitter.query.omnifunc()| provides completion in treesitter query + files (set by default). + • |vim.treesitter.query.edit()| provides live editing of treesitter queries. + • |Query:iter_matches()| now has the ability to set the maximum start depth + for matches. + • `@injection.language` now has smarter resolution and will fall back to + language aliases (e.g., filetype or custom shorthands) registered via + |vim.treesitter.language.register()| and/or attempt lower case variants of + the text. + • `@injection.filename` will try to match the node text via + |vim.filetype.match()| and treat the result as a language name in the same + way as `@injection.language`. + • The `#set!` directive supports `injection.self` and `injection.parent` for + injecting either the current node's language or the parent + |LanguageTree|'s language, respectively. + • The `#set!` directive can set the "url" property of a node to have the + node emit a hyperlink. Hyperlinks are UI specific: in the TUI, the OSC 8 + control sequence is used. + • Improved error messages for query parsing. + +• TUI: + • Builtin TUI can now recognize "super" (|<D-|) and "meta" (|<T-|) modifiers + in a terminal emulator that supports |tui-csiu|. + • The |TermResponse| event can be used with |v:termresponse| to read escape + sequence responses from the host terminal. + • A clipboard provider which uses OSC 52 to copy the selection to the system + clipboard is now bundled by default and will be automatically enabled under + certain conditions. |clipboard-osc52| + • 'termsync' option asks the host terminal to buffer screen updates until + the redraw cycle is complete. Requires support from the host terminal. + +• UI: + • Enhanced support for rendering multibyte characters using composing + characters: the maximum limit was increased from 1+6 codepoints to + 31 bytes, which is guaranteed to fit all chars from before but often more. + • NOTE: the regexp engine still has a hard-coded limit of considering + 6 composing chars only. + + +============================================================================== +CHANGED FEATURES + +These existing features changed their behavior. + +• API: + • |nvim_buf_call()| and |nvim_win_call()| now preserve any return value (NB: + not multiple return values) + +• Editor: + • |gx| now uses |vim.ui.open()| and not netrw. To customize, you can redefine + `vim.ui.open` or remap `gx`. To continue using netrw (deprecated): >vim + :call netrw#BrowseX(expand(exists("g:netrw_gx") ? g:netrw_gx : '<cfile>'), netrw#CheckIfRemote())<CR> + +• LSP: + • LSP hover and signature help now use Treesitter for highlighting of + Markdown content. Note that highlighting of code examples requires + a matching parser and may be affected by custom queries. + • |LspRequest| autocmd callbacks contain more information about the LSP + request status update that occurred. + +• Lua: + • |vim.wait()| cannot be called in |api-fast|. + • |vim.diagnostic.config()| now accepts virtual text relevant options to + |nvim_buf_set_extmark()| (e.g. "virt_text_pos" and "hl_mode") in its + "virtual_text" table, which gives users more control over how diagnostic + virtual text is displayed. + • |vim.diagnostic.get()| and |vim.diagnostic.count()| accept multiple + namespaces rather than just a single namespace. + • |vim.diagnostic.enable()| gained new parameters, and the old signature is + deprecated. + • |vim.diagnostic.config()| now accepts a function for the virtual_text.prefix + option, which allows for rendering e.g., diagnostic severities differently. + +• Options: + • Attempting to set an invalid keycode option (e.g. `set t_foo=123`) no + longer gives an error. + +• Terminal: + • Terminal buffers started with no arguments (and use 'shell') close + automatically if the job exited without error, eliminating the (often + unwanted) "[Process exited 0]" message. |default-autocmds| + +• Treesitter: + • |Query:iter_matches()|, |vim.treesitter.query.add_predicate()|, and + |vim.treesitter.query.add_directive()| accept a new `all` option which + ensures that all matching nodes are returned as a table. The default option + `all=false` returns only a single node, breaking captures with quantifiers + like `(comment)+ @comment`; it is only provided for backward compatibility + and will be removed after Nvim 0.10. + • |vim.treesitter.query.add_predicate()| and + |vim.treesitter.query.add_directive()| now accept an options table rather + than a boolean "force" argument. To force a predicate or directive to + override an existing predicate or directive, use `{ force = true }`. + +============================================================================== +REMOVED FEATURES + +These deprecated features were removed. + +• Vimball support, including `:Vimuntar` command + +• Support for legacy treesitter injection queries + +• 'shortmess' flags: + • |shm-f|. Always use "(3 of 5)", never "(file 3 of 5)". + • |shm-i|. Always use "[noeol]". + • |shm-x|. Always use "[dos]", "[unix]" and "[mac]". + • |shm-n|. Always use "[New]". + +============================================================================== +DEPRECATIONS + +See |deprecated-0.10|. + + vim:tw=78:ts=8:sw=2:et:ft=help:norl: diff --git a/runtime/doc/news-0.9.txt b/runtime/doc/news-0.9.txt index 789bc9e0bc..7905d6c3e3 100644 --- a/runtime/doc/news-0.9.txt +++ b/runtime/doc/news-0.9.txt @@ -4,7 +4,7 @@ NVIM REFERENCE MANUAL -Notable changes in Nvim 0.9 from 0.8 *news-0.9* +Notable changes since Nvim 0.8 *news-0.9* Type |gO| to see the table of contents. @@ -162,7 +162,7 @@ The following new APIs or features were added. • |vim.diagnostic| now supports LSP DiagnosticsTag. See: https://microsoft.github.io/language-server-protocol/specification/#diagnosticTag -• |vim.diagnostic.is_disabled()| checks if diagnostics are disabled in a given +• vim.diagnostic.is_disabled() checks if diagnostics are disabled in a given buffer or namespace. • Treesitter captures can now be transformed by directives. This will allow @@ -290,34 +290,6 @@ The following deprecated functions or APIs were removed. ============================================================================== DEPRECATIONS -The following functions are now deprecated and will be removed in the next -release. - -• |vim.treesitter.language.add()| replaces `vim.treesitter.language.require_language()` - -• |vim.treesitter.get_node_at_pos()| and |vim.treesitter.get_node_at_cursor()| - are both deprecated in favor of |vim.treesitter.get_node()|. - -• `vim.api.nvim_get_hl_by_name()`, `vim.api.nvim_get_hl_by_id()` were deprecated, use |nvim_get_hl()| instead. - -• The following top level Treesitter functions have been moved: - `vim.treesitter.inspect_language()` -> `vim.treesitter.language.inspect()` - `vim.treesitter.get_query_files()` -> `vim.treesitter.query.get_files()` - `vim.treesitter.set_query()` -> `vim.treesitter.query.set()` - `vim.treesitter.query.set_query()` -> `vim.treesitter.query.set()` - `vim.treesitter.get_query()` -> `vim.treesitter.query.get()` - `vim.treesitter.query.get_query()` -> `vim.treesitter.query.get()` - `vim.treesitter.parse_query()` -> `vim.treesitter.query.parse()` - `vim.treesitter.query.parse_query()` -> `vim.treesitter.query.parse()` - `vim.treesitter.add_predicate()` -> `vim.treesitter.query.add_predicate()` - `vim.treesitter.add_directive()` -> `vim.treesitter.query.add_directive()` - `vim.treesitter.list_predicates()` -> `vim.treesitter.query.list_predicates()` - `vim.treesitter.list_directives()` -> `vim.treesitter.query.list_directives()` - `vim.treesitter.query.get_range()` -> `vim.treesitter.get_range()` - `vim.treesitter.query.get_node_text()` -> `vim.treesitter.get_node_text()` - -• |nvim_exec()| is now deprecated in favor of |nvim_exec2()|. - -• Renamed |vim.pretty_print()| to |vim.print()|. +See |deprecated-0.9|. vim:tw=78:ts=8:sw=2:et:ft=help:norl: diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 3029414500..708e127136 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -4,529 +4,156 @@ NVIM REFERENCE MANUAL -Notable changes in Nvim 0.10 from 0.9 *news* +Notable changes since Nvim 0.10 *news* -For changes in Nvim 0.9, see |news-0.9|. +For changes in the previous release, see |news-0.10|. Type |gO| to see the table of contents. ============================================================================== -BREAKING CHANGES *news-breaking* - -The following changes may require adaptations in user config or plugins. - -• In some cases, the cursor in the Nvim |TUI| used to blink even without - configuring 'guicursor' as mentioned in |cursor-blinking|. This was a bug - that has now been fixed. If your cursor has stopped blinking, add the - following (or similar, adapted to user preference) to your |config| file: >vim - - set guicursor+=n-v-c:blinkon500-blinkoff500 -< -• |vim.tbl_islist()| now checks whether a table is actually list-like (i.e., - has integer keys without gaps and starting from 1). For the previous - behavior (only check for integer keys, allow gaps or not starting with 1), - use |vim.tbl_isarray()|. - -• "#" followed by a digit no longer stands for a function key at the start of - the lhs of a mapping. - -• `:behave` was removed. - - If you used `:behave xterm`, the following is equivalent: >vim - - set mousemodel=extend -< - - If you used `:behave mswin`, the following is equivalent: >vim - - set selection=exclusive - set selectmode=mouse,key - set mousemodel=popup - set keymodel=startsel,stopsel -< -• When switching windows, |CursorMoved| autocommands trigger when Nvim is back - in the main loop rather than immediately. This is more compatible with Vim. - -• |-l| ensures output ends with a newline if the script prints messages and - doesn't cause Nvim to exit. - -• |LspRequest| and LspProgressUpdate (renamed to |LspProgress|) autocmds were - promoted from a |User| autocmd to first class citizen. - -• Renamed `vim.treesitter.playground` to `vim.treesitter.dev`. - -• Removed functions from the |vim.json| module: - • Unnecessary, undocumented functions which caused global side-effects. - • `vim.json.null` is redundant with `vim.NIL`. - • `vim.json.array_mt` (and related) is redundant with `vim.empty_dict()`. - -• Removed some Vim 5.0<= option compatibilities: - • |'backspace'| no longer supports number values. Instead: - • for `backspace=0` set `backspace=` (empty) - • for `backspace=1` set `backspace=indent,eol` - • for `backspace=2` set `backspace=indent,eol,start` (default behavior in Nvim) - • for `backspace=3` set `backspace=indent,eol,nostop` - • |'backupdir'| and |'directory'| will no longer remove a `>` at the start - of the option. - -• |LanguageTree:parse()| will no longer parse injections by default and - now requires an explicit range argument to be passed. If injections are - required, provide an explicit range via `parser:parse({ start_row, end_row })`. - -• |vim.lsp.util.parse_snippet()| will now strictly follow the snippet grammar - defined by LSP, and hence previously parsed snippets might now be considered - invalid input. - -• |OptionSet| autocommand args |v:option_new|, |v:option_old|, - |v:option_oldlocal|, |v:option_oldglobal| now have the type of the option - instead of always being strings. |v:option_old| is now the old global value - for all global-local options, instead of just string global-local options. - -• Local value for a global-local number/boolean option is now unset when - the option is set (e.g. using |:set| or |nvim_set_option_value()|) without a - scope, which means they now behave the same way as string options. - -• Signs placed through the legacy |sign-commands| are now stored and displayed - as |extmarks| internally. Along with the following changes: - • A sign placed twice in the same group with the same identifier will be moved. - • Legacy signs are always deleted along with the line it is placed on. - • Legacy and extmark signs will show up in both |:sign-place-list| and |nvim_buf_get_extmarks()|. - • Legacy and extmark signs are displayed and listed with the same priority: - line number -> priority -> sign id -> recently placed - -• Default color scheme has been updated to be "Nvim branded" and accessible. - Use `:colorscheme vim` to revert to the old legacy color scheme. - - Here is a list of Nvim specific highlight groups which are now defined in - a meaningfully different way and might need an update: - • |hl-FloatBorder| is linked to |hl-NormalFloat| instead of |hl-WinSeparator|. - • |hl-NormalFloat| is not linked to |hl-Pmenu|. - • |hl-WinBar| has different background. - • |hl-WinBarNC| is similar to |hl-WinBar| but not bold. - • |hl-WinSeparator| is linked to |hl-Normal| instead of |hl-VertSplit|. - - This also might result into some color schemes looking differently due to - them relying on implicit assumptions about how highlight groups are defined - by default. To account for this, define all attributes of highlight groups - explicitly. Alternatively, use `:colorscheme vim` or `:source - $VIMRUNTIME/colors/vim.lua` to restore previous definitions. - -• 'termguicolors' is enabled by default when Nvim is able to determine that - the host terminal emulator supports 24-bit color. - -• Treesitter highlight groups have been renamed to be more in line with - upstream tree-sitter and Helix to make it easier to share queries. The full - list is documented in |treesitter-highlight-groups|. - -• |vim.lsp.codelens.refresh()| now takes an `opts` argument. With this change, - the default behavior of just refreshing the current buffer has been replaced by - refreshing all buffers. - -• |shm-q| now fully hides macro recording message instead of only shortening it. - -• Returning any truthy value from a callback passed to |nvim_create_autocmd()| - (rather than just `true`) will delete the autocommand. - -• |vim.lsp.util.extract_completion_items()| will no longer return reliable - results, since it does not apply `itemDefaults` when its input is a - `CompletionList`. - Moreover, since support for LSP `completionList.itemDefaults` was added, - some third party plugins might be negatively impacted in case the language - servers support the feature but the plugin does not. - If necessary, the respective capability can be - removed when calling |vim.lsp.protocol.make_client_capabilities()|. - -• |:TOhtml| has been rewritten in Lua to support Neovim-specific decorations, - and many options have been removed. - -============================================================================== BREAKING CHANGES IN HEAD *news-breaking-dev* -The following breaking changes were made during the development cycle to -unreleased features on Nvim HEAD. + ====== Remove this section before release. ====== -• Removed `vim.treesitter.foldtext` as transparent foldtext is now supported - https://github.com/neovim/neovim/pull/20750 +The following changes to UNRELEASED features were made during the development +cycle (Nvim HEAD, the "master" branch). ============================================================================== -NEW FEATURES *news-features* - -The following new APIs and features were added. - -• Performance: - • 'diffopt' "linematch" scoring algorithm now favours larger and less groups - https://github.com/neovim/neovim/pull/23611 - • Treesitter highlighting now parses injections incrementally during - screen redraws only for the line range being rendered. This significantly - improves performance in large files with many injections. - • 'breakindent' performance is significantly improved for wrapped lines. - • Cursor movement, insertion with [count] and |screenpos()| are now faster. - -• |vim.iter()| provides a generic iterator interface for tables and Lua - iterators |for-in|. - -• |vim.ringbuf()| creates ring buffers. - -• |vim.keycode()| translates keycodes in a string. - -• |'smoothscroll'| option to scroll by screen line rather than by text line - when |'wrap'| is set. - -• |nvim_buf_set_extmark()| supports inline virtual text. - -• 'foldtext' now supports virtual text format. |fold-foldtext| - -• |'foldtext'| can be set to an empty string to disable and render the line: - as normal with regular highlighting and no line wrapping. - -• The terminal buffer now supports reflow (wrapped lines adapt when the buffer - is resized horizontally). Note: Lines that are not visible and kept in - |'scrollback'| are not reflown. - -• |vim.system()| for running system commands. - -• |vim.lpeg| and |vim.re| expose the bundled Lpeg expression grammar parser - and its regex interface. - -• |nvim_win_text_height()| computes the number of screen lines occupied - by a range of text in a given window. - -• Mapping APIs now support abbreviations when mode short-name has suffix "a". - -• Better cmdline completion for string option value. |complete-set-option| - -• Builtin TUI can now recognize "super" (|<D-|) and "meta" (|<T-|) modifiers in a - terminal emulator that supports |tui-csiu|. - -• Editor - • By default, the swapfile "ATTENTION" |E325| dialog is skipped if the - swapfile is owned by a running Nvim process, instead of prompting. If you - always want the swapfile dialog, delete the default SwapExists handler: - `autocmd! nvim_swapfile`. |default-autocmds| - -• LSP - • LSP method names are available in |vim.lsp.protocol.Methods|. - • Implemented LSP inlay hints: |lsp-inlay_hint| - https://microsoft.github.io/language-server-protocol/specification/#textDocument_inlayHint - • Implemented pull diagnostic textDocument/diagnostic: |vim.lsp.diagnostic.on_diagnostic()| - https://microsoft.github.io/language-server-protocol/specification/#textDocument_diagnostic - • |vim.lsp.status()| consumes the last progress messages as a string. - • LSP client now always saves and restores named buffer marks when applying - text edits. - • LSP client now supports the `positionEncoding` server capability. If a server - responds with the `positionEncoding` capability in its initialization - response, Nvim automatically sets the client's `offset_encoding` field. - • Dynamic registration of LSP capabilities. An implication of this change is - that checking a client's `server_capabilities` is no longer a sufficient - indicator to see if a server supports a feature. Instead use - `client.supports_method(<method>)`. It considers both the dynamic - capabilities and static `server_capabilities`. - • `anchor_bias` option to |lsp-handlers| aids in positioning of floating - windows. - • |vim.lsp.util.locations_to_items()| sets the `user_data` of each item to - the original LSP `Location` or `LocationLink`. - • Added support for connecting to servers using named pipes (Windows) or - unix domain sockets (Unix) via |vim.lsp.rpc.domain_socket_connect()|. - • Added support for `completionList.itemDefaults`, reducing overhead when - computing completion items where properties often share the same value - (e.g. `commitCharacters`). Note that this might affect plugins and - language servers that don't support the feature, and in such cases the - respective capability can be unset. - -• Treesitter - • Bundled parsers and queries (highlight, folds) for Markdown, Python, and - Bash. - • |vim.treesitter.query.omnifunc()| for treesitter query files (set by - default). - • |Query:iter_matches()| now has the ability to set the maximum start depth - for matches. - • `@injection.language` now has smarter resolution and will fall back to - language aliases (e.g., filetype or custom shorthands) registered via - |vim.treesitter.language.register()| and/or attempt lower case variants of - the text. - • The `#set!` directive now supports `injection.self` and `injection.parent` - for injecting either the current node's language or the parent - |LanguageTree|'s language, respectively. - • |vim.treesitter.query.edit()| allows live editing of treesitter - queries. - • Improved error messages for query parsing. - • |:InspectTree| shows node ranges in 0-based indexing instead of 1-based - indexing. - • |:InspectTree| shows root nodes - • |:InspectTree| now supports |folding| - -• |vim.ui.open()| opens URIs using the system default handler (macOS `open`, - Windows `explorer`, Linux `xdg-open`, etc.) - -• |vim.wo| can now be double indexed for |:setlocal| behaviour. Currently only - `0` for the buffer index is currently supported. - -• Lua type annotations for: - • `vim.*` - • `vim.fn.*` - • `vim.api.*` - • `vim.v.*` - -• Improved messages for type errors in `vim.api.*` calls (including `opts` params) +BREAKING CHANGES *news-breaking* -• Functions that take a severity as an optional parameter (e.g. - |vim.diagnostic.get()|) now also accept a list of severities |vim.diagnostic.severity| +These changes may require adaptations in your config or plugins. -• New RPC client type `msgpack-rpc` is added for |nvim_set_client_info()| to - support fully MessagePack-RPC compliant clients. +API -• Floating windows can now show footer with new `footer` and `footer_pos` - config fields. Uses |hl-FloatFooter| by default. +• `vim.rpcnotify(0)` and `rpcnotify(0)` broadcast to ALL channels. Previously + they would "multicast" only to subscribed channels (controlled by + `nvim_subscribe()`). Plugins and clients that want "multicast" behavior must + now maintain their own list of channels. + • In the future, |vim.rpcnotify()| may accept a list of channels, if there + is demand for this use-case. -• Floating windows can now be hidden by setting `hide` in |nvim_open_win()| or - |nvim_win_set_config()|. +DEFAULTS -• |:terminal| command now accepts some |:command-modifiers| (specifically - |:horizontal| and those that affect splitting a window). +• TODO -• |$NVIM_APPNAME| can be set to a relative path instead of only a name. +EDITOR -• |:fclose| command. +• The order in which signs are placed was changed. Higher priority signs will now appear left of lower priority signs. -• |vim.snippet| handles expansion of snippets in LSP format. +EVENTS -• 'complete' option supports "f" flag for completing buffer names. +• TODO -• |vim.base64.encode()| and |vim.base64.decode()| encode and decode strings - using Base64 encoding. - -• The |TermResponse| autocommand event can be used with |v:termresponse| to - read escape sequence responses from the terminal. - -• A clipboard provider which uses OSC 52 to copy the selection to the system - clipboard is now bundled by default and will be automatically enabled under - certain conditions. |clipboard-osc52| - -• 'termsync' option asks the terminal emulator to buffer screen updates until - the redraw cycle is complete. Requires support from the terminal. - -• |vim.text.hexencode()| and |vim.text.hexdecode()| convert strings to and - from byte representations. +LSP -• 'completeopt' option supports "popup" flag to show extra information in a - floating window. +• TODO -• |nvim_input_mouse()| supports mouse buttons "x1" and "x2". +LUA -• |v_Q-default| and |v_@-default| repeat a register for each line of a visual - selection. +• TODO -• |vim.diagnostic.count()| returns the number of diagnostics for a given - buffer and/or namespace, by severity. This is a faster alternative to - |vim.diagnostic.get()| when only the number of diagnostics is needed, but - not the diagnostics themselves. +OPTIONS -• |vim.deepcopy()| has a `noref` argument to avoid hashing table values. +• TODO -• Terminal buffers emit a |TermRequest| autocommand event when the child - process emits an OSC or DCS control sequence. +PLUGINS -• Terminal buffers respond to OSC background and foreground requests. |default-autocmds| +• TODO -• |vim.version.le()| and |vim.version.ge()| are added to |vim.version|. +TREESITTER -• |extmarks| can be associated with a URL and URLs are included as a new - highlight attribute. The TUI will display URLs using the OSC 8 control - sequence, enabling clickable text in supporting terminals. +• TODO -• Added |nvim_tabpage_set_win()| to set the current window of a tabpage. +TUI -• Clicking on a tabpage in the tabline with the middle mouse button closes it. +• TODO -• |namespace| can now have window scopes. |nvim_win_add_ns()| +============================================================================== +NEW FEATURES *news-features* -• |extmarks| option `scoped`: only show the extmarks in its namespace's scope. +The following new features were added. -============================================================================== -CHANGED FEATURES *news-changed* +API -The following changes to existing APIs or features add new behavior. +• TODO -• |vim.tbl_contains()| now works for general tables and allows specifying a - predicate function that is checked for each value. (Use |vim.list_contains()| - for checking list-like tables (integer keys without gaps) for literal values.) +DEFAULTS -• |vim.region()| can use a string accepted by |getpos()| as position. +• Keymaps: + - |grn| in Normal mode maps to |vim.lsp.buf.rename()| + - |grr| in Normal mode maps to |vim.lsp.buf.references()| + - |gra| in Normal and Visual mode maps to |vim.lsp.buf.code_action()| + - CTRL-S in Insert mode maps to |vim.lsp.buf.signature_help()| -• |vim.diagnostic.config()| now accepts a function for the virtual_text.prefix - option, which allows for rendering e.g., diagnostic severities differently. +EDITOR -• Defaults: - • On Windows 'isfname' does not include ":". Drive letters are handled - correctly without it. (Use |gF| for filepaths suffixed with ":line:col"). - • 'comments' includes "fb:•". - • 'shortmess' includes the "C" flag. - • Automatic linting of treesitter query files (see |ft-query-plugin|). - Can be disabled via: >lua - vim.g.query_lint_on = {} -< - • Enabled treesitter highlighting for treesitter query files. - • Enabled treesitter highlighting for help files. - • Enabled treesitter highlighting for Lua files. +• TODO -• The `workspace/didChangeWatchedFiles` LSP client capability is now enabled - by default. - • On Mac or Windows, `libuv.fs_watch` is used as the backend. - • On Linux, `fswatch` (recommended) is used as the backend if available, - otherwise `libuv.fs_event` is used on each subdirectory. +EVENTS -• |LspRequest| autocmd callbacks now contain additional information about the LSP - request status update that occurred. +• TODO -• |:source| without arguments treats a buffer with its 'filetype' set to "lua" - as Lua code regardless of its extension. +LSP -• |:lua| with a |[range]| executes that range in the current buffer as Lua code - regardless of its extension. +• TODO -• |:checkhealth| buffer now implements |folding|. The initial folding status is - defined by the 'foldenable' option. +LUA -• |:Man| now respects 'wrapmargin' +• TODO -• |gx| now uses |vim.ui.open()| and not netrw. To customize, you can redefine - `vim.ui.open` or remap `gx`. To continue using netrw (deprecated): >vim +OPTIONS - :call netrw#BrowseX(expand(exists("g:netrw_gx")? g:netrw_gx : '<cfile>'), netrw#CheckIfRemote())<CR> +• TODO -• |vim.lsp.start()| now maps |K| to use |vim.lsp.buf.hover()| if the server - supports it, unless |'keywordprg'| was customized before calling - |vim.lsp.start()|. +PERFORMANCE -• Terminal buffers started with no arguments (and use 'shell') close - automatically if the job exited without error, eliminating the (often - unwanted) "[Process exited 0]" message. +• TODO -• |vim.diagnostic.config()| now accepts virtual text relevant options to - |nvim_buf_set_extmark()| (e.g. "virt_text_pos" and "hl_mode") in its - "virtual_text" table, which gives users more control over how diagnostic - virtual text is displayed. +PLUGINS -• Extmarks now fully support multi-line ranges, and a single extmark can be - used to highlight a range of arbitrary length. The |nvim_buf_set_extmark()| - API function already allowed you to define such ranges, but highlight regions - were not rendered consistently for a range that covers more than one line break. - This has now been fixed. Signs defined as part of a multi-line extmark also - apply to every line in the range, not just the first. - In addition, |nvim_buf_get_extmarks()| has gained an "overlap" option to - return such ranges even if they started before the specified position. +• TODO -• The following flags were added to |nvim_buf_set_extmark()|: - - "undo_restore": opt-out extmarks of precise undo tracking. - - "invalidate": automatically hide or delete extmarks. - - "virt_text_repeat_linebreak": repeat virtual text on wrapped lines. +STARTUP -• LSP hover and signature help now use Treesitter for highlighting of Markdown - content. - Note that syntax highlighting of code examples requires a matching parser - and may be affected by custom queries. +• TODO -• Support for rendering multibyte characters using composing characters has been - enhanced. The maximum limit have been increased from 1+6 codepoints to - 31 bytes, which is guaranteed to fit all chars from before but often more. +TERMINAL - NOTE: the regexp engine still has a hard-coded limit of considering - 6 composing chars only. +• TODO -• |vim.wait()| is no longer allowed to be called in |api-fast|. +TREESITTER -• Vimscript function |exists()| supports checking |v:lua| functions. +• TODO -• Added "force_crlf" option field in |nvim_open_term()|. +TUI -• Attempting to set an invalid keycode option (e.g. `set t_foo=123`) no longer - gives an error. +• TODO -• Passing 0 to |nvim_get_chan_info()| gets info about the current channel. +UI -• |:checkhealth| buffer can now be opened in a split window using modifiers like - |:vertical|, |:horizontal| and |:botright|. +• TODO -• |nvim_open_win()| and |nvim_win_set_config()| now support opening normal (split) - windows, and moving floating windows into split windows. -• 'errorfile' (|-q|) accepts `-` as an alias for stdin. +• |CompleteDone| now sets the `reason` key in `v:event` which specifies the reason + for completion being done. -• |--startuptime| reports the startup times for both processes (TUI + server) as separate sections. +============================================================================== +CHANGED FEATURES *news-changed* -• |nvim_buf_call()| and |nvim_win_call()| now preserves any return value (NB: not multiple return values) +These existing features changed their behavior. -• Treesitter - • |Query:iter_matches()|, |vim.treesitter.query.add_predicate()|, and - |vim.treesitter.query.add_directive()| accept a new `all` option which - ensures that all matching nodes are returned as a table. The default option - `all=false` returns only a single node, breaking captures with quantifiers - like `(comment)+ @comment; it is only provided for backward compatibility - and will be removed after Nvim 0.10. - • |vim.treesitter.query.add_predicate()| and - |vim.treesitter.query.add_directive()| now accept an options table rather - than a boolean "force" argument. To force a predicate or directive to - override an existing predicate or directive, use `{ force = true }`. +• N/A ============================================================================== REMOVED FEATURES *news-removed* -The following deprecated functions or APIs were removed. - -• Vimball support - - :Vimuntar command - -• Support for legacy treesitter injection queries +These deprecated features were removed. -• 'shortmess' flags: - - |shm-f|. Always uses "(3 of 5)", never "(file 3 of 5)" - - |shm-i|. Always use "[noeol]". - - |shm-x|. Always use "[dos]", "[unix]" and "[mac]" - - |shm-n|. Always use "[New]". +• N/A ============================================================================== DEPRECATIONS *news-deprecations* -The following functions are now deprecated and will be removed in a future -release. - -• Configuring |diagnostic-signs| using |:sign-define| or |sign_define()|. Use - the "signs" key of |vim.diagnostic.config()| instead. - -• Checkhealth functions: - - |health#report_error|, |vim.health.report_error()| Use |vim.health.error()| instead. - - |health#report_info|, |vim.health.report_info()| Use |vim.health.info()| instead. - - |health#report_ok|, |vim.health.report_ok()| Use |vim.health.ok()| instead. - - |health#report_start|, |vim.health.report_start()| Use |vim.health.start()| instead. - - |health#report_warn|, |vim.health.report_warn()| Use |vim.health.warn()| instead. - -• |API| functions: - - |nvim_buf_get_option()| Use |nvim_get_option_value()| instead. - - |nvim_buf_set_option()| Use |nvim_set_option_value()| instead. - - |nvim_get_option()| Use |nvim_get_option_value()| instead. - - |nvim_set_option()| Use |nvim_set_option_value()| instead. - - |nvim_win_get_option()| Use |nvim_get_option_value()| instead. - - |nvim_win_set_option()| Use |nvim_set_option_value()| instead. - -• vim.lsp functions: - - |vim.lsp.util.get_progress_messages()| Use |vim.lsp.status()| instead. - - |vim.lsp.get_active_clients()| Use |vim.lsp.get_clients()| instead. - - |vim.lsp.for_each_buffer_client()| Use |vim.lsp.get_clients()| instead. - - |vim.lsp.util.trim_empty_lines()| Use |vim.split()| with `trimempty` instead. - - |vim.lsp.util.try_trim_markdown_code_blocks()| - - |vim.lsp.util.set_lines()| - - |vim.lsp.util.extract_completion_items()| - - |vim.lsp.util.parse_snippet()| - - |vim.lsp.util.text_document_completion_list_to_complete_items()| - -• `vim.loop` has been renamed to |vim.uv|. - -• vim.treesitter functions: - - |LanguageTree:for_each_child()| Use |LanguageTree:children()| (non-recursive) instead. - -• The "term_background" UI option |ui-ext-options| is deprecated and no longer - populated. Background color detection is now performed in Lua by the Nvim - core, not the TUI. - -• vim.shared functions: - - |vim.tbl_add_reverse_lookup()| +See |deprecated-0.11|. vim:tw=78:ts=8:sw=2:et:ft=help:norl: diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index d4e05cee26..a6ebc7e958 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -126,6 +126,46 @@ color index is just forwarded. Editor highlighting (|syntax-highlighting|, |highlight-groups|, etc.) has higher precedence: it is applied after terminal colors are resolved. +------------------------------------------------------------------------------ +EVENTS *terminal-events* + +Applications running in a :terminal buffer can send requests, which Nvim +exposes via the |TermRequest| event. + +OSC 7: change working directory *terminal-osc7* + +To handle OSC 7 emitted from :terminal processes, this code will :cd to the +directory indicated in the request. >lua + + vim.api.nvim_create_autocmd({ 'TermRequest' }, { + desc = 'Handles OSC 7 dir change requests', + callback = function(ev) + if string.sub(vim.v.termrequest, 1, 4) == '\x1b]7;' then + local dir = string.gsub(vim.v.termrequest, '\x1b]7;file://[^/]*', '') + if vim.fn.isdirectory(dir) == 0 then + vim.notify('invalid dir: '..dir) + return + end + vim.api.nvim_buf_set_var(ev.buf, 'osc7_dir', dir) + if vim.o.autochdir and vim.api.nvim_get_current_buf() == ev.buf then + vim.cmd.cd(dir) + end + end + end + }) + vim.api.nvim_create_autocmd({ 'BufEnter', 'WinEnter', 'DirChanged' }, { + callback = function(ev) + if vim.b.osc7_dir and vim.fn.isdirectory(vim.b.osc7_dir) == 1 then + vim.cmd.cd(vim.b.osc7_dir) + end + end + }) + +To try it out, select the above code and source it with `:'<,'>lua`, then run +this command in a :terminal buffer: > + + printf "\033]7;file://./foo/bar\033\\" + ============================================================================== Status Variables *terminal-status* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 133b2bc33c..5570e62ab8 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -270,6 +270,7 @@ created, thus they behave slightly differently: Option Reason ~ 'previewwindow' there can only be a single one 'scroll' specific to existing window + 'winfixbuf' specific to existing window 'winfixheight' specific to existing window 'winfixwidth' specific to existing window @@ -638,10 +639,9 @@ A jump table for the options with a short description can be found at |Q_op|. *'allowrevins'* *'ari'* *'noallowrevins'* *'noari'* 'allowrevins' 'ari' boolean (default off) global - Allow CTRL-_ in Insert and Command-line mode. This is default off, to - avoid that users that accidentally type CTRL-_ instead of SHIFT-_ get - into reverse Insert mode, and don't know how to get out. See - 'revins'. + Allow CTRL-_ in Insert mode. This is default off, to avoid that users + that accidentally type CTRL-_ instead of SHIFT-_ get into reverse + Insert mode, and don't know how to get out. See 'revins'. *'ambiwidth'* *'ambw'* 'ambiwidth' 'ambw' string (default "single") @@ -1442,7 +1442,7 @@ A jump table for the options with a short description can be found at |Q_op|. 'commentstring' 'cms' string (default "") local to buffer A template for a comment. The "%s" in the value is replaced with the - comment text. For example, C uses "/*%s*/". Currently only used to + comment text. For example, C uses "/*%s*/". Used for |commenting| and to add markers for folding, see |fold-marker|. *'complete'* *'cpt'* *E535* @@ -2851,25 +2851,33 @@ A jump table for the options with a short description can be found at |Q_op|. This is a scanf-like string that uses the same format as the 'errorformat' option: see |errorformat|. + If ripgrep ('grepprg') is available, this option defaults to `%f:%l:%c:%m`. + *'grepprg'* *'gp'* -'grepprg' 'gp' string (default "grep -n ", - Unix: "grep -n $* /dev/null") +'grepprg' 'gp' string (default see below) global or local to buffer |global-local| Program to use for the |:grep| command. This option may contain '%' and '#' characters, which are expanded like when used in a command- line. The placeholder "$*" is allowed to specify where the arguments will be included. Environment variables are expanded |:set_env|. See |option-backslash| about including spaces and backslashes. - When your "grep" accepts the "-H" argument, use this to make ":grep" - also work well with a single file: >vim - set grepprg=grep\ -nH -< Special value: When 'grepprg' is set to "internal" the |:grep| command + Special value: When 'grepprg' is set to "internal" the |:grep| command works like |:vimgrep|, |:lgrep| like |:lvimgrep|, |:grepadd| like |:vimgrepadd| and |:lgrepadd| like |:lvimgrepadd|. See also the section |:make_makeprg|, since most of the comments there apply equally to 'grepprg'. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. + This option defaults to: + - `rg --vimgrep -uu ` if ripgrep is available (|:checkhealth|), + - `grep -HIn $* /dev/null` on Unix, + - `findstr /n $* nul` on Windows. + Ripgrep can perform additional filtering such as using .gitignore rules + and skipping hidden files. This is disabled by default (see the -u option) + to more closely match the behaviour of standard grep. + You can make ripgrep match Vim's case handling using the + -i/--ignore-case and -S/--smart-case options. + An |OptionSet| autocmd can be used to set it up to match automatically. *'guicursor'* *'gcr'* *E545* *E546* *E548* *E549* 'guicursor' 'gcr' string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20") @@ -4952,9 +4960,6 @@ A jump table for the options with a short description can be found at |Q_op|. Minimum is 1, maximum is 100000. Only in |terminal| buffers. - Note: Lines that are not visible and kept in scrollback are not - reflown when the terminal buffer is resized horizontally. - *'scrollbind'* *'scb'* *'noscrollbind'* *'noscb'* 'scrollbind' 'scb' boolean (default off) local to window @@ -5696,8 +5701,7 @@ A jump table for the options with a short description can be found at |Q_op|. highlighted with |hl-NonText|. You may also want to add "lastline" to the 'display' option to show as much of the last line as possible. - NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y - and scrolling with the mouse. + NOTE: partly implemented, doesn't work yet for |gj| and |gk|. *'softtabstop'* *'sts'* 'softtabstop' 'sts' number (default 0) @@ -6271,6 +6275,8 @@ A jump table for the options with a short description can be found at |Q_op|. "split" when both are present. uselast If included, jump to the previously used window when jumping to errors with |quickfix| commands. + If a window has 'winfixbuf' enabled, 'switchbuf' is currently not + applied to the split window. *'synmaxcol'* *'smc'* 'synmaxcol' 'smc' number (default 3000) @@ -6349,7 +6355,7 @@ A jump table for the options with a short description can be found at |Q_op|. appear wrong in many places. The value must be more than 0 and less than 10000. - There are four main ways to use tabs in Vim: + There are five main ways to use tabs in Vim: 1. Always keep 'tabstop' at 8, set 'softtabstop' and 'shiftwidth' to 4 (or 3 or whatever you prefer) and use 'noexpandtab'. Then Vim will use a mix of tabs and spaces, but typing <Tab> and <BS> will @@ -6882,6 +6888,7 @@ A jump table for the options with a short description can be found at |Q_op|. Level Messages ~ ---------------------------------------------------------------------- + 1 Enables Lua tracing (see above). Does not produce messages. 2 When a file is ":source"'ed, or |shada| file is read or written. 3 UI info, terminal capabilities. 4 Shell commands. @@ -7216,11 +7223,20 @@ A jump table for the options with a short description can be found at |Q_op|. will scroll 'window' minus two lines, with a minimum of one. When 'window' is equal to 'lines' minus one CTRL-F and CTRL-B scroll in a much smarter way, taking care of wrapping lines. - When resizing the Vim window, the value is smaller than 1 or more than - or equal to 'lines' it will be set to 'lines' minus 1. + When resizing the Vim window, and the value is smaller than 1 or more + than or equal to 'lines' it will be set to 'lines' minus 1. Note: Do not confuse this with the height of the Vim window, use 'lines' for that. + *'winfixbuf'* *'wfb'* *'nowinfixbuf'* *'nowfb'* +'winfixbuf' 'wfb' boolean (default off) + local to window + If enabled, the window and the buffer it is displaying are paired. + For example, attempting to change the buffer with |:edit| will fail. + Other commands which change a window's buffer such as |:cnext| will + also skip any window with 'winfixbuf' enabled. However if an Ex + command has a "!" modifier, it can force switching buffers. + *'winfixheight'* *'wfh'* *'nowinfixheight'* *'nowfh'* 'winfixheight' 'wfh' boolean (default off) local to window |local-noglobal| diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index 17136ee650..1ef182127c 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -1375,6 +1375,19 @@ Finally, these constructs are unique to Perl: ============================================================================== 10. Highlighting matches *match-highlight* + *syntax-vs-match* + Note that the match highlight mechanism is independent + of |syntax-highlighting|, which is (usually) a buffer-local + highlighting, while matching is window-local, both methods + can be freely mixed. Match highlighting functions give you + a bit more flexibility in when and how to apply, but are + typically only used for temporary highlighting, without strict + rules. Both methods can be used to conceal text. + + Thus the matching functions like |matchadd()| won't consider + syntax rules and functions like |synconcealed()| and the + other way around. + *:mat* *:match* :mat[ch] {group} /{pattern}/ Define a pattern to highlight in the current window. It will diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index d8fcd066a8..81acd9cf7e 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -446,10 +446,6 @@ settings are described below, in |netrw-browser-options|, and in messages don't always seem to show up this way, but one doesn't have to quit the window. - *g:netrw_win95ftp* =1 if using Win95, will remove four trailing blank - lines that o/s's ftp "provides" on transfers - =0 force normal ftp behavior (no trailing line removal) - *g:netrw_cygwin* =1 assume scp under windows is from cygwin. Also permits network browsing to use ls with time and size sorting (default if windows) @@ -825,8 +821,6 @@ set in the user's <.vimrc> file: (see also |netrw-settings| |netrw-protocol|) g:netrw_uid Holds current user-id for ftp. g:netrw_use_nt_rcp =0 don't use WinNT/2K/XP's rcp (default) =1 use WinNT/2K/XP's rcp, binary mode - g:netrw_win95ftp =0 use unix-style ftp even if win95/98/ME/etc - =1 use default method to do ftp > ----------------------------------------------------------------------- < *netrw-internal-variables* @@ -955,21 +949,8 @@ messages) you may write a NetReadFixup() function: endfunction > The NetReadFixup() function will be called if it exists and thus allows you to -customize your reading process. As a further example, <netrw.vim> contains -just such a function to handle Windows 95 ftp. For whatever reason, Windows -95's ftp dumps four blank lines at the end of a transfer, and so it is -desirable to automate their removal. Here's some code taken from <netrw.vim> -itself: -> - if has("win95") && g:netrw_win95ftp - fun! NetReadFixup(method, line1, line2) - if method == 3 " ftp (no <.netrc>) - let fourblanklines= line2 - 3 - silent fourblanklines .. "," .. line2 .. "g/^\s*/d" - endif - endfunction - endif -> +customize your reading process. + (Related topics: |ftp| |netrw-userpass| |netrw-start|) ============================================================================== @@ -2048,11 +2029,7 @@ passwords: better way to do that: I can use a regular ssh account which uses a password to access the material without the need to key-in the password each time. It's good for security and convenience. I tried ssh public key - authorization + ssh-agent, implementing this, and it works! Here are two - links with instructions: - - http://www.ibm.com/developerworks/library/l-keyc2/ - http://sial.org/howto/openssh/publickey-auth/ + authorization + ssh-agent, implementing this, and it works! Ssh hints: @@ -3397,16 +3374,7 @@ Example: Clear netrw's marked file list via a mapping on gu > (This section is likely to grow as I get feedback) (also see |netrw-debug|) *netrw-p1* - P1. I use windows 95, and my ftp dumps four blank lines at the {{{2 - end of every read. - - See |netrw-fixup|, and put the following into your - <.vimrc> file: - - let g:netrw_win95ftp= 1 - - *netrw-p2* - P2. I use Windows, and my network browsing with ftp doesn't sort by {{{2 + P1. I use Windows, and my network browsing with ftp doesn't sort by {{{2 time or size! -or- The remote system is a Windows server; why don't I get sorts by time or size? @@ -3432,8 +3400,8 @@ Example: Clear netrw's marked file list via a mapping on gu > modify its listing behavior. - *netrw-p3* - P3. I tried rcp://user@host/ (or protocol other than ftp) and netrw {{{2 + *netrw-p2* + P2. I tried rcp://user@host/ (or protocol other than ftp) and netrw {{{2 used ssh! That wasn't what I asked for... Netrw has two methods for browsing remote directories: ssh @@ -3441,8 +3409,8 @@ Example: Clear netrw's marked file list via a mapping on gu > When it comes time to do download a file (not just a directory listing), netrw will use the given protocol to do so. - *netrw-p4* - P4. I would like long listings to be the default. {{{2 + *netrw-p3* + P3. I would like long listings to be the default. {{{2 Put the following statement into your |vimrc|: > @@ -3451,8 +3419,8 @@ Example: Clear netrw's marked file list via a mapping on gu > Check out |netrw-browser-var| for more customizations that you can set. - *netrw-p5* - P5. My times come up oddly in local browsing {{{2 + *netrw-p4* + P4. My times come up oddly in local browsing {{{2 Does your system's strftime() accept the "%c" to yield dates such as "Sun Apr 27 11:49:23 1997"? If not, do a @@ -3461,16 +3429,16 @@ Example: Clear netrw's marked file list via a mapping on gu > let g:netrw_timefmt= "%X" (where X is the option) < - *netrw-p6* - P6. I want my current directory to track my browsing. {{{2 + *netrw-p5* + P5. I want my current directory to track my browsing. {{{2 How do I do that? Put the following line in your |vimrc|: > let g:netrw_keepdir= 0 < - *netrw-p7* - P7. I use Chinese (or other non-ascii) characters in my filenames, {{{2 + *netrw-p6* + P6. I use Chinese (or other non-ascii) characters in my filenames, {{{2 and netrw (Explore, Sexplore, Hexplore, etc) doesn't display them! (taken from an answer provided by Wu Yongwei on the vim @@ -3484,8 +3452,8 @@ Example: Clear netrw's marked file list via a mapping on gu > (...it is one more reason to recommend that people use utf-8!) - *netrw-p8* - P8. I'm getting "ssh is not executable on your system" -- what do I {{{2 + *netrw-p7* + P7. I'm getting "ssh is not executable on your system" -- what do I {{{2 do? (Dudley Fox) Most people I know use putty for windows ssh. It @@ -3567,8 +3535,8 @@ Example: Clear netrw's marked file list via a mapping on gu > of the others will use the string in g:netrw_ssh_cmd by default. - *netrw-p9* *netrw-ml_get* - P9. I'm browsing, changing directory, and bang! ml_get errors {{{2 + *netrw-p8* *netrw-ml_get* + P8. I'm browsing, changing directory, and bang! ml_get errors {{{2 appear and I have to kill vim. Any way around this? Normally netrw attempts to avoid writing swapfiles for @@ -3578,8 +3546,8 @@ Example: Clear netrw's marked file list via a mapping on gu > in your <.vimrc>: > let g:netrw_use_noswf= 0 < - *netrw-p10* - P10. I'm being pestered with "[something] is a directory" and {{{2 + *netrw-p9* + P9. I'm being pestered with "[something] is a directory" and {{{2 "Press ENTER or type command to continue" prompts... The "[something] is a directory" prompt is issued by Vim, @@ -3589,8 +3557,8 @@ Example: Clear netrw's marked file list via a mapping on gu > I also suggest that you set your |'cmdheight'| to 2 (or more) in your <.vimrc> file. - *netrw-p11* - P11. I want to have two windows; a thin one on the left and my {{{2 + *netrw-p10* + P10. I want to have two windows; a thin one on the left and my {{{2 editing window on the right. How may I accomplish this? You probably want netrw running as in a side window. If so, you @@ -3615,8 +3583,8 @@ Example: Clear netrw's marked file list via a mapping on gu > <middlemouse> to select the file. - *netrw-p12* - P12. My directory isn't sorting correctly, or unwanted letters are {{{2 + *netrw-p11* + P11. My directory isn't sorting correctly, or unwanted letters are {{{2 appearing in the listed filenames, or things aren't lining up properly in the wide listing, ... @@ -3625,8 +3593,8 @@ Example: Clear netrw's marked file list via a mapping on gu > Multibyte encodings use two (or more) bytes per character. You may need to change |g:netrw_sepchr| and/or |g:netrw_xstrlen|. - *netrw-p13* - P13. I'm a Windows + putty + ssh user, and when I attempt to {{{2 + *netrw-p12* + P12. I'm a Windows + putty + ssh user, and when I attempt to {{{2 browse, the directories are missing trailing "/"s so netrw treats them as file transfers instead of as attempts to browse subdirectories. How may I fix this? @@ -3646,8 +3614,8 @@ Example: Clear netrw's marked file list via a mapping on gu > "let g:netrw_sftp_cmd = "d:\\dev\\putty\\PSFTP.exe" "let g:netrw_scp_cmd = "d:\\dev\\putty\\PSCP.exe" < - *netrw-p14* - P14. I would like to speed up writes using Nwrite and scp/ssh {{{2 + *netrw-p13* + P13. I would like to speed up writes using Nwrite and scp/ssh {{{2 style connections. How? (Thomer M. Gil) Try using ssh's ControlMaster and ControlPath (see the ssh_config @@ -3673,8 +3641,8 @@ Example: Clear netrw's marked file list via a mapping on gu > vim scp://host.domain.com//home/user/.bashrc < - *netrw-p15* - P15. How may I use a double-click instead of netrw's usual single {{{2 + *netrw-p14* + P14. How may I use a double-click instead of netrw's usual single {{{2 click to open a file or directory? (Ben Fritz) First, disable netrw's mapping with > @@ -3686,8 +3654,8 @@ Example: Clear netrw's marked file list via a mapping on gu > all netrw's mouse mappings, not just the <leftmouse> one. (see |g:netrw_mousemaps|) - *netrw-p16* - P16. When editing remote files (ex. :e ftp://hostname/path/file), {{{2 + *netrw-p15* + P15. When editing remote files (ex. :e ftp://hostname/path/file), {{{2 under Windows I get an |E303| message complaining that its unable to open a swap file. @@ -3695,8 +3663,8 @@ Example: Clear netrw's marked file list via a mapping on gu > directory. Start netrw from your $HOME or other writable directory. - *netrw-p17* - P17. Netrw is closing buffers on its own. {{{2 + *netrw-p16* + P16. Netrw is closing buffers on its own. {{{2 What steps will reproduce the problem? 1. :Explore, navigate directories, open a file 2. :Explore, open another file @@ -3709,15 +3677,15 @@ Example: Clear netrw's marked file list via a mapping on gu > It appears that the buffers are not exactly closed; a ":ls!" will show them (although ":ls" does not). - *netrw-P18* - P18. How to locally edit a file that's only available via {{{2 + *netrw-P17* + P17. How to locally edit a file that's only available via {{{2 another server accessible via ssh? See http://stackoverflow.com/questions/12469645/ "Using Vim to Remotely Edit A File on ServerB Only Accessible From ServerA" - *netrw-P19* - P19. How do I get numbering on in directory listings? {{{2 + *netrw-P18* + P18. How do I get numbering on in directory listings? {{{2 With |g:netrw_bufsettings|, you can control netrw's buffer settings; try putting > let g:netrw_bufsettings="noma nomod nu nobl nowrap ro nornu" @@ -3725,8 +3693,8 @@ Example: Clear netrw's marked file list via a mapping on gu > instead, try > let g:netrw_bufsettings="noma nomod nonu nobl nowrap ro rnu" < - *netrw-P20* - P20. How may I have gvim start up showing a directory listing? {{{2 + *netrw-P19* + P19. How may I have gvim start up showing a directory listing? {{{2 Try putting the following code snippet into your .vimrc: > augroup VimStartup au! @@ -3738,8 +3706,8 @@ Example: Clear netrw's marked file list via a mapping on gu > This snippet assumes that you have client-server enabled (ie. a "huge" vim version). - *netrw-P21* - P21. I've made a directory (or file) with an accented character, {{{2 + *netrw-P20* + P20. I've made a directory (or file) with an accented character, {{{2 but netrw isn't letting me enter that directory/read that file: Its likely that the shell or o/s is using a different encoding @@ -3749,8 +3717,8 @@ Example: Clear netrw's marked file list via a mapping on gu > au FileType netrw set enc=latin1 < - *netrw-P22* - P22. I get an error message when I try to copy or move a file: {{{2 + *netrw-P21* + P21. I get an error message when I try to copy or move a file: {{{2 > **error** (netrw) tried using g:netrw_localcopycmd<cp>; it doesn't work! < diff --git a/runtime/doc/pi_tar.txt b/runtime/doc/pi_tar.txt index dd1edb5707..c8570044e5 100644 --- a/runtime/doc/pi_tar.txt +++ b/runtime/doc/pi_tar.txt @@ -147,7 +147,8 @@ Copyright 2005-2017: *tar-copyright* v2 * converted to use Vim7's new autoload feature by Bram Moolenaar v1 (original) * Michael Toren - (see http://michael.toren.net/code/) + (see http://michael.toren.net/code/ + link seems dead) ============================================================================== vim:tw=78:ts=8:noet:ft=help diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index b8182347f8..a39f4bc5d7 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -185,7 +185,7 @@ See 'clipboard' for details and options. The presence of a working clipboard tool implicitly enables the '+' and "*" registers. Nvim looks for these clipboard tools, in order of priority: - - |g:clipboard| + - |g:clipboard| (unless unset or `false`) - pbcopy, pbpaste (macOS) - wl-copy, wl-paste (if $WAYLAND_DISPLAY is set) - waycopy, waypaste (if $WAYLAND_DISPLAY is set) diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 9037ecc0f9..5d3c0cbdc2 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -275,7 +275,8 @@ processing a quickfix or location list command, it will be aborted. current window is used instead of the quickfix list. *:cb* *:cbuffer* *E681* -:cb[uffer][!] [bufnr] Read the error list from the current buffer. +:[range]cb[uffer][!] [bufnr] + Read the error list from the current buffer. When [bufnr] is given it must be the number of a loaded buffer. That buffer will then be used instead of the current buffer. @@ -284,26 +285,31 @@ processing a quickfix or location list command, it will be aborted. See |:cc| for [!]. *:lb* *:lbuffer* -:lb[uffer][!] [bufnr] Same as ":cbuffer", except the location list for the +:[range]lb[uffer][!] [bufnr] + Same as ":cbuffer", except the location list for the current window is used instead of the quickfix list. *:cgetb* *:cgetbuffer* -:cgetb[uffer] [bufnr] Read the error list from the current buffer. Just +:[range]cgetb[uffer] [bufnr] + Read the error list from the current buffer. Just like ":cbuffer" but don't jump to the first error. *:lgetb* *:lgetbuffer* -:lgetb[uffer] [bufnr] Same as ":cgetbuffer", except the location list for +:[range]lgetb[uffer] [bufnr] + Same as ":cgetbuffer", except the location list for the current window is used instead of the quickfix list. *:cad* *:cadd* *:caddbuffer* -:cad[dbuffer] [bufnr] Read the error list from the current buffer and add +:[range]cad[dbuffer] [bufnr] + Read the error list from the current buffer and add the errors to the current quickfix list. If a quickfix list is not present, then a new list is created. Otherwise, same as ":cbuffer". *:laddb* *:laddbuffer* -:laddb[uffer] [bufnr] Same as ":caddbuffer", except the location list for +:[range]laddb[uffer] [bufnr] + Same as ":caddbuffer", except the location list for the current window is used instead of the quickfix list. @@ -1282,6 +1288,15 @@ g:compiler_gcc_ignore_unmatched_lines commands run from make are generating false positives. +PANDOC *quickfix-pandoc* *compiler-pandoc* + +The Pandoc compiler plugin expects that an output file type extension is +passed to make, say :make html or :make pdf. + +Additional arguments can be passed to pandoc: + +- either by appending them to make, say `:make html --self-contained` . +- or setting them in `b:pandoc_compiler_args` or `g:pandoc_compiler_args` PERL *quickfix-perl* *compiler-perl* diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 572dc8a841..c0d00d16cb 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -620,8 +620,7 @@ In Insert or Command-line mode: the help Short explanation of each option: *option-list* -'aleph' 'al' ASCII code of the letter Aleph (Hebrew) -'allowrevins' 'ari' allow CTRL-_ in Insert and Command-line mode +'allowrevins' 'ari' allow CTRL-_ in Insert mode 'ambiwidth' 'ambw' what to do with Unicode chars of ambiguous width 'arabic' 'arab' for Arabic as a default second language 'arabicshape' 'arshape' do shaping for Arabic characters @@ -739,8 +738,6 @@ Short explanation of each option: *option-list* 'helplang' 'hlg' preferred help languages 'hidden' 'hid' don't unload buffer when it is |abandon|ed 'history' 'hi' number of command-lines that are remembered -'hkmap' 'hk' Hebrew keyboard mapping -'hkmapp' 'hkp' phonetic Hebrew keyboard mapping 'hlsearch' 'hls' highlight matches with last search pattern 'icon' let Vim set the text of the window icon 'iconstring' string to use for the Vim icon text @@ -939,6 +936,7 @@ Short explanation of each option: *option-list* 'wildoptions' 'wop' specifies how command line completion is done 'winaltkeys' 'wak' when the windows system handles ALT keys 'window' 'wi' nr of lines to scroll for CTRL-F and CTRL-B +'winfixbuf' 'wfb' keep window focused on a single buffer 'winfixheight' 'wfh' keep window height when opening/closing windows 'winfixwidth' 'wfw' keep window width when opening/closing windows 'winheight' 'wh' minimum number of lines for the current window @@ -1140,7 +1138,7 @@ Context-sensitive completion on the command-line: |-b| -b binary mode |-l| -l lisp mode |-A| -A Arabic mode ('arabic' is set) -|-H| -H Hebrew mode ('hkmap' and 'rightleft' are set) +|-H| -H Hebrew mode (Hebrew keymap & 'rightleft' are set) |-V| -V Verbose, give informative messages |-r| -r give list of swap files |-r| -r {file} .. recover aborted edit session diff --git a/runtime/doc/remote_plugin.txt b/runtime/doc/remote_plugin.txt index cfe4b08000..694c339ef2 100644 --- a/runtime/doc/remote_plugin.txt +++ b/runtime/doc/remote_plugin.txt @@ -30,9 +30,9 @@ check whether a plugin host is available for their chosen programming language. Plugin hosts are programs that provide a high-level environment for plugins, taking care of most boilerplate involved in defining commands, autocmds, and -functions that are implemented over |RPC| connections. Hosts are loaded only -when one of their registered plugins require it, keeping Nvim's startup as -fast as possible, even if many plugins/hosts are installed. +functions implemented over |RPC| connections. Hosts are loaded only when one +of their registered plugins require it, keeping Nvim's startup as fast as +possible, even if many plugins/hosts are installed. ============================================================================== 3. Example *remote-plugin-example* diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index ae827fa06f..2263b20d1a 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -149,8 +149,8 @@ q Stops recording. @@ Repeat the previous @{0-9a-z":*} [count] times. *v_@-default* -{Visual}@{0-9a-z".=*+} In Visual mode, execute the contents of the register -{Visual}@@ but for each selected line. +{Visual}@{0-9a-z".=*+} In linewise Visual mode, execute the contents of the +{Visual}@@ register for each selected line. See |visual-repeat|, |default-mappings|. *Q* @@ -158,8 +158,8 @@ Q Repeat the last recorded register [count] times. See |reg_recorded()|. *v_Q-default* -{Visual}Q In Visual mode, repeat the last recorded register for - each selected line. +{Visual}Q In linewise Visual mode, repeat the last recorded + register for each selected line. See |visual-repeat|, |default-mappings|. *:@* diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index 0360ce67f6..6fa260be40 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -72,9 +72,10 @@ on the same line, the attributes of the sign with the highest priority is used independently of the sign group. The default priority for a sign is 10. The priority is assigned at the time of placing a sign. -When two signs with the same priority are present, and one has an icon or text -in the signcolumn while the other has line highlighting, then both are -displayed. +When multiple signs that each have an icon or text are present, signs are +ordered with increasing priority from left to right, up until the maximum +width set in 'signcolumn'. Lower priority signs that do not fit are hidden. +Highest priority signs with highlight attributes are always shown. When the line on which the sign is placed is deleted, the sign is removed along with it. diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index 29e4a7b0aa..269d52352d 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -51,6 +51,17 @@ To search for the next misspelled word: *[S* [S Like "]S" but search backwards. + *]r* +]r Move to next "rare" word after the cursor. + A count before the command can be used to repeat. + 'wrapscan' applies. + + *[r* +[r Like "]r" but search backwards, find the "rare" + word before the cursor. Doesn't recognize words + split over two lines, thus may stop at words that are + not highlighted as rare. + To add words to your own word list: diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index a6619bc381..fb8ed0344b 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -139,7 +139,7 @@ argument. --cmd {command} *--cmd* {command} will be executed before processing any vimrc file. - Otherwise it acts like -c {command}. You can use up to 10 of + Otherwise, it acts like -c {command}. You can use up to 10 of these commands, independently from "-c" commands. *-S* @@ -260,7 +260,8 @@ argument. -A Arabic mode. Sets the 'arabic' option on. *-H* --H Hebrew mode. Sets the 'rightleft' option on and 'keymap' to "hebrew" +-H Hebrew mode. Sets the 'rightleft' option on and the 'keymap' + option to "hebrew". *-V* *verbose* -V[N] Verbose. Sets the 'verbose' option to [N] (default: 10). @@ -306,7 +307,7 @@ argument. few windows will be editing an empty file. *-O* --O[N] Open N windows, split vertically. Otherwise it's like -o. +-O[N] Open N windows, split vertically. Otherwise, it's like -o. If both the -o and the -O option are given, the last one on the command line determines how the windows will be split. @@ -325,8 +326,10 @@ argument. This can be used to start Vim in a special mode, with special mappings and settings. A shell alias can be used to make - this easy to use. For example: > - alias vimc vim -u ~/.config/nvim/c_init.vim !* + this easy to use. For example, in a C shell descendant: > + alias vimc 'nvim -u ~/.config/nvim/c_init.vim \!*' +< And in a Bash shell: > + alias vimc='nvim -u ~/.config/nvim/c_init.vim' < Also consider using autocommands; see |autocommand|. When {vimrc} is "NONE" (all uppercase), all initializations @@ -544,7 +547,7 @@ accordingly, proceeding as follows: This means that Nvim will figure out the values of 'shellpipe' and 'shellredir' for you, unless you have set them yourself. -12. Set 'updatecount' to zero, if "-n" command argument used +12. Set 'updatecount' to zero, if "-n" command argument used. 13. Set binary options if the |-b| flag was given. @@ -581,11 +584,11 @@ Avoiding trojan horses ~ *trojan-horse* While reading the "vimrc" or the "exrc" file in the current directory, some commands can be disabled for security reasons by setting the 'secure' option. -This is always done when executing the command from a tags file. Otherwise it -would be possible that you accidentally use a vimrc or tags file that somebody -else created and contains nasty commands. The disabled commands are the ones -that start a shell, the ones that write to a file, and ":autocmd". The ":map" -commands are echoed, so you can see which keys are being mapped. +This is always done when executing the command from a tags file. Otherwise, +it would be possible that you accidentally use a vimrc or tags file that +somebody else created and contains nasty commands. The disabled commands are +the ones that start a shell, the ones that write to a file, and ":autocmd". +The ":map" commands are echoed, so you can see which keys are being mapped. If you want Vim to execute all commands in a local vimrc file, you can reset the 'secure' option in the EXINIT or VIMINIT environment variable or in the global exrc or vimrc file. This is not possible in vimrc or @@ -743,7 +746,7 @@ these are terminal or file dependent. Note that the options 'binary', 'paste' and 'readonly' are included, this might not always be what you want. -When special keys are used in mappings, The 'cpoptions' option will be +When special keys are used in mappings, the 'cpoptions' option will be temporarily set to its Vim default, to avoid the mappings to be misinterpreted. This makes the file incompatible with Vi, but makes sure it can be used with different terminals. @@ -797,8 +800,8 @@ You can quickly start editing with a previously saved View or Session with the *:mks* *:mksession* :mks[ession][!] [file] Write a Vim script that restores the current editing session. - When [!] is included an existing file is overwritten. - When [file] is omitted "Session.vim" is used. + When [!] is included, an existing file is overwritten. + When [file] is omitted, "Session.vim" is used. The output of ":mksession" is like ":mkvimrc", but additional commands are added to the file. Which ones depends on the 'sessionoptions' option. The @@ -810,16 +813,16 @@ resulting file, when executed with a ":source" command: 3. Closes all windows in the current tab page, except the current one; closes all tab pages except the current one (this results in currently loaded buffers to be unloaded, some may become hidden if 'hidden' is set or - otherwise specified); wipes out the current buffer, if it is empty - and unnamed. -4. Restores the current directory if 'sessionoptions' contains "curdir", or - sets the current directory to where the Session file is if 'sessionoptions' - contains "sesdir". + otherwise specified); wipes out the current buffer, if it is empty and + unnamed. +4. Restores the current directory, if 'sessionoptions' contains "curdir", or + sets the current directory to where the Session file is, if + 'sessionoptions' contains "sesdir". 5. Restores GUI Vim window position, if 'sessionoptions' contains "winpos". 6. Restores screen size, if 'sessionoptions' contains "resize". 7. Reloads the buffer list, with the last cursor positions. If 'sessionoptions' contains "buffers" then all buffers are restored, - including hidden and unloaded buffers. Otherwise only buffers in windows + including hidden and unloaded buffers. Otherwise, only buffers in windows are restored. 8. Restores all windows with the same layout. If 'sessionoptions' contains "help", help windows are restored. If 'sessionoptions' contains "blank", @@ -846,14 +849,14 @@ A session includes all tab pages, unless "tabpages" was removed from The |SessionLoadPost| autocmd event is triggered after a session file is loaded/sourced. *SessionLoad-variable* -While the session file is loading the SessionLoad global variable is set to 1. -Plugins can use this to postpone some work until the SessionLoadPost event is -triggered. +While the session file is loading, the SessionLoad global variable is set to +1. Plugins can use this to postpone some work until the SessionLoadPost event +is triggered. *:mkvie* *:mkview* :mkvie[w][!] [file] Write a Vim script that restores the contents of the current window. - When [!] is included an existing file is overwritten. + When [!] is included, an existing file is overwritten. When [file] is omitted or is a number from 1 to 9, a name is generated and 'viewdir' prepended. When the last path part of 'viewdir' does not exist, this @@ -868,14 +871,13 @@ triggered. The output of ":mkview" contains these items: 1. The argument list used in the window. When the global argument list is - used it is reset to the global list. + used, it is reset to the global list. The index in the argument list is also restored. 2. The file being edited in the window. If there is no file, the window is made empty. -3. Restore mappings, abbreviations and options local to the window if - 'viewoptions' contains "options" or "localoptions". For the options it - restores only values that are local to the current buffer and values local - to the window. +3. Restore mappings, abbreviations and options local to the window, if + 'viewoptions' contains "options" or "localoptions". Only option values + that are local to the current buffer and the current window are restored. When storing the view as part of a session and "options" is in 'sessionoptions', global values for local options will be stored too. 4. Restore folds when using manual folding and 'viewoptions' contains @@ -909,7 +911,7 @@ Note that Views and Sessions are not perfect: The combination of ":mkview" and ":loadview" can be used to store up to ten different views of a file. These are remembered in the directory specified with the 'viewdir' option. The views are stored using the file name. If a -file is renamed or accessed through a (symbolic) link the view will not be +file is renamed or accessed through a (symbolic) link, the view will not be found. You might want to clean up your 'viewdir' directory now and then. @@ -969,7 +971,7 @@ Notes for Unix: allow just anybody to read and write your ShaDa file! - Vim will not overwrite a ShaDa file that is not writable by the current "real" user. This helps for when you did "su" to become root, but your - $HOME is still set to a normal user's home directory. Otherwise Vim would + $HOME is still set to a normal user's home directory. Otherwise, Vim would create a ShaDa file owned by root that nobody else can read. - The ShaDa file cannot be a symbolic link. This is to avoid security issues. @@ -1001,11 +1003,11 @@ using this command: > vim -c "normal '0" -In a csh compatible shell you could make an alias for it: > +In a C shell descendant, you could make an alias for it: > alias lvim vim -c '"'normal "'"0'"' -For a bash-like shell: > +For a Bash-like shell: > alias lvim='vim -c "normal '\''0"' @@ -1099,15 +1101,15 @@ MANUALLY READING AND WRITING *shada-read-write* Two commands can be used to read and write the ShaDa file manually. This can be used to exchange registers between two running Vim programs: First type ":wsh" in one and then ":rsh" in the other. Note that if the register -already contained something, then ":rsh!" would be required. Also note -however that this means everything will be overwritten with information from +already contained something, then ":rsh!" would be required. Also note, +however, that this means everything will be overwritten with information from the first Vim, including the command line history, etc. The ShaDa file itself can be edited by hand too, although we suggest you start with an existing one to get the format right. You need to understand MessagePack (or, more likely, find software that is able to use it) format to do this. This can be useful in order to create a second file, say -"~/.my.shada" which could contain certain settings that you always want when +"~/.my.shada", which could contain certain settings that you always want when you first start Nvim. For example, you can preload registers with particular data, or put certain commands in the command line history. A line in your |config| file like > @@ -1171,8 +1173,8 @@ running) you have additional options: described in |shada-error-handling|). If 'shada' is empty, marks for up to 100 files will be written. When you get error "E929: All .tmp.X files exist, - cannot write ShaDa file!" check that no old temp files - were left behind (e.g. + cannot write ShaDa file!", check that no old temp + files were left behind (e.g. ~/.local/state/nvim/shada/main.shada.tmp*). Note: Executing :wshada will reset all |'quote| marks. @@ -1192,7 +1194,7 @@ running) you have additional options: the list is edited. If you get the |press-enter| prompt you can press "q" and still get the prompt to enter a file number. - Use ! to abandon a modified buffer. |abandon| + Use [!] to abandon a modified buffer. |abandon| SHADA FILE FORMAT *shada-format* diff --git a/runtime/doc/support.txt b/runtime/doc/support.txt index 80a035068a..5b8b32fa16 100644 --- a/runtime/doc/support.txt +++ b/runtime/doc/support.txt @@ -13,12 +13,13 @@ Supported platforms *supported-platforms* `System` `Tier` `Versions` `Tested versions` Linux 1 >= 2.6.32, glibc >= 2.12 Ubuntu 22.04 -macOS (Intel) 1 >= 10.15 macOS 12 -Windows 64-bit 1 >= 8 (see note below) Windows Server 2022 +macOS (Intel) 1 >= 11 macOS 12 +macOS (M1) 2 >= 11 macOS 14 +Windows 64-bit 1 >= Windows 10 Version 1809 Windows Server 2022 FreeBSD 1 >= 10 FreeBSD 13 -macOS (M1) 2 >= 10.15 OpenBSD 2 >= 7 MinGW 2 MinGW-w64 +Windows 64-bit 3 < Windows 10 Version 1809 Note: Windows 10 "Version 1809" or later is required for |:terminal|. To check your Windows version, run the "winver" command and look for "Version xxxx" @@ -33,7 +34,7 @@ Support types ~ systems are maintained to the best of our ability, without being a top priority. -* Tier 3: Not tested and no guarantees, but may work. +* Tier 3: Not tested and no guarantees, and not all features may work. Adding support for a new platform ~ diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index c02752a2b7..7893822a66 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -461,8 +461,24 @@ nasm_loose_syntax unofficial parser allowed syntax not as Error nasm_ctx_outside_macro contexts outside macro not as Error nasm_no_warn potentially risky syntax not as ToDo +ASTRO *astro.vim* *ft-astro-syntax* -ASPPERL and ASPVBS *ft-aspperl-syntax* *ft-aspvbs-syntax* +Configuration + +The following variables control certain syntax highlighting features. +You can add them to your .vimrc: > + let g:astro_typescript = "enable" +< +Enables TypeScript and TSX for ".astro" files. Default Value: "disable" > + let g:astro_stylus = "enable" +< +Enables Stylus for ".astro" files. Default Value: "disable" + +NOTE: You need to install an external plugin to support stylus in astro files. + + +ASPPERL *ft-aspperl-syntax* +ASPVBS *ft-aspvbs-syntax* `*.asp` and `*.asa` files could be either Perl or Visual Basic script. Since it's hard to detect this you can set two global variables to tell Vim what you are @@ -998,9 +1014,9 @@ Two syntax highlighting files exist for Euphoria. One for Euphoria version 3.1.1, which is the default syntax highlighting file, and one for Euphoria version 4.0.5 or later. -Euphoria version 3.1.1 (https://www.rapideuphoria.com/) is still necessary -for developing applications for the DOS platform, which Euphoria version 4 -(https://www.openeuphoria.org/) does not support. +Euphoria version 3.1.1 (https://www.rapideuphoria.com/ link seems dead) is +still necessary for developing applications for the DOS platform, which +Euphoria version 4 (https://www.openeuphoria.org/) does not support. The following file extensions are auto-detected as Euphoria file type: > @@ -1057,7 +1073,8 @@ Elixir. FLEXWIKI *flexwiki.vim* *ft-flexwiki-syntax* -FlexWiki is an ASP.NET-based wiki package available at https://www.flexwiki.com +FlexWiki is an ASP.NET-based wiki package available at +https://www.flexwiki.com NOTE: This site currently doesn't work, on Wikipedia is mentioned that development stopped in 2009. @@ -1527,15 +1544,25 @@ Function names are not highlighted, as the way to find functions depends on how you write Java code. The syntax file knows two possible ways to highlight functions: -If you write function declarations that are always indented by either -a tab, 8 spaces or 2 spaces you may want to set > +If you write function declarations that are consistently indented by either +a tab, or a space . . . or eight space character(s), you may want to set > :let java_highlight_functions="indent" + :let java_highlight_functions="indent1" + :let java_highlight_functions="indent2" + :let java_highlight_functions="indent3" + :let java_highlight_functions="indent4" + :let java_highlight_functions="indent5" + :let java_highlight_functions="indent6" + :let java_highlight_functions="indent7" + :let java_highlight_functions="indent8" +Note that in terms of 'shiftwidth', this is the leftmost step of indentation. However, if you follow the Java guidelines about how functions and classes are -supposed to be named (with respect to upper and lowercase), use > +supposed to be named (with respect to upper- and lowercase) and there is any +amount of indentation, you may want to set > :let java_highlight_functions="style" -If both options do not work for you, but you would still want function -declarations to be highlighted create your own definitions by changing the -definitions in java.vim or by creating your own java.vim which includes the +If neither setting does work for you, but you would still want function +declarations to be highlighted, create your own definitions by changing the +definitions in java.vim or by creating your own java.vim that includes the original one and then adds the code to highlight functions. In Java 1.1 the functions System.out.println() and System.err.println() should @@ -1599,6 +1626,15 @@ To disable syntax highlighting of errors: > let g:vim_json_warnings = 0 +JQ *jq.vim* *jq_quote_highlight* *ft-jq-syntax* + +To disable numbers having their own color add the following to your vimrc: > + hi link jqNumber Normal + +If you want quotes to have different highlighting than strings > + let g:jq_quote_highlight = 1 + + LACE *lace.vim* *ft-lace-syntax* Lace (Language for Assembly of Classes in Eiffel) is case insensitive, but the @@ -1764,10 +1800,19 @@ MARKDOWN *ft-markdown-syntax* If you have long regions there might be wrong highlighting. At the cost of slowing down displaying, you can have the engine look further back to sync on -the start of a region, for example 500 lines: > +the start of a region, for example 500 lines (default is 50): > :let g:markdown_minlines = 500 +If you want to enable fenced code block syntax highlighting in your markdown +documents you can enable like this: > + + :let g:markdown_fenced_languages = ['html', 'python', 'bash=sh'] + +To disable markdown syntax concealing add the following to your vimrc: > + + :let g:markdown_syntax_conceal = 0 + MATHEMATICA *mma.vim* *ft-mma-syntax* *ft-mathematica-syntax* @@ -1985,6 +2030,95 @@ by the camlp4 preprocessor. Setting the variable > prevents highlighting of "end" as error, which is useful when sources contain very long structures that Vim does not synchronize anymore. +PANDOC *ft-pandoc-syntax* + +By default, markdown files will be detected as filetype "markdown". +Alternatively, you may want them to be detected as filetype "pandoc" instead. +To do so, set the following: > + + :let g:markdown_md = 'pandoc' + +The pandoc syntax plugin uses |conceal| for pretty highlighting. Default is 1 > + + :let g:pandoc#syntax#conceal#use = 1 + +To specify elements that should not be concealed, set the following variable: > + + :let g:pandoc#syntax#conceal#blacklist = [] + +This is a list of the rules wich can be used here: + + - titleblock + - image + - block + - subscript + - superscript + - strikeout + - atx + - codeblock_start + - codeblock_delim + - footnote + - definition + - list + - newline + - dashes + - ellipses + - quotes + - inlinecode + - inlinemath + +You can customize the way concealing works. For example, if you prefer to mark +footnotes with the `*` symbol: > + + :let g:pandoc#syntax#conceal#cchar_overrides = {"footnote" : "*"} + +To conceal the urls in links, use: > + + :let g:pandoc#syntax#conceal#urls = 1 + +Prevent highlighting specific codeblock types so that they remain Normal. +Codeblock types include "definition" for codeblocks inside definition blocks +and "delimited" for delimited codeblocks. Default = [] > + + :let g:pandoc#syntax#codeblocks#ignore = ['definition'] + +Use embedded highlighting for delimited codeblocks where a language is +specified. Default = 1 > + + :let g:pandoc#syntax#codeblocks#embeds#use = 1 + +For specify what languages and using what syntax files to highlight embeds. This is a +list of language names. When the language pandoc and vim use don't match, you +can use the "PANDOC=VIM" syntax. For example: > + + :let g:pandoc#syntax#codeblocks#embeds#langs = ["ruby", "bash=sh"] + +To use italics and strong in emphases. Default = 1 > + + :let g:pandoc#syntax#style#emphases = 1 + +"0" will add "block" to g:pandoc#syntax#conceal#blacklist, because otherwise +you couldn't tell where the styles are applied. + +To add underline subscript, superscript and strikeout text styles. Default = 1 > + + :let g:pandoc#syntax#style#underline_special = 1 + +Detect and highlight definition lists. Disabling this can improve performance. +Default = 1 (i.e., enabled by default) > + + :let g:pandoc#syntax#style#use_definition_lists = 1 + +The pandoc syntax script also comes with the following commands: > + + :PandocHighlight LANG + +Enables embedded highlighting for language LANG in codeblocks. Uses the +syntax for items in g:pandoc#syntax#codeblocks#embeds#langs. > + + :PandocUnhighlight LANG + +Disables embedded highlighting for language LANG in codeblocks. PAPP *papp.vim* *ft-papp-syntax* @@ -2099,8 +2233,8 @@ perl_string_as_statement, it will be highlighted as in the second line. The syncing has 3 options. The first two switch off some triggering of synchronization and should only be needed in case it fails to work properly. If while scrolling all of a sudden the whole screen changes color completely -then you should try and switch off one of those. Let me know if you can -figure out the line that causes the mistake. +then you should try and switch off one of those. Let the developer know if +you can figure out the line that causes the mistake. One triggers on "^\s*sub\s*" and the other on "^[$@%]" more or less. > @@ -3173,7 +3307,7 @@ The g:vimsyn_embed option allows users to select what, if any, types of embedded script highlighting they wish to have. > g:vimsyn_embed == 0 : disable (don't embed any scripts) - g:vimsyn_embed == 'lPr' : support embedded lua, python and ruby + g:vimsyn_embed == 'lpPr' : support embedded lua, perl, python and ruby < This option is disabled by default. *g:vimsyn_folding* @@ -3183,7 +3317,11 @@ Some folding is now supported with syntax/vim.vim: > g:vimsyn_folding == 0 or doesn't exist: no syntax-based folding g:vimsyn_folding =~ 'a' : augroups g:vimsyn_folding =~ 'f' : fold functions + g:vimsyn_folding =~ 'h' : fold heredocs + g:vimsyn_folding =~ 'l' : fold lua script + g:vimsyn_folding =~ 'p' : fold perl script g:vimsyn_folding =~ 'P' : fold python script + g:vimsyn_folding =~ 'r' : fold ruby script < *g:vimsyn_noerror* Not all error highlighting that syntax/vim.vim does may be correct; Vim script @@ -3708,7 +3846,9 @@ Whether or not it is actually concealed depends on the value of the 'conceallevel' option. The 'concealcursor' option is used to decide whether concealable items in the current line are displayed unconcealed to be able to edit the line. -Another way to conceal text is with |matchadd()|. + +Another way to conceal text is with |matchadd()|, but internally this works a +bit differently |syntax-vs-match|. concealends *:syn-concealends* @@ -3716,7 +3856,9 @@ When the "concealends" argument is given, the start and end matches of the region, but not the contents of the region, are marked as concealable. Whether or not they are actually concealed depends on the setting on the 'conceallevel' option. The ends of a region can only be concealed separately -in this way when they have their own highlighting via "matchgroup" +in this way when they have their own highlighting via "matchgroup". The +|synconcealed()| function can be used to retrieve information about conealed +items. cchar *:syn-cchar* *E844* @@ -4767,11 +4909,11 @@ guisp={color-name} *guisp* Colors which define Nvim's default color scheme: NvimDarkBlue NvimLightBlue NvimDarkCyan NvimLightCyan + NvimDarkGray1 NvimLightGray1 + NvimDarkGray2 NvimLightGray2 + NvimDarkGray3 NvimLightGray3 + NvimDarkGray4 NvimLightGray4 NvimDarkGreen NvimLightGreen - NvimDarkGrey1 NvimLightGrey1 - NvimDarkGrey2 NvimLightGrey2 - NvimDarkGrey3 NvimLightGrey3 - NvimDarkGrey4 NvimLightGrey4 NvimDarkMagenta NvimLightMagenta NvimDarkRed NvimLightRed NvimDarkYellow NvimLightYellow @@ -4868,7 +5010,7 @@ MatchParen Character under the cursor or just before it, if it *hl-ModeMsg* ModeMsg 'showmode' message (e.g., "-- INSERT --"). *hl-MsgArea* -MsgArea Area for messages and cmdline. +MsgArea Area for messages and command-line, see also 'cmdheight'. *hl-MsgSeparator* MsgSeparator Separator for scrolled messages |msgsep|. *hl-MoreMsg* @@ -4914,6 +5056,8 @@ QuickFixLine Current |quickfix| item in the quickfix window. Combined with *hl-Search* Search Last search pattern highlighting (see 'hlsearch'). Also used for similar items that need to stand out. + *hl-SnippetTabstop* +SnippetTabstop Tabstops in snippets. |vim.snippet| *hl-SpecialKey* SpecialKey Unprintable characters: Text displayed differently from what it really is. But not 'listchars' whitespace. |hl-Whitespace| diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index 49b2773253..2f50e31ee5 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -187,7 +187,7 @@ gt *i_CTRL-<PageDown>* *i_<C-PageDown>* :1tabnext " go to the first tab page :$tabnext " go to the last tab page :tabnext $ " as above - :tabnext # " go to the last accessed tab page + :tabnext # " go to the last accessed tab page :tabnext - " go to the previous tab page :tabnext -1 " as above :tabnext + " go to the next tab page @@ -216,8 +216,8 @@ gT Go to the previous tab page. Wraps around from the first one :tabl[ast] Go to the last tab page. <C-Tab> *CTRL-<Tab>* *<C-Tab>* -CTRL-W g<Tab> *g<Tab>* *CTRL-W_g<Tab>* -g<Tab> Go to the last accessed tab page. +g<Tab> *g<Tab>* *CTRL-W_g<Tab>* +CTRL-W g<Tab> Go to the last accessed tab page. Other commands: *:tabs* @@ -239,18 +239,17 @@ REORDERING TAB PAGES: Move the current tab page to after tab page N. Use zero to make the current tab page the first one. N is counted before the move, thus if the second tab is the current one, - `:tabmove 1` and `:tabmove 2` have no effect. + `:tabmove 1` and `:tabmove 2` have no effect. Without N the tab page is made the last one. > - :.tabmove " do nothing - :-tabmove " move the tab page to the left - :+tabmove " move the tab page to the right - :0tabmove " move the tab page to the beginning of the tab - " list + :.tabmove " do nothing + :-tabmove " move the tab page to the left + :+tabmove " move the tab page to the right + :0tabmove " move the tab page to the first :tabmove 0 " as above :tabmove " move the tab page to the last :$tabmove " as above :tabmove $ " as above - :tabmove # " move the tab page after the last accessed + :tabmove # " move the tab page after the last accessed " tab page :tabm[ove] +[N] diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index 2b5b253a09..ef1654d365 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -400,19 +400,24 @@ the pattern. *tag-!* If the tag is in the current file this will always work. Otherwise the performed actions depend on whether the current file was changed, whether a ! -is added to the command and on the 'autowrite' option: - - tag in file autowrite ~ -current file changed ! option action ~ - --------------------------------------------------------------------------- - yes x x x goto tag - no no x x read other file, goto tag - no yes yes x abandon current file, read other file, goto - tag - no yes no on write current file, read other file, goto - tag - no yes no off fail - --------------------------------------------------------------------------- +is added to the command and on the 'autowrite' and 'winfixbuf' options: + + tag in file winfixbuf autowrite ~ +current file changed ! option option action ~ + ----------------------------------------------------------------------------- + yes x x off x goto tag + no no x off x read other file, goto tag + no yes yes off x abandon current file, + read other file, goto tag + no yes no off on write current file, + read other file, goto tag + no yes no off off fail + yes x yes x x goto tag + no no no on x fail + no yes no on x fail + no yes no on on fail + no yes no on off fail + ----------------------------------------------------------------------------- - If the tag is in the current file, the command will always work. - If the tag is in another file and the current file was not changed, the @@ -428,6 +433,8 @@ current file changed ! option action ~ the changes, use the ":w" command and then use ":tag" without an argument. This works because the tag is put on the stack anyway. If you want to lose the changes you can use the ":tag!" command. +- If the tag is in another file and the window includes 'winfixbuf', the + command will fail. If the tag is in the same file then it may succeed. *tag-security* Note that Vim forbids some commands, for security reasons. This works like @@ -773,9 +780,17 @@ CTRL-W i Open a new window, with the cursor on the first line beginning of the file. If a count is given, the count'th matching line is displayed. + *[d-default* + Mapped to |vim.diagnostic.goto_prev()| by default. + |default-mappings| + *]d* ]d like "[d", but start at the current cursor position. + *]d-default* + Mapped to |vim.diagnostic.goto_next()| by default. + |default-mappings| + *:ds* *:dsearch* :[range]ds[earch][!] [count] [/]string[/] Like "[d" and "]d", but search in [range] lines @@ -822,6 +837,10 @@ CTRL-W d Open a new window, with the cursor on the first beginning of the file. If a count is given, the count'th matching line is jumped to. + *CTRL-W_d-default* + Mapped to |vim.diagnostic.open_float()| by default. + |default-mappings| + *:dsp* *:dsplit* :[range]dsp[lit][!] [count] [/]string[/] Like "CTRL-W d", but search in [range] lines diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 5f33802ad5..0b84bb60d4 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -18,18 +18,31 @@ changes. This documentation may also not fully reflect the latest changes. PARSER FILES *treesitter-parsers* Parsers are the heart of treesitter. They are libraries that treesitter will -search for in the `parser` runtime directory. By default, Nvim bundles parsers -for C, Lua, Vimscript, Vimdoc and Treesitter query files, but parsers can be -installed via a plugin like https://github.com/nvim-treesitter/nvim-treesitter -or even manually. +search for in the `parser` runtime directory. + +Nvim includes these parsers: + +- C +- Lua +- Markdown +- Vimscript +- Vimdoc +- Treesitter query files |ft-query-plugin| + +You can install more parsers manually, or with a plugin like +https://github.com/nvim-treesitter/nvim-treesitter . Parsers are searched for as `parser/{lang}.*` in any 'runtimepath' directory. If multiple parsers for the same language are found, the first one is used. (NOTE: This typically implies the priority "user config > plugins > bundled".) -A parser can also be loaded manually using a full path: >lua + +To load a parser from its filepath: >lua vim.treesitter.language.add('python', { path = "/path/to/python.so" }) < +Parser names are assumed to be lower case if the file system is +case-sensitive. + To associate certain |filetypes| with a treesitter language (name of parser), use |vim.treesitter.language.register()|. For example, to use the `xml` treesitter parser for buffers with filetype `svg` or `xslt`, use: >lua @@ -65,6 +78,8 @@ An instance `TSNode` of a treesitter node supports the following methods. TSNode:parent() *TSNode:parent()* Get the node's immediate parent. + Prefer |TSNode:child_containing_descendant()| + for iterating over the node's ancestors. TSNode:next_sibling() *TSNode:next_sibling()* Get the node's next sibling. @@ -101,6 +116,9 @@ TSNode:named_child({index}) *TSNode:named_child()* Get the node's named child at the given {index}, where zero represents the first named child. +TSNode:child_containing_descendant({descendant}) *TSNode:child_containing_descendant()* + Get the node's child that contains {descendant}. + TSNode:start() *TSNode:start()* Get the node's start position. Return three values: the row, column and total byte count (all zero-based). @@ -149,7 +167,7 @@ TSNode:sexpr() *TSNode:sexpr()* Get an S-expression representing the node as a string. TSNode:id() *TSNode:id()* - Get an unique identifier for the node inside its own tree. + Get a unique identifier for the node inside its own tree. No guarantees are made about this identifier's internal representation, except for being a primitive Lua type with value equality (so not a @@ -469,10 +487,11 @@ The following is a list of standard captures used in queries for Nvim, highlighted according to the current colorscheme (use |:Inspect| on one to see the exact definition): -@variable various variable names -@variable.builtin built-in variable names (e.g. `this` / `self`) -@variable.parameter parameters of a function -@variable.member object and struct fields +@variable various variable names +@variable.builtin built-in variable names (e.g. `this`, `self`) +@variable.parameter parameters of a function +@variable.parameter.builtin special parameters (e.g. `_`, `it`) +@variable.member object and struct fields @constant constant identifiers @constant.builtin built-in constant values @@ -480,7 +499,7 @@ the exact definition): @module modules or namespaces @module.builtin built-in modules or namespaces -@label GOTO and other labels (e.g. `label:` in C), including heredoc labels +@label `GOTO` and other labels (e.g. `label:` in C), including heredoc labels @string string literals @string.documentation string documenting code (e.g. Python docstrings) @@ -501,9 +520,9 @@ the exact definition): @type type or class definitions and annotations @type.builtin built-in types @type.definition identifiers in type definitions (e.g. `typedef <type> <identifier>` in C) -@type.qualifier type qualifiers (e.g. `const`) -@attribute attribute annotations (e.g. Python decorators) +@attribute attribute annotations (e.g. Python decorators, Rust lifetimes) +@attribute.builtin builtin annotations (e.g. `@property` in Python) @property the key in key/value pairs @function function definitions @@ -515,27 +534,28 @@ the exact definition): @function.method.call method calls @constructor constructor calls and definitions -@operator symbolic operators (e.g. `+` / `*`) +@operator symbolic operators (e.g. `+`, `*`) @keyword keywords not fitting into specific categories @keyword.coroutine keywords related to coroutines (e.g. `go` in Go, `async/await` in Python) @keyword.function keywords that define a function (e.g. `func` in Go, `def` in Python) -@keyword.operator operators that are English words (e.g. `and` / `or`) -@keyword.import keywords for including modules (e.g. `import` / `from` in Python) -@keyword.storage modifiers that affect storage in memory or life-time -@keyword.repeat keywords related to loops (e.g. `for` / `while`) +@keyword.operator operators that are English words (e.g. `and`, `or`) +@keyword.import keywords for including modules (e.g. `import`, `from` in Python) +@keyword.type keywords defining composite types (e.g. `struct`, `enum`) +@keyword.modifier keywords defining type modifiers (e.g. `const`, `static`, `public`) +@keyword.repeat keywords related to loops (e.g. `for`, `while`) @keyword.return keywords like `return` and `yield` @keyword.debug keywords related to debugging -@keyword.exception keywords related to exceptions (e.g. `throw` / `catch`) +@keyword.exception keywords related to exceptions (e.g. `throw`, `catch`) -@keyword.conditional keywords related to conditionals (e.g. `if` / `else`) -@keyword.conditional.ternary ternary operator (e.g. `?` / `:`) +@keyword.conditional keywords related to conditionals (e.g. `if`, `else`) +@keyword.conditional.ternary ternary operator (e.g. `?`, `:`) @keyword.directive various preprocessor directives and shebangs @keyword.directive.define preprocessor definition directives -@punctuation.delimiter delimiters (e.g. `;` / `.` / `,`) -@punctuation.bracket brackets (e.g. `()` / `{}` / `[]`) +@punctuation.delimiter delimiters (e.g. `;`, `.`, `,`) +@punctuation.bracket brackets (e.g. `()`, `{}`, `[]`) @punctuation.special special symbols (e.g. `{}` in string interpolation) @comment line and block comments @@ -543,7 +563,7 @@ the exact definition): @comment.error error-type comments (e.g. `ERROR`, `FIXME`, `DEPRECATED`) @comment.warning warning-type comments (e.g. `WARNING`, `FIX`, `HACK`) -@comment.todo todo-type comments (e.g. `TODO`, `WIP`, `FIXME`) +@comment.todo todo-type comments (e.g. `TODO`, `WIP`) @comment.note note-type comments (e.g. `NOTE`, `INFO`, `XXX`) @markup.strong bold text @@ -552,10 +572,15 @@ the exact definition): @markup.underline underlined text (only for literal underline markup!) @markup.heading headings, titles (including markers) +@markup.heading.1 top-level heading +@markup.heading.2 section heading +@markup.heading.3 subsection heading +@markup.heading.4 and so on +@markup.heading.5 and so forth +@markup.heading.6 six levels ought to be enough for anybody @markup.quote block quotes @markup.math math environments (e.g. `$ ... $` in LaTeX) -@markup.environment environments (e.g. in LaTeX) @markup.link text references, footnotes, citations, etc. @markup.link.label link, reference descriptions @@ -573,6 +598,7 @@ the exact definition): @diff.delta changed text (for diff files) @tag XML-style tag names (e.g. in XML, HTML, etc.) +@tag.builtin XML-style tag names (e.g. HTML5 tags) @tag.attribute XML-style tag attributes @tag.delimiter XML-style tag delimiters @@ -646,6 +672,10 @@ parent tree. The language injection query allows you to specify these • `@injection.language` - indicates that the captured node’s text may contain the name of a language that should be used to re-parse the `@injection.content`. + • `@injection.filename` - indicates that the captured node’s text may + contain a filename; the corresponding filetype is then looked-up up via + |vim.filetype.match()| and treated as the name of a language that should + be used to re-parse the `@injection.content`. The language injection behavior can also be configured by some properties associated with patterns: @@ -719,8 +749,7 @@ get_captures_at_pos({bufnr}, {row}, {col}) • {col} (`integer`) Position column Return: ~ - (`table[]`) List of captures - `{ capture = "name", metadata = { ... } }` + (`{capture: string, lang: string, metadata: table}[]`) get_node({opts}) *vim.treesitter.get_node()* Returns the smallest named node at the given position @@ -978,7 +1007,7 @@ add_directive({name}, {handler}, {opts}) Parameters: ~ • {name} (`string`) Name of the directive, without leading # - • {handler} (`function`) + • {handler} (`fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: table)`) • match: A table mapping capture IDs to a list of captured nodes • pattern: the index of the matching pattern in the query @@ -986,10 +1015,10 @@ add_directive({name}, {handler}, {opts}) • predicate: list of strings containing the full directive being called, e.g. `(node (#set! conceal "-"))` would get the predicate `{ "#set!", "conceal", "-" }` - • {opts} (`table<string, any>`) Optional options: - • force (boolean): Override an existing predicate of the - same name - • all (boolean): Use the correct implementation of the + • {opts} (`table`) A table with the following fields: + • {force}? (`boolean`) Override an existing predicate of + the same name + • {all}? (`boolean`) Use the correct implementation of the match table where capture IDs map to a list of nodes instead of a single node. Defaults to false (for backward compatibility). This option will eventually become the @@ -1001,13 +1030,13 @@ add_predicate({name}, {handler}, {opts}) Parameters: ~ • {name} (`string`) Name of the predicate, without leading # - • {handler} (`function`) + • {handler} (`fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: table)`) • see |vim.treesitter.query.add_directive()| for argument meanings - • {opts} (`table<string, any>`) Optional options: - • force (boolean): Override an existing predicate of the - same name - • all (boolean): Use the correct implementation of the + • {opts} (`table`) A table with the following fields: + • {force}? (`boolean`) Override an existing predicate of + the same name + • {all}? (`boolean`) Use the correct implementation of the match table where capture IDs map to a list of nodes instead of a single node. Defaults to false (for backward compatibility). This option will eventually become the @@ -1079,13 +1108,13 @@ list_directives() *vim.treesitter.query.list_directives()* Lists the currently available directives to use in queries. Return: ~ - (`string[]`) List of supported directives. + (`string[]`) Supported directives. list_predicates() *vim.treesitter.query.list_predicates()* Lists the currently available predicates to use in queries. Return: ~ - (`string[]`) List of supported predicates. + (`string[]`) Supported predicates. omnifunc({findstart}, {base}) *vim.treesitter.query.omnifunc()* Omnifunc for completing node names and predicates in treesitter queries. @@ -1132,10 +1161,10 @@ Query:iter_captures({node}, {source}, {start}, {stop}) i.e., to get syntax highlight matches in the current viewport). When omitted, the {start} and {stop} row values are used from the given node. - 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: >lua - for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do + The iterator returns four values: a numeric id identifying the capture, + the captured node, metadata from any directives processing the match, and + the match itself. The following example shows how to get captures by name: >lua + for id, node, metadata, match 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 @@ -1144,6 +1173,10 @@ Query:iter_captures({node}, {source}, {start}, {stop}) end < + Note: ~ + • Captures are only returned if the query pattern of a specific capture + contained predicates. + Parameters: ~ • {node} (`TSNode`) under which the search will occur • {source} (`integer|string`) Source buffer or string to extract text @@ -1154,8 +1187,8 @@ Query:iter_captures({node}, {source}, {start}, {stop}) Defaults to `node:end_()`. Return: ~ - (`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata`) - capture id, capture node, metadata + (`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch`) + capture id, capture node, metadata, match *Query:iter_matches()* Query:iter_matches({node}, {source}, {start}, {stop}, {opts}) @@ -1198,6 +1231,8 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts}) • max_start_depth (integer) if non-zero, sets the maximum start depth for each match. This is used to prevent traversing too deep into a tree. + • match_limit (integer) Set the maximum number of + in-progress matches (Default: 256). • all (boolean) When set, the returned match table maps capture IDs to a list of nodes. Older versions of iter_matches incorrectly mapped capture IDs to a single @@ -1291,7 +1326,11 @@ LanguageTree:included_regions() *LanguageTree:included_regions()* (`table<integer, Range6[]>`) LanguageTree:invalidate({reload}) *LanguageTree:invalidate()* - Invalidates this parser and all its children + Invalidates this parser and its children. + + Should only be called when the tracked state of the LanguageTree is not + valid against the parse tree in treesitter. Doesn't clear filesystem + cache. Called often, so needs to be fast. Parameters: ~ • {reload} (`boolean?`) @@ -1319,7 +1358,7 @@ LanguageTree:language_for_range({range}) • {range} (`Range4`) `{ start_line, start_col, end_line, end_col }` Return: ~ - (`vim.treesitter.LanguageTree`) Managing {range} + (`vim.treesitter.LanguageTree`) tree Managing {range} *LanguageTree:named_node_for_range()* LanguageTree:named_node_for_range({range}, {opts}) @@ -1327,12 +1366,12 @@ LanguageTree:named_node_for_range({range}, {opts}) Parameters: ~ • {range} (`Range4`) `{ start_line, start_col, end_line, end_col }` - • {opts} (`table?`) Optional keyword arguments: - • ignore_injections boolean Ignore injected languages - (default true) + • {opts} (`table?`) A table with the following fields: + • {ignore_injections}? (`boolean`, default: `true`) Ignore + injected languages Return: ~ - (`TSNode?`) Found node + (`TSNode?`) LanguageTree:parse({range}) *LanguageTree:parse()* Recursively parse all regions in the language tree using @@ -1359,8 +1398,9 @@ LanguageTree:register_cbs({cbs}, {recursive}) Registers callbacks for the |LanguageTree|. Parameters: ~ - • {cbs} (`table`) An |nvim_buf_attach()|-like table argument with - the following handlers: + • {cbs} (`table<TSCallbackNameOn,function>`) An + |nvim_buf_attach()|-like table argument with the + following handlers: • `on_bytes` : see |nvim_buf_attach()|, but this will be called after the parsers callback. • `on_changedtree` : a callback that will be called every @@ -1387,9 +1427,9 @@ LanguageTree:tree_for_range({range}, {opts}) Parameters: ~ • {range} (`Range4`) `{ start_line, start_col, end_line, end_col }` - • {opts} (`table?`) Optional keyword arguments: - • ignore_injections boolean Ignore injected languages - (default true) + • {opts} (`table?`) A table with the following fields: + • {ignore_injections}? (`boolean`, default: `true`) Ignore + injected languages Return: ~ (`TSTree?`) diff --git a/runtime/doc/uganda.txt b/runtime/doc/uganda.txt index 42e275c230..0b281dc3a0 100644 --- a/runtime/doc/uganda.txt +++ b/runtime/doc/uganda.txt @@ -199,7 +199,7 @@ visited about once a year to check the progress (at our own cost). I have visited the centre myself many times, starting in 1993. The visit reports are on the ICCF web site. -If you have any further questions, send me e-mail: <Bram@vim.org>. +If you have any further questions, send e-mail: <Bram@vim.org>. The address of the centre is: Kibaale Children's Centre @@ -219,9 +219,8 @@ Canada: Contact Kuwasha in Surrey, Canada. They take care of the Canadian sponsors for the children in Kibaale. Kuwasha forwards 100% of the money to the project in Uganda. You can send them a one time donation directly. - Please send me a note so that I know what has been donated - because of Vim. Look on their site for information about - sponsorship: https://www.kuwasha.net/ + Look on their site for information about sponsorship: + https://www.kuwasha.net/ If you make a donation to Kuwasha you will receive a tax receipt which can be submitted with your tax return. diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index b8d47923ca..1f5132bd30 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -331,8 +331,8 @@ numerical highlight ids to the actual attributes. `blend`: blend level (0-100). Could be used by UIs to support blending floating windows to the background or to signal a transparent cursor. - `url`: a URL associated with this highlight. UIs can - display this URL however they wish. + `url`: URL associated with this highlight. UIs should + present the region as a clickable hyperlink. For absent color keys the default color should be used. Don't store the default value in the table, rather a sentinel value, so that @@ -656,6 +656,11 @@ tabs. the screen and copy it to the viewport destination once `win_viewport` is received. +["win_viewport_margins", grid, win, top, bottom, left, right] ~ + Indicates the margins of a window grid which are _not_ part of the + viewport as indicated by the `win_viewport` event. This happens + e.g. in the presence of 'winbar' and floating window borders. + ["win_extmark", grid, win, ns_id, mark_id, row, col] ~ Updates the position of an extmark which is currently visible in a window. Only emitted if the mark has the `ui_watched` attribute. @@ -772,9 +777,10 @@ This UI extension delegates presentation of messages and dialogs. Messages that would otherwise render in the message/cmdline screen space, are emitted as UI events. -Nvim will not allocate screen space for the cmdline or messages, and -'cmdheight' will be forced zero. Cmdline state is emitted as |ui-cmdline| -events, which the UI must handle. +Nvim will not allocate screen space for the cmdline or messages. 'cmdheight' +will be set to zero, but can be changed and used for the replacing cmdline or +message window. Cmdline state is emitted as |ui-cmdline| events, which the UI +must handle. ["msg_show", kind, content, replace_last] ~ Display a message to the user. diff --git a/runtime/doc/usr_01.txt b/runtime/doc/usr_01.txt index 6b94806941..d285a35f26 100644 --- a/runtime/doc/usr_01.txt +++ b/runtime/doc/usr_01.txt @@ -85,7 +85,7 @@ The Vim user manual and reference manual are Copyright (c) 1988 by Bram Moolenaar. This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later. The latest version is presently available at: - https://www.opencontent.org/openpub/ + https://opencontent.org/openpub/ People who contribute to the manuals must agree with the above copyright notice. diff --git a/runtime/doc/usr_30.txt b/runtime/doc/usr_30.txt index a0d22482c3..98dbefae46 100644 --- a/runtime/doc/usr_30.txt +++ b/runtime/doc/usr_30.txt @@ -246,7 +246,7 @@ code block the cursor is in: > =a{ -I you have really badly indented code, you can re-indent the whole file with: +If you have really badly indented code, you can re-indent the whole file with: > gg=G diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 56750420e9..ab2eecdfaf 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -794,6 +794,7 @@ Cursor and mark position: *cursor-functions* *mark-functions* Working with text in the current buffer: *text-functions* getline() get a line or list of lines from the buffer getregion() get a region of text from the buffer + getregionpos() get a list of positions for a region setline() replace a line in the buffer append() append line or list of lines in the buffer indent() indent of a specific line @@ -948,7 +949,7 @@ Syntax and highlighting: *syntax-functions* *highlighting-functions* synIDattr() get a specific attribute of a syntax ID synIDtrans() get translated syntax ID synstack() get list of syntax IDs at a specific position - synconcealed() get info about concealing + synconcealed() get info about (syntax) concealing diff_hlID() get highlight ID for diff mode at a position matchadd() define a pattern to highlight (a "match") matchaddpos() define a list of positions to highlight @@ -2500,13 +2501,9 @@ When you write a compiler file and put it in your personal runtime directory variable to make the default file skip the settings. *:CompilerSet* The second mechanism is to use ":set" for ":compiler!" and ":setlocal" for -":compiler". Vim defines the ":CompilerSet" user command for this. However, -older Vim versions don't, thus your plugin should define it then. This is an -example: > +":compiler". Vim defines the ":CompilerSet" user command for this. This is +an example: > - if exists(":CompilerSet") != 2 - command -nargs=* CompilerSet setlocal <args> - endif CompilerSet errorformat& " use the default 'errorformat' CompilerSet makeprg=nmake diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 33f57580c7..0287271d4c 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -14,6 +14,7 @@ Various commands *various* *CTRL-L* CTRL-L Clears and redraws the screen. The redraw may happen later, after processing typeahead. + See also |nvim__redraw()|. *CTRL-L-default* By default, also clears search highlighting |:nohlsearch| and updates diffs |:diffupdate|. @@ -21,6 +22,7 @@ CTRL-L Clears and redraws the screen. The redraw may happen *:mod* *:mode* :mod[e] Clears and redraws the screen. + See also |nvim__redraw()|. *:redr* *:redraw* :redr[aw][!] Redraws pending screen updates now, or the entire @@ -28,6 +30,7 @@ CTRL-L Clears and redraws the screen. The redraw may happen |:mode| or |CTRL-L|. Useful to update the screen during a script or function (or a mapping if 'lazyredraw' set). + See also |nvim__redraw()|. *:redraws* *:redrawstatus* :redraws[tatus][!] Redraws the status line and window bar of the current @@ -35,11 +38,12 @@ CTRL-L Clears and redraws the screen. The redraw may happen included. Redraws the commandline instead if it contains the 'ruler'. Useful if 'statusline' or 'winbar' includes an item that doesn't cause automatic updating. + See also |nvim__redraw()|. *:redrawt* *:redrawtabline* :redrawt[abline] Redraw the tabline. Useful to update the tabline when 'tabline' includes an item that doesn't trigger - automatic updating. + automatic updating. See also |nvim__redraw()|. *N<Del>* <Del> When entering a number: Remove the last digit. @@ -396,6 +400,7 @@ gx Opens the current filepath or URL (decided by |:command| - filter by command name |:files| - filter by file name |:highlight| - filter by highlight group + |:history| - filter by history commands |:jumps| - filter by file name |:let| - filter by variable name |:list| - filter whole line @@ -557,5 +562,47 @@ LessInitFunc in your vimrc, for example: > set nocursorcolumn nocursorline endfunc < +============================================================================== +3. Commenting *commenting* + +Nvim supports commenting and uncommenting of lines based on 'commentstring'. + +Acting on a single line behaves as follows: +- If the line matches 'commentstring', the comment markers are removed (e.g. + `/*foo*/` is transformed to `foo`). +- Otherwise the comment markers are added to the current line (e.g. `foo` is + transformed to `/*foo*/`). Blank lines are ignored. + +Acting on multiple lines behaves as follows: +- If each affected non-blank line matches 'commentstring', then all comment + markers are removed. +- Otherwise all affected lines are converted to comments; blank lines are + transformed to empty comments (e.g. `/**/`). Comment markers are aligned to + the least indented line. + +Matching 'commentstring' does not account for whitespace in comment markers. +Removing comment markers is first attempted exactly, with fallback to using +markers trimmed from whitespace. + +If the filetype of the buffer is associated with a language for which a +|treesitter| parser is installed, then |vim.filetype.get_option()| is called +to look up the value of 'commentstring' corresponding to the cursor position. +(This can be different from the buffer's 'commentstring' in case of +|treesitter-language-injections|.) + + *gc* *gc-default* +gc{motion} Comment or uncomment lines covered by {motion}. + + *gcc* *gcc-default* +gcc Comment or uncomment [count] lines starting at cursor. + + *v_gc* *v_gc-default* +{Visual}gc Comment or uncomment the selected line(s). + + *o_gc* *o_gc-default* +gc Text object for the largest contiguous block of + non-blank commented lines around the cursor (e.g. + `gcgc` uncomments a comment block; `dgc` deletes it). + Works only in Operator-pending mode. vim:noet:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index b0caf9fdaf..5d894bb5e1 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -1,19 +1,19 @@ *vim_diff.txt* Nvim - NVIM REFERENCE MANUAL + NVIM REFERENCE MANUAL -Differences between Nvim and Vim *vim-differences* +Differences between Nvim and Vim *vim-differences* Nvim differs from Vim in many ways, although editor and Vimscript (not Vim9script) features are mostly identical. This document is a complete and centralized reference of the differences. - Type |gO| to see the table of contents. + Type |gO| to see the table of contents. ============================================================================== -Configuration *nvim-config* +Configuration *nvim-config* User configuration and data files are found in standard |base-directories| (see also |$NVIM_APPNAME|). Note in particular: @@ -24,7 +24,7 @@ User configuration and data files are found in standard |base-directories| session information. |shada| ============================================================================== -Defaults *nvim-defaults* +Defaults *nvim-defaults* - Filetype detection is enabled by default. This can be disabled by adding ":filetype off" to |init.vim|. @@ -52,6 +52,8 @@ Defaults *nvim-defaults* - 'encoding' is UTF-8 (cf. 'fileencoding' for file-content encoding) - 'fillchars' defaults (in effect) to "vert:│,fold:·,foldsep:│" - 'formatoptions' defaults to "tcqj" +- 'grepprg' uses the -H and -I flags for regular grep, + and defaults to using ripgrep if available - 'hidden' is enabled - 'history' defaults to 10000 (the maximum) - 'hlsearch' is enabled @@ -80,6 +82,7 @@ Defaults *nvim-defaults* - 'tags' defaults to "./tags;,tags" - 'termguicolors' is enabled by default if Nvim can detect support from the host terminal +- 'ttimeout' is enabled - 'ttimeoutlen' defaults to 50 - 'ttyfast' is always set - 'undodir' defaults to ~/.local/state/nvim/undo// (|xdg|), auto-created @@ -96,7 +99,7 @@ Defaults *nvim-defaults* - |g:vimsyn_embed| defaults to "l" to enable Lua highlighting DEFAULT MOUSE - *default-mouse* *disable-mouse* + *default-mouse* *disable-mouse* By default the mouse is enabled, and <RightMouse> opens a |popup-menu| with standard actions ("Cut", "Copy", "Paste", …). Mouse is NOT enabled in |command-mode| or the |more-prompt|, so you can temporarily disable it just by @@ -120,7 +123,7 @@ To remove the "How-to disable mouse" menu item and the separator above it: >vim aunmenu PopUp.-1- < DEFAULT MAPPINGS - *default-mappings* + *default-mappings* Nvim creates the following default mappings at |startup|. You can disable any of these in your config by simply removing the mapping, e.g. ":unmap Y". @@ -133,11 +136,21 @@ of these in your config by simply removing the mapping, e.g. ":unmap Y". - @ |v_@-default| - # |v_#-default| - * |v_star-default| +- gc |gc-default| |v_gc-default| |o_gc-default| +- gcc |gcc-default| +- gr prefix |gr-default| + - |grn| + - |grr| + - |gra| +- <C-S> |i_CTRL-S| +- ]d |]d-default| +- [d |[d-default| +- <C-W>d |CTRL-W_d-default| - Nvim LSP client defaults |lsp-defaults| - K |K-lsp-default| DEFAULT AUTOCOMMANDS - *default-autocmds* + *default-autocmds* Default autocommands exist in the following groups. Use ":autocmd! {group}" to remove them and ":autocmd {group}" to see how they're defined. @@ -161,7 +174,7 @@ nvim_swapfile: swapfile…" message. ============================================================================== -New Features *nvim-features* +New Features *nvim-features* MAJOR COMPONENTS @@ -186,7 +199,7 @@ USER EXPERIENCE Working intuitively and consistently is a major goal of Nvim. - *feature-compile* + *feature-compile* - Nvim always includes ALL features, in contrast to Vim (which ships various combinations of 100+ optional features). |feature-compile| Think of it as a leaner version of Vim's "HUGE" build. This reduces surface area for bugs, @@ -233,182 +246,176 @@ by Nvim developers. FEATURES Command-line: - The expression prompt (|@=|, |c_CTRL-R_=|, |i_CTRL-R_=|) is highlighted +- The expression prompt (|@=|, |c_CTRL-R_=|, |i_CTRL-R_=|) is highlighted using a built-in Vimscript expression parser. |expr-highlight| - *E5408* *E5409* - |input()|, |inputdialog()| support custom highlighting. |input()-highlight| - *g:Nvim_color_cmdline* - (Experimental) Command-line (|:|) is colored by callback defined in - `g:Nvim_color_cmdline` (this callback is for testing only, and will be - removed in the future). +- *E5408* *E5409* |input()|, |inputdialog()| support custom highlighting. + |input()-highlight| +- (Experimental) *g:Nvim_color_cmdline* Command-line (|:|) is colored by + callback defined in `g:Nvim_color_cmdline` (this callback is for testing + only, and will be removed in the future). Commands: - |:checkhealth| - |:drop| is always available - |:Man| is available by default, with many improvements such as completion - |:match| can be invoked before highlight group is defined - |:source| works with Lua +- |:checkhealth| +- |:drop| is always available +- |:Man| is available by default, with many improvements such as completion +- |:match| can be invoked before highlight group is defined +- |:source| works with Lua User commands can support |:command-preview| to show results as you type - |:write| with "++p" flag creates parent directories. +- |:write| with "++p" flag creates parent directories. Events: - |RecordingEnter| - |RecordingLeave| - |SearchWrapped| - |Signal| - |TabNewEntered| - |TermClose| - |TermOpen| - |UIEnter| - |UILeave| +- |RecordingEnter| +- |RecordingLeave| +- |SearchWrapped| +- |Signal| +- |TabNewEntered| +- |TermClose| +- |TermOpen| +- |UIEnter| +- |UILeave| Functions: - |dictwatcheradd()| notifies a callback whenever a |Dict| is modified - |dictwatcherdel()| - |menu_get()| - |msgpackdump()|, |msgpackparse()| provide msgpack de/serialization - |stdpath()| - |system()|, |systemlist()| can run {cmd} directly (without 'shell') - |matchadd()| can be called before highlight group is defined - |tempname()| tries to recover if the Nvim |tempdir| disappears. - |writefile()| with "p" flag creates parent directories. +- |dictwatcheradd()| notifies a callback whenever a |Dict| is modified +- |dictwatcherdel()| +- |menu_get()| +- |msgpackdump()|, |msgpackparse()| provide msgpack de/serialization +- |stdpath()| +- |system()|, |systemlist()| can run {cmd} directly (without 'shell') +- |matchadd()| can be called before highlight group is defined +- |tempname()| tries to recover if the Nvim |tempdir| disappears. +- |writefile()| with "p" flag creates parent directories. Highlight groups: - |highlight-blend| controls blend level for a highlight group - |expr-highlight| highlight groups (prefixed with "Nvim") - |hl-NormalFloat| highlights floating window - |hl-FloatBorder| highlights border of a floating window - |hl-FloatTitle| highlights title of a floating window - |hl-FloatFooter| highlights footer of a floating window - |hl-NormalNC| highlights non-current windows - |hl-MsgArea| highlights messages/cmdline area - |hl-MsgSeparator| highlights separator for scrolled messages - |hl-Substitute| - |hl-TermCursor| - |hl-TermCursorNC| - |hl-WinSeparator| highlights window separators - |hl-Whitespace| highlights 'listchars' whitespace - |hl-WinBar| highlights 'winbar' - |hl-WinBarNC| highlights non-current window 'winbar' +- |highlight-blend| controls blend level for a highlight group +- |expr-highlight| highlight groups (prefixed with "Nvim") +- |hl-NormalFloat| highlights floating window +- |hl-FloatBorder| highlights border of a floating window +- |hl-FloatTitle| highlights title of a floating window +- |hl-FloatFooter| highlights footer of a floating window +- |hl-NormalNC| highlights non-current windows +- |hl-MsgArea| highlights messages/cmdline area +- |hl-MsgSeparator| highlights separator for scrolled messages +- |hl-Substitute| +- |hl-TermCursor| +- |hl-TermCursorNC| +- |hl-WinSeparator| highlights window separators +- |hl-Whitespace| highlights 'listchars' whitespace +- |hl-WinBar| highlights 'winbar' +- |hl-WinBarNC| highlights non-current window 'winbar' Input/Mappings: - ALT (|META|) chords always work (even in the |TUI|). Map |<M-| with any key: +- ALT (|META|) chords always work (even in the |TUI|). Map |<M-| with any key: <M-1>, <M-BS>, <M-Del>, <M-Ins>, <M-/>, <M-\>, <M-Space>, <M-Enter>, etc. - Case-sensitive: <M-a> and <M-A> are two different keycodes. - - ALT may behave like <Esc> if not mapped. |i_ALT| |v_ALT| |c_ALT| + - Case-sensitive: <M-a> and <M-A> are two different keycodes. +- ALT may behave like <Esc> if not mapped. |i_ALT| |v_ALT| |c_ALT| Normal commands: - |gO| shows a filetype-defined "outline" of the current buffer. - |Q| replays the last recorded macro instead of switching to Ex mode (|gQ|). +- |gO| shows a filetype-defined "outline" of the current buffer. +- |Q| replays the last recorded macro instead of switching to Ex mode (|gQ|). Options: - Local values for global-local number/boolean options are unset when the - option is set without a scope (e.g. by using |:set|), similarly to how - global-local string options work. - - 'autoread' works in the terminal (if it supports "focus" events) - 'cpoptions' flags: |cpo-_| - 'diffopt' "linematch" feature - 'exrc' searches for ".nvim.lua", ".nvimrc", or ".exrc" files. The + +Local values for global-local number/boolean options are unset when the option +is set without a scope (e.g. by using |:set|), similarly to how global-local +string options work. + +- 'autoread' works in the terminal (if it supports "focus" events) +- 'cpoptions' flags: |cpo-_| +- 'diffopt' "linematch" feature +- 'exrc' searches for ".nvim.lua", ".nvimrc", or ".exrc" files. The user is prompted whether to trust the file. - 'fillchars' flags: "msgsep", "horiz", "horizup", "horizdown", +- 'fillchars' flags: "msgsep", "horiz", "horizup", "horizdown", "vertleft", "vertright", "verthoriz" - 'foldcolumn' supports up to 9 dynamic/fixed columns - 'guicursor' works in the terminal (TUI) - 'inccommand' shows interactive results for |:substitute|-like commands +- 'foldcolumn' supports up to 9 dynamic/fixed columns +- 'guicursor' works in the terminal (TUI) +- 'inccommand' shows interactive results for |:substitute|-like commands and |:command-preview| commands - 'jumpoptions' "view" tries to restore the |mark-view| when moving through - the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|. - 'laststatus' global statusline support - 'mousescroll' amount to scroll by when scrolling with a mouse - 'pumblend' pseudo-transparent popupmenu - 'scrollback' - 'shortmess' "F" flag does not affect output from autocommands - 'signcolumn' supports up to 9 dynamic/fixed columns - 'statuscolumn' full control of columns using 'statusline' format - 'tabline' middle-click on tabpage label closes tabpage, +- 'jumpoptions' "view" tries to restore the |mark-view| when moving through +- the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|. +- 'laststatus' global statusline support +- 'mousescroll' amount to scroll by when scrolling with a mouse +- 'pumblend' pseudo-transparent popupmenu +- 'scrollback' +- 'shortmess' + - "F" flag does not affect output from autocommands. + - "q" flag fully hides macro recording message. +- 'signcolumn' supports up to 9 dynamic/fixed columns +- 'statuscolumn' full control of columns using 'statusline' format +- 'tabline' middle-click on tabpage label closes tabpage, and %@Func@foo%X can call any function on mouse-click - 'termpastefilter' - 'ttimeout', 'ttimeoutlen' behavior was simplified - 'winblend' pseudo-transparency in floating windows |api-floatwin| - 'winhighlight' window-local highlights +- 'termpastefilter' +- 'ttimeout', 'ttimeoutlen' behavior was simplified +- 'winblend' pseudo-transparency in floating windows |api-floatwin| +- 'winhighlight' window-local highlights Providers: - If a Python interpreter is available on your `$PATH`, |:python| and +- If a Python interpreter is available on your `$PATH`, |:python| and |:python3| are always available. See |provider-python|. Shell: - Shell output (|:!|, |:make|, …) is always routed through the UI, so it +- Shell output (|:!|, |:make|, …) is always routed through the UI, so it cannot "mess up" the screen. (You can still use "chansend(v:stderr,…)" if you want to mess up the screen :) - - Nvim throttles (skips) messages from shell commands (|:!|, |:grep|, |:make|) +- Nvim throttles (skips) messages from shell commands (|:!|, |:grep|, |:make|) if there is too much output. No data is lost, this only affects display and improves performance. |:terminal| output is never throttled. - - |:!| does not support "interactive" commands. Use |:terminal| instead. +- |:!| does not support "interactive" commands. Use |:terminal| instead. (GUI Vim has a similar limitation, see ":help gui-pty" in Vim.) - - :!start is not special-cased on Windows. - - |system()| does not support writing/reading "backgrounded" commands. |E5677| +- :!start is not special-cased on Windows. +- |system()| does not support writing/reading "backgrounded" commands. |E5677| Signs: - Signs are removed if the associated line is deleted. - Signs placed twice with the same identifier in the same group are moved. +- Signs are removed if the associated line is deleted. +- Signs placed twice with the same identifier in the same group are moved. Startup: - |-e| and |-es| invoke the same "improved Ex mode" as -E and -Es. - |-E| and |-Es| read stdin as text (into buffer 1). - |-es| and |-Es| have improved behavior: +- |-e| and |-es| invoke the same "improved Ex mode" as -E and -Es. +- |-E| and |-Es| read stdin as text (into buffer 1). +- |-es| and |-Es| have improved behavior: - Quits automatically, don't need "-c qa!". - Skips swap-file dialog. - |-s| reads Normal commands from stdin if the script name is "-". - Reading text (instead of commands) from stdin |--|: +- |-s| reads Normal commands from stdin if the script name is "-". +- Reading text (instead of commands) from stdin |--|: - works by default: "-" file is optional - works in more cases: |-Es|, file args TUI: - *:set-termcap* - Start Nvim with 'verbose' level 3 to show terminal capabilities: > - nvim -V3 + *:set-termcap* +- Start Nvim with 'verbose' level 3 to show terminal capabilities: > + nvim -V3 < - *'term'* *E529* *E530* *E531* - 'term' reflects the terminal type derived from |$TERM| and other environment + *'term'* *E529* *E530* *E531* +- 'term' reflects the terminal type derived from |$TERM| and other environment checks. For debugging only; not reliable during startup. >vim - :echo &term -< "builtin_x" means one of the |builtin-terms| was chosen, because the expected + :echo &term +- "builtin_x" means one of the |builtin-terms| was chosen, because the expected terminfo file was not found on the system. - - Nvim will use 256-colour capability on Linux virtual terminals. Vim uses +- Nvim will use 256-colour capability on Linux virtual terminals. Vim uses only 8 colours plus bright foreground on Linux VTs. - - Vim combines what is in its |builtin-terms| with what it reads from terminfo, +- Vim combines what is in its |builtin-terms| with what it reads from terminfo, and has a 'ttybuiltin' setting to control how that combination works. Nvim uses one or the other, it does not attempt to merge the two. UI/Display: - |Visual| selection highlights the character at cursor. |visual-use| - - messages: When showing messages longer than 'cmdheight', only +- |Visual| selection highlights the character at cursor. |visual-use| +- messages: When showing messages longer than 'cmdheight', only scroll the message lines, not the entire screen. The separator line is decorated by |hl-MsgSeparator| and the "msgsep" flag of 'fillchars'. *msgsep* Variables: - |v:progpath| is always absolute ("full") - |v:windowid| is always available (for use by external UIs) - |OptionSet| autocommand args |v:option_new|, |v:option_old|, - |v:option_oldlocal|, |v:option_oldglobal| have the type of the option +- |v:progpath| is always absolute ("full") +- |v:windowid| is always available (for use by external UIs) +- |OptionSet| autocommand args |v:option_new|, |v:option_old|, +- |v:option_oldlocal|, |v:option_oldglobal| have the type of the option instead of always being strings. |v:option_old| is now the old global value for all global-local options, instead of just string global-local options. Vimscript: - |:redir| nested in |execute()| works. +- |:redir| nested in |execute()| works. ============================================================================== -Upstreamed features *nvim-upstreamed* +Upstreamed features *nvim-upstreamed* These Nvim features were later integrated into Vim. @@ -423,45 +430,44 @@ These Nvim features were later integrated into Vim. - 'statusline' supports unlimited alignment sections ============================================================================== -Other changes *nvim-changed* +Other changes *nvim-changed* This section documents various low-level behavior changes. |mkdir()| behaviour changed: -1. Assuming /tmp/foo does not exist and /tmp can be written to - mkdir('/tmp/foo/bar', 'p', 0700) will create both /tmp/foo and /tmp/foo/bar - with 0700 permissions. Vim mkdir will create /tmp/foo with 0755. -2. If you try to create an existing directory with `'p'` (e.g. mkdir('/', - 'p')) mkdir() will silently exit. In Vim this was an error. -3. mkdir() error messages now include strerror() text when mkdir fails. +- 1. Assuming /tmp/foo does not exist and /tmp can be written to + mkdir('/tmp/foo/bar', 'p', 0700) will create both /tmp/foo and + /tmp/foo/bar with 0700 permissions. Vim mkdir will create /tmp/foo with + 0755. +- 2. If you try to create an existing directory with `'p'` (e.g. mkdir('/', + 'p')) mkdir() will silently exit. In Vim this was an error. +- 3. mkdir() error messages now include strerror() text when mkdir fails. |string()| and |:echo| behaviour changed: -1. No maximum recursion depth limit is applied to nested container - structures. -2. |string()| fails immediately on nested containers, not when recursion limit - was exceeded. -3. When |:echo| encounters duplicate containers like >vim - +- 1. No maximum recursion depth limit is applied to nested container + structures. +- 2. |string()| fails immediately on nested containers, not when recursion + limit was exceeded. +- 3. When |:echo| encounters duplicate containers like >vim let l = [] echo [l, l] -< - it does not use "[...]" (was: "[[], [...]]", now: "[[], []]"). "..." is - only used for recursive containers. -4. |:echo| printing nested containers adds "@level" after "..." designating - the level at which recursive container was printed: |:echo-self-refer|. - Same thing applies to |string()| (though it uses construct like - "{E724@level}"), but this is not reliable because |string()| continues to - error out. -5. Stringifyed infinite and NaN values now use |str2float()| and can be evaled - back. -6. (internal) Trying to print or stringify VAR_UNKNOWN in Vim results in - nothing, E908, in Nvim it is internal error. +< it does not use "[...]" (was: "[[], [...]]", now: "[[], []]"). "..." is + only used for recursive containers. +- 4. |:echo| printing nested containers adds "@level" after "..." designating + the level at which recursive container was printed: |:echo-self-refer|. + Same thing applies to |string()| (though it uses construct like + "{E724@level}"), but this is not reliable because |string()| continues to + error out. +- 5. Stringifyed infinite and NaN values now use |str2float()| and can be + evaled back. +- 6. (internal) Trying to print or stringify VAR_UNKNOWN in Vim results in + nothing, E908, in Nvim it is internal error. |json_decode()| behaviour changed: -1. It may output |msgpack-special-dict|. -2. |msgpack-special-dict| is emitted also in case of duplicate keys, while in - Vim it errors out. -3. It accepts only valid JSON. Trailing commas are not accepted. +- 1. It may output |msgpack-special-dict|. +- 2. |msgpack-special-dict| is emitted also in case of duplicate keys, while + in Vim it errors out. +- 3. It accepts only valid JSON. Trailing commas are not accepted. |json_encode()| behaviour slightly changed: now |msgpack-special-dict| values are accepted, but |v:none| is not. @@ -505,38 +511,37 @@ Lua interface (|lua.txt|): 'runtimepath'. |lua-module-load| Commands: - |:doautocmd| does not warn about "No matching autocommands". - |:wincmd| accepts a count. - `:write!` does not show a prompt if the file was updated externally. - |:=| does not accept |ex-flags|. With an arg it is equivalent to |:lua=| +- |:doautocmd| does not warn about "No matching autocommands". +- |:wincmd| accepts a count. +- `:write!` does not show a prompt if the file was updated externally. +- |:=| does not accept |ex-flags|. With an arg it is equivalent to |:lua=| Command-line: - The meanings of arrow keys do not change depending on 'wildoptions'. +- The meanings of arrow keys do not change depending on 'wildoptions'. Functions: - |input()| and |inputdialog()| support for each other’s features (return on +- |input()| and |inputdialog()| support for each other’s features (return on cancel and completion respectively) via dictionary argument (replaces all other arguments if used), and "cancelreturn" can have any type if passed in a dictionary. - |input()| and |inputdialog()| support user-defined cmdline highlighting. +- |input()| and |inputdialog()| support user-defined cmdline highlighting. Highlight groups: - |hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other +- |hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other groups - |hl-CurSearch| highlights match under cursor instead of last match found +- |hl-CurSearch| highlights match under cursor instead of last match found using |n| or |N| - |hl-CursorLine| is low-priority unless foreground color is set - |hl-VertSplit| superseded by |hl-WinSeparator| - Highlight groups names are allowed to contain `@` characters. - It is an error to define a highlight group with a name that doesn't match - the regexp `[a-zA-Z0-9_.@-]*` (see |group-name|). - -Macro/|recording| behavior - Replay of a macro recorded during :lmap produces the same actions as when it +- |hl-CursorLine| is low-priority unless foreground color is set +- |hl-VertSplit| superseded by |hl-WinSeparator| +- Highlight groups names are allowed to contain `@` characters. + - It is an error to define a highlight group with a name that doesn't match + the regexp `[a-zA-Z0-9_.@-]*` (see |group-name|). + +Macro (|recording|) behavior: +- Replay of a macro recorded during :lmap produces the same actions as when it was recorded. In Vim if a macro is recorded while using :lmap'ped keys then the behaviour during record and replay differs. - - 'keymap' is implemented via :lmap instead of :lnoremap so that you can use +- 'keymap' is implemented via :lmap instead of :lnoremap so that you can use macros and 'keymap' at the same time. This also means you can use |:imap| on the results of keys from 'keymap'. @@ -547,13 +552,13 @@ Mappings: lhs of a mapping. Motion: - The |jumplist| avoids useless/phantom jumps. +- The |jumplist| avoids useless/phantom jumps. Performance: - Folds are not updated during insert-mode. +- Folds are not updated during insert-mode. Syntax highlighting: - syncolor.vim has been removed. Nvim now sets up default highlighting groups +- syncolor.vim has been removed. Nvim now sets up default highlighting groups automatically for both light and dark backgrounds, regardless of whether or not syntax highlighting is enabled. This means that |:syntax-on| and |:syntax-enable| are now identical. Users who previously used an @@ -561,10 +566,10 @@ Syntax highlighting: colorscheme. |:colorscheme| Vimscript compatibility: - `count` does not alias to |v:count| - `errmsg` does not alias to |v:errmsg| - `shell_error` does not alias to |v:shell_error| - `this_session` does not alias to |v:this_session| +- `count` does not alias to |v:count| +- `errmsg` does not alias to |v:errmsg| +- `shell_error` does not alias to |v:shell_error| +- `this_session` does not alias to |v:this_session| Working directory (Vim implemented some of these after Nvim): - |DirChanged| and |DirChangedPre| can be triggered when switching to another @@ -582,11 +587,8 @@ Autocommands: - |TermResponse| is fired for any OSC sequence received from the terminal, instead of the Primary Device Attributes response. |v:termresponse| -Options: -- |shm-q| fully hides macro recording message instead of only shortening it. - ============================================================================== -Missing features *nvim-missing* +Missing features *nvim-missing* These legacy Vim features are not yet implemented: @@ -596,75 +598,75 @@ These legacy Vim features are not yet implemented: - *'previewpopup'* ============================================================================== -Removed legacy features *nvim-removed* +Removed legacy features *nvim-removed* These Vim features were intentionally removed from Nvim. Aliases: - ex (alias for "nvim -e") - exim (alias for "nvim -E") - gex (GUI) - gview (GUI) - gvim (GUI) - gvimdiff (GUI) - rgview (GUI) - rgvim (GUI) - rview - rvim - view (alias for "nvim -R") - vimdiff (alias for "nvim -d" |diff-mode|) +- ex (alias for "nvim -e") +- exim (alias for "nvim -E") +- gex (GUI) +- gview (GUI) +- gvim (GUI) +- gvimdiff (GUI) +- rgview (GUI) +- rgvim (GUI) +- rview +- rvim +- view (alias for "nvim -R") +- vimdiff (alias for "nvim -d" |diff-mode|) Commands: - :behave - :fixdel - *hardcopy* `:hardcopy` was removed. Instead, use `:TOhtml` and print the +- :behave +- :fixdel +- *hardcopy* `:hardcopy` was removed. Instead, use `:TOhtml` and print the resulting HTML using a web browser or other HTML viewer. - :helpfind - :mode (no longer accepts an argument) - :open - :Print - :promptfind - :promptrepl - :scriptversion (always version 1) - :shell - :sleep! (does not hide the cursor; same as :sleep) - :smile - :tearoff - :cstag - :cscope - :lcscope - :scscope - :Vimuntar - The old `:TOhtml`, replaced by a Lua version (contains many differences) +- :helpfind +- :mode (no longer accepts an argument) +- :open +- :Print +- :promptfind +- :promptrepl +- :scriptversion (always version 1) +- :shell +- :sleep! (does not hide the cursor; same as :sleep) +- :smile +- :tearoff +- :cstag +- :cscope +- :lcscope +- :scscope +- :Vimuntar +- `:TOhtml` was replaced by a Lua version (with various differences) Compile-time features: - Emacs tags support - X11 integration (see |x11-selection|) +- Emacs tags support +- X11 integration (see |x11-selection|) Cscope: *cscope* - Cscope support was removed in favour of plugin-based solutions such as: +- Cscope support was removed in favour of plugin-based solutions such as: https://github.com/dhananjaylatkar/cscope_maps.nvim Eval: - Vim9script - *cscope_connection()* - *err_teapot()* - *js_encode()* - *js_decode()* - *v:none* (used by Vim to represent JavaScript "undefined"); use |v:null| instead. - *v:sizeofint* - *v:sizeoflong* - *v:sizeofpointer* +- Vim9script +- *cscope_connection()* +- *err_teapot()* +- *js_encode()* +- *js_decode()* +- *v:none* (used by Vim to represent JavaScript "undefined"); use |v:null| instead. +- *v:sizeofint* +- *v:sizeoflong* +- *v:sizeofpointer* Events: - *SafeStateAgain* - *SigUSR1* Use |Signal| to detect `SIGUSR1` signal instead. +- *SafeStateAgain* +- *SigUSR1* Use |Signal| to detect `SIGUSR1` signal instead. Highlight groups: - *hl-StatusLineTerm* *hl-StatusLineTermNC* are unnecessary because Nvim - supports 'winhighlight' window-local highlights. - For example, to mimic Vim's StatusLineTerm: >vim +- *hl-StatusLineTerm* *hl-StatusLineTermNC* are unnecessary because Nvim + supports 'winhighlight' window-local highlights. For example, to mimic Vim's + StatusLineTerm: >vim hi StatusLineTerm ctermfg=black ctermbg=green hi StatusLineTermNC ctermfg=green autocmd TermOpen,WinEnter * if &buftype=='terminal' @@ -673,48 +675,45 @@ Highlight groups: < Options: - *'aleph'* *'al'* - antialias - 'backspace' no longer supports number values. Instead: +- *'aleph'* *'al'* +- antialias +- 'backspace' no longer supports number values. Instead: - for `backspace=0` set `backspace=` (empty) - for `backspace=1` set `backspace=indent,eol` - for `backspace=2` set `backspace=indent,eol,start` (default behavior in Nvim) - for `backspace=3` set `backspace=indent,eol,nostop` - *'balloondelay'* *'bdlay'* - *'ballooneval'* *'beval'* *'noballooneval'* *'nobeval'* - *'balloonexpr'* *'bexpr'* - bioskey (MS-DOS) - conskey (MS-DOS) - *'cp'* *'nocompatible'* *'nocp'* *'compatible'* (Nvim is always "nocompatible".) - 'cpoptions' (gjpkHw<*- and all POSIX flags were removed) - *'cryptmethod'* *'cm'* *'key'* (Vim encryption implementation) - cscopepathcomp - cscopeprg - cscopequickfix - cscoperelative - cscopetag - cscopetagorder - cscopeverbose - *'ed'* *'edcompatible'* *'noed'* *'noedcompatible'* - 'encoding' ("utf-8" is always used) - esckeys - 'guioptions' "t" flag was removed - *'guifontset'* *'gfs'* (Use 'guifont' instead.) - *'guipty'* (Nvim uses pipes and PTYs consistently on all platforms.) - 'highlight' (Names of builtin |highlight-groups| cannot be changed.) - *'hkmap'* *'hk'* use `set keymap=hebrew` instead. - *'hkmapp'* *'hkp'* use `set keymap=hebrewp` instead. - keyprotocol - - *'pastetoggle'* *'pt'* Just Paste It.™ |paste| is handled automatically when +- *'balloondelay'* *'bdlay'* +- *'ballooneval'* *'beval'* *'noballooneval'* *'nobeval'* +- *'balloonexpr'* *'bexpr'* +- bioskey (MS-DOS) +- conskey (MS-DOS) +- *'cp'* *'nocompatible'* *'nocp'* *'compatible'* (Nvim is always "nocompatible".) +- 'cpoptions' (gjpkHw<*- and all POSIX flags were removed) +- *'cryptmethod'* *'cm'* *'key'* (Vim encryption implementation) +- cscopepathcomp +- cscopeprg +- cscopequickfix +- cscoperelative +- cscopetag +- cscopetagorder +- cscopeverbose +- *'ed'* *'edcompatible'* *'noed'* *'noedcompatible'* +- 'encoding' ("utf-8" is always used) +- esckeys +- 'guioptions' "t" flag was removed +- *'guifontset'* *'gfs'* (Use 'guifont' instead.) +- *'guipty'* (Nvim uses pipes and PTYs consistently on all platforms.) +- 'highlight' (Names of builtin |highlight-groups| cannot be changed.) +- *'hkmap'* *'hk'* use `set keymap=hebrew` instead. +- *'hkmapp'* *'hkp'* use `set keymap=hebrewp` instead. +- keyprotocol +- *'pastetoggle'* *'pt'* Just Paste It.™ |paste| is handled automatically when you paste text using your terminal's or GUI's paste feature (CTRL-SHIFT-v, CMD-v (macOS), middle-click, …). - - *'imactivatefunc'* *'imaf'* - *'imactivatekey'* *'imak'* - *'imstatusfunc'* *'imsf'* - *'insertmode'* *'im'* Use the following script to emulate 'insertmode': ->vim +- *'imactivatefunc'* *'imaf'* +- *'imactivatekey'* *'imak'* +- *'imstatusfunc'* *'imsf'* +- *'insertmode'* *'im'* Use the following script to emulate 'insertmode': >vim autocmd BufWinEnter * startinsert inoremap <Esc> <C-X><C-Z><C-]> inoremap <C-C> <C-X><C-Z> @@ -740,50 +739,44 @@ Options: end end) EOF -< - *'macatsui'* - *'maxcombine'* *'mco'* - Nvim counts maximum character sizes in bytes, not codepoints. This is - guaranteed to be big enough to always fit all chars properly displayed - in vim with 'maxcombine' set to 6. - - You can still edit text with larger characters than fits in the screen buffer, - you just can't see them. Use |g8| or |ga|. See |mbyte-combining|. - - NOTE: the rexexp engine still has a hard-coded limit of considering +- *'macatsui'* +- *'maxcombine'* *'mco'* : Nvim counts maximum character sizes in bytes, not + codepoints. This is guaranteed to be big enough to always fit all chars + properly displayed in vim with 'maxcombine' set to 6. + - You can still edit text with larger characters than fits in the screen + buffer, you just can't see them. Use |g8| or |ga|. See |mbyte-combining|. + - NOTE: the rexexp engine still has a hard-coded limit of considering 6 composing chars only. - - *'maxmem'* Nvim delegates memory-management to the OS. - *'maxmemtot'* Nvim delegates memory-management to the OS. - printoptions - *'printdevice'* - *'printencoding'* - *'printexpr'* - *'printfont'* - *'printheader'* - *'printmbcharset'* - *'prompt'* *'noprompt'* - *'remap'* *'noremap'* - *'restorescreen'* *'rs'* *'norestorescreen'* *'nors'* - *'secure'* - Everything is allowed in 'exrc' files since they must be explicitly marked - trusted. - *'shelltype'* - 'shortmess' flags: *shm-f* *shm-n* *shm-x* *shm-i* (behave like always on) - *'shortname'* *'sn'* *'noshortname'* *'nosn'* - *'swapsync'* *'sws'* - *'termencoding'* *'tenc'* (Vim 7.4.852 also removed this for Windows) - *'terse'* *'noterse'* (Add "s" to 'shortmess' instead) - textauto - textmode - *'toolbar'* *'tb'* - *'toolbariconsize'* *'tbis'* - *'ttybuiltin'* *'tbi'* *'nottybuiltin'* *'notbi'* - *'ttyfast'* *'tf'* *'nottyfast'* *'notf'* - *'ttymouse'* *'ttym'* - *'ttyscroll'* *'tsl'* - *'ttytype'* *'tty'* - weirdinvert +- *'maxmem'* Nvim delegates memory-management to the OS. +- *'maxmemtot'* Nvim delegates memory-management to the OS. +- printoptions +- *'printdevice'* +- *'printencoding'* +- *'printexpr'* +- *'printfont'* +- *'printheader'* +- *'printmbcharset'* +- *'prompt'* *'noprompt'* +- *'remap'* *'noremap'* +- *'restorescreen'* *'rs'* *'norestorescreen'* *'nors'* +- *'secure'* : Everything is allowed in 'exrc' files, because they must be + explicitly marked as "trusted". +- *'shelltype'* +- 'shortmess' flags: *shm-f* *shm-n* *shm-x* *shm-i* (behave like always on) +- *'shortname'* *'sn'* *'noshortname'* *'nosn'* +- *'swapsync'* *'sws'* +- *'termencoding'* *'tenc'* (Vim 7.4.852 also removed this for Windows) +- *'terse'* *'noterse'* (Add "s" to 'shortmess' instead) +- textauto +- textmode +- *'toolbar'* *'tb'* +- *'toolbariconsize'* *'tbis'* +- *'ttybuiltin'* *'tbi'* *'nottybuiltin'* *'notbi'* +- *'ttyfast'* *'tf'* *'nottyfast'* *'notf'* +- *'ttymouse'* *'ttym'* +- *'ttyscroll'* *'tsl'* +- *'ttytype'* *'tty'* +- weirdinvert Plugins: @@ -799,47 +792,47 @@ Providers: - *if_tcl* Startup: - --literal (file args are always literal; to expand wildcards on Windows, use - |:n| e.g. `nvim +"n *"`) - Easy mode: eview, evim, nvim -y - Restricted mode: rview, rvim, nvim -Z - Vi mode: nvim -v +- `--literal`: File args are always literal; to expand wildcards on Windows, + use |:n| e.g. `nvim +"n *"` +- Easy mode: eview, evim, nvim -y +- Restricted mode: rview, rvim, nvim -Z +- Vi mode: nvim -v Test functions: - test_alloc_fail() - test_autochdir() - test_disable_char_avail() - test_feedinput() - test_garbagecollect_soon - test_getvalue() - test_ignore_error() - test_null_blob() - test_null_channel() - test_null_dict() - test_null_function() - test_null_job() - test_null_list() - test_null_partial() - test_null_string() - test_option_not_set() - test_override() - test_refcount() - test_scrollbar() - test_setmouse() - test_settime() - test_srand_seed() +- test_alloc_fail() +- test_autochdir() +- test_disable_char_avail() +- test_feedinput() +- test_garbagecollect_soon +- test_getvalue() +- test_ignore_error() +- test_null_blob() +- test_null_channel() +- test_null_dict() +- test_null_function() +- test_null_job() +- test_null_list() +- test_null_partial() +- test_null_string() +- test_option_not_set() +- test_override() +- test_refcount() +- test_scrollbar() +- test_setmouse() +- test_settime() +- test_srand_seed() TUI: - *t_xx* *termcap-options* *t_AB* *t_Sb* *t_vb* *t_SI* - Nvim does not have special `t_XX` options nor <t_XX> keycodes to configure + *t_xx* *termcap-options* *t_AB* *t_Sb* *t_vb* *t_SI* +- Nvim does not have special `t_XX` options nor <t_XX> keycodes to configure terminal capabilities. Instead Nvim treats the terminal as any other UI, e.g. 'guicursor' sets the terminal cursor style if possible. - *termcap* - Nvim never uses the termcap database, only |terminfo| and |builtin-terms|. + *termcap* +- Nvim never uses the termcap database, only |terminfo| and |builtin-terms|. - *xterm-8bit* *xterm-8-bit* - Xterm can be run in a mode where it uses true 8-bit CSI. Supporting this + *xterm-8bit* *xterm-8-bit* +- Xterm can be run in a mode where it uses true 8-bit CSI. Supporting this requires autodetection of whether the terminal is in UTF-8 mode or non-UTF-8 mode, as the 8-bit CSI character has to be written differently in each case. Vim issues a "request version" sequence to the terminal at startup and looks diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index a20fb6d31e..6d3aaa62dd 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -366,15 +366,15 @@ one of the last commands to extend the highlighted text, the repeating will be applied up to the rightmost column of the longest line. Any count passed to the `.` command is not used. -Visual mode |default-mappings| "@" and "Q" can repeat commands in a register -for all selected lines. See |v_@-default| and |v_Q-default| for details. For -example, given the text: +Visual mode |default-mappings| "@" and "Q" repeat a register for all selected +lines if the selection is linewise. See |v_@-default| and |v_Q-default| for +details. For example, given the text: 123(hello)321 456(world)654 456(NOT THIS)654 -With register "x" containing the commands `yi(VP`, Visually selecting the +With register "x" containing the commands `yi(VP`, visually selecting the first two lines and typing `@x` produces: hello diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt index e71e31abf8..15d836a83d 100644 --- a/runtime/doc/vvars.txt +++ b/runtime/doc/vvars.txt @@ -60,9 +60,10 @@ v:collate *v:completed_item* *completed_item-variable* v:completed_item - Dictionary containing the most recent |complete-items| after - |CompleteDone|. Empty if the completion failed, or after - leaving and re-entering insert mode. + Dictionary containing the |complete-items| for the most + recently completed word after |CompleteDone|. Empty if the + completion failed, or after leaving and re-entering insert + mode. Note: Plugins can modify the value to emulate the builtin |CompleteDone| event behavior. @@ -186,6 +187,7 @@ v:event changed_window Is |v:true| if the event fired while changing window (or tab) on |DirChanged|. status Job status or exit code, -1 means "unknown". |TermClose| + reason Reason for completion being done. |CompleteDone| *v:exception* *exception-variable* v:exception diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index b71e7c80ab..4791e73929 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -502,35 +502,33 @@ horizontally split windows. CTRL-W H does it the other way around. *CTRL-W_K* CTRL-W K Move the current window to be at the very top, using the full - width of the screen. This works like closing the current - window and then creating another one with ":topleft split", - except that the current window contents is used for the new - window. + width of the screen. This works like `:topleft split`, except + it is applied to the current window and no new window is + created. *CTRL-W_J* CTRL-W J Move the current window to be at the very bottom, using the - full width of the screen. This works like closing the current - window and then creating another one with ":botright split", - except that the current window contents is used for the new - window. + full width of the screen. This works like `:botright split`, + except it is applied to the current window and no new window + is created. *CTRL-W_H* CTRL-W H Move the current window to be at the far left, using the - full height of the screen. This works like closing the - current window and then creating another one with - `:vert topleft split`, except that the current window contents - is used for the new window. + full height of the screen. This works like + `:vert topleft split`, except it is applied to the current + window and no new window is created. *CTRL-W_L* CTRL-W L Move the current window to be at the far right, using the full - height of the screen. This works like closing the - current window and then creating another one with - `:vert botright split`, except that the current window - contents is used for the new window. + height of the screen. This works like `:vert botright split`, + except it is applied to the current window and no new window + is created. *CTRL-W_T* CTRL-W T Move the current window to a new tab page. This fails if there is only one window in the current tab page. + This works like `:tab split`, except the previous window is + closed. When a count is specified the new tab page will be opened before the tab page with this index. Otherwise it comes after the current tab page. diff --git a/runtime/filetype.lua b/runtime/filetype.lua index 3f2a7c2960..4880ed55ef 100644 --- a/runtime/filetype.lua +++ b/runtime/filetype.lua @@ -11,7 +11,12 @@ vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'StdinReadPost' }, { if not vim.api.nvim_buf_is_valid(args.buf) then return end - local ft, on_detect = vim.filetype.match({ filename = args.match, buf = args.buf }) + local ft, on_detect = vim.filetype.match({ + -- The unexpanded file name is needed here. #27914 + -- Neither args.file nor args.match are guaranteed to be unexpanded. + filename = vim.fn.bufname(args.buf), + buf = args.buf, + }) if not ft then -- Generic configuration file used as fallback ft = require('vim.filetype.detect').conf(args.file, args.buf) diff --git a/runtime/ftplugin/arduino.vim b/runtime/ftplugin/arduino.vim new file mode 100644 index 0000000000..dae3dd83d3 --- /dev/null +++ b/runtime/ftplugin/arduino.vim @@ -0,0 +1,66 @@ +" Vim filetype plugin file +" Language: Arduino +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Ken Takata <https://github.com/k-takata> +" Last Change: 2024 Apr 12 +" +" Most of the part was copied from c.vim. + +" Only do this when not done yet for this buffer +if exists("b:did_ftplugin") + finish +endif + +" Don't load another plugin for this buffer +let b:did_ftplugin = 1 + +" Using line continuation here. +let s:cpo_save = &cpo +set cpo-=C + +let b:undo_ftplugin = "setl fo< com< ofu< cms< def< inc<" + +if !exists("g:arduino_recommended_style") || g:arduino_recommended_style != 0 + " Use the default setting of Arduino IDE. + setlocal expandtab tabstop=2 softtabstop=2 shiftwidth=2 + let b:undo_ftplugin ..= " et< ts< sts< sw<" +endif + +" Set 'formatoptions' to break comment lines but not other lines, +" and insert the comment leader when hitting <CR> or using "o". +setlocal fo-=t fo+=croql + +" These options have the right value as default, but the user may have +" overruled that. +setlocal commentstring& define& include& + +" Set completion with CTRL-X CTRL-O to autoloaded function. +if exists('&ofu') + setlocal ofu=ccomplete#Complete +endif + +" Set 'comments' to format dashed lists in comments. +" Also include ///, used for Doxygen. +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// + +" When the matchit plugin is loaded, this makes the % command skip parens and +" braces in comments properly. +if !exists("b:match_words") + let b:match_words = '^\s*#\s*if\(\|def\|ndef\)\>:^\s*#\s*elif\>:^\s*#\s*else\>:^\s*#\s*endif\>' + let b:match_skip = 's:comment\|string\|character\|special' + let b:undo_ftplugin ..= " | unlet! b:match_skip b:match_words" +endif + +" Win32 and GTK can filter files in the browse dialog +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Arduino Source Files (*.ino, *.pde)\t*.ino;*.pde\n" + if has("win32") + let b:browsefilter ..= "All Files (*.*)\t*\n" + else + let b:browsefilter ..= "All Files (*)\t*\n" + endif + let b:undo_ftplugin ..= " | unlet! b:browsefilter" +endif + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/ftplugin/asm.vim b/runtime/ftplugin/asm.vim index f6a92d57d7..0ae1610394 100644 --- a/runtime/ftplugin/asm.vim +++ b/runtime/ftplugin/asm.vim @@ -1,13 +1,23 @@ " Vim filetype plugin file " Language: asm " Maintainer: Colin Caine <cmcaine at the common googlemail domain> -" Last Change: 23 May 2020 +" Last Change: 2020 May 23 " 2023 Aug 28 by Vim Project (undo_ftplugin) +" 2024 Apr 09 by Vim Project (add Matchit support) if exists("b:did_ftplugin") | finish | endif let b:did_ftplugin = 1 +setl include=^\\s*%\\s*include setl comments=:;,s1:/*,mb:*,ex:*/,:// setl commentstring=;%s -let b:undo_ftplugin = "setl commentstring< comments<" +let b:undo_ftplugin = "setl commentstring< comments< include<" + +" Matchit support +if !exists('b:match_words') + let b:match_skip = 's:comment\|string\|character\|special' + let b:match_words = '^\s*%\s*if\%(\|num\|idn\|nidn\)\>:^\s*%\s*elif\>:^\s*%\s*else\>:^\s*%\s*endif\>,^\s*%\s*macro\>:^\s*%\s*endmacro\>,^\s*%\s*rep\>:^\s*%\s*endrep\>' + let b:match_ignorecase = 1 + let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words b:match_skip" +endif diff --git a/runtime/ftplugin/astro.vim b/runtime/ftplugin/astro.vim new file mode 100644 index 0000000000..0b0e03447b --- /dev/null +++ b/runtime/ftplugin/astro.vim @@ -0,0 +1,186 @@ +" Vim filetype plugin file +" Language: Astro +" Maintainer: Romain Lafourcade <romainlafourcade@gmail.com> +" Last Change: 2024 Apr 21 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:cpo_save = &cpo +set cpo-=C + +function! s:IdentifyScope(start, end) abort + let pos_start = searchpairpos(a:start, '', a:end, 'bnW') + let pos_end = searchpairpos(a:start, '', a:end, 'nW') + + return pos_start != [0, 0] + \ && pos_end != [0, 0] + \ && pos_start[0] != getpos('.')[1] +endfunction + +function! s:AstroComments() abort + if s:IdentifyScope('^---\n\s*\S', '^---\n\n') + \ || s:IdentifyScope('^\s*<script', '^\s*<\/script>') + " ECMAScript comments + setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// + setlocal commentstring=//%s + + elseif s:IdentifyScope('^\s*<style', '^\s*<\/style>') + " CSS comments + setlocal comments=s1:/*,mb:*,ex:*/ + setlocal commentstring=/*%s*/ + + else + " HTML comments + setlocal comments=s:<!--,m:\ \ \ \ ,e:--> + setlocal commentstring=<!--%s--> + endif +endfunction + +" https://code.visualstudio.com/docs/languages/jsconfig +function! s:CollectPathsFromConfig() abort + let config_json = findfile('tsconfig.json', '.;') + + if empty(config_json) + let config_json = findfile('jsconfig.json', '.;') + + if empty(config_json) + return + endif + endif + + let paths_from_config = config_json + \ ->readfile() + \ ->filter({ _, val -> val =~ '^\s*[\[\]{}"0-9]' }) + \ ->join() + \ ->json_decode() + \ ->get('compilerOptions', {}) + \ ->get('paths', {}) + + if !empty(paths_from_config) + let b:astro_paths = paths_from_config + \ ->map({key, val -> [ + \ key->glob2regpat(), + \ val[0]->substitute('\/\*$', '', '') + \ ]}) + \ ->values() + endif + + let b:undo_ftplugin ..= " | unlet! b:astro_paths" +endfunction + +function! s:AstroInclude(filename) abort + let decorated_filename = a:filename + \ ->substitute("^", "@", "") + + let found_path = b: + \ ->get("astro_paths", []) + \ ->indexof({ key, val -> decorated_filename =~ val[0]}) + + if found_path != -1 + let alias = b:astro_paths[found_path][0] + let path = b:astro_paths[found_path][1] + \ ->substitute('\(\/\)*$', '/', '') + + return decorated_filename + \ ->substitute(alias, path, '') + endif + + return a:filename +endfunction + +let b:undo_ftplugin = "setlocal" + \ .. " formatoptions<" + \ .. " path<" + \ .. " suffixesadd<" + \ .. " matchpairs<" + \ .. " comments<" + \ .. " commentstring<" + \ .. " iskeyword<" + \ .. " define<" + \ .. " include<" + \ .. " includeexpr<" + +" Create self-resetting autocommand group +augroup Astro + autocmd! * <buffer> +augroup END + +" Set 'formatoptions' to break comment lines but not other lines, +" and insert the comment leader when hitting <CR> or using "o". +setlocal formatoptions-=t +setlocal formatoptions+=croql + +" Remove irrelevant part of 'path'. +setlocal path-=/usr/include + +" Seed 'path' with default directories for :find, gf, etc. +setlocal path+=src/** +setlocal path+=public/** + +" Help Vim find extension-less filenames +let &l:suffixesadd = + \ ".astro" + \ .. ",.js,.jsx,.es,.es6,.cjs,.mjs,.jsm" + \ .. ",.json" + \ .. ",.scss,.sass,.css" + \ .. ",.svelte" + \ .. ",.ts,.tsx,.d.ts" + \ .. ",.vue" + +" From $VIMRUNTIME/ftplugin/html.vim +setlocal matchpairs+=<:> + +" Matchit configuration +if exists("loaded_matchit") + let b:match_ignorecase = 0 + + " From $VIMRUNTIME/ftplugin/javascript.vim + let b:match_words = + \ '\<do\>:\<while\>,' + \ .. '<\@<=\([^ \t>/]\+\)\%(\s\+[^>]*\%([^/]>\|$\)\|>\|$\):<\@<=/\1>,' + \ .. '<\@<=\%([^ \t>/]\+\)\%(\s\+[^/>]*\|$\):/>' + + " From $VIMRUNTIME/ftplugin/html.vim + let b:match_words ..= + \ '<!--:-->,' + \ .. '<:>,' + \ .. '<\@<=[ou]l\>[^>]*\%(>\|$\):<\@<=li\>:<\@<=/[ou]l>,' + \ .. '<\@<=dl\>[^>]*\%(>\|$\):<\@<=d[td]\>:<\@<=/dl>,' + \ .. '<\@<=\([^/!][^ \t>]*\)[^>]*\%(>\|$\):<\@<=/\1>' + + let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words" +endif + +" Change what constitutes a word, mainly useful for CSS/SASS +setlocal iskeyword+=- +setlocal iskeyword+=$ +setlocal iskeyword+=% + +" Define paths/aliases for module resolution +call s:CollectPathsFromConfig() + +" Find ESM imports +setlocal include=^\\s*\\(import\\\|import\\s\\+[^\/]\\+from\\)\\s\\+['\"] + +" Process aliases if file can't be found +setlocal includeexpr=s:AstroInclude(v:fname) + +" Set 'define' to a comprehensive value +" From $VIMRUNTIME/ftplugin/javascript.vim and +" $VIMRUNTIME/ftplugin/sass.vim +let &l:define = + \ '\(^\s*(*async\s\+function\|(*function\)' + \ .. '\|^\s*\(\*\|static\|async\|get\|set\|\i\+\.\)' + \ .. '\|^\s*\(\ze\i\+\)\(([^)]*).*{$\|\s*[:=,]\)' + + +" Set &comments and &commentstring according to current scope +autocmd Astro CursorMoved <buffer> call s:AstroComments() + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: textwidth=78 tabstop=8 shiftwidth=4 softtabstop=4 expandtab diff --git a/runtime/ftplugin/bp.vim b/runtime/ftplugin/bp.vim new file mode 100644 index 0000000000..5ddebe5233 --- /dev/null +++ b/runtime/ftplugin/bp.vim @@ -0,0 +1,14 @@ +" Blueprint build system filetype plugin file +" Language: Blueprint +" Maintainer: Bruno BELANYI <bruno.vim@belanyi.fr> +" Latest Revision: 2024-04-19 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +setlocal comments=b://,s1:/*,mb:*,ex:*/ +setlocal commentstring=//\ %s + +let b:undo_ftplugin = "setlocal comments< commentstring<" diff --git a/runtime/ftplugin/cgdbrc.vim b/runtime/ftplugin/cgdbrc.vim new file mode 100644 index 0000000000..46cf135c5c --- /dev/null +++ b/runtime/ftplugin/cgdbrc.vim @@ -0,0 +1,21 @@ +" Vim filetype plugin file +" Language: cgdbrc +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Documentation: https://cgdb.github.io/docs/Configuring-CGDB.html +" Latest Revision: 2024-04-09 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +let s:save_cpoptions = &cpoptions +set cpoptions&vim + +let b:undo_ftplugin = 'setl com< cms<' + +setlocal commentstring=#%s +setlocal comments=:# + +let &cpoptions = s:save_cpoptions +unlet s:save_cpoptions diff --git a/runtime/ftplugin/checkhealth.vim b/runtime/ftplugin/checkhealth.vim index 4b530e6f7c..62a1970b4a 100644 --- a/runtime/ftplugin/checkhealth.vim +++ b/runtime/ftplugin/checkhealth.vim @@ -9,9 +9,6 @@ endif runtime! ftplugin/help.vim setlocal wrap breakindent linebreak -setlocal foldexpr=getline(v:lnum-1)=~'^=\\{78}$'?'>1':(getline(v:lnum)=~'^=\\{78}'?0:'=') -setlocal foldmethod=expr -setlocal foldtext=v:lua.require('vim.health').foldtext() let &l:iskeyword='!-~,^*,^|,^",192-255' if exists("b:undo_ftplugin") diff --git a/runtime/ftplugin/cmake.vim b/runtime/ftplugin/cmake.vim index 94c007629b..9fcf87a759 100644 --- a/runtime/ftplugin/cmake.vim +++ b/runtime/ftplugin/cmake.vim @@ -2,6 +2,7 @@ " Language: CMake " Maintainer: Keith Smiley <keithbsmiley@gmail.com> " Last Change: 2018 Aug 30 +" 2024 Apr 20 - add include and suffixadd (Vim Project) " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -15,7 +16,7 @@ set cpo&vim " Don't load another plugin for this buffer let b:did_ftplugin = 1 -let b:undo_ftplugin = "setl commentstring<" +let b:undo_ftplugin = "setl inc< sua< commentstring<" if exists('loaded_matchit') let b:match_words = '\<if\>:\<elseif\>\|\<else\>:\<endif\>' @@ -27,6 +28,8 @@ if exists('loaded_matchit') let b:undo_ftplugin .= "| unlet b:match_words" endif +setlocal include=\s*include +setlocal suffixesadd=.cmake,-config.cmake setlocal commentstring=#\ %s " restore 'cpo' and clean up buffer variable diff --git a/runtime/ftplugin/dart.vim b/runtime/ftplugin/dart.vim new file mode 100644 index 0000000000..42c90c212f --- /dev/null +++ b/runtime/ftplugin/dart.vim @@ -0,0 +1,15 @@ +" Vim filetype plugin +" Language: dart +" Maintainer: Riley Bruins <ribru17@gmail.com> +" Last Change: 2024 May 18 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +" Set 'comments' to format dashed lists in comments. +" Also include ///, used for Doxygen. +setl comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// commentstring=//\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/deb822sources.vim b/runtime/ftplugin/deb822sources.vim new file mode 100644 index 0000000000..4936f42bf9 --- /dev/null +++ b/runtime/ftplugin/deb822sources.vim @@ -0,0 +1,16 @@ +" Language: Debian sources.list +" Maintainer: Debian Vim Maintainers <team+vim@tracker.debian.org> +" Last Change: 2024 Mar 20 +" License: Vim License +" URL: https://salsa.debian.org/vim-team/vim-debian/blob/main/ftplugin/deb822sources.vim + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin=1 + +setlocal comments=:# +setlocal commentstring=#%s +setlocal formatoptions-=t + +let b:undo_ftplugin = 'setlocal comments< commentstring< formatoptions<' diff --git a/runtime/ftplugin/dts.vim b/runtime/ftplugin/dts.vim new file mode 100644 index 0000000000..42e38338b7 --- /dev/null +++ b/runtime/ftplugin/dts.vim @@ -0,0 +1,16 @@ +" Vim filetype plugin file +" Language: dts/dtsi (device tree files) +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Latest Revision: 2024 Apr 12 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +let b:undo_ftplugin = 'setl inc< cms< com<' + +setlocal include=^\\%(#include\\\|/include/\\) +" same as C +setlocal commentstring& +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// diff --git a/runtime/ftplugin/gdb.vim b/runtime/ftplugin/gdb.vim index 2473b13af0..7c10633be4 100644 --- a/runtime/ftplugin/gdb.vim +++ b/runtime/ftplugin/gdb.vim @@ -1,12 +1,20 @@ " Vim filetype plugin file " Language: gdb " Maintainer: Michaël Peeters <NOSPAMm.vim@noekeon.org> -" Last Changed: 26 Oct 2017 +" Last Changed: 2017-10-26 +" 2024-04-10: - add Matchit support (by Vim Project) if exists("b:did_ftplugin") | finish | endif let b:did_ftplugin = 1 setlocal commentstring=#%s +setlocal include=^\\s*source " Undo the stuff we changed. -let b:undo_ftplugin = "setlocal cms<" +let b:undo_ftplugin = "setlocal cms< include<" + +" Matchit support +if !exists('b:match_words') + let b:match_words = '\<\%(if\|while\|define\|document\)\>:\<else\>:\<end\>' + let b:undo_ftplugin ..= " | unlet! b:match_words" +endif diff --git a/runtime/ftplugin/graphql.vim b/runtime/ftplugin/graphql.vim new file mode 100644 index 0000000000..56f6e36e20 --- /dev/null +++ b/runtime/ftplugin/graphql.vim @@ -0,0 +1,13 @@ +" Vim filetype plugin +" Language: graphql +" Maintainer: Riley Bruins <ribru17@gmail.com> +" Last Change: 2024 May 18 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setl comments=:# commentstring=#\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/hyprlang.vim b/runtime/ftplugin/hyprlang.vim new file mode 100644 index 0000000000..5c186c3c5e --- /dev/null +++ b/runtime/ftplugin/hyprlang.vim @@ -0,0 +1,13 @@ +" Vim filetype plugin +" Language: hyprlang +" Maintainer: ribru17 <ribru17@gmail.com> +" Last Change: 2024 May 18 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setl comments=:# commentstring=#\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/java.vim b/runtime/ftplugin/java.vim index bb7e7cd72c..fa2b61075f 100644 --- a/runtime/ftplugin/java.vim +++ b/runtime/ftplugin/java.vim @@ -1,10 +1,9 @@ " Vim filetype plugin file " Language: Java -" -" This runtime file is looking for a new maintainer. -" -" Former maintainer: Dan Sharp -" Last Change: 2012 Mar 11 +" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com> +" Former Maintainer: Dan Sharp +" Repository: https://github.com/zzzyxwvut/java-vim.git +" Last Change: 2024 Apr 18 " 2024 Jan 14 by Vim Project (browsefilter) if exists("b:did_ftplugin") | finish | endif @@ -23,8 +22,36 @@ set suffixes+=.class " name to / and append .java to the name, then search the path. setlocal includeexpr=substitute(v:fname,'\\.','/','g') setlocal suffixesadd=.java -if exists("g:ftplugin_java_source_path") - let &l:path=g:ftplugin_java_source_path . ',' . &l:path + +" Clean up in case this file is sourced again. +unlet! s:zip_func_upgradable + +" Documented in ":help ft-java-plugin". +if exists("g:ftplugin_java_source_path") && + \ type(g:ftplugin_java_source_path) == type("") + if filereadable(g:ftplugin_java_source_path) + if exists("#zip") && + \ g:ftplugin_java_source_path =~# '.\.\%(jar\|zip\)$' + if !exists("s:zip_files") + let s:zip_files = {} + endif + + let s:zip_files[bufnr('%')] = g:ftplugin_java_source_path + let s:zip_files[0] = g:ftplugin_java_source_path + let s:zip_func_upgradable = 1 + + function! JavaFileTypeZipFile() abort + let @/ = substitute(v:fname, '\.', '\\/', 'g') . '.java' + return get(s:zip_files, bufnr('%'), s:zip_files[0]) + endfunction + + " E120 for "inex=s:JavaFileTypeZipFile()" before v8.2.3900. + setlocal includeexpr=JavaFileTypeZipFile() + setlocal suffixesadd< + endif + else + let &l:path = g:ftplugin_java_source_path . ',' . &l:path + endif endif " Set 'formatoptions' to break comment lines but not other lines, @@ -53,6 +80,25 @@ let b:undo_ftplugin = "setlocal suffixes< suffixesadd<" . \ " formatoptions< comments< commentstring< path< includeexpr<" . \ " | unlet! b:browsefilter" +" See ":help vim9-mix". +if !has("vim9script") + let &cpo = s:save_cpo + unlet s:save_cpo + finish +endif + +if exists("s:zip_func_upgradable") + delfunction! JavaFileTypeZipFile + + def! s:JavaFileTypeZipFile(): string + @/ = substitute(v:fname, '\.', '\\/', 'g') .. '.java' + return get(zip_files, bufnr('%'), zip_files[0]) + enddef + + setlocal includeexpr=s:JavaFileTypeZipFile() + setlocal suffixesadd< +endif + " Restore the saved compatibility options. let &cpo = s:save_cpo unlet s:save_cpo diff --git a/runtime/ftplugin/jj.vim b/runtime/ftplugin/jj.vim new file mode 100644 index 0000000000..cc5d700a30 --- /dev/null +++ b/runtime/ftplugin/jj.vim @@ -0,0 +1,19 @@ +" Vim filetype plugin +" Language: jj description +" Maintainer: Gregory Anders <greg@gpanders.com> +" Last Change: 2024 May 8 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +" Use the same formatoptions and textwidth as the gitcommit ftplugin +setlocal nomodeline formatoptions+=tl textwidth=72 +setlocal formatoptions-=c formatoptions-=r formatoptions-=o formatoptions-=q formatoptions+=n +setlocal formatlistpat=^\\s*\\d\\+[\\]:.)}]\\s\\+\\\|^\\s*[-*+]\\s\\+ + +setlocal comments=b:JJ: +setlocal commentstring=JJ:\ %s + +let b:undo_ftplugin = 'setl modeline< formatoptions< textwidth< formatlistpat< comments< commentstring<' diff --git a/runtime/ftplugin/jq.vim b/runtime/ftplugin/jq.vim new file mode 100644 index 0000000000..88958e80dd --- /dev/null +++ b/runtime/ftplugin/jq.vim @@ -0,0 +1,16 @@ +" Vim compiler file +" Language: jq +" Maintainer: Vito <vito.blog@gmail.com> +" Last Change: 2024 Apr 29 +" Upstream: https://github.com/vito-c/jq.vim + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setlocal include=^\\s*\\%(import\\\|include\\) +setlocal commentstring=#%s +compiler jq + +let b:undo_ftplugin = 'setl commentstring< include<' diff --git a/runtime/ftplugin/kconfig.vim b/runtime/ftplugin/kconfig.vim index 940ba7427f..767490701b 100644 --- a/runtime/ftplugin/kconfig.vim +++ b/runtime/ftplugin/kconfig.vim @@ -1,9 +1,9 @@ " Vim filetype plugin file " Vim syntax file -" Maintainer: Christian Brabandt <cb@256bit.org> -" Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2015-05-29 -" License: Vim (see :h license) +" Maintainer: Christian Brabandt <cb@256bit.org> +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Revision: 2024-04-12 +" License: Vim (see :h license) " Repository: https://github.com/chrisbra/vim-kconfig if exists("b:did_ftplugin") @@ -11,17 +11,12 @@ if exists("b:did_ftplugin") endif let b:did_ftplugin = 1 -let s:cpo_save = &cpo -set cpo&vim - -let b:undo_ftplugin = "setl com< cms< fo<" +let b:undo_ftplugin = "setl inc< com< cms< fo<" +setlocal include=source\\s\\+ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql " For matchit.vim if exists("loaded_matchit") let b:match_words = '^\<menu\>:\<endmenu\>,^\<if\>:\<endif\>,^\<choice\>:\<endchoice\>' endif - -let &cpo = s:cpo_save -unlet s:cpo_save diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 277ce3c0b3..fdeaae4c3f 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -24,7 +24,7 @@ if !exists('g:no_plugin_maps') && !exists('g:no_man_maps') nnoremap <silent> <buffer> k gk nnoremap <silent> <buffer> gO :lua require'man'.show_toc()<CR> nnoremap <silent> <buffer> <2-LeftMouse> :Man<CR> - if get(b:, 'pager') + if get(g:, 'pager') nnoremap <silent> <buffer> <nowait> q :lclose<CR><C-W>q else nnoremap <silent> <buffer> <nowait> q :lclose<CR><C-W>c diff --git a/runtime/ftplugin/nim.vim b/runtime/ftplugin/nim.vim new file mode 100644 index 0000000000..4ab8b7b536 --- /dev/null +++ b/runtime/ftplugin/nim.vim @@ -0,0 +1,13 @@ +" Vim filetype plugin +" Language: nim +" Maintainer: Riley Bruins <ribru17@gmail.com> +" Last Change: 2024 May 19 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setl comments=exO:]#,fs1:#[,mb:*,ex:]#,:# commentstring=#\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/ondir.vim b/runtime/ftplugin/ondir.vim new file mode 100644 index 0000000000..0854578c91 --- /dev/null +++ b/runtime/ftplugin/ondir.vim @@ -0,0 +1,18 @@ +" Vim filetype plugin file +" Language: ondir <https://github.com/alecthomas/ondir> +" Maintainer: Jon Parise <jon@indelible.org> + +if exists('b:did_ftplugin') + finish +endif + +let s:cpo_save = &cpoptions + +setlocal comments=:# commentstring=#\ %s + +let b:undo_ftplugin = 'setl comments< commentstring<' + +let &cpoptions = s:cpo_save +unlet s:cpo_save + +" vim: et ts=4 sw=2 sts=2: diff --git a/runtime/ftplugin/prisma.vim b/runtime/ftplugin/prisma.vim new file mode 100644 index 0000000000..8b733da64c --- /dev/null +++ b/runtime/ftplugin/prisma.vim @@ -0,0 +1,13 @@ +" Vim filetype plugin +" Language: prisma +" Maintainer: Riley Bruins <ribru17@gmail.com> +" Last Change: 2024 May 19 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setl comments=:///,:// commentstring=//\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/purescript.vim b/runtime/ftplugin/purescript.vim new file mode 100644 index 0000000000..b3300bb324 --- /dev/null +++ b/runtime/ftplugin/purescript.vim @@ -0,0 +1,14 @@ +" Vim filetype plugin +" Language: purescript +" Maintainer: Riley Bruins <ribru17@gmail.com> +" Last Change: 2024 May 19 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setl comments=s1f:{-,mb:\ ,ex:-},:--\ \|,:-- +setl commentstring=--\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/python.vim b/runtime/ftplugin/python.vim index 79acaa6f08..c000296726 100644 --- a/runtime/ftplugin/python.vim +++ b/runtime/ftplugin/python.vim @@ -1,10 +1,9 @@ " Vim filetype plugin file " Language: python -" Maintainer: Tom Picton <tom@tompicton.co.uk> +" Maintainer: Tom Picton <tom@tompicton.com> " Previous Maintainer: James Sully <sullyj3@gmail.com> " Previous Maintainer: Johannes Zellner <johannes@zellner.org> -" Last Change: Mon, 5 October 2020 -" 2024 Jan 14 by Vim Project (browsefilter) +" Last Change: 2024/05/13 " https://github.com/tpict/vim-ftplugin-python if exists("b:did_ftplugin") | finish | endif @@ -15,7 +14,7 @@ set cpo&vim setlocal cinkeys-=0# setlocal indentkeys-=0# setlocal include=^\\s*\\(from\\\|import\\) -setlocal define=^\\s*\\(def\\\|class\\) +setlocal define=^\\s*\\(\\(async\\s\\+\\)\\?def\\\|class\\) " For imports with leading .., append / and replace additional .s with ../ let b:grandparent_match = '^\(.\.\)\(\.*\)' @@ -57,14 +56,14 @@ let b:next_end='\v\S\n*(%$\|^(\s*\n*)*(class\|def\|async def)\|^\S)' let b:prev_end='\v\S\n*(^(\s*\n*)*(class\|def\|async def)\|^\S)' if !exists('g:no_plugin_maps') && !exists('g:no_python_maps') - execute "nnoremap <silent> <buffer> ]] :call <SID>Python_jump('n', '". b:next_toplevel."', 'W', v:count1)<cr>" - execute "nnoremap <silent> <buffer> [[ :call <SID>Python_jump('n', '". b:prev_toplevel."', 'Wb', v:count1)<cr>" - execute "nnoremap <silent> <buffer> ][ :call <SID>Python_jump('n', '". b:next_endtoplevel."', 'W', v:count1, 0)<cr>" - execute "nnoremap <silent> <buffer> [] :call <SID>Python_jump('n', '". b:prev_endtoplevel."', 'Wb', v:count1, 0)<cr>" - execute "nnoremap <silent> <buffer> ]m :call <SID>Python_jump('n', '". b:next."', 'W', v:count1)<cr>" - execute "nnoremap <silent> <buffer> [m :call <SID>Python_jump('n', '". b:prev."', 'Wb', v:count1)<cr>" - execute "nnoremap <silent> <buffer> ]M :call <SID>Python_jump('n', '". b:next_end."', 'W', v:count1, 0)<cr>" - execute "nnoremap <silent> <buffer> [M :call <SID>Python_jump('n', '". b:prev_end."', 'Wb', v:count1, 0)<cr>" + execute "nnoremap <silent> <buffer> ]] :<C-U>call <SID>Python_jump('n', '". b:next_toplevel."', 'W', v:count1)<cr>" + execute "nnoremap <silent> <buffer> [[ :<C-U>call <SID>Python_jump('n', '". b:prev_toplevel."', 'Wb', v:count1)<cr>" + execute "nnoremap <silent> <buffer> ][ :<C-U>call <SID>Python_jump('n', '". b:next_endtoplevel."', 'W', v:count1, 0)<cr>" + execute "nnoremap <silent> <buffer> [] :<C-U>call <SID>Python_jump('n', '". b:prev_endtoplevel."', 'Wb', v:count1, 0)<cr>" + execute "nnoremap <silent> <buffer> ]m :<C-U>call <SID>Python_jump('n', '". b:next."', 'W', v:count1)<cr>" + execute "nnoremap <silent> <buffer> [m :<C-U>call <SID>Python_jump('n', '". b:prev."', 'Wb', v:count1)<cr>" + execute "nnoremap <silent> <buffer> ]M :<C-U>call <SID>Python_jump('n', '". b:next_end."', 'W', v:count1, 0)<cr>" + execute "nnoremap <silent> <buffer> [M :<C-U>call <SID>Python_jump('n', '". b:prev_end."', 'Wb', v:count1, 0)<cr>" execute "onoremap <silent> <buffer> ]] :call <SID>Python_jump('o', '". b:next_toplevel."', 'W', v:count1)<cr>" execute "onoremap <silent> <buffer> [[ :call <SID>Python_jump('o', '". b:prev_toplevel."', 'Wb', v:count1)<cr>" diff --git a/runtime/ftplugin/requirements.vim b/runtime/ftplugin/requirements.vim new file mode 100644 index 0000000000..fcfc1ac269 --- /dev/null +++ b/runtime/ftplugin/requirements.vim @@ -0,0 +1,43 @@ +" the Requirements File Format syntax support for Vim +" Version: 1.8.0 +" Author: raimon <raimon49@hotmail.com> +" Upstream: https://github.com/raimon49/requirements.txt.vim +" License: MIT LICENSE +" The MIT License (MIT) +" +" Copyright (c) 2015 raimon +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to deal +" in the Software without restriction, including without limitation the rights +" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +" copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in all +" copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +" SOFTWARE. +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:save_cpoptions = &cpoptions +set cpoptions&vim + +let b:undo_ftplugin = "setl iskeyword< commentstring<" +" pip options contain "-" +setlocal iskeyword+=- +setlocal commentstring=#\ %s +compiler pip_compile + +let &cpoptions = s:save_cpoptions +unlet s:save_cpoptions +" vim: et sw=4 ts=4 sts=4: diff --git a/runtime/ftplugin/rescript.vim b/runtime/ftplugin/rescript.vim new file mode 100644 index 0000000000..dd94c68799 --- /dev/null +++ b/runtime/ftplugin/rescript.vim @@ -0,0 +1,13 @@ +" Vim filetype plugin +" Language: rescript +" Maintainer: Riley Bruins <ribru17@gmail.com> +" Last Change: 2024 May 21 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setl comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// commentstring=//\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/roc.vim b/runtime/ftplugin/roc.vim new file mode 100644 index 0000000000..c66510874d --- /dev/null +++ b/runtime/ftplugin/roc.vim @@ -0,0 +1,14 @@ +" Roc filetype plugin file +" Language: Roc +" Maintainer: nat-418 <93013864+nat-418@users.noreply.github.com> +" Latest Revision: 2024-04-5 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +setlocal comments=:##,:# +setlocal commentstring=#\ %s + +let b:undo_ftplugin = "setl com< cms<" diff --git a/runtime/ftplugin/rust.vim b/runtime/ftplugin/rust.vim index 7f1a86ea95..fb15b444d0 100644 --- a/runtime/ftplugin/rust.vim +++ b/runtime/ftplugin/rust.vim @@ -1,7 +1,7 @@ " Language: Rust " Description: Vim ftplugin for Rust " Maintainer: Chris Morgan <me@chrismorgan.info> -" Last Change: 2023-09-11 +" Last Change: 2024-03-17 " For bugs, patches and license go to https://github.com/rust-lang/rust.vim if exists("b:did_ftplugin") @@ -94,14 +94,15 @@ if has('conceal') && get(g:, 'rust_conceal', 0) endif " Motion Commands {{{1 - -" Bind motion commands to support hanging indents -nnoremap <silent> <buffer> [[ :call rust#Jump('n', 'Back')<CR> -nnoremap <silent> <buffer> ]] :call rust#Jump('n', 'Forward')<CR> -xnoremap <silent> <buffer> [[ :call rust#Jump('v', 'Back')<CR> -xnoremap <silent> <buffer> ]] :call rust#Jump('v', 'Forward')<CR> -onoremap <silent> <buffer> [[ :call rust#Jump('o', 'Back')<CR> -onoremap <silent> <buffer> ]] :call rust#Jump('o', 'Forward')<CR> +if !exists("g:no_plugin_maps") && !exists("g:no_rust_maps") + " Bind motion commands to support hanging indents + nnoremap <silent> <buffer> [[ :call rust#Jump('n', 'Back')<CR> + nnoremap <silent> <buffer> ]] :call rust#Jump('n', 'Forward')<CR> + xnoremap <silent> <buffer> [[ :call rust#Jump('v', 'Back')<CR> + xnoremap <silent> <buffer> ]] :call rust#Jump('v', 'Forward')<CR> + onoremap <silent> <buffer> [[ :call rust#Jump('o', 'Back')<CR> + onoremap <silent> <buffer> ]] :call rust#Jump('o', 'Forward')<CR> +endif " Commands {{{1 @@ -176,12 +177,12 @@ let b:undo_ftplugin = " \|delcommand -buffer RustInfoToClipboard \|delcommand -buffer RustInfoToFile \|delcommand -buffer RustTest - \|nunmap <buffer> [[ - \|nunmap <buffer> ]] - \|xunmap <buffer> [[ - \|xunmap <buffer> ]] - \|ounmap <buffer> [[ - \|ounmap <buffer> ]] + \|silent! nunmap <buffer> [[ + \|silent! nunmap <buffer> ]] + \|silent! xunmap <buffer> [[ + \|silent! xunmap <buffer> ]] + \|silent! ounmap <buffer> [[ + \|silent! ounmap <buffer> ]] \|setlocal matchpairs-=<:> \|unlet b:match_skip \" diff --git a/runtime/ftplugin/sh.vim b/runtime/ftplugin/sh.vim index 6d2093bf83..c47aa520e9 100644 --- a/runtime/ftplugin/sh.vim +++ b/runtime/ftplugin/sh.vim @@ -4,7 +4,7 @@ " Previous Maintainer: Dan Sharp " Contributor: Enno Nagel <ennonagel+vim@gmail.com> " Eisuke Kawashima -" Last Change: 2024 Feb 27 +" Last Change: 2024 May 06 by Vim Project (MANPAGER=) if exists("b:did_ftplugin") finish @@ -46,11 +46,11 @@ endif if get(b:, "is_bash", 0) if !has("gui_running") && executable("less") - command! -buffer -nargs=1 ShKeywordPrg silent exe '!bash -c "{ help "<args>" 2>/dev/null || man "<args>"; } | LESS= less"' | redraw! + command! -buffer -nargs=1 ShKeywordPrg silent exe '!bash -c "{ help "<args>" 2>/dev/null || MANPAGER= man "<args>"; } | LESS= less"' | redraw! elseif has("terminal") command! -buffer -nargs=1 ShKeywordPrg silent exe ':term bash -c "help "<args>" 2>/dev/null || man "<args>""' else - command! -buffer -nargs=1 ShKeywordPrg echo system('bash -c "help <args>" 2>/dev/null || man "<args>"') + command! -buffer -nargs=1 ShKeywordPrg echo system('bash -c "help <args>" 2>/dev/null || MANPAGER= man "<args>"') endif setlocal keywordprg=:ShKeywordPrg let b:undo_ftplugin ..= " | setl kp< | sil! delc -buffer ShKeywordPrg" diff --git a/runtime/ftplugin/slint.vim b/runtime/ftplugin/slint.vim new file mode 100644 index 0000000000..ac8057f39d --- /dev/null +++ b/runtime/ftplugin/slint.vim @@ -0,0 +1,15 @@ +" Vim filetype plugin +" Language: slint +" Maintainer: Riley Bruins <ribru17@gmail.com> +" Last Change: 2024 May 19 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +" Set 'comments' to format dashed lists in comments. +" Also include ///, used for Doxygen. +setl comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// commentstring=//\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/sql.vim b/runtime/ftplugin/sql.vim index c85232f51b..61b7e67255 100644 --- a/runtime/ftplugin/sql.vim +++ b/runtime/ftplugin/sql.vim @@ -4,6 +4,7 @@ " Maintainer: David Fishburn <dfishburn dot vim at gmail dot com> " Last Change: 2017 Mar 07 " 2024 Jan 14 by Vim Project (browsefilter) +" 2024 May 18 by Vim Project (set comment options) " Download: http://vim.sourceforge.net/script.php?script_id=454 " For more details please use: @@ -105,6 +106,8 @@ set cpo&vim setlocal formatoptions-=t setlocal formatoptions+=c +setlocal comments=:-- commentstring=--\ %s + " Functions/Commands to allow the user to change SQL syntax dialects " through the use of :SQLSetType <tab> for completion. " This works with both Vim 6 and 7. @@ -266,7 +269,7 @@ if exists("b:did_ftplugin") && exists("b:current_ftplugin") && b:current_ftplugi finish endif -let b:undo_ftplugin = "setl comments< formatoptions< define< omnifunc<" . +let b:undo_ftplugin = "setl comments< commentstring< formatoptions< define< omnifunc<" . \ " | unlet! b:browsefilter b:match_words" " Don't load another plugin for this buffer diff --git a/runtime/ftplugin/sshdconfig.vim b/runtime/ftplugin/sshdconfig.vim new file mode 100644 index 0000000000..3d394549bc --- /dev/null +++ b/runtime/ftplugin/sshdconfig.vim @@ -0,0 +1,10 @@ +" Vim filetype plugin file +" Language: OpenSSH server configuration file +" Maintainer: Yinzuo Jiang <jiangyinzuo@foxmail.com> +" Latest Revision: 2024-05-17 + +if exists("b:did_ftplugin") + finish +endif + +runtime! ftplugin/conf.vim diff --git a/runtime/ftplugin/stylus.vim b/runtime/ftplugin/stylus.vim new file mode 100644 index 0000000000..1c5f14eebe --- /dev/null +++ b/runtime/ftplugin/stylus.vim @@ -0,0 +1,54 @@ +" Vim filetype plugin +" Language: Stylus +" Maintainer: Marc Harter +" Credits: Tim Pope + +" Only do this when not done yet for this buffer +if exists("b:did_ftplugin") + finish +endif + +let s:save_cpo = &cpo +set cpo-=C + +" Define some defaults in case the included ftplugins don't set them. +let s:undo_ftplugin = "" +let s:browsefilter = "All Files (*.*)\t*.*\n" + +runtime! ftplugin/html.vim ftplugin/html_*.vim ftplugin/html/*.vim +unlet! b:did_ftplugin + +" Override our defaults if these were set by an included ftplugin. +if exists("b:undo_ftplugin") + let s:undo_ftplugin = b:undo_ftplugin + unlet b:undo_ftplugin +endif +if exists("b:browsefilter") + let s:browsefilter = b:browsefilter + unlet b:browsefilter +endif + +" Change the browse dialog on Win32 to show mainly Styl-related files +if has("gui_win32") + let b:browsefilter="Stylus Files (*.styl)\t*.styl\n" . s:browsefilter +endif + +setlocal comments= commentstring=//\ %s +setlocal suffixesadd=.styl +setlocal formatoptions+=r + +" Add '-' and '#' to the what makes up a keyword. +" This means that 'e' and 'w' work properly now, for properties +" and valid variable names. +setl iskeyword+=#,- + +" Add a Stylus command (to see if it's valid) +command -buffer Stylus !clear; cat % |stylus + + +let b:undo_ftplugin = "setl sua< isk< cms< com< fo< " + \ " | unlet! b:browsefilter b:match_words | " . s:undo_ftplugin + +let &cpo = s:save_cpo + +" vim:set sw=2: diff --git a/runtime/ftplugin/tutor.vim b/runtime/ftplugin/tutor.vim index 30783d9799..45ee582cba 100644 --- a/runtime/ftplugin/tutor.vim +++ b/runtime/ftplugin/tutor.vim @@ -19,7 +19,7 @@ setlocal noundofile setlocal keywordprg=:help setlocal iskeyword=@,-,_ -" The user will have to enable the folds himself, but we provide the foldexpr +" The user will have to enable the folds themself, but we provide the foldexpr " function. setlocal foldmethod=manual setlocal foldexpr=tutor#TutorFolds() diff --git a/runtime/ftplugin/typst.vim b/runtime/ftplugin/typst.vim new file mode 100644 index 0000000000..c2d7811ace --- /dev/null +++ b/runtime/ftplugin/typst.vim @@ -0,0 +1,14 @@ +" Vim filetype plugin +" Language: typst +" Maintainer: Riley Bruins <ribru17@gmail.com> +" Last Change: 2024 May 19 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// +setlocal commentstring=//\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/uci.vim b/runtime/ftplugin/uci.vim new file mode 100644 index 0000000000..984dab6c5f --- /dev/null +++ b/runtime/ftplugin/uci.vim @@ -0,0 +1,21 @@ +" Vim ftplugin file +" Language: OpenWrt Unified Configuration Interface +" Maintainer: Colin Caine <complaints@cmcaine.co.uk> +" Upstream: https://github.com/cmcaine/vim-uci +" Last Change: 2024 Apr 17 +" +" For more information on uci, see https://openwrt.org/docs/guide-user/base-system/uci + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +" UCI files are indented with tabs. +setl noexpandtab +setl shiftwidth=0 +setl softtabstop=0 + +setl commentstring=#\ %s + +let b:undo_ftplugin = "setlocal et< cms< sts< sw<" diff --git a/runtime/ftplugin/v.vim b/runtime/ftplugin/v.vim new file mode 100644 index 0000000000..4d4f60d797 --- /dev/null +++ b/runtime/ftplugin/v.vim @@ -0,0 +1,15 @@ +" Vim filetype plugin +" Language: v +" Maintainer: Riley Bruins <ribru17@gmail.com> +" Last Change: 2024 May 19 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +" Set 'comments' to format dashed lists in comments. +" Also include ///, used for Doxygen. +setl comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// commentstring=//\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/verilog.vim b/runtime/ftplugin/verilog.vim index 83c3754e05..c20be25865 100644 --- a/runtime/ftplugin/verilog.vim +++ b/runtime/ftplugin/verilog.vim @@ -3,6 +3,7 @@ " Maintainer: Chih-Tsun Huang <cthuang@cs.nthu.edu.tw> " Last Change: 2017 Aug 25 by Chih-Tsun Huang " 2024 Jan 14 by Vim Project (browsefilter) +" 2024 May 20 by Riley Bruins <ribru17@gmail.com> (commentstring) " URL: http://www.cs.nthu.edu.tw/~cthuang/vim/ftplugin/verilog.vim " " Credits: @@ -22,7 +23,7 @@ let s:cpo_save = &cpo set cpo&vim " Undo the plugin effect -let b:undo_ftplugin = "setlocal fo< com< tw<" +let b:undo_ftplugin = "setlocal fo< com< tw< cms<" \ . "| unlet! b:browsefilter b:match_ignorecase b:match_words" " Set 'formatoptions' to break comment lines but not other lines, @@ -31,6 +32,7 @@ setlocal fo-=t fo+=croqlm1 " Set 'comments' to format dashed lists in comments. setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// +setlocal commentstring=//\ %s " Format comments to be up to 78 characters long if &textwidth == 0 diff --git a/runtime/ftplugin/vim.vim b/runtime/ftplugin/vim.vim index f5dae0f94e..5c99df7f5a 100644 --- a/runtime/ftplugin/vim.vim +++ b/runtime/ftplugin/vim.vim @@ -1,7 +1,7 @@ " Vim filetype plugin -" Language: Vim -" Maintainer: The Vim Project <https://github.com/vim/vim> -" Last Change: 2023 Aug 10 +" Language: Vim +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2024 Apr 13 " Former Maintainer: Bram Moolenaar <Bram@vim.org> " Only do this when not done yet for this buffer @@ -50,7 +50,7 @@ setlocal isk+=# setlocal keywordprg=:help " Comments starts with # in Vim9 script. We have to guess which one to use. -if "\n" .. getline(1, 10)->join("\n") =~# '\n\s*vim9\%[script]\>' +if "\n" .. getline(1, 32)->join("\n") =~# '\n\s*vim9\%[script]\>' setlocal commentstring=#%s else setlocal commentstring=\"%s diff --git a/runtime/ftplugin/yaml.vim b/runtime/ftplugin/yaml.vim index db7cbd7ddb..8bfc45e4c8 100644 --- a/runtime/ftplugin/yaml.vim +++ b/runtime/ftplugin/yaml.vim @@ -16,6 +16,13 @@ let b:undo_ftplugin = "setl com< cms< et< fo<" setlocal comments=:# commentstring=#\ %s expandtab setlocal formatoptions-=t formatoptions+=croql +" rime input method engine uses `*.custom.yaml` as its config files +if expand('%:r:e') ==# 'custom' + compiler rime_deployer + setlocal include=__include:\\s* + let b:undo_ftplugin ..= " inc<" +endif + if !exists("g:yaml_recommended_style") || g:yaml_recommended_style != 0 let b:undo_ftplugin ..= " sw< sts<" setlocal shiftwidth=2 softtabstop=2 diff --git a/runtime/ftplugin/zathurarc.vim b/runtime/ftplugin/zathurarc.vim new file mode 100644 index 0000000000..259fb137a1 --- /dev/null +++ b/runtime/ftplugin/zathurarc.vim @@ -0,0 +1,22 @@ +" Vim filetype plugin file +" Language: Zathurarc +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Documentation: https://pwmt.org/projects/zathura/documentation/ +" Upstream: https://github.com/Freed-Wu/zathurarc.vim +" Latest Revision: 2024-04-02 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +let s:save_cpoptions = &cpoptions +set cpoptions&vim + +let b:undo_ftplugin = 'setlocal comments< commentstring< include<' +setlocal comments=:# +setlocal commentstring=#\ %s +setlocal include=^\sinclude + +let &cpoptions = s:save_cpoptions +unlet s:save_cpoptions diff --git a/runtime/ftplugin/zig.vim b/runtime/ftplugin/zig.vim index 28b8cd5a67..ea229b6a49 100644 --- a/runtime/ftplugin/zig.vim +++ b/runtime/ftplugin/zig.vim @@ -1,68 +1,52 @@ " Vim filetype plugin file -" Language: Zig -" Upstream: https://github.com/ziglang/zig.vim +" Language: Zig +" Maintainer: Mathias Lindgren <math.lindgren@gmail.com> +" Last Change: 2024 May 21 +" Based on: https://github.com/ziglang/zig.vim -" Only do this when not done yet for this buffer if exists("b:did_ftplugin") finish endif let b:did_ftplugin = 1 -let s:cpo_orig = &cpo +let s:cpo_save = &cpo set cpo&vim compiler zig_build " Match Zig builtin fns setlocal iskeyword+=@-@ - -" Recommended code style, no tabs and 4-space indentation -setlocal expandtab -setlocal tabstop=8 -setlocal softtabstop=4 -setlocal shiftwidth=4 - setlocal formatoptions-=t formatoptions+=croql - -setlocal suffixesadd=.zig,.zir +setlocal suffixesadd=.zig,.zir,.zon +let &l:define='\v(<fn>|<const>|<var>|^\s*\#\s*define)' +let b:undo_ftplugin = 'setl isk< fo< sua< mp< def<' + +if get(g:, 'zig_recommended_style', 1) + setlocal expandtab + setlocal tabstop=8 + setlocal softtabstop=4 + setlocal shiftwidth=4 + let b:undo_ftplugin .= ' | setl et< ts< sts< sw<' +endif if has('comments') setlocal comments=:///,://!,:// setlocal commentstring=//\ %s + let b:undo_ftplugin .= ' | setl com< cms<' endif if has('find_in_path') let &l:includeexpr='substitute(v:fname, "^([^.])$", "\1.zig", "")' let &l:include='\v(\@import>|\@cInclude>|^\s*\#\s*include)' -endif - -let &l:define='\v(<fn>|<const>|<var>|^\s*\#\s*define)' - -" Safety check: don't execute zig from current directory -if !exists('g:zig_std_dir') && exists('*json_decode') && - \ executable('zig') && dist#vim#IsSafeExecutable('zig', 'zig') - silent let s:env = system('zig env') - if v:shell_error == 0 - let g:zig_std_dir = json_decode(s:env)['std_dir'] - endif - unlet! s:env + let b:undo_ftplugin .= ' | setl inex< inc<' endif if exists('g:zig_std_dir') - let &l:path = g:zig_std_dir . ',' . &l:path + let &l:path .= ',' . g:zig_std_dir + let b:undo_ftplugin .= ' | setl pa<' endif -let b:undo_ftplugin = - \ 'setl isk< et< ts< sts< sw< fo< sua< mp< com< cms< inex< inc< pa<' - -augroup vim-zig - autocmd! * <buffer> - autocmd BufWritePre <buffer> if get(g:, 'zig_fmt_autosave', 1) | call zig#fmt#Format() | endif -augroup END - -let b:undo_ftplugin .= '|au! vim-zig * <buffer>' - -let &cpo = s:cpo_orig -unlet s:cpo_orig +let &cpo = s:cpo_save +unlet s:cpo_save " vim: tabstop=8 shiftwidth=4 softtabstop=4 expandtab diff --git a/runtime/ftplugin/zsh.vim b/runtime/ftplugin/zsh.vim index 40986fccbe..aee890024f 100644 --- a/runtime/ftplugin/zsh.vim +++ b/runtime/ftplugin/zsh.vim @@ -2,7 +2,7 @@ " Language: Zsh shell script " Maintainer: Christian Brabandt <cb@256bit.org> " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2023-10-07 +" Latest Revision: 2024 May 06 by Vim Project (MANPAGER=) " License: Vim (see :h license) " Repository: https://github.com/chrisbra/vim-zsh @@ -24,7 +24,7 @@ if executable('zsh') && &shell !~# '/\%(nologin\|false\)$' elseif has('terminal') command! -buffer -nargs=1 ZshKeywordPrg silent exe ':term zsh -c "autoload -Uz run-help; run-help <args>"' else - command! -buffer -nargs=1 ZshKeywordPrg echo system('zsh -c "autoload -Uz run-help; run-help <args> 2>/dev/null"') + command! -buffer -nargs=1 ZshKeywordPrg echo system('MANPAGER= zsh -c "autoload -Uz run-help; run-help <args> 2>/dev/null"') endif if !exists('current_compiler') compiler zsh diff --git a/runtime/indent/arduino.vim b/runtime/indent/arduino.vim new file mode 100644 index 0000000000..88717ac00d --- /dev/null +++ b/runtime/indent/arduino.vim @@ -0,0 +1,16 @@ +" Vim indent file +" Language: Arduino +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Ken Takata <https://github.com/k-takata> +" Last Change: 2024 Apr 03 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" Use C indenting. +setlocal cindent + +let b:undo_indent = "setl cin<" diff --git a/runtime/indent/asm.vim b/runtime/indent/asm.vim new file mode 100644 index 0000000000..054612b9d6 --- /dev/null +++ b/runtime/indent/asm.vim @@ -0,0 +1,29 @@ +" Vim indent file +" Language: asm +" Maintainer: Philip Jones <philj56@gmail.com> +" Upstream: https://github.com/philj56/vim-asm-indent +" Last Change: 2017-Jul-01 +" 2024 Apr 25 by Vim Project (undo_indent) + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=s:getAsmIndent() +setlocal indentkeys=<:>,!^F,o,O + +let b:undo_indent = "setlocal indentexpr< indentkeys<" + +function! s:getAsmIndent() + let line = getline(v:lnum) + let ind = shiftwidth() + + " If the line is a label (starts with ':' terminated keyword), + " then don't indent + if line =~ '^\s*\k\+:' + let ind = 0 + endif + + return ind +endfunction diff --git a/runtime/indent/astro.vim b/runtime/indent/astro.vim new file mode 100644 index 0000000000..25a2aa0c29 --- /dev/null +++ b/runtime/indent/astro.vim @@ -0,0 +1,88 @@ +" Vim indent file (experimental). +" Language: Astro +" Author: Wuelner Martínez <wuelner.martinez@outlook.com> +" Maintainer: Wuelner Martínez <wuelner.martinez@outlook.com> +" URL: https://github.com/wuelnerdotexe/vim-astro +" Last Change: 2022 Aug 07 +" Based On: Evan Lecklider's vim-svelte +" Changes: See https://github.com/evanleck/vim-svelte +" Credits: See vim-svelte on github + +" Only load this indent file when no other was loaded yet. +if exists('b:did_indent') + finish +endif + +let b:html_indent_script1 = 'inc' +let b:html_indent_style1 = 'inc' + +" Embedded HTML indent. +runtime! indent/html.vim +let s:html_indent = &l:indentexpr +unlet b:did_indent + +let b:did_indent = 1 + +setlocal indentexpr=GetAstroIndent() +setlocal indentkeys=<>>,/,0{,{,},0},0),0],0\,<<>,,!^F,*<Return>,o,O,e,; + +let b:undo_indent = 'setl inde< indk<' + +" Only define the function once. +if exists('*GetAstroIndent') + finish +endif + +let s:cpoptions_save = &cpoptions +setlocal cpoptions&vim + +function! GetAstroIndent() + let l:current_line_number = v:lnum + + if l:current_line_number == 0 + return 0 + endif + + let l:current_line = getline(l:current_line_number) + + if l:current_line =~ '^\s*</\?\(script\|style\)' + return 0 + endif + + let l:previous_line_number = prevnonblank(l:current_line_number - 1) + let l:previous_line = getline(l:previous_line_number) + let l:previous_line_indent = indent(l:previous_line_number) + + if l:previous_line =~ '^\s*</\?\(script\|style\)' + return l:previous_line_indent + shiftwidth() + endif + + execute 'let l:indent = ' . s:html_indent + + if searchpair('<style>', '', '</style>', 'bW') && + \ l:previous_line =~ ';$' && l:current_line !~ '}' + return l:previous_line_indent + endif + + if synID(l:previous_line_number, match( + \ l:previous_line, '\S' + \ ) + 1, 0) == hlID('htmlTag') && synID(l:current_line_number, match( + \ l:current_line, '\S' + \ ) + 1, 0) != hlID('htmlEndTag') + let l:indents_match = l:indent == l:previous_line_indent + let l:previous_closes = l:previous_line =~ '/>$' + + if l:indents_match && + \ !l:previous_closes && l:previous_line =~ '<\(\u\|\l\+:\l\+\)' + return l:previous_line_indent + shiftwidth() + elseif !l:indents_match && l:previous_closes + return l:previous_line_indent + endif + endif + + return l:indent +endfunction + +let &cpoptions = s:cpoptions_save +unlet s:cpoptions_save +" vim: ts=8 diff --git a/runtime/indent/json5.vim b/runtime/indent/json5.vim new file mode 100644 index 0000000000..5977a4b912 --- /dev/null +++ b/runtime/indent/json5.vim @@ -0,0 +1,11 @@ +" Vim indent file +" Language: JSON5 +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2024-03-26 + +if exists("b:did_indent") + finish +endif + +" Same as jsonc indenting for now +runtime! indent/jsonc.vim diff --git a/runtime/indent/stylus.vim b/runtime/indent/stylus.vim new file mode 100644 index 0000000000..89634f0914 --- /dev/null +++ b/runtime/indent/stylus.vim @@ -0,0 +1,121 @@ +" Vim indent file +" Language: Stylus +" Maintainer: Marc Harter +" Last Change: 2010 May 21 +" Based On: sass.vim from Tim Pope +" +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetStylusIndent() +setlocal indentkeys=o,O,*<Return>,},],0),!^F +let b:undo_indent = "setl indentexpr< indentkeys<" + +if exists("*GetStylusIndent") " only define once + finish +endif + +function s:prevnonblanknoncomment(lnum) + let lnum = a:lnum + while lnum > 1 + let lnum = prevnonblank(lnum) + let line = getline(lnum) + if line =~ '\*/' + while lnum > 1 && line !~ '/\*' + let lnum -= 1 + endwhile + if line =~ '^\s*/\*' + let lnum -= 1 + else + break + endif + else + break + endif + endwhile + return lnum +endfunction + +function s:count_braces(lnum, count_open) + let n_open = 0 + let n_close = 0 + let line = getline(a:lnum) + let pattern = '[{}]' + let i = match(line, pattern) + while i != -1 + if synIDattr(synID(a:lnum, i + 1, 0), 'name') !~ 'css\%(Comment\|StringQ\{1,2}\)' + if line[i] == '{' + let n_open += 1 + elseif line[i] == '}' + if n_open > 0 + let n_open -= 1 + else + let n_close += 1 + endif + endif + endif + let i = match(line, pattern, i + 1) + endwhile + return a:count_open ? n_open : n_close +endfunction + +" function CheckCSSIndent() +" let line = getline(v:lnum) +" if line =~ '^\s*\*' +" return cindent(v:lnum) +" endif +" +" let pnum = s:prevnonblanknoncomment(v:lnum - 1) +" if pnum == 0 +" return 0 +" endif + +function! GetStylusIndent() + let line = getline(v:lnum) + if line =~ '^\s*\*' + return cindent(v:lnum) + endif + + let pnum = s:prevnonblanknoncomment(v:lnum - 1) + if pnum == 0 + return 0 + endif + + let lnum = prevnonblank(v:lnum-1) + if lnum == 0 + return 0 + endif + + let pline = getline(pnum) + + if pline =~ '[}{]' + return indent(pnum) + s:count_braces(pnum, 1) * &sw - s:count_braces(v:lnum, 0) * &sw + endif + + let line = substitute(getline(lnum),'[\s()]\+$','','') " get last line strip ending whitespace + let cline = substitute(substitute(getline(v:lnum),'\s\+$','',''),'^\s\+','','') " get current line, trimmed + let lastcol = strlen(line) " get last col in prev line + let line = substitute(line,'^\s\+','','') " then remove preceeding whitespace + let indent = indent(lnum) " get indent on prev line + let cindent = indent(v:lnum) " get indent on current line + let increase = indent + &sw " increase indent by the shift width + if indent == indent(lnum) + let indent = cindent <= indent ? indent : increase + endif + + let group = synIDattr(synID(lnum,lastcol,1),'name') + + " if group !~? 'css.*' && line =~? ')\s*$' " match user functions + " return increase + if group =~? '\v^%(cssTagName|cssClassName|cssIdentifier|cssSelectorOp|cssSelectorOp2|cssBraces|cssAttributeSelector|cssPseudo|stylusId|stylusClass)$' + return increase + elseif (group == 'stylusUserFunction') && (indent(lnum) == '0') " mixin definition + return increase + else + return indent + endif +endfunction + +" vim:set sw=2; diff --git a/runtime/lua/_vim9script.lua b/runtime/lua/_vim9script.lua index ca0e913d51..23c078cb87 100644 --- a/runtime/lua/_vim9script.lua +++ b/runtime/lua/_vim9script.lua @@ -82,7 +82,7 @@ local vim9 = (function() end M.index = function(obj, idx) - if vim.tbl_islist(obj) then + if vim.islist(obj) then if idx < 0 then return obj[#obj + idx + 1] else @@ -127,7 +127,7 @@ local vim9 = (function() assert(type(finish) == 'number') local slicer - if vim.tbl_islist(obj) then + if vim.islist(obj) then slicer = vim.list_slice elseif type(obj) == 'string' then slicer = string.sub @@ -168,7 +168,7 @@ local vim9 = (function() end M.iter = function(expr) - if vim.tbl_islist(expr) then + if vim.islist(expr) then return ipairs(expr) else return pairs(expr) @@ -234,7 +234,7 @@ vim9['convert'] = (function() elseif type(val) == 'table' then if vim.tbl_isempty(val) then return vim.empty_dict() - elseif vim.tbl_islist(val) then + elseif vim.islist(val) then error(string.format('Cannot pass list to dictionary? %s', vim.inspect(val))) else return val @@ -280,7 +280,7 @@ vim9['fn'] = (function() error("haven't written this code yet") end - if vim.tbl_islist(right) then + if vim.islist(right) then vim.list_extend(left, right) return left else diff --git a/runtime/lua/editorconfig.lua b/runtime/lua/editorconfig.lua index 49d63807a6..dcd7425c29 100644 --- a/runtime/lua/editorconfig.lua +++ b/runtime/lua/editorconfig.lua @@ -1,31 +1,79 @@ -local M = {} +--- @brief +--- Nvim supports EditorConfig. When a file is opened, after running |ftplugin|s +--- and |FileType| autocommands, Nvim searches all parent directories of that file +--- for ".editorconfig" files, parses them, and applies any properties that match +--- the opened file. Think of it like 'modeline' for an entire (recursive) +--- directory. For more information see https://editorconfig.org/. + +--- @brief [g:editorconfig]() [b:editorconfig]() +--- +--- EditorConfig is enabled by default. To disable it, add to your config: +--- ```lua +--- vim.g.editorconfig = false +--- ``` +--- +--- (Vimscript: `let g:editorconfig = v:false`). It can also be disabled +--- per-buffer by setting the [b:editorconfig] buffer-local variable to `false`. +--- +--- Nvim stores the applied properties in [b:editorconfig] if it is not `false`. + +--- @brief [editorconfig-custom-properties]() +--- +--- New properties can be added by adding a new entry to the "properties" table. +--- The table key is a property name and the value is a callback function which +--- accepts the number of the buffer to be modified, the value of the property +--- in the `.editorconfig` file, and (optionally) a table containing all of the +--- other properties and their values (useful for properties which depend on other +--- properties). The value is always a string and must be coerced if necessary. +--- Example: +--- +--- ```lua +--- +--- require('editorconfig').properties.foo = function(bufnr, val, opts) +--- if opts.charset and opts.charset ~= "utf-8" then +--- error("foo can only be set when charset is utf-8", 0) +--- end +--- vim.b[bufnr].foo = val +--- end +--- +--- ``` + +--- @brief [editorconfig-properties]() +--- +--- The following properties are supported by default: --- @type table<string,fun(bufnr: integer, val: string, opts?: table)> -M.properties = {} +local properties = {} +--- @private --- Modified version of the builtin assert that does not include error position information --- ----@param v any Condition ----@param message string Error message to display if condition is false or nil ----@return any v if not false or nil, otherwise an error is displayed ---- ----@private +--- @param v any Condition +--- @param message string Error message to display if condition is false or nil +--- @return any v if not false or nil, otherwise an error is displayed local function assert(v, message) return v or error(message, 0) end +--- @private --- Show a warning message ---- ----@param msg string Message to show ---- ----@private +--- @param msg string Message to show local function warn(msg, ...) - vim.notify_once(string.format(msg, ...), vim.log.levels.WARN, { + vim.notify_once(msg:format(...), vim.log.levels.WARN, { title = 'editorconfig', }) end -function M.properties.charset(bufnr, val) +--- If "true", then stop searching for `.editorconfig` files in parent +--- directories. This property must be at the top-level of the +--- `.editorconfig` file (i.e. it must not be within a glob section). +function properties.root() + -- Unused +end + +--- One of `"utf-8"`, `"utf-8-bom"`, `"latin1"`, `"utf-16be"`, or `"utf-16le"`. +--- Sets the 'fileencoding' and 'bomb' options. +function properties.charset(bufnr, val) assert( vim.list_contains({ 'utf-8', 'utf-8-bom', 'latin1', 'utf-16be', 'utf-16le' }, val), 'charset must be one of "utf-8", "utf-8-bom", "latin1", "utf-16be", or "utf-16le"' @@ -40,14 +88,18 @@ function M.properties.charset(bufnr, val) end end -function M.properties.end_of_line(bufnr, val) +--- One of `"lf"`, `"crlf"`, or `"cr"`. +--- These correspond to setting 'fileformat' to "unix", "dos", or "mac", +--- respectively. +function properties.end_of_line(bufnr, val) vim.bo[bufnr].fileformat = assert( ({ lf = 'unix', crlf = 'dos', cr = 'mac' })[val], 'end_of_line must be one of "lf", "crlf", or "cr"' ) end -function M.properties.indent_style(bufnr, val, opts) +--- One of `"tab"` or `"space"`. Sets the 'expandtab' option. +function properties.indent_style(bufnr, val, opts) assert(val == 'tab' or val == 'space', 'indent_style must be either "tab" or "space"') vim.bo[bufnr].expandtab = val == 'space' if val == 'tab' and not opts.indent_size then @@ -56,7 +108,11 @@ function M.properties.indent_style(bufnr, val, opts) end end -function M.properties.indent_size(bufnr, val, opts) +--- A number indicating the size of a single indent. Alternatively, use the +--- value "tab" to use the value of the tab_width property. Sets the +--- 'shiftwidth' and 'softtabstop' options. If this value is not "tab" and +--- the tab_width property is not set, 'tabstop' is also set to this value. +function properties.indent_size(bufnr, val, opts) if val == 'tab' then vim.bo[bufnr].shiftwidth = 0 vim.bo[bufnr].softtabstop = 0 @@ -70,11 +126,14 @@ function M.properties.indent_size(bufnr, val, opts) end end -function M.properties.tab_width(bufnr, val) +--- The display size of a single tab character. Sets the 'tabstop' option. +function properties.tab_width(bufnr, val) vim.bo[bufnr].tabstop = assert(tonumber(val), 'tab_width must be a number') end -function M.properties.max_line_length(bufnr, val) +--- A number indicating the maximum length of a single +--- line. Sets the 'textwidth' option. +function properties.max_line_length(bufnr, val) local n = tonumber(val) if n then vim.bo[bufnr].textwidth = n @@ -84,7 +143,8 @@ function M.properties.max_line_length(bufnr, val) end end -function M.properties.trim_trailing_whitespace(bufnr, val) +--- When `"true"`, trailing whitespace is automatically removed when the buffer is written. +function properties.trim_trailing_whitespace(bufnr, val) assert( val == 'true' or val == 'false', 'trim_trailing_whitespace must be either "true" or "false"' @@ -109,7 +169,9 @@ function M.properties.trim_trailing_whitespace(bufnr, val) end end -function M.properties.insert_final_newline(bufnr, val) +--- `"true"` or `"false"` to ensure the file always has a trailing newline as its last byte. +--- Sets the 'fixendofline' and 'endofline' options. +function properties.insert_final_newline(bufnr, val) assert(val == 'true' or val == 'false', 'insert_final_newline must be either "true" or "false"') vim.bo[bufnr].fixendofline = val == 'true' @@ -128,63 +190,56 @@ function M.properties.insert_final_newline(bufnr, val) end end ---- Modified version of |glob2regpat()| that does not match path separators on *. +--- @private +--- Modified version of [glob2regpat()] that does not match path separators on `*`. --- ---- This function replaces single instances of * with the regex pattern [^/]*. However, the star in ---- the replacement pattern also gets interpreted by glob2regpat, so we insert a placeholder, pass ---- it through glob2regpat, then replace the placeholder with the actual regex pattern. +--- This function replaces single instances of `*` with the regex pattern `[^/]*`. +--- However, the star in the replacement pattern also gets interpreted by glob2regpat, +--- so we insert a placeholder, pass it through glob2regpat, then replace the +--- placeholder with the actual regex pattern. --- ----@param glob string Glob to convert into a regular expression ----@return string Regular expression ---- ----@private +--- @param glob string Glob to convert into a regular expression +--- @return string regex Regular expression local function glob2regpat(glob) local placeholder = '@@PLACEHOLDER@@' - return ( - string.gsub( - vim.fn.glob2regpat( - vim.fn.substitute( - string.gsub(glob, '{(%d+)%.%.(%d+)}', '[%1-%2]'), - '\\*\\@<!\\*\\*\\@!', - placeholder, - 'g' - ) - ), - placeholder, - '[^/]*' - ) + local glob1 = vim.fn.substitute( + glob:gsub('{(%d+)%.%.(%d+)}', '[%1-%2]'), + '\\*\\@<!\\*\\*\\@!', + placeholder, + 'g' ) + local regpat = vim.fn.glob2regpat(glob1) + return (regpat:gsub(placeholder, '[^/]*')) end +--- @private --- Parse a single line in an EditorConfig file ---- ----@param line string Line ----@return string|nil If the line contains a pattern, the glob pattern ----@return string|nil If the line contains a key-value pair, the key ----@return string|nil If the line contains a key-value pair, the value ---- ----@private +--- @param line string Line +--- @return string? glob pattern if the line contains a pattern +--- @return string? key if the line contains a key-value pair +--- @return string? value if the line contains a key-value pair local function parse_line(line) - if line:find('^%s*[^ #;]') then - local glob = (line:match('%b[]') or ''):match('^%s*%[(.*)%]%s*$') - if glob then - return glob, nil, nil - end + if not line:find('^%s*[^ #;]') then + return + end - local key, val = line:match('^%s*([^:= ][^:=]-)%s*[:=]%s*(.-)%s*$') - if key ~= nil and val ~= nil then - return nil, key:lower(), val:lower() - end + --- @type string? + local glob = (line:match('%b[]') or ''):match('^%s*%[(.*)%]%s*$') + if glob then + return glob + end + + local key, val = line:match('^%s*([^:= ][^:=]-)%s*[:=]%s*(.-)%s*$') + if key ~= nil and val ~= nil then + return nil, key:lower(), val:lower() end end ---- Parse options from an .editorconfig file ---- ----@param filepath string File path of the file to apply EditorConfig settings to ----@param dir string Current directory ----@return table<string,string|boolean> Table of options to apply to the given file ---- ----@private +--- @private +--- Parse options from an `.editorconfig` file +--- @param filepath string File path of the file to apply EditorConfig settings to +--- @param dir string Current directory +--- @return table<string,string|boolean> Table of options to apply to the given file local function parse(filepath, dir) local pat --- @type vim.regex? local opts = {} --- @type table<string,string|boolean> @@ -215,11 +270,14 @@ local function parse(filepath, dir) return opts end ---- Configure the given buffer with options from an .editorconfig file ---- ----@param bufnr integer Buffer number to configure ---- ----@private +local M = {} + +-- Exposed for use in syntax/editorconfig.vim` +M.properties = properties + +--- @private +--- Configure the given buffer with options from an `.editorconfig` file +--- @param bufnr integer Buffer number to configure function M.config(bufnr) bufnr = bufnr or vim.api.nvim_get_current_buf() if not vim.api.nvim_buf_is_valid(bufnr) then @@ -249,6 +307,7 @@ function M.config(bufnr) if val ~= 'unset' then local func = M.properties[opt] if func then + --- @type boolean, string? local ok, err = pcall(func, bufnr, val, opts) if ok then applied[opt] = val diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua index ac15aff60c..02e841030f 100644 --- a/runtime/lua/man.lua +++ b/runtime/lua/man.lua @@ -411,15 +411,13 @@ local function find_man() return false end ----@param pager boolean -local function set_options(pager) +local function set_options() vim.bo.swapfile = false vim.bo.buftype = 'nofile' vim.bo.bufhidden = 'unload' vim.bo.modified = false vim.bo.readonly = true vim.bo.modifiable = false - vim.b.pager = pager vim.bo.filetype = 'man' end @@ -475,7 +473,7 @@ local function put_page(page) vim.cmd([[silent! keeppatterns keepjumps %s/\s\{199,}/\=repeat(' ', 10)/g]]) vim.cmd('1') -- Move cursor to first line highlight_man_page() - set_options(false) + set_options() end local function format_candidate(path, psect) @@ -662,7 +660,8 @@ function M.init_pager() vim.cmd.file({ 'man://' .. fn.fnameescape(ref):lower(), mods = { silent = true } }) end - set_options(true) + vim.g.pager = true + set_options() end ---@param count integer @@ -716,11 +715,11 @@ function M.open_page(count, smods, args) local target = ('%s(%s)'):format(name, sect) local ok, ret = pcall(function() - if smods.tab == -1 and find_man() then - vim.cmd.tag({ target, mods = { silent = true, keepalt = true } }) + smods.silent = true + smods.keepalt = true + if smods.hide or (smods.tab == -1 and find_man()) then + vim.cmd.tag({ target, mods = smods }) else - smods.silent = true - smods.keepalt = true vim.cmd.stag({ target, mods = smods }) end end) @@ -730,7 +729,7 @@ function M.open_page(count, smods, args) if not ok then error(ret) else - set_options(false) + set_options() end vim.b.man_sect = sect diff --git a/runtime/lua/provider/clipboard/health.lua b/runtime/lua/provider/clipboard/health.lua deleted file mode 100644 index dc33cb0ab0..0000000000 --- a/runtime/lua/provider/clipboard/health.lua +++ /dev/null @@ -1,40 +0,0 @@ -local health = vim.health -local executable = health.executable - -local M = {} - -function M.check() - health.start('Clipboard (optional)') - - if - os.getenv('TMUX') - and executable('tmux') - and executable('pbpaste') - and not health.cmd_ok('pbpaste') - then - local tmux_version = string.match(vim.fn.system('tmux -V'), '%d+%.%d+') - local advice = { - 'Install tmux 2.6+. https://superuser.com/q/231130', - 'or use tmux with reattach-to-user-namespace. https://superuser.com/a/413233', - } - health.error('pbcopy does not work with tmux version: ' .. tmux_version, advice) - end - - local clipboard_tool = vim.fn['provider#clipboard#Executable']() - if vim.g.clipboard ~= nil and clipboard_tool == '' then - local error_message = vim.fn['provider#clipboard#Error']() - health.error( - error_message, - "Use the example in :help g:clipboard as a template, or don't set g:clipboard at all." - ) - elseif clipboard_tool:find('^%s*$') then - health.warn( - 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.', - ':help clipboard' - ) - else - health.ok('Clipboard tool found: ' .. clipboard_tool) - end -end - -return M diff --git a/runtime/lua/provider/node/health.lua b/runtime/lua/provider/node/health.lua deleted file mode 100644 index a434f8a92b..0000000000 --- a/runtime/lua/provider/node/health.lua +++ /dev/null @@ -1,105 +0,0 @@ -local health = vim.health -local executable = health.executable -local iswin = vim.loop.os_uname().sysname == 'Windows_NT' - -local M = {} - -function M.check() - health.start('Node.js provider (optional)') - - if health.provider_disabled('node') then - return - end - - if - not executable('node') - or (not executable('npm') and not executable('yarn') and not executable('pnpm')) - then - health.warn( - '`node` and `npm` (or `yarn`, `pnpm`) must be in $PATH.', - 'Install Node.js and verify that `node` and `npm` (or `yarn`, `pnpm`) commands work.' - ) - return - end - - -- local node_v = vim.fn.split(system({'node', '-v'}), "\n")[1] or '' - local ok, node_v = health.cmd_ok({ 'node', '-v' }) - health.info('Node.js: ' .. node_v) - if not ok or vim.version.lt(node_v, '6.0.0') then - health.warn('Nvim node.js host does not support Node ' .. node_v) - -- Skip further checks, they are nonsense if nodejs is too old. - return - end - if vim.fn['provider#node#can_inspect']() == 0 then - health.warn( - 'node.js on this system does not support --inspect-brk so $NVIM_NODE_HOST_DEBUG is ignored.' - ) - end - - local node_detect_table = vim.fn['provider#node#Detect']() - local host = node_detect_table[1] - if host:find('^%s*$') then - health.warn('Missing "neovim" npm (or yarn, pnpm) package.', { - 'Run in shell: npm install -g neovim', - 'Run in shell (if you use yarn): yarn global add neovim', - 'Run in shell (if you use pnpm): pnpm install -g neovim', - 'You may disable this provider (and warning) by adding `let g:loaded_node_provider = 0` to your init.vim', - }) - return - end - health.info('Nvim node.js host: ' .. host) - - local manager = 'npm' - if executable('yarn') then - manager = 'yarn' - elseif executable('pnpm') then - manager = 'pnpm' - end - - local latest_npm_cmd = ( - iswin and 'cmd /c ' .. manager .. ' info neovim --json' or manager .. ' info neovim --json' - ) - local latest_npm - ok, latest_npm = health.cmd_ok(vim.split(latest_npm_cmd, ' ')) - if not ok or latest_npm:find('^%s$') then - health.error( - 'Failed to run: ' .. latest_npm_cmd, - { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' } - ) - return - end - - local pcall_ok, pkg_data = pcall(vim.json.decode, latest_npm) - if not pcall_ok then - return 'error: ' .. latest_npm - end - local latest_npm_subtable = pkg_data['dist-tags'] or {} - latest_npm = latest_npm_subtable['latest'] or 'unable to parse' - - local current_npm_cmd = { 'node', host, '--version' } - local current_npm - ok, current_npm = health.cmd_ok(current_npm_cmd) - if not ok then - health.error( - 'Failed to run: ' .. table.concat(current_npm_cmd, ' '), - { 'Report this issue with the output of: ', table.concat(current_npm_cmd, ' ') } - ) - return - end - - if latest_npm ~= 'unable to parse' and vim.version.lt(current_npm, latest_npm) then - local message = 'Package "neovim" is out-of-date. Installed: ' - .. current_npm - .. ' latest: ' - .. latest_npm - health.warn(message({ - 'Run in shell: npm install -g neovim', - 'Run in shell (if you use yarn): yarn global add neovim', - 'Run in shell (if you use pnpm): pnpm install -g neovim', - })) - else - health.ok('Latest "neovim" npm/yarn/pnpm package is installed: ' .. current_npm) - end -end - -return M diff --git a/runtime/lua/provider/perl/health.lua b/runtime/lua/provider/perl/health.lua deleted file mode 100644 index 535093d793..0000000000 --- a/runtime/lua/provider/perl/health.lua +++ /dev/null @@ -1,90 +0,0 @@ -local health = vim.health - -local M = {} - -function M.check() - health.start('Perl provider (optional)') - - if health.provider_disabled('perl') then - return - end - - local perl_exec, perl_warnings = vim.provider.perl.detect() - - if not perl_exec then - health.warn(assert(perl_warnings), { - 'See :help provider-perl for more information.', - 'You may disable this provider (and warning) by adding `let g:loaded_perl_provider = 0` to your init.vim', - }) - health.warn('No usable perl executable found') - return - end - - health.info('perl executable: ' .. perl_exec) - - -- we cannot use cpanm that is on the path, as it may not be for the perl - -- set with g:perl_host_prog - local ok = health.cmd_ok({ perl_exec, '-W', '-MApp::cpanminus', '-e', '' }) - if not ok then - return { perl_exec, '"App::cpanminus" module is not installed' } - end - - local latest_cpan_cmd = { - perl_exec, - '-MApp::cpanminus::fatscript', - '-e', - 'my $app = App::cpanminus::script->new; $app->parse_options ("--info", "-q", "Neovim::Ext"); exit $app->doit', - } - local latest_cpan - ok, latest_cpan = health.cmd_ok(latest_cpan_cmd) - if not ok or latest_cpan:find('^%s*$') then - health.error( - 'Failed to run: ' .. table.concat(latest_cpan_cmd, ' '), - { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' } - ) - return - elseif latest_cpan[1] == '!' then - local cpanm_errs = vim.split(latest_cpan, '!') - if cpanm_errs[1]:find("Can't write to ") then - local advice = {} - for i = 2, #cpanm_errs do - advice[#advice + 1] = cpanm_errs[i] - end - - health.warn(cpanm_errs[1], advice) - -- Last line is the package info - latest_cpan = cpanm_errs[#cpanm_errs] - else - health.error('Unknown warning from command: ' .. latest_cpan_cmd, cpanm_errs) - return - end - end - latest_cpan = vim.fn.matchstr(latest_cpan, [[\(\.\?\d\)\+]]) - if latest_cpan:find('^%s*$') then - health.error('Cannot parse version number from cpanm output: ' .. latest_cpan) - return - end - - local current_cpan_cmd = { perl_exec, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION' } - local current_cpan - ok, current_cpan = health.cmd_ok(current_cpan_cmd) - if not ok then - health.error( - 'Failed to run: ' .. table.concat(current_cpan_cmd, ' '), - { 'Report this issue with the output of: ', table.concat(current_cpan_cmd, ' ') } - ) - return - end - - if vim.version.lt(current_cpan, latest_cpan) then - local message = 'Module "Neovim::Ext" is out-of-date. Installed: ' - .. current_cpan - .. ', latest: ' - .. latest_cpan - health.warn(message, 'Run in shell: cpanm -n Neovim::Ext') - else - health.ok('Latest "Neovim::Ext" cpan module is installed: ' .. current_cpan) - end -end - -return M diff --git a/runtime/lua/provider/ruby/health.lua b/runtime/lua/provider/ruby/health.lua deleted file mode 100644 index 04f6e303e6..0000000000 --- a/runtime/lua/provider/ruby/health.lua +++ /dev/null @@ -1,71 +0,0 @@ -local health = vim.health -local executable = health.executable -local iswin = vim.loop.os_uname().sysname == 'Windows_NT' - -local M = {} - -function M.check() - health.start('Ruby provider (optional)') - - if health.provider_disabled('ruby') then - return - end - - if not executable('ruby') or not executable('gem') then - health.warn( - '`ruby` and `gem` must be in $PATH.', - 'Install Ruby and verify that `ruby` and `gem` commands work.' - ) - return - end - health.info('Ruby: ' .. health.system({ 'ruby', '-v' })) - - local ruby_detect_table = vim.provider.ruby.detect() - local host = ruby_detect_table[1] - if (not host) or host:find('^%s*$') then - health.warn('`neovim-ruby-host` not found.', { - 'Run `gem install neovim` to ensure the neovim RubyGem is installed.', - 'Run `gem environment` to ensure the gem bin directory is in $PATH.', - 'If you are using rvm/rbenv/chruby, try "rehashing".', - 'See :help g:ruby_host_prog for non-standard gem installations.', - 'You may disable this provider (and warning) by adding `let g:loaded_ruby_provider = 0` to your init.vim', - }) - return - end - health.info('Host: ' .. host) - - local latest_gem_cmd = (iswin and 'cmd /c gem list -ra "^^neovim$"' or 'gem list -ra ^neovim$') - local ok, latest_gem = health.cmd_ok(vim.split(latest_gem_cmd, ' ')) - if not ok or latest_gem:find('^%s*$') then - health.error( - 'Failed to run: ' .. latest_gem_cmd, - { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' } - ) - return - end - local gem_split = vim.split(latest_gem, [[neovim (\|, \|)$]]) - latest_gem = gem_split[1] or 'not found' - - local current_gem_cmd = { host, '--version' } - local current_gem - ok, current_gem = health.cmd_ok(current_gem_cmd) - if not ok then - health.error( - 'Failed to run: ' .. table.concat(current_gem_cmd, ' '), - { 'Report this issue with the output of: ', table.concat(current_gem_cmd, ' ') } - ) - return - end - - if vim.version.lt(current_gem, latest_gem) then - local message = 'Gem "neovim" is out-of-date. Installed: ' - .. current_gem - .. ', latest: ' - .. latest_gem - health.warn(message, 'Run in shell: gem update neovim') - else - health.ok('Latest "neovim" gem is installed: ' .. current_gem) - end -end - -return M diff --git a/runtime/lua/vim/_comment.lua b/runtime/lua/vim/_comment.lua new file mode 100644 index 0000000000..044cd69716 --- /dev/null +++ b/runtime/lua/vim/_comment.lua @@ -0,0 +1,263 @@ +---@nodoc +---@class vim._comment.Parts +---@field left string Left part of comment +---@field right string Right part of comment + +--- Get 'commentstring' at cursor +---@param ref_position integer[] +---@return string +local function get_commentstring(ref_position) + local buf_cs = vim.bo.commentstring + + local has_ts_parser, ts_parser = pcall(vim.treesitter.get_parser) + if not has_ts_parser then + return buf_cs + end + + -- Try to get 'commentstring' associated with local tree-sitter language. + -- This is useful for injected languages (like markdown with code blocks). + local row, col = ref_position[1] - 1, ref_position[2] + local ref_range = { row, col, row, col + 1 } + + -- - Get 'commentstring' from the deepest LanguageTree which both contains + -- reference range and has valid 'commentstring' (meaning it has at least + -- one associated 'filetype' with valid 'commentstring'). + -- In simple cases using `parser:language_for_range()` would be enough, but + -- it fails for languages without valid 'commentstring' (like 'comment'). + local ts_cs, res_level = nil, 0 + + ---@param lang_tree vim.treesitter.LanguageTree + local function traverse(lang_tree, level) + if not lang_tree:contains(ref_range) then + return + end + + local lang = lang_tree:lang() + local filetypes = vim.treesitter.language.get_filetypes(lang) + for _, ft in ipairs(filetypes) do + local cur_cs = vim.filetype.get_option(ft, 'commentstring') + if cur_cs ~= '' and level > res_level then + ts_cs = cur_cs + end + end + + for _, child_lang_tree in pairs(lang_tree:children()) do + traverse(child_lang_tree, level + 1) + end + end + traverse(ts_parser, 1) + + return ts_cs or buf_cs +end + +--- Compute comment parts from 'commentstring' +---@param ref_position integer[] +---@return vim._comment.Parts +local function get_comment_parts(ref_position) + local cs = get_commentstring(ref_position) + + if cs == nil or cs == '' then + vim.api.nvim_echo({ { "Option 'commentstring' is empty.", 'WarningMsg' } }, true, {}) + return { left = '', right = '' } + end + + if not (type(cs) == 'string' and cs:find('%%s') ~= nil) then + error(vim.inspect(cs) .. " is not a valid 'commentstring'.") + end + + -- Structure of 'commentstring': <left part> <%s> <right part> + local left, right = cs:match('^(.-)%%s(.-)$') + return { left = left, right = right } +end + +--- Make a function that checks if a line is commented +---@param parts vim._comment.Parts +---@return fun(line: string): boolean +local function make_comment_check(parts) + local l_esc, r_esc = vim.pesc(parts.left), vim.pesc(parts.right) + + -- Commented line has the following structure: + -- <whitespace> <trimmed left> <anything> <trimmed right> <whitespace> + local regex = '^%s-' .. vim.trim(l_esc) .. '.*' .. vim.trim(r_esc) .. '%s-$' + + return function(line) + return line:find(regex) ~= nil + end +end + +--- Compute comment-related information about lines +---@param lines string[] +---@param parts vim._comment.Parts +---@return string indent +---@return boolean is_commented +local function get_lines_info(lines, parts) + local comment_check = make_comment_check(parts) + + local is_commented = true + local indent_width = math.huge + ---@type string + local indent + + for _, l in ipairs(lines) do + -- Update lines indent: minimum of all indents except blank lines + local _, indent_width_cur, indent_cur = l:find('^(%s*)') + + -- Ignore blank lines completely when making a decision + if indent_width_cur < l:len() then + -- NOTE: Copying actual indent instead of recreating it with `indent_width` + -- allows to handle both tabs and spaces + if indent_width_cur < indent_width then + ---@diagnostic disable-next-line:cast-local-type + indent_width, indent = indent_width_cur, indent_cur + end + + -- Update comment info: commented if every non-blank line is commented + if is_commented then + is_commented = comment_check(l) + end + end + end + + -- `indent` can still be `nil` in case all `lines` are empty + return indent or '', is_commented +end + +--- Compute whether a string is blank +---@param x string +---@return boolean is_blank +local function is_blank(x) + return x:find('^%s*$') ~= nil +end + +--- Make a function which comments a line +---@param parts vim._comment.Parts +---@param indent string +---@return fun(line: string): string +local function make_comment_function(parts, indent) + local prefix, nonindent_start, suffix = indent .. parts.left, indent:len() + 1, parts.right + local blank_comment = indent .. vim.trim(parts.left) .. vim.trim(parts.right) + + return function(line) + if is_blank(line) then + return blank_comment + end + return prefix .. line:sub(nonindent_start) .. suffix + end +end + +--- Make a function which uncomments a line +---@param parts vim._comment.Parts +---@return fun(line: string): string +local function make_uncomment_function(parts) + local l_esc, r_esc = vim.pesc(parts.left), vim.pesc(parts.right) + local regex = '^(%s*)' .. l_esc .. '(.*)' .. r_esc .. '(%s-)$' + local regex_trimmed = '^(%s*)' .. vim.trim(l_esc) .. '(.*)' .. vim.trim(r_esc) .. '(%s-)$' + + return function(line) + -- Try regex with exact comment parts first, fall back to trimmed parts + local indent, new_line, trail = line:match(regex) + if new_line == nil then + indent, new_line, trail = line:match(regex_trimmed) + end + + -- Return original if line is not commented + if new_line == nil then + return line + end + + -- Prevent trailing whitespace + if is_blank(new_line) then + indent, trail = '', '' + end + + return indent .. new_line .. trail + end +end + +--- Comment/uncomment buffer range +---@param line_start integer +---@param line_end integer +---@param ref_position? integer[] +local function toggle_lines(line_start, line_end, ref_position) + ref_position = ref_position or { line_start, 0 } + local parts = get_comment_parts(ref_position) + local lines = vim.api.nvim_buf_get_lines(0, line_start - 1, line_end, false) + local indent, is_comment = get_lines_info(lines, parts) + + local f = is_comment and make_uncomment_function(parts) or make_comment_function(parts, indent) + + -- Direct `nvim_buf_set_lines()` essentially removes both regular and + -- extended marks (squashes to empty range at either side of the region) + -- inside region. Use 'lockmarks' to preserve regular marks. + -- Preserving extmarks is not a universally good thing to do: + -- - Good for non-highlighting in text area extmarks (like showing signs). + -- - Debatable for highlighting in text area (like LSP semantic tokens). + -- Mostly because it causes flicker as highlighting is preserved during + -- comment toggling. + package.loaded['vim._comment']._lines = vim.tbl_map(f, lines) + local lua_cmd = string.format( + 'vim.api.nvim_buf_set_lines(0, %d, %d, false, package.loaded["vim._comment"]._lines)', + line_start - 1, + line_end + ) + vim.cmd.lua({ lua_cmd, mods = { lockmarks = true } }) + package.loaded['vim._comment']._lines = nil +end + +--- Operator which toggles user-supplied range of lines +---@param mode string? +---|"'line'" +---|"'char'" +---|"'block'" +local function operator(mode) + -- Used without arguments as part of expression mapping. Otherwise it is + -- called as 'operatorfunc'. + if mode == nil then + vim.o.operatorfunc = "v:lua.require'vim._comment'.operator" + return 'g@' + end + + -- Compute target range + local mark_from, mark_to = "'[", "']" + local lnum_from, col_from = vim.fn.line(mark_from), vim.fn.col(mark_from) + local lnum_to, col_to = vim.fn.line(mark_to), vim.fn.col(mark_to) + + -- Do nothing if "from" mark is after "to" (like in empty textobject) + if (lnum_from > lnum_to) or (lnum_from == lnum_to and col_from > col_to) then + return + end + + -- NOTE: use cursor position as reference for possibly computing local + -- tree-sitter-based 'commentstring'. Recompute every time for a proper + -- dot-repeat. In Visual and sometimes Normal mode it uses start position. + toggle_lines(lnum_from, lnum_to, vim.api.nvim_win_get_cursor(0)) + return '' +end + +--- Select contiguous commented lines at cursor +local function textobject() + local lnum_cur = vim.fn.line('.') + local parts = get_comment_parts({ lnum_cur, vim.fn.col('.') }) + local comment_check = make_comment_check(parts) + + if not comment_check(vim.fn.getline(lnum_cur)) then + return + end + + -- Compute commented range + local lnum_from = lnum_cur + while (lnum_from >= 2) and comment_check(vim.fn.getline(lnum_from - 1)) do + lnum_from = lnum_from - 1 + end + + local lnum_to = lnum_cur + local n_lines = vim.api.nvim_buf_line_count(0) + while (lnum_to <= n_lines - 1) and comment_check(vim.fn.getline(lnum_to + 1)) do + lnum_to = lnum_to + 1 + end + + -- Select range linewise for operator to act upon + vim.cmd('normal! ' .. lnum_from .. 'GV' .. lnum_to .. 'G') +end + +return { operator = operator, textobject = textobject, toggle_lines = toggle_lines } diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 91baee1a1e..5b964b84a0 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -1,3 +1,31 @@ +--- Default user commands +do + vim.api.nvim_create_user_command('Inspect', function(cmd) + if cmd.bang then + vim.print(vim.inspect_pos()) + else + vim.show_pos() + end + end, { desc = 'Inspect highlights and extmarks at the cursor', bang = true }) + + vim.api.nvim_create_user_command('InspectTree', function(cmd) + if cmd.mods ~= '' or cmd.count ~= 0 then + local count = cmd.count ~= 0 and cmd.count or '' + local new = cmd.mods ~= '' and 'new' or 'vnew' + + vim.treesitter.inspect_tree({ + command = ('%s %s%s'):format(cmd.mods, count, new), + }) + else + vim.treesitter.inspect_tree() + end + end, { desc = 'Inspect treesitter language tree for buffer', count = true }) + + vim.api.nvim_create_user_command('EditQuery', function(cmd) + vim.treesitter.query.edit(cmd.fargs[1]) + end, { desc = 'Edit treesitter query', nargs = '?' }) +end + --- Default mappings do --- Default maps for * and # in visual mode. @@ -50,42 +78,126 @@ do --- See |&-default| vim.keymap.set('n', '&', ':&&<CR>', { desc = ':help &-default' }) - --- Use Q in visual mode to execute a macro on each line of the selection. #21422 + --- Use Q in Visual mode to execute a macro on each line of the selection. #21422 + --- This only make sense in linewise Visual mode. #28287 --- --- Applies to @x and includes @@ too. vim.keymap.set( 'x', 'Q', - ':normal! @<C-R>=reg_recorded()<CR><CR>', - { silent = true, desc = ':help v_Q-default' } + "mode() == 'V' ? ':normal! @<C-R>=reg_recorded()<CR><CR>' : 'Q'", + { silent = true, expr = true, desc = ':help v_Q-default' } ) vim.keymap.set( 'x', '@', - "':normal! @'.getcharstr().'<CR>'", + "mode() == 'V' ? ':normal! @'.getcharstr().'<CR>' : '@'", { silent = true, expr = true, desc = ':help v_@-default' } ) - --- Map |gx| to call |vim.ui.open| on the identifier under the cursor + + --- Map |gx| to call |vim.ui.open| on the <cfile> at cursor. do local function do_open(uri) - local _, err = vim.ui.open(uri) - if err then - vim.notify(err, vim.log.levels.ERROR) + local cmd, err = vim.ui.open(uri) + local rv = cmd and cmd:wait(1000) or nil + if cmd and rv and rv.code ~= 0 then + err = ('vim.ui.open: command %s (%d): %s'):format( + (rv.code == 124 and 'timeout' or 'failed'), + rv.code, + vim.inspect(cmd.cmd) + ) end + return err end local gx_desc = 'Opens filepath or URI under cursor with the system handler (file explorer, web browser, …)' vim.keymap.set({ 'n' }, 'gx', function() - do_open(vim.fn.expand('<cfile>')) + local err = do_open(require('vim.ui')._get_url()) + if err then + vim.notify(err, vim.log.levels.ERROR) + end end, { desc = gx_desc }) vim.keymap.set({ 'x' }, 'gx', function() local lines = vim.fn.getregion(vim.fn.getpos('.'), vim.fn.getpos('v'), { type = vim.fn.mode() }) -- Trim whitespace on each line and concatenate. - do_open(table.concat(vim.iter(lines):map(vim.trim):totable())) + local err = do_open(table.concat(vim.iter(lines):map(vim.trim):totable())) + if err then + vim.notify(err, vim.log.levels.ERROR) + end end, { desc = gx_desc }) end + + --- Default maps for built-in commenting. + --- + --- See |gc-default| and |gcc-default|. + do + local operator_rhs = function() + return require('vim._comment').operator() + end + vim.keymap.set({ 'n', 'x' }, 'gc', operator_rhs, { expr = true, desc = 'Toggle comment' }) + + local line_rhs = function() + return require('vim._comment').operator() .. '_' + end + vim.keymap.set('n', 'gcc', line_rhs, { expr = true, desc = 'Toggle comment line' }) + + local textobject_rhs = function() + require('vim._comment').textobject() + end + vim.keymap.set({ 'o' }, 'gc', textobject_rhs, { desc = 'Comment textobject' }) + end + + --- Default maps for LSP functions. + --- + --- These are mapped unconditionally to avoid different behavior depending on whether an LSP + --- client is attached. If no client is attached, or if a server does not support a capability, an + --- error message is displayed rather than exhibiting different behavior. + --- + --- See |grr|, |grn|, |gra|, |i_CTRL-S|. + do + vim.keymap.set('n', 'grn', function() + vim.lsp.buf.rename() + end, { desc = 'vim.lsp.buf.rename()' }) + + vim.keymap.set({ 'n', 'x' }, 'gra', function() + vim.lsp.buf.code_action() + end, { desc = 'vim.lsp.buf.code_action()' }) + + vim.keymap.set('n', 'grr', function() + vim.lsp.buf.references() + end, { desc = 'vim.lsp.buf.references()' }) + + vim.keymap.set('i', '<C-S>', function() + vim.lsp.buf.signature_help() + end, { desc = 'vim.lsp.buf.signature_help()' }) + end + + --- Map [d and ]d to move to the previous/next diagnostic. Map <C-W>d to open a floating window + --- for the diagnostic under the cursor. + --- + --- See |[d-default|, |]d-default|, and |CTRL-W_d-default|. + do + vim.keymap.set('n', ']d', function() + vim.diagnostic.goto_next({ float = false }) + end, { desc = 'Jump to the next diagnostic' }) + + vim.keymap.set('n', '[d', function() + vim.diagnostic.goto_prev({ float = false }) + end, { desc = 'Jump to the previous diagnostic' }) + + vim.keymap.set('n', '<C-W>d', function() + vim.diagnostic.open_float() + end, { desc = 'Show diagnostics under the cursor' }) + + vim.keymap.set( + 'n', + '<C-W><C-D>', + '<C-W>d', + { remap = true, desc = 'Show diagnostics under the cursor' } + ) + end end --- Default menus @@ -93,7 +205,6 @@ do --- Right click popup menu -- TODO VimScript, no l10n vim.cmd([[ - aunmenu * vnoremenu PopUp.Cut "+x vnoremenu PopUp.Copy "+y anoremenu PopUp.Paste "+gP @@ -102,6 +213,7 @@ do nnoremenu PopUp.Select\ All ggVG vnoremenu PopUp.Select\ All gg0oG$ inoremenu PopUp.Select\ All <C-Home><C-O>VG + anoremenu PopUp.Inspect <Cmd>Inspect<CR> anoremenu PopUp.-1- <Nop> anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR> ]]) @@ -128,7 +240,7 @@ do end local info = vim.api.nvim_get_chan_info(vim.bo[args.buf].channel) local argv = info.argv or {} - if #argv == 1 and argv[1] == vim.o.shell then + if table.concat(argv, ' ') == vim.o.shell then vim.api.nvim_buf_delete(args.buf, { force = true }) end end, @@ -136,8 +248,9 @@ do vim.api.nvim_create_autocmd('TermRequest', { group = nvim_terminal_augroup, - desc = 'Respond to OSC foreground/background color requests', + desc = 'Handles OSC foreground/background color requests', callback = function(args) + --- @type integer local channel = vim.bo[args.buf].channel if channel == 0 then return @@ -181,232 +294,146 @@ do return end vim.v.swapchoice = 'e' -- Choose "(E)dit". - vim.notify(('W325: Ignoring swapfile from Nvim process %d'):format(info.pid)) + vim.notify( + ('W325: Ignoring swapfile from Nvim process %d'):format(info.pid), + vim.log.levels.WARN + ) end, }) -end - --- Only do the following when the TUI is attached -local tty = nil -for _, ui in ipairs(vim.api.nvim_list_uis()) do - if ui.chan == 1 and ui.stdout_tty then - tty = ui - break - end -end -if tty then - local group = vim.api.nvim_create_augroup('nvim_tty', {}) - - --- Set an option after startup (so that OptionSet is fired), but only if not - --- already set by the user. - --- - --- @param option string Option name - --- @param value any Option value - local function setoption(option, value) - if vim.api.nvim_get_option_info2(option, {}).was_set then - -- Don't do anything if option is already set - return - end - - -- Wait until Nvim is finished starting to set the option to ensure the - -- OptionSet event fires. - if vim.v.vim_did_enter == 1 then - vim.o[option] = value - else - vim.api.nvim_create_autocmd('VimEnter', { - group = group, - once = true, - nested = true, - callback = function() - setoption(option, value) - end, - }) + -- Only do the following when the TUI is attached + local tty = nil + for _, ui in ipairs(vim.api.nvim_list_uis()) do + if ui.chan == 1 and ui.stdout_tty then + tty = ui + break end end - --- Guess value of 'background' based on terminal color. - --- - --- We write Operating System Command (OSC) 11 to the terminal to request the - --- terminal's background color. We then wait for a response. If the response - --- matches `rgba:RRRR/GGGG/BBBB/AAAA` where R, G, B, and A are hex digits, then - --- compute the luminance[1] of the RGB color and classify it as light/dark - --- accordingly. Note that the color components may have anywhere from one to - --- four hex digits, and require scaling accordingly as values out of 4, 8, 12, - --- or 16 bits. Also note the A(lpha) component is optional, and is parsed but - --- ignored in the calculations. - --- - --- [1] https://en.wikipedia.org/wiki/Luma_%28video%29 - do - --- Parse a string of hex characters as a color. - --- - --- The string can contain 1 to 4 hex characters. The returned value is - --- between 0.0 and 1.0 (inclusive) representing the intensity of the color. - --- - --- For instance, if only a single hex char "a" is used, then this function - --- returns 0.625 (10 / 16), while a value of "aa" would return 0.664 (170 / - --- 256). + if tty then + local group = vim.api.nvim_create_augroup('nvim_tty', {}) + + --- Set an option after startup (so that OptionSet is fired), but only if not + --- already set by the user. --- - --- @param c string Color as a string of hex chars - --- @return number? Intensity of the color - local function parsecolor(c) - if #c == 0 or #c > 4 then - return nil + --- @param option string Option name + --- @param value any Option value + local function setoption(option, value) + if vim.api.nvim_get_option_info2(option, {}).was_set then + -- Don't do anything if option is already set + return end - local val = tonumber(c, 16) - if not val then - return nil + -- Wait until Nvim is finished starting to set the option to ensure the + -- OptionSet event fires. + if vim.v.vim_did_enter == 1 then + --- @diagnostic disable-next-line:no-unknown + vim.o[option] = value + else + vim.api.nvim_create_autocmd('VimEnter', { + group = group, + once = true, + nested = true, + callback = function() + setoption(option, value) + end, + }) end - - local max = tonumber(string.rep('f', #c), 16) - return val / max end - --- Parse an OSC 11 response - --- - --- Either of the two formats below are accepted: - --- - --- OSC 11 ; rgb:<red>/<green>/<blue> - --- - --- or + --- Guess value of 'background' based on terminal color. --- - --- OSC 11 ; rgba:<red>/<green>/<blue>/<alpha> + --- We write Operating System Command (OSC) 11 to the terminal to request the + --- terminal's background color. We then wait for a response. If the response + --- matches `rgba:RRRR/GGGG/BBBB/AAAA` where R, G, B, and A are hex digits, then + --- compute the luminance[1] of the RGB color and classify it as light/dark + --- accordingly. Note that the color components may have anywhere from one to + --- four hex digits, and require scaling accordingly as values out of 4, 8, 12, + --- or 16 bits. Also note the A(lpha) component is optional, and is parsed but + --- ignored in the calculations. --- - --- where - --- - --- <red>, <green>, <blue>, <alpha> := h | hh | hhh | hhhh - --- - --- The alpha component is ignored, if present. - --- - --- @param resp string OSC 11 response - --- @return string? Red component - --- @return string? Green component - --- @return string? Blue component - local function parseosc11(resp) - local r, g, b - r, g, b = resp:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$') - if not r and not g and not b then - local a - r, g, b, a = resp:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$') - if not a or #a > 4 then - return nil, nil, nil + --- [1] https://en.wikipedia.org/wiki/Luma_%28video%29 + do + --- Parse a string of hex characters as a color. + --- + --- The string can contain 1 to 4 hex characters. The returned value is + --- between 0.0 and 1.0 (inclusive) representing the intensity of the color. + --- + --- For instance, if only a single hex char "a" is used, then this function + --- returns 0.625 (10 / 16), while a value of "aa" would return 0.664 (170 / + --- 256). + --- + --- @param c string Color as a string of hex chars + --- @return number? Intensity of the color + local function parsecolor(c) + if #c == 0 or #c > 4 then + return nil end - end - if r and g and b and #r <= 4 and #g <= 4 and #b <= 4 then - return r, g, b - end - - return nil, nil, nil - end - - local timer = assert(vim.uv.new_timer()) - - local id = vim.api.nvim_create_autocmd('TermResponse', { - group = group, - nested = true, - callback = function(args) - local resp = args.data ---@type string - local r, g, b = parseosc11(resp) - if r and g and b then - local rr = parsecolor(r) - local gg = parsecolor(g) - local bb = parsecolor(b) - - if rr and gg and bb then - local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb) - local bg = luminance < 0.5 and 'dark' or 'light' - setoption('background', bg) - end - - return true + local val = tonumber(c, 16) + if not val then + return nil end - end, - }) - - io.stdout:write('\027]11;?\007') - timer:start(1000, 0, function() - -- Delete the autocommand if no response was received - vim.schedule(function() - -- Suppress error if autocommand has already been deleted - pcall(vim.api.nvim_del_autocmd, id) - end) - - if not timer:is_closing() then - timer:close() + local max = tonumber(string.rep('f', #c), 16) + return val / max end - end) - end - --- If the TUI (term_has_truecolor) was able to determine that the host - --- terminal supports truecolor, enable 'termguicolors'. Otherwise, query the - --- terminal (using both XTGETTCAP and SGR + DECRQSS). If the terminal's - --- response indicates that it does support truecolor enable 'termguicolors', - --- but only if the user has not already disabled it. - do - if tty.rgb then - -- The TUI was able to determine truecolor support - setoption('termguicolors', true) - else - local caps = {} ---@type table<string, boolean> - require('vim.termcap').query({ 'Tc', 'RGB', 'setrgbf', 'setrgbb' }, function(cap, found) - if not found then - return + --- Parse an OSC 11 response + --- + --- Either of the two formats below are accepted: + --- + --- OSC 11 ; rgb:<red>/<green>/<blue> + --- + --- or + --- + --- OSC 11 ; rgba:<red>/<green>/<blue>/<alpha> + --- + --- where + --- + --- <red>, <green>, <blue>, <alpha> := h | hh | hhh | hhhh + --- + --- The alpha component is ignored, if present. + --- + --- @param resp string OSC 11 response + --- @return string? Red component + --- @return string? Green component + --- @return string? Blue component + local function parseosc11(resp) + local r, g, b + r, g, b = resp:match('^\027%]11;rgb:(%x+)/(%x+)/(%x+)$') + if not r and not g and not b then + local a + r, g, b, a = resp:match('^\027%]11;rgba:(%x+)/(%x+)/(%x+)/(%x+)$') + if not a or #a > 4 then + return nil, nil, nil + end end - caps[cap] = true - if caps.Tc or caps.RGB or (caps.setrgbf and caps.setrgbb) then - setoption('termguicolors', true) + if r and g and b and #r <= 4 and #g <= 4 and #b <= 4 then + return r, g, b end - end) - local timer = assert(vim.uv.new_timer()) + return nil, nil, nil + end - -- Arbitrary colors to set in the SGR sequence - local r = 1 - local g = 2 - local b = 3 + local timer = assert(vim.uv.new_timer()) local id = vim.api.nvim_create_autocmd('TermResponse', { group = group, nested = true, callback = function(args) local resp = args.data ---@type string - local decrqss = resp:match('^\027P1%$r([%d;:]+)m$') - - if decrqss then - -- The DECRQSS SGR response first contains attributes separated by - -- semicolons, followed by the SGR itself with parameters separated - -- by colons. Some terminals include "0" in the attribute list - -- unconditionally; others do not. Our SGR sequence did not set any - -- attributes, so there should be no attributes in the list. - local attrs = vim.split(decrqss, ';') - if #attrs ~= 1 and (#attrs ~= 2 or attrs[1] ~= '0') then - return true - end - - -- The returned SGR sequence should begin with 48:2 - local sgr = attrs[#attrs]:match('^48:2:([%d:]+)$') - if not sgr then - return true - end - - -- The remaining elements of the SGR sequence should be the 3 colors - -- we set. Some terminals also include an additional parameter - -- (which can even be empty!), so handle those cases as well - local params = vim.split(sgr, ':') - if #params ~= 3 and (#params ~= 4 or (params[1] ~= '' and params[1] ~= '1')) then - return true - end - - if - tonumber(params[#params - 2]) == r - and tonumber(params[#params - 1]) == g - and tonumber(params[#params]) == b - then - setoption('termguicolors', true) + local r, g, b = parseosc11(resp) + if r and g and b then + local rr = parsecolor(r) + local gg = parsecolor(g) + local bb = parsecolor(b) + + if rr and gg and bb then + local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb) + local bg = luminance < 0.5 and 'dark' or 'light' + setoption('background', bg) end return true @@ -414,15 +441,7 @@ if tty then end, }) - -- Write SGR followed by DECRQSS. This sets the background color then - -- immediately asks the terminal what the background color is. If the - -- terminal responds to the DECRQSS with the same SGR sequence that we - -- sent then the terminal supports truecolor. - local decrqss = '\027P$qm\027\\' - if os.getenv('TMUX') then - decrqss = string.format('\027Ptmux;%s\027\\', decrqss:gsub('\027', '\027\027')) - end - io.stdout:write(string.format('\027[48;2;%d;%d;%dm%s', r, g, b, decrqss)) + io.stdout:write('\027]11;?\007') timer:start(1000, 0, function() -- Delete the autocommand if no response was received @@ -436,5 +455,114 @@ if tty then end end) end + + --- If the TUI (term_has_truecolor) was able to determine that the host + --- terminal supports truecolor, enable 'termguicolors'. Otherwise, query the + --- terminal (using both XTGETTCAP and SGR + DECRQSS). If the terminal's + --- response indicates that it does support truecolor enable 'termguicolors', + --- but only if the user has not already disabled it. + do + if tty.rgb then + -- The TUI was able to determine truecolor support + setoption('termguicolors', true) + else + local caps = {} ---@type table<string, boolean> + require('vim.termcap').query({ 'Tc', 'RGB', 'setrgbf', 'setrgbb' }, function(cap, found) + if not found then + return + end + + caps[cap] = true + if caps.Tc or caps.RGB or (caps.setrgbf and caps.setrgbb) then + setoption('termguicolors', true) + end + end) + + local timer = assert(vim.uv.new_timer()) + + -- Arbitrary colors to set in the SGR sequence + local r = 1 + local g = 2 + local b = 3 + + local id = vim.api.nvim_create_autocmd('TermResponse', { + group = group, + nested = true, + callback = function(args) + local resp = args.data ---@type string + local decrqss = resp:match('^\027P1%$r([%d;:]+)m$') + + if decrqss then + -- The DECRQSS SGR response first contains attributes separated by + -- semicolons, followed by the SGR itself with parameters separated + -- by colons. Some terminals include "0" in the attribute list + -- unconditionally; others do not. Our SGR sequence did not set any + -- attributes, so there should be no attributes in the list. + local attrs = vim.split(decrqss, ';') + if #attrs ~= 1 and (#attrs ~= 2 or attrs[1] ~= '0') then + return false + end + + -- The returned SGR sequence should begin with 48:2 + local sgr = attrs[#attrs]:match('^48:2:([%d:]+)$') + if not sgr then + return false + end + + -- The remaining elements of the SGR sequence should be the 3 colors + -- we set. Some terminals also include an additional parameter + -- (which can even be empty!), so handle those cases as well + local params = vim.split(sgr, ':') + if #params ~= 3 and (#params ~= 4 or (params[1] ~= '' and params[1] ~= '1')) then + return true + end + + if + tonumber(params[#params - 2]) == r + and tonumber(params[#params - 1]) == g + and tonumber(params[#params]) == b + then + setoption('termguicolors', true) + end + + return true + end + end, + }) + + -- Write SGR followed by DECRQSS. This sets the background color then + -- immediately asks the terminal what the background color is. If the + -- terminal responds to the DECRQSS with the same SGR sequence that we + -- sent then the terminal supports truecolor. + local decrqss = '\027P$qm\027\\' + if os.getenv('TMUX') then + decrqss = string.format('\027Ptmux;%s\027\\', decrqss:gsub('\027', '\027\027')) + end + -- Reset attributes first, as other code may have set attributes. + io.stdout:write(string.format('\027[0m\027[48;2;%d;%d;%dm%s', r, g, b, decrqss)) + + timer:start(1000, 0, function() + -- Delete the autocommand if no response was received + vim.schedule(function() + -- Suppress error if autocommand has already been deleted + pcall(vim.api.nvim_del_autocmd, id) + end) + + if not timer:is_closing() then + timer:close() + end + end) + end + end + end +end + +--- Default options +do + --- Default 'grepprg' to ripgrep if available. + if vim.fn.executable('rg') == 1 then + -- Use -uu to make ripgrep not check ignore files/skip dot-files + vim.o.grepprg = 'rg --vimgrep -uu ' + vim.o.grepformat = '%f:%l:%c:%m' end end diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 6cf77b4648..5e9be509c8 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -74,7 +74,6 @@ vim.log = { --- Examples: --- --- ```lua ---- --- local on_exit = function(obj) --- print(obj.code) --- print(obj.signal) @@ -122,6 +121,7 @@ vim.log = { --- asynchronously. Receives SystemCompleted object, see return of SystemObj:wait(). --- --- @return vim.SystemObj Object with the fields: +--- - cmd (string[]) Command name and args --- - pid (integer) Process ID --- - wait (fun(timeout: integer|nil): SystemCompleted) Wait for the process to complete. Upon --- timeout the process is sent the KILL signal (9) and the exit code is set to 124. Cannot @@ -655,11 +655,14 @@ local on_key_cbs = {} --- @type table<integer,function> --- ---@note {fn} will be removed on error. ---@note {fn} will not be cleared by |nvim_buf_clear_namespace()| ----@note {fn} will receive the keys after mappings have been evaluated --- ----@param fn fun(key: string)? Function invoked on every key press. |i_CTRL-V| ---- Passing in nil when {ns_id} is specified removes the ---- callback associated with namespace {ns_id}. +---@param fn fun(key: string, typed: string)? +--- Function invoked on every key press. |i_CTRL-V| +--- {key} is the key after mappings have been applied, and +--- {typed} is the key(s) before mappings are applied, which +--- may be empty if {key} is produced by non-typed keys. +--- When {fn} is nil and {ns_id} is specified, the callback +--- associated with namespace {ns_id} is removed. ---@param ns_id integer? Namespace ID. If nil or 0, generates and returns a --- new |nvim_create_namespace()| id. --- @@ -685,11 +688,11 @@ end --- Executes the on_key callbacks. ---@private -function vim._on_key(char) +function vim._on_key(buf, typed_buf) local failed_ns_ids = {} local failed_messages = {} for k, v in pairs(on_key_cbs) do - local ok, err_msg = pcall(v, char) + local ok, err_msg = pcall(v, buf, typed_buf) if not ok then vim.on_key(nil, k) table.insert(failed_ns_ids, k) @@ -1028,6 +1031,42 @@ function vim._cs_remote(rcid, server_addr, connect_error, args) } end +do + local function truncated_echo(msg) + -- Truncate message to avoid hit-enter-prompt + local max_width = vim.o.columns * math.max(vim.o.cmdheight - 1, 0) + vim.v.echospace + local msg_truncated = string.sub(msg, 1, max_width) + vim.api.nvim_echo({ { msg_truncated, 'WarningMsg' } }, true, {}) + end + + local notified = false + + function vim._truncated_echo_once(msg) + if not notified then + truncated_echo(msg) + notified = true + return true + end + return false + end +end + +--- This is basically the same as debug.traceback(), except the full paths are shown. +local function traceback() + local level = 4 + local backtrace = { 'stack traceback:' } + while true do + local info = debug.getinfo(level, 'Sl') + if not info then + break + end + local msg = (' %s:%s'):format(info.source:sub(2), info.currentline) + table.insert(backtrace, msg) + level = level + 1 + end + return table.concat(backtrace, '\n') +end + --- Shows a deprecation message to the user. --- ---@param name string Deprecated feature (function, API, etc.). @@ -1039,55 +1078,46 @@ end --- ---@return string|nil # Deprecated message, or nil if no message was shown. function vim.deprecate(name, alternative, version, plugin, backtrace) - vim.validate { - name = { name, 'string' }, - alternative = { alternative, 'string', true }, - version = { version, 'string', true }, - plugin = { plugin, 'string', true }, - } plugin = plugin or 'Nvim' - - -- Only issue warning if feature is hard-deprecated as specified by MAINTAIN.md. - -- e.g., when planned to be removed in version = '0.12' (soft-deprecated since 0.10-dev), - -- show warnings since 0.11, including 0.11-dev (hard_deprecated_since = 0.11-dev). if plugin == 'Nvim' then - local current_version = vim.version() ---@type vim.Version - local removal_version = assert(vim.version.parse(version)) - local is_hard_deprecated ---@type boolean - - if removal_version.minor > 0 then - local hard_deprecated_since = assert(vim.version._version({ - major = removal_version.major, - minor = removal_version.minor - 1, - patch = 0, - prerelease = 'dev', -- Show deprecation warnings in devel (nightly) version as well - })) - is_hard_deprecated = (current_version >= hard_deprecated_since) - else - -- Assume there will be no next minor version before bumping up the major version; - -- therefore we can always show a warning. - assert(removal_version.minor == 0, vim.inspect(removal_version)) - is_hard_deprecated = true - end + require('vim.deprecated.health').add(name, version, traceback(), alternative) + + -- Only issue warning if feature is hard-deprecated as specified by MAINTAIN.md. + -- Example: if removal_version is 0.12 (soft-deprecated since 0.10-dev), show warnings starting at + -- 0.11, including 0.11-dev + local major, minor = version:match('(%d+)%.(%d+)') + major, minor = tonumber(major), tonumber(minor) + local hard_deprecated_since = string.format('nvim-%d.%d', major, minor - 1) + -- Assume there will be no next minor version before bumping up the major version + local is_hard_deprecated = minor == 0 or vim.fn.has(hard_deprecated_since) == 1 if not is_hard_deprecated then return end - end - local msg = ('%s is deprecated'):format(name) - msg = alternative and ('%s, use %s instead.'):format(msg, alternative) or (msg .. '.') - msg = ('%s%s\nThis feature will be removed in %s version %s'):format( - msg, - (plugin == 'Nvim' and ' :help deprecated' or ''), - plugin, - version - ) - local displayed = vim.notify_once(msg, vim.log.levels.WARN) - if displayed and backtrace ~= false then - vim.notify(debug.traceback('', 2):sub(2), vim.log.levels.WARN) + local msg = ('%s is deprecated. Run ":checkhealth vim.deprecated" for more information'):format( + name + ) + + local displayed = vim._truncated_echo_once(msg) + return displayed and msg or nil + else + vim.validate { + name = { name, 'string' }, + alternative = { alternative, 'string', true }, + version = { version, 'string', true }, + plugin = { plugin, 'string', true }, + } + + local msg = ('%s is deprecated'):format(name) + msg = alternative and ('%s, use %s instead.'):format(msg, alternative) or (msg .. '.') + msg = ('%s\nFeature will be removed in %s %s'):format(msg, plugin, version) + local displayed = vim.notify_once(msg, vim.log.levels.WARN) + if displayed and backtrace ~= false then + vim.notify(debug.traceback('', 2):sub(2), vim.log.levels.WARN) + end + return displayed and msg or nil end - return displayed and msg or nil end require('vim._options') diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua index afbd6211cd..f5d1640c82 100644 --- a/runtime/lua/vim/_inspector.lua +++ b/runtime/lua/vim/_inspector.lua @@ -55,8 +55,8 @@ function vim.inspect_pos(bufnr, row, col, filter) bufnr = bufnr == 0 and vim.api.nvim_get_current_buf() or bufnr local results = { - treesitter = {}, - syntax = {}, + treesitter = {}, --- @type table[] + syntax = {}, --- @type table[] extmarks = {}, semantic_tokens = {}, buffer = bufnr, @@ -93,7 +93,7 @@ function vim.inspect_pos(bufnr, row, col, filter) end -- namespace id -> name map - local nsmap = {} + local nsmap = {} --- @type table<integer,string> for name, id in pairs(vim.api.nvim_get_namespaces()) do nsmap[id] = name end diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index cb4c8749b8..6edf2a5a96 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -14,14 +14,21 @@ function vim.api.nvim__buf_debug_extmarks(buffer, keys, dot) end --- @private --- @param buffer integer ---- @param first integer ---- @param last integer -function vim.api.nvim__buf_redraw_range(buffer, first, last) end +--- @return table<string,any> +function vim.api.nvim__buf_stats(buffer) end --- @private ---- @param buffer integer +--- EXPERIMENTAL: this API may change in the future. +--- +--- Sets info for the completion item at the given index. If the info text was +--- shown in a window, returns the window and buffer ids, or empty dict if not +--- shown. +--- +--- @param index integer Completion candidate index +--- @param opts vim.api.keyset.complete_set Optional parameters. +--- • info: (string) info text. --- @return table<string,any> -function vim.api.nvim__buf_stats(buffer) end +function vim.api.nvim__complete_set(index, opts) end --- @private --- @return string @@ -93,6 +100,32 @@ function vim.api.nvim__inspect_cell(grid, row, col) end function vim.api.nvim__invalidate_glyph_cache() end --- @private +--- EXPERIMENTAL: this API may change in the future. +--- +--- Instruct Nvim to redraw various components. +--- +--- @param opts vim.api.keyset.redraw Optional parameters. +--- • win: Target a specific `window-ID` as described below. +--- • buf: Target a specific buffer number as described below. +--- • flush: Update the screen with pending updates. +--- • valid: When present mark `win`, `buf`, or all windows for +--- redraw. When `true`, only redraw changed lines (useful for +--- decoration providers). When `false`, forcefully redraw. +--- • range: Redraw a range in `buf`, the buffer in `win` or the +--- current buffer (useful for decoration providers). Expects a +--- tuple `[first, last]` with the first and last line number of +--- the range, 0-based end-exclusive `api-indexing`. +--- • cursor: Immediately update cursor position on the screen in +--- `win` or the current window. +--- • statuscolumn: Redraw the 'statuscolumn' in `buf`, `win` or +--- all windows. +--- • statusline: Redraw the 'statusline' in `buf`, `win` or all +--- windows. +--- • winbar: Redraw the 'winbar' in `buf`, `win` or all windows. +--- • tabline: Redraw the 'tabline'. +function vim.api.nvim__redraw(opts) end + +--- @private --- @return any[] function vim.api.nvim__runtime_inspect() end @@ -111,6 +144,36 @@ function vim.api.nvim__stats() end --- @return any function vim.api.nvim__unpack(str) end +--- @private +--- EXPERIMENTAL: this API will change in the future. +--- +--- Scopes a namespace to the a window, so extmarks in the namespace will be +--- active only in the given window. +--- +--- @param window integer Window handle, or 0 for current window +--- @param ns_id integer Namespace +--- @return boolean +function vim.api.nvim__win_add_ns(window, ns_id) end + +--- @private +--- EXPERIMENTAL: this API will change in the future. +--- +--- Unscopes a namespace (un-binds it from the given scope). +--- +--- @param window integer Window handle, or 0 for current window +--- @param ns_id integer the namespace to remove +--- @return boolean +function vim.api.nvim__win_del_ns(window, ns_id) end + +--- @private +--- EXPERIMENTAL: this API will change in the future. +--- +--- Gets the namespace scopes for a given window. +--- +--- @param window integer Window handle, or 0 for current window +--- @return integer[] +function vim.api.nvim__win_get_ns(window) end + --- Adds a highlight to buffer. --- --- Useful for plugins that dynamically generate highlights to a buffer (like @@ -623,8 +686,8 @@ function vim.api.nvim_buf_line_count(buffer) end --- • url: A URL to associate with this extmark. In the TUI, the --- OSC 8 control sequence is used to generate a clickable --- hyperlink to this URL. ---- • scoped: boolean that indicates that the extmark should only ---- be displayed in the namespace scope. (experimental) +--- • scoped: boolean (EXPERIMENTAL) enables "scoping" for the +--- extmark. See `nvim__win_add_ns()` --- @return integer function vim.api.nvim_buf_set_extmark(buffer, ns_id, line, col, opts) end @@ -669,7 +732,7 @@ function vim.api.nvim_buf_set_lines(buffer, start, end_, strict_indexing, replac --- @return boolean function vim.api.nvim_buf_set_mark(buffer, name, line, col, opts) end ---- Sets the full file name for a buffer +--- Sets the full file name for a buffer, like `:file_f` --- --- @param buffer integer Buffer handle, or 0 for current buffer --- @param name string Buffer name @@ -822,16 +885,6 @@ function vim.api.nvim_command(command) end --- @return string function vim.api.nvim_command_output(command) end ---- Set info for the completion candidate index. if the info was shown in a ---- window, then the window and buffer ids are returned for further ---- customization. If the text was not shown, an empty dict is returned. ---- ---- @param index integer the completion candidate index ---- @param opts vim.api.keyset.complete_set Optional parameters. ---- • info: (string) info text. ---- @return table<string,any> -function vim.api.nvim_complete_set(index, opts) end - --- Create or get an autocommand group `autocmd-groups`. --- --- To get an existing group id, do: @@ -897,8 +950,8 @@ function vim.api.nvim_create_augroup(name, opts) end --- • callback (function|string) optional: Lua function (or --- Vimscript function name, if string) called when the event(s) --- is triggered. Lua callback can return a truthy value (not ---- `false` or `nil`) to delete the autocommand. Receives a ---- table argument with these keys: +--- `false` or `nil`) to delete the autocommand. Receives one +--- argument, a table with these keys: *event-args* --- • id: (number) autocommand id --- • event: (string) name of the triggered event --- `autocmd-events` @@ -907,7 +960,7 @@ function vim.api.nvim_create_augroup(name, opts) end --- • buf: (number) expanded value of <abuf> --- • file: (string) expanded value of <afile> --- • data: (any) arbitrary data passed from ---- `nvim_exec_autocmds()` +--- `nvim_exec_autocmds()` *event-data* --- • command (string) optional: Vim command to execute on event. --- Cannot be used with {callback} --- • once (boolean) optional: defaults to false. Run the @@ -1718,9 +1771,8 @@ function vim.api.nvim_open_term(buffer, opts) end --- • footer_pos: Footer position. Must be set with `footer` --- option. Value can be one of "left", "center", or "right". --- Default is `"left"`. ---- • noautocmd: If true then no buffer-related autocommand ---- events such as `BufEnter`, `BufLeave` or `BufWinEnter` may ---- fire from calling this function. +--- • noautocmd: If true then all autocommands are blocked for +--- the duration of the call. --- • fixed: If true when anchor is NW or SW, the float window --- would be kept fixed even if the window would be truncated. --- • hide: If true the floating window will be hidden. @@ -2092,13 +2144,6 @@ function vim.api.nvim_tabpage_set_var(tabpage, name, value) end --- @param win integer Window handle, must already belong to {tabpage} function vim.api.nvim_tabpage_set_win(tabpage, win) end ---- Adds the namespace scope to the window. ---- ---- @param window integer Window handle, or 0 for current window ---- @param ns_id integer the namespace to add ---- @return boolean -function vim.api.nvim_win_add_ns(window, ns_id) end - --- Calls a function with window as temporary current window. --- --- @param window integer Window handle, or 0 for current window @@ -2151,12 +2196,6 @@ function vim.api.nvim_win_get_cursor(window) end --- @return integer function vim.api.nvim_win_get_height(window) end ---- Gets all the namespaces scopes associated with a window. ---- ---- @param window integer Window handle, or 0 for current window ---- @return integer[] -function vim.api.nvim_win_get_ns(window) end - --- Gets the window number --- --- @param window integer Window handle, or 0 for current window @@ -2210,24 +2249,17 @@ function vim.api.nvim_win_hide(window) end --- @return boolean function vim.api.nvim_win_is_valid(window) end ---- Removes the namespace scope from the window. ---- ---- @param window integer Window handle, or 0 for current window ---- @param ns_id integer the namespace to remove ---- @return boolean -function vim.api.nvim_win_remove_ns(window, ns_id) end - --- Sets the current buffer in a window, without side effects --- --- @param window integer Window handle, or 0 for current window --- @param buffer integer Buffer handle function vim.api.nvim_win_set_buf(window, buffer) end ---- Configures window layout. Currently only for floating and external windows ---- (including changing a split window to those layouts). +--- Configures window layout. Cannot be used to move the last window in a +--- tabpage to a different one. --- ---- When reconfiguring a floating window, absent option keys will not be ---- changed. `row`/`col` and `relative` must be reconfigured together. +--- When reconfiguring a window, absent option keys will not be changed. +--- `row`/`col` and `relative` must be reconfigured together. --- --- @param window integer Window handle, or 0 for current window --- @param config vim.api.keyset.win_config Map defining the window configuration, see `nvim_open_win()` diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index 37e4372196..f7cd92a3b2 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -207,6 +207,18 @@ error('Cannot require a meta file') --- @field buf? integer --- @field filetype? string +--- @class vim.api.keyset.redraw +--- @field flush? boolean +--- @field cursor? boolean +--- @field valid? boolean +--- @field statuscolumn? boolean +--- @field statusline? boolean +--- @field tabline? boolean +--- @field winbar? boolean +--- @field range? any[] +--- @field win? integer +--- @field buf? integer + --- @class vim.api.keyset.runtime --- @field is_lua? boolean --- @field do_source? boolean @@ -253,7 +265,6 @@ error('Cannot require a meta file') --- @field undo_restore? boolean --- @field url? string --- @field scoped? boolean ---- @field _subpriority? integer --- @class vim.api.keyset.user_command --- @field addr? any diff --git a/runtime/lua/vim/_meta/api_keysets_extra.lua b/runtime/lua/vim/_meta/api_keysets_extra.lua index d61dd2c02f..76b56b04e7 100644 --- a/runtime/lua/vim/_meta/api_keysets_extra.lua +++ b/runtime/lua/vim/_meta/api_keysets_extra.lua @@ -124,7 +124,7 @@ error('Cannot require a meta file') --- @field commalist boolean --- @field flaglist boolean --- @field was_set boolean ---- @field last_set_id integer +--- @field last_set_sid integer --- @field last_set_linenr integer --- @field last_set_chan integer --- @field type 'string'|'boolean'|'number' diff --git a/runtime/lua/vim/_meta/base64.lua b/runtime/lua/vim/_meta/base64.lua index f25b4af234..8ba59e1703 100644 --- a/runtime/lua/vim/_meta/base64.lua +++ b/runtime/lua/vim/_meta/base64.lua @@ -3,11 +3,11 @@ --- Encode {str} using Base64. --- --- @param str string String to encode ---- @return string Encoded string +--- @return string : Encoded string function vim.base64.encode(str) end --- Decode a Base64 encoded string. --- --- @param str string Base64 encoded string ---- @return string Decoded string +--- @return string : Decoded string function vim.base64.decode(str) end diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua index 9a67667f02..75737bd040 100644 --- a/runtime/lua/vim/_meta/builtin.lua +++ b/runtime/lua/vim/_meta/builtin.lua @@ -115,19 +115,19 @@ function vim.stricmp(a, b) end --- Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not --- supplied, it defaults to false (use UTF-32). Returns the byte index. --- ---- Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. +--- Invalid UTF-8 and NUL is treated like in |vim.str_utfindex()|. --- An {index} in the middle of a UTF-16 sequence is rounded upwards to --- the end of that sequence. --- @param str string ---- @param index number ---- @param use_utf16? any +--- @param index integer +--- @param use_utf16? boolean function vim.str_byteindex(str, index, use_utf16) end --- Gets a list of the starting byte positions of each UTF-8 codepoint in the given string. --- --- Embedded NUL bytes are treated as terminating the string. --- @param str string ---- @return table +--- @return integer[] function vim.str_utf_pos(str) end --- Gets the distance (in bytes) from the starting byte of the codepoint (character) that {index} @@ -148,8 +148,8 @@ function vim.str_utf_pos(str) end --- ``` --- --- @param str string ---- @param index number ---- @return number +--- @param index integer +--- @return integer function vim.str_utf_start(str, index) end --- Gets the distance (in bytes) from the last byte of the codepoint (character) that {index} points @@ -168,8 +168,8 @@ function vim.str_utf_start(str, index) end --- ``` --- --- @param str string ---- @param index number ---- @return number +--- @param index integer +--- @return integer function vim.str_utf_end(str, index) end --- Convert byte index to UTF-32 and UTF-16 indices. If {index} is not @@ -180,7 +180,7 @@ function vim.str_utf_end(str, index) end --- {index} in the middle of a UTF-8 sequence is rounded upwards to the end of --- that sequence. --- @param str string ---- @param index? number +--- @param index? integer --- @return integer UTF-32 index --- @return integer UTF-16 index function vim.str_utfindex(str, index) end @@ -193,15 +193,14 @@ function vim.str_utfindex(str, index) end --- can accept, see ":Man 3 iconv". --- --- @param str string Text to convert ---- @param from number Encoding of {str} ---- @param to number Target encoding ---- @param opts? table<string,any> ---- @return string|nil Converted string if conversion succeeds, `nil` otherwise. +--- @param from string Encoding of {str} +--- @param to string Target encoding +--- @return string? : Converted string if conversion succeeds, `nil` otherwise. function vim.iconv(str, from, to, opts) end --- Schedules {fn} to be invoked soon by the main event-loop. Useful --- to avoid |textlock| or other temporary restrictions. ---- @param fn function +--- @param fn fun() function vim.schedule(fn) end --- Wait for {time} in milliseconds until {callback} returns `true`. @@ -215,7 +214,6 @@ function vim.schedule(fn) end --- Examples: --- --- ```lua ---- --- --- --- -- Wait for 100 ms, allowing other events to process --- vim.wait(100, function() end) diff --git a/runtime/lua/vim/_meta/builtin_types.lua b/runtime/lua/vim/_meta/builtin_types.lua index 0bbc3e9bc8..9f0d2e7038 100644 --- a/runtime/lua/vim/_meta/builtin_types.lua +++ b/runtime/lua/vim/_meta/builtin_types.lua @@ -127,3 +127,11 @@ --- @field skipcol integer --- @field topfill integer --- @field topline integer + +--- @class vim.fn.getscriptinfo.ret +--- @field autoload false +--- @field functions? string[] +--- @field name string +--- @field sid string +--- @field variables? table<string, any> +--- @field version 1 diff --git a/runtime/lua/vim/_meta/diff.lua b/runtime/lua/vim/_meta/diff.lua index f265139448..617bc87f59 100644 --- a/runtime/lua/vim/_meta/diff.lua +++ b/runtime/lua/vim/_meta/diff.lua @@ -1,5 +1,46 @@ ---@meta +--- Optional parameters: +--- @class vim.diff.Opts +--- @inlinedoc +--- +--- Invoked for each hunk in the diff. Return a negative number +--- to cancel the callback for any remaining hunks. +--- Arguments: +--- - `start_a` (`integer`): Start line of hunk in {a}. +--- - `count_a` (`integer`): Hunk size in {a}. +--- - `start_b` (`integer`): Start line of hunk in {b}. +--- - `count_b` (`integer`): Hunk size in {b}. +--- @field on_hunk fun(start_a: integer, count_a: integer, start_b: integer, count_b: integer): integer +--- +--- Form of the returned diff: +--- - `unified`: String in unified format. +--- - `indices`: Array of hunk locations. +--- Note: This option is ignored if `on_hunk` is used. +--- (default: `'unified'`) +--- @field result_type 'unified'|'indices' +--- +--- Run linematch on the resulting hunks from xdiff. When integer, only hunks +--- upto this size in lines are run through linematch. +--- Requires `result_type = indices`, ignored otherwise. +--- @field linematch boolean|integer +--- +--- Diff algorithm to use. Values: +--- - `myers`: the default algorithm +--- - `minimal`: spend extra time to generate the smallest possible diff +--- - `patience`: patience diff algorithm +--- - `histogram`: histogram diff algorithm +--- (default: `'myers'`) +--- @field algorithm 'myers'|'minimal'|'patience'|'histogram' +--- @field ctxlen integer Context length +--- @field interhunkctxlen integer Inter hunk context length +--- @field ignore_whitespace boolean Ignore whitespace +--- @field ignore_whitespace_change boolean Ignore whitespace change +--- @field ignore_whitespace_change_at_eol boolean Ignore whitespace change at end-of-line. +--- @field ignore_cr_at_eol boolean Ignore carriage return at end-of-line +--- @field ignore_blank_lines boolean Ignore blank lines +--- @field indent_heuristic boolean Use the indent heuristic for the internal diff library. + -- luacheck: no unused args --- Run diff on strings {a} and {b}. Any indices returned by this function, @@ -24,47 +65,7 @@ --- ---@param a string First string to compare ---@param b string Second string to compare ----@param opts table<string,any> Optional parameters: ---- - `on_hunk` (callback): ---- Invoked for each hunk in the diff. Return a negative number ---- to cancel the callback for any remaining hunks. ---- Args: ---- - `start_a` (integer): Start line of hunk in {a}. ---- - `count_a` (integer): Hunk size in {a}. ---- - `start_b` (integer): Start line of hunk in {b}. ---- - `count_b` (integer): Hunk size in {b}. ---- - `result_type` (string): Form of the returned diff: ---- - "unified": (default) String in unified format. ---- - "indices": Array of hunk locations. ---- Note: This option is ignored if `on_hunk` is used. ---- - `linematch` (boolean|integer): Run linematch on the resulting hunks ---- from xdiff. When integer, only hunks upto this size in ---- lines are run through linematch. Requires `result_type = indices`, ---- ignored otherwise. ---- - `algorithm` (string): ---- Diff algorithm to use. Values: ---- - "myers" the default algorithm ---- - "minimal" spend extra time to generate the ---- smallest possible diff ---- - "patience" patience diff algorithm ---- - "histogram" histogram diff algorithm ---- - `ctxlen` (integer): Context length ---- - `interhunkctxlen` (integer): ---- Inter hunk context length ---- - `ignore_whitespace` (boolean): ---- Ignore whitespace ---- - `ignore_whitespace_change` (boolean): ---- Ignore whitespace change ---- - `ignore_whitespace_change_at_eol` (boolean) ---- Ignore whitespace change at end-of-line. ---- - `ignore_cr_at_eol` (boolean) ---- Ignore carriage return at end-of-line ---- - `ignore_blank_lines` (boolean) ---- Ignore blank lines ---- - `indent_heuristic` (boolean): ---- Use the indent heuristic for the internal ---- diff library. ---- ----@return string|table|nil +---@param opts vim.diff.Opts +---@return string|integer[][]? --- See {opts.result_type}. `nil` if {opts.on_hunk} is given. function vim.diff(a, b, opts) end diff --git a/runtime/lua/vim/_meta/lpeg.lua b/runtime/lua/vim/_meta/lpeg.lua index 1ce40f3340..73b3375c82 100644 --- a/runtime/lua/vim/_meta/lpeg.lua +++ b/runtime/lua/vim/_meta/lpeg.lua @@ -6,11 +6,10 @@ error('Cannot require a meta file') -- with types being renamed to include the vim namespace and with some descriptions made less verbose. --- @brief <pre>help ---- LPeg is a pattern-matching library for Lua, based on ---- Parsing Expression Grammars (https://bford.info/packrat/) (PEGs). +--- LPeg is a pattern-matching library for Lua, based on Parsing Expression +--- Grammars (PEGs). https://bford.info/packrat/ --- ---- *lua-lpeg* ---- *vim.lpeg.Pattern* +--- *lua-lpeg* *vim.lpeg.Pattern* --- The LPeg library for parsing expression grammars is included as `vim.lpeg` --- (https://www.inf.puc-rio.br/~roberto/lpeg/). --- diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 757720d8fb..428b7c4d4f 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -11,10 +11,9 @@ vim.bo = vim.bo ---@field [integer] vim.wo vim.wo = vim.wo ---- Allow CTRL-_ in Insert and Command-line mode. This is default off, to ---- avoid that users that accidentally type CTRL-_ instead of SHIFT-_ get ---- into reverse Insert mode, and don't know how to get out. See ---- 'revins'. +--- Allow CTRL-_ in Insert mode. This is default off, to avoid that users +--- that accidentally type CTRL-_ instead of SHIFT-_ get into reverse +--- Insert mode, and don't know how to get out. See 'revins'. --- --- @type boolean vim.o.allowrevins = false @@ -975,7 +974,7 @@ vim.bo.comments = vim.o.comments vim.bo.com = vim.bo.comments --- A template for a comment. The "%s" in the value is replaced with the ---- comment text. For example, C uses "/*%s*/". Currently only used to +--- comment text. For example, C uses "/*%s*/". Used for `commenting` and to --- add markers for folding, see `fold-marker`. --- --- @type string @@ -2626,6 +2625,8 @@ vim.go.gd = vim.go.gdefault --- This is a scanf-like string that uses the same format as the --- 'errorformat' option: see `errorformat`. --- +--- If ripgrep ('grepprg') is available, this option defaults to `%f:%l:%c:%m`. +--- --- @type string vim.o.grepformat = "%f:%l:%m,%f:%l%m,%f %l%m" vim.o.gfm = vim.o.grepformat @@ -2637,12 +2638,6 @@ vim.go.gfm = vim.go.grepformat --- line. The placeholder "$*" is allowed to specify where the arguments --- will be included. Environment variables are expanded `:set_env`. See --- `option-backslash` about including spaces and backslashes. ---- When your "grep" accepts the "-H" argument, use this to make ":grep" ---- also work well with a single file: ---- ---- ```vim ---- set grepprg=grep\ -nH ---- ``` --- Special value: When 'grepprg' is set to "internal" the `:grep` command --- works like `:vimgrep`, `:lgrep` like `:lvimgrep`, `:grepadd` like --- `:vimgrepadd` and `:lgrepadd` like `:lvimgrepadd`. @@ -2650,9 +2645,19 @@ vim.go.gfm = vim.go.grepformat --- apply equally to 'grepprg'. --- This option cannot be set from a `modeline` or in the `sandbox`, for --- security reasons. ---- ---- @type string -vim.o.grepprg = "grep -n $* /dev/null" +--- This option defaults to: +--- - `rg --vimgrep -uu ` if ripgrep is available (`:checkhealth`), +--- - `grep -HIn $* /dev/null` on Unix, +--- - `findstr /n $* nul` on Windows. +--- Ripgrep can perform additional filtering such as using .gitignore rules +--- and skipping hidden files. This is disabled by default (see the -u option) +--- to more closely match the behaviour of standard grep. +--- You can make ripgrep match Vim's case handling using the +--- -i/--ignore-case and -S/--smart-case options. +--- An `OptionSet` autocmd can be used to set it up to match automatically. +--- +--- @type string +vim.o.grepprg = "grep -HIn $* /dev/null" vim.o.gp = vim.o.grepprg vim.bo.grepprg = vim.o.grepprg vim.bo.gp = vim.bo.grepprg @@ -5195,9 +5200,6 @@ vim.wo.scr = vim.wo.scroll --- Minimum is 1, maximum is 100000. --- Only in `terminal` buffers. --- ---- Note: Lines that are not visible and kept in scrollback are not ---- reflown when the terminal buffer is resized horizontally. ---- --- @type integer vim.o.scrollback = -1 vim.o.scbk = vim.o.scrollback @@ -6079,8 +6081,7 @@ vim.go.sta = vim.go.smarttab --- highlighted with `hl-NonText`. --- You may also want to add "lastline" to the 'display' option to show as --- much of the last line as possible. ---- NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y ---- and scrolling with the mouse. +--- NOTE: partly implemented, doesn't work yet for `gj` and `gk`. --- --- @type boolean vim.o.smoothscroll = false @@ -6746,6 +6747,8 @@ vim.bo.swf = vim.bo.swapfile --- "split" when both are present. --- uselast If included, jump to the previously used window when --- jumping to errors with `quickfix` commands. +--- If a window has 'winfixbuf' enabled, 'switchbuf' is currently not +--- applied to the split window. --- --- @type string vim.o.switchbuf = "uselast" @@ -6847,7 +6850,7 @@ vim.go.tpm = vim.go.tabpagemax --- appear wrong in many places. --- The value must be more than 0 and less than 10000. --- ---- There are four main ways to use tabs in Vim: +--- There are five main ways to use tabs in Vim: --- 1. Always keep 'tabstop' at 8, set 'softtabstop' and 'shiftwidth' to 4 --- (or 3 or whatever you prefer) and use 'noexpandtab'. Then Vim --- will use a mix of tabs and spaces, but typing <Tab> and <BS> will @@ -7443,6 +7446,7 @@ vim.bo.vts = vim.bo.vartabstop --- --- Level Messages ~ --- ---------------------------------------------------------------------- +--- 1 Enables Lua tracing (see above). Does not produce messages. --- 2 When a file is ":source"'ed, or `shada` file is read or written. --- 3 UI info, terminal capabilities. --- 4 Shell commands. @@ -7863,8 +7867,8 @@ vim.wo.winbl = vim.wo.winblend --- will scroll 'window' minus two lines, with a minimum of one. --- When 'window' is equal to 'lines' minus one CTRL-F and CTRL-B scroll --- in a much smarter way, taking care of wrapping lines. ---- When resizing the Vim window, the value is smaller than 1 or more than ---- or equal to 'lines' it will be set to 'lines' minus 1. +--- When resizing the Vim window, and the value is smaller than 1 or more +--- than or equal to 'lines' it will be set to 'lines' minus 1. --- Note: Do not confuse this with the height of the Vim window, use --- 'lines' for that. --- @@ -7874,6 +7878,18 @@ vim.o.wi = vim.o.window vim.go.window = vim.o.window vim.go.wi = vim.go.window +--- If enabled, the window and the buffer it is displaying are paired. +--- For example, attempting to change the buffer with `:edit` will fail. +--- Other commands which change a window's buffer such as `:cnext` will +--- also skip any window with 'winfixbuf' enabled. However if an Ex +--- command has a "!" modifier, it can force switching buffers. +--- +--- @type boolean +vim.o.winfixbuf = false +vim.o.wfb = vim.o.winfixbuf +vim.wo.winfixbuf = vim.o.winfixbuf +vim.wo.wfb = vim.wo.winfixbuf + --- Keep the window height when windows are opened or closed and --- 'equalalways' is set. Also for `CTRL-W_=`. Set by default for the --- `preview-window` and `quickfix-window`. diff --git a/runtime/lua/vim/_meta/re.lua b/runtime/lua/vim/_meta/re.lua index 14c94c7824..d16751fbbf 100644 --- a/runtime/lua/vim/_meta/re.lua +++ b/runtime/lua/vim/_meta/re.lua @@ -8,11 +8,11 @@ error('Cannot require a meta file') -- See 'lpeg.html' for license --- @brief ---- The `vim.re` module provides a conventional regex-like syntax for pattern usage ---- within LPeg |vim.lpeg|. +--- The `vim.re` module provides a conventional regex-like syntax for pattern usage within LPeg +--- |vim.lpeg|. (Unrelated to |vim.regex| which provides Vim |regexp| from Lua.) --- ---- See https://www.inf.puc-rio.br/~roberto/lpeg/re.html for the original ---- documentation including regex syntax and more concrete examples. +--- See https://www.inf.puc-rio.br/~roberto/lpeg/re.html for the original documentation including +--- regex syntax and examples. --- Compiles the given {string} and returns an equivalent LPeg pattern. The given string may define --- either an expression or a grammar. The optional {defs} table provides extra Lua values to be used @@ -30,8 +30,8 @@ function vim.re.compile(string, defs) end --- @param subject string --- @param pattern vim.lpeg.Pattern|string --- @param init? integer ---- @return integer|nil the index where the occurrence starts, nil if no match ---- @return integer|nil the index where the occurrence ends, nil if no match +--- @return integer|nil : the index where the occurrence starts, nil if no match +--- @return integer|nil : the index where the occurrence ends, nil if no match function vim.re.find(subject, pattern, init) end --- Does a global substitution, replacing all occurrences of {pattern} in the given {subject} by diff --git a/runtime/lua/vim/_meta/spell.lua b/runtime/lua/vim/_meta/spell.lua index 57f2180895..c636db3b53 100644 --- a/runtime/lua/vim/_meta/spell.lua +++ b/runtime/lua/vim/_meta/spell.lua @@ -3,11 +3,11 @@ -- luacheck: no unused args --- Check {str} for spelling errors. Similar to the Vimscript function ---- |spellbadword()|. +--- [spellbadword()]. --- --- Note: The behaviour of this function is dependent on: 'spelllang', --- 'spellfile', 'spellcapcheck' and 'spelloptions' which can all be local to ---- the buffer. Consider calling this with |nvim_buf_call()|. +--- the buffer. Consider calling this with [nvim_buf_call()]. --- --- Example: --- @@ -20,7 +20,7 @@ --- ``` --- --- @param str string ---- @return {[1]: string, [2]: string, [3]: string}[] +--- @return {[1]: string, [2]: 'bad'|'rare'|'local'|'caps', [3]: integer}[] --- List of tuples with three items: --- - The badly spelled word. --- - The type of the spelling error: diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index ac25547212..f4daacfb7d 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -1598,11 +1598,10 @@ function vim.fn.eventhandler() end --- The result is a Number: --- 1 exists --- 0 does not exist ---- -1 not implemented on this system --- |exepath()| can be used to get the full path of an executable. --- --- @param expr any ---- @return 0|1|-1 +--- @return 0|1 function vim.fn.executable(expr) end --- Execute {command} and capture its output. @@ -1959,6 +1958,7 @@ function vim.fn.extendnew(expr1, expr2, expr3) end --- 't' Handle keys as if typed; otherwise they are handled as --- if coming from a mapping. This matters for undo, --- opening folds, etc. +--- 'L' Lowlevel input. Other flags are not used. --- 'i' Insert the string instead of appending (see above). --- 'x' Execute commands until typeahead is empty. This is --- similar to using ":normal!". You can call feedkeys() @@ -2703,14 +2703,14 @@ function vim.fn.getcellwidths() end function vim.fn.getchangelist(buf) end --- Get a single character from the user or input stream. ---- If [expr] is omitted, wait until a character is available. ---- If [expr] is 0, only get a character when one is available. +--- If {expr} is omitted, wait until a character is available. +--- If {expr} is 0, only get a character when one is available. --- Return zero otherwise. ---- If [expr] is 1, only check if a character is available, it is +--- If {expr} is 1, only check if a character is available, it is --- not consumed. Return zero if no character available. --- If you prefer always getting a string use |getcharstr()|. --- ---- Without [expr] and when [expr] is 0 a whole character or +--- Without {expr} and when {expr} is 0 a whole character or --- special key is returned. If it is a single character, the --- result is a Number. Use |nr2char()| to convert it to a String. --- Otherwise a String is returned with the encoded character. @@ -2720,11 +2720,11 @@ function vim.fn.getchangelist(buf) end --- also a String when a modifier (shift, control, alt) was used --- that is not included in the character. --- ---- When [expr] is 0 and Esc is typed, there will be a short delay +--- When {expr} is 0 and Esc is typed, there will be a short delay --- while Vim waits to see if this is the start of an escape --- sequence. --- ---- When [expr] is 1 only the first byte is returned. For a +--- When {expr} is 1 only the first byte is returned. For a --- one-byte character it is the character itself as a number. --- Use nr2char() to convert it to a String. --- @@ -2828,10 +2828,10 @@ function vim.fn.getcharsearch() end --- Get a single character from the user or input stream as a --- string. ---- If [expr] is omitted, wait until a character is available. ---- If [expr] is 0 or false, only get a character when one is +--- If {expr} is omitted, wait until a character is available. +--- If {expr} is 0 or false, only get a character when one is --- available. Return an empty string otherwise. ---- If [expr] is 1 or true, only check if a character is +--- If {expr} is 1 or true, only check if a character is --- available, it is not consumed. Return an empty string --- if no character is available. --- Otherwise this works like |getchar()|, except that a number @@ -3581,6 +3581,43 @@ function vim.fn.getreginfo(regname) end --- @return string[] function vim.fn.getregion(pos1, pos2, opts) end +--- Same as |getregion()|, but returns a list of positions +--- describing the buffer text segments bound by {pos1} and +--- {pos2}. +--- The segments are a pair of positions for every line: > +--- [[{start_pos}, {end_pos}], ...] +--- < +--- The position is a |List| with four numbers: +--- [bufnum, lnum, col, off] +--- "bufnum" is the buffer number. +--- "lnum" and "col" are the position in the buffer. The first +--- column is 1. +--- If the "off" number of a starting position is non-zero, it is +--- the offset in screen columns from the start of the character. +--- E.g., a position within a <Tab> or after the last character. +--- If the "off" number of an ending position is non-zero, it is +--- the offset of the character's first cell not included in the +--- selection, otherwise all its cells are included. +--- +--- Apart from the options supported by |getregion()|, {opts} also +--- supports the following: +--- +--- eol If |TRUE|, indicate positions beyond +--- the end of a line with "col" values +--- one more than the length of the line. +--- If |FALSE|, positions are limited +--- within their lines, and if a line is +--- empty or the selection is entirely +--- beyond the end of a line, a "col" +--- value of 0 is used for both positions. +--- (default: |FALSE|) +--- +--- @param pos1 table +--- @param pos2 table +--- @param opts? table +--- @return integer[][][] +function vim.fn.getregionpos(pos1, pos2, opts) end + --- The result is a String, which is type of register {regname}. --- The value will be one of: --- "v" for |charwise| text @@ -3628,11 +3665,11 @@ function vim.fn.getregtype(regname) end --- --- Examples: >vim --- echo getscriptinfo({'name': 'myscript'}) ---- echo getscriptinfo({'sid': 15}).variables +--- echo getscriptinfo({'sid': 15})[0].variables --- < --- --- @param opts? table ---- @return any +--- @return vim.fn.getscriptinfo.ret[] function vim.fn.getscriptinfo(opts) end --- If {tabnr} is not specified, then information about all the @@ -5993,7 +6030,7 @@ function vim.fn.min(expr) end function vim.fn.mkdir(name, flags, prot) end --- Return a string that indicates the current mode. ---- If [expr] is supplied and it evaluates to a non-zero Number or +--- If {expr} is supplied and it evaluates to a non-zero Number or --- a non-empty String (|non-zero-arg|), then the full mode is --- returned, otherwise only the first letter is returned. --- Also see |state()|. @@ -6520,6 +6557,9 @@ function vim.fn.prevnonblank(lnum) end --- echo printf("%1$*2$.*3$f", 1.4142135, 6, 2) --- < 1.41 --- +--- You will get an overflow error |E1510|, when the field-width +--- or precision will result in a string longer than 6400 chars. +--- --- *E1500* --- You cannot mix positional and non-positional arguments: >vim --- echo printf("%s%1$s", "One", "Two") @@ -6580,7 +6620,7 @@ function vim.fn.prevnonblank(lnum) end --- --- @param fmt any --- @param expr1? any ---- @return any +--- @return string function vim.fn.printf(fmt, expr1) end --- Returns the effective prompt text for buffer {buf}. {buf} can @@ -7259,6 +7299,7 @@ function vim.fn.screenstring(row, col) end --- When a match has been found its line number is returned. --- If there is no match a 0 is returned and the cursor doesn't --- move. No error message is given. +--- To get the matched string, use |matchbufline()|. --- --- {flags} is a String, which can contain these character flags: --- 'b' search Backward instead of forward @@ -8289,10 +8330,11 @@ function vim.fn.sha256(string) end --- Otherwise encloses {string} in single-quotes and replaces all --- "'" with "'\''". --- ---- If {special} is a |non-zero-arg|: ---- - Special items such as "!", "%", "#" and "<cword>" will be ---- preceded by a backslash. The backslash will be removed again ---- by the |:!| command. +--- The {special} argument adds additional escaping of keywords +--- used in Vim commands. If it is a |non-zero-arg|: +--- - Special items such as "!", "%", "#" and "<cword>" (as listed +--- in |expand()|) will be preceded by a backslash. +--- The backslash will be removed again by the |:!| command. --- - The <NL> character is escaped. --- --- If 'shell' contains "csh" in the tail: @@ -8796,7 +8838,8 @@ function vim.fn.sinh(expr) end --- Similar to using a |slice| "expr[start : end]", but "end" is --- used exclusive. And for a string the indexes are used as --- character indexes instead of byte indexes. ---- Also, composing characters are not counted. +--- Also, composing characters are treated as a part of the +--- preceding base character. --- When {end} is omitted the slice continues to the last item. --- When {end} is -1 the last item is omitted. --- Returns an empty value if {start} or {end} are invalid. @@ -9204,8 +9247,8 @@ function vim.fn.strcharlen(string) end --- of byte index and length. --- When {skipcc} is omitted or zero, composing characters are --- counted separately. ---- When {skipcc} set to 1, Composing characters are ignored, ---- similar to |slice()|. +--- When {skipcc} set to 1, composing characters are treated as a +--- part of the preceding base character, similar to |slice()|. --- When a character index is used where a character does not --- exist it is omitted and counted as one character. For --- example: >vim @@ -9225,7 +9268,7 @@ function vim.fn.strcharpart(src, start, len, skipcc) end --- in String {string}. --- When {skipcc} is omitted or zero, composing characters are --- counted separately. ---- When {skipcc} set to 1, Composing characters are ignored. +--- When {skipcc} set to 1, composing characters are ignored. --- |strcharlen()| always does this. --- --- Returns zero on error. @@ -9348,10 +9391,10 @@ function vim.fn.stridx(haystack, needle, start) end --- for infinite and NaN floating-point values representations --- which use |str2float()|. Strings are also dumped literally, --- only single quote is escaped, which does not allow using YAML ---- for parsing back binary strings. |eval()| should always work for ---- strings and floats though and this is the only official ---- method, use |msgpackdump()| or |json_encode()| if you need to ---- share data with other application. +--- for parsing back binary strings. |eval()| should always work +--- for strings and floats though, and this is the only official +--- method. Use |msgpackdump()| or |json_encode()| if you need to +--- share data with other applications. --- --- @param expr any --- @return string @@ -9747,6 +9790,10 @@ function vim.fn.synIDtrans(synID) end --- synconcealed(lnum, 5) [1, 'X', 2] --- synconcealed(lnum, 6) [0, '', 0] --- +--- Note: Doesn't consider |matchadd()| highlighting items, +--- since syntax and matching highlighting are two different +--- mechanisms |syntax-vs-match|. +--- --- @param lnum integer --- @param col integer --- @return {[1]: integer, [2]: string, [3]: integer} @@ -10591,17 +10638,16 @@ function vim.fn.win_move_statusline(nr, offset) end --- [1, 1], unless there is a tabline, then it is [2, 1]. --- {nr} can be the window number or the |window-ID|. Use zero --- for the current window. ---- Returns [0, 0] if the window cannot be found in the current ---- tabpage. +--- Returns [0, 0] if the window cannot be found. --- --- @param nr integer --- @return any function vim.fn.win_screenpos(nr) end ---- Move the window {nr} to a new split of the window {target}. ---- This is similar to moving to {target}, creating a new window ---- using |:split| but having the same contents as window {nr}, and ---- then closing {nr}. +--- Temporarily switch to window {target}, then move window {nr} +--- to a new split adjacent to {target}. +--- Unlike commands such as |:split|, no new windows are created +--- (the |window-ID| of window {nr} is unchanged after the move). --- --- Both {nr} and {target} can be window numbers or |window-ID|s. --- Both must be in the current tab page. @@ -10724,7 +10770,9 @@ function vim.fn.winline() end --- # the number of the last accessed window (where --- |CTRL-W_p| goes to). If there is no previous --- window or it is in another tab page 0 is ---- returned. +--- returned. May refer to the current window in +--- some cases (e.g. when evaluating 'statusline' +--- expressions). --- {N}j the number of the Nth window below the --- current window (where |CTRL-W_j| goes to). --- {N}k the number of the Nth window above the current diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index ee6d8ddf35..e00402ab3f 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -54,9 +54,10 @@ vim.v.cmdbang = ... --- @type string vim.v.collate = ... ---- Dictionary containing the most recent `complete-items` after ---- `CompleteDone`. Empty if the completion failed, or after ---- leaving and re-entering insert mode. +--- Dictionary containing the `complete-items` for the most +--- recently completed word after `CompleteDone`. Empty if the +--- completion failed, or after leaving and re-entering insert +--- mode. --- Note: Plugins can modify the value to emulate the builtin --- `CompleteDone` event behavior. --- @type any @@ -194,6 +195,7 @@ vim.v.errors = ... --- changed_window Is `v:true` if the event fired while --- changing window (or tab) on `DirChanged`. --- status Job status or exit code, -1 means "unknown". `TermClose` +--- reason Reason for completion being done. `CompleteDone` --- @type any vim.v.event = ... diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua index 13ad6cc58f..b41e298dd7 100644 --- a/runtime/lua/vim/_options.lua +++ b/runtime/lua/vim/_options.lua @@ -642,7 +642,7 @@ end --- @param t table<any,any> --- @param val any local function remove_one_item(t, val) - if vim.tbl_islist(t) then + if vim.islist(t) then local remove_index = nil for i, v in ipairs(t) do if v == val then diff --git a/runtime/lua/vim/_system.lua b/runtime/lua/vim/_system.lua index e97a5fc6c3..d603971495 100644 --- a/runtime/lua/vim/_system.lua +++ b/runtime/lua/vim/_system.lua @@ -18,6 +18,7 @@ local uv = vim.uv --- @field stderr? string --- @class vim.SystemState +--- @field cmd string[] --- @field handle? uv.uv_process_t --- @field timer? uv.uv_timer_t --- @field pid? integer @@ -56,6 +57,7 @@ local function close_handles(state) end --- @class vim.SystemObj +--- @field cmd string[] --- @field pid integer --- @field private _state vim.SystemState --- @field wait fun(self: vim.SystemObj, timeout?: integer): vim.SystemCompleted @@ -68,6 +70,7 @@ local SystemObj = {} --- @return vim.SystemObj local function new_systemobj(state) return setmetatable({ + cmd = state.cmd, pid = state.pid, _state = state, }, { __index = SystemObj }) diff --git a/runtime/lua/vim/_watch.lua b/runtime/lua/vim/_watch.lua index 97c5481ad1..02b3f536c2 100644 --- a/runtime/lua/vim/_watch.lua +++ b/runtime/lua/vim/_watch.lua @@ -200,11 +200,13 @@ function M.watchdirs(path, opts, callback) local max_depth = 100 for name, type in vim.fs.dir(path, { depth = max_depth }) do - local filepath = vim.fs.joinpath(path, name) - if type == 'directory' and not skip(filepath, opts) then - local handle = assert(uv.new_fs_event()) - handles[filepath] = handle - handle:start(filepath, {}, create_on_change(filepath)) + if type == 'directory' then + local filepath = vim.fs.joinpath(path, name) + if not skip(filepath, opts) then + local handle = assert(uv.new_fs_event()) + handles[filepath] = handle + handle:start(filepath, {}, create_on_change(filepath)) + end end end @@ -290,6 +292,10 @@ function M.fswatch(path, opts, callback) if data and #vim.trim(data) > 0 then vim.schedule(function() + if vim.fn.has('linux') == 1 and vim.startswith(data, 'Event queue overflow') then + data = 'inotify(7) limit reached, see :h fswatch-limitations for more info.' + end + vim.notify('fswatch: ' .. data, vim.log.levels.ERROR) end) end @@ -303,6 +309,8 @@ function M.fswatch(path, opts, callback) fswatch_output_handler(line, opts, callback) end end, + -- --latency is locale dependent but tostring() isn't and will always have '.' as decimal point. + env = { LC_NUMERIC = 'C' }, }) return function() diff --git a/runtime/lua/vim/deprecated/health.lua b/runtime/lua/vim/deprecated/health.lua new file mode 100644 index 0000000000..0f6b1f578c --- /dev/null +++ b/runtime/lua/vim/deprecated/health.lua @@ -0,0 +1,42 @@ +local M = {} +local health = vim.health + +local deprecated = {} + +function M.check() + if next(deprecated) == nil then + health.ok('No deprecated functions detected') + return + end + + for name, v in vim.spairs(deprecated) do + health.start('') + + local version, backtraces, alternative = v[1], v[2], v[3] + local major, minor = version:match('(%d+)%.(%d+)') + major, minor = tonumber(major), tonumber(minor) + local removal_version = string.format('nvim-%d.%d', major, minor) + local will_be_removed = vim.fn.has(removal_version) == 1 and 'was removed' or 'will be removed' + + local msg = ('%s is deprecated. Feature %s in Nvim %s'):format(name, will_be_removed, version) + local msg_alternative = alternative and ('use %s instead.'):format(alternative) + local advice = { msg_alternative } + table.insert(advice, backtraces) + advice = vim.iter(advice):flatten():totable() + health.warn(msg, advice) + end +end + +function M.add(name, version, backtrace, alternative) + if deprecated[name] == nil then + deprecated[name] = { version, { backtrace }, alternative } + return + end + + local it = vim.iter(deprecated[name][2]) + if it:find(backtrace) == nil then + table.insert(deprecated[name][2], backtrace) + end +end + +return M diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index d5075d7d3d..348204abb7 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -76,7 +76,7 @@ local M = {} --- before lower severities (e.g. ERROR is displayed before WARN). --- Options: --- - {reverse}? (boolean) Reverse sort order ---- (default: `false) +--- (default: `false`) --- @field severity_sort? boolean|{reverse?:boolean} --- @class (private) vim.diagnostic.OptsResolved @@ -152,6 +152,8 @@ local M = {} --- @field suffix? string|table|(fun(diagnostic:vim.Diagnostic,i:integer,total:integer): string, string) --- --- @field focus_id? string +--- +--- @field border? string see |nvim_open_win()|. --- @class vim.diagnostic.Opts.Underline --- @@ -239,6 +241,17 @@ local M = {} --- whole line the sign is placed in. --- @field linehl? table<vim.diagnostic.Severity,string> +-- TODO: inherit from `vim.diagnostic.Opts`, implement its fields. +--- Optional filters |kwargs|, or `nil` for all. +--- @class vim.diagnostic.Filter +--- @inlinedoc +--- +--- Diagnostic namespace, or `nil` for all. +--- @field ns_id? integer +--- +--- Buffer number, or 0 for current buffer, or `nil` for all buffers. +--- @field bufnr? integer + --- @nodoc --- @enum vim.diagnostic.Severity M.severity = { @@ -361,43 +374,46 @@ local function to_severity(severity) end --- @param severity vim.diagnostic.SeverityFilter ---- @param diagnostics vim.Diagnostic[] ---- @return vim.Diagnostic[] -local function filter_by_severity(severity, diagnostics) - if not severity then - return diagnostics - end - +--- @return fun(vim.Diagnostic):boolean +local function severity_predicate(severity) if type(severity) ~= 'table' then severity = assert(to_severity(severity)) - --- @param t vim.Diagnostic - return vim.tbl_filter(function(t) - return t.severity == severity - end, diagnostics) + ---@param d vim.Diagnostic + return function(d) + return d.severity == severity + end end - if severity.min or severity.max then --- @cast severity {min:vim.diagnostic.Severity,max:vim.diagnostic.Severity} local min_severity = to_severity(severity.min) or M.severity.HINT local max_severity = to_severity(severity.max) or M.severity.ERROR - --- @param t vim.Diagnostic - return vim.tbl_filter(function(t) - return t.severity <= min_severity and t.severity >= max_severity - end, diagnostics) + --- @param d vim.Diagnostic + return function(d) + return d.severity <= min_severity and d.severity >= max_severity + end end --- @cast severity vim.diagnostic.Severity[] - local severities = {} --- @type table<vim.diagnostic.Severity,true> for _, s in ipairs(severity) do severities[assert(to_severity(s))] = true end - --- @param t vim.Diagnostic - return vim.tbl_filter(function(t) - return severities[t.severity] - end, diagnostics) + --- @param d vim.Diagnostic + return function(d) + return severities[d.severity] + end +end + +--- @param severity vim.diagnostic.SeverityFilter +--- @param diagnostics vim.Diagnostic[] +--- @return vim.Diagnostic[] +local function filter_by_severity(severity, diagnostics) + if not severity then + return diagnostics + end + return vim.tbl_filter(severity_predicate(severity), diagnostics) end --- @param bufnr integer @@ -682,6 +698,13 @@ local function get_diagnostics(bufnr, opts, clamp) opts = opts or {} local namespace = opts.namespace + + if type(namespace) == 'number' then + namespace = { namespace } + end + + ---@cast namespace integer[] + local diagnostics = {} -- Memoized results of buf_line_count per bufnr @@ -696,10 +719,18 @@ local function get_diagnostics(bufnr, opts, clamp) end, }) + local match_severity = opts.severity and severity_predicate(opts.severity) + or function(_) + return true + end + ---@param b integer ---@param d vim.Diagnostic local function add(b, d) - if not opts.lnum or d.lnum == opts.lnum then + if + match_severity(d) + and (not opts.lnum or (opts.lnum >= d.lnum and opts.lnum <= (d.end_lnum or d.lnum))) + then if clamp and api.nvim_buf_is_loaded(b) then local line_count = buf_line_count[b] - 1 if @@ -742,15 +773,15 @@ local function get_diagnostics(bufnr, opts, clamp) end elseif bufnr == nil then for b, t in pairs(diagnostic_cache) do - add_all_diags(b, t[namespace] or {}) + for _, iter_namespace in ipairs(namespace) do + add_all_diags(b, t[iter_namespace] or {}) + end end else bufnr = get_bufnr(bufnr) - add_all_diags(bufnr, diagnostic_cache[bufnr][namespace] or {}) - end - - if opts.severity then - diagnostics = filter_by_severity(opts.severity, diagnostics) + for _, iter_namespace in ipairs(namespace) do + add_all_diags(bufnr, diagnostic_cache[bufnr][iter_namespace] or {}) + end end return diagnostics @@ -781,21 +812,52 @@ local function set_list(loclist, opts) end end +--- Jump to the diagnostic with the highest severity. First sort the +--- diagnostics by severity. The first diagnostic then contains the highest severity, and we can +--- discard all diagnostics with a lower severity. +--- @param diagnostics vim.Diagnostic[] +local function filter_highest(diagnostics) + table.sort(diagnostics, function(a, b) + return a.severity < b.severity + end) + + -- Find the first diagnostic where the severity does not match the highest severity, and remove + -- that element and all subsequent elements from the array + local worst = (diagnostics[1] or {}).severity + local len = #diagnostics + for i = 2, len do + if diagnostics[i].severity ~= worst then + for j = i, len do + diagnostics[j] = nil + end + break + end + end +end + --- @param position {[1]: integer, [2]: integer} --- @param search_forward boolean --- @param bufnr integer --- @param opts vim.diagnostic.GotoOpts ---- @param namespace integer +--- @param namespace integer[]|integer --- @return vim.Diagnostic? local function next_diagnostic(position, search_forward, bufnr, opts, namespace) position[1] = position[1] - 1 bufnr = get_bufnr(bufnr) local wrap = if_nil(opts.wrap, true) - local line_count = api.nvim_buf_line_count(bufnr) - local diagnostics = - get_diagnostics(bufnr, vim.tbl_extend('keep', opts, { namespace = namespace }), true) + + local get_opts = vim.deepcopy(opts) + get_opts.namespace = get_opts.namespace or namespace + + local diagnostics = get_diagnostics(bufnr, get_opts, true) + + if opts._highest then + filter_highest(diagnostics) + end + local line_diagnostics = diagnostic_lines(diagnostics) + local line_count = api.nvim_buf_line_count(bufnr) for i = 0, line_count do local offset = i * (search_forward and 1 or -1) local lnum = position[1] + offset @@ -814,14 +876,14 @@ local function next_diagnostic(position, search_forward, bufnr, opts, namespace) return a.col < b.col end is_next = function(d) - return math.min(d.col, line_length - 1) > position[2] + return math.min(d.col, math.max(line_length - 1, 0)) > position[2] end else sort_diagnostics = function(a, b) return a.col > b.col end is_next = function(d) - return math.min(d.col, line_length - 1) < position[2] + return math.min(d.col, math.max(line_length - 1, 0)) < position[2] end end table.sort(line_diagnostics[lnum], sort_diagnostics) @@ -952,7 +1014,7 @@ function M.set(namespace, bufnr, diagnostics, opts) bufnr = { bufnr, 'n' }, diagnostics = { diagnostics, - vim.tbl_islist, + vim.islist, 'a list of diagnostics', }, opts = { opts, 't', true }, @@ -1115,10 +1177,10 @@ end --- A table with the following keys: --- @class vim.diagnostic.GetOpts --- ---- Limit diagnostics to the given namespace. ---- @field namespace? integer +--- Limit diagnostics to one or more namespaces. +--- @field namespace? integer[]|integer --- ---- Limit diagnostics to the given line number. +--- Limit diagnostics to those spanning the specified line number. --- @field lnum? integer --- --- See |diagnostic-severity|. @@ -1137,7 +1199,11 @@ end --- @field wrap? boolean --- --- See |diagnostic-severity|. ---- @field severity vim.diagnostic.Severity +--- @field severity? vim.diagnostic.SeverityFilter +--- +--- Go to the diagnostic with the highest severity. +--- (default: `false`) +--- @field package _highest? boolean --- --- If `true`, call |vim.diagnostic.open_float()| after moving. --- If a table, pass the table as the {opts} parameter to |vim.diagnostic.open_float()|. @@ -1164,7 +1230,7 @@ M.handlers.signs = { bufnr = { bufnr, 'n' }, diagnostics = { diagnostics, - vim.tbl_islist, + vim.islist, 'a list of diagnostics', }, opts = { opts, 't', true }, @@ -1213,9 +1279,7 @@ M.handlers.signs = { vim.deprecate( 'Defining diagnostic signs with :sign-define or sign_define()', 'vim.diagnostic.config()', - '0.12', - nil, - false + '0.12' ) if not opts.signs.text then @@ -1287,7 +1351,7 @@ M.handlers.underline = { bufnr = { bufnr, 'n' }, diagnostics = { diagnostics, - vim.tbl_islist, + vim.islist, 'a list of diagnostics', }, opts = { opts, 't', true }, @@ -1360,7 +1424,7 @@ M.handlers.virtual_text = { bufnr = { bufnr, 'n' }, diagnostics = { diagnostics, - vim.tbl_islist, + vim.islist, 'a list of diagnostics', }, opts = { opts, 't', true }, @@ -1481,7 +1545,7 @@ end --- diagnostics, use |vim.diagnostic.reset()|. --- --- To hide diagnostics and prevent them from re-displaying, use ---- |vim.diagnostic.disable()|. +--- |vim.diagnostic.enable()|. --- ---@param namespace integer? Diagnostic namespace. When omitted, hide --- diagnostics from all namespaces. @@ -1506,25 +1570,32 @@ function M.hide(namespace, bufnr) end end ---- Check whether diagnostics are disabled in a given buffer. +--- Check whether diagnostics are enabled. --- ----@param bufnr integer? Buffer number, or 0 for current buffer. ----@param namespace integer? Diagnostic namespace. When omitted, checks if ---- all diagnostics are disabled in {bufnr}. ---- Otherwise, only checks if diagnostics from ---- {namespace} are disabled. ----@return boolean -function M.is_disabled(bufnr, namespace) - bufnr = get_bufnr(bufnr) - if namespace and M.get_namespace(namespace).disabled then - return true +--- @param filter vim.diagnostic.Filter? +--- @return boolean +--- @since 12 +function M.is_enabled(filter) + filter = filter or {} + if filter.ns_id and M.get_namespace(filter.ns_id).disabled then + return false + elseif filter.bufnr == nil then + -- See enable() logic. + return vim.tbl_isempty(diagnostic_disabled) and not diagnostic_disabled[1] end + local bufnr = get_bufnr(filter.bufnr) if type(diagnostic_disabled[bufnr]) == 'table' then - return diagnostic_disabled[bufnr][namespace] + return not diagnostic_disabled[bufnr][filter.ns_id] end - return diagnostic_disabled[bufnr] ~= nil + return diagnostic_disabled[bufnr] == nil +end + +--- @deprecated use `vim.diagnostic.is_enabled()` +function M.is_disabled(bufnr, namespace) + vim.deprecate('vim.diagnostic.is_disabled()', 'vim.diagnostic.is_enabled()', '0.12') + return not M.is_enabled { bufnr = bufnr or 0, ns_id = namespace } end --- Display diagnostics for the given namespace and buffer. @@ -1547,7 +1618,7 @@ function M.show(namespace, bufnr, diagnostics, opts) diagnostics = { diagnostics, function(v) - return v == nil or vim.tbl_islist(v) + return v == nil or vim.islist(v) end, 'a list of diagnostics', }, @@ -1570,7 +1641,7 @@ function M.show(namespace, bufnr, diagnostics, opts) return end - if M.is_disabled(bufnr, namespace) then + if not M.is_enabled { bufnr = bufnr or 0, ns_id = namespace } then return end @@ -1668,7 +1739,7 @@ function M.open_float(opts, ...) if scope == 'line' then --- @param d vim.Diagnostic diagnostics = vim.tbl_filter(function(d) - return d.lnum == lnum + return lnum >= d.lnum and lnum <= d.end_lnum end, diagnostics) elseif scope == 'cursor' then -- LSP servers can send diagnostics with `end_col` past the length of the line @@ -1912,71 +1983,95 @@ function M.setloclist(opts) set_list(true, opts) end ---- Disable diagnostics in the given buffer. ---- ----@param bufnr integer? Buffer number, or 0 for current buffer. When ---- omitted, disable diagnostics in all buffers. ----@param namespace integer? Only disable diagnostics for the given namespace. +--- @deprecated use `vim.diagnostic.enable(false, …)` function M.disable(bufnr, namespace) - vim.validate({ bufnr = { bufnr, 'n', true }, namespace = { namespace, 'n', true } }) - if bufnr == nil then - if namespace == nil then - -- Disable everything (including as yet non-existing buffers and - -- namespaces) by setting diagnostic_disabled to an empty table and set - -- its metatable to always return true. This metatable is removed - -- in enable() - diagnostic_disabled = setmetatable({}, { - __index = function() - return true - end, - }) - else - local ns = M.get_namespace(namespace) - ns.disabled = true - end + vim.deprecate('vim.diagnostic.disable()', 'vim.diagnostic.enable(false, …)', '0.12') + M.enable(false, { bufnr = bufnr, ns_id = namespace }) +end + +--- Enables or disables diagnostics. +--- +--- To "toggle", pass the inverse of `is_enabled()`: +--- +--- ```lua +--- vim.diagnostic.enable(not vim.diagnostic.is_enabled()) +--- ``` +--- +--- @param enable (boolean|nil) true/nil to enable, false to disable +--- @param filter vim.diagnostic.Filter? +function M.enable(enable, filter) + -- Deprecated signature. Drop this in 0.12 + local legacy = (enable or filter) + and vim.tbl_contains({ 'number', 'nil' }, type(enable)) + and vim.tbl_contains({ 'number', 'nil' }, type(filter)) + + if legacy then + vim.deprecate( + 'vim.diagnostic.enable(buf:number, namespace:number)', + 'vim.diagnostic.enable(enable:boolean, filter:table)', + '0.12' + ) + + vim.validate({ + enable = { enable, 'n', true }, -- Legacy `bufnr` arg. + filter = { filter, 'n', true }, -- Legacy `namespace` arg. + }) + + local ns_id = type(filter) == 'number' and filter or nil + filter = {} + filter.ns_id = ns_id + filter.bufnr = type(enable) == 'number' and enable or nil + enable = true else - bufnr = get_bufnr(bufnr) - if namespace == nil then - diagnostic_disabled[bufnr] = true - else - if type(diagnostic_disabled[bufnr]) ~= 'table' then - diagnostic_disabled[bufnr] = {} - end - diagnostic_disabled[bufnr][namespace] = true - end + filter = filter or {} + vim.validate({ + enable = { enable, 'b', true }, + filter = { filter, 't', true }, + }) end - M.hide(namespace, bufnr) -end + enable = enable == nil and true or enable + local bufnr = filter.bufnr ---- Enable diagnostics in the given buffer. ---- ----@param bufnr integer? Buffer number, or 0 for current buffer. When ---- omitted, enable diagnostics in all buffers. ----@param namespace integer? Only enable diagnostics for the given namespace. -function M.enable(bufnr, namespace) - vim.validate({ bufnr = { bufnr, 'n', true }, namespace = { namespace, 'n', true } }) if bufnr == nil then - if namespace == nil then - -- Enable everything by setting diagnostic_disabled to an empty table - diagnostic_disabled = {} + if filter.ns_id == nil then + diagnostic_disabled = ( + enable + -- Enable everything by setting diagnostic_disabled to an empty table. + and {} + -- Disable everything (including as yet non-existing buffers and namespaces) by setting + -- diagnostic_disabled to an empty table and set its metatable to always return true. + or setmetatable({}, { + __index = function() + return true + end, + }) + ) else - local ns = M.get_namespace(namespace) - ns.disabled = false + local ns = M.get_namespace(filter.ns_id) + ns.disabled = not enable end else bufnr = get_bufnr(bufnr) - if namespace == nil then - diagnostic_disabled[bufnr] = nil + if filter.ns_id == nil then + diagnostic_disabled[bufnr] = (not enable) and true or nil else if type(diagnostic_disabled[bufnr]) ~= 'table' then - return + if enable then + return + else + diagnostic_disabled[bufnr] = {} + end end - diagnostic_disabled[bufnr][namespace] = nil + diagnostic_disabled[bufnr][filter.ns_id] = (not enable) and true or nil end end - M.show(namespace, bufnr) + if enable then + M.show(filter.ns_id, bufnr) + else + M.hide(filter.ns_id, bufnr) + end end --- Parse a diagnostic from a string. @@ -2059,7 +2154,7 @@ function M.toqflist(diagnostics) vim.validate({ diagnostics = { diagnostics, - vim.tbl_islist, + vim.islist, 'a list of diagnostics', }, }) @@ -2099,7 +2194,7 @@ function M.fromqflist(list) vim.validate({ list = { list, - vim.tbl_islist, + vim.islist, 'a list of quickfix items', }, }) diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index fba76f93b2..d1fdd0aa16 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -14,7 +14,8 @@ local M = {} local function starsetf(ft, opts) return { function(path, bufnr) - local f = type(ft) == 'function' and ft(path, bufnr) or ft + -- Note: when `ft` is a function its return value may be nil. + local f = type(ft) ~= 'function' and ft or ft(path, bufnr) if not vim.g.ft_ignore_pat then return f end @@ -236,6 +237,7 @@ local extension = { bbclass = 'bitbake', bl = 'blank', blp = 'blueprint', + bp = 'bp', bsd = 'bsdl', bsdl = 'bsdl', bst = 'bst', @@ -246,13 +248,15 @@ local extension = { bzl = 'bzl', bazel = 'bzl', BUILD = 'bzl', + mdh = 'c', + epro = 'c', qc = 'c', cabal = 'cabal', cairo = 'cairo', capnp = 'capnp', cdc = 'cdc', cdl = 'cdl', - toc = 'cdrtoc', + toc = detect_line1('\\contentsline', 'tex', 'cdrtoc'), cfc = 'cf', cfm = 'cf', cfi = 'cf', @@ -282,6 +286,7 @@ local extension = { cbl = 'cobol', atg = 'coco', recipe = 'conaryrecipe', + ctags = 'conf', hook = function(path, bufnr) return M._getline(bufnr, 1) == '[Trigger]' and 'confini' or nil end, @@ -335,6 +340,7 @@ local extension = { si = 'cuplsim', cyn = 'cynpp', cypher = 'cypher', + dfy = 'dafny', dart = 'dart', drt = 'dart', ds = 'datascript', @@ -366,6 +372,7 @@ local extension = { dtsi = 'dts', dtso = 'dts', its = 'dts', + keymap = 'dts', dylan = 'dylan', intr = 'dylanintr', lid = 'dylanlid', @@ -465,6 +472,7 @@ local extension = { glsl = 'glsl', gn = 'gn', gni = 'gn', + gnuplot = 'gnuplot', gpi = 'gnuplot', go = 'go', gp = 'gp', @@ -502,7 +510,16 @@ local extension = { vc = 'hercules', heex = 'heex', hex = 'hex', + ['a43'] = 'hex', + ['a90'] = 'hex', ['h32'] = 'hex', + ['h80'] = 'hex', + ['h86'] = 'hex', + ihex = 'hex', + ihe = 'hex', + ihx = 'hex', + int = 'hex', + mcs = 'hex', hjson = 'hjson', m3u = 'hlsplaylist', m3u8 = 'hlsplaylist', @@ -530,6 +547,7 @@ local extension = { inf = 'inform', INF = 'inform', ii = 'initng', + inko = 'inko', inp = detect.inp, ms = detect_seq(detect.nroff, 'xmath'), iss = 'iss', @@ -554,6 +572,7 @@ local extension = { jsx = 'javascriptreact', clp = 'jess', jgr = 'jgraph', + jjdescription = 'jj', j73 = 'jovial', jov = 'jovial', jovial = 'jovial', @@ -565,7 +584,14 @@ local extension = { geojson = 'json', webmanifest = 'json', ipynb = 'json', + ['jupyterlab-settings'] = 'json', + ['sublime-project'] = 'json', + ['sublime-settings'] = 'json', + ['sublime-workspace'] = 'json', ['json-patch'] = 'json', + bd = 'json', + bda = 'json', + xci = 'json', json5 = 'json5', jsonc = 'jsonc', jsonl = 'jsonl', @@ -609,12 +635,13 @@ local extension = { el = 'lisp', lsp = 'lisp', asd = 'lisp', + stsg = 'lisp', lt = 'lite', lite = 'lite', livemd = 'livebook', lgt = 'logtalk', lotos = 'lotos', - lot = 'lotos', + lot = detect_line1('\\contentsline', 'tex', 'lotos'), lout = 'lout', lou = 'lout', ulpc = 'lpc', @@ -625,6 +652,7 @@ local extension = { nse = 'lua', rockspec = 'lua', lua = 'lua', + tlu = 'lua', luau = 'luau', lrc = 'lyrics', m = detect.m, @@ -644,12 +672,12 @@ local extension = { mws = 'maple', mpl = 'maple', mv = 'maple', - mkdn = 'markdown', - md = 'markdown', - mdwn = 'markdown', - mkd = 'markdown', - markdown = 'markdown', - mdown = 'markdown', + mkdn = detect.markdown, + md = detect.markdown, + mdwn = detect.markdown, + mkd = detect.markdown, + markdown = detect.markdown, + mdown = detect.markdown, mhtml = 'mason', comp = 'mason', mason = 'mason', @@ -744,12 +772,19 @@ local extension = { ora = 'ora', org = 'org', org_archive = 'org', + pandoc = 'pandoc', + pdk = 'pandoc', + pd = 'pandoc', + pdc = 'pandoc', pxsl = 'papp', papp = 'papp', pxml = 'papp', pas = 'pascal', - lpr = 'pascal', + lpr = detect_line1('<%?xml', 'xml', 'pascal'), dpr = 'pascal', + txtpb = 'pbtxt', + textproto = 'pbtxt', + textpb = 'pbtxt', pbtxt = 'pbtxt', g = 'pccts', pcmk = 'pcmk', @@ -810,6 +845,7 @@ local extension = { psf = 'psf', psl = 'psl', pug = 'pug', + purs = 'purescript', arr = 'pyret', pxd = 'pyrex', pyx = 'pyrex', @@ -869,6 +905,7 @@ local extension = { Snw = 'rnoweb', robot = 'robot', resource = 'robot', + roc = 'roc', ron = 'ron', rsc = 'routeros', x = 'rpcgen', @@ -911,11 +948,13 @@ local extension = { sexp = 'sexplib', bash = detect.bash, bats = detect.bash, + cygport = detect.bash, ebuild = detect.bash, eclass = detect.bash, env = detect.sh, ksh = detect.ksh, sh = detect.sh, + mdd = 'sh', sieve = 'sieve', siv = 'sieve', sig = detect.sig, @@ -933,6 +972,7 @@ local extension = { cdf = 'skill', sl = 'slang', ice = 'slice', + slint = 'slint', score = 'slrnsc', sol = 'solidity', smali = 'smali', @@ -983,6 +1023,8 @@ local extension = { mata = 'stata', ado = 'stata', stp = 'stp', + styl = 'stylus', + stylus = 'stylus', quark = 'supercollider', sface = 'surface', svelte = 'svelte', @@ -1005,6 +1047,7 @@ local extension = { tk = 'tcl', jacl = 'tcl', tl = 'teal', + templ = 'templ', tmpl = 'template', ti = 'terminfo', dtx = 'tex', @@ -1012,6 +1055,26 @@ local extension = { bbl = 'tex', latex = 'tex', sty = 'tex', + pgf = 'tex', + nlo = 'tex', + nls = 'tex', + thm = 'tex', + eps_tex = 'tex', + pygtex = 'tex', + pygstyle = 'tex', + clo = 'tex', + aux = 'tex', + brf = 'tex', + ind = 'tex', + lof = 'tex', + loe = 'tex', + nav = 'tex', + vrb = 'tex', + ins = 'tex', + tikz = 'tex', + bbx = 'tex', + cbx = 'tex', + beamer = 'tex', cls = detect.cls, texi = 'texinfo', txi = 'texinfo', @@ -1035,6 +1098,7 @@ local extension = { mts = 'typescript', cts = 'typescript', tsx = 'typescriptreact', + tsp = 'typespec', uc = 'uc', uit = 'uil', uil = 'uil', @@ -1062,6 +1126,7 @@ local extension = { vdmrt = 'vdmrt', vdmsl = 'vdmsl', vdm = 'vdmsl', + vto = 'vento', vr = 'vera', vri = 'vera', vrh = 'vera', @@ -1110,6 +1175,14 @@ local extension = { csproj = 'xml', wpl = 'xml', xmi = 'xml', + xpr = 'xml', + xpfm = 'xml', + spfm = 'xml', + bxml = 'xml', + xcu = 'xml', + xlb = 'xml', + xlc = 'xml', + xba = 'xml', xpm = detect_line1('XPM2', 'xpm2', 'xpm'), xpm2 = 'xpm2', xqy = 'xquery', @@ -1127,6 +1200,7 @@ local extension = { yml = 'yaml', yaml = 'yaml', eyaml = 'yaml', + mplstyle = 'yaml', yang = 'yang', yuck = 'yuck', z8a = 'z8a', @@ -1136,6 +1210,8 @@ local extension = { zut = 'zimbutempl', zs = 'zserio', zsh = 'zsh', + zunit = 'zsh', + ['zsh-theme'] = 'zsh', vala = 'vala', web = detect.web, pl = detect.pl, @@ -1229,7 +1305,12 @@ local filename = { ['/etc/default/cdrdao'] = 'cdrdaoconf', ['/etc/defaults/cdrdao'] = 'cdrdaoconf', ['cfengine.conf'] = 'cfengine', + cgdbrc = 'cgdbrc', + ['init.trans'] = 'clojure', + ['.trans'] = 'clojure', ['CMakeLists.txt'] = 'cmake', + ['CMakeCache.txt'] = 'cmakecache', + ['.cling_history'] = 'cpp', ['.alias'] = detect.csh, ['.cshrc'] = detect.csh, ['.login'] = detect.csh, @@ -1237,6 +1318,12 @@ local filename = { ['csh.login'] = detect.csh, ['csh.logout'] = detect.csh, ['auto.master'] = 'conf', + ['texdoc.cnf'] = 'conf', + ['.x11vncrc'] = 'conf', + ['.chktexrc'] = 'conf', + ['.ripgreprc'] = 'conf', + ripgreprc = 'conf', + ['.mbsyncrc'] = 'conf', ['configure.in'] = 'config', ['configure.ac'] = 'config', crontab = 'crontab', @@ -1262,12 +1349,31 @@ local filename = { npmrc = 'dosini', ['/etc/yum.conf'] = 'dosini', ['.npmrc'] = 'dosini', - ['/etc/pacman.conf'] = 'confini', + ['pip.conf'] = 'dosini', + ['setup.cfg'] = 'dosini', + ['pudb.cfg'] = 'dosini', + ['.coveragerc'] = 'dosini', + ['.pypirc'] = 'dosini', + ['.pylintrc'] = 'dosini', + ['pylintrc'] = 'dosini', + ['.replyrc'] = 'dosini', + ['.gitlint'] = 'dosini', + ['.oelint.cfg'] = 'dosini', + ['psprint.conf'] = 'dosini', + sofficerc = 'dosini', + ['mimeapps.list'] = 'dosini', + ['.wakatime.cfg'] = 'dosini', + ['nfs.conf'] = 'dosini', + ['nfsmount.conf'] = 'dosini', + ['.notmuch-config'] = 'dosini', + ['pacman.conf'] = 'confini', + ['paru.conf'] = 'confini', ['mpv.conf'] = 'confini', dune = 'dune', jbuild = 'dune', ['dune-workspace'] = 'dune', ['dune-project'] = 'dune', + Earthfile = 'earthfile', ['.editorconfig'] = 'editorconfig', ['elinks.conf'] = 'elinks', ['mix.lock'] = 'elixir', @@ -1302,7 +1408,7 @@ local filename = { ['.gnashpluginrc'] = 'gnash', gnashpluginrc = 'gnash', gnashrc = 'gnash', - ['.gnuplot'] = 'gnuplot', + ['.gnuplot_history'] = 'gnuplot', ['go.sum'] = 'gosum', ['go.work.sum'] = 'gosum', ['go.work'] = 'gowork', @@ -1328,6 +1434,10 @@ local filename = { ['/etc/host.conf'] = 'hostconf', ['/etc/hosts.allow'] = 'hostsaccess', ['/etc/hosts.deny'] = 'hostsaccess', + ['hyprland.conf'] = 'hyprlang', + ['hyprpaper.conf'] = 'hyprlang', + ['hypridle.conf'] = 'hyprlang', + ['hyprlock.conf'] = 'hyprlang', ['/.icewm/menu'] = 'icemenu', ['.indent.pro'] = 'indent', indentrc = 'indent', @@ -1335,17 +1445,21 @@ local filename = { ['ipf.conf'] = 'ipfilter', ['ipf6.conf'] = 'ipfilter', ['ipf.rules'] = 'ipfilter', + ['.node_repl_history'] = 'javascript', ['Pipfile.lock'] = 'json', ['.firebaserc'] = 'json', ['.prettierrc'] = 'json', ['.stylelintrc'] = 'json', + ['flake.lock'] = 'json', ['.babelrc'] = 'jsonc', ['.eslintrc'] = 'jsonc', ['.hintrc'] = 'jsonc', + ['.jscsrc'] = 'jsonc', ['.jsfmtrc'] = 'jsonc', ['.jshintrc'] = 'jsonc', ['.luaurc'] = 'jsonc', ['.swrc'] = 'jsonc', + ['.vsconfig'] = 'jsonc', ['.justfile'] = 'just', Kconfig = 'kconfig', ['Kconfig.debug'] = 'kconfig', @@ -1365,10 +1479,14 @@ local filename = { ['.lsl'] = detect.lsl, ['.busted'] = 'lua', ['.luacheckrc'] = 'lua', + ['.lua_history'] = 'lua', + ['config.ld'] = 'lua', + ['rock_manifest'] = 'lua', ['lynx.cfg'] = 'lynx', ['m3overrides'] = 'm3build', ['m3makefile'] = 'm3build', ['cm3.cfg'] = 'm3quake', + ['.m4_history'] = 'm4', ['.followup'] = 'mail', ['.article'] = 'mail', ['.letter'] = 'mail', @@ -1376,6 +1494,7 @@ local filename = { ['/etc/mail/aliases'] = 'mailaliases', mailcap = 'mailcap', ['.mailcap'] = 'mailcap', + Kbuild = 'make', ['/etc/man.conf'] = 'manconf', ['man.config'] = 'manconf', ['maxima-init.mac'] = 'maxima', @@ -1389,6 +1508,8 @@ local filename = { ['mplayer.conf'] = 'mplayerconf', mrxvtrc = 'mrxvtrc', ['.mrxvtrc'] = 'mrxvtrc', + ['.msmtprc'] = 'msmtp', + ['.mysql_history'] = 'mysql', ['/etc/nanorc'] = 'nanorc', Neomuttrc = 'neomuttrc', ['.netrc'] = 'netrc', @@ -1397,6 +1518,7 @@ local filename = { ['.octaverc'] = 'octave', octaverc = 'octave', ['octave.conf'] = 'octave', + ['.ondirrc'] = 'ondir', opam = 'opam', ['pacman.log'] = 'pacmanlog', ['/etc/pam.conf'] = 'pamconf', @@ -1441,8 +1563,11 @@ local filename = { ['MANIFEST.in'] = 'pymanifest', ['.pythonstartup'] = 'python', ['.pythonrc'] = 'python', + ['.python_history'] = 'python', + ['.jline-jython.history'] = 'python', SConstruct = 'python', qmldir = 'qmldir', + ['.Rhistory'] = 'r', ['.Rprofile'] = 'r', Rprofile = 'r', ['Rprofile.site'] = 'r', @@ -1452,12 +1577,16 @@ local filename = { ['.inputrc'] = 'readline', ['.reminders'] = 'remind', ['requirements.txt'] = 'requirements', + ['constraints.txt'] = 'requirements', + ['requirements.in'] = 'requirements', ['resolv.conf'] = 'resolv', ['robots.txt'] = 'robots', Gemfile = 'ruby', Puppetfile = 'ruby', ['.irbrc'] = 'ruby', irbrc = 'ruby', + ['.irb_history'] = 'ruby', + irb_history = 'ruby', Vagrantfile = 'ruby', ['smb.conf'] = 'samba', screenrc = 'screen', @@ -1467,10 +1596,15 @@ local filename = { ['/etc/services'] = 'services', ['/etc/serial.conf'] = 'setserial', ['/etc/udev/cdsymlinks.conf'] = 'sh', + ['.ash_history'] = 'sh', + ['makepkg.conf'] = 'sh', + ['.makepkg.conf'] = 'sh', + ['user-dirs.dirs'] = 'sh', + ['user-dirs.defaults'] = 'sh', + ['.xprofile'] = 'sh', ['bash.bashrc'] = detect.bash, bashrc = detect.bash, ['.bashrc'] = detect.bash, - ['.env'] = detect.sh, ['.kshrc'] = detect.ksh, ['.profile'] = detect.sh, ['/etc/profile'] = detect.sh, @@ -1484,6 +1618,7 @@ local filename = { ['/etc/slp.spi'] = 'slpspi', ['.slrnrc'] = 'slrnrc', ['sendmail.cf'] = 'sm', + ['.sqlite_history'] = 'sql', ['squid.conf'] = 'squid', ['ssh_config'] = 'sshconfig', ['sshd_config'] = 'sshdconfig', @@ -1496,7 +1631,10 @@ local filename = { ['undo.data'] = 'taskdata', ['.tclshrc'] = 'tcl', ['.wishrc'] = 'tcl', + ['.tclsh-history'] = 'tcl', ['tclsh.rc'] = 'tcl', + ['.xsctcmdhistory'] = 'tcl', + ['.xsdbcmdhistory'] = 'tcl', ['texmf.cnf'] = 'texmf', COPYING = 'text', README = 'text', @@ -1513,13 +1651,17 @@ local filename = { ['Gopkg.lock'] = 'toml', ['/.cargo/credentials'] = 'toml', ['Cargo.lock'] = 'toml', + ['.black'] = 'toml', + black = detect_line1('tool%.black', 'toml', nil), ['trustees.conf'] = 'trustees', + ['.ts_node_repl_history'] = 'typescript', ['/etc/udev/udev.conf'] = 'udevconf', ['/etc/updatedb.conf'] = 'updatedb', ['fdrupstream.log'] = 'upstreamlog', vgrindefs = 'vgrindefs', ['.exrc'] = 'vim', ['_exrc'] = 'vim', + ['.netrwhist'] = 'vim', ['_viminfo'] = 'viminfo', ['.viminfo'] = 'viminfo', ['.wgetrc'] = 'wget', @@ -1541,15 +1683,20 @@ local filename = { fglrxrc = 'xml', ['/etc/blkid.tab'] = 'xml', ['/etc/blkid.tab.old'] = 'xml', + ['fonts.conf'] = 'xml', ['.clangd'] = 'yaml', ['.clang-format'] = 'yaml', ['.clang-tidy'] = 'yaml', + ['yarn.lock'] = 'yaml', + matplotlibrc = 'yaml', + zathurarc = 'zathurarc', ['/etc/zprofile'] = 'zsh', ['.zlogin'] = 'zsh', ['.zlogout'] = 'zsh', ['.zshrc'] = 'zsh', ['.zprofile'] = 'zsh', ['.zcompdump'] = 'zsh', + ['.zsh_history'] = 'zsh', ['.zshenv'] = 'zsh', ['.zfbfmarks'] = 'zsh', -- END FILENAME @@ -1599,9 +1746,8 @@ local pattern = { ['.*%.blade%.php'] = 'blade', ['bzr_log%..*'] = 'bzr', ['.*enlightenment/.*%.cfg'] = 'c', - ['${HOME}/cabal%.config'] = 'cabalconfig', - ['${HOME}/%.config/cabal/config'] = 'cabalconfig', - ['${XDG_CONFIG_HOME}/cabal/config'] = 'cabalconfig', + ['.*/%.cabal/config'] = 'cabalconfig', + ['.*/cabal/config'] = 'cabalconfig', ['cabal%.project%..*'] = starsetf('cabalproject'), ['.*/%.calendar/.*'] = starsetf('calendar'), ['.*/share/calendar/.*/calendar%..*'] = starsetf('calendar'), @@ -1619,6 +1765,7 @@ local pattern = { }, ['[cC]hange[lL]og.*'] = starsetf(detect.changelog), ['.*%.%.ch'] = 'chill', + ['.*/etc/translate%-shell'] = 'clojure', ['.*%.cmake%.in'] = 'cmake', -- */cmus/rc and */.cmus/rc ['.*/%.?cmus/rc'] = 'cmusrc', @@ -1653,8 +1800,12 @@ local pattern = { ['php%.ini%-.*'] = 'dosini', ['.*/%.aws/config'] = 'confini', ['.*/%.aws/credentials'] = 'confini', - ['.*/etc/pacman%.conf'] = 'confini', ['.*/etc/yum%.conf'] = 'dosini', + ['.*/lxqt/.*%.conf'] = 'dosini', + ['.*/screengrab/.*%.conf'] = 'dosini', + ['.*/bpython/config'] = 'dosini', + ['.*/mypy/config'] = 'dosini', + ['.*/flatpak/repo/config'] = 'dosini', ['.*lvs'] = 'dracula', ['.*lpe'] = 'dracula', ['.*/dtrace/.*%.d'] = 'dtrace', @@ -1745,12 +1896,14 @@ local pattern = { ['.*%.[Ss][Uu][Bb]'] = 'krl', ['lilo%.conf.*'] = starsetf('lilo'), ['.*/etc/logcheck/.*%.d.*/.*'] = starsetf('logcheck'), + ['.*/ldscripts/.*'] = 'ld', ['.*lftp/rc'] = 'lftp', ['.*/%.libao'] = 'libao', ['.*/etc/libao%.conf'] = 'libao', ['.*/etc/.*limits%.conf'] = 'limits', ['.*/etc/limits'] = 'limits', ['.*/etc/.*limits%.d/.*%.conf'] = 'limits', + ['.*/supertux2/config'] = 'lisp', ['.*/LiteStep/.*/.*%.rc'] = 'litestep', ['.*/etc/login%.access'] = 'loginaccess', ['.*/etc/login%.defs'] = 'logindefs', @@ -1889,6 +2042,7 @@ local pattern = { ['.*%.[1-9]'] = detect.nroff, ['.*%.ml%.cppo'] = 'ocaml', ['.*%.mli%.cppo'] = 'ocaml', + ['.*/octave/history'] = 'octave', ['.*%.opam%.template'] = 'opam', ['.*/openvpn/.*/.*%.conf'] = 'openvpn', ['.*%.[Oo][Pp][Ll]'] = 'opl', @@ -1918,6 +2072,9 @@ local pattern = { ['.*/queries/.*%.scm'] = 'query', -- treesitter queries (Neovim only) ['.*,v'] = 'rcs', ['%.reminders.*'] = starsetf('remind'), + ['.*%-requirements%.txt'] = 'requirements', + ['requirements/.*%.txt'] = 'requirements', + ['requires/.*%.txt'] = 'requirements', ['[rR]akefile.*'] = starsetf('ruby'), ['[rR]antfile'] = 'ruby', ['[rR]akefile'] = 'ruby', @@ -1927,7 +2084,9 @@ local pattern = { ['.*/etc/services'] = 'services', ['.*/etc/serial%.conf'] = 'setserial', ['.*/etc/udev/cdsymlinks%.conf'] = 'sh', + ['.*/neofetch/config%.conf'] = 'sh', ['%.bash[_%-]aliases'] = detect.bash, + ['%.bash[_%-]history'] = detect.bash, ['%.bash[_%-]logout'] = detect.bash, ['%.bash[_%-]profile'] = detect.bash, ['%.kshrc.*'] = detect.ksh, @@ -1979,6 +2138,7 @@ local pattern = { ['.*termcap.*'] = starsetf(function(path, bufnr) return require('vim.filetype.detect').printcap('term') end), + ['.*/tex/latex/.*%.cfg'] = 'tex', ['.*%.t%.html'] = 'tilde', ['%.?tmux.*%.conf'] = 'tmux', ['%.?tmux.*%.conf.*'] = { 'tmux', { priority = -1 } }, @@ -1996,6 +2156,7 @@ local pattern = { ['.*/%.init/.*%.conf'] = 'upstart', ['.*/usr/share/upstart/.*%.override'] = 'upstart', ['.*%.[Ll][Oo][Gg]'] = detect.log, + ['.*/etc/config/.*'] = starsetf(detect.uci), ['.*%.vhdl_[0-9].*'] = starsetf('vhdl'), ['.*%.ws[fc]'] = 'wsh', ['.*/Xresources/.*'] = starsetf('xdefaults'), @@ -2022,6 +2183,7 @@ local pattern = { -- Increase priority to run before the pattern below ['XF86Config%-4.*'] = starsetf(detect.xfree86_v4, { priority = -math.huge + 1 }), ['XF86Config.*'] = starsetf(detect.xfree86_v3), + ['.*/%.bundle/config'] = 'yaml', ['%.zcompdump.*'] = starsetf('zsh'), -- .zlog* and zlog* ['%.?zlog.*'] = starsetf('zsh'), @@ -2029,7 +2191,7 @@ local pattern = { ['%.?zsh.*'] = starsetf('zsh'), -- Ignored extension ['.*~'] = function(path, bufnr) - local short = path:gsub('~$', '', 1) + local short = path:gsub('~+$', '', 1) if path ~= short and short ~= '' then return M.match({ buf = bufnr, filename = fn.fnameescape(short) }) end @@ -2157,7 +2319,6 @@ end --- vim.filetype.add { --- pattern = { --- ['.*'] = { ---- priority = -math.huge, --- function(path, bufnr) --- local content = vim.api.nvim_buf_get_lines(bufnr, 0, 1, false)[1] or '' --- if vim.regex([[^#!.*\\<mine\\>]]):match_str(content) ~= nil then @@ -2166,6 +2327,7 @@ end --- return 'drawing' --- end --- end, +--- { priority = -math.huge }, --- }, --- }, --- } @@ -2385,7 +2547,9 @@ function M.match(args) end -- Next, check file extension - local ext = fn.fnamemodify(name, ':e') + -- Don't use fnamemodify() with :e modifier here, + -- as that's empty when there is only an extension. + local ext = name:match('%.([^.]-)$') or '' ft, on_detect = dispatch(extension[ext], path, bufnr) if ft then return ft, on_detect diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 3db4f2bcdc..ba86d8de5a 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -458,6 +458,9 @@ end --- @type vim.filetype.mapfn function M.def(_, bufnr) + if getline(bufnr, 1):find('%%%%') then + return 'tex' + end if vim.g.filetype_def == 'modula2' or is_modula2(bufnr) then return modula2(bufnr) end @@ -738,7 +741,9 @@ end --- @type vim.filetype.mapfn function M.inp(_, bufnr) - if getline(bufnr, 1):find('^%*') then + if getline(bufnr, 1):find('%%%%') then + return 'tex' + elseif getline(bufnr, 1):find('^%*') then return 'abaqus' else for _, line in ipairs(getlines(bufnr, 1, 500)) do @@ -889,6 +894,11 @@ local function m4(contents) end end +--- @type vim.filetype.mapfn +function M.markdown(_, _) + return vim.g.filetype_md or 'markdown' +end + --- Rely on the file to start with a comment. --- MS message text files use ';', Sendmail files use '#' or 'dnl' --- @type vim.filetype.mapfn @@ -1131,12 +1141,14 @@ end --- Distinguish between "default", Prolog and Cproto prototype file. --- @type vim.filetype.mapfn function M.proto(_, bufnr) - -- Cproto files have a comment in the first line and a function prototype in - -- the second line, it always ends in ";". Indent files may also have - -- comments, thus we can't match comments to see the difference. - -- IDL files can have a single ';' in the second line, require at least one - -- character before the ';'. - if getline(bufnr, 2):find('.;$') then + if getline(bufnr, 2):find('/%* Generated automatically %*/') then + return 'c' + elseif getline(bufnr, 2):find('.;$') then + -- Cproto files have a comment in the first line and a function prototype in + -- the second line, it always ends in ";". Indent files may also have + -- comments, thus we can't match comments to see the difference. + -- IDL files can have a single ';' in the second line, require at least one + -- character before the ';'. return 'cpp' end -- Recognize Prolog by specific text in the first non-empty line; @@ -1574,6 +1586,26 @@ function M.typ(_, bufnr) return 'typst' end +--- @type vim.filetype.mapfn +function M.uci(_, bufnr) + -- Return "uci" iff the file has a config or package statement near the + -- top of the file and all preceding lines were comments or blank. + for _, line in ipairs(getlines(bufnr, 1, 3)) do + -- Match a config or package statement at the start of the line. + if + line:find('^%s*[cp]%s+%S') + or line:find('^%s*config%s+%S') + or line:find('^%s*package%s+%S') + then + return 'uci' + end + -- Match a line that is either all blank or blank followed by a comment + if not (line:find('^%s*$') or line:find('^%s*#')) then + break + end + end +end + -- Determine if a .v file is Verilog, V, or Coq --- @type vim.filetype.mapfn function M.v(_, bufnr) diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index f9fe122f01..b05220ee2c 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -1,6 +1,7 @@ local M = {} local iswin = vim.uv.os_uname().sysname == 'Windows_NT' +local os_sep = iswin and '\\' or '/' --- Iterate over all the parents of the given path. --- @@ -47,19 +48,23 @@ function M.dirname(file) return nil end vim.validate({ file = { file, 's' } }) - if iswin and file:match('^%w:[\\/]?$') then - return (file:gsub('\\', '/')) - elseif not file:match('[\\/]') then + if iswin then + file = file:gsub(os_sep, '/') --[[@as string]] + if file:match('^%w:/?$') then + return file + end + end + if not file:match('/') then return '.' elseif file == '/' or file:match('^/[^/]+$') then return '/' end ---@type string - local dir = file:match('[/\\]$') and file:sub(1, #file - 1) or file:match('^([/\\]?.+)[/\\]') + local dir = file:match('/$') and file:sub(1, #file - 1) or file:match('^(/?.+)/') if iswin and dir:match('^%w:$') then return dir .. '/' end - return (dir:gsub('\\', '/')) + return dir end --- Return the basename of the given path @@ -72,10 +77,13 @@ function M.basename(file) return nil end vim.validate({ file = { file, 's' } }) - if iswin and file:match('^%w:[\\/]?$') then - return '' + if iswin then + file = file:gsub(os_sep, '/') --[[@as string]] + if file:match('^%w:/?$') then + return '' + end end - return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/')) + return file:match('/$') and '' or (file:match('[^/]*$')) end --- Concatenate directories and/or file paths into a single path with normalization @@ -112,8 +120,9 @@ function M.dir(path, opts) skip = { opts.skip, { 'function' }, true }, }) + path = M.normalize(path) if not opts.depth or opts.depth == 1 then - local fs = vim.uv.fs_scandir(M.normalize(path)) + local fs = vim.uv.fs_scandir(path) return function() if not fs then return @@ -129,7 +138,7 @@ function M.dir(path, opts) --- @type string, integer local dir0, level = unpack(table.remove(dirs, 1)) local dir = level == 1 and dir0 or M.joinpath(path, dir0) - local fs = vim.uv.fs_scandir(M.normalize(dir)) + local fs = vim.uv.fs_scandir(dir) while fs do local name, t = vim.uv.fs_scandir_next(fs) if not name then @@ -189,13 +198,6 @@ end --- Examples: --- --- ```lua ---- -- location of Cargo.toml from the current buffer's path ---- local cargo = vim.fs.find('Cargo.toml', { ---- upward = true, ---- stop = vim.uv.os_homedir(), ---- path = vim.fs.dirname(vim.api.nvim_buf_get_name(0)), ---- }) ---- --- -- list all test directories under the runtime directory --- local test_dirs = vim.fs.find( --- {'test', 'tst', 'testdir'}, @@ -326,29 +328,204 @@ function M.find(names, opts) return matches end +--- Find the first parent directory containing a specific "marker", relative to a file path or +--- buffer. +--- +--- If the buffer is unnamed (has no backing file) or has a non-empty 'buftype' then the search +--- begins from Nvim's |current-directory|. +--- +--- Example: +--- +--- ```lua +--- -- Find the root of a Python project, starting from file 'main.py' +--- vim.fs.root(vim.fs.joinpath(vim.env.PWD, 'main.py'), {'pyproject.toml', 'setup.py' }) +--- +--- -- Find the root of a git repository +--- vim.fs.root(0, '.git') +--- +--- -- Find the parent directory containing any file with a .csproj extension +--- vim.fs.root(0, function(name, path) +--- return name:match('%.csproj$') ~= nil +--- end) +--- ``` +--- +--- @param source integer|string Buffer number (0 for current buffer) or file path (absolute or +--- relative to the |current-directory|) to begin the search from. +--- @param marker (string|string[]|fun(name: string, path: string): boolean) A marker, or list +--- of markers, to search for. If a function, the function is called for each +--- evaluated item and should return true if {name} and {path} are a match. +--- @return string? # Directory path containing one of the given markers, or nil if no directory was +--- found. +function M.root(source, marker) + assert(source, 'missing required argument: source') + assert(marker, 'missing required argument: marker') + + local path ---@type string + if type(source) == 'string' then + path = source + elseif type(source) == 'number' then + if vim.bo[source].buftype ~= '' then + path = assert(vim.uv.cwd()) + else + path = vim.api.nvim_buf_get_name(source) + end + else + error('invalid type for argument "source": expected string or buffer number') + end + + local paths = M.find(marker, { + upward = true, + path = vim.fn.fnamemodify(path, ':p:h'), + }) + + if #paths == 0 then + return nil + end + + return vim.fs.dirname(paths[1]) +end + +--- Split a Windows path into a prefix and a body, such that the body can be processed like a POSIX +--- path. The path must use forward slashes as path separator. +--- +--- Does not check if the path is a valid Windows path. Invalid paths will give invalid results. +--- +--- Examples: +--- - `//./C:/foo/bar` -> `//./C:`, `/foo/bar` +--- - `//?/UNC/server/share/foo/bar` -> `//?/UNC/server/share`, `/foo/bar` +--- - `//./system07/C$/foo/bar` -> `//./system07`, `/C$/foo/bar` +--- - `C:/foo/bar` -> `C:`, `/foo/bar` +--- - `C:foo/bar` -> `C:`, `foo/bar` +--- +--- @param path string Path to split. +--- @return string, string, boolean : prefix, body, whether path is invalid. +local function split_windows_path(path) + local prefix = '' + + --- Match pattern. If there is a match, move the matched pattern from the path to the prefix. + --- Returns the matched pattern. + --- + --- @param pattern string Pattern to match. + --- @return string|nil Matched pattern + local function match_to_prefix(pattern) + local match = path:match(pattern) + + if match then + prefix = prefix .. match --[[ @as string ]] + path = path:sub(#match + 1) + end + + return match + end + + local function process_unc_path() + return match_to_prefix('[^/]+/+[^/]+/+') + end + + if match_to_prefix('^//[?.]/') then + -- Device paths + local device = match_to_prefix('[^/]+/+') + + -- Return early if device pattern doesn't match, or if device is UNC and it's not a valid path + if not device or (device:match('^UNC/+$') and not process_unc_path()) then + return prefix, path, false + end + elseif match_to_prefix('^//') then + -- Process UNC path, return early if it's invalid + if not process_unc_path() then + return prefix, path, false + end + elseif path:match('^%w:') then + -- Drive paths + prefix, path = path:sub(1, 2), path:sub(3) + end + + -- If there are slashes at the end of the prefix, move them to the start of the body. This is to + -- ensure that the body is treated as an absolute path. For paths like C:foo/bar, there are no + -- slashes at the end of the prefix, so it will be treated as a relative path, as it should be. + local trailing_slash = prefix:match('/+$') + + if trailing_slash then + prefix = prefix:sub(1, -1 - #trailing_slash) + path = trailing_slash .. path --[[ @as string ]] + end + + return prefix, path, true +end + +--- Resolve `.` and `..` components in a POSIX-style path. This also removes extraneous slashes. +--- `..` is not resolved if the path is relative and resolving it requires the path to be absolute. +--- If a relative path resolves to the current directory, an empty string is returned. +--- +--- @see M.normalize() +--- @param path string Path to resolve. +--- @return string Resolved path. +local function path_resolve_dot(path) + local is_path_absolute = vim.startswith(path, '/') + local new_path_components = {} + + for component in vim.gsplit(path, '/') do + if component == '.' or component == '' then -- luacheck: ignore 542 + -- Skip `.` components and empty components + elseif component == '..' then + if #new_path_components > 0 and new_path_components[#new_path_components] ~= '..' then + -- For `..`, remove the last component if we're still inside the current directory, except + -- when the last component is `..` itself + table.remove(new_path_components) + elseif is_path_absolute then -- luacheck: ignore 542 + -- Reached the root directory in absolute path, do nothing + else + -- Reached current directory in relative path, add `..` to the path + table.insert(new_path_components, component) + end + else + table.insert(new_path_components, component) + end + end + + return (is_path_absolute and '/' or '') .. table.concat(new_path_components, '/') +end + --- @class vim.fs.normalize.Opts --- @inlinedoc --- --- Expand environment variables. --- (default: `true`) ---- @field expand_env boolean - ---- Normalize a path to a standard format. A tilde (~) character at the ---- beginning of the path is expanded to the user's home directory and any ---- backslash (\) characters are converted to forward slashes (/). Environment ---- variables are also expanded. +--- @field expand_env? boolean --- ---- Examples: +--- @field package _fast? boolean --- ---- ```lua ---- vim.fs.normalize('C:\\\\Users\\\\jdoe') ---- -- 'C:/Users/jdoe' +--- Path is a Windows path. +--- (default: `true` in Windows, `false` otherwise) +--- @field win? boolean + +--- Normalize a path to a standard format. A tilde (~) character at the beginning of the path is +--- expanded to the user's home directory and environment variables are also expanded. "." and ".." +--- components are also resolved, except when the path is relative and trying to resolve it would +--- result in an absolute path. +--- - "." as the only part in a relative path: +--- - "." => "." +--- - "././" => "." +--- - ".." when it leads outside the current directory +--- - "foo/../../bar" => "../bar" +--- - "../../foo" => "../../foo" +--- - ".." in the root directory returns the root directory. +--- - "/../../" => "/" --- ---- vim.fs.normalize('~/src/neovim') ---- -- '/home/jdoe/src/neovim' +--- On Windows, backslash (\) characters are converted to forward slashes (/). --- ---- vim.fs.normalize('$XDG_CONFIG_HOME/nvim/init.vim') ---- -- '/Users/jdoe/.config/nvim/init.vim' +--- Examples: +--- ```lua +--- [[C:\Users\jdoe]] => "C:/Users/jdoe" +--- "~/src/neovim" => "/home/jdoe/src/neovim" +--- "$XDG_CONFIG_HOME/nvim/init.vim" => "/Users/jdoe/.config/nvim/init.vim" +--- "~/src/nvim/api/../tui/./tui.c" => "/home/jdoe/src/nvim/tui/tui.c" +--- "./foo/bar" => "foo/bar" +--- "foo/../../../bar" => "../../bar" +--- "/home/jdoe/../../../bar" => "/bar" +--- "C:foo/../../baz" => "C:../baz" +--- "C:/foo/../../baz" => "C:/baz" +--- [[\\?\UNC\server\share\foo\..\..\..\bar]] => "//?/UNC/server/share/bar" --- ``` --- ---@param path (string) Path to normalize @@ -357,28 +534,79 @@ end function M.normalize(path, opts) opts = opts or {} - vim.validate({ - path = { path, { 'string' } }, - expand_env = { opts.expand_env, { 'boolean' }, true }, - }) + if not opts._fast then + vim.validate({ + path = { path, { 'string' } }, + expand_env = { opts.expand_env, { 'boolean' }, true }, + win = { opts.win, { 'boolean' }, true }, + }) + end + + local win = opts.win == nil and iswin or not not opts.win + local os_sep_local = win and '\\' or '/' + + -- Empty path is already normalized + if path == '' then + return '' + end - if path:sub(1, 1) == '~' then + -- Expand ~ to users home directory + if vim.startswith(path, '~') then local home = vim.uv.os_homedir() or '~' - if home:sub(-1) == '\\' or home:sub(-1) == '/' then + if home:sub(-1) == os_sep_local then home = home:sub(1, -2) end path = home .. path:sub(2) end + -- Expand environment variables if `opts.expand_env` isn't `false` if opts.expand_env == nil or opts.expand_env then path = path:gsub('%$([%w_]+)', vim.uv.os_getenv) end - path = path:gsub('\\', '/'):gsub('/+', '/') - if iswin and path:match('^%w:/$') then - return path + if win then + -- Convert path separator to `/` + path = path:gsub(os_sep_local, '/') + end + + -- Check for double slashes at the start of the path because they have special meaning + local double_slash = false + if not opts._fast then + double_slash = vim.startswith(path, '//') and not vim.startswith(path, '///') + end + + local prefix = '' + + if win then + local is_valid --- @type boolean + -- Split Windows paths into prefix and body to make processing easier + prefix, path, is_valid = split_windows_path(path) + + -- If path is not valid, return it as-is + if not is_valid then + return prefix .. path + end + + -- Remove extraneous slashes from the prefix + prefix = prefix:gsub('/+', '/') + end + + if not opts._fast then + -- Resolve `.` and `..` components and remove extraneous slashes from path, then recombine prefix + -- and path. + path = path_resolve_dot(path) end - return (path:gsub('(.)/$', '%1')) + + -- Preserve leading double slashes as they indicate UNC paths and DOS device paths in + -- Windows and have implementation-defined behavior in POSIX. + path = (double_slash and '/' or '') .. prefix .. path + + -- Change empty path to `.` + if path == '' then + path = '.' + end + + return path end return M diff --git a/runtime/lua/vim/func.lua b/runtime/lua/vim/func.lua index 206d1bae95..f71659ffb4 100644 --- a/runtime/lua/vim/func.lua +++ b/runtime/lua/vim/func.lua @@ -32,10 +32,11 @@ local M = {} --- first n arguments passed to {fn}. --- --- @param fn F Function to memoize. +--- @param strong? boolean Do not use a weak table --- @return F # Memoized version of {fn} --- @nodoc -function M._memoize(hash, fn) - return require('vim.func._memoize')(hash, fn) +function M._memoize(hash, fn, strong) + return require('vim.func._memoize')(hash, fn, strong) end return M diff --git a/runtime/lua/vim/func/_memoize.lua b/runtime/lua/vim/func/_memoize.lua index 835bf64c93..65210351bf 100644 --- a/runtime/lua/vim/func/_memoize.lua +++ b/runtime/lua/vim/func/_memoize.lua @@ -36,15 +36,19 @@ end --- @generic F: function --- @param hash integer|string|fun(...): any --- @param fn F +--- @param strong? boolean --- @return F -return function(hash, fn) +return function(hash, fn, strong) vim.validate({ hash = { hash, { 'number', 'string', 'function' } }, fn = { fn, 'function' }, }) ---@type table<any,table<any,any>> - local cache = setmetatable({}, { __mode = 'kv' }) + local cache = {} + if not strong then + setmetatable(cache, { __mode = 'kv' }) + end hash = resolve_hash(hash) diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index f6f7abef8f..f40f04a064 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -1,43 +1,96 @@ +--- @brief +---<pre>help +--- health.vim is a minimal framework to help users troubleshoot configuration and +--- any other environment conditions that a plugin might care about. Nvim ships +--- with healthchecks for configuration, performance, python support, ruby +--- support, clipboard support, and more. +--- +--- To run all healthchecks, use: >vim +--- +--- :checkhealth +--- < +--- Plugin authors are encouraged to write new healthchecks. |health-dev| +--- +--- Commands *health-commands* +--- +--- *:che* *:checkhealth* +--- :che[ckhealth] Run all healthchecks. +--- *E5009* +--- Nvim depends on |$VIMRUNTIME|, 'runtimepath' and 'packpath' to +--- find the standard "runtime files" for syntax highlighting, +--- filetype-specific behavior, and standard plugins (including +--- :checkhealth). If the runtime files cannot be found then +--- those features will not work. +--- +--- :che[ckhealth] {plugins} +--- Run healthcheck(s) for one or more plugins. E.g. to run only +--- the standard Nvim healthcheck: >vim +--- :checkhealth vim.health +--- < +--- To run the healthchecks for the "foo" and "bar" plugins +--- (assuming they are on 'runtimepath' and they have implemented +--- the Lua `require("foo.health").check()` interface): >vim +--- :checkhealth foo bar +--- < +--- To run healthchecks for Lua submodules, use dot notation or +--- "*" to refer to all submodules. For example Nvim provides +--- `vim.lsp` and `vim.treesitter`: >vim +--- :checkhealth vim.lsp vim.treesitter +--- :checkhealth vim* +--- < +--- +--- Create a healthcheck *health-dev* *vim.health* +--- +--- Healthchecks are functions that check the user environment, configuration, or +--- any other prerequisites that a plugin cares about. Nvim ships with +--- healthchecks in: +--- - $VIMRUNTIME/autoload/health/ +--- - $VIMRUNTIME/lua/vim/lsp/health.lua +--- - $VIMRUNTIME/lua/vim/treesitter/health.lua +--- - and more... +--- +--- To add a new healthcheck for your own plugin, simply create a "health.lua" +--- module on 'runtimepath' that returns a table with a "check()" function. Then +--- |:checkhealth| will automatically find and invoke the function. +--- +--- For example if your plugin is named "foo", define your healthcheck module at +--- one of these locations (on 'runtimepath'): +--- - lua/foo/health/init.lua +--- - lua/foo/health.lua +--- +--- If your plugin also provides a submodule named "bar" for which you want +--- a separate healthcheck, define the healthcheck at one of these locations: +--- - lua/foo/bar/health/init.lua +--- - lua/foo/bar/health.lua +--- +--- All such health modules must return a Lua table containing a `check()` +--- function. +--- +--- Copy this sample code into `lua/foo/health.lua`, replacing "foo" in the path +--- with your plugin name: >lua +--- +--- local M = {} +--- +--- M.check = function() +--- vim.health.start("foo report") +--- -- make sure setup function parameters are ok +--- if check_setup() then +--- vim.health.ok("Setup is correct") +--- else +--- vim.health.error("Setup is incorrect") +--- end +--- -- do some more checking +--- -- ... +--- end +--- +--- return M +---</pre> + local M = {} local s_output = {} ---@type string[] ---- Returns the fold text of the current healthcheck section -function M.foldtext() - local foldtext = vim.fn.foldtext() - - if vim.bo.filetype ~= 'checkhealth' then - return foldtext - end - - if vim.b.failedchecks == nil then - vim.b.failedchecks = vim.empty_dict() - end - - if vim.b.failedchecks[foldtext] == nil then - local warning = '- WARNING ' - local warninglen = string.len(warning) - local err = '- ERROR ' - local errlen = string.len(err) - local failedchecks = vim.b.failedchecks - failedchecks[foldtext] = false - - local foldcontent = vim.api.nvim_buf_get_lines(0, vim.v.foldstart - 1, vim.v.foldend, false) - for _, line in ipairs(foldcontent) do - if string.sub(line, 1, warninglen) == warning or string.sub(line, 1, errlen) == err then - failedchecks[foldtext] = true - break - end - end - - vim.b.failedchecks = failedchecks - end - - return vim.b.failedchecks[foldtext] and '+WE' .. foldtext:sub(4) or foldtext -end - ---- @param path string path to search for the healthcheck ---- @return string[] { name, func, type } representing a healthcheck +-- From a path return a list [{name}, {func}, {type}] representing a healthcheck local function filepath_to_healthcheck(path) path = vim.fs.normalize(path) local name --- @type string @@ -178,7 +231,9 @@ local function collect_output(output) vim.list_extend(s_output, vim.split(output, '\n')) end ---- Starts a new report. +--- Starts a new report. Most plugins should call this only once, but if +--- you want different sections to appear in your report, call this once +--- per section. --- --- @param name string function M.start(name) @@ -186,7 +241,7 @@ function M.start(name) collect_output(input) end ---- Reports a message in the current section. +--- Reports an informational message. --- --- @param msg string function M.info(msg) @@ -194,7 +249,7 @@ function M.info(msg) collect_output(input) end ---- Reports a successful healthcheck. +--- Reports a "success" message. --- --- @param msg string function M.ok(msg) @@ -202,7 +257,7 @@ function M.ok(msg) collect_output(input) end ---- Reports a health warning. +--- Reports a warning. --- --- @param msg string --- @param ... string|string[] Optional advice @@ -211,7 +266,7 @@ function M.warn(msg, ...) collect_output(input) end ---- Reports a failed healthcheck. +--- Reports an error. --- --- @param msg string --- @param ... string|string[] Optional advice @@ -220,54 +275,7 @@ function M.error(msg, ...) collect_output(input) end ---- @param type string -local function deprecate(type) - local before = string.format('vim.health.report_%s()', type) - local after = string.format('vim.health.%s()', type) - local message = vim.deprecate(before, after, '0.11') - if message then - M.warn(message) - end - vim.cmd.redraw() - vim.print('Running healthchecks...') -end - ---- @deprecated ---- @param name string -function M.report_start(name) - deprecate('start') - M.start(name) -end - ---- @deprecated ---- @param msg string -function M.report_info(msg) - deprecate('info') - M.info(msg) -end - ---- @deprecated ---- @param msg string -function M.report_ok(msg) - deprecate('ok') - M.ok(msg) -end - ---- @deprecated ---- @param msg string -function M.report_warn(msg, ...) - deprecate('warn') - M.warn(msg, ...) -end - ---- @deprecated ---- @param msg string -function M.report_error(msg, ...) - deprecate('error') - M.error(msg, ...) -end - -function M.provider_disabled(provider) +function M._provider_disabled(provider) local loaded_var = 'loaded_' .. provider .. '_provider' local v = vim.g[loaded_var] if v == 0 then @@ -307,7 +315,7 @@ local function shellify(cmd) return table.concat(escaped, ' ') end -function M.cmd_ok(cmd) +function M._cmd_ok(cmd) local out = vim.fn.system(cmd) return vim.v.shell_error == 0, out end @@ -320,7 +328,7 @@ end --- - stderr (boolean): Append stderr to stdout --- - ignore_error (boolean): If true, ignore error output --- - timeout (number): Number of seconds to wait before timing out (default 30) -function M.system(cmd, args) +function M._system(cmd, args) args = args or {} local stdin = args.stdin or '' local stderr = vim.F.if_nil(args.stderr, false) @@ -341,7 +349,7 @@ function M.system(cmd, args) if jobid < 1 then local message = - string.format('Command error (job=%d): %s (in %s)', jobid, shellify(cmd), vim.loop.cwd()) + string.format('Command error (job=%d): %s (in %s)', jobid, shellify(cmd), vim.uv.cwd()) error(message) return opts.output, 1 end @@ -360,7 +368,7 @@ function M.system(cmd, args) jobid, shell_error_code, shellify(cmd), - vim.loop.cwd() + vim.uv.cwd() ) if opts.output:find('%S') then emsg = string.format('%s\noutput: %s', emsg, opts.output) @@ -386,7 +394,7 @@ local path2name = function(path) path = path:gsub('^.*/lua/', '') -- Remove the filename (health.lua) - path = vim.fn.fnamemodify(path, ':h') + path = vim.fs.dirname(path) -- Change slashes to dots path = path:gsub('/', '.') @@ -401,17 +409,20 @@ end local PATTERNS = { '/autoload/health/*.vim', '/lua/**/**/health.lua', '/lua/**/**/health/init.lua' } --- :checkhealth completion function used by cmdexpand.c get_healthcheck_names() M._complete = function() - local names = vim.tbl_flatten(vim.tbl_map(function(pattern) - return vim.tbl_map(path2name, vim.api.nvim_get_runtime_file(pattern, true)) - end, PATTERNS)) - -- Remove duplicates - local unique = {} - vim.tbl_map(function(f) - unique[f] = true - end, names) + local unique = vim + .iter(vim.tbl_map(function(pattern) + return vim.tbl_map(path2name, vim.api.nvim_get_runtime_file(pattern, true)) + end, PATTERNS)) + :flatten() + :fold({}, function(t, name) + t[name] = true -- Remove duplicates + return t + end) -- vim.health is this file, which is not a healthcheck unique['vim'] = nil - return vim.tbl_keys(unique) + local rv = vim.tbl_keys(unique) + table.sort(rv) + return rv end --- Runs the specified healthchecks. @@ -497,11 +508,4 @@ function M._check(mods, plugin_names) vim.print('') end -local fn_bool = function(key) - return function(...) - return vim.fn[key](...) == 1 - end -end -M.executable = fn_bool('executable') - return M diff --git a/runtime/lua/nvim/health.lua b/runtime/lua/vim/health/health.lua index 0480e4df4e..5bc03199ee 100644 --- a/runtime/lua/nvim/health.lua +++ b/runtime/lua/vim/health/health.lua @@ -1,23 +1,11 @@ local M = {} local health = require('vim.health') -local fn_bool = function(key) - return function(...) - return vim.fn[key](...) == 1 - end -end - -local has = fn_bool('has') -local executable = fn_bool('executable') -local empty = fn_bool('empty') -local filereadable = fn_bool('filereadable') -local filewritable = fn_bool('filewritable') - local shell_error = function() return vim.v.shell_error ~= 0 end -local suggest_faq = 'https://github.com/neovim/neovim/blob/docs/install/BUILD.md#building' +local suggest_faq = 'https://github.com/neovim/neovim/blob/master/BUILD.md#building' local function check_runtime() health.start('Runtime') @@ -62,11 +50,11 @@ local function check_config() local init_lua = vim.fn.stdpath('config') .. '/init.lua' local init_vim = vim.fn.stdpath('config') .. '/init.vim' - local vimrc = empty(vim.env.MYVIMRC) and init_lua or vim.env.MYVIMRC + local vimrc = vim.env.MYVIMRC and vim.fn.expand(vim.env.MYVIMRC) or init_lua - if not filereadable(vimrc) and not filereadable(init_vim) then + if vim.fn.filereadable(vimrc) == 0 and vim.fn.filereadable(init_vim) == 0 then ok = false - local has_vim = filereadable(vim.fn.expand('~/.vimrc')) + local has_vim = vim.fn.filereadable(vim.fn.expand('~/.vimrc')) == 1 health.warn( ('%s user config file: %s'):format( -1 == vim.fn.getfsize(vimrc) and 'Missing' or 'Unreadable', @@ -77,7 +65,7 @@ local function check_config() end -- If $VIM is empty we don't care. Else make sure it is valid. - if not empty(vim.env.VIM) and not filereadable(vim.env.VIM .. '/runtime/doc/nvim.txt') then + if vim.env.VIM and vim.fn.filereadable(vim.env.VIM .. '/runtime/doc/nvim.txt') == 0 then ok = false health.error('$VIM is invalid: ' .. vim.env.VIM) end @@ -121,17 +109,17 @@ local function check_config() local writeable = true local shadaopt = vim.fn.split(vim.o.shada, ',') local shadafile = ( - empty(vim.o.shada) and vim.o.shada + vim.o.shada == '' and vim.o.shada or vim.fn.substitute(vim.fn.matchstr(shadaopt[#shadaopt], '^n.\\+'), '^n', '', '') ) shadafile = ( - empty(vim.o.shadafile) - and (empty(shadafile) and vim.fn.stdpath('state') .. '/shada/main.shada' or vim.fn.expand( + vim.o.shadafile == '' + and (shadafile == '' and vim.fn.stdpath('state') .. '/shada/main.shada' or vim.fn.expand( shadafile )) or (vim.o.shadafile == 'NONE' and '' or vim.o.shadafile) ) - if not empty(shadafile) and empty(vim.fn.glob(shadafile)) then + if shadafile ~= '' and vim.fn.glob(shadafile) == '' then -- Since this may be the first time Nvim has been run, try to create a shada file. if not pcall(vim.cmd.wshada) then writeable = false @@ -139,12 +127,15 @@ local function check_config() end if not writeable - or (not empty(shadafile) and (not filereadable(shadafile) or not filewritable(shadafile))) + or ( + shadafile ~= '' + and (vim.fn.filereadable(shadafile) == 0 or vim.fn.filewritable(shadafile) ~= 1) + ) then ok = false health.error( 'shada file is not ' - .. ((not writeable or filereadable(shadafile)) and 'writeable' or 'readable') + .. ((not writeable or vim.fn.filereadable(shadafile) == 1) and 'writeable' or 'readable') .. ':\n' .. shadafile ) @@ -160,7 +151,7 @@ local function check_performance() -- Check buildtype local buildtype = vim.fn.matchstr(vim.fn.execute('version'), [[\v\cbuild type:?\s*[^\n\r\t ]+]]) - if empty(buildtype) then + if buildtype == '' then health.error('failed to get build type from :version') elseif vim.regex([[\v(MinSizeRel|Release|RelWithDebInfo)]]):match_str(buildtype) then health.ok(buildtype) @@ -196,12 +187,12 @@ local function check_rplugin_manifest() local require_update = false local handle_path = function(path) local python_glob = vim.fn.glob(path .. '/rplugin/python*', true, true) - if empty(python_glob) then + if vim.tbl_isempty(python_glob) then return end local python_dir = python_glob[1] - local python_version = vim.fn.fnamemodify(python_dir, ':t') + local python_version = vim.fs.basename(python_dir) local scripts = vim.fn.glob(python_dir .. '/*.py', true, true) vim.list_extend(scripts, vim.fn.glob(python_dir .. '/*/__init__.py', true, true)) @@ -213,12 +204,12 @@ local function check_rplugin_manifest() script = vim.fn.tr(vim.fn.fnamemodify(script, ':h'), '\\', '/') end if not existing_rplugins[script] then - local msg = vim.fn.printf('"%s" is not registered.', vim.fn.fnamemodify(path, ':t')) + local msg = vim.fn.printf('"%s" is not registered.', vim.fs.basename(path)) if python_version == 'pythonx' then - if not has('python3') then + if vim.fn.has('python3') == 0 then msg = msg .. ' (python3 not available)' end - elseif not has(python_version) then + elseif vim.fn.has(python_version) == 0 then msg = msg .. vim.fn.printf(' (%s not available)', python_version) else require_update = true @@ -232,7 +223,7 @@ local function check_rplugin_manifest() end end - for _, path in ipairs(vim.fn.map(vim.fn.split(vim.o.runtimepath, ','), 'resolve(v:val)')) do + for _, path in ipairs(vim.fn.map(vim.split(vim.o.runtimepath, ','), 'resolve(v:val)')) do handle_path(path) end @@ -244,7 +235,7 @@ local function check_rplugin_manifest() end local function check_tmux() - if empty(vim.env.TMUX) or not executable('tmux') then + if not vim.env.TMUX or vim.fn.executable('tmux') == 0 then return end @@ -255,7 +246,7 @@ local function check_tmux() if shell_error() then health.error('command failed: ' .. cmd .. '\n' .. out) return 'error' - elseif empty(val) then + elseif val == '' then cmd = 'tmux show-option -qvgs ' .. option -- try session scope out = vim.fn.system(vim.fn.split(cmd)) val = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g') @@ -274,7 +265,7 @@ local function check_tmux() { 'set escape-time in ~/.tmux.conf:\nset-option -sg escape-time 10', suggest_faq } local tmux_esc_time = get_tmux_option('escape-time') if tmux_esc_time ~= 'error' then - if empty(tmux_esc_time) then + if tmux_esc_time == '' then health.error('`escape-time` is not set', suggestions) elseif tonumber(tmux_esc_time) > 300 then health.error('`escape-time` (' .. tmux_esc_time .. ') is higher than 300ms', suggestions) @@ -286,7 +277,7 @@ local function check_tmux() -- check focus-events local tmux_focus_events = get_tmux_option('focus-events') if tmux_focus_events ~= 'error' then - if empty(tmux_focus_events) or tmux_focus_events ~= 'on' then + if tmux_focus_events == '' or tmux_focus_events ~= 'on' then health.warn( "`focus-events` is not enabled. |'autoread'| may not work.", { '(tmux 1.9+ only) Set `focus-events` in ~/.tmux.conf:\nset-option -g focus-events on' } @@ -301,7 +292,7 @@ local function check_tmux() local cmd = 'tmux show-option -qvg default-terminal' local out = vim.fn.system(vim.fn.split(cmd)) local tmux_default_term = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g') - if empty(tmux_default_term) then + if tmux_default_term == '' then cmd = 'tmux show-option -qvgs default-terminal' out = vim.fn.system(vim.fn.split(cmd)) tmux_default_term = vim.fn.substitute(out, [[\v(\s|\r|\n)]], '', 'g') @@ -341,7 +332,7 @@ local function check_tmux() end local function check_terminal() - if not executable('infocmp') then + if vim.fn.executable('infocmp') == 0 then return end @@ -354,13 +345,12 @@ local function check_terminal() if shell_error() and ( - not has('win32') - or empty( - vim.fn.matchstr( + vim.fn.has('win32') == 0 + or vim.fn.matchstr( out, [[infocmp: couldn't open terminfo file .\+\%(conemu\|vtpcon\|win32con\)]] ) - ) + == '' ) then health.error('command failed: ' .. cmd .. '\n' .. out) @@ -368,14 +358,14 @@ local function check_terminal() health.info( vim.fn.printf( 'key_backspace (kbs) terminfo entry: `%s`', - (empty(kbs_entry) and '? (not found)' or kbs_entry) + (kbs_entry == '' and '? (not found)' or kbs_entry) ) ) health.info( vim.fn.printf( 'key_dc (kdch1) terminfo entry: `%s`', - (empty(kbs_entry) and '? (not found)' or kdch1_entry) + (kbs_entry == '' and '? (not found)' or kdch1_entry) ) ) end @@ -393,6 +383,19 @@ local function check_terminal() end end +local function check_external_tools() + health.start('External Tools') + + if vim.fn.executable('rg') == 1 then + local rg = vim.fn.exepath('rg') + local cmd = 'rg -V' + local out = vim.fn.system(vim.fn.split(cmd)) + health.ok(('%s (%s)'):format(vim.trim(out), rg)) + else + health.warn('ripgrep not available') + end +end + function M.check() check_config() check_runtime() @@ -400,6 +403,7 @@ function M.check() check_rplugin_manifest() check_terminal() check_tmux() + check_external_tools() end return M diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index effe280dee..f278bd357f 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -1,26 +1,3 @@ ----@brief ---- ---- Nvim includes a function for highlighting a selection on yank. ---- ---- To enable it, add the following to your `init.vim`: ---- ---- ```vim ---- au TextYankPost * silent! lua vim.highlight.on_yank() ---- ``` ---- ---- You can customize the highlight group and the duration of the highlight via: ---- ---- ```vim ---- au TextYankPost * silent! lua vim.highlight.on_yank {higroup="IncSearch", timeout=150} ---- ``` ---- ---- If you want to exclude visual selections from highlighting on yank, use: ---- ---- ```vim ---- au TextYankPost * silent! lua vim.highlight.on_yank {on_visual=false} ---- ``` ---- - local api = vim.api local M = {} @@ -40,6 +17,23 @@ M.priorities = { user = 200, } +--- @class vim.highlight.range.Opts +--- @inlinedoc +--- +--- Type of range. See [setreg()] +--- (default: `'charwise'`) +--- @field regtype? string +--- +--- Indicates whether the range is end-inclusive +--- (default: `false`) +--- @field inclusive? boolean +--- +--- Indicates priority of highlight +--- (default: `vim.highlight.priorities.user`) +--- @field priority? integer +--- +--- @field package _scoped? boolean + --- Apply highlight group to range of text. --- ---@param bufnr integer Buffer number to apply highlighting to @@ -47,10 +41,7 @@ M.priorities = { ---@param higroup string Highlight group to use for highlighting ---@param start integer[]|string Start of region as a (line, column) tuple or string accepted by |getpos()| ---@param finish integer[]|string End of region as a (line, column) tuple or string accepted by |getpos()| ----@param opts table|nil Optional parameters ---- - regtype type of range (see |setreg()|, default charwise) ---- - inclusive boolean indicating whether the range is end-inclusive (default false) ---- - priority number indicating priority of highlight (default priorities.user) +---@param opts? vim.highlight.range.Opts function M.range(bufnr, ns, higroup, start, finish, opts) opts = opts or {} local regtype = opts.regtype or 'v' @@ -80,10 +71,16 @@ function M.range(bufnr, ns, higroup, start, finish, opts) end local yank_ns = api.nvim_create_namespace('hlyank') -local yank_timer -local yank_cancel +local yank_timer --- @type uv.uv_timer_t? +local yank_cancel --- @type fun()? ---- Highlight the yanked text +--- Highlight the yanked text during a |TextYankPost| event. +--- +--- Add the following to your `init.vim`: +--- +--- ```vim +--- autocmd TextYankPost * silent! lua vim.highlight.on_yank {higroup='Visual', timeout=300} +--- ``` --- --- @param opts table|nil Optional parameters --- - higroup highlight group for yanked region (default "IncSearch") @@ -128,22 +125,23 @@ function M.on_yank(opts) local winid = vim.api.nvim_get_current_win() if yank_timer then yank_timer:close() + assert(yank_cancel) yank_cancel() end + vim.api.nvim__win_add_ns(winid, yank_ns) M.range(bufnr, yank_ns, higroup, "'[", "']", { regtype = event.regtype, inclusive = event.inclusive, priority = opts.priority or M.priorities.user, _scoped = true, }) - vim.api.nvim_win_add_ns(winid, yank_ns) yank_cancel = function() yank_timer = nil yank_cancel = nil pcall(vim.api.nvim_buf_clear_namespace, bufnr, yank_ns, 0, -1) - pcall(vim.api.nvim_win_remove_ns, winid, yank_ns) + pcall(vim.api.nvim__win_del_ns, winid, yank_ns) end yank_timer = vim.defer_fn(yank_cancel, timeout) diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua index a37b7f7858..1093759efe 100644 --- a/runtime/lua/vim/iter.lua +++ b/runtime/lua/vim/iter.lua @@ -7,6 +7,7 @@ --- `vim.iter()`: --- --- - List tables (arrays, |lua-list|) yield only the value of each element. +--- - Holes (nil values) are allowed. --- - Use |Iter:enumerate()| to also pass the index to the next stage. --- - Or initialize with ipairs(): `vim.iter(ipairs(…))`. --- - Non-list tables (|lua-dict|) yield both the key and value of each element. @@ -60,9 +61,6 @@ --- vim.iter(rb):totable() --- -- { "a", "b" } --- ``` ---- ---- In addition to the |vim.iter()| function, the |vim.iter| module provides ---- convenience functions like |vim.iter.filter()| and |vim.iter.totable()|. --- LuaLS is bad at generics which this module mostly deals with --- @diagnostic disable:no-unknown @@ -83,13 +81,13 @@ end --- Special case implementations for iterators on list tables. ---@nodoc ----@class ListIter : Iter +---@class ArrayIter : Iter ---@field _table table Underlying table data ---@field _head number Index to the front of a table iterator ---@field _tail number Index to the end of a table iterator (exclusive) -local ListIter = {} -ListIter.__index = setmetatable(ListIter, Iter) -ListIter.__call = function(self) +local ArrayIter = {} +ArrayIter.__index = setmetatable(ArrayIter, Iter) +ArrayIter.__call = function(self) return self:next() end @@ -113,36 +111,34 @@ end local function sanitize(t) if type(t) == 'table' and getmetatable(t) == packedmt then - -- Remove length tag + -- Remove length tag and metatable t.n = nil + setmetatable(t, nil) end return t end ---- Flattens a single list-like table. Errors if it attempts to flatten a +--- Flattens a single array-like table. Errors if it attempts to flatten a --- dict-like table ----@param v table table which should be flattened +---@param t table table which should be flattened ---@param max_depth number depth to which the table should be flattened ---@param depth number current iteration depth ---@param result table output table that contains flattened result ---@return table|nil flattened table if it can be flattened, otherwise nil -local function flatten(v, max_depth, depth, result) - if depth < max_depth and type(v) == 'table' then - local i = 0 - for _ in pairs(v) do - i = i + 1 - - if v[i] == nil then +local function flatten(t, max_depth, depth, result) + if depth < max_depth and type(t) == 'table' then + for k, v in pairs(t) do + if type(k) ~= 'number' or k <= 0 or math.floor(k) ~= k then -- short-circuit: this is not a list like table return nil end - if flatten(v[i], max_depth, depth + 1, result) == nil then + if flatten(v, max_depth, depth + 1, result) == nil then return nil end end - else - result[#result + 1] = v + elseif t ~= nil then + result[#result + 1] = t end return result @@ -201,7 +197,7 @@ function Iter:filter(f) end ---@private -function ListIter:filter(f) +function ArrayIter:filter(f) local inc = self._head < self._tail and 1 or -1 local n = self._head for i = self._head, self._tail - inc, inc do @@ -236,11 +232,11 @@ end ---@return Iter ---@diagnostic disable-next-line:unused-local function Iter:flatten(depth) -- luacheck: no unused args - error('flatten() requires a list-like table') + error('flatten() requires an array-like table') end ---@private -function ListIter:flatten(depth) +function ArrayIter:flatten(depth) depth = depth or 1 local inc = self._head < self._tail and 1 or -1 local target = {} @@ -250,7 +246,7 @@ function ListIter:flatten(depth) -- exit early if we try to flatten a dict-like table if flattened == nil then - error('flatten() requires a list-like table') + error('flatten() requires an array-like table') end for _, v in pairs(flattened) do @@ -330,7 +326,7 @@ function Iter:map(f) end ---@private -function ListIter:map(f) +function ArrayIter:map(f) local inc = self._head < self._tail and 1 or -1 local n = self._head for i = self._head, self._tail - inc, inc do @@ -363,7 +359,7 @@ function Iter:each(f) end ---@private -function ListIter:each(f) +function ArrayIter:each(f) local inc = self._head < self._tail and 1 or -1 for i = self._head, self._tail - inc, inc do f(unpack(self._table[i])) @@ -374,7 +370,7 @@ end --- Collect the iterator into a table. --- --- The resulting table depends on the initial source in the iterator pipeline. ---- List-like tables and function iterators will be collected into a list-like +--- Array-like tables and function iterators will be collected into an array-like --- table. If multiple values are returned from the final stage in the iterator --- pipeline, each value will be included in a table. --- @@ -391,7 +387,7 @@ end --- -- { { 'a', 1 }, { 'c', 3 } } --- ``` --- ---- The generated table is a list-like table with consecutive, numeric indices. +--- The generated table is an array-like table with consecutive, numeric indices. --- To create a map-like table with arbitrary keys, use |Iter:fold()|. --- --- @@ -411,12 +407,12 @@ function Iter:totable() end ---@private -function ListIter:totable() - if self.next ~= ListIter.next or self._head >= self._tail then +function ArrayIter:totable() + if self.next ~= ArrayIter.next or self._head >= self._tail then return Iter.totable(self) end - local needs_sanitize = getmetatable(self._table[1]) == packedmt + local needs_sanitize = getmetatable(self._table[self._head]) == packedmt -- Reindex and sanitize. local len = self._tail - self._head @@ -453,20 +449,25 @@ function Iter:join(delim) return table.concat(self:totable(), delim) end ---- Folds ("reduces") an iterator into a single value. +--- Folds ("reduces") an iterator into a single value. [Iter:reduce()]() --- --- Examples: --- --- ```lua --- -- Create a new table with only even values ---- local t = { a = 1, b = 2, c = 3, d = 4 } ---- local it = vim.iter(t) ---- it:filter(function(k, v) return v % 2 == 0 end) ---- it:fold({}, function(t, k, v) ---- t[k] = v ---- return t ---- end) ---- -- { b = 2, d = 4 } +--- vim.iter({ a = 1, b = 2, c = 3, d = 4 }) +--- :filter(function(k, v) return v % 2 == 0 end) +--- :fold({}, function(acc, k, v) +--- acc[k] = v +--- return acc +--- end) --> { b = 2, d = 4 } +--- +--- -- Get the "maximum" item of an iterable. +--- vim.iter({ -99, -4, 3, 42, 0, 0, 7 }) +--- :fold({}, function(acc, v) +--- acc.max = math.max(v, acc.max or v) +--- return acc +--- end) --> { max = 42 } --- ``` --- ---@generic A @@ -491,7 +492,7 @@ function Iter:fold(init, f) end ---@private -function ListIter:fold(init, f) +function ArrayIter:fold(init, f) local acc = init local inc = self._head < self._tail and 1 or -1 for i = self._head, self._tail - inc, inc do @@ -523,7 +524,7 @@ function Iter:next() end ---@private -function ListIter:next() +function ArrayIter:next() if self._head ~= self._tail then local v = self._table[self._head] local inc = self._head < self._tail and 1 or -1 @@ -546,11 +547,11 @@ end --- ---@return Iter function Iter:rev() - error('rev() requires a list-like table') + error('rev() requires an array-like table') end ---@private -function ListIter:rev() +function ArrayIter:rev() local inc = self._head < self._tail and 1 or -1 self._head, self._tail = self._tail - inc, self._head - inc return self @@ -574,11 +575,11 @@ end --- ---@return any function Iter:peek() - error('peek() requires a list-like table') + error('peek() requires an array-like table') end ---@private -function ListIter:peek() +function ArrayIter:peek() if self._head ~= self._tail then return self._table[self._head] end @@ -633,7 +634,7 @@ function Iter:find(f) return unpack(result) end ---- Gets the first value in a |list-iterator| that satisfies a predicate, starting from the end. +--- Gets the first value satisfying a predicate, from the end of a |list-iterator|. --- --- Advances the iterator. Returns nil and drains the iterator if no value is found. --- @@ -655,11 +656,11 @@ end ---@return any ---@diagnostic disable-next-line: unused-local function Iter:rfind(f) -- luacheck: no unused args - error('rfind() requires a list-like table') + error('rfind() requires an array-like table') end ---@private -function ListIter:rfind(f) +function ArrayIter:rfind(f) if type(f) ~= 'function' then local val = f f = function(v) @@ -707,9 +708,10 @@ function Iter:take(n) end ---@private -function ListIter:take(n) - local inc = self._head < self._tail and 1 or -1 - self._tail = math.min(self._tail, self._head + n * inc) +function ArrayIter:take(n) + local inc = self._head < self._tail and n or -n + local cmp = self._head < self._tail and math.min or math.max + self._tail = cmp(self._tail, self._head + inc) return self end @@ -719,19 +721,19 @@ end --- --- ```lua --- local it = vim.iter({1, 2, 3, 4}) ---- it:nextback() +--- it:pop() --- -- 4 ---- it:nextback() +--- it:pop() --- -- 3 --- ``` --- ---@return any -function Iter:nextback() - error('nextback() requires a list-like table') +function Iter:pop() + error('pop() requires an array-like table') end --- @nodoc -function ListIter:nextback() +function ArrayIter:pop() if self._head ~= self._tail then local inc = self._head < self._tail and 1 or -1 self._tail = self._tail - inc @@ -741,27 +743,27 @@ end --- Gets the last value of a |list-iterator| without consuming it. --- ---- See also |Iter:last()|. ---- --- Example: --- --- ```lua --- local it = vim.iter({1, 2, 3, 4}) ---- it:peekback() +--- it:rpeek() --- -- 4 ---- it:peekback() +--- it:rpeek() --- -- 4 ---- it:nextback() +--- it:pop() --- -- 4 --- ``` --- +---@see Iter.last +--- ---@return any -function Iter:peekback() - error('peekback() requires a list-like table') +function Iter:rpeek() + error('rpeek() requires an array-like table') end ---@nodoc -function ListIter:peekback() +function ArrayIter:rpeek() if self._head ~= self._tail then local inc = self._head < self._tail and 1 or -1 return self._table[self._tail - inc] @@ -790,7 +792,7 @@ function Iter:skip(n) end ---@private -function ListIter:skip(n) +function ArrayIter:skip(n) local inc = self._head < self._tail and n or -n self._head = self._head + inc if (inc > 0 and self._head > self._tail) or (inc < 0 and self._head < self._tail) then @@ -799,27 +801,27 @@ function ListIter:skip(n) return self end ---- Skips `n` values backwards from the end of a |list-iterator| pipeline. +--- Discards `n` values from the end of a |list-iterator| pipeline. --- --- Example: --- --- ```lua ---- local it = vim.iter({ 1, 2, 3, 4, 5 }):skipback(2) +--- local it = vim.iter({ 1, 2, 3, 4, 5 }):rskip(2) --- it:next() --- -- 1 ---- it:nextback() +--- it:pop() --- -- 3 --- ``` --- ---@param n number Number of values to skip. ---@return Iter ---@diagnostic disable-next-line: unused-local -function Iter:skipback(n) -- luacheck: no unused args - error('skipback() requires a list-like table') +function Iter:rskip(n) -- luacheck: no unused args + error('rskip() requires an array-like table') end ---@private -function ListIter:skipback(n) +function ArrayIter:rskip(n) local inc = self._head < self._tail and n or -n self._tail = self._tail - inc if (inc > 0 and self._head > self._tail) or (inc < 0 and self._head < self._tail) then @@ -830,63 +832,49 @@ end --- Gets the nth value of an iterator (and advances to it). --- +--- If `n` is negative, offsets from the end of a |list-iterator|. +--- --- Example: --- --- ```lua ---- --- local it = vim.iter({ 3, 6, 9, 12 }) --- it:nth(2) --- -- 6 --- it:nth(2) --- -- 12 --- ---- ``` ---- ----@param n number The index of the value to return. ----@return any -function Iter:nth(n) - if n > 0 then - return self:skip(n - 1):next() - end -end - ---- Gets the nth value from the end of a |list-iterator| (and advances to it). ---- ---- Example: ---- ---- ```lua ---- ---- local it = vim.iter({ 3, 6, 9, 12 }) ---- it:nthback(2) +--- local it2 = vim.iter({ 3, 6, 9, 12 }) +--- it2:nth(-2) --- -- 9 ---- it:nthback(2) +--- it2:nth(-2) --- -- 3 ---- --- ``` --- ----@param n number The index of the value to return. +---@param n number Index of the value to return. May be negative if the source is a |list-iterator|. ---@return any -function Iter:nthback(n) +function Iter:nth(n) if n > 0 then - return self:skipback(n - 1):nextback() + return self:skip(n - 1):next() + elseif n < 0 then + return self:rskip(math.abs(n) - 1):pop() end end --- Sets the start and end of a |list-iterator| pipeline. --- ---- Equivalent to `:skip(first - 1):skipback(len - last + 1)`. +--- Equivalent to `:skip(first - 1):rskip(len - last + 1)`. --- ---@param first number ---@param last number ---@return Iter ---@diagnostic disable-next-line: unused-local function Iter:slice(first, last) -- luacheck: no unused args - error('slice() requires a list-like table') + error('slice() requires an array-like table') end ---@private -function ListIter:slice(first, last) - return self:skip(math.max(0, first - 1)):skipback(math.max(0, self._tail - last - 1)) +function ArrayIter:slice(first, last) + return self:skip(math.max(0, first - 1)):rskip(math.max(0, self._tail - last - 1)) end --- Returns true if any of the items in the iterator match the given predicate. @@ -952,6 +940,8 @@ end --- --- ``` --- +---@see Iter.rpeek +--- ---@return any function Iter:last() local last = self:next() @@ -964,7 +954,7 @@ function Iter:last() end ---@private -function ListIter:last() +function ArrayIter:last() local inc = self._head < self._tail and 1 or -1 local v = self._table[self._tail - inc] self._head = self._tail @@ -1009,7 +999,7 @@ function Iter:enumerate() end ---@private -function ListIter:enumerate() +function ArrayIter:enumerate() local inc = self._head < self._tail and 1 or -1 for i = self._head, self._tail - inc, inc do local v = self._table[i] @@ -1039,17 +1029,14 @@ function Iter.new(src, ...) local t = {} - -- O(n): scan the source table to decide if it is a list (consecutive integer indices 1…n). - local count = 0 - for _ in pairs(src) do - count = count + 1 - local v = src[count] - if v == nil then + -- O(n): scan the source table to decide if it is an array (only positive integer indices). + for k, v in pairs(src) do + if type(k) ~= 'number' or k <= 0 or math.floor(k) ~= k then return Iter.new(pairs(src)) end - t[count] = v + t[#t + 1] = v end - return ListIter.new(t) + return ArrayIter.new(t) end if type(src) == 'function' then @@ -1077,69 +1064,21 @@ function Iter.new(src, ...) return it end ---- Create a new ListIter +--- Create a new ArrayIter --- ----@param t table List-like table. Caller guarantees that this table is a valid list. +---@param t table Array-like table. Caller guarantees that this table is a valid array. Can have +--- holes (nil values). ---@return Iter ---@private -function ListIter.new(t) +function ArrayIter.new(t) local it = {} it._table = t it._head = 1 it._tail = #t + 1 - setmetatable(it, ListIter) + setmetatable(it, ArrayIter) return it end ---- Collects an |iterable| into a table. ---- ---- ```lua ---- -- Equivalent to: ---- vim.iter(f):totable() ---- ``` ---- ----@param f function Iterator function ----@return table -function M.totable(f, ...) - return Iter.new(f, ...):totable() -end - ---- Filters a table or other |iterable|. ---- ---- ```lua ---- -- Equivalent to: ---- vim.iter(src):filter(f):totable() ---- ``` ---- ----@see |Iter:filter()| ---- ----@param f fun(...):boolean Filter function. Accepts the current iterator or table values as ---- arguments and returns true if those values should be kept in the ---- final table ----@param src table|function Table or iterator function to filter ----@return table -function M.filter(f, src, ...) - return Iter.new(src, ...):filter(f):totable() -end - ---- Maps a table or other |iterable|. ---- ---- ```lua ---- -- Equivalent to: ---- vim.iter(src):map(f):totable() ---- ``` ---- ----@see |Iter:map()| ---- ----@param f fun(...): any? Map function. Accepts the current iterator or table values as ---- arguments and returns one or more new values. Nil values are removed ---- from the final table. ----@param src table|function Table or iterator function to filter ----@return table -function M.map(f, src, ...) - return Iter.new(src, ...):map(f):totable() -end - return setmetatable(M, { __call = function(_, ...) return Iter.new(...) diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua index 84e9b4197d..ec00c56c7a 100644 --- a/runtime/lua/vim/keymap.lua +++ b/runtime/lua/vim/keymap.lua @@ -1,5 +1,20 @@ local keymap = {} +--- Table of |:map-arguments|. +--- Same as |nvim_set_keymap()| {opts}, except: +--- - {replace_keycodes} defaults to `true` if "expr" is `true`. +--- +--- Also accepts: +--- @class vim.keymap.set.Opts : vim.api.keyset.keymap +--- @inlinedoc +--- +--- Creates buffer-local mapping, `0` or `true` for current buffer. +--- @field buffer? integer|boolean +--- +--- Make the mapping recursive. Inverse of {noremap}. +--- (Default: `false`) +--- @field remap? boolean + --- Adds a new |mapping|. --- Examples: --- @@ -18,20 +33,12 @@ local keymap = {} --- vim.keymap.set('n', '[%%', '<Plug>(MatchitNormalMultiBackward)') --- ``` --- ----@param mode string|table Mode short-name, see |nvim_set_keymap()|. +---@param mode string|string[] Mode short-name, see |nvim_set_keymap()|. --- Can also be list of modes to create mapping on multiple modes. ---@param lhs string Left-hand side |{lhs}| of the mapping. ---@param rhs string|function Right-hand side |{rhs}| of the mapping, can be a Lua function. --- ----@param opts table|nil Table of |:map-arguments|. ---- - Same as |nvim_set_keymap()| {opts}, except: ---- - "replace_keycodes" defaults to `true` if "expr" is `true`. ---- - "noremap": inverse of "remap" (see below). ---- - Also accepts: ---- - "buffer": (integer|boolean) Creates buffer-local mapping, `0` or `true` ---- for current buffer. ---- - "remap": (boolean) Make the mapping recursive. Inverse of "noremap". ---- Defaults to `false`. +---@param opts? vim.keymap.set.Opts ---@see |nvim_set_keymap()| ---@see |maparg()| ---@see |mapcheck()| @@ -81,6 +88,13 @@ function keymap.set(mode, lhs, rhs, opts) end end +--- @class vim.keymap.del.Opts +--- @inlinedoc +--- +--- Remove a mapping from the given buffer. +--- When `0` or `true`, use the current buffer. +--- @field buffer? integer|boolean + --- Remove an existing mapping. --- Examples: --- @@ -92,11 +106,8 @@ end --- ---@param modes string|string[] ---@param lhs string ----@param opts table|nil A table of optional arguments: ---- - "buffer": (integer|boolean) Remove a mapping from the given buffer. ---- When `0` or `true`, use the current buffer. +---@param opts? vim.keymap.del.Opts ---@see |vim.keymap.set()| ---- function keymap.del(modes, lhs, opts) vim.validate({ mode = { modes, { 's', 't' } }, @@ -106,6 +117,7 @@ function keymap.del(modes, lhs, opts) opts = opts or {} modes = type(modes) == 'string' and { modes } or modes + --- @cast modes string[] local buffer = false ---@type false|integer if opts.buffer ~= nil then diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index d3d8948654..ea77a22416 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -85,7 +85,7 @@ function Loader.get_hash(path) end local function normalize(path) - return fs.normalize(path, { expand_env = false }) + return fs.normalize(path, { expand_env = false, _fast = true }) end --- Gets the rtp excluding after directories. diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index d5c376ba44..1592fd3151 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -1,7 +1,5 @@ local api = vim.api -local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend local validate = vim.validate -local if_nil = vim.F.if_nil local lsp = vim._defer_require('vim.lsp', { _changetracking = ..., --- @module 'vim.lsp._changetracking' @@ -45,6 +43,9 @@ lsp._request_name_to_capability = { [ms.textDocument_prepareCallHierarchy] = { 'callHierarchyProvider' }, [ms.callHierarchy_incomingCalls] = { 'callHierarchyProvider' }, [ms.callHierarchy_outgoingCalls] = { 'callHierarchyProvider' }, + [ms.textDocument_prepareTypeHierarchy] = { 'typeHierarchyProvider' }, + [ms.typeHierarchy_subtypes] = { 'typeHierarchyProvider' }, + [ms.typeHierarchy_supertypes] = { 'typeHierarchyProvider' }, [ms.textDocument_rename] = { 'renameProvider' }, [ms.textDocument_prepareRename] = { 'renameProvider', 'prepareProvider' }, [ms.textDocument_codeAction] = { 'codeActionProvider' }, @@ -63,6 +64,12 @@ lsp._request_name_to_capability = { [ms.textDocument_inlayHint] = { 'inlayHintProvider' }, [ms.textDocument_diagnostic] = { 'diagnosticProvider' }, [ms.inlayHint_resolve] = { 'inlayHintProvider', 'resolveProvider' }, + [ms.textDocument_documentLink] = { 'documentLinkProvider' }, + [ms.documentLink_resolve] = { 'documentLinkProvider', 'resolveProvider' }, + [ms.textDocument_didClose] = { 'textDocumentSync', 'openClose' }, + [ms.textDocument_didOpen] = { 'textDocumentSync', 'openClose' }, + [ms.textDocument_willSave] = { 'textDocumentSync', 'willSave' }, + [ms.textDocument_willSaveWaitUntil] = { 'textDocumentSync', 'willSaveWaitUntil' }, } -- TODO improve handling of scratch buffers with LSP attached. @@ -108,40 +115,7 @@ function lsp._buf_get_line_ending(bufnr) end -- Tracks all clients created via lsp.start_client -local active_clients = {} --- @type table<integer,vim.lsp.Client> -local all_buffer_active_clients = {} --- @type table<integer,table<integer,true>> -local uninitialized_clients = {} --- @type table<integer,vim.lsp.Client> - ----@param bufnr? integer ----@param fn fun(client: vim.lsp.Client, client_id: integer, bufnr: integer) -local function for_each_buffer_client(bufnr, fn, restrict_client_ids) - validate({ - fn = { fn, 'f' }, - restrict_client_ids = { restrict_client_ids, 't', true }, - }) - bufnr = resolve_bufnr(bufnr) - local client_ids = all_buffer_active_clients[bufnr] - if not client_ids or tbl_isempty(client_ids) then - return - end - - if restrict_client_ids and #restrict_client_ids > 0 then - local filtered_client_ids = {} --- @type table<integer,true> - for client_id in pairs(client_ids) do - if vim.list_contains(restrict_client_ids, client_id) then - filtered_client_ids[client_id] = true - end - end - client_ids = filtered_client_ids - end - - for client_id in pairs(client_ids) do - local client = active_clients[client_id] - if client then - fn(client, client_id, bufnr) - end - end -end +local all_clients = {} --- @type table<integer,vim.lsp.Client> local client_errors_base = table.maxn(lsp.rpc.client_errors) local client_errors_offset = 0 @@ -156,7 +130,7 @@ end --- Can be used to look up the string from a the number or the number --- from the string. --- @nodoc -lsp.client_errors = tbl_extend( +lsp.client_errors = vim.tbl_extend( 'error', lsp.rpc.client_errors, client_error('BEFORE_INIT_CALLBACK_ERROR'), @@ -199,16 +173,41 @@ local function once(fn) end end +--- @param client vim.lsp.Client +--- @param config vim.lsp.ClientConfig +--- @return boolean +local function reuse_client_default(client, config) + if client.name ~= config.name then + return false + end + + if config.root_dir then + for _, dir in ipairs(client.workspace_folders or {}) do + -- note: do not need to check client.root_dir since that should be client.workspace_folders[1] + if config.root_dir == dir.name then + return true + end + end + end + + -- TODO(lewis6991): also check config.workspace_folders + + return false +end + --- @class vim.lsp.start.Opts --- @inlinedoc --- --- Predicate used to decide if a client should be re-used. Used on all --- running clients. The default implementation re-uses a client if name and --- root_dir matches. ---- @field reuse_client fun(client: vim.lsp.Client, config: table): boolean +--- @field reuse_client fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean --- --- Buffer handle to attach to if starting or re-using a client (0 for current). --- @field bufnr integer +--- +--- Suppress error reporting if the LSP server fails to start (default false). +--- @field silent? boolean --- Create a new LSP client and start a language server or reuses an already --- running client if one is found matching `name` and `root_dir`. @@ -220,7 +219,7 @@ end --- vim.lsp.start({ --- name = 'my-server-name', --- cmd = {'name-of-language-server-executable'}, ---- root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]), +--- root_dir = vim.fs.root(0, {'pyproject.toml', 'setup.py'}), --- }) --- ``` --- @@ -229,9 +228,9 @@ end --- - `name` arbitrary name for the LSP client. Should be unique per language server. --- - `cmd` command string[] or function, described at |vim.lsp.start_client()|. --- - `root_dir` path to the project root. By default this is used to decide if an existing client ---- should be re-used. The example above uses |vim.fs.find()| and |vim.fs.dirname()| to detect the ---- root by traversing the file system upwards starting from the current directory until either ---- a `pyproject.toml` or `setup.py` file is found. +--- should be re-used. The example above uses |vim.fs.root()| and |vim.fs.dirname()| to detect +--- the root by traversing the file system upwards starting from the current directory until +--- either a `pyproject.toml` or `setup.py` file is found. --- - `workspace_folders` list of `{ uri:string, name: string }` tables specifying the project root --- folders used by the language server. If `nil` the property is derived from `root_dir` for --- convenience. @@ -251,30 +250,32 @@ end --- @return integer? client_id function lsp.start(config, opts) opts = opts or {} - local reuse_client = opts.reuse_client - or function(client, conf) - return client.root_dir == conf.root_dir and client.name == conf.name - end - + local reuse_client = opts.reuse_client or reuse_client_default local bufnr = resolve_bufnr(opts.bufnr) - for _, clients in ipairs({ uninitialized_clients, lsp.get_clients() }) do - for _, client in pairs(clients) do - if reuse_client(client, config) then - lsp.buf_attach_client(bufnr, client.id) + for _, client in pairs(all_clients) do + if reuse_client(client, config) then + if lsp.buf_attach_client(bufnr, client.id) then return client.id + else + return nil end end end - local client_id = lsp.start_client(config) + local client_id, err = lsp.start_client(config) + if err then + if not opts.silent then + vim.notify(err, vim.log.levels.WARN) + end + return nil + end - if not client_id then - return -- lsp.start_client will have printed an error + if client_id and lsp.buf_attach_client(bufnr, client_id) then + return client_id end - lsp.buf_attach_client(bufnr, client_id) - return client_id + return nil end --- Consumes the latest progress messages from all clients and formats them as a string. @@ -317,6 +318,7 @@ local function is_empty_or_default(bufnr, option) end local info = api.nvim_get_option_info2(option, { buf = bufnr }) + ---@param e vim.fn.getscriptinfo.ret local scriptinfo = vim.tbl_filter(function(e) return e.sid == info.last_set_sid end, vim.fn.getscriptinfo()) @@ -355,7 +357,7 @@ function lsp._set_defaults(client, bufnr) and is_empty_or_default(bufnr, 'keywordprg') and vim.fn.maparg('K', 'n', false, false) == '' then - vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr }) + vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr, desc = 'vim.lsp.buf.hover()' }) end end) if client.supports_method(ms.textDocument_diagnostic) then @@ -383,48 +385,30 @@ local function reset_defaults(bufnr) end) end ---- @param client vim.lsp.Client -local function on_client_init(client) - local id = client.id - uninitialized_clients[id] = nil - -- Only assign after initialized. - active_clients[id] = client - -- If we had been registered before we start, then send didOpen This can - -- happen if we attach to buffers before initialize finishes or if - -- someone restarts a client. - for bufnr, client_ids in pairs(all_buffer_active_clients) do - if client_ids[id] then - client.on_attach(bufnr) - end - end -end - --- @param code integer --- @param signal integer --- @param client_id integer local function on_client_exit(code, signal, client_id) - local client = active_clients[client_id] or uninitialized_clients[client_id] - - for bufnr, client_ids in pairs(all_buffer_active_clients) do - if client_ids[client_id] then - vim.schedule(function() - if client and client.attached_buffers[bufnr] then - api.nvim_exec_autocmds('LspDetach', { - buffer = bufnr, - modeline = false, - data = { client_id = client_id }, - }) - end + local client = all_clients[client_id] + + for bufnr in pairs(client.attached_buffers) do + vim.schedule(function() + if client and client.attached_buffers[bufnr] then + api.nvim_exec_autocmds('LspDetach', { + buffer = bufnr, + modeline = false, + data = { client_id = client_id }, + }) + end - local namespace = vim.lsp.diagnostic.get_namespace(client_id) - vim.diagnostic.reset(namespace, bufnr) + local namespace = vim.lsp.diagnostic.get_namespace(client_id) + vim.diagnostic.reset(namespace, bufnr) + client.attached_buffers[bufnr] = nil - client_ids[client_id] = nil - if vim.tbl_isempty(client_ids) then - reset_defaults(bufnr) - end - end) - end + if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then + reset_defaults(bufnr) + end + end) end local name = client.name or 'unknown' @@ -432,8 +416,7 @@ local function on_client_exit(code, signal, client_id) -- Schedule the deletion of the client object so that it exists in the execution of LspDetach -- autocommands vim.schedule(function() - active_clients[client_id] = nil - uninitialized_clients[client_id] = nil + all_clients[client_id] = nil -- Client can be absent if executable starts, but initialize fails -- init/attach won't have happened @@ -455,51 +438,26 @@ end --- Starts and initializes a client with the given configuration. --- @param config vim.lsp.ClientConfig Configuration for the server. ---- @return integer|nil client_id |vim.lsp.get_client_by_id()| Note: client may not be ---- fully initialized. Use `on_init` to do any actions once ---- the client has been initialized. +--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be +--- fully initialized. Use `on_init` to do any actions once +--- the client has been initialized. +--- @return string? # Error message, if any function lsp.start_client(config) - local client = require('vim.lsp.client').create(config) - - if not client then - return + local ok, res = pcall(require('vim.lsp.client').create, config) + if not ok then + return nil, res --[[@as string]] end - --- @diagnostic disable-next-line: invisible - table.insert(client._on_init_cbs, on_client_init) + local client = assert(res) + --- @diagnostic disable-next-line: invisible table.insert(client._on_exit_cbs, on_client_exit) - -- Store the uninitialized_clients for cleanup in case we exit before initialize finishes. - uninitialized_clients[client.id] = client + all_clients[client.id] = client client:initialize() - return client.id -end - ---- Notify all attached clients that a buffer has changed. ----@param _ integer ----@param bufnr integer ----@param changedtick integer ----@param firstline integer ----@param lastline integer ----@param new_lastline integer ----@return true? -local function text_document_did_change_handler( - _, - bufnr, - changedtick, - firstline, - lastline, - new_lastline -) - -- Detach (nvim_buf_attach) via returning True to on_lines if no clients are attached - if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then - return true - end - util.buf_versions[bufnr] = changedtick - changetracking.send_changes(bufnr, firstline, lastline, new_lastline) + return client.id, nil end ---Buffer lifecycle handler for textDocument/didSave @@ -543,6 +501,117 @@ local function text_document_did_save_handler(bufnr) end end +---@param bufnr integer resolved buffer +---@param client vim.lsp.Client +local function buf_detach_client(bufnr, client) + api.nvim_exec_autocmds('LspDetach', { + buffer = bufnr, + modeline = false, + data = { client_id = client.id }, + }) + + changetracking.reset_buf(client, bufnr) + + if client.supports_method(ms.textDocument_didClose) then + local uri = vim.uri_from_bufnr(bufnr) + local params = { textDocument = { uri = uri } } + client.notify(ms.textDocument_didClose, params) + end + + client.attached_buffers[bufnr] = nil + util.buf_versions[bufnr] = nil + + local namespace = lsp.diagnostic.get_namespace(client.id) + vim.diagnostic.reset(namespace, bufnr) +end + +--- @type table<integer,true> +local attached_buffers = {} + +--- @param bufnr integer +local function buf_attach(bufnr) + if attached_buffers[bufnr] then + return + end + attached_buffers[bufnr] = true + + local uri = vim.uri_from_bufnr(bufnr) + local augroup = ('lsp_b_%d_save'):format(bufnr) + local group = api.nvim_create_augroup(augroup, { clear = true }) + api.nvim_create_autocmd('BufWritePre', { + group = group, + buffer = bufnr, + desc = 'vim.lsp: textDocument/willSave', + callback = function(ctx) + for _, client in ipairs(lsp.get_clients({ bufnr = ctx.buf })) do + local params = { + textDocument = { + uri = uri, + }, + reason = protocol.TextDocumentSaveReason.Manual, ---@type integer + } + if client.supports_method(ms.textDocument_willSave) then + client.notify(ms.textDocument_willSave, params) + end + if client.supports_method(ms.textDocument_willSaveWaitUntil) then + local result, err = + client.request_sync(ms.textDocument_willSaveWaitUntil, params, 1000, ctx.buf) + if result and result.result then + util.apply_text_edits(result.result, ctx.buf, client.offset_encoding) + elseif err then + log.error(vim.inspect(err)) + end + end + end + end, + }) + api.nvim_create_autocmd('BufWritePost', { + group = group, + buffer = bufnr, + desc = 'vim.lsp: textDocument/didSave handler', + callback = function(ctx) + text_document_did_save_handler(ctx.buf) + end, + }) + -- First time, so attach and set up stuff. + api.nvim_buf_attach(bufnr, false, { + on_lines = function(_, _, changedtick, firstline, lastline, new_lastline) + if #lsp.get_clients({ bufnr = bufnr }) == 0 then + return true -- detach + end + util.buf_versions[bufnr] = changedtick + changetracking.send_changes(bufnr, firstline, lastline, new_lastline) + end, + + on_reload = function() + local clients = lsp.get_clients({ bufnr = bufnr }) + local params = { textDocument = { uri = uri } } + for _, client in ipairs(clients) do + changetracking.reset_buf(client, bufnr) + if client.supports_method(ms.textDocument_didClose) then + client.notify(ms.textDocument_didClose, params) + end + end + for _, client in ipairs(clients) do + client:_text_document_did_open_handler(bufnr) + end + end, + + on_detach = function() + local clients = lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) + for _, client in ipairs(clients) do + buf_detach_client(bufnr, client) + end + attached_buffers[bufnr] = nil + end, + + -- TODO if we know all of the potential clients ahead of time, then we + -- could conditionally set this. + -- utf_sizes = size_index > 1; + utf_sizes = true, + }) +end + --- Implements the `textDocument/did…` notifications required to track a buffer --- for any language server. --- @@ -561,92 +630,24 @@ function lsp.buf_attach_client(bufnr, client_id) log.warn(string.format('buf_attach_client called on unloaded buffer (id: %d): ', bufnr)) return false end - local buffer_client_ids = all_buffer_active_clients[bufnr] - -- This is our first time attaching to this buffer. - if not buffer_client_ids then - buffer_client_ids = {} - all_buffer_active_clients[bufnr] = buffer_client_ids - local uri = vim.uri_from_bufnr(bufnr) - local augroup = ('lsp_c_%d_b_%d_save'):format(client_id, bufnr) - local group = api.nvim_create_augroup(augroup, { clear = true }) - api.nvim_create_autocmd('BufWritePre', { - group = group, - buffer = bufnr, - desc = 'vim.lsp: textDocument/willSave', - callback = function(ctx) - for _, client in ipairs(lsp.get_clients({ bufnr = ctx.buf })) do - local params = { - textDocument = { - uri = uri, - }, - reason = protocol.TextDocumentSaveReason.Manual, - } - if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSave') then - client.notify(ms.textDocument_willSave, params) - end - if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'willSaveWaitUntil') then - local result, err = - client.request_sync(ms.textDocument_willSaveWaitUntil, params, 1000, ctx.buf) - if result and result.result then - util.apply_text_edits(result.result, ctx.buf, client.offset_encoding) - elseif err then - log.error(vim.inspect(err)) - end - end - end - end, - }) - api.nvim_create_autocmd('BufWritePost', { - group = group, - buffer = bufnr, - desc = 'vim.lsp: textDocument/didSave handler', - callback = function(ctx) - text_document_did_save_handler(ctx.buf) - end, - }) - -- First time, so attach and set up stuff. - api.nvim_buf_attach(bufnr, false, { - on_lines = text_document_did_change_handler, - on_reload = function() - local params = { textDocument = { uri = uri } } - for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do - changetracking.reset_buf(client, bufnr) - if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then - client.notify(ms.textDocument_didClose, params) - end - client:_text_document_did_open_handler(bufnr) - end - end, - on_detach = function() - local params = { textDocument = { uri = uri } } - for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do - changetracking.reset_buf(client, bufnr) - if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then - client.notify(ms.textDocument_didClose, params) - end - client.attached_buffers[bufnr] = nil - end - util.buf_versions[bufnr] = nil - all_buffer_active_clients[bufnr] = nil - end, - -- TODO if we know all of the potential clients ahead of time, then we - -- could conditionally set this. - -- utf_sizes = size_index > 1; - utf_sizes = true, - }) + local client = lsp.get_client_by_id(client_id) + if not client then + return false end - if buffer_client_ids[client_id] then + buf_attach(bufnr) + + if client.attached_buffers[bufnr] then return true end - -- This is our first time attaching this client to this buffer. - buffer_client_ids[client_id] = true - local client = active_clients[client_id] + client.attached_buffers[bufnr] = true + + -- This is our first time attaching this client to this buffer. -- Send didOpen for the client if it is initialized. If it isn't initialized -- then it will send didOpen on initialize. - if client then + if client.initialized then client:_on_attach(bufnr) end return true @@ -665,7 +666,7 @@ function lsp.buf_detach_client(bufnr, client_id) }) bufnr = resolve_bufnr(bufnr) - local client = lsp.get_client_by_id(client_id) + local client = all_clients[client_id] if not client or not client.attached_buffers[bufnr] then vim.notify( string.format( @@ -675,32 +676,9 @@ function lsp.buf_detach_client(bufnr, client_id) ) ) return + else + buf_detach_client(bufnr, client) end - - api.nvim_exec_autocmds('LspDetach', { - buffer = bufnr, - modeline = false, - data = { client_id = client_id }, - }) - - changetracking.reset_buf(client, bufnr) - - if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then - local uri = vim.uri_from_bufnr(bufnr) - local params = { textDocument = { uri = uri } } - client.notify(ms.textDocument_didClose, params) - end - - client.attached_buffers[bufnr] = nil - util.buf_versions[bufnr] = nil - - all_buffer_active_clients[bufnr][client_id] = nil - if #vim.tbl_keys(all_buffer_active_clients[bufnr]) == 0 then - all_buffer_active_clients[bufnr] = nil - end - - local namespace = lsp.diagnostic.get_namespace(client_id) - vim.diagnostic.reset(namespace, bufnr) end --- Checks if a buffer is attached for a particular client. @@ -708,7 +686,7 @@ end ---@param bufnr (integer) Buffer handle, or 0 for current ---@param client_id (integer) the client id function lsp.buf_is_attached(bufnr, client_id) - return (all_buffer_active_clients[resolve_bufnr(bufnr)] or {})[client_id] == true + return lsp.get_clients({ bufnr = bufnr, id = client_id, _uninitialized = true })[1] ~= nil end --- Gets a client by id, or nil if the id is invalid. @@ -718,7 +696,7 @@ end --- ---@return (nil|vim.lsp.Client) client rpc object function lsp.get_client_by_id(client_id) - return active_clients[client_id] or uninitialized_clients[client_id] + return all_clients[client_id] end --- Returns list of buffers attached to client_id. @@ -726,7 +704,7 @@ end ---@param client_id integer client id ---@return integer[] buffers list of buffer ids function lsp.get_buffers_by_client_id(client_id) - local client = lsp.get_client_by_id(client_id) + local client = all_clients[client_id] return client and vim.tbl_keys(client.attached_buffers) or {} end @@ -742,17 +720,22 @@ end --- By default asks the server to shutdown, unless stop was requested --- already for this client, then force-shutdown is attempted. --- ----@param client_id integer|vim.lsp.Client id or |vim.lsp.Client| object, or list thereof ----@param force boolean|nil shutdown forcefully +---@param client_id integer|integer[]|vim.lsp.Client[] id, list of id's, or list of |vim.lsp.Client| objects +---@param force? boolean shutdown forcefully function lsp.stop_client(client_id, force) + --- @type integer[]|vim.lsp.Client[] local ids = type(client_id) == 'table' and client_id or { client_id } for _, id in ipairs(ids) do - if type(id) == 'table' and id.stop ~= nil then - id.stop(force) - elseif active_clients[id] then - active_clients[id].stop(force) - elseif uninitialized_clients[id] then - uninitialized_clients[id].stop(true) + if type(id) == 'table' then + if id.stop then + id.stop(force) + end + else + --- @cast id -vim.lsp.Client + local client = all_clients[id] + if client then + client.stop(force) + end end end end @@ -772,6 +755,9 @@ end --- --- Only return clients supporting the given method --- @field method? string +--- +--- Also return uninitialized clients. +--- @field package _uninitialized? boolean --- Get active clients. --- @@ -784,15 +770,16 @@ function lsp.get_clients(filter) local clients = {} --- @type vim.lsp.Client[] - local t = filter.bufnr and (all_buffer_active_clients[resolve_bufnr(filter.bufnr)] or {}) - or active_clients - for client_id in pairs(t) do - local client = active_clients[client_id] + local bufnr = filter.bufnr and resolve_bufnr(filter.bufnr) + + for _, client in pairs(all_clients) do if client and (filter.id == nil or client.id == filter.id) + and (filter.bufnr == nil or client.attached_buffers[bufnr]) and (filter.name == nil or client.name == filter.name) and (filter.method == nil or client.supports_method(filter.method, { bufnr = filter.bufnr })) + and (filter._uninitialized or client.initialized) then clients[#clients + 1] = client end @@ -810,15 +797,9 @@ end api.nvim_create_autocmd('VimLeavePre', { desc = 'vim.lsp: exit handler', callback = function() + local active_clients = lsp.get_clients() log.info('exit_handler', active_clients) - for _, client in pairs(uninitialized_clients) do - client.stop(true) - end - -- TODO handle v:dying differently? - if tbl_isempty(active_clients) then - return - end - for _, client in pairs(active_clients) do + for _, client in pairs(all_clients) do client.stop() end @@ -827,7 +808,7 @@ api.nvim_create_autocmd('VimLeavePre', { local send_kill = false for client_id, client in pairs(active_clients) do - local timeout = if_nil(client.flags.exit_timeout, false) + local timeout = client.flags.exit_timeout if timeout then send_kill = true timeouts[client_id] = timeout @@ -910,7 +891,7 @@ function lsp.buf_request(bufnr, method, params, handler) local function _cancel_all_requests() for client_id, request_id in pairs(client_request_ids) do - local client = active_clients[client_id] + local client = all_clients[client_id] client.cancel_request(request_id) end end @@ -924,12 +905,12 @@ end ---@param bufnr (integer) Buffer handle, or 0 for current. ---@param method (string) LSP method name ---@param params (table|nil) Parameters to send to the server ----@param handler fun(results: table<integer, {error: lsp.ResponseError, result: any}>) (function) +---@param handler fun(results: table<integer, {error: lsp.ResponseError?, result: any}>) (function) --- Handler called after all requests are completed. Server results are passed as --- a `client_id:result` map. ---@return function cancel Function that cancels all requests. function lsp.buf_request_all(bufnr, method, params, handler) - local results = {} --- @type table<integer,{error:string, result:any}> + local results = {} --- @type table<integer,{error: lsp.ResponseError?, result: any}> local result_count = 0 local expected_result_count = 0 @@ -967,10 +948,10 @@ end ---@param params table? Parameters to send to the server ---@param timeout_ms integer? Maximum time in milliseconds to wait for a result. --- (default: `1000`) ----@return table<integer, {err: lsp.ResponseError, result: any}>? result Map of client_id:request_result. +---@return table<integer, {error: lsp.ResponseError?, result: any}>? result Map of client_id:request_result. ---@return string? err On timeout, cancel, or error, `err` is a string describing the failure reason, and `result` is nil. function lsp.buf_request_sync(bufnr, method, params, timeout_ms) - local request_results + local request_results ---@type table local cancel = lsp.buf_request_all(bufnr, method, params, function(it) request_results = it @@ -1106,7 +1087,7 @@ end ---@return boolean stopped true if client is stopped, false otherwise. function lsp.client_is_stopped(client_id) assert(client_id, 'missing client_id param') - return active_clients[client_id] == nil and not uninitialized_clients[client_id] + return not all_clients[client_id] end --- Gets a map of client_id:client pairs for the given buffer, where each value @@ -1172,7 +1153,11 @@ function lsp.for_each_buffer_client(bufnr, fn) 'lsp.get_clients({ bufnr = bufnr }) with regular loop', '0.12' ) - return for_each_buffer_client(bufnr, fn) + bufnr = resolve_bufnr(bufnr) + + for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do + fn(client, client.id, bufnr) + end end --- Function to manage overriding defaults for LSP handlers. diff --git a/runtime/lua/vim/lsp/_meta/protocol.lua b/runtime/lua/vim/lsp/_meta/protocol.lua index a5da5ac6b7..9a11972007 100644 --- a/runtime/lua/vim/lsp/_meta/protocol.lua +++ b/runtime/lua/vim/lsp/_meta/protocol.lua @@ -2534,8 +2534,14 @@ error('Cannot require a meta file') ---@proposed ---@field inlineCompletionProvider? boolean|lsp.InlineCompletionOptions --- +---Text document specific server capabilities. +--- +---@since 3.18.0 +---@proposed +---@field textDocument? lsp._anonym12.textDocument +--- ---Workspace specific server capabilities. ----@field workspace? lsp._anonym12.workspace +---@field workspace? lsp._anonym14.workspace --- ---Experimental server capabilities. ---@field experimental? lsp.LSPAny @@ -2598,8 +2604,10 @@ error('Cannot require a meta file') ---appears in the user interface. ---@field source? string --- ----The diagnostic's message. It usually appears in the user interface ----@field message string +---The diagnostic's message. It usually appears in the user interface. +--- +---@since 3.18.0 - support for `MarkupContent`. This is guarded by the client capability `textDocument.diagnostic.markupMessageSupport`. +---@field message string|lsp.MarkupContent --- ---Additional metadata about the diagnostic. --- @@ -2684,7 +2692,7 @@ error('Cannot require a meta file') ---capabilities. --- ---@since 3.17.0 ----@field completionItem? lsp._anonym13.completionItem +---@field completionItem? lsp._anonym15.completionItem ---Hover options. ---@class lsp.HoverOptions: lsp.WorkDoneProgressOptions @@ -2811,6 +2819,8 @@ error('Cannot require a meta file') ---errors are currently presented to the user for the given range. There is no guarantee ---that these accurately reflect the error state of the resource. The primary parameter ---to compute code actions is the provided range. +--- +---Note that the client should check the `textDocument.diagnostic.markupMessageSupport` server capability before sending diagnostics with markup messages to a server. ---@field diagnostics lsp.Diagnostic[] --- ---Requested kind of actions to return. @@ -3146,7 +3156,7 @@ error('Cannot require a meta file') ---@class lsp.NotebookDocumentSyncOptions --- ---The notebooks to be synced ----@field notebookSelector (lsp._anonym14.notebookSelector|lsp._anonym16.notebookSelector)[] +---@field notebookSelector (lsp._anonym16.notebookSelector|lsp._anonym18.notebookSelector)[] --- ---Whether save notification should be forwarded to ---the server. Will only be honored if mode === `notebook`. @@ -3514,7 +3524,7 @@ error('Cannot require a meta file') ---anymore since the information is outdated). --- ---@since 3.17.0 ----@field staleRequestSupport? lsp._anonym18.staleRequestSupport +---@field staleRequestSupport? lsp._anonym20.staleRequestSupport --- ---Client capabilities specific to regular expressions. --- @@ -3590,7 +3600,7 @@ error('Cannot require a meta file') ---create file, rename file and delete file changes. --- ---@since 3.16.0 ----@field changeAnnotationSupport? lsp._anonym19.changeAnnotationSupport +---@field changeAnnotationSupport? lsp._anonym21.changeAnnotationSupport ---@class lsp.DidChangeConfigurationClientCapabilities --- @@ -3617,20 +3627,20 @@ error('Cannot require a meta file') ---@field dynamicRegistration? boolean --- ---Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. ----@field symbolKind? lsp._anonym20.symbolKind +---@field symbolKind? lsp._anonym22.symbolKind --- ---The client supports tags on `SymbolInformation`. ---Clients supporting tags have to handle unknown tags gracefully. --- ---@since 3.16.0 ----@field tagSupport? lsp._anonym21.tagSupport +---@field tagSupport? lsp._anonym23.tagSupport --- ---The client support partial workspace symbols. The client will send the ---request `workspaceSymbol/resolve` to the server to resolve additional ---properties. --- ---@since 3.17.0 ----@field resolveSupport? lsp._anonym22.resolveSupport +---@field resolveSupport? lsp._anonym24.resolveSupport ---The client capabilities of a {@link ExecuteCommandRequest}. ---@class lsp.ExecuteCommandClientCapabilities @@ -3775,9 +3785,9 @@ error('Cannot require a meta file') --- ---The client supports the following `CompletionItem` specific ---capabilities. ----@field completionItem? lsp._anonym23.completionItem +---@field completionItem? lsp._anonym25.completionItem --- ----@field completionItemKind? lsp._anonym27.completionItemKind +---@field completionItemKind? lsp._anonym29.completionItemKind --- ---Defines how the client handles whitespace and indentation ---when accepting a completion item that uses multi line @@ -3794,7 +3804,7 @@ error('Cannot require a meta file') ---capabilities. --- ---@since 3.17.0 ----@field completionList? lsp._anonym28.completionList +---@field completionList? lsp._anonym30.completionList ---@class lsp.HoverClientCapabilities --- @@ -3813,7 +3823,7 @@ error('Cannot require a meta file') --- ---The client supports the following `SignatureInformation` ---specific properties. ----@field signatureInformation? lsp._anonym29.signatureInformation +---@field signatureInformation? lsp._anonym31.signatureInformation --- ---The client supports to send additional context information for a ---`textDocument/signatureHelp` request. A client that opts into @@ -3891,7 +3901,7 @@ error('Cannot require a meta file') --- ---Specific capabilities for the `SymbolKind` in the ---`textDocument/documentSymbol` request. ----@field symbolKind? lsp._anonym31.symbolKind +---@field symbolKind? lsp._anonym33.symbolKind --- ---The client supports hierarchical document symbols. ---@field hierarchicalDocumentSymbolSupport? boolean @@ -3901,7 +3911,7 @@ error('Cannot require a meta file') ---Clients supporting tags have to handle unknown tags gracefully. --- ---@since 3.16.0 ----@field tagSupport? lsp._anonym32.tagSupport +---@field tagSupport? lsp._anonym34.tagSupport --- ---The client supports an additional label presented in the UI when ---registering a document symbol provider. @@ -3920,7 +3930,7 @@ error('Cannot require a meta file') ---set the request can only return `Command` literals. --- ---@since 3.8.0 ----@field codeActionLiteralSupport? lsp._anonym33.codeActionLiteralSupport +---@field codeActionLiteralSupport? lsp._anonym35.codeActionLiteralSupport --- ---Whether code action supports the `isPreferred` property. --- @@ -3943,7 +3953,7 @@ error('Cannot require a meta file') ---properties via a separate `codeAction/resolve` request. --- ---@since 3.16.0 ----@field resolveSupport? lsp._anonym35.resolveSupport +---@field resolveSupport? lsp._anonym37.resolveSupport --- ---Whether the client honors the change annotations in ---text edits and resource operations returned via the @@ -4051,12 +4061,12 @@ error('Cannot require a meta file') ---Specific options for the folding range kind. --- ---@since 3.17.0 ----@field foldingRangeKind? lsp._anonym36.foldingRangeKind +---@field foldingRangeKind? lsp._anonym38.foldingRangeKind --- ---Specific options for the folding range. --- ---@since 3.17.0 ----@field foldingRange? lsp._anonym37.foldingRange +---@field foldingRange? lsp._anonym39.foldingRange ---@class lsp.SelectionRangeClientCapabilities --- @@ -4075,7 +4085,7 @@ error('Cannot require a meta file') ---Clients supporting tags have to handle unknown tags gracefully. --- ---@since 3.15.0 ----@field tagSupport? lsp._anonym38.tagSupport +---@field tagSupport? lsp._anonym40.tagSupport --- ---Whether the client interprets the version property of the ---`textDocument/publishDiagnostics` notification's parameter. @@ -4119,7 +4129,7 @@ error('Cannot require a meta file') ---`request.range` are both set to true but the server only provides a ---range provider the client might not render a minimap correctly or might ---even decide to not show any semantic tokens at all. ----@field requests lsp._anonym39.requests +---@field requests lsp._anonym41.requests --- ---The token types that the client supports. ---@field tokenTypes string[] @@ -4202,7 +4212,7 @@ error('Cannot require a meta file') --- ---Indicates which properties a client can resolve lazily on an inlay ---hint. ----@field resolveSupport? lsp._anonym42.resolveSupport +---@field resolveSupport? lsp._anonym44.resolveSupport ---Client capabilities specific to diagnostic pull requests. --- @@ -4216,6 +4226,9 @@ error('Cannot require a meta file') --- ---Whether the clients supports related documents for document diagnostic pulls. ---@field relatedDocumentSupport? boolean +--- +---Whether the client supports `MarkupContent` in diagnostic messages. +---@field markupMessageSupport? boolean ---Client capabilities specific to inline completions. --- @@ -4244,7 +4257,7 @@ error('Cannot require a meta file') ---@class lsp.ShowMessageRequestClientCapabilities --- ---Capabilities specific to the `MessageActionItem` type. ----@field messageActionItem? lsp._anonym43.messageActionItem +---@field messageActionItem? lsp._anonym45.messageActionItem ---Client capabilities for the showDocument request. --- @@ -4671,7 +4684,7 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@alias lsp.DocumentDiagnosticReport lsp.RelatedFullDocumentDiagnosticReport|lsp.RelatedUnchangedDocumentDiagnosticReport ----@alias lsp.PrepareRenameResult lsp.Range|lsp._anonym44.PrepareRenameResult|lsp._anonym45.PrepareRenameResult +---@alias lsp.PrepareRenameResult lsp.Range|lsp._anonym46.PrepareRenameResult|lsp._anonym47.PrepareRenameResult ---A document selector is the combination of one or many document filters. --- @@ -4692,7 +4705,7 @@ error('Cannot require a meta file') ---An event describing a change to a text document. If only a text is provided ---it is considered to be the full content of the document. ----@alias lsp.TextDocumentContentChangeEvent lsp._anonym46.TextDocumentContentChangeEvent|lsp._anonym47.TextDocumentContentChangeEvent +---@alias lsp.TextDocumentContentChangeEvent lsp._anonym48.TextDocumentContentChangeEvent|lsp._anonym49.TextDocumentContentChangeEvent ---MarkedString can be used to render human readable text. It is either a markdown string ---or a code-block that provides a language and a code snippet. The language identifier @@ -4706,7 +4719,7 @@ error('Cannot require a meta file') --- ---Note that markdown strings will be sanitized - that means html will be escaped. ---@deprecated use MarkupContent instead. ----@alias lsp.MarkedString string|lsp._anonym48.MarkedString +---@alias lsp.MarkedString string|lsp._anonym50.MarkedString ---A document filter describes a top level text document or ---a notebook cell document. @@ -4739,14 +4752,14 @@ error('Cannot require a meta file') ---\@sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**package.json' }` --- ---@since 3.17.0 ----@alias lsp.TextDocumentFilter lsp._anonym49.TextDocumentFilter|lsp._anonym50.TextDocumentFilter|lsp._anonym51.TextDocumentFilter +---@alias lsp.TextDocumentFilter lsp._anonym51.TextDocumentFilter|lsp._anonym52.TextDocumentFilter|lsp._anonym53.TextDocumentFilter ---A notebook document filter denotes a notebook document by ---different properties. The properties will be match ---against the notebook's URI (same as with documents) --- ---@since 3.17.0 ----@alias lsp.NotebookDocumentFilter lsp._anonym52.NotebookDocumentFilter|lsp._anonym53.NotebookDocumentFilter|lsp._anonym54.NotebookDocumentFilter +---@alias lsp.NotebookDocumentFilter lsp._anonym54.NotebookDocumentFilter|lsp._anonym55.NotebookDocumentFilter|lsp._anonym56.NotebookDocumentFilter ---The glob pattern to watch relative to the base path. Glob patterns can have the following syntax: ---- `*` to match one or more characters in a path segment @@ -4856,7 +4869,19 @@ error('Cannot require a meta file') ---The client's version as defined by the client. ---@field version? string ----@class lsp._anonym12.workspace +---@class lsp._anonym13.textDocument.diagnostic +--- +---Whether the server supports `MarkupContent` in diagnostic messages. +---@field markupMessageSupport? boolean + +---@class lsp._anonym12.textDocument +--- +---Capabilities specific to the diagnostic pull model. +--- +---@since 3.18.0 +---@field diagnostic? lsp._anonym13.textDocument.diagnostic + +---@class lsp._anonym14.workspace --- ---The server supports workspace folder. --- @@ -4868,7 +4893,7 @@ error('Cannot require a meta file') ---@since 3.16.0 ---@field fileOperations? lsp.FileOperationOptions ----@class lsp._anonym13.completionItem +---@class lsp._anonym15.completionItem --- ---The server has support for completion item label ---details (see also `CompletionItemLabelDetails`) when @@ -4877,11 +4902,11 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field labelDetailsSupport? boolean ----@class lsp._anonym15.notebookSelector.cells +---@class lsp._anonym17.notebookSelector.cells --- ---@field language string ----@class lsp._anonym14.notebookSelector +---@class lsp._anonym16.notebookSelector --- ---The notebook to be synced If a string ---value is provided it matches against the @@ -4889,13 +4914,13 @@ error('Cannot require a meta file') ---@field notebook string|lsp.NotebookDocumentFilter --- ---The cells of the matching notebook to be synced. ----@field cells? lsp._anonym15.notebookSelector.cells[] +---@field cells? lsp._anonym17.notebookSelector.cells[] ----@class lsp._anonym17.notebookSelector.cells +---@class lsp._anonym19.notebookSelector.cells --- ---@field language string ----@class lsp._anonym16.notebookSelector +---@class lsp._anonym18.notebookSelector --- ---The notebook to be synced If a string ---value is provided it matches against the @@ -4903,9 +4928,9 @@ error('Cannot require a meta file') ---@field notebook? string|lsp.NotebookDocumentFilter --- ---The cells of the matching notebook to be synced. ----@field cells lsp._anonym17.notebookSelector.cells[] +---@field cells lsp._anonym19.notebookSelector.cells[] ----@class lsp._anonym18.staleRequestSupport +---@class lsp._anonym20.staleRequestSupport --- ---The client will actively cancel the request. ---@field cancel boolean @@ -4915,14 +4940,14 @@ error('Cannot require a meta file') ---response with error code `ContentModified` ---@field retryOnContentModified string[] ----@class lsp._anonym19.changeAnnotationSupport +---@class lsp._anonym21.changeAnnotationSupport --- ---Whether the client groups edits with equal labels into tree nodes, ---for instance all edits labelled with "Changes in Strings" would ---be a tree node. ---@field groupsOnLabel? boolean ----@class lsp._anonym20.symbolKind +---@class lsp._anonym22.symbolKind --- ---The symbol kind values the client supports. When this ---property exists the client also guarantees that it will @@ -4934,32 +4959,32 @@ error('Cannot require a meta file') ---the initial version of the protocol. ---@field valueSet? lsp.SymbolKind[] ----@class lsp._anonym21.tagSupport +---@class lsp._anonym23.tagSupport --- ---The tags supported by the client. ---@field valueSet lsp.SymbolTag[] ----@class lsp._anonym22.resolveSupport +---@class lsp._anonym24.resolveSupport --- ---The properties that a client can resolve lazily. Usually ---`location.range` ---@field properties string[] ----@class lsp._anonym24.completionItem.tagSupport +---@class lsp._anonym26.completionItem.tagSupport --- ---The tags supported by the client. ---@field valueSet lsp.CompletionItemTag[] ----@class lsp._anonym25.completionItem.resolveSupport +---@class lsp._anonym27.completionItem.resolveSupport --- ---The properties that a client can resolve lazily. ---@field properties string[] ----@class lsp._anonym26.completionItem.insertTextModeSupport +---@class lsp._anonym28.completionItem.insertTextModeSupport --- ---@field valueSet lsp.InsertTextMode[] ----@class lsp._anonym23.completionItem +---@class lsp._anonym25.completionItem --- ---Client supports snippets as insert text. --- @@ -4988,7 +5013,7 @@ error('Cannot require a meta file') ---a resolve call. --- ---@since 3.15.0 ----@field tagSupport? lsp._anonym24.completionItem.tagSupport +---@field tagSupport? lsp._anonym26.completionItem.tagSupport --- ---Client support insert replace edit to control different behavior if a ---completion item is inserted in the text or should replace text. @@ -5001,14 +5026,14 @@ error('Cannot require a meta file') ---and `details` could be resolved lazily. --- ---@since 3.16.0 ----@field resolveSupport? lsp._anonym25.completionItem.resolveSupport +---@field resolveSupport? lsp._anonym27.completionItem.resolveSupport --- ---The client supports the `insertTextMode` property on ---a completion item to override the whitespace handling mode ---as defined by the client (see `insertTextMode`). --- ---@since 3.16.0 ----@field insertTextModeSupport? lsp._anonym26.completionItem.insertTextModeSupport +---@field insertTextModeSupport? lsp._anonym28.completionItem.insertTextModeSupport --- ---The client has support for completion item label ---details (see also `CompletionItemLabelDetails`). @@ -5016,7 +5041,7 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field labelDetailsSupport? boolean ----@class lsp._anonym27.completionItemKind +---@class lsp._anonym29.completionItemKind --- ---The completion item kind values the client supports. When this ---property exists the client also guarantees that it will @@ -5028,7 +5053,7 @@ error('Cannot require a meta file') ---the initial version of the protocol. ---@field valueSet? lsp.CompletionItemKind[] ----@class lsp._anonym28.completionList +---@class lsp._anonym30.completionList --- ---The client supports the following itemDefaults on ---a completion list. @@ -5040,7 +5065,7 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field itemDefaults? string[] ----@class lsp._anonym30.signatureInformation.parameterInformation +---@class lsp._anonym32.signatureInformation.parameterInformation --- ---The client supports processing label offsets instead of a ---simple label string. @@ -5048,14 +5073,14 @@ error('Cannot require a meta file') ---@since 3.14.0 ---@field labelOffsetSupport? boolean ----@class lsp._anonym29.signatureInformation +---@class lsp._anonym31.signatureInformation --- ---Client supports the following content formats for the documentation ---property. The order describes the preferred format of the client. ---@field documentationFormat? lsp.MarkupKind[] --- ---Client capabilities specific to parameter information. ----@field parameterInformation? lsp._anonym30.signatureInformation.parameterInformation +---@field parameterInformation? lsp._anonym32.signatureInformation.parameterInformation --- ---The client supports the `activeParameter` property on `SignatureInformation` ---literal. @@ -5070,7 +5095,7 @@ error('Cannot require a meta file') ---@since 3.18.0 ---@field noActiveParameterSupport? boolean ----@class lsp._anonym31.symbolKind +---@class lsp._anonym33.symbolKind --- ---The symbol kind values the client supports. When this ---property exists the client also guarantees that it will @@ -5082,12 +5107,12 @@ error('Cannot require a meta file') ---the initial version of the protocol. ---@field valueSet? lsp.SymbolKind[] ----@class lsp._anonym32.tagSupport +---@class lsp._anonym34.tagSupport --- ---The tags supported by the client. ---@field valueSet lsp.SymbolTag[] ----@class lsp._anonym34.codeActionLiteralSupport.codeActionKind +---@class lsp._anonym36.codeActionLiteralSupport.codeActionKind --- ---The code action kind values the client supports. When this ---property exists the client also guarantees that it will @@ -5095,18 +5120,18 @@ error('Cannot require a meta file') ---to a default value when unknown. ---@field valueSet lsp.CodeActionKind[] ----@class lsp._anonym33.codeActionLiteralSupport +---@class lsp._anonym35.codeActionLiteralSupport --- ---The code action kind is support with the following value ---set. ----@field codeActionKind lsp._anonym34.codeActionLiteralSupport.codeActionKind +---@field codeActionKind lsp._anonym36.codeActionLiteralSupport.codeActionKind ----@class lsp._anonym35.resolveSupport +---@class lsp._anonym37.resolveSupport --- ---The properties that a client can resolve lazily. ---@field properties string[] ----@class lsp._anonym36.foldingRangeKind +---@class lsp._anonym38.foldingRangeKind --- ---The folding range kind values the client supports. When this ---property exists the client also guarantees that it will @@ -5114,7 +5139,7 @@ error('Cannot require a meta file') ---to a default value when unknown. ---@field valueSet? lsp.FoldingRangeKind[] ----@class lsp._anonym37.foldingRange +---@class lsp._anonym39.foldingRange --- ---If set, the client signals that it supports setting collapsedText on ---folding ranges to display custom labels instead of the default text. @@ -5122,52 +5147,52 @@ error('Cannot require a meta file') ---@since 3.17.0 ---@field collapsedText? boolean ----@class lsp._anonym38.tagSupport +---@class lsp._anonym40.tagSupport --- ---The tags supported by the client. ---@field valueSet lsp.DiagnosticTag[] ----@class lsp._anonym40.requests.range +---@class lsp._anonym42.requests.range ----@class lsp._anonym41.requests.full +---@class lsp._anonym43.requests.full --- ---The client will send the `textDocument/semanticTokens/full/delta` request if ---the server provides a corresponding handler. ---@field delta? boolean ----@class lsp._anonym39.requests +---@class lsp._anonym41.requests --- ---The client will send the `textDocument/semanticTokens/range` request if ---the server provides a corresponding handler. ----@field range? boolean|lsp._anonym40.requests.range +---@field range? boolean|lsp._anonym42.requests.range --- ---The client will send the `textDocument/semanticTokens/full` request if ---the server provides a corresponding handler. ----@field full? boolean|lsp._anonym41.requests.full +---@field full? boolean|lsp._anonym43.requests.full ----@class lsp._anonym42.resolveSupport +---@class lsp._anonym44.resolveSupport --- ---The properties that a client can resolve lazily. ---@field properties string[] ----@class lsp._anonym43.messageActionItem +---@class lsp._anonym45.messageActionItem --- ---Whether the client supports additional attributes which ---are preserved and send back to the server in the ---request's response. ---@field additionalPropertiesSupport? boolean ----@class lsp._anonym44.PrepareRenameResult +---@class lsp._anonym46.PrepareRenameResult --- ---@field range lsp.Range --- ---@field placeholder string ----@class lsp._anonym45.PrepareRenameResult +---@class lsp._anonym47.PrepareRenameResult --- ---@field defaultBehavior boolean ----@class lsp._anonym46.TextDocumentContentChangeEvent +---@class lsp._anonym48.TextDocumentContentChangeEvent --- ---The range of the document that changed. ---@field range lsp.Range @@ -5180,18 +5205,18 @@ error('Cannot require a meta file') ---The new text for the provided range. ---@field text string ----@class lsp._anonym47.TextDocumentContentChangeEvent +---@class lsp._anonym49.TextDocumentContentChangeEvent --- ---The new text of the whole document. ---@field text string ----@class lsp._anonym48.MarkedString +---@class lsp._anonym50.MarkedString --- ---@field language string --- ---@field value string ----@class lsp._anonym49.TextDocumentFilter +---@class lsp._anonym51.TextDocumentFilter --- ---A language id, like `typescript`. ---@field language string @@ -5202,7 +5227,7 @@ error('Cannot require a meta file') ---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. ---@field pattern? string ----@class lsp._anonym50.TextDocumentFilter +---@class lsp._anonym52.TextDocumentFilter --- ---A language id, like `typescript`. ---@field language? string @@ -5213,7 +5238,7 @@ error('Cannot require a meta file') ---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. ---@field pattern? string ----@class lsp._anonym51.TextDocumentFilter +---@class lsp._anonym53.TextDocumentFilter --- ---A language id, like `typescript`. ---@field language? string @@ -5224,7 +5249,7 @@ error('Cannot require a meta file') ---A glob pattern, like **/*.{ts,js}. See TextDocumentFilter for examples. ---@field pattern string ----@class lsp._anonym52.NotebookDocumentFilter +---@class lsp._anonym54.NotebookDocumentFilter --- ---The type of the enclosing notebook. ---@field notebookType string @@ -5235,7 +5260,7 @@ error('Cannot require a meta file') ---A glob pattern. ---@field pattern? string ----@class lsp._anonym53.NotebookDocumentFilter +---@class lsp._anonym55.NotebookDocumentFilter --- ---The type of the enclosing notebook. ---@field notebookType? string @@ -5246,7 +5271,7 @@ error('Cannot require a meta file') ---A glob pattern. ---@field pattern? string ----@class lsp._anonym54.NotebookDocumentFilter +---@class lsp._anonym56.NotebookDocumentFilter --- ---The type of the enclosing notebook. ---@field notebookType? string diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 50121f30b2..49833eaeec 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -35,13 +35,13 @@ function M.hover() request(ms.textDocument_hover, params) end -local function request_with_options(name, params, options) +local function request_with_opts(name, params, opts) local req_handler --- @type function? - if options then + if opts then req_handler = function(err, result, ctx, config) local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) local handler = client.handlers[name] or vim.lsp.handlers[name] - handler(err, result, ctx, vim.tbl_extend('force', config or {}, options)) + handler(err, result, ctx, vim.tbl_extend('force', config or {}, opts)) end end request(name, params, req_handler) @@ -62,14 +62,13 @@ end --- vim.lsp.buf.references(nil, { on_list = on_list }) --- ``` --- ---- If you prefer loclist do something like this: +--- If you prefer loclist instead of qflist: --- ```lua ---- local function on_list(options) ---- vim.fn.setloclist(0, {}, ' ', options) ---- vim.cmd.lopen() ---- end +--- vim.lsp.buf.definition({ loclist = true }) +--- vim.lsp.buf.references(nil, { loclist = true }) --- ``` --- @field on_list? fun(t: vim.lsp.LocationOpts.OnList) +--- @field loclist? boolean --- @class vim.lsp.LocationOpts.OnList --- @field items table[] Structured like |setqflist-what| @@ -83,32 +82,32 @@ end --- Jumps to the declaration of the symbol under the cursor. --- @note Many servers do not implement this method. Generally, see |vim.lsp.buf.definition()| instead. ---- @param options? vim.lsp.LocationOpts -function M.declaration(options) +--- @param opts? vim.lsp.LocationOpts +function M.declaration(opts) local params = util.make_position_params() - request_with_options(ms.textDocument_declaration, params, options) + request_with_opts(ms.textDocument_declaration, params, opts) end --- Jumps to the definition of the symbol under the cursor. ---- @param options? vim.lsp.LocationOpts -function M.definition(options) +--- @param opts? vim.lsp.LocationOpts +function M.definition(opts) local params = util.make_position_params() - request_with_options(ms.textDocument_definition, params, options) + request_with_opts(ms.textDocument_definition, params, opts) end --- Jumps to the definition of the type of the symbol under the cursor. ---- @param options? vim.lsp.LocationOpts -function M.type_definition(options) +--- @param opts? vim.lsp.LocationOpts +function M.type_definition(opts) local params = util.make_position_params() - request_with_options(ms.textDocument_typeDefinition, params, options) + request_with_opts(ms.textDocument_typeDefinition, params, opts) end --- Lists all the implementations for the symbol under the cursor in the --- quickfix window. ---- @param options? vim.lsp.LocationOpts -function M.implementation(options) +--- @param opts? vim.lsp.LocationOpts +function M.implementation(opts) local params = util.make_position_params() - request_with_options(ms.textDocument_implementation, params, options) + request_with_opts(ms.textDocument_implementation, params, opts) end --- Displays signature information about the symbol under the cursor in a @@ -213,25 +212,25 @@ end --- Formats a buffer using the attached (and optionally filtered) language --- server clients. --- ---- @param options? vim.lsp.buf.format.Opts -function M.format(options) - options = options or {} - local bufnr = options.bufnr or api.nvim_get_current_buf() +--- @param opts? vim.lsp.buf.format.Opts +function M.format(opts) + opts = opts or {} + local bufnr = opts.bufnr or api.nvim_get_current_buf() local mode = api.nvim_get_mode().mode - local range = options.range + local range = opts.range if not range and mode == 'v' or mode == 'V' then range = range_from_selection(bufnr, mode) end local method = range and ms.textDocument_rangeFormatting or ms.textDocument_formatting local clients = vim.lsp.get_clients({ - id = options.id, + id = opts.id, bufnr = bufnr, - name = options.name, + name = opts.name, method = method, }) - if options.filter then - clients = vim.tbl_filter(options.filter, clients) + if opts.filter then + clients = vim.tbl_filter(opts.filter, clients) end if #clients == 0 then @@ -250,12 +249,12 @@ function M.format(options) return params end - if options.async then + if opts.async then local function do_format(idx, client) if not client then return end - local params = set_range(client, util.make_formatting_params(options.formatting_options)) + local params = set_range(client, util.make_formatting_params(opts.formatting_options)) client.request(method, params, function(...) local handler = client.handlers[method] or vim.lsp.handlers[method] handler(...) @@ -264,9 +263,9 @@ function M.format(options) end do_format(next(clients)) else - local timeout_ms = options.timeout_ms or 1000 + local timeout_ms = opts.timeout_ms or 1000 for _, client in pairs(clients) do - local params = set_range(client, util.make_formatting_params(options.formatting_options)) + local params = set_range(client, util.make_formatting_params(opts.formatting_options)) local result, err = client.request_sync(method, params, timeout_ms, bufnr) if result and result.result then util.apply_text_edits(result.result, bufnr, client.offset_encoding) @@ -295,18 +294,18 @@ end --- ---@param new_name string|nil If not provided, the user will be prompted for a new --- name using |vim.ui.input()|. ----@param options? vim.lsp.buf.rename.Opts Additional options: -function M.rename(new_name, options) - options = options or {} - local bufnr = options.bufnr or api.nvim_get_current_buf() +---@param opts? vim.lsp.buf.rename.Opts Additional options: +function M.rename(new_name, opts) + opts = opts or {} + local bufnr = opts.bufnr or api.nvim_get_current_buf() local clients = vim.lsp.get_clients({ bufnr = bufnr, - name = options.name, + name = opts.name, -- Clients must at least support rename, prepareRename is optional method = ms.textDocument_rename, }) - if options.filter then - clients = vim.tbl_filter(options.filter, clients) + if opts.filter then + clients = vim.tbl_filter(opts.filter, clients) end if #clients == 0 then @@ -415,21 +414,21 @@ end --- ---@param context (table|nil) Context for the request ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references ----@param options? vim.lsp.ListOpts -function M.references(context, options) +---@param opts? vim.lsp.ListOpts +function M.references(context, opts) validate({ context = { context, 't', true } }) local params = util.make_position_params() params.context = context or { includeDeclaration = true, } - request_with_options(ms.textDocument_references, params, options) + request_with_opts(ms.textDocument_references, params, opts) end --- Lists all symbols in the current buffer in the quickfix window. ---- @param options? vim.lsp.ListOpts -function M.document_symbol(options) +--- @param opts? vim.lsp.ListOpts +function M.document_symbol(opts) local params = { textDocument = util.make_text_document_params() } - request_with_options(ms.textDocument_documentSymbol, params, options) + request_with_opts(ms.textDocument_documentSymbol, params, opts) end --- @param call_hierarchy_items lsp.CallHierarchyItem[]? @@ -461,7 +460,14 @@ local function call_hierarchy(method) vim.notify(err.message, vim.log.levels.WARN) return end + if not result then + vim.notify('No item resolved', vim.log.levels.WARN) + return + end local call_hierarchy_item = pick_call_hierarchy_item(result) + if not call_hierarchy_item then + return + end local client = vim.lsp.get_client_by_id(ctx.client_id) if client then client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr) @@ -488,6 +494,80 @@ function M.outgoing_calls() call_hierarchy(ms.callHierarchy_outgoingCalls) end +--- Lists all the subtypes or supertypes of the symbol under the +--- cursor in the |quickfix| window. If the symbol can resolve to +--- multiple items, the user can pick one using |vim.ui.select()|. +---@param kind "subtypes"|"supertypes" +function M.typehierarchy(kind) + local method = kind == 'subtypes' and ms.typeHierarchy_subtypes or ms.typeHierarchy_supertypes + + --- Merge results from multiple clients into a single table. Client-ID is preserved. + --- + --- @param results table<integer, {error: lsp.ResponseError?, result: lsp.TypeHierarchyItem[]?}> + --- @return [integer, lsp.TypeHierarchyItem][] + local function merge_results(results) + local merged_results = {} + for client_id, client_result in pairs(results) do + if client_result.error then + vim.notify(client_result.error.message, vim.log.levels.WARN) + elseif client_result.result then + for _, item in pairs(client_result.result) do + table.insert(merged_results, { client_id, item }) + end + end + end + return merged_results + end + + local bufnr = api.nvim_get_current_buf() + local params = util.make_position_params() + --- @param results table<integer, {error: lsp.ResponseError?, result: lsp.TypeHierarchyItem[]?}> + vim.lsp.buf_request_all(bufnr, ms.textDocument_prepareTypeHierarchy, params, function(results) + local merged_results = merge_results(results) + if #merged_results == 0 then + vim.notify('No items resolved', vim.log.levels.INFO) + return + end + + if #merged_results == 1 then + local item = merged_results[1] + local client = vim.lsp.get_client_by_id(item[1]) + if client then + client.request(method, { item = item[2] }, nil, bufnr) + else + vim.notify( + string.format('Client with id=%d disappeared during call hierarchy request', item[1]), + vim.log.levels.WARN + ) + end + else + local select_opts = { + prompt = 'Select a type hierarchy item:', + kind = 'typehierarchy', + format_item = function(item) + if not item[2].detail or #item[2].detail == 0 then + return item[2].name + end + return string.format('%s %s', item[2].name, item[2].detail) + end, + } + + vim.ui.select(merged_results, select_opts, function(item) + local client = vim.lsp.get_client_by_id(item[1]) + if client then + --- @type lsp.TypeHierarchyItem + client.request(method, { item = item[2] }, nil, bufnr) + else + vim.notify( + string.format('Client with id=%d disappeared during call hierarchy request', item[1]), + vim.log.levels.WARN + ) + end + end) + end + end) +end + --- List workspace folders. --- function M.list_workspace_folders() @@ -514,28 +594,9 @@ function M.add_workspace_folder(workspace_folder) print(workspace_folder, ' is not a valid directory') return end - local new_workspace = { - uri = vim.uri_from_fname(workspace_folder), - name = workspace_folder, - } - local params = { event = { added = { new_workspace }, removed = {} } } - local bufnr = vim.api.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() for _, client in pairs(vim.lsp.get_clients({ bufnr = bufnr })) do - local found = false - for _, folder in pairs(client.workspace_folders or {}) do - if folder.name == workspace_folder then - found = true - print(workspace_folder, 'is already part of this workspace') - break - end - end - if not found then - client.notify(ms.workspace_didChangeWorkspaceFolders, params) - if not client.workspace_folders then - client.workspace_folders = {} - end - table.insert(client.workspace_folders, new_workspace) - end + client:_add_workspace_folder(workspace_folder) end end @@ -547,23 +608,12 @@ function M.remove_workspace_folder(workspace_folder) workspace_folder = workspace_folder or npcall(vim.fn.input, 'Workspace Folder: ', vim.fn.expand('%:p:h')) api.nvim_command('redraw') - if not (workspace_folder and #workspace_folder > 0) then + if not workspace_folder or #workspace_folder == 0 then return end - local workspace = { - uri = vim.uri_from_fname(workspace_folder), - name = workspace_folder, - } - local params = { event = { added = {}, removed = { workspace } } } - local bufnr = vim.api.nvim_get_current_buf() + local bufnr = api.nvim_get_current_buf() for _, client in pairs(vim.lsp.get_clients({ bufnr = bufnr })) do - for idx, folder in pairs(client.workspace_folders) do - if folder.name == workspace_folder then - client.notify(ms.workspace_didChangeWorkspaceFolders, params) - client.workspace_folders[idx] = nil - return - end - end + client:_remove_workspace_folder(workspace_folder) end print(workspace_folder, 'is not currently part of the workspace') end @@ -575,14 +625,14 @@ end --- string means no filtering is done. --- --- @param query string? optional ---- @param options? vim.lsp.ListOpts -function M.workspace_symbol(query, options) +--- @param opts? vim.lsp.ListOpts +function M.workspace_symbol(query, opts) query = query or npcall(vim.fn.input, 'Query: ') if query == nil then return end local params = { query = query } - request_with_options(ms.workspace_symbol, params, options) + request_with_opts(ms.workspace_symbol, params, opts) end --- Send request to the server to resolve document highlights for the current @@ -774,19 +824,19 @@ end --- Selects a code action available at the current --- cursor position. --- ----@param options? vim.lsp.buf.code_action.Opts +---@param opts? vim.lsp.buf.code_action.Opts ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction ---@see vim.lsp.protocol.CodeActionTriggerKind -function M.code_action(options) - validate({ options = { options, 't', true } }) - options = options or {} +function M.code_action(opts) + validate({ options = { opts, 't', true } }) + opts = opts or {} -- Detect old API call code_action(context) which should now be -- code_action({ context = context} ) --- @diagnostic disable-next-line:undefined-field - if options.diagnostics or options.only then - options = { options = options } + if opts.diagnostics or opts.only then + opts = { options = opts } end - local context = options.context or {} + local context = opts.context or {} if not context.triggerKind then context.triggerKind = vim.lsp.protocol.CodeActionTriggerKind.Invoked end @@ -816,17 +866,17 @@ function M.code_action(options) results[ctx.client_id] = { error = err, result = result, ctx = ctx } remaining = remaining - 1 if remaining == 0 then - on_code_action_results(results, options) + on_code_action_results(results, opts) end end for _, client in ipairs(clients) do ---@type lsp.CodeActionParams local params - if options.range then - assert(type(options.range) == 'table', 'code_action range must be a table') - local start = assert(options.range.start, 'range must have a `start` property') - local end_ = assert(options.range['end'], 'range must have a `end` property') + if opts.range then + assert(type(opts.range) == 'table', 'code_action range must be a table') + local start = assert(opts.range.start, 'range must have a `start` property') + local end_ = assert(opts.range['end'], 'range must have a `end` property') params = util.make_given_range_params(start, end_, bufnr, client.offset_encoding) elseif mode == 'v' or mode == 'V' then local range = range_from_selection(bufnr, mode) diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index ff0db166d5..4beb7fefda 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -15,7 +15,7 @@ local validate = vim.validate --- @inlinedoc --- --- Allow using incremental sync for buffer edits ---- (defailt: `true`) +--- (default: `true`) --- @field allow_incremental_sync? boolean --- --- Debounce `didChange` notifications to the server by the given number in milliseconds. @@ -37,7 +37,7 @@ local validate = vim.validate --- `is_closing` and `terminate`. --- See |vim.lsp.rpc.request()|, |vim.lsp.rpc.notify()|. --- For TCP there is a builtin RPC client factory: |vim.lsp.rpc.connect()| ---- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient? +--- @field cmd string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient --- --- Directory to launch the `cmd` process. Not related to `root_dir`. --- (default: cwd) @@ -185,6 +185,10 @@ local validate = vim.validate --- @field root_dir string --- --- @field attached_buffers table<integer,true> +--- +--- Buffers that should be attached to upon initialize() +--- @field package _buffers_to_attach table<integer,true> +--- --- @field private _log_prefix string --- --- Track this so that we can escalate automatically if we've already tried a @@ -225,7 +229,7 @@ local validate = vim.validate --- If {status} is `true`, the function returns {request_id} as the second --- result. You can use this with `client.cancel_request(request_id)` to cancel --- the request. ---- @field request fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer): boolean, integer? +--- @field request fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer? --- --- Sends a request to the server and synchronously waits for the response. --- This is a wrapper around {client.request} @@ -416,7 +420,7 @@ local function get_workspace_folders(workspace_folders, root_dir) return { { uri = vim.uri_from_fname(root_dir), - name = string.format('%s', root_dir), + name = root_dir, }, } end @@ -502,25 +506,17 @@ function Client.create(config) } -- Start the RPC client. - local rpc --- @type vim.lsp.rpc.PublicClient? local config_cmd = config.cmd if type(config_cmd) == 'function' then - rpc = config_cmd(dispatchers) + self.rpc = config_cmd(dispatchers) else - rpc = lsp.rpc.start(config_cmd, dispatchers, { + self.rpc = lsp.rpc.start(config_cmd, dispatchers, { cwd = config.cmd_cwd, env = config.cmd_env, detached = config.detached, }) end - -- Return nil if the rpc client fails to start - if not rpc then - return - end - - self.rpc = rpc - setmetatable(self, Client) return self @@ -575,6 +571,7 @@ function Client:initialize() initializationOptions = config.init_options, capabilities = self.capabilities, trace = self._trace, + workDoneToken = '1', } self:_run_callbacks( @@ -608,8 +605,19 @@ function Client:initialize() self:_notify(ms.workspace_didChangeConfiguration, { settings = self.settings }) end + -- If server is being restarted, make sure to re-attach to any previously attached buffers. + -- Save which buffers before on_init in case new buffers are attached. + local reattach_bufs = vim.deepcopy(self.attached_buffers) + self:_run_callbacks(self._on_init_cbs, lsp.client_errors.ON_INIT_CALLBACK_ERROR, self, result) + for buf in pairs(reattach_bufs) do + -- The buffer may have been detached in the on_init callback. + if self.attached_buffers[buf] then + self:_on_attach(buf) + end + end + log.info( self._log_prefix, 'server_capabilities', @@ -647,10 +655,10 @@ end --- checks for capabilities and handler availability. --- --- @param method string LSP method name. ---- @param params table|nil LSP request params. ---- @param handler lsp.Handler|nil Response |lsp-handler| for this method. ---- @param bufnr integer Buffer handle (0 for current). ---- @return boolean status, integer|nil request_id {status} is a bool indicating +--- @param params? table LSP request params. +--- @param handler? lsp.Handler Response |lsp-handler| for this method. +--- @param bufnr? integer Buffer handle (0 for current). +--- @return boolean status, integer? request_id {status} is a bool indicating --- whether the request was successful. If it is `false`, then it will --- always be `false` (the client has shutdown). If it was --- successful, then it will return {request_id} as the @@ -693,7 +701,7 @@ function Client:_request(method, params, handler, bufnr) local request = { type = 'pending', bufnr = bufnr, method = method } self.requests[request_id] = request api.nvim_exec_autocmds('LspRequest', { - buffer = bufnr, + buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil, modeline = false, data = { client_id = self.id, request_id = request_id, request = request }, }) @@ -709,7 +717,7 @@ local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'err --- --- @param ... string List to write to the buffer local function err_message(...) - local message = table.concat(vim.tbl_flatten({ ... })) + local message = table.concat(vim.iter({ ... }):flatten():totable()) if vim.in_fast_event() then vim.schedule(function() api.nvim_err_writeln(message) @@ -761,7 +769,7 @@ function Client:_request_sync(method, params, timeout_ms, bufnr) return request_result end ---- @private +--- @package --- Sends a notification to an LSP server. --- --- @param method string LSP method name. @@ -804,7 +812,7 @@ function Client:_cancel_request(id) if request and request.type == 'pending' then request.type = 'cancel' api.nvim_exec_autocmds('LspRequest', { - buffer = request.bufnr, + buffer = api.nvim_buf_is_valid(request.bufnr) and request.bufnr or nil, modeline = false, data = { client_id = self.id, request_id = id, request = request }, }) @@ -900,7 +908,7 @@ end --- @param bufnr integer Number of the buffer, or 0 for current function Client:_text_document_did_open_handler(bufnr) changetracking.init(self, bufnr) - if not vim.tbl_get(self.server_capabilities, 'textDocumentSync', 'openClose') then + if not self.supports_method(ms.textDocument_didOpen) then return end if not api.nvim_buf_is_loaded(bufnr) then @@ -1053,4 +1061,45 @@ function Client:_on_exit(code, signal) ) end +--- @package +--- Add a directory to the workspace folders. +--- @param dir string? +function Client:_add_workspace_folder(dir) + for _, folder in pairs(self.workspace_folders or {}) do + if folder.name == dir then + print(dir, 'is already part of this workspace') + return + end + end + + local wf = assert(get_workspace_folders(nil, dir)) + + self:_notify(ms.workspace_didChangeWorkspaceFolders, { + event = { added = wf, removed = {} }, + }) + + if not self.workspace_folders then + self.workspace_folders = {} + end + vim.list_extend(self.workspace_folders, wf) +end + +--- @package +--- Remove a directory to the workspace folders. +--- @param dir string? +function Client:_remove_workspace_folder(dir) + local wf = assert(get_workspace_folders(nil, dir)) + + self:_notify(ms.workspace_didChangeWorkspaceFolders, { + event = { added = {}, removed = wf }, + }) + + for idx, folder in pairs(self.workspace_folders) do + if folder.name == dir then + table.remove(self.workspace_folders, idx) + break + end + end +end + return Client diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 48c096c0c1..c85bb6aa32 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -79,7 +79,7 @@ function M.run() local lenses_by_client = lens_cache_by_buf[bufnr] or {} for client, lenses in pairs(lenses_by_client) do for _, lens in pairs(lenses) do - if lens.range.start.line == (line - 1) then + if lens.range.start.line == (line - 1) and lens.command and lens.command.command ~= '' then table.insert(options, { client = client, lens = lens }) end end @@ -164,7 +164,7 @@ function M.display(lenses, bufnr, client_id) return a.range.start.character < b.range.start.character end) for j, lens in ipairs(line_lenses) do - local text = lens.command and lens.command.title or 'Unresolved lens ...' + local text = (lens.command and lens.command.title or 'Unresolved lens ...'):gsub('%s+', ' ') table.insert(chunks, { text, 'LspCodeLens' }) if j < num_line_lenses then table.insert(chunks, { ' | ', 'LspCodeLensSeparator' }) @@ -231,7 +231,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) countdown() else assert(client) - client.request('codeLens/resolve', lens, function(_, result) + client.request(ms.codeLens_resolve, lens, function(_, result) if api.nvim_buf_is_loaded(bufnr) and result and result.command then lens.command = result.command -- Eager display to have some sort of incremental feedback @@ -299,14 +299,18 @@ function M.refresh(opts) local bufnr = opts.bufnr and resolve_bufnr(opts.bufnr) local buffers = bufnr and { bufnr } or vim.tbl_filter(api.nvim_buf_is_loaded, api.nvim_list_bufs()) - local params = { - textDocument = util.make_text_document_params(), - } for _, buf in ipairs(buffers) do if not active_refreshes[buf] then + local params = { + textDocument = util.make_text_document_params(buf), + } active_refreshes[buf] = true - vim.lsp.buf_request(buf, ms.textDocument_codeLens, params, M.on_codelens) + + local request_ids = vim.lsp.buf_request(buf, ms.textDocument_codeLens, params, M.on_codelens) + if vim.tbl_isempty(request_ids) then + active_refreshes[buf] = nil + end end end end diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index daf4fec8d2..f9d394642c 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -3,6 +3,7 @@ local protocol = require('vim.lsp.protocol') local ms = protocol.Methods local util = require('vim.lsp.util') local api = vim.api +local completion = require('vim.lsp._completion') --- @type table<string,lsp.Handler> local M = {} @@ -12,7 +13,7 @@ local M = {} --- Writes to error buffer. ---@param ... string Will be concatenated before being written local function err_message(...) - vim.notify(table.concat(vim.tbl_flatten({ ... })), vim.log.levels.ERROR) + vim.notify(table.concat(vim.iter({ ... }):flatten():totable()), vim.log.levels.ERROR) api.nvim_command('redraw') end @@ -22,16 +23,16 @@ M[ms.workspace_executeCommand] = function(_, _, _, _) end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress ----@param result lsp.ProgressParams +---@param params lsp.ProgressParams ---@param ctx lsp.HandlerContext -M[ms.dollar_progress] = function(_, result, ctx) +M[ms.dollar_progress] = function(_, params, ctx) local client = vim.lsp.get_client_by_id(ctx.client_id) if not client then err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update') return vim.NIL end local kind = nil - local value = result.value + local value = params.value if type(value) == 'table' then kind = value.kind @@ -39,21 +40,21 @@ M[ms.dollar_progress] = function(_, result, ctx) -- So that consumers always have it available, even if they consume a -- subset of the full sequence if kind == 'begin' then - client.progress.pending[result.token] = value.title + client.progress.pending[params.token] = value.title else - value.title = client.progress.pending[result.token] + value.title = client.progress.pending[params.token] if kind == 'end' then - client.progress.pending[result.token] = nil + client.progress.pending[params.token] = nil end end end - client.progress:push(result) + client.progress:push(params) api.nvim_exec_autocmds('LspProgress', { pattern = kind, modeline = false, - data = { client_id = ctx.client_id, result = result }, + data = { client_id = ctx.client_id, params = params }, }) end @@ -253,26 +254,24 @@ M[ms.textDocument_references] = function(_, result, ctx, config) local title = 'References' local items = util.locations_to_items(result, client.offset_encoding) + local list = { title = title, items = items, context = ctx } if config.loclist then - vim.fn.setloclist(0, {}, ' ', { title = title, items = items, context = ctx }) - api.nvim_command('lopen') + vim.fn.setloclist(0, {}, ' ', list) + vim.cmd.lopen() elseif config.on_list then - assert(type(config.on_list) == 'function', 'on_list is not a function') - config.on_list({ title = title, items = items, context = ctx }) + assert(vim.is_callable(config.on_list), 'on_list is not a function') + config.on_list(list) else - vim.fn.setqflist({}, ' ', { title = title, items = items, context = ctx }) - api.nvim_command('botright copen') + vim.fn.setqflist({}, ' ', list) + vim.cmd('botright copen') end end --- Return a function that converts LSP responses to list items and opens the list --- ---- The returned function has an optional {config} parameter that accepts a table ---- with the following keys: ---- ---- loclist: (boolean) use the location list (default is to use the quickfix list) +--- The returned function has an optional {config} parameter that accepts |vim.lsp.ListOpts| --- ----@param map_result function `((resp, bufnr) -> list)` to convert the response +---@param map_result fun(resp, bufnr: integer): table to convert the response ---@param entity string name of the resource used in a `not found` error message ---@param title_fn fun(ctx: lsp.HandlerContext): string Function to call to generate list title ---@return lsp.Handler @@ -286,15 +285,16 @@ local function response_to_list(map_result, entity, title_fn) local title = title_fn(ctx) local items = map_result(result, ctx.bufnr) + local list = { title = title, items = items, context = ctx } if config.loclist then - vim.fn.setloclist(0, {}, ' ', { title = title, items = items, context = ctx }) - api.nvim_command('lopen') + vim.fn.setloclist(0, {}, ' ', list) + vim.cmd.lopen() elseif config.on_list then - assert(type(config.on_list) == 'function', 'on_list is not a function') - config.on_list({ title = title, items = items, context = ctx }) + assert(vim.is_callable(config.on_list), 'on_list is not a function') + config.on_list(list) else - vim.fn.setqflist({}, ' ', { title = title, items = items, context = ctx }) - api.nvim_command('botright copen') + vim.fn.setqflist({}, ' ', list) + vim.cmd('botright copen') end end end @@ -354,7 +354,7 @@ M[ms.textDocument_completion] = function(_, result, _, _) local textMatch = vim.fn.match(line_to_cursor, '\\k*$') local prefix = line_to_cursor:sub(textMatch + 1) - local matches = util.text_document_completion_list_to_complete_items(result, prefix) + local matches = completion._lsp_to_complete_items(result, prefix) vim.fn.complete(textMatch + 1, matches) end @@ -428,7 +428,7 @@ local function location_handler(_, result, ctx, config) -- textDocument/definition can return Location or Location[] -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition - if not vim.tbl_islist(result) then + if not vim.islist(result) then result = { result } end @@ -436,7 +436,7 @@ local function location_handler(_, result, ctx, config) local items = util.locations_to_items(result, client.offset_encoding) if config.on_list then - assert(type(config.on_list) == 'function', 'on_list is not a function') + assert(vim.is_callable(config.on_list), 'on_list is not a function') config.on_list({ title = title, items = items }) return end @@ -444,8 +444,13 @@ local function location_handler(_, result, ctx, config) util.jump_to_location(result[1], client.offset_encoding, config.reuse_win) return end - vim.fn.setqflist({}, ' ', { title = title, items = items }) - api.nvim_command('botright copen') + if config.loclist then + vim.fn.setloclist(0, {}, ' ', { title = title, items = items }) + vim.cmd.lopen() + else + vim.fn.setqflist({}, ' ', { title = title, items = items }) + vim.cmd('botright copen') + end end --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration @@ -555,7 +560,7 @@ local function make_call_hierarchy_handler(direction) end end vim.fn.setqflist({}, ' ', { title = 'LSP call hierarchy', items = items }) - api.nvim_command('botright copen') + vim.cmd('botright copen') end end @@ -565,6 +570,45 @@ M[ms.callHierarchy_incomingCalls] = make_call_hierarchy_handler('from') --- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_outgoingCalls M[ms.callHierarchy_outgoingCalls] = make_call_hierarchy_handler('to') +--- Displays type hierarchy in the quickfix window. +local function make_type_hierarchy_handler() + --- @param result lsp.TypeHierarchyItem[] + return function(_, result, ctx, _) + if not result then + return + end + local function format_item(item) + if not item.detail or #item.detail == 0 then + return item.name + end + return string.format('%s %s', item.name, item.detail) + end + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + local items = {} + for _, type_hierarchy_item in pairs(result) do + local col = util._get_line_byte_from_position( + ctx.bufnr, + type_hierarchy_item.range.start, + client.offset_encoding + ) + table.insert(items, { + filename = assert(vim.uri_to_fname(type_hierarchy_item.uri)), + text = format_item(type_hierarchy_item), + lnum = type_hierarchy_item.range.start.line + 1, + col = col + 1, + }) + end + vim.fn.setqflist({}, ' ', { title = 'LSP type hierarchy', items = items }) + vim.cmd('botright copen') + end +end + +--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_incomingCalls +M[ms.typeHierarchy_subtypes] = make_type_hierarchy_handler() + +--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_outgoingCalls +M[ms.typeHierarchy_supertypes] = make_type_hierarchy_handler() + --- @see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_logMessage --- @param result lsp.LogMessageParams M[ms.window_logMessage] = function(_, result, ctx, _) @@ -615,7 +659,8 @@ M[ms.window_showDocument] = function(_, result, ctx, _) if result.external then -- TODO(lvimuser): ask the user for confirmation - local ret, err = vim.ui.open(uri) + local cmd, err = vim.ui.open(uri) + local ret = cmd and cmd:wait(2000) or nil if ret == nil or ret.code ~= 0 then return { diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index 797a1097f9..a79ae76eb9 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -51,6 +51,29 @@ end local function check_watcher() vim.health.start('vim.lsp: File watcher') + + -- Only run the check if file watching has been enabled by a client. + local clients = vim.lsp.get_clients() + if + --- @param client vim.lsp.Client + vim.iter(clients):all(function(client) + local has_capability = vim.tbl_get( + client.capabilities, + 'workspace', + 'didChangeWatchedFiles', + 'dynamicRegistration' + ) + local has_dynamic_capability = + client.dynamic_capabilities:get(vim.lsp.protocol.Methods.workspace_didChangeWatchedFiles) + return has_capability == nil + or has_dynamic_capability == nil + or client.workspace_folders == nil + end) + then + report_info('file watching "(workspace/didChangeWatchedFiles)" disabled on all clients') + return + end + local watchfunc = vim.lsp._watchfiles._watchfunc assert(watchfunc) local watchfunc_name --- @type string diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index ec676ea97f..f98496456b 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -4,13 +4,30 @@ local ms = require('vim.lsp.protocol').Methods local api = vim.api local M = {} ----@class (private) vim.lsp.inlay_hint.bufstate +---@class (private) vim.lsp.inlay_hint.globalstate Global state for inlay hints +---@field enabled boolean Whether inlay hints are enabled for this scope +---@type vim.lsp.inlay_hint.globalstate +local globalstate = { + enabled = false, +} + +---@class (private) vim.lsp.inlay_hint.bufstate: vim.lsp.inlay_hint.globalstate Buffer local state for inlay hints ---@field version? integer ---@field client_hints? table<integer, table<integer, lsp.InlayHint[]>> client_id -> (lnum -> hints) ---@field applied table<integer, integer> Last version of hints applied to this line ----@field enabled boolean Whether inlay hints are enabled for this buffer ---@type table<integer, vim.lsp.inlay_hint.bufstate> -local bufstates = {} +local bufstates = vim.defaulttable(function(_) + return setmetatable({ applied = {} }, { + __index = globalstate, + __newindex = function(state, key, value) + if globalstate[key] == value then + rawset(state, key, nil) + else + rawset(state, key, value) + end + end, + }) +end) local namespace = api.nvim_create_namespace('vim_lsp_inlayhint') local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {}) @@ -34,22 +51,22 @@ function M.on_inlayhint(err, result, ctx, _) return end local bufstate = bufstates[bufnr] - if not bufstate or not bufstate.enabled then + if not bufstate.enabled then return end if not (bufstate.client_hints and bufstate.version) then bufstate.client_hints = vim.defaulttable() bufstate.version = ctx.version end - local hints_by_client = bufstate.client_hints + local client_hints = bufstate.client_hints local client = assert(vim.lsp.get_client_by_id(client_id)) - local new_hints_by_lnum = vim.defaulttable() + local new_lnum_hints = vim.defaulttable() local num_unprocessed = #result if num_unprocessed == 0 then - hints_by_client[client_id] = {} + client_hints[client_id] = {} bufstate.version = ctx.version - api.nvim__buf_redraw_range(bufnr, 0, -1) + api.nvim__redraw({ buf = bufnr, valid = true }) return end @@ -73,15 +90,15 @@ function M.on_inlayhint(err, result, ctx, _) for _, hint in ipairs(result) do local lnum = hint.position.line hint.position.character = pos_to_byte(hint.position) - table.insert(new_hints_by_lnum[lnum], hint) + table.insert(new_lnum_hints[lnum], hint) end - hints_by_client[client_id] = new_hints_by_lnum + client_hints[client_id] = new_lnum_hints bufstate.version = ctx.version - api.nvim__buf_redraw_range(bufnr, 0, -1) + api.nvim__redraw({ buf = bufnr, valid = true }) end ---- |lsp-handler| for the method `textDocument/inlayHint/refresh` +--- |lsp-handler| for the method `workspace/inlayHint/refresh` ---@param ctx lsp.HandlerContext ---@private function M.on_refresh(err, _, ctx, _) @@ -91,11 +108,7 @@ function M.on_refresh(err, _, ctx, _) for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do for _, winid in ipairs(api.nvim_list_wins()) do if api.nvim_win_get_buf(winid) == bufnr then - local bufstate = bufstates[bufnr] - if bufstate then - util._refresh(ms.textDocument_inlayHint, { bufnr = bufnr }) - break - end + util._refresh(ms.textDocument_inlayHint, { bufnr = bufnr }) end end end @@ -123,7 +136,8 @@ end --- local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer --- --- local client = vim.lsp.get_client_by_id(hint.client_id) ---- resolved_hint = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0).result +--- local resp = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) +--- local resolved_hint = assert(resp and resp.result, resp.err) --- vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding) --- --- location = resolved_hint.label[1].location @@ -154,7 +168,7 @@ function M.get(filter) end local bufstate = bufstates[bufnr] - if not (bufstate and bufstate.client_hints) then + if not bufstate.client_hints then return {} end @@ -175,19 +189,19 @@ function M.get(filter) end --- @type vim.lsp.inlay_hint.get.ret[] - local hints = {} + local result = {} for _, client in pairs(clients) do - local hints_by_lnum = bufstate.client_hints[client.id] - if hints_by_lnum then + local lnum_hints = bufstate.client_hints[client.id] + if lnum_hints then for lnum = range.start.line, range['end'].line do - local line_hints = hints_by_lnum[lnum] or {} - for _, hint in pairs(line_hints) do + local hints = lnum_hints[lnum] or {} + for _, hint in pairs(hints) do local line, char = hint.position.line, hint.position.character if (line > range.start.line or char >= range.start.character) and (line < range['end'].line or char <= range['end'].character) then - table.insert(hints, { + table.insert(result, { bufnr = bufnr, client_id = client.id, inlay_hint = hint, @@ -197,18 +211,15 @@ function M.get(filter) end end end - return hints + return result end --- Clear inlay hints ---@param bufnr (integer) Buffer handle, or 0 for current local function clear(bufnr) - if bufnr == nil or bufnr == 0 then + if bufnr == 0 then bufnr = api.nvim_get_current_buf() end - if not bufstates[bufnr] then - return - end local bufstate = bufstates[bufnr] local client_lens = (bufstate or {}).client_hints or {} local client_ids = vim.tbl_keys(client_lens) --- @type integer[] @@ -218,19 +229,18 @@ local function clear(bufnr) end end api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) - api.nvim__buf_redraw_range(bufnr, 0, -1) + api.nvim__redraw({ buf = bufnr, valid = true }) end --- Disable inlay hints for a buffer ----@param bufnr (integer|nil) Buffer handle, or 0 or nil for current +---@param bufnr (integer) Buffer handle, or 0 for current local function _disable(bufnr) - if bufnr == nil or bufnr == 0 then + if bufnr == 0 then bufnr = api.nvim_get_current_buf() end clear(bufnr) - if bufstates[bufnr] then - bufstates[bufnr] = { enabled = false, applied = {} } - end + bufstates[bufnr] = nil + bufstates[bufnr].enabled = false end --- Refresh inlay hints, only if we have attached clients that support it @@ -244,30 +254,38 @@ local function _refresh(bufnr, opts) end --- Enable inlay hints for a buffer ----@param bufnr (integer|nil) Buffer handle, or 0 or nil for current +---@param bufnr (integer) Buffer handle, or 0 for current local function _enable(bufnr) - if bufnr == nil or bufnr == 0 then + if bufnr == 0 then bufnr = api.nvim_get_current_buf() end - local bufstate = bufstates[bufnr] - if not bufstate then - bufstates[bufnr] = { applied = {}, enabled = true } - api.nvim_create_autocmd('LspNotify', { - buffer = bufnr, - callback = function(opts) - if - opts.data.method ~= ms.textDocument_didChange - and opts.data.method ~= ms.textDocument_didOpen - then - return - end - if bufstates[bufnr] and bufstates[bufnr].enabled then - _refresh(bufnr, { client_id = opts.data.client_id }) - end - end, - group = augroup, - }) - _refresh(bufnr) + bufstates[bufnr] = nil + bufstates[bufnr].enabled = true + _refresh(bufnr) +end + +api.nvim_create_autocmd('LspNotify', { + callback = function(args) + ---@type integer + local bufnr = args.buf + + if + args.data.method ~= ms.textDocument_didChange + and args.data.method ~= ms.textDocument_didOpen + then + return + end + if bufstates[bufnr].enabled then + _refresh(bufnr, { client_id = args.data.client_id }) + end + end, + group = augroup, +}) +api.nvim_create_autocmd('LspAttach', { + callback = function(args) + ---@type integer + local bufnr = args.buf + api.nvim_buf_attach(bufnr, false, { on_reload = function(_, cb_bufnr) clear(cb_bufnr) @@ -278,32 +296,30 @@ local function _enable(bufnr) end, on_detach = function(_, cb_bufnr) _disable(cb_bufnr) + bufstates[cb_bufnr] = nil end, }) - api.nvim_create_autocmd('LspDetach', { - buffer = bufnr, - callback = function(args) - local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_inlayHint }) - - if - not vim.iter(clients):any(function(c) - return c.id ~= args.data.client_id - end) - then - _disable(bufnr) - end - end, - group = augroup, - }) - else - bufstate.enabled = true - _refresh(bufnr) - end -end + end, + group = augroup, +}) +api.nvim_create_autocmd('LspDetach', { + callback = function(args) + ---@type integer + local bufnr = args.buf + local clients = vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_inlayHint }) + if not vim.iter(clients):any(function(c) + return c.id ~= args.data.client_id + end) then + _disable(bufnr) + end + end, + group = augroup, +}) api.nvim_set_decoration_provider(namespace, { on_win = function(_, _, bufnr, topline, botline) - local bufstate = bufstates[bufnr] + ---@type vim.lsp.inlay_hint.bufstate + local bufstate = rawget(bufstates, bufnr) if not bufstate then return end @@ -311,14 +327,18 @@ api.nvim_set_decoration_provider(namespace, { if bufstate.version ~= util.buf_versions[bufnr] then return end - local hints_by_client = assert(bufstate.client_hints) + + if not bufstate.client_hints then + return + end + local client_hints = assert(bufstate.client_hints) for lnum = topline, botline do if bufstate.applied[lnum] ~= bufstate.version then api.nvim_buf_clear_namespace(bufnr, namespace, lnum, lnum + 1) - for _, hints_by_lnum in pairs(hints_by_client) do - local line_hints = hints_by_lnum[lnum] or {} - for _, hint in pairs(line_hints) do + for _, lnum_hints in pairs(client_hints) do + local hints = lnum_hints[lnum] or {} + for _, hint in pairs(hints) do local text = '' local label = hint.label if type(label) == 'string' then @@ -349,34 +369,65 @@ api.nvim_set_decoration_provider(namespace, { end, }) ---- @param bufnr (integer|nil) Buffer handle, or 0 or nil for current +--- Query whether inlay hint is enabled in the {filter}ed scope +--- @param filter vim.lsp.inlay_hint.enable.Filter --- @return boolean --- @since 12 -function M.is_enabled(bufnr) +function M.is_enabled(filter) + vim.validate({ filter = { filter, 'table', true } }) + filter = filter or {} + local bufnr = filter.bufnr + vim.validate({ bufnr = { bufnr, 'number', true } }) - if bufnr == nil or bufnr == 0 then + if bufnr == nil then + return globalstate.enabled + elseif bufnr == 0 then bufnr = api.nvim_get_current_buf() end - return bufstates[bufnr] and bufstates[bufnr].enabled or false + return bufstates[bufnr].enabled end ---- Enables or disables inlay hints for a buffer. +--- Optional filters |kwargs|, or `nil` for all. +--- @class vim.lsp.inlay_hint.enable.Filter +--- @inlinedoc +--- Buffer number, or 0 for current buffer, or nil for all. +--- @field bufnr integer? + +--- Enables or disables inlay hints for the {filter}ed scope. --- --- To "toggle", pass the inverse of `is_enabled()`: --- --- ```lua ---- vim.lsp.inlay_hint.enable(0, not vim.lsp.inlay_hint.is_enabled()) +--- vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled()) --- ``` --- ---- @param bufnr (integer|nil) Buffer handle, or 0 or nil for current --- @param enable (boolean|nil) true/nil to enable, false to disable +--- @param filter vim.lsp.inlay_hint.enable.Filter? --- @since 12 -function M.enable(bufnr, enable) - vim.validate({ enable = { enable, 'boolean', true }, bufnr = { bufnr, 'number', true } }) - if enable == false then - _disable(bufnr) +function M.enable(enable, filter) + vim.validate({ enable = { enable, 'boolean', true }, filter = { filter, 'table', true } }) + enable = enable == nil or enable + filter = filter or {} + + if filter.bufnr == nil then + globalstate.enabled = enable + for _, bufnr in ipairs(api.nvim_list_bufs()) do + if api.nvim_buf_is_loaded(bufnr) then + if enable == false then + _disable(bufnr) + else + _enable(bufnr) + end + else + bufstates[bufnr] = nil + end + end else - _enable(bufnr) + if enable == false then + _disable(filter.bufnr) + else + _enable(filter.bufnr) + end end end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 599f02425e..419c2ff644 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -10,6 +10,8 @@ local function get_value_set(tbl) return value_set end +local sysname = vim.uv.os_uname().sysname + -- Protocol for the Microsoft Language Server Protocol (mslsp) local protocol = {} @@ -835,7 +837,10 @@ function protocol.make_client_capabilities() refreshSupport = true, }, didChangeWatchedFiles = { - dynamicRegistration = true, + -- TODO(lewis6991): do not advertise didChangeWatchedFiles on Linux + -- or BSD since all the current backends are too limited. + -- Ref: #27807, #28058, #23291, #26520 + dynamicRegistration = sysname == 'Darwin' or sysname == 'Windows_NT', relativePatternSupport = true, }, inlayHint = { diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 984e4f040a..3c63a12da2 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -621,95 +621,67 @@ local function merge_dispatchers(dispatchers) return merged end ---- Create a LSP RPC client factory that connects via TCP to the given host and port. +--- Create a LSP RPC client factory that connects to either: +--- +--- - a named pipe (windows) +--- - a domain socket (unix) +--- - a host and port via TCP --- --- Return a function that can be passed to the `cmd` field for --- |vim.lsp.start_client()| or |vim.lsp.start()|. --- ----@param host string host to connect to ----@param port integer port to connect to +---@param host_or_path string host to connect to or path to a pipe/domain socket +---@param port integer? TCP port to connect to. If absent the first argument must be a pipe ---@return fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient -function M.connect(host, port) +function M.connect(host_or_path, port) return function(dispatchers) dispatchers = merge_dispatchers(dispatchers) - local tcp = assert(uv.new_tcp()) + local handle = ( + port == nil + and assert( + uv.new_pipe(false), + string.format('Pipe with name %s could not be opened.', host_or_path) + ) + or assert(uv.new_tcp(), 'Could not create new TCP socket') + ) local closing = false + -- Connect returns a PublicClient synchronously so the caller + -- can immediately send messages before the connection is established + -- -> Need to buffer them until that happens + local connected = false + -- size should be enough because the client can't really do anything until initialization is done + -- which required a response from the server - implying the connection got established + local msgbuf = vim.ringbuf(10) local transport = { write = function(msg) - tcp:write(msg) - end, - is_closing = function() - return closing - end, - terminate = function() - if not closing then - closing = true - tcp:shutdown() - tcp:close() - dispatchers.on_exit(0, 0) + if connected then + local _, err = handle:write(msg) + if err and not closing then + log.error('Error on handle:write: %q', err) + end + else + msgbuf:push(msg) end end, - } - local client = new_client(dispatchers, transport) - tcp:connect(host, port, function(err) - if err then - vim.schedule(function() - vim.notify( - string.format('Could not connect to %s:%s, reason: %s', host, port, vim.inspect(err)), - vim.log.levels.WARN - ) - end) - return - end - local handle_body = function(body) - client:handle_body(body) - end - tcp:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) - client:on_error(M.client_errors.READ_ERROR, read_err) - end)) - end) - - return public_client(client) - end -end - ---- Create a LSP RPC client factory that connects via named pipes (Windows) ---- or unix domain sockets (Unix) to the given pipe_path (file path on ---- Unix and name on Windows). ---- ---- Return a function that can be passed to the `cmd` field for ---- |vim.lsp.start_client()| or |vim.lsp.start()|. ---- ----@param pipe_path string file path of the domain socket (Unix) or name of the named pipe (Windows) to connect to ----@return fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient -function M.domain_socket_connect(pipe_path) - return function(dispatchers) - dispatchers = merge_dispatchers(dispatchers) - local pipe = - assert(uv.new_pipe(false), string.format('pipe with name %s could not be opened.', pipe_path)) - local closing = false - local transport = { - write = vim.schedule_wrap(function(msg) - pipe:write(msg) - end), is_closing = function() return closing end, terminate = function() if not closing then closing = true - pipe:shutdown() - pipe:close() + handle:shutdown() + handle:close() dispatchers.on_exit(0, 0) end end, } local client = new_client(dispatchers, transport) - pipe:connect(pipe_path, function(err) + local function on_connect(err) if err then + local address = port == nil and host_or_path or (host_or_path .. ':' .. port) vim.schedule(function() vim.notify( - string.format('Could not connect to :%s, reason: %s', pipe_path, vim.inspect(err)), + string.format('Could not connect to %s, reason: %s', address, vim.inspect(err)), vim.log.levels.WARN ) end) @@ -718,10 +690,19 @@ function M.domain_socket_connect(pipe_path) local handle_body = function(body) client:handle_body(body) end - pipe:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) + handle:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) client:on_error(M.client_errors.READ_ERROR, read_err) end)) - end) + connected = true + for msg in msgbuf do + handle:write(msg) + end + end + if port == nil then + handle:connect(host_or_path, on_connect) + else + handle:connect(host_or_path, port, on_connect) + end return public_client(client) end @@ -741,7 +722,7 @@ end --- @param cmd string[] Command to start the LSP server. --- @param dispatchers? vim.lsp.rpc.Dispatchers --- @param extra_spawn_params? vim.lsp.rpc.ExtraSpawnParams ---- @return vim.lsp.rpc.PublicClient? : Client RPC object, with these methods: +--- @return vim.lsp.rpc.PublicClient : Client RPC object, with these methods: --- - `notify()` |vim.lsp.rpc.notify()| --- - `request()` |vim.lsp.rpc.request()| --- - `is_closing()` returns a boolean indicating if the RPC is closing. @@ -816,8 +797,7 @@ function M.start(cmd, dispatchers, extra_spawn_params) end local msg = string.format('Spawning language server with cmd: `%s` failed%s', vim.inspect(cmd), sfx) - vim.notify(msg, vim.log.levels.WARN) - return nil + error(msg) end sysobj = sysobj_or_err --[[@as vim.SystemObj]] diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 20ac0a125f..ef2502b12e 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -394,7 +394,7 @@ function STHighlighter:process_response(response, client, version) current_result.namespace_cleared = false -- redraw all windows displaying buffer - api.nvim__buf_redraw_range(self.bufnr, 0, -1) + api.nvim__redraw({ buf = self.bufnr, valid = true }) end --- on_win handler for the decoration provider (see |nvim_set_decoration_provider|) @@ -570,9 +570,9 @@ local M = {} --- client.server_capabilities.semanticTokensProvider = nil --- ``` --- ----@param bufnr integer ----@param client_id integer ----@param opts? table Optional keyword arguments +---@param bufnr (integer) Buffer number, or `0` for current buffer +---@param client_id (integer) The ID of the |vim.lsp.Client| +---@param opts? (table) Optional keyword arguments --- - debounce (integer, default: 200): Debounce token requests --- to the server by the given number in milliseconds function M.start(bufnr, client_id, opts) @@ -581,6 +581,10 @@ function M.start(bufnr, client_id, opts) client_id = { client_id, 'n', false }, }) + if bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end + opts = opts or {} assert( (not opts.debounce or type(opts.debounce) == 'number'), @@ -626,14 +630,18 @@ end --- of `start()`, so you should only need this function to manually disengage the semantic --- token engine without fully detaching the LSP client from the buffer. --- ----@param bufnr integer ----@param client_id integer +---@param bufnr (integer) Buffer number, or `0` for current buffer +---@param client_id (integer) The ID of the |vim.lsp.Client| function M.stop(bufnr, client_id) vim.validate({ bufnr = { bufnr, 'n', false }, client_id = { client_id, 'n', false }, }) + if bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end + local highlighter = STHighlighter.active[bufnr] if not highlighter then return @@ -741,12 +749,15 @@ end --- mark will be deleted by the semantic token engine when appropriate; for --- example, when the LSP sends updated tokens. This function is intended for --- use inside |LspTokenUpdate| callbacks. ----@param token (table) a semantic token, found as `args.data.token` in |LspTokenUpdate|. ----@param bufnr (integer) the buffer to highlight +---@param token (table) A semantic token, found as `args.data.token` in |LspTokenUpdate| +---@param bufnr (integer) The buffer to highlight, or `0` for current buffer ---@param client_id (integer) The ID of the |vim.lsp.Client| ---@param hl_group (string) Highlight group name ---@param opts? vim.lsp.semantic_tokens.highlight_token.Opts Optional parameters: function M.highlight_token(token, bufnr, client_id, hl_group, opts) + if bufnr == 0 then + bufnr = api.nvim_get_current_buf() + end local highlighter = STHighlighter.active[bufnr] if not highlighter then return diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index f8e5b6a90d..5a229a1169 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1,5 +1,4 @@ local protocol = require('vim.lsp.protocol') -local snippet = require('vim.lsp._snippet_grammar') local validate = vim.validate local api = vim.api local list_extend = vim.list_extend @@ -343,68 +342,6 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) return col end ---- Process and return progress reports from lsp server ----@private ----@deprecated Use vim.lsp.status() or access client.progress directly -function M.get_progress_messages() - vim.deprecate('vim.lsp.util.get_progress_messages()', 'vim.lsp.status()', '0.11') - local new_messages = {} - local progress_remove = {} - - for _, client in ipairs(vim.lsp.get_clients()) do - local groups = {} - for progress in client.progress do - local value = progress.value - if type(value) == 'table' and value.kind then - local group = groups[progress.token] - if not group then - group = { - done = false, - progress = true, - title = 'empty title', - } - groups[progress.token] = group - end - group.title = value.title or group.title - group.cancellable = value.cancellable or group.cancellable - if value.kind == 'end' then - group.done = true - end - group.message = value.message or group.message - group.percentage = value.percentage or group.percentage - end - end - - for _, group in pairs(groups) do - table.insert(new_messages, group) - end - - 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, - done = ctx.done, - progress = true, - } - table.insert(new_messages, new_report) - - if ctx.done then - table.insert(progress_remove, { client = client, token = token }) - end - end - end - - for _, item in ipairs(progress_remove) do - item.client.messages.progress[item.token] = nil - end - - return new_messages -end - --- Applies a list of text edits to a buffer. ---@param text_edits table list of `TextEdit` objects ---@param bufnr integer Buffer id @@ -541,38 +478,6 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) end end --- local valid_windows_path_characters = "[^<>:\"/\\|?*]" --- local valid_unix_path_characters = "[^/]" --- https://github.com/davidm/lua-glob-pattern --- https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names --- function M.glob_to_regex(glob) --- end - ---- Can be used to extract the completion items from a ---- `textDocument/completion` request, which may return one of ---- `CompletionItem[]`, `CompletionList` or null. ---- ---- Note that this method doesn't apply `itemDefaults` to `CompletionList`s, and hence the returned ---- results might be incorrect. ---- ----@deprecated ----@param result table The result of a `textDocument/completion` request ----@return lsp.CompletionItem[] List of completion items ----@see https://microsoft.github.io/language-server-protocol/specification#textDocument_completion -function M.extract_completion_items(result) - vim.deprecate('vim.lsp.util.extract_completion_items()', nil, '0.11') - if type(result) == 'table' and result.items then - -- result is a `CompletionList` - return result.items - elseif result ~= nil then - -- result is `CompletionItem[]` - return result - else - -- result is `null` - return {} - end -end - --- Applies a `TextDocumentEdit`, which is a list of changes to a single --- document. --- @@ -615,38 +520,6 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) M.apply_text_edits(text_document_edit.edits, bufnr, offset_encoding) end ---- Parses snippets in a completion entry. ---- ----@deprecated ----@param input string unparsed snippet ----@return string parsed snippet -function M.parse_snippet(input) - vim.deprecate('vim.lsp.util.parse_snippet()', nil, '0.11') - local ok, parsed = pcall(function() - return snippet.parse(input) - end) - if not ok then - return input - end - - return tostring(parsed) -end - ---- Turns the result of a `textDocument/completion` request into vim-compatible ---- |complete-items|. ---- ----@deprecated ----@param result table The result of a `textDocument/completion` call, e.g. ---- from |vim.lsp.buf.completion()|, which may be one of `CompletionItem[]`, ---- `CompletionList` or `null` ----@param prefix (string) the prefix to filter the completion items ----@return table[] items ----@see complete-items -function M.text_document_completion_list_to_complete_items(result, prefix) - vim.deprecate('vim.lsp.util.text_document_completion_list_to_complete_items()', nil, '0.11') - return vim.lsp._completion._lsp_to_complete_items(result, prefix) -end - local function path_components(path) return vim.split(path, '/', { plain = true }) end @@ -690,7 +563,7 @@ end --- --- It deletes existing buffers that conflict with the renamed file name only when --- * `opts` requests overwriting; or ---- * the conflicting buffers are not loaded, so that deleting thme does not result in data loss. +--- * the conflicting buffers are not loaded, so that deleting them does not result in data loss. --- --- @param old_fname string --- @param new_fname string diff --git a/runtime/lua/provider/python/health.lua b/runtime/lua/vim/provider/health.lua index 333890b62b..63e0da448a 100644 --- a/runtime/lua/provider/python/health.lua +++ b/runtime/lua/vim/provider/health.lua @@ -1,14 +1,235 @@ local health = vim.health -local executable = health.executable -local iswin = vim.loop.os_uname().sysname == 'Windows_NT' +local iswin = vim.uv.os_uname().sysname == 'Windows_NT' local M = {} +local function clipboard() + health.start('Clipboard (optional)') + + if + os.getenv('TMUX') + and vim.fn.executable('tmux') == 1 + and vim.fn.executable('pbpaste') == 1 + and not health._cmd_ok('pbpaste') + then + local tmux_version = string.match(vim.fn.system('tmux -V'), '%d+%.%d+') + local advice = { + 'Install tmux 2.6+. https://superuser.com/q/231130', + 'or use tmux with reattach-to-user-namespace. https://superuser.com/a/413233', + } + health.error('pbcopy does not work with tmux version: ' .. tmux_version, advice) + end + + local clipboard_tool = vim.fn['provider#clipboard#Executable']() + if vim.g.clipboard ~= nil and clipboard_tool == '' then + local error_message = vim.fn['provider#clipboard#Error']() + health.error( + error_message, + "Use the example in :help g:clipboard as a template, or don't set g:clipboard at all." + ) + elseif clipboard_tool:find('^%s*$') then + health.warn( + 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.', + ':help clipboard' + ) + else + health.ok('Clipboard tool found: ' .. clipboard_tool) + end +end + +local function node() + health.start('Node.js provider (optional)') + + if health._provider_disabled('node') then + return + end + + if + vim.fn.executable('node') == 0 + or ( + vim.fn.executable('npm') == 0 + and vim.fn.executable('yarn') == 0 + and vim.fn.executable('pnpm') == 0 + ) + then + health.warn( + '`node` and `npm` (or `yarn`, `pnpm`) must be in $PATH.', + 'Install Node.js and verify that `node` and `npm` (or `yarn`, `pnpm`) commands work.' + ) + return + end + + -- local node_v = vim.fn.split(system({'node', '-v'}), "\n")[1] or '' + local ok, node_v = health._cmd_ok({ 'node', '-v' }) + health.info('Node.js: ' .. node_v) + if not ok or vim.version.lt(node_v, '6.0.0') then + health.warn('Nvim node.js host does not support Node ' .. node_v) + -- Skip further checks, they are nonsense if nodejs is too old. + return + end + if vim.fn['provider#node#can_inspect']() == 0 then + health.warn( + 'node.js on this system does not support --inspect-brk so $NVIM_NODE_HOST_DEBUG is ignored.' + ) + end + + local node_detect_table = vim.fn['provider#node#Detect']() + local host = node_detect_table[1] + if host:find('^%s*$') then + health.warn('Missing "neovim" npm (or yarn, pnpm) package.', { + 'Run in shell: npm install -g neovim', + 'Run in shell (if you use yarn): yarn global add neovim', + 'Run in shell (if you use pnpm): pnpm install -g neovim', + 'You may disable this provider (and warning) by adding `let g:loaded_node_provider = 0` to your init.vim', + }) + return + end + health.info('Nvim node.js host: ' .. host) + + local manager = 'npm' + if vim.fn.executable('yarn') == 1 then + manager = 'yarn' + elseif vim.fn.executable('pnpm') == 1 then + manager = 'pnpm' + end + + local latest_npm_cmd = ( + iswin and 'cmd /c ' .. manager .. ' info neovim --json' or manager .. ' info neovim --json' + ) + local latest_npm + ok, latest_npm = health._cmd_ok(vim.split(latest_npm_cmd, ' ')) + if not ok or latest_npm:find('^%s$') then + health.error( + 'Failed to run: ' .. latest_npm_cmd, + { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' } + ) + return + end + + local pcall_ok, pkg_data = pcall(vim.json.decode, latest_npm) + if not pcall_ok then + return 'error: ' .. latest_npm + end + local latest_npm_subtable = pkg_data['dist-tags'] or {} + latest_npm = latest_npm_subtable['latest'] or 'unable to parse' + + local current_npm_cmd = { 'node', host, '--version' } + local current_npm + ok, current_npm = health._cmd_ok(current_npm_cmd) + if not ok then + health.error( + 'Failed to run: ' .. table.concat(current_npm_cmd, ' '), + { 'Report this issue with the output of: ', table.concat(current_npm_cmd, ' ') } + ) + return + end + + if latest_npm ~= 'unable to parse' and vim.version.lt(current_npm, latest_npm) then + local message = 'Package "neovim" is out-of-date. Installed: ' + .. current_npm:gsub('%\n$', '') + .. ', latest: ' + .. latest_npm:gsub('%\n$', '') + + health.warn(message, { + 'Run in shell: npm install -g neovim', + 'Run in shell (if you use yarn): yarn global add neovim', + 'Run in shell (if you use pnpm): pnpm install -g neovim', + }) + else + health.ok('Latest "neovim" npm/yarn/pnpm package is installed: ' .. current_npm) + end +end + +local function perl() + health.start('Perl provider (optional)') + + if health._provider_disabled('perl') then + return + end + + local perl_exec, perl_warnings = vim.provider.perl.detect() + + if not perl_exec then + health.warn(assert(perl_warnings), { + 'See :help provider-perl for more information.', + 'You may disable this provider (and warning) by adding `let g:loaded_perl_provider = 0` to your init.vim', + }) + health.warn('No usable perl executable found') + return + end + + health.info('perl executable: ' .. perl_exec) + + -- we cannot use cpanm that is on the path, as it may not be for the perl + -- set with g:perl_host_prog + local ok = health._cmd_ok({ perl_exec, '-W', '-MApp::cpanminus', '-e', '' }) + if not ok then + return { perl_exec, '"App::cpanminus" module is not installed' } + end + + local latest_cpan_cmd = { + perl_exec, + '-MApp::cpanminus::fatscript', + '-e', + 'my $app = App::cpanminus::script->new; $app->parse_options ("--info", "-q", "Neovim::Ext"); exit $app->doit', + } + local latest_cpan + ok, latest_cpan = health._cmd_ok(latest_cpan_cmd) + if not ok or latest_cpan:find('^%s*$') then + health.error( + 'Failed to run: ' .. table.concat(latest_cpan_cmd, ' '), + { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' } + ) + return + elseif latest_cpan[1] == '!' then + local cpanm_errs = vim.split(latest_cpan, '!') + if cpanm_errs[1]:find("Can't write to ") then + local advice = {} + for i = 2, #cpanm_errs do + advice[#advice + 1] = cpanm_errs[i] + end + + health.warn(cpanm_errs[1], advice) + -- Last line is the package info + latest_cpan = cpanm_errs[#cpanm_errs] + else + health.error('Unknown warning from command: ' .. latest_cpan_cmd, cpanm_errs) + return + end + end + latest_cpan = vim.fn.matchstr(latest_cpan, [[\(\.\?\d\)\+]]) + if latest_cpan:find('^%s*$') then + health.error('Cannot parse version number from cpanm output: ' .. latest_cpan) + return + end + + local current_cpan_cmd = { perl_exec, '-W', '-MNeovim::Ext', '-e', 'print $Neovim::Ext::VERSION' } + local current_cpan + ok, current_cpan = health._cmd_ok(current_cpan_cmd) + if not ok then + health.error( + 'Failed to run: ' .. table.concat(current_cpan_cmd, ' '), + { 'Report this issue with the output of: ', table.concat(current_cpan_cmd, ' ') } + ) + return + end + + if vim.version.lt(current_cpan, latest_cpan) then + local message = 'Module "Neovim::Ext" is out-of-date. Installed: ' + .. current_cpan + .. ', latest: ' + .. latest_cpan + health.warn(message, 'Run in shell: cpanm -n Neovim::Ext') + else + health.ok('Latest "Neovim::Ext" cpan module is installed: ' .. current_cpan) + end +end + local function is(path, ty) if not path then return false end - local stat = vim.loop.fs_stat(path) + local stat = vim.uv.fs_stat(path) if not stat then return false end @@ -60,7 +281,7 @@ local function check_bin(bin) if not is(bin, 'file') and (not iswin or not is(bin .. '.exe', 'file')) then health.error('"' .. bin .. '" was not found.') return false - elseif not executable(bin) then + elseif vim.fn.executable(bin) == 0 then health.error('"' .. bin .. '" is not executable.') return false end @@ -69,22 +290,22 @@ end -- Fetch the contents of a URL. local function download(url) - local has_curl = executable('curl') + local has_curl = vim.fn.executable('curl') == 1 if has_curl and vim.fn.system({ 'curl', '-V' }):find('Protocols:.*https') then - local out, rc = health.system({ 'curl', '-sL', url }, { stderr = true, ignore_error = true }) + local out, rc = health._system({ 'curl', '-sL', url }, { stderr = true, ignore_error = true }) if rc ~= 0 then return 'curl error with ' .. url .. ': ' .. rc else return out end - elseif executable('python') then + elseif vim.fn.executable('python') == 1 then local script = "try:\n\ from urllib.request import urlopen\n\ except ImportError:\n\ from urllib2 import urlopen\n\ response = urlopen('" .. url .. "')\n\ print(response.read().decode('utf8'))\n" - local out, rc = health.system({ 'python', '-c', script }) + local out, rc = health._system({ 'python', '-c', script }) if out == '' and rc ~= 0 then return 'python urllib.request error: ' .. rc else @@ -141,7 +362,7 @@ end local function version_info(python) local pypi_version = latest_pypi_version() - local python_version, rc = health.system({ + local python_version, rc = health._system({ python, '-c', 'import sys; print(".".join(str(x) for x in sys.version_info[:3]))', @@ -152,7 +373,7 @@ local function version_info(python) end local nvim_path - nvim_path, rc = health.system({ + nvim_path, rc = health._system({ python, '-c', 'import sys; sys.path = [p for p in sys.path if p != ""]; import neovim; print(neovim.__file__)', @@ -177,7 +398,7 @@ local function version_info(python) -- Try to get neovim.VERSION (added in 0.1.11dev). local nvim_version - nvim_version, rc = health.system({ + nvim_version, rc = health._system({ python, '-c', 'from neovim import VERSION as v; print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))', @@ -214,7 +435,7 @@ local function version_info(python) return { python_version, nvim_version, pypi_version, version_status } end -function M.check() +local function python() health.start('Python 3 provider (optional)') local pyname = 'python3' ---@type string? @@ -224,7 +445,7 @@ function M.check() local host_prog_var = pyname .. '_host_prog' local python_multiple = {} - if health.provider_disabled(pyname) then + if health._provider_disabled(pyname) then return end @@ -266,7 +487,7 @@ function M.check() end if pyenv ~= '' then - python_exe = health.system({ pyenv, 'which', pyname }, { stderr = true }) + python_exe = health._system({ pyenv, 'which', pyname }, { stderr = true }) if python_exe == '' then health.warn('pyenv could not find ' .. pyname .. '.') end @@ -284,7 +505,7 @@ function M.check() if path_bin ~= vim.fs.normalize(python_exe) and vim.tbl_contains(python_multiple, path_bin) - and executable(path_bin) + and vim.fn.executable(path_bin) == 1 then python_multiple[#python_multiple + 1] = path_bin end @@ -472,7 +693,7 @@ function M.check() bin_dir, table.concat( vim.tbl_map(function(v) - return vim.fn.fnamemodify(v, ':t') + return vim.fs.basename(v) end, venv_bins), ', ' ) @@ -489,7 +710,7 @@ function M.check() health.info(msg) health.info( 'Python version: ' - .. health.system( + .. health._system( 'python -c "import platform, sys; sys.stdout.write(platform.python_version())"' ) ) @@ -497,4 +718,75 @@ function M.check() end end +local function ruby() + health.start('Ruby provider (optional)') + + if health._provider_disabled('ruby') then + return + end + + if vim.fn.executable('ruby') == 0 or vim.fn.executable('gem') == 0 then + health.warn( + '`ruby` and `gem` must be in $PATH.', + 'Install Ruby and verify that `ruby` and `gem` commands work.' + ) + return + end + health.info('Ruby: ' .. health._system({ 'ruby', '-v' })) + + local host, _ = vim.provider.ruby.detect() + if (not host) or host:find('^%s*$') then + health.warn('`neovim-ruby-host` not found.', { + 'Run `gem install neovim` to ensure the neovim RubyGem is installed.', + 'Run `gem environment` to ensure the gem bin directory is in $PATH.', + 'If you are using rvm/rbenv/chruby, try "rehashing".', + 'See :help g:ruby_host_prog for non-standard gem installations.', + 'You may disable this provider (and warning) by adding `let g:loaded_ruby_provider = 0` to your init.vim', + }) + return + end + health.info('Host: ' .. host) + + local latest_gem_cmd = (iswin and 'cmd /c gem list -ra "^^neovim$"' or 'gem list -ra ^neovim$') + local ok, latest_gem = health._cmd_ok(vim.split(latest_gem_cmd, ' ')) + if not ok or latest_gem:find('^%s*$') then + health.error( + 'Failed to run: ' .. latest_gem_cmd, + { "Make sure you're connected to the internet.", 'Are you behind a firewall or proxy?' } + ) + return + end + local gem_split = vim.split(latest_gem, [[neovim (\|, \|)$]]) + latest_gem = gem_split[1] or 'not found' + + local current_gem_cmd = { host, '--version' } + local current_gem + ok, current_gem = health._cmd_ok(current_gem_cmd) + if not ok then + health.error( + 'Failed to run: ' .. table.concat(current_gem_cmd, ' '), + { 'Report this issue with the output of: ', table.concat(current_gem_cmd, ' ') } + ) + return + end + + if vim.version.lt(current_gem, latest_gem) then + local message = 'Gem "neovim" is out-of-date. Installed: ' + .. current_gem + .. ', latest: ' + .. latest_gem + health.warn(message, 'Run in shell: gem update neovim') + else + health.ok('Latest "neovim" gem is installed: ' .. current_gem) + end +end + +function M.check() + clipboard() + node() + perl() + python() + ruby() +end + return M diff --git a/runtime/lua/vim/secure.lua b/runtime/lua/vim/secure.lua index 3992eef78a..41a3d3ba25 100644 --- a/runtime/lua/vim/secure.lua +++ b/runtime/lua/vim/secure.lua @@ -126,7 +126,7 @@ end --- --- The trust database is located at |$XDG_STATE_HOME|/nvim/trust. --- ----@param opts? vim.trust.opts +---@param opts vim.trust.opts ---@return boolean success true if operation was successful ---@return string msg full path if operation was successful, else error message function M.trust(opts) diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index bd553598c7..e9e4326057 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -356,7 +356,7 @@ end --- We only merge empty tables or tables that are not an array (indexed by integers) local function can_merge(v) - return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.tbl_isarray(v)) + return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.isarray(v)) end local function tbl_extend(behavior, deep_extend, ...) @@ -402,7 +402,7 @@ end --- ---@see |extend()| --- ----@param behavior string Decides what to do if a key is found in more than one map: +---@param behavior 'error'|'keep'|'force' Decides what to do if a key is found in more than one map: --- - "error": raise an error --- - "keep": use value from the leftmost map --- - "force": use value from the rightmost map @@ -418,7 +418,7 @@ end --- ---@generic T1: table ---@generic T2: table ----@param behavior "error"|"keep"|"force" (string) Decides what to do if a key is found in more than one map: +---@param behavior 'error'|'keep'|'force' Decides what to do if a key is found in more than one map: --- - "error": raise an error --- - "keep": use value from the leftmost map --- - "force": use value from the rightmost map @@ -502,7 +502,7 @@ end --- ---@param o table Table to index ---@param ... any Optional keys (0 or more, variadic) via which to index the table ----@return any : Nested value indexed by key (if it exists), else nil +---@return any # Nested value indexed by key (if it exists), else nil function vim.tbl_get(o, ...) local keys = { ... } if #keys == 0 then @@ -544,6 +544,7 @@ function vim.list_extend(dst, src, start, finish) return dst end +--- @deprecated --- Creates a copy of a list-like table such that any nested tables are --- "unrolled" and appended to the result. --- @@ -552,6 +553,7 @@ end ---@param t table List-like table ---@return table Flattened copy of the given list-like table function vim.tbl_flatten(t) + vim.deprecate('vim.tbl_flatten', 'vim.iter(…):flatten():totable()', '0.13') local result = {} --- @param _t table<any,any> local function _tbl_flatten(_t) @@ -578,7 +580,7 @@ end ---@return fun(table: table<K, V>, index?: K):K, V # |for-in| iterator over sorted keys and their values ---@return T function vim.spairs(t) - vim.validate({ t = { t, 't' } }) + assert(type(t) == 'table', ('expected table, got %s'):format(type(t))) --- @cast t table<any,any> -- collect the keys @@ -601,16 +603,16 @@ end --- Tests if `t` is an "array": a table indexed _only_ by integers (potentially non-contiguous). --- ---- If the indexes start from 1 and are contiguous then the array is also a list. |vim.tbl_islist()| +--- If the indexes start from 1 and are contiguous then the array is also a list. |vim.islist()| --- --- Empty table `{}` is an array, unless it was created by |vim.empty_dict()| or returned as --- a dict-like |API| or Vimscript result, for example from |rpcrequest()| or |vim.fn|. --- ---@see https://github.com/openresty/luajit2#tableisarray --- ----@param t table +---@param t? table ---@return boolean `true` if array-like table, else `false`. -function vim.tbl_isarray(t) +function vim.isarray(t) if type(t) ~= 'table' then return false end @@ -640,17 +642,23 @@ function vim.tbl_isarray(t) end end +--- @deprecated +function vim.tbl_islist(t) + vim.deprecate('vim.tbl_islist', 'vim.islist', '0.12') + return vim.islist(t) +end + --- Tests if `t` is a "list": a table indexed _only_ by contiguous integers starting from 1 (what --- |lua-length| calls a "regular array"). --- --- Empty table `{}` is a list, unless it was created by |vim.empty_dict()| or returned as --- a dict-like |API| or Vimscript result, for example from |rpcrequest()| or |vim.fn|. --- ----@see |vim.tbl_isarray()| +---@see |vim.isarray()| --- ----@param t table +---@param t? table ---@return boolean `true` if list-like table, else `false`. -function vim.tbl_islist(t) +function vim.islist(t) if type(t) ~= 'table' then return false end @@ -788,6 +796,61 @@ do return type(val) == t or (t == 'callable' and vim.is_callable(val)) end + --- @param param_name string + --- @param spec vim.validate.Spec + --- @return string? + local function is_param_valid(param_name, spec) + if type(spec) ~= 'table' then + return string.format('opt[%s]: expected table, got %s', param_name, type(spec)) + end + + local val = spec[1] -- Argument value + local types = spec[2] -- Type name, or callable + local optional = (true == spec[3]) + + if type(types) == 'string' then + types = { types } + end + + if vim.is_callable(types) then + -- Check user-provided validation function + local valid, optional_message = types(val) + if not valid then + local error_message = + string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val)) + if optional_message ~= nil then + error_message = string.format('%s. Info: %s', error_message, optional_message) + end + + return error_message + end + elseif type(types) == 'table' then + local success = false + for i, t in ipairs(types) do + local t_name = type_names[t] + if not t_name then + return string.format('invalid type name: %s', t) + end + types[i] = t_name + + if (optional and val == nil) or _is_type(val, t_name) then + success = true + break + end + end + if not success then + return string.format( + '%s: expected %s, got %s', + param_name, + table.concat(types, '|'), + type(val) + ) + end + else + return string.format('invalid type name: %s', tostring(types)) + end + end + --- @param opt table<vim.validate.Type,vim.validate.Spec> --- @return boolean, string? local function is_valid(opt) @@ -795,63 +858,27 @@ do return false, string.format('opt: expected table, got %s', type(opt)) end - for param_name, spec in pairs(opt) do - if type(spec) ~= 'table' then - return false, string.format('opt[%s]: expected table, got %s', param_name, type(spec)) - end - - local val = spec[1] -- Argument value - local types = spec[2] -- Type name, or callable - local optional = (true == spec[3]) + local report --- @type table<string,string>? - if type(types) == 'string' then - types = { types } + for param_name, spec in pairs(opt) do + local msg = is_param_valid(param_name, spec) + if msg then + report = report or {} + report[param_name] = msg end + end - if vim.is_callable(types) then - -- Check user-provided validation function - local valid, optional_message = types(val) - if not valid then - local error_message = - string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val)) - if optional_message ~= nil then - error_message = error_message .. string.format('. Info: %s', optional_message) - end - - return false, error_message - end - elseif type(types) == 'table' then - local success = false - for i, t in ipairs(types) do - local t_name = type_names[t] - if not t_name then - return false, string.format('invalid type name: %s', t) - end - types[i] = t_name - - if (optional and val == nil) or _is_type(val, t_name) then - success = true - break - end - end - if not success then - return false, - string.format( - '%s: expected %s, got %s', - param_name, - table.concat(types, '|'), - type(val) - ) - end - else - return false, string.format('invalid type name: %s', tostring(types)) + if report then + for _, msg in vim.spairs(report) do -- luacheck: ignore + return false, msg end end return true end - --- Validates a parameter specification (types and values). + --- Validates a parameter specification (types and values). Specs are evaluated in alphanumeric + --- order, until the first failure. --- --- Usage example: --- diff --git a/runtime/lua/vim/snippet.lua b/runtime/lua/vim/snippet.lua index 5e60efa778..3d8f73f362 100644 --- a/runtime/lua/vim/snippet.lua +++ b/runtime/lua/vim/snippet.lua @@ -254,9 +254,10 @@ local function display_choices(tabstop) assert(tabstop.choices, 'Tabstop has no choices') local start_col = tabstop:get_range()[2] + 1 - local matches = vim.iter.map(function(choice) - return { word = choice } - end, tabstop.choices) + local matches = {} --- @type table[] + for _, choice in ipairs(tabstop.choices) do + matches[#matches + 1] = { word = choice } + end vim.defer_fn(function() vim.fn.complete(start_col, matches) @@ -342,7 +343,7 @@ local function setup_autocmds(bufnr) or cursor_row > snippet_range[3] or (cursor_row == snippet_range[3] and cursor_col > snippet_range[4]) then - M.exit() + M.stop() return true end @@ -361,7 +362,7 @@ local function setup_autocmds(bufnr) end -- The cursor is either not on a tabstop or we reached the end, so exit the session. - M.exit() + M.stop() return true end, }) @@ -377,7 +378,7 @@ local function setup_autocmds(bufnr) (snippet_range[1] == snippet_range[3] and snippet_range[2] == snippet_range[4]) or snippet_range[3] + 1 > vim.fn.line('$') then - M.exit() + M.stop() end if not M.active() then @@ -400,7 +401,7 @@ end --- Refer to https://microsoft.github.io/language-server-protocol/specification/#snippet_syntax --- for the specification of valid input. --- ---- Tabstops are highlighted with hl-SnippetTabstop. +--- Tabstops are highlighted with |hl-SnippetTabstop|. --- --- @param input string function M.expand(input) @@ -446,17 +447,22 @@ function M.expand(input) base_indent = base_indent .. (snippet_lines[#snippet_lines]:match('(^%s*)%S') or '') --- @type string end - local lines = vim.iter.map(function(i, line) + local shiftwidth = vim.fn.shiftwidth() + local curbuf = vim.api.nvim_get_current_buf() + local expandtab = vim.bo[curbuf].expandtab + + local lines = {} --- @type string[] + for i, line in ipairs(text_to_lines(text)) do -- Replace tabs by spaces. - if vim.o.expandtab then - line = line:gsub('\t', (' '):rep(vim.fn.shiftwidth())) --- @type string + if expandtab then + line = line:gsub('\t', (' '):rep(shiftwidth)) --- @type string end -- Add the base indentation. if i > 1 then line = base_indent .. line end - return line - end, ipairs(text_to_lines(text))) + lines[#lines + 1] = line + end table.insert(snippet_text, table.concat(lines, '\n')) end @@ -526,37 +532,13 @@ end --- @alias vim.snippet.Direction -1 | 1 ---- Returns `true` if there is an active snippet which can be jumped in the given direction. ---- You can use this function to navigate a snippet as follows: +--- Jumps to the next (or previous) placeholder in the current snippet, if possible. --- ---- ```lua ---- vim.keymap.set({ 'i', 's' }, '<Tab>', function() ---- if vim.snippet.jumpable(1) then ---- return '<cmd>lua vim.snippet.jump(1)<cr>' ---- else ---- return '<Tab>' ---- end ---- end, { expr = true }) ---- ``` ---- ---- @param direction (vim.snippet.Direction) Navigation direction. -1 for previous, 1 for next. ---- @return boolean -function M.jumpable(direction) - if not M.active() then - return false - end - - return M._session:get_dest_index(direction) ~= nil -end - ---- Jumps within the active snippet in the given direction. ---- If the jump isn't possible, the function call does nothing. ---- ---- You can use this function to navigate a snippet as follows: +--- For example, map `<Tab>` to jump while a snippet is active: --- --- ```lua --- vim.keymap.set({ 'i', 's' }, '<Tab>', function() ---- if vim.snippet.jumpable(1) then +--- if vim.snippet.active({ direction = 1 }) then --- return '<cmd>lua vim.snippet.jump(1)<cr>' --- else --- return '<Tab>' @@ -598,15 +580,41 @@ function M.jump(direction) setup_autocmds(M._session.bufnr) end ---- Returns `true` if there's an active snippet in the current buffer. +--- @class vim.snippet.ActiveFilter +--- @field direction vim.snippet.Direction Navigation direction. -1 for previous, 1 for next. + +--- Returns `true` if there's an active snippet in the current buffer, +--- applying the given filter if provided. +--- +--- You can use this function to navigate a snippet as follows: +--- +--- ```lua +--- vim.keymap.set({ 'i', 's' }, '<Tab>', function() +--- if vim.snippet.active({ direction = 1 }) then +--- return '<cmd>lua vim.snippet.jump(1)<cr>' +--- else +--- return '<Tab>' +--- end +--- end, { expr = true }) +--- ``` --- +--- @param filter? vim.snippet.ActiveFilter Filter to constrain the search with: +--- - `direction` (vim.snippet.Direction): Navigation direction. Will return `true` if the snippet +--- can be jumped in the given direction. --- @return boolean -function M.active() - return M._session ~= nil and M._session.bufnr == vim.api.nvim_get_current_buf() +function M.active(filter) + local active = M._session ~= nil and M._session.bufnr == vim.api.nvim_get_current_buf() + + local in_direction = true + if active and filter and filter.direction then + in_direction = M._session:get_dest_index(filter.direction) ~= nil + end + + return active and in_direction end --- Exits the current snippet. -function M.exit() +function M.stop() if not M.active() then return end diff --git a/runtime/lua/vim/termcap.lua b/runtime/lua/vim/termcap.lua index ec29acca48..1da2e71839 100644 --- a/runtime/lua/vim/termcap.lua +++ b/runtime/lua/vim/termcap.lua @@ -12,7 +12,7 @@ local M = {} --- emulator supports the XTGETTCAP sequence. --- --- @param caps string|table A terminal capability or list of capabilities to query ---- @param cb function(cap:string, found:bool, seq:string?) Callback function which is called for +--- @param cb fun(cap:string, found:boolean, seq:string?) Callback function which is called for --- each capability in {caps}. {found} is set to true if the capability was found or false --- otherwise. {seq} is the control sequence for the capability if found, or nil for --- boolean capabilities. diff --git a/runtime/lua/vim/text.lua b/runtime/lua/vim/text.lua index 576b962838..bc90d490aa 100644 --- a/runtime/lua/vim/text.lua +++ b/runtime/lua/vim/text.lua @@ -5,7 +5,7 @@ local M = {} --- Hex encode a string. --- --- @param str string String to encode ---- @return string Hex encoded string +--- @return string : Hex encoded string function M.hexencode(str) local bytes = { str:byte(1, #str) } local enc = {} ---@type string[] @@ -18,7 +18,7 @@ end --- Hex decode a string. --- --- @param enc string String to decode ---- @return string Decoded string +--- @return string : Decoded string function M.hexdecode(enc) assert(#enc % 2 == 0, 'string must have an even number of hex characters') local str = {} ---@type string[] diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index a09619f369..db544c1ab1 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -257,7 +257,7 @@ end ---@param row integer Position row ---@param col integer Position column --- ----@return table[] List of captures `{ capture = "name", metadata = { ... } }` +---@return {capture: string, lang: string, metadata: table}[] function M.get_captures_at_pos(bufnr, row, col) if bufnr == 0 then bufnr = api.nvim_get_current_buf() @@ -327,7 +327,7 @@ function M.get_captures_at_cursor(winnr) end --- Optional keyword arguments: ---- @class vim.treesitter.get_node.Opts +--- @class vim.treesitter.get_node.Opts : vim.treesitter.LanguageTree.tree_for_range.Opts --- @inlinedoc --- --- Buffer number (nil or 0 for current buffer) diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index d96cc966de..eecf1ad6b1 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -4,10 +4,21 @@ local Range = require('vim.treesitter._range') local api = vim.api +---Treesitter folding is done in two steps: +---(1) compute the fold levels with the syntax tree and cache the result (`compute_folds_levels`) +---(2) evaluate foldexpr for each window, which reads from the cache (`foldupdate`) ---@class TS.FoldInfo ----@field levels string[] the foldexpr result for each line ----@field levels0 integer[] the raw fold levels ----@field edits? {[1]: integer, [2]: integer} line range edited since the last invocation of the callback scheduled in on_bytes. 0-indexed, end-exclusive. +--- +---@field levels string[] the cached foldexpr result for each line +---@field levels0 integer[] the cached raw fold levels +--- +---The range edited since the last invocation of the callback scheduled in on_bytes. +---Should compute fold levels in this range. +---@field on_bytes_range? Range2 +--- +---The range on which to evaluate foldexpr. +---When in insert mode, the evaluation is deferred to InsertLeave. +---@field foldupdate_range? Range2 local FoldInfo = {} FoldInfo.__index = FoldInfo @@ -80,45 +91,16 @@ function FoldInfo:add_range(srow, erow) list_insert(self.levels0, srow + 1, erow, -1) end ----@package +---@param range Range2 ---@param srow integer ---@param erow_old integer ---@param erow_new integer 0-indexed, exclusive -function FoldInfo:edit_range(srow, erow_old, erow_new) - if self.edits then - self.edits[1] = math.min(srow, self.edits[1]) - if erow_old <= self.edits[2] then - self.edits[2] = self.edits[2] + (erow_new - erow_old) - end - self.edits[2] = math.max(self.edits[2], erow_new) - else - self.edits = { srow, erow_new } +local function edit_range(range, srow, erow_old, erow_new) + range[1] = math.min(srow, range[1]) + if erow_old <= range[2] then + range[2] = range[2] + (erow_new - erow_old) end -end - ----@package ----@return integer? srow ----@return integer? erow 0-indexed, exclusive -function FoldInfo:flush_edit() - if self.edits then - local srow, erow = self.edits[1], self.edits[2] - self.edits = nil - return srow, erow - end -end - ---- If a parser doesn't have any ranges explicitly set, treesitter will ---- return a range with end_row and end_bytes with a value of UINT32_MAX, ---- so clip end_row to the max buffer line. ---- ---- TODO(lewis6991): Handle this generally ---- ---- @param bufnr integer ---- @param erow integer? 0-indexed, exclusive ---- @return integer -local function normalise_erow(bufnr, erow) - local max_erow = api.nvim_buf_line_count(bufnr) - return math.min(erow or max_erow, max_erow) + range[2] = math.max(range[2], erow_new) end -- TODO(lewis6991): Setup a decor provider so injections folds can be parsed @@ -128,9 +110,9 @@ end ---@param srow integer? ---@param erow integer? 0-indexed, exclusive ---@param parse_injections? boolean -local function get_folds_levels(bufnr, info, srow, erow, parse_injections) +local function compute_folds_levels(bufnr, info, srow, erow, parse_injections) srow = srow or 0 - erow = normalise_erow(bufnr, erow) + erow = erow or api.nvim_buf_line_count(bufnr) local parser = ts.get_parser(bufnr) @@ -149,27 +131,43 @@ local function get_folds_levels(bufnr, info, srow, erow, parse_injections) -- Collect folds starting from srow - 1, because we should first subtract the folds that end at -- srow - 1 from the level of srow - 1 to get accurate level of srow. - for id, node, metadata in query:iter_captures(tree:root(), bufnr, math.max(srow - 1, 0), erow) do - if query.captures[id] == 'fold' then - local range = ts.get_range(node, bufnr, metadata[id]) - local start, _, stop, stop_col = Range.unpack4(range) - - if stop_col == 0 then - stop = stop - 1 - end - - local fold_length = stop - start + 1 - - -- Fold only multiline nodes that are not exactly the same as previously met folds - -- Checking against just the previously found fold is sufficient if nodes - -- are returned in preorder or postorder when traversing tree - if - fold_length > vim.wo.foldminlines and not (start == prev_start and stop == prev_stop) - then - enter_counts[start + 1] = (enter_counts[start + 1] or 0) + 1 - leave_counts[stop + 1] = (leave_counts[stop + 1] or 0) + 1 - prev_start = start - prev_stop = stop + for _, match, metadata in + query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow, { all = true }) + do + for id, nodes in pairs(match) do + if query.captures[id] == 'fold' then + local range = ts.get_range(nodes[1], bufnr, metadata[id]) + local start, _, stop, stop_col = Range.unpack4(range) + + for i = 2, #nodes, 1 do + local node_range = ts.get_range(nodes[i], bufnr, metadata[id]) + local node_start, _, node_stop, node_stop_col = Range.unpack4(node_range) + if node_start < start then + start = node_start + end + if node_stop > stop then + stop = node_stop + stop_col = node_stop_col + end + end + + if stop_col == 0 then + stop = stop - 1 + end + + local fold_length = stop - start + 1 + + -- Fold only multiline nodes that are not exactly the same as previously met folds + -- Checking against just the previously found fold is sufficient if nodes + -- are returned in preorder or postorder when traversing tree + if + fold_length > vim.wo.foldminlines and not (start == prev_start and stop == prev_stop) + then + enter_counts[start + 1] = (enter_counts[start + 1] or 0) + 1 + leave_counts[stop + 1] = (leave_counts[stop + 1] or 0) + 1 + prev_start = start + prev_stop = stop + end end end end @@ -215,7 +213,7 @@ local function get_folds_levels(bufnr, info, srow, erow, parse_injections) clamped = nestmax end - -- Record the "real" level, so that it can be used as "base" of later get_folds_levels(). + -- Record the "real" level, so that it can be used as "base" of later compute_folds_levels(). info.levels0[lnum] = adjusted info.levels[lnum] = prefix .. tostring(clamped) @@ -236,18 +234,17 @@ local group = api.nvim_create_augroup('treesitter/fold', {}) --- --- Nvim usually automatically updates folds when text changes, but it doesn't work here because --- FoldInfo update is scheduled. So we do it manually. -local function foldupdate(bufnr) - local function do_update() - for _, win in ipairs(vim.fn.win_findbuf(bufnr)) do - api.nvim_win_call(win, function() - if vim.wo.foldmethod == 'expr' then - vim._foldupdate() - end - end) - end +---@package +---@param srow integer +---@param erow integer 0-indexed, exclusive +function FoldInfo:foldupdate(bufnr, srow, erow) + if self.foldupdate_range then + edit_range(self.foldupdate_range, srow, erow, erow) + else + self.foldupdate_range = { srow, erow } end - if api.nvim_get_mode().mode == 'i' then + if api.nvim_get_mode().mode:match('^i') then -- foldUpdate() is guarded in insert mode. So update folds on InsertLeave if #(api.nvim_get_autocmds({ group = group, @@ -259,12 +256,25 @@ local function foldupdate(bufnr) group = group, buffer = bufnr, once = true, - callback = do_update, + callback = function() + self:do_foldupdate(bufnr) + end, }) return end - do_update() + self:do_foldupdate(bufnr) +end + +---@package +function FoldInfo:do_foldupdate(bufnr) + local srow, erow = self.foldupdate_range[1], self.foldupdate_range[2] + self.foldupdate_range = nil + for _, win in ipairs(vim.fn.win_findbuf(bufnr)) do + if vim.wo[win].foldmethod == 'expr' then + vim._foldupdate(win, srow, erow) + end + end end --- Schedule a function only if bufnr is loaded. @@ -272,7 +282,7 @@ end --- * queries seem to use the old buffer state in on_bytes for some unknown reason; --- * to avoid textlock; --- * to avoid infinite recursion: ---- get_folds_levels → parse → _do_callback → on_changedtree → get_folds_levels. +--- compute_folds_levels → parse → _do_callback → on_changedtree → compute_folds_levels. ---@param bufnr integer ---@param fn function local function schedule_if_loaded(bufnr, fn) @@ -289,16 +299,27 @@ end ---@param tree_changes Range4[] local function on_changedtree(bufnr, foldinfo, tree_changes) schedule_if_loaded(bufnr, function() + local srow_upd, erow_upd ---@type integer?, integer? + local max_erow = api.nvim_buf_line_count(bufnr) for _, change in ipairs(tree_changes) do local srow, _, erow, ecol = Range.unpack4(change) - if ecol > 0 then + -- If a parser doesn't have any ranges explicitly set, treesitter will + -- return a range with end_row and end_bytes with a value of UINT32_MAX, + -- so clip end_row to the max buffer line. + -- TODO(lewis6991): Handle this generally + if erow > max_erow then + erow = max_erow + elseif ecol > 0 then erow = erow + 1 end -- Start from `srow - foldminlines`, because this edit may have shrunken the fold below limit. - get_folds_levels(bufnr, foldinfo, math.max(srow - vim.wo.foldminlines, 0), erow) + srow = math.max(srow - vim.wo.foldminlines, 0) + compute_folds_levels(bufnr, foldinfo, srow, erow) + srow_upd = srow_upd and math.min(srow_upd, srow) or srow + erow_upd = erow_upd and math.max(erow_upd, erow) or erow end if #tree_changes > 0 then - foldupdate(bufnr) + foldinfo:foldupdate(bufnr, srow_upd, erow_upd) end end) end @@ -335,19 +356,29 @@ local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col, foldinfo:add_range(end_row_old, end_row_new) end end - foldinfo:edit_range(start_row, end_row_old, end_row_new) + + if foldinfo.on_bytes_range then + edit_range(foldinfo.on_bytes_range, start_row, end_row_old, end_row_new) + else + foldinfo.on_bytes_range = { start_row, end_row_new } + end + if foldinfo.foldupdate_range then + edit_range(foldinfo.foldupdate_range, start_row, end_row_old, end_row_new) + end -- This callback must not use on_bytes arguments, because they can be outdated when the callback -- is invoked. For example, `J` with non-zero count triggers multiple on_bytes before executing - -- the scheduled callback. So we should collect the edits. + -- the scheduled callback. So we accumulate the edited ranges in `on_bytes_range`. schedule_if_loaded(bufnr, function() - local srow, erow = foldinfo:flush_edit() - if not srow then + if not foldinfo.on_bytes_range then return end + local srow, erow = foldinfo.on_bytes_range[1], foldinfo.on_bytes_range[2] + foldinfo.on_bytes_range = nil -- Start from `srow - foldminlines`, because this edit may have shrunken the fold below limit. - get_folds_levels(bufnr, foldinfo, math.max(srow - vim.wo.foldminlines, 0), erow) - foldupdate(bufnr) + srow = math.max(srow - vim.wo.foldminlines, 0) + compute_folds_levels(bufnr, foldinfo, srow, erow) + foldinfo:foldupdate(bufnr, srow, erow) end) end end @@ -366,7 +397,7 @@ function M.foldexpr(lnum) if not foldinfos[bufnr] then foldinfos[bufnr] = FoldInfo.new() - get_folds_levels(bufnr, foldinfos[bufnr]) + compute_folds_levels(bufnr, foldinfos[bufnr]) parser:register_cbs({ on_changedtree = function(tree_changes) @@ -390,10 +421,10 @@ api.nvim_create_autocmd('OptionSet', { pattern = { 'foldminlines', 'foldnestmax' }, desc = 'Refresh treesitter folds', callback = function() - for _, bufnr in ipairs(vim.tbl_keys(foldinfos)) do + for bufnr, _ in pairs(foldinfos) do foldinfos[bufnr] = FoldInfo.new() - get_folds_levels(bufnr, foldinfos[bufnr]) - foldupdate(bufnr) + compute_folds_levels(bufnr, foldinfos[bufnr]) + foldinfos[bufnr]:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr)) end end, }) diff --git a/runtime/lua/vim/treesitter/_meta.lua b/runtime/lua/vim/treesitter/_meta.lua index 19d97d2820..177699a207 100644 --- a/runtime/lua/vim/treesitter/_meta.lua +++ b/runtime/lua/vim/treesitter/_meta.lua @@ -20,6 +20,7 @@ error('Cannot require a meta file') ---@field descendant_for_range fun(self: TSNode, start_row: integer, start_col: integer, end_row: integer, end_col: integer): TSNode? ---@field named_descendant_for_range fun(self: TSNode, start_row: integer, start_col: integer, end_row: integer, end_col: integer): TSNode? ---@field parent fun(self: TSNode): TSNode? +---@field child_containing_descendant fun(self: TSNode, descendant: TSNode): TSNode? ---@field next_sibling fun(self: TSNode): TSNode? ---@field prev_sibling fun(self: TSNode): TSNode? ---@field next_named_sibling fun(self: TSNode): TSNode? @@ -34,22 +35,6 @@ error('Cannot require a meta file') ---@field byte_length fun(self: TSNode): integer local TSNode = {} ----@param query TSQuery ----@param captures true ----@param start? integer ----@param end_? integer ----@param opts? table ----@return fun(): integer, TSNode, vim.treesitter.query.TSMatch -function TSNode:_rawquery(query, captures, start, end_, opts) end - ----@param query TSQuery ----@param captures false ----@param start? integer ----@param end_? integer ----@param opts? table ----@return fun(): integer, vim.treesitter.query.TSMatch -function TSNode:_rawquery(query, captures, start, end_, opts) end - ---@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string) ---@class TSParser: userdata @@ -76,9 +61,17 @@ function TSNode:_rawquery(query, captures, start, end_, opts) end ---@field captures string[] ---@field patterns table<integer, (integer|string)[][]> +--- @param lang string +vim._ts_inspect_language = function(lang) end + ---@return integer vim._ts_get_language_version = function() end +--- @param path string +--- @param lang string +--- @param symbol_name? string +vim._ts_add_language = function(path, lang, symbol_name) end + ---@return integer vim._ts_get_minimum_language_version = function() end @@ -90,3 +83,31 @@ vim._ts_parse_query = function(lang, query) end ---@param lang string ---@return TSParser vim._create_ts_parser = function(lang) end + +--- @class TSQueryMatch: userdata +--- @field captures fun(self: TSQueryMatch): table<integer,TSNode[]> +local TSQueryMatch = {} + +--- @return integer match_id +--- @return integer pattern_index +function TSQueryMatch:info() end + +--- @class TSQueryCursor: userdata +--- @field remove_match fun(self: TSQueryCursor, id: integer) +local TSQueryCursor = {} + +--- @return integer capture +--- @return TSNode captured_node +--- @return TSQueryMatch match +function TSQueryCursor:next_capture() end + +--- @return TSQueryMatch match +function TSQueryCursor:next_match() end + +--- @param node TSNode +--- @param query TSQuery +--- @param start integer? +--- @param stop integer? +--- @param opts? { max_start_depth?: integer, match_limit?: integer} +--- @return TSQueryCursor +function vim._create_ts_querycursor(node, query, start, stop, opts) end diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua index 6216d4e891..12b4cbc7b9 100644 --- a/runtime/lua/vim/treesitter/_query_linter.lua +++ b/runtime/lua/vim/treesitter/_query_linter.lua @@ -122,7 +122,7 @@ local parse = vim.func._memoize(hash_parse, function(node, buf, lang) end) --- @param buf integer ---- @param match vim.treesitter.query.TSMatch +--- @param match table<integer,TSNode[]> --- @param query vim.treesitter.Query --- @param lang_context QueryLinterLanguageContext --- @param diagnostics vim.Diagnostic[] diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index dc2a14d238..5c91f101c0 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -226,7 +226,7 @@ function TSTreeView:draw(bufnr) text = string.format('(%s', item.node:type()) end else - text = string.format('"%s"', item.node:type():gsub('\n', '\\n'):gsub('"', '\\"')) + text = string.format('%q', item.node:type()):gsub('\n', 'n') end local next = self:get(i + 1) diff --git a/runtime/lua/vim/treesitter/health.lua b/runtime/lua/vim/treesitter/health.lua index a9b066d158..ed3616ef46 100644 --- a/runtime/lua/vim/treesitter/health.lua +++ b/runtime/lua/vim/treesitter/health.lua @@ -24,7 +24,7 @@ function M.check() else local lang = ts.language.inspect(parsername) health.ok( - string.format('Parser: %-10s ABI: %d, path: %s', parsername, lang._abi_version, parser) + string.format('Parser: %-20s ABI: %d, path: %s', parsername, lang._abi_version, parser) ) end end diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 388680259a..d2f986b874 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -4,7 +4,7 @@ local Range = require('vim.treesitter._range') local ns = api.nvim_create_namespace('treesitter/highlighter') ----@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata +---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch ---@class (private) vim.treesitter.highlighter.Query ---@field private _query vim.treesitter.Query? @@ -215,7 +215,7 @@ end ---@param start_row integer ---@param new_end integer function TSHighlighter:on_bytes(_, _, start_row, _, _, _, _, _, new_end) - api.nvim__buf_redraw_range(self.bufnr, start_row, start_row + new_end + 1) + api.nvim__redraw({ buf = self.bufnr, range = { start_row, start_row + new_end + 1 } }) end ---@package @@ -227,7 +227,7 @@ end ---@param changes Range6[] function TSHighlighter:on_changedtree(changes) for _, ch in ipairs(changes) do - api.nvim__buf_redraw_range(self.bufnr, ch[1], ch[4] + 1) + api.nvim__redraw({ buf = self.bufnr, range = { ch[1], ch[4] + 1 } }) end end @@ -243,6 +243,46 @@ function TSHighlighter:get_query(lang) return self._queries[lang] end +--- @param match TSQueryMatch +--- @param bufnr integer +--- @param capture integer +--- @param metadata vim.treesitter.query.TSMetadata +--- @return string? +local function get_url(match, bufnr, capture, metadata) + ---@type string|number|nil + local url = metadata[capture] and metadata[capture].url + + if not url or type(url) == 'string' then + return url + end + + local captures = match:captures() + + if not captures[url] then + return + end + + -- Assume there is only one matching node. If there is more than one, take the URL + -- from the first. + local other_node = captures[url][1] + + return vim.treesitter.get_node_text(other_node, bufnr, { + metadata = metadata[url], + }) +end + +--- @param capture_name string +--- @return boolean?, integer +local function get_spell(capture_name) + if capture_name == 'spell' then + return true, 0 + elseif capture_name == 'nospell' then + -- Give nospell a higher priority so it always overrides spell captures. + return false, 1 + end + return nil, 0 +end + ---@param self vim.treesitter.highlighter ---@param buf integer ---@param line integer @@ -258,12 +298,16 @@ local function on_line_impl(self, buf, line, is_spell_nav) end if state.iter == nil or state.next_row < line then + -- Mainly used to skip over folds + + -- TODO(lewis6991): Creating a new iterator loses the cached predicate results for query + -- matches. Move this logic inside iter_captures() so we can maintain the cache. state.iter = state.highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1) end while line >= state.next_row do - local capture, node, metadata = state.iter(line) + local capture, node, metadata, match = state.iter(line) local range = { root_end_row + 1, 0, root_end_row + 1, 0 } if node then @@ -275,27 +319,30 @@ local function on_line_impl(self, buf, line, is_spell_nav) local hl = state.highlighter_query:get_hl_from_capture(capture) local capture_name = state.highlighter_query:query().captures[capture] - local spell = nil ---@type boolean? - if capture_name == 'spell' then - spell = true - elseif capture_name == 'nospell' then - spell = false - end - -- Give nospell a higher priority so it always overrides spell captures. - local spell_pri_offset = capture_name == 'nospell' and 1 or 0 + local spell, spell_pri_offset = get_spell(capture_name) + + -- The "priority" attribute can be set at the pattern level or on a particular capture + local priority = ( + tonumber(metadata.priority or metadata[capture] and metadata[capture].priority) + or vim.highlight.priorities.treesitter + ) + spell_pri_offset + + -- The "conceal" attribute can be set at the pattern level or on a particular capture + local conceal = metadata.conceal or metadata[capture] and metadata[capture].conceal + + local url = get_url(match, buf, capture, metadata) if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then - local priority = (tonumber(metadata.priority) or vim.highlight.priorities.treesitter) - + spell_pri_offset api.nvim_buf_set_extmark(buf, ns, start_row, start_col, { end_line = end_row, end_col = end_col, hl_group = hl, ephemeral = true, priority = priority, - conceal = metadata.conceal, + conceal = conceal, spell = spell, + url = url, }) end end diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index 47abf65332..d0a74daa6c 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -88,6 +88,9 @@ function M.add(lang, opts) filetype = { filetype, { 'string', 'table' }, true }, }) + -- parser names are assumed to be lowercase (consistent behavior on case-insensitive file systems) + lang = lang:lower() + if vim._ts_has_language(lang) then M.register(lang, filetype) return diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 62714d3f1b..b0812123b9 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -81,7 +81,7 @@ local TSCallbackNames = { ---List of regions this tree should manage and parse. If nil then regions are ---taken from _trees. This is mostly a short-lived cache for included_regions() ---@field private _lang string Language name ----@field private _parent_lang? string Parent language name +---@field private _parent? vim.treesitter.LanguageTree Parent LanguageTree ---@field private _source (integer|string) Buffer or string to parse ---@field private _trees table<integer, TSTree> Reference to parsed tree (one for each language). ---Each key is the index of region, which is synced with _regions and _valid. @@ -106,9 +106,8 @@ LanguageTree.__index = LanguageTree ---@param source (integer|string) Buffer or text string to parse ---@param lang string Root language of this tree ---@param opts vim.treesitter.LanguageTree.new.Opts? ----@param parent_lang? string Parent language name of this tree ---@return vim.treesitter.LanguageTree parser object -function LanguageTree.new(source, lang, opts, parent_lang) +function LanguageTree.new(source, lang, opts) language.add(lang) opts = opts or {} @@ -122,7 +121,6 @@ function LanguageTree.new(source, lang, opts, parent_lang) local self = { _source = source, _lang = lang, - _parent_lang = parent_lang, _children = {}, _trees = {}, _opts = opts, @@ -158,8 +156,10 @@ function LanguageTree:_set_logger() local lang = self:lang() - vim.fn.mkdir(vim.fn.stdpath('log'), 'p') - local logfilename = vim.fs.joinpath(vim.fn.stdpath('log'), 'treesitter.log') + local logdir = vim.fn.stdpath('log') --[[@as string]] + + vim.fn.mkdir(logdir, 'p') + local logfilename = vim.fs.joinpath(logdir, 'treesitter.log') local logfile, openerr = io.open(logfilename, 'a+') @@ -225,7 +225,10 @@ function LanguageTree:_log(...) self._logger('nvim', table.concat(msg, ' ')) end ---- Invalidates this parser and all its children +--- Invalidates this parser and its children. +--- +--- Should only be called when the tracked state of the LanguageTree is not valid against the parse +--- tree in treesitter. Doesn't clear filesystem cache. Called often, so needs to be fast. ---@param reload boolean|nil function LanguageTree:invalidate(reload) self._valid = false @@ -460,24 +463,6 @@ function LanguageTree:parse(range) return self._trees end ----@deprecated Misleading name. Use `LanguageTree:children()` (non-recursive) instead, ---- add recursion yourself if needed. ---- Invokes the callback for each |LanguageTree| and its children recursively ---- ----@param fn fun(tree: vim.treesitter.LanguageTree, lang: string) ----@param include_self boolean|nil Whether to include the invoking tree in the results -function LanguageTree:for_each_child(fn, include_self) - vim.deprecate('LanguageTree:for_each_child()', 'LanguageTree:children()', '0.11') - if include_self then - fn(self, self._lang) - end - - for _, child in pairs(self._children) do - --- @diagnostic disable-next-line:deprecated - child:for_each_child(fn, true) - end -end - --- Invokes the callback for each |LanguageTree| recursively. --- --- Note: This includes the invoking tree's child trees as well. @@ -505,19 +490,25 @@ function LanguageTree:add_child(lang) self:remove_child(lang) end - local child = LanguageTree.new(self._source, lang, self._opts, self:lang()) + local child = LanguageTree.new(self._source, lang, self._opts) -- Inherit recursive callbacks for nm, cb in pairs(self._callbacks_rec) do vim.list_extend(child._callbacks_rec[nm], cb) end + child._parent = self self._children[lang] = child self:_do_callback('child_added', self._children[lang]) return self._children[lang] end +--- @package +function LanguageTree:parent() + return self._parent +end + --- Removes a child language from this |LanguageTree|. --- ---@private @@ -752,7 +743,6 @@ local has_parser = vim.func._memoize(1, function(lang) end) --- Return parser name for language (if exists) or filetype (if registered and exists). ---- Also attempts with the input lower-cased. --- ---@param alias string language or filetype name ---@return string? # resolved parser name @@ -766,19 +756,10 @@ local function resolve_lang(alias) return alias end - if has_parser(alias:lower()) then - return alias:lower() - end - local lang = vim.treesitter.language.get_lang(alias) if lang and has_parser(lang) then return lang end - - lang = vim.treesitter.language.get_lang(alias:lower()) - if lang and has_parser(lang) then - return lang - end end ---@private @@ -792,7 +773,7 @@ function LanguageTree:_get_injection(match, metadata) local combined = metadata['injection.combined'] ~= nil local injection_lang = metadata['injection.language'] --[[@as string?]] local lang = metadata['injection.self'] ~= nil and self:lang() - or metadata['injection.parent'] ~= nil and self._parent_lang + or metadata['injection.parent'] ~= nil and self._parent:lang() or (injection_lang and resolve_lang(injection_lang)) local include_children = metadata['injection.include-children'] ~= nil @@ -802,7 +783,11 @@ function LanguageTree:_get_injection(match, metadata) -- Lang should override any other language tag if name == 'injection.language' then local text = vim.treesitter.get_node_text(node, self._source, { metadata = metadata[id] }) - lang = resolve_lang(text) + lang = resolve_lang(text:lower()) -- language names are always lower case + elseif name == 'injection.filename' then + local text = vim.treesitter.get_node_text(node, self._source, { metadata = metadata[id] }) + local ft = vim.filetype.match({ filename = text }) + lang = ft and resolve_lang(ft) elseif name == 'injection.content' then ranges = get_node_ranges(node, self._source, metadata[id], include_children) end @@ -1054,20 +1039,19 @@ function LanguageTree:_on_detach(...) end end ---- Registers callbacks for the |LanguageTree|. ----@param cbs table An |nvim_buf_attach()|-like table argument with the following handlers: ---- - `on_bytes` : see |nvim_buf_attach()|, but this will be called _after_ the parsers callback. +--- Registers callbacks for the [LanguageTree]. +---@param cbs table<TSCallbackNameOn,function> An [nvim_buf_attach()]-like table argument with the following handlers: +--- - `on_bytes` : see [nvim_buf_attach()], but this will be called _after_ the parsers callback. --- - `on_changedtree` : a callback that will be called every time the tree has syntactical changes. --- It will be passed two arguments: a table of the ranges (as node ranges) that --- changed and the changed tree. --- - `on_child_added` : emitted when a child is added to the tree. --- - `on_child_removed` : emitted when a child is removed from the tree. ---- - `on_detach` : emitted when the buffer is detached, see |nvim_buf_detach_event|. +--- - `on_detach` : emitted when the buffer is detached, see [nvim_buf_detach_event]. --- Takes one argument, the number of the buffer. --- @param recursive? boolean Apply callbacks recursively for all children. Any new children will --- also inherit the callbacks. function LanguageTree:register_cbs(cbs, recursive) - ---@cast cbs table<TSCallbackNameOn,function> if not cbs then return end @@ -1091,7 +1075,14 @@ end ---@param range Range ---@return boolean local function tree_contains(tree, range) - return Range.contains({ tree:root():range() }, range) + local tree_ranges = tree:included_ranges(false) + + return Range.contains({ + tree_ranges[1][1], + tree_ranges[1][2], + tree_ranges[#tree_ranges][3], + tree_ranges[#tree_ranges][4], + }, range) end --- Determines whether {range} is contained in the |LanguageTree|. @@ -1108,12 +1099,18 @@ function LanguageTree:contains(range) return false end +--- @class vim.treesitter.LanguageTree.tree_for_range.Opts +--- @inlinedoc +--- +--- Ignore injected languages +--- (default: `true`) +--- @field ignore_injections? boolean + --- Gets the tree that contains {range}. --- ---@param range Range4 `{ start_line, start_col, end_line, end_col }` ----@param opts table|nil Optional keyword arguments: ---- - ignore_injections boolean Ignore injected languages (default true) ----@return TSTree|nil +---@param opts? vim.treesitter.LanguageTree.tree_for_range.Opts +---@return TSTree? function LanguageTree:tree_for_range(range, opts) opts = opts or {} local ignore = vim.F.if_nil(opts.ignore_injections, true) @@ -1139,9 +1136,8 @@ end --- Gets the smallest named node that contains {range}. --- ---@param range Range4 `{ start_line, start_col, end_line, end_col }` ----@param opts table|nil Optional keyword arguments: ---- - ignore_injections boolean Ignore injected languages (default true) ----@return TSNode | nil Found node +---@param opts? vim.treesitter.LanguageTree.tree_for_range.Opts +---@return TSNode? function LanguageTree:named_node_for_range(range, opts) local tree = self:tree_for_range(range, opts) if tree then @@ -1152,7 +1148,7 @@ end --- Gets the appropriate language that contains {range}. --- ---@param range Range4 `{ start_line, start_col, end_line, end_col }` ----@return vim.treesitter.LanguageTree Managing {range} +---@return vim.treesitter.LanguageTree tree Managing {range} function LanguageTree:language_for_range(range) for _, child in pairs(self._children) do if child:contains(range) then diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index a086f5e876..ef5c2143a7 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -1,5 +1,6 @@ local api = vim.api local language = require('vim.treesitter.language') +local memoize = vim.func._memoize local M = {} @@ -88,7 +89,7 @@ end --- ---@param lang string Language to get query for ---@param query_name string Name of the query to load (e.g., "highlights") ----@param is_included (boolean|nil) Internal parameter, most of the time left as `nil` +---@param is_included? boolean Internal parameter, most of the time left as `nil` ---@return string[] query_files List of files to load for given query and language function M.get_files(lang, query_name, is_included) local query_path = string.format('queries/%s/%s.scm', lang, query_name) @@ -211,8 +212,8 @@ end ---@param lang string Language to use for the query ---@param query_name string Name of the query (e.g. "highlights") --- ----@return vim.treesitter.Query|nil : Parsed query. `nil` if no query files are found. -M.get = vim.func._memoize('concat-2', function(lang, query_name) +---@return vim.treesitter.Query? : Parsed query. `nil` if no query files are found. +M.get = memoize('concat-2', function(lang, query_name) if explicit_queries[lang][query_name] then return explicit_queries[lang][query_name] end @@ -242,10 +243,10 @@ end) ---@param lang string Language to use for the query ---@param query string Query in s-expr syntax --- ----@return vim.treesitter.Query Parsed query +---@return vim.treesitter.Query : Parsed query --- ----@see |vim.treesitter.query.get()| -M.parse = vim.func._memoize('concat-2', function(lang, query) +---@see [vim.treesitter.query.get()] +M.parse = memoize('concat-2', function(lang, query) language.add(lang) local ts_query = vim._ts_parse_query(lang, query) @@ -258,7 +259,7 @@ end) --- handling the "any" vs "all" semantics. They are called from the --- predicate_handlers table with the appropriate arguments for each predicate. local impl = { - --- @param match vim.treesitter.query.TSMatch + --- @param match table<integer,TSNode[]> --- @param source integer|string --- @param predicate any[] --- @param any boolean @@ -293,7 +294,7 @@ local impl = { return not any end, - --- @param match vim.treesitter.query.TSMatch + --- @param match table<integer,TSNode[]> --- @param source integer|string --- @param predicate any[] --- @param any boolean @@ -333,7 +334,7 @@ local impl = { end, }) - --- @param match vim.treesitter.query.TSMatch + --- @param match table<integer,TSNode[]> --- @param source integer|string --- @param predicate any[] --- @param any boolean @@ -356,7 +357,7 @@ local impl = { end end)(), - --- @param match vim.treesitter.query.TSMatch + --- @param match table<integer,TSNode[]> --- @param source integer|string --- @param predicate any[] --- @param any boolean @@ -383,13 +384,7 @@ local impl = { end, } ----@nodoc ----@class vim.treesitter.query.TSMatch ----@field pattern? integer ----@field active? boolean ----@field [integer] TSNode[] - ----@alias TSPredicate fun(match: vim.treesitter.query.TSMatch, pattern: integer, source: integer|string, predicate: any[]): boolean +---@alias TSPredicate fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[]): boolean -- Predicate handler receive the following arguments -- (match, pattern, bufnr, predicate) @@ -462,17 +457,8 @@ local predicate_handlers = { end for _, node in ipairs(nodes) do - local ancestor_types = {} --- @type table<string, boolean> - for _, type in ipairs({ unpack(predicate, 3) }) do - ancestor_types[type] = true - end - - local cur = node:parent() - while cur do - if ancestor_types[cur:type()] then - return true - end - cur = cur:parent() + if node:__has_ancestor(predicate) then + return true end end return false @@ -504,7 +490,7 @@ predicate_handlers['any-vim-match?'] = predicate_handlers['any-match?'] ---@field [integer] vim.treesitter.query.TSMetadata ---@field [string] integer|string ----@alias TSDirective fun(match: vim.treesitter.query.TSMatch, _, _, predicate: (string|integer)[], metadata: vim.treesitter.query.TSMetadata) +---@alias TSDirective fun(match: table<integer,TSNode[]>, _, _, predicate: (string|integer)[], metadata: vim.treesitter.query.TSMetadata) -- Predicate handler receive the following arguments -- (match, pattern, bufnr, predicate) @@ -534,6 +520,9 @@ local directive_handlers = { ['offset!'] = function(match, _, _, pred, metadata) local capture_id = pred[2] --[[@as integer]] local nodes = match[capture_id] + if not nodes or #nodes == 0 then + return + end assert(#nodes == 1, '#offset! does not support captures on multiple nodes') local node = nodes[1] @@ -567,6 +556,9 @@ local directive_handlers = { assert(type(id) == 'number') local nodes = match[id] + if not nodes or #nodes == 0 then + return + end assert(#nodes == 1, '#gsub! does not support captures on multiple nodes') local node = nodes[1] local text = vim.treesitter.get_node_text(node, bufnr, { metadata = metadata[id] }) or '' @@ -589,6 +581,9 @@ local directive_handlers = { assert(type(capture_id) == 'number') local nodes = match[capture_id] + if not nodes or #nodes == 0 then + return + end assert(#nodes == 1, '#trim! does not support captures on multiple nodes') local node = nodes[1] @@ -618,20 +613,23 @@ local directive_handlers = { end, } +--- @class vim.treesitter.query.add_predicate.Opts +--- @inlinedoc +--- +--- Override an existing predicate of the same name +--- @field force? boolean +--- +--- Use the correct implementation of the match table where capture IDs map to +--- a list of nodes instead of a single node. Defaults to false (for backward +--- compatibility). This option will eventually become the default and removed. +--- @field all? boolean + --- Adds a new predicate to be used in queries --- ---@param name string Name of the predicate, without leading # ----@param handler function(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: table) +---@param handler fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: table) --- - see |vim.treesitter.query.add_directive()| for argument meanings ----@param opts table<string, any> Optional options: ---- - force (boolean): Override an existing ---- predicate of the same name ---- - all (boolean): Use the correct ---- implementation of the match table where ---- capture IDs map to a list of nodes instead ---- of a single node. Defaults to false (for ---- backward compatibility). This option will ---- eventually become the default and removed. +---@param opts vim.treesitter.query.add_predicate.Opts function M.add_predicate(name, handler, opts) -- Backward compatibility: old signature had "force" as boolean argument if type(opts) == 'boolean' then @@ -669,20 +667,12 @@ end --- metadata table `metadata[capture_id].key = value` --- ---@param name string Name of the directive, without leading # ----@param handler function(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: table) +---@param handler fun(match: table<integer,TSNode[]>, pattern: integer, source: integer|string, predicate: any[], metadata: table) --- - match: A table mapping capture IDs to a list of captured nodes --- - pattern: the index of the matching pattern in the query file --- - predicate: list of strings containing the full directive being called, e.g. --- `(node (#set! conceal "-"))` would get the predicate `{ "#set!", "conceal", "-" }` ----@param opts table<string, any> Optional options: ---- - force (boolean): Override an existing ---- predicate of the same name ---- - all (boolean): Use the correct ---- implementation of the match table where ---- capture IDs map to a list of nodes instead ---- of a single node. Defaults to false (for ---- backward compatibility). This option will ---- eventually become the default and removed. +---@param opts vim.treesitter.query.add_predicate.Opts function M.add_directive(name, handler, opts) -- Backward compatibility: old signature had "force" as boolean argument if type(opts) == 'boolean' then @@ -711,13 +701,13 @@ function M.add_directive(name, handler, opts) end --- Lists the currently available directives to use in queries. ----@return string[] List of supported directives. +---@return string[] : Supported directives. function M.list_directives() return vim.tbl_keys(directive_handlers) end --- Lists the currently available predicates to use in queries. ----@return string[] List of supported predicates. +---@return string[] : Supported predicates. function M.list_predicates() return vim.tbl_keys(predicate_handlers) end @@ -731,13 +721,19 @@ local function is_directive(name) end ---@private ----@param match vim.treesitter.query.TSMatch ----@param pattern integer +---@param match TSQueryMatch ---@param source integer|string -function Query:match_preds(match, pattern, source) +function Query:match_preds(match, source) + local _, pattern = match:info() local preds = self.info.patterns[pattern] - for _, pred in pairs(preds or {}) do + if not preds then + return true + end + + local captures = match:captures() + + for _, pred in pairs(preds) do -- Here we only want to return if a predicate DOES NOT match, and -- continue on the other case. This way unknown predicates will not be considered, -- which allows some testing and easier user extensibility (#12173). @@ -759,7 +755,7 @@ function Query:match_preds(match, pattern, source) return false end - local pred_matches = handler(match, pattern, source, pred) + local pred_matches = handler(captures, pattern, source, pred) if not xor(is_not, pred_matches) then return false @@ -770,30 +766,40 @@ function Query:match_preds(match, pattern, source) end ---@private ----@param match vim.treesitter.query.TSMatch ----@param metadata vim.treesitter.query.TSMetadata -function Query:apply_directives(match, pattern, source, metadata) +---@param match TSQueryMatch +---@return vim.treesitter.query.TSMetadata metadata +function Query:apply_directives(match, source) + ---@type vim.treesitter.query.TSMetadata + local metadata = {} + local _, pattern = match:info() local preds = self.info.patterns[pattern] - for _, pred in pairs(preds or {}) do + if not preds then + return metadata + end + + local captures = match:captures() + + for _, pred in pairs(preds) 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) + handler(captures, pattern, source, pred, metadata) end end + + return metadata end --- Returns the start and stop value if set else the node's range. -- When the node's range is used, the stop is incremented by 1 -- to make the search inclusive. ----@param start integer|nil ----@param stop integer|nil +---@param start integer? +---@param stop integer? ---@param node TSNode ---@return integer, integer local function value_or_node_range(start, stop, node) @@ -807,6 +813,12 @@ local function value_or_node_range(start, stop, node) return start, stop end +--- @param match TSQueryMatch +--- @return integer +local function match_id_hash(_, match) + return (match:info()) +end + --- Iterate over all captures from all matches inside {node} --- --- {source} is needed if the query contains predicates; then the caller @@ -816,12 +828,13 @@ end --- as the {node}, i.e., to get syntax highlight matches in the current --- viewport). When omitted, the {start} and {stop} row values are used from the given node. --- ---- The iterator returns three values: a numeric id identifying the capture, ---- the captured node, and metadata from any directives processing the match. +--- The iterator returns four values: a numeric id identifying the capture, +--- the captured node, metadata from any directives processing the match, +--- and the match itself. --- The following example shows how to get captures by name: --- --- ```lua ---- for id, node, metadata in query:iter_captures(tree:root(), bufnr, first, last) do +--- for id, node, metadata, match 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 @@ -835,8 +848,10 @@ end ---@param start? integer Starting line for the search. Defaults to `node:start()`. ---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`. --- ----@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata): ---- capture id, capture node, metadata +---@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch): +--- capture id, capture node, metadata, match +--- +---@note Captures are only returned if the query pattern of a specific capture contained predicates. function Query:iter_captures(node, source, start, stop) if type(source) == 'number' and source == 0 then source = api.nvim_get_current_buf() @@ -844,24 +859,30 @@ function Query:iter_captures(node, source, start, stop) start, stop = value_or_node_range(start, stop, node) - local raw_iter = node:_rawquery(self.query, true, start, stop) ---@type fun(): integer, TSNode, vim.treesitter.query.TSMatch + local cursor = vim._create_ts_querycursor(node, self.query, start, stop, { match_limit = 256 }) + + local apply_directives = memoize(match_id_hash, self.apply_directives, true) + local match_preds = memoize(match_id_hash, self.match_preds, true) + local function iter(end_line) - local capture, captured_node, match = raw_iter() - local metadata = {} - - if match ~= nil then - local active = self:match_preds(match, match.pattern, source) - match.active = active - if not active then - if end_line and captured_node:range() > end_line then - return nil, captured_node, nil - end - return iter(end_line) -- tail call: try next match - end + local capture, captured_node, match = cursor:next_capture() - self:apply_directives(match, match.pattern, source, metadata) + if not capture then + return end - return capture, captured_node, metadata + + if not match_preds(self, match, source) then + local match_id = match:info() + cursor:remove_match(match_id) + if end_line and captured_node:range() > end_line then + return nil, captured_node, nil, nil + end + return iter(end_line) -- tail call: try next match + end + + local metadata = apply_directives(self, match, source) + + return capture, captured_node, metadata, match end return iter end @@ -903,45 +924,55 @@ end ---@param opts? table Optional keyword arguments: --- - max_start_depth (integer) if non-zero, sets the maximum start depth --- for each match. This is used to prevent traversing too deep into a tree. +--- - match_limit (integer) Set the maximum number of in-progress matches (Default: 256). --- - all (boolean) When set, the returned match table maps capture IDs to a list of nodes. --- Older versions of iter_matches incorrectly mapped capture IDs to a single node, which is --- incorrect behavior. This option will eventually become the default and removed. --- ---@return (fun(): integer, table<integer, TSNode[]>, table): pattern id, match, metadata function Query:iter_matches(node, source, start, stop, opts) - local all = opts and opts.all + opts = opts or {} + opts.match_limit = opts.match_limit or 256 + if type(source) == 'number' and source == 0 then source = api.nvim_get_current_buf() end start, stop = value_or_node_range(start, stop, node) - local raw_iter = node:_rawquery(self.query, false, start, stop, opts) ---@type fun(): integer, vim.treesitter.query.TSMatch + local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts) + local function iter() - local pattern, match = raw_iter() - local metadata = {} + local match = cursor:next_match() - if match ~= nil then - local active = self:match_preds(match, pattern, source) - if not active then - return iter() -- tail call: try next match - end + if not match then + return + end - self:apply_directives(match, pattern, source, metadata) + local match_id, pattern = match:info() + + if not self:match_preds(match, source) then + cursor:remove_match(match_id) + return iter() -- tail call: try next match end - if not all then + local metadata = self:apply_directives(match, source) + + local captures = match:captures() + + if not opts.all then -- Convert the match table into the old buggy version for backward -- compatibility. This is slow. Plugin authors, if you're reading this, set the "all" -- option! local old_match = {} ---@type table<integer, TSNode> - for k, v in pairs(match or {}) do + for k, v in pairs(captures or {}) do old_match[k] = v[#v] end return pattern, old_match, metadata end - return pattern, match, metadata + -- TODO(lewis6991): create a new function that returns {match, metadata} + return pattern, captures, metadata end return iter end diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua index b0e7ca1a35..99b9b78e2a 100644 --- a/runtime/lua/vim/ui.lua +++ b/runtime/lua/vim/ui.lua @@ -114,15 +114,20 @@ end --- Examples: --- --- ```lua +--- -- Asynchronous. --- vim.ui.open("https://neovim.io/") --- vim.ui.open("~/path/to/file") ---- vim.ui.open("$VIMRUNTIME") +--- -- Synchronous (wait until the process exits). +--- local cmd, err = vim.ui.open("$VIMRUNTIME") +--- if cmd then +--- cmd:wait() +--- end --- ``` --- ---@param path string Path or URL to open --- ----@return vim.SystemCompleted|nil # Command result, or nil if not found. ----@return string|nil # Error message on failure +---@return vim.SystemObj|nil # Command object, or nil if not found. +---@return nil|string # Error message on failure, or nil on success. --- ---@see |vim.system()| function M.open(path) @@ -144,21 +149,37 @@ function M.open(path) else return nil, 'vim.ui.open: rundll32 not found' end + elseif vim.fn.executable('wslview') == 1 then + cmd = { 'wslview', path } elseif vim.fn.executable('explorer.exe') == 1 then cmd = { 'explorer.exe', path } elseif vim.fn.executable('xdg-open') == 1 then cmd = { 'xdg-open', path } else - return nil, 'vim.ui.open: no handler found (tried: explorer.exe, xdg-open)' + return nil, 'vim.ui.open: no handler found (tried: wslview, explorer.exe, xdg-open)' end - local rv = vim.system(cmd, { text = true, detach = true }):wait() - if rv.code ~= 0 then - local msg = ('vim.ui.open: command failed (%d): %s'):format(rv.code, vim.inspect(cmd)) - return rv, msg - end + return vim.system(cmd, { text = true, detach = true }), nil +end - return rv, nil +--- Gets the URL at cursor, if any. +function M._get_url() + if vim.bo.filetype == 'markdown' then + local range = vim.api.nvim_win_get_cursor(0) + vim.treesitter.get_parser():parse(range) + -- marking the node as `markdown_inline` is required. Setting it to `markdown` does not + -- work. + local current_node = vim.treesitter.get_node { lang = 'markdown_inline' } + while current_node do + local type = current_node:type() + if type == 'inline_link' or type == 'image' then + local child = assert(current_node:named_child(1)) + return vim.treesitter.get_node_text(child, 0) + end + current_node = current_node:parent() + end + end + return vim.fn.expand('<cfile>') end return M diff --git a/runtime/mswin.vim b/runtime/mswin.vim index 107a2acc2e..689bc792cf 100644 --- a/runtime/mswin.vim +++ b/runtime/mswin.vim @@ -1,7 +1,7 @@ " Set options and add mapping such that Vim behaves a lot like MS-Windows " " Maintainer: The Vim Project <https://github.com/vim/vim> -" Last Change: 2024 Mar 3 +" Last Change: 2024 Mar 13 " Former Maintainer: Bram Moolenaar <Bram@vim.org> " Bail out if this isn't wanted. @@ -27,7 +27,10 @@ set backspace=indent,eol,start whichwrap+=<,>,[,] " backspace in Visual mode deletes selection vnoremap <BS> d -if has("clipboard_working") +" the better solution would be to use has("clipboard_working"), +" but that may not be available yet while starting up, so let's just check if +" clipboard support has been compiled in and assume it will be working :/ +if has("clipboard") " CTRL-X and SHIFT-Del are Cut vnoremap <C-X> "+x vnoremap <S-Del> "+x @@ -43,7 +46,7 @@ if has("clipboard_working") cmap <C-V> <C-R>+ cmap <S-Insert> <C-R>+ else - " Use unnamed register while clipboard not exist + " Use the unnamed register when clipboard support not available " CTRL-X and SHIFT-Del are Cut vnoremap <C-X> x @@ -67,7 +70,7 @@ endif " Uses the paste.vim autoload script. " Use CTRL-G u to have CTRL-Z only undo the paste. -if has("clipboard_working") +if has("clipboard") exe 'inoremap <script> <C-V> <C-G>u' . paste#paste_cmd['i'] exe 'vnoremap <script> <C-V> ' . paste#paste_cmd['v'] endif diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index bff8bc4bff..517b8b20fe 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,11 @@ </screenshots> <releases> + <release date="2024-05-16" version="0.10.0"/> + <release date="2023-12-30" version="0.9.5"/> + <release date="2023-10-09" version="0.9.4"/> + <release date="2023-09-07" version="0.9.2"/> + <release date="2023-05-29" version="0.9.1"/> <release date="2023-04-07" version="0.9.0"/> <release date="2023-02-02" version="0.8.3"/> <release date="2022-12-29" version="0.8.2"/> diff --git a/runtime/optwin.vim b/runtime/optwin.vim index fc60f70335..5b5b33e4ad 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -444,6 +444,7 @@ if has("statusline") call <SID>AddOption("statusline", gettext("alternate format to be used for a status line")) call <SID>OptionG("stl", &stl) endif +call append("$", "\t" .. s:local_to_window) call <SID>AddOption("equalalways", gettext("make all windows the same size when adding/removing windows")) call <SID>BinOptionG("ea", &ea) call <SID>AddOption("eadirection", gettext("in which direction 'equalalways' works: \"ver\", \"hor\" or \"both\"")) @@ -452,6 +453,8 @@ call <SID>AddOption("winheight", gettext("minimal number of lines used for the c call append("$", " \tset wh=" . &wh) call <SID>AddOption("winminheight", gettext("minimal number of lines used for any window")) call append("$", " \tset wmh=" . &wmh) +call <SID>AddOption("winfixbuf", gettext("keep window focused on a single buffer")) +call <SID>OptionG("wfb", &wfb) call <SID>AddOption("winfixheight", gettext("keep the height of the window")) call append("$", "\t" .. s:local_to_window) call <SID>BinOptionL("wfh") diff --git a/runtime/pack/dist/opt/matchit/autoload/matchit.vim b/runtime/pack/dist/opt/matchit/autoload/matchit.vim index dc2aba696d..aa977488e5 100644 --- a/runtime/pack/dist/opt/matchit/autoload/matchit.vim +++ b/runtime/pack/dist/opt/matchit/autoload/matchit.vim @@ -1,6 +1,6 @@ " matchit.vim: (global plugin) Extended "%" matching " autload script of matchit plugin, see ../plugin/matchit.vim -" Last Change: Jan 24, 2022 +" Last Change: May 20, 2024 " Neovim does not support scriptversion if has("vimscript-4") @@ -87,9 +87,9 @@ function matchit#Match_wrapper(word, forward, mode) range let s:last_mps = &mps " quote the special chars in 'matchpairs', replace [,:] with \| and then " append the builtin pairs (/*, */, #if, #ifdef, #ifndef, #else, #elif, - " #endif) + " #elifdef, #elifndef, #endif) let default = escape(&mps, '[$^.*~\\/?]') .. (strlen(&mps) ? "," : "") .. - \ '\/\*:\*\/,#\s*if\%(n\=def\)\=:#\s*else\>:#\s*elif\>:#\s*endif\>' + \ '\/\*:\*\/,#\s*if\%(n\=def\)\=:#\s*else\>:#\s*elif\%(n\=def\)\=\>:#\s*endif\>' " s:all = pattern with all the keywords let match_words = match_words .. (strlen(match_words) ? "," : "") .. default let s:last_words = match_words @@ -101,6 +101,8 @@ function matchit#Match_wrapper(word, forward, mode) range let s:pat = s:ParseWords(match_words) endif let s:all = substitute(s:pat, s:notslash .. '\zs[,:]\+', '\\|', 'g') + " un-escape \, to , + let s:all = substitute(s:all, '\\,', ',', 'g') " Just in case there are too many '\(...)' groups inside the pattern, make " sure to use \%(...) groups, so that error E872 can be avoided let s:all = substitute(s:all, '\\(', '\\%(', 'g') @@ -112,6 +114,8 @@ function matchit#Match_wrapper(word, forward, mode) range let s:patBR = substitute(match_words .. ',', \ s:notslash .. '\zs[,:]*,[,:]*', ',', 'g') let s:patBR = substitute(s:patBR, s:notslash .. '\zs:\{2,}', ':', 'g') + " un-escape \, to , + let s:patBR = substitute(s:patBR, '\\,', ',', 'g') endif " Second step: set the following local variables: @@ -534,6 +538,8 @@ fun! s:Choose(patterns, string, comma, branch, prefix, suffix, ...) else let currpat = substitute(current, s:notslash .. a:branch, '\\|', 'g') endif + " un-escape \, to , + let currpat = substitute(currpat, '\\,', ',', 'g') while a:string !~ a:prefix .. currpat .. a:suffix let tail = strpart(tail, i) let i = matchend(tail, s:notslash .. a:comma) diff --git a/runtime/pack/dist/opt/matchit/doc/matchit.txt b/runtime/pack/dist/opt/matchit/doc/matchit.txt index 88d0c38e4d..8d56df6ddc 100644 --- a/runtime/pack/dist/opt/matchit/doc/matchit.txt +++ b/runtime/pack/dist/opt/matchit/doc/matchit.txt @@ -4,7 +4,7 @@ For instructions on installing this file, type `:help matchit-install` inside Vim. -For Vim version 9.0. Last change: 2023 June 28 +For Vim version 9.1. Last change: 2024 May 20 VIM REFERENCE MANUAL by Benji Fisher et al @@ -176,8 +176,8 @@ defined automatically. 2.1 Temporarily disable the matchit plugin *matchit-disable* *:MatchDisable* -To temporarily reset the plugins, that are setup you can run the following -command: > +To temporarily disable the matchit plugin, after it hat been loaded, +execute this command: > :MatchDisable This will delete all the defined key mappings to the Vim default. diff --git a/runtime/pack/dist/opt/matchit/plugin/matchit.vim b/runtime/pack/dist/opt/matchit/plugin/matchit.vim index d6c735d7b4..08fee09642 100644 --- a/runtime/pack/dist/opt/matchit/plugin/matchit.vim +++ b/runtime/pack/dist/opt/matchit/plugin/matchit.vim @@ -1,7 +1,7 @@ " matchit.vim: (global plugin) Extended "%" matching " Maintainer: Christian Brabandt -" Version: 1.19 -" Last Change: 2023, June 28th +" Version: 1.20 +" Last Change: 2024 May 20 " Repository: https://github.com/chrisbra/matchit " Previous URL:http://www.vim.org/script.php?script_id=39 " Previous Maintainer: Benji Fisher PhD <benji@member.AMS.org> diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index a253acc63f..f78a082cb7 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -427,7 +427,17 @@ func s:StartDebug_prompt(dict) let s:promptbuf = bufnr('') call prompt_setprompt(s:promptbuf, 'gdb> ') set buftype=prompt - file gdb + + if empty(glob('gdb')) + file gdb + elseif empty(glob('Termdebug-gdb-console')) + file Termdebug-gdb-console + else + call s:Echoerr("You have a file/folder named 'gdb' + \ or 'Termdebug-gdb-console'. + \ Please exit and rename them because Termdebug may not work as expected.") + endif + call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) @@ -1481,9 +1491,12 @@ func s:GotoAsmwinOrCreateIt() if s:asmbuf > 0 && bufexists(s:asmbuf) exe 'buffer' . s:asmbuf - else + elseif empty(glob('Termdebug-asm-listing')) silent file Termdebug-asm-listing let s:asmbuf = bufnr('Termdebug-asm-listing') + else + call s:Echoerr("You have a file/folder named 'Termdebug-asm-listing'. + \ Please exit and rename it because Termdebug may not work as expected.") endif if mdf != 'vert' && s:GetDisasmWindowHeight() > 0 @@ -1550,9 +1563,12 @@ func s:GotoVariableswinOrCreateIt() if s:varbuf > 0 && bufexists(s:varbuf) exe 'buffer' . s:varbuf - else + elseif empty(glob('Termdebug-variables-listing')) silent file Termdebug-variables-listing let s:varbuf = bufnr('Termdebug-variables-listing') + else + call s:Echoerr("You have a file/folder named 'Termdebug-variables-listing'. + \ Please exit and rename it because Termdebug may not work as expected.") endif if mdf != 'vert' && s:GetVariablesWindowHeight() > 0 @@ -1679,7 +1695,7 @@ func s:CreateBreakpoint(id, subid, enabled) endif endif call sign_define('debugBreakpoint' .. nr, - \ #{text: strpart(label, 0, 2), + \ #{text: slice(label, 0, 2), \ texthl: hiName}) endif endfunc diff --git a/runtime/plugin/matchparen.vim b/runtime/plugin/matchparen.vim index 4235a0d39b..6c061c9fb8 100644 --- a/runtime/plugin/matchparen.vim +++ b/runtime/plugin/matchparen.vim @@ -1,6 +1,6 @@ " Vim plugin for showing matching parens " Maintainer: The Vim Project <https://github.com/vim/vim> -" Last Change: 2023 Oct 20 +" Last Change: 2024 May 18 " Former Maintainer: Bram Moolenaar <Bram@vim.org> " Exit quickly when: @@ -22,7 +22,8 @@ let s:has_matchaddpos = exists('*matchaddpos') augroup matchparen " Replace all matchparen autocommands - autocmd! CursorMoved,CursorMovedI,WinEnter,BufWinEnter,WinScrolled * call s:Highlight_Matching_Pair() + autocmd! CursorMoved,CursorMovedI,WinEnter,WinScrolled * call s:Highlight_Matching_Pair() + autocmd! BufWinEnter * autocmd SafeState * ++once call s:Highlight_Matching_Pair() autocmd! WinLeave,BufLeave * call s:Remove_Matches() if exists('##TextChanged') autocmd! TextChanged,TextChangedI * call s:Highlight_Matching_Pair() @@ -217,7 +218,7 @@ command NoMatchParen call s:NoMatchParen() func s:NoMatchParen() let w = winnr() - noau windo silent! call matchdelete(3) + noau windo call s:Remove_Matches() unlet! g:loaded_matchparen exe "noau ". w . "wincmd w" au! matchparen diff --git a/runtime/plugin/netrwPlugin.vim b/runtime/plugin/netrwPlugin.vim index ed6f7dc008..c70e6518ff 100644 --- a/runtime/plugin/netrwPlugin.vim +++ b/runtime/plugin/netrwPlugin.vim @@ -2,6 +2,8 @@ " PLUGIN SECTION " Maintainer: This runtime file is looking for a new maintainer. " Date: Feb 09, 2021 +" Last Change: +" 2024 May 08 by Vim Project: cleanup legacy Win9X checks " Former Maintainer: Charles E Campbell " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim " Copyright: Copyright (C) 1999-2021 Charles E. Campbell {{{1 @@ -35,7 +37,7 @@ augroup FileExplorer au BufLeave * if &ft != "netrw"|let w:netrw_prvfile= expand("%:p")|endif au BufEnter * sil call s:LocalBrowse(expand("<amatch>")) au VimEnter * sil call s:VimEnter(expand("<amatch>")) - if has("win32") || has("win95") || has("win64") || has("win16") + if has("win32") au BufEnter .* sil call s:LocalBrowse(expand("<amatch>")) endif augroup END diff --git a/runtime/plugin/nvim.lua b/runtime/plugin/nvim.lua deleted file mode 100644 index 743d3044b6..0000000000 --- a/runtime/plugin/nvim.lua +++ /dev/null @@ -1,24 +0,0 @@ -vim.api.nvim_create_user_command('Inspect', function(cmd) - if cmd.bang then - vim.print(vim.inspect_pos()) - else - vim.show_pos() - end -end, { desc = 'Inspect highlights and extmarks at the cursor', bang = true }) - -vim.api.nvim_create_user_command('InspectTree', function(cmd) - if cmd.mods ~= '' or cmd.count ~= 0 then - local count = cmd.count ~= 0 and cmd.count or '' - local new = cmd.mods ~= '' and 'new' or 'vnew' - - vim.treesitter.inspect_tree({ - command = ('%s %s%s'):format(cmd.mods, count, new), - }) - else - vim.treesitter.inspect_tree() - end -end, { desc = 'Inspect treesitter language tree for buffer', count = true }) - -vim.api.nvim_create_user_command('EditQuery', function(cmd) - vim.treesitter.query.edit(cmd.fargs[1]) -end, { desc = 'Edit treesitter query', nargs = '?' }) diff --git a/runtime/queries/bash/highlights.scm b/runtime/queries/bash/highlights.scm index b4360ce7e1..feb0e038ea 100644 --- a/runtime/queries/bash/highlights.scm +++ b/runtime/queries/bash/highlights.scm @@ -68,10 +68,6 @@ argument: "$" @string) ; bare dollar (concatenation - [ - (simple_expansion) - (expansion) - ] (word) @string) [ @@ -110,7 +106,15 @@ ; trap -l ((word) @constant.builtin - (#match? @constant.builtin "^SIG(HUP|INT|QUIT|ILL|TRAP|ABRT|BUS|FPE|KILL|USR[12]|SEGV|PIPE|ALRM|TERM|STKFLT|CHLD|CONT|STOP|TSTP|TT(IN|OU)|URG|XCPU|XFSZ|VTALRM|PROF|WINCH|IO|PWR|SYS|RTMIN([+]([1-9]|1[0-5]))?|RTMAX(-([1-9]|1[0-4]))?)$")) + (#any-of? @constant.builtin + "SIGHUP" "SIGINT" "SIGQUIT" "SIGILL" "SIGTRAP" "SIGABRT" "SIGBUS" "SIGFPE" "SIGKILL" "SIGUSR1" + "SIGSEGV" "SIGUSR2" "SIGPIPE" "SIGALRM" "SIGTERM" "SIGSTKFLT" "SIGCHLD" "SIGCONT" "SIGSTOP" + "SIGTSTP" "SIGTTIN" "SIGTTOU" "SIGURG" "SIGXCPU" "SIGXFSZ" "SIGVTALRM" "SIGPROF" "SIGWINCH" + "SIGIO" "SIGPWR" "SIGSYS" "SIGRTMIN" "SIGRTMIN+1" "SIGRTMIN+2" "SIGRTMIN+3" "SIGRTMIN+4" + "SIGRTMIN+5" "SIGRTMIN+6" "SIGRTMIN+7" "SIGRTMIN+8" "SIGRTMIN+9" "SIGRTMIN+10" "SIGRTMIN+11" + "SIGRTMIN+12" "SIGRTMIN+13" "SIGRTMIN+14" "SIGRTMIN+15" "SIGRTMAX-14" "SIGRTMAX-13" + "SIGRTMAX-12" "SIGRTMAX-11" "SIGRTMAX-10" "SIGRTMAX-9" "SIGRTMAX-8" "SIGRTMAX-7" "SIGRTMAX-6" + "SIGRTMAX-5" "SIGRTMAX-4" "SIGRTMAX-3" "SIGRTMAX-2" "SIGRTMAX-1" "SIGRTMAX")) ((word) @boolean (#any-of? @boolean "true" "false")) @@ -120,10 +124,15 @@ (test_operator) @operator (command_substitution - "$(" @punctuation.bracket) + "$(" @punctuation.special + ")" @punctuation.special) (process_substitution - "<(" @punctuation.bracket) + [ + "<(" + ">(" + ] @punctuation.special + ")" @punctuation.special) (arithmetic_expansion [ @@ -156,27 +165,21 @@ (command_name (word) @function.call) -((command_name - (word) @function.builtin) - ; format-ignore +(command_name + (word) @function.builtin (#any-of? @function.builtin - "alias" "bg" "bind" "break" "builtin" "caller" "cd" - "command" "compgen" "complete" "compopt" "continue" - "coproc" "dirs" "disown" "echo" "enable" "eval" - "exec" "exit" "fc" "fg" "getopts" "hash" "help" - "history" "jobs" "kill" "let" "logout" "mapfile" - "popd" "printf" "pushd" "pwd" "read" "readarray" - "return" "set" "shift" "shopt" "source" "suspend" - "test" "time" "times" "trap" "type" "typeset" - "ulimit" "umask" "unalias" "wait")) + "alias" "bg" "bind" "break" "builtin" "caller" "cd" "command" "compgen" "complete" "compopt" + "continue" "coproc" "dirs" "disown" "echo" "enable" "eval" "exec" "exit" "fc" "fg" "getopts" + "hash" "help" "history" "jobs" "kill" "let" "logout" "mapfile" "popd" "printf" "pushd" "pwd" + "read" "readarray" "return" "set" "shift" "shopt" "source" "suspend" "test" "time" "times" + "trap" "type" "typeset" "ulimit" "umask" "unalias" "wait")) (command - argument: - [ - (word) @variable.parameter - (concatenation - (word) @variable.parameter) - ]) + argument: [ + (word) @variable.parameter + (concatenation + (word) @variable.parameter) + ]) (number) @number @@ -225,5 +228,5 @@ ((program . - (comment) @keyword.directive) + (comment) @keyword.directive @nospell) (#lua-match? @keyword.directive "^#!/")) diff --git a/runtime/queries/bash/injections.scm b/runtime/queries/bash/injections.scm new file mode 100644 index 0000000000..427b414958 --- /dev/null +++ b/runtime/queries/bash/injections.scm @@ -0,0 +1,3 @@ +(heredoc_redirect + (heredoc_body) @injection.content + (heredoc_end) @injection.language) diff --git a/runtime/queries/c/folds.scm b/runtime/queries/c/folds.scm index 2e2a6b4d0c..bb26a62eb5 100644 --- a/runtime/queries/c/folds.scm +++ b/runtime/queries/c/folds.scm @@ -16,6 +16,7 @@ (preproc_function_def) (initializer_list) (gnu_asm_expression) + (preproc_include)+ ] @fold (compound_statement diff --git a/runtime/queries/c/highlights.scm b/runtime/queries/c/highlights.scm index c848f68dca..170937c8f8 100644 --- a/runtime/queries/c/highlights.scm +++ b/runtime/queries/c/highlights.scm @@ -7,16 +7,19 @@ [ "default" - "enum" - "struct" - "typedef" - "union" "goto" "asm" "__asm__" ] @keyword [ + "enum" + "struct" + "union" + "typedef" +] @keyword.type + +[ "sizeof" "offsetof" ] @keyword.operator @@ -142,9 +145,6 @@ (char_literal) @character -((preproc_arg) @function.macro - (#set! "priority" 90)) - (preproc_defined) @function.macro ((field_expression @@ -159,21 +159,26 @@ (statement_identifier) @label +(declaration + type: (type_identifier) @_type + declarator: (identifier) @label + (#eq? @_type "__label__")) + [ (type_identifier) (type_descriptor) ] @type -(storage_class_specifier) @keyword.storage +(storage_class_specifier) @keyword.modifier [ (type_qualifier) (gnu_asm_qualifier) "__extension__" -] @type.qualifier +] @keyword.modifier (linkage_specification - "extern" @keyword.storage) + "extern" @keyword.modifier) (type_definition declarator: (type_identifier) @type.definition) @@ -232,10 +237,10 @@ (argument_list (identifier) @variable.builtin)) -((attribute_specifier +(attribute_specifier (argument_list (call_expression - function: (identifier) @variable.builtin)))) + function: (identifier) @variable.builtin))) ((call_expression function: (identifier) @function.builtin) @@ -258,18 +263,16 @@ function: (identifier) @function.call) (call_expression - function: - (field_expression - field: (field_identifier) @function.call)) + function: (field_expression + field: (field_identifier) @function.call)) (function_declarator declarator: (identifier) @function) (function_declarator - declarator: - (parenthesized_declarator - (pointer_declarator - declarator: (field_identifier) @function))) + declarator: (parenthesized_declarator + (pointer_declarator + declarator: (field_identifier) @function))) (preproc_function_def name: (identifier) @function.macro) diff --git a/runtime/queries/lua/highlights.scm b/runtime/queries/lua/highlights.scm index 0b0bf35a8b..0c8f07f290 100644 --- a/runtime/queries/lua/highlights.scm +++ b/runtime/queries/lua/highlights.scm @@ -180,27 +180,24 @@ (vararg_expression) @variable.parameter.builtin (function_declaration - name: - [ - (identifier) @function - (dot_index_expression - field: (identifier) @function) - ]) + name: [ + (identifier) @function + (dot_index_expression + field: (identifier) @function) + ]) (function_declaration - name: - (method_index_expression - method: (identifier) @function.method)) + name: (method_index_expression + method: (identifier) @function.method)) (assignment_statement (variable_list . - name: - [ - (identifier) @function - (dot_index_expression - field: (identifier) @function) - ]) + name: [ + (identifier) @function + (dot_index_expression + field: (identifier) @function) + ]) (expression_list . value: (function_definition))) @@ -211,27 +208,24 @@ value: (function_definition))) (function_call - name: - [ - (identifier) @function.call - (dot_index_expression - field: (identifier) @function.call) - (method_index_expression - method: (identifier) @function.method.call) - ]) + name: [ + (identifier) @function.call + (dot_index_expression + field: (identifier) @function.call) + (method_index_expression + method: (identifier) @function.method.call) + ]) (function_call (identifier) @function.builtin - ; format-ignore (#any-of? @function.builtin ; built-in functions in Lua 5.1 - "assert" "collectgarbage" "dofile" "error" "getfenv" "getmetatable" "ipairs" - "load" "loadfile" "loadstring" "module" "next" "pairs" "pcall" "print" - "rawequal" "rawget" "rawlen" "rawset" "require" "select" "setfenv" "setmetatable" - "tonumber" "tostring" "type" "unpack" "xpcall" - "__add" "__band" "__bnot" "__bor" "__bxor" "__call" "__concat" "__div" "__eq" "__gc" - "__idiv" "__index" "__le" "__len" "__lt" "__metatable" "__mod" "__mul" "__name" "__newindex" - "__pairs" "__pow" "__shl" "__shr" "__sub" "__tostring" "__unm")) + "assert" "collectgarbage" "dofile" "error" "getfenv" "getmetatable" "ipairs" "load" "loadfile" + "loadstring" "module" "next" "pairs" "pcall" "print" "rawequal" "rawget" "rawlen" "rawset" + "require" "select" "setfenv" "setmetatable" "tonumber" "tostring" "type" "unpack" "xpcall" + "__add" "__band" "__bnot" "__bor" "__bxor" "__call" "__concat" "__div" "__eq" "__gc" "__idiv" + "__index" "__le" "__len" "__lt" "__metatable" "__mod" "__mul" "__name" "__newindex" "__pairs" + "__pow" "__shl" "__shr" "__sub" "__tostring" "__unm")) ; Others (comment) @comment @spell @@ -255,21 +249,19 @@ (dot_index_expression field: (identifier) @_method (#any-of? @_method "find" "match" "gmatch" "gsub")) - arguments: - (arguments - . - (_) - . - (string - content: (string_content) @string.regexp))) + arguments: (arguments + . + (_) + . + (string + content: (string_content) @string.regexp))) ;("123"):match("%d+") (function_call (method_index_expression method: (identifier) @_method (#any-of? @_method "find" "match" "gmatch" "gsub")) - arguments: - (arguments - . - (string - content: (string_content) @string.regexp))) + arguments: (arguments + . + (string + content: (string_content) @string.regexp))) diff --git a/runtime/queries/lua/injections.scm b/runtime/queries/lua/injections.scm index c8a1843c84..4345c71ce8 100644 --- a/runtime/queries/lua/injections.scm +++ b/runtime/queries/lua/injections.scm @@ -1,66 +1,69 @@ ((function_call - name: - [ - (identifier) @_cdef_identifier - (_ - _ - (identifier) @_cdef_identifier) - ] - arguments: - (arguments - (string - content: _ @injection.content))) + name: [ + (identifier) @_cdef_identifier + (_ + _ + (identifier) @_cdef_identifier) + ] + arguments: (arguments + (string + content: _ @injection.content))) (#set! injection.language "c") (#eq? @_cdef_identifier "cdef")) ((function_call name: (_) @_vimcmd_identifier - arguments: - (arguments - (string - content: _ @injection.content))) + arguments: (arguments + (string + content: _ @injection.content))) (#set! injection.language "vim") - (#any-of? @_vimcmd_identifier "vim.cmd" "vim.api.nvim_command" "vim.api.nvim_command" "vim.api.nvim_exec2")) + (#any-of? @_vimcmd_identifier + "vim.cmd" "vim.api.nvim_command" "vim.api.nvim_command" "vim.api.nvim_exec2")) ((function_call name: (_) @_vimcmd_identifier - arguments: - (arguments - (string - content: _ @injection.content) .)) + arguments: (arguments + (string + content: _ @injection.content) .)) (#set! injection.language "query") (#any-of? @_vimcmd_identifier "vim.treesitter.query.set" "vim.treesitter.query.parse")) ((function_call name: (_) @_vimcmd_identifier - arguments: - (arguments - . - (_) - . - (string - content: _ @_method) - . - (string - content: _ @injection.content))) + arguments: (arguments + . + (_) + . + (string + content: _ @_method) + . + (string + content: _ @injection.content))) (#any-of? @_vimcmd_identifier "vim.rpcrequest" "vim.rpcnotify") (#eq? @_method "nvim_exec_lua") (#set! injection.language "lua")) +; exec_lua [[ ... ]] in functionaltests +((function_call + name: (identifier) @_function + arguments: (arguments + (string + content: (string_content) @injection.content))) + (#eq? @_function "exec_lua") + (#set! injection.language "lua")) + ; vim.api.nvim_create_autocmd("FileType", { command = "injected here" }) (function_call name: (_) @_vimcmd_identifier - arguments: - (arguments - . - (_) - . - (table_constructor - (field - name: (identifier) @_command - value: - (string - content: (_) @injection.content))) .) + arguments: (arguments + . + (_) + . + (table_constructor + (field + name: (identifier) @_command + value: (string + content: (_) @injection.content))) .) ; limit so only 2-argument functions gets matched before pred handle (#eq? @_vimcmd_identifier "vim.api.nvim_create_autocmd") (#eq? @_command "command") @@ -68,31 +71,29 @@ (function_call name: (_) @_user_cmd - arguments: - (arguments - . - (_) - . - (string - content: (_) @injection.content) - . - (_) .) + arguments: (arguments + . + (_) + . + (string + content: (_) @injection.content) + . + (_) .) (#eq? @_user_cmd "vim.api.nvim_create_user_command") (#set! injection.language "vim")) (function_call name: (_) @_user_cmd - arguments: - (arguments - . - (_) - . - (_) - . - (string - content: (_) @injection.content) - . - (_) .) + arguments: (arguments + . + (_) + . + (_) + . + (string + content: (_) @injection.content) + . + (_) .) ; Limiting predicate handling to only functions with 4 arguments (#eq? @_user_cmd "vim.api.nvim_buf_create_user_command") (#set! injection.language "vim")) diff --git a/runtime/queries/markdown/highlights.scm b/runtime/queries/markdown/highlights.scm index 7c26fd710c..4b445e02f3 100644 --- a/runtime/queries/markdown/highlights.scm +++ b/runtime/queries/markdown/highlights.scm @@ -1,34 +1,34 @@ ;From MDeiml/tree-sitter-markdown & Helix (setext_heading (paragraph) @markup.heading.1 - (setext_h1_underline) @markup.heading.1.marker) + (setext_h1_underline) @markup.heading.1) (setext_heading (paragraph) @markup.heading.2 - (setext_h2_underline) @markup.heading.2.marker) + (setext_h2_underline) @markup.heading.2) (atx_heading - (atx_h1_marker) @markup.heading.1.marker + (atx_h1_marker) @markup.heading.1 (inline) @markup.heading.1) (atx_heading - (atx_h2_marker) @markup.heading.2.marker + (atx_h2_marker) @markup.heading.2 (inline) @markup.heading.2) (atx_heading - (atx_h3_marker) @markup.heading.3.marker + (atx_h3_marker) @markup.heading.3 (inline) @markup.heading.3) (atx_heading - (atx_h4_marker) @markup.heading.4.marker + (atx_h4_marker) @markup.heading.4 (inline) @markup.heading.4) (atx_heading - (atx_h5_marker) @markup.heading.5.marker + (atx_h5_marker) @markup.heading.5 (inline) @markup.heading.5) (atx_heading - (atx_h6_marker) @markup.heading.6.marker + (atx_h6_marker) @markup.heading.6 (inline) @markup.heading.6) (info_string) @label @@ -54,12 +54,12 @@ (#set! "priority" 90)) (fenced_code_block - (fenced_code_block_delimiter) @markup.raw.delimiter + (fenced_code_block_delimiter) @markup.raw.block (#set! conceal "")) (fenced_code_block (info_string - (language) @conceal + (language) @label (#set! conceal ""))) (link_destination) @markup.link.url @@ -69,6 +69,10 @@ (link_label) ] @markup.link.label +((link_label) + . + ":" @punctuation.delimiter) + [ (list_marker_plus) (list_marker_minus) @@ -79,7 +83,7 @@ ; NOTE: The following has been commented out due to issues with spaces in the ; list marker nodes generated by the parser. If those spaces ever get captured -; by a different node (e.g. block_continuation) we can safely readd these +; by a different node (e.g. block_continuation) we can safely re-add these ; conceals. ; ;; Conceal bullet points ; ([(list_marker_plus) (list_marker_star)] @@ -107,6 +111,12 @@ ((block_quote) @markup.quote (#set! "priority" 90)) +([ + (plus_metadata) + (minus_metadata) +] @keyword.directive + (#set! "priority" 90)) + [ (block_continuation) (block_quote_marker) diff --git a/runtime/queries/markdown_inline/highlights.scm b/runtime/queries/markdown_inline/highlights.scm index e9b41c31d5..233ab411cd 100644 --- a/runtime/queries/markdown_inline/highlights.scm +++ b/runtime/queries/markdown_inline/highlights.scm @@ -16,10 +16,10 @@ ] @string.escape ; Conceal codeblock and text style markers -((code_span_delimiter) @markup.raw.delimiter - (#set! conceal "")) - -((emphasis_delimiter) @conceal +([ + (code_span_delimiter) + (emphasis_delimiter) +] @conceal (#set! conceal "")) ; Conceal inline links @@ -33,6 +33,18 @@ ] @markup.link (#set! conceal "")) +[ + (link_label) + (link_text) + (link_title) + (image_description) +] @markup.link.label + +(inline_link + (link_text) @_label + (link_destination) @_url + (#set! @_label "url" @_url)) + ; Conceal image links (image [ @@ -75,13 +87,6 @@ (uri_autolink) ] @markup.link.url @nospell -[ - (link_label) - (link_text) - (link_title) - (image_description) -] @markup.link.label - ; Replace common HTML entities. ((entity_reference) @character.special (#eq? @character.special " ") diff --git a/runtime/queries/python/folds.scm b/runtime/queries/python/folds.scm index 7c547db38f..ecb9352d78 100644 --- a/runtime/queries/python/folds.scm +++ b/runtime/queries/python/folds.scm @@ -21,3 +21,8 @@ (dictionary) (string) ] @fold + +[ + (import_statement) + (import_from_statement) +]+ @fold diff --git a/runtime/queries/python/highlights.scm b/runtime/queries/python/highlights.scm index 764521c7be..5e5a2a88de 100644 --- a/runtime/queries/python/highlights.scm +++ b/runtime/queries/python/highlights.scm @@ -17,11 +17,9 @@ (#lua-match? @constant.builtin "^__[a-zA-Z0-9_]*__$")) ((identifier) @constant.builtin - ; format-ignore - (#any-of? @constant.builtin + (#any-of? @constant.builtin ; https://docs.python.org/3/library/constants.html - "NotImplemented" "Ellipsis" - "quit" "exit" "copyright" "credits" "license")) + "NotImplemented" "Ellipsis" "quit" "exit" "copyright" "credits" "license")) "_" @constant.builtin ; match wildcard @@ -37,9 +35,8 @@ ((assignment left: (identifier) @type.definition - right: - (call - function: (identifier) @_func)) + right: (call + function: (identifier) @_func)) (#any-of? @_func "TypeVar" "NewType")) ; Function calls @@ -47,18 +44,16 @@ function: (identifier) @function.call) (call - function: - (attribute - attribute: (identifier) @function.method.call)) + function: (attribute + attribute: (identifier) @function.method.call)) ((call function: (identifier) @constructor) (#lua-match? @constructor "^%u")) ((call - function: - (attribute - attribute: (identifier) @constructor)) + function: (attribute + attribute: (identifier) @constructor)) (#lua-match? @constructor "^%u")) ; Decorators @@ -84,12 +79,19 @@ ((decorator (identifier) @attribute.builtin) - (#any-of? @attribute.builtin "classmethod" "property")) + (#any-of? @attribute.builtin "classmethod" "property" "staticmethod")) ; Builtin functions ((call function: (identifier) @function.builtin) - (#any-of? @function.builtin "abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray" "bytes" "callable" "chr" "classmethod" "compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec" "filter" "float" "format" "frozenset" "getattr" "globals" "hasattr" "hash" "help" "hex" "id" "input" "int" "isinstance" "issubclass" "iter" "len" "list" "locals" "map" "max" "memoryview" "min" "next" "object" "oct" "open" "ord" "pow" "print" "property" "range" "repr" "reversed" "round" "set" "setattr" "slice" "sorted" "staticmethod" "str" "sum" "super" "tuple" "type" "vars" "zip" "__import__")) + (#any-of? @function.builtin + "abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray" "bytes" "callable" "chr" + "classmethod" "compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec" + "filter" "float" "format" "frozenset" "getattr" "globals" "hasattr" "hash" "help" "hex" "id" + "input" "int" "isinstance" "issubclass" "iter" "len" "list" "locals" "map" "max" "memoryview" + "min" "next" "object" "oct" "open" "ord" "pow" "print" "property" "range" "repr" "reversed" + "round" "set" "setattr" "slice" "sorted" "staticmethod" "str" "sum" "super" "tuple" "type" + "vars" "zip" "__import__")) ; Function definitions (function_definition @@ -104,10 +106,9 @@ ((call function: (identifier) @_isinstance - arguments: - (argument_list - (_) - (identifier) @type)) + arguments: (argument_list + (_) + (identifier) @type)) (#eq? @_isinstance "isinstance")) ; Normal parameters @@ -187,7 +188,7 @@ ((module . - (comment) @keyword.directive) + (comment) @keyword.directive @nospell) (#lua-match? @keyword.directive "^#!/")) (string) @string @@ -200,22 +201,44 @@ ; doc-strings (module . + (comment)* + . (expression_statement - (string) @string.documentation @spell)) + (string) @string.documentation)) (class_definition - body: - (block - . - (expression_statement - (string) @string.documentation @spell))) + body: (block + . + (expression_statement + (string) @string.documentation))) (function_definition - body: - (block - . - (expression_statement - (string) @string.documentation @spell))) + body: (block + . + (expression_statement + (string) @string.documentation))) + +(module + . + (comment)* + . + (expression_statement + (string + (string_content) @spell))) + +(class_definition + body: (block + . + (expression_statement + (string + (string_content) @spell)))) + +(function_definition + body: (block + . + (expression_statement + (string + (string_content) @spell)))) ; Tokens [ @@ -277,7 +300,6 @@ [ "assert" - "class" "exec" "global" "nonlocal" @@ -285,10 +307,14 @@ "print" "with" "as" - "type" ] @keyword [ + "type" + "class" +] @keyword.type + +[ "async" "await" ] @keyword.coroutine @@ -371,33 +397,28 @@ name: (identifier) @type) (class_definition - body: - (block - (function_definition - name: (identifier) @function.method))) + body: (block + (function_definition + name: (identifier) @function.method))) (class_definition - superclasses: - (argument_list - (identifier) @type)) + superclasses: (argument_list + (identifier) @type)) ((class_definition - body: - (block - (expression_statement - (assignment - left: (identifier) @variable.member)))) - (#lua-match? @variable.member "^%l.*$")) + body: (block + (expression_statement + (assignment + left: (identifier) @variable.member)))) + (#lua-match? @variable.member "^[%l_].*$")) ((class_definition - body: - (block - (expression_statement - (assignment - left: - (_ - (identifier) @variable.member))))) - (#lua-match? @variable.member "^%l.*$")) + body: (block + (expression_statement + (assignment + left: (_ + (identifier) @variable.member))))) + (#lua-match? @variable.member "^[%l_].*$")) ((class_definition (block @@ -406,32 +427,31 @@ (#any-of? @constructor "__new__" "__init__")) ((identifier) @type.builtin - ; format-ignore (#any-of? @type.builtin ; https://docs.python.org/3/library/exceptions.html - "BaseException" "Exception" "ArithmeticError" "BufferError" "LookupError" "AssertionError" "AttributeError" - "EOFError" "FloatingPointError" "GeneratorExit" "ImportError" "ModuleNotFoundError" "IndexError" "KeyError" - "KeyboardInterrupt" "MemoryError" "NameError" "NotImplementedError" "OSError" "OverflowError" "RecursionError" - "ReferenceError" "RuntimeError" "StopIteration" "StopAsyncIteration" "SyntaxError" "IndentationError" "TabError" - "SystemError" "SystemExit" "TypeError" "UnboundLocalError" "UnicodeError" "UnicodeEncodeError" "UnicodeDecodeError" - "UnicodeTranslateError" "ValueError" "ZeroDivisionError" "EnvironmentError" "IOError" "WindowsError" - "BlockingIOError" "ChildProcessError" "ConnectionError" "BrokenPipeError" "ConnectionAbortedError" - "ConnectionRefusedError" "ConnectionResetError" "FileExistsError" "FileNotFoundError" "InterruptedError" - "IsADirectoryError" "NotADirectoryError" "PermissionError" "ProcessLookupError" "TimeoutError" "Warning" + "BaseException" "Exception" "ArithmeticError" "BufferError" "LookupError" "AssertionError" + "AttributeError" "EOFError" "FloatingPointError" "GeneratorExit" "ImportError" + "ModuleNotFoundError" "IndexError" "KeyError" "KeyboardInterrupt" "MemoryError" "NameError" + "NotImplementedError" "OSError" "OverflowError" "RecursionError" "ReferenceError" "RuntimeError" + "StopIteration" "StopAsyncIteration" "SyntaxError" "IndentationError" "TabError" "SystemError" + "SystemExit" "TypeError" "UnboundLocalError" "UnicodeError" "UnicodeEncodeError" + "UnicodeDecodeError" "UnicodeTranslateError" "ValueError" "ZeroDivisionError" "EnvironmentError" + "IOError" "WindowsError" "BlockingIOError" "ChildProcessError" "ConnectionError" + "BrokenPipeError" "ConnectionAbortedError" "ConnectionRefusedError" "ConnectionResetError" + "FileExistsError" "FileNotFoundError" "InterruptedError" "IsADirectoryError" + "NotADirectoryError" "PermissionError" "ProcessLookupError" "TimeoutError" "Warning" "UserWarning" "DeprecationWarning" "PendingDeprecationWarning" "SyntaxWarning" "RuntimeWarning" "FutureWarning" "ImportWarning" "UnicodeWarning" "BytesWarning" "ResourceWarning" ; https://docs.python.org/3/library/stdtypes.html - "bool" "int" "float" "complex" "list" "tuple" "range" "str" - "bytes" "bytearray" "memoryview" "set" "frozenset" "dict" "type" "object")) + "bool" "int" "float" "complex" "list" "tuple" "range" "str" "bytes" "bytearray" "memoryview" + "set" "frozenset" "dict" "type" "object")) ; Regex from the `re` module (call - function: - (attribute - object: (identifier) @_re) - arguments: - (argument_list - . - (string - (string_content) @string.regexp)) + function: (attribute + object: (identifier) @_re) + arguments: (argument_list + . + (string + (string_content) @string.regexp)) (#eq? @_re "re")) diff --git a/runtime/queries/query/highlights.scm b/runtime/queries/query/highlights.scm index cdedb23e29..210d03dc33 100644 --- a/runtime/queries/query/highlights.scm +++ b/runtime/queries/query/highlights.scm @@ -53,35 +53,33 @@ . (comment)* . - (comment) @keyword.import) + (comment) @keyword.import @nospell) (#lua-match? @keyword.import "^;+ *inherits *:")) ((program . (comment)* . - (comment) @keyword.directive) + (comment) @keyword.directive @nospell) (#lua-match? @keyword.directive "^;+ *extends *$")) -((comment) @keyword.directive +((comment) @keyword.directive @nospell (#lua-match? @keyword.directive "^;+%s*format%-ignore%s*$")) ((predicate name: (identifier) @_name - parameters: - (parameters - (string - "\"" @string - "\"" @string) @string.regexp)) + parameters: (parameters + (string + "\"" @string + "\"" @string) @string.regexp)) (#any-of? @_name "match" "not-match" "vim-match" "not-vim-match" "lua-match" "not-lua-match")) ((predicate name: (identifier) @_name - parameters: - (parameters - (string - "\"" @string - "\"" @string) @string.regexp - . - (string) .)) + parameters: (parameters + (string + "\"" @string + "\"" @string) @string.regexp + . + (string) .)) (#any-of? @_name "gsub" "not-gsub")) diff --git a/runtime/queries/vim/highlights.scm b/runtime/queries/vim/highlights.scm index 54832ffa56..14e5a8128f 100644 --- a/runtime/queries/vim/highlights.scm +++ b/runtime/queries/vim/highlights.scm @@ -42,9 +42,8 @@ function: (identifier) @function.call) (call_expression - function: - (scoped_identifier - (identifier) @function.call)) + function: (scoped_identifier + (identifier) @function.call)) (parameters (identifier) @variable.parameter) @@ -127,11 +126,14 @@ "view" "eval" "sign" + "abort" ] @keyword (map_statement cmd: _ @keyword) +(keycode) @character.special + (command_name) @function.macro ; Filetype command @@ -204,10 +206,9 @@ (command_attribute name: _ @property - val: - (behavior - name: _ @constant - val: (identifier)? @function)?) + val: (behavior + name: _ @constant + val: (identifier)? @function)?) ; Edit command (plus_plus_opt @@ -277,8 +278,6 @@ "/" "%" ".." - "is" - "isnot" "==" "!=" ">" @@ -297,9 +296,15 @@ "..=" "<<" "=<<" + "->" (match_case) ] @operator +[ + "is" + "isnot" +] @keyword.operator + ; Some characters have different meanings based on the context (unary_operation "!" @operator) diff --git a/runtime/queries/vim/injections.scm b/runtime/queries/vim/injections.scm index 16ec57ca99..5feb832ec4 100644 --- a/runtime/queries/vim/injections.scm +++ b/runtime/queries/vim/injections.scm @@ -28,5 +28,7 @@ ((set_item option: (option_name) @_option value: (set_value) @injection.content) - (#any-of? @_option "includeexpr" "inex" "printexpr" "pexpr" "formatexpr" "fex" "indentexpr" "inde" "foldtext" "fdt" "foldexpr" "fde" "diffexpr" "dex" "patchexpr" "pex" "charconvert" "ccv") + (#any-of? @_option + "includeexpr" "inex" "printexpr" "pexpr" "formatexpr" "fex" "indentexpr" "inde" "foldtext" "fdt" + "foldexpr" "fde" "diffexpr" "dex" "patchexpr" "pex" "charconvert" "ccv") (#set! injection.language "vim")) diff --git a/runtime/queries/vimdoc/highlights.scm b/runtime/queries/vimdoc/highlights.scm index 294fa94f10..70a3a2f206 100644 --- a/runtime/queries/vimdoc/highlights.scm +++ b/runtime/queries/vimdoc/highlights.scm @@ -7,39 +7,46 @@ (column_heading) @markup.heading.4 (column_heading - "~" @markup.heading.4.marker + "~" @markup.heading.4 + (#set! conceal "")) + +(tag + "*" @label (#set! conceal "")) (tag - "*" @markup.heading.5.marker - (#set! conceal "") text: (_) @label) (taglink "|" @markup.link - (#set! conceal "") + (#set! conceal "")) + +(taglink text: (_) @markup.link) (optionlink text: (_) @markup.link) (codespan - "`" @markup.raw.delimiter - (#set! conceal "") + "`" @markup.raw + (#set! conceal "")) + +(codespan text: (_) @markup.raw) ((codeblock) @markup.raw.block (#set! "priority" 90)) (codeblock - [ - ">" - (language) - ] @markup.raw.delimiter + ">" @markup.raw + (#set! conceal "")) + +(codeblock + (language) @label (#set! conceal "")) (block - "<" @markup.raw.delimiter + "<" @markup.raw (#set! conceal "")) (argument) @variable.parameter @@ -48,6 +55,8 @@ (url) @string.special.url +(modeline) @keyword.directive + ((note) @comment.note (#any-of? @comment.note "Note:" "NOTE:" "Notes:")) diff --git a/runtime/syntax/astro.vim b/runtime/syntax/astro.vim new file mode 100644 index 0000000000..0816051ada --- /dev/null +++ b/runtime/syntax/astro.vim @@ -0,0 +1,190 @@ +" Vim syntax file. +" Language: Astro +" Author: Wuelner Martínez <wuelner.martinez@outlook.com> +" Maintainer: Wuelner Martínez <wuelner.martinez@outlook.com> +" URL: https://github.com/wuelnerdotexe/vim-astro +" Last Change: 2022 Aug 22 +" Based On: Evan Lecklider's vim-svelte +" Changes: See https://github.com/evanleck/vim-svelte +" Credits: See vim-svelte on github + +" Quit when a (custom) syntax file was already loaded. +if !exists('main_syntax') + if exists('b:current_syntax') + finish + endif + let main_syntax = 'astro' +elseif exists('b:current_syntax') && b:current_syntax == 'astro' + finish +endif + +" Astro syntax variables are initialized. +let g:astro_typescript = get(g:, 'astro_typescript', 'disable') +let g:astro_stylus = get(g:, 'astro_stylus', 'disable') + +let s:cpoptions_save = &cpoptions +set cpoptions&vim + +" Embedded HTML syntax. +runtime! syntax/html.vim + +" htmlTagName: expand HTML tag names to include mixed case and periods. +syntax match htmlTagName contained "\<[a-zA-Z\.]*\>" + +" astroDirectives: add Astro Directives to HTML arguments. +syntax match astroDirectives contained '\<[a-z]\+:[a-z|]*\>' containedin=htmlTag + +unlet b:current_syntax + +if g:astro_typescript == 'enable' + " Embedded TypeScript syntax. + syntax include @astroJavaScript syntax/typescript.vim + + " javaScriptExpression: a javascript expression is used as an arg value. + syntax clear javaScriptExpression + syntax region javaScriptExpression + \ contained start=+&{+ + \ keepend end=+};+ + \ contains=@astroJavaScript,@htmlPreproc + + " javaScript: add TypeScript support to HTML script tag. + syntax clear javaScript + syntax region javaScript + \ start=+<script\_[^>]*>+ + \ keepend + \ end=+</script\_[^>]*>+me=s-1 + \ contains=htmlScriptTag,@astroJavaScript,@htmlPreproc,htmlCssStyleComment +else + " Embedded JavaScript syntax. + syntax include @astroJavaScript syntax/javascript.vim +endif + +" astroFence: detect the Astro fence. +syntax match astroFence contained +^---$+ + +" astrojavaScript: add TypeScript support to Astro code fence. +syntax region astroJavaScript + \ start=+^---$+ + \ keepend + \ end=+^---$+ + \ contains=htmlTag,@astroJavaScript,@htmlPreproc,htmlCssStyleComment,htmlEndTag,astroFence + \ fold + +unlet b:current_syntax + +if g:astro_typescript == 'enable' + " Embedded TypeScript React (TSX) syntax. + syntax include @astroJavaScriptReact syntax/typescriptreact.vim +else + " Embedded JavaScript React (JSX) syntax. + syntax include @astroJavaScriptReact syntax/javascriptreact.vim +endif + +" astroJavaScriptExpression: add {JSX or TSX} support to Astro expresions. +execute 'syntax region astroJavaScriptExpression start=+{+ keepend end=+}+ ' . + \ 'contains=@astroJavaScriptReact, @htmlPreproc containedin=' . join([ + \ 'htmlArg', 'htmlBold', 'htmlBoldItalic', 'htmlBoldItalicUnderline', + \ 'htmlBoldUnderline', 'htmlBoldUnderlineItalic', 'htmlH1', 'htmlH2', + \ 'htmlH3', 'htmlH4', 'htmlH5', 'htmlH6', 'htmlHead', 'htmlItalic', + \ 'htmlItalicBold', 'htmlItalicBoldUnderline', 'htmlItalicUnderline', + \ 'htmlItalicUnderlineBold', 'htmlLeadingSpace', 'htmlLink', + \ 'htmlStrike', 'htmlString', 'htmlTag', 'htmlTitle', 'htmlUnderline', + \ 'htmlUnderlineBold', 'htmlUnderlineBoldItalic', + \ 'htmlUnderlineItalic', 'htmlUnderlineItalicBold', 'htmlValue' + \ ], ',') + +" cssStyle: add CSS style tags support in TypeScript React. +syntax region cssStyle + \ start=+<style\_[^>]*>+ + \ keepend + \ end=+</style\_[^>]*>+me=s-1 + \ contains=htmlTag,@htmlCss,htmlCssStyleComment,@htmlPreproc,htmlEndTag + \ containedin=@astroJavaScriptReact + +unlet b:current_syntax + +" Embedded SCSS syntax. +syntax include @astroScss syntax/scss.vim + +" cssStyle: add SCSS style tags support in Astro. +syntax region scssStyle + \ start=/<style\>\_[^>]*\(lang\)=\("\|''\)[^\2]*scss[^\2]*\2\_[^>]*>/ + \ keepend + \ end=+</style>+me=s-1 + \ contains=@astroScss,astroSurroundingTag + \ fold + +unlet b:current_syntax + +" Embedded SASS syntax. +syntax include @astroSass syntax/sass.vim + +" cssStyle: add SASS style tags support in Astro. +syntax region sassStyle + \ start=/<style\>\_[^>]*\(lang\)=\("\|''\)[^\2]*sass[^\2]*\2\_[^>]*>/ + \ keepend + \ end=+</style>+me=s-1 + \ contains=@astroSass,astroSurroundingTag + \ fold + +unlet b:current_syntax + +" Embedded LESS syntax. +syntax include @astroLess syntax/less.vim + +" cssStyle: add LESS style tags support in Astro. +syntax region lessStyle + \ start=/<style\>\_[^>]*\(lang\)=\("\|''\)[^\2]*less[^\2]*\2\_[^>]*>/ + \ keepend + \ end=+</style>+me=s-1 + \ contains=@astroLess,astroSurroundingTag + \ fold + +unlet b:current_syntax + +" Embedded Stylus syntax. +" NOTE: Vim does not provide stylus support by default, but you can install +" this plugin to support it: https://github.com/wavded/vim-stylus +if g:astro_stylus == 'enable' + try + " Embedded Stylus syntax. + syntax include @astroStylus syntax/stylus.vim + + " stylusStyle: add Stylus style tags support in Astro. + syntax region stylusStyle + \ start=/<style\>\_[^>]*\(lang\)=\("\|''\)[^\2]*stylus[^\2]*\2\_[^>]*>/ + \ keepend + \ end=+</style>+me=s-1 + \ contains=@astroStylus,astroSurroundingTag + \ fold + + unlet b:current_syntax + catch + echomsg "you need install a external plugin for support stylus in .astro files" + endtry +endif + +" astroSurroundingTag: add surround HTML tag to script and style. +syntax region astroSurroundingTag + \ start=+<\(script\|style\)+ + \ end=+>+ + \ contains=htmlTagError,htmlTagN,htmlArg,htmlValue,htmlEvent,htmlString + \ contained + \ fold + +" Define the default highlighting. +" Only used when an item doesn't have highlighting yet. +highlight default link astroDirectives Special +highlight default link astroFence Comment + +let b:current_syntax = 'astro' +if main_syntax == 'astro' + unlet main_syntax +endif + +" Sync from start because of the wacky nesting. +syntax sync fromstart + +let &cpoptions = s:cpoptions_save +unlet s:cpoptions_save +" vim: ts=8 diff --git a/runtime/syntax/cgdbrc.vim b/runtime/syntax/cgdbrc.vim new file mode 100644 index 0000000000..1ace370d8c --- /dev/null +++ b/runtime/syntax/cgdbrc.vim @@ -0,0 +1,16 @@ +" Vim syntax file +" Language: cgdbrc +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Documentation: https://cgdb.github.io/docs/Configuring-CGDB.html +" Latest Revision: 2024-04-09 + +if exists('b:current_syntax') + finish +endif +let b:current_syntax = 'cgdbrc' + +runtime! syntax/vim.vim + +syn region cgdbComment start="^\s*\#" skip="\\$" end="$" contains=@Spell + +highlight default link cgdbComment Comment diff --git a/runtime/syntax/cmakecache.vim b/runtime/syntax/cmakecache.vim new file mode 100644 index 0000000000..f07c719811 --- /dev/null +++ b/runtime/syntax/cmakecache.vim @@ -0,0 +1,60 @@ +" Vim syntax file +" Language: cmakecache - CMakeCache.txt files generated by CMake +" Author: bfrg <https://github.com/bfrg> +" Upstream: https://github.com/bfrg/vim-cmakecache-syntax +" Last Change: Nov 28, 2019 +" License: Same as Vim itself (see :h license) + +if exists('b:current_syntax') + finish +endif + +let s:cpo_save = &cpoptions +set cpoptions&vim + +" Comments start with # or // +syntax region CMakeCacheComment start="#\|//" end="$" + +" Match 'key' in key:type=value +syntax match CMakeCacheKey "^\s*\w\+\(-ADVANCED\)\=:"me=e-1 + +" Highlight 'str' in key:STRING=str (many thanks to Nickspoons in #vim!) +syntax region CMakeCacheStringVar matchgroup=CMakeCacheType start=":STRING="ms=s+1,rs=e-1 end="$" contains=CMakeCacheString keepend +syntax region CMakeCacheString start="="ms=s+1 end="$" contained + +" Highlight boolean 'value' in key:BOOL=value +syntax region CMakeCacheBoolVar matchgroup=CMakeCacheType start=":BOOL="ms=s+1,rs=e-1 end="$" contains=CMakeCacheBool keepend +syntax region CMakeCacheBool start="="ms=s+1 end="$" contained + +" Highlight 'path' in key:PATH=path +syntax region CMakeCachePathVar matchgroup=CMakeCacheType start=":PATH="ms=s+1,rs=e-1 end="$" contains=CMakeCachePath keepend +syntax region CMakeCachePath start="="ms=s+1 end="$" contained + +" Highlight 'file' in key:FILEPATH=file +syntax region CMakeCacheFilePathVar matchgroup=CMakeCacheType start=":FILEPATH="ms=s+1,rs=e-1 end="$" contains=CMakeCacheFilePath keepend +syntax region CMakeCacheFilePath start="="ms=s+1 end="$" contained + +" Highlight 'value' in key:STATIC=value +syntax region CMakeCacheStaticVar matchgroup=CMakeCacheType start=":STATIC="ms=s+1,rs=e-1 end="$" contains=CMakeCacheStatic keepend +syntax region CMakeCacheStatic start="="ms=s+1 end="$" contained + +" Highlight 'value' in key:Internal=value +syntax region CMakeCacheInternalVar matchgroup=CMakeCacheType start=":INTERNAL="ms=s+1,rs=e-1 end="$" contains=CMakeCacheInternal keepend +syntax region CMakeCacheInternal start="="ms=s+1 end="$" contained + +hi def link CMakeCacheComment Comment +hi def link CMakeCacheKey Identifier +hi def link CMakeCacheString String +hi def link CMakeCacheBool Constant +hi def link CMakeCachePath Directory +hi def link CMakeCacheFilePath Normal +hi def link CMakeCacheStatic Normal +hi def link CMakeCacheInternal Normal + +" Highlight 'type' in key:type=value +hi def link CMakeCacheType Type + +let b:current_syntax = 'cmakecache' + +let &cpoptions = s:cpo_save +unlet s:cpo_save diff --git a/runtime/syntax/cpp.vim b/runtime/syntax/cpp.vim index 8daf90a33c..ff1226b7b0 100644 --- a/runtime/syntax/cpp.vim +++ b/runtime/syntax/cpp.vim @@ -2,7 +2,8 @@ " Language: C++ " Current Maintainer: vim-jp (https://github.com/vim-jp/vim-cpp) " Previous Maintainer: Ken Shan <ccshan@post.harvard.edu> -" Last Change: 2023 Dec 08 +" Last Change: 2024 May 04 +" 2024 May 04 by Vim Project (fix digit separator in octals and floats) " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -55,11 +56,11 @@ if !exists("cpp_no_cpp14") syn match cppNumbers display transparent "\<\d\|\.\d" contains=cppNumber,cppFloat syn match cppNumber display contained "\<0\([Uu]\=\([Ll]\|LL\|ll\)\|\([Ll]\|LL\|ll\)\=[Uu]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" syn match cppNumber display contained "\<[1-9]\('\=\d\+\)*\([Uu]\=\([Ll]\|LL\|ll\)\|\([Ll]\|LL\|ll\)\=[Uu]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" - syn match cppNumber display contained "\<0\o\+\([Uu]\=\([Ll]\|LL\|ll\)\|\([Ll]\|LL\|ll\)\=[Uu]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" + syn match cppNumber display contained "\<0\('\=\o\+\)\+\([Uu]\=\([Ll]\|LL\|ll\)\|\([Ll]\|LL\|ll\)\=[Uu]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" syn match cppNumber display contained "\<0b[01]\('\=[01]\+\)*\([Uu]\=\([Ll]\|LL\|ll\)\|\([Ll]\|LL\|ll\)\=[Uu]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" syn match cppNumber display contained "\<0x\x\('\=\x\+\)*\([Uu]\=\([Ll]\|LL\|ll\)\|\([Ll]\|LL\|ll\)\=[Uu]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" - syn match cppFloat display contained "\<\d\+\.\d*\(e[-+]\=\d\+\)\=\([FfLl]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" - syn match cppFloat display contained "\<\.\d\+\(e[-+]\=\d\+\)\=\([FfLl]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" + syn match cppFloat display contained "\<\d\('\=\d\+\)*\.\(\d\('\=\d\+\)*\)\=\(e[-+]\=\d\+\)\=\([FfLl]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" + syn match cppFloat display contained "\.\d\('\=\d\+\)*\(e[-+]\=\d\+\)\=\([FfLl]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" syn match cppFloat display contained "\<\d\+e[-+]\=\d\+\([FfLl]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" syn region cppString start=+\(L\|u\|u8\|U\)\="+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"\(sv\|s\|_\i*\)\=+ end='$' contains=cSpecial,cFormat,@Spell endif diff --git a/runtime/syntax/cuda.vim b/runtime/syntax/cuda.vim index 13d70e343a..8306d096a0 100644 --- a/runtime/syntax/cuda.vim +++ b/runtime/syntax/cuda.vim @@ -1,7 +1,8 @@ " Vim syntax file " Language: CUDA (NVIDIA Compute Unified Device Architecture) " Maintainer: Timothy B. Terriberry <tterribe@users.sourceforge.net> -" Last Change: 2018 Feb 06 +" Last Change: 2024 Apr 04 +" Contributor: jiangyinzuo " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -11,12 +12,13 @@ endif " Read the C++ syntax to start with runtime! syntax/cpp.vim -" CUDA extentions -syn keyword cudaStorageClass __device__ __global__ __host__ -syn keyword cudaStorageClass __constant__ __shared__ -syn keyword cudaStorageClass __inline__ __align__ __thread__ +" CUDA extentions. +" Reference: https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#c-language-extensions +syn keyword cudaStorageClass __device__ __global__ __host__ __managed__ +syn keyword cudaStorageClass __constant__ __grid_constant__ __shared__ +syn keyword cudaStorageClass __inline__ __noinline__ __forceinline__ __inline_hint__ +syn keyword cudaStorageClass __align__ __thread__ __restrict__ "syn keyword cudaStorageClass __import__ __export__ __location__ -syn keyword cudaStructure template syn keyword cudaType char1 char2 char3 char4 syn keyword cudaType uchar1 uchar2 uchar3 uchar4 syn keyword cudaType short1 short2 short3 short4 @@ -25,37 +27,23 @@ syn keyword cudaType int1 int2 int3 int4 syn keyword cudaType uint1 uint2 uint3 uint4 syn keyword cudaType long1 long2 long3 long4 syn keyword cudaType ulong1 ulong2 ulong3 ulong4 +syn keyword cudaType longlong1 longlong2 longlong3 longlong4 +syn keyword cudaType ulonglong1 ulonglong2 ulonglong3 ulonglong4 syn keyword cudaType float1 float2 float3 float4 -syn keyword cudaType ufloat1 ufloat2 ufloat3 ufloat4 +syn keyword cudaType double1 double2 double3 double4 syn keyword cudaType dim3 texture textureReference syn keyword cudaType cudaError_t cudaDeviceProp cudaMemcpyKind syn keyword cudaType cudaArray cudaChannelFormatKind syn keyword cudaType cudaChannelFormatDesc cudaTextureAddressMode syn keyword cudaType cudaTextureFilterMode cudaTextureReadMode -syn keyword cudaVariable gridDim blockIdx blockDim threadIdx +syn keyword cudaVariable gridDim blockIdx blockDim threadIdx warpSize +syn keyword cudaConstant __CUDA_ARCH__ syn keyword cudaConstant __DEVICE_EMULATION__ +" There are too many CUDA enumeration constants. We only define a subset of commonly used constants. +" Reference: https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__TYPES.html syn keyword cudaConstant cudaSuccess -" Many more errors are defined, but only these are listed in the maunal -syn keyword cudaConstant cudaErrorMemoryAllocation -syn keyword cudaConstant cudaErrorInvalidDevicePointer -syn keyword cudaConstant cudaErrorInvalidSymbol -syn keyword cudaConstant cudaErrorMixedDeviceExecution -syn keyword cudaConstant cudaMemcpyHostToHost -syn keyword cudaConstant cudaMemcpyHostToDevice -syn keyword cudaConstant cudaMemcpyDeviceToHost -syn keyword cudaConstant cudaMemcpyDeviceToDevice -syn keyword cudaConstant cudaReadModeElementType -syn keyword cudaConstant cudaReadModeNormalizedFloat -syn keyword cudaConstant cudaFilterModePoint -syn keyword cudaConstant cudaFilterModeLinear -syn keyword cudaConstant cudaAddressModeClamp -syn keyword cudaConstant cudaAddressModeWrap -syn keyword cudaConstant cudaChannelFormatKindSigned -syn keyword cudaConstant cudaChannelFormatKindUnsigned -syn keyword cudaConstant cudaChannelFormatKindFloat hi def link cudaStorageClass StorageClass -hi def link cudaStructure Structure hi def link cudaType Type hi def link cudaVariable Identifier hi def link cudaConstant Constant diff --git a/runtime/syntax/debcontrol.vim b/runtime/syntax/debcontrol.vim index b173a7b3f7..ea82d85b03 100644 --- a/runtime/syntax/debcontrol.vim +++ b/runtime/syntax/debcontrol.vim @@ -3,7 +3,7 @@ " Maintainer: Debian Vim Maintainers " Former Maintainers: Gerfried Fuchs <alfie@ist.org> " Wichert Akkerman <wakkerma@debian.org> -" Last Change: 2023 Dec 22 +" Last Change: 2024 Mar 26 " URL: https://salsa.debian.org/vim-team/vim-debian/blob/main/syntax/debcontrol.vim " Standard syntax initialization @@ -93,7 +93,7 @@ syn case ignore " Catch-all for the legal fields syn region debcontrolField matchgroup=debcontrolKey start="^\%(\%(XSBC-Original-\)\=Maintainer\|Standards-Version\|Bugs\|Origin\|X[SB]-Python-Version\|\%(XS-\)\=Vcs-Mtn\|\%(XS-\)\=Testsuite\%(-Triggers\)\=\|Build-Profiles\|Tag\|Subarchitecture\|Kernel-Version\|Installer-Menu-Item\): " end="$" contains=debcontrolVariable,debcontrolEmail oneline -syn region debcontrolMultiField matchgroup=debcontrolKey start="^\%(Build-\%(Conflicts\|Depends\)\%(-Arch\|-Indep\)\=\|\%(Pre-\)\=Depends\|Recommends\|Suggests\|Breaks\|Enhances\|Replaces\|Conflicts\|Provides\|Built-Using\|Uploaders\|X[SBC]\{0,3\}\%(Private-\)\=-[-a-zA-Z0-9]\+\): *" skip="^[ \t]" end="^$"me=s-1 end="^[^ \t#]"me=s-1 contains=debcontrolEmail,debcontrolVariable,debcontrolComment +syn region debcontrolMultiField matchgroup=debcontrolKey start="^\%(Build-\%(Conflicts\|Depends\)\%(-Arch\|-Indep\)\=\|\%(Pre-\)\=Depends\|Recommends\|Suggests\|Breaks\|Enhances\|Replaces\|Conflicts\|Provides\|Built-Using\|Static-Built-Using\|Uploaders\|X[SBC]\{0,3\}\%(Private-\)\=-[-a-zA-Z0-9]\+\): *" skip="^[ \t]" end="^$"me=s-1 end="^[^ \t#]"me=s-1 contains=debcontrolEmail,debcontrolVariable,debcontrolComment syn region debcontrolMultiFieldSpell matchgroup=debcontrolKey start="^Description: *" skip="^[ \t]" end="^$"me=s-1 end="^[^ \t#]"me=s-1 contains=debcontrolEmail,debcontrolVariable,debcontrolComment,@Spell " Fields for which we do strict syntax checking diff --git a/runtime/syntax/go.vim b/runtime/syntax/go.vim index ba776f949c..feed9646fb 100644 --- a/runtime/syntax/go.vim +++ b/runtime/syntax/go.vim @@ -5,7 +5,8 @@ " go.vim: Vim syntax file for Go. " Language: Go " Maintainer: Billie Cleek <bhcleek@gmail.com> -" Latest Revision: 2024-01-21 +" Latest Revision: 2024-04-13 +" 2024-03-17: - fix goPackageComment highlight (by Vim Project) " License: BSD-style. See LICENSE file in source repository. " Repository: https://github.com/fatih/vim-go @@ -190,7 +191,7 @@ else syn region goRawString start=+`+ end=+`+ endif -syn match goImportString /^\%(\s\+\|import \)\(\h\w* \)\?\zs"[^"]\+"$/ contained containedin=goImport +syn match goImportString /^\%(\s\+\|import \)\(\h\w* \)\?\zs"[^"]\+"/ contained containedin=goImport if s:HighlightFormatStrings() " [n] notation is valid for specifying explicit argument indexes @@ -531,12 +532,12 @@ if s:HighlightBuildConstraints() || s:FoldEnable('package_comment') " matched as comments to avoid looking like working build constraints. " The he, me, and re options let the "package" itself be highlighted by " the usual rules. - exe 'syn region goPackageComment start=/\v(\/\/.*\n)+\s*package/' - \ . ' end=/\v\n\s*package/he=e-7,me=e-7,re=e-7' + exe 'syn region goPackageComment start=/\v(\/\/.*\n)+\s*package\s/' + \ . ' end=/\v\n\s*package\s/he=e-8,me=e-8,re=e-8' \ . ' contains=@goCommentGroup,@Spell' \ . (s:FoldEnable('package_comment') ? ' fold' : '') - exe 'syn region goPackageComment start=/\v^\s*\/\*.*\n(.*\n)*\s*\*\/\npackage/' - \ . ' end=/\v\*\/\n\s*package/he=e-7,me=e-7,re=e-7' + exe 'syn region goPackageComment start=/\v^\s*\/\*.*\n(.*\n)*\s*\*\/\npackage\s/' + \ . ' end=/\v\*\/\n\s*package\s/he=e-8,me=e-8,re=e-8' \ . ' contains=@goCommentGroup,@Spell' \ . (s:FoldEnable('package_comment') ? ' fold' : '') hi def link goPackageComment Comment diff --git a/runtime/syntax/haskell.vim b/runtime/syntax/haskell.vim index b48b278084..509aa25122 100644 --- a/runtime/syntax/haskell.vim +++ b/runtime/syntax/haskell.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Haskell " Maintainer: Haskell Cafe mailinglist <haskell-cafe@haskell.org> -" Last Change: 2020 Oct 4 by Marcin Szamotulski <profunctor@pm.me> +" Last Change: 2024 Mar 28 by Enrico Maria De Angelis <enricomaria.dean6elis@gmail.com> " Original Author: John Williams <jrw@pobox.com> " " Thanks to Ryan Crumley for suggestions and John Meacham for @@ -104,8 +104,8 @@ endif " Comments -syn match hsLineComment "---*\([^-!#$%&\*\+./<=>\?@\\^|~].*\)\?$" contains=@Spell -syn region hsBlockComment start="{-" end="-}" contains=hsBlockComment,@Spell +syn match hsLineComment "---*\([^-!#$%&\*\+./<=>\?@\\^|~].*\)\?$" contains=hsTodo,@Spell +syn region hsBlockComment start="{-" end="-}" contains=hsBlockComment,hsTodo,@Spell syn region hsPragma start="{-#" end="#-}" syn keyword hsTodo contained FIXME TODO XXX NOTE @@ -164,6 +164,7 @@ hi def link hsLiterateComment hsComment hi def link hsBlockComment hsComment hi def link hsLineComment hsComment hi def link hsComment Comment +hi def link hsTodo Todo hi def link hsPragma SpecialComment hi def link hsBoolean Boolean hi def link hsType Type diff --git a/runtime/syntax/i3config.vim b/runtime/syntax/i3config.vim index 8131639a11..f4d789e418 100644 --- a/runtime/syntax/i3config.vim +++ b/runtime/syntax/i3config.vim @@ -2,8 +2,8 @@ " Language: i3 config file " Original Author: Josef Litos (JosefLitos/i3config.vim) " Maintainer: Quentin Hibon (github user hiqua) -" Version: 1.0.2 -" Last Change: 2023-12-28 +" Version: 1.2.3 +" Last Change: 2024-05-23 " References: " http://i3wm.org/docs/userguide.html#configuring @@ -27,225 +27,246 @@ syn keyword i3ConfigTodo TODO FIXME XXX contained syn match i3ConfigSeparator /[,;\\]/ contained syn match i3ConfigParen /[{}]/ contained syn keyword i3ConfigBoolean yes no enabled disabled on off true false contained -syn region i3ConfigString start=/\W\@<="/ skip=/\\\("\|$\)/ end=/"\|$/ contained contains=i3ConfigShCommand,i3ConfigShDelim,i3ConfigShOper,i3ConfigShParam,i3ConfigNumber,i3ConfigVariable,i3ConfigExecAction keepend extend -syn region i3ConfigString start=/\W\@<='/ skip=/\\$/ end=/'\|$/ contained contains=i3ConfigShCommand,i3ConfigShDelim,i3ConfigShOper,i3ConfigShParam,i3ConfigNumber,i3ConfigVariable,i3ConfigExecAction keepend extend +" String in simpler (matchable end) and more robust (includes `extend` keyword) forms +syn cluster i3ConfigStrIn contains=i3ConfigShCommand,i3ConfigShDelim,i3ConfigShOper,i3ConfigShParam,@i3ConfigNumVar,i3ConfigExecAction +syn match i3ConfigString /\(["']\)[^\\"')\]}]*\1/ contained contains=@i3ConfigStrIn +syn region i3ConfigString start=/"[^\\"')\]}]*[\\')\]}]/ skip=/\\\@<=\("\|$\)/ end=/"\|$/ contained contains=@i3ConfigStrIn keepend extend +syn region i3ConfigString start=/'[^\\"')\]}]*[\\")\]}]/ skip=/\\\@<=$/ end=/'\|$/ contained contains=@i3ConfigStrIn keepend extend syn match i3ConfigColor /#[0-9A-Fa-f]\{3,8}/ contained syn match i3ConfigNumber /[0-9A-Za-z_$-]\@<!-\?\d\+\w\@!/ contained +" Grouping of common usages +syn cluster i3ConfigStrVar contains=i3ConfigString,i3ConfigVariable +syn cluster i3ConfigNumVar contains=i3ConfigNumber,i3ConfigVariable +syn cluster i3ConfigColVar contains=i3ConfigColor,i3ConfigVariable +syn cluster i3ConfigIdent contains=i3ConfigString,i3ConfigNumber,i3ConfigVariable +syn cluster i3ConfigValue contains=@i3ConfigIdent,i3ConfigBoolean " 4.1 Include directive -syn keyword i3ConfigIncludeKeyword include contained -syn match i3ConfigIncludeCommand /`[^`]*`/ contained contains=i3ConfigShDelim,i3ConfigShParam,i3ConfigShOper,i3ConfigShCommand,i3ConfigString -syn match i3ConfigParamLine /^include .*$/ contains=i3ConfigIncludeKeyword,i3ConfigString,i3ConfigVariable,i3ConfigIncludeCommand,i3ConfigShOper +syn match i3ConfigIncludeCommand /`[^`]*`/ contained contains=@i3ConfigSh +syn region i3ConfigParamLine matchgroup=i3ConfigKeyword start=/include / end=/$/ contained contains=@i3ConfigStrVar,i3ConfigIncludeCommand,i3ConfigShOper keepend " 4.2 Comments -syn match i3ConfigComment /^\s*#.*$/ contains=i3ConfigTodo +syn match i3ConfigComment /#.*$/ contained contains=i3ConfigTodo " 4.3 Fonts -syn keyword i3ConfigFontKeyword font contained +syn match i3ConfigFontSize / \d\+\(px\)\?$/ contained syn match i3ConfigColonOperator /:/ contained -syn match i3ConfigFontNamespace /\w\+:/ contained contains=i3ConfigColonOperator -syn match i3ConfigFontSize / \d\+\(px\)\?\s\?$/ contained -syn region i3ConfigFont start=/^\s*font / skip=/\\$/ end=/$/ contains=i3ConfigFontKeyword,i3ConfigFontNamespace,i3ConfigFontSize,i3ConfigSeparator keepend +syn match i3ConfigFontNamespace /pango:/ contained contains=i3ConfigColonOperator +syn region i3ConfigParamLine matchgroup=i3ConfigKeyword start=/font / skip=/\\$/ end=/$/ contained contains=i3ConfigFontNamespace,i3ConfigFontSize,i3ConfigSeparator keepend containedin=i3ConfigBarBlock " 4.4-4.5 Keyboard/Mouse bindings -syn keyword i3ConfigBindKeyword bindsym bindcode contained -syn match i3ConfigBindArgument /--\(release\|border\|whole-window\|exclude-titlebar\)/ contained +syn match i3ConfigBindArgument /--\(release\|border\|whole-window\|exclude-titlebar\) / contained nextgroup=i3ConfigBindArgument,i3ConfigBindCombo syn match i3ConfigBindModifier /+/ contained syn keyword i3ConfigBindModkey Ctrl Shift Mod1 Mod2 Mod3 Mod4 Mod5 contained -syn match i3ConfigBindCombo /[$0-9A-Za-z_+]\+ / contained contains=i3ConfigBindModifier,i3ConfigVariable,i3ConfigBindModkey -syn match i3ConfigBindComboLine /bind\(sym\|code\)\( --[a-z-]\+\)* [$0-9A-Za-z_+]\+ / contained contains=i3ConfigBindKeyword,i3ConfigBindArgument,i3ConfigBindCombo -syn region i3ConfigBind start=/^\s*bind\(sym\|code\) / skip=/\\$/ end=/$/ contains=i3ConfigBindComboLine,i3ConfigCriteria,i3ConfigAction,i3ConfigSeparator,i3ConfigActionKeyword,i3ConfigOption,i3ConfigString,i3ConfigNumber,i3ConfigVariable,i3ConfigBoolean keepend +syn match i3ConfigBindCombo /[$0-9A-Za-z_+]\+/ contained contains=i3ConfigBindModifier,i3ConfigVariable,i3ConfigBindModkey nextgroup=i3ConfigBind +syn cluster i3ConfigBinder contains=i3ConfigCriteria,@i3ConfigCommand,i3ConfigSeparator +syn region i3ConfigBind start=/\zs/ skip=/\\$/ end=/$/ contained contains=@i3ConfigBinder keepend +syn keyword i3ConfigBindKeyword bindsym bindcode contained skipwhite nextgroup=i3ConfigBindArgument,i3ConfigBindCombo " 4.6 Binding modes -syn region i3ConfigKeyword start=/^mode\( --pango_markup\)\? \([^'" {]\+\|'[^']\+'\|".\+"\)\s\+{$/ end=/^\s*}$/ contains=i3ConfigShParam,i3ConfigString,i3ConfigBind,i3ConfigComment,i3ConfigNumber,i3ConfigParen,i3ConfigVariable fold keepend extend +syn region i3ConfigModeBlock matchgroup=i3ConfigKeyword start=/mode\ze\( --pango_markup\)\? \([^'" {]\+\|'[^']\+'\|".\+"\)\s\+{$/ end=/^}\zs$/ contained contains=i3ConfigShParam,@i3ConfigStrVar,i3ConfigBindKeyword,i3ConfigComment,i3ConfigParen fold keepend extend " 4.7 Floating modifier syn match i3ConfigKeyword /^floating_modifier [$0-9A-Za-z]*$/ contains=i3ConfigVariable,i3ConfigBindModkey " 4.8 Floating window size syn keyword i3ConfigSizeSpecial x contained -syn match i3ConfigSize / -\?\d\+ x -\?\d\+/ contained contains=i3ConfigSizeSpecial,i3ConfigNumber -syn match i3ConfigKeyword /^floating_\(maximum\|minimum\)_size .*$/ contains=i3ConfigSize +syn match i3ConfigSize /-\?\d\+ x -\?\d\+/ contained contains=i3ConfigSizeSpecial,i3ConfigNumber +syn keyword i3ConfigKeyword floating_maximum_size floating_minimum_size contained skipwhite nextgroup=i3ConfigSize " 4.9 Orientation syn keyword i3ConfigOrientationOpts vertical horizontal auto contained -syn match i3ConfigKeyword /^default_orientation \w*$/ contains=i3ConfigOrientationOpts +syn keyword i3ConfigKeyword default_orientation contained skipwhite nextgroup=i3ConfigOrientationOpts " 4.10 Layout mode syn keyword i3ConfigWorkspaceLayoutOpts default stacking tabbed contained -syn match i3ConfigKeyword /^workspace_layout \w*$/ contains=i3ConfigWorkspaceLayoutOpts +syn keyword i3ConfigKeyword workspace_layout contained skipwhite nextgroup=i3ConfigWorkspaceLayoutOpts " 4.11 Title alignment syn keyword i3ConfigTitleAlignOpts left center right contained -syn match i3ConfigKeyword /^title_align .*$/ contains=i3ConfigTitleAlignOpts +syn keyword i3ConfigKeyword title_align contained skipwhite nextgroup=i3ConfigTitleAlignOpts -" 4.12 Border style -syn keyword i3ConfigBorderOpts none normal pixel contained -syn match i3ConfigKeyword /^default\(_floating\)\?_border .*$/ contains=i3ConfigBorderOpts,i3ConfigNumber,i3ConfigVariable +" 4.12 Border size +syn keyword i3ConfigBorderOpts none normal pixel contained skipwhite nextgroup=@i3ConfigNumVar +syn keyword i3ConfigKeyword default_floating_border default_border contained skipwhite nextgroup=i3ConfigBorderOpts " 4.13 Hide edge borders syn keyword i3ConfigEdgeOpts none vertical horizontal both smart smart_no_gaps contained -syn match i3ConfigKeyword /^hide_edge_borders \w*$/ contains=i3ConfigEdgeOpts +syn keyword i3ConfigKeyword hide_edge_borders contained skipwhite nextgroup=i3ConfigEdgeOpts " 4.14 Smart Borders syn keyword i3ConfigSmartBorderOpts no_gaps contained -syn match i3ConfigKeyword /^smart_borders \(on\|off\|no_gaps\)$/ contains=i3ConfigSmartBorderOpts,i3ConfigBoolean +syn keyword i3ConfigKeyword smart_borders contained skipwhite nextgroup=i3ConfigSmartBorderOpts,i3ConfigBoolean " 4.15 Arbitrary commands -syn region i3ConfigKeyword start=/^for_window / end=/$/ contains=i3ConfigForWindowKeyword,i3ConfigCriteria keepend +syn keyword i3ConfigKeyword for_window contained skipwhite nextgroup=i3ConfigCriteria " 4.16 No opening focus -syn match i3ConfigKeyword /^no_focus .*$/ contains=i3ConfigCondition +syn keyword i3ConfigKeyword no_focus contained skipwhite nextgroup=i3ConfigCondition " 4.17 Variables syn match i3ConfigVariable /\$[0-9A-Za-z_:|[\]-]\+/ -syn keyword i3ConfigSetKeyword set contained -syn match i3ConfigSet /^set \$.*$/ contains=i3ConfigSetKeyword,i3ConfigVariable,i3ConfigColor,i3ConfigString,i3ConfigNumber,i3ConfigShCommand,i3ConfigShDelim,i3ConfigShParam,i3ConfigShOper,i3ConfigBindModkey +syn region i3ConfigSet start=/\$/ skip=/\\$/ end=/$/ contained contains=@i3ConfigSh,@i3ConfigValue,i3ConfigColor,i3ConfigBindModkey keepend +syn keyword i3ConfigKeyword set contained skipwhite nextgroup=i3ConfigSet " 4.18 X resources -syn keyword i3ConfigResourceKeyword set_from_resource contained -syn match i3ConfigParamLine /^set_from_resource\s\+.*$/ contains=i3ConfigResourceKeyword,i3ConfigCondition,i3ConfigColor,i3ConfigVariable,i3ConfigString,i3ConfigNumber +syn region i3ConfigParamLine matchgroup=i3ConfigKeyword start=/set_from_resource\ze \$/ end=/$/ contained contains=@i3ConfigColVar,i3ConfigDotOperator " 4.19 Assign clients to workspaces -syn keyword i3ConfigAssignKeyword assign contained syn match i3ConfigAssignSpecial /→\|number/ contained -syn match i3ConfigAssign /^assign .*$/ contains=i3ConfigAssignKeyword,i3ConfigAssignSpecial,i3ConfigCondition,i3ConfigVariable,i3ConfigString,i3ConfigNumber +syn region i3ConfigKeyword start=/assign / end=/$/ contained contains=i3ConfigAssignSpecial,i3ConfigCondition,@i3ConfigIdent keepend " 4.20 Executing shell commands -syn keyword i3ConfigExecKeyword exec contained -syn keyword i3ConfigExecAlwaysKeyword exec_always contained -syn match i3ConfigShCmdDelim /\$(/ contained -syn region i3ConfigShCommand start=/\$(/ end=/)/ contained contains=i3ConfigShCmdDelim,i3ConfigExecAction,i3ConfigShCommand,i3ConfigShDelim,i3ConfigShOper,i3ConfigShParam,i3ConfigString,i3ConfigNumber,i3ConfigVariable keepend extend +syn region i3ConfigShCommand matchgroup=i3ConfigShDelim start=/\$(/ end=/)/ contained contains=i3ConfigExecAction,i3ConfigShCommand,i3ConfigShDelim,i3ConfigShOper,i3ConfigShParam,i3ConfigString,i3ConfigNumber,i3ConfigVariable extend syn match i3ConfigShDelim /[[\]{}();`]\+/ contained syn match i3ConfigShOper /[<>&|+=~^*!.?]\+/ contained -syn match i3ConfigShParam /\<-[0-9A-Za-z_-]\+\>/ contained containedin=i3ConfigVar -syn region i3ConfigExec start=/^\s*exec\(_always\)\?\( --no-startup-id\)\? [^{]/ skip=/\\$/ end=/$/ contains=i3ConfigExecKeyword,i3ConfigExecAlwaysKeyword,i3ConfigShCommand,i3ConfigShDelim,i3ConfigShOper,i3ConfigShParam,i3ConfigNumber,i3ConfigString,i3ConfigVariable,i3ConfigExecAction keepend +syn match i3ConfigShParam /\<-[A-Za-z-][0-9A-Za-z_-]*\>/ contained +syn cluster i3ConfigSh contains=@i3ConfigIdent,i3ConfigShOper,i3ConfigShDelim,i3ConfigShParam,i3ConfigShCommand +syn region i3ConfigExec start=/ \ze[^{]/ skip=/\\$/ end=/$/ contained contains=i3ConfigExecAction,@i3ConfigSh keepend +syn keyword i3ConfigKeyword exec_always exec contained nextgroup=i3ConfigExec " 4.21 Workspaces per output -syn keyword i3ConfigWorkspaceKeyword workspace contained -syn keyword i3ConfigWorkspaceOutput output contained -syn keyword i3ConfigWorkspaceDir prev next back_and_forth number contained -syn region i3ConfigWorkspaceLine start=/^workspace / skip=/\\$/ end=/$/ contains=i3ConfigWorkspaceKeyword,i3ConfigNumber,i3ConfigString,i3ConfigGaps,i3ConfigWorkspaceOutput,i3ConfigVariable,i3ConfigBoolean,i3ConfigSeparator keepend +syn match i3ConfigOutputIdent /[^'",; ]\+/ contained contains=@i3ConfigIdent,i3ConfigColonOperator skipwhite nextgroup=i3ConfigOutputIdent +syn region i3ConfigOutputIdent start=/['"]/ end=/\ze/ contained contains=@i3ConfigIdent skipwhite nextgroup=i3ConfigOutputIdent +syn keyword i3ConfigOutput output contained skipwhite nextgroup=i3ConfigOutputIdent +syn match i3ConfigWorkspaceIdent /[^'",; ]\+/ contained contains=@i3ConfigIdent skipwhite nextgroup=i3ConfigGaps,i3ConfigOutput +syn region i3ConfigWorkspaceIdent start=/['"]/ end=/\ze/ contained contains=@i3ConfigIdent skipwhite nextgroup=i3ConfigGaps,i3ConfigOutput +syn keyword i3ConfigKeyword workspace contained skipwhite nextgroup=i3ConfigWorkspaceIdent " 4.22 Changing colors -syn match i3ConfigDotOperator /\./ contained -syn keyword i3ConfigClientOpts focused focused_inactive unfocused urgent placeholder background contained -syn match i3ConfigKeyword /^client\..*$/ contains=i3ConfigDotOperator,i3ConfigClientOpts,i3ConfigColor,i3ConfigVariable +syn keyword i3ConfigClientOpts focused focused_inactive focused_tab_title unfocused urgent placeholder background contained skipwhite nextgroup=i3ConfigColorSeq +syn match i3ConfigDotOperator /\./ contained nextgroup=i3ConfigClientOpts +syn keyword i3ConfigKeyword client contained nextgroup=i3ConfigDotOperator " 4.23 Interprocess communication -syn match i3ConfigIpcKeyword /ipc-socket/ contained -syn match i3ConfigParamLine /^ipc-socket .*$/ contains=i3ConfigIpcKeyword +syn region i3ConfigParamLine matchgroup=i3ConfigKeyword start=/ipc-socket / end=/$/ contained contains=i3ConfigNumber,i3ConfigShOper " 4.24 Focus follows mouse -syn match i3ConfigKeyword /^focus_follows_mouse \(yes\|no\)$/ contains=i3ConfigBoolean +syn keyword i3ConfigFocusFollowsMouseOpts always contained +syn keyword i3ConfigKeyword focus_follows_mouse contained skipwhite nextgroup=i3ConfigBoolean,i3ConfigFocusFollowsMouseOpts " 4.25 Mouse warping syn keyword i3ConfigMouseWarpingOpts output container none contained -syn match i3ConfigKeyword /^mouse_warping \w*$/ contains=i3ConfigMouseWarpingOpts +syn keyword i3ConfigKeyword mouse_warping contained skipwhite nextgroup=i3ConfigMouseWarpingOpts " 4.26 Popups while fullscreen syn keyword i3ConfigPopupFullscreenOpts smart ignore leave_fullscreen contained -syn match i3ConfigKeyword /^popup_during_fullscreen \w*$/ contains=i3ConfigPopupFullscreenOpts +syn keyword i3ConfigKeyword popup_during_fullscreen contained skipwhite nextgroup=i3ConfigPopupFullscreenOpts " 4.27 Focus wrapping syn keyword i3ConfigFocusWrappingOpts force workspace contained -syn match i3ConfigKeyword /^focus_wrapping \(yes\|no\|force\|workspace\)$/ contains=i3ConfigBoolean,i3ConfigFocusWrappingOpts +syn keyword i3ConfigKeyword focus_wrapping contained skipwhite nextgroup=i3ConfigBoolean,i3ConfigFocusWrappingOpts " 4.28 Forcing Xinerama -syn match i3ConfigKeyword /^force_xinerama \(yes\|no\)$/ contains=i3ConfigBoolean - " 4.29 Automatic workspace back-and-forth -syn match i3ConfigKeyword /^workspace_auto_back_and_forth \(yes\|no\)$/ contains=i3ConfigBoolean +" 4.32 Show marks in title +syn keyword i3ConfigKeyword force_xinerama workspace_auto_back_and_forth show_marks contained skipwhite nextgroup=i3ConfigBoolean " 4.30 Delay urgency hint -syn keyword i3ConfigTimeUnit ms contained -syn match i3ConfigKeyword /^force_display_urgency_hint \d\+\( ms\)\?$/ contains=i3ConfigNumber,i3ConfigTimeUnit +syn match i3ConfigTimeUnit / \d\+\( ms\)\?$/ contained contains=i3ConfigNumber +syn keyword i3ConfigKeyword force_display_urgency_hint contained nextgroup=i3ConfigTimeUnit " 4.31 Focus on window activation syn keyword i3ConfigFocusOnActivationOpts smart urgent focus none contained -syn match i3ConfigKeyword /^focus_on_window_activation \w*$/ contains=i3ConfigFocusOnActivationOpts - -" 4.32 Show marks in title -syn match i3ConfigShowMarks /^show_marks \(yes\|no\)$/ contains=i3ConfigBoolean +syn keyword i3ConfigKeyword focus_on_window_activation contained skipwhite nextgroup=i3ConfigFocusOnActivationOpts " 4.34 Tiling drag -syn keyword i3ConfigTilingDragOpts modifier titlebar contained -syn match i3ConfigKeyword /^tiling_drag\( off\|\( modifier\| titlebar\)\{1,2\}\)$/ contains=i3ConfigTilingDragOpts,i3ConfigBoolean +syn keyword i3ConfigTilingDragOpts modifier titlebar contained skipwhite nextgroup=i3ConfigTilingDragOpts +syn keyword i3ConfigKeyword tiling_drag contained skipwhite nextgroup=i3ConfigTilingDragOpts,i3ConfigBoolean + +" 4.35 Gaps (+6.24) +syn keyword i3ConfigGapsWhich inner outer horizontal vertical left right top bottom contained skipwhite nextgroup=i3ConfigGapsWhere,@i3ConfigNumVar +syn keyword i3ConfigGapsWhere current all contained skipwhite nextgroup=i3ConfigGapsOper +syn keyword i3ConfigGapsOper set plus minus toggle contained skipwhite nextgroup=@i3ConfigNumVar +syn match i3ConfigGaps /gaps/ contained contains=i3ConfigCommand skipwhite nextgroup=i3ConfigGapsWhich +syn keyword i3ConfigCommand gaps contained skipwhite nextgroup=i3ConfigGapsWhich -" 4.35 Gaps -syn keyword i3ConfigGapsOpts inner outer horizontal vertical left right top bottom current all set plus minus toggle contained -syn region i3ConfigGaps start=/gaps/ skip=/\\$/ end=/[,;]\|$/ contained contains=i3ConfigGapsOpts,i3ConfigNumber,i3ConfigVariable,i3ConfigSeparator keepend -syn match i3ConfigGapsLine /^gaps .*$/ contains=i3ConfigGaps -syn keyword i3ConfigSmartGapOpts inverse_outer contained -syn match i3ConfigKeyword /^smart_gaps \(on\|off\|inverse_outer\)$/ contains=i3ConfigSmartGapOpts,i3ConfigBoolean +syn keyword i3ConfigSmartGapOpts inverse_outer toggle contained +syn keyword i3ConfigKeyword smart_gaps contained skipwhite nextgroup=i3ConfigSmartGapOpts,i3ConfigBoolean " 5 Configuring bar -syn match i3ConfigBarModifier /^\s\+modifier \S\+$/ contained contains=i3ConfigBindModifier,i3ConfigVariable,i3ConfigBindModkey,i3ConfigBarOptVals -syn keyword i3ConfigBarOpts bar i3bar_command status_command workspace_command mode hidden_state id position output tray_output tray_padding separator_symbol workspace_buttons workspace_min_width strip_workspace_numbers strip_workspace_name binding_mode_indicator padding contained +syn keyword i3ConfigBarOpts modifier contained skipwhite nextgroup=i3ConfigBindCombo,i3ConfigBarOptVals +syn keyword i3ConfigBarOpts i3bar_command status_command workspace_command contained skipwhite nextgroup=@i3ConfigSh +syn keyword i3ConfigBarOpts mode hidden_state id position output tray_output tray_padding separator_symbol workspace_buttons workspace_min_width strip_workspace_numbers strip_workspace_name binding_mode_indicator padding contained skipwhite nextgroup=i3ConfigBarOptVals,@i3ConfigValue,i3ConfigShOper syn keyword i3ConfigBarOptVals dock hide invisible show none top bottom primary nonprimary contained -syn region i3ConfigBarBlock start=/^bar {$/ end=/^}$/ contains=i3ConfigBarOpts,i3ConfigBarOptVals,i3ConfigBarModifier,i3ConfigBind,i3ConfigString,i3ConfigComment,i3ConfigFont,i3ConfigBoolean,i3ConfigNumber,i3ConfigParen,i3ConfigColor,i3ConfigVariable,i3ConfigColorsBlock,i3ConfigShOper,i3ConfigShCommand fold keepend extend +syn region i3ConfigBarBlock matchgroup=i3ConfigKeyword start=/bar\ze {$/ end=/^\s*}\zs$/ contained contains=i3ConfigBarOpts,i3ConfigComment,i3ConfigParen,i3ConfigBindKeyword,i3ConfigColorsBlock fold keepend extend " 5.16 Color block -syn keyword i3ConfigColorsKeyword colors contained -syn match i3ConfigColorsOpts /\(focused_\)\?\(background\|statusline\|separator\)\|\(focused\|active\|inactive\|urgent\)_workspace\|binding_mode/ contained -syn region i3ConfigColorsBlock start=/^\s\+colors {$/ end=/^\s\+}$/ contained contains=i3ConfigColorsKeyword,i3ConfigColorsOpts,i3ConfigColor,i3ConfigVariable,i3ConfigComment,i3ConfigParen fold keepend extend +syn match i3ConfigColorSeq /#[0-9A-Fa-f]\{3,8}\|\$[0-9A-Za-z_:|[\]-]\+/ contained contains=@i3ConfigColVar skipwhite nextgroup=i3ConfigColorSeq +syn keyword i3ConfigColorsOpts background statusline separator contained skipwhite nextgroup=@i3ConfigColVar +syn match i3ConfigColorsOpts /focused_\(background\|statusline\|separator\)\|\(focused\|active\|inactive\|urgent\)_workspace\|binding_mode/ contained skipwhite nextgroup=i3ConfigColorSeq +syn region i3ConfigColorsBlock matchgroup=i3ConfigKeyword start=/^\s\+colors \ze{$/ end=/^\s\+}\zs$/ contained contains=i3ConfigColorsOpts,i3ConfigComment,i3ConfigParen fold keepend extend " 6.0 Command criteria syn keyword i3ConfigConditionProp class instance window_role window_type machine id title urgent workspace con_mark con_id floating_from tiling_from contained syn keyword i3ConfigConditionSpecial __focused__ all floating tiling contained -syn region i3ConfigCondition start=/\[/ end=/\]/ contained contains=i3ConfigShDelim,i3ConfigConditionProp,i3ConfigShOper,i3ConfigConditionSpecial,i3ConfigNumber,i3ConfigString keepend extend -syn region i3ConfigCriteria start=/\[/ skip=/\\$/ end=/\(;\|$\)/ contained contains=i3ConfigCondition,i3ConfigAction,i3ConfigActionKeyword,i3ConfigOption,i3ConfigBoolean,i3ConfigNumber,i3ConfigVariable,i3ConfigSeparator keepend transparent +syn region i3ConfigCondition matchgroup=i3ConfigShDelim start=/\[/ end=/\]/ contained contains=i3ConfigConditionProp,i3ConfigShOper,i3ConfigConditionSpecial,@i3ConfigIdent keepend extend +syn region i3ConfigCriteria start=/\[/ skip=/\\$/ end=/\(;\|$\)/ contained contains=i3ConfigCondition,@i3ConfigCommand,i3ConfigSeparator keepend transparent " 6.1 Actions through shell syn match i3ConfigExecActionKeyword /i3-msg/ contained -syn region i3ConfigExecAction start=/[a-z3-]\+msg "/ skip=/ "\|\\$/ end=/"\|$/ contained contains=i3ConfigExecActionKeyword,i3ConfigShCommand,i3ConfigNumber,i3ConfigShOper,i3ConfigCriteria,i3ConfigAction,i3ConfigActionKeyword,i3ConfigOption,i3ConfigVariable keepend extend -syn region i3ConfigExecAction start=/[a-z3-]\+msg '/ skip=/ '\|\\$/ end=/'\|$/ contained contains=i3ConfigExecActionKeyword,i3ConfigShCommand,i3ConfigNumber,i3ConfigShOper,i3ConfigCriteria,i3ConfigAction,i3ConfigActionKeyword,i3ConfigOption,i3ConfigVariable keepend extend -syn region i3ConfigExecAction start=/[a-z3-]\+msg ['"-]\@!/ skip=/\\$/ end=/[&|;})'"]\@=\|$/ contained contains=i3ConfigExecActionKeyword,i3ConfigShCommand,i3ConfigNumber,i3ConfigShOper,i3ConfigCriteria,i3ConfigAction,i3ConfigActionKeyword,i3ConfigOption,i3ConfigVariable keepend extend +syn cluster i3ConfigExecActionVal contains=i3ConfigExecActionKeyword,i3ConfigCriteria,i3ConfigAction,i3ConfigActionKeyword,i3ConfigOption,@i3ConfigNumVar +syn region i3ConfigExecAction start=/[a-z3-]\+msg "/ skip=/ "\|\\$/ end=/"\|$/ contained contains=i3ConfigExecActionKeyword,@i3ConfigExecActionVal keepend extend +syn region i3ConfigExecAction start=/[a-z3-]\+msg '/ skip=/ '\|\\$/ end=/'\|$/ contained contains=i3ConfigExecActionKeyword,@i3ConfigExecActionVal keepend extend +syn region i3ConfigExecAction start=/[a-z3-]\+msg ['"-]\@!/ skip=/\\$/ end=/[&|;})'"]\@=\|$/ contained contains=i3ConfigExecActionKeyword,@i3ConfigExecActionVal keepend extend " 6.1 Executing applications (4.20) -syn region i3ConfigAction start=/exec/ skip=/\\$/ end=/[,;]\|$/ contained contains=i3ConfigExecKeyword,i3ConfigExecAction,i3ConfigShCommand,i3ConfigShDelim,i3ConfigShOper,i3ConfigShParam,i3ConfigNumber,i3ConfigString,i3ConfigVariable,i3ConfigSeparator keepend +syn region i3ConfigAction matchgroup=i3ConfigCommand start=/exec / skip=/\\$/ end=/\ze[,;]\|$/ contained contains=i3ConfigExecAction,@i3ConfigSh keepend " 6.3 Manipulating layout -syn keyword i3ConfigLayoutKeyword layout contained syn keyword i3ConfigLayoutOpts default tabbed stacking splitv splith toggle split all contained -syn region i3ConfigAction start=/layout/ skip=/\\$/ end=/[,;]\|$/ contained contains=i3ConfigLayoutKeyword,i3ConfigLayoutOpts,i3ConfigSeparator keepend transparent +syn region i3ConfigAction matchgroup=i3ConfigCommand start=/layout / skip=/\\$/ end=/\ze[,;]\|$/ contained contains=i3ConfigLayoutOpts keepend transparent " 6.4 Focusing containers -syn keyword i3ConfigFocusKeyword focus contained -syn keyword i3ConfigFocusOpts left right up down workspace parent child next prev sibling floating tiling mode_toggle contained -syn keyword i3ConfigFocusOutputOpts left right down up current primary nonprimary next prev contained -syn region i3ConfigFocusOutput start=/ output / skip=/\\$/ end=/[,;]\|$/ contained contains=i3ConfigWorkspaceOutput,i3ConfigFocusOutputOpts,i3ConfigString,i3ConfigNumber,i3ConfigSeparator keepend -syn match i3ConfigFocusOutputLine /^focus output .*$/ contains=i3ConfigFocusKeyword,i3ConfigFocusOutput -syn region i3ConfigAction start=/focus/ skip=/\\$/ end=/[,;]\|$/ contained contains=i3ConfigFocusKeyword,i3ConfigFocusOpts,i3ConfigFocusOutput,i3ConfigString,i3ConfigSeparator keepend transparent +syn keyword i3ConfigFocusOpts left right up down parent child next prev sibling floating tiling mode_toggle contained +syn keyword i3ConfigOutputDir left right down up current primary nonprimary next prev contained skipwhite +syn keyword i3ConfigFocusOutput output contained skipwhite nextgroup=i3ConfigOutputIdent,i3ConfigOutputDir +syn keyword i3ConfigActionKeyword focus contained skipwhite nextgroup=i3ConfigFocusOpts,i3ConfigFocusOutput +syn keyword i3ConfigKeyword focus skipwhite contained nextgroup=i3ConfigFocusOutput " 6.8 Focusing workspaces (4.21) -syn region i3ConfigAction start=/workspace / skip=/\\$/ end=/[,;]\|$/ contained contains=i3ConfigWorkspaceKeyword,i3ConfigWorkspaceDir,i3ConfigNumber,i3ConfigString,i3ConfigGaps,i3ConfigWorkspaceOutput,i3ConfigVariable,i3ConfigBoolean,i3ConfigSeparator keepend transparent +syn keyword i3ConfigWorkspaceDir prev next back_and_forth contained +syn keyword i3ConfigWorkspaceDir number contained skipwhite nextgroup=i3ConfigWorkspaceIdent +syn keyword i3ConfigActionKeyword workspace contained skipwhite nextgroup=i3ConfigWorkspaceDir,i3ConfigWorkspaceIdent " 6.8.2 Renaming workspaces -syn keyword i3ConfigRenameKeyword rename contained -syn region i3ConfigAction start=/rename workspace/ end=/[,;]\|$/ contained contains=i3ConfigRenameKeyword,i3ConfigMoveDir,i3ConfigMoveType,i3ConfigNumber,i3ConfigVariable,i3ConfigString keepend transparent +syn region i3ConfigWorkspaceFromTo start=/workspace\( .*\)\? to/ end=/\ze[,;]\|$/ contained contains=i3ConfigMoveType,@i3ConfigWorkspaceIdent keepend transparent +syn keyword i3ConfigActionKeyword rename contained skipwhite nextgroup=i3ConfigWorkspaceFromTo " 6.5,6.9-6.11 Moving containers -syn keyword i3ConfigMoveKeyword move contained -syn keyword i3ConfigMoveDir left right down up position absolute center to current contained -syn keyword i3ConfigMoveType window container workspace output mark mouse scratchpad contained -syn match i3ConfigUnit / px\| ppt/ contained -syn region i3ConfigAction start=/move/ skip=/\\$/ end=/[,;]\|$/ contained contains=i3ConfigMoveKeyword,i3ConfigMoveDir,i3ConfigMoveType,i3ConfigWorkspaceDir,i3ConfigUnit,i3ConfigNumber,i3ConfigVariable,i3ConfigString,i3ConfigSeparator,i3ConfigShParam keepend transparent +syn match i3ConfigUnit /-\?\d\+\( px\| ppt\)\?/ contained contains=i3ConfigNumber skipwhite nextgroup=i3ConfigUnit,i3ConfigResizeExtra +syn keyword i3ConfigMoveDir left right down up position contained skipwhite nextgroup=i3ConfigUnit +syn match i3ConfigMoveDir /position \(mouse\|center\)/ contained +syn keyword i3ConfigMoveDir absolute contained skipwhite nextgroup=i3ConfigMoveDir +syn keyword i3ConfigMoveDir absolute contained + +syn keyword i3ConfigMoveType mark contained skipwhite nextgroup=i3ConfigOutputIdent +syn keyword i3ConfigMoveType scratchpad contained +syn keyword i3ConfigMoveType output contained skipwhite nextgroup=i3ConfigOutputIdent,i3ConfigOutputDir +syn keyword i3ConfigMoveType workspace contained skipwhite nextgroup=i3ConfigMoveType,i3ConfigWorkspaceIdent,i3ConfigWorkspaceDir +syn keyword i3ConfigMoveType window container contained skipwhite nextgroup=i3ConfigMoveType +syn keyword i3ConfigMoveTo to contained +syn match i3ConfigMoveType /to/ contained contains=i3ConfigMoveTo skipwhite nextgroup=i3ConfigMoveType +syn match i3ConfigActionKeyword /move\( --no-auto-back-and-forth\)\?/ contained contains=i3ConfigShParam skipwhite nextgroup=i3ConfigMoveType,i3ConfigMoveDir " 6.12 Resizing containers/windows -syn keyword i3ConfigResizeKeyword resize contained -syn keyword i3ConfigResizeOpts grow shrink up down left right set width height or contained -syn region i3ConfigAction start=/resize/ skip=/\\$/ end=/[,;]\|$/ contained contains=i3ConfigResizeKeyword,i3ConfigResizeOpts,i3ConfigNumber,i3ConfigUnit,i3ConfigSeparator keepend transparent +syn keyword i3ConfigResizeExtra or height contained skipwhite nextgroup=i3ConfigUnit +syn keyword i3ConfigResizeDir up down left right width height contained skipwhite nextgroup=i3ConfigUnit +syn keyword i3ConfigResizeType grow shrink contained skipwhite nextgroup=i3ConfigResizeDir +syn keyword i3ConfigResizeType set contained skipwhite nextgroup=i3ConfigResizeDir,i3ConfigUnit +syn keyword i3ConfigActionKeyword resize contained skipwhite nextgroup=i3ConfigResizeType " 6.14 VIM-like marks -syn match i3ConfigMark /mark\( --\(add\|replace\)\( --toggle\)\?\)\?/ contained contains=i3ConfigShParam -syn region i3ConfigAction start=/\<mark/ skip=/\\$/ end=/[,;]\|$/ contained contains=i3ConfigMark,i3ConfigNumber,i3ConfigString,i3ConfigSeparator keepend transparent +syn match i3ConfigMarkOpt /--\(add\|replace\)\( --toggle\)\?/ contained contains=i3ConfigShParam skipwhite nextgroup=i3ConfigOutputIdent +syn keyword i3ConfigActionKeyword mark contained skipwhite nextgroup=i3ConfigMarkOpt,i3ConfigOutputIdent -" 6.24 Changing gaps (4.35) -syn region i3ConfigAction start=/gaps/ skip=/\\$/ end=/[,;]\|$/ contained contains=i3ConfigGaps keepend transparent +" Commands usable for direct config calls - for enforcing start of line for Commands +syn match i3ConfigTopLevelDirective /^\s*/ nextgroup=i3ConfigComment,i3ConfigKeyword,i3ConfigCommand,i3ConfigBindKeyword,i3ConfigParamLine,i3ConfigModeBlock,i3ConfigBarBlock,i3ConfigError " Commands useable in keybinds -syn keyword i3ConfigActionKeyword mode append_layout kill open fullscreen sticky split floating swap unmark show_marks title_window_icon title_format border restart reload exit scratchpad nop bar contained -syn keyword i3ConfigOption default enable disable toggle key restore current horizontal vertical auto none normal pixel show container with id con_id padding hidden_state hide dock invisible contained +syn keyword i3ConfigActionKeyword mode append_layout kill open fullscreen sticky split floating swap unmark title_window_icon title_format border restart reload exit scratchpad nop bar contained skipwhite nextgroup=i3ConfigOption,@i3ConfigValue +syn keyword i3ConfigOption default enable disable toggle key restore current horizontal vertical auto none normal pixel show container with id con_id padding hidden_state hide dock invisible contained skipwhite nextgroup=i3ConfigOption,@i3ConfigValue +" Commands usable at runtime (outside loading config) +syn cluster i3ConfigCommand contains=i3ConfigCommand,i3ConfigAction,i3ConfigActionKeyword,@i3ConfigValue,i3ConfigColor " Define the highlighting. hi def link i3ConfigError Error @@ -260,18 +281,15 @@ hi def link i3ConfigBoolean Boolean hi def link i3ConfigString String hi def link i3ConfigColor Constant hi def link i3ConfigNumber Number -hi def link i3ConfigIncludeKeyword i3ConfigKeyword hi def link i3ConfigComment Comment -hi def link i3ConfigFontKeyword i3ConfigKeyword hi def link i3ConfigColonOperator i3ConfigOperator hi def link i3ConfigFontNamespace i3ConfigOption hi def link i3ConfigFontSize i3ConfigNumber -hi def link i3ConfigFont i3ConfigString -hi def link i3ConfigBindKeyword i3ConfigKeyword hi def link i3ConfigBindArgument i3ConfigShParam hi def link i3ConfigBindModifier i3ConfigOperator hi def link i3ConfigBindModkey Special hi def link i3ConfigBindCombo SpecialChar +hi def link i3ConfigBindKeyword i3ConfigKeyword hi def link i3ConfigSizeSpecial i3ConfigOperator hi def link i3ConfigOrientationOpts i3ConfigOption hi def link i3ConfigWorkspaceLayoutOpts i3ConfigOption @@ -280,54 +298,47 @@ hi def link i3ConfigBorderOpts i3ConfigOption hi def link i3ConfigEdgeOpts i3ConfigOption hi def link i3ConfigSmartBorderOpts i3ConfigOption hi def link i3ConfigVariable Variable -hi def link i3ConfigSetKeyword i3ConfigKeyword -hi def link i3ConfigResourceKeyword i3ConfigKeyword -hi def link i3ConfigAssignKeyword i3ConfigKeyword hi def link i3ConfigAssignSpecial i3ConfigOption -hi def link i3ConfigExecKeyword i3ConfigCommand -hi def link i3ConfigExecAlwaysKeyword i3ConfigKeyword hi def link i3ConfigShParam PreProc hi def link i3ConfigShDelim Delimiter hi def link i3ConfigShOper Operator -hi def link i3ConfigShCmdDelim i3ConfigShDelim hi def link i3ConfigShCommand Normal -hi def link i3ConfigWorkspaceKeyword i3ConfigCommand -hi def link i3ConfigWorkspaceOutput i3ConfigMoveType -hi def link i3ConfigWorkspaceDir i3ConfigOption +hi def link i3ConfigOutputIdent i3ConfigParamLine +hi def link i3ConfigOutput i3ConfigMoveType +hi def link i3ConfigWorkspaceIdent i3ConfigParamLine hi def link i3ConfigDotOperator i3ConfigOperator hi def link i3ConfigClientOpts i3ConfigOption -hi def link i3ConfigIpcKeyword i3ConfigKeyword +hi def link i3ConfigFocusFollowsMouseOpts i3ConfigOption hi def link i3ConfigMouseWarpingOpts i3ConfigOption hi def link i3ConfigPopupFullscreenOpts i3ConfigOption hi def link i3ConfigFocusWrappingOpts i3ConfigOption hi def link i3ConfigTimeUnit i3ConfigNumber hi def link i3ConfigFocusOnActivationOpts i3ConfigOption -hi def link i3ConfigShowMarks i3ConfigCommand hi def link i3ConfigTilingDragOpts i3ConfigOption -hi def link i3ConfigGapsOpts i3ConfigOption -hi def link i3ConfigGaps i3ConfigCommand +hi def link i3ConfigGapsWhich i3ConfigOption +hi def link i3ConfigGapsWhere i3ConfigOption +hi def link i3ConfigGapsOper i3ConfigOption hi def link i3ConfigSmartGapOpts i3ConfigOption hi def link i3ConfigBarModifier i3ConfigKeyword hi def link i3ConfigBarOpts i3ConfigKeyword hi def link i3ConfigBarOptVals i3ConfigOption -hi def link i3ConfigColorsKeyword i3ConfigKeyword hi def link i3ConfigColorsOpts i3ConfigOption hi def link i3ConfigConditionProp i3ConfigShParam hi def link i3ConfigConditionSpecial Constant hi def link i3ConfigExecActionKeyword i3ConfigShCommand hi def link i3ConfigExecAction i3ConfigString -hi def link i3ConfigLayoutKeyword i3ConfigCommand hi def link i3ConfigLayoutOpts i3ConfigOption -hi def link i3ConfigFocusKeyword i3ConfigCommand hi def link i3ConfigFocusOpts i3ConfigOption -hi def link i3ConfigFocusOutputOpts i3ConfigOption -hi def link i3ConfigRenameKeyword i3ConfigCommand -hi def link i3ConfigMoveKeyword i3ConfigCommand +hi def link i3ConfigOutputDir i3ConfigOption +hi def link i3ConfigFocusOutput i3ConfigOutput +hi def link i3ConfigWorkspaceDir i3ConfigOption hi def link i3ConfigMoveDir i3ConfigOption hi def link i3ConfigMoveType Constant +hi def link i3ConfigMoveTo i3ConfigOption hi def link i3ConfigUnit i3ConfigNumber -hi def link i3ConfigResizeKeyword i3ConfigCommand -hi def link i3ConfigResizeOpts i3ConfigOption +hi def link i3ConfigResizeExtra i3ConfigOption +hi def link i3ConfigResizeDir i3ConfigOption +hi def link i3ConfigResizeType i3ConfigOption hi def link i3ConfigMark i3ConfigCommand hi def link i3ConfigActionKeyword i3ConfigCommand hi def link i3ConfigOption Type diff --git a/runtime/syntax/java.vim b/runtime/syntax/java.vim index f6d2660277..9867b147c2 100644 --- a/runtime/syntax/java.vim +++ b/runtime/syntax/java.vim @@ -1,8 +1,9 @@ " Vim syntax file -" Language: Java -" Maintainer: Claudio Fleiner <claudio@fleiner.com> -" URL: https://github.com/fleiner/vim/blob/master/runtime/syntax/java.vim -" Last Change: 2024 Mar 02 +" Language: Java +" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com> +" Former Maintainer: Claudio Fleiner <claudio@fleiner.com> +" Repository: https://github.com/zzzyxwvut/java-vim.git +" Last Change: 2024 May 10 " Please check :help java.vim for comments on some of the options available. @@ -19,6 +20,34 @@ endif let s:cpo_save = &cpo set cpo&vim +"""" STRIVE TO REMAIN COMPATIBLE FOR AT LEAST VIM 7.0. +let s:ff = {} + +function! s:ff.LeftConstant(x, y) abort + return a:x +endfunction + +function! s:ff.RightConstant(x, y) abort + return a:y +endfunction + +if !exists("*s:ReportOnce") + function s:ReportOnce(message) abort + echomsg 'syntax/java.vim: ' . a:message + endfunction +else + function! s:ReportOnce(dummy) + endfunction +endif + +" Admit the ASCII dollar sign to keyword characters (JLS-17, §3.8): +try + exec 'syntax iskeyword ' . &l:iskeyword . ',$' +catch /\<E410:/ + call s:ReportOnce(v:exception) + setlocal iskeyword+=$ +endtry + " some characters that cannot be in a java program (outside a string) syn match javaError "[\\@`]" syn match javaError "<<<\|\.\.\|=>\|||=\|&&=\|\*\/" @@ -27,16 +56,18 @@ syn match javaError "<<<\|\.\.\|=>\|||=\|&&=\|\*\/" syn match javaError2 "#\|=<" hi def link javaError2 javaError -" keyword definitions +" Keywords (JLS-17, §3.9): syn keyword javaExternal native package -syn match javaExternal "\<import\>\(\s\+static\>\)\?" +syn match javaExternal "\<import\>\%(\s\+static\>\)\=" syn keyword javaError goto const syn keyword javaConditional if else switch syn keyword javaRepeat while for do syn keyword javaBoolean true false syn keyword javaConstant null syn keyword javaTypedef this super -syn keyword javaOperator var new instanceof +syn keyword javaOperator new instanceof +syn match javaOperator "\<var\>\%(\s*(\)\@!" + " Since the yield statement, which could take a parenthesised operand, " and _qualified_ yield methods get along within the switch block " (JLS-17, §3.8), it seems futile to make a region definition for this @@ -44,38 +75,63 @@ syn keyword javaOperator var new instanceof " backtrack (arbitrarily) 80 bytes, at most, on the matched line and, " if necessary, on the line before that (h: \@<=), trying to match " neither a method reference nor a qualified method invocation. -syn match javaOperator "\%(\%(::\|\.\)[[:space:]\n]*\)\@80<!\<yield\>" +try + syn match javaOperator "\%(\%(::\|\.\)[[:space:]\n]*\)\@80<!\<yield\>" + let s:ff.Peek = s:ff.LeftConstant +catch /\<E59:/ + call s:ReportOnce(v:exception) + syn match javaOperator "\%(\%(::\|\.\)[[:space:]\n]*\)\@<!\<yield\>" + let s:ff.Peek = s:ff.RightConstant +endtry + syn keyword javaType boolean char byte short int long float double syn keyword javaType void syn keyword javaStatement return -syn keyword javaStorageClass static synchronized transient volatile final strictfp serializable +syn keyword javaStorageClass static synchronized transient volatile strictfp serializable syn keyword javaExceptions throw try catch finally syn keyword javaAssert assert -syn keyword javaMethodDecl synchronized throws -syn keyword javaClassDecl extends implements interface -" to differentiate the keyword class from MyClass.class we use a match here +syn keyword javaMethodDecl throws +" Differentiate a "MyClass.class" literal from the keyword "class". syn match javaTypedef "\.\s*\<class\>"ms=s+1 -syn keyword javaClassDecl enum +syn keyword javaClassDecl enum extends implements interface +syn match javaClassDecl "\<permits\>\%(\s*(\)\@!" +syn match javaClassDecl "\<record\>\%(\s*(\)\@!" syn match javaClassDecl "^class\>" syn match javaClassDecl "[^.]\s*\<class\>"ms=s+1 -syn match javaAnnotation "@\([_$a-zA-Z][_$a-zA-Z0-9]*\.\)*[_$a-zA-Z][_$a-zA-Z0-9]*\>" contains=javaString +syn match javaAnnotation "@\%(\K\k*\.\)*\K\k*\>" syn match javaClassDecl "@interface\>" syn keyword javaBranch break continue nextgroup=javaUserLabelRef skipwhite syn match javaUserLabelRef "\k\+" contained syn match javaVarArg "\.\.\." -syn keyword javaScopeDecl public protected private abstract +syn keyword javaScopeDecl public protected private +syn keyword javaConceptKind abstract final +syn match javaConceptKind "\<non-sealed\>" +syn match javaConceptKind "\<sealed\>\%(\s*(\)\@!" syn match javaConceptKind "\<default\>\%(\s*\%(:\|->\)\)\@!" -function s:isModuleInfoDeclarationCurrentBuffer() abort - return fnamemodify(bufname("%"), ":t") =~ '^module-info\%(\.class\>\)\@!' -endfunction +" Note that a "module-info" file will be recognised with an arbitrary +" file extension (or no extension at all) so that more than one such +" declaration for the same Java module can be maintained for modular +" testing in a project without attendant confusion for IDEs, with the +" ".java\=" extension used for a production version and an arbitrary +" extension used for a testing version. +let s:module_info_cur_buf = fnamemodify(bufname("%"), ":t") =~ '^module-info\%(\.class\>\)\@!' +lockvar s:module_info_cur_buf + +if !(v:version < 704) + " Request the new regexp engine for [:upper:] and [:lower:]. + let [s:ff.Engine, s:ff.UpperCase, s:ff.LowerCase] = repeat([s:ff.LeftConstant], 3) +else + " XXX: \C\<[^a-z0-9]\k*\> rejects "type", but matches "τύπος". + " XXX: \C\<[^A-Z0-9]\k*\> rejects "Method", but matches "Μέθοδος". + let [s:ff.Engine, s:ff.UpperCase, s:ff.LowerCase] = repeat([s:ff.RightConstant], 3) +endif -" Java Modules(Since Java 9, for "module-info.java" file) -if s:isModuleInfoDeclarationCurrentBuffer() - syn keyword javaModuleStorageClass module transitive - syn keyword javaModuleStmt open requires exports opens uses provides - syn keyword javaModuleExternal to with - syn cluster javaTop add=javaModuleStorageClass,javaModuleStmt,javaModuleExternal +" Java modules (since Java 9, for "module-info.java" file). +if s:module_info_cur_buf + syn keyword javaModuleStorageClass module transitive + syn keyword javaModuleStmt open requires exports opens uses provides + syn keyword javaModuleExternal to with endif if exists("java_highlight_java_lang_ids") @@ -91,35 +147,33 @@ if exists("java_highlight_all") || exists("java_highlight_java") || exists("ja " keywords can be pre-sorted and appended without disturbing " the current keyword placement. The below _match_es follow suit. - syn keyword javaR_JavaLang ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException ClassCastException IllegalArgumentException IllegalMonitorStateException IllegalThreadStateException IndexOutOfBoundsException NegativeArraySizeException NullPointerException NumberFormatException RuntimeException SecurityException StringIndexOutOfBoundsException IllegalStateException UnsupportedOperationException EnumConstantNotPresentException TypeNotPresentException IllegalCallerException LayerInstantiationException - syn cluster javaTop add=javaR_JavaLang + syn keyword javaR_JavaLang ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException ClassCastException IllegalArgumentException IllegalMonitorStateException IllegalThreadStateException IndexOutOfBoundsException NegativeArraySizeException NullPointerException NumberFormatException RuntimeException SecurityException StringIndexOutOfBoundsException IllegalStateException UnsupportedOperationException EnumConstantNotPresentException TypeNotPresentException IllegalCallerException LayerInstantiationException WrongThreadException MatchException syn cluster javaClasses add=javaR_JavaLang hi def link javaR_JavaLang javaR_Java " Member enumerations: - syn match javaC_JavaLang "\%(\<Thread\.\)\@<=\<State\>" - syn match javaC_JavaLang "\%(\<Character\.\)\@<=\<UnicodeScript\>" - syn match javaC_JavaLang "\%(\<ProcessBuilder\.Redirect\.\)\@<=\<Type\>" - syn match javaC_JavaLang "\%(\<StackWalker\.\)\@<=\<Option\>" - syn match javaC_JavaLang "\%(\<System\.Logger\.\)\@<=\<Level\>" + exec 'syn match javaC_JavaLang "\%(\<Thread\.\)\@' . s:ff.Peek('7', '') . '<=\<State\>"' + exec 'syn match javaC_JavaLang "\%(\<Character\.\)\@' . s:ff.Peek('10', '') . '<=\<UnicodeScript\>"' + exec 'syn match javaC_JavaLang "\%(\<ProcessBuilder\.Redirect\.\)\@' . s:ff.Peek('24', '') . '<=\<Type\>"' + exec 'syn match javaC_JavaLang "\%(\<StackWalker\.\)\@' . s:ff.Peek('12', '') . '<=\<Option\>"' + exec 'syn match javaC_JavaLang "\%(\<System\.Logger\.\)\@' . s:ff.Peek('14', '') . '<=\<Level\>"' " Member classes: - syn match javaC_JavaLang "\%(\<Character\.\)\@<=\<Subset\>" - syn match javaC_JavaLang "\%(\<Character\.\)\@<=\<UnicodeBlock\>" - syn match javaC_JavaLang "\%(\<ProcessBuilder\.\)\@<=\<Redirect\>" - syn match javaC_JavaLang "\%(\<ModuleLayer\.\)\@<=\<Controller\>" - syn match javaC_JavaLang "\%(\<Runtime\.\)\@<=\<Version\>" - syn match javaC_JavaLang "\%(\<System\.\)\@<=\<LoggerFinder\>" - syn match javaC_JavaLang "\%(\<Enum\.\)\@<=\<EnumDesc\>" + exec 'syn match javaC_JavaLang "\%(\<Character\.\)\@' . s:ff.Peek('10', '') . '<=\<Subset\>"' + exec 'syn match javaC_JavaLang "\%(\<Character\.\)\@' . s:ff.Peek('10', '') . '<=\<UnicodeBlock\>"' + exec 'syn match javaC_JavaLang "\%(\<ProcessBuilder\.\)\@' . s:ff.Peek('15', '') . '<=\<Redirect\>"' + exec 'syn match javaC_JavaLang "\%(\<ModuleLayer\.\)\@' . s:ff.Peek('12', '') . '<=\<Controller\>"' + exec 'syn match javaC_JavaLang "\%(\<Runtime\.\)\@' . s:ff.Peek('8', '') . '<=\<Version\>"' + exec 'syn match javaC_JavaLang "\%(\<System\.\)\@' . s:ff.Peek('7', '') . '<=\<LoggerFinder\>"' + exec 'syn match javaC_JavaLang "\%(\<Enum\.\)\@' . s:ff.Peek('5', '') . '<=\<EnumDesc\>"' syn keyword javaC_JavaLang Boolean Character Class ClassLoader Compiler Double Float Integer Long Math Number Object Process Runtime SecurityManager String StringBuffer Thread ThreadGroup Byte Short Void InheritableThreadLocal Package RuntimePermission ThreadLocal StrictMath StackTraceElement Enum ProcessBuilder StringBuilder ClassValue Module ModuleLayer StackWalker Record syn match javaC_JavaLang "\<System\>" " See javaDebug. - syn cluster javaTop add=javaC_JavaLang + " As of JDK 21, java.lang.Compiler is no more (deprecated in JDK 9). + syn keyword javaLangDeprecated Compiler syn cluster javaClasses add=javaC_JavaLang hi def link javaC_JavaLang javaC_Java syn keyword javaE_JavaLang AbstractMethodError ClassCircularityError ClassFormatError Error IllegalAccessError IncompatibleClassChangeError InstantiationError InternalError LinkageError NoClassDefFoundError NoSuchFieldError NoSuchMethodError OutOfMemoryError StackOverflowError ThreadDeath UnknownError UnsatisfiedLinkError VerifyError VirtualMachineError ExceptionInInitializerError UnsupportedClassVersionError AssertionError BootstrapMethodError - syn cluster javaTop add=javaE_JavaLang syn cluster javaClasses add=javaE_JavaLang hi def link javaE_JavaLang javaE_Java syn keyword javaX_JavaLang ClassNotFoundException CloneNotSupportedException Exception IllegalAccessException InstantiationException InterruptedException NoSuchMethodException Throwable NoSuchFieldException ReflectiveOperationException - syn cluster javaTop add=javaX_JavaLang syn cluster javaClasses add=javaX_JavaLang hi def link javaX_JavaLang javaX_Java @@ -135,10 +189,9 @@ if exists("java_highlight_all") || exists("java_highlight_java") || exists("ja syn keyword javaLangObject clone equals finalize getClass hashCode syn keyword javaLangObject notify notifyAll toString wait hi def link javaLangObject javaConstant - syn cluster javaTop add=javaLangObject endif -if filereadable(expand("<sfile>:p:h")."/javaid.vim") +if filereadable(expand("<sfile>:p:h") . "/javaid.vim") source <sfile>:p:h/javaid.vim endif @@ -151,7 +204,7 @@ if exists("java_space_errors") endif endif -syn match javaUserLabel "^\s*\<\K\k*\>\%(\<default\>\)\@<!\s*:"he=e-1 +exec 'syn match javaUserLabel "^\s*\<\K\k*\>\%(\<default\>\)\@' . s:ff.Peek('7', '') . '<!\s*:"he=e-1' syn region javaLabelRegion transparent matchgroup=javaLabel start="\<case\>" matchgroup=NONE end=":\|->" contains=javaLabelCastType,javaLabelNumber,javaCharacter,javaString,javaConstant,@javaClasses,javaLabelDefault,javaLabelVarType,javaLabelWhenClause syn region javaLabelRegion transparent matchgroup=javaLabel start="\<default\>\%(\s*\%(:\|->\)\)\@=" matchgroup=NONE end=":\|->" oneline " Consider grouped _default_ _case_ labels, i.e. @@ -170,13 +223,6 @@ hi def link javaLabelVarType javaOperator hi def link javaLabelNumber javaNumber hi def link javaLabelCastType javaType -" highlighting C++ keywords as errors removed, too many people find it -" annoying. Was: if !exists("java_allow_cpp_keywords") - -" The following cluster contains all java groups except the contained ones -syn cluster javaTop add=javaExternal,javaError,javaBranch,javaLabelRegion,javaConditional,javaRepeat,javaBoolean,javaConstant,javaTypedef,javaOperator,javaType,javaStatement,javaStorageClass,javaAssert,javaExceptions,javaMethodDecl,javaClassDecl,javaScopeDecl,javaConceptKind,javaError2,javaUserLabel,javaLangObject,javaAnnotation,javaVarArg - - " Comments syn keyword javaTodo contained TODO FIXME XXX @@ -187,39 +233,56 @@ if exists("java_comment_strings") syn match javaCommentCharacter contained "'\\[^']\{1,6\}'" contains=javaSpecialChar syn match javaCommentCharacter contained "'\\''" contains=javaSpecialChar syn match javaCommentCharacter contained "'[^\\]'" - syn cluster javaCommentSpecial add=javaCommentString,javaCommentCharacter,javaNumber - syn cluster javaCommentSpecial2 add=javaComment2String,javaCommentCharacter,javaNumber + syn cluster javaCommentSpecial add=javaCommentString,javaCommentCharacter,javaNumber,javaStrTempl + syn cluster javaCommentSpecial2 add=javaComment2String,javaCommentCharacter,javaNumber,javaStrTempl endif -syn region javaComment start="/\*" end="\*/" contains=@javaCommentSpecial,javaTodo,@Spell +syn region javaComment matchgroup=javaCommentStart start="/\*" end="\*/" contains=@javaCommentSpecial,javaTodo,javaCommentError,javaSpaceError,@Spell syn match javaCommentStar contained "^\s*\*[^/]"me=e-1 syn match javaCommentStar contained "^\s*\*$" -syn match javaLineComment "//.*" contains=@javaCommentSpecial2,javaTodo,@Spell +syn match javaLineComment "//.*" contains=@javaCommentSpecial2,javaTodo,javaCommentMarkupTag,javaSpaceError,@Spell +syn match javaCommentMarkupTag contained "@\%(end\|highlight\|link\|replace\|start\)\>" nextgroup=javaCommentMarkupTagAttr,javaSpaceError skipwhite +syn match javaCommentMarkupTagAttr contained "\<region\>" nextgroup=javaCommentMarkupTagAttr,javaSpaceError skipwhite +exec 'syn region javaCommentMarkupTagAttr contained transparent matchgroup=htmlArg start=/\<\%(re\%(gex\|gion\|placement\)\|substring\|t\%(arget\|ype\)\)\%(\s*=\)\@=/ matchgroup=htmlString end=/\%(=\s*\)\@' . s:ff.Peek('80', '') . '<=\%("[^"]\+"\|' . "\x27[^\x27]\\+\x27" . '\|\%([.-]\|\k\)\+\)/ nextgroup=javaCommentMarkupTagAttr,javaSpaceError skipwhite oneline' +hi def link javaCommentMarkupTagAttr htmlArg hi def link javaCommentString javaString hi def link javaComment2String javaString hi def link javaCommentCharacter javaCharacter - -syn cluster javaTop add=javaComment,javaLineComment +syn match javaCommentError contained "/\*"me=e-1 display +hi def link javaCommentError javaError +hi def link javaCommentStart javaComment if !exists("java_ignore_javadoc") && main_syntax != 'jsp' syntax case ignore - " syntax coloring for javadoc comments (HTML) + + " Include HTML syntax coloring for Javadoc comments. syntax include @javaHtml syntax/html.vim unlet b:current_syntax - " HTML enables spell checking for all text that is not in a syntax item. This - " is wrong for Java (all identifiers would be spell-checked), so it's undone - " here. - syntax spell default - - syn region javaDocComment start="/\*\*" end="\*/" keepend contains=javaCommentTitle,@javaHtml,javaDocTags,javaDocSeeTag,javaTodo,@Spell - syn region javaCommentTitle contained matchgroup=javaDocComment start="/\*\*" matchgroup=javaCommentTitle keepend end="\.$" end="\.[ \t\r<&]"me=e-1 end="[^{]@"me=s-2,he=s-1 end="\*/"me=s-1,he=s-1 contains=@javaHtml,javaCommentStar,javaTodo,@Spell,javaDocTags,javaDocSeeTag - - syn region javaDocTags contained start="{@\(code\|link\|linkplain\|inherit[Dd]oc\|doc[rR]oot\|value\)" end="}" - syn match javaDocTags contained "@\(param\|exception\|throws\|since\)\s\+\S\+" contains=javaDocParam - syn match javaDocParam contained "\s\S\+" - syn match javaDocTags contained "@\(version\|author\|return\|deprecated\|serial\|serialField\|serialData\)\>" - syn region javaDocSeeTag contained matchgroup=javaDocTags start="@see\s\+" matchgroup=NONE end="\_."re=e-1 contains=javaDocSeeTagParam - syn match javaDocSeeTagParam contained @"\_[^"]\+"\|<a\s\+\_.\{-}</a>\|\(\k\|\.\)*\(#\k\+\((\_[^)]\+)\)\=\)\=@ extend + + " HTML enables spell checking for all text that is not in a syntax + " item (:syntax spell toplevel); instead, limit spell checking to + " items matchable with syntax groups containing the @Spell cluster. + try + syntax spell default + catch /\<E390:/ + call s:ReportOnce(v:exception) + endtry + + syn region javaDocComment start="/\*\*" end="\*/" keepend contains=javaCommentTitle,@javaHtml,javaDocTags,javaDocSeeTag,javaDocCodeTag,javaDocSnippetTag,javaTodo,javaCommentError,javaSpaceError,@Spell + exec 'syn region javaCommentTitle contained matchgroup=javaDocComment start="/\*\*" matchgroup=javaCommentTitle end="\.$" end="\.[ \t\r]\@=" end="\%(^\s*\**\s*\)\@' . s:ff.Peek('80', '') . '<=@"me=s-2,he=s-1 end="\*/"me=s-1,he=s-1 contains=@javaHtml,javaCommentStar,javaTodo,javaCommentError,javaSpaceError,@Spell,javaDocTags,javaDocSeeTag,javaDocCodeTag,javaDocSnippetTag' + syn region javaCommentTitle contained matchgroup=javaDocComment start="/\*\*\s*\r\=\n\=\s*\**\s*\%({@return\>\)\@=" matchgroup=javaCommentTitle end="}\%(\s*\.*\)*" contains=@javaHtml,javaCommentStar,javaTodo,javaCommentError,javaSpaceError,@Spell,javaDocTags,javaDocSeeTag,javaDocCodeTag,javaDocSnippetTag + syn region javaDocTags contained start="{@\%(li\%(teral\|nk\%(plain\)\=\)\|inherit[Dd]oc\|doc[rR]oot\|value\)\>" end="}" + syn match javaDocTags contained "@\%(param\|exception\|throws\|since\)\s\+\S\+" contains=javaDocParam + syn match javaDocParam contained "\s\S\+" + syn match javaDocTags contained "@\%(version\|author\|return\|deprecated\|serial\%(Field\|Data\)\=\)\>" + syn region javaDocSeeTag contained matchgroup=javaDocTags start="@see\s\+" matchgroup=NONE end="\_."re=e-1 contains=javaDocSeeTagParam + syn match javaDocSeeTagParam contained @"\_[^"]\+"\|<a\s\+\_.\{-}</a>\|\%(\k\|\.\)*\%(#\k\+\%((\_[^)]*)\)\=\)\=@ contains=@javaHtml extend + syn region javaCodeSkipBlock contained transparent start="{\%(@code\>\)\@!" end="}" contains=javaCodeSkipBlock,javaDocCodeTag + syn region javaDocCodeTag contained start="{@code\>" end="}" contains=javaDocCodeTag,javaCodeSkipBlock + exec 'syn region javaDocSnippetTagAttr contained transparent matchgroup=htmlArg start=/\<\%(class\|file\|id\|lang\|region\)\%(\s*=\)\@=/ matchgroup=htmlString end=/:$/ end=/\%(=\s*\)\@' . s:ff.Peek('80', '') . '<=\%("[^"]\+"\|' . "\x27[^\x27]\\+\x27" . '\|\%([.\\/-]\|\k\)\+\)/ nextgroup=javaDocSnippetTagAttr skipwhite skipnl' + syn region javaSnippetSkipBlock contained transparent start="{\%(@snippet\>\)\@!" end="}" contains=javaSnippetSkipBlock,javaDocSnippetTag,javaCommentMarkupTag + syn region javaDocSnippetTag contained start="{@snippet\>" end="}" contains=javaDocSnippetTag,javaSnippetSkipBlock,javaDocSnippetTagAttr,javaCommentMarkupTag + syntax case match endif @@ -234,8 +297,9 @@ syn match javaSpecialChar contained "\\\%(u\x\x\x\x\|[0-3]\o\o\|\o\o\=\|[bstn syn region javaString start=+"+ end=+"+ end=+$+ contains=javaSpecialChar,javaSpecialError,@Spell syn region javaString start=+"""[ \t\x0c\r]*$+hs=e+1 end=+"""+he=s-1 contains=javaSpecialChar,javaSpecialError,javaTextBlockError,@Spell syn match javaTextBlockError +"""\s*"""+ -" The next line is commented out, it can cause a crash for a long line -"syn match javaStringError +"\%([^"\\]\|\\.\)*$+ +syn region javaStrTemplEmbExp contained matchgroup=javaStrTempl start="\\{" end="}" contains=TOP +exec 'syn region javaStrTempl start=+\%(\.[[:space:]\n]*\)\@' . s:ff.Peek('80', '') . '<="+ end=+"+ contains=javaStrTemplEmbExp,javaSpecialChar,javaSpecialError,@Spell' +exec 'syn region javaStrTempl start=+\%(\.[[:space:]\n]*\)\@' . s:ff.Peek('80', '') . '<="""[ \t\x0c\r]*$+hs=e+1 end=+"""+he=s-1 contains=javaStrTemplEmbExp,javaSpecialChar,javaSpecialError,javaTextBlockError,@Spell' syn match javaCharacter "'[^']*'" contains=javaSpecialChar,javaSpecialCharError syn match javaCharacter "'\\''" contains=javaSpecialChar syn match javaCharacter "'[^\\]'" @@ -254,25 +318,38 @@ syn match javaNumber "\<0[xX]\%(\x\%(_*\x\)*\.\=\|\%(\x\%(_*\x\)*\)\=\.\x\%( " Unicode characters syn match javaSpecial "\\u\x\x\x\x" -syn cluster javaTop add=javaString,javaCharacter,javaNumber,javaSpecial,javaStringError,javaTextBlockError - +" Method declarations (JLS-17, §8.4.3, §8.4.4, §9.4). if exists("java_highlight_functions") - if java_highlight_functions == "indent" - syn match javaFuncDef "^\(\t\| \{8\}\)[_$a-zA-Z][_$a-zA-Z0-9_. \[\]<>]*([^-+*/]*)" contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation - syn region javaFuncDef start=+^\(\t\| \{8\}\)[$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*,\s*+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation - syn match javaFuncDef "^ [$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*)" contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation - syn region javaFuncDef start=+^ [$_a-zA-Z][$_a-zA-Z0-9_. \[\]<>]*([^-+*/]*,\s*+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,@javaClasses,javaAnnotation + syn cluster javaFuncParams contains=javaAnnotation,@javaClasses,javaType,javaVarArg,javaComment,javaLineComment + + if java_highlight_functions =~# '^indent[1-8]\=$' + let s:last = java_highlight_functions[-1 :] + let s:indent = s:last != 't' ? repeat("\x20", s:last) : "\t" + syn cluster javaFuncParams add=javaScopeDecl,javaConceptKind,javaStorageClass,javaExternal + " Try to not match other type members, initialiser blocks, enum + " constants (JLS-17, §8.9.1), and constructors (JLS-17, §8.1.7): + " at any _conventional_ indentation, skip over all fields with + " "[^=]*", all records with "\<record\s", and let the "*Skip*" + " definitions take care of constructor declarations and enum + " constants (with no support for @Foo(value = "bar")). + exec 'syn region javaFuncDef start=+^' . s:indent . '\%(<[^>]\+>\+\s\+\|\%(\%(@\%(\K\k*\.\)*\K\k*\>\)\s\+\)\+\)\=\%(\<\K\k*\>\.\)*\K\k*\>[^=]*\%(\<record\)\@' . s:ff.Peek('6', '') . '<!\s\K\k*\s*(+ end=+)+ contains=@javaFuncParams' + " As long as package-private constructors cannot be matched with + " javaFuncDef, do not look with javaConstructorSkipDeclarator for + " them. + exec 'syn match javaConstructorSkipDeclarator transparent +^' . s:indent . '\%(\%(@\%(\K\k*\.\)*\K\k*\>\)\s\+\)*p\%(ublic\|rotected\|rivate\)\s\+\%(<[^>]\+>\+\s\+\)\=\K\k*\s*\ze(+ contains=javaAnnotation,javaScopeDecl' + exec 'syn match javaEnumSkipArgumentativeConstant transparent +^' . s:indent . '\%(\%(@\%(\K\k*\.\)*\K\k*\>\)\s\+\)*\K\k*\s*\ze(+ contains=javaAnnotation' + unlet s:indent s:last else - " This line catches method declarations at any indentation>0, but it assumes - " two things: - " 1. class names are always capitalized (ie: Button) - " 2. method names are never capitalized (except constructors, of course) - "syn region javaFuncDef start=+^\s\+\(\(public\|protected\|private\|static\|abstract\|final\|native\|synchronized\)\s\+\)*\(\(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\(<[^>]*>\)\=\(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*([^0-9]+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses - syn region javaFuncDef start=+^\s\+\%(\%(public\|protected\|private\|static\|\%(abstract\|default\)\|final\|native\|synchronized\)\s\+\)*\%(<.*>\s\+\)\?\%(\%(void\|boolean\|char\|byte\|short\|int\|long\|float\|double\|\%([A-Za-z_][A-Za-z0-9_$]*\.\)*[A-Z][A-Za-z0-9_$]*\)\%(<[^(){}]*>\)\=\%(\[\]\)*\s\+[a-z][A-Za-z0-9_$]*\|[A-Z][A-Za-z0-9_$]*\)\s*(+ end=+)+ contains=javaScopeDecl,javaType,javaStorageClass,javaComment,javaLineComment,@javaClasses,javaAnnotation + " This is the "style" variant (:help ft-java-syntax). + syn cluster javaFuncParams add=javaScopeDecl,javaConceptKind,javaStorageClass,javaExternal + + " Match arbitrarily indented camelCasedName method declarations. + " Match: [@ɐ] [abstract] [<α, β>] Τʬ[<γ>][[][]] μʭʭ(/* ... */); + exec 'syn region javaFuncDef start=/' . s:ff.Engine('\%#=2', '') . '^\s\+\%(\%(@\%(\K\k*\.\)*\K\k*\>\)\s\+\)*\%(p\%(ublic\|rotected\|rivate\)\s\+\)\=\%(\%(abstract\|default\)\s\+\|\%(\%(final\|\%(native\|strictfp\)\|s\%(tatic\|ynchronized\)\)\s\+\)*\)\=\%(<.*[[:space:]-]\@' . s:ff.Peek('1', '') . '<!>\s\+\)\=\%(void\|\%(b\%(oolean\|yte\)\|char\|short\|int\|long\|float\|double\|\%(\<\K\k*\>\.\)*\<' . s:ff.UpperCase('[$_[:upper:]]', '[^a-z0-9]') . '\k*\>\%(<[^(){}]*[[:space:]-]\@' . s:ff.Peek('1', '') . '<!>\)\=\)\%(\[\]\)*\)\s\+\<' . s:ff.LowerCase('[$_[:lower:]]', '[^A-Z0-9]') . '\k*\>\s*(/ end=/)/ skip=/\/\*.\{-}\*\/\|\/\/.*$/ contains=@javaFuncParams' endif - syn match javaLambdaDef "\<\K\k*\>\%(\<default\>\)\@<!\s*->" - syn match javaBraces "[{}]" - syn cluster javaTop add=javaFuncDef,javaBraces,javaLambdaDef + + exec 'syn match javaLambdaDef "\<\K\k*\>\%(\<default\>\)\@' . s:ff.Peek('7', '') . '<!\s*->"' + syn match javaBraces "[{}]" endif if exists("java_highlight_debug") @@ -280,8 +357,11 @@ if exists("java_highlight_debug") syn match javaDebugSpecial contained "\\\%(u\x\x\x\x\|[0-3]\o\o\|\o\o\=\|[bstnfr"'\\]\)" syn region javaDebugString contained start=+"+ end=+"+ contains=javaDebugSpecial syn region javaDebugString contained start=+"""[ \t\x0c\r]*$+hs=e+1 end=+"""+he=s-1 contains=javaDebugSpecial,javaDebugTextBlockError -" The next line is commented out, it can cause a crash for a long line -" syn match javaDebugStringError contained +"\%([^"\\]\|\\.\)*$+ + " The highlight groups of java{StrTempl,Debug{,Paren,StrTempl}}\, + " share one colour by default. Do not conflate unrelated parens. + syn region javaDebugStrTemplEmbExp contained matchgroup=javaDebugStrTempl start="\\{" end="}" contains=javaComment,javaLineComment,javaDebug\%(Paren\)\@!.* + exec 'syn region javaDebugStrTempl contained start=+\%(\.[[:space:]\n]*\)\@' . s:ff.Peek('80', '') . '<="+ end=+"+ contains=javaDebugStrTemplEmbExp,javaDebugSpecial' + exec 'syn region javaDebugStrTempl contained start=+\%(\.[[:space:]\n]*\)\@' . s:ff.Peek('80', '') . '<="""[ \t\x0c\r]*$+hs=e+1 end=+"""+he=s-1 contains=javaDebugStrTemplEmbExp,javaDebugSpecial,javaDebugTextBlockError' syn match javaDebugTextBlockError contained +"""\s*"""+ syn match javaDebugCharacter contained "'[^\\]'" syn match javaDebugSpecialCharacter contained "'\\.'" @@ -297,18 +377,18 @@ if exists("java_highlight_debug") syn keyword javaDebugType contained null this super syn region javaDebugParen start=+(+ end=+)+ contained contains=javaDebug.*,javaDebugParen - " to make this work you must define the highlighting for these groups - syn match javaDebug "\<System\.\(out\|err\)\.print\(ln\)*\s*("me=e-1 contains=javaDebug.* nextgroup=javaDebugParen - syn match javaDebug "\<p\s*("me=e-1 contains=javaDebug.* nextgroup=javaDebugParen - syn match javaDebug "[A-Za-z][a-zA-Z0-9_]*\.printStackTrace\s*("me=e-1 contains=javaDebug.* nextgroup=javaDebugParen - syn match javaDebug "\<trace[SL]\=\s*("me=e-1 contains=javaDebug.* nextgroup=javaDebugParen - - syn cluster javaTop add=javaDebug + " To make this work, define the highlighting for these groups. + syn match javaDebug "\<System\.\%(out\|err\)\.print\%(ln\)\=\s*("me=e-1 contains=javaDebug.* nextgroup=javaDebugParen +" FIXME: What API does "p" belong to? +" syn match javaDebug "\<p\s*("me=e-1 contains=javaDebug.* nextgroup=javaDebugParen + syn match javaDebug "\<\K\k*\.printStackTrace\s*("me=e-1 contains=javaDebug.* nextgroup=javaDebugParen +" FIXME: What API do "trace*" belong to? +" syn match javaDebug "\<trace[SL]\=\s*("me=e-1 contains=javaDebug.* nextgroup=javaDebugParen hi def link javaDebug Debug hi def link javaDebugString DebugString - hi def link javaDebugStringError javaError - hi def link javaDebugTextBlockError javaDebugStringError + hi def link javaDebugStrTempl Macro + hi def link javaDebugTextBlockError Error hi def link javaDebugType DebugType hi def link javaDebugBoolean DebugBoolean hi def link javaDebugNumber Debug @@ -326,7 +406,6 @@ endif if exists("java_mark_braces_in_parens_as_errors") syn match javaInParen contained "[{}]" hi def link javaInParen javaError - syn cluster javaTop add=javaInParen endif " catch errors caused by wrong parenthesis @@ -343,13 +422,24 @@ syn match javaParenError "\]" hi def link javaParenError javaError if exists("java_highlight_functions") - syn match javaLambdaDef "([a-zA-Z0-9_<>\[\], \t]*)\s*->" - " needs to be defined after the parenthesis error catcher to work + " Make ()-matching definitions after the parenthesis error catcher. + exec 'syn match javaLambdaDef "\k\@' . s:ff.Peek('4', '') . '<!(\%(\k\|[[:space:]<>?\[\]@,.]\)*)\s*->"' endif +" The @javaTop cluster comprises non-contained Java syntax groups. +" Note that the syntax file "aidl.vim" relies on its availability. +syn cluster javaTop contains=TOP,javaDocComment,javaFold,javaParenError,javaParenT + if !exists("java_minlines") let java_minlines = 10 endif + +" Note that variations of a /*/ balanced comment, e.g., /*/*/, /*//*/, +" /* /*/, /* /*/, etc., may have their rightmost /*/ part accepted +" as a comment start by ':syntax sync ccomment'; consider alternatives +" to make synchronisation start further towards file's beginning by +" bumping up g:java_minlines or issuing ':syntax sync fromstart' or +" preferring &foldmethod set to 'syntax'. exec "syn sync ccomment javaComment minlines=" . java_minlines " The default highlighting. @@ -376,12 +466,12 @@ hi def link javaSpecial Special hi def link javaSpecialError Error hi def link javaSpecialCharError Error hi def link javaString String +hi def link javaStrTempl Macro hi def link javaCharacter Character hi def link javaSpecialChar SpecialChar hi def link javaNumber Number hi def link javaError Error -hi def link javaStringError Error -hi def link javaTextBlockError javaStringError +hi def link javaTextBlockError Error hi def link javaStatement Statement hi def link javaOperator Operator hi def link javaComment Comment @@ -394,6 +484,8 @@ hi def link javaAnnotation PreProc hi def link javaCommentTitle SpecialComment hi def link javaDocTags Special +hi def link javaDocCodeTag Special +hi def link javaDocSnippetTag Special hi def link javaDocParam Function hi def link javaDocSeeTagParam Function hi def link javaCommentStar javaComment @@ -403,12 +495,14 @@ hi def link javaExternal Include hi def link htmlComment Special hi def link htmlCommentPart Special +hi def link htmlArg Type +hi def link htmlString String hi def link javaSpaceError Error -if s:isModuleInfoDeclarationCurrentBuffer() - hi def link javaModuleStorageClass StorageClass - hi def link javaModuleStmt Statement - hi def link javaModuleExternal Include +if s:module_info_cur_buf + hi def link javaModuleStorageClass StorageClass + hi def link javaModuleStmt Statement + hi def link javaModuleExternal Include endif let b:current_syntax = "java" @@ -417,9 +511,8 @@ if main_syntax == 'java' unlet main_syntax endif -delfunction! s:isModuleInfoDeclarationCurrentBuffer -let b:spell_options="contained" +let b:spell_options = "contained" let &cpo = s:cpo_save -unlet s:cpo_save +unlet s:module_info_cur_buf s:ff s:cpo_save -" vim: ts=8 +" vim: sw=2 ts=8 noet sta diff --git a/runtime/syntax/jj.vim b/runtime/syntax/jj.vim new file mode 100644 index 0000000000..a2911a0268 --- /dev/null +++ b/runtime/syntax/jj.vim @@ -0,0 +1,20 @@ +" Vim syntax file +" Language: jj description +" Maintainer: Gregory Anders <greg@gpanders.com> +" Last Change: 2024 May 8 + +if exists('b:current_syntax') + finish +endif +let b:current_syntax = 'jj' + +syn match jjAdded "A .*" contained +syn match jjRemoved "D .*" contained +syn match jjChanged "M .*" contained + +syn region jjComment start="^JJ: " end="$" contains=jjAdded,jjRemoved,jjChanged + +hi def link jjComment Comment +hi def link jjAdded Added +hi def link jjRemoved Removed +hi def link jjChanged Changed diff --git a/runtime/syntax/jq.vim b/runtime/syntax/jq.vim new file mode 100644 index 0000000000..272dcb4ebe --- /dev/null +++ b/runtime/syntax/jq.vim @@ -0,0 +1,130 @@ +" Vim compiler file +" Language: jq +" Maintainer: Vito <vito.blog@gmail.com> +" Last Change: 2024 Apr 17 +" Upstream: https://github.com/vito-c/jq.vim +" +" Quit when a (custom) syntax file was already loaded +if exists('b:current_syntax') + finish +endif + +" syn include @jqHtml syntax/html.vim " Doc comment HTML + +" jqTodo +syntax keyword jqTodo contained TODO FIXME NOTE XXX + +" jqKeywords +syntax keyword jqKeywords and or not empty +syntax keyword jqKeywords try catch +syntax keyword jqKeywords reduce as label break foreach +syntax keyword jqKeywords import include module modulemeta +syntax keyword jqKeywords env nth has in while error stderr debug + +" jqConditional +syntax keyword jqConditional if then elif else end + +" jqConditions +syntax keyword jqCondtions true false null + +" jqSpecials +syntax keyword jqType type +syntax match jqType /[\|;]/ " not really a type I did this for coloring reasons though :help group-name +syntax region jqParentheses start=+(+ end=+)+ fold transparent + +" jq Functions +syntax keyword jqFunction add all any arrays ascii_downcase floor +syntax keyword jqFunction ascii_upcase booleans bsearch builtins capture combinations +syntax keyword jqFunction \contains del delpaths endswith explode +syntax keyword jqFunction finites first flatten format from_entries +syntax keyword jqFunction fromdate fromdateiso8601 fromjson fromstream get_jq_origin +syntax keyword jqFunction get_prog_origin get_search_list getpath gmtime group_by +syntax keyword jqFunction gsub halt halt_error implode index indices infinite +syntax keyword jqFunction input input_filename input_line_number inputs inside +syntax keyword jqFunction isempty isfinite isinfinite isnan isnormal iterables +syntax keyword jqFunction join keys keys_unsorted last leaf_paths +syntax keyword jqFunction length limit localtime ltrimstr map map_values +syntax keyword jqFunction match max max_by min min_by +syntax keyword jqFunction mktime nan normals now +syntax keyword jqFunction nulls numbers objects path paths range +syntax keyword jqFunction recurse recurse_down repeat reverse rindex +syntax keyword jqFunction rtrimstr scalars scalars_or_empty scan select +syntax keyword jqFunction setpath sort sort_by split splits with_entries +syntax keyword jqFunction startswith strflocaltime strftime strings strptime sub +syntax keyword jqFunction test to_entries todate todateiso8601 tojson __loc__ +syntax keyword jqFunction tonumber tostream tostring transpose truncate_stream +syntax keyword jqFunction unique unique_by until utf8bytelength values walk +" TODO: $__loc__ is going to be a pain + +" jq Math Functions +syntax keyword jqFunction acos acosh asin asinh atan atanh cbrt ceil cos cosh +syntax keyword jqFunction erf erfc exp exp10 exp2 expm1 fabs floor gamma j0 j1 +syntax keyword jqFunction lgamma lgamma_r log log10 log1p log2 logb nearbyint +syntax keyword jqFunction pow10 rint round significand sin sinh sqrt tan tanh +syntax keyword jqFunction tgamma trunc y0 y1 +syntax keyword jqFunction atan2 copysign drem fdim fmax fmin fmod frexp hypot +syntax keyword jqFunction jn ldexp modf nextafter nexttoward pow remainder +syntax keyword jqFunction scalb scalbln yn +syntax keyword jqFunction fma + +" jq SQL-style Operators +syntax keyword jqFunction INDEX JOIN IN + +" Macro +syntax match jqMacro "@\%(text\|json\|html\|uri\|[ct]sv\|sh\|base64d\?\)\>" + +" Comments +syntax match jqComment "#.*" contains=jqTodo + +" Variables +syn match jqVariables /$[_A-Za-z0-9]\+/ + +" Definition +syntax keyword jqKeywords def nextgroup=jqNameDefinition skipwhite +syn match jqNameDefinition /\<[_A-Za-z0-9]\+\>/ contained nextgroup=jqPostNameDefinition +syn match jqNameDefinition /`[^`]\+`/ contained nextgroup=jqPostNameDefinition + +" Strings +syn region jqError start=+'+ end=+'\|$\|[;)]\@=+ +syn region jqString matchgroup=jqQuote + \ start=+"+ skip=+\\[\\"]+ end=+"+ + \ contains=@Spell,jqInterpolation +syn region jqInterpolation matchgroup=jqInterpolationDelimiter + \ start=+\%([^\\]\%(\\\\\)*\\\)\@<!\\(+ end=+)+ + \ contained contains=TOP + +" Operators +syn match jqOperator /:\|\([-+*/%<>=]\|\/\/\)=\?\|[!|]=\|?\/\// +"syn region jqRange matchgroup=jqSquareBracket start=+\[+ skip=+:+ end=+\]+ + +" Errors +syn keyword jqError _assign _flatten _modify _nwise _plus _negate _minus _multiply +syn keyword jqError _divide _mod _strindices _equal _notequal _less _greater _lesseq +syn keyword jqError _greatereq _sort_by_impl _group_by_impl _min_by_impl _max_by_impl _match_impl _input +" TODO: these errors should show up when doing def _flatten: as well + +" Numbers +syn match jqNumber /\<0[dDfFlL]\?\>/ " Just a bare 0 +syn match jqNumber /\<[1-9]\d*[dDfFlL]\?\>/ " A multi-digit number - octal numbers with leading 0's are deprecated in Scala + +if !exists('jq_quote_highlight') + highlight def link jqQuote String +else + highlight def link jqQuote Type +endif + +hi def link jqCondtions Boolean +hi def link jqVariables Identifier +hi def link jqNameDefinition Function +hi def link jqTodo Todo +hi def link jqComment Comment +hi def link jqKeywords Keyword +hi def link jqType Type +hi def link jqOperator Operator +hi def link jqFunction Function +hi def link jqMacro Macro +hi def link jqError Error +hi def link jqString String +hi def link jqInterpolationDelimiter Delimiter +hi def link jqConditional Conditional +hi def link jqNumber Number diff --git a/runtime/syntax/json.vim b/runtime/syntax/json.vim index 3f49b0c5ea..f61a17e120 100644 --- a/runtime/syntax/json.vim +++ b/runtime/syntax/json.vim @@ -1,6 +1,6 @@ " Vim syntax file " Language: JSON -" Maintainer: vacancy +" Maintainer: Vito <vito.blog@gmail.com> " Previous Maintainer: Eli Parra <eli@elzr.com> " Last Change: 2019 Sep 17 " Version: 0.12 diff --git a/runtime/syntax/ondir.vim b/runtime/syntax/ondir.vim new file mode 100644 index 0000000000..4aeb014e1b --- /dev/null +++ b/runtime/syntax/ondir.vim @@ -0,0 +1,35 @@ +" Vim syntax file +" Language: ondir <https://github.com/alecthomas/ondir> +" Maintainer: Jon Parise <jon@indelible.org> + +if exists('b:current_syntax') + finish +endif + +let s:cpo_save = &cpoptions +set cpoptions&vim + +syn case match + +syn match ondirComment "#.*" contains=@Spell +syn keyword ondirKeyword final contained skipwhite nextgroup=ondirKeyword +syn keyword ondirKeyword enter leave contained skipwhite nextgroup=ondirPath +syn match ondirPath "[^:]\+" contained display +syn match ondirColon ":" contained display + +syn include @ondirShell syntax/sh.vim +syn region ondirContent start="^\s\+" end="^\ze\S.*$" keepend contained contains=@ondirShell + +syn region ondirSection start="^\(final\|enter\|leave\)" end="^\ze\S.*$" fold contains=ondirKeyword,ondirPath,ondirColon,ondirContent + +hi def link ondirComment Comment +hi def link ondirKeyword Keyword +hi def link ondirPath Special +hi def link ondirColon Operator + +let b:current_syntax = 'ondir' + +let &cpoptions = s:cpo_save +unlet s:cpo_save + +" vim: et ts=4 sw=2 sts=2: diff --git a/runtime/syntax/pamconf.vim b/runtime/syntax/pamconf.vim index 29132848a9..1b5f901348 100644 --- a/runtime/syntax/pamconf.vim +++ b/runtime/syntax/pamconf.vim @@ -1,9 +1,9 @@ " Vim syntax file " Language: pam(8) configuration file " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2020/08/04 +" Latest Change: 2024/03/31 " Changes By: Haochen Tong - +" Vim Project for the @include syntax if exists("b:current_syntax") finish @@ -23,6 +23,13 @@ syn match pamconfType '-\?[[:alpha:]]\+' syn keyword pamconfTypeKeyword contained account auth password session +" The @include syntax is Debian specific +syn match pamconfInclude '^@include' + \ nextgroup=pamconfIncludeFile + \ skipwhite + +syn match pamconfIncludeFile '\f\+$' + if s:has_service_field syn match pamconfService '^[[:graph:]]\+' \ nextgroup=pamconfType, @@ -124,6 +131,8 @@ hi def link pamconfMPath String hi def link pamconfMPathLineCont pamconfServiceLineCont hi def link pamconfArgs Normal hi def link pamconfArgsLineCont pamconfServiceLineCont +hi def link pamconfInclude Include +hi def link pamconfIncludeFile Include let b:current_syntax = "pamconf" diff --git a/runtime/syntax/pandoc.vim b/runtime/syntax/pandoc.vim new file mode 100644 index 0000000000..7928cc665d --- /dev/null +++ b/runtime/syntax/pandoc.vim @@ -0,0 +1,709 @@ +scriptencoding utf-8 +" +" Language: Pandoc (superset of Markdown) +" Maintainer: Felipe Morales <hel.sheep@gmail.com> +" Maintainer: Caleb Maclennan <caleb@alerque.com> +" Upstream: https://github.com/vim-pandoc/vim-pandoc-syntax +" +" Contributor: David Sanson <dsanson@gmail.com> +" Jorge Israel Peña <jorge.israel.p@gmail.com> +" Original Author: Jeremy Schultz <taozhyn@gmail.com> +" Version: 5.0 +" Last Change: 2024 Apr 08 + +let s:cpo_save = &cpo +set cpo&vim + +" Configuration: {{{1 +" +" use conceal? {{{2 +if !exists('g:pandoc#syntax#conceal#use') + let g:pandoc#syntax#conceal#use = 1 +endif +"}}}2 + +" what groups not to use conceal in. works as a blacklist {{{2 +if !exists('g:pandoc#syntax#conceal#blacklist') + let g:pandoc#syntax#conceal#blacklist = [] +endif +" }}}2 + +" cchars used in conceal rules {{{2 +" utf-8 defaults (preferred) +if &encoding ==# 'utf-8' + let s:cchars = { + \'newline': '↵', + \'image': '▨', + \'super': 'ⁿ', + \'sub': 'ₙ', + \'strike': 'x̶', + \'atx': '§', + \'codelang': 'λ', + \'codeend': '—', + \'abbrev': '→', + \'footnote': '†', + \'definition': ' ', + \'li': '•', + \'html_c_s': '‹', + \'html_c_e': '›', + \'quote_s': '“', + \'quote_e': '”'} +else + " ascii defaults + let s:cchars = { + \'newline': ' ', + \'image': 'i', + \'super': '^', + \'sub': '_', + \'strike': '~', + \'atx': '#', + \'codelang': 'l', + \'codeend': '-', + \'abbrev': 'a', + \'footnote': 'f', + \'definition': ' ', + \'li': '*', + \'html_c_s': '+', + \'html_c_e': '+'} +endif +" }}}2 + +" if the user has a dictionary with replacements for the default cchars, use those {{{2 +if exists('g:pandoc#syntax#conceal#cchar_overrides') + let s:cchars = extend(s:cchars, g:pandoc#syntax#conceal#cchar_overrides) +endif +" }}}2 + +"should the urls in links be concealed? {{{2 +if !exists('g:pandoc#syntax#conceal#urls') + let g:pandoc#syntax#conceal#urls = 0 +endif +" should backslashes in escapes be concealed? {{{2 +if !exists('g:pandoc#syntax#conceal#backslash') + let g:pandoc#syntax#conceal#backslash = 0 +endif +" }}}2 + +" leave specified codeblocks as Normal (i.e. 'unhighlighted') {{{2 +if !exists('g:pandoc#syntax#codeblocks#ignore') + let g:pandoc#syntax#codeblocks#ignore = [] +endif +" }}}2 + +" use embedded highlighting for delimited codeblocks where a language is specifed. {{{2 +if !exists('g:pandoc#syntax#codeblocks#embeds#use') + let g:pandoc#syntax#codeblocks#embeds#use = 1 +endif +" }}}2 + +" for what languages and using what vim syntax files highlight those embeds. {{{2 +" defaults to None. +if !exists('g:pandoc#syntax#codeblocks#embeds#langs') + let g:pandoc#syntax#codeblocks#embeds#langs = [] +endif +" }}}2 + +" use italics ? {{{2 +if !exists('g:pandoc#syntax#style#emphases') + let g:pandoc#syntax#style#emphases = 1 +endif +" if 0, we don't conceal the emphasis marks, otherwise there wouldn't be a way +" to tell where the styles apply. +if g:pandoc#syntax#style#emphases == 0 + call add(g:pandoc#syntax#conceal#blacklist, 'block') +endif +" }}}2 + +" underline subscript, superscript and strikeout? {{{2 +if !exists('g:pandoc#syntax#style#underline_special') + let g:pandoc#syntax#style#underline_special = 1 +endif +" }}}2 + +" protect code blocks? {{{2 +if !exists('g:pandoc#syntax#protect#codeblocks') + let g:pandoc#syntax#protect#codeblocks = 1 +endif +" }}}2 + +" use color column? {{{2 +if !exists('g:pandoc#syntax#colorcolumn') + let g:pandoc#syntax#colorcolumn = 0 +endif +" }}}2 + +" highlight new lines? {{{2 +if !exists('g:pandoc#syntax#newlines') + let g:pandoc#syntax#newlines = 1 +endif +" }}} + +" detect roman-numeral list items? {{{2 +if !exists('g:pandoc#syntax#roman_lists') + let g:pandoc#syntax#roman_lists = 0 +endif +" }}}2 + +" disable syntax highlighting for definition lists? (better performances) {{{2 +if !exists('g:pandoc#syntax#use_definition_lists') + let g:pandoc#syntax#use_definition_lists = 1 +endif +" }}}2 + +" }}}1 + +" Functions: {{{1 +" EnableEmbedsforCodeblocksWithLang {{{2 +function! EnableEmbedsforCodeblocksWithLang(entry) + " prevent embedded language syntaxes from changing 'foldmethod' + if has('folding') + let s:foldmethod = &l:foldmethod + let s:foldtext = &l:foldtext + endif + + try + let s:langname = matchstr(a:entry, '^[^=]*') + let s:langsyntaxfile = matchstr(a:entry, '[^=]*$') + unlet! b:current_syntax + exe 'syn include @'.toupper(s:langname).' syntax/'.s:langsyntaxfile.'.vim' + " We might have just turned off spellchecking by including the file, + " so we turn it back on here. + exe 'syntax spell toplevel' + exe 'syn region pandocDelimitedCodeBlock_' . s:langname . ' start=/\(\_^\( \+\|\t\)\=\(`\{3,}`*\|\~\{3,}\~*\)\s*\%({[^.]*\.\)\=' . s:langname . '\>.*\n\)\@<=\_^/' . + \' end=/\_$\n\(\( \+\|\t\)\=\(`\{3,}`*\|\~\{3,}\~*\)\_$\n\_$\)\@=/ contained containedin=pandocDelimitedCodeBlock' . + \' contains=@' . toupper(s:langname) + exe 'syn region pandocDelimitedCodeBlockinBlockQuote_' . s:langname . ' start=/>\s\(`\{3,}`*\|\~\{3,}\~*\)\s*\%({[^.]*\.\)\=' . s:langname . '\>/' . + \ ' end=/\(`\{3,}`*\|\~\{3,}\~*\)/ contained containedin=pandocDelimitedCodeBlock' . + \' contains=@' . toupper(s:langname) . + \',pandocDelimitedCodeBlockStart,pandocDelimitedCodeBlockEnd,pandodDelimitedCodeblockLang,pandocBlockQuoteinDelimitedCodeBlock' + catch /E484/ + echo "No syntax file found for '" . s:langsyntaxfile . "'" + endtry + + if exists('s:foldmethod') && s:foldmethod !=# &l:foldmethod + let &l:foldmethod = s:foldmethod + endif + if exists('s:foldtext') && s:foldtext !=# &l:foldtext + let &l:foldtext = s:foldtext + endif +endfunction +" }}}2 + +" DisableEmbedsforCodeblocksWithLang {{{2 +function! DisableEmbedsforCodeblocksWithLang(langname) + try + exe 'syn clear pandocDelimitedCodeBlock_'.a:langname + exe 'syn clear pandocDelimitedCodeBlockinBlockQuote_'.a:langname + catch /E28/ + echo "No existing highlight definitions found for '" . a:langname . "'" + endtry +endfunction +" }}}2 + +" WithConceal {{{2 +function! s:WithConceal(rule_group, rule, conceal_rule) + let l:rule_tail = '' + if g:pandoc#syntax#conceal#use != 0 + if index(g:pandoc#syntax#conceal#blacklist, a:rule_group) == -1 + let l:rule_tail = ' ' . a:conceal_rule + endif + endif + execute a:rule . l:rule_tail +endfunction +" }}}2 + +" }}}1 + +" Commands: {{{1 +command! -buffer -nargs=1 -complete=syntax PandocHighlight call EnableEmbedsforCodeblocksWithLang(<f-args>) +command! -buffer -nargs=1 -complete=syntax PandocUnhighlight call DisableEmbedsforCodeblocksWithLang(<f-args>) +" }}}1 + +" BASE: +syntax clear +syntax spell toplevel +" }}}1 + +" Syntax Rules: {{{1 + +" Embeds: {{{2 + +" prevent embedded language syntaxes from changing 'foldmethod' +if has('folding') + let s:foldmethod = &l:foldmethod +endif + +" HTML: {{{3 +" Set embedded HTML highlighting +syn include @HTML syntax/html.vim +syn match pandocHTML /<\/\?\a\_.\{-}>/ contains=@HTML +" Support HTML multi line comments +syn region pandocHTMLComment start=/<!--\s\=/ end=/\s\=-->/ keepend contains=pandocHTMLCommentStart,pandocHTMLCommentEnd +call s:WithConceal('html_c_s', 'syn match pandocHTMLCommentStart /<!--/ contained', 'conceal cchar='.s:cchars['html_c_s']) +call s:WithConceal('html_c_e', 'syn match pandocHTMLCommentEnd /-->/ contained', 'conceal cchar='.s:cchars['html_c_e']) +" }}}3 + +" LaTeX: {{{3 +" Set embedded LaTex (pandoc extension) highlighting +" Unset current_syntax so the 2nd include will work +unlet b:current_syntax +syn include @LATEX syntax/tex.vim +if index(g:pandoc#syntax#conceal#blacklist, 'inlinemath') == -1 + " Can't use WithConceal here because it will mess up all other conceals + " when dollar signs are used normally. It must be skipped entirely if + " inlinemath is blacklisted + syn region pandocLaTeXInlineMath start=/\v\\@<!\$\S@=/ end=/\v\\@<!\$\d@!/ keepend contains=@LATEX + syn region pandocLaTeXInlineMath start=/\\\@<!\\(/ end=/\\\@<!\\)/ keepend contains=@LATEX +endif +syn match pandocEscapedDollar /\\\$/ conceal cchar=$ +syn match pandocProtectedFromInlineLaTeX /\\\@<!\${.*}\(\(\s\|[[:punct:]]\)\([^$]*\|.*\(\\\$.*\)\{2}\)\n\n\|$\)\@=/ display +" contains=@LATEX +syn region pandocLaTeXMathBlock start=/\$\$/ end=/\$\$/ keepend contains=@LATEX +syn region pandocLaTeXMathBlock start=/\\\@<!\\\[/ end=/\\\@<!\\\]/ keepend contains=@LATEX +syn match pandocLaTeXCommand /\\[[:alpha:]]\+\(\({.\{-}}\)\=\(\[.\{-}\]\)\=\)*/ contains=@LATEX +syn region pandocLaTeXRegion start=/\\begin{\z(.\{-}\)}/ end=/\\end{\z1}/ keepend contains=@LATEX +" we rehighlight sectioning commands, because otherwise tex.vim captures all text until EOF or a new sectioning command +syn region pandocLaTexSection start=/\\\(part\|chapter\|\(sub\)\{,2}section\|\(sub\)\=paragraph\)\*\=\(\[.*\]\)\={/ end=/\}/ keepend +syn match pandocLaTexSectionCmd /\\\(part\|chapter\|\(sub\)\{,2}section\|\(sub\)\=paragraph\)/ contained containedin=pandocLaTexSection +syn match pandocLaTeXDelimiter /[[\]{}]/ contained containedin=pandocLaTexSection +" }}}3 + +if exists('s:foldmethod') && s:foldmethod !=# &l:foldmethod + let &l:foldmethod = s:foldmethod +endif + +" }}}2 + +" Titleblock: {{{2 +syn region pandocTitleBlock start=/\%^%/ end=/\n\n/ contains=pandocReferenceLabel,pandocReferenceURL,pandocNewLine +call s:WithConceal('titleblock', 'syn match pandocTitleBlockMark /%\ / contained containedin=pandocTitleBlock,pandocTitleBlockTitle', 'conceal') +syn match pandocTitleBlockTitle /\%^%.*\n/ contained containedin=pandocTitleBlock +" }}}2 + +" Blockquotes: {{{2 +syn match pandocBlockQuote /^\s\{,3}>.*\n\(.*\n\@1<!\n\)*/ contains=@Spell,pandocEmphasis,pandocStrong,pandocPCite,pandocSuperscript,pandocSubscript,pandocStrikeout,pandocUListItem,pandocNoFormatted,pandocAmpersandEscape,pandocLaTeXInlineMath,pandocEscapedDollar,pandocLaTeXCommand,pandocLaTeXMathBlock,pandocLaTeXRegion skipnl +syn match pandocBlockQuoteMark /\_^\s\{,3}>/ contained containedin=pandocEmphasis,pandocStrong,pandocPCite,pandocSuperscript,pandocSubscript,pandocStrikeout,pandocUListItem,pandocNoFormatted +" }}}2 + +" Code Blocks: {{{2 +if g:pandoc#syntax#protect#codeblocks == 1 + syn match pandocCodeblock /\([ ]\{4}\|\t\).*$/ +endif +syn region pandocCodeBlockInsideIndent start=/\(\(\d\|\a\|*\).*\n\)\@<!\(^\(\s\{8,}\|\t\+\)\).*\n/ end=/.\(\n^\s*\n\)\@=/ contained +" }}}2 + +" Links: {{{2 + +" Base: {{{3 +syn region pandocReferenceLabel matchgroup=pandocOperator start=/!\{,1}\\\@<!\^\@<!\[/ skip=/\(\\\@<!\]\]\@=\|`.*\\\@<!].*`\)/ end=/\\\@<!\]/ keepend display +if g:pandoc#syntax#conceal#urls == 1 + syn region pandocReferenceURL matchgroup=pandocOperator start=/\]\@1<=(/ end=/)/ keepend conceal +else + syn region pandocReferenceURL matchgroup=pandocOperator start=/\]\@1<=(/ end=/)/ keepend +endif +" let's not consider "a [label] a" as a label, remove formatting - Note: breaks implicit links +syn match pandocNoLabel /\]\@1<!\(\s\{,3}\|^\)\[[^\[\]]\{-}\]\(\s\+\|$\)[\[(]\@!/ contains=pandocPCite +syn match pandocLinkTip /\s*".\{-}"/ contained containedin=pandocReferenceURL contains=@Spell,pandocAmpersandEscape display +call s:WithConceal('image', 'syn match pandocImageIcon /!\[\@=/ display', 'conceal cchar='. s:cchars['image']) +" }}}3 + +" Definitions: {{{3 +syn region pandocReferenceDefinition start=/\[.\{-}\]:/ end=/\(\n\s*".*"$\|$\)/ keepend +syn match pandocReferenceDefinitionLabel /\[\zs.\{-}\ze\]:/ contained containedin=pandocReferenceDefinition display +syn match pandocReferenceDefinitionAddress /:\s*\zs.*/ contained containedin=pandocReferenceDefinition +syn match pandocReferenceDefinitionTip /\s*".\{-}"/ contained containedin=pandocReferenceDefinition,pandocReferenceDefinitionAddress contains=@Spell,pandocAmpersandEscape +" }}}3 + +" Automatic_links: {{{3 +syn match pandocAutomaticLink /<\(https\{0,1}.\{-}\|[A-Za-z0-9!#$%&'*+\-/=?^_`{|}~.]\{-}@[A-Za-z0-9\-]\{-}\.\w\{-}\)>/ contains=NONE +" }}}3 + +" }}}2 + +" Citations: {{{2 +" parenthetical citations +syn match pandocPCite "\^\@<!\[[^\[\]]\{-}-\{0,1}@[[:alnum:]_][[:digit:][:lower:][:upper:]_:.#$%&\-+?<>~/]*.\{-}\]" contains=pandocEmphasis,pandocStrong,pandocLatex,pandocCiteKey,@Spell,pandocAmpersandEscape display +" in-text citations with location +syn match pandocICite "@[[:alnum:]_][[:digit:][:lower:][:upper:]_:.#$%&\-+?<>~/]*\s\[.\{-1,}\]" contains=pandocCiteKey,@Spell display +" cite keys +syn match pandocCiteKey /\(-\=@[[:alnum:]_][[:digit:][:lower:][:upper:]_:.#$%&\-+?<>~/]*\)/ containedin=pandocPCite,pandocICite contains=@NoSpell display +syn match pandocCiteAnchor /[-@]/ contained containedin=pandocCiteKey display +syn match pandocCiteLocator /[\[\]]/ contained containedin=pandocPCite,pandocICite +" }}}2 + +" Text Styles: {{{2 + +" Emphasis: {{{3 +call s:WithConceal('block', 'syn region pandocEmphasis matchgroup=pandocOperator start=/\\\@1<!\(\_^\|\s\|[[:punct:]]\)\@<=\*\S\@=/ skip=/\(\*\*\|__\)/ end=/\*\([[:punct:]]\|\s\|\_$\)\@=/ contains=@Spell,pandocNoFormattedInEmphasis,pandocLatexInlineMath,pandocAmpersandEscape', 'concealends') +call s:WithConceal('block', 'syn region pandocEmphasis matchgroup=pandocOperator start=/\\\@1<!\(\_^\|\s\|[[:punct:]]\)\@<=_\S\@=/ skip=/\(\*\*\|__\)/ end=/\S\@1<=_\([[:punct:]]\|\s\|\_$\)\@=/ contains=@Spell,pandocNoFormattedInEmphasis,pandocLatexInlineMath,pandocAmpersandEscape', 'concealends') +" }}}3 + +" Strong: {{{3 +call s:WithConceal('block', 'syn region pandocStrong matchgroup=pandocOperator start=/\(\\\@<!\*\)\{2}/ end=/\(\\\@<!\*\)\{2}/ contains=@Spell,pandocNoFormattedInStrong,pandocLatexInlineMath,pandocAmpersandEscape', 'concealends') +call s:WithConceal('block', 'syn region pandocStrong matchgroup=pandocOperator start=/__/ end=/__/ contains=@Spell,pandocNoFormattedInStrong,pandocLatexInlineMath,pandocAmpersandEscape', 'concealends') +" }}}3 + +" Strong Emphasis: {{{3 +call s:WithConceal('block', 'syn region pandocStrongEmphasis matchgroup=pandocOperator start=/\*\{3}\(\S[^*]*\(\*\S\|\n[^*]*\*\S\)\)\@=/ end=/\S\@<=\*\{3}/ contains=@Spell,pandocAmpersandEscape', 'concealends') +call s:WithConceal('block', 'syn region pandocStrongEmphasis matchgroup=pandocOperator start=/\(___\)\S\@=/ end=/\S\@<=___/ contains=@Spell,pandocAmpersandEscape', 'concealends') +" }}}3 + +" Mixed: {{{3 +call s:WithConceal('block', 'syn region pandocStrongInEmphasis matchgroup=pandocOperator start=/\*\*/ end=/\*\*/ contained containedin=pandocEmphasis contains=@Spell,pandocAmpersandEscape', 'concealends') +call s:WithConceal('block', 'syn region pandocStrongInEmphasis matchgroup=pandocOperator start=/__/ end=/__/ contained containedin=pandocEmphasis contains=@Spell,pandocAmpersandEscape', 'concealends') +call s:WithConceal('block', 'syn region pandocEmphasisInStrong matchgroup=pandocOperator start=/\\\@1<!\(\_^\|\s\|[[:punct:]]\)\@<=\*\S\@=/ skip=/\(\*\*\|__\)/ end=/\S\@<=\*\([[:punct:]]\|\s\|\_$\)\@=/ contained containedin=pandocStrong contains=@Spell,pandocAmpersandEscape', 'concealends') +call s:WithConceal('block', 'syn region pandocEmphasisInStrong matchgroup=pandocOperator start=/\\\@<!\(\_^\|\s\|[[:punct:]]\)\@<=_\S\@=/ skip=/\(\*\*\|__\)/ end=/\S\@<=_\([[:punct:]]\|\s\|\_$\)\@=/ contained containedin=pandocStrong contains=@Spell,pandocAmpersandEscape', 'concealends') +" }}}3 + +" Inline Code: {{{3 +" Using single back ticks +call s:WithConceal('inlinecode', 'syn region pandocNoFormatted matchgroup=pandocOperator start=/\\\@<!`/ end=/\\\@<!`/ nextgroup=pandocNoFormattedAttrs', 'concealends') +call s:WithConceal('inlinecode', 'syn region pandocNoFormattedInEmphasis matchgroup=pandocOperator start=/\\\@<!`/ end=/\\\@<!`/ nextgroup=pandocNoFormattedAttrs contained', 'concealends') +call s:WithConceal('inlinecode', 'syn region pandocNoFormattedInStrong matchgroup=pandocOperator start=/\\\@<!`/ end=/\\\@<!`/ nextgroup=pandocNoFormattedAttrs contained', 'concealends') +" Using double back ticks +call s:WithConceal('inlinecode', 'syn region pandocNoFormatted matchgroup=pandocOperator start=/\\\@<!``/ end=/\\\@<!``/ nextgroup=pandocNoFormattedAttrs', 'concealends') +call s:WithConceal('inlinecode', 'syn region pandocNoFormattedInEmphasis matchgroup=pandocOperator start=/\\\@<!``/ end=/\\\@<!``/ nextgroup=pandocNoFormattedAttrs contained', 'concealends') +call s:WithConceal('inlinecode', 'syn region pandocNoFormattedInStrong matchgroup=pandocOperator start=/\\\@<!``/ end=/\\\@<!``/ nextgroup=pandocNoFormattedAttrs contained', 'concealends') +syn match pandocNoFormattedAttrs /{.\{-}}/ contained +" }}}3 + +" Subscripts: {{{3 +syn region pandocSubscript start=/\~\(\([[:graph:]]\(\\ \)\=\)\{-}\~\)\@=/ end=/\~/ keepend +call s:WithConceal('subscript', 'syn match pandocSubscriptMark /\~/ contained containedin=pandocSubscript', 'conceal cchar='.s:cchars['sub']) +" }}}3 + +" Superscript: {{{3 +syn region pandocSuperscript start=/\^\(\([[:graph:]]\(\\ \)\=\)\{-}\^\)\@=/ skip=/\\ / end=/\^/ keepend +call s:WithConceal('superscript', 'syn match pandocSuperscriptMark /\^/ contained containedin=pandocSuperscript', 'conceal cchar='.s:cchars['super']) +" }}}3 + +" Strikeout: {{{3 +syn region pandocStrikeout start=/\~\~/ end=/\~\~/ contains=@Spell,pandocAmpersandEscape keepend +call s:WithConceal('strikeout', 'syn match pandocStrikeoutMark /\~\~/ contained containedin=pandocStrikeout', 'conceal cchar='.s:cchars['strike']) +" }}}3 + +" }}}2 + +" Headers: {{{2 +syn match pandocAtxHeader /\(\%^\|<.\+>.*\n\|^\s*\n\)\@<=#\{1,6}.*\n/ contains=pandocEmphasis,pandocStrong,pandocNoFormatted,pandocLaTeXInlineMath,pandocEscapedDollar,@Spell,pandocAmpersandEscape,pandocReferenceLabel,pandocReferenceURL display +syn match pandocAtxHeaderMark /\(^#\{1,6}\|\\\@<!#\+\(\s*.*$\)\@=\)/ contained containedin=pandocAtxHeader +call s:WithConceal('atx', 'syn match pandocAtxStart /#/ contained containedin=pandocAtxHeaderMark', 'conceal cchar='.s:cchars['atx']) +syn match pandocSetexHeader /^.\+\n[=]\+$/ contains=pandocEmphasis,pandocStrong,pandocNoFormatted,pandocLaTeXInlineMath,pandocEscapedDollar,@Spell,pandocAmpersandEscape +syn match pandocSetexHeader /^.\+\n[-]\+$/ contains=pandocEmphasis,pandocStrong,pandocNoFormatted,pandocLaTeXInlineMath,pandocEscapedDollar,@Spell,pandocAmpersandEscape +syn match pandocHeaderAttr /{.*}/ contained containedin=pandocAtxHeader,pandocSetexHeader +syn match pandocHeaderID /#[-_:.[:lower:][:upper:]]*/ contained containedin=pandocHeaderAttr +" }}}2 + +" Line Blocks: {{{2 +syn region pandocLineBlock start=/^|/ end=/\(^|\(.*\n|\@!\)\@=.*\)\@<=\n/ transparent +syn match pandocLineBlockDelimiter /^|/ contained containedin=pandocLineBlock +" }}}2 + +" Tables: {{{2 + +" Simple: {{{3 +syn region pandocSimpleTable start=/\%#=2\(^.*[[:graph:]].*\n\)\@<!\(^.*[[:graph:]].*\n\)\(-\{2,}\s*\)\+\n\n\@!/ end=/\n\n/ containedin=ALLBUT,pandocDelimitedCodeBlock,pandocDelimitedCodeBlockStart,pandocYAMLHeader keepend +syn match pandocSimpleTableDelims /\-/ contained containedin=pandocSimpleTable +syn match pandocSimpleTableHeader /\%#=2\(^.*[[:graph:]].*\n\)\@<!\(^.*[[:graph:]].*\n\)/ contained containedin=pandocSimpleTable + +syn region pandocTable start=/\%#=2^\(-\{2,}\s*\)\+\n\n\@!/ end=/\%#=2^\(-\{2,}\s*\)\+\n\n/ containedin=ALLBUT,pandocDelimitedCodeBlock,pandocYAMLHeader keepend +syn match pandocTableDelims /\-/ contained containedin=pandocTable +syn region pandocTableMultilineHeader start=/\%#=2\(^-\{2,}\n\)\@<=./ end=/\%#=2\n-\@=/ contained containedin=pandocTable +" }}}3 + +" Grid: {{{3 +syn region pandocGridTable start=/\%#=2\n\@1<=+-/ end=/+\n\n/ containedin=ALLBUT,pandocDelimitedCodeBlock,pandocYAMLHeader keepend +syn match pandocGridTableDelims /[\|=]/ contained containedin=pandocGridTable +syn match pandocGridTableDelims /\%#=2\([\-+][\-+=]\@=\|[\-+=]\@1<=[\-+]\)/ contained containedin=pandocGridTable +syn match pandocGridTableHeader /\%#=2\(^.*\n\)\(+=.*\)\@=/ contained containedin=pandocGridTable +" }}}3 + +" Pipe: {{{3 +" with beginning and end pipes +syn region pandocPipeTable start=/\%#=2\([+|]\n\)\@<!\n\@1<=|\(.*|\)\@=/ end=/|.*\n\(\n\|{\)/ containedin=ALLBUT,pandocDelimitedCodeBlock,pandocYAMLHeader keepend +" without beginning and end pipes +syn region pandocPipeTable start=/\%#=2^.*\n-.\{-}|/ end=/|.*\n\n/ keepend +syn match pandocPipeTableDelims /[\|\-:+]/ contained containedin=pandocPipeTable +syn match pandocPipeTableHeader /\(^.*\n\)\(|-\)\@=/ contained containedin=pandocPipeTable +syn match pandocPipeTableHeader /\(^.*\n\)\(-\)\@=/ contained containedin=pandocPipeTable +" }}}3 + +syn match pandocTableHeaderWord /\<.\{-}\>/ contained containedin=pandocGridTableHeader,pandocPipeTableHeader contains=@Spell +" }}}2 + +" Delimited Code Blocks: {{{2 +" this is here because we can override strikeouts and subscripts +syn region pandocDelimitedCodeBlock start=/^\(>\s\)\?\z(\([ ]\+\|\t\)\=\~\{3,}\~*\)/ end=/^\z1\~*/ skipnl contains=pandocDelimitedCodeBlockStart,pandocDelimitedCodeBlockEnd keepend +syn region pandocDelimitedCodeBlock start=/^\(>\s\)\?\z(\([ ]\+\|\t\)\=`\{3,}`*\)/ end=/^\z1`*/ skipnl contains=pandocDelimitedCodeBlockStart,pandocDelimitedCodeBlockEnd keepend +call s:WithConceal('codeblock_start', 'syn match pandocDelimitedCodeBlockStart /\(\(\_^\n\_^\|\%^\)\(>\s\)\?\( \+\|\t\)\=\)\@<=\(\~\{3,}\~*\|`\{3,}`*\)/ contained containedin=pandocDelimitedCodeBlock nextgroup=pandocDelimitedCodeBlockLanguage', 'conceal cchar='.s:cchars['codelang']) +syn match pandocDelimitedCodeBlockLanguage /\(\s\?\)\@<=.\+\(\_$\)\@=/ contained +call s:WithConceal('codeblock_delim', 'syn match pandocDelimitedCodeBlockEnd /\(`\{3,}`*\|\~\{3,}\~*\)\(\_$\n\(>\s\)\?\_$\)\@=/ contained containedin=pandocDelimitedCodeBlock', 'conceal cchar='.s:cchars['codeend']) +syn match pandocBlockQuoteinDelimitedCodeBlock '^>' contained containedin=pandocDelimitedCodeBlock +syn match pandocCodePre /<pre>.\{-}<\/pre>/ skipnl +syn match pandocCodePre /<code>.\{-}<\/code>/ skipnl + +" enable highlighting for embedded region in codeblocks if there exists a +" g:pandoc#syntax#codeblocks#embeds#langs *list*. +" +" entries in this list are the language code interpreted by pandoc, +" if this differs from the name of the vim syntax file, append =vimname +" e.g. let g:pandoc#syntax#codeblocks#embeds#langs = ["haskell", "literatehaskell=lhaskell"] +" +if g:pandoc#syntax#codeblocks#embeds#use != 0 + for l in g:pandoc#syntax#codeblocks#embeds#langs + call EnableEmbedsforCodeblocksWithLang(l) + endfor +endif +" }}}2 + +" Abbreviations: {{{2 +syn region pandocAbbreviationDefinition start=/^\*\[.\{-}\]:\s*/ end='$' contains=pandocNoFormatted,@Spell,pandocAmpersandEscape +call s:WithConceal('abbrev', 'syn match pandocAbbreviationSeparator /:/ contained containedin=pandocAbbreviationDefinition', 'conceal cchar='.s:cchars['abbrev']) +syn match pandocAbbreviation /\*\[.\{-}\]/ contained containedin=pandocAbbreviationDefinition +call s:WithConceal('abbrev', 'syn match pandocAbbreviationHead /\*\[/ contained containedin=pandocAbbreviation', 'conceal') +call s:WithConceal('abbrev', 'syn match pandocAbbreviationTail /\]/ contained containedin=pandocAbbreviation', 'conceal') +" }}}2 + +" Footnotes: {{{2 +" we put these here not to interfere with superscripts. +syn match pandocFootnoteID /\[\^[^\]]\+\]/ nextgroup=pandocFootnoteDef + +" Inline footnotes +syn region pandocFootnoteDef start=/\^\[/ skip=/\[.\{-}]/ end=/\]/ contains=pandocReferenceLabel,pandocReferenceURL,pandocLatex,pandocPCite,pandocCiteKey,pandocStrong,pandocEmphasis,pandocStrongEmphasis,pandocNoFormatted,pandocSuperscript,pandocSubscript,pandocStrikeout,pandocEnDash,pandocEmDash,pandocEllipses,pandocBeginQuote,pandocEndQuote,@Spell,pandocAmpersandEscape skipnl keepend +call s:WithConceal('footnote', 'syn match pandocFootnoteDefHead /\^\[/ contained containedin=pandocFootnoteDef', 'conceal cchar='.s:cchars['footnote']) +call s:WithConceal('footnote', 'syn match pandocFootnoteDefTail /\]/ contained containedin=pandocFootnoteDef', 'conceal') + +" regular footnotes +syn region pandocFootnoteBlock start=/\[\^.\{-}\]:\s*\n*/ end=/^\n^\s\@!/ contains=pandocReferenceLabel,pandocReferenceURL,pandocLatex,pandocPCite,pandocCiteKey,pandocStrong,pandocEmphasis,pandocNoFormatted,pandocSuperscript,pandocSubscript,pandocStrikeout,pandocEnDash,pandocEmDash,pandocNewLine,pandocStrongEmphasis,pandocEllipses,pandocBeginQuote,pandocEndQuote,pandocLaTeXInlineMath,pandocEscapedDollar,pandocLaTeXCommand,pandocLaTeXMathBlock,pandocLaTeXRegion,pandocAmpersandEscape,@Spell skipnl +syn match pandocFootnoteBlockSeparator /:/ contained containedin=pandocFootnoteBlock +syn match pandocFootnoteID /\[\^.\{-}\]/ contained containedin=pandocFootnoteBlock +call s:WithConceal('footnote', 'syn match pandocFootnoteIDHead /\[\^/ contained containedin=pandocFootnoteID', 'conceal cchar='.s:cchars['footnote']) +call s:WithConceal('footnote', 'syn match pandocFootnoteIDTail /\]/ contained containedin=pandocFootnoteID', 'conceal') +" }}}2 + +" List Items: {{{2 +" Unordered lists +syn match pandocUListItem /^>\=\s*[*+-]\s\+-\@!.*$/ nextgroup=pandocUListItem,pandocLaTeXMathBlock,pandocLaTeXInlineMath,pandocEscapedDollar,pandocDelimitedCodeBlock,pandocListItemContinuation contains=@Spell,pandocEmphasis,pandocStrong,pandocNoFormatted,pandocStrikeout,pandocSubscript,pandocSuperscript,pandocStrongEmphasis,pandocStrongEmphasis,pandocPCite,pandocICite,pandocCiteKey,pandocReferenceLabel,pandocLaTeXCommand,pandocLaTeXMathBlock,pandocLaTeXInlineMath,pandocEscapedDollar,pandocReferenceURL,pandocAutomaticLink,pandocFootnoteDef,pandocFootnoteBlock,pandocFootnoteID,pandocAmpersandEscape skipempty display +call s:WithConceal('list', 'syn match pandocUListItemBullet /^>\=\s*\zs[*+-]/ contained containedin=pandocUListItem', 'conceal cchar='.s:cchars['li']) + +" Ordered lists +syn match pandocListItem /^\s*(\?\(\d\+\|\l\|\#\|@\)[.)].*$/ nextgroup=pandocListItem,pandocLaTeXMathBlock,pandocLaTeXInlineMath,pandocEscapedDollar,pandocDelimitedCodeBlock,pandocListItemContinuation contains=@Spell,pandocEmphasis,pandocStrong,pandocNoFormatted,pandocStrikeout,pandocSubscript,pandocSuperscript,pandocStrongEmphasis,pandocStrongEmphasis,pandocPCite,pandocICite,pandocCiteKey,pandocReferenceLabel,pandocLaTeXCommand,pandocLaTeXMathBlock,pandocLaTeXInlineMath,pandocEscapedDollar,pandocAutomaticLink,pandocFootnoteDef,pandocFootnoteBlock,pandocFootnoteID,pandocAmpersandEscape skipempty display + +" support for roman numerals up to 'c' +if g:pandoc#syntax#roman_lists != 0 + syn match pandocListItem /^\s*(\?x\=l\=\(i\{,3}[vx]\=\)\{,3}c\{,3}[.)].*$/ nextgroup=pandocListItem,pandocMathBlock,pandocLaTeXInlineMath,pandocEscapedDollar,pandocDelimitedCodeBlock,pandocListItemContinuation,pandocAutomaticLink skipempty display +endif +syn match pandocListItemBullet /^(\?.\{-}[.)]/ contained containedin=pandocListItem +syn match pandocListItemBulletId /\(\d\+\|\l\|\#\|@.\{-}\|x\=l\=\(i\{,3}[vx]\=\)\{,3}c\{,3}\)/ contained containedin=pandocListItemBullet + +syn match pandocListItemContinuation /^\s\+\([-+*]\s\+\|(\?.\+[).]\)\@<!\([[:upper:][:lower:]_"[]\|\*\S\)\@=.*$/ nextgroup=pandocLaTeXMathBlock,pandocLaTeXInlineMath,pandocEscapedDollar,pandocDelimitedCodeBlock,pandocListItemContinuation,pandocListItem contains=@Spell,pandocEmphasis,pandocStrong,pandocNoFormatted,pandocStrikeout,pandocSubscript,pandocSuperscript,pandocStrongEmphasis,pandocStrongEmphasis,pandocPCite,pandocICite,pandocCiteKey,pandocReferenceLabel,pandocReferenceURL,pandocLaTeXCommand,pandocLaTeXMathBlock,pandocLaTeXInlineMath,pandocEscapedDollar,pandocAutomaticLink,pandocFootnoteDef,pandocFootnoteBlock,pandocFootnoteID,pandocAmpersandEscape contained skipempty display +" }}}2 + +" Definitions: {{{2 +if g:pandoc#syntax#use_definition_lists == 1 + syn region pandocDefinitionBlock start=/^\%(\_^\s*\([`~]\)\1\{2,}\)\@!.*\n\(^\s*\n\)\=\s\{0,2}\([:~]\)\(\3\{2,}\3*\)\@!/ skip=/\n\n\zs\s/ end=/\n\n/ contains=pandocDefinitionBlockMark,pandocDefinitionBlockTerm,pandocCodeBlockInsideIndent,pandocEmphasis,pandocStrong,pandocStrongEmphasis,pandocNoFormatted,pandocStrikeout,pandocSubscript,pandocSuperscript,pandocFootnoteID,pandocReferenceURL,pandocReferenceLabel,pandocLaTeXMathBlock,pandocLaTeXInlineMath,pandocEscapedDollar,pandocAutomaticLink,pandocEmDash,pandocEnDash,pandocFootnoteDef,pandocFootnoteBlock,pandocFootnoteID + syn match pandocDefinitionBlockTerm /^.*\n\(^\s*\n\)\=\(\s*[:~]\)\@=/ contained contains=pandocNoFormatted,pandocEmphasis,pandocStrong,pandocLaTeXInlineMath,pandocEscapedDollar,pandocFootnoteDef,pandocFootnoteBlock,pandocFootnoteID nextgroup=pandocDefinitionBlockMark + call s:WithConceal('definition', 'syn match pandocDefinitionBlockMark /^\s*[:~]/ contained', 'conceal cchar='.s:cchars['definition']) +endif +" }}}2 + +" Special: {{{2 + +" New_lines: {{{3 +if g:pandoc#syntax#newlines == 1 + call s:WithConceal('newline', 'syn match pandocNewLine /\%(\%(\S\)\@<= \{2,}\|\\\)$/ display containedin=pandocEmphasis,pandocStrong,pandocStrongEmphasis,pandocStrongInEmphasis,pandocEmphasisInStrong', 'conceal cchar='.s:cchars['newline']) +endif +" }}}3 + +" Emdashes: {{{3 +if &encoding ==# 'utf-8' + call s:WithConceal('emdashes', 'syn match pandocEllipses /\([^-]\)\@<=---\([^-]\)\@=/ display', 'conceal cchar=—') +endif +" }}}3 + +" Endashes: {{{3 +if &encoding ==# 'utf-8' + call s:WithConceal('endashes', 'syn match pandocEllipses /\([^-]\)\@<=--\([^-]\)\@=/ display', 'conceal cchar=–') +endif +" }}}3 + +" Ellipses: {{{3 +if &encoding ==# 'utf-8' + call s:WithConceal('ellipses', 'syn match pandocEllipses /\.\.\./ display', 'conceal cchar=…') +endif +" }}}3 + +" Quotes: {{{3 +if &encoding ==# 'utf-8' + call s:WithConceal('quotes', 'syn match pandocBeginQuote /"\</ containedin=pandocEmphasis,pandocStrong,pandocListItem,pandocListItemContinuation,pandocUListItem display', 'conceal cchar='.s:cchars['quote_s']) + call s:WithConceal('quotes', 'syn match pandocEndQuote /\(\>[[:punct:]]*\)\@<="[[:blank:][:punct:]\n]\@=/ containedin=pandocEmphasis,pandocStrong,pandocUListItem,pandocListItem,pandocListItemContinuation display', 'conceal cchar='.s:cchars['quote_e']) +endif +" }}}3 + +" Hrule: {{{3 +syn match pandocHRule /^\s*\([*\-_]\)\s*\%(\1\s*\)\{2,}$/ display +" }}}3 + +" Backslashes: {{{3 +if g:pandoc#syntax#conceal#backslash == 1 + syn match pandocBackslash /\v\\@<!\\((re)?newcommand)@!/ containedin=ALLBUT,pandocCodeblock,pandocCodeBlockInsideIndent,pandocNoFormatted,pandocNoFormattedInEmphasis,pandocNoFormattedInStrong,pandocDelimitedCodeBlock,pandocLineBlock,pandocYAMLHeader conceal +endif +" }}}3 + +" &-escaped Special Characters: {{{3 +syn match pandocAmpersandEscape /\v\&(#\d+|#x\x+|[[:alnum:]]+)\;/ contains=NoSpell +" }}}3 + +" YAML: {{{2 +try + unlet! b:current_syntax + syn include @YAML syntax/yaml.vim +catch /E484/ +endtry +syn region pandocYAMLHeader start=/\%(\%^\|\_^\s*\n\)\@<=\_^-\{3}\ze\n.\+/ end=/^\([-.]\)\1\{2}$/ keepend contains=@YAML containedin=TOP +" }}}2 + +" }}}1 + +" Styling: {{{1 +function! s:SetupPandocHighlights() + + hi def link pandocOperator Operator + + " override this for consistency + hi pandocTitleBlock term=italic gui=italic + hi def link pandocTitleBlockTitle Directory + hi def link pandocAtxHeader Title + hi def link pandocAtxStart Operator + hi def link pandocSetexHeader Title + hi def link pandocHeaderAttr Comment + hi def link pandocHeaderID Identifier + + hi def link pandocLaTexSectionCmd texSection + hi def link pandocLaTeXDelimiter texDelimiter + + hi def link pandocHTMLComment Comment + hi def link pandocHTMLCommentStart Delimiter + hi def link pandocHTMLCommentEnd Delimiter + hi def link pandocBlockQuote Comment + hi def link pandocBlockQuoteMark Comment + hi def link pandocAmpersandEscape Special + + " if the user sets g:pandoc#syntax#codeblocks#ignore to contain + " a codeblock type, don't highlight it so that it remains Normal + if index(g:pandoc#syntax#codeblocks#ignore, 'definition') == -1 + hi def link pandocCodeBlockInsideIndent String + endif + + if index(g:pandoc#syntax#codeblocks#ignore, 'delimited') == -1 + hi def link pandocDelimitedCodeBlock Special + endif + + hi def link pandocDelimitedCodeBlockStart Delimiter + hi def link pandocDelimitedCodeBlockEnd Delimiter + hi def link pandocDelimitedCodeBlockLanguage Comment + hi def link pandocBlockQuoteinDelimitedCodeBlock pandocBlockQuote + hi def link pandocCodePre String + + hi def link pandocLineBlockDelimiter Delimiter + + hi def link pandocListItemBullet Operator + hi def link pandocUListItemBullet Operator + hi def link pandocListItemBulletId Identifier + + hi def link pandocReferenceLabel Label + hi def link pandocReferenceURL Underlined + hi def link pandocLinkTip Identifier + hi def link pandocImageIcon Operator + + hi def link pandocReferenceDefinition Operator + hi def link pandocReferenceDefinitionLabel Label + hi def link pandocReferenceDefinitionAddress Underlined + hi def link pandocReferenceDefinitionTip Identifier + + hi def link pandocAutomaticLink Underlined + + hi def link pandocDefinitionBlockTerm Identifier + hi def link pandocDefinitionBlockMark Operator + + hi def link pandocSimpleTableDelims Delimiter + hi def link pandocSimpleTableHeader pandocStrong + hi def link pandocTableMultilineHeader pandocStrong + hi def link pandocTableDelims Delimiter + hi def link pandocGridTableDelims Delimiter + hi def link pandocGridTableHeader Delimiter + hi def link pandocPipeTableDelims Delimiter + hi def link pandocPipeTableHeader Delimiter + hi def link pandocTableHeaderWord pandocStrong + + hi def link pandocAbbreviationHead Type + hi def link pandocAbbreviation Label + hi def link pandocAbbreviationTail Type + hi def link pandocAbbreviationSeparator Identifier + hi def link pandocAbbreviationDefinition Comment + + hi def link pandocFootnoteID Label + hi def link pandocFootnoteIDHead Type + hi def link pandocFootnoteIDTail Type + hi def link pandocFootnoteDef Comment + hi def link pandocFootnoteDefHead Type + hi def link pandocFootnoteDefTail Type + hi def link pandocFootnoteBlock Comment + hi def link pandocFootnoteBlockSeparator Operator + + hi def link pandocPCite Operator + hi def link pandocICite Operator + hi def link pandocCiteKey Label + hi def link pandocCiteAnchor Operator + hi def link pandocCiteLocator Operator + + if g:pandoc#syntax#style#emphases == 1 + hi pandocEmphasis gui=italic cterm=italic + hi pandocStrong gui=bold cterm=bold + hi pandocStrongEmphasis gui=bold,italic cterm=bold,italic + hi pandocStrongInEmphasis gui=bold,italic cterm=bold,italic + hi pandocEmphasisInStrong gui=bold,italic cterm=bold,italic + if !exists('s:hi_tail') + let s:fg = '' " Vint can't figure ou these get set dynamically + let s:bg = '' " so initialize them manually first + for s:i in ['fg', 'bg'] + let s:tmp_val = synIDattr(synIDtrans(hlID('String')), s:i) + let s:tmp_ui = has('gui_running') || (has('termguicolors') && &termguicolors) ? 'gui' : 'cterm' + if !empty(s:tmp_val) && s:tmp_val != -1 + exe 'let s:'.s:i . ' = "'.s:tmp_ui.s:i.'='.s:tmp_val.'"' + else + exe 'let s:'.s:i . ' = ""' + endif + endfor + let s:hi_tail = ' '.s:fg.' '.s:bg + endif + exe 'hi pandocNoFormattedInEmphasis gui=italic cterm=italic'.s:hi_tail + exe 'hi pandocNoFormattedInStrong gui=bold cterm=bold'.s:hi_tail + endif + hi def link pandocNoFormatted String + hi def link pandocNoFormattedAttrs Comment + hi def link pandocSubscriptMark Operator + hi def link pandocSuperscriptMark Operator + hi def link pandocStrikeoutMark Operator + if g:pandoc#syntax#style#underline_special == 1 + hi pandocSubscript gui=underline cterm=underline + hi pandocSuperscript gui=underline cterm=underline + hi pandocStrikeout gui=underline cterm=underline + endif + hi def link pandocNewLine Error + hi def link pandocHRule Delimiter +endfunction + +call s:SetupPandocHighlights() + +" }}}1 + +let b:current_syntax = 'pandoc' + +syntax sync clear +syntax sync minlines=1000 + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: set fdm=marker foldlevel=0: diff --git a/runtime/syntax/requirements.vim b/runtime/syntax/requirements.vim new file mode 100644 index 0000000000..a87d1e9a39 --- /dev/null +++ b/runtime/syntax/requirements.vim @@ -0,0 +1,67 @@ +" the Requirements File Format syntax support for Vim +" Version: 1.8.0 +" Author: raimon <raimon49@hotmail.com> +" Upstream: https://github.com/raimon49/requirements.txt.vim +" License: MIT LICENSE +" The MIT License (MIT) +" +" Copyright (c) 2015 raimon +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to deal +" in the Software without restriction, including without limitation the rights +" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +" copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in all +" copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +" SOFTWARE. + +if exists("b:current_syntax") && b:current_syntax == "requirements" + finish +endif + +syn case match + +" https://pip.pypa.io/en/stable/reference/requirements-file-format/ +" https://pip.pypa.io/en/stable/reference/inspect-report/#example +syn keyword requirementsKeyword implementation_name implementation_version os_name platform_machine platform_release platform_system platform_version python_full_version platform_python_implementation python_version sys_platform contained +syn region requirementsSubst matchgroup=requirementsSubstDelim start="\V${" end="\V}" +syn region requirementsString matchgroup=requirementsStringDelim start=`'` skip=`\\'` end=`'` +syn region requirementsString matchgroup=requirementsStringDelim start=`"` skip=`\\"` end=`"` +syn match requirementsVersion "\v\d+[a-zA-Z0-9\.\-\*]*" +syn region requirementsComment start="[ \t]*#" end="$" +syn match requirementsCommandOption "\v^\[?--?[a-zA-Z\-]*\]?" +syn match requirementsVersionSpecifiers "\v(\=\=\=?|\<\=?|\>\=?|\~\=|\!\=)" +syn match requirementsPackageName "\v^([a-zA-Z0-9][a-zA-Z0-9\-_\.]*[a-zA-Z0-9])" +syn match requirementsExtras "\v\[\S+\]" +syn match requirementsVersionControls "\v(git\+?|hg\+|svn\+|bzr\+).*://.\S+" +syn match requirementsURLs "\v(\@\s)?(https?|ftp|gopher)://?[^\s/$.?#].\S*" +syn match requirementsEnvironmentMarkers "\v;\s[^#]+" contains=requirementsKeyword,requirementsVersionSpecifiers,requirementsString + +hi def link requirementsKeyword Keyword +hi def link requirementsSubstDelim Delimiter +hi def link requirementsSubst PreProc +hi def link requirementsStringDelim Delimiter +hi def link requirementsString String +hi def link requirementsVersion Number +hi def link requirementsComment Comment +hi def link requirementsCommandOption Special +hi def link requirementsVersionSpecifiers Boolean +hi def link requirementsPackageName Identifier +hi def link requirementsExtras Type +hi def link requirementsVersionControls Underlined +hi def link requirementsURLs Underlined +hi def link requirementsEnvironmentMarkers Macro + +let b:current_syntax = "requirements" + +" vim: et sw=4 ts=4 sts=4: diff --git a/runtime/syntax/shared/debversions.vim b/runtime/syntax/shared/debversions.vim index 4aec246e27..e18eca96b1 100644 --- a/runtime/syntax/shared/debversions.vim +++ b/runtime/syntax/shared/debversions.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Debian version information " Maintainer: Debian Vim Maintainers -" Last Change: 2024 Jan 25 +" Last Change: 2024 Apr 27 " URL: https://salsa.debian.org/vim-team/vim-debian/blob/main/syntax/shared/debversions.vim let s:cpo = &cpo @@ -11,7 +11,7 @@ let g:debSharedSupportedVersions = [ \ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', 'sid', 'rc-buggy', \ 'bullseye', 'bookworm', 'trixie', 'forky', \ - \ 'trusty', 'xenial', 'bionic', 'focal', 'jammy', 'mantic', 'noble', + \ 'trusty', 'xenial', 'bionic', 'focal', 'jammy', 'mantic', 'noble', 'oracular', \ 'devel' \ ] let g:debSharedUnsupportedVersions = [ diff --git a/runtime/syntax/spec.vim b/runtime/syntax/spec.vim index 12ce8d5ac1..4cb3a343eb 100644 --- a/runtime/syntax/spec.vim +++ b/runtime/syntax/spec.vim @@ -111,7 +111,7 @@ syn region specDescriptionArea matchgroup=specSection start='^%description' end= syn region specPackageArea matchgroup=specSection start='^%package' end='^%'me=e-1 contains=specPackageOpts,specPreAmble,specComment "%% Scripts Section %% -syn region specScriptArea matchgroup=specSection start='^%\(prep\|build\|install\|clean\|check\|pre\|postun\|preun\|post\|posttrans\)\>' skip='^%{\|^%\(define\|patch\d*\|configure\|GNUconfigure\|setup\|autosetup\|autopatch\|find_lang\|make_build\|makeinstall\|make_install\)\>' end='^%'me=e-1 contains=specSpecialVariables,specVariables,@specCommands,specVariables,shDo,shFor,shCaseEsac,specNoNumberHilite,specCommandOpts,shComment,shIf,specSpecialChar,specMacroIdentifier,specSectionMacroArea,specSectionMacroBracketArea,shOperator,shQuote1,shQuote2 +syn region specScriptArea matchgroup=specSection start='^%\(prep\|generate_buildrequires\|conf\|build\|install\|clean\|check\|pre\|postun\|preun\|post\|posttrans\)\>' skip='^%{\|^%\(define\|patch\d*\|configure\|GNUconfigure\|setup\|autosetup\|autopatch\|find_lang\|make_build\|makeinstall\|make_install\)\>' end='^%'me=e-1 contains=specSpecialVariables,specVariables,@specCommands,specVariables,shDo,shFor,shCaseEsac,specNoNumberHilite,specCommandOpts,shComment,shIf,specSpecialChar,specMacroIdentifier,specSectionMacroArea,specSectionMacroBracketArea,shOperator,shQuote1,shQuote2 "%% Changelog Section %% syn region specChangelogArea matchgroup=specSection start='^%changelog' end='^%'me=e-1 contains=specEmail,specURL,specWeekday,specMonth,specNumber,specComment,specLicense diff --git a/runtime/syntax/ssa.vim b/runtime/syntax/ssa.vim index a5dbf37c30..3cfae816ff 100644 --- a/runtime/syntax/ssa.vim +++ b/runtime/syntax/ssa.vim @@ -2,7 +2,7 @@ " Language: SubStation Alpha " Maintainer: ObserverOfTime <chronobserver@disroot.org> " Filenames: *.ass,*.ssa -" Last Change: 2022 Oct 10 +" Last Change: 2024 Apr 28 if exists('b:current_syntax') finish @@ -20,16 +20,20 @@ syn match ssaSection /^\[[a-zA-Z0-9+ ]\+\]$/ syn match ssaHeader /^[^;!:]\+:/ skipwhite nextgroup=ssaField " Fields -syn match ssaField /[^,]*/ contained skipwhite nextgroup=ssaDelimiter +syn match ssaField /[^,]*\(,\|$\)/ contained skipwhite contains=ssaDelimiter,ssaTime nextgroup=ssaField " Time -syn match ssaTime /\d:\d\d:\d\d\.\d\d/ contained skipwhite nextgroup=ssaDelimiter +syn match ssaTime /\d:\d\d:\d\d\.\d\d/ contained " Delimiter -syn match ssaDelimiter /,/ contained skipwhite nextgroup=ssaField,ssaTime,ssaText +syn match ssaDelimiter /,/ contained + +" Dialogue +syn match ssaDialogue /^Dialogue:/ transparent skipwhite nextgroup=ssaDialogueFields +syn match ssaDialogueFields /\([^,]*,\)\{9\}/ contained transparent skipwhite contains=ssaField,ssaDelimiter nextgroup=ssaText " Text -syn match ssaText /\(^Dialogue:\(.*,\)\{9}\)\@<=.*$/ contained contains=@ssaTags,@Spell +syn match ssaText /.*$/ contained contains=@ssaTags,@Spell syn cluster ssaTags contains=ssaOverrideTag,ssaEscapeChar,ssaTextComment,ssaItalics,ssaBold,ssaUnderline,ssaStrikeout " Override tags @@ -60,4 +64,4 @@ hi ssaItalics cterm=italic gui=italic hi ssaStrikeout cterm=strikethrough gui=strikethrough hi ssaUnderline cterm=underline gui=underline -let b:current_syntax = 'srt' +let b:current_syntax = 'ssa' diff --git a/runtime/syntax/sshconfig.vim b/runtime/syntax/sshconfig.vim index 750289d83e..99e10fd1ee 100644 --- a/runtime/syntax/sshconfig.vim +++ b/runtime/syntax/sshconfig.vim @@ -63,8 +63,7 @@ syn keyword sshconfigMAC hmac-sha2-256 syn keyword sshconfigMAC hmac-sha2-512 syn keyword sshconfigMAC hmac-md5 syn keyword sshconfigMAC hmac-md5-96 -syn keyword sshconfigMAC hmac-ripemd160 -syn match sshconfigMAC "\<hmac-ripemd160@openssh\.com\>" +syn match sshconfigMAC "\<hmac-ripemd160\%(@openssh\.com\)\?\>" syn match sshconfigMAC "\<umac-64@openssh\.com\>" syn match sshconfigMAC "\<umac-128@openssh\.com\>" syn match sshconfigMAC "\<hmac-sha1-etm@openssh\.com\>" @@ -107,33 +106,35 @@ syn keyword sshconfigSysLogFacility DAEMON USER AUTH AUTHPRIV LOCAL0 LOCAL1 syn keyword sshconfigSysLogFacility LOCAL2 LOCAL3 LOCAL4 LOCAL5 LOCAL6 LOCAL7 syn keyword sshconfigAddressFamily inet inet6 -syn match sshconfigIPQoS "af1[123]" -syn match sshconfigIPQoS "af2[123]" -syn match sshconfigIPQoS "af3[123]" -syn match sshconfigIPQoS "af4[123]" -syn match sshconfigIPQoS "cs[0-7]" -syn keyword sshconfigIPQoS ef lowdelay throughput reliability +syn match sshconfigIPQoS "\<af[1-4][1-3]\>" +syn match sshconfigIPQoS "\<cs[0-7]\>" +syn keyword sshconfigIPQoS ef le lowdelay throughput reliability syn keyword sshconfigKbdInteractive bsdauth pam skey syn keyword sshconfigKexAlgo diffie-hellman-group1-sha1 syn keyword sshconfigKexAlgo diffie-hellman-group14-sha1 syn keyword sshconfigKexAlgo diffie-hellman-group-exchange-sha1 syn keyword sshconfigKexAlgo diffie-hellman-group-exchange-sha256 +syn keyword sshconfigKexAlgo diffie-hellman-group16-sha512 +syn keyword sshconfigKexAlgo diffie-hellman-group18-sha512 +syn keyword sshconfigKexAlgo diffie-hellman-group14-sha256 syn keyword sshconfigKexAlgo ecdh-sha2-nistp256 syn keyword sshconfigKexAlgo ecdh-sha2-nistp384 syn keyword sshconfigKexAlgo ecdh-sha2-nistp521 -syn match sshconfigKexAlgo "\<curve25519-sha256@libssh\.org\>" +syn match sshconfigKexAlgo "\<curve25519-sha256\%(@libssh\.org\)\?\>" +syn match sshconfigKexAlgo "\<sntrup761x25519-sha512@openssh\.com\>" syn keyword sshconfigTunnel point-to-point ethernet -syn match sshconfigVar "%[rhplLdun]\>" +syn match sshconfigVar "%[CdfHhIijKkLlnprTtu]\>" +syn match sshconfigVar "%%" syn match sshconfigSpecial "[*?]" -syn match sshconfigNumber "\d\+" +syn match sshconfigNumber "\<\d\+\>" syn match sshconfigHostPort "\<\(\d\{1,3}\.\)\{3}\d\{1,3}\(:\d\+\)\?\>" syn match sshconfigHostPort "\<\([-a-zA-Z0-9]\+\.\)\+[-a-zA-Z0-9]\{2,}\(:\d\+\)\?\>" syn match sshconfigHostPort "\<\(\x\{,4}:\)\+\x\{,4}[:/]\d\+\>" -syn match sshconfigHostPort "\(Host \)\@<=.\+" -syn match sshconfigHostPort "\(HostName \)\@<=.\+" +syn match sshconfigHostPort "\<\c\(Host \+\)\@<=.\+" +syn match sshconfigHostPort "\<\c\(Hostname \+\)\@<=.\+" " case off syn case ignore @@ -142,10 +143,10 @@ syn case ignore " Keywords syn keyword sshconfigHostSect Host -syn keyword sshconfigMatch canonical final exec host originalhost user localuser all +syn keyword sshconfigMatch canonical final exec localnetwork host originalhost tagged user localuser all -syn keyword sshconfigKeyword AddressFamily syn keyword sshconfigKeyword AddKeysToAgent +syn keyword sshconfigKeyword AddressFamily syn keyword sshconfigKeyword BatchMode syn keyword sshconfigKeyword BindAddress syn keyword sshconfigKeyword BindInterface @@ -157,16 +158,18 @@ syn keyword sshconfigKeyword CanonicalizePermittedCNAMEs syn keyword sshconfigKeyword CASignatureAlgorithms syn keyword sshconfigKeyword CertificateFile syn keyword sshconfigKeyword ChallengeResponseAuthentication +syn keyword sshconfigKeyword ChannelTimeout syn keyword sshconfigKeyword CheckHostIP syn keyword sshconfigKeyword Ciphers syn keyword sshconfigKeyword ClearAllForwardings syn keyword sshconfigKeyword Compression -syn keyword sshconfigKeyword ConnectTimeout syn keyword sshconfigKeyword ConnectionAttempts +syn keyword sshconfigKeyword ConnectTimeout syn keyword sshconfigKeyword ControlMaster syn keyword sshconfigKeyword ControlPath syn keyword sshconfigKeyword ControlPersist syn keyword sshconfigKeyword DynamicForward +syn keyword sshconfigKeyword EnableEscapeCommandline syn keyword sshconfigKeyword EnableSSHKeysign syn keyword sshconfigKeyword EscapeChar syn keyword sshconfigKeyword ExitOnForwardFailure @@ -176,18 +179,17 @@ syn keyword sshconfigKeyword ForwardAgent syn keyword sshconfigKeyword ForwardX11 syn keyword sshconfigKeyword ForwardX11Timeout syn keyword sshconfigKeyword ForwardX11Trusted -syn keyword sshconfigKeyword GSSAPIAuthentication -syn keyword sshconfigKeyword GSSAPIDelegateCredentials syn keyword sshconfigKeyword GatewayPorts syn keyword sshconfigKeyword GlobalKnownHostsFile +syn keyword sshconfigKeyword GSSAPIAuthentication +syn keyword sshconfigKeyword GSSAPIDelegateCredentials syn keyword sshconfigKeyword HashKnownHosts -syn keyword sshconfigKeyword HostKeyAlgorithms -syn keyword sshconfigKeyword HostKeyAlias -syn keyword sshconfigKeyword HostName -syn keyword sshconfigKeyword HostbasedAuthentication syn keyword sshconfigKeyword HostbasedAcceptedAlgorithms +syn keyword sshconfigKeyword HostbasedAuthentication syn keyword sshconfigKeyword HostbasedKeyTypes -syn keyword sshconfigKeyword IPQoS +syn keyword sshconfigKeyword HostKeyAlgorithms +syn keyword sshconfigKeyword HostKeyAlias +syn keyword sshconfigKeyword Hostname syn keyword sshconfigKeyword IdentitiesOnly syn keyword sshconfigKeyword IdentityAgent syn keyword sshconfigKeyword IdentityFile @@ -206,15 +208,16 @@ syn keyword sshconfigKeyword MACs syn keyword sshconfigKeyword Match syn keyword sshconfigKeyword NoHostAuthenticationForLocalhost syn keyword sshconfigKeyword NumberOfPasswordPrompts -syn keyword sshconfigKeyword PKCS11Provider +syn keyword sshconfigKeyword ObscureKeystrokeTiming syn keyword sshconfigKeyword PasswordAuthentication syn keyword sshconfigKeyword PermitLocalCommand syn keyword sshconfigKeyword PermitRemoteOpen +syn keyword sshconfigKeyword PKCS11Provider syn keyword sshconfigKeyword Port syn keyword sshconfigKeyword PreferredAuthentications syn keyword sshconfigKeyword ProxyCommand syn keyword sshconfigKeyword ProxyJump -syn keyword sshconfigKeyword ProxyUseFDPass +syn keyword sshconfigKeyword ProxyUseFdpass syn keyword sshconfigKeyword PubkeyAcceptedAlgorithms syn keyword sshconfigKeyword PubkeyAcceptedKeyTypes syn keyword sshconfigKeyword PubkeyAuthentication @@ -229,18 +232,19 @@ syn keyword sshconfigKeyword SendEnv syn keyword sshconfigKeyword ServerAliveCountMax syn keyword sshconfigKeyword ServerAliveInterval syn keyword sshconfigKeyword SessionType -syn keyword sshconfigKeyword SmartcardDevice syn keyword sshconfigKeyword SetEnv +syn keyword sshconfigKeyword SmartcardDevice syn keyword sshconfigKeyword StdinNull syn keyword sshconfigKeyword StreamLocalBindMask syn keyword sshconfigKeyword StreamLocalBindUnlink syn keyword sshconfigKeyword StrictHostKeyChecking syn keyword sshconfigKeyword SyslogFacility +syn keyword sshconfigKeyword Tag syn keyword sshconfigKeyword TCPKeepAlive syn keyword sshconfigKeyword Tunnel syn keyword sshconfigKeyword TunnelDevice -syn keyword sshconfigKeyword UseBlacklistedKeys syn keyword sshconfigKeyword UpdateHostKeys +syn keyword sshconfigKeyword UseBlacklistedKeys syn keyword sshconfigKeyword User syn keyword sshconfigKeyword UserKnownHostsFile syn keyword sshconfigKeyword VerifyHostKeyDNS @@ -268,9 +272,9 @@ syn keyword sshconfigDeprecated UsePrivilegedPort hi def link sshconfigComment Comment hi def link sshconfigTodo Todo hi def link sshconfigHostPort sshconfigConstant -hi def link sshconfigNumber sshconfigConstant +hi def link sshconfigNumber Number hi def link sshconfigConstant Constant -hi def link sshconfigYesNo sshconfigEnum +hi def link sshconfigYesNo Boolean hi def link sshconfigCipher sshconfigDeprecated hi def link sshconfigCiphers sshconfigEnum hi def link sshconfigMAC sshconfigEnum diff --git a/runtime/syntax/sshdconfig.vim b/runtime/syntax/sshdconfig.vim index c0d9c3f598..8b539d907b 100644 --- a/runtime/syntax/sshdconfig.vim +++ b/runtime/syntax/sshdconfig.vim @@ -64,8 +64,7 @@ syn keyword sshdconfigMAC hmac-sha2-256 syn keyword sshdconfigMAC hmac-sha2-512 syn keyword sshdconfigMAC hmac-md5 syn keyword sshdconfigMAC hmac-md5-96 -syn keyword sshdconfigMAC hmac-ripemd160 -syn match sshdconfigMAC "\<hmac-ripemd160@openssh\.com\>" +syn match sshdconfigMAC "\<hmac-ripemd160\%(@openssh\.com\)\?\>" syn match sshdconfigMAC "\<umac-64@openssh\.com\>" syn match sshdconfigMAC "\<umac-128@openssh\.com\>" syn match sshdconfigMAC "\<hmac-sha1-etm@openssh\.com\>" @@ -108,12 +107,9 @@ syn keyword sshdconfigSysLogFacility LOCAL2 LOCAL3 LOCAL4 LOCAL5 LOCAL6 LOCAL7 syn keyword sshdconfigCompression delayed -syn match sshdconfigIPQoS "af1[123]" -syn match sshdconfigIPQoS "af2[123]" -syn match sshdconfigIPQoS "af3[123]" -syn match sshdconfigIPQoS "af4[123]" -syn match sshdconfigIPQoS "cs[0-7]" -syn keyword sshdconfigIPQoS ef lowdelay throughput reliability +syn match sshdconfigIPQoS "\<af[1-4][1-3]\>" +syn match sshdconfigIPQoS "\<cs[0-7]\>" +syn keyword sshdconfigIPQoS ef le lowdelay throughput reliability syn keyword sshdconfigKexAlgo diffie-hellman-group1-sha1 syn keyword sshdconfigKexAlgo diffie-hellman-group14-sha1 @@ -125,20 +121,20 @@ syn keyword sshdconfigKexAlgo diffie-hellman-group-exchange-sha256 syn keyword sshdconfigKexAlgo ecdh-sha2-nistp256 syn keyword sshdconfigKexAlgo ecdh-sha2-nistp384 syn keyword sshdconfigKexAlgo ecdh-sha2-nistp521 -syn keyword sshdconfigKexAlgo curve25519-sha256 -syn match sshdconfigKexAlgo "\<curve25519-sha256@libssh\.org\>" +syn match sshdconfigKexAlgo "\<curve25519-sha256\%(@libssh\.org\)\?\>" syn match sshdconfigKexAlgo "\<sntrup4591761x25519-sha512@tinyssh\.org\>" +syn match sshdconfigKexAlgo "\<sntrup761x25519-sha512@openssh\.com\>" syn keyword sshdconfigTunnel point-to-point ethernet syn keyword sshdconfigSubsystem internal-sftp -syn match sshdconfigVar "%[hu]\>" +syn match sshdconfigVar "%[CDFfhiKksTtUu]\>" syn match sshdconfigVar "%%" syn match sshdconfigSpecial "[*?]" -syn match sshdconfigNumber "\d\+" +syn match sshdconfigNumber "\<\d\+\>" syn match sshdconfigHostPort "\<\(\d\{1,3}\.\)\{3}\d\{1,3}\(:\d\+\)\?\>" syn match sshdconfigHostPort "\<\([-a-zA-Z0-9]\+\.\)\+[-a-zA-Z0-9]\{2,}\(:\d\+\)\?\>" " FIXME: this matches quite a few things which are NOT valid IPv6 addresses @@ -162,15 +158,16 @@ syn keyword sshdconfigKeyword AllowStreamLocalForwarding syn keyword sshdconfigKeyword AllowTcpForwarding syn keyword sshdconfigKeyword AllowUsers syn keyword sshdconfigKeyword AuthenticationMethods -syn keyword sshdconfigKeyword AuthorizedKeysFile syn keyword sshdconfigKeyword AuthorizedKeysCommand syn keyword sshdconfigKeyword AuthorizedKeysCommandUser +syn keyword sshdconfigKeyword AuthorizedKeysFile syn keyword sshdconfigKeyword AuthorizedPrincipalsCommand syn keyword sshdconfigKeyword AuthorizedPrincipalsCommandUser syn keyword sshdconfigKeyword AuthorizedPrincipalsFile syn keyword sshdconfigKeyword Banner syn keyword sshdconfigKeyword CASignatureAlgorithms syn keyword sshdconfigKeyword ChallengeResponseAuthentication +syn keyword sshdconfigKeyword ChannelTimeout syn keyword sshdconfigKeyword ChrootDirectory syn keyword sshdconfigKeyword Ciphers syn keyword sshdconfigKeyword ClientAliveCountMax @@ -187,22 +184,22 @@ syn keyword sshdconfigKeyword GatewayPorts syn keyword sshdconfigKeyword GSSAPIAuthentication syn keyword sshdconfigKeyword GSSAPICleanupCredentials syn keyword sshdconfigKeyword GSSAPIEnablek5users -syn keyword sshdconfigKeyword GSSAPIKeyExchange syn keyword sshdconfigKeyword GSSAPIKexAlgorithms +syn keyword sshdconfigKeyword GSSAPIKeyExchange syn keyword sshdconfigKeyword GSSAPIStoreCredentialsOnRekey syn keyword sshdconfigKeyword GSSAPIStrictAcceptorCheck -syn keyword sshdconfigKeyword HostCertificate -syn keyword sshdconfigKeyword HostKey -syn keyword sshdconfigKeyword HostKeyAgent -syn keyword sshdconfigKeyword HostKeyAlgorithms syn keyword sshdconfigKeyword HostbasedAcceptedAlgorithms syn keyword sshdconfigKeyword HostbasedAcceptedKeyTypes syn keyword sshdconfigKeyword HostbasedAuthentication syn keyword sshdconfigKeyword HostbasedUsesNameFromPacketOnly -syn keyword sshdconfigKeyword IPQoS +syn keyword sshdconfigKeyword HostCertificate +syn keyword sshdconfigKeyword HostKey +syn keyword sshdconfigKeyword HostKeyAgent +syn keyword sshdconfigKeyword HostKeyAlgorithms syn keyword sshdconfigKeyword IgnoreRhosts syn keyword sshdconfigKeyword IgnoreUserKnownHosts syn keyword sshdconfigKeyword Include +syn keyword sshdconfigKeyword IPQoS syn keyword sshdconfigKeyword KbdInteractiveAuthentication syn keyword sshdconfigKeyword KerberosAuthentication syn keyword sshdconfigKeyword KerberosGetAFSToken @@ -213,9 +210,9 @@ syn keyword sshdconfigKeyword KerberosUseKuserok syn keyword sshdconfigKeyword KexAlgorithms syn keyword sshdconfigKeyword KeyRegenerationInterval syn keyword sshdconfigKeyword ListenAddress +syn keyword sshdconfigKeyword LoginGraceTime syn keyword sshdconfigKeyword LogLevel syn keyword sshdconfigKeyword LogVerbose -syn keyword sshdconfigKeyword LoginGraceTime syn keyword sshdconfigKeyword MACs syn keyword sshdconfigKeyword Match syn keyword sshdconfigKeyword MaxAuthTries @@ -223,8 +220,6 @@ syn keyword sshdconfigKeyword MaxSessions syn keyword sshdconfigKeyword MaxStartups syn keyword sshdconfigKeyword ModuliFile syn keyword sshdconfigKeyword PasswordAuthentication -syn keyword sshdconfigKeyword PerSourceMaxStartups -syn keyword sshdconfigKeyword PerSourceNetBlockSize syn keyword sshdconfigKeyword PermitBlacklistedKeys syn keyword sshdconfigKeyword PermitEmptyPasswords syn keyword sshdconfigKeyword PermitListen @@ -234,6 +229,8 @@ syn keyword sshdconfigKeyword PermitTTY syn keyword sshdconfigKeyword PermitTunnel syn keyword sshdconfigKeyword PermitUserEnvironment syn keyword sshdconfigKeyword PermitUserRC +syn keyword sshdconfigKeyword PerSourceMaxStartups +syn keyword sshdconfigKeyword PerSourceNetBlockSize syn keyword sshdconfigKeyword PidFile syn keyword sshdconfigKeyword Port syn keyword sshdconfigKeyword PrintLastLog @@ -243,23 +240,24 @@ syn keyword sshdconfigKeyword PubkeyAcceptedAlgorithms syn keyword sshdconfigKeyword PubkeyAcceptedKeyTypes syn keyword sshdconfigKeyword PubkeyAuthentication syn keyword sshdconfigKeyword PubkeyAuthOptions -syn keyword sshdconfigKeyword RSAAuthentication +syn keyword sshdconfigKeyword RDomain syn keyword sshdconfigKeyword RekeyLimit syn keyword sshdconfigKeyword RequiredRSASize syn keyword sshdconfigKeyword RevokedKeys -syn keyword sshdconfigKeyword RDomain syn keyword sshdconfigKeyword RhostsRSAAuthentication +syn keyword sshdconfigKeyword RSAAuthentication syn keyword sshdconfigKeyword SecurityKeyProvider syn keyword sshdconfigKeyword ServerKeyBits syn keyword sshdconfigKeyword SetEnv syn keyword sshdconfigKeyword ShowPatchLevel -syn keyword sshdconfigKeyword StrictModes syn keyword sshdconfigKeyword StreamLocalBindMask syn keyword sshdconfigKeyword StreamLocalBindUnlink +syn keyword sshdconfigKeyword StrictModes syn keyword sshdconfigKeyword Subsystem syn keyword sshdconfigKeyword SyslogFacility syn keyword sshdconfigKeyword TCPKeepAlive syn keyword sshdconfigKeyword TrustedUserCAKeys +syn keyword sshdconfigKeyword UnusedConnectionTimeout syn keyword sshdconfigKeyword UseBlacklist syn keyword sshdconfigKeyword UseBlocklist syn keyword sshdconfigKeyword UseDNS @@ -278,14 +276,13 @@ syn keyword sshdconfigKeyword XAuthLocation hi def link sshdconfigComment Comment hi def link sshdconfigTodo Todo hi def link sshdconfigHostPort sshdconfigConstant -hi def link sshdconfigTime sshdconfigConstant -hi def link sshdconfigNumber sshdconfigConstant +hi def link sshdconfigTime Number +hi def link sshdconfigNumber Number hi def link sshdconfigConstant Constant -hi def link sshdconfigYesNo sshdconfigEnum +hi def link sshdconfigYesNo Boolean hi def link sshdconfigAddressFamily sshdconfigEnum hi def link sshdconfigPrivilegeSeparation sshdconfigEnum hi def link sshdconfigTcpForwarding sshdconfigEnum -hi def link sshdconfigRootLogin sshdconfigEnum hi def link sshdconfigCiphers sshdconfigEnum hi def link sshdconfigMAC sshdconfigEnum hi def link sshdconfigHostKeyAlgo sshdconfigEnum diff --git a/runtime/syntax/stylus.vim b/runtime/syntax/stylus.vim new file mode 100644 index 0000000000..fd0f33b65a --- /dev/null +++ b/runtime/syntax/stylus.vim @@ -0,0 +1,61 @@ +" Vim syntax file +" Language: Stylus +" Maintainer: Hsiaoming Yang <lepture@me.com>, Marc Harter +" Filenames: *.styl, *.stylus +" Based On: Tim Pope (sass.vim) +" Created: Dec 14, 2011 +" Modified: Apr 29, 2024 + +if main_syntax == "css" + syn sync minlines=10 +endif + +" let b:current_syntax = "css" +" +if main_syntax == 'css' + unlet main_syntax +endif + +syn case ignore + +syn cluster stylusCssSelectors contains=cssTagName,cssSelector,cssPseudo +syn cluster stylusCssValues contains=cssValueLength,cssValueInteger,cssValueNumber,cssValueAngle,cssValueTime,cssValueFrequency,cssColorVal,cssCommonVal,cssFontVal,cssListVal,cssTextVal,cssVisualVal,cssBorderVal,cssBackgroundVal,cssFuncVal,cssAdvancedVal +syn cluster stylusCssProperties contains=cssProp,cssBackgroundProp,cssTableProp,cssBorderProp,cssFontProp,cssColorProp,cssBoxProp,cssTextProp,cssListProp,cssVisualProp,cssAdvancedProp,cssCommonProp,cssSpecialProp + +syn match stylusVariable "$\?[[:alnum:]_-]\+" +syn match stylusVariableAssignment "\%([[:alnum:]_-]\+\s*\)\@<==" nextgroup=stylusCssAttribute,stylusVariable skipwhite + +syn match stylusProperty "\%([{};]\s*\|^\)\@<=\%([[:alnum:]-]\|#{[^{}]*}\)\+:" contains=@stylusCssProperties,@stylusCssSelectors skipwhite nextgroup=stylusCssAttribute contained containedin=cssDefineBlock +syn match stylusProperty "^\s*\zs\s\%(\%([[:alnum:]-]\|#{[^{}]*}\)\+[ :]\|:[[:alnum:]-]\+\)"hs=s+1 contains=@stylusCssProperties,@stylusCssSelectors skipwhite nextgroup=stylusCssAttribute +syn match stylusProperty "^\s*\zs\s\%(:\=[[:alnum:]-]\+\s*=\)"hs=s+1 contains=@stylusCssProperties,@stylusCssSelectors skipwhite nextgroup=stylusCssAttribute + +syn match stylusCssAttribute +\%("\%([^"]\|\\"\)*"\|'\%([^']\|\\'\)*'\|#{[^{}]*}\|[^{};]\)*+ contained contains=@stylusCssValues,cssImportant,stylusFunction,stylusVariable,stylusControl,stylusUserFunction,stylusInterpolation,cssString,stylusComment,cssComment + +syn match stylusInterpolation %{[[:alnum:]_-]\+}% + +syn match stylusFunction "\<\%(red\|green\|blue\|alpha\|dark\|light\)\>(\@=" contained +syn match stylusFunction "\<\%(hue\|saturation\|lightness\|push\|unshift\|typeof\|unit\|match\)\>(\@=" contained +syn match stylusFunction "\<\%(hsla\|hsl\|rgba\|rgb\|lighten\|darken\)\>(\@=" contained +syn match stylusFunction "\<\%(abs\|ceil\|floor\|round\|min\|max\|even\|odd\|sum\|avg\|sin\|cos\|join\)\>(\@=" contained +syn match stylusFunction "\<\%(desaturate\|saturate\|invert\|unquote\|quote\|s\)\>(\@=" contained +syn match stylusFunction "\<\%(operate\|length\|warn\|error\|last\|p\|\)\>(\@=" contained +syn match stylusFunction "\<\%(opposite-position\|image-size\|add-property\)\>(\@=" contained + +syn keyword stylusVariable null true false arguments +syn keyword stylusControl if else unless for in return + +syn match stylusImport "@\%(import\|require\)" nextgroup=stylusImportList +syn match stylusImportList "[^;]\+" contained contains=cssString.*,cssMediaType,cssURL + +syn match stylusAmpersand "&" +syn match stylusClass "[[:alnum:]_-]\+" contained +syn match stylusClassChar "\.[[:alnum:]_-]\@=" nextgroup=stylusClass +syn match stylusEscape "^\s*\zs\\" +syn match stylusId "[[:alnum:]_-]\+" contained +syn match stylusIdChar "#[[:alnum:]_-]\@=" nextgroup=stylusId + +syn region stylusComment start="//" end="$" contains=cssTodo,@Spell fold + +let b:current_syntax = "stylus" + +" vim:set sw=2: diff --git a/runtime/syntax/swayconfig.vim b/runtime/syntax/swayconfig.vim index 69fe26d370..401412adfd 100644 --- a/runtime/syntax/swayconfig.vim +++ b/runtime/syntax/swayconfig.vim @@ -2,8 +2,8 @@ " Language: sway config file " Original Author: Josef Litos (JosefLitos/i3config.vim) " Maintainer: James Eapen <james.eapen@vai.org> -" Version: 1.0.2 -" Last Change: 2023-12-28 +" Version: 1.2.3 +" Last Change: 2024-05-23 " References: " http://i3wm.org/docs/userguide.html#configuring @@ -16,42 +16,37 @@ if exists("b:current_syntax") finish endif +" before i3 load to give i3ConfigKeyword lower priority +syn cluster i3ConfigCommand contains=i3ConfigCommand,i3ConfigAction,i3ConfigActionKeyword,@i3ConfigValue,i3ConfigColor,i3ConfigKeyword + runtime! syntax/i3config.vim -" i3 extensions -syn keyword i3ConfigActionKeyword opacity urgent shortcuts_inhibitor splitv splith splitt contained -syn keyword i3ConfigOption set plus minus allow deny csd v h t contained +" Sway extensions to i3 +syn keyword i3ConfigActionKeyword opacity urgent shortcuts_inhibitor splitv splith splitt contained contained skipwhite nextgroup=i3ConfigOption +syn keyword i3ConfigOption set plus minus allow deny csd v h t contained contained skipwhite nextgroup=i3ConfigOption,@i3ConfigValue syn keyword i3ConfigConditionProp app_id pid shell contained syn keyword i3ConfigWorkspaceDir prev_on_output next_on_output contained -syn keyword swayConfigBindKeyword bindswitch bindgesture contained -syn match i3ConfigBindArgument /--\(locked\|to-code\|no-repeat\|input-device=[:0-9a-zA-Z_/-]\+\|no-warn\)/ contained -syn region i3ConfigBind start=/^\s*bind\(switch\|gesture\) / skip=/\\$/ end=/$/ contains=swayConfigBindKeyword,swayConfigBindswitch,swayConfigBindswitchArgument,swayConfigBindgesture,swayConfigBindgestureArgument,i3ConfigCriteria,i3ConfigAction,i3ConfigSeparator,i3ConfigActionKeyword,i3ConfigOption,i3ConfigString,i3ConfigNumber,i3ConfigVariable,i3ConfigBoolean keepend - -syn match swayConfigBindBlockHeader /^\s*bind\(sym\|code\) .*{$/ contained contains=i3ConfigBindKeyword,i3ConfigBindArgument,i3ConfigParen -syn match swayConfigBindBlockCombo /^\s\+\(--[a-z-]\+ \)*[$a-zA-Z0-9_+]\+ [a-z[]\@=/ contained contains=i3ConfigBindArgument,i3ConfigBindCombo -syn region i3ConfigBind start=/^\s*bind\(sym\|code\) .*{$/ end=/^\s*}$/ contains=swayConfigBindBlockHeader,swayConfigBindBlockCombo,i3ConfigCriteria,i3ConfigAction,i3ConfigSeparator,i3ConfigActionKeyword,i3ConfigOption,i3ConfigString,i3ConfigNumber,i3ConfigVariable,i3ConfigBoolean,i3ConfigComment,i3ConfigParen fold keepend extend -" fix for extra long bindsym blocks that would be parsed incorrectly when scrolling up -syn region i3ConfigBlockOrphan start=/^\s\+\S/ skip=/^\s\|^$/ end=/^}\?/ contains=swayConfigBindBlockCombo,i3ConfigCriteria,i3ConfigAction,i3ConfigSeparator,i3ConfigActionKeyword,i3ConfigOption,i3ConfigString,i3ConfigNumber,i3ConfigVariable,i3ConfigBoolean,i3ConfigComment,i3ConfigParen keepend extend - -syn keyword i3ConfigClientOpts focused_tab_title contained +syn match i3ConfigBindArgument /--\(locked\|to-code\|no-repeat\|input-device=[^ '"]*\|no-warn\) / contained contains=i3ConfigShOper,@i3ConfigStrVar nextgroup=i3ConfigBindArgument,i3ConfigBindCombo +syn region i3ConfigBindArgument start=/--input-device=['"]/ end=/\s/ contained contains=@i3ConfigIdent,i3ConfigShOper,i3ConfigString nextgroup=i3ConfigBindArgument,i3ConfigBindCombo -syn region swayConfigExecBlock start=/exec\(_always\)\? {/ end=/^}$/ contains=i3ConfigExecKeyword,i3ConfigExecAlwaysKeyword,i3ConfigShCommand,i3ConfigShDelim,i3ConfigShOper,i3ConfigShParam,i3ConfigNumber,i3ConfigString,i3ConfigVariable,i3ConfigComment fold keepend extend +syn region i3ConfigBindCombo matchgroup=i3ConfigParen start=/{$/ end=/^\s*}$/ contained contains=i3ConfigBindArgument,i3ConfigBindCombo,i3ConfigComment fold keepend extend +" hack for blocks with start outside parsing range +syn region swayConfigBlockOrphan start=/^\s\+\(--[a-z-]\+ \)*\([A-Z$][$a-zA-Z0-9_+]\+\|[a-z]\) [a-z[]/ skip=/\\$\|$\n^\s*}$/ end=/$/ contains=i3ConfigBindArgument,i3ConfigBindCombo,i3ConfigParen keepend extend -syn keyword swayConfigFloatingModifierOpts normal inverse contained -syn match i3ConfigKeyword /^floating_modifier [$a-zA-Z0-9+]\+ \(normal\|inverse\)$/ contains=i3ConfigVariable,i3ConfigBindModkey,swayConfigFloatingModifierOpts +syn region i3ConfigExec start=/ {$/ end=/^\s*}$/ contained contains=i3ConfigExecAction,@i3ConfigSh,i3ConfigComment fold keepend extend -syn keyword swayConfigSmartGapsOpts toggle contained -syn match i3ConfigKeyword /^smart_gaps toggle$/ contains=i3ConfigSmartGapOpts,i3ConfigBoolean,swayConfigSmartGapsOpts +syn keyword swayConfigFloatingModifierOpts normal inverse none contained +syn match i3ConfigKeyword /floating_modifier \(none\|[$a-zA-Z0-9+]\+ \(normal\|inverse\)\)$/ contained contains=i3ConfigVariable,i3ConfigBindModkey,swayConfigFloatingModifierOpts -syn keyword swayConfigFocusFollowsMouseOpts always contained -syn match i3ConfigKeyword /^focus_follows_mouse always$/ contains=i3ConfigBoolean,swayConfigFocusFollowsMouseOpts +syn match swayConfigI3Param /--i3/ contains=i3ConfigShParam skipwhite nextgroup=i3ConfigEdgeOpts +syn keyword i3ConfigKeyword hide_edge_borders contained skipwhite nextgroup=swayConfigI3Param,i3ConfigEdgeOpts -syn match i3ConfigKeyword /^hide_edge_borders --i3 \w*$/ contains=i3ConfigEdgeKeyword,i3ConfigShParam - -syn keyword i3ConfigBarOpts swaybar_command gaps height pango_markup status_edge_padding status_padding wrap_scroll tray_bindcode tray_bindsym icon_theme contained +syn keyword i3ConfigBarOpts swaybar_command contained skipwhite nextgroup=@i3ConfigSh +syn region i3ConfigBarOpts matchgroup=i3ConfigBarOpts start=/gaps/ end=/$/ contained contains=@i3ConfigNumVar +syn keyword i3ConfigBarOpts height pango_markup status_edge_padding status_padding wrap_scroll tray_bindcode tray_bindsym icon_theme contained skipwhite nextgroup=i3ConfigBarOptVals,@i3ConfigValue,i3ConfigShOper syn keyword i3ConfigBarOptVals overlay contained syn keyword i3ConfigExecActionKeyword swaymsg contained @@ -59,77 +54,88 @@ syn keyword i3ConfigExecActionKeyword swaymsg contained " Sway-only options " Xwayland syn keyword swayConfigXOpt enable disable force contained -syn match i3ConfigKeyword /^xwayland \w*$/ contains=swayConfigXOpt +syn keyword i3ConfigKeyword xwayland contained skipwhite nextgroup=swayConfigXOpt " Inhibit idle -syn keyword swayConfigInhibitKeyword inhibit_idle contained syn keyword swayConfigInhibitOpts focus fullscreen open none visible contained -syn match i3ConfigAction /inhibit_idle \w*/ contained contains=swayConfigInhibitKeyword,swayConfigInhibitOpts +syn keyword i3ConfigActionKeyword inhibit_idle contained skipwhite nextgroup=swayConfigInhibitOpts " Bindswitch -syn match swayConfigBindswitchArgument /--\(locked\|no-warn\|reload\)/ contained -syn keyword swayConfigBindswitchType lid tablet contained +syn match swayConfigBindswitchArgument /--\(locked\|no-warn\|reload\) / contained nextgroup=swayConfigBindswitchArgument,swayConfigBindswitchType +syn keyword swayConfigBindswitchType lid tablet contained nextgroup=swayConfigBindswitchCombo syn keyword swayConfigBindswitchState toggle contained -syn match swayConfigBindswitch /\(lid\|tablet\):\(on\|off\|toggle\) / contained contains=swayConfigBindswitchType,i3ConfigColonOperator,swayConfigBindswitchState,i3ConfigBoolean -syn region i3ConfigBind start=/^\s*bindswitch\s\+.*{$/ end=/^\s*}$/ contains=swayConfigBindKeyword,swayConfigBindswitch,swayConfigBindswitchArgument,i3ConfigNumber,i3ConfigVariable,i3ConfigAction,i3ConfigActionKeyword,i3ConfigOption,i3ConfigSeparator,i3ConfigString,i3ConfigCriteria,swayConfigOutputCommand,i3ConfigBoolean,i3ConfigComment,i3ConfigParen fold keepend extend +syn match swayConfigBindswitchCombo /:\(on\|off\|toggle\) / contained contains=i3ConfigColonOperator,swayConfigBindswitchState,i3ConfigBoolean nextgroup=i3ConfigBind +syn region swayConfigBindswitchType matchgroup=i3ConfigParen start=/{$/ end=/^\s*}$/ contained contains=swayConfigBindswitchArgument,swayConfigBindswitchType,i3ConfigComment fold keepend extend +syn keyword i3ConfigBindKeyword bindswitch contained skipwhite nextgroup=swayConfigBindswitchArgument,swayConfigBindswitchType +" hack for blocks with start outside parsing range +syn region swayConfigBlockOrphan start=/^\s\+\(lid\|tablet\):/ skip=/\\$\|$\n^\s*}$/ end=/$/ contains=swayConfigBindswitchArgument,swayConfigBindswitchType,i3ConfigParen keepend extend " Bindgesture -syn match swayConfigBindgestureArgument /--\(exact\|input-device=[:0-9a-zA-Z_/-]\+\|no-warn\)/ contained +syn match swayConfigBindgestureArgument /--\(exact\|input-device=[:0-9a-zA-Z_/-]\+\|no-warn\) / contained nextgroup=swayConfigBindgestureArgument,swayConfigBindgestureCombo syn keyword swayConfigBindgestureType hold swipe pinch contained syn keyword swayConfigBindgestureDir up down left right inward outward clockwise counterclockwise contained -syn match swayConfigBindgesture /\(hold\(:[1-5]\)\?\|swipe\(:[3-5]\)\?\(:up\|:down\|:left\|:right\)\?\|pinch\(:[2-5]\)\?:\(+\?\(inward\|outward\|clockwise\|counterclockwise\|up\|down\|left\|right\)\)\+\) / contained contains=i3ConfigNumber,swayConfigBindgestureType,i3ConfigColonOperator,swayConfigBindgestureDir,i3ConfigBindModifier -syn region i3ConfigBind start=/^\s*bindgesture\s\+.*{$/ end=/^\s*}$/ contains=swayConfigBindKeyword,swayConfigBindgesture,swayConfigBindgestureArgument,i3ConfigCriteria,i3ConfigAction,i3ConfigSeparator,i3ConfigActionKeyword,i3ConfigOption,i3ConfigString,i3ConfigNumber,i3ConfigVariable,i3ConfigBoolean,i3ConfigParen fold keepend extend +syn match swayConfigBindgestureCombo /\(hold\(:[1-5]\)\?\|swipe\(:[3-5]\)\?\(:up\|:down\|:left\|:right\)\?\|pinch\(:[2-5]\)\?:\(+\?\(inward\|outward\|clockwise\|counterclockwise\|up\|down\|left\|right\)\)\+\) / contained contains=i3ConfigNumber,swayConfigBindgestureType,i3ConfigColonOperator,swayConfigBindgestureDir,i3ConfigBindModifier nextgroup=swayConfigBindgestureCombo,i3ConfigBind +syn region swayConfigBindgestureCombo matchgroup=i3ConfigParen start=/{$/ end=/^\s*}$/ contained contains=swayConfigBindgestureArgument,swayConfigBindgestureCombo,i3ConfigComment fold keepend extend +syn keyword i3ConfigBindKeyword bindgesture contained skipwhite nextgroup=swayConfigBindgestureArgument,swayConfigBindgestureCombo +" hack for blocks with start outside parsing range +syn region swayConfigBlockOrphan start=/^\s\+\(--[a-z-]\+ \)*\(hold\|swipe\|pinch\):/ skip=/\\$\|$\n^\s*}$/ end=/$/ contains=swayConfigBindgestureArgument,swayConfigBindgestureCombo,i3ConfigParen keepend extend " Tiling drag threshold -syn match i3ConfigKeyword /^tiling_drag_threshold \d\+$/ contains=i3ConfigNumber - " Titlebar commands -syn match i3ConfigKeyword /^titlebar_border_thickness \(\d\+\|\$\S\+\)$/ contains=i3ConfigNumber,i3ConfigVariable -syn match i3ConfigKeyword /^titlebar_padding \(\d\+\|\$\S\+\)\( \d\+\)\?$/ contains=i3ConfigNumber,i3ConfigVariable +syn keyword i3ConfigKeyword tiling_drag_threshold titlebar_border_thickness contained skipwhite nextgroup=@i3ConfigNumVar +syn match i3ConfigKeyword /titlebar_padding \(\d\+\|\$\S\+\)\( \d\+\)\?$/ contained contains=@i3ConfigNumVar -syn match swayConfigDeviceOps /[*,:;]/ contained +syn match swayConfigDeviceOper /[*:;!]/ contained " Input devices -syn keyword swayConfigInputKeyword input contained -syn keyword swayConfigInputType touchpad pointer keyboard touch tablet_tool tablet_pad switch contained -syn match swayConfigInputTypePair /\<type:\w\+\>/ contained contains=i3ConfigColonOperator,swayConfigInputType -syn region swayConfigInputStart start=/^input / end=/\s/ contained contains=swayConfigInputKeyword,swayConfigInputTypePair,i3ConfigString keepend extend -syn keyword swayConfigInputOpts xkb_layout xkb_variant xkb_rules xkb_switch_layout xkb_numlock xkb_file xkb_capslock xkb_model repeat_delay repeat_rate map_to_output map_to_region map_from_region tool_mode accel_profile dwt dwtp drag_lock drag click_method middle_emulation tap events calibration_matrix natural_scroll left_handed pointer_accel scroll_button scroll_factor scroll_method tap_button_map contained -syn keyword swayConfigInputOptVals absolute relative adaptive flat none button_areas clickfinger toggle two_finger edge on_button_down lrm lmr next prev pen eraser brush pencil airbrush disabled_on_external_mouse disable contained -syn match swayConfigXkbOptsPairVal /:[0-9a-z_-]\+/ contained contains=i3ConfigColonOperator -syn match swayConfigXkbOptsPair /[a-z]\+:[0-9a-z_-]\+/ contained contains=swayConfigXkbOptsPairVal -syn match swayConfigInputXkbOpts /xkb_options \([a-z]\+:[0-9a-z_-]\+,\?\)\+/ contained contains=swayConfigXkbOptsPair,swayConfigDeviceOps -syn region i3ConfigAction start=/input/ skip=/\\$/ end=/\([,;]\|$\)/ contained contains=swayConfigInputStart,swayConfigInputXkbOpts,swayConfigInputOpts,swayConfigInputOptVals,i3ConfigVariable,i3ConfigNumber,i3ConfigBoolean,swayConfigDeviceOps keepend transparent -syn region i3ConfigInput start=/^input/ skip=/\\$/ end=/$/ contains=swayConfigInputStart,swayConfigInputXkbOpts,swayConfigInputOpts,swayConfigInputOptVals,i3ConfigVariable,i3ConfigNumber,i3ConfigBoolean,swayConfigDeviceOps keepend -syn region i3ConfigInput start=/^input .* {/ end=/}$/ contains=swayConfigInputStart,swayConfigInputXkbOpts,swayConfigInputOpts,swayConfigInputOptVals,i3ConfigVariable,i3ConfigNumber,i3ConfigBoolean,swayConfigDeviceOps,i3ConfigParen keepend extend +syn keyword swayConfigInputOpts xkb_variant xkb_rules xkb_switch_layout xkb_numlock xkb_file xkb_capslock xkb_model repeat_delay repeat_rate map_to_output map_to_region map_from_region tool_mode accel_profile dwt dwtp drag_lock drag click_method middle_emulation tap events calibration_matrix natural_scroll left_handed pointer_accel scroll_button scroll_factor scroll_method tap_button_map contained skipwhite nextgroup=swayConfigInputOptVals,@i3ConfigValue +syn keyword swayConfigInputOptVals absolute relative adaptive flat none button_areas clickfinger toggle two_finger edge on_button_down lrm lmr next prev pen eraser brush pencil airbrush disabled_on_external_mouse disable enable contained skipwhite nextgroup=swayConfigInputOpts,@i3ConfigValue,swayConfigDeviceOper +syn match swayConfigDeviceOper /,/ contained nextgroup=swayConfigXkbOptsPair,swayConfigXkbLayout +syn match swayConfigXkbLayout /[a-z]\+/ contained nextgroup=swayConfigDeviceOper +syn keyword swayConfigInputOpts xkb_layout contained skipwhite nextgroup=swayConfigXkbLayout +syn match swayConfigXkbOptsPairVal /[0-9a-z_-]\+/ contained contains=i3ConfigNumber skipwhite nextgroup=swayConfigDeviceOper,swayConfigInputOpts +syn match swayConfigXkbOptsPair /[a-z]\+:/ contained contains=i3ConfigColonOperator nextgroup=swayConfigXkbOptsPairVal +syn keyword swayConfigInputOpts xkb_options contained skipwhite nextgroup=swayConfigXkbOptsPair + +syn region swayConfigInput start=/\s/ skip=/\\$/ end=/\ze[,;]\|$/ contained contains=swayConfigInputOpts,@i3ConfigValue keepend +syn region swayConfigInput matchgroup=i3ConfigParen start=/ {$/ end=/^\s*}$/ contained contains=swayConfigInputOpts,@i3ConfigValue,i3ConfigComment keepend extend +syn keyword swayConfigInputType touchpad pointer keyboard touch tablet_tool tablet_pad switch contained nextgroup=swayConfigInput +syn match swayConfigInputIdent /type:!\?/ contained contains=swayConfigDeviceOper nextgroup=swayConfigInputType +syn match swayConfigInputIdent /[^t '"]\S*/ contained contains=i3ConfigOutputIdent nextgroup=swayConfigInput +syn region swayConfigInputIdent start=/['"]/ end=/\ze/ contained contains=i3ConfigOutputIdent nextgroup=swayConfigInput +syn keyword i3ConfigKeyword input contained skipwhite nextgroup=swayConfigInputIdent " Seat -syn keyword swayConfigSeatKeyword seat contained -syn keyword swayConfigSeatOpts attach cursor fallback hide_cursor idle_inhibit idle_wake keyboard_grouping shortcuts_inhibitor pointer_constraint xcursor_theme contained -syn match swayConfigSeatOptVals /when-typing/ contained -syn keyword swayConfigSeatOptVals move set press release none smart activate deactivate toggle escape enable disable contained -syn region i3ConfigAction start=/seat/ skip=/\\$/ end=/\([,;]\|$\)/ contained contains=swayConfigSeatKeyword,i3ConfigString,i3ConfigNumber,i3ConfigBoolean,swayConfigSeatOptVals,swayConfigSeatOpts,swayConfigDeviceOps,swayConfigInputType keepend transparent -syn region swayConfigSeat start=/seat/ skip=/\\$/ end=/$/ contains=swayConfigSeatKeyword,i3ConfigString,i3ConfigNumber,i3ConfigBoolean,swayConfigSeatOptVals,swayConfigSeatOpts,swayConfigDeviceOps,swayConfigInputType keepend -syn region swayConfigSeat start=/seat .* {$/ end=/}$/ contains=swayConfigSeatKeyword,i3ConfigString,i3ConfigNumber,i3ConfigBoolean,swayConfigSeatOptVals,swayConfigSeatOpts,swayConfigDeviceOps,i3ConfigParen,swayConfigInputType keepend extend +syn keyword swayConfigSeatOpts cursor fallback hide_cursor keyboard_grouping shortcuts_inhibitor pointer_constraint xcursor_theme contained skipwhite nextgroup=swayConfigSeatOptVals,@i3ConfigValue +syn match swayConfigInputTypeSeq / \w\+/ contained contains=swayConfigInputType nextgroup=swayConfigInputTypeSeq,swayConfigSeatOpts +syn keyword swayConfigSeatOpts idle_inhibit idle_wake contained nextgroup=swayConfigInputTypeSeq +syn keyword swayConfigSeatOpts attach contained skipwhite nextgroup=swayConfigSeatIdent +syn match swayConfigSeatOptVals /when-typing/ contained skipwhite nextgroup=swayConfigSeatOptVals +syn keyword swayConfigSeatOptVals move set press release none smart activate deactivate toggle escape enable disable contained skipwhite nextgroup=swayConfigSeatOpts +syn region swayConfigSeat start=/\s/ skip=/\\$/ end=/\ze[,;]\|$/ contained contains=swayConfigSeatOpts,@i3ConfigValue keepend +syn region swayConfigSeat matchgroup=i3ConfigParen start=/ {$/ end=/^\s*}$/ contained contains=swayConfigSeatOpts,@i3ConfigValue,i3ConfigComment keepend extend +syn match swayConfigSeatIdent /[^ ]\+/ contained contains=i3ConfigOutputIdent skipwhite nextgroup=swayConfigSeat +syn keyword i3ConfigKeyword seat contained skipwhite nextgroup=swayConfigSeatIdent " Output monitors -syn keyword swayConfigOutputKeyword output contained -syn keyword swayConfigOutputOpts mode resolution res modeline position pos scale scale_filter subpixel background bg transform disable enable power dpms max_render_time adaptive_sync render_bit_depth contained -syn keyword swayConfigOutputOptVals linear nearest smart rgb bgr vrgb vbgr none normal flipped fill stretch fit center tile solid_color clockwise anticlockwise toggle contained -syn match swayConfigOutputOptVals /--custom\|flipped-\(90\|180\|270\)/ contained -syn match swayConfigOutputFPS /@[0-9.]\+Hz/ contained -syn match swayConfigOutputMode / [0-9]\+x[0-9]\+\(@[0-9.]\+Hz\)\?/ contained contains=swayConfigOutputFPS -syn region i3ConfigAction start=/output/ skip=/\\$/ end=/\([,;]\|$\)/ contained contains=swayConfigOutputKeyword,swayConfigOutputMode,swayConfigOutputOpts,swayConfigOutputOptVals,i3ConfigVariable,i3ConfigNumber,i3ConfigString,i3ConfigColor,i3ConfigBoolean,swayConfigDeviceOps keepend transparent -syn region swayConfigOutput start=/^output/ skip=/\\$/ end=/$/ contains=swayConfigOutputKeyword,swayConfigOutputMode,swayConfigOutputOpts,swayConfigOutputOptVals,i3ConfigVariable,i3ConfigNumber,i3ConfigString,i3ConfigColor,i3ConfigBoolean,swayConfigDeviceOps keepend -syn region swayConfigOutput start=/^output .* {$/ end=/}$/ contains=swayConfigOutputKeyword,swayConfigOutputMode,swayConfigOutputOpts,swayConfigOutputOptVals,i3ConfigVariable,i3ConfigNumber,i3ConfigString,i3ConfigColor,i3ConfigBoolean,swayConfigDeviceOps,i3ConfigParen keepend extend +syn keyword swayConfigOutputOpts mode resolution res modeline position pos scale scale_filter subpixel transform disable enable power dpms max_render_time adaptive_sync render_bit_depth contained skipwhite nextgroup=swayConfigOutputOptVals,@i3ConfigValue,swayConfigOutputMode +syn keyword swayConfigOutputOptVals linear nearest smart rgb bgr vrgb vbgr none clockwise anticlockwise toggle contained skipwhite nextgroup=swayConfigOutputOptVals,@i3ConfigValue +syn keyword swayConfigOutputBgVals solid_color fill stretch fit center tile contained skipwhite nextgroup=@i3ConfigColVar +syn match swayConfigOutputBg /[#$]\S\+ solid_color/ contained contains=@i3ConfigColVar,swayConfigOutputBgVals +syn match swayConfigOutputBg /[^b# '"]\S*/ contained contains=i3ConfigShOper skipwhite nextgroup=swayConfigOutputBgVals +syn region swayConfigOutputBg start=/['"]/ end=/\ze/ contained contains=@i3ConfigIdent skipwhite nextgroup=swayConfigOutputBgVals +syn keyword swayConfigOutputOpts bg background contained skipwhite nextgroup=swayConfigOutputBg +syn match swayConfigOutputFPS /@[0-9.]\+Hz/ contained skipwhite nextgroup=swayConfigOutputOpts +syn match swayConfigOutputMode /\(--custom \)\?[0-9]\+x[0-9]\+/ contained contains=i3ConfigShParam skipwhite nextgroup=swayConfigOutputFPS,swayConfigOutputOpts +syn match swayConfigOutputOptVals /\(flipped-\)\?\(90\|180\|270\)\|flipped\|normal/ contained contains=i3ConfigNumber skipwhite nextgroup=swayConfigOutputOptsVals +syn region swayConfigOutput start=/\s/ skip=/\\$/ end=/\ze[,;]\|$/ contained contains=swayConfigOutputOpts,@i3ConfigValue keepend +syn region swayConfigOutput matchgroup=i3ConfigParen start=/ {$/ end=/^\s*}$/ contained contains=swayConfigOutputOpts,@i3ConfigValue,i3ConfigComment keepend extend +syn match swayConfigOutputIdent /[^ ]\+/ contained contains=i3ConfigOutputIdent skipwhite nextgroup=swayConfigOutput +syn keyword i3ConfigKeyword output contained skipwhite nextgroup=swayConfigOutputIdent " Define the highlighting. -hi def link swayConfigSmartGapsOpts i3ConfigOption hi def link swayConfigFloatingModifierOpts i3ConfigOption -hi def link swayConfigFocusFollowsMouseOpts i3ConfigOption -hi def link swayConfigBindKeyword i3ConfigBindKeyword hi def link swayConfigXOpt i3ConfigOption -hi def link swayConfigInhibitKeyword i3ConfigCommand hi def link swayConfigInhibitOpts i3ConfigOption hi def link swayConfigBindswitchArgument i3ConfigBindArgument hi def link swayConfigBindswitchType i3ConfigMoveType @@ -137,20 +143,18 @@ hi def link swayConfigBindswitchState i3ConfigMoveDir hi def link swayConfigBindgestureArgument i3ConfigBindArgument hi def link swayConfigBindgestureType i3ConfigMoveType hi def link swayConfigBindgestureDir i3ConfigMoveDir -hi def link swayConfigDeviceOps i3ConfigOperator -hi def link swayConfigInputKeyword i3ConfigCommand +hi def link swayConfigDeviceOper i3ConfigOperator hi def link swayConfigInputType i3ConfigMoveType -hi def link swayConfigInputTypePair i3ConfigMoveDir +hi def link swayConfigInputIdent i3ConfigMoveDir hi def link swayConfigInputOptVals i3ConfigShParam hi def link swayConfigInputOpts i3ConfigOption -hi def link swayConfigXkbOptsPairVal i3ConfigString +hi def link swayConfigXkbOptsPairVal i3ConfigParamLine hi def link swayConfigXkbOptsPair i3ConfigShParam -hi def link swayConfigInputXkbOpts i3ConfigOption -hi def link swayConfigSeatKeyword i3ConfigCommand +hi def link swayConfigXkbLayout i3ConfigParamLine hi def link swayConfigSeatOptVals swayConfigInputOptVals hi def link swayConfigSeatOpts swayConfigInputOpts -hi def link swayConfigOutputKeyword i3ConfigCommand hi def link swayConfigOutputOptVals swayConfigInputOptVals +hi def link swayConfigOutputBgVals swayConfigInputOptVals hi def link swayConfigOutputOpts swayConfigInputOpts hi def link swayConfigOutputFPS Constant hi def link swayConfigOutputMode i3ConfigNumber diff --git a/runtime/syntax/tutor.vim b/runtime/syntax/tutor.vim index 83ca547fdd..399026790a 100644 --- a/runtime/syntax/tutor.vim +++ b/runtime/syntax/tutor.vim @@ -8,6 +8,8 @@ syn include @TUTORSHELL syntax/sh.vim unlet b:current_syntax syn include @VIMNORMAL syntax/vimnormal.vim +syn iskeyword @,-,_,48-57 + syn match tutorLink /\[.\{-}\](.\{-})/ contains=tutorInlineNormal syn match tutorLinkBands /\[\|\]\|(\|)/ contained containedin=tutorLink,tutorLinkAnchor conceal syn match tutorLinkAnchor /(.\{-})/ contained containedin=tutorLink conceal diff --git a/runtime/syntax/uci.vim b/runtime/syntax/uci.vim new file mode 100644 index 0000000000..fdf5bfd9b3 --- /dev/null +++ b/runtime/syntax/uci.vim @@ -0,0 +1,33 @@ +" Vim syntax file +" Language: OpenWrt Unified Configuration Interface +" Maintainer: Colin Caine <complaints@cmcaine.co.uk> +" Upstream: https://github.com/cmcaine/vim-uci +" Last Change: 2021 Sep 19 +" +" For more information on uci, see https://openwrt.org/docs/guide-user/base-system/uci + +if exists("b:current_syntax") + finish +endif + +" Fancy zero-width non-capturing look-behind to see what the last word was. +" Would be really nice if there was some less obscure or more efficient way to +" do this. +syntax match uciOptionName '\%(\%(option\|list\)\s\+\)\@<=\S*' +syntax match uciConfigName '\%(\%(package\|config\)\s\+\)\@<=\S*' +syntax keyword uciConfigDec package config nextgroup=uciConfigName skipwhite +syntax keyword uciOptionType option list nextgroup=uciOptionName skipwhite + +" Standard matches. +syntax match uciComment "#.*$" +syntax region uciString start=+"+ end=+"+ skip=+\\"+ +syntax region uciString start=+'+ end=+'+ skip=+\\'+ + +highlight default link uciConfigName Identifier +highlight default link uciOptionName Constant +highlight default link uciConfigDec Statement +highlight default link uciOptionType Type +highlight default link uciComment Comment +highlight default link uciString Normal + +let b:current_syntax = "uci" diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 2857146949..4fc640bab1 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -15,6 +15,8 @@ endif let s:keepcpo = &cpo set cpo&vim +let s:vim9script = "\n" .. getline(1, 32)->join("\n") =~# '\n\s*vim9\%[script]\>' + " vimTodo: contains common special-notices for comments {{{2 " Use the vimCommentGroup cluster to add your own. syn keyword vimTodo contained COMBAK FIXME TODO XXX @@ -67,7 +69,7 @@ syn case match " Special Vim Highlighting (not automatic) {{{1 " Set up folding commands for this syntax highlighting file {{{2 -if exists("g:vimsyn_folding") && g:vimsyn_folding =~# '[afhlmpPrt]' +if exists("g:vimsyn_folding") && g:vimsyn_folding =~# '[afhHlmpPrt]' if g:vimsyn_folding =~# 'a' com! -nargs=* VimFolda <args> fold else @@ -83,6 +85,11 @@ if exists("g:vimsyn_folding") && g:vimsyn_folding =~# '[afhlmpPrt]' else com! -nargs=* VimFoldh <args> endif + if g:vimsyn_folding =~# 'H' + com! -nargs=* VimFoldH <args> fold + else + com! -nargs=* VimFoldH <args> + endif if g:vimsyn_folding =~# 'l' com! -nargs=* VimFoldl <args> fold else @@ -117,6 +124,7 @@ else com! -nargs=* VimFolda <args> com! -nargs=* VimFoldf <args> com! -nargs=* VimFoldh <args> + com! -nargs=* VimFoldH <args> com! -nargs=* VimFoldl <args> com! -nargs=* VimFoldm <args> com! -nargs=* VimFoldp <args> @@ -145,16 +153,18 @@ endif " Numbers {{{2 " ======= -syn match vimNumber '\<\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment -syn match vimNumber '-\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment -syn match vimNumber '\<0[xX]\x\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment -syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment -syn match vimNumber '\<0[zZ][a-zA-Z0-9.]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment -syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment -syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,vimComment,vim9Comment +syn case ignore +syn match vimNumber '\<\d\+\%(\.\d\+\%(e[+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment +syn match vimNumber '\<0b[01]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment +syn match vimNumber '\<0o\=\o\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment +syn match vimNumber '\<0x\x\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment +syn match vimNumber '\<0z\>' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment +syn match vimNumber '\<0z\%(\x\x\)\+\%(\.\%(\x\x\)\+\)*' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment +syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment +syn case match " All vimCommands are contained by vimIsCommand. {{{2 -syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimEcho,vimEchoHL,vimExecute,vimIsCommand,vimExtCmd,vimFunction,vimGlobal,vimHighlight,vimLet,vimMap,vimMark,vimNotFunc,vimNorm,vimSet,vimSyntax,vimUnlet,vimUnmap,vimUserCmd +syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimDef,@vimEcho,vimEnddef,vimEndfunction,vimExecute,vimIsCommand,vimExtCmd,vimFor,vimFunction,vimGlobal,vimHighlight,vimLet,vimMap,vimMark,vimNotFunc,vimNorm,vimSet,vimSyntax,vimUnlet,vimUnmap,vimUserCmd,vimMenu,vimMenutranslate syn match vimCmdSep "[:|]\+" skipwhite nextgroup=@vimCmdList,vimSubst1 syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" contains=vimCommand syn match vimVar contained "\<\h[a-zA-Z0-9#_]*\>" @@ -171,9 +181,9 @@ syn cluster vimExprList contains=vimEnvvar,vimFunc,vimFuncVar,vimNumber,vimOper, " (buftype != nofile test avoids having append, change, insert show up in the command window) " ======================= if &buftype != 'nofile' - syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=a\%[ppend]$" matchgroup=vimCommand end="^\.$"" - syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=c\%[hange]$" matchgroup=vimCommand end="^\.$"" - syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=i\%[nsert]$" matchgroup=vimCommand end="^\.$"" + syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=a\%[ppend]$" matchgroup=vimCommand end="^\.$" extend + syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=c\%[hange]$" matchgroup=vimCommand end="^\.$" extend + syn region vimInsert matchgroup=vimCommand start="^[: \t]*\(\d\+\(,\d\+\)\=\)\=i\%[nsert]$" matchgroup=vimCommand end="^\.$" extend endif " Behave! {{{2 @@ -196,7 +206,7 @@ syn keyword vimFTOption contained detect indent off on plugin " Augroup : vimAugroupError removed because long augroups caused sync'ing problems. {{{2 " ======= : Trade-off: Increasing synclines with slower editing vs augroup END error checking. -syn cluster vimAugroupList contains=@vimCmdList,vimFilter,vimFunc,vimLineComment,vimSpecFile,vimOper,vimNumber,vimOperParen,vimComment,vim9Comment,vimString,vimSubst,vimRegister,vimCmplxRepeat,vimRegion,vimNotation,vimCtrlChar,vimFuncVar,vimContinue +syn cluster vimAugroupList contains=@vimCmdList,vimFilter,vimFunc,vimLineComment,vimSpecFile,vimOper,vimNumber,vimOperParen,@vimComment,vimString,vimSubst,vimRegister,vimCmplxRepeat,vimNotation,vimCtrlChar,vimFuncVar,vimContinue syn match vimAugroup "\<aug\%[roup]\>" contains=vimAugroupKey,vimAugroupBang skipwhite nextgroup=vimAugroupBang,vimAutoCmdGroup if exists("g:vimsyn_folding") && g:vimsyn_folding =~# 'a' syn region vimAugroup fold start="\<aug\%[roup]\>\ze\s\+\%([eE][nN][dD]\)\@!\S\+" matchgroup=vimAugroupKey end="\<aug\%[roup]\>\ze\s\+[eE][nN][dD]\>" contains=vimAutoCmd,@vimAugroupList,vimAugroupkey skipwhite nextgroup=vimAugroupEnd @@ -214,48 +224,95 @@ syn keyword vimAugroupKey contained aug[roup] skipwhite nextgroup=vimAugroupBan " Operators: {{{2 " ========= -syn cluster vimOperGroup contains=vimEnvvar,vimFunc,vimFuncVar,vimOper,vimOperParen,vimNumber,vimString,vimType,vimRegister,@vimContinue,vim9Comment,vimVar +syn cluster vimOperGroup contains=vimEnvvar,vimFunc,vimFuncVar,vimOper,vimOperParen,vimNumber,vimString,vimRegister,@vimContinue,vim9Comment,vimVar syn match vimOper "||\|&&\|[-+*/%.!]" skipwhite nextgroup=vimString,vimSpecFile syn match vimOper "\%#=1\(==\|!=\|>=\|<=\|=\~\|!\~\|>\|<\|=\|!\~#\)[?#]\{0,2}" skipwhite nextgroup=vimString,vimSpecFile syn match vimOper "\(\<is\|\<isnot\)[?#]\{0,2}\>" skipwhite nextgroup=vimString,vimSpecFile -syn region vimOperParen matchgroup=vimParenSep start="(" end=")" contains=vimoperStar,@vimOperGroup +syn region vimOperParen matchgroup=vimParenSep start="(" end=")" contains=@vimOperGroup syn region vimOperParen matchgroup=vimSep start="#\={" end="}" contains=@vimOperGroup nextgroup=vimVar,vimFuncVar if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_noopererror") syn match vimOperError ")" endif -" Functions : Tag is provided for those who wish to highlight tagged functions {{{2 +" Functions: Tag is provided for those who wish to highlight tagged functions {{{2 " ========= -syn cluster vimFuncList contains=vimCommand,vimFunctionError,vimFuncKey,Tag,vimFuncSID -syn cluster vimFuncBodyList contains=@vimCmdList,vimCmplxRepeat,vimComment,vim9Comment,vimContinue,vimCtrlChar,vimEnvvar,vimFBVar,vimFunc,vimFunction,vimFuncVar,vimLetHereDoc,vimLineComment,vimNotation,vimNotFunc,vimNumber,vimOper,vimOperParen,vimRegion,vimRegister,vimSearch,vimSpecFile,vimString,vimSubst -syn match vimFunction "\<\(fu\%[nction]\)!\=\s\+\%(<[sS][iI][dD]>\|[sSgGbBwWtTlL]:\)\=\%(\i\|[#.]\|{.\{-1,}}\)*\ze\s*(" contains=@vimFuncList nextgroup=vimFuncBody -syn match vimFunction "\<def!\=\ze\s*(" contains=@vimFuncList nextgroup=vimFuncBody +syn cluster vimFuncList contains=vimFuncBang,vimFunctionError,vimFuncKey,vimFuncSID,Tag +syn cluster vimDefList contains=vimFuncBang,vimFunctionError,vimDefKey,vimFuncSID,Tag + +syn cluster vimFuncBodyCommon contains=@vimCmdList,vimCmplxRepeat,vimContinue,vimCtrlChar,vimDef,vimEnvvar,vimFBVar,vimFunc,vimFunction,vimLetHereDoc,vimNotation,vimNotFunc,vimNumber,vimOper,vimOperParen,vimRegister,vimSearch,vimSpecFile,vimString,vimSubst,vimFuncFold +syn cluster vimFuncBodyList contains=@vimFuncBodyCommon,vimComment,vimLineComment,vimFuncVar,vimInsert +syn cluster vimDefBodyList contains=@vimFuncBodyCommon,vim9Comment,vim9LineComment + +syn region vimFuncPattern contained matchgroup=vimOper start="/" end="$" contains=@vimSubstList +syn match vimFunction "\<fu\%[nction]\>" skipwhite nextgroup=vimCmdSep,vimComment,vimFuncPattern contains=vimFuncKey +syn match vimDef "\<def\>" skipwhite nextgroup=vimCmdSep,vimComment,vimFuncPattern contains=vimDefKey + +syn match vimFunction "\<fu\%[nction]\>!\=\s*\%(<[sS][iI][dD]>\|[sg]:\)\=\%(\i\|[#.]\|{.\{-1,}}\)\+" contains=@vimFuncList skipwhite nextgroup=vimFuncParams +syn match vimDef "\<def\s\+new\%(\i\|{.\{-1,}}\)\+" contains=@vimDefList nextgroup=vimDefParams +syn match vimDef "\<def\>!\=\s*\%(<[sS][iI][dD]>\|[sg]:\)\=\%(\i\|[#.]\|{.\{-1,}}\)\+" contains=@vimDefList nextgroup=vimDefParams + +syn match vimFuncComment contained +".*+ skipwhite skipnl nextgroup=vimFuncBody,vimEndfunction +syn match vimDefComment contained "#.*" skipwhite skipnl nextgroup=vimDefBody,vimEnddef + +syn match vimFuncBang contained "!" +syn match vimFuncSID contained "\c<sid>" +syn match vimFuncSID contained "\<[sg]:" +syn keyword vimFuncKey contained fu[nction] +syn keyword vimDefKey contained def + +syn region vimFuncParams contained matchgroup=Delimiter start="(" skip=+\n\s*\\\|\n\s*"\\ + end=")" skipwhite skipnl nextgroup=vimFuncBody,vimFuncComment,vimEndfunction,vimFuncMod contains=vimFuncParam,@vimContinue +syn region vimDefParams contained matchgroup=Delimiter start="(" end=")" skipwhite skipnl nextgroup=vimDefBody,vimDefComment,vimEnddef,vimReturnType contains=vimDefParam,vim9Comment +syn match vimFuncParam contained "\<\h\w*\>\|\.\.\." skipwhite nextgroup=vimFuncParamEquals +syn match vimDefParam contained "\<\h\w*\>" skipwhite nextgroup=vimParamType,vimFuncParamEquals + +syn match vimFuncParamEquals contained "=" skipwhite nextgroup=@vimExprList +syn match vimFuncMod contained "\<\%(abort\|closure\|dict\|range\)\>" skipwhite skipnl nextgroup=vimFuncBody,vimFuncComment,vimEndfunction,vimFuncMod + +syn region vimFuncBody contained start="^" matchgroup=vimCommand end="\<endfu\%[nction]\>" contains=@vimFuncBodyList +syn region vimDefBody contained start="^" matchgroup=vimCommand end="\<enddef\>" contains=@vimDefBodyList + +syn match vimEndfunction "\<endf\%[unction]\>" +syn match vimEnddef "\<enddef\>" if exists("g:vimsyn_folding") && g:vimsyn_folding =~# 'f' - syn region vimFuncBody contained fold start="\ze\s*(" matchgroup=vimCommand end="\<\(endf\>\|endfu\%[nction]\>\|enddef\>\)" contains=@vimFuncBodyList -else - syn region vimFuncBody contained start="\ze\s*(" matchgroup=vimCommand end="\<\(endf\>\|endfu\%[nction]\>\|enddef\>\)" contains=@vimFuncBodyList + syn region vimFuncFold start="^\s*:\=\s*fu\%[nction]\>!\=\s*\%(<[sS][iI][dD]>\|[sg]:\)\=\%(\i\|[#.]\|{.\{-1,}}\)\+\s*(" end="^\s*:\=\s*endf\%[unction]\>" contains=vimFunction fold keepend extend transparent + syn region vimFuncFold start="^\s*:\=\s*def\>!\=\s*\%(<[sS][iI][dD]>\|[sg]:\)\=\%(\i\|[#.]\)\+(" end="^\s*:\=\s*enddef\>" contains=vimDef fold keepend extend transparent + syn region vimFuncFold start="^\s*:\=\s*def\s\+new\i\+(" end="^\s*:\=\s*enddef\>" contains=vimDef fold keepend extend transparent endif -syn match vimFuncVar contained "a:\(\K\k*\|\d\+\)" -syn match vimFuncSID contained "\c<sid>\|\<s:" -syn keyword vimFuncKey contained fu[nction] -syn match vimFuncBlank contained "\s\+" -syn keyword vimPattern contained start skip end +syn match vimFuncVar contained "a:\%(\K\k*\|\d\+\)\>" +syn match vimFuncBlank contained "\s\+" +" Types: {{{2 +" ===== " vimTypes : new for vim9 -syn match vimType ":\s*\zs\<\(bool\|number\|float\|string\|blob\|list<\|dict<\|job\|channel\|func\)\>" +syn region vimReturnType contained start=":\s" end="$" matchgroup=vim9Comment end="\ze#" skipwhite skipnl nextgroup=vimDefBody,vimDefComment,vimEnddef contains=vimTypeSep transparent +syn match vimParamType contained ":\s\+\a" skipwhite skipnl nextgroup=vimFuncParamEquals contains=vimTypeSep,@vimType + +syn match vimTypeSep contained ":\s\@=" skipwhite nextgroup=@vimType +syn keyword vimType contained any blob bool channel float job number string void +syn match vimType contained "\<func\>" +syn region vimCompoundType contained matchgroup=vimType start="\<func(" end=")" nextgroup=vimTypeSep contains=@vimType oneline transparent +syn region vimCompoundType contained matchgroup=vimType start="\<\%(list\|dict\)<" end=">" contains=@vimType oneline transparent +syn match vimUserType contained "\<\u\w*\>" -" Keymaps: (Vim Project Addition) {{{2 +syn cluster vimType contains=vimType,vimCompoundType,vimUserType + +" Keymaps: {{{2 " ======= -" TODO: autogenerated vimCommand keyword list does not handle all abbreviations -" : handle Vim9 script comments when something like #13104 is merged -syn match vimKeymapStart "^" contained skipwhite nextgroup=vimKeymapLhs,vimKeymapLineComment +syn match vimKeymapStart "^" contained skipwhite nextgroup=vimKeymapLhs,@vimKeymapLineComment syn match vimKeymapLhs "\S\+" contained skipwhite nextgroup=vimKeymapRhs contains=vimNotation syn match vimKeymapRhs "\S\+" contained skipwhite nextgroup=vimKeymapTailComment contains=vimNotation syn match vimKeymapTailComment "\S.*" contained -syn match vimKeymapLineComment +".*+ contained contains=@vimCommentGroup,vimCommentString,vimCommentTitle + +" TODO: remove when :" comment is matched in parts as "ex-colon comment" --djk +if s:vim9script + syn match vim9KeymapLineComment "#.*" contained contains=@vimCommentGroup,vimCommentString,vim9CommentTitle +else + syn match vimKeymapLineComment +".*+ contained contains=@vimCommentGroup,vimCommentString,vimCommentTitle +endif +syn cluster vimKeymapLineComment contains=vim9\=KeymapLineComment syn region vimKeymap matchgroup=vimCommand start="\<loadk\%[eymap]\>" end="\%$" contains=vimKeymapStart @@ -271,9 +328,10 @@ syn match vimSpecFileMod "\(:[phtre]\)\+" contained " User-Specified Commands: {{{2 " ======================= -syn cluster vimUserCmdList contains=@vimCmdList,vimCmplxRepeat,vimComment,vim9Comment,vimCtrlChar,vimEscapeBrace,vimFunc,vimNotation,vimNumber,vimOper,vimRegion,vimRegister,vimSpecFile,vimString,vimSubst,vimSubstRep,vimSubstRange +syn cluster vimUserCmdList contains=@vimCmdList,vimCmplxRepeat,@vimComment,vimCtrlChar,vimEscapeBrace,vimFunc,vimNotation,vimNumber,vimOper,vimRegister,vimSpecFile,vimString,vimSubst,vimSubstRep,vimSubstRange syn keyword vimUserCommand contained com[mand] -syn match vimUserCmd "\<com\%[mand]!\=\>.*$" contains=vimUserAttrb,vimUserAttrbError,vimUserCommand,@vimUserCmdList,vimComFilter +syn match vimUserCmdName contained "\<\u\w*\>" nextgroup=vimUserCmdBlock skipwhite +syn match vimUserCmd "\<com\%[mand]!\=\>.*$" contains=vimUserAttrb,vimUserAttrbError,vimUserCommand,@vimUserCmdList,vimComFilter,vimCmdBlock,vimUserCmdName syn match vimUserAttrbError contained "-\a\+\ze\s" syn match vimUserAttrb contained "-nargs=[01*?+]" contains=vimUserAttrbKey,vimOper syn match vimUserAttrb contained "-complete=" contains=vimUserAttrbKey,vimOper nextgroup=vimUserAttrbCmplt,vimUserCmdError @@ -296,20 +354,41 @@ syn match vimUserAttrbCmpltFunc contained ",\%([sS]:\|<[sS][iI][dD]>\)\=\%(\h\w* syn case match syn match vimUserAttrbCmplt contained "custom,\u\w*" +syn region vimUserCmdBlock contained matchgroup=vimSep start="{" end="}" contains=@vimDefBodyList + " Lower Priority Comments: after some vim commands... {{{2 " ======================= -syn match vimComment excludenl +\s"[^\-:.%#=*].*$+lc=1 contains=@vimCommentGroup,vimCommentString -syn match vimComment +\<endif\s\+".*$+lc=5 contains=@vimCommentGroup,vimCommentString -syn match vimComment +\<else\s\+".*$+lc=4 contains=@vimCommentGroup,vimCommentString -syn region vimCommentString contained oneline start='\S\s\+"'ms=e end='"' -" Vim9 comments - TODO: might be highlighted while they don't work -syn match vim9Comment excludenl +\s#[^{].*$+lc=1 contains=@vimCommentGroup,vimCommentString -syn match vim9Comment +\<endif\s\+#[^{].*$+lc=5 contains=@vimCommentGroup,vimCommentString -syn match vim9Comment +\<else\s\+#[^{].*$+lc=4 contains=@vimCommentGroup,vimCommentString -" Vim9 comment inside expression -syn match vim9Comment +\s\zs#[^{].*$+ms=s+1 contains=@vimCommentGroup,vimCommentString -syn match vim9Comment +^\s*#[^{].*$+ contains=@vimCommentGroup,vimCommentString -syn match vim9Comment +^\s*#$+ contains=@vimCommentGroup,vimCommentString +syn region vimCommentString contained oneline start='\S\s\+"'ms=e end='"' + +if s:vim9script + syn match vimComment excludenl +\s"[^\-:.%#=*].*$+lc=1 contains=@vimCommentGroup,vimCommentString contained + syn match vimComment +\<endif\s\+".*$+lc=5 contains=@vimCommentGroup,vimCommentString contained + syn match vimComment +\<else\s\+".*$+lc=4 contains=@vimCommentGroup,vimCommentString contained + " Vim9 comments - TODO: might be highlighted while they don't work + syn match vim9Comment excludenl +\s#[^{].*$+lc=1 contains=@vimCommentGroup,vimCommentString + syn match vim9Comment +\<endif\s\+#[^{].*$+lc=5 contains=@vimCommentGroup,vimCommentString + syn match vim9Comment +\<else\s\+#[^{].*$+lc=4 contains=@vimCommentGroup,vimCommentString + " Vim9 comment inside expression + " syn match vim9Comment +\s\zs#[^{].*$+ms=s+1 contains=@vimCommentGroup,vimCommentString + " syn match vim9Comment +^\s*#[^{].*$+ contains=@vimCommentGroup,vimCommentString + " syn match vim9Comment +^\s*#$+ contains=@vimCommentGroup,vimCommentString + + syn cluster vimComment contains=vim9Comment +else + syn match vimComment excludenl +\s"[^\-:.%#=*].*$+lc=1 contains=@vimCommentGroup,vimCommentString + syn match vimComment +\<endif\s\+".*$+lc=5 contains=@vimCommentGroup,vimCommentString + syn match vimComment +\<else\s\+".*$+lc=4 contains=@vimCommentGroup,vimCommentString + " Vim9 comments - TODO: might be highlighted while they don't work + syn match vim9Comment excludenl +\s#[^{].*$+lc=1 contains=@vimCommentGroup,vimCommentString contained + syn match vim9Comment +\<endif\s\+#[^{].*$+lc=5 contains=@vimCommentGroup,vimCommentString contained + syn match vim9Comment +\<else\s\+#[^{].*$+lc=4 contains=@vimCommentGroup,vimCommentString contained + " Vim9 comment inside expression + syn match vim9Comment +\s\zs#[^{].*$+ms=s+1 contains=@vimCommentGroup,vimCommentString contained + syn match vim9Comment +^\s*#[^{].*$+ contains=@vimCommentGroup,vimCommentString contained + syn match vim9Comment +^\s*#$+ contains=@vimCommentGroup,vimCommentString contained + + syn cluster vimComment contains=vimComment +endif " Environment Variables: {{{2 " ===================== @@ -399,7 +478,7 @@ syn match vimCmplxRepeat '[^a-zA-Z_/\\()]q[0-9a-zA-Z"]\>'lc=1 syn match vimCmplxRepeat '@[0-9a-z".=@:]\ze\($\|[^a-zA-Z]\>\)' " Set command and associated set-options (vimOptions) with comment {{{2 -syn region vimSet matchgroup=vimCommand start="\<\%(setl\%[ocal]\|setg\%[lobal]\|se\%[t]\)\>" skip="\%(\\\\\)*\\.\n\@!" end="$" end="|" matchgroup=vimNotation end="<[cC][rR]>" keepend contains=vimSetEqual,vimOption,vimErrSetting,vimComment,vim9Comment,vimSetString,vimSetMod +syn region vimSet matchgroup=vimCommand start="\<\%(setl\%[ocal]\|setg\%[lobal]\|se\%[t]\)\>" skip="\%(\\\\\)*\\.\n\@!" end="$" end="|" matchgroup=vimNotation end="<[cC][rR]>" keepend contains=vimSetEqual,vimOption,vimErrSetting,@vimComment,vimSetString,vimSetMod syn region vimSetEqual contained start="[=:]\|[-+^]=" skip="\\\\\|\\\s" end="[| \t]"me=e-1 end="$" contains=vimCtrlChar,vimSetSep,vimNotation,vimEnvvar syn region vimSetString contained start=+="+hs=s+1 skip=+\\\\\|\\"+ end=+"+ contains=vimCtrlChar syn match vimSetSep contained "[,:]" @@ -415,36 +494,52 @@ syn keyword vimUnlet unl[et] skipwhite nextgroup=vimUnletBang,vimUnletVars syn match vimUnletBang contained "!" skipwhite nextgroup=vimUnletVars syn region vimUnletVars contained start="$\I\|\h" skip="\n\s*\\" end="$" end="|" contains=vimVar,vimEnvvar,vimContinue,vimString,vimNumber -VimFoldh syn region vimLetHereDoc matchgroup=vimLetHereDocStart start='=<<\s*\%(trim\s\+\%(eval\s\+\)\=\|eval\s\+\%(trim\s\+\)\=\)\=\z(\L\S*\)' matchgroup=vimLetHereDocStop end='^\s*\z1\s*$' +VimFoldh syn region vimLetHereDoc matchgroup=vimLetHereDocStart start='=<<\s*\%(trim\s\+\%(eval\s\+\)\=\|eval\s\+\%(trim\s\+\)\=\)\=\z(\L\S*\)' matchgroup=vimLetHereDocStop end='^\s*\z1\s*$' extend +syn keyword vimLet var skipwhite nextgroup=vimVar,vimFuncVar,vimLetHereDoc " For: {{{2 " === syn keyword vimFor for skipwhite nextgroup=vimVar,vimVarList + " Abbreviations: {{{2 " ============= " GEN_SYN_VIM: vimCommand abbrev, START_STR='syn keyword vimAbb', END_STR='skipwhite nextgroup=vimMapMod,vimMapLhs' syn keyword vimAbb ab[breviate] ca[bbrev] cnorea[bbrev] cuna[bbrev] ia[bbrev] inorea[bbrev] iuna[bbrev] norea[bbrev] una[bbreviate] skipwhite nextgroup=vimMapMod,vimMapLhs +" GEN_SYN_VIM: vimCommand abclear, START_STR='syn keyword vimAbb', END_STR='skipwhite nextgroup=vimMapMod' +syn keyword vimAbb abc[lear] cabc[lear] iabc[lear] skipwhite nextgroup=vimMapMod " Autocmd: {{{2 " ======= syn match vimAutoEventList contained "\(!\s\+\)\=\(\a\+,\)*\a\+" contains=vimAutoEvent,nvimAutoEvent nextgroup=vimAutoCmdSpace syn match vimAutoCmdSpace contained "\s\+" nextgroup=vimAutoCmdSfxList -syn match vimAutoCmdSfxList contained "\S*" skipwhite nextgroup=vimAutoCmdMod +syn match vimAutoCmdSfxList contained "\S*" skipwhite nextgroup=vimAutoCmdMod,vimAutoCmdBlock syn keyword vimAutoCmd au[tocmd] do[autocmd] doautoa[ll] skipwhite nextgroup=vimAutoEventList -syn match vimAutoCmdMod "\(++\)\=\(once\|nested\)" +syn match vimAutoCmdMod "\(++\)\=\(once\|nested\)" skipwhite nextgroup=vimAutoCmdBlock +syn region vimAutoCmdBlock contained matchgroup=vimSep start="{" end="}" contains=@vimDefBodyList " Echo And Execute: -- prefer strings! {{{2 " ================ -" GEN_SYN_VIM: vimCommand echo, START_STR='syn keyword vimEcho', END_STR='skipwhite nextgroup=vimEchoExpr' -syn keyword vimEcho ec[ho] echoe[rr] echom[sg] echoc[onsole] echon echow[indow] skipwhite nextgroup=vimEchoExpr -syn region vimEchoExpr contained start="[^[:space:]|]" skip=+\\\\\|\\|\|\n\s*\\\|\n\s*"\\ + matchgroup=vimCmdSep end="|" end="$" contains=@vimContinue,@vimExprList - -syn match vimEchoHL "\<echohl\=\>" skipwhite nextgroup=vimGroup,vimHLGroup,vimEchoHLNone,vimOnlyHLGroup,nvimHLGroup +" NOTE: No trailing comments + +syn region vimEcho + \ matchgroup=vimCommand + \ start="\<ec\%[ho]\>" + \ start="\<echoe\%[rr]\>" + \ start="\<echom\%[sg]\>" + \ start="\<echoc\%[onsole]\>" + \ start="\<echon\>" + \ start="\<echow\%[indow]\>" + \ skip=+\\|\|\n\s*\\\|\n\s*"\\ + + \ matchgroup=vimCmdSep end="|" excludenl end="$" contains=@vimContinue,@vimExprList transparent + +syn match vimEchohl "\<echohl\=\>" skipwhite nextgroup=vimGroup,vimHLGroup,vimEchohlNone,vimOnlyHLGroup,nvimHLGroup syn case ignore -syn keyword vimEchoHLNone none +syn keyword vimEchohlNone contained none syn case match -syn region vimExecute oneline excludenl matchgroup=vimCommand start="\<exe\%[cute]\>" skip="\(\\\\\)*\\|" end="$\||\|<[cC][rR]>" contains=vimFuncVar,vimIsCommand,vimOper,vimNotation,vimOperParen,vimString,vimVar +syn cluster vimEcho contains=vimEcho,vimEchohl + +syn region vimExecute matchgroup=vimCommand start="\<exe\%[cute]\>" skip=+\\|\|\n\s*\\\|\n\s*"\\ + matchgroup=vimCmdSep end="|" excludenl end="$" contains=@vimContinue,@vimExprList transparent " Maps: {{{2 " ==== @@ -457,27 +552,41 @@ syn keyword vimMap cmapc[lear] imapc[lear] lmapc[lear] nmapc[lear] omapc[lear] s syn keyword vimMap mapc[lear] skipwhite nextgroup=vimMapBang,vimMapMod " GEN_SYN_VIM: vimCommand unmap, START_STR='syn keyword vimUnmap', END_STR='skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs' syn keyword vimUnmap cu[nmap] iu[nmap] lu[nmap] nun[map] ou[nmap] sunm[ap] tunma[p] unm[ap] vu[nmap] xu[nmap] skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs -syn match vimMapLhs contained "\S\+" contains=vimNotation,vimCtrlChar skipwhite nextgroup=vimMapRhs -syn match vimMapBang contained "\a\@1<=!" skipwhite nextgroup=vimMapMod,vimMapLhs +syn match vimMapLhs contained "\%(.\|\S\)\+" contains=vimCtrlChar,vimNotation skipwhite nextgroup=vimMapRhs +syn match vimMapLhs contained "\%(.\|\S\)\+\ze\s*$" contains=vimCtrlChar,vimNotation skipwhite skipnl nextgroup=vimMapRhsContinue +syn match vimMapBang contained "\a\@1<=!" skipwhite nextgroup=vimMapMod,vimMapLhs syn match vimMapMod contained "\%#=1\c<\(buffer\|expr\|\(local\)\=leader\|nowait\|plug\|script\|sid\|unique\|silent\)\+>" contains=vimMapModKey,vimMapModErr skipwhite nextgroup=vimMapMod,vimMapLhs -syn match vimMapRhs contained ".*" contains=vimNotation,vimCtrlChar skipnl nextgroup=vimMapRhsExtend -syn match vimMapRhsExtend contained "^\s*\\.*$" contains=vimContinue +syn region vimMapRhs contained start="\S" skip=+\\|\|\@1<=|\|\n\s*\\\|\n\s*"\\ + end="|" end="$" contains=@vimContinue,vimCtrlChar,vimNotation skipnl nextgroup=vimMapRhsContinue +" assume a continuation comment introduces the RHS +syn region vimMapRhsContinue contained start=+^\s*\%(\\\|"\\ \)+ skip=+\\|\|\@1<=|\|\n\s*\\\|\n\s*"\\ + end="|" end="$" contains=@vimContinue,vimCtrlChar,vimNotation syn case ignore syn keyword vimMapModKey contained buffer expr leader localleader nowait plug script sid silent unique syn case match " Menus: {{{2 " ===== -syn cluster vimMenuList contains=vimMenuBang,vimMenuPriority,vimMenuName,vimMenuMod -" GEN_SYN_VIM: vimCommand menu, START_STR='syn keyword vimCommand', END_STR='skipwhite nextgroup=@vimMenuList' -syn keyword vimCommand am[enu] an[oremenu] aun[menu] cme[nu] cnoreme[nu] cunme[nu] ime[nu] inoreme[nu] iunme[nu] me[nu] nme[nu] nnoreme[nu] noreme[nu] nunme[nu] ome[nu] onoreme[nu] ounme[nu] sme[nu] snoreme[nu] sunme[nu] tlm[enu] tln[oremenu] tlu[nmenu] unme[nu] vme[nu] vnoreme[nu] vunme[nu] xme[nu] xnoreme[nu] xunme[nu] skipwhite nextgroup=@vimMenuList -syn match vimMenuName "[^ \t\\<]\+" contained nextgroup=vimMenuNameMore,vimMenuMap -syn match vimMenuPriority "\d\+\(\.\d\+\)*" contained skipwhite nextgroup=vimMenuName -syn match vimMenuNameMore "\c\\\s\|<tab>\|\\\." contained nextgroup=vimMenuName,vimMenuNameMore contains=vimNotation -syn match vimMenuMod contained "\c<\(script\|silent\)\+>" skipwhite contains=vimMapModKey,vimMapModErr nextgroup=@vimMenuList -syn match vimMenuMap "\s" contained skipwhite nextgroup=vimMenuRhs -syn match vimMenuRhs ".*$" contained contains=vimString,vimComment,vim9Comment,vimIsCommand -syn match vimMenuBang "!" contained skipwhite nextgroup=@vimMenuList +" NOTE: tail comments disallowed +" GEN_SYN_VIM: vimCommand menu, START_STR='syn keyword vimMenu', END_STR='skipwhite nextgroup=vimMenuBang,vimMenuMod,vimMenuName,vimMenuPriority,vimMenuStatus' +syn keyword vimMenu am[enu] an[oremenu] aun[menu] cme[nu] cnoreme[nu] cunme[nu] ime[nu] inoreme[nu] iunme[nu] me[nu] nme[nu] nnoreme[nu] noreme[nu] nunme[nu] ome[nu] onoreme[nu] ounme[nu] sme[nu] snoreme[nu] sunme[nu] tlm[enu] tln[oremenu] tlu[nmenu] tm[enu] tu[nmenu] unme[nu] vme[nu] vnoreme[nu] vunme[nu] xme[nu] xnoreme[nu] xunme[nu] skipwhite nextgroup=vimMenuBang,vimMenuMod,vimMenuName,vimMenuPriority,vimMenuStatus +syn keyword vimMenu popu[p] skipwhite nextgroup=vimMenuBang,vimMenuName +syn region vimMenuRhs contained contains=@vimContinue,vimNotation start="|\@!\S" skip=+\\\\\|\\|\|\n\s*\\\|\n\s*"\\ + end="$" matchgroup=vimSep end="|" +syn region vimMenuRhsContinue contained contains=@vimContinue,vimNotation start=+^\s*\%(\\\|"\\ \)+ skip=+\\\\\|\\|\|\n\s*\\\|\n\s*"\\ + end="$" matchgroup=vimSep end="|" +syn match vimMenuName "\.\@!\%(\\\s\|\S\)\+" contained contains=vimMenuNotation,vimNotation skipwhite nextgroup=vimCmdSep,vimMenuRhs +syn match vimMenuName "\.\@!\%(\\\s\|\S\)\+\ze\s*$" contained contains=vimMenuNotation,vimNotation skipwhite skipnl nextgroup=vimCmdSep,vimMenuRhsContinue +syn match vimMenuNotation "&\a\|&&\|\\\s\|\\\." contained +syn match vimMenuPriority "\<\d\+\%(\.\d\+\)*\>" contained skipwhite nextgroup=vimMenuName +syn match vimMenuMod "\c<\%(script\|silent\|special\)>" contained skipwhite nextgroup=vimMenuName,vimMenuPriority,vimMenuMod contains=vimMapModKey,vimMapModErr +syn keyword vimMenuStatus enable disable nextgroup=vimMenuName skipwhite +syn match vimMenuBang "\a\@1<=!" contained skipwhite nextgroup=vimMenuName,vimMenuMod + +syn region vimMenutranslate + \ matchgroup=vimCommand start="\<menut\%[ranslate]\>" + \ skip=+\\\\\|\\|\|\n\s*\\\|\n\s*"\\ + + \ end="$" matchgroup=vimCmdSep end="|" matchgroup=vimMenuClear end="\<clear\ze\s*\%(["#|]\|$\)" + \ contains=@vimContinue,vimMenutranslateName keepend transparent +" oneline is sufficient to match the current formatting in runtime/lang/*.vim +syn match vimMenutranslateName "\%(\\\s\|\S\)\+" contained contains=vimMenuNotation,vimNotation +syn match vimMenutranslateComment +".*+ contained containedin=vimMenutranslate " Angle-Bracket Notation: (tnx to Michael Geddes) {{{2 " ====================== @@ -505,9 +614,9 @@ syn case match " User Function Highlighting: {{{2 " (following Gautam Iyer's suggestion) " ========================== -syn match vimFunc "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA-Z0-9_.]*\)\ze\s*(" contains=vimFuncEcho,vimFuncName,vimUserFunc,vimExecute -syn match vimUserFunc contained "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA-Z0-9_.]*\)\|\<\u[a-zA-Z0-9.]*\>\|\<if\>" contains=vimNotation -syn keyword vimFuncEcho contained ec ech echo +syn match vimFunc "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA-Z0-9_.]*\)\ze\s*(" contains=vimFuncEcho,vimFuncName,vimUserFunc,vimExecute +syn match vimUserFunc contained "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA-Z0-9_.]*\)\|\<\u[a-zA-Z0-9.]*\>\|\<if\>" contains=vimNotation +syn keyword vimFuncEcho contained ec ech echo " User Command Highlighting: {{{2 syn match vimUsrCmd '^\s*\zs\u\%(\w*\)\@>\%([(#[]\|\s\+\%([-+*/%]\=\|\.\.\)=\)\@!' @@ -547,8 +656,7 @@ if has("conceal") syn match vimSynCcharValue contained "\S" endif -syn match vimSyntax "\<sy\%[ntax]\>" contains=vimCommand skipwhite nextgroup=vimSynType,vimComment,vim9Comment -syn match vimAuSyntax contained "\s+sy\%[ntax]" contains=vimCommand skipwhite nextgroup=vimSynType,vimComment,vim9Comment +syn match vimSyntax "\<sy\%[ntax]\>" contains=vimCommand skipwhite nextgroup=vimSynType,@vimComment syn cluster vimFuncBodyList add=vimSyntax " Syntax: case {{{2 @@ -637,12 +745,12 @@ syn match vimIsCommand "<Bar>\s*\a\+" transparent contains=vimCommand,vimNotatio " Highlighting: {{{2 " ============ -syn cluster vimHighlightCluster contains=vimHiLink,vimHiClear,vimHiKeyList,vimComment,vim9Comment +syn cluster vimHighlightCluster contains=vimHiLink,vimHiClear,vimHiKeyList,@vimComment if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_novimhictermerror") syn match vimHiCtermError contained "\D\i*" endif syn match vimHighlight "\<hi\%[ghlight]\>" skipwhite nextgroup=vimHiBang,@vimHighlightCluster -syn match vimHiBang contained "!" skipwhite nextgroup=@vimHighlightCluster +syn match vimHiBang contained "\a\@1<=!" skipwhite nextgroup=@vimHighlightCluster syn match vimHiGroup contained "\i\+" syn case ignore @@ -661,16 +769,17 @@ syn match vimHiGuiFontname contained "'[a-zA-Z\-* ]\+'" syn match vimHiGuiRgb contained "#\x\{6}" " Highlighting: hi group key=arg ... {{{2 -syn cluster vimHiCluster contains=vimGroup,vimHiBlend,vimHiGroup,vimHiTerm,vimHiCTerm,vimHiStartStop,vimHiCtermFgBg,vimHiCtermul,vimHiGui,vimHiGuiFont,vimHiGuiFgBg,vimHiKeyError,vimNotation,vimComment,vim9comment -syn region vimHiKeyList contained oneline start="\i\+" skip="\\\\\|\\|" end="$\||" contains=@vimHiCluster +syn cluster vimHiCluster contains=vimGroup,vimHiBlend,vimHiGroup,vimHiTerm,vimHiCTerm,vimHiStartStop,vimHiCtermFgBg,vimHiCtermul,vimHiCtermfont,vimHiGui,vimHiGuiFont,vimHiGuiFgBg,vimHiKeyError,vimNotation,vimComment,vim9comment +syn region vimHiKeyList contained start="\i\+" skip=+\\\\\|\\|\|\n\s*\\\|\n\s*"\\ + matchgroup=vimCmdSep end="|" excludenl end="$" contains=@vimContinue,@vimHiCluster if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_vimhikeyerror") syn match vimHiKeyError contained "\i\+="he=e-1 endif syn match vimHiTerm contained "\cterm="he=e-1 nextgroup=vimHiAttribList -syn match vimHiStartStop contained "\c\(start\|stop\)="he=e-1 nextgroup=vimHiTermcap,vimOption +syn match vimHiStartStop contained "\c\%(start\|stop\)="he=e-1 nextgroup=vimHiTermcap,vimOption syn match vimHiCTerm contained "\ccterm="he=e-1 nextgroup=vimHiAttribList syn match vimHiCtermFgBg contained "\ccterm[fb]g="he=e-1 nextgroup=vimHiNmbr,vimHiCtermColor,vimFgBgAttrib,vimHiCtermError syn match vimHiCtermul contained "\cctermul="he=e-1 nextgroup=vimHiNmbr,vimHiCtermColor,vimFgBgAttrib,vimHiCtermError +syn match vimHiCtermfont contained "\cctermfont="he=e-1 nextgroup=vimHiNmbr,vimHiCtermColor,vimFgBgAttrib,vimHiCtermError syn match vimHiGui contained "\cgui="he=e-1 nextgroup=vimHiAttribList syn match vimHiGuiFont contained "\cfont="he=e-1 nextgroup=vimHiFontname syn match vimHiGuiFgBg contained "\cgui\%([fb]g\|sp\)="he=e-1 nextgroup=vimHiGroup,vimHiGuiFontname,vimHiGuiRgb,vimFgBgAttrib @@ -679,12 +788,13 @@ syn match vimHiBlend contained "\cblend="he=e-1 nextgroup=vimHiNmbr syn match vimHiNmbr contained '\d\+' " Highlight: clear {{{2 -syn keyword vimHiClear contained clear nextgroup=vimHiGroup +syn keyword vimHiClear contained clear skipwhite nextgroup=vimGroup,vimHiGroup " Highlight: link {{{2 " see tst24 (hi def vs hi) (Jul 06, 2018) "syn region vimHiLink contained oneline matchgroup=vimCommand start="\(\<hi\%[ghlight]\s\+\)\@<=\(\(def\%[ault]\s\+\)\=link\>\|\<def\>\)" end="$" contains=vimHiGroup,vimGroup,vimHLGroup,vimNotation -syn region vimHiLink contained oneline matchgroup=vimCommand start="\(\<hi\%[ghlight]\s\+\)\@<=\(\(def\%[ault]\s\+\)\=link\>\|\<def\>\)" end="$" contains=@vimHiCluster +" TODO: simplify and allow line continuations --djk +syn region vimHiLink contained matchgroup=Type start="\%(\<hi\%[ghlight]!\=\s\+\)\@<=\%(\%(def\%[ault]\s\+\)\=link\>\|\<def\%[ault]\>\)" skip=+\\\\\|\\|\|\n\s*\\\|\n\s*"\\ + matchgroup=vimCmdSep end="|" excludenl end="$" contains=@vimContinue,@vimHiCluster " Control Characters: {{{2 " ================== @@ -692,16 +802,21 @@ syn match vimCtrlChar "[--]" " Beginners - Patterns that involve ^ {{{2 " ========= -syn match vimLineComment +^[ \t:]*".*$+ contains=@vimCommentGroup,vimCommentString,vimCommentTitle,vimComment -syn match vimLineComment +^[ \t:]*"\("[^"]*"\|[^"]\)*$+ contains=@vimCommentGroup,vimCommentString,vimCommentTitle -syn match vim9LineComment +^[ \t:]\+#.*$+ contains=@vimCommentGroup,vimCommentString,vimCommentTitle +if s:vim9script + syn match vimLineComment +^[ \t:]*".*$+ contains=@vimCommentGroup,vimCommentString,vimCommentTitle contained + syn match vim9LineComment +^[ \t:]*#.*$+ contains=@vimCommentGroup,vimCommentString,vim9CommentTitle +else + syn match vimLineComment +^[ \t:]*".*$+ contains=@vimCommentGroup,vimCommentString,vimCommentTitle + syn match vim9LineComment +^[ \t:]*#.*$+ contains=@vimCommentGroup,vimCommentString,vim9CommentTitle contained +endif syn match vimCommentTitle '"\s*\%([sS]:\|\h\w*#\)\=\u\w*\(\s\+\u\w*\)*:'hs=s+1 contained contains=vimCommentTitleLeader,vimTodo,@vimCommentGroup -" Note: Look-behind to work around nextgroup skipnl consuming leading whitespace and preventing a match +syn match vim9CommentTitle '#\s*\%([sS]:\|\h\w*#\)\=\u\w*\(\s\+\u\w*\)*:'hs=s+1 contained contains=vim9CommentTitleLeader,vimTodo,@vimCommentGroup syn match vimContinue "^\s*\zs\\" syn match vimContinueComment '^\s*\zs["#]\\ .*' contained syn cluster vimContinue contains=vimContinue,vimContinueComment syn region vimString start="^\s*\\\z(['"]\)" skip='\\\\\|\\\z1' end="\z1" oneline keepend contains=@vimStringGroup,vimContinue syn match vimCommentTitleLeader '"\s\+'ms=s+1 contained +syn match vim9CommentTitleLeader '#\s\+'ms=s+1 contained " Searches And Globals: {{{2 " ==================== @@ -710,6 +825,17 @@ syn match vimSearchDelim '^\s*\zs[/?]\|[/?]$' contained syn region vimGlobal matchgroup=Statement start='\<g\%[lobal]!\=/' skip='\\.' end='/' skipwhite nextgroup=vimSubst1 syn region vimGlobal matchgroup=Statement start='\<v\%[global]!\=/' skip='\\.' end='/' skipwhite nextgroup=vimSubst1 +" Vim9 Script Regions: {{{2 +" ================== + +if s:vim9script + syn cluster vimLegacyTop contains=TOP,vim9LegacyHeader,vim9Comment,vim9LineComment + VimFoldH syn region vim9LegacyHeader start="\%^" end="^\ze\s*vim9s\%[cript]\>" contains=@vimLegacyTop,vimComment,vimLineComment + + syn keyword vim9Vim9ScriptArg noclear contained + syn keyword vim9Vim9Script vim9s[cript] nextgroup=vim9Vim9ScriptArg skipwhite +endif + " Embedded Scripts: {{{2 " ================ " perl,ruby : Benoit Cerrina @@ -895,6 +1021,7 @@ if exists("g:vimsyn_minlines") endif exe "syn sync maxlines=".s:vimsyn_maxlines syn sync linecont "^\s\+\\" +syn sync linebreaks=1 syn sync match vimAugroupSyncA groupthere NONE "\<aug\%[roup]\>\s\+[eE][nN][dD]" " ==================== @@ -913,7 +1040,6 @@ if !exists("skip_vim_syntax_inits") hi def link vimHiAttribList vimError hi def link vimHiCtermError vimError hi def link vimHiKeyError vimError - hi def link vimKeyCodeError vimError hi def link vimMapModErr vimError hi def link vimSubstFlagErr vimError hi def link vimSynCaseError vimError @@ -921,17 +1047,15 @@ if !exists("skip_vim_syntax_inits") hi def link vimBufnrWarn vimWarn endif + hi def link vim9Vim9ScriptArg Special hi def link vimAbb vimCommand hi def link vimAddress vimMark hi def link vimAugroupBang vimBang hi def link vimAugroupError vimError hi def link vimAugroupKey vimCommand - hi def link vimAuHighlight vimHighlight - hi def link vimAutoCmdOpt vimOption hi def link vimAutoCmd vimCommand hi def link vimAutoEvent Type hi def link vimAutoCmdMod Special - hi def link vimAutoSet vimCommand hi def link vimBang vimOper hi def link vimBehaveBang vimBang hi def link vimBehaveModel vimBehave @@ -943,29 +1067,37 @@ if !exists("skip_vim_syntax_inits") hi def link vim9Comment Comment hi def link vimCommentString vimString hi def link vimCommentTitle PreProc + hi def link vim9CommentTitle PreProc hi def link vimCondHL vimCommand hi def link vimConst vimCommand hi def link vimContinue Special hi def link vimContinueComment vimComment hi def link vimCtrlChar SpecialChar + hi def link vimDefComment vimComment + hi def link vimDefKey vimCommand + hi def link vimDefParam vimVar hi def link vimEcho vimCommand - hi def link vimEchoHLNone vimGroup - hi def link vimEchoHL vimCommand + hi def link vimEchohlNone vimGroup + hi def link vimEchohl vimCommand hi def link vimElseIfErr Error - hi def link vimElseif vimCondHL + hi def link vimEndfunction vimCommand + hi def link vimEnddef vimCommand hi def link vimEnvvar PreProc hi def link vimError Error hi def link vimEscape Special hi def link vimFBVar vimVar hi def link vimFgBgAttrib vimHiAttrib hi def link vimFuncEcho vimCommand - hi def link vimHiCtermul vimHiTerm - hi def link vimFold Folded hi def link vimFor vimCommand hi def link vimFTCmd vimCommand hi def link vimFTOption vimSynType + hi def link vimFuncBang vimBang + hi def link vimFuncComment vimComment hi def link vimFuncKey vimCommand hi def link vimFuncName Function + hi def link vimFuncMod Special + hi def link vimFuncParam vimVar + hi def link vimFuncParamEquals vimOper hi def link vimFuncSID Special hi def link vimFuncVar Identifier hi def link vimGroupAdd vimSynOption @@ -976,8 +1108,11 @@ if !exists("skip_vim_syntax_inits") hi def link vimHiAttrib PreProc hi def link vimHiBang vimBang hi def link vimHiBlend vimHiTerm - hi def link vimHiClear vimHighlight + hi def link vimHiClear Type + hi def link vimHiCtermColor Constant hi def link vimHiCtermFgBg vimHiTerm + hi def link vimHiCtermfont vimHiTerm + hi def link vimHiCtermul vimHiTerm hi def link vimHiCTerm vimHiTerm hi def link vimHighlight vimCommand hi def link vimHiGroup vimGroupName @@ -989,13 +1124,11 @@ if !exists("skip_vim_syntax_inits") hi def link vimHiStartStop vimHiTerm hi def link vimHiTerm Type hi def link vimHLGroup vimGroup - hi def link vimHLMod PreProc hi def link vimInsert vimString hi def link vimIskSep Delimiter - hi def link vimKeyCode vimSpecFile + hi def link vim9KeymapLineComment vimKeymapLineComment hi def link vimKeymapLineComment vimComment hi def link vimKeymapTailComment vimComment - hi def link vimKeyword Statement hi def link vimLet vimCommand hi def link vimLetHereDoc vimString hi def link vimLetHereDocStart Special @@ -1010,9 +1143,14 @@ if !exists("skip_vim_syntax_inits") hi def link vimMark Number hi def link vimMarkNumber vimNumber hi def link vimMenuBang vimBang + hi def link vimMenuClear Special hi def link vimMenuMod vimMapMod - hi def link vimMenuNameMore vimMenuName hi def link vimMenuName PreProc + hi def link vimMenu vimCommand + hi def link vimMenuNotation vimNotation + hi def link vimMenuPriority Number + hi def link vimMenuStatus Special + hi def link vimMenutranslateComment vimComment hi def link vimMtchComment vimComment hi def link vimNorm vimCommand hi def link vimNotation Special @@ -1021,7 +1159,6 @@ if !exists("skip_vim_syntax_inits") hi def link vimNumber Number hi def link vimOperError Error hi def link vimOper Operator - hi def link vimOperStar vimOper hi def link vimOption PreProc hi def link vimParenSep Delimiter hi def link vimPatSepErr vimError @@ -1040,10 +1177,10 @@ if !exists("skip_vim_syntax_inits") hi def link vimSetMod vimOption hi def link vimSetSep Statement hi def link vimSetString vimString + hi def link vim9Vim9Script vimCommand hi def link vimSpecFile Identifier hi def link vimSpecFileMod vimSpecFile hi def link vimSpecial Type - hi def link vimStatement Statement hi def link vimStringCont vimString hi def link vimString String hi def link vimStringEnd vimString @@ -1108,6 +1245,8 @@ let b:current_syntax = "vim" " Cleanup: {{{1 delc VimFolda delc VimFoldf +delc VimFoldh +delc VimFoldH delc VimFoldl delc VimFoldm delc VimFoldp @@ -1115,5 +1254,5 @@ delc VimFoldP delc VimFoldr delc VimFoldt let &cpo = s:keepcpo -unlet s:keepcpo -" vim:ts=18 fdm=marker +unlet s:keepcpo s:vim9script +" vim:ts=18 fdm=marker ft=vim diff --git a/runtime/syntax/yaml.vim b/runtime/syntax/yaml.vim index 49f7d049a7..6ec806a4cb 100644 --- a/runtime/syntax/yaml.vim +++ b/runtime/syntax/yaml.vim @@ -2,8 +2,7 @@ " Language: YAML (YAML Ain't Markup Language) 1.2 " Maintainer: Nikolai Pavlov <zyx.vim@gmail.com> " First author: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2015-03-28 -" removed duplicate yamlKeyValueDelimiter (pull #4799) +" Latest Revision: 2024-04-01 if exists('b:current_syntax') finish @@ -15,21 +14,21 @@ set cpo&vim " Choose the schema to use " TODO: Validate schema if !exists('b:yaml_schema') - if exists('g:yaml_schema') - let b:yaml_schema = g:yaml_schema - else - let b:yaml_schema = 'core' - endif + if exists('g:yaml_schema') + let b:yaml_schema = g:yaml_schema + else + let b:yaml_schema = 'core' + endif endif let s:ns_char = '\%([\n\r\uFEFF \t]\@!\p\)' let s:ns_word_char = '[[:alnum:]_\-]' let s:ns_uri_char = '\%(%\x\x\|'.s:ns_word_char.'\|[#/;?:@&=+$,.!~*''()[\]]\)' let s:ns_tag_char = '\%(%\x\x\|'.s:ns_word_char.'\|[#/;?:@&=+$.~*''()]\)' -let s:c_ns_anchor_char = '\%([\n\r\uFEFF \t,[\]{}]\@!\p\)' let s:c_indicator = '[\-?:,[\]{}#&*!|>''"%@`]' let s:c_flow_indicator = '[,[\]{}]' +let s:ns_anchor_char = substitute(s:ns_char, '\v\C[\zs', '\=s:c_flow_indicator[1:-2]', '') let s:ns_char_without_c_indicator = substitute(s:ns_char, '\v\C[\zs', '\=s:c_indicator[1:-2]', '') let s:_collection = '[^\@!\(\%(\\\.\|\[^\\\]]\)\+\)]' @@ -38,8 +37,8 @@ function s:SimplifyToAssumeAllPrintable(p) return substitute(a:p, '\V\C\\%('.s:_collection.'\\@!\\p\\)', '[^\1]', '') endfunction let s:ns_char = s:SimplifyToAssumeAllPrintable(s:ns_char) +let s:ns_anchor_char = s:SimplifyToAssumeAllPrintable(s:ns_anchor_char) let s:ns_char_without_c_indicator = s:SimplifyToAssumeAllPrintable(s:ns_char_without_c_indicator) -let s:c_ns_anchor_char = s:SimplifyToAssumeAllPrintable(s:c_ns_anchor_char) function s:SimplifyAdjacentCollections(p) return substitute(a:p, '\V\C'.s:_collection.'\\|'.s:_collection, '[\1\2]', 'g') @@ -60,9 +59,10 @@ let s:c_ns_tag_property = s:c_verbatim_tag. \ '\|'.s:c_ns_shorthand_tag. \ '\|'.s:c_non_specific_tag -let s:c_ns_anchor_name = s:c_ns_anchor_char.'\+' +let s:c_ns_anchor_name = s:ns_anchor_char.'\+' let s:c_ns_anchor_property = '&'.s:c_ns_anchor_name let s:c_ns_alias_node = '\*'.s:c_ns_anchor_name +let s:c_ns_properties = '\%(\%('.s:c_ns_tag_property.'\|'.s:c_ns_anchor_property.'\)\s\+\)\+' let s:ns_directive_name = s:ns_char.'\+' @@ -100,57 +100,71 @@ execute 'syn region yamlDirective oneline start='.string('^\ze%'.s:ns_directive_ \ 'yamlReservedDirective '. \ 'keepend' -syn match yamlTAGDirective '%TAG\s\+' contained nextgroup=yamlTagHandle -execute 'syn match yamlTagHandle contained nextgroup=yamlTagPrefix '.string(s:c_tag_handle.'\s\+') -execute 'syn match yamlTagPrefix contained nextgroup=yamlComment ' . string(s:ns_tag_prefix) +syn match yamlTAGDirective /%TAG\ze\s/ contained nextgroup=yamlTagHandle skipwhite +execute 'syn match yamlTagHandle' string(s:c_tag_handle) 'contained nextgroup=yamlTagPrefix skipwhite' +execute 'syn match yamlTagPrefix' string(s:ns_tag_prefix) 'contained nextgroup=yamlComment skipwhite' -syn match yamlYAMLDirective '%YAML\s\+' contained nextgroup=yamlYAMLVersion -syn match yamlYAMLVersion '\d\+\.\d\+' contained nextgroup=yamlComment +syn match yamlYAMLDirective /%YAML\ze\s/ contained nextgroup=yamlYAMLVersion skipwhite +syn match yamlYAMLVersion /\d\+\.\d\+/ contained nextgroup=yamlComment skipwhite execute 'syn match yamlReservedDirective contained nextgroup=yamlComment '. \string('%\%(\%(TAG\|YAML\)\s\)\@!'.s:ns_directive_name) syn region yamlFlowString matchgroup=yamlFlowStringDelimiter start='"' skip='\\"' end='"' - \ contains=yamlEscape - \ nextgroup=yamlKeyValueDelimiter + \ contains=yamlEscape contained nextgroup=yamlFlowMappingDelimiter,yamlComment skipwhite syn region yamlFlowString matchgroup=yamlFlowStringDelimiter start="'" skip="''" end="'" - \ contains=yamlSingleEscape - \ nextgroup=yamlKeyValueDelimiter + \ contains=yamlSingleEscape contained nextgroup=yamlFlowMappingDelimiter,yamlComment skipwhite syn match yamlEscape contained '\\\%([\\"abefnrtv\^0_ NLP\n]\|x\x\x\|u\x\{4}\|U\x\{8}\)' syn match yamlSingleEscape contained "''" -syn match yamlBlockScalarHeader contained '\s\+\zs[|>]\%([+-]\=[1-9]\|[1-9]\=[+-]\)\=' - syn cluster yamlConstant contains=yamlBool,yamlNull -syn cluster yamlFlow contains=yamlFlowString,yamlFlowMapping,yamlFlowCollection -syn cluster yamlFlow add=yamlFlowMappingKey,yamlFlowMappingMerge -syn cluster yamlFlow add=@yamlConstant,yamlPlainScalar,yamlFloat -syn cluster yamlFlow add=yamlTimestamp,yamlInteger,yamlMappingKeyStart -syn cluster yamlFlow add=yamlComment -syn region yamlFlowMapping matchgroup=yamlFlowIndicator start='{' end='}' contains=@yamlFlow -syn region yamlFlowCollection matchgroup=yamlFlowIndicator start='\[' end='\]' contains=@yamlFlow +syn cluster yamlFlowNode contains=yamlFlowString,yamlFlowMapping,yamlFlowCollection +syn cluster yamlFlowNode add=yamlFlowMappingKey,yamlFlowMappingKeyStart,yamlFlowMappingMerge +syn cluster yamlFlowNode add=@yamlConstant,yamlPlainScalar,yamlFloat,yamlComment +syn cluster yamlFlowNode add=yamlTimestamp,yamlInteger,yamlAlias,yamlFlowNodeProperties +syn region yamlFlowMapping matchgroup=yamlFlowIndicator start='{\@<!{{\@!' end='}' contains=@yamlFlowNode +syn region yamlFlowCollection matchgroup=yamlFlowIndicator start='\[' end='\]' contains=@yamlFlowNode execute 'syn match yamlPlainScalar /'.s:ns_plain_out.'/' execute 'syn match yamlPlainScalar contained /'.s:ns_plain_in.'/' -syn match yamlMappingKeyStart '?\ze\s' -syn match yamlMappingKeyStart '?' contained - -execute 'syn match yamlFlowMappingKey /\%#=1'.s:ns_plain_in.'\%(\s\+'.s:ns_plain_in.'\)*\ze\s*:/ contained '. - \'nextgroup=yamlKeyValueDelimiter' -syn match yamlFlowMappingMerge /<<\ze\s*:/ contained nextgroup=yamlKeyValueDelimiter - -syn match yamlBlockCollectionItemStart '^\s*\zs-\%(\s\+-\)*\s' nextgroup=yamlBlockMappingKey,yamlBlockMappingMerge -" Use the old regexp engine, the NFA engine doesn't like all the \@ items. -execute 'syn match yamlBlockMappingKey /\%#=1^\s*\zs'.s:ns_plain_out.'\%(\s\+'.s:ns_plain_out.'\)*\ze\s*:\%(\s\|$\)/ '. - \'nextgroup=yamlKeyValueDelimiter' -execute 'syn match yamlBlockMappingKey /\%#=1\s*\zs'.s:ns_plain_out.'\%(\s\+'.s:ns_plain_out.'\)*\ze\s*:\%(\s\|$\)/ contained '. - \'nextgroup=yamlKeyValueDelimiter' -syn match yamlBlockMappingMerge /^\s*\zs<<\ze:\%(\s\|$\)/ nextgroup=yamlKeyValueDelimiter -syn match yamlBlockMappingMerge /<<\ze\s*:\%(\s\|$\)/ nextgroup=yamlKeyValueDelimiter contained - -syn match yamlKeyValueDelimiter /\s*:/ contained +execute 'syn match yamlFlowMappingKey /'.s:ns_plain_in.'\%(\s\+'.s:ns_plain_in.'\)*\ze\s*:/ contained '. + \'nextgroup=yamlFlowMappingDelimiter skipwhite' +syn match yamlFlowMappingKeyStart /?/ contained nextgroup=@yamlFlowNode skipwhite +syn match yamlFlowMappingMerge /<<\ze\s*:/ contained nextgroup=yamlFlowMappingDelimiter skipwhite +syn match yamlFlowMappingDelimiter /:/ contained nextgroup=@yamlFlowNode skipwhite +execute 'syn match yamlFlowNodeProperties' string(s:c_ns_properties) + \ 'contained contains=yamlNodeTag,yamlAnchor nextgroup=@yamlFlowNode skipwhite' + +execute 'syn match yamlBlockMappingKey /^\s*\zs'.s:ns_plain_out.'\%(\s\+'.s:ns_plain_out.'\)*\ze\s*:\%(\s\|$\)/ '. + \'nextgroup=yamlBlockMappingDelimiter skipwhite' +execute 'syn match yamlBlockMappingKey /'.s:ns_plain_out.'\%(\s\+'.s:ns_plain_out.'\)*\ze\s*:\%(\s\|$\)/ contained '. + \'nextgroup=yamlBlockMappingDelimiter skipwhite' +syn match yamlBlockMappingKeyString /^\s*\zs\%("\%([^"]\|\\"\)*"\|'\%([^']\|''\)*'\)\ze\s*:\%(\s\|$\)/ + \ contains=yamlFlowString nextgroup=yamlBlockMappingDelimiter skipwhite +syn match yamlBlockMappingKeyString /\%("\%([^"]\|\\"\)*"\|'\%([^']\|''\)*'\)\ze\s*:\%(\s\|$\)/ contained + \ contains=yamlFlowString nextgroup=yamlBlockMappingDelimiter skipwhite +syn match yamlBlockMappingMerge /^\s*\zs<<\ze\s*:\%(\s\|$\)/ nextgroup=yamlBlockMappingDelimiter skipwhite +syn match yamlBlockMappingMerge /<<\ze\s*:\%(\s\|$\)/ contained nextgroup=yamlBlockMappingDelimiter skipwhite + +syn match yamlBlockMappingDelimiter /^\s*\zs:\ze\%(\s\|$\)/ nextgroup=@yamlBlockNode skipwhite +syn match yamlBlockMappingDelimiter /:\ze\%(\s\|$\)/ contained nextgroup=@yamlBlockNode skipwhite +syn match yamlBlockMappingKeyStart /^\s*\zs?\ze\%(\s\|$\)/ nextgroup=@yamlBlockNode skipwhite +syn match yamlBlockMappingKeyStart /?\ze\%(\s\|$\)/ contained nextgroup=@yamlBlockNode skipwhite + +syn match yamlBlockCollectionItemStart /^\s*\zs-\ze\%(\s\|$\)/ nextgroup=@yamlBlockNode skipwhite +syn match yamlBlockCollectionItemStart /-\ze\%(\s\|$\)/ contained nextgroup=@yamlBlockNode skipwhite + +execute 'syn match yamlBlockNodeProperties' string(s:c_ns_properties) + \ 'contained contains=yamlNodeTag,yamlAnchor nextgroup=@yamlFlowNode,yamlBlockScalarHeader skipwhite' +syn match yamlBlockScalarHeader '[|>]\%([1-9][+-]\|[+-]\?[1-9]\?\)\%(\s\+#.*\)\?$' contained + \ contains=yamlComment nextgroup=yamlBlockString skipnl +syn region yamlBlockString start=/^\z(\s\+\)/ skip=/^$/ end=/^\%(\z1\)\@!/ contained + +syn cluster yamlBlockNode contains=@yamlFlowNode,yamlBlockMappingKey,yamlBlockMappingKeyString, + \yamlBlockMappingMerge,yamlBlockMappingKeyStart,yamlBlockCollectionItemStart, + \yamlBlockNodeProperties,yamlBlockScalarHeader syn cluster yamlScalarWithSpecials contains=yamlPlainScalar,yamlBlockMappingKey,yamlFlowMappingKey @@ -164,8 +178,8 @@ elseif b:yaml_schema is# 'core' syn keyword yamlNull null Null NULL contained containedin=@yamlScalarWithSpecials syn keyword yamlBool true True TRUE false False FALSE contained containedin=@yamlScalarWithSpecials exe 'syn match yamlNull /'.s:_bounder.'\@1<!\~'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' - exe 'syn match yamlInteger /'.s:_bounder.'\@1<!\%([+-]\=\%(0\%(b[0-1_]\+\|[0-7_]\+\|x[0-9a-fA-F_]\+\)\=\|\%([1-9][0-9_]*\%(:[0-5]\=\d\)\+\)\)\|[1-9][0-9_]*\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' - exe 'syn match yamlFloat /'.s:_bounder.'\@1<!\%([+-]\=\%(\%(\d[0-9_]*\)\.[0-9_]*\%([eE][+-]\=\d\+\)\=\|\.[0-9_]\+\%([eE][-+]\=[0-9]\+\)\=\|\d[0-9_]*\%(:[0-5]\=\d\)\+\.[0-9_]*\|\.\%(inf\|Inf\|INF\)\)\|\%(\.\%(nan\|NaN\|NAN\)\)\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' + exe 'syn match yamlInteger /'.s:_bounder.'\@1<!\%([-+]\=\%(\%(0\%(b[0-1_]\+\|o\?[0-7_]\+\|x[0-9a-fA-F_]\+\)\=\|\%([1-9][0-9_]*\%(:[0-5]\=\d\)\+\)\)\|[1-9][0-9_]*\)\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' + exe 'syn match yamlFloat /'.s:_bounder.'\@1<!\%([-+]\=\%(\%(\d[0-9_]*\)\.[0-9_]*\%([eE][-+]\=\d\+\)\=\|\.[0-9_]\+\%([eE][-+]\=[0-9]\+\)\=\|\d[0-9_]*\%(:[0-5]\=\d\)\+\.[0-9_]*\|\.\%(inf\|Inf\|INF\)\)\|\%(\.\%(nan\|NaN\|NAN\)\)\)'.s:_bounder.'\@!/ contained containedin=@yamlScalarWithSpecials' elseif b:yaml_schema is# 'pyyaml' syn keyword yamlNull null Null NULL contained containedin=@yamlScalarWithSpecials syn keyword yamlBool true True TRUE false False FALSE yes Yes YES no No NO on On ON off Off OFF contained containedin=@yamlScalarWithSpecials @@ -208,17 +222,27 @@ hi def link yamlFlowStringDelimiter yamlString hi def link yamlEscape SpecialChar hi def link yamlSingleEscape SpecialChar -hi def link yamlBlockCollectionItemStart Label -hi def link yamlBlockMappingKey Identifier -hi def link yamlBlockMappingMerge Special - -hi def link yamlFlowMappingKey Identifier -hi def link yamlFlowMappingMerge Special - +hi def link yamlMappingKey Identifier hi def link yamlMappingKeyStart Special -hi def link yamlFlowIndicator Special +hi def link yamlMappingMerge Special hi def link yamlKeyValueDelimiter Special +hi def link yamlFlowIndicator Special +hi def link yamlFlowMappingKey yamlMappingKey +hi def link yamlFlowMappingKeyStart yamlMappingKeyStart +hi def link yamlFlowMappingMerge yamlMappingMerge +hi def link yamlFlowMappingDelimiter yamlKeyValueDelimiter + +hi def link yamlBlockMappingKey yamlMappingKey +hi def link yamlBlockMappingKeyStart yamlMappingKeyStart +hi def link yamlBlockMappingMerge yamlMappingMerge +hi def link yamlBlockMappingDelimiter yamlKeyValueDelimiter +hi def link yamlBlockCollectionItemStart Label +hi def link yamlBlockScalarHeader Special +" We do not link yamlBlockString to yamlString, because yamlPlainScalar is +" not highlighted as string neighter, and also due to historical reasons. +" hi def link yamlBlockString yamlString + hi def link yamlConstant Constant hi def link yamlNull yamlConstant @@ -234,10 +258,18 @@ hi def link yamlTimestamp Number let b:current_syntax = "yaml" -unlet s:ns_word_char s:ns_uri_char s:c_verbatim_tag s:c_named_tag_handle s:c_secondary_tag_handle s:c_primary_tag_handle s:c_tag_handle s:ns_tag_char s:c_ns_shorthand_tag s:c_non_specific_tag s:c_ns_tag_property s:c_ns_anchor_char s:c_ns_anchor_name s:c_ns_anchor_property s:c_ns_alias_node s:ns_char s:ns_directive_name s:ns_local_tag_prefix s:ns_global_tag_prefix s:ns_tag_prefix s:c_indicator s:ns_plain_safe_out s:c_flow_indicator s:ns_plain_safe_in s:ns_plain_first_in s:ns_plain_first_out s:ns_plain_char_in s:ns_plain_char_out s:ns_plain_out s:ns_plain_in s:ns_char_without_c_indicator s:ns_plain_safe_in_without_colhash s:ns_plain_safe_out_without_colhash -unlet s:_collection s:_neg_collection +unlet s:ns_char s:ns_word_char s:ns_uri_char s:ns_tag_char s:c_indicator s:c_flow_indicator + \ s:ns_anchor_char s:ns_char_without_c_indicator s:_collection s:_neg_collection + \ s:c_verbatim_tag s:c_named_tag_handle s:c_secondary_tag_handle s:c_primary_tag_handle + \ s:c_tag_handle s:c_ns_shorthand_tag s:c_non_specific_tag s:c_ns_tag_property + \ s:c_ns_anchor_name s:c_ns_anchor_property s:c_ns_alias_node s:c_ns_properties + \ s:ns_directive_name s:ns_local_tag_prefix s:ns_global_tag_prefix s:ns_tag_prefix + \ s:ns_plain_safe_out s:ns_plain_safe_in s:ns_plain_safe_in_without_colhash s:ns_plain_safe_out_without_colhash + \ s:ns_plain_first_in s:ns_plain_first_out s:ns_plain_char_in s:ns_plain_char_out s:ns_plain_out s:ns_plain_in delfunction s:SimplifyAdjacentCollections delfunction s:SimplifyToAssumeAllPrintable let &cpo = s:cpo_save unlet s:cpo_save + +" vim: set et sw=4 sts=4 ts=8: diff --git a/runtime/syntax/zathurarc.vim b/runtime/syntax/zathurarc.vim new file mode 100644 index 0000000000..5e0526d02a --- /dev/null +++ b/runtime/syntax/zathurarc.vim @@ -0,0 +1,37 @@ +" Vim syntax file +" Language: Zathurarc +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Documentation: https://pwmt.org/projects/zathura/documentation/ +" Upstream: https://github.com/Freed-Wu/zathurarc.vim +" Latest Revision: 2024-04-02 + +if exists('b:current_syntax') + finish +endif +let b:current_syntax = 'zathurarc' + +syntax case match +syntax iskeyword @,48-57,_,192-255,- + +syntax region zathurarcComment start="\%([ \t]*\&\([^\\]\zs\|^\)\)#" end="$" +syntax match zathurarcBracket /[<>]/ contained +syntax match zathurarcNotation `<[A-Z][a-z0-9]\+>` contains=zathurarcBracket +syntax match zathurarcNumber `\<[0-9.]\>` +syntax region zathurarcString start=`"` skip=`\\"` end=`"` +syntax region zathurarcString start=`'` skip=`\\'` end=`'` +syntax keyword zathurarcMode normal fullscreen presentation index +syntax keyword zathurarcBoolean true false +syntax keyword zathurarcCommand include map set unmap +syntax keyword zathurarcOption abort-clear-search adjust-open advance-pages-per-row completion-bg completion-fg completion-group-bg completion-group-fg completion-highlight-bg completion-highlight-fg continuous-hist-save database dbus-raise-window dbus-service default-bg default-fg exec-command filemonitor first-page-column font guioptions highlight-active-color highlight-color highlight-fg highlight-transparency incremental-search index-active-bg index-active-fg index-bg index-fg inputbar-bg inputbar-fg link-hadjust link-zoom n-completion-items notification-bg notification-error-bg notification-error-fg notification-fg notification-warning-bg notification-warning-fg page-cache-size page-padding page-right-to-left page-thumbnail-size pages-per-row recolor recolor-darkcolor recolor-keephue recolor-lightcolor recolor-reverse-video render-loading render-loading-bg render-loading-fg sandbox scroll-full-overlap scroll-hstep scroll-page-aware scroll-step scroll-wrap search-hadjust selection-clipboard selection-notification show-directories show-hidden show-recent statusbar-basename statusbar-bg statusbar-fg statusbar-h-padding statusbar-home-tilde statusbar-page-percent statusbar-v-padding synctex synctex-editor-command vertical-center window-height window-icon window-icon-document window-title-basename window-title-home-tilde window-title-page window-width zoom-center zoom-max zoom-min zoom-step + +highlight default link zathurarcComment Comment +highlight default link zathurarcNumber Number +highlight default link zathurarcMode Macro +highlight default link zathurarcString String +highlight default link zathurarcBoolean Boolean +" same as vim +highlight default link zathurarcBracket Delimiter +highlight default link zathurarcNotation Special +highlight default link zathurarcCommand Statement +highlight default link zathurarcOption PreProc +" ex: nowrap diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor index c3decdef11..622eb7cc06 100644 --- a/runtime/tutor/en/vim-01-beginner.tutor +++ b/runtime/tutor/en/vim-01-beginner.tutor @@ -39,7 +39,7 @@ instead of text to type. Now, move to the next lesson (use the `j`{normal} key to scroll down). -## Lesson 1.1: MOVING THE CURSOR +# Lesson 1.1: MOVING THE CURSOR ** To move the cursor, press the `h`, `j`, `k`, `l` keys as indicated. ** @@ -87,7 +87,7 @@ NOTE: [:q!](:q) `<Enter>`{normal} discards any changes you made. In a few lesson 5. Move the cursor down to Lesson 1.3. -## Lesson 1.3: TEXT EDITING - DELETION +# Lesson 1.3: TEXT EDITING: DELETION ** Press `x`{normal} to delete the character under the cursor. ** @@ -251,8 +251,8 @@ The format for a delete command with the [d](d) delete operator is as follows: Thus typing `de`{normal} will delete from the cursor to the end of the word. -NOTE: Pressing just the motion while in Normal mode without an operator - will move the cursor as specified. +NOTE: Pressing just the motion while in Normal mode without an operator + will move the cursor as specified. # Lesson 2.4: USING A COUNT FOR A MOTION @@ -360,7 +360,7 @@ Fiix the errors oon thhis line and reeplace them witth undo. 7. To undo previous actions, type: `u`{normal} (lowercase u) To undo all the changes on a line, type: `U`{normal} (capital U) - To undo the undo's, type: `<C-r>`{normal} + To undo the undos, type: `<C-r>`{normal} # Lesson 3.1: THE PUT COMMAND @@ -381,7 +381,7 @@ b) Violets are blue, c) Intelligence is learned, a) Roses are red, -NOTE: You can also put the text before the cursor with `P`{normal} (capital P) +NOTE: You can also put the text before the cursor with `P`{normal} (capital P). # Lesson 3.2: THE REPLACE COMMAND @@ -508,7 +508,7 @@ NOTE: When the search reaches the end of the file it will continue at the # Lesson 4.3: MATCHING PARENTHESES SEARCH -** Type `%`{normal} to find a matching ),], or }. ** +** Type `%`{normal} to find a matching ), ], or }. ** 1. Place the cursor on any (, [, or { in the line below marked ✓. @@ -518,9 +518,9 @@ NOTE: When the search reaches the end of the file it will continue at the 4. Type `%`{normal} to move the cursor to the other matching bracket. - 5. Move the cursor to another (,),[,],{ or } and see what `%`{normal} does. + 5. Move the cursor to another (, ), [, ], {, or } and see what `%`{normal} does. -This ( is a test line with ('s, ['s ] and {'s } in it. )) +This ( is a test line with ('s, ['s, ] and {'s } in it. )) NOTE: This is very useful in debugging a program with unmatched parentheses! @@ -534,7 +534,7 @@ NOTE: This is very useful in debugging a program with unmatched parentheses! ~~~ cmd :s/thee/the/ ~~~ - NOTE: the [:s](:s) command only changed the first match of "thee" in the line. + NOTE: The [:s](:s) command only changed the first match of "thee" in the line. 3. Now type ~~~ cmd @@ -565,14 +565,14 @@ Usually thee best time to see thee flowers is in thee spring. to find every occurrence in the whole file, with a prompt whether to substitute or not. -NOTE: You can also select the lines you want to substitute first using visual-mode. +NOTE: You can also select the lines you want to substitute first using Visual mode. This will be explained more in a future lesson. # Lesson 4 SUMMARY 1. `<C-g>`{normal} displays your location and the file status. `G`{normal} moves to the end of the file. - number `G`{normal} moves to that line number. + number `G`{normal} moves to that line number. `gg`{normal} moves to the first line. 2. Typing `/`{normal} followed by a phrase searches FORWARD for the phrase. @@ -582,14 +582,14 @@ NOTE: You can also select the lines you want to substitute first using visual-mo `<C-o>`{normal} takes you back to older positions, `<C-i>`{normal} to newer positions. - 3. Typing `%`{normal} while the cursor is on a (,),[,],{, or } goes to its + 3. Typing `%`{normal} while the cursor is on a (, ), [, ], {, or } goes to its match. 4. To substitute new for the first old in a line type ~~~ cmd :s/old/new ~~~ - To substitute new for all 'old's on a line type + To substitute new for all olds on a line type ~~~ cmd :s/old/new/g ~~~ @@ -638,7 +638,7 @@ NOTE: All `:`{vim} commands are executed when you press `<Enter>`{normal}. ~~~ cmd :w TEST ~~~ - (where TEST is the filename you chose.) + (where TEST is the filename you chose.) 4. This saves the current file under the name TEST. To verify this, type `:!{unix:(ls),win:(dir)}`{vim} again to see your directory. @@ -667,7 +667,7 @@ NOTE: If you were to exit Neovim and start it again with `nvim TEST`, the file 4. Type - `:w TEST`{vim} + `w TEST`{vim} where TEST is a filename that does not exist yet. Verify that you see @@ -688,8 +688,8 @@ NOTE: Pressing [v](v) starts [Visual selection](visual-mode). You can move the c 1. Place the cursor just above this line. -NOTE: After executing Step 2 you will see text from Lesson 5.3. Then move - DOWN to see this lesson again. +NOTE: After executing Step 2 you will see text from Lesson 5.3. Then move + DOWN to see this lesson again. 2. Now retrieve your TEST file using the command @@ -762,7 +762,7 @@ Open up a line above this by typing O while the cursor is on this line. This li will allow you to pract appendi text to a line. This line will allow you to practice appending text to a line. -NOTE: [a](a), [i](i) and [A](A) all go to the same Insert mode, the only +NOTE: [a](a), [i](i), and [A](A) all go to the same Insert mode, the only difference is where the characters are inserted. # Lesson 6.3: ANOTHER WAY TO REPLACE @@ -810,9 +810,9 @@ NOTE: Replace mode is like Insert mode, but every typed character a) This is the first item. b) -NOTE: you can use `y`{normal} as an operator: `yw`{normal} yanks one word. +NOTE: You can use `y`{normal} as an operator: `yw`{normal} yanks one word. -NOTE: you can use `P`{normal} to put before the cursor, rather than after. +NOTE: You can use `P`{normal} to put before the cursor, rather than after. # Lesson 6.5: SET OPTION @@ -864,7 +864,7 @@ NOTE: If you want to ignore case for just one search command, use [\c](/\c) 4. The `y`{normal} operator copies text, `p`{normal} pastes it. 5. Typing a capital `R`{normal} enters Replace mode until `<Esc>`{normal} is - pressed. + pressed. 6. Typing "[:set](:set) xxx" sets the option "xxx". Some options are: @@ -872,7 +872,7 @@ NOTE: If you want to ignore case for just one search command, use [\c](/\c) 'is' 'incsearch' show partial matches for a search phrase 'hls' 'hlsearch' highlight all matching phrases - You can either use the long or the short option name. + You can either use the long or the short option name. 7. Prepend "no" to switch an option off: ~~~ cmd @@ -921,8 +921,8 @@ To start using more features create an "init.vim" file. `:w`{vim} - You can add all your preferred settings to this "init.vim" file. - For more information type `:help init.vim`{vim}. +You can add all your preferred settings to this "init.vim" file. +For more information type `:help init.vim`{vim}. # Lesson 7.3: COMPLETION diff --git a/runtime/tutor/ja/vim-01-beginner.tutor b/runtime/tutor/ja/vim-01-beginner.tutor index 411ebc04f8..82d56bd745 100644 --- a/runtime/tutor/ja/vim-01-beginner.tutor +++ b/runtime/tutor/ja/vim-01-beginner.tutor @@ -1,32 +1,32 @@ # Neovimのチュートリアルへようこそ - -Neovim は、このチュートリアルで説明するには多すぎる程のコマンドを備えた非常に -強力なエディターです。このチュートリアルは、あなたが Neovim を万能エディターとして + Neovim は、このチュートリアルで説明するには多すぎる程のコマンドを備えた非常に強 +力なエディターです。このチュートリアルは、あなたが Neovim を万能エディターとして 使いこなせるようになるのに十分なコマンドについて説明をするようになっています。 -このチュートリアルが、体を使うことで覚えられる仕組みになっていることを、 -心しておかなければなりません。正しく学習するには実際にやってなければならない -のです。テキストをただ読むだけでは、何がが重要だったか忘れてしまうでしょう! +このチュートリアルが、体を使うことで覚えられる仕組みになっていることを、心してお +かなければなりません。正しく学習するには実際にやってなければならないのです。 +テキストをただ読むだけでは、何がが重要だったか忘れてしまうでしょう! -それでは、CapsLockキーが押されていないことを確認した後、画面にレッスン 0が -全部表示されるところまで、`j`{normal} キーを押してカーソルを移動しましょう。 +それでは、CapsLock キーが押されていないことを確認した後、画面にレッスン 0が全部 +表示されるところまで、`j`{normal} キーを押してカーソルを移動しましょう。 # レッスン 0 -NOTE: 以下の練習用コマンドにはこの文章を変更するものもありますが、それらの -変更は保存されません。失敗を恐れる必要はありません、 [<Esc>](<Esc>) キーを押した後、 - [u](u) キーを押すことで最後の操作を元に戻せる事を覚えておいてください。 +NOTE: 以下の練習用コマンドにはこの文章を変更するものもありますが、それらの変更は +保存されません。失敗を恐れる必要はありません、 [<Esc>](<Esc>) キーを押した後、 [u](u) キーを押 +すことで最後の操作を元に戻せる事を覚えておいてください。 -このチュートリアルはインタラクティブな設計になっており、 -あなたが知っておくべきことがいくつかあります。 --[このようなリンク](holy-grail )の上で [<Enter>](<Enter>) キーを押すことで、リンクされたヘルプを開くことができます。 +このチュートリアルはインタラクティブな設計になっており、あなたが知っておくべきこ +とがいくつかあります。 +-[このようなリンク](holy-grail )の上で [<Enter>](<Enter>) キーを押すことで、リンクされたヘルプを開くこと + ができます。 -もしくは、ドキュメントで検索したい単語の上で [K](K) キー(大文字)を押してみましょう。 -(注: 現在、日本語の単語には対応していません。) + (注: 日本語の単語には対応していません。) -ヘルプウィンドウは `:q`{vim} `<Enter>`{normal} で閉じることができます。 -画面の左端に ✗ が表示されている場合、その行のテキストを編集しなければなりません。 -正しいテキストに書き換えることで、左端の ✗ は ✓ に変わります。 -Neovim がいかに優れているか、おわかりいただけるでしょうか? +画面の左端に ✗ が表示されている場合、その行にあるテキストを編集しなければなりま +せん。正しいテキストに書き換えることで、左端の ✗ は ✓ に変わります。 + Neovim がいかに優れているか、おわかりいただけるでしょうか? また、次のようにコマンドを実行するよう求められることや、(後で詳しく説明します。) @@ -36,8 +36,8 @@ Neovim がいかに優れているか、おわかりいただけるでしょう ~~~ normal <Esc>0f<Space>d3wP$P ~~~ -< と > の間に囲まれたテキスト(例えば `<Enter>`{normal})は、 -そのテキストをタイプするのではなく、そのキーを押すことを表しています。 +< と > の間に囲まれたテキスト(例えば `<Enter>`{normal})は、そのテキストをタイプするのでは +なく、そのキーを押すことを表しています。 では、次のレッスンに向かいましょう。( `j`{normal} キーでスクロールダウンします。) @@ -58,11 +58,11 @@ Neovim がいかに優れているか、おわかりいただけるでしょう 3. 下へのキーを使って、レッスン1.2 に移動しましょう。 -NOTE: 何をタイプしているか判らなくなったら、`<Esc>`{normal} を押してノーマルモードにします。 +NOTE: 何を入力しているか判らなくなったら、`<Esc>`{normal} を押してノーマルモードにします。 それから入力しようとしていたコマンドを再入力しましょう。 -NOTE: カーソルキーでも移動できます。しかし hjkl に一度慣れてしまえば、 - はるかに速く移動することができるでしょう。 +NOTE: カーソルキーでも移動できます。しかし hjkl に一度慣れてしまえば、はるかに速 + く移動することができるでしょう。 # レッスン 1.2: NEOVIM の起動と終了 @@ -76,13 +76,13 @@ NOTE: カーソルキーでも移動できます。しかし hjkl に一度慣 これにより編集した内容を保存せずにエディタが終了します。 - 3. Neovim を開いて、このチュートリアルを始める為のコマンドを実行し、 - ここに戻ってきます。そのコマンドは: + 3. Neovim を開いて、このチュートリアルを始める為のコマンドを実行し、ここに戻っ + てきます。そのコマンドは: `:Tutor`{vim} `<Enter>`{normal} - 4. これまでのステップを覚え自信がついたならば、ステップ 1 から 3 までを実際に試して、 - エディタを1度終了してから再び起動しましょう。 + 4. これまでのステップを覚え自信がついたならば、ステップ 1 から 3 までを実際に試 + して、エディタを1度終了してから再び起動しましょう。 NOTE: [:q!](:q) `<Enter>`{normal} は全ての変更を破棄します。 後に変更をファイルに保存する方法についても勉強していきましょう。 @@ -115,13 +115,13 @@ NOTE: 全てのレッスンを通じて、頭で覚えようとしないでく 1. 以下の ✗ と示された最初の行にカーソルを移動しましょう。 - 2. 1行目を2行目と同じ様にするために、テキストを挿入しなければ - ならない位置の次の文字にカーソルを移動します。 + 2. 1行目を2行目と同じ様にするために、テキストを挿入しなければならない位置の次の + 文字にカーソルを移動します。 3. `i`{normal} キーを押してから、追加が必要な文字をタイプしましょう。 - 4. 間違いを修正したら `<Esc>`{normal} を押してノーマルモードに戻り、 - 正しい文になる様にステップ 2 から 4 を繰り返しましょう。 + 4. 間違いを修正したら `<Esc>`{normal} を押してノーマルモードに戻り、正しい文になる様にス + テップ 2 から 4 を繰り返しましょう。 この には 足り テキスト が 。 この 行 には 幾つか 足りない テキスト が ある。 @@ -139,8 +139,8 @@ NOTE: 全てのレッスンを通じて、頭で覚えようとしないでく 3. テキストを追加し終えたら、 `<Esc>`{normal} を押してノーマルモードに戻りましょう。 - 4. 2行目の ✗ と示された場所へ移動し、ステップ 2 から 3 を繰り返して - 文法を修正しましょう。 + 4. 2行目の ✗ と示された場所へ移動し、ステップ 2 から 3 を繰り返して文法を修正し + ましょう。 この 行 には 間違った テキスト が あり この 行 には 間違った テキスト が あります。 @@ -156,15 +156,15 @@ NOTE: 全てのレッスンを通じて、頭で覚えようとしないでく !! NOTE: 以下のあらゆるステップを行う前に、このレッスンを全部読みましょう!! - 1. レッスン 1.2 でやったように `:q!`{vim} をタイプして、このチュートリアルを終了します。 - もしくは、別のターミナルにアクセスできる場合、そこで以下の内容を行ってください。 + 1. レッスン 1.2 でやったように `:q!`{vim} を入力して、このチュートリアルを終了します。 + もしくは、別のターミナルにアクセスできる場合、そこで以下の内容を行います。 2. シェルプロンプトでこのコマンドをタイプします: ~~~ sh $ nvim tutor ~~~ - 'nvim' が Nvim エディタを起動するコマンド、'tutor' は編集したい - ファイルの名前です。変更できるファイルの名前を使いましょう。 + 'nvim' が Nvim エディタを起動するコマンド、'tutor' は編集したいファイルの名 + 前です。変更できるファイルの名前を使いましょう。 3. 前のレッスンで学んだように、テキストを挿入、削除します。 @@ -174,8 +174,8 @@ NOTE: 全てのレッスンを通じて、頭で覚えようとしないでく ~~~ コマンドを実行するには `<Enter>`{normal} を押さなければなりません。 - 5. ステップ 1 でこのチュートリアルを終了した場合は - 再度起動した後、以下の要約へ進みましょう。 + 5. ステップ 1 でこのチュートリアルを終了した場合は再度起動した後、以下の要約へ + 進みましょう。 6. 以上のステップを読んで理解した上でこれを実行しましょう。 @@ -197,8 +197,8 @@ NOTE: 全てのレッスンを通じて、頭で覚えようとしないでく `i`{normal} テキストのタイプ `<Esc>`{normal} カーソル位置に追加 `A`{normal} テキストの追加 `<Esc>`{normal} 行末に追加 -NOTE: `<Esc>`{normal} キーを押すとノーマルモードに移行します。 - その際間違ったり、入力途中のコマンドを取り消すことができます。 +NOTE: `<Esc>`{normal} キーを押すとノーマルモードに移行します。その際間違ったり、入力途中の + コマンドを取り消すことができます。 さて、続けてレッスン 2 を始めましょう。 @@ -252,12 +252,12 @@ NOTE: `<Esc>`{normal} キーを押すとノーマルモードに移行します つまり `de`{normal} とタイプすると、カーソル位置から単語の終わりまでを削除します。 -NOTE: 冒険したい人は、ノーマルモードにてオペレータなしにモーションを押してみましょう。 - カーソルが目的語一覧で示される位置に移動するはずです。 +NOTE: 冒険したい人は、ノーマルモードにてオペレータなしにモーションを押してみまし + ょう。カーソルが目的語一覧で示される位置に移動するはずです。 # レッスン 2.4: モーションにカウントを使用する -** 何回も繰り返し行いたいのモーションの前にその回数をタイプします。 ** +** 何回も繰り返し行いたいモーションの前にその回数をタイプします。 ** 1. 以下の ✓ と示された行の先頭にカーソルを移動します。 @@ -277,16 +277,16 @@ This is just a line with words you can move around in. ** オペレータとカウントをタイプすると、その操作が複数回繰り返されます。 ** -既述の削除のオペレータとモーションの組み合わせにカウントを追加することで、 -より多くの削除が行えます: +既述の削除のオペレータとモーションの組み合わせにカウントを追加することで、より多 +くの削除が行えます: d 数値 モーション 1. ✗ と示された行の最初の大文字の単語にカーソルを移動しましょう。 2. 大文字の単語2つを `d2w`{normal} とタイプして削除します。 - 3. 連続した大文字の単語を、異なるカウントを指定した1つのコマンドで削除し、 - ステップ 1 と 2 を繰り返します。 + 3. 連続した大文字の単語を、異なるカウントを指定した1つのコマンドで削除し、ステ + ップ 1 と 2 を繰り返します。 このABC DE行のFGHI JK LMN OP単語はQ RS TUV綺麗になった。 @@ -294,8 +294,8 @@ This is just a line with words you can move around in. ** 行全体を削除するには `dd`{normal} とタイプします。 ** -行全体を削除する頻度が多いので、Viのデザイナーは行の -削除を d の2回タイプという簡単なものに決めました。 +行全体を削除する頻度が多いので、Viのデザイナーは行の削除を d の2回タイプという簡 +単なものに決めました。 1. 以下の詩の2行目にカーソルを移動します。 @@ -315,9 +315,9 @@ This is just a line with words you can move around in. # Lesson 2.7: THE UNDO COMMAND -** 最後のコマンドを取り消す(Undo)には `u`{normal} を押します。`U`{normal} は行全体の取り消しです。 ** +** 最後のコマンドを取り消す(Undo)には `u`{normal} を押します。`U`{normal} は行全体の取り消しです。** - 1. 以下の ✗ と示された行にカーソルを移動し、最初の間違いにカーソルを移動しましょう。 + 1. 以下の ✗ と示された行にある、最初の間違いにカーソルを移動しましょう。 2. `x`{normal} をタイプして最初のいらない文字を削除しましょう。 @@ -329,8 +329,8 @@ This is just a line with words you can move around in. 6. `u`{normal} をタイプして直前の `U`{normal} コマンドを取り消しましょう。 - 7. ではコマンドを再実行するのに `<C-r>`{normal} (Ctrl + R)を数回 - タイプしてみましょう(取り消しの取り消し)。 + 7. ではコマンドを再実行するのに `<C-r>`{normal} (Ctrl + R)を数回タイプしてみましょう(取り + 消しの取り消し)。 このの行のの間違いを修正々し、後でそれらの修正をを取り消しまますす。 @@ -372,7 +372,7 @@ This is just a line with words you can move around in. 2. `dd`{normal} とタイプして行を削除し、Neovim のレジスタに格納しましょう。 - 3. 削除した行が本来あるべき位置の上の行である c) 行まで、カーソルを移動させましょう。 + 3. 削除した行が本来あるべき位置の上の行である c) 行にカーソルを移動しましょう。 4. `p`{normal} をタイプして格納した行をカーソルの下に戻します。 @@ -447,15 +447,15 @@ NOTE: タイプ中の間違いはバックスペースキーを使って直す # レッスン 3 要約 1. 既に削除されたテキストを再配置するには、[p](p) をタイプします。 - これは削除されたテキストをカーソルの後に挿入します(行単位で削除されたのならば、 - カーソルのある次の行に挿入されます)。 + これは削除されたテキストをカーソルの後に挿入します(行単位で削除されたのなら + ば、カーソルのある次の行に挿入されます)。 - 2. カーソルの下の文字を置き換えるには、[r](r) をタイプした後、 - それを置き換える文字をタイプします。 + 2. カーソルの下の文字を置き換えるには、[r](r) をタイプした後、それを置き換える + 文字をタイプします。 - 3. [変更オペレータ](c)ではカーソル位置から特定のモーションで指定される終端までを - 変更することが可能です。例えば `ce`{normal} ならばカーソル位置から単語の - 終わりまで、`c$`{normal} ならば行の終わりまでを変更します。 + 3. [変更オペレータ](c)ではカーソル位置から特定のモーションで指定される終端までを変更 + することが可能です。例えば `ce`{normal} ならばカーソル位置から単語の終わりまで、`c$`{normal} な + らば行の終わりまでを変更します。 4. 変更コマンドの形式は @@ -465,16 +465,16 @@ NOTE: タイプ中の間違いはバックスペースキーを使って直す # レッスン 4.1: 位置とファイルの情報 -** ファイル内での位置とファイルの状態を表示するには `<C-g>`{normal} をタイプします。 - ファイル内のある行に移動するには `G`{normal} をタイプします。 ** +** ファイル内での位置とファイルの状態を表示するには `<C-g>`{normal} をタイプします。ファイ + ル内のある行に移動するには `G`{normal} をタイプします。 ** NOTE: ステップを実行する前に、このレッスン全てに目を通しましょう!! 1. `<Ctrl>`{normal} を押したまま `g`{normal} を押しましょう。この操作を `<C-g>`{normal} と呼んでいます。 - ページの一番下にファイル名と行番号が表示されるはずです。 - ステップ 3のために行番号を覚えておきましょう。 + ページの一番下にファイル名と行番号が表示されるはずです。 ステップ 3のために + 行番号を覚えておきましょう。 -NOTE: 画面の右下隅にカーソルの位置が表示されているかもしれません。 +NOTE: 画面の右下隅にカーソルの座標が既に表示されているかもしれません。 これは ['ruler']('ruler') オプションを設定することで表示されます。 2. ファイルの最下行に移動するために [G](G) をタイプしましょう。 @@ -489,8 +489,8 @@ NOTE: 画面の右下隅にカーソルの位置が表示されているかも ** 語句を検索するには `/`{normal} と、前方検索する語句をタイプします。 ** - 1. ノーマルモードで `/`{normal} という文字をタイプします。画面一番下に `:`{normal} コマンドと - 同じ様に カーソルが現れることに気づくでしょう。 + 1. ノーマルモードで `/`{normal} という文字をタイプします。画面一番下に `:`{normal} コマンドと同じ様 + にカーソルが現れることに気づくでしょう。 2. では、'errroor' `<Enter>`{normal} とタイプしましょう。これが検索したい単語です。 @@ -499,13 +499,13 @@ NOTE: 画面の右下隅にカーソルの位置が表示されているかも 4. 逆方向に語句を検索する場合は、`/`{normal} の代わりに [?](?) コマンドを使用します。 - 5. 元の場所に戻るには `<C-o>`{normal} (`<Ctrl>`{normal} を押し続けながら `o`{normal} をタイプ)を - タイプします。さらに戻るにはこれを繰り返します。`<C-i>`{normal} は前方向です。 + 5. 元の場所に戻るには `<C-o>`{normal} (`<Ctrl>`{normal} を押し続けながら `o`{normal} をタイプ)を入力します。 + さらに戻るにはこれを繰り返します。`<C-i>`{normal} は前方向です。 "errroor" は error とスペルが違います; errroor はいわゆる error です。 -NOTE: 検索がファイルの終わりに達すると、オプション ['wrapscan']('wrapscan') が設定されている場合は、 - ファイルの先頭から検索を続行します。 +NOTE: 検索がファイルの終わりに達すると、オプション ['wrapscan']('wrapscan') が設定されている場 + 合は、ファイルの先頭から検索を続行します。 # レッスン 4.3: 対応する括弧を検索 @@ -523,7 +523,7 @@ NOTE: 検索がファイルの終わりに達すると、オプション ['wraps This ( is a test line with ('s, ['s ] and {'s } in it. )) -NOTE: この機能は括弧が一致していないプログラムをデバッグするのにとても役立ちます! +NOTE: この機能は括弧が一致していないプログラムのデバッグにとても役立ちます! # レッスン 4.4: 代替コマンド @@ -535,8 +535,8 @@ NOTE: この機能は括弧が一致していないプログラムをデバッ ~~~ cmd :s/thee/the/ ~~~ - NOTE: [:s](:s) コマンドはその行で最初に見つかったものにだけ - 行われることに気をつけましょう。 + NOTE: [:s](:s) コマンドはその行で最初に見つかったものにだけ行われることに気をつけ + ましょう。 3. さらに、次のようにタイプします ~~~ cmd @@ -578,8 +578,8 @@ NOTE: 置き換えたいテキストをビジュアルモードで選択する 検索の後の `n`{normal} は同じ方向の次の検索を、`N`{normal} は逆方向の検索をします。 `<C-o>`{normal} は場所を前に移し、`<C-i>`{normal} は場所を次に移動します。 - 3. (,),[,],{, もしくは } 上にカーソルがある状態で `%`{normal} をタイプすると - 対になる文字へ移動します。 + 3. (,),[,],{, もしくは } の上にカーソルがある状態で `%`{normal} をタイプすると対になる文 + 字へ移動します。 4. 現在行の最初の old を new に置換する。 ~~~ cmd @@ -647,7 +647,7 @@ NOTE: ここで Neovim を終了し、`nvim TEST` で起動すると、保存し ~~~ # レッスン 5.3: 選択した書き込み -** ファイルの一部を保存するには、`v`{normal} モーションと `:w ファイル名`{vim} をタイプします。 ** +** ファイルの一部を保存するには `v`{normal} モーションと `:w ファイル名`{vim} をタイプします。 ** 1. この行にカーソルを移動します。 @@ -675,8 +675,8 @@ NOTE: ここで Neovim を終了し、`nvim TEST` で起動すると、保存し それは削除しないでおいて下さい。次のレッスンで使用します。 NOTE: [v](v) を押すと、ビジュアル(Visual)選択が始まります。カーソルを動かすことで、 - 選択範囲を大きくも小さくもできます。さらに、その選択範囲に対して - オペレータを適用できます。例えば `d`{normal} はテキストを削除します。 + 選択範囲を大きくも小さくもできます。さらに、その選択範囲に対してオペレータ + を適用できます。例えば `d`{normal} はテキストを削除します。 # レッスン 5.4: ファイルの取込と合併 @@ -694,10 +694,9 @@ NOTE: ステップ 2 の実行後、レッスン 5.3 のテキストが現れま ここでいう TEST は使うファイルの名前のことです。 読み込まれたファイルは、カーソル行の下にあります。 - 3. 取り込んだファイルを確認してみましょう。カーソルを戻すと、レッスン5.3 の - オリジナルとファイルによるものの2つがあることがわかります。 + 3. 取り込んだファイルを確認してみましょう。カーソルを戻すと、レッスン5.3 のオリ + ジナルとファイルによるものの2つがあることがわかります。 - 4. 残りのチュートリアルの為に、`u`{normal} を押して最後のコマンドを取り消します。 NOTE: 外部コマンドの出力を読み込むこともできます。例えば、 @@ -711,11 +710,13 @@ NOTE: 外部コマンドの出力を読み込むこともできます。例え よく使う例: `:!{unix:(ls ),win:(dir)}`{vim} - ディレクトリ内の一覧を見る。 - `:!{unix:(rm ),win:(del)} ファイル名`{vim} - ファイルを削除する。 + `:!{unix:(rm ),win:(del)} ファイル名`{vim} - ファイルを削除する。 - 2. [:w](:w) ファイル名 ファイル名 によってファイル名というファイルがディスクに書き込まれる。 + 2. [:w](:w) ファイル名 ファイル名 によってファイル名というファイルがディスクに書 + き込まれる。 - 3. [v](v) モーション で :w ファイル名 とすると、ビジュアル選択行がファイルに保存される。 + 3. [v](v) モーション で :w ファイル名 とすると、ビジュアル選択行がファイルに保存 + される。 4. [:r](:r) ファイル名 によりファイル名というファイルがディスクより取り込まれ、 カーソル位置の下に挿入される。 @@ -731,13 +732,12 @@ NOTE: 外部コマンドの出力を読み込むこともできます。例え 2. `o`{normal} (小文字) をタイプして、カーソルの下の行を[開き](o)、挿入モードに入ります。 - 3. いくつか文字をタイプしてから、挿入モードを終了する為に `<Esc>`{normal} を - タイプします。 + 3. いくつか文字をタイプしてから、挿入モードを終了する為に `<Esc>`{normal} を入力します。 `o`{normal} をタイプするとカーソルは開いた行へ移動し挿入モードに入ります。 - 4. カーソルの上の行に挿入するには、小文字の `o`{normal} ではなく、 - 単純に[大文字の O](O)をタイプします。次の行で試してみましょう。 + 4. カーソルの上の行に挿入するには、小文字の `o`{normal} ではなく、単純に[大文字の O](O)をタイ + プします。次の行で試してみましょう。 この行の上へ挿入するには、この行へカーソルを置いて `O`{normal} をタイプします。 @@ -751,33 +751,33 @@ NOTE: 外部コマンドの出力を読み込むこともできます。例え 3. カーソルの後ろにテキストを[追加](a)するために `a`{normal} (小文字) をタイプします。 - 4. その下の行のような単語に完成させます。挿入モードを抜ける為に `<Esc>`{normal} を押します。 + 4. 下の行のように単語を完成させます。挿入モードを抜ける為に `<Esc>`{normal} を押します。 5. `e`{normal} を使って次の不完全な単語へ移動し、ステップ 3 と 4 を繰り返します。 This li will allow you to pract appendi text to a line. This line will allow you to practice appending text to a line. -NOTE: [a](a), [i](i) と [A](A) は同じ挿入モードへ移りますが、文字が挿入される位置だけが異なります。 +NOTE: [a](a), [i](i) と [A](A) は同じ挿入モードへ移りますが、文字が挿入される位置は異なります。 # レッスン 6.3: その他の置換方法 ** 1文字以上を置き換える(Replace)には大文字の `R`{normal} とタイプしましょう。 ** - 1. 以下の ✗ と示された行にカーソルを移動します。最初の "xxx" の先頭に移動します。 + 1. 以下の ✗ と示された行にある、最初の "xxx" の先頭にカーソルを移動します。 2. `R`{normal} ([大文字 R](R)) を押して、2行目の数値をタイプすることで、"xxx" が置換されます。 - 3. 置換モードを抜けるには `<Esc>`{normal} を押します。行の残りが変更されていないままに - なることに注意してください。 + 3. 置換モードを抜けるには `<Esc>`{normal} を押します。行の残りが変更されていないままにな + ることに注意してください。 4. 残った "xxx" をステップを繰り返して置換しましょう。 Adding 123 to xxx gives you xxx. Adding 123 to 456 gives you 579. -NOTE: 置換モードは挿入モードに似ていますが、全てのタイプされた文字は - 既存の文字を削除します。 +NOTE: 置換モードは挿入モードに似ていますが、全てのタイプされた文字は既存の文字を + 削除します。 # レッスン 6.4: テキストのコピーとペースト @@ -793,11 +793,11 @@ NOTE: 置換モードは挿入モードに似ていますが、全てのタイ 5. `p`{normal} を押して貼り付け([put](put))てから、次をタイプします: a second <ESC> - 6. `a`{normal} を押してから、 "second" とタイプします。その後、`<Esc>`{normal}を - 押して挿入モードを終了します。 + 6. `a`{normal} を押してから、 "second" とタイプします。その後、`<Esc>`{normal} を押して挿入モードを + 終了します。 - 7. ビジュアルモードで " item." を選択し、`y`{normal} で yank、次の行の行末まで `j$`{normal} で - 移動し、 `p`{normal} でテキストをそこに put します。 + 7. ビジュアルモードで " item." を選択し、`y`{normal} で yank、次の行の行末まで `j$`{normal} で移動 + し、 `p`{normal} でテキストをそこに put します。 a) This is the first item. b) @@ -840,8 +840,8 @@ NOTE: マッチの強調表示をやめるには次の様に入力します: ~~~ cmd :nohlsearch ~~~ -NOTE: 1つの検索コマンドだけ大文字小文字の区別をやめたいならば、 - 語句内で [\c](/\c) を使用します: /ignore\c <Enter> +NOTE: 1つの検索コマンドだけ大文字小文字の区別をやめたいならば、語句内で [\c](/\c) を使 + 用します: /ignore\c <Enter> # レッスン 6 要約 @@ -887,8 +887,8 @@ Neovim には広範にわたるオンラインヘルプシステムがありま `<C-w><C-w>`{normal} とタイプすると ヘルプウィンドウへジャンプします。 `:q`{vim} とタイプすると ヘルプウィンドウを閉じられます。 -":help" コマンドに引数を与えることにより、あらゆる題名のヘルプを見つけること -ができます。これらを試してみましょう(`<Enter>`{normal} をタイプし忘れないように): +":help" コマンドに引数を与えることにより、あらゆる題名のヘルプを見つけることがで +きます。これらを試してみましょう(`<Enter>`{normal} をタイプし忘れないように): ~~~ cmd :help w :help c_CTRL-D @@ -900,16 +900,15 @@ Neovim には広範にわたるオンラインヘルプシステムがありま ** Neovim の特徴を発揮する ** -Neovim はとても自由度の高いエディタです。あなたの好きなように -カスタマイズすることができます。より多くの機能を使いはじめるには -"init.vim" ファイルを作成します。 +Neovim はとても自由度の高いエディタです。あなたの好きなようにカスタマイズするこ +とができます。より多くの機能を使いはじめるには "init.vim" ファイルを作成します。 1. "init.vim" ファイルの編集を開始します。 `:call mkdir(stdpath('config'),'p')`{vim} `:exe 'edit' stdpath('config').'/init.vim'`{vim} - 3. 以下のようにファイルへ書き込みます。 + 2. 以下のようにしてファイルを保存します。 `:w`{vim} @@ -951,11 +950,11 @@ NOTE: 補完は多くのコマンドで動作します。特に `:help`{vim} の # おわりに -これにて Neovim のチュートリアルを終わります。エディタを簡単に、しかも充分に -使うことができるようにと、Neovim の持つ概念の要点のみを伝えようとしました。 -Neovim にはさらに多くのコマンドがあり、ここで全てを説明することはできません。 -ヘルプを沢山活用してください。オンライン上にも数多の教材や動画を -見つけることができます。ここにいくつか紹介します: +これにて Neovim のチュートリアルを終わります。エディタを簡単に、しかも充分に使う +ことができるようにと、Neovim の持つ概念の要点のみを伝えようとしました。Neovim に +はさらに多くのコマンドがあり、ここで全てを説明することはできません。ヘルプを沢山 +活用してください。オンライン上にも数多の教材や動画を見つけることができます。 +ここにいくつか紹介します: - *Learn Vim Progressively*: https://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/ @@ -975,11 +974,12 @@ Neovim にはさらに多くのコマンドがあり、ここで全てを説明 https://vim-jp.org/vimdoc-ja/ もしあなたが本が好きならば、*Practical Vim* by Drew Neil をお勧めします。 +本書は翻訳版も出版されています。(邦題: *実践Vim 思考のスピードで編集しよう!*) (続編である *Modern Vim* には Neovimについての内容も含まれています。) -このチュートリアルは Colorado State University の Charles Smith のアイデア -を基に、Colorado School of Mines の Michael C. Pierce と Robert K. Ware の -両名によって書かれました。 E-mail: bware@mines.colorado.edu. +このチュートリアルは Colorado State University の Charles Smith のアイデアを基に +Colorado School of Mines の Michael C. Pierce と Robert K. Ware の両名によって書 +かれました。 E-mail: bware@mines.colorado.edu. Modified for Vim by Bram Moolenaar. Modified for vim-tutor-mode by Felipe Morales. diff --git a/runtime/tutor/ja/vim-01-beginner.tutor.json b/runtime/tutor/ja/vim-01-beginner.tutor.json index 5af4d5da94..6452a27db4 100644 --- a/runtime/tutor/ja/vim-01-beginner.tutor.json +++ b/runtime/tutor/ja/vim-01-beginner.tutor.json @@ -18,7 +18,7 @@ "312": -1, "313": -1, "314": -1, - "335": "この行の間違いを修正し、後でそれらの修正をを取り消します。", + "335": "この行の間違いを修正し、後でそれらの修正を取り消します。", "381": -1, "382": -1, "383": -1, diff --git a/scripts/check_urls.vim b/scripts/check_urls.vim index 3580b79475..b75dc29c48 100644 --- a/scripts/check_urls.vim +++ b/scripts/check_urls.vim @@ -6,21 +6,41 @@ " Written by Christian Brabandt. func Test_check_URLs() +"20.10.23, added by Restorer if has("win32") - echoerr "Doesn't work on MS-Windows" - return + let s:outdev = 'nul' + else + let s:outdev = '/dev/null' endif +" Restorer: For Windows users. If "curl" or "wget" is installed on the system +" but not in %PATH%, add the full path to them to %PATH% environment variable. if executable('curl') " Note: does not follow redirects! - let s:command = 'curl --silent --fail --output /dev/null --head ' + let s:command1 = 'curl --silent --max-time 5 --fail --output ' ..s:outdev.. ' --head ' + let s:command2 = "" elseif executable('wget') " Note: only allow a couple of redirects - let s:command = 'wget --quiet -S --spider --max-redirect=2 --timeout=5 --tries=2 -O /dev/null ' + let s:command1 = 'wget --quiet -S --spider --max-redirect=2 --timeout=5 --tries=2 -O ' ..s:outdev.. ' ' + let s:command2 = "" + elseif has("win32") "20.10.23, added by Restorer + if executable('powershell') + if 2 == system('powershell -nologo -noprofile "$psversiontable.psversion.major"') + echoerr 'To work in OS Windows requires the program "PowerShell" version 3.0 or higher' + return + endif + let s:command1 = + \ "powershell -nologo -noprofile \"{[Net.ServicePointManager]::SecurityProtocol = 'Tls12, Tls11, Tls, Ssl3'};try{(Invoke-WebRequest -MaximumRedirection 2 -TimeoutSec 5 -Uri " + let s:command2 = ').StatusCode}catch{exit [int]$Error[0].Exception.Status}"' + endif else - echoerr 'Only works when "curl" or "wget" is available' + echoerr 'Only works when "curl" or "wget", or "powershell" is available' return endif + " Do the testing. + set report =999 + set nomore shm +=s + let pat='\(https\?\|ftp\)://[^\t* ]\+' exe 'helpgrep' pat helpclose @@ -36,22 +56,21 @@ func Test_check_URLs() put =urls " remove some more invalid items " empty lines - v/./d + "20.10.23, Restorer: '_' is a little faster, see `:h global` + v/./d _ " remove # anchors %s/#.*$//e " remove trailing stuff (parenthesis, dot, comma, quotes), but only for HTTP " links - g/^h/s#[.,)'"/>][:.]\?$## - g#^[hf]t\?tp:/\(/\?\.*\)$#d - silent! g/ftp://,$/d - silent! g/=$/d + g/^h/s#[.),'"`/>][:.,]\?$## + g#^[hf]t\?tp:/\(/\?\.*\)$#d _ + silent! g/ftp://,$/d _ + silent! g/=$/d _ let a = getline(1,'$') let a = uniq(sort(a)) - %d + %d _ call setline(1, a) - " Do the testing. - set nomore %s/.*/\=TestURL(submatch(0))/ " highlight the failures @@ -61,8 +80,10 @@ endfunc func TestURL(url) " Relies on the return code to determine whether a page is valid echom printf("Testing URL: %d/%d %s", line('.'), line('$'), a:url) - call system(s:command . shellescape(a:url)) + call system(s:command1 .. shellescape(a:url) .. s:command2) return printf("%s %d", a:url, v:shell_error) endfunc call Test_check_URLs() + +" vim: sw=2 sts=2 et diff --git a/scripts/cliff.toml b/scripts/cliff.toml index 3fc10e5d16..51725cacfc 100644 --- a/scripts/cliff.toml +++ b/scripts/cliff.toml @@ -1,39 +1,38 @@ -# configuration file for git-cliff (0.1.0) +# configuration file for git-cliff [changelog] # changelog header header = """ # Changelog\n -All notable changes to this project will be documented in this file.\n +For notable changes, see runtime/doc/news.txt (or `:help news` in Nvim).\n +Following is a list of fixes/features commits.\n """ # template for the changelog body -# https://tera.netlify.app/docs/#introduction +# https://github.com/Keats/tera +# https://keats.github.io/tera/docs/ body = """ {% if version %}\ - ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} + # [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} {% else %}\ - ## [unreleased] + # [unreleased] {% endif %}\ {% for group, commits in commits | group_by(attribute="group") %} - ### {{ group | upper_first }} - {% for commit in commits%}\ + {{ group | striptags | upper_first }} + -------------------------------------------------------------------------------- + {% for commit in commits | sort(attribute="message")%}\ {% if not commit.scope %}\ - - {{ commit.message | upper_first }} + - {{ commit.id | truncate(length=12, end="") }} {{ commit.message }} {% endif %}\ {% endfor %}\ {% for group, commits in commits | group_by(attribute="scope") %}\ - {% for commit in commits %}\ - - **{{commit.scope}}**: {{ commit.message | upper_first }} + {% for commit in commits | sort(attribute="message") %}\ + - {{ commit.id | truncate(length=12, end="") }} {{commit.scope}}: {{ commit.message }} {% endfor %}\ {% endfor %} {% endfor %}\n """ # remove the leading and trailing whitespace from the template trim = true -# changelog footer -footer = """ -<!-- generated by git-cliff --> -""" [git] # parse the commits based on https://www.conventionalcommits.org @@ -48,16 +47,18 @@ commit_preprocessors = [ ] # regex for parsing and grouping commits commit_parsers = [ - { message = "!:", group = "Breaking"}, - { message = "^feat", group = "Features"}, - { message = "^fix", group = "Bug Fixes"}, - { message = "^doc", group = "Documentation"}, - { message = "^perf", group = "Performance"}, - { message = "^refactor", group = "Refactor"}, - { message = "^test", group = "Testing"}, - { message = "^chore", group = "Miscellaneous Tasks"}, - { message = "^build", group = "Build System"}, - { message = "^Revert", group = "Reverted Changes"}, + { message = "!:", group = "<!-- 0 -->BREAKING"}, + { message = "^feat", group = "<!-- 1 -->FEATURES"}, + { message = "^fix", group = "<!-- 2 -->FIXES"}, + { message = "^perf", group = "<!-- 3 -->PERFORMANCE"}, + { message = "^build", group = "<!-- 4 -->BUILD"}, + { message = "^vim-patch", group = "<!-- 5 -->VIM PATCHES"}, + { message = "^refactor", group = "<!-- 6 -->REFACTOR" }, + { message = "^ci", group = "<!-- 8 -->CI" }, + { message = "^test", group = "<!-- 9 -->TESTING" }, + { message = "^docs", group = "<!-- 99 -->DOCUMENTATION" }, + { message = "^revert", group = "<!-- 999 -->REVERTED CHANGES" }, + { message = ".*", group = "<!-- 9999 -->OTHER"}, ] # filter out the commits that are not matched by commit parsers filter_commits = true diff --git a/scripts/gen_help_html.lua b/scripts/gen_help_html.lua index 43040151eb..cdfb85bde6 100644 --- a/scripts/gen_help_html.lua +++ b/scripts/gen_help_html.lua @@ -50,6 +50,7 @@ local spell_dict = { local spell_ignore_files = { ['backers.txt'] = true, ['news.txt'] = { 'tree-sitter' }, -- in news, may refer to the upstream "tree-sitter" library + ['news-0.10.txt'] = { 'tree-sitter' }, } local language = nil @@ -63,13 +64,20 @@ local new_layout = { ['channel.txt'] = true, ['deprecated.txt'] = true, ['develop.txt'] = true, + ['dev_style.txt'] = true, + ['dev_theme.txt'] = true, + ['dev_tools.txt'] = true, + ['dev_vimpatch.txt'] = true, + ['faq.txt'] = true, ['lua.txt'] = true, ['luaref.txt'] = true, ['news.txt'] = true, + ['news-0.9.txt'] = true, + ['news-0.10.txt'] = true, ['nvim.txt'] = true, - ['pi_health.txt'] = true, ['provider.txt'] = true, ['ui.txt'] = true, + ['vim_diff.txt'] = true, } -- TODO: These known invalid |links| require an update to the relevant docs. @@ -523,6 +531,8 @@ local function visit_node(root, level, lang_tree, headings, opt, stats) return ('%s<a href="%s">%s</a>%s'):format(ws(), fixed_url, fixed_url, removed_chars) elseif node_name == 'word' or node_name == 'uppercase_name' then return text + elseif node_name == 'note' then + return ('<b>%s</b>'):format(text) elseif node_name == 'h1' or node_name == 'h2' or node_name == 'h3' then if is_noise(text, stats.noise_lines) then return '' -- Discard common "noise" lines. @@ -685,6 +695,8 @@ local function visit_node(root, level, lang_tree, headings, opt, stats) return string.format('%s</span>', s) end return s + elseif node_name == 'modeline' then + return '' elseif node_name == 'ERROR' then if ignore_parse_error(opt.fname, trimmed) then return text @@ -824,8 +836,7 @@ local function gen_one(fname, to_fname, old, commit, parser_path) <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@docsearch/css@3" /> <link rel="preconnect" href="https://X185E15FPG-dsn.algolia.net" crossorigin /> - <link href="/css/normalize.min.css" rel="stylesheet"> - <link href="/css/bootstrap.css" rel="stylesheet"> + <link href="/css/bootstrap.min.css" rel="stylesheet"> <link href="/css/main.css" rel="stylesheet"> <link href="help.css" rel="stylesheet"> <link href="/highlight/styles/neovim.min.css" rel="stylesheet"> @@ -1086,14 +1097,19 @@ local function gen_css(fname) padding-bottom: 10px; /* Tabs are used for alignment in old docs, so we must match Vim's 8-char expectation. */ tab-size: 8; - white-space: pre; + white-space: pre-wrap; font-size: 16px; font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; + word-wrap: break-word; } - .old-help-para pre { - /* All text in .old-help-para is formatted as "white-space:pre" so text following <pre> is - already visually separated by the linebreak. */ + .old-help-para pre, .old-help-para pre:hover { + /* Text following <pre> is already visually separated by the linebreak. */ margin-bottom: 0; + /* Long lines that exceed the textwidth should not be wrapped (no "pre-wrap"). + Since text may overflow horizontally, we make the contents to be scrollable + (only if necessary) to prevent overlapping with the navigation bar at the right. */ + white-space: pre; + overflow-x: auto; } /* TODO: should this rule be deleted? help tags are rendered as <code> or <span>, not <a> */ @@ -1422,9 +1438,8 @@ function M.test_gen(help_dir) help_dir, tmpdir, -- Because gen() is slow (~30s), this test is limited to a few files. - { 'pi_health.txt', 'help.txt', 'index.txt', 'nvim.txt' } + { 'help.txt', 'index.txt', 'nvim.txt' } ) - eq(4, #rv.helpfiles) eq(0, rv.err_count, 'parse errors in :help docs') eq({}, rv.invalid_links, 'invalid tags in :help docs') end diff --git a/scripts/gen_lsp.lua b/scripts/gen_lsp.lua index 19fad7bab4..04d19f22e6 100644 --- a/scripts/gen_lsp.lua +++ b/scripts/gen_lsp.lua @@ -259,10 +259,13 @@ function M.gen(opt) if prefix then anonymous_classname = anonymous_classname .. '.' .. prefix end - local anonym = vim.tbl_flatten { -- remove nil - anonymous_num > 1 and '' or nil, - '---@class ' .. anonymous_classname, - } + local anonym = vim + .iter({ + (anonymous_num > 1 and { '' } or {}), + { '---@class ' .. anonymous_classname }, + }) + :flatten() + :totable() --- @class vim._gen_lsp.StructureLiteral translated to anonymous @class. --- @field deprecated? string diff --git a/scripts/gen_vimdoc.lua b/scripts/gen_vimdoc.lua index 22df411a35..9c6225efc3 100755 --- a/scripts/gen_vimdoc.lua +++ b/scripts/gen_vimdoc.lua @@ -94,12 +94,12 @@ end local function fn_helptag_fmt_common(fun) local fn_sfx = fun.table and '' or '()' if fun.classvar then - return fmt('*%s:%s%s*', fun.classvar, fun.name, fn_sfx) + return fmt('%s:%s%s', fun.classvar, fun.name, fn_sfx) end if fun.module then - return fmt('*%s.%s%s*', fun.module, fun.name, fn_sfx) + return fmt('%s.%s%s', fun.module, fun.name, fn_sfx) end - return fmt('*%s%s*', fun.name, fn_sfx) + return fun.name .. fn_sfx end --- @type table<string,nvim.gen_vimdoc.Config> @@ -129,7 +129,7 @@ local config = { return name .. ' Functions' end, helptag_fmt = function(name) - return fmt('*api-%s*', name:lower()) + return fmt('api-%s', name:lower()) end, }, lua = { @@ -241,22 +241,22 @@ local config = { end, helptag_fmt = function(name) if name == '_editor' then - return '*lua-vim*' + return 'lua-vim' elseif name == '_options' then - return '*lua-vimscript*' + return 'lua-vimscript' elseif name == 'tohtml' then - return '*tohtml*' + return 'tohtml' end - return '*vim.' .. name:lower() .. '*' + return 'vim.' .. name:lower() end, fn_helptag_fmt = function(fun) local name = fun.name if vim.startswith(name, 'vim.') then local fn_sfx = fun.table and '' or '()' - return fmt('*%s%s*', name, fn_sfx) + return name .. fn_sfx elseif fun.classvar == 'Option' then - return fmt('*vim.opt:%s()*', name) + return fmt('vim.opt:%s()', name) end return fn_helptag_fmt_common(fun) @@ -297,9 +297,9 @@ local config = { end, helptag_fmt = function(name) if name:lower() == 'lsp' then - return '*lsp-core*' + return 'lsp-core' end - return fmt('*lsp-%s*', name:lower()) + return fmt('lsp-%s', name:lower()) end, }, diagnostic = { @@ -312,7 +312,7 @@ local config = { return 'Lua module: vim.diagnostic' end, helptag_fmt = function() - return '*diagnostic-api*' + return 'diagnostic-api' end, }, treesitter = { @@ -337,9 +337,43 @@ local config = { end, helptag_fmt = function(name) if name:lower() == 'treesitter' then - return '*lua-treesitter-core*' + return 'lua-treesitter-core' end - return '*lua-treesitter-' .. name:lower() .. '*' + return 'lua-treesitter-' .. name:lower() + end, + }, + editorconfig = { + filename = 'editorconfig.txt', + files = { + 'runtime/lua/editorconfig.lua', + }, + section_order = { + 'editorconfig.lua', + }, + section_fmt = function(_name) + return 'EditorConfig integration' + end, + helptag_fmt = function(name) + return name:lower() + end, + fn_xform = function(fun) + fun.table = true + fun.name = vim.split(fun.name, '.', { plain = true })[2] + end, + }, + health = { + filename = 'health.txt', + files = { + 'runtime/lua/vim/health.lua', + }, + section_order = { + 'health.lua', + }, + section_fmt = function(_name) + return 'Checkhealth' + end, + helptag_fmt = function(name) + return name:lower() end, }, } @@ -469,10 +503,12 @@ local function inline_type(obj, classes) local desc_append = {} for _, f in ipairs(cls.fields) do - local fdesc, default = get_default(f.desc) - local fty = render_type(f.type, nil, default) - local fnm = fmt_field_name(f.name) - table.insert(desc_append, table.concat({ '-', fnm, fty, fdesc }, ' ')) + if not f.access then + local fdesc, default = get_default(f.desc) + local fty = render_type(f.type, nil, default) + local fnm = fmt_field_name(f.name) + table.insert(desc_append, table.concat({ '-', fnm, fty, fdesc }, ' ')) + end end desc = desc .. '\n' .. table.concat(desc_append, '\n') @@ -593,6 +629,12 @@ local function render_fun_header(fun, cfg) if fun.classvar then nm = fmt('%s:%s', fun.classvar, nm) end + if nm == 'vim.bo' then + nm = 'vim.bo[{bufnr}]' + end + if nm == 'vim.wo' then + nm = 'vim.wo[{winid}][{bufnr}]' + end local proto = fun.table and nm or nm .. '(' .. table.concat(args, ', ') .. ')' @@ -600,7 +642,7 @@ local function render_fun_header(fun, cfg) cfg.fn_helptag_fmt = fn_helptag_fmt_common end - local tag = cfg.fn_helptag_fmt(fun) + local tag = '*' .. cfg.fn_helptag_fmt(fun) .. '*' if #proto + #tag > TEXT_WIDTH - 8 then table.insert(ret, fmt('%78s\n', tag)) @@ -747,10 +789,17 @@ local function render_funs(funs, classes, cfg) ret[#ret + 1] = render_fun(f, classes, cfg) end - -- Sort via prototype + -- Sort via prototype. Experimental API functions ("nvim__") sort last. table.sort(ret, function(a, b) local a1 = ('\n' .. a):match('\n[a-zA-Z_][^\n]+\n') local b1 = ('\n' .. b):match('\n[a-zA-Z_][^\n]+\n') + + local a1__ = a1:find('^%s*nvim__') and 1 or 0 + local b1__ = b1:find('^%s*nvim__') and 1 or 0 + if a1__ ~= b1__ then + return a1__ < b1__ + end + return a1:lower() < b1:lower() end) @@ -816,7 +865,7 @@ local function make_section(filename, cfg, section_docs, funs_txt) local sectname = cfg.section_name and cfg.section_name[filename] or mktitle(name) -- section tag: e.g., "*api-autocmd*" - local help_tag = cfg.helptag_fmt(sectname) + local help_tag = '*' .. cfg.helptag_fmt(sectname) .. '*' if funs_txt == '' and #section_docs == 0 then return @@ -845,9 +894,9 @@ local function render_section(section, add_header) }) end - if section.doc and #section.doc > 0 then - table.insert(doc, '\n\n') - vim.list_extend(doc, section.doc) + local sdoc = '\n\n' .. table.concat(section.doc or {}, '\n') + if sdoc:find('[^%s]') then + doc[#doc + 1] = sdoc end if section.funs_txt then @@ -880,6 +929,7 @@ end --- @param cfg nvim.gen_vimdoc.Config local function gen_target(cfg) + print('Target:', cfg.filename) local sections = {} --- @type table<string,nvim.gen_vimdoc.Section> expand_files(cfg.files) @@ -891,7 +941,7 @@ local function gen_target(cfg) local all_classes = {} --- First pass so we can collect all classes - for _, f in pairs(cfg.files) do + for _, f in vim.spairs(cfg.files) do local ext = assert(f:match('%.([^.]+)$')) --[[@as 'h'|'c'|'lua']] local parser = assert(parsers[ext]) local classes, funs, briefs = parser(f) @@ -899,13 +949,14 @@ local function gen_target(cfg) all_classes = vim.tbl_extend('error', all_classes, classes) end - for f, r in pairs(file_results) do + for f, r in vim.spairs(file_results) do local classes, funs, briefs = r[1], r[2], r[3] local briefs_txt = {} --- @type string[] for _, b in ipairs(briefs) do briefs_txt[#briefs_txt + 1] = md_to_vimdoc(b, 0, 0, TEXT_WIDTH) end + print(' Processing file:', f) local funs_txt = render_funs(funs, all_classes, cfg) if next(classes) then local classes_txt = render_classes(classes) @@ -923,8 +974,9 @@ local function gen_target(cfg) for _, f in ipairs(cfg.section_order) do local section = sections[f] if section then + print(string.format(" Rendering section: '%s'", section.title)) local add_sep_and_header = not vim.tbl_contains(cfg.append_only or {}, f) - table.insert(docs, render_section(section, add_sep_and_header)) + docs[#docs + 1] = render_section(section, add_sep_and_header) end end @@ -945,7 +997,7 @@ local function gen_target(cfg) end local function run() - for _, cfg in pairs(config) do + for _, cfg in vim.spairs(config) do gen_target(cfg) end end diff --git a/scripts/git-log-pretty-since.sh b/scripts/git-log-pretty-since.sh deleted file mode 100755 index 95dcee23f5..0000000000 --- a/scripts/git-log-pretty-since.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash - -# Prints a nicely-formatted commit history. -# - Commits are grouped below their merge-commit. -# - Issue numbers are moved next to the commit-id. -# -# Parameters: -# $1 "since" commit -# $2 "inverse match" regex pattern - -set -e -set -u -set -o pipefail - -__SINCE=$1 -__INVMATCH=$2 - -is_merge_commit() { - git rev-parse "$1" >/dev/null 2>&1 \ - || { echo "ERROR: invalid commit: $1"; exit 1; } - git log "$1"^2 >/dev/null 2>&1 && return 0 || return 1 -} - -# Removes parens from issue/ticket/PR numbers. -# -# Example: -# in: 3340e08becbf foo (#9423) -# out: 3340e08becbf foo #9423 -_deparen() { - sed 's/(\(\#[0-9]\{3,\}\))/\1/g' -} - -# Cleans up issue/ticket/PR numbers in the commit descriptions. -# -# Example: -# in: 3340e08becbf foo (#9423) -# out: 3340e08becbf #9423 foo -_format_ticketnums() { - nvim -Es +'g/\v(#[0-9]{3,})/norm! ngEldE0ep' +'%p' | _deparen -} - -for commit in $(git log --format='%H' --first-parent "$__SINCE"..HEAD); do - if is_merge_commit "${commit}" ; then - if [ -z "$__INVMATCH" ] || ! git log --oneline "${commit}^1..${commit}^2" \ - | >/dev/null 2>&1 grep -E "$__INVMATCH" ; then - git log -1 --oneline "${commit}" - git log --format=' %h %s' "${commit}^1..${commit}^2" - fi - else - git log -1 --oneline "${commit}" - fi -done | _format_ticketnums diff --git a/scripts/legacy2luatest.pl b/scripts/legacy2luatest.pl deleted file mode 100755 index 8155353fc7..0000000000 --- a/scripts/legacy2luatest.pl +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/env perl - -use strict; -use warnings; -use 5.010; -use autodie; - -use File::Basename; -use File::Spec::Functions; - -sub read_in_file { - my $in_file = $_[0]; - - # Will contain lines before first STARTTEST - # as Lua comments. - my @description_lines = (); - - # Will contain alternating blocks of lines of textual input - # (text between ENDTEST and EOF/next STARTTEST) and test commands - # (commands between STARTTEST and ENDTEST) as Lua code. - my @test_body_lines = (); - - # Will contain current command block, i.e. lines - # between STARTTEST and ENDTEST. - my @command_lines = (); - - # Will contain current input block, i.e. lines - # between ENDTEST and STARTTEST. - my @input_lines = (); - - open my $in_file_handle, '<', $in_file; - - use constant EMIT_DESCRIPTION => 0; - use constant EMIT_COMMAND => 1; - use constant EMIT_INPUT => 2; - - # Push lines from current input and - # command blocks into @test_body_lines - # in the correct order. - sub end_input { - my $input_lines = $_[0]; - my $command_lines = $_[1]; - my $test_body_lines = $_[2]; - - # If there are input lines, wrap with an `insert` - # command and add before the previous command block. - if (@{$input_lines}) { - my $last_input_line = pop @{$input_lines}; - unshift @{$command_lines}, ''; - unshift @{$command_lines}, $last_input_line . ']=])'; - unshift @{$command_lines}, @{$input_lines}; - unshift @{$command_lines}, "insert([=["; - - @{$input_lines} = (); - } - - # Output remaining command lines. - push @{$test_body_lines}, @{$command_lines}; - @{$command_lines} = (); - } - - sub format_comment { - # Handle empty comments. - if (/^$/) { - return ''; - } - - # Capitalize first character and emit as Lua comment. - my $comment = '-- ' . ucfirst $_; - - # Add trailing dot if not already there. - $comment .= '.' unless $comment =~ /\.$/; - - return $comment; - } - - my %states = ( - # Add test description to @description_lines. - EMIT_DESCRIPTION() => sub { - if (/^STARTTEST/) { - return EMIT_COMMAND; - } - - # If not an empty line, emit as Lua comment. - if (!/^$/) { - # Remove modeline - s/vim:.*set f\w+=vim//g; - # Remove trailing ":" - s/\s*:\s*$//g; - push @description_lines, '-- ' . $_; - } - - return EMIT_DESCRIPTION; - }, - # Add test commands to @command_lines. - EMIT_COMMAND() => sub { - if (/^ENDTEST/) { - return EMIT_INPUT; - } - - # If line starts with ':"', emit a comment. - if (/^:"/) { - # Remove Vim comment prefix. - s/^:"\s*//; - - push @command_lines, format_comment $_; - - return EMIT_COMMAND; - } - - # Extract possible inline comment. - if (/^[^"]*"[^"]*$/) { - # Remove command part and prepended whitespace. - s/^(.*?)\s*"\s*//; - - push @command_lines, format_comment $_; - - # Set implicit variable to command without comment. - $_ = $1; - } - - # Only continue if remaining command is not empty. - if (!/^:?\s*$/) { - # Replace terminal escape characters with <esc>. - s/\e/<esc>/g; - - my $startstr = "'"; - my $endstr = "'"; - - # If line contains single quotes or backslashes, use double - # square brackets to wrap string. - if (/'/ || /\\/) { - # If the line contains a closing square bracket, - # wrap it with [=[...]=]. - if (/\]/) { - $startstr = '[=['; - $endstr = ']=]'; - } else { - $startstr = '[['; - $endstr = ']]'; - } - } - - # Emit 'feed' if not a search ('/') or ex (':') command. - if (!/^\// && !/^:/) { - # If command does not end with <esc>, insert trailing <cr>. - my $command = 'feed(' . $startstr . $_; - $command .= '<cr>' unless /<esc>$/; - $command .= $endstr . ')'; - - push @command_lines, $command; - } else { - # Remove prepending ':'. - s/^://; - push @command_lines, 'execute(' . $startstr . $_ . $endstr . ')'; - } - } - - return EMIT_COMMAND; - }, - # Add input to @input_lines. - EMIT_INPUT() => sub { - if (/^STARTTEST/) { - end_input \@input_lines, \@command_lines, \@test_body_lines; - return EMIT_COMMAND; - } - - # Skip initial lines if they are empty. - if (@input_lines or !/^$/) { - push @input_lines, ' ' . $_; - } - return EMIT_INPUT; - }, - ); - - my $state = EMIT_DESCRIPTION; - - while (<$in_file_handle>) { - # Remove trailing newline character and process line. - chomp; - $state = $states{$state}->($_); - } - - # If not all lines have been processed yet, - # do it now. - end_input \@input_lines, \@command_lines, \@test_body_lines; - - close $in_file_handle; - - return (\@description_lines, \@test_body_lines); -} - -sub read_ok_file { - my $ok_file = $_[0]; - my @assertions = (); - - if (-f $ok_file) { - push @assertions, ''; - push @assertions, "-- Assert buffer contents."; - push @assertions, "expect([=["; - - open my $ok_file_handle, '<', $ok_file; - - while (<$ok_file_handle>) { - # Remove trailing newline character and process line. - chomp; - push @assertions, ' ' . $_; - } - - close $ok_file_handle; - - $assertions[-1] .= "]=])"; - } - - return \@assertions; -} - -my $legacy_testfile = $ARGV[0]; -my $out_dir = $ARGV[1]; - -if ($#ARGV != 1) { - say "Convert a legacy Vim test to a Neovim lua spec."; - say ''; - say "Usage: $0 legacy-testfile output-directory"; - say ''; - say "legacy-testfile: Path to .in or .ok file."; - say "output-directory: Directory where Lua spec will be saved to."; - say ''; - say "Note: Only works reliably for fairly simple tests."; - say " Manual adjustments to generated spec files are required."; - exit 1; -} - -my @legacy_suffixes = ('.in', '.ok'); -my ($base_name, $base_path, $suffix) = fileparse($legacy_testfile, @legacy_suffixes); -my $in_file = catfile($base_path, $base_name . '.in'); -my $ok_file = catfile($base_path, $base_name . '.ok'); - -# Remove leading 'test'. -my $test_name = $base_name; -$test_name =~ s/^test_?//; - -my $spec_file = do { - if ($test_name =~ /^([0-9]+)/) { - catfile($out_dir, sprintf('%03d', $1) . '_spec.lua') - } else { - catfile($out_dir, $test_name . '_spec.lua') - } -}; - -if (! -f $in_file) { - say "Test input file $in_file not found."; - exit 2; -} - -if (! -d $out_dir) { - say "Output directory $out_dir does not exist."; - exit 3; -} - -if (-f $spec_file) { - say "Output file $spec_file already exists."; - print "Overwrite (Y/n)? "; - my $input = <STDIN>; - chomp($input); - unless ($input =~ /^y|Y/) { - say "Aborting."; - exit 4; - } -} - -# Read .in and .ok files. -my ($description_lines, $test_body_lines) = read_in_file $in_file; -my $assertion_lines = read_ok_file $ok_file; - -# Append assertions to test body. -push @{$test_body_lines}, @{$assertion_lines} if @{$assertion_lines}; - -# Write spec file. -open my $spec_file_handle, ">", $spec_file; - -print $spec_file_handle <<"EOS"; -@{[join "\n", @{$description_lines}]} - -local helpers = require('test.functional.helpers') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect - -describe('$test_name', function() - before_each(clear) - - it('is working', function() -@{[join "\n", map { /^$/ ? '' : ' ' . $_ } @{$test_body_lines}]} - end) -end) -EOS - -close $spec_file_handle; - -say "Written to $spec_file." diff --git a/scripts/luacats_grammar.lua b/scripts/luacats_grammar.lua index ca26c70156..29f3bda5aa 100644 --- a/scripts/luacats_grammar.lua +++ b/scripts/luacats_grammar.lua @@ -170,7 +170,7 @@ local grammar = P { ltype = parenOpt(v.ty_union), ty_union = v.ty_opt * rep(Pf('|') * v.ty_opt), - ty = v.ty_fun + ident + v.ty_table + literal + paren(v.ty), + ty = v.ty_fun + ident + v.ty_table + literal + paren(v.ty) + v.ty_generic, ty_param = Pf('<') * comma1(v.ltype) * fill * P('>'), ty_opt = v.ty * opt(v.ty_param) * opt(P('[]')) * opt(P('?')), ty_index = (Pf('[') * (v.ltype + ident + rep1(num)) * fill * P(']')), @@ -179,6 +179,7 @@ local grammar = P { ty_table = Pf('{') * comma1(v.table_elem) * fill * P('}'), fun_param = lname * opt(colon * v.ltype), ty_fun = Pf('fun') * paren(comma(lname * opt(colon * v.ltype))) * opt(colon * comma1(v.ltype)), + ty_generic = P('`') * letter * P('`'), } return grammar --[[@as nvim.luacats.grammar]] diff --git a/scripts/luacats_parser.lua b/scripts/luacats_parser.lua index cd671fb9dc..cb301b32e4 100644 --- a/scripts/luacats_parser.lua +++ b/scripts/luacats_parser.lua @@ -281,8 +281,8 @@ local function filter_decl(line) -- M.fun = vim._memoize(function(...) -- -> -- function M.fun(...) - line = line:gsub('^local (.+) = .*_memoize%([^,]+, function%((.*)%)$', 'local function %1(%2)') - line = line:gsub('^(.+) = .*_memoize%([^,]+, function%((.*)%)$', 'function %1(%2)') + line = line:gsub('^local (.+) = memoize%([^,]+, function%((.*)%)$', 'local function %1(%2)') + line = line:gsub('^(.+) = memoize%([^,]+, function%((.*)%)$', 'function %1(%2)') return line end diff --git a/scripts/movedocs.pl b/scripts/movedocs.pl deleted file mode 100755 index 923c633f13..0000000000 --- a/scripts/movedocs.pl +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -if ($ARGV[0] eq '--help') { - print << "EOF"; -Usage: - - $0 file.h file.c - -Removes documentation attached to function declarations in file.h and adds them -to function definitions found in file.c. - - $0 file.c - -Moves documentation attached to function declaration present in the same file as -the definition. -EOF - exit 0; -} - -my $hfile = shift @ARGV; -my @cfiles = @ARGV; - -my %docs = (); -my $F; - -sub write_lines { - my $file = shift; - my @lines = @_; - - my $F; - - open $F, '>', $file; - print $F (join "", @lines); - close $F; -} - -if (@cfiles) { - open $F, '<', $hfile - or die "Failed to open $hfile."; - - my @hlines = (); - - my $lastdoc = ''; - - while (<$F>) { - if (/^\/\/\/?/) { - $lastdoc .= $_; - } elsif (/^\S.*?(\w+)\(.*(?:,|\);?|FUNC_ATTR_\w+;?)$/) { - die "Documentation for $1 was already defined" if (defined $docs{$1}); - if ($lastdoc ne '') { - $docs{$1} = $lastdoc; - $lastdoc = ''; - } - push @hlines, $_; - } elsif ($lastdoc ne '') { - push @hlines, $lastdoc; - $lastdoc = ''; - push @hlines, $_; - } else { - push @hlines, $_; - } - } - - close $F; - - my %clines_hash = (); - - for my $cfile (@cfiles) { - open $F, '<', $cfile - or die "Failed to open $cfile."; - - my @clines = (); - - while (<$F>) { - if (/^\S.*?(\w+)\(.*[,)]$/ and defined $docs{$1}) { - push @clines, $docs{$1}; - delete $docs{$1}; - } elsif (/^(?!static\s)\S.*?(\w+)\(.*[,)]$/ and not defined $docs{$1}) { - print STDERR "Documentation not defined for $1\n"; - } - push @clines, $_; - } - - close $F; - - $clines_hash{$cfile} = \@clines; - } - - while (my ($func, $value) = each %docs) { - die "Function not found: $func\n"; - } - - write_lines($hfile, @hlines); - while (my ($cfile, $clines) = each %clines_hash) { - write_lines($cfile, @$clines); - } -} else { - open $F, '<', $hfile; - - my @lines; - - my $lastdoc = ''; - my $defstart = ''; - my $funcname; - - sub clear_lastdoc { - if ($lastdoc ne '') { - push @lines, $lastdoc; - $lastdoc = ''; - } - } - - sub record_lastdoc { - my $funcname = shift; - if ($lastdoc ne '') { - $docs{$funcname} = $lastdoc; - $lastdoc = ''; - } - } - - sub add_doc { - my $funcname = shift; - if (defined $docs{$funcname}) { - push @lines, $docs{$funcname}; - delete $docs{$funcname}; - } - } - - sub clear_defstart { - push @lines, $defstart; - $defstart = ''; - } - - while (<$F>) { - if (/\/\*/ .. /\*\// and not /\/\*.*?\*\//) { - push @lines, $_; - } elsif (/^\/\/\/?/) { - $lastdoc .= $_; - } elsif (/^\S.*?(\w+)\(.*(?:,|(\);?))$/) { - if (not $2) { - $defstart .= $_; - $funcname = $1; - } elsif ($2 eq ');') { - record_lastdoc $1; - push @lines, $_; - } elsif ($2 eq ')') { - clear_lastdoc; - add_doc $1; - push @lines, $_; - } - } elsif ($defstart ne '') { - $defstart .= $_; - if (/[{}]/) { - clear_lastdoc; - clear_defstart; - } elsif (/\);$/) { - record_lastdoc $funcname; - clear_defstart; - } elsif (/\)$/) { - clear_lastdoc; - add_doc $funcname; - clear_defstart; - } - } else { - clear_lastdoc; - push @lines, $_; - } - } - - close $F; - - while (my ($func, $value) = each %docs) { - die "Function not found: $func\n"; - } - - write_lines($hfile, @lines); -} diff --git a/scripts/release.sh b/scripts/release.sh index 4321d96f62..257fa127c4 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -31,36 +31,36 @@ __DATE=$(date +'%Y-%m-%d') __LAST_TAG=$(git describe --abbrev=0) [ -z "$__LAST_TAG" ] && { echo 'ERROR: no tag found'; exit 1; } __VERSION_MAJOR=$(grep 'set(NVIM_VERSION_MAJOR' CMakeLists.txt\ - |$__sed 's/.*NVIM_VERSION_MAJOR ([[:digit:]]).*/\1/') + |$__sed 's/.*NVIM_VERSION_MAJOR ([[:digit:]]+).*/\1/') __VERSION_MINOR=$(grep 'set(NVIM_VERSION_MINOR' CMakeLists.txt\ - |$__sed 's/.*NVIM_VERSION_MINOR ([[:digit:]]).*/\1/') + |$__sed 's/.*NVIM_VERSION_MINOR ([[:digit:]]+).*/\1/') __VERSION_PATCH=$(grep 'set(NVIM_VERSION_PATCH' CMakeLists.txt\ - |$__sed 's/.*NVIM_VERSION_PATCH ([[:digit:]]).*/\1/') + |$__sed 's/.*NVIM_VERSION_PATCH ([[:digit:]]+).*/\1/') __VERSION="${__VERSION_MAJOR}.${__VERSION_MINOR}.${__VERSION_PATCH}" __API_LEVEL=$(grep 'set(NVIM_API_LEVEL ' CMakeLists.txt\ - |$__sed 's/.*NVIM_API_LEVEL ([[:digit:]]).*/\1/') + |$__sed 's/.*NVIM_API_LEVEL ([[:digit:]]+).*/\1/') { [ -z "$__VERSION_MAJOR" ] || [ -z "$__VERSION_MINOR" ] || [ -z "$__VERSION_PATCH" ]; } \ && { echo "ERROR: version parse failed: '${__VERSION}'"; exit 1; } __RELEASE_MSG="NVIM v${__VERSION} -FEATURES: - -FIXES: - -CHANGES: - " __BUMP_MSG="version bump" echo "Most recent tag: ${__LAST_TAG}" echo "Release version: ${__VERSION}" +_git_log_pretty() { + git cliff --config scripts/cliff.toml --unreleased || echo 'git cliff failed' +} + _do_release_commit() { $__sed -i.bk 's/(NVIM_VERSION_PRERELEASE) "-dev"/\1 ""/' CMakeLists.txt if grep '(NVIM_API_PRERELEASE true)' CMakeLists.txt > /dev/null; then $__sed -i.bk 's/(NVIM_API_PRERELEASE) true/\1 false/' CMakeLists.txt build/bin/nvim --api-info > "test/functional/fixtures/api_level_$__API_LEVEL.mpack" git add "test/functional/fixtures/api_level_${__API_LEVEL}.mpack" + VIMRUNTIME=./runtime build/bin/nvim -u NONE -l scripts/gen_vimdoc.lua + git add -u -- runtime/doc/ fi $__sed -i.bk 's,(<releases>),\1\ @@ -71,7 +71,7 @@ _do_release_commit() { echo "Building changelog since ${__LAST_TAG}..." git add CMakeLists.txt - (echo "${__RELEASE_MSG}"; ./scripts/git-log-pretty-since.sh "$__LAST_TAG" 'vim-patch:[^[:space:]]') | git commit --edit -F - + (echo "${__RELEASE_MSG}"; _git_log_pretty) | git commit --edit -F - fi git tag --sign -a v"${__VERSION}" -m "NVIM v${__VERSION}" diff --git a/scripts/text_utils.lua b/scripts/text_utils.lua index 937408c546..75b3bfedd5 100644 --- a/scripts/text_utils.lua +++ b/scripts/text_utils.lua @@ -318,7 +318,7 @@ local function align_tags(text_width) --- @param line string --- @return string return function(line) - local tag_pat = '%s+(%*[^ ]+%*)%s*$' + local tag_pat = '%s*(%*.+%*)%s*$' local tags = {} for m in line:gmatch(tag_pat) do table.insert(tags, m) @@ -327,7 +327,9 @@ local function align_tags(text_width) if #tags > 0 then line = line:gsub(tag_pat, '') local tags_str = ' ' .. table.concat(tags, ' ') - local pad = string.rep(' ', text_width - #line - #tags_str) + --- @type integer + local conceal_offset = select(2, tags_str:gsub('%*', '')) - 2 + local pad = string.rep(' ', text_width - #line - #tags_str + conceal_offset) return line .. pad .. tags_str end @@ -352,7 +354,7 @@ function M.md_to_vimdoc(text, start_indent, indent, text_width, is_list) local s = table.concat(lines, '\n') -- Reduce whitespace in code-blocks - s = s:gsub('\n+%s*>([a-z]+)\n?\n', ' >%1\n') + s = s:gsub('\n+%s*>([a-z]+)\n', ' >%1\n') s = s:gsub('\n+%s*>\n?\n', ' >\n') return s diff --git a/scripts/update_terminfo.sh b/scripts/update_terminfo.sh index e12365ba8f..34525dec32 100755 --- a/scripts/update_terminfo.sh +++ b/scripts/update_terminfo.sh @@ -77,7 +77,7 @@ for term in $sorted_terms; do continue fi printf '\n' - infocmp -L -1 -A "$db" "$term" | sed -e '1d' -e 's#^#// #' | tr '\t' ' ' + infocmp -L -x -1 -A "$db" "$term" | sed -e '1d' -e 's#^#// #' | tr '\t' ' ' printf 'static const int8_t %s[] = {\n' "${entries[$term]}" printf ' ' od -v -t d1 < "$path" | cut -c9- | xargs | tr ' ' ',' diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 45dd7f5fee..e8758c064f 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -122,7 +122,7 @@ commit_message() { if [[ "${vim_message}" == "vim-patch:${vim_version}:"* ]]; then printf '%s\n\n%s\n\n%s' "${vim_message}" "${vim_commit_url}" "${vim_coauthors}" else - printf 'vim-patch:%s\n\n%s\n\n%s\n\n%s' "$vim_version" "$vim_message" "$vim_commit_url" "$vim_coauthors" + printf 'vim-patch:%s: %s\n\n%s\n\n%s' "${vim_version:0:7}" "${vim_message}" "${vim_commit_url}" "${vim_coauthors}" fi } diff --git a/scripts/windows.ti b/scripts/windows.ti index 34028b8e00..a4916a9830 100644 --- a/scripts/windows.ti +++ b/scripts/windows.ti @@ -25,7 +25,7 @@ win32con|ANSI emulation for libuv on legacy console, Se=\E[0 q, Ss=\E[%p1%d q, use=cygwin, use=libuv+exkey, -conemu|ANIS X3.64 and Xterm 256 colors for ConEmu with libuv, +conemu|ANSI X3.64 and Xterm 256 colors for ConEmu with libuv, ccc@, mc5i@, xenl@, acsc@, rmacs@, smacs@, blink@, cbt@, cvvis@, cnorm=\E[?25h, cud1=\E[B, dim@, flash@, hts@, initc@, invis@, is2@, kf46@, kf47@, kf48@, kf49@, kf50@, kf51@, kf52@, kf53@, kf54@, @@ -47,7 +47,7 @@ conemu|ANIS X3.64 and Xterm 256 colors for ConEmu with libuv, kUP3@, kUP4@, kUP5@, kUP6@, kUP7@, rmxx@, smxx@, xm@, use=libuv+basekey, use=libuv+exkey, use=xterm+256color, use=xterm-new, -vtpcon|ANIS emulation for console virtual terminal sequence with libuv, +vtpcon|ANSI emulation for console virtual terminal sequence with libuv, ccc@, mc5i@, xenl@, blink@, acsc=jjkkllmmnnqqttuuvvwwxx, cvvis@, cud1=\E[B, dim@, flash@, initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E, diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c index 6af43d8eb7..254355e5a2 100644 --- a/src/cjson/lua_cjson.c +++ b/src/cjson/lua_cjson.c @@ -174,9 +174,9 @@ typedef struct { typedef struct { /* convert null in json objects to lua nil instead of vim.NIL */ - int luanil_object; + bool luanil_object; /* convert null in json arrays to lua nil instead of vim.NIL */ - int luanil_array; + bool luanil_array; } json_options_t; typedef struct { @@ -1453,15 +1453,11 @@ static int json_decode(lua_State *l) luaL_checktype(l, -1, LUA_TTABLE); lua_getfield(l, -1, "object"); - if (!lua_isnil(l, -1)) { - options.luanil_object = true; - } + options.luanil_object = lua_toboolean(l, -1); lua_pop(l, 1); lua_getfield(l, -1, "array"); - if (!lua_isnil(l, -1)) { - options.luanil_array = true; - } + options.luanil_array = lua_toboolean(l, -1); /* Also pop the luanil table */ lua_pop(l, 2); break; diff --git a/src/clint.py b/src/clint.py index 062901b43a..41058469b1 100755 --- a/src/clint.py +++ b/src/clint.py @@ -1689,7 +1689,7 @@ def CheckSpacing(filename, clean_lines, linenum, error): # Look for < that is not surrounded by spaces. This is only # triggered if both sides are missing spaces, even though - # technically should should flag if at least one side is missing a + # technically should flag if at least one side is missing a # space. This is done to avoid some false positives with shifts. match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) if (match and not FindNextMatchingAngleBracket(clean_lines, linenum, @@ -1989,12 +1989,12 @@ def CheckLanguage(filename, clean_lines, linenum, error): match = Search(r'\b(strncpy|STRNCPY)\b', line) if match: error(filename, linenum, 'runtime/printf', 4, - 'Use xstrlcpy or snprintf instead of %s (unless this is from Vim)' + 'Use xstrlcpy, xmemcpyz or snprintf instead of %s (unless this is from Vim)' % match.group(1)) match = Search(r'\b(strcpy)\b', line) if match: error(filename, linenum, 'runtime/printf', 4, - 'Use xstrlcpy or snprintf instead of %s' % match.group(1)) + 'Use xstrlcpy, xmemcpyz or snprintf instead of %s' % match.group(1)) match = Search(r'\b(STRNCAT|strncat|strcat|vim_strcat)\b', line) if match: error(filename, linenum, 'runtime/printf', 4, diff --git a/src/coverity-model.c b/src/coverity-model.c index 4338b75ea2..44953a3de4 100644 --- a/src/coverity-model.c +++ b/src/coverity-model.c @@ -111,3 +111,31 @@ void * xmemdup(const void *data, size_t len) __coverity_writeall__(p); return p; } + +// Teach coverity that lua errors are noreturn + +typedef struct {} lua_State; + +int luaL_typerror(lua_State *L, int narg, const char *tname) +{ + __coverity_panic__(); + return 0; +} + +int luaL_error(lua_State *L, const char *fmt, ...) +{ + __coverity_panic__(); + return 0; +} + +int luaL_argerror(lua_State *L, int numarg, const char *extramsg) +{ + __coverity_panic__(); + return 0; +} + +void *luaL_checkudata(lua_State *L, int ud, const char *tname) +{ + return __coverity_alloc_nosize__() +} + diff --git a/src/klib/kvec.h b/src/klib/kvec.h index a32b35a14c..1b9e6fd9f8 100644 --- a/src/klib/kvec.h +++ b/src/klib/kvec.h @@ -153,6 +153,12 @@ type init_array[INIT_SIZE]; \ } +#define KVI_INITIAL_VALUE(v) { \ + .size = 0, \ + .capacity = ARRAY_SIZE((v).init_array), \ + .items = (v).init_array \ +} + /// Initialize vector with preallocated array /// /// @param[out] v Vector to initialize. @@ -218,6 +224,17 @@ static inline void *_memcpy_free(void *const restrict dest, void *const restrict } \ } while (0) +#define kvi_concat_len(v, data, len) \ + if (len > 0) { \ + kvi_ensure_more_space(v, len); \ + assert((v).items); \ + memcpy((v).items + (v).size, data, sizeof((v).items[0]) * len); \ + (v).size = (v).size + len; \ + } + +#define kvi_concat(v, str) kvi_concat_len(v, str, strlen(str)) +#define kvi_splice(v1, v0) kvi_concat_len(v1, (v0).items, (v0).size) + /// Get location where to store new element to a vector with preallocated array /// /// @param[in,out] v Vector to push to. diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 047b22edcc..937cfaaa31 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -33,7 +33,7 @@ find_package(Libuv 1.28.0 REQUIRED) find_package(Libvterm 0.3.3 REQUIRED) find_package(Lpeg REQUIRED) find_package(Msgpack 1.0.0 REQUIRED) -find_package(Treesitter 0.20.9 REQUIRED) +find_package(Treesitter 0.22.6 REQUIRED) find_package(Unibilium 2.0 REQUIRED) target_link_libraries(main_lib INTERFACE @@ -327,7 +327,7 @@ set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h) set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h) set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) -set(LUA_API_C_BINDINGS ${GENERATED_DIR}/lua_api_c_bindings.generated.c) +set(LUA_API_C_BINDINGS ${GENERATED_DIR}/lua_api_c_bindings.generated.h) set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h) # NVIM_RUNTIME_DIR @@ -450,20 +450,27 @@ endif() #------------------------------------------------------------------------------- get_target_property(prop main_lib INTERFACE_COMPILE_DEFINITIONS) -foreach(gen_cdef ${prop}) - if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS") - list(APPEND gen_cflags "-D${gen_cdef}") - endif() -endforeach() +if(NOT "${prop}" STREQUAL "prop-NOTFOUND") + foreach(gen_cdef ${prop}) + if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS") + list(APPEND gen_cflags "-D${gen_cdef}") + endif() + endforeach() +endif() get_directory_property(targets BUILDSYSTEM_TARGETS) foreach(target ${targets}) get_target_property(prop ${target} INTERFACE_INCLUDE_DIRECTORIES) - foreach(gen_include ${prop}) - list(APPEND gen_cflags "-I${gen_include}") - endforeach() + if(NOT "${prop}" STREQUAL "prop-NOTFOUND") + message(STATUS "${target} props '${prop}'") + foreach(gen_include ${prop}) + list(APPEND gen_cflags "-I${gen_include}") + endforeach() + endif() endforeach() +list(REMOVE_DUPLICATES gen_cflags) + if(APPLE AND CMAKE_OSX_SYSROOT) list(APPEND gen_cflags "-isysroot" "${CMAKE_OSX_SYSROOT}") endif() @@ -498,7 +505,6 @@ set(LUA_GEN_DEPS ${GENERATOR_PRELOAD} $<TARGET_FILE:nlua0>) # NVIM_GENERATED_FOR_HEADERS: generated headers to be included in headers # NVIM_GENERATED_FOR_SOURCES: generated headers to be included in sources -# NVIM_GENERATED_SOURCES: generated source files # These lists must be mutually exclusive. foreach(sfile ${NVIM_SOURCES} ${GENERATED_API_DISPATCH} @@ -613,10 +619,6 @@ add_custom_command( VERBATIM ) -list(APPEND NVIM_GENERATED_SOURCES - "${LUA_API_C_BINDINGS}" -) - add_custom_command( OUTPUT ${GENERATED_UI_EVENTS_CALL} ${GENERATED_UI_EVENTS_REMOTE} @@ -650,10 +652,7 @@ list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_OPTIONS_MAP}" "${GENERATED_UNICODE_TABLES}" "${VIM_MODULE_FILE}" -) - -list(APPEND NVIM_GENERATED_SOURCES - "${PROJECT_BINARY_DIR}/cmake.config/auto/pathdef.c" + "${PROJECT_BINARY_DIR}/cmake.config/auto/pathdef.h" ) add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} @@ -702,7 +701,6 @@ endif() target_sources(main_lib INTERFACE ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} - ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS} ${EXTERNAL_SOURCES} @@ -826,9 +824,12 @@ add_glob_target( FLAGS --quiet EXCLUDE ${EXCLUDE_CLANG_TIDY}) -# These are the same warnings as https://neovim.io/doc/reports/clang/. The -# checks we ignore are meant to be removed eventually, but we can only do so -# after we properly fix the problems without breaking CI. +# The checks we ignore are meant to be removed eventually, but we can only +# enable each warning after we fix all instances of that specific warning as to +# not break CI. +if(APPLE) + string(APPEND CLANG_ANALYZER_IGNORE "-clang-analyzer-core.NonNullParamChecker,") +endif() add_glob_target( TARGET clang-analyzer COMMAND ${CLANG_TIDY_PRG} @@ -837,12 +838,12 @@ add_glob_target( --checks=' -*, clang-analyzer-*, - -clang-analyzer-core.NonNullParamChecker, -clang-analyzer-core.NullDereference, -clang-analyzer-core.UndefinedBinaryOperatorResult, -clang-analyzer-core.uninitialized.Assign, -clang-analyzer-optin.performance.Padding, -clang-analyzer-security.insecureAPI.strcpy, + ${CLANG_ANALYZER_IGNORE} ' EXCLUDE ${EXCLUDE_CLANG_TIDY}) @@ -899,15 +900,6 @@ add_subdirectory(po) add_custom_target(generated-sources DEPENDS ${NVIM_GENERATED_FOR_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} - ${NVIM_GENERATED_SOURCES} -) - -set(VIMDOC_FILES - ${NVIM_RUNTIME_DIR}/doc/api.txt - ${NVIM_RUNTIME_DIR}/doc/diagnostic.txt - ${NVIM_RUNTIME_DIR}/doc/lsp.txt - ${NVIM_RUNTIME_DIR}/doc/lua.txt - ${NVIM_RUNTIME_DIR}/doc/treesitter.txt ) file(GLOB API_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/nvim/api/*.c) @@ -920,23 +912,22 @@ file(GLOB LUA_SOURCES CONFIGURE_DEPENDS ${NVIM_RUNTIME_DIR}/lua/vim/treesitter/*.lua ) -add_custom_command( - OUTPUT ${VIMDOC_FILES} - COMMAND ${CMAKE_COMMAND} -E env "VIMRUNTIME=${NVIM_RUNTIME_DIR}" - $<TARGET_FILE:nvim_bin> -l scripts/gen_vimdoc.lua +add_target(doc-vim + COMMAND $<TARGET_FILE:nvim_bin> -u NONE -l scripts/gen_vimdoc.lua DEPENDS nvim ${API_SOURCES} ${LUA_SOURCES} ${PROJECT_SOURCE_DIR}/scripts/gen_vimdoc.lua - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} -) - -add_custom_command( - OUTPUT ${GEN_EVAL_TOUCH} - COMMAND ${CMAKE_COMMAND} -E touch ${GEN_EVAL_TOUCH} - COMMAND ${CMAKE_COMMAND} -E env "VIMRUNTIME=${NVIM_RUNTIME_DIR}" - $<TARGET_FILE:nvim_bin> -l ${PROJECT_SOURCE_DIR}/scripts/gen_eval_files.lua + ${NVIM_RUNTIME_DIR}/doc/api.txt + ${NVIM_RUNTIME_DIR}/doc/diagnostic.txt + ${NVIM_RUNTIME_DIR}/doc/lsp.txt + ${NVIM_RUNTIME_DIR}/doc/lua.txt + ${NVIM_RUNTIME_DIR}/doc/treesitter.txt + ) + +add_target(doc-eval + COMMAND $<TARGET_FILE:nvim_bin> -u NONE -l ${PROJECT_SOURCE_DIR}/scripts/gen_eval_files.lua DEPENDS nvim ${FUNCS_METADATA} @@ -944,22 +935,14 @@ add_custom_command( ${PROJECT_SOURCE_DIR}/src/nvim/eval.lua ${PROJECT_SOURCE_DIR}/src/nvim/options.lua ${PROJECT_SOURCE_DIR}/src/nvim/vvars.lua - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} -) + ${NVIM_RUNTIME_DIR}/doc/builtin.txt + ) -add_custom_target(doc-eval DEPENDS ${GEN_EVAL_TOUCH}) -add_custom_target(doc-vim DEPENDS ${VIMDOC_FILES}) add_custom_target(doc) add_dependencies(doc doc-vim doc-eval) -set(lintdoc_touch ${TOUCHES_DIR}/lintdoc) -add_custom_command( - OUTPUT ${lintdoc_touch} - COMMAND ${CMAKE_COMMAND} -E touch ${lintdoc_touch} - COMMAND ${CMAKE_COMMAND} -E env "VIMRUNTIME=${NVIM_RUNTIME_DIR}" - $<TARGET_FILE:nvim_bin> --clean -l scripts/lintdoc.lua - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} +add_target(lintdoc + COMMAND $<TARGET_FILE:nvim_bin> -u NONE -l scripts/lintdoc.lua DEPENDS ${DOCFILES} - USES_TERMINAL) -add_custom_target(lintdoc DEPENDS ${lintdoc_touch}) + CUSTOM_COMMAND_ARGS USES_TERMINAL) add_dependencies(lintdoc nvim) diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index d71bcc4bcf..ca8367b7ce 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -381,15 +381,15 @@ cleanup: /// - desc (string) optional: description (for documentation and troubleshooting). /// - callback (function|string) optional: Lua function (or Vimscript function name, if /// string) called when the event(s) is triggered. Lua callback can return a truthy -/// value (not `false` or `nil`) to delete the autocommand. Receives a table argument -/// with these keys: +/// value (not `false` or `nil`) to delete the autocommand. Receives one argument, +/// a table with these keys: [event-args]() /// - id: (number) autocommand id /// - event: (string) name of the triggered event |autocmd-events| /// - group: (number|nil) autocommand group id, if any /// - match: (string) expanded value of [<amatch>] /// - buf: (number) expanded value of [<abuf>] /// - file: (string) expanded value of [<afile>] -/// - data: (any) arbitrary data passed from [nvim_exec_autocmds()] +/// - data: (any) arbitrary data passed from [nvim_exec_autocmds()] [event-data]() /// - command (string) optional: Vim command to execute on event. Cannot be used with /// {callback} /// - once (boolean) optional: defaults to false. Run the autocommand diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 7f195de959..7e64808709 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -39,6 +39,7 @@ #include "nvim/memory_defs.h" #include "nvim/move.h" #include "nvim/ops.h" +#include "nvim/option_vars.h" #include "nvim/pos_defs.h" #include "nvim/state_defs.h" #include "nvim/types_defs.h" @@ -229,20 +230,6 @@ Boolean nvim_buf_detach(uint64_t channel_id, Buffer buffer, Error *err) return true; } -/// @nodoc -void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *err) -{ - buf_T *buf = find_buffer_by_handle(buffer, err); - if (!buf) { - return; - } - if (last < 0) { - last = buf->b_ml.ml_line_count; - } - - redraw_buf_range_later(buf, (linenr_T)first + 1, (linenr_T)last); -} - /// Gets a line-range from the buffer. /// /// Indexing is zero-based, end-exclusive. Negative indices are interpreted @@ -529,18 +516,18 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In // Another call to ml_get_buf() may free the lines, so we make copies char *str_at_start = ml_get_buf(buf, (linenr_T)start_row); - size_t len_at_start = strlen(str_at_start); - str_at_start = arena_memdupz(arena, str_at_start, len_at_start); - start_col = start_col < 0 ? (int64_t)len_at_start + start_col + 1 : start_col; - VALIDATE_RANGE((start_col >= 0 && (size_t)start_col <= len_at_start), "start_col", { + colnr_T len_at_start = ml_get_buf_len(buf, (linenr_T)start_row); + str_at_start = arena_memdupz(arena, str_at_start, (size_t)len_at_start); + start_col = start_col < 0 ? len_at_start + start_col + 1 : start_col; + VALIDATE_RANGE((start_col >= 0 && start_col <= len_at_start), "start_col", { return; }); char *str_at_end = ml_get_buf(buf, (linenr_T)end_row); - size_t len_at_end = strlen(str_at_end); - str_at_end = arena_memdupz(arena, str_at_end, len_at_end); - end_col = end_col < 0 ? (int64_t)len_at_end + end_col + 1 : end_col; - VALIDATE_RANGE((end_col >= 0 && (size_t)end_col <= len_at_end), "end_col", { + colnr_T len_at_end = ml_get_buf_len(buf, (linenr_T)end_row); + str_at_end = arena_memdupz(arena, str_at_end, (size_t)len_at_end); + end_col = end_col < 0 ? len_at_end + end_col + 1 : end_col; + VALIDATE_RANGE((end_col >= 0 && end_col <= len_at_end), "end_col", { return; }); @@ -563,12 +550,10 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In if (start_row == end_row) { old_byte = (bcount_t)end_col - start_col; } else { - old_byte += (bcount_t)len_at_start - start_col; + old_byte += len_at_start - start_col; for (int64_t i = 1; i < end_row - start_row; i++) { int64_t lnum = start_row + i; - - const char *bufline = ml_get_buf(buf, (linenr_T)lnum); - old_byte += (bcount_t)(strlen(bufline)) + 1; + old_byte += ml_get_buf_len(buf, (linenr_T)lnum) + 1; } old_byte += (bcount_t)end_col + 1; } @@ -577,7 +562,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In 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 = len_at_end - (size_t)end_col; + size_t last_part_len = (size_t)len_at_end - (size_t)end_col; if (replacement.size == 1) { firstlen += last_part_len; } @@ -970,7 +955,7 @@ String nvim_buf_get_name(Buffer buffer, Error *err) return cstr_as_string(buf->b_ffname); } -/// Sets the full file name for a buffer +/// Sets the full file name for a buffer, like |:file_f| /// /// @param buffer Buffer handle, or 0 for current buffer /// @param name Buffer name @@ -986,12 +971,23 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err) try_start(); + const bool is_curbuf = buf == curbuf; + const int save_acd = p_acd; + if (!is_curbuf) { + // Temporarily disable 'autochdir' when setting file name for another buffer. + p_acd = false; + } + // Using aucmd_*: autocommands will be executed by rename_buffer aco_save_T aco; aucmd_prepbuf(&aco, buf); int ren_ret = rename_buffer(name.data); aucmd_restbuf(&aco); + if (!is_curbuf) { + p_acd = save_acd; + } + if (try_end(err)) { return; } @@ -1271,10 +1267,13 @@ static void fix_cursor(win_T *win, linenr_T lo, linenr_T hi, linenr_T extra) } else if (extra < 0) { check_cursor_lnum(win); } - check_cursor_col_win(win); + check_cursor_col(win); changed_cline_bef_curs(win); + win->w_valid &= ~(VALID_BOTLINE_AP); + update_topline(win); + } else { + invalidate_botline(win); } - invalidate_botline(win); } /// Fix cursor position after replacing text @@ -1309,7 +1308,7 @@ static void fix_cursor_cols(win_T *win, linenr_T start_row, colnr_T start_col, l // it's easier to work with a single value here. // col and coladd are fixed by a later call - // to check_cursor_col_win when necessary + // to check_cursor_col when necessary win->w_cursor.col += win->w_cursor.coladd; win->w_cursor.coladd = 0; @@ -1324,7 +1323,7 @@ static void fix_cursor_cols(win_T *win, linenr_T start_row, colnr_T start_col, l // it already (in case virtualedit is active) // column might be additionally adjusted below // to keep it inside col range if needed - colnr_T len = (colnr_T)strlen(ml_get_buf(win->w_buffer, new_end_row)); + colnr_T len = ml_get_buf_len(win->w_buffer, new_end_row); if (win->w_cursor.col < len) { win->w_cursor.col = len; } @@ -1345,7 +1344,7 @@ static void fix_cursor_cols(win_T *win, linenr_T start_row, colnr_T start_col, l } } - check_cursor_col_win(win); + check_cursor_col(win); changed_cline_bef_curs(win); invalidate_botline(win); } @@ -1424,6 +1423,7 @@ void buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool for (size_t i = 0; i < n; i++) { linenr_T lnum = start + (linenr_T)i; char *bufstr = ml_get_buf(buf, lnum); - push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl, arena); + size_t bufstrlen = (size_t)ml_get_buf_len(buf, lnum); + push_linestr(lstate, l, bufstr, bufstrlen, start_idx + (int)i, replace_nl, arena); } } diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 6254e9fbd8..af3bfe2c03 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -20,6 +20,9 @@ #include "nvim/lua/executor.h" #include "nvim/memory.h" #include "nvim/memory_defs.h" +#include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/channel_defs.h" +#include "nvim/msgpack_rpc/unpacker.h" #include "nvim/option.h" #include "nvim/option_defs.h" #include "nvim/pos_defs.h" @@ -697,3 +700,109 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope, set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err); }); } + +/// @deprecated Use nvim_exec_lua() instead. +/// +/// Calls many API methods atomically. +/// +/// This has two main usages: +/// 1. To perform several requests from an async context atomically, i.e. +/// without interleaving redraws, RPC requests from other clients, or user +/// interactions (however API methods may trigger autocommands or event +/// processing which have such side effects, e.g. |:sleep| may wake timers). +/// 2. To minimize RPC overhead (roundtrips) of a sequence of many requests. +/// +/// @param channel_id +/// @param calls an array of calls, where each call is described by an array +/// with two elements: the request name, and an array of arguments. +/// @param[out] err Validation error details (malformed `calls` parameter), +/// if any. Errors from batched calls are given in the return value. +/// +/// @return Array of two elements. The first is an array of return +/// values. The second is NIL if all calls succeeded. If a call resulted in +/// an error, it is a three-element array with the zero-based index of the call +/// which resulted in an error, the error type and the error message. If an +/// error occurred, the values from all preceding calls will still be returned. +Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *err) + FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(12) FUNC_API_REMOTE_ONLY +{ + Array rv = arena_array(arena, 2); + Array results = arena_array(arena, calls.size); + Error nested_error = ERROR_INIT; + + size_t i; // also used for freeing the variables + for (i = 0; i < calls.size; i++) { + VALIDATE_T("'calls' item", kObjectTypeArray, calls.items[i].type, { + goto theend; + }); + Array call = calls.items[i].data.array; + VALIDATE_EXP((call.size == 2), "'calls' item", "2-item Array", NULL, { + goto theend; + }); + VALIDATE_T("name", kObjectTypeString, call.items[0].type, { + goto theend; + }); + String name = call.items[0].data.string; + VALIDATE_T("call args", kObjectTypeArray, call.items[1].type, { + goto theend; + }); + Array args = call.items[1].data.array; + + MsgpackRpcRequestHandler handler = + msgpack_rpc_get_handler_for(name.data, + name.size, + &nested_error); + + if (ERROR_SET(&nested_error)) { + break; + } + + Object result = handler.fn(channel_id, args, arena, &nested_error); + if (ERROR_SET(&nested_error)) { + // error handled after loop + break; + } + // TODO(bfredl): wasteful copy. It could be avoided to encoding to msgpack + // directly here. But `result` might become invalid when next api function + // is called in the loop. + ADD_C(results, copy_object(result, arena)); + if (handler.ret_alloc) { + api_free_object(result); + } + } + + ADD_C(rv, ARRAY_OBJ(results)); + if (ERROR_SET(&nested_error)) { + Array errval = arena_array(arena, 3); + ADD_C(errval, INTEGER_OBJ((Integer)i)); + ADD_C(errval, INTEGER_OBJ(nested_error.type)); + ADD_C(errval, STRING_OBJ(copy_string(cstr_as_string(nested_error.msg), arena))); + ADD_C(rv, ARRAY_OBJ(errval)); + } else { + ADD_C(rv, NIL); + } + +theend: + api_clear_error(&nested_error); + return rv; +} + +/// @deprecated +/// +/// @param channel_id Channel id (passed automatically by the dispatcher) +/// @param event Event type string +void nvim_subscribe(uint64_t channel_id, String event) + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY +{ + // Does nothing. `rpcnotify(0,…)` broadcasts to all channels, there are no "subscriptions". +} + +/// @deprecated +/// +/// @param channel_id Channel id (passed automatically by the dispatcher) +/// @param event Event type string +void nvim_unsubscribe(uint64_t channel_id, String event) + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY +{ + // Does nothing. `rpcnotify(0,…)` broadcasts to all channels, there are no "subscriptions". +} diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 1b03a97edb..85cce45560 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -121,7 +121,7 @@ Array virt_text_to_array(VirtText vt, bool hl_name, Arena *arena) Array hl_array = arena_array(arena, i < j ? j - i + 1 : 0); for (; i < j; i++) { int hl_id = kv_A(vt, i).hl_id; - if (hl_id > 0) { + if (hl_id >= 0) { ADD_C(hl_array, hl_group_name(hl_id, hl_name)); } } @@ -131,11 +131,11 @@ Array virt_text_to_array(VirtText vt, bool hl_name, Arena *arena) Array chunk = arena_array(arena, 2); ADD_C(chunk, CSTR_AS_OBJ(text)); if (hl_array.size > 0) { - if (hl_id > 0) { + if (hl_id >= 0) { ADD_C(hl_array, hl_group_name(hl_id, hl_name)); } ADD_C(chunk, ARRAY_OBJ(hl_array)); - } else if (hl_id > 0) { + } else if (hl_id >= 0) { ADD_C(chunk, hl_group_name(hl_id, hl_name)); } ADD_C(chunks, ARRAY_OBJ(chunk)); @@ -489,8 +489,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// used together with virt_text. /// - url: A URL to associate with this extmark. In the TUI, the OSC 8 control /// sequence is used to generate a clickable hyperlink to this URL. -/// - scoped: boolean that indicates that the extmark should only be -/// displayed in the namespace scope. (experimental) +/// - scoped: boolean (EXPERIMENTAL) enables "scoping" for the extmark. See +/// |nvim__win_add_ns()| /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -682,7 +682,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; }); - size_t len = 0; + colnr_T len = 0; if (HAS_KEY(opts, set_extmark, spell)) { hl.flags |= (opts->spell) ? kSHSpellOn : kSHSpellOff; @@ -712,16 +712,16 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer }); line = buf->b_ml.ml_line_count; } else if (line < buf->b_ml.ml_line_count) { - len = opts->ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line + 1)); + len = opts->ephemeral ? MAXCOL : ml_get_buf_len(buf, (linenr_T)line + 1); } if (col == -1) { - col = (Integer)len; - } else if (col > (Integer)len) { + col = len; + } else if (col > len) { VALIDATE_RANGE(!strict, "col", { goto error; }); - col = (Integer)len; + col = len; } else if (col < -1) { VALIDATE_RANGE(false, "col", { goto error; @@ -730,7 +730,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (col2 >= 0) { if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) { - len = opts->ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line2 + 1)); + len = opts->ephemeral ? MAXCOL : ml_get_buf_len(buf, (linenr_T)line2 + 1); } else if (line2 == buf->b_ml.ml_line_count) { // We are trying to add an extmark past final newline len = 0; @@ -738,11 +738,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // reuse len from before line2 = (int)line; } - if (col2 > (Integer)len) { + if (col2 > len) { VALIDATE_RANGE(!strict, "end_col", { goto error; }); - col2 = (int)len; + col2 = len; } } else if (line2 >= 0) { col2 = 0; @@ -761,32 +761,20 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = c; } - DecorPriority subpriority = DECOR_PRIORITY_BASE; - if (HAS_KEY(opts, set_extmark, _subpriority)) { - VALIDATE_RANGE((opts->_subpriority >= 0 && opts->_subpriority <= UINT16_MAX), - "_subpriority", { - goto error; - }); - subpriority = (DecorPriority)opts->_subpriority; - } - if (kv_size(virt_text.data.virt_text)) { - decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true, - subpriority); + decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true); } if (kv_size(virt_lines.data.virt_lines)) { - decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true, - subpriority); + decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true); } if (url != NULL) { DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT; sh.url = url; - decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, 0, 0, subpriority); + decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, 0, 0); } if (has_hl) { DecorSignHighlight sh = decor_sh_from_inline(hl); - decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id, - subpriority); + decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id); } } else { if (opts->ephemeral) { @@ -1177,7 +1165,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width) String str = chunk.items[0].data.string; - int hl_id = 0; + int hl_id = -1; if (chunk.size == 2) { Object hl = chunk.items[1]; if (hl.type == kObjectTypeArray) { @@ -1227,13 +1215,15 @@ String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error return mt_inspect(buf->b_marktree, keys, dot); } -/// Adds the namespace scope to the window. +/// EXPERIMENTAL: this API will change in the future. +/// +/// Scopes a namespace to the a window, so extmarks in the namespace will be active only in the +/// given window. /// /// @param window Window handle, or 0 for current window -/// @param ns_id the namespace to add +/// @param ns_id Namespace /// @return true if the namespace was added, else false -Boolean nvim_win_add_ns(Window window, Integer ns_id, Error *err) - FUNC_API_SINCE(12) +Boolean nvim__win_add_ns(Window window, Integer ns_id, Error *err) { win_T *win = find_window_by_handle(window, err); if (!win) { @@ -1246,17 +1236,20 @@ Boolean nvim_win_add_ns(Window window, Integer ns_id, Error *err) set_put(uint32_t, &win->w_ns_set, (uint32_t)ns_id); - changed_window_setting_win(win); + if (map_has(uint32_t, win->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(win); + } return true; } -/// Gets all the namespaces scopes associated with a window. +/// EXPERIMENTAL: this API will change in the future. +/// +/// Gets the namespace scopes for a given window. /// /// @param window Window handle, or 0 for current window /// @return a list of namespaces ids -ArrayOf(Integer) nvim_win_get_ns(Window window, Arena *arena, Error *err) - FUNC_API_SINCE(12) +ArrayOf(Integer) nvim__win_get_ns(Window window, Arena *arena, Error *err) { win_T *win = find_window_by_handle(window, err); if (!win) { @@ -1272,13 +1265,14 @@ ArrayOf(Integer) nvim_win_get_ns(Window window, Arena *arena, Error *err) return rv; } -/// Removes the namespace scope from the window. +/// EXPERIMENTAL: this API will change in the future. +/// +/// Unscopes a namespace (un-binds it from the given scope). /// /// @param window Window handle, or 0 for current window /// @param ns_id the namespace to remove /// @return true if the namespace was removed, else false -Boolean nvim_win_remove_ns(Window window, Integer ns_id, Error *err) - FUNC_API_SINCE(12) +Boolean nvim__win_del_ns(Window window, Integer ns_id, Error *err) { win_T *win = find_window_by_handle(window, err); if (!win) { @@ -1291,7 +1285,9 @@ Boolean nvim_win_remove_ns(Window window, Integer ns_id, Error *err) set_del(uint32_t, &win->w_ns_set, (uint32_t)ns_id); - changed_window_setting_win(win); + if (map_has(uint32_t, win->w_buffer->b_extmark_ns, (uint32_t)ns_id)) { + changed_window_setting(win); + } return true; } diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h index fe91d9760d..00d8aa8428 100644 --- a/src/nvim/api/keysets_defs.h +++ b/src/nvim/api/keysets_defs.h @@ -56,8 +56,6 @@ typedef struct { Boolean undo_restore; String url; Boolean scoped; - - Integer _subpriority; } Dict(set_extmark); typedef struct { @@ -375,3 +373,17 @@ typedef struct { Boolean ignore_blank_lines; Boolean indent_heuristic; } Dict(xdl_diff); + +typedef struct { + OptionalKeys is_set__redraw_; + Boolean flush; + Boolean cursor; + Boolean valid; + Boolean statuscolumn; + Boolean statusline; + Boolean tabline; + Boolean winbar; + Array range; + Window win; + Buffer buf; +} Dict(redraw); diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index a70ef1e50b..a78d78c057 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -76,8 +76,7 @@ static Object typval_cbuf_to_obj(EncodedData *edata, const char *data, size_t le do { \ ufunc_T *fp = find_func(fun); \ if (fp != NULL && (fp->uf_flags & FC_LUAREF)) { \ - LuaRef ref = api_new_luaref(fp->uf_luaref); \ - kvi_push(edata->stack, LUAREF_OBJ(ref)); \ + kvi_push(edata->stack, LUAREF_OBJ(api_new_luaref(fp->uf_luaref))); \ } else { \ TYPVAL_ENCODE_CONV_NIL(tv); \ } \ diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 1cd98aa0c4..a17e78cc31 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -524,10 +524,10 @@ String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col } char *bufstr = ml_get_buf(buf, (linenr_T)lnum); - size_t line_length = strlen(bufstr); + colnr_T line_length = ml_get_buf_len(buf, (linenr_T)lnum); - start_col = start_col < 0 ? (int64_t)line_length + start_col + 1 : start_col; - end_col = end_col < 0 ? (int64_t)line_length + end_col + 1 : end_col; + start_col = start_col < 0 ? line_length + start_col + 1 : start_col; + end_col = end_col < 0 ? line_length + end_col + 1 : end_col; if (start_col >= MAXCOL || end_col >= MAXCOL) { api_set_error(err, kErrorTypeValidation, "Column index is too high"); @@ -539,7 +539,7 @@ String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col return rv; } - if ((size_t)start_col >= line_length) { + if (start_col >= line_length) { return rv; } diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 040abb1e3f..56a3f1cf23 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -146,7 +146,11 @@ void nvim_tabpage_set_win(Tabpage tabpage, Window win, Error *err) } if (tp == curtab) { - win_enter(wp, true); + try_start(); + win_goto(wp); + if (!try_end(err) && curwin != wp) { + api_set_error(err, kErrorTypeException, "Failed to switch to window %d", win); + } } else if (tp->tp_curwin != wp) { tp->tp_prevwin = tp->tp_curwin; tp->tp_curwin = wp; diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 692e3f95fc..fdf25c75d7 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -67,7 +67,6 @@ static void mpack_str_small(char **buf, const char *str, size_t len) static void remote_ui_destroy(RemoteUI *ui) FUNC_ATTR_NONNULL_ALL { - kv_destroy(ui->call_buf); xfree(ui->packer.startptr); XFREE_CLEAR(ui->term_name); xfree(ui); @@ -190,8 +189,6 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona .anydata = ui, }; ui->wildmenu_active = false; - ui->call_buf = (Array)ARRAY_DICT_INIT; - kv_ensure_space(ui->call_buf, 16); pmap_put(uint64_t)(&connected_uis, channel_id, ui); ui_attach_impl(ui, channel_id); @@ -533,7 +530,8 @@ static void ui_alloc_buf(RemoteUI *ui) static void prepare_call(RemoteUI *ui, const char *name) { - if (ui->packer.startptr && BUF_POS(ui) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + if (ui->packer.startptr + && (BUF_POS(ui) > UI_BUF_SIZE - EVENT_BUF_SIZE || ui->ncells_pending >= 500)) { ui_flush_buf(ui); } @@ -582,7 +580,7 @@ static void ui_flush_callback(PackerBuffer *packer) void remote_ui_grid_clear(RemoteUI *ui, Integer grid) { - Array args = ui->call_buf; + MAXSIZE_TEMP_ARRAY(args, 1); if (ui->ui_ext[kUILinegrid]) { ADD_C(args, INTEGER_OBJ(grid)); } @@ -592,7 +590,7 @@ void remote_ui_grid_clear(RemoteUI *ui, Integer grid) void remote_ui_grid_resize(RemoteUI *ui, Integer grid, Integer width, Integer height) { - Array args = ui->call_buf; + MAXSIZE_TEMP_ARRAY(args, 3); if (ui->ui_ext[kUILinegrid]) { ADD_C(args, INTEGER_OBJ(grid)); } else { @@ -608,7 +606,7 @@ void remote_ui_grid_scroll(RemoteUI *ui, Integer grid, Integer top, Integer bot, Integer right, Integer rows, Integer cols) { if (ui->ui_ext[kUILinegrid]) { - Array args = ui->call_buf; + MAXSIZE_TEMP_ARRAY(args, 7); ADD_C(args, INTEGER_OBJ(grid)); ADD_C(args, INTEGER_OBJ(top)); ADD_C(args, INTEGER_OBJ(bot)); @@ -618,20 +616,19 @@ void remote_ui_grid_scroll(RemoteUI *ui, Integer grid, Integer top, Integer bot, ADD_C(args, INTEGER_OBJ(cols)); push_call(ui, "grid_scroll", args); } else { - Array args = ui->call_buf; + MAXSIZE_TEMP_ARRAY(args, 4); ADD_C(args, INTEGER_OBJ(top)); ADD_C(args, INTEGER_OBJ(bot - 1)); ADD_C(args, INTEGER_OBJ(left)); ADD_C(args, INTEGER_OBJ(right - 1)); push_call(ui, "set_scroll_region", args); - args = ui->call_buf; + kv_size(args) = 0; ADD_C(args, INTEGER_OBJ(rows)); push_call(ui, "scroll", args); - // some clients have "clear" being affected by scroll region, - // so reset it. - args = ui->call_buf; + // some clients have "clear" being affected by scroll region, so reset it. + kv_size(args) = 0; ADD_C(args, INTEGER_OBJ(0)); ADD_C(args, INTEGER_OBJ(ui->height - 1)); ADD_C(args, INTEGER_OBJ(0)); @@ -646,7 +643,7 @@ void remote_ui_default_colors_set(RemoteUI *ui, Integer rgb_fg, Integer rgb_bg, if (!ui->ui_ext[kUITermColors]) { HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp); } - Array args = ui->call_buf; + MAXSIZE_TEMP_ARRAY(args, 5); ADD_C(args, INTEGER_OBJ(rgb_fg)); ADD_C(args, INTEGER_OBJ(rgb_bg)); ADD_C(args, INTEGER_OBJ(rgb_sp)); @@ -656,15 +653,15 @@ void remote_ui_default_colors_set(RemoteUI *ui, Integer rgb_fg, Integer rgb_bg, // Deprecated if (!ui->ui_ext[kUILinegrid]) { - args = ui->call_buf; + kv_size(args) = 0; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); push_call(ui, "update_fg", args); - args = ui->call_buf; + kv_size(args) = 0; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); push_call(ui, "update_bg", args); - args = ui->call_buf; + kv_size(args) = 0; ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); push_call(ui, "update_sp", args); } @@ -677,7 +674,7 @@ void remote_ui_hl_attr_define(RemoteUI *ui, Integer id, HlAttrs rgb_attrs, HlAtt return; } - Array args = ui->call_buf; + MAXSIZE_TEMP_ARRAY(args, 4); ADD_C(args, INTEGER_OBJ(id)); MAXSIZE_TEMP_DICT(rgb, HLATTRS_DICT_SIZE); MAXSIZE_TEMP_DICT(cterm, HLATTRS_DICT_SIZE); @@ -705,14 +702,14 @@ void remote_ui_hl_attr_define(RemoteUI *ui, Integer id, HlAttrs rgb_attrs, HlAtt void remote_ui_highlight_set(RemoteUI *ui, int id) { - Array args = ui->call_buf; - if (ui->hl_id == id) { return; } + ui->hl_id = id; MAXSIZE_TEMP_DICT(dict, HLATTRS_DICT_SIZE); hlattrs2dict(&dict, NULL, syn_attr2entry(id), ui->rgb, false); + MAXSIZE_TEMP_ARRAY(args, 1); ADD_C(args, DICTIONARY_OBJ(dict)); push_call(ui, "highlight_set", args); } @@ -721,7 +718,7 @@ void remote_ui_highlight_set(RemoteUI *ui, int id) void remote_ui_grid_cursor_goto(RemoteUI *ui, Integer grid, Integer row, Integer col) { if (ui->ui_ext[kUILinegrid]) { - Array args = ui->call_buf; + MAXSIZE_TEMP_ARRAY(args, 3); ADD_C(args, INTEGER_OBJ(grid)); ADD_C(args, INTEGER_OBJ(row)); ADD_C(args, INTEGER_OBJ(col)); @@ -741,7 +738,7 @@ void remote_ui_cursor_goto(RemoteUI *ui, Integer row, Integer col) } ui->client_row = row; ui->client_col = col; - Array args = ui->call_buf; + MAXSIZE_TEMP_ARRAY(args, 2); ADD_C(args, INTEGER_OBJ(row)); ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "cursor_goto", args); @@ -750,7 +747,7 @@ void remote_ui_cursor_goto(RemoteUI *ui, Integer row, Integer col) void remote_ui_put(RemoteUI *ui, const char *cell) { ui->client_col++; - Array args = ui->call_buf; + MAXSIZE_TEMP_ARRAY(args, 1); ADD_C(args, CSTR_AS_OBJ(cell)); push_call(ui, "put", args); } @@ -781,11 +778,14 @@ void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startco for (size_t i = 0; i < ncells; i++) { repeat++; if (i == ncells - 1 || attrs[i] != attrs[i + 1] || chunk[i] != chunk[i + 1]) { - if (UI_BUF_SIZE - BUF_POS(ui) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) { + if (UI_BUF_SIZE - BUF_POS(ui) < 2 * (1 + 2 + MAX_SCHAR_SIZE + 5 + 5) + 1 + || ui->ncells_pending >= 500) { // close to overflowing the redraw buffer. finish this event, // flush, and start a new "grid_line" event at the current position. // For simplicity leave place for the final "clear" element // as well, hence the factor of 2 in the check. + // Also if there is a lot of packed cells, pass them of to the UI to + // let it start processing them mpack_w2(&lenpos, nelem); // We only ever set the wrap field on the final "grid_line" event for the line. @@ -831,11 +831,6 @@ void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startco } mpack_w2(&lenpos, nelem); mpack_bool(buf, flags & kLineFlagWrap); - - if (ui->ncells_pending > 500) { - // pass off cells to UI to let it start processing them - ui_flush_buf(ui); - } } else { for (int i = 0; i < endcol - startcol; i++) { remote_ui_cursor_goto(ui, row, startcol + i); @@ -951,12 +946,12 @@ void remote_ui_event(RemoteUI *ui, char *name, Array args) push_call(ui, name, new_args); goto free_ret; } else if (strequal(name, "cmdline_block_show")) { - Array new_args = ui->call_buf; Array block = args.items[0].data.array; Array new_block = arena_array(&arena, block.size); for (size_t i = 0; i < block.size; i++) { ADD_C(new_block, ARRAY_OBJ(translate_contents(ui, block.items[i].data.array, &arena))); } + MAXSIZE_TEMP_ARRAY(new_args, 1); ADD_C(new_args, ARRAY_OBJ(new_block)); push_call(ui, name, new_args); goto free_ret; @@ -973,18 +968,18 @@ void remote_ui_event(RemoteUI *ui, char *name, Array args) ui->wildmenu_active = (args.items[4].data.integer == -1) || !ui->ui_ext[kUIPopupmenu]; if (ui->wildmenu_active) { - Array new_args = ui->call_buf; Array items = args.items[0].data.array; Array new_items = arena_array(&arena, items.size); for (size_t i = 0; i < items.size; i++) { ADD_C(new_items, items.items[i].data.array.items[0]); } + MAXSIZE_TEMP_ARRAY(new_args, 1); ADD_C(new_args, ARRAY_OBJ(new_items)); push_call(ui, "wildmenu_show", new_args); if (args.items[1].data.integer != -1) { - Array new_args2 = ui->call_buf; - ADD_C(new_args2, args.items[1]); - push_call(ui, "wildmenu_select", new_args2); + kv_size(new_args) = 0; + ADD_C(new_args, args.items[1]); + push_call(ui, "wildmenu_select", new_args); } goto free_ret; } diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index c2f02c34f8..2bd8792d71 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -118,6 +118,10 @@ void win_viewport(Integer grid, Window win, Integer topline, Integer botline, In Integer curcol, Integer line_count, Integer scroll_delta) FUNC_API_SINCE(7) FUNC_API_CLIENT_IGNORE; +void win_viewport_margins(Integer grid, Window win, Integer top, Integer bottom, Integer left, + Integer right) + FUNC_API_SINCE(12) FUNC_API_CLIENT_IGNORE; + void win_extmark(Integer grid, Window win, Integer ns_id, Integer mark_id, Integer row, Integer col) FUNC_API_SINCE(10) FUNC_API_REMOTE_ONLY; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 84a2f24dbc..fc780e1248 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -45,10 +45,12 @@ #include "nvim/keycodes.h" #include "nvim/log.h" #include "nvim/lua/executor.h" +#include "nvim/lua/treesitter.h" #include "nvim/macros_defs.h" #include "nvim/mapping.h" #include "nvim/mark.h" #include "nvim/mark_defs.h" +#include "nvim/math.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -167,7 +169,7 @@ Dictionary nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena *arena, E /// @param[out] err Error details, if any /// // TODO(bfredl): val should take update vs reset flag -void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err) +void nvim_set_hl(uint64_t channel_id, Integer ns_id, String name, Dict(highlight) *val, Error *err) FUNC_API_SINCE(7) { int hl_id = syn_check_group(name.data, name.size); @@ -184,7 +186,9 @@ void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err) HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); if (!ERROR_SET(err)) { - ns_hl_def((NS)ns_id, hl_id, attrs, link_id, val); + WITH_SCRIPT_CONTEXT(channel_id, { + ns_hl_def((NS)ns_id, hl_id, attrs, link_id, val); + }); } } @@ -275,6 +279,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) bool typed = false; bool execute = false; bool dangerous = false; + bool lowlevel = false; for (size_t i = 0; i < mode.size; i++) { switch (mode.data[i]) { @@ -290,6 +295,8 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) execute = true; break; case '!': dangerous = true; break; + case 'L': + lowlevel = true; break; } } @@ -305,10 +312,14 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) } else { keys_esc = keys.data; } - ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE), - insert ? 0 : typebuf.tb_len, !typed, false); - if (vgetc_busy) { - typebuf_was_filled = true; + if (lowlevel) { + input_enqueue_raw(cstr_as_string(keys_esc)); + } else { + ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE), + insert ? 0 : typebuf.tb_len, !typed, false); + if (vgetc_busy) { + typebuf_was_filled = true; + } } if (escape_ks) { @@ -876,6 +887,11 @@ void nvim_set_current_buf(Buffer buffer, Error *err) return; } + if (curwin->w_p_wfb) { + api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer); + return; + } + try_start(); int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0); if (!try_end(err) && result == FAIL) { @@ -953,21 +969,21 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) FUNC_API_SINCE(6) { try_start(); + // Block autocommands for now so they don't mess with the buffer before we + // finish configuring it. + block_autocmds(); + buf_T *buf = buflist_new(NULL, NULL, 0, BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0)); - try_end(err); if (buf == NULL) { + unblock_autocmds(); goto fail; } // Open the memline for the buffer. This will avoid spurious autocmds when // a later nvim_buf_set_lines call would have needed to "open" the buffer. - try_start(); - block_autocmds(); - int status = ml_open(buf); - unblock_autocmds(); - try_end(err); - if (status == FAIL) { + if (ml_open(buf) == FAIL) { + unblock_autocmds(); goto fail; } @@ -978,21 +994,39 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) buf->b_last_changedtick_pum = buf_get_changedtick(buf); // Only strictly needed for scratch, but could just as well be consistent - // and do this now. buffer is created NOW, not when it latter first happen + // and do this now. Buffer is created NOW, not when it later first happens // to reach a window or aucmd_prepbuf() .. buf_copy_options(buf, BCO_ENTER | BCO_NOHELP); if (scratch) { - set_string_option_direct_in_buf(buf, kOptBufhidden, "hide", OPT_LOCAL, 0); - set_string_option_direct_in_buf(buf, kOptBuftype, "nofile", OPT_LOCAL, 0); + set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0, kOptReqBuf, + buf); + set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0, kOptReqBuf, + buf); assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already buf->b_p_swf = false; buf->b_p_ml = false; } + + unblock_autocmds(); + + bufref_T bufref; + set_bufref(&bufref, buf); + if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, false, buf) + && !bufref_valid(&bufref)) { + goto fail; + } + if (listed + && apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf) + && !bufref_valid(&bufref)) { + goto fail; + } + + try_end(err); return buf->b_fnum; fail: - if (!ERROR_SET(err)) { + if (!try_end(err)) { api_set_error(err, kErrorTypeException, "Failed to create buffer"); } return 0; @@ -1300,36 +1334,6 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, }); } -/// Subscribes to event broadcasts. -/// -/// @param channel_id Channel id (passed automatically by the dispatcher) -/// @param event Event type string -void nvim_subscribe(uint64_t channel_id, String event) - FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY -{ - size_t length = (event.size < METHOD_MAXLEN ? event.size : METHOD_MAXLEN); - char e[METHOD_MAXLEN + 1]; - memcpy(e, event.data, length); - e[length] = NUL; - rpc_subscribe(channel_id, e); -} - -/// Unsubscribes to event broadcasts. -/// -/// @param channel_id Channel id (passed automatically by the dispatcher) -/// @param event Event type string -void nvim_unsubscribe(uint64_t channel_id, String event) - FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY -{ - size_t length = (event.size < METHOD_MAXLEN - ? event.size - : METHOD_MAXLEN); - char e[METHOD_MAXLEN + 1]; - memcpy(e, event.data, length); - e[length] = NUL; - rpc_unsubscribe(channel_id, e); -} - /// Returns the 24-bit RGB value of a |nvim_get_color_map()| color name or /// "#rrggbb" hexadecimal string. /// @@ -1666,90 +1670,6 @@ Array nvim_list_chans(Arena *arena) return channel_all_info(arena); } -/// Calls many API methods atomically. -/// -/// This has two main usages: -/// 1. To perform several requests from an async context atomically, i.e. -/// without interleaving redraws, RPC requests from other clients, or user -/// interactions (however API methods may trigger autocommands or event -/// processing which have such side effects, e.g. |:sleep| may wake timers). -/// 2. To minimize RPC overhead (roundtrips) of a sequence of many requests. -/// -/// @param channel_id -/// @param calls an array of calls, where each call is described by an array -/// with two elements: the request name, and an array of arguments. -/// @param[out] err Validation error details (malformed `calls` parameter), -/// if any. Errors from batched calls are given in the return value. -/// -/// @return Array of two elements. The first is an array of return -/// values. The second is NIL if all calls succeeded. If a call resulted in -/// an error, it is a three-element array with the zero-based index of the call -/// which resulted in an error, the error type and the error message. If an -/// error occurred, the values from all preceding calls will still be returned. -Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *err) - FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY -{ - Array rv = arena_array(arena, 2); - Array results = arena_array(arena, calls.size); - Error nested_error = ERROR_INIT; - - size_t i; // also used for freeing the variables - for (i = 0; i < calls.size; i++) { - VALIDATE_T("'calls' item", kObjectTypeArray, calls.items[i].type, { - goto theend; - }); - Array call = calls.items[i].data.array; - VALIDATE_EXP((call.size == 2), "'calls' item", "2-item Array", NULL, { - goto theend; - }); - VALIDATE_T("name", kObjectTypeString, call.items[0].type, { - goto theend; - }); - String name = call.items[0].data.string; - VALIDATE_T("call args", kObjectTypeArray, call.items[1].type, { - goto theend; - }); - Array args = call.items[1].data.array; - - MsgpackRpcRequestHandler handler = - msgpack_rpc_get_handler_for(name.data, - name.size, - &nested_error); - - if (ERROR_SET(&nested_error)) { - break; - } - - Object result = handler.fn(channel_id, args, arena, &nested_error); - if (ERROR_SET(&nested_error)) { - // error handled after loop - break; - } - // TODO(bfredl): wasteful copy. It could be avoided to encoding to msgpack - // directly here. But `result` might become invalid when next api function - // is called in the loop. - ADD_C(results, copy_object(result, arena)); - if (handler.ret_alloc) { - api_free_object(result); - } - } - - ADD_C(rv, ARRAY_OBJ(results)); - if (ERROR_SET(&nested_error)) { - Array errval = arena_array(arena, 3); - ADD_C(errval, INTEGER_OBJ((Integer)i)); - ADD_C(errval, INTEGER_OBJ(nested_error.type)); - ADD_C(errval, STRING_OBJ(copy_string(cstr_as_string(nested_error.msg), arena))); - ADD_C(rv, ARRAY_OBJ(errval)); - } else { - ADD_C(rv, NIL); - } - -theend: - api_clear_error(&nested_error); - return rv; -} - /// Writes a message to vim output or error buffer. The string is split /// and flushed after each newline. Incomplete lines are kept for writing /// later. @@ -1858,12 +1778,13 @@ Float nvim__id_float(Float flt) /// @return Map of various internal stats. Dictionary nvim__stats(Arena *arena) { - Dictionary rv = arena_dict(arena, 5); + Dictionary rv = arena_dict(arena, 6); PUT_C(rv, "fsync", INTEGER_OBJ(g_stats.fsync)); PUT_C(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip)); PUT_C(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count())); PUT_C(rv, "redraw", INTEGER_OBJ(g_stats.redraw)); PUT_C(rv, "arena_alloc_count", INTEGER_OBJ((Integer)arena_alloc_count)); + PUT_C(rv, "ts_query_parse_count", INTEGER_OBJ((Integer)tslua_query_parse_count)); return rv; } @@ -2332,20 +2253,18 @@ void nvim_error_event(uint64_t channel_id, Integer lvl, String data) ELOG("async error on channel %" PRId64 ": %s", channel_id, data.size ? data.data : ""); } -/// Set info for the completion candidate index. -/// if the info was shown in a window, then the -/// window and buffer ids are returned for further -/// customization. If the text was not shown, an -/// empty dict is returned. +/// EXPERIMENTAL: this API may change in the future. /// -/// @param index the completion candidate index +/// Sets info for the completion item at the given index. If the info text was shown in a window, +/// returns the window and buffer ids, or empty dict if not shown. +/// +/// @param index Completion candidate index /// @param opts Optional parameters. /// - info: (string) info text. /// @return Dictionary containing these keys: /// - winid: (number) floating window id /// - bufnr: (number) buffer id in floating window -Dictionary nvim_complete_set(Integer index, Dict(complete_set) *opts, Arena *arena) - FUNC_API_SINCE(12) +Dictionary nvim__complete_set(Integer index, Dict(complete_set) *opts, Arena *arena) { Dictionary rv = arena_dict(arena, 2); if (HAS_KEY(opts, complete_set, info)) { @@ -2357,3 +2276,159 @@ Dictionary nvim_complete_set(Integer index, Dict(complete_set) *opts, Arena *are } return rv; } + +static void redraw_status(win_T *wp, Dict(redraw) *opts, bool *flush) +{ + if (opts->statuscolumn && *wp->w_p_stc != NUL) { + wp->w_nrwidth_line_count = 0; + changed_window_setting(wp); + } + win_grid_alloc(wp); + + // Flush later in case winbar was just hidden or shown for the first time, or + // statuscolumn is being drawn. + if (wp->w_lines_valid == 0) { + *flush = true; + } + + // Mark for redraw in case flush will happen, otherwise redraw now. + if (*flush && (opts->statusline || opts->winbar)) { + wp->w_redr_status = true; + } else if (opts->statusline || opts->winbar) { + win_check_ns_hl(wp); + if (opts->winbar) { + win_redr_winbar(wp); + } + if (opts->statusline) { + win_redr_status(wp); + } + win_check_ns_hl(NULL); + } +} + +/// EXPERIMENTAL: this API may change in the future. +/// +/// Instruct Nvim to redraw various components. +/// +/// @see |:redraw| +/// +/// @param opts Optional parameters. +/// - win: Target a specific |window-ID| as described below. +/// - buf: Target a specific buffer number as described below. +/// - flush: Update the screen with pending updates. +/// - valid: When present mark `win`, `buf`, or all windows for +/// redraw. When `true`, only redraw changed lines (useful for +/// decoration providers). When `false`, forcefully redraw. +/// - range: Redraw a range in `buf`, the buffer in `win` or the +/// current buffer (useful for decoration providers). Expects a +/// tuple `[first, last]` with the first and last line number +/// of the range, 0-based end-exclusive |api-indexing|. +/// - cursor: Immediately update cursor position on the screen in +/// `win` or the current window. +/// - statuscolumn: Redraw the 'statuscolumn' in `buf`, `win` or +/// all windows. +/// - statusline: Redraw the 'statusline' in `buf`, `win` or all +/// windows. +/// - winbar: Redraw the 'winbar' in `buf`, `win` or all windows. +/// - tabline: Redraw the 'tabline'. +void nvim__redraw(Dict(redraw) *opts, Error *err) + FUNC_API_SINCE(12) +{ + win_T *win = NULL; + buf_T *buf = NULL; + + if (HAS_KEY(opts, redraw, win)) { + win = find_window_by_handle(opts->win, err); + if (ERROR_SET(err)) { + return; + } + } + + if (HAS_KEY(opts, redraw, buf)) { + VALIDATE(win == NULL, "%s", "cannot use both 'buf' and 'win'", { + return; + }); + buf = find_buffer_by_handle(opts->buf, err); + if (ERROR_SET(err)) { + return; + } + } + + int count = (win != NULL) + (buf != NULL); + VALIDATE(popcount(opts->is_set__redraw_) > count, "%s", "at least one action required", { + return; + }); + + if (HAS_KEY(opts, redraw, valid)) { + // UPD_VALID redraw type does not actually do anything on it's own. Setting + // it here without scrolling or changing buffer text seems pointless but + // the expectation is that this may be called by decoration providers whose + // "on_win" callback may set "w_redr_top/bot". + int type = opts->valid ? UPD_VALID : UPD_NOT_VALID; + if (win != NULL) { + redraw_later(win, type); + } else if (buf != NULL) { + redraw_buf_later(buf, type); + } else { + redraw_all_later(type); + } + } + + if (HAS_KEY(opts, redraw, range)) { + VALIDATE(kv_size(opts->range) == 2 + && kv_A(opts->range, 0).type == kObjectTypeInteger + && kv_A(opts->range, 1).type == kObjectTypeInteger + && kv_A(opts->range, 0).data.integer >= 0 + && kv_A(opts->range, 1).data.integer >= -1, + "%s", "Invalid 'range': Expected 2-tuple of Integers", { + return; + }); + linenr_T first = (linenr_T)kv_A(opts->range, 0).data.integer + 1; + linenr_T last = (linenr_T)kv_A(opts->range, 1).data.integer; + buf_T *rbuf = win ? win->w_buffer : (buf ? buf : curbuf); + if (last == -1) { + last = rbuf->b_ml.ml_line_count; + } + redraw_buf_range_later(rbuf, first, last); + } + + if (opts->cursor) { + setcursor_mayforce(win ? win : curwin, true); + } + + bool flush = opts->flush; + if (opts->tabline) { + // Flush later in case tabline was just hidden or shown for the first time. + if (redraw_tabline && firstwin->w_lines_valid == 0) { + flush = true; + } else { + draw_tabline(); + } + } + + bool save_lz = p_lz; + int save_rd = RedrawingDisabled; + RedrawingDisabled = 0; + p_lz = false; + if (opts->statuscolumn || opts->statusline || opts->winbar) { + if (win == NULL) { + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (buf == NULL || wp->w_buffer == buf) { + redraw_status(wp, opts, &flush); + } + } + } else { + redraw_status(win, opts, &flush); + } + } + + // Flush pending screen updates if "flush" or "clear" is true, or when + // redrawing a status component may have changed the grid dimensions. + if (flush && !cmdpreview) { + update_screen(); + } + ui_flush(); + + RedrawingDisabled = save_rd; + p_lz = save_lz; +} diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 543c7b8113..3a9986a7d1 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -12,6 +12,7 @@ #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/autocmd_defs.h" +#include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/decoration.h" #include "nvim/decoration_defs.h" @@ -198,9 +199,8 @@ /// - footer_pos: Footer position. Must be set with `footer` option. /// Value can be one of "left", "center", or "right". /// Default is `"left"`. -/// - noautocmd: If true then no buffer-related autocommand events such as -/// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from -/// calling this function. +/// - noautocmd: If true then all autocommands are blocked for the duration of +/// the call. /// - fixed: If true when anchor is NW or SW, the float window /// would be kept fixed even if the window would be truncated. /// - hide: If true the floating window will be hidden. @@ -224,25 +224,33 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err } WinConfig fconfig = WIN_CONFIG_INIT; - if (!parse_float_config(config, &fconfig, false, true, err)) { + if (!parse_win_config(NULL, config, &fconfig, false, err)) { return 0; } bool is_split = HAS_KEY_X(config, split) || HAS_KEY_X(config, vertical); + Window rv = 0; + if (fconfig.noautocmd) { + block_autocmds(); + } win_T *wp = NULL; tabpage_T *tp = curtab; + win_T *parent = NULL; + if (config->win != -1) { + parent = find_window_by_handle(fconfig.window, err); + if (!parent) { + // find_window_by_handle has already set the error + goto cleanup; + } else if (is_split && parent->w_floating) { + api_set_error(err, kErrorTypeException, "Cannot split a floating window"); + goto cleanup; + } + tp = win_find_tabpage(parent); + } if (is_split) { - win_T *parent = NULL; - if (config->win != -1) { - parent = find_window_by_handle(fconfig.window, err); - if (!parent) { - // find_window_by_handle has already set the error - return 0; - } else if (parent->w_floating) { - api_set_error(err, kErrorTypeException, "Cannot split a floating window"); - return 0; - } + if (!check_split_disallowed_err(parent ? parent : curwin, err)) { + goto cleanup; // error already set } if (HAS_KEY_X(config, vertical) && !HAS_KEY_X(config, split)) { @@ -254,18 +262,20 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err } int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER; - if (parent == NULL) { - wp = win_split_ins(0, flags, NULL, 0); - } else { - tp = win_find_tabpage(parent); - switchwin_T switchwin; - // `parent` is valid in `tp`, so switch_win should not fail. - const int result = switch_win(&switchwin, parent, tp, true); - (void)result; - assert(result == OK); - wp = win_split_ins(0, flags, NULL, 0); - restore_win(&switchwin, true); - } + TRY_WRAP(err, { + int size = (flags & WSP_VERT) ? fconfig.width : fconfig.height; + if (parent == NULL || parent == curwin) { + wp = win_split_ins(size, flags, NULL, 0, NULL); + } else { + switchwin_T switchwin; + // `parent` is valid in `tp`, so switch_win should not fail. + const int result = switch_win(&switchwin, parent, tp, true); + assert(result == OK); + (void)result; + wp = win_split_ins(size, flags, NULL, 0, NULL); + restore_win(&switchwin, true); + } + }); if (wp) { wp->w_config = fconfig; } @@ -273,30 +283,64 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err wp = win_new_float(NULL, false, fconfig, err); } if (!wp) { - api_set_error(err, kErrorTypeException, "Failed to create window"); - return 0; + if (!ERROR_SET(err)) { + api_set_error(err, kErrorTypeException, "Failed to create window"); + } + goto cleanup; } - switchwin_T switchwin; - if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { - apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); + + // Autocommands may close `wp` or move it to another tabpage, so update and check `tp` after each + // event. In each case, `wp` should already be valid in `tp`, so switch_win should not fail. + // Also, autocommands may free the `buf` to switch to, so store a bufref to check. + bufref_T bufref; + set_bufref(&bufref, buf); + if (!fconfig.noautocmd) { + switchwin_T switchwin; + const int result = switch_win_noblock(&switchwin, wp, tp, true); + assert(result == OK); + (void)result; + if (apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf)) { + tp = win_find_tabpage(wp); + } + restore_win_noblock(&switchwin, true); } - restore_win_noblock(&switchwin, true); - if (enter) { + if (tp && enter) { goto_tabpage_win(tp, wp); + tp = win_find_tabpage(wp); } - if (win_valid_any_tab(wp) && buf != wp->w_buffer) { - win_set_buf(wp, buf, !enter || fconfig.noautocmd, err); + if (tp && bufref_valid(&bufref) && buf != wp->w_buffer) { + // win_set_buf temporarily makes `wp` the curwin to set the buffer. + // If not entering `wp`, block Enter and Leave events. (cringe) + const bool au_no_enter_leave = curwin != wp && !fconfig.noautocmd; + if (au_no_enter_leave) { + autocmd_no_enter++; + autocmd_no_leave++; + } + win_set_buf(wp, buf, err); + if (!fconfig.noautocmd) { + tp = win_find_tabpage(wp); + } + if (au_no_enter_leave) { + autocmd_no_enter--; + autocmd_no_leave--; + } } - if (!win_valid_any_tab(wp)) { + if (!tp) { api_set_error(err, kErrorTypeException, "Window was closed immediately"); - return 0; + goto cleanup; } if (fconfig.style == kWinStyleMinimal) { win_set_minimal_style(wp); didset_window_options(wp, true); } - return wp->handle; + rv = wp->handle; + +cleanup: + if (fconfig.noautocmd) { + unblock_autocmds(); + } + return rv; #undef HAS_KEY_X } @@ -330,11 +374,11 @@ static int win_split_flags(WinSplit split, bool toplevel) return flags; } -/// Configures window layout. Currently only for floating and external windows -/// (including changing a split window to those layouts). +/// Configures window layout. Cannot be used to move the last window in a +/// tabpage to a different one. /// -/// When reconfiguring a floating window, absent option keys will not be -/// changed. `row`/`col` and `relative` must be reconfigured together. +/// When reconfiguring a window, absent option keys will not be changed. +/// `row`/`col` and `relative` must be reconfigured together. /// /// @see |nvim_open_win()| /// @@ -350,6 +394,7 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) if (!win) { return; } + tabpage_T *win_tp = win_find_tabpage(win); bool was_split = !win->w_floating; bool has_split = HAS_KEY_X(config, split); @@ -361,26 +406,31 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) && !(HAS_KEY_X(config, external) ? config->external : fconfig.external) && (has_split || has_vertical || was_split); - if (!parse_float_config(config, &fconfig, !was_split || to_split, false, err)) { + if (!parse_win_config(win, config, &fconfig, !was_split || to_split, err)) { return; } + win_T *parent = NULL; + if (config->win != -1) { + parent = find_window_by_handle(fconfig.window, err); + if (!parent) { + return; + } else if (to_split && parent->w_floating) { + api_set_error(err, kErrorTypeException, "Cannot split a floating window"); + return; + } + + // Prevent autocmd window from being moved into another tabpage + if (is_aucmd_win(win) && win_find_tabpage(win) != win_find_tabpage(parent)) { + api_set_error(err, kErrorTypeException, "Cannot move autocmd win to another tabpage"); + return; + } + } if (was_split && !to_split) { if (!win_new_float(win, false, fconfig, err)) { return; } redraw_later(win, UPD_NOT_VALID); } else if (to_split) { - win_T *parent = NULL; - if (config->win != -1) { - parent = find_window_by_handle(fconfig.window, err); - if (!parent) { - return; - } else if (parent->w_floating) { - api_set_error(err, kErrorTypeException, "Cannot split a floating window"); - return; - } - } - WinSplit old_split = win_split_dir(win); if (has_vertical && !has_split) { if (config->vertical) { @@ -413,17 +463,59 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) return; } - if (was_split) { - win_T *new_curwin = NULL; + if (!check_split_disallowed_err(win, err)) { + return; // error already set + } + // Can't move the cmdwin or its old curwin to a different tabpage. + if ((win == cmdwin_win || win == cmdwin_old_curwin) && parent != NULL + && win_find_tabpage(parent) != win_tp) { + api_set_error(err, kErrorTypeException, "%s", e_cmdwin); + return; + } + + bool to_split_ok = false; + // If we are moving curwin to another tabpage, switch windows *before* we remove it from the + // window list or remove its frame (if non-floating), so it's valid for autocommands. + const bool curwin_moving_tp + = win == curwin && parent != NULL && win_tp != win_find_tabpage(parent); + if (curwin_moving_tp) { + if (was_split) { + int dir; + win_goto(winframe_find_altwin(win, &dir, NULL, NULL)); + } else { + win_goto(win_float_find_altwin(win, NULL)); + } + + // Autocommands may have been a real nuisance and messed things up... + if (curwin == win) { + api_set_error(err, kErrorTypeException, "Failed to switch away from window %d", + win->handle); + return; + } + win_tp = win_find_tabpage(win); + if (!win_tp || !win_valid_any_tab(parent)) { + api_set_error(err, kErrorTypeException, "Windows to split were closed"); + goto restore_curwin; + } + if (was_split == win->w_floating || parent->w_floating) { + api_set_error(err, kErrorTypeException, "Floating state of windows to split changed"); + goto restore_curwin; + } + } + int dir = 0; + frame_T *unflat_altfr = NULL; + win_T *altwin = NULL; + + if (was_split) { // If the window is the last in the tabpage or `fconfig.win` is // a handle to itself, we can't split it. if (win->w_frame->fr_parent == NULL) { // FIXME(willothy): if the window is the last in the tabpage but there is another tabpage // and the target window is in that other tabpage, should we move the window to that // tabpage and close the previous one, or just error? - api_set_error(err, kErrorTypeValidation, "Cannot move last window"); - return; + api_set_error(err, kErrorTypeException, "Cannot move last window"); + goto restore_curwin; } else if (parent != NULL && parent->handle == win->handle) { int n_frames = 0; for (frame_T *fr = win->w_frame->fr_parent->fr_child; fr != NULL; fr = fr->fr_next) { @@ -459,83 +551,82 @@ void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err) } // If the frame doesn't have a parent, the old frame // was the root frame and we need to create a top-level split. - int dir; - new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); + altwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp, &unflat_altfr); } else if (n_frames == 2) { // There are two windows in the frame, we can just rotate it. - int dir; - neighbor = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); - new_curwin = neighbor; + altwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp, &unflat_altfr); + neighbor = altwin; } else { // There is only one window in the frame, we can't split it. - api_set_error(err, kErrorTypeValidation, "Cannot split window into itself"); - return; + api_set_error(err, kErrorTypeException, "Cannot split window into itself"); + goto restore_curwin; } - // Set the parent to whatever the correct - // neighbor window was determined to be. + // Set the parent to whatever the correct neighbor window was determined to be. parent = neighbor; } else { - int dir; - new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp); - } - // move to neighboring window if we're moving the current window to a new tabpage - if (curwin == win && parent != NULL && new_curwin != NULL - && win_tp != win_find_tabpage(parent)) { - win_enter(new_curwin, true); + altwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp, &unflat_altfr); } - win_remove(win, win_tp == curtab ? NULL : win_tp); } else { - win_remove(win, win_tp == curtab ? NULL : win_tp); - ui_comp_remove_grid(&win->w_grid_alloc); - if (win->w_config.external) { - for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - if (tp == curtab) { - continue; - } - if (tp->tp_curwin == win) { - tp->tp_curwin = tp->tp_firstwin; - } - } - } - win->w_pos_changed = true; + altwin = win_float_find_altwin(win, win_tp == curtab ? NULL : win_tp); } - int flags = win_split_flags(fconfig.split, parent == NULL); + win_remove(win, win_tp == curtab ? NULL : win_tp); + if (win_tp == curtab) { + last_status(false); // may need to remove last status line + win_comp_pos(); // recompute window positions + } - if (parent == NULL) { - if (!win_split_ins(0, flags, win, 0)) { - // TODO(willothy): What should this error message say? - api_set_error(err, kErrorTypeException, "Failed to split window"); - return; - } - } else { - win_execute_T args; + int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER; + tabpage_T *const parent_tp = parent ? win_find_tabpage(parent) : curtab; - tabpage_T *tp = win_find_tabpage(parent); - if (!win_execute_before(&args, parent, tp)) { - // TODO(willothy): how should we handle this / what should the message be? - api_set_error(err, kErrorTypeException, "Failed to switch to tabpage %d", tp->handle); - win_execute_after(&args); - return; + TRY_WRAP(err, { + const bool need_switch = parent != NULL && parent != curwin; + switchwin_T switchwin; + if (need_switch) { + // `parent` is valid in its tabpage, so switch_win should not fail. + const int result = switch_win(&switchwin, parent, parent_tp, true); + (void)result; + assert(result == OK); } - // This should return the same ptr to `win`, but we check for - // NULL to detect errors. - win_T *res = win_split_ins(0, flags, win, 0); - win_execute_after(&args); - if (!res) { - // TODO(willothy): What should this error message say? - api_set_error(err, kErrorTypeException, "Failed to split window"); - return; + to_split_ok = win_split_ins(0, flags, win, 0, unflat_altfr) != NULL; + if (!to_split_ok) { + // Restore `win` to the window list now, so it's valid for restore_win (if used). + win_append(win->w_prev, win, win_tp == curtab ? NULL : win_tp); + } + if (need_switch) { + restore_win(&switchwin, true); + } + }); + if (!to_split_ok) { + if (was_split) { + // win_split_ins doesn't change sizes or layout if it fails to insert an existing window, so + // just undo winframe_remove. + winframe_restore(win, dir, unflat_altfr); + } + if (!ERROR_SET(err)) { + api_set_error(err, kErrorTypeException, "Failed to move window %d into split", win->handle); + } + +restore_curwin: + // If `win` was the original curwin, and autocommands didn't move it outside of curtab, be a + // good citizen and try to return to it. + if (curwin_moving_tp && win_valid(win)) { + win_goto(win); } + return; + } + + // If `win` moved tabpages and was the curwin of its old one, select a new curwin for it. + if (win_tp != parent_tp && win_tp->tp_curwin == win) { + win_tp->tp_curwin = altwin; } + if (HAS_KEY_X(config, width)) { win_setwidth_win(fconfig.width, win); } if (HAS_KEY_X(config, height)) { win_setheight_win(fconfig.height, win); } - redraw_later(win, UPD_NOT_VALID); - return; } else { win_config_float(win, fconfig); win->w_pos_changed = true; @@ -947,8 +1038,19 @@ static void parse_border_style(Object style, WinConfig *fconfig, Error *err) } } -static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, bool reconf, - bool new_win, Error *err) +static void generate_api_error(win_T *wp, const char *attribute, Error *err) +{ + if (wp->w_floating) { + api_set_error(err, kErrorTypeValidation, + "Missing 'relative' field when reconfiguring floating window %d", + wp->handle); + } else { + api_set_error(err, kErrorTypeValidation, "non-float cannot have '%s'", attribute); + } +} + +static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fconfig, bool reconf, + Error *err) { #define HAS_KEY_X(d, key) HAS_KEY(d, win_config, key) bool has_relative = false, relative_is_win = false, is_split = false; @@ -973,7 +1075,7 @@ static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, boo } else if (!config->external) { if (HAS_KEY_X(config, vertical) || HAS_KEY_X(config, split)) { is_split = true; - } else if (new_win) { + } else if (wp == NULL) { // new win api_set_error(err, kErrorTypeValidation, "Must specify 'relative' or 'external' when creating a float"); return false; @@ -1007,7 +1109,7 @@ static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, boo if (HAS_KEY_X(config, row)) { if (!has_relative || is_split) { - api_set_error(err, kErrorTypeValidation, "non-float cannot have 'row'"); + generate_api_error(wp, "row", err); return false; } fconfig->row = config->row; @@ -1015,7 +1117,7 @@ static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, boo if (HAS_KEY_X(config, col)) { if (!has_relative || is_split) { - api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'"); + generate_api_error(wp, "col", err); return false; } fconfig->col = config->col; @@ -1023,7 +1125,7 @@ static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, boo if (HAS_KEY_X(config, bufpos)) { if (!has_relative || is_split) { - api_set_error(err, kErrorTypeValidation, "non-float cannot have 'bufpos'"); + generate_api_error(wp, "bufpos", err); return false; } else { if (!parse_float_bufpos(config->bufpos, &fconfig->bufpos)) { @@ -1065,17 +1167,32 @@ static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, boo } if (relative_is_win || is_split) { + if (reconf && relative_is_win) { + win_T *target_win = find_window_by_handle(config->win, err); + if (!target_win) { + return false; + } + + if (target_win == wp) { + api_set_error(err, kErrorTypeException, "floating window cannot be relative to itself"); + return false; + } + } fconfig->window = curwin->handle; if (HAS_KEY_X(config, win)) { if (config->win > 0) { fconfig->window = config->win; } } - } else if (has_relative) { - if (HAS_KEY_X(config, win)) { + } else if (HAS_KEY_X(config, win)) { + if (has_relative) { api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win' and relative=''"); return false; + } else if (!is_split) { + api_set_error(err, kErrorTypeValidation, + "non-float with 'win' requires at least 'split' or 'vertical'"); + return false; } } @@ -1186,8 +1303,8 @@ static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, boo } if (HAS_KEY_X(config, noautocmd)) { - if (!new_win) { - api_set_error(err, kErrorTypeValidation, "Invalid key: 'noautocmd'"); + if (wp) { + api_set_error(err, kErrorTypeValidation, "'noautocmd' cannot be used with existing windows"); return false; } fconfig->noautocmd = config->noautocmd; diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index ed51eedf1b..54a19513db 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -61,11 +61,17 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err) if (!win || !buf) { return; } + + if (win->w_p_wfb) { + api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer); + return; + } + if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) { api_set_error(err, kErrorTypeException, "%s", e_cmdwin); return; } - win_set_buf(win, buf, false, err); + win_set_buf(win, buf, err); } /// Gets the (1,0)-indexed, buffer-relative cursor position for a given window @@ -132,7 +138,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) win->w_cursor.col = (colnr_T)col; win->w_cursor.coladd = 0; // When column is out of range silently correct it. - check_cursor_col_win(win); + check_cursor_col(win); // Make sure we stick in this column. win->w_set_curswant = true; @@ -142,7 +148,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) switchwin_T switchwin; switch_win(&switchwin, win, NULL, true); update_topline(curwin); - validate_cursor(); + validate_cursor(curwin); restore_win(&switchwin, true); redraw_later(win, UPD_VALID); diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c index a02c22deae..4d493c9d03 100644 --- a/src/nvim/arglist.c +++ b/src/nvim/arglist.c @@ -623,6 +623,8 @@ void ex_argument(exarg_T *eap) /// Edit file "argn" of the argument lists. void do_argfile(exarg_T *eap, int argn) { + bool is_split_cmd = *eap->cmd == 's'; + int old_arg_idx = curwin->w_arg_idx; if (argn < 0 || argn >= ARGCOUNT) { @@ -637,10 +639,16 @@ void do_argfile(exarg_T *eap, int argn) return; } + if (!is_split_cmd + && (&ARGLIST[argn])->ae_fnum != curbuf->b_fnum + && !check_can_set_curbuf_forceit(eap->forceit)) { + return; + } + setpcmark(); // split window or create new tab page first - if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) { + if (is_split_cmd || cmdmod.cmod_tab != 0) { if (win_split(0, 0) == FAIL) { return; } diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index ca438e87b4..94c5080f8d 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -88,6 +88,7 @@ return { 'SafeState', -- going to wait for a character 'SearchWrapped', -- after the search wrapped around 'SessionLoadPost', -- after loading a session file + 'SessionWritePost', -- after writing a session file 'ShellCmdPost', -- after ":!cmd" 'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd". 'Signal', -- after nvim process received a signal diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 3f93906942..c5d81d4cd2 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -712,7 +712,7 @@ char *au_event_disable(char *what) } else { STRCAT(new_ei, what); } - set_string_option_direct(kOptEventignore, new_ei, 0, SID_NONE); + set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(new_ei), 0, SID_NONE); xfree(new_ei); return save_ei; } @@ -720,7 +720,7 @@ char *au_event_disable(char *what) void au_event_restore(char *old_ei) { if (old_ei != NULL) { - set_string_option_direct(kOptEventignore, old_ei, 0, SID_NONE); + set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(old_ei), 0, SID_NONE); xfree(old_ei); } } @@ -1325,20 +1325,22 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) buf->b_nwindows++; win_init_empty(auc_win); // set cursor and topline to safe values - // Make sure w_localdir and globaldir are NULL to avoid a chdir() in - // win_enter_ext(). + // Make sure w_localdir, tp_localdir and globaldir are NULL to avoid a + // chdir() in win_enter_ext(). XFREE_CLEAR(auc_win->w_localdir); + aco->tp_localdir = curtab->tp_localdir; + curtab->tp_localdir = NULL; aco->globaldir = globaldir; globaldir = NULL; block_autocmds(); // We don't want BufEnter/WinEnter autocommands. if (need_append) { - win_append(lastwin, auc_win); + win_append(lastwin, auc_win, NULL); pmap_put(int)(&window_handles, auc_win->handle, auc_win); win_config_float(auc_win, auc_win->w_config); } // Prevent chdir() call in win_enter_ext(), through do_autochdir() - int save_acd = p_acd; + const int save_acd = p_acd; p_acd = false; // no redrawing and don't set the window title RedrawingDisabled++; @@ -1427,12 +1429,19 @@ win_found: vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab + // If :lcd has been used in the autocommand window, correct current + // directory before restoring tp_localdir and globaldir. + if (awp->w_localdir != NULL) { + win_fix_current_dir(); + } + xfree(curtab->tp_localdir); + curtab->tp_localdir = aco->tp_localdir; xfree(globaldir); globaldir = aco->globaldir; // the buffer contents may have changed VIsual_active = aco->save_VIsual_active; - check_cursor(); + check_cursor(curwin); if (curwin->w_topline > curbuf->b_ml.ml_line_count) { curwin->w_topline = curbuf->b_ml.ml_line_count; curwin->w_topfill = 0; @@ -1464,12 +1473,12 @@ win_found: // In case the autocommand moves the cursor to a position that does not // exist in curbuf VIsual_active = aco->save_VIsual_active; - check_cursor(); + check_cursor(curwin); } } VIsual_active = aco->save_VIsual_active; - check_cursor(); // just in case lines got deleted + check_cursor(curwin); // just in case lines got deleted if (VIsual_active) { check_pos(curbuf, &VIsual); } @@ -1750,7 +1759,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force saveRedobuff(&save_redo); did_save_redobuff = true; } - did_filetype = keep_filetype; + curbuf->b_did_filetype = curbuf->b_keep_filetype; } // Note that we are applying autocmds. Some commands need to know. @@ -1760,7 +1769,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force // Remember that FileType was triggered. Used for did_filetype(). if (event == EVENT_FILETYPE) { - did_filetype = true; + curbuf->b_did_filetype = true; } char *tail = path_tail(fname); @@ -1864,7 +1873,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force if (did_save_redobuff) { restoreRedobuff(&save_redo); } - did_filetype = false; + curbuf->b_did_filetype = false; while (au_pending_free_buf != NULL) { buf_T *b = au_pending_free_buf->b_next; @@ -1901,7 +1910,7 @@ BYPASS_AU: } if (retval == OK && event == EVENT_FILETYPE) { - au_did_filetype = true; + curbuf->b_au_did_filetype = true; } return retval; @@ -2645,7 +2654,7 @@ void do_filetype_autocmd(buf_T *buf, bool force) secure = 0; ft_recursive++; - did_filetype = true; + buf->b_did_filetype = true; // Only pass true for "force" when it is true or // used recursively, to avoid endless recurrence. apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, force || ft_recursive == 1, buf); diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index 8019cb7145..b298fbb6a1 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -15,14 +15,6 @@ #include "nvim/pos_defs.h" #include "nvim/types_defs.h" -// Set by the apply_autocmds_group function if the given event is equal to -// EVENT_FILETYPE. Used by the readfile function in order to determine if -// EVENT_BUFREADPOST triggered the EVENT_FILETYPE. -// -// Relying on this value requires one to reset it prior calling -// apply_autocmds_group. -EXTERN bool au_did_filetype INIT( = false); - /// For CursorMoved event EXTERN win_T *last_cursormoved_win INIT( = NULL); /// For CursorMoved event, only used when last_cursormoved_win == curwin @@ -31,9 +23,6 @@ EXTERN pos_T last_cursormoved INIT( = { 0, 0, 0 }); EXTERN bool autocmd_busy INIT( = false); ///< Is apply_autocmds() busy? EXTERN int autocmd_no_enter INIT( = false); ///< Buf/WinEnter autocmds disabled EXTERN int autocmd_no_leave INIT( = false); ///< Buf/WinLeave autocmds disabled -EXTERN bool did_filetype INIT( = false); ///< FileType event found -/// value for did_filetype when starting to execute autocommands -EXTERN bool keep_filetype INIT( = false); /// When deleting the current buffer, another one must be loaded. /// If we know which one is preferred, au_new_curbuf is set to it. diff --git a/src/nvim/autocmd_defs.h b/src/nvim/autocmd_defs.h index 6535f8a7ea..490782b209 100644 --- a/src/nvim/autocmd_defs.h +++ b/src/nvim/autocmd_defs.h @@ -19,6 +19,7 @@ typedef struct { 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 *tp_localdir; ///< saved value of tp_localdir char *globaldir; ///< saved value of globaldir bool save_VIsual_active; ///< saved VIsual_active int save_State; ///< saved State diff --git a/src/nvim/base64.c b/src/nvim/base64.c index d461b7e3ff..a645c64fe3 100644 --- a/src/nvim/base64.c +++ b/src/nvim/base64.c @@ -132,12 +132,18 @@ char *base64_encode(const char *src, size_t src_len) /// Decode a Base64 encoded string. /// +/// The returned string is NOT null-terminated, because the decoded string may +/// contain embedded NULLs. Use the output parameter out_lenp to determine the +/// length of the returned string. +/// /// @param src Base64 encoded string /// @param src_len Length of {src} +/// @param [out] out_lenp Returns the length of the decoded string /// @return Decoded string -char *base64_decode(const char *src, size_t src_len) +char *base64_decode(const char *src, size_t src_len, size_t *out_lenp) { assert(src != NULL); + assert(out_lenp != NULL); char *dest = NULL; @@ -155,7 +161,7 @@ char *base64_decode(const char *src, size_t src_len) const uint8_t *s = (const uint8_t *)src; - dest = xmalloc(out_len + 1); + dest = xmalloc(out_len); int acc = 0; int acc_len = 0; @@ -203,7 +209,7 @@ char *base64_decode(const char *src, size_t src_len) } } - dest[out_len] = '\0'; + *out_lenp = out_len; return dest; @@ -212,5 +218,7 @@ invalid: xfree((void *)dest); } + *out_lenp = 0; + return NULL; } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f6c7229485..39d0d24d47 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -117,7 +117,6 @@ # include "buffer.c.generated.h" #endif -static const char *e_auabort = N_("E855: Autocommands caused command to abort"); static const char e_attempt_to_delete_buffer_that_is_in_use_str[] = N_("E937: Attempt to delete a buffer that is in use: %s"); @@ -268,7 +267,7 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) // The autocommands in readfile() may change the buffer, but only AFTER // reading the file. set_bufref(&old_curbuf, curbuf); - modified_was_set = false; + curbuf->b_modified_was_set = false; // mark cursor position as being invalid curwin->w_valid = 0; @@ -351,7 +350,7 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) // the changed flag. Unless in readonly mode: "ls | nvim -R -". // When interrupted and 'cpoptions' contains 'i' set changed flag. if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - || modified_was_set // ":set modified" used in autocmd + || curbuf->b_modified_was_set // autocmd did ":set modified" || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) { changed(curbuf); } else if (retval != FAIL && !read_stdin && !read_fifo) { @@ -569,7 +568,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i } buf->b_locked--; buf->b_locked_split--; - if (abort_if_last && last_nonfloat(win)) { + if (abort_if_last && one_window(win)) { // Autocommands made this the only window. emsg(_(e_auabort)); return false; @@ -588,7 +587,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i } buf->b_locked--; buf->b_locked_split--; - if (abort_if_last && last_nonfloat(win)) { + if (abort_if_last && one_window(win)) { // Autocommands made this the only window. emsg(_(e_auabort)); return false; @@ -1306,6 +1305,12 @@ int do_buffer(int action, int start, int dir, int count, int forceit) } return FAIL; } + + if (action == DOBUF_GOTO && buf != curbuf && !check_can_set_curbuf_forceit(forceit)) { + // disallow navigating to another buffer when 'winfixbuf' is applied + return FAIL; + } + if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) { // disallow navigating to the dummy buffer semsg(_(e_nobufnr), count); @@ -1720,7 +1725,7 @@ void enter_buffer(buf_T *buf) // ":ball" used in an autocommand. If there already is a filetype we // might prefer to keep it. if (*curbuf->b_p_ft == NUL) { - did_filetype = false; + curbuf->b_did_filetype = false; } open_buffer(false, NULL, 0); @@ -1747,7 +1752,7 @@ void enter_buffer(buf_T *buf) maketitle(); // when autocmds didn't change it if (curwin->w_topline == 1 && !curwin->w_topline_was_set) { - scroll_cursor_halfway(false, false); // redisplay at correct position + scroll_cursor_halfway(curwin, false, false); // redisplay at correct position } // Change directories when the 'acd' option is set. @@ -2167,7 +2172,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit) // cursor is at to BOL and w_cursor.lnum is checked due to getfile() if (!p_sol && col != 0) { curwin->w_cursor.col = col; - check_cursor_col(); + check_cursor_col(curwin); curwin->w_cursor.coladd = 0; curwin->w_set_curswant = true; } @@ -2192,7 +2197,7 @@ void buflist_getfpos(void) curwin->w_cursor.col = 0; } else { curwin->w_cursor.col = fpos->col; - check_cursor_col(); + check_cursor_col(curwin); curwin->w_cursor.coladd = 0; curwin->w_set_curswant = true; } @@ -3252,7 +3257,7 @@ void fileinfo(int fullname, int shorthelp, bool dont_truncate) (int64_t)curwin->w_cursor.lnum, (int64_t)curbuf->b_ml.ml_line_count, n); - validate_virtcol(); + validate_virtcol(curwin); size_t len = strlen(buffer); col_print(buffer + len, IOSIZE - len, (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 9653b5e09c..8f5f2c44f7 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -47,8 +47,8 @@ typedef struct { #define VALID_VIRTCOL 0x04 // w_virtcol (file col) is valid #define VALID_CHEIGHT 0x08 // w_cline_height and w_cline_folded valid #define VALID_CROW 0x10 // w_cline_row is valid -#define VALID_BOTLINE 0x20 // w_botine and w_empty_rows are valid -#define VALID_BOTLINE_AP 0x40 // w_botine is approximated +#define VALID_BOTLINE 0x20 // w_botline and w_empty_rows are valid +#define VALID_BOTLINE_AP 0x40 // w_botline is approximated #define VALID_TOPLINE 0x80 // w_topline is valid (for cursor position) // flags for b_flags @@ -139,6 +139,8 @@ typedef struct { #define w_ve_flags w_onebuf_opt.wo_ve_flags // flags for 'virtualedit' OptInt wo_nuw; #define w_p_nuw w_onebuf_opt.wo_nuw // 'numberwidth' + int wo_wfb; +#define w_p_wfb w_onebuf_opt.wo_wfb // 'winfixbuf' int wo_wfh; #define w_p_wfh w_onebuf_opt.wo_wfh // 'winfixheight' int wo_wfw; @@ -459,6 +461,19 @@ struct file_buffer { bool b_marks_read; // Have we read ShaDa marks yet? + bool b_modified_was_set; ///< did ":set modified" + bool b_did_filetype; ///< FileType event found + bool b_keep_filetype; ///< value for did_filetype when starting + ///< to execute autocommands + + /// Set by the apply_autocmds_group function if the given event is equal to + /// EVENT_FILETYPE. Used by the readfile function in order to determine if + /// EVENT_BUFREADPOST triggered the EVENT_FILETYPE. + /// + /// Relying on this value requires one to reset it prior calling + /// apply_autocmds_group(). + bool b_au_did_filetype; + // The following only used in undo.c. u_header_T *b_u_oldhead; // pointer to oldest header u_header_T *b_u_newhead; // pointer to newest header; may not be valid diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c index 94a6604fc1..27de03954a 100644 --- a/src/nvim/bufwrite.c +++ b/src/nvim/bufwrite.c @@ -900,38 +900,31 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o // remove old backup, if present os_remove(*backupp); + // copy the file + if (os_copy(fname, *backupp, UV_FS_COPYFILE_FICLONE) != 0) { + *err = set_err(_("E509: Cannot create backup file (add ! to override)")); + XFREE_CLEAR(*backupp); + *backupp = NULL; + continue; + } + // set file protection same as original file, but // strip s-bit. os_setperm(*backupp, perm & 0777); #ifdef UNIX - // // Try to set the group of the backup same as the original file. If // this fails, set the protection bits for the group same as the // protection bits for others. - // if (file_info_new.stat.st_gid != file_info_old->stat.st_gid && os_chown(*backupp, (uv_uid_t)-1, (uv_gid_t)file_info_old->stat.st_gid) != 0) { os_setperm(*backupp, (perm & 0707) | ((perm & 07) << 3)); } -# ifdef HAVE_XATTR - os_copy_xattr(fname, *backupp); -# endif -#endif - - // copy the file - if (os_copy(fname, *backupp, UV_FS_COPYFILE_FICLONE) != 0) { - *err = set_err(_("E509: Cannot create backup file (add ! to override)")); - XFREE_CLEAR(*backupp); - *backupp = NULL; - continue; - } - -#ifdef UNIX os_file_settime(*backupp, (double)file_info_old->stat.st_atim.tv_sec, (double)file_info_old->stat.st_mtim.tv_sec); #endif + os_set_acl(*backupp, acl); #ifdef HAVE_XATTR os_copy_xattr(fname, *backupp); diff --git a/src/nvim/change.c b/src/nvim/change.c index 1c7724f010..05772d39e9 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -221,7 +221,7 @@ static void changed_lines_invalidate_win(win_T *wp, linenr_T lnum, colnr_T col, void changed_lines_invalidate_buf(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { changed_lines_invalidate_win(wp, lnum, col, lnume, xtra); } @@ -342,9 +342,8 @@ static void changed_common(buf_T *buf, linenr_T lnum, colnr_T col, linenr_T lnum && (last < wp->w_topline || (wp->w_topline >= lnum && wp->w_topline < lnume - && win_linetabsize(wp, wp->w_topline, ml_get(wp->w_topline), MAXCOL) - <= (wp->w_skipcol - + sms_marker_overlap(wp, win_col_off(wp) - win_col_off2(wp)))))) { + && win_linetabsize(wp, wp->w_topline, ml_get_buf(buf, wp->w_topline), MAXCOL) + <= (wp->w_skipcol + sms_marker_overlap(wp, -1))))) { wp->w_skipcol = 0; } @@ -707,14 +706,14 @@ void ins_char(int c) void ins_char_bytes(char *buf, size_t charlen) { // Break tabs if needed. - if (virtual_active() && curwin->w_cursor.coladd > 0) { + if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { coladvance_force(getviscol()); } size_t col = (size_t)curwin->w_cursor.col; linenr_T lnum = curwin->w_cursor.lnum; char *oldp = ml_get(lnum); - size_t linelen = strlen(oldp) + 1; // length of old line including NUL + size_t linelen = (size_t)ml_get_len(lnum) + 1; // length of old line including NUL // The lengths default to the values for when not replacing. size_t oldlen = 0; // nr of bytes inserted @@ -815,13 +814,13 @@ void ins_str(char *s) int newlen = (int)strlen(s); linenr_T lnum = curwin->w_cursor.lnum; - if (virtual_active() && curwin->w_cursor.coladd > 0) { + if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { coladvance_force(getviscol()); } colnr_T col = curwin->w_cursor.col; char *oldp = ml_get(lnum); - int oldlen = (int)strlen(oldp); + int oldlen = ml_get_len(lnum); char *newp = xmalloc((size_t)oldlen + (size_t)newlen + 1); if (col > 0) { @@ -879,7 +878,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) colnr_T col = curwin->w_cursor.col; bool fixpos = fixpos_arg; char *oldp = ml_get(lnum); - colnr_T oldlen = (colnr_T)strlen(oldp); + colnr_T oldlen = ml_get_len(lnum); // Can't do anything when the cursor is on the NUL after the line. if (col >= oldlen) { @@ -918,7 +917,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) // fixpos is true, we don't want to end up positioned at the NUL, // unless "restart_edit" is set or 'virtualedit' contains "onemore". if (col > 0 && fixpos && restart_edit == 0 - && (get_ve_flags() & VE_ONEMORE) == 0) { + && (get_ve_flags(curwin) & VE_ONEMORE) == 0) { curwin->w_cursor.col--; curwin->w_cursor.coladd = 0; curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col); @@ -926,21 +925,24 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) count = oldlen - col; movelen = 1; } + colnr_T newlen = oldlen - count; // If the old line has been allocated the deletion can be done in the // existing line. Otherwise a new line has to be allocated. - bool was_alloced = ml_line_alloced(); // check if oldp was allocated + bool alloc_newp = !ml_line_alloced(); // check if oldp was allocated char *newp; - if (was_alloced) { + if (!alloc_newp) { ml_add_deleted_len(curbuf->b_ml.ml_line_ptr, oldlen); newp = oldp; // use same allocated memory } else { // need to allocate a new line - newp = xmalloc((size_t)(oldlen + 1 - count)); + newp = xmalloc((size_t)newlen + 1); memmove(newp, oldp, (size_t)col); } memmove(newp + col, oldp + col + count, (size_t)movelen); - if (!was_alloced) { + if (alloc_newp) { ml_replace(lnum, newp, false); + } else { + curbuf->b_ml.ml_line_len -= count; } // mark the buffer as changed and prepare for displaying @@ -1041,7 +1043,7 @@ bool copy_indent(int size, char *src) if (p == NULL) { // Allocate memory for the result: the copied indent, new indent // and the rest of the line. - line_len = (int)strlen(get_cursor_line_ptr()) + 1; + line_len = get_cursor_line_len() + 1; assert(ind_len + line_len >= 0); size_t line_size; STRICT_ADD(ind_len, line_len, &line_size, size_t); @@ -1114,7 +1116,7 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) colnr_T mincol = curwin->w_cursor.col + 1; // make a copy of the current line so we can mess with it - char *saved_line = xstrdup(get_cursor_line_ptr()); + char *saved_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); if (State & VREPLACE_FLAG) { // With MODE_VREPLACE we make a copy of the next line, which we will be @@ -1125,7 +1127,8 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // the line, replacing what was there before and pushing the right // stuff onto the replace stack. -- webb. if (curwin->w_cursor.lnum < orig_line_count) { - next_line = xstrdup(ml_get(curwin->w_cursor.lnum + 1)); + next_line = xstrnsave(ml_get(curwin->w_cursor.lnum + 1), + (size_t)ml_get_len(curwin->w_cursor.lnum + 1)); } else { next_line = xstrdup(""); } @@ -1487,7 +1490,7 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) leader = xmalloc((size_t)bytes); allocated = leader; // remember to free it later - xstrlcpy(leader, saved_line, (size_t)lead_len + 1); + xmemcpyz(leader, saved_line, (size_t)lead_len); // TODO(vim): handle multi-byte and double width chars for (int li = 0; li < comment_start; li++) { @@ -1834,6 +1837,13 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) saved_line = NULL; if (did_append) { + // Always move extmarks - Here we move only the line where the cursor is, + // the previous mark_adjust() took care of the lines after. + int cols_added = mincol - 1 + less_cols_off - less_cols; + extmark_splice(curbuf, (int)lnum - 1, mincol - 1 - cols_spliced, + 0, less_cols_off, less_cols_off, + 1, cols_added, 1 + cols_added, kExtmarkUndo); + changed_lines(curbuf, curwin->w_cursor.lnum, curwin->w_cursor.col, curwin->w_cursor.lnum + 1, 1, true); did_append = false; @@ -1844,12 +1854,6 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) curwin->w_cursor.col + less_cols_off, 1, -less_cols, 0); } - // Always move extmarks - Here we move only the line where the - // cursor is, the previous mark_adjust takes care of the lines after - int cols_added = mincol - 1 + less_cols_off - less_cols; - extmark_splice(curbuf, (int)lnum - 1, mincol - 1 - cols_spliced, - 0, less_cols_off, less_cols_off, - 1, cols_added, 1 + cols_added, kExtmarkUndo); } else { changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); } @@ -1861,7 +1865,7 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) } if (did_append) { // bail out and just get the final length of the line we just manipulated - bcount_t extra = (bcount_t)strlen(ml_get(curwin->w_cursor.lnum)); + bcount_t extra = ml_get_len(curwin->w_cursor.lnum); extmark_splice(curbuf, (int)curwin->w_cursor.lnum - 1, 0, 0, 0, 0, 1, 0, 1 + extra, kExtmarkUndo); changed_lines(curbuf, curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1, true); @@ -1905,7 +1909,7 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // stuff onto the replace stack (via ins_char()). if (State & VREPLACE_FLAG) { // Put new line in p_extra - p_extra = xstrdup(get_cursor_line_ptr()); + p_extra = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); // Put back original line ml_replace(curwin->w_cursor.lnum, next_line, false); @@ -1932,19 +1936,16 @@ theend: /// If "fixpos" is true fix the cursor position when done. void truncate_line(int fixpos) { - char *newp; linenr_T lnum = curwin->w_cursor.lnum; colnr_T col = curwin->w_cursor.col; + char *old_line = ml_get(lnum); + char *newp = col == 0 ? xstrdup("") : xstrnsave(old_line, (size_t)col); + int deleted = ml_get_len(lnum) - col; - if (col == 0) { - newp = xstrdup(""); - } else { - newp = xstrnsave(ml_get(lnum), (size_t)col); - } ml_replace(lnum, newp, false); // mark the buffer as changed and prepare for displaying - changed_bytes(lnum, curwin->w_cursor.col); + inserted_bytes(lnum, curwin->w_cursor.col, deleted, 0); // If "fixpos" is true we don't want to end up positioned at the NUL. if (fixpos && curwin->w_cursor.col > 0) { diff --git a/src/nvim/channel.c b/src/nvim/channel.c index ebeaffe5a1..41635747f8 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -29,6 +29,7 @@ #include "nvim/log.h" #include "nvim/lua/executor.h" #include "nvim/main.h" +#include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/msgpack_rpc/channel.h" @@ -543,8 +544,11 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, const char **err } #else if (embedded_mode) { - stdin_dup_fd = dup(STDIN_FILENO); - stdout_dup_fd = dup(STDOUT_FILENO); + // Redirect stdout/stdin (the UI channel) to stderr. Use fnctl(F_DUPFD_CLOEXEC) instead of dup() + // to prevent child processes from inheriting the file descriptors, which are used by UIs to + // detect when Nvim exits. + stdin_dup_fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, STDERR_FILENO + 1); + stdout_dup_fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, STDERR_FILENO + 1); dup2(STDERR_FILENO, STDOUT_FILENO); dup2(STDERR_FILENO, STDIN_FILENO); } @@ -646,35 +650,52 @@ static inline list_T *buffer_to_tv_list(const char *const buf, const size_t len) void on_channel_data(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) { Channel *chan = data; - on_channel_output(stream, chan, buf, count, eof, &chan->on_data); + on_channel_output(stream, chan, buf, eof, &chan->on_data); } void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) { Channel *chan = data; - on_channel_output(stream, chan, buf, count, eof, &chan->on_stderr); + on_channel_output(stream, chan, buf, eof, &chan->on_stderr); } -static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, size_t count, bool eof, +static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, bool eof, CallbackReader *reader) { - // stub variable, to keep reading consistent with the order of events, only - // consider the count parameter. - size_t r; - char *ptr = rbuffer_read_ptr(buf, &r); + size_t count; + char *output = rbuffer_read_ptr(buf, &count); - if (eof) { - reader->eof = true; - } else { - if (chan->term) { - terminal_receive(chan->term, ptr, count); + if (chan->term) { + if (!eof) { + char *p = output; + char *end = output + count; + while (p < end) { + // Don't pass incomplete UTF-8 sequences to libvterm. #16245 + // Composing chars can be passed separately, so utf_ptr2len_len() is enough. + int clen = utf_ptr2len_len(p, (int)(end - p)); + if (clen > end - p) { + count = (size_t)(p - output); + break; + } + p += clen; + } } + terminal_receive(chan->term, output, count); + } + + if (count) { rbuffer_consumed(buf, count); + } + // Move remaining data to start of buffer, so the buffer can never wrap around. + rbuffer_reset(buf); - if (callback_reader_set(*reader)) { - ga_concat_len(&reader->buffer, ptr, count); - } + if (callback_reader_set(*reader)) { + ga_concat_len(&reader->buffer, output, count); + } + + if (eof) { + reader->eof = true; } if (callback_reader_set(*reader)) { diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 20bd364c7e..c611d4cfd6 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1051,7 +1051,7 @@ char *skiptowhite(const char *p) /// /// @return Pointer to the next whitespace character. char *skiptowhite_esc(const char *p) - FUNC_ATTR_PURE + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE { while (*p != ' ' && *p != '\t' && *p != NUL) { if (((*p == '\\') || (*p == Ctrl_V)) && (*(p + 1) != NUL)) { @@ -1457,10 +1457,20 @@ bool rem_backslash(const char *str) /// @param p void backslash_halve(char *p) { - for (; *p; p++) { - if (rem_backslash(p)) { - STRMOVE(p, p + 1); + for (; *p && !rem_backslash(p); p++) {} + if (*p != NUL) { + char *dst = p; + goto start; + while (*p != NUL) { + if (rem_backslash(p)) { +start: + *dst++ = *(p + 1); + p += 2; + } else { + *dst++ = *p++; + } } + *dst = '\0'; } } @@ -1472,8 +1482,16 @@ void backslash_halve(char *p) char *backslash_halve_save(const char *p) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { - // TODO(philix): simplify and improve backslash_halve_save algorithm - char *res = xstrdup(p); - backslash_halve(res); + char *res = xmalloc(strlen(p) + 1); + char *dst = res; + while (*p != NUL) { + if (rem_backslash(p)) { + *dst++ = *(p + 1); + p += 2; + } else { + *dst++ = *p++; + } + } + *dst = '\0'; return res; } diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index f172646edf..808df44941 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -1294,7 +1294,7 @@ char *addstar(char *fname, size_t len, int context) } } else { retval = xmalloc(len + 4); - xstrlcpy(retval, fname, len + 1); + xmemcpyz(retval, fname, len); // Don't add a star to *, ~, ~user, $var or `cmd`. // * would become **, which walks the whole tree. @@ -2598,7 +2598,8 @@ static char *get_healthcheck_names(expand_T *xp FUNC_ATTR_UNUSED, int idx) last_gen = get_cmdline_last_prompt_id(); } - if (names.type == kObjectTypeArray && idx < (int)names.data.array.size) { + if (names.type == kObjectTypeArray && idx < (int)names.data.array.size + && names.data.array.items[idx].type == kObjectTypeString) { return names.data.array.items[idx].data.string.data; } return NULL; @@ -2938,7 +2939,7 @@ void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regmatch, ch static void expand_shellcmd_onedir(char *buf, char *s, size_t l, char *pat, char ***matches, int *numMatches, int flags, hashtab_T *ht, garray_T *gap) { - xstrlcpy(buf, s, l + 1); + xmemcpyz(buf, s, l); add_pathsep(buf); l = strlen(buf); xstrlcpy(buf + l, pat, MAXPATHL - l); @@ -2986,7 +2987,6 @@ static void expand_shellcmd(char *filepat, char ***matches, int *numMatches, int char *path = NULL; garray_T ga; char *buf = xmalloc(MAXPATHL); - char *e; int flags = flagsarg; bool did_curdir = false; @@ -3022,7 +3022,7 @@ static void expand_shellcmd(char *filepat, char ***matches, int *numMatches, int ga_init(&ga, (int)sizeof(char *), 10); hashtab_T found_ht; hash_init(&found_ht); - for (char *s = path;; s = e) { + for (char *s = path, *e;; s = e) { e = vim_strchr(s, ENV_SEPCHAR); if (e == NULL) { e = s + strlen(s); @@ -3047,6 +3047,7 @@ static void expand_shellcmd(char *filepat, char ***matches, int *numMatches, int if (l > MAXPATHL - 5) { break; } + assert(l <= strlen(s)); expand_shellcmd_onedir(buf, s, l, pat, matches, numMatches, flags, &found_ht, &ga); if (*e != NUL) { e++; @@ -3241,6 +3242,7 @@ static int ExpandUserLua(expand_T *xp, int *num_file, char ***file) /// Adds matches to `ga`. /// If "dirs" is true only expand directory names. void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dirs) + FUNC_ATTR_NONNULL_ALL { expand_T xpc; ExpandInit(&xpc); diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c index 6a9290270a..983ab8b59b 100644 --- a/src/nvim/cmdhist.c +++ b/src/nvim/cmdhist.c @@ -294,7 +294,7 @@ static int last_maptick = -1; // last seen maptick /// @param histype may be one of the HIST_ values. /// @param in_map consider maptick when inside a mapping /// @param sep separator character used (search hist) -void add_to_history(int histype, const char *new_entry, int in_map, int sep) +void add_to_history(int histype, const char *new_entry, size_t new_entrylen, bool in_map, int sep) { histentry_T *hisptr; @@ -334,11 +334,10 @@ void add_to_history(int histype, const char *new_entry, int in_map, int sep) hist_free_entry(hisptr); // Store the separator after the NUL of the string. - size_t len = strlen(new_entry); - hisptr->hisstr = xstrnsave(new_entry, len + 2); + hisptr->hisstr = xstrnsave(new_entry, new_entrylen + 2); hisptr->timestamp = os_time(); hisptr->additional_elements = NULL; - hisptr->hisstr[len + 1] = (char)sep; + hisptr->hisstr[new_entrylen + 1] = (char)sep; hisptr->hisnum = ++hisnum[histype]; if (histype == HIST_SEARCH && in_map) { @@ -536,7 +535,7 @@ void f_histadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } init_history(); - add_to_history(histype, str, false, NUL); + add_to_history(histype, str, strlen(str), false, NUL); rettv->vval.v_number = true; } @@ -663,7 +662,8 @@ void ex_history(exarg_T *eap) i = 0; } if (hist[i].hisstr != NULL - && hist[i].hisnum >= j && hist[i].hisnum <= k) { + && hist[i].hisnum >= j && hist[i].hisnum <= k + && !message_filtered(hist[i].hisstr)) { msg_putchar('\n'); snprintf(IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ', hist[i].hisnum); diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index e93e658f1e..2e9e68843a 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -57,7 +57,7 @@ int getviscol2(colnr_T col, colnr_T coladd) /// The caller must have saved the cursor line for undo! int coladvance_force(colnr_T wcol) { - int rc = coladvance2(&curwin->w_cursor, true, false, wcol); + int rc = coladvance2(curwin, &curwin->w_cursor, true, false, wcol); if (wcol == MAXCOL) { curwin->w_valid &= ~VALID_VIRTCOL; @@ -76,25 +76,26 @@ int coladvance_force(colnr_T wcol) /// beginning at coladd 0. /// /// @return OK if desired column is reached, FAIL if not -int coladvance(colnr_T wcol) +int coladvance(win_T *wp, colnr_T wcol) { - int rc = getvpos(&curwin->w_cursor, wcol); + int rc = getvpos(wp, &wp->w_cursor, wcol); if (wcol == MAXCOL || rc == FAIL) { - curwin->w_valid &= ~VALID_VIRTCOL; - } else if (*get_cursor_pos_ptr() != TAB) { + wp->w_valid &= ~VALID_VIRTCOL; + } else if (*(ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) + wp->w_cursor.col) != TAB) { // Virtcol is valid when not on a TAB - curwin->w_valid |= VALID_VIRTCOL; - curwin->w_virtcol = wcol; + wp->w_valid |= VALID_VIRTCOL; + wp->w_virtcol = wcol; } return rc; } -/// @param addspaces change the text to achieve our goal? +/// @param addspaces change the text to achieve our goal? only for wp=curwin! /// @param finetune change char offset for the exact column /// @param wcol_arg column to move to (can be negative) -static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_arg) +static int coladvance2(win_T *wp, pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_arg) { + assert(wp == curwin || !addspaces); colnr_T wcol = wcol_arg; int idx; colnr_T col = 0; @@ -104,30 +105,31 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a || (State & MODE_TERMINAL) || restart_edit != NUL || (VIsual_active && *p_sel != 'o') - || ((get_ve_flags() & VE_ONEMORE) && wcol < MAXCOL); + || ((get_ve_flags(wp) & VE_ONEMORE) && wcol < MAXCOL); - char *line = ml_get_buf(curbuf, pos->lnum); + char *line = ml_get_buf(wp->w_buffer, pos->lnum); + int linelen = ml_get_buf_len(wp->w_buffer, pos->lnum); if (wcol >= MAXCOL) { - idx = (int)strlen(line) - 1 + one_more; + idx = linelen - 1 + one_more; col = wcol; if ((addspaces || finetune) && !VIsual_active) { - curwin->w_curswant = linetabsize(curwin, pos->lnum) + one_more; - if (curwin->w_curswant > 0) { - curwin->w_curswant--; + wp->w_curswant = linetabsize(wp, pos->lnum) + one_more; + if (wp->w_curswant > 0) { + wp->w_curswant--; } } } else { - int width = curwin->w_width_inner - win_col_off(curwin); + int width = wp->w_width_inner - win_col_off(wp); int csize = 0; if (finetune - && curwin->w_p_wrap - && curwin->w_width_inner != 0 + && wp->w_p_wrap + && wp->w_width_inner != 0 && wcol >= (colnr_T)width && width > 0) { - csize = linetabsize(curwin, pos->lnum); + csize = linetabsize(wp, pos->lnum); if (csize > 0) { csize--; } @@ -143,7 +145,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a } CharsizeArg csarg; - CSType cstype = init_charsize_arg(&csarg, curwin, pos->lnum, line); + CSType cstype = init_charsize_arg(&csarg, wp, pos->lnum, line); StrCharInfo ci = utf_ptr2StrCharInfo(line); col = 0; while (col <= wcol && *ci.ptr != NUL) { @@ -159,14 +161,14 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a // is needed to ensure that a virtual position off the end of // a line has the correct indexing. The one_more comparison // replaces an explicit add of one_more later on. - if (col > wcol || (!virtual_active() && one_more == 0)) { + if (col > wcol || (!virtual_active(wp) && one_more == 0)) { idx -= 1; // Don't count the chars from 'showbreak'. csize -= head; col -= csize; } - if (virtual_active() + if (virtual_active(wp) && addspaces && wcol >= 0 && ((col != wcol && col != wcol + 1) || csize > 1)) { @@ -187,7 +189,6 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a col = wcol; } else { // Break a tab - int linelen = (int)strlen(line); int correct = wcol - col - csize + 1; // negative!! char *newline; @@ -229,14 +230,14 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a if (!one_more) { colnr_T scol, ecol; - getvcol(curwin, pos, &scol, NULL, &ecol); + getvcol(wp, pos, &scol, NULL, &ecol); pos->coladd = ecol - scol; } } else { int b = (int)wcol - (int)col; // The difference between wcol and col is used to set coladd. - if (b > 0 && b < (MAXCOL - 2 * curwin->w_width_inner)) { + if (b > 0 && b < (MAXCOL - 2 * wp->w_width_inner)) { pos->coladd = b; } @@ -245,7 +246,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a } // Prevent from moving onto a trail byte. - mark_mb_adjustpos(curbuf, pos); + mark_mb_adjustpos(wp->w_buffer, pos); if (wcol < 0 || col < wcol) { return FAIL; @@ -256,9 +257,9 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a /// Return in "pos" the position of the cursor advanced to screen column "wcol". /// /// @return OK if desired column is reached, FAIL if not -int getvpos(pos_T *pos, colnr_T wcol) +int getvpos(win_T *wp, pos_T *pos, colnr_T wcol) { - return coladvance2(pos, false, virtual_active(), wcol); + return coladvance2(wp, pos, false, virtual_active(wp), wcol); } /// Increment the cursor position. See inc() for return values. @@ -294,7 +295,7 @@ linenr_T get_cursor_rel_lnum(win_T *wp, linenr_T lnum) // Loop until we reach to_line, skipping folds. for (; from_line < to_line; from_line++, retval++) { // If from_line is in a fold, set it to the last line of that fold. - hasFoldingWin(wp, from_line, NULL, &from_line, true, NULL); + hasFolding(wp, from_line, NULL, &from_line); } // If to_line is in a closed fold, the line count is off by +1. Correct it. @@ -314,8 +315,7 @@ void check_pos(buf_T *buf, pos_T *pos) } if (pos->col > 0) { - char *line = ml_get_buf(buf, pos->lnum); - colnr_T len = (colnr_T)strlen(line); + colnr_T len = ml_get_buf_len(buf, pos->lnum); if (pos->col > len) { pos->col = len; } @@ -329,7 +329,7 @@ void check_cursor_lnum(win_T *win) if (win->w_cursor.lnum > buf->b_ml.ml_line_count) { // If there is a closed fold at the end of the file, put the cursor in // its first line. Otherwise in the last line. - if (!hasFolding(buf->b_ml.ml_line_count, &win->w_cursor.lnum, NULL)) { + if (!hasFolding(win, buf->b_ml.ml_line_count, &win->w_cursor.lnum, NULL)) { win->w_cursor.lnum = buf->b_ml.ml_line_count; } } @@ -338,32 +338,26 @@ void check_cursor_lnum(win_T *win) } } -/// Make sure curwin->w_cursor.col is valid. -void check_cursor_col(void) -{ - check_cursor_col_win(curwin); -} - /// Make sure win->w_cursor.col is valid. Special handling of insert-mode. /// @see mb_check_adjust_col -void check_cursor_col_win(win_T *win) +void check_cursor_col(win_T *win) { colnr_T oldcol = win->w_cursor.col; colnr_T oldcoladd = win->w_cursor.col + win->w_cursor.coladd; - unsigned cur_ve_flags = get_ve_flags(); + unsigned cur_ve_flags = get_ve_flags(win); - colnr_T len = (colnr_T)strlen(ml_get_buf(win->w_buffer, win->w_cursor.lnum)); + colnr_T len = ml_get_buf_len(win->w_buffer, win->w_cursor.lnum); if (len == 0) { win->w_cursor.col = 0; } else if (win->w_cursor.col >= len) { // Allow cursor past end-of-line when: // - in Insert mode or restarting Insert mode // - in Visual mode and 'selection' isn't "old" - // - 'virtualedit' is set */ + // - 'virtualedit' is set if ((State & MODE_INSERT) || restart_edit || (VIsual_active && *p_sel != 'o') || (cur_ve_flags & VE_ONEMORE) - || virtual_active()) { + || virtual_active(win)) { win->w_cursor.col = len; } else { win->w_cursor.col = len - 1; @@ -403,10 +397,10 @@ void check_cursor_col_win(win_T *win) } /// Make sure curwin->w_cursor in on a valid character -void check_cursor(void) +void check_cursor(win_T *wp) { - check_cursor_lnum(curwin); - check_cursor_col(); + check_cursor_lnum(wp); + check_cursor_col(wp); } /// Check if VIsual position is valid, correct it if not. @@ -418,7 +412,7 @@ void check_visual_pos(void) VIsual.col = 0; VIsual.coladd = 0; } else { - int len = (int)strlen(ml_get(VIsual.lnum)); + int len = ml_get_len(VIsual.lnum); if (VIsual.col > len) { VIsual.col = len; @@ -453,8 +447,8 @@ bool set_leftcol(colnr_T leftcol) changed_cline_bef_curs(curwin); // TODO(hinidu): I think it should be colnr_T or int, but p_siso is long. // Perhaps we can change p_siso to int. - int64_t lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1; - validate_virtcol(); + int64_t lastcol = curwin->w_leftcol + curwin->w_width_inner - win_col_off(curwin) - 1; + validate_virtcol(curwin); bool retval = false; // If the cursor is right or left of the screen, move it to last or first @@ -462,10 +456,10 @@ bool set_leftcol(colnr_T leftcol) int siso = get_sidescrolloff_value(curwin); if (curwin->w_virtcol > (colnr_T)(lastcol - siso)) { retval = true; - coladvance((colnr_T)(lastcol - siso)); + coladvance(curwin, (colnr_T)(lastcol - siso)); } else if (curwin->w_virtcol < curwin->w_leftcol + siso) { retval = true; - coladvance((colnr_T)(curwin->w_leftcol + siso)); + coladvance(curwin, (colnr_T)(curwin->w_leftcol + siso)); } // If the start of the character under the cursor is not on the screen, @@ -475,10 +469,10 @@ bool set_leftcol(colnr_T leftcol) getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e); if (e > (colnr_T)lastcol) { retval = true; - coladvance(s - 1); + coladvance(curwin, s - 1); } else if (s < curwin->w_leftcol) { retval = true; - if (coladvance(e + 1) == FAIL) { // there isn't another character + if (coladvance(curwin, e + 1) == FAIL) { // there isn't another character curwin->w_leftcol = s; // adjust w_leftcol instead changed_cline_bef_curs(curwin); } @@ -514,3 +508,15 @@ char *get_cursor_pos_ptr(void) { return ml_get_buf(curbuf, curwin->w_cursor.lnum) + curwin->w_cursor.col; } + +/// @return length (excluding the NUL) of the cursor line. +colnr_T get_cursor_line_len(void) +{ + return ml_get_buf_len(curbuf, curwin->w_cursor.lnum); +} + +/// @return length (excluding the NUL) of the cursor position. +colnr_T get_cursor_pos_len(void) +{ + return ml_get_buf_len(curbuf, curwin->w_cursor.lnum) - curwin->w_cursor.col; +} diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 51d5d08f78..303d0318b5 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -346,7 +346,12 @@ char *next_virt_text_chunk(VirtText vt, size_t *pos, int *attr) for (; text == NULL && *pos < kv_size(vt); (*pos)++) { text = kv_A(vt, *pos).text; int hl_id = kv_A(vt, *pos).hl_id; - *attr = hl_combine_attr(*attr, hl_id > 0 ? syn_id2attr(hl_id) : 0); + if (hl_id >= 0) { + *attr = MAX(*attr, 0); + if (hl_id > 0) { + *attr = hl_combine_attr(*attr, syn_id2attr(hl_id)); + } + } } return text; } @@ -454,21 +459,18 @@ static void decor_range_add_from_inline(DecorState *state, int start_row, int st if (decor.ext) { DecorVirtText *vt = decor.data.ext.vt; while (vt) { - decor_range_add_virt(state, start_row, start_col, end_row, end_col, vt, owned, - DECOR_PRIORITY_BASE); + decor_range_add_virt(state, start_row, start_col, end_row, end_col, vt, owned); vt = vt->next; } uint32_t idx = decor.data.ext.sh_idx; while (idx != DECOR_ID_INVALID) { DecorSignHighlight *sh = &kv_A(decor_items, idx); - decor_range_add_sh(state, start_row, start_col, end_row, end_col, sh, owned, ns, mark_id, - DECOR_PRIORITY_BASE); + decor_range_add_sh(state, start_row, start_col, end_row, end_col, sh, owned, ns, mark_id); idx = sh->next; } } else { DecorSignHighlight sh = decor_sh_from_inline(decor.data.hl); - decor_range_add_sh(state, start_row, start_col, end_row, end_col, &sh, owned, ns, mark_id, - DECOR_PRIORITY_BASE); + decor_range_add_sh(state, start_row, start_col, end_row, end_col, &sh, owned, ns, mark_id); } } @@ -478,8 +480,7 @@ static void decor_range_insert(DecorState *state, DecorRange range) size_t index; for (index = kv_size(state->active) - 1; index > 0; index--) { DecorRange item = kv_A(state->active, index - 1); - if ((item.priority < range.priority) - || ((item.priority == range.priority) && (item.subpriority <= range.subpriority))) { + if (item.priority <= range.priority) { break; } kv_A(state->active, index) = kv_A(state->active, index - 1); @@ -488,7 +489,7 @@ static void decor_range_insert(DecorState *state, DecorRange range) } void decor_range_add_virt(DecorState *state, int start_row, int start_col, int end_row, int end_col, - DecorVirtText *vt, bool owned, DecorPriority subpriority) + DecorVirtText *vt, bool owned) { bool is_lines = vt->flags & kVTIsLines; DecorRange range = { @@ -498,15 +499,13 @@ void decor_range_add_virt(DecorState *state, int start_row, int start_col, int e .attr_id = 0, .owned = owned, .priority = vt->priority, - .subpriority = subpriority, .draw_col = -10, }; decor_range_insert(state, range); } void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end_row, int end_col, - DecorSignHighlight *sh, bool owned, uint32_t ns, uint32_t mark_id, - DecorPriority subpriority) + DecorSignHighlight *sh, bool owned, uint32_t ns, uint32_t mark_id) { if (sh->flags & kSHIsSign) { return; @@ -519,7 +518,6 @@ void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end .attr_id = 0, .owned = owned, .priority = sh->priority, - .subpriority = subpriority, .draw_col = -10, }; @@ -742,14 +740,15 @@ void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[], if (kv_size(signs)) { int width = wp->w_minscwidth == SCL_NUM ? 1 : wp->w_scwidth; - int idx = MIN(width, num_text) - 1; + int len = MIN(width, num_text); + int idx = 0; qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(kv_A(signs, 0)), sign_item_cmp); for (size_t i = 0; i < kv_size(signs); i++) { DecorSignHighlight *sh = kv_A(signs, i).sh; - if (idx >= 0 && sh->text[0]) { + if (idx < len && sh->text[0]) { memcpy(sattrs[idx].text, sh->text, SIGN_WIDTH * sizeof(sattr_T)); - sattrs[idx--].hl_id = sh->hl_id; + sattrs[idx++].hl_id = sh->hl_id; } if (*num_id == 0) { *num_id = sh->number_hl_id; @@ -792,13 +791,12 @@ static const uint32_t signtext_filter[4] = {[kMTMetaSignText] = kMTFilterSelect /// @param clear kFalse, kTrue or kNone for an, added/deleted, cleared, or initialized range. void buf_signcols_count_range(buf_T *buf, int row1, int row2, int add, TriState clear) { - if (!buf->b_signcols.autom || !buf_meta_total(buf, kMTMetaSignText)) { + if (!buf->b_signcols.autom || row2 < row1 || !buf_meta_total(buf, kMTMetaSignText)) { return; } // Allocate an array of integers holding the number of signs in the range. - assert(row2 >= row1); - int *count = xcalloc(sizeof(int), (size_t)(row2 + 1 - row1)); + int *count = xcalloc((size_t)(row2 + 1 - row1), sizeof(int)); MarkTreeIter itr[1]; MTPair pair = { 0 }; @@ -890,9 +888,9 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines, TriState has_fo } assert(lnum > 0); - bool below_fold = lnum > 1 && hasFoldingWin(wp, lnum - 1, NULL, NULL, true, NULL); + bool below_fold = lnum > 1 && hasFolding(wp, lnum - 1, NULL, NULL); if (has_fold == kNone) { - has_fold = hasFoldingWin(wp, lnum, NULL, NULL, true, NULL); + has_fold = hasFolding(wp, lnum, NULL, NULL); } const int row = lnum - 1; diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index a63cc7afc0..86d4de79f0 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -48,8 +48,6 @@ typedef struct { int attr_id; ///< cached lookup of inl.hl_id if it was a highlight bool owned; ///< ephemeral decoration, free memory immediately DecorPriority priority; - DecorPriority subpriority; ///< Secondary priority value used for ordering (#27131). - ///< Reflects the order of patterns/captures in the query file. DecorRangeKind kind; /// Screen column to draw the virtual text. /// When -1, it should be drawn on the current screen line after deciding where. diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h index 8d0075b169..c9475257b1 100644 --- a/src/nvim/decoration_defs.h +++ b/src/nvim/decoration_defs.h @@ -10,7 +10,7 @@ typedef struct { char *text; - int hl_id; + int hl_id; ///< -1 if not specified } VirtTextChunk; typedef kvec_t(VirtTextChunk) VirtText; diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c index 2417c14f7f..74f444d8e8 100644 --- a/src/nvim/decoration_provider.c +++ b/src/nvim/decoration_provider.c @@ -35,7 +35,7 @@ static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE; static void decor_provider_error(DecorProvider *provider, const char *name, const char *msg) { const char *ns_name = describe_ns(provider->ns_id, "(UNKNOWN PLUGIN)"); - ELOG("error in provider %s.%s: %s", ns_name, name, msg); + ILOG("error in provider %s.%s: %s", ns_name, name, msg); msg_schedule_semsg_multiline("Error in decoration provider %s.%s:\n%s", ns_name, name, msg); } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 2b3010e063..ea846b46ec 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -756,7 +756,7 @@ static int diff_write_buffer(buf_T *buf, mmfile_t *m, linenr_T start, linenr_T e // xdiff requires one big block of memory with all the text. for (linenr_T lnum = start; lnum <= end; lnum++) { - len += strlen(ml_get_buf(buf, lnum)) + 1; + len += (size_t)ml_get_buf_len(buf, lnum) + 1; } char *ptr = try_malloc(len); if (ptr == NULL) { @@ -1347,7 +1347,7 @@ void ex_diffsplit(exarg_T *eap) set_bufref(&old_curbuf, curbuf); // Need to compute w_fraction when no redraw happened yet. - validate_cursor(); + validate_cursor(curwin); set_fraction(curwin); // don't use a new tab page, each tab page has its own diffs @@ -1429,6 +1429,7 @@ void diff_win_options(win_T *wp, bool addbuf) wp->w_p_wrap_save = wp->w_p_wrap; } wp->w_p_wrap = false; + wp->w_skipcol = 0; } if (!wp->w_p_diff) { @@ -1437,7 +1438,8 @@ void diff_win_options(win_T *wp, bool addbuf) } wp->w_p_fdm_save = xstrdup(wp->w_p_fdm); } - set_string_option_direct_in_win(wp, kOptFoldmethod, "diff", OPT_LOCAL, 0); + set_option_direct_for(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("diff"), OPT_LOCAL, 0, kOptReqWin, + wp); if (!wp->w_p_diff) { wp->w_p_fen_save = wp->w_p_fen; @@ -1457,7 +1459,7 @@ void diff_win_options(win_T *wp, bool addbuf) foldUpdateAll(wp); // make sure topline is not halfway through a fold - changed_window_setting_win(wp); + changed_window_setting(wp); if (vim_strchr(p_sbo, 'h') == NULL) { do_cmdline_cmd("set sbo+=hor"); } @@ -1497,8 +1499,9 @@ void ex_diffoff(exarg_T *eap) wp->w_p_crb = wp->w_p_crb_save; } if (!(diff_flags & DIFF_FOLLOWWRAP)) { - if (!wp->w_p_wrap) { - wp->w_p_wrap = wp->w_p_wrap_save; + if (!wp->w_p_wrap && wp->w_p_wrap_save) { + wp->w_p_wrap = true; + wp->w_leftcol = 0; } } free_string_option(wp->w_p_fdm); @@ -1522,7 +1525,7 @@ void ex_diffoff(exarg_T *eap) // make sure topline is not halfway a fold and cursor is // invalidated - changed_window_setting_win(wp); + changed_window_setting(wp); // Note: 'sbo' is not restored, it's a global option. diff_buf_adjust(wp); @@ -2137,7 +2140,7 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus) } // A closed fold never has filler lines. - if (hasFoldingWin(wp, lnum, NULL, NULL, true, NULL)) { + if (hasFolding(wp, lnum, NULL, NULL)) { return 0; } @@ -2451,8 +2454,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin) changed_line_abv_curs_win(towin); check_topfill(towin, false); - hasFoldingWin(towin, towin->w_topline, &towin->w_topline, - NULL, true, NULL); + hasFolding(towin, towin->w_topline, &towin->w_topline, NULL); } /// This is called when 'diffopt' is changed. @@ -2988,7 +2990,7 @@ theend: // Check that the cursor is on a valid character and update its // position. When there were filler lines the topline has become // invalid. - check_cursor(); + check_cursor(curwin); changed_line_abv_curs(); if (diff_need_update) { diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index f91ff9274b..a358a1723a 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -2078,7 +2078,7 @@ void ex_loadkeymap(exarg_T *eap) char buf[KMAP_LLEN + 11]; char *save_cpo = p_cpo; - if (!getline_equal(eap->getline, eap->cookie, getsourceline)) { + if (!getline_equal(eap->ea_getline, eap->cookie, getsourceline)) { emsg(_("E105: Using :loadkeymap not in a sourced file")); return; } @@ -2094,7 +2094,7 @@ void ex_loadkeymap(exarg_T *eap) // Get each line of the sourced file, break at the end. while (true) { - char *line = eap->getline(0, eap->cookie, 0, true); + char *line = eap->ea_getline(0, eap->cookie, 0, true); if (line == NULL) { break; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 4281cdff33..07944081da 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -73,7 +73,7 @@ typedef struct { int col; ///< visual column on screen, after wrapping int boguscols; ///< nonexistent columns added to "col" to force wrapping int old_boguscols; ///< bogus boguscols - int vcol_off; ///< offset for concealed characters + int vcol_off_co; ///< offset for concealed characters int off; ///< offset relative start of line @@ -288,26 +288,23 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int if (item->draw_col < 0) { continue; } - int col = 0; if (item->kind == kDecorKindUIWatched) { // send mark position to UI - col = item->draw_col; - WinExtmark m = { (NS)item->data.ui.ns_id, item->data.ui.mark_id, win_row, col }; + WinExtmark m = { (NS)item->data.ui.ns_id, item->data.ui.mark_id, win_row, item->draw_col }; kv_push(win_extmark_arr, m); } if (vt) { int vcol = item->draw_col - col_off; - col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text, - vt->hl_mode, max_col, vcol); + int col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text, + vt->hl_mode, max_col, vcol); if (vt->pos == kVPosEndOfLine && do_eol) { state->eol_col = col + 1; } + *end_col = MAX(*end_col, col); } if (!vt || !(vt->flags & kVTRepeatLinebreak)) { item->draw_col = INT_MIN; // deactivate } - - *end_col = MAX(*end_col, col); } } @@ -853,43 +850,6 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t } } -static colnr_T get_trailcol(win_T *wp, const char *ptr, const char *line) -{ - colnr_T trailcol = MAXCOL; - // find start of trailing whitespace - if (wp->w_p_lcs_chars.trail) { - trailcol = (colnr_T)strlen(ptr); - while (trailcol > 0 && ascii_iswhite(ptr[trailcol - 1])) { - trailcol--; - } - trailcol += (colnr_T)(ptr - line); - } - - return trailcol; -} - -static colnr_T get_leadcol(win_T *wp, const char *ptr, const char *line) -{ - colnr_T leadcol = 0; - - // find end of leading whitespace - if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) { - leadcol = 0; - while (ascii_iswhite(ptr[leadcol])) { - leadcol++; - } - if (ptr[leadcol] == NUL) { - // in a line full of spaces all of them are treated as trailing - leadcol = 0; - } else { - // keep track of the first column not filled with spaces - leadcol += (colnr_T)(ptr - line + 1); - } - } - - return leadcol; -} - /// Start a screen line at column zero. static void win_line_start(win_T *wp, winlinevars_T *wlv) { @@ -898,16 +858,16 @@ static void win_line_start(win_T *wp, winlinevars_T *wlv) wlv->need_lbr = false; for (int i = 0; i < wp->w_grid.cols; i++) { linebuf_char[i] = schar_from_ascii(' '); - linebuf_attr[i] = -1; + linebuf_attr[i] = 0; linebuf_vcol[i] = -1; } } static void fix_for_boguscols(winlinevars_T *wlv) { - wlv->n_extra += wlv->vcol_off; - wlv->vcol -= wlv->vcol_off; - wlv->vcol_off = 0; + wlv->n_extra += wlv->vcol_off_co; + wlv->vcol -= wlv->vcol_off_co; + wlv->vcol_off_co = 0; wlv->col -= wlv->boguscols; wlv->old_boguscols = wlv->boguscols; wlv->boguscols = 0; @@ -1027,7 +987,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s int conceal_attr = win_hl_attr(wp, HLF_CONCEAL); bool is_concealing = false; bool did_wcol = false; -#define vcol_hlc(wlv) ((wlv).vcol - (wlv).vcol_off) +#define vcol_hlc(wlv) ((wlv).vcol - (wlv).vcol_off_co) assert(startrow < endrow); @@ -1070,7 +1030,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s has_decor = decor_redraw_line(wp, lnum - 1, &decor_state); - decor_providers_invoke_line(wp, lnum - 1, &has_decor); + if (!end_fill) { + decor_providers_invoke_line(wp, lnum - 1, &has_decor); + } if (has_decor) { extra_check = true; @@ -1301,17 +1263,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s nextlinecol = MAXCOL; nextline_idx = 0; } else { - const size_t line_len = strlen(line); + const colnr_T line_len = ml_get_buf_len(wp->w_buffer, lnum); if (line_len < SPWORDLEN) { // Short line, use it completely and append the start of the // next line. nextlinecol = 0; - memmove(nextline, line, line_len); + memmove(nextline, line, (size_t)line_len); STRMOVE(nextline + line_len, nextline + SPWORDLEN); - nextline_idx = (int)line_len + 1; + nextline_idx = line_len + 1; } else { // Long line, use only the last SPWORDLEN bytes. - nextlinecol = (int)line_len - SPWORDLEN; + nextlinecol = line_len - SPWORDLEN; memmove(nextline, line + nextlinecol, SPWORDLEN); nextline_idx = SPWORDLEN + 1; } @@ -1339,8 +1301,28 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s || wp->w_p_lcs_chars.nbsp) { extra_check = true; } - trailcol = get_trailcol(wp, ptr, line); - leadcol = get_leadcol(wp, ptr, line); + // find start of trailing whitespace + if (wp->w_p_lcs_chars.trail) { + trailcol = ml_get_buf_len(wp->w_buffer, lnum); + while (trailcol > 0 && ascii_iswhite(ptr[trailcol - 1])) { + trailcol--; + } + trailcol += (colnr_T)(ptr - line); + } + // find end of leading whitespace + if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) { + leadcol = 0; + while (ascii_iswhite(ptr[leadcol])) { + leadcol++; + } + if (ptr[leadcol] == NUL) { + // in a line full of spaces all of them are treated as trailing + leadcol = 0; + } else { + // keep track of the first column not filled with spaces + leadcol += (colnr_T)(ptr - line + 1); + } + } } // 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the @@ -1396,7 +1378,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s // the end of the line may be before the start of the displayed part. if (wlv.vcol < start_col && (wp->w_p_cuc || wlv.color_cols - || virtual_active() + || virtual_active(wp) || (VIsual_active && wp->w_buffer == curwin->w_buffer))) { wlv.vcol = start_col; } @@ -1433,7 +1415,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s pos_T pos = wp->w_cursor; wp->w_cursor.lnum = lnum; wp->w_cursor.col = linecol; - size_t len = spell_move_to(wp, FORWARD, true, true, &spell_hlf); + size_t len = spell_move_to(wp, FORWARD, SMT_ALL, true, &spell_hlf); // spell_move_to() may call ml_get() and make "line" invalid line = ml_get_buf(wp->w_buffer, lnum); @@ -1569,7 +1551,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s // When only updating the columns and that's done, stop here. if (col_rows > 0) { - win_put_linebuf(wp, wlv.row, 0, wlv.off, wlv.off, bg_attr, false); + wlv_put_linebuf(wp, &wlv, wlv.off, false, bg_attr, 0); // Need to update more screen lines if: // - 'statuscolumn' needs to be drawn, or // - LineNrAbove or LineNrBelow is used, or @@ -1616,7 +1598,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } } - if (cul_screenline && wlv.vcol >= left_curline_col && wlv.vcol < right_curline_col) { + if (cul_screenline && wlv.filler_todo <= 0 + && wlv.vcol >= left_curline_col && wlv.vcol < right_curline_col) { apply_cursorline_highlight(wp, &wlv); } @@ -1625,7 +1608,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s && lnum == wp->w_cursor.lnum && wlv.vcol >= wp->w_virtcol) { draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row); // don't clear anything after wlv.col - win_put_linebuf(wp, wlv.row, 0, wlv.col, wlv.col, bg_attr, false); + wlv_put_linebuf(wp, &wlv, wlv.col, false, bg_attr, 0); // Pretend we have finished updating the window. Except when // 'cursorcolumn' is set. if (wp->w_p_cuc) { @@ -1643,7 +1626,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } int extmark_attr = 0; - if (area_highlighting || spv->spv_has_spell || extra_check) { + if (wlv.filler_todo <= 0 + && (area_highlighting || spv->spv_has_spell || extra_check)) { if (wlv.n_extra == 0 || !wlv.extra_for_extmark) { wlv.reset_extra_attr = false; } @@ -1674,11 +1658,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } decor_need_recheck = false; } - if (wlv.filler_todo <= 0) { - extmark_attr = decor_redraw_col(wp, (colnr_T)(ptr - line), - may_have_inline_virt ? -3 : wlv.off, - selected, &decor_state); - } + extmark_attr = decor_redraw_col(wp, (colnr_T)(ptr - line), + may_have_inline_virt ? -3 : wlv.off, + selected, &decor_state); if (may_have_inline_virt) { handle_inline_virtual_text(wp, &wlv, ptr - line, selected); if (wlv.n_extra > 0 && wlv.virt_inline_hl_mode <= kHlModeReplace) { @@ -2236,9 +2218,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } else { int saved_nextra = wlv.n_extra; - if (wlv.vcol_off > 0) { + if (wlv.vcol_off_co > 0) { // there are characters to conceal - tab_len += wlv.vcol_off; + tab_len += wlv.vcol_off_co; } // boguscols before fix_for_boguscols() from above. if (wp->w_p_lcs_chars.tab1 && wlv.old_boguscols > 0 @@ -2280,27 +2262,27 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } // n_extra will be increased by fix_for_boguscols() - // macro below, so need to adjust for that here - if (wlv.vcol_off > 0) { - wlv.n_extra -= wlv.vcol_off; + // below, so need to adjust for that here + if (wlv.vcol_off_co > 0) { + wlv.n_extra -= wlv.vcol_off_co; } } } { - int vc_saved = wlv.vcol_off; + int vc_saved = wlv.vcol_off_co; // Tab alignment should be identical regardless of // 'conceallevel' value. So tab compensates of all // previous concealed characters, and thus resets - // vcol_off and boguscols accumulated so far in the + // vcol_off_co and boguscols accumulated so far in the // line. Note that the tab can be longer than // 'tabstop' when there are concealed characters. fix_for_boguscols(&wlv); // Make sure, the highlighting for the tab char will be // correctly set further below (effectively reverts the - // FIX_FOR_BOGSUCOLS macro). + // fix_for_boguscols() call). if (wlv.n_extra == tab_len + vc_saved && wp->w_p_list && wp->w_p_lcs_chars.tab1) { tab_len += vc_saved; @@ -2342,7 +2324,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s && wlv.line_attr == 0 && wlv.line_attr_lowprio == 0) { // In virtualedit, visual selections may extend beyond end of line - if (!(area_highlighting && virtual_active() + if (!(area_highlighting && virtual_active(wp) && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol)) { wlv.p_extra = ""; } @@ -2385,7 +2367,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s mb_schar = schar_from_ascii(mb_c); } else if (VIsual_active && (VIsual_mode == Ctrl_V || VIsual_mode == 'v') - && virtual_active() + && virtual_active(wp) && wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol && wlv.col < grid->cols) { @@ -2423,12 +2405,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } else { mb_schar = schar_from_ascii(' '); } + + if (utf_char2cells(mb_c) > 1) { + // When the first char to be concealed is double-width, + // need to advance one more virtual column. + wlv.n_extra++; + } + mb_c = schar_get_first_codepoint(mb_schar); prev_syntax_id = syntax_seqnr; if (wlv.n_extra > 0) { - wlv.vcol_off += wlv.n_extra; + wlv.vcol_off_co += wlv.n_extra; } wlv.vcol += wlv.n_extra; if (is_wrapped && wlv.n_extra > 0) { @@ -2454,11 +2443,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s // In the cursor line and we may be concealing characters: correct // the cursor column when we reach its position. - if (!did_wcol + // With 'virtualedit' we may never reach cursor position, but we still + // need to correct the cursor column, so do that at end of line. + if (!did_wcol && wlv.filler_todo <= 0 && wp == curwin && lnum == wp->w_cursor.lnum && conceal_cursor_line(wp) - && (int)wp->w_virtcol <= wlv.vcol + wlv.skip_cells) { + && (wlv.vcol + wlv.skip_cells >= wp->w_virtcol || mb_schar == NUL)) { wp->w_wcol = wlv.col - wlv.boguscols; + if (wlv.vcol + wlv.skip_cells < wp->w_virtcol) { + // Cursor beyond end of the line with 'virtualedit'. + wp->w_wcol += wp->w_virtcol - wlv.vcol - wlv.skip_cells; + } wp->w_wrow = wlv.row; did_wcol = true; wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; @@ -2546,7 +2541,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s : wlv.char_attr; linebuf_attr[wlv.off] = eol_attr; - linebuf_vcol[wlv.off] = MAXCOL; + linebuf_vcol[wlv.off] = wlv.vcol; wlv.col++; wlv.off++; wlv.vcol++; @@ -2569,13 +2564,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s advance_color_col(&wlv, vcol_hlc(wlv)); - bool has_virttext = false; // Make sure alignment is the same regardless // if listchars=eol:X is used or not. const int eol_skip = (lcs_eol_todo && eol_hl_off == 0 ? 1 : 0); if (has_decor) { - has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip); + decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip); + } + + for (int i = wlv.col; i < grid->cols; i++) { + linebuf_vcol[wlv.off + (i - wlv.col)] = wlv.vcol + (i - wlv.col); } if (((wp->w_p_cuc @@ -2583,7 +2581,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s && wp->w_virtcol < grid->cols * (ptrdiff_t)(wlv.row - startrow + 1) + start_col && lnum != wp->w_cursor.lnum) || wlv.color_cols || wlv.line_attr_lowprio || wlv.line_attr - || wlv.diff_hlf != 0 || has_virttext)) { + || wlv.diff_hlf != 0 || wp->w_buffer->terminal)) { int rightmost_vcol = get_rightmost_vcol(wp, wlv.color_cols); const int cuc_attr = win_hl_attr(wp, HLF_CUC); const int mc_attr = win_hl_attr(wp, HLF_MC); @@ -2597,47 +2595,39 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s : 0; const int base_attr = hl_combine_attr(wlv.line_attr_lowprio, diff_attr); - if (base_attr || wlv.line_attr || has_virttext) { + if (base_attr || wlv.line_attr || wp->w_buffer->terminal) { rightmost_vcol = INT_MAX; } while (wlv.col < grid->cols) { linebuf_char[wlv.off] = schar_from_ascii(' '); - linebuf_vcol[wlv.off] = MAXCOL; - wlv.col++; + advance_color_col(&wlv, vcol_hlc(wlv)); int col_attr = base_attr; - if (wp->w_p_cuc && vcol_hlc(wlv) == wp->w_virtcol) { - col_attr = cuc_attr; + if (wp->w_p_cuc && vcol_hlc(wlv) == wp->w_virtcol + && lnum != wp->w_cursor.lnum) { + col_attr = hl_combine_attr(col_attr, cuc_attr); } else if (wlv.color_cols && vcol_hlc(wlv) == *wlv.color_cols) { - col_attr = hl_combine_attr(wlv.line_attr_lowprio, mc_attr); + col_attr = hl_combine_attr(col_attr, mc_attr); + } + + if (wp->w_buffer->terminal && wlv.vcol < TERM_ATTRS_MAX) { + col_attr = hl_combine_attr(col_attr, term_attrs[wlv.vcol]); } col_attr = hl_combine_attr(col_attr, wlv.line_attr); linebuf_attr[wlv.off] = col_attr; + // linebuf_vcol[] already filled by the for loop above wlv.off++; + wlv.col++; + wlv.vcol++; - if (vcol_hlc(wlv) >= rightmost_vcol) { + if (vcol_hlc(wlv) > rightmost_vcol) { break; } - - wlv.vcol += 1; - } - } - - // TODO(bfredl): integrate with the common beyond-the-end-loop - if (wp->w_buffer->terminal) { - // terminal buffers may need to highlight beyond the end of the logical line - while (wlv.col >= 0 && wlv.col < grid->cols) { - linebuf_char[wlv.off] = schar_from_ascii(' '); - linebuf_attr[wlv.off] = wlv.vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[wlv.vcol]; - linebuf_vcol[wlv.off] = wlv.vcol; - wlv.off++; - wlv.vcol++; - wlv.col++; } } @@ -2645,7 +2635,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, grid->cols, 0); } draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row); - win_put_linebuf(wp, wlv.row, 0, wlv.col, grid->cols, bg_attr, false); + // Set increasing virtual columns in grid->vcols[] to set correct curswant + // (or "coladd" for 'virtualedit') when clicking after end of line. + wlv_put_linebuf(wp, &wlv, wlv.col, true, bg_attr, SLF_INC_VCOL); wlv.row++; // Update w_cline_height and w_cline_folded if the cursor line was @@ -2712,10 +2704,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } } - // Apply lowest-priority line attr now, so everything can override it. - wlv.char_attr = hl_combine_attr(wlv.line_attr_lowprio, wlv.char_attr); + if (wlv.filler_todo <= 0) { + // Apply lowest-priority line attr now, so everything can override it. + wlv.char_attr = hl_combine_attr(wlv.line_attr_lowprio, wlv.char_attr); + } - vcol_prev = wlv.vcol; + if (wlv.filler_todo <= 0) { + vcol_prev = wlv.vcol; + } // Store character to be displayed. // Skip characters that are left of the screen for 'nowrap'. @@ -2753,11 +2749,21 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s wlv.off++; wlv.col++; } else if (wp->w_p_cole > 0 && is_concealing) { + bool concealed_wide = utf_char2cells(mb_c) > 1; + wlv.skip_cells--; - wlv.vcol_off++; + wlv.vcol_off_co++; + if (concealed_wide) { + // When a double-width char is concealed, + // need to advance one more virtual column. + wlv.vcol++; + wlv.vcol_off_co++; + } + if (wlv.n_extra > 0) { - wlv.vcol_off += wlv.n_extra; + wlv.vcol_off_co += wlv.n_extra; } + if (is_wrapped) { // Special voodoo required if 'wrap' is on. // @@ -2778,7 +2784,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s wlv.n_attr = 0; } - if (utf_char2cells(mb_c) > 1) { + if (concealed_wide) { // Need to fill two screen columns. wlv.boguscols++; wlv.col++; @@ -2813,7 +2819,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s wlv.char_attr = vcol_save_attr; } - // restore attributes after "predeces" in 'listchars' + // restore attributes after "precedes" in 'listchars' if (n_attr3 > 0 && --n_attr3 == 0) { wlv.char_attr = saved_attr3; } @@ -2855,6 +2861,23 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s && !wp->w_p_rl; // Not right-to-left. int draw_col = wlv.col - wlv.boguscols; + + for (int i = draw_col; i < grid->cols; i++) { + linebuf_vcol[wlv.off + (i - draw_col)] = wlv.vcol - 1; + } + + // Apply 'cursorline' highlight. + if (wlv.boguscols != 0 && (wlv.line_attr_lowprio != 0 || wlv.line_attr != 0)) { + int attr = hl_combine_attr(wlv.line_attr_lowprio, wlv.line_attr); + while (draw_col < grid->cols) { + linebuf_char[wlv.off] = schar_from_char(' '); + linebuf_attr[wlv.off] = attr; + // linebuf_vcol[] already filled by the for loop above + wlv.off++; + draw_col++; + } + } + if (virt_line_offset >= 0) { draw_virt_text_item(buf, virt_line_offset, kv_A(virt_lines, virt_line_index).line, kHlModeReplace, grid->cols, 0); @@ -2862,7 +2885,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s draw_virt_text(wp, buf, win_col_offset, &draw_col, wlv.row); } - win_put_linebuf(wp, wlv.row, 0, draw_col, grid->cols, bg_attr, wrap); + wlv_put_linebuf(wp, &wlv, draw_col, true, bg_attr, wrap ? SLF_WRAP : 0); if (wrap) { ScreenGrid *current_grid = grid; int current_row = wlv.row; @@ -2874,7 +2897,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } wlv.boguscols = 0; - wlv.vcol_off = 0; + wlv.vcol_off_co = 0; wlv.row++; // When not wrapping and finished diff lines, break here. @@ -2922,19 +2945,28 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s return wlv.row; } -static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clear_width, - int bg_attr, bool wrap) +/// Call grid_put_linebuf() using values from "wlv". +/// Also takes care of putting "<<<" on the first line for 'smoothscroll' +/// when 'showbreak' is not set. +/// +/// @param clear_end clear until the end of the screen line. +/// @param flags for grid_put_linebuf(), but shouldn't contain SLF_RIGHTLEFT. +static void wlv_put_linebuf(win_T *wp, const winlinevars_T *wlv, int endcol, bool clear_end, + int bg_attr, int flags) { ScreenGrid *grid = &wp->w_grid; - int start_col = 0; + int startcol = 0; + int clear_width = clear_end ? grid->cols : endcol; + assert(!(flags & SLF_RIGHTLEFT)); if (wp->w_p_rl) { - linebuf_mirror(&start_col, &endcol, &clear_width, grid->cols); + linebuf_mirror(&startcol, &endcol, &clear_width, grid->cols); + flags |= SLF_RIGHTLEFT; } // Take care of putting "<<<" on the first line for 'smoothscroll'. - if (row == 0 && wp->w_skipcol > 0 + if (wlv->row == 0 && wp->w_skipcol > 0 // do not overwrite the 'showbreak' text with "<<<" && *get_showbreak_value(wp) == NUL // do not overwrite the 'listchars' "precedes" text with "<<<" @@ -2959,6 +2991,8 @@ static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clea } } + int row = wlv->row; + int coloff = 0; grid_adjust(&grid, &row, &coloff); - grid_put_linebuf(grid, row, coloff, start_col, endcol, clear_width, wp->w_p_rl, bg_attr, wrap); + grid_put_linebuf(grid, row, coloff, startcol, endcol, clear_width, bg_attr, wlv->vcol - 1, flags); } diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 145229bacc..bda0ccc870 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -59,6 +59,7 @@ #include <stdlib.h> #include <string.h> +#include "klib/kvec.h" #include "nvim/api/private/defs.h" #include "nvim/ascii_defs.h" #include "nvim/autocmd.h" @@ -119,8 +120,6 @@ #include "nvim/vim_defs.h" #include "nvim/window.h" -#include "klib/kvec.h" - /// corner value flags for hsep_connected and vsep_connected typedef enum { WC_TOP_LEFT = 0, @@ -180,8 +179,12 @@ bool default_grid_alloc(void) // Allocation of the screen buffers is done only when the size changes and // when Rows and Columns have been set and we have started doing full // screen stuff. - if ((default_grid.chars != NULL && Rows == default_grid.rows && Columns == default_grid.cols) - || Rows == 0 || Columns == 0 || (!full_screen && default_grid.chars == NULL)) { + if ((default_grid.chars != NULL + && Rows == default_grid.rows + && Columns == default_grid.cols) + || Rows == 0 + || Columns == 0 + || (!full_screen && default_grid.chars == NULL)) { resizing = false; return false; } @@ -199,8 +202,8 @@ bool default_grid_alloc(void) grid_alloc(&default_grid, Rows, Columns, true, true); stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size); - tab_page_click_defs - = stl_alloc_click_defs(tab_page_click_defs, Columns, &tab_page_click_defs_size); + tab_page_click_defs = stl_alloc_click_defs(tab_page_click_defs, Columns, + &tab_page_click_defs_size); default_grid.comp_height = Rows; default_grid.comp_width = Columns; @@ -223,7 +226,8 @@ void screenclear(void) // blank out the default grid for (int i = 0; i < default_grid.rows; i++) { - grid_clear_line(&default_grid, default_grid.line_offset[i], default_grid.cols, true); + grid_clear_line(&default_grid, default_grid.line_offset[i], + default_grid.cols, true); } ui_call_grid_clear(1); // clear the display @@ -270,7 +274,7 @@ void screen_resize(int width, int height) return; } - if (width < 0 || height < 0) { // just checking... + if (width < 0 || height < 0) { // just checking... return; } @@ -311,9 +315,9 @@ void screen_resize(int width, int height) RedrawingDisabled++; - win_new_screensize(); // fit the windows in the new sized screen + win_new_screensize(); // fit the windows in the new sized screen - comp_col(); // recompute columns for shown command and ruler + comp_col(); // recompute columns for shown command and ruler RedrawingDisabled--; @@ -407,7 +411,8 @@ void check_screensize(void) /// Return true if redrawing should currently be done. bool redrawing(void) { - return !RedrawingDisabled && !(p_lz && char_avail() && !KeyTyped && !do_redraw); + return !RedrawingDisabled + && !(p_lz && char_avail() && !KeyTyped && !do_redraw); } /// Redraw the parts of the screen that is marked for redraw. @@ -416,7 +421,14 @@ bool redrawing(void) /// and redraw_all_later() to mark parts of the screen as needing a redraw. int update_screen(void) { - static bool did_intro = false; + static bool still_may_intro = true; + if (still_may_intro) { + if (!may_show_intro()) { + redraw_later(firstwin, UPD_NOT_VALID); + still_may_intro = false; + } + } + bool is_stl_global = global_stl_height() > 0; // Don't do anything if the screen structures are (not yet) valid. @@ -478,7 +490,8 @@ int update_screen(void) if (msg_grid.chars) { // non-displayed part of msg_grid is considered invalid. for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) { - grid_clear_line(&msg_grid, msg_grid.line_offset[i], msg_grid.cols, i < p_ch); + grid_clear_line(&msg_grid, msg_grid.line_offset[i], + msg_grid.cols, i < p_ch); } } msg_grid.throttled = false; @@ -488,7 +501,8 @@ int update_screen(void) if (type == UPD_NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) { was_invalidated = ui_comp_set_screen_valid(false); for (int i = valid; i < Rows - p_ch; i++) { - grid_clear_line(&default_grid, default_grid.line_offset[i], Columns, false); + grid_clear_line(&default_grid, default_grid.line_offset[i], + Columns, false); } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_floating) { @@ -534,10 +548,10 @@ int update_screen(void) hl_changed = true; } - if (type == UPD_CLEAR) { // first clear screen - screenclear(); // will reset clear_cmdline - // and set UPD_NOT_VALID for each window - cmdline_screen_cleared(); // clear external cmdline state + if (type == UPD_CLEAR) { // first clear screen + screenclear(); // will reset clear_cmdline + // and set UPD_NOT_VALID for each window + cmdline_screen_cleared(); // clear external cmdline state type = UPD_NOT_VALID; // must_redraw may be set indirectly, avoid another redraw later must_redraw = 0; @@ -561,7 +575,7 @@ int update_screen(void) redraw_tabline = true; } - if (clear_cmdline) { // going to clear cmdline (done below) + if (clear_cmdline) { // going to clear cmdline (done below) msg_check_for_delay(false); } @@ -570,9 +584,8 @@ int update_screen(void) // TODO(bfredl): special casing curwin here is SÅ JÄVLA BULL. // Either this should be done for all windows or not at all. if (curwin->w_redr_type < UPD_NOT_VALID - && curwin->w_nrwidth - != ((curwin->w_p_nu || curwin->w_p_rnu || *curwin->w_p_stc) ? number_width(curwin) - : 0)) { + && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu || *curwin->w_p_stc) + ? number_width(curwin) : 0)) { curwin->w_redr_type = UPD_NOT_VALID; } @@ -594,7 +607,8 @@ int update_screen(void) buf_T *buf = wp->w_buffer; if (buf->b_mod_set) { - if (buf->b_mod_tick_syn < display_tick && syntax_present(wp)) { + if (buf->b_mod_tick_syn < display_tick + && syntax_present(wp)) { syn_stack_apply_changes(buf); buf->b_mod_tick_syn = display_tick; } @@ -666,10 +680,9 @@ int update_screen(void) } // May put up an introductory message when not editing a file - if (!did_intro) { - maybe_intro_message(); + if (still_may_intro) { + intro_message(false); } - did_intro = true; decor_providers_invoke_end(); @@ -758,8 +771,8 @@ static void win_redr_border(win_T *wp) } if (wp->w_config.title) { - int title_col - = win_get_bordertext_col(icol, wp->w_config.title_width, wp->w_config.title_pos); + int title_col = win_get_bordertext_col(icol, wp->w_config.title_width, + wp->w_config.title_pos); win_redr_bordertext(wp, wp->w_config.title_chunks, title_col); } if (adj[1]) { @@ -794,8 +807,8 @@ static void win_redr_border(win_T *wp) } if (wp->w_config.footer) { - int footer_col - = win_get_bordertext_col(icol, wp->w_config.footer_width, wp->w_config.footer_pos); + int footer_col = win_get_bordertext_col(icol, wp->w_config.footer_width, + wp->w_config.footer_pos); win_redr_bordertext(wp, wp->w_config.footer_chunks, footer_col); } if (adj[1]) { @@ -808,24 +821,25 @@ static void win_redr_border(win_T *wp) /// Set cursor to its position in the current window. void setcursor(void) { - setcursor_mayforce(false); + setcursor_mayforce(curwin, false); } /// Set cursor to its position in the current window. /// @param force when true, also when not redrawing. -void setcursor_mayforce(bool force) +void setcursor_mayforce(win_T *wp, bool force) { if (force || redrawing()) { - validate_cursor(); + validate_cursor(wp); - ScreenGrid *grid = &curwin->w_grid; - int row = curwin->w_wrow; - int col = curwin->w_wcol; - if (curwin->w_p_rl) { + ScreenGrid *grid = &wp->w_grid; + int row = wp->w_wrow; + int col = wp->w_wcol; + if (wp->w_p_rl) { // With 'rightleft' set and the cursor on a double-wide character, // position it on the leftmost column. - col = curwin->w_width_inner - curwin->w_wcol - - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 && vim_isprintc(gchar_cursor())) ? 2 : 1); + char *cursor = ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) + wp->w_cursor.col; + col = wp->w_width_inner - wp->w_wcol - ((utf_ptr2cells(cursor) == 2 + && vim_isprintc(utf_ptr2char(cursor))) ? 2 : 1); } grid_adjust(&grid, &row, &col); @@ -839,19 +853,22 @@ void setcursor_mayforce(bool force) void show_cursor_info_later(bool force) { int state = get_real_state(); - int empty_line - = (State & MODE_INSERT) == 0 && *ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum) == NUL; + int empty_line = (State & MODE_INSERT) == 0 + && *ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum) == NUL; // Only draw when something changed. - validate_virtcol_win(curwin); - if (force || curwin->w_cursor.lnum != curwin->w_stl_cursor.lnum + validate_virtcol(curwin); + if (force + || curwin->w_cursor.lnum != curwin->w_stl_cursor.lnum || curwin->w_cursor.col != curwin->w_stl_cursor.col || curwin->w_virtcol != curwin->w_stl_virtcol || curwin->w_cursor.coladd != curwin->w_stl_cursor.coladd || curwin->w_topline != curwin->w_stl_topline || curwin->w_buffer->b_ml.ml_line_count != curwin->w_stl_line_count - || curwin->w_topfill != curwin->w_stl_topfill || empty_line != curwin->w_stl_empty - || reg_recording != curwin->w_stl_recording || state != curwin->w_stl_state + || curwin->w_topfill != curwin->w_stl_topfill + || empty_line != curwin->w_stl_empty + || reg_recording != curwin->w_stl_recording + || state != curwin->w_stl_state || (VIsual_active && VIsual_mode != curwin->w_stl_visual_mode)) { if (curwin->w_status_height || global_stl_height()) { curwin->w_redr_status = true; @@ -863,7 +880,8 @@ void show_cursor_info_later(bool force) curwin->w_redr_status = true; } - if ((p_icon && (stl_syntax & STL_IN_ICON)) || (p_title && (stl_syntax & STL_IN_TITLE))) { + if ((p_icon && (stl_syntax & STL_IN_ICON)) + || (p_title && (stl_syntax & STL_IN_TITLE))) { need_maketitle = true; } } @@ -909,13 +927,21 @@ int showmode(void) msg_ext_clear(true); } - // don't make non-flushed message part of the showmode + // Don't make non-flushed message part of the showmode and reset global + // variables before flushing to to avoid recursiveness. + bool draw_mode = redraw_mode; + bool clear_cmd = clear_cmdline; + redraw_cmdline = false; + redraw_mode = false; + clear_cmdline = false; msg_ext_ui_flush(); msg_grid_validate(); bool do_mode = ((p_smd && msg_silent == 0) - && ((State & MODE_TERMINAL) || (State & MODE_INSERT) || restart_edit != NUL + && ((State & MODE_TERMINAL) + || (State & MODE_INSERT) + || restart_edit != NUL || VIsual_active)); bool can_show_mode = (p_ch != 0 || ui_has(kUIMessages)); @@ -930,14 +956,14 @@ int showmode(void) msg_check_for_delay(false); // if the cmdline is more than one line high, erase top lines - bool need_clear = clear_cmdline; - if (clear_cmdline && cmdline_row < Rows - 1) { + bool need_clear = clear_cmd; + if (clear_cmd && cmdline_row < Rows - 1) { msg_clr_cmdline(); // will reset clear_cmdline } // Position on the last line in the window, column 0 msg_pos_mode(); - int attr = HL_ATTR(HLF_CM); // Highlight mode + int attr = HL_ATTR(HLF_CM); // Highlight mode // When the screen is too narrow to show the entire mode message, // avoid scrolling and truncate instead. @@ -993,8 +1019,8 @@ int showmode(void) msg_puts_attr(_(" REVERSE"), attr); } msg_puts_attr(_(" INSERT"), attr); - } else if (restart_edit == 'I' || restart_edit == 'i' || restart_edit == 'a' - || restart_edit == 'A') { + } else if (restart_edit == 'I' || restart_edit == 'i' + || restart_edit == 'a' || restart_edit == 'A') { if (curbuf->terminal) { msg_puts_attr(_(" (terminal)"), attr); } else { @@ -1008,7 +1034,8 @@ int showmode(void) if (State & MODE_LANGMAP) { if (curwin->w_p_arab) { msg_puts_attr(_(" Arabic"), attr); - } else if (get_keymap_str(curwin, " (%s)", NameBuff, MAXPATHL)) { + } else if (get_keymap_str(curwin, " (%s)", + NameBuff, MAXPATHL)) { msg_puts_attr(NameBuff, attr); } } @@ -1021,25 +1048,21 @@ int showmode(void) // Don't concatenate separate words to avoid translation // problems. - switch ((VIsual_select ? 4 : 0) + (VIsual_mode == Ctrl_V) * 2 + (VIsual_mode == 'V')) { + switch ((VIsual_select ? 4 : 0) + + (VIsual_mode == Ctrl_V) * 2 + + (VIsual_mode == 'V')) { case 0: - p = N_(" VISUAL"); - break; + p = N_(" VISUAL"); break; case 1: - p = N_(" VISUAL LINE"); - break; + p = N_(" VISUAL LINE"); break; case 2: - p = N_(" VISUAL BLOCK"); - break; + p = N_(" VISUAL BLOCK"); break; case 4: - p = N_(" SELECT"); - break; + p = N_(" SELECT"); break; case 5: - p = N_(" SELECT LINE"); - break; + p = N_(" SELECT LINE"); break; default: - p = N_(" SELECT BLOCK"); - break; + p = N_(" SELECT BLOCK"); break; } msg_puts_attr(_(p), attr); } @@ -1048,26 +1071,27 @@ int showmode(void) need_clear = true; } - if (reg_recording != 0 && edit_submode == NULL // otherwise it gets too long - ) { + if (reg_recording != 0 + && edit_submode == NULL // otherwise it gets too long + ) { recording_mode(attr); need_clear = true; } mode_displayed = true; - if (need_clear || clear_cmdline || redraw_mode) { + if (need_clear || clear_cmd || draw_mode) { msg_clr_eos(); } - msg_didout = false; // overwrite this message + msg_didout = false; // overwrite this message length = msg_col; msg_col = 0; msg_no_more = false; lines_left = save_lines_left; - need_wait_return = nwr_save; // never ask for hit-return for this - } else if (clear_cmdline && msg_silent == 0) { + need_wait_return = nwr_save; // never ask for hit-return for this + } else if (clear_cmd && msg_silent == 0) { // Clear the whole command line. Will reset "clear_cmdline". msg_clr_cmdline(); - } else if (redraw_mode) { + } else if (draw_mode) { msg_pos_mode(); msg_clr_eos(); } @@ -1090,10 +1114,6 @@ int showmode(void) grid_line_flush(); } - redraw_cmdline = false; - redraw_mode = false; - clear_cmdline = false; - return length; } @@ -1142,14 +1162,12 @@ static void recording_mode(int attr) } msg_puts_attr(_("recording"), attr); - char reg_str[8]; - reg_str[utf_char2bytes(reg_recording, reg_str)] = 0; - char s[16]; - snprintf(s, ARRAY_SIZE(s), " @%s", reg_str); + char s[4]; + snprintf(s, ARRAY_SIZE(s), " @%c", reg_recording); msg_puts_attr(s, attr); } -#define COL_RULER 17 // columns needed by standard ruler +#define COL_RULER 17 // columns needed by standard ruler /// Compute columns for ruler and shown command. 'sc_col' is also used to /// decide what the maximum length of a message on the status line can be. @@ -1170,15 +1188,17 @@ void comp_col(void) } if (p_sc && *p_sloc == 'l') { sc_col += SHOWCMD_COLS; - if (!p_ru || last_has_status) { // no need for separating space + if (!p_ru || last_has_status) { // no need for separating space sc_col++; } } - assert(sc_col >= 0 && INT_MIN + sc_col <= Columns); + assert(sc_col >= 0 + && INT_MIN + sc_col <= Columns); sc_col = Columns - sc_col; - assert(ru_col >= 0 && INT_MIN + ru_col <= Columns); + assert(ru_col >= 0 + && INT_MIN + ru_col <= Columns); ru_col = Columns - ru_col; - if (sc_col <= 0) { // screen too narrow, will become a mess + if (sc_col <= 0) { // screen too narrow, will become a mess sc_col = 1; } if (ru_col <= 0) { @@ -1223,7 +1243,8 @@ static bool win_redraw_signcols(win_T *wp) static bool hsep_connected(win_T *wp, WindowCorner corner) { bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT); - int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) ? wp->w_winrow - 1 : W_ENDROW(wp); + int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) + ? wp->w_winrow - 1 : W_ENDROW(wp); frame_T *fr = wp->w_frame; while (fr->fr_parent != NULL) { @@ -1257,8 +1278,8 @@ static bool hsep_connected(win_T *wp, WindowCorner corner) static bool vsep_connected(win_T *wp, WindowCorner corner) { bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT); - int sep_col - = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) ? wp->w_wincol - 1 : W_ENDCOL(wp); + int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) + ? wp->w_wincol - 1 : W_ENDCOL(wp); frame_T *fr = wp->w_frame; while (fr->fr_parent != NULL) { @@ -1423,19 +1444,19 @@ static void draw_sep_connectors_win(win_T *wp) /// bot: from bot_start to last row (when scrolled up) static void win_update(win_T *wp) { - int top_end = 0; // Below last row of the top area that needs - // updating. 0 when no top area updating. - int mid_start = 999; // first row of the mid area that needs - // updating. 999 when no mid area updating. - int mid_end = 0; // Below last row of the mid area that needs - // updating. 0 when no mid area updating. - int bot_start = 999; // first row of the bot area that needs - // updating. 999 when no bot area updating - bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit - bool top_to_mod = false; // redraw above mod_top - - int bot_scroll_start = 999; // first line that needs to be redrawn due to - // scrolling. only used for EOB + int top_end = 0; // Below last row of the top area that needs + // updating. 0 when no top area updating. + int mid_start = 999; // first row of the mid area that needs + // updating. 999 when no mid area updating. + int mid_end = 0; // Below last row of the mid area that needs + // updating. 0 when no mid area updating. + int bot_start = 999; // first row of the bot area that needs + // updating. 999 when no bot area updating + bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit + bool top_to_mod = false; // redraw above mod_top + + int bot_scroll_start = 999; // first line that needs to be redrawn due to + // scrolling. only used for EOB static bool recursive = false; // being called recursively @@ -1444,10 +1465,9 @@ static void win_update(win_T *wp) DID_NONE = 1, // didn't update a line DID_LINE = 2, // updated a normal line DID_FOLD = 3, // updated a folded line - } did_update - = DID_NONE; + } did_update = DID_NONE; - linenr_T syntax_last_parsed = 0; // last parsed text line + linenr_T syntax_last_parsed = 0; // last parsed text line linenr_T mod_top = 0; linenr_T mod_bot = 0; @@ -1501,7 +1521,7 @@ static void win_update(win_T *wp) // Make sure skipcol is valid, it depends on various options and the window // width. - if (wp->w_skipcol > 0) { + if (wp->w_skipcol > 0 && wp->w_width_inner > win_col_off(wp)) { int w = 0; int width1 = wp->w_width_inner - win_col_off(wp); int width2 = width1 + win_col_off2(wp); @@ -1555,12 +1575,14 @@ static void win_update(win_T *wp) // previous line invalid. Simple solution: redraw all visible // lines above the change. // Same for a match pattern. - if (screen_search_hl.rm.regprog != NULL && re_multiline(screen_search_hl.rm.regprog)) { + if (screen_search_hl.rm.regprog != NULL + && re_multiline(screen_search_hl.rm.regprog)) { top_to_mod = true; } else { const matchitem_T *cur = wp->w_match_head; while (cur != NULL) { - if (cur->mit_match.regprog != NULL && re_multiline(cur->mit_match.regprog)) { + if (cur->mit_match.regprog != NULL + && re_multiline(cur->mit_match.regprog)) { top_to_mod = true; break; } @@ -1597,14 +1619,14 @@ static void win_update(win_T *wp) } } - hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL); + hasFolding(wp, mod_top, &mod_top, NULL); if (mod_top > lnumt) { mod_top = lnumt; } // Now do the same for the bottom line (one above mod_bot). mod_bot--; - hasFoldingWin(wp, mod_bot, NULL, &mod_bot, true, NULL); + hasFolding(wp, mod_bot, NULL, &mod_bot); mod_bot++; if (mod_bot < lnumb) { mod_bot = lnumb; @@ -1653,11 +1675,13 @@ static void win_update(win_T *wp) // 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up // 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in // w_lines[] that needs updating. - if ((type == UPD_VALID || type == UPD_SOME_VALID || type == UPD_INVERTED - || type == UPD_INVERTED_ALL) + if ((type == UPD_VALID || type == UPD_SOME_VALID + || type == UPD_INVERTED || type == UPD_INVERTED_ALL) && !wp->w_botfill && !wp->w_old_botfill) { - if (mod_top != 0 && wp->w_topline == mod_top - && (!wp->w_lines[0].wl_valid || wp->w_topline == wp->w_lines[0].wl_lnum)) { + if (mod_top != 0 + && wp->w_topline == mod_top + && (!wp->w_lines[0].wl_valid + || wp->w_topline == wp->w_lines[0].wl_lnum)) { // w_topline is the first changed line and window is not scrolled, // the scrolling from changed lines will be done further down. } else if (wp->w_lines[0].wl_valid @@ -1675,13 +1699,13 @@ static void win_update(win_T *wp) if (j >= wp->w_grid.rows - 2) { break; } - hasFoldingWin(wp, ln, NULL, &ln, true, NULL); + hasFolding(wp, ln, NULL, &ln); } } else { j = wp->w_lines[0].wl_lnum - wp->w_topline; } - if (j < wp->w_grid.rows - 2) { // not too far off - int i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1, true); + if (j < wp->w_grid.rows - 2) { // not too far off + int i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1, wp->w_height_inner); // insert extra lines for previously invisible filler lines if (wp->w_lines[0].wl_lnum != wp->w_topline) { i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill; @@ -1726,7 +1750,8 @@ static void win_update(win_T *wp) int j = -1; int row = 0; for (int i = 0; i < wp->w_lines_valid; i++) { - if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lnum == wp->w_topline) { + if (wp->w_lines[i].wl_valid + && wp->w_lines[i].wl_lnum == wp->w_topline) { j = i; break; } @@ -1765,7 +1790,8 @@ static void win_update(win_T *wp) wp->w_lines[idx] = wp->w_lines[j]; // stop at line that didn't fit, unless it is still // valid (no lines deleted) - if (row > 0 && bot_start + row + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { + if (row > 0 && bot_start + row + + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { wp->w_lines_valid = idx + 1; break; } @@ -1781,8 +1807,8 @@ static void win_update(win_T *wp) // Correct the first entry for filler lines at the top // when it won't get updated below. if (win_may_fill(wp) && bot_start > 0) { - wp->w_lines[0].wl_size - = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true) + wp->w_topfill); + wp->w_lines[0].wl_size = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true) + + wp->w_topfill); } } } @@ -1845,13 +1871,15 @@ static void win_update(win_T *wp) } else { from = wp->w_old_cursor_lnum; to = curwin->w_cursor.lnum; - if (from == 0) { // Visual mode just started + if (from == 0) { // Visual mode just started from = to; } } - if (VIsual.lnum != wp->w_old_visual_lnum || VIsual.col != wp->w_old_visual_col) { - if (wp->w_old_visual_lnum < from && wp->w_old_visual_lnum != 0) { + if (VIsual.lnum != wp->w_old_visual_lnum + || VIsual.col != wp->w_old_visual_col) { + if (wp->w_old_visual_lnum < from + && wp->w_old_visual_lnum != 0) { from = wp->w_old_visual_lnum; } if (wp->w_old_visual_lnum > to) { @@ -1883,7 +1911,7 @@ static void win_update(win_T *wp) // Highlight to the end of the line, unless 'virtualedit' has // "block". if (curwin->w_curswant == MAXCOL) { - if (get_ve_flags() & VE_BLOCK) { + if (get_ve_flags(curwin) & VE_BLOCK) { pos_T pos; int cursor_above = curwin->w_cursor.lnum < VIsual.lnum; @@ -1907,7 +1935,8 @@ static void win_update(win_T *wp) } } - if (fromc != wp->w_old_cursor_fcol || toc != wp->w_old_cursor_lcol) { + if (fromc != wp->w_old_cursor_fcol + || toc != wp->w_old_cursor_lcol) { if (from > VIsual.lnum) { from = VIsual.lnum; } @@ -1961,7 +1990,7 @@ static void win_update(win_T *wp) } else { mid_start = 0; } - while (lnum < from && idx < wp->w_lines_valid) { // find start + while (lnum < from && idx < wp->w_lines_valid) { // find start if (wp->w_lines[idx].wl_valid) { mid_start += wp->w_lines[idx].wl_size; } else if (!scrolled_down) { @@ -1976,8 +2005,9 @@ static void win_update(win_T *wp) } srow += mid_start; mid_end = wp->w_grid.rows; - for (; idx < wp->w_lines_valid; idx++) { // find end - if (wp->w_lines[idx].wl_valid && wp->w_lines[idx].wl_lnum >= to + 1) { + for (; idx < wp->w_lines_valid; idx++) { // find end + if (wp->w_lines[idx].wl_valid + && wp->w_lines[idx].wl_lnum >= to + 1) { // Only update until first row of this line mid_end = srow; break; @@ -2021,12 +2051,12 @@ static void win_update(win_T *wp) } // Update all the window rows. - int idx = 0; // first entry in w_lines[].wl_size - int row = 0; // current window row to display - int srow = 0; // starting row of the current line + int idx = 0; // first entry in w_lines[].wl_size + int row = 0; // current window row to display + int srow = 0; // starting row of the current line - bool eof = false; // if true, we hit the end of the file - bool didline = false; // if true, we finished the last line + bool eof = false; // if true, we hit the end of the file + bool didline = false; // if true, we finished the last line while (true) { // stop updating when reached the end of the window (check for _past_ // the end of the window is at the end of the loop) @@ -2052,19 +2082,27 @@ static void win_update(win_T *wp) // When syntax folding is being used, the saved syntax states will // already have been updated, we can't see where the syntax state is // the same again, just update until the end of the window. - if (row < top_end || (row >= mid_start && row < mid_end) || top_to_mod - || idx >= wp->w_lines_valid || (row + wp->w_lines[idx].wl_size > bot_start) + if (row < top_end + || (row >= mid_start && row < mid_end) + || top_to_mod + || idx >= wp->w_lines_valid + || (row + wp->w_lines[idx].wl_size > bot_start) || (mod_top != 0 && (lnum == mod_top || (lnum >= mod_top - && (lnum < mod_bot || did_update == DID_FOLD - || (did_update == DID_LINE && syntax_present(wp) - && ((foldmethodIsSyntax(wp) && hasAnyFolding(wp)) + && (lnum < mod_bot + || did_update == DID_FOLD + || (did_update == DID_LINE + && syntax_present(wp) + && ((foldmethodIsSyntax(wp) + && hasAnyFolding(wp)) || syntax_check_changed(lnum))) // match in fixed position might need redraw // if lines were inserted or deleted - || (wp->w_match_head != NULL && buf->b_mod_xlines != 0))))) - || lnum == wp->w_cursorline || lnum == wp->w_last_cursorline) { + || (wp->w_match_head != NULL + && buf->b_mod_xlines != 0))))) + || lnum == wp->w_cursorline + || lnum == wp->w_last_cursorline) { if (lnum == mod_top) { top_to_mod = false; } @@ -2074,7 +2112,9 @@ static void win_update(win_T *wp) // Don't do this when the change continues until the end. // Don't scroll when dollar_vcol >= 0, keep the "$". // Don't scroll when redrawing the top, scrolled already above. - if (lnum == mod_top && mod_bot != MAXLNUM && !(dollar_vcol >= 0 && mod_bot == mod_top + 1) + if (lnum == mod_top + && mod_bot != MAXLNUM + && !(dollar_vcol >= 0 && mod_bot == mod_top + 1) && row >= top_end) { int old_rows = 0; linenr_T l; @@ -2086,15 +2126,18 @@ static void win_update(win_T *wp) for (i = idx; i < wp->w_lines_valid; i++) { // Only valid lines have a meaningful wl_lnum. Invalid // lines are part of the changed area. - if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lnum == mod_bot) { + if (wp->w_lines[i].wl_valid + && wp->w_lines[i].wl_lnum == mod_bot) { break; } old_rows += wp->w_lines[i].wl_size; - if (wp->w_lines[i].wl_valid && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) { + if (wp->w_lines[i].wl_valid + && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) { // Must have found the last valid entry above mod_bot. // Add following invalid entries. i++; - while (i < wp->w_lines_valid && !wp->w_lines[i].wl_valid) { + while (i < wp->w_lines_valid + && !wp->w_lines[i].wl_valid) { old_rows += wp->w_lines[i++].wl_size; } break; @@ -2113,7 +2156,7 @@ static void win_update(win_T *wp) // rows, and may insert/delete lines int j = idx; for (l = lnum; l < mod_bot; l++) { - if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) { + if (hasFolding(wp, l, NULL, &l)) { new_rows++; } else if (l == wp->w_topline) { int n = plines_win_nofill(wp, l, false) + wp->w_topfill; @@ -2177,7 +2220,8 @@ static void win_update(win_T *wp) } wp->w_lines[j] = wp->w_lines[i]; // stop at a line that won't fit - if (x + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) { + if (x + (int)wp->w_lines[j].wl_size + > wp->w_grid.rows) { wp->w_lines_valid = j + 1; break; } @@ -2187,8 +2231,8 @@ static void win_update(win_T *wp) if (bot_start > x) { bot_start = x; } - } else { // j > i - // move entries in w_lines[] downwards + } else { // j > i + // move entries in w_lines[] downwards j -= i; wp->w_lines_valid += (linenr_T)j; if (wp->w_lines_valid > wp->w_grid.rows) { @@ -2213,20 +2257,25 @@ static void win_update(win_T *wp) // When lines are folded, display one line for all of them. // Otherwise, display normally (can be several display lines when // 'wrap' is on). - foldinfo_T foldinfo - = wp->w_p_cul && lnum == wp->w_cursor.lnum ? cursorline_fi : fold_info(wp, lnum); - - if (foldinfo.fi_lines == 0 && idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid - && wp->w_lines[idx].wl_lnum == lnum && lnum > wp->w_topline + foldinfo_T foldinfo = wp->w_p_cul && lnum == wp->w_cursor.lnum + ? cursorline_fi : fold_info(wp, lnum); + + if (foldinfo.fi_lines == 0 + && idx < wp->w_lines_valid + && wp->w_lines[idx].wl_valid + && wp->w_lines[idx].wl_lnum == lnum + && lnum > wp->w_topline && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) - && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows && win_get_fill(wp, lnum) == 0) { + && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows + && win_get_fill(wp, lnum) == 0) { // This line is not going to fit. Don't draw anything here, // will draw "@ " lines below. row = wp->w_grid.rows + 1; } else { prepare_search_hl(wp, &screen_search_hl, lnum); // Let the syntax stuff know we skipped a few lines. - if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum && syntax_present(wp)) { + if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum + && syntax_present(wp)) { syntax_end_parsing(wp, syntax_last_parsed + 1); } @@ -2234,8 +2283,8 @@ static void win_update(win_T *wp) // Display one line spellvars_T zero_spv = { 0 }; - row = win_line(wp, lnum, srow, wp->w_grid.rows, 0, display_buf_line ? &spv : &zero_spv, - foldinfo); + row = win_line(wp, lnum, srow, wp->w_grid.rows, 0, + display_buf_line ? &spv : &zero_spv, foldinfo); if (display_buf_line) { syntax_last_parsed = lnum; @@ -2258,7 +2307,7 @@ static void win_update(win_T *wp) wp->w_lines[idx].wl_lnum = lnum; wp->w_lines[idx].wl_valid = true; - if (row > wp->w_grid.rows) { // past end of grid + if (row > wp->w_grid.rows) { // past end of grid // we may need the size of that too long line later on if (dollar_vcol == -1) { wp->w_lines[idx].wl_size = (uint16_t)plines_win(wp, lnum, true); @@ -2278,8 +2327,8 @@ static void win_update(win_T *wp) // the text doesn't need to be redrawn, but the number column does. if ((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot && buf->b_mod_xlines != 0) || (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum)) { - foldinfo_T info - = wp->w_p_cul && lnum == wp->w_cursor.lnum ? cursorline_fi : fold_info(wp, lnum); + foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum + ? cursorline_fi : fold_info(wp, lnum); win_line(wp, lnum, srow, wp->w_grid.rows, wp->w_lines[idx].wl_size, &spv, info); } @@ -2295,7 +2344,7 @@ static void win_update(win_T *wp) // 'statuscolumn' width has changed or errored, start from the top. if (wp->w_redr_statuscol) { - redr_statuscol: +redr_statuscol: wp->w_redr_statuscol = false; idx = 0; row = 0; @@ -2345,7 +2394,7 @@ static void win_update(win_T *wp) // Window ends in filler lines. wp->w_botline = lnum; wp->w_filler_rows = wp->w_grid.rows - srow; - } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate" + } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate" // Last line isn't finished: Display "@@@" in the last screen line. grid_line_start(&wp->w_grid, wp->w_grid.rows - 1); grid_line_fill(0, MIN(wp->w_grid.cols, 3), wp->w_p_fcs_chars.lastline, at_attr); @@ -2353,18 +2402,19 @@ static void win_update(win_T *wp) grid_line_flush(); set_empty_rows(wp, srow); wp->w_botline = lnum; - } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" + } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" // Last line isn't finished: Display "@@@" at the end. // If this would split a doublewidth char in two, we need to display "@@@@" instead grid_line_start(&wp->w_grid, wp->w_grid.rows - 1); int width = grid_line_getchar(MAX(wp->w_grid.cols - 3, 0), NULL) == NUL ? 4 : 3; - grid_line_fill(MAX(wp->w_grid.cols - width, 0), wp->w_grid.cols, wp->w_p_fcs_chars.lastline, - at_attr); + grid_line_fill(MAX(wp->w_grid.cols - width, 0), wp->w_grid.cols, + wp->w_p_fcs_chars.lastline, at_attr); grid_line_flush(); set_empty_rows(wp, srow); wp->w_botline = lnum; } else { - win_draw_end(wp, wp->w_p_fcs_chars.lastline, true, srow, wp->w_grid.rows, HLF_AT); + win_draw_end(wp, wp->w_p_fcs_chars.lastline, true, srow, + wp->w_grid.rows, HLF_AT); set_empty_rows(wp, srow); wp->w_botline = lnum; } @@ -2400,7 +2450,9 @@ static void win_update(win_T *wp) lastline = 0; } - win_draw_end(wp, wp->w_p_fcs_chars.eob, false, MAX(lastline, row), wp->w_grid.rows, HLF_EOB); + win_draw_end(wp, wp->w_p_fcs_chars.eob, false, MAX(lastline, row), + wp->w_grid.rows, + HLF_EOB); set_empty_rows(wp, row); } @@ -2418,9 +2470,9 @@ static void win_update(win_T *wp) // Send win_extmarks if needed for (size_t n = 0; n < kv_size(win_extmark_arr); n++) { - ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle, kv_A(win_extmark_arr, n).ns_id, - (Integer)kv_A(win_extmark_arr, n).mark_id, kv_A(win_extmark_arr, n).win_row, - kv_A(win_extmark_arr, n).win_col); + ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle, + kv_A(win_extmark_arr, n).ns_id, (Integer)kv_A(win_extmark_arr, n).mark_id, + kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col); } if (dollar_vcol == -1) { @@ -2478,9 +2530,11 @@ void win_scroll_lines(win_T *wp, int row, int line_count) } if (line_count < 0) { - grid_del_lines(&wp->w_grid, row, -line_count, wp->w_grid.rows, 0, wp->w_grid.cols); + grid_del_lines(&wp->w_grid, row, -line_count, + wp->w_grid.rows, 0, wp->w_grid.cols); } else { - grid_ins_lines(&wp->w_grid, row, line_count, wp->w_grid.rows, 0, wp->w_grid.cols); + grid_ins_lines(&wp->w_grid, row, line_count, + wp->w_grid.rows, 0, wp->w_grid.cols); } } @@ -2599,7 +2653,7 @@ void redraw_later(win_T *wp, int type) if (type >= UPD_NOT_VALID) { wp->w_lines_valid = 0; } - if (must_redraw < type) { // must_redraw is the maximum of all windows + if (must_redraw < type) { // must_redraw is the maximum of all windows must_redraw = type; } } @@ -2662,7 +2716,8 @@ void redraw_buf_line_later(buf_T *buf, linenr_T line, bool force) void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf && lastline >= wp->w_topline && firstline < wp->w_botline) { + if (wp->w_buffer == buf + && lastline >= wp->w_topline && firstline < wp->w_botline) { if (wp->w_redraw_top == 0 || wp->w_redraw_top > firstline) { wp->w_redraw_top = firstline; } @@ -2679,7 +2734,9 @@ void redraw_buf_status_later(buf_T *buf) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer == buf - && (wp->w_status_height || (wp == curwin && global_stl_height()) || wp->w_winbar_height)) { + && (wp->w_status_height + || (wp == curwin && global_stl_height()) + || wp->w_winbar_height)) { wp->w_redr_status = true; set_must_redraw(UPD_VALID); } @@ -2692,7 +2749,8 @@ void status_redraw_all(void) bool is_stl_global = global_stl_height() != 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if ((!is_stl_global && wp->w_status_height) || wp == curwin || wp->w_winbar_height) { + if ((!is_stl_global && wp->w_status_height) || wp == curwin + || wp->w_winbar_height) { wp->w_redr_status = true; redraw_later(wp, UPD_VALID); } @@ -2711,9 +2769,8 @@ void status_redraw_buf(buf_T *buf) bool is_stl_global = global_stl_height() != 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf - && ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin) - || wp->w_winbar_height)) { + if (wp->w_buffer == buf && ((!is_stl_global && wp->w_status_height) + || (is_stl_global && wp == curwin) || wp->w_winbar_height)) { wp->w_redr_status = true; redraw_later(wp, UPD_VALID); } @@ -2743,7 +2800,8 @@ void redraw_statuslines(void) } /// Redraw all status lines at the bottom of frame "frp". -void win_redraw_last_status(const frame_T *frp) FUNC_ATTR_NONNULL_ARG(1) +void win_redraw_last_status(const frame_T *frp) + FUNC_ATTR_NONNULL_ARG(1) { if (frp->fr_layout == FR_LEAF) { frp->fr_win->w_redr_status = true; @@ -2767,9 +2825,11 @@ void win_redraw_last_status(const frame_T *frp) FUNC_ATTR_NONNULL_ARG(1) /// Used to remove the "$" from a change command. /// Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot /// may become invalid and the whole window will have to be redrawn. -void redrawWinline(win_T *wp, linenr_T lnum) FUNC_ATTR_NONNULL_ALL +void redrawWinline(win_T *wp, linenr_T lnum) + FUNC_ATTR_NONNULL_ALL { - if (lnum >= wp->w_topline && lnum < wp->w_botline) { + if (lnum >= wp->w_topline + && lnum < wp->w_botline) { if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { wp->w_redraw_top = lnum; } @@ -2782,7 +2842,8 @@ void redrawWinline(win_T *wp, linenr_T lnum) FUNC_ATTR_NONNULL_ALL /// Return true if the cursor line in window "wp" may be concealed, according /// to the 'concealcursor' option. -bool conceal_cursor_line(const win_T *wp) FUNC_ATTR_NONNULL_ALL +bool conceal_cursor_line(const win_T *wp) + FUNC_ATTR_NONNULL_ALL { int c; @@ -2806,7 +2867,8 @@ bool conceal_cursor_line(const win_T *wp) FUNC_ATTR_NONNULL_ALL /// Whether cursorline is drawn in a special way /// /// If true, both old and new cursorline will need to be redrawn when moving cursor within windows. -bool win_cursorline_standout(const win_T *wp) FUNC_ATTR_NONNULL_ALL +bool win_cursorline_standout(const win_T *wp) + FUNC_ATTR_NONNULL_ALL { return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp)); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index b7b32883c2..220b92d099 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -185,7 +185,7 @@ static void insert_enter(InsertState *s) curwin->w_cursor = save_cursor; State = MODE_INSERT; - check_cursor_col(); + check_cursor_col(curwin); State = save_state; } } @@ -282,7 +282,7 @@ static void insert_enter(InsertState *s) // correct in very rare cases). // Also do this if curswant is greater than the current virtual // column. Eg after "^O$" or "^O80|". - validate_virtcol(); + validate_virtcol(curwin); update_curswant(); if (((ins_at_eol && curwin->w_cursor.lnum == o_lnum) || curwin->w_curswant > curwin->w_virtcol) @@ -365,9 +365,10 @@ static void insert_enter(InsertState *s) did_cursorhold = false; // ins_redraw() triggers TextChangedI only when no characters - // are in the typeahead buffer, so only reset curbuf->b_last_changedtick + // are in the typeahead buffer, so reset curbuf->b_last_changedtick // if the TextChangedI was not blocked by char_avail() (e.g. using :norm!) - if (!char_avail()) { + // and the TextChangedI autocommand has been triggered. + if (!char_avail() && curbuf->b_last_changedtick_i == buf_get_changedtick(curbuf)) { curbuf->b_last_changedtick = buf_get_changedtick(curbuf); } } @@ -468,7 +469,7 @@ static int insert_check(VimState *state) && curwin->w_topline == s->old_topline && curwin->w_topfill == s->old_topfill) { s->mincol = curwin->w_wcol; - validate_cursor_col(); + validate_cursor_col(curwin); if (curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(), curbuf->b_p_ts, @@ -478,7 +479,7 @@ static int insert_check(VimState *state) || curwin->w_topfill > 0)) { if (curwin->w_topfill > 0) { curwin->w_topfill--; - } else if (hasFolding(curwin->w_topline, NULL, &s->old_topline)) { + } else if (hasFolding(curwin, curwin->w_topline, NULL, &s->old_topline)) { set_topline(curwin, s->old_topline + 1); } else { set_topline(curwin, curwin->w_topline + 1); @@ -491,7 +492,7 @@ static int insert_check(VimState *state) s->did_backspace = false; - validate_cursor(); // may set must_redraw + validate_cursor(curwin); // may set must_redraw // Redraw the display when no characters are waiting. // Also shows mode, ruler and positions cursor. @@ -743,7 +744,7 @@ static int insert_handle_key(InsertState *s) ins_ctrl_o(); // don't move the cursor left when 'virtualedit' has "onemore". - if (get_ve_flags() & VE_ONEMORE) { + if (get_ve_flags(curwin) & VE_ONEMORE) { ins_at_eol = false; s->nomove = true; } @@ -1451,7 +1452,7 @@ void edit_putchar(int c, bool highlight) int attr; update_topline(curwin); // just in case w_topline isn't valid - validate_cursor(); + validate_cursor(curwin); if (highlight) { attr = HL_ATTR(HLF_8); } else { @@ -1521,7 +1522,7 @@ static void init_prompt(int cmdchar_todo) ml_append(curbuf->b_ml.ml_line_count, prompt, 0, false); } curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); inserted_bytes(curbuf->b_ml.ml_line_count, 0, 0, (colnr_T)strlen(prompt)); } @@ -1536,13 +1537,13 @@ static void init_prompt(int cmdchar_todo) } if (cmdchar_todo == 'A') { - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } if (curwin->w_cursor.col < (colnr_T)strlen(prompt)) { curwin->w_cursor.col = (colnr_T)strlen(prompt); } // Make sure the cursor is in a valid position. - check_cursor(); + check_cursor(curwin); } /// @return true if the cursor is in the editable position of the prompt line. @@ -2394,7 +2395,7 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove) pos_T tpos = curwin->w_cursor; curwin->w_cursor = *end_insert_pos; - check_cursor_col(); // make sure it is not past the line + check_cursor_col(curwin); // make sure it is not past the line while (true) { if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) { curwin->w_cursor.col--; @@ -2471,7 +2472,7 @@ void free_last_insert(void) void beginline(int flags) { if ((flags & BL_SOL) && !p_sol) { - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } else { curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; @@ -2497,13 +2498,13 @@ int oneright(void) { char *ptr; - if (virtual_active()) { + if (virtual_active(curwin)) { pos_T prevpos = curwin->w_cursor; // Adjust for multi-wide char (excluding TAB) ptr = get_cursor_pos_ptr(); - coladvance(getviscol() + ((*ptr != TAB && vim_isprintc(utf_ptr2char(ptr))) - ? ptr2cells(ptr) : 1)); + coladvance(curwin, getviscol() + ((*ptr != TAB && vim_isprintc(utf_ptr2char(ptr))) + ? ptr2cells(ptr) : 1)); curwin->w_set_curswant = true; // Return OK if the cursor moved, FAIL otherwise (at window edge). return (prevpos.col != curwin->w_cursor.col @@ -2519,7 +2520,7 @@ int oneright(void) // move "l" bytes right, but don't end up on the NUL, unless 'virtualedit' // contains "onemore". - if (ptr[l] == NUL && (get_ve_flags() & VE_ONEMORE) == 0) { + if (ptr[l] == NUL && (get_ve_flags(curwin) & VE_ONEMORE) == 0) { return FAIL; } curwin->w_cursor.col += l; @@ -2531,7 +2532,7 @@ int oneright(void) int oneleft(void) { - if (virtual_active()) { + if (virtual_active(curwin)) { int v = getviscol(); if (v == 0) { @@ -2541,7 +2542,7 @@ int oneleft(void) // We might get stuck on 'showbreak', skip over it. int width = 1; while (true) { - coladvance(v - width); + coladvance(curwin, v - width); // getviscol() is slow, skip it when 'showbreak' is empty, // 'breakindent' is not set and there are no multi-byte // characters @@ -2590,7 +2591,7 @@ void cursor_up_inner(win_T *wp, linenr_T n) // Count each sequence of folded lines as one logical line. // go to the start of the current fold - hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFolding(wp, lnum, &lnum, NULL); while (n--) { // move up one line @@ -2602,7 +2603,7 @@ void cursor_up_inner(win_T *wp, linenr_T n) // Insert mode or when 'foldopen' contains "all": it will open // in a moment. if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & FDO_ALL))) { - hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFolding(wp, lnum, &lnum, NULL); } } if (lnum < 1) { @@ -2625,7 +2626,7 @@ int cursor_up(linenr_T n, bool upd_topline) cursor_up_inner(curwin, n); // try to advance to the column we want to be at - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); if (upd_topline) { update_topline(curwin); // make sure curwin->w_topline is valid @@ -2678,7 +2679,7 @@ int cursor_down(int n, bool upd_topline) cursor_down_inner(curwin, n); // try to advance to the column we want to be at - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); if (upd_topline) { update_topline(curwin); // make sure curwin->w_topline is valid @@ -2968,7 +2969,7 @@ static void replace_do_bs(int limit_col) } del_char_after_col(limit_col); if (l_State & VREPLACE_FLAG) { - orig_len = (int)strlen(get_cursor_pos_ptr()); + orig_len = get_cursor_pos_len(); } replace_push(cc); replace_pop_ins(); @@ -2976,7 +2977,7 @@ static void replace_do_bs(int limit_col) if (l_State & VREPLACE_FLAG) { // Get the number of screen cells used by the inserted characters char *p = get_cursor_pos_ptr(); - int ins_len = (int)strlen(p) - orig_len; + int ins_len = get_cursor_pos_len() - orig_len; int vcol = start_vcol; for (int i = 0; i < ins_len; i++) { vcol += win_chartabsize(curwin, p + i, vcol); @@ -3274,7 +3275,7 @@ static void ins_reg(void) // Cursor may be moved back a column. curwin->w_cursor = curpos; - check_cursor(); + check_cursor(curwin); } if (regname == NUL || !valid_yank_reg(regname, false)) { vim_beep(BO_REG); @@ -3466,7 +3467,7 @@ static bool ins_esc(int *count, int cmdchar, bool nomove) && (curwin->w_cursor.col != 0 || curwin->w_cursor.coladd > 0) && (restart_edit == NUL || (gchar_cursor() == NUL && !VIsual_active)) && !revins_on) { - if (curwin->w_cursor.coladd > 0 || get_ve_flags() == VE_ALL) { + if (curwin->w_cursor.coladd > 0 || get_ve_flags(curwin) == VE_ALL) { oneleft(); if (restart_edit != NUL) { curwin->w_cursor.coladd++; @@ -3598,7 +3599,7 @@ static void ins_ctrl_o(void) } else { restart_edit = 'I'; } - if (virtual_active()) { + if (virtual_active(curwin)) { ins_at_eol = false; // cursor always keeps its column } else { ins_at_eol = (gchar_cursor() == NUL); @@ -3673,23 +3674,6 @@ static void ins_del(void) AppendCharToRedobuff(K_DEL); } -// Delete one character for ins_bs(). -static void ins_bs_one(colnr_T *vcolp) -{ - dec_cursor(); - getvcol(curwin, &curwin->w_cursor, vcolp, NULL, NULL); - if (State & REPLACE_FLAG) { - // Don't delete characters before the insert point when in - // Replace mode - if (curwin->w_cursor.lnum != Insstart.lnum - || curwin->w_cursor.col >= Insstart.col) { - replace_do_bs(-1); - } - } else { - del_char(false); - } -} - /// Handle Backspace, delete-word and delete-line in Insert mode. /// /// @param c character that was typed @@ -3760,7 +3744,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) return false; } Insstart.lnum--; - Insstart.col = (colnr_T)strlen(ml_get(Insstart.lnum)); + Insstart.col = ml_get_len(Insstart.lnum); } // In replace mode: // cc < 0: NL was inserted, delete it @@ -3785,9 +3769,10 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) if (has_format_option(FO_AUTO) && has_format_option(FO_WHITE_PAR)) { char *ptr = ml_get_buf_mut(curbuf, curwin->w_cursor.lnum); - int len = (int)strlen(ptr); + int len = get_cursor_line_len(); if (len > 0 && ptr[len - 1] == ' ') { ptr[len - 1] = NUL; + curbuf->b_ml.ml_line_len--; } } @@ -3845,42 +3830,74 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) // Handle deleting one 'shiftwidth' or 'softtabstop'. if (mode == BACKSPACE_CHAR && ((p_sta && in_indent) - || ((get_sts_value() != 0 - || tabstop_count(curbuf->b_p_vsts_array)) + || ((get_sts_value() != 0 || tabstop_count(curbuf->b_p_vsts_array)) && curwin->w_cursor.col > 0 && (*(get_cursor_pos_ptr() - 1) == TAB || (*(get_cursor_pos_ptr() - 1) == ' ' && (!*inserted_space_p || arrow_used)))))) { - colnr_T vcol; - colnr_T want_vcol; - *inserted_space_p = false; - // Compute the virtual column where we want to be. Since - // 'showbreak' may get in the way, need to get the last column of - // the previous character. - getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); - colnr_T start_vcol = vcol; - dec_cursor(); - getvcol(curwin, &curwin->w_cursor, NULL, NULL, &want_vcol); - inc_cursor(); + + bool const use_ts = !curwin->w_p_list || curwin->w_p_lcs_chars.tab1; + char *const line = get_cursor_line_ptr(); + char *const cursor_ptr = line + curwin->w_cursor.col; + + colnr_T vcol = 0; + colnr_T space_vcol = 0; + StrCharInfo sci = utf_ptr2StrCharInfo(line); + StrCharInfo space_sci = sci; + bool prev_space = false; + + // Compute virtual column of cursor position, and find the last + // whitespace before cursor that is preceded by non-whitespace. + // Use charsize_nowrap() so that virtual text and wrapping are ignored. + while (sci.ptr < cursor_ptr) { + bool cur_space = ascii_iswhite(sci.chr.value); + if (!prev_space && cur_space) { + space_sci = sci; + space_vcol = vcol; + } + vcol += charsize_nowrap(curbuf, use_ts, vcol, sci.chr.value); + sci = utfc_next(sci); + prev_space = cur_space; + } + + // Compute the virtual column where we want to be. + colnr_T want_vcol = vcol > 0 ? vcol - 1 : 0; if (p_sta && in_indent) { - int ts = get_sw_value(curbuf); - want_vcol = (want_vcol / ts) * ts; + want_vcol -= want_vcol % get_sw_value(curbuf); } else { - want_vcol = tabstop_start(want_vcol, - get_sts_value(), - curbuf->b_p_vsts_array); + want_vcol = tabstop_start(want_vcol, get_sts_value(), curbuf->b_p_vsts_array); } - // delete characters until we are at or before want_vcol - while (vcol > want_vcol && curwin->w_cursor.col > 0 - && (cc = (uint8_t)(*(get_cursor_pos_ptr() - 1)), ascii_iswhite(cc))) { - ins_bs_one(&vcol); + // Find the position to stop backspacing. + // Use charsize_nowrap() so that virtual text and wrapping are ignored. + while (true) { + int size = charsize_nowrap(curbuf, use_ts, space_vcol, space_sci.chr.value); + if (space_vcol + size > want_vcol) { + break; + } + space_vcol += size; + space_sci = utfc_next(space_sci); + } + colnr_T const want_col = (int)(space_sci.ptr - line); + + // Delete characters until we are at or before want_col. + while (curwin->w_cursor.col > want_col) { + dec_cursor(); + if (State & REPLACE_FLAG) { + // Don't delete characters before the insert point when in Replace mode. + if (curwin->w_cursor.lnum != Insstart.lnum + || curwin->w_cursor.col >= Insstart.col) { + replace_do_bs(-1); + } + } else { + del_char(false); + } } - // insert extra spaces until we are at want_vcol - while (vcol < want_vcol) { - // Remember the first char we inserted + // Insert extra spaces until we are at want_vcol. + for (; space_vcol < want_vcol; space_vcol++) { + // Remember the first char we inserted. if (curwin->w_cursor.lnum == Insstart_orig.lnum && curwin->w_cursor.col < Insstart_orig.col) { Insstart_orig.col = curwin->w_cursor.col; @@ -3894,13 +3911,6 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) replace_push(NUL); } } - getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); - } - - // If we are now back where we started delete one character. Can - // happen when using 'sts' and 'linebreak'. - if (vcol >= start_vcol) { - ins_bs_one(&vcol); } } else { // Delete up to starting point, start of line or previous word. @@ -4027,7 +4037,7 @@ static void ins_left(void) // always break undo when moving upwards/downwards, else undo may break start_arrow(&tpos); curwin->w_cursor.lnum--; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); curwin->w_set_curswant = true; // so we stay at the end } else { vim_beep(BO_CRSR); @@ -4061,7 +4071,7 @@ static void ins_end(int c) if (c == K_C_END) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; } - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); curwin->w_curswant = MAXCOL; start_arrow(&tpos); @@ -4095,13 +4105,13 @@ static void ins_right(void) foldOpenCursor(); } undisplay_dollar(); - if (gchar_cursor() != NUL || virtual_active()) { + if (gchar_cursor() != NUL || virtual_active(curwin)) { start_arrow_with_change(&curwin->w_cursor, end_change); if (!end_change) { AppendCharToRedobuff(K_RIGHT); } curwin->w_set_curswant = true; - if (virtual_active()) { + if (virtual_active(curwin)) { oneright(); } else { curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr()); @@ -4156,7 +4166,7 @@ static void ins_up(bool startcol) pos_T tpos = curwin->w_cursor; if (cursor_up(1, true) == OK) { if (startcol) { - coladvance(getvcol_nolist(&Insstart)); + coladvance(curwin, getvcol_nolist(&Insstart)); } if (old_topline != curwin->w_topline || old_topfill != curwin->w_topfill) { @@ -4183,7 +4193,7 @@ static void ins_pageup(void) } pos_T tpos = curwin->w_cursor; - if (onepage(BACKWARD, 1) == OK) { + if (pagescroll(BACKWARD, 1, false) == OK) { start_arrow(&tpos); can_cindent = true; } else { @@ -4201,7 +4211,7 @@ static void ins_down(bool startcol) pos_T tpos = curwin->w_cursor; if (cursor_down(1, true) == OK) { if (startcol) { - coladvance(getvcol_nolist(&Insstart)); + coladvance(curwin, getvcol_nolist(&Insstart)); } if (old_topline != curwin->w_topline || old_topfill != curwin->w_topfill) { @@ -4228,7 +4238,7 @@ static void ins_pagedown(void) } pos_T tpos = curwin->w_cursor; - if (onepage(FORWARD, 1) == OK) { + if (pagescroll(FORWARD, 1, false) == OK) { start_arrow(&tpos); can_cindent = true; } else { @@ -4330,7 +4340,7 @@ static bool ins_tab(void) if (State & VREPLACE_FLAG) { pos = curwin->w_cursor; cursor = &pos; - saved_line = xstrdup(get_cursor_line_ptr()); + saved_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); ptr = saved_line + pos.col; } else { ptr = get_cursor_pos_ptr(); @@ -4411,13 +4421,13 @@ static bool ins_tab(void) if (i > 0) { STRMOVE(ptr, ptr + i); // correct replace stack. - if ((State & REPLACE_FLAG) - && !(State & VREPLACE_FLAG)) { + if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) { for (temp = i; --temp >= 0;) { replace_join(repl_off); } } if (!(State & VREPLACE_FLAG)) { + curbuf->b_ml.ml_line_len -= i; inserted_bytes(fpos.lnum, change_col, cursor->col - change_col, fpos.col - change_col); } @@ -4462,8 +4472,7 @@ bool ins_eol(int c) // Strange Vi behaviour: In Replace mode, typing a NL will not delete the // character under the cursor. Only push a NUL on the replace stack, // nothing to put back when the NL is deleted. - if ((State & REPLACE_FLAG) - && !(State & VREPLACE_FLAG)) { + if ((State & REPLACE_FLAG) && !(State & VREPLACE_FLAG)) { replace_push(NUL); } @@ -4474,13 +4483,13 @@ bool ins_eol(int c) // Put cursor on NUL if on the last char and coladd is 1 (happens after // CTRL-O). - if (virtual_active() && curwin->w_cursor.coladd > 0) { - coladvance(getviscol()); + if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { + coladvance(curwin, getviscol()); } // NL in reverse insert will always start in the end of current line. if (revins_on) { - curwin->w_cursor.col += (colnr_T)strlen(get_cursor_pos_ptr()); + curwin->w_cursor.col += get_cursor_pos_len(); } AppendToRedobuff(NL_STR); @@ -4574,7 +4583,7 @@ int ins_copychar(linenr_T lnum) } // try to advance to the cursor column - validate_virtcol(); + validate_virtcol(curwin); int const end_vcol = curwin->w_virtcol; char *line = ml_get(lnum); @@ -4720,7 +4729,7 @@ colnr_T get_nolist_virtcol(void) if (curwin->w_p_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) { return getvcol_nolist(&curwin->w_cursor); } - validate_virtcol(); + validate_virtcol(curwin); return curwin->w_virtcol; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7e3060100c..64883e69a2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17,6 +17,7 @@ #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" +#include "nvim/change.h" #include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cmdexpand_defs.h" @@ -763,8 +764,8 @@ void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) return; } - if (getline_equal(eap->getline, eap->cookie, getsourceline)) { - evalarg->eval_getline = eap->getline; + if (getline_equal(eap->ea_getline, eap->cookie, getsourceline)) { + evalarg->eval_getline = eap->ea_getline; evalarg->eval_cookie = eap->cookie; } } @@ -968,12 +969,12 @@ int skip_expr(char **pp, evalarg_T *const evalarg) /// Convert "tv" to a string. /// -/// @param convert when true convert a List into a sequence of lines. +/// @param join_list when true convert a List into a sequence of lines. /// /// @return an allocated string. -static char *typval2string(typval_T *tv, bool convert) +static char *typval2string(typval_T *tv, bool join_list) { - if (convert && tv->v_type == VAR_LIST) { + if (join_list && tv->v_type == VAR_LIST) { garray_T ga; ga_init(&ga, (int)sizeof(char), 80); if (tv->vval.v_list != NULL) { @@ -984,24 +985,28 @@ static char *typval2string(typval_T *tv, bool convert) } ga_append(&ga, NUL); return (char *)ga.ga_data; + } else if (tv->v_type == VAR_LIST || tv->v_type == VAR_DICT) { + return encode_tv2string(tv, NULL); } return xstrdup(tv_get_string(tv)); } /// Top level evaluation function, returning a string. /// -/// @param convert when true convert a List into a sequence of lines. +/// @param join_list when true convert a List into a sequence of lines. /// /// @return pointer to allocated memory, or NULL for failure. -char *eval_to_string(char *arg, bool convert) +char *eval_to_string_eap(char *arg, bool join_list, exarg_T *eap) { typval_T tv; char *retval; - if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { + evalarg_T evalarg; + fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); + if (eval0(arg, &tv, NULL, &evalarg) == FAIL) { retval = NULL; } else { - retval = typval2string(&tv, convert); + retval = typval2string(&tv, join_list); tv_clear(&tv); } clear_evalarg(&EVALARG_EVALUATE, NULL); @@ -1009,6 +1014,11 @@ char *eval_to_string(char *arg, bool convert) return retval; } +char *eval_to_string(char *arg, bool join_list) +{ + return eval_to_string_eap(arg, join_list, NULL); +} + /// Call eval_to_string() without using current local variables and using /// textlock. /// @@ -1372,112 +1382,24 @@ Object eval_foldtext(win_T *wp) return retval; } -/// Get an lvalue +/// Get the lval of a list/dict/blob subitem starting at "p". Loop +/// until no more [idx] or .key is following. /// -/// Lvalue may be -/// - variable: "name", "na{me}" -/// - dictionary item: "dict.key", "dict['key']" -/// - list item: "list[expr]" -/// - list slice: "list[expr:expr]" -/// -/// Indexing only works if trying to use it with an existing List or Dictionary. -/// -/// @param[in] name Name to parse. -/// @param rettv Pointer to the value to be assigned or NULL. -/// @param[out] lp Lvalue definition. When evaluation errors occur `->ll_name` -/// is NULL. -/// @param[in] unlet True if using `:unlet`. This results in slightly -/// different behaviour when something is wrong; must end in -/// space or cmd separator. -/// @param[in] skip True when skipping. /// @param[in] flags @see GetLvalFlags. -/// @param[in] fne_flags Flags for find_name_end(). /// -/// @return A pointer to just after the name, including indexes. Returns NULL -/// for a parsing error, but it is still needed to free items in lp. -char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const bool unlet, - const bool skip, const int flags, const int fne_flags) - FUNC_ATTR_NONNULL_ARG(1, 3) +/// @return A pointer to the character after the subscript on success or NULL on +/// failure. +static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv, hashtab_T *ht, + dictitem_T *v, int unlet, int flags) { - bool empty1 = false; int quiet = flags & GLV_QUIET; - - // Clear everything in "lp". - CLEAR_POINTER(lp); - - if (skip) { - // When skipping just find the end of the name. - lp->ll_name = name; - return (char *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); - } - - // Find the end of the name. - char *expr_start; - char *expr_end; - char *p = (char *)find_name_end(name, (const char **)&expr_start, - (const char **)&expr_end, - fne_flags); - if (expr_start != NULL) { - // Don't expand the name when we already know there is an error. - if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) - && *p != '[' && *p != '.') { - semsg(_(e_trailing_arg), p); - return NULL; - } - - lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p); - lp->ll_name = lp->ll_exp_name; - if (lp->ll_exp_name == NULL) { - // Report an invalid expression in braces, unless the - // expression evaluation has been cancelled due to an - // aborting error, an interrupt, or an exception. - if (!aborting() && !quiet) { - emsg_severe = true; - semsg(_(e_invarg2), name); - return NULL; - } - lp->ll_name_len = 0; - } else { - lp->ll_name_len = strlen(lp->ll_name); - } - } else { - lp->ll_name = name; - lp->ll_name_len = (size_t)(p - lp->ll_name); - } - - // Without [idx] or .key we are done. - if ((*p != '[' && *p != '.') || lp->ll_name == NULL) { - return p; - } - - hashtab_T *ht = NULL; - - // Only pass &ht when we would write to the variable, it prevents autoload - // as well. - dictitem_T *v = find_var(lp->ll_name, lp->ll_name_len, - (flags & GLV_READ_ONLY) ? NULL : &ht, - flags & GLV_NO_AUTOLOAD); - if (v == NULL && !quiet) { - semsg(_("E121: Undefined variable: %.*s"), - (int)lp->ll_name_len, lp->ll_name); - } - if (v == NULL) { - return NULL; - } - - lp->ll_tv = &v->di_tv; - - if (tv_is_luafunc(lp->ll_tv)) { - // For v:lua just return a pointer to the "." after the "v:lua". - // If the caller is trans_function_name() it will check for a Lua function name. - return p; - } - - // Loop until no more [idx] or .key is following. typval_T var1; var1.v_type = VAR_UNKNOWN; typval_T var2; var2.v_type = VAR_UNKNOWN; + bool empty1 = false; + + // Loop until no more [idx] or .key is following. while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { if (*p == '.' && lp->ll_tv->v_type != VAR_DICT) { if (!quiet) { @@ -1512,6 +1434,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const char *key = NULL; if (*p == '.') { key = p + 1; + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {} if (len == 0) { if (!quiet) { @@ -1730,6 +1653,116 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const return p; } +/// Get an lvalue +/// +/// Lvalue may be +/// - variable: "name", "na{me}" +/// - dictionary item: "dict.key", "dict['key']" +/// - list item: "list[expr]" +/// - list slice: "list[expr:expr]" +/// +/// Indexing only works if trying to use it with an existing List or Dictionary. +/// +/// @param[in] name Name to parse. +/// @param rettv Pointer to the value to be assigned or NULL. +/// @param[out] lp Lvalue definition. When evaluation errors occur `->ll_name` +/// is NULL. +/// @param[in] unlet True if using `:unlet`. This results in slightly +/// different behaviour when something is wrong; must end in +/// space or cmd separator. +/// @param[in] skip True when skipping. +/// @param[in] flags @see GetLvalFlags. +/// @param[in] fne_flags Flags for find_name_end(). +/// +/// @return A pointer to just after the name, including indexes. Returns NULL +/// for a parsing error, but it is still needed to free items in lp. +char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const bool unlet, + const bool skip, const int flags, const int fne_flags) + FUNC_ATTR_NONNULL_ARG(1, 3) +{ + int quiet = flags & GLV_QUIET; + + // Clear everything in "lp". + CLEAR_POINTER(lp); + + if (skip) { + // When skipping just find the end of the name. + lp->ll_name = name; + return (char *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); + } + + // Find the end of the name. + char *expr_start; + char *expr_end; + char *p = (char *)find_name_end(name, (const char **)&expr_start, + (const char **)&expr_end, + fne_flags); + if (expr_start != NULL) { + // Don't expand the name when we already know there is an error. + if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) + && *p != '[' && *p != '.') { + semsg(_(e_trailing_arg), p); + return NULL; + } + + lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p); + lp->ll_name = lp->ll_exp_name; + if (lp->ll_exp_name == NULL) { + // Report an invalid expression in braces, unless the + // expression evaluation has been cancelled due to an + // aborting error, an interrupt, or an exception. + if (!aborting() && !quiet) { + emsg_severe = true; + semsg(_(e_invarg2), name); + return NULL; + } + lp->ll_name_len = 0; + } else { + lp->ll_name_len = strlen(lp->ll_name); + } + } else { + lp->ll_name = name; + lp->ll_name_len = (size_t)(p - lp->ll_name); + } + + // Without [idx] or .key we are done. + if ((*p != '[' && *p != '.') || lp->ll_name == NULL) { + return p; + } + + hashtab_T *ht = NULL; + + // Only pass &ht when we would write to the variable, it prevents autoload + // as well. + dictitem_T *v = find_var(lp->ll_name, lp->ll_name_len, + (flags & GLV_READ_ONLY) ? NULL : &ht, + flags & GLV_NO_AUTOLOAD); + if (v == NULL && !quiet) { + semsg(_("E121: Undefined variable: %.*s"), + (int)lp->ll_name_len, lp->ll_name); + } + if (v == NULL) { + return NULL; + } + + lp->ll_tv = &v->di_tv; + + if (tv_is_luafunc(lp->ll_tv)) { + // For v:lua just return a pointer to the "." after the "v:lua". + // If the caller is trans_function_name() it will check for a Lua function name. + return p; + } + + // If the next character is a "." or a "[", then process the subitem. + p = get_lval_subscript(lp, p, name, rettv, ht, v, unlet, flags); + if (p == NULL) { + return NULL; + } + + lp->ll_name_len = (size_t)(p - lp->ll_name); + return p; +} + /// Clear lval "lp" that was filled by get_lval(). void clear_lval(lval_T *lp) { @@ -1892,7 +1925,7 @@ void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, evalarg_T *const *errp = true; // Default: there is an error. - const char *expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); + const char *expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon, false); if (expr == NULL) { return fi; } @@ -4766,6 +4799,88 @@ bool set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack) return abort; } +/// Mark the dict "dd" with "copyID". +/// Also see set_ref_in_item(). +static bool set_ref_in_item_dict(dict_T *dd, int copyID, ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + if (dd == NULL || dd->dv_copyID == copyID) { + return false; + } + + // Didn't see this dict yet. + dd->dv_copyID = copyID; + if (ht_stack == NULL) { + return set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack); + } + + ht_stack_T *const newitem = xmalloc(sizeof(ht_stack_T)); + newitem->ht = &dd->dv_hashtab; + newitem->prev = *ht_stack; + *ht_stack = newitem; + + QUEUE *w = NULL; + DictWatcher *watcher = NULL; + QUEUE_FOREACH(w, &dd->watchers, { + watcher = tv_dict_watcher_node_data(w); + set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack); + }) + + return false; +} + +/// Mark the list "ll" with "copyID". +/// Also see set_ref_in_item(). +static bool set_ref_in_item_list(list_T *ll, int copyID, ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + if (ll == NULL || ll->lv_copyID == copyID) { + return false; + } + + // Didn't see this list yet. + ll->lv_copyID = copyID; + if (list_stack == NULL) { + return set_ref_in_list_items(ll, copyID, ht_stack); + } + + list_stack_T *const newitem = xmalloc(sizeof(list_stack_T)); + newitem->list = ll; + newitem->prev = *list_stack; + *list_stack = newitem; + + return false; +} + +/// Mark the partial "pt" with "copyID". +/// Also see set_ref_in_item(). +static bool set_ref_in_item_partial(partial_T *pt, int copyID, ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + if (pt == NULL || pt->pt_copyID == copyID) { + return false; + } + + // Didn't see this partial yet. + pt->pt_copyID = copyID; + + bool abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); + + if (pt->pt_dict != NULL) { + typval_T dtv; + + dtv.v_type = VAR_DICT; + dtv.vval.v_dict = pt->pt_dict; + abort = abort || set_ref_in_item(&dtv, copyID, ht_stack, list_stack); + } + + for (int i = 0; i < pt->pt_argc; i++) { + abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID, ht_stack, list_stack); + } + + return abort; +} + /// Mark all lists and dicts referenced through typval "tv" with "copyID". /// /// @param tv Typval content will be marked. @@ -4780,71 +4895,15 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack bool abort = false; switch (tv->v_type) { - case VAR_DICT: { - dict_T *dd = tv->vval.v_dict; - if (dd != NULL && dd->dv_copyID != copyID) { - // Didn't see this dict yet. - dd->dv_copyID = copyID; - if (ht_stack == NULL) { - abort = set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack); - } else { - ht_stack_T *const newitem = xmalloc(sizeof(ht_stack_T)); - newitem->ht = &dd->dv_hashtab; - newitem->prev = *ht_stack; - *ht_stack = newitem; - } - - QUEUE *w = NULL; - DictWatcher *watcher = NULL; - QUEUE_FOREACH(w, &dd->watchers, { - watcher = tv_dict_watcher_node_data(w); - set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack); - }) - } - break; - } - - case VAR_LIST: { - list_T *ll = tv->vval.v_list; - if (ll != NULL && ll->lv_copyID != copyID) { - // Didn't see this list yet. - ll->lv_copyID = copyID; - if (list_stack == NULL) { - abort = set_ref_in_list_items(ll, copyID, ht_stack); - } else { - list_stack_T *const newitem = xmalloc(sizeof(list_stack_T)); - newitem->list = ll; - newitem->prev = *list_stack; - *list_stack = newitem; - } - } - break; - } - - case VAR_PARTIAL: { - partial_T *pt = tv->vval.v_partial; - - // A partial does not have a copyID, because it cannot contain itself. - if (pt != NULL) { - abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID); - if (pt->pt_dict != NULL) { - typval_T dtv; - - dtv.v_type = VAR_DICT; - dtv.vval.v_dict = pt->pt_dict; - abort = abort || set_ref_in_item(&dtv, copyID, ht_stack, list_stack); - } - - for (int i = 0; i < pt->pt_argc; i++) { - abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID, - ht_stack, list_stack); - } - } - break; - } + case VAR_DICT: + return set_ref_in_item_dict(tv->vval.v_dict, copyID, ht_stack, list_stack); + case VAR_LIST: + return set_ref_in_item_list(tv->vval.v_list, copyID, ht_stack, list_stack); case VAR_FUNC: abort = set_ref_in_func(tv->vval.v_string, NULL, copyID); break; + case VAR_PARTIAL: + return set_ref_in_item_partial(tv->vval.v_partial, copyID, ht_stack, list_stack); case VAR_UNKNOWN: case VAR_BOOL: case VAR_SPECIAL: @@ -6697,7 +6756,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (charcol) { len = mb_charlen(ml_get(pos.lnum)); } else { - len = (int)strlen(ml_get(pos.lnum)); + len = ml_get_len(pos.lnum); } // We accept "$" for the column number: last column. @@ -6787,7 +6846,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (charcol) { pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr()); } else { - pos.col = (colnr_T)strlen(get_cursor_line_ptr()); + pos.col = get_cursor_line_len(); } } return &pos; @@ -7517,29 +7576,44 @@ int check_luafunc_name(const char *const str, const bool paren) return (int)(p - str); } -/// Return the character "str[index]" where "index" is the character index. If -/// "index" is out of range NULL is returned. +/// Return the character "str[index]" where "index" is the character index, +/// including composing characters. +/// If "index" is out of range NULL is returned. char *char_from_string(const char *str, varnumber_T index) { - size_t nbyte = 0; varnumber_T nchar = index; - if (str == NULL || index < 0) { + if (str == NULL) { return NULL; } size_t slen = strlen(str); - while (nchar > 0 && nbyte < slen) { - nbyte += (size_t)utf_ptr2len(str + nbyte); - nchar--; + + // do the same as for a list: a negative index counts from the end + if (index < 0) { + int clen = 0; + + for (size_t nbyte = 0; nbyte < slen; clen++) { + nbyte += (size_t)utfc_ptr2len(str + nbyte); + } + nchar = clen + index; + if (nchar < 0) { + // unlike list: index out of range results in empty string + return NULL; + } + } + + size_t nbyte = 0; + for (; nchar > 0 && nbyte < slen; nchar--) { + nbyte += (size_t)utfc_ptr2len(str + nbyte); } if (nbyte >= slen) { return NULL; } - return xmemdupz(str + nbyte, (size_t)utf_ptr2len(str + nbyte)); + return xmemdupz(str + nbyte, (size_t)utfc_ptr2len(str + nbyte)); } /// Get the byte index for character index "idx" in string "str" with length -/// "str_len". +/// "str_len". Composing characters are included. /// If going over the end return "str_len". /// If "idx" is negative count from the end, -1 is the last character. /// When going over the start return -1. @@ -7550,7 +7624,7 @@ static ssize_t char_idx2byte(const char *str, size_t str_len, varnumber_T idx) if (nchar >= 0) { while (nchar > 0 && nbyte < str_len) { - nbyte += (size_t)utf_ptr2len(str + nbyte); + nbyte += (size_t)utfc_ptr2len(str + nbyte); nchar--; } } else { @@ -7567,7 +7641,8 @@ static ssize_t char_idx2byte(const char *str, size_t str_len, varnumber_T idx) return (ssize_t)nbyte; } -/// Return the slice "str[first:last]" using character indexes. +/// Return the slice "str[first : last]" using character indexes. Composing +/// characters are included. /// /// @param exclusive true for slice(). /// @@ -7589,7 +7664,7 @@ char *string_slice(const char *str, varnumber_T first, varnumber_T last, bool ex end_byte = char_idx2byte(str, slen, last); if (!exclusive && end_byte >= 0 && end_byte < (ssize_t)slen) { // end index is inclusive - end_byte += utf_ptr2len(str + end_byte); + end_byte += utfc_ptr2len(str + end_byte); } } @@ -7876,8 +7951,8 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char .channel_id = LUA_INTERNAL_CALL, }; bool should_free; - // should_free is ignored as script_sctx will be resolved to a fnmae - // & new_script_item will consume it. + // should_free is ignored as script_ctx will be resolved to a fname + // and new_script_item() will consume it. char *sc_name = get_scriptname(last_set, &should_free); new_script_item(sc_name, ¤t_sctx.sc_sid); } @@ -8083,10 +8158,12 @@ void ex_echo(exarg_T *eap) // Call msg_start() after eval1(), evaluating the expression // may cause a message to appear. if (eap->cmdidx == CMD_echo) { - // Mark the saved text as finishing the line, so that what - // follows is displayed on a new line when scrolling back - // at the more prompt. - msg_sb_eol(); + if (!msg_didout) { + // Mark the saved text as finishing the line, so that what + // follows is displayed on a new line when scrolling back + // at the more prompt. + msg_sb_eol(); + } msg_start(); } } else if (eap->cmdidx == CMD_echo) { @@ -8182,7 +8259,7 @@ void ex_execute(exarg_T *eap) did_emsg = save_did_emsg; } } else if (eap->cmdidx == CMD_execute) { - do_cmdline(ga.ga_data, eap->getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE); + do_cmdline(ga.ga_data, eap->ea_getline, eap->cookie, DOCMD_NOWAIT|DOCMD_VERBOSE); } } @@ -8752,7 +8829,7 @@ void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) /// an empty typval_T. typval_T eval_call_provider(char *provider, char *method, list_T *arguments, bool discard) { - if (!eval_has_provider(provider)) { + if (!eval_has_provider(provider, false)) { semsg("E319: No \"%s\" provider found. Run \":checkhealth provider\"", provider); return (typval_T){ @@ -8810,7 +8887,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo } /// Checks if provider for feature `feat` is enabled. -bool eval_has_provider(const char *feat) +bool eval_has_provider(const char *feat, bool throw_if_fast) { if (!strequal(feat, "clipboard") && !strequal(feat, "python3") @@ -8823,6 +8900,11 @@ bool eval_has_provider(const char *feat) return false; } + if (throw_if_fast && !nlua_is_deferred_safe()) { + semsg(e_luv_api_disabled, "Vimscript function"); + return false; + } + char name[32]; // Normalized: "python3_compiled" => "python3". snprintf(name, sizeof(name), "%s", feat); strchrsub(name, '_', '\0'); // Chop any "_xx" suffix. @@ -8885,6 +8967,7 @@ void invoke_prompt_callback(void) // Add a new line for the prompt before invoking the callback, so that // text can always be inserted above the last line. ml_append(lnum, "", 0, false); + appended_lines_mark(lnum, 1); curwin->w_cursor.lnum = lnum + 1; curwin->w_cursor.col = 0; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index b7120d5dd5..7d4438ded6 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -2071,14 +2071,13 @@ M.funcs = { The result is a Number: 1 exists 0 does not exist - -1 not implemented on this system |exepath()| can be used to get the full path of an executable. ]=], fast = true, name = 'executable', params = { { 'expr', 'any' } }, - returns = '0|1|-1', + returns = '0|1', signature = 'executable({expr})', }, execute = { @@ -2481,6 +2480,7 @@ M.funcs = { 't' Handle keys as if typed; otherwise they are handled as if coming from a mapping. This matters for undo, opening folds, etc. + 'L' Lowlevel input. Other flags are not used. 'i' Insert the string instead of appending (see above). 'x' Execute commands until typeahead is empty. This is similar to using ":normal!". You can call feedkeys() @@ -3383,14 +3383,14 @@ M.funcs = { args = { 0, 1 }, desc = [=[ Get a single character from the user or input stream. - If [expr] is omitted, wait until a character is available. - If [expr] is 0, only get a character when one is available. + If {expr} is omitted, wait until a character is available. + If {expr} is 0, only get a character when one is available. Return zero otherwise. - If [expr] is 1, only check if a character is available, it is + If {expr} is 1, only check if a character is available, it is not consumed. Return zero if no character available. If you prefer always getting a string use |getcharstr()|. - Without [expr] and when [expr] is 0 a whole character or + Without {expr} and when {expr} is 0 a whole character or special key is returned. If it is a single character, the result is a Number. Use |nr2char()| to convert it to a String. Otherwise a String is returned with the encoded character. @@ -3400,11 +3400,11 @@ M.funcs = { also a String when a modifier (shift, control, alt) was used that is not included in the character. - When [expr] is 0 and Esc is typed, there will be a short delay + When {expr} is 0 and Esc is typed, there will be a short delay while Vim waits to see if this is the start of an escape sequence. - When [expr] is 1 only the first byte is returned. For a + When {expr} is 1 only the first byte is returned. For a one-byte character it is the character itself as a number. Use nr2char() to convert it to a String. @@ -3449,7 +3449,7 @@ M.funcs = { name = 'getchar', params = {}, returns = 'integer', - signature = 'getchar([expr])', + signature = 'getchar([{expr}])', }, getcharmod = { desc = [=[ @@ -3526,10 +3526,10 @@ M.funcs = { desc = [=[ Get a single character from the user or input stream as a string. - If [expr] is omitted, wait until a character is available. - If [expr] is 0 or false, only get a character when one is + If {expr} is omitted, wait until a character is available. + If {expr} is 0 or false, only get a character when one is available. Return an empty string otherwise. - If [expr] is 1 or true, only check if a character is + If {expr} is 1 or true, only check if a character is available, it is not consumed. Return an empty string if no character is available. Otherwise this works like |getchar()|, except that a number @@ -3538,7 +3538,7 @@ M.funcs = { name = 'getcharstr', params = {}, returns = 'string', - signature = 'getcharstr([expr])', + signature = 'getcharstr([{expr}])', }, getcmdcompltype = { desc = [=[ @@ -4414,6 +4414,46 @@ M.funcs = { returns = 'string[]', signature = 'getregion({pos1}, {pos2} [, {opts}])', }, + getregionpos = { + args = { 2, 3 }, + base = 1, + desc = [=[ + Same as |getregion()|, but returns a list of positions + describing the buffer text segments bound by {pos1} and + {pos2}. + The segments are a pair of positions for every line: > + [[{start_pos}, {end_pos}], ...] + < + The position is a |List| with four numbers: + [bufnum, lnum, col, off] + "bufnum" is the buffer number. + "lnum" and "col" are the position in the buffer. The first + column is 1. + If the "off" number of a starting position is non-zero, it is + the offset in screen columns from the start of the character. + E.g., a position within a <Tab> or after the last character. + If the "off" number of an ending position is non-zero, it is + the offset of the character's first cell not included in the + selection, otherwise all its cells are included. + + Apart from the options supported by |getregion()|, {opts} also + supports the following: + + eol If |TRUE|, indicate positions beyond + the end of a line with "col" values + one more than the length of the line. + If |FALSE|, positions are limited + within their lines, and if a line is + empty or the selection is entirely + beyond the end of a line, a "col" + value of 0 is used for both positions. + (default: |FALSE|) + ]=], + name = 'getregionpos', + params = { { 'pos1', 'table' }, { 'pos2', 'table' }, { 'opts', 'table' } }, + returns = 'integer[][][]', + signature = 'getregionpos({pos1}, {pos2} [, {opts}])', + }, getregtype = { args = { 0, 1 }, base = 1, @@ -4470,11 +4510,12 @@ M.funcs = { Examples: >vim echo getscriptinfo({'name': 'myscript'}) - echo getscriptinfo({'sid': 15}).variables + echo getscriptinfo({'sid': 15})[0].variables < ]=], name = 'getscriptinfo', params = { { 'opts', 'table' } }, + returns = 'vim.fn.getscriptinfo.ret[]', signature = 'getscriptinfo([{opts}])', }, gettabinfo = { @@ -4905,6 +4946,7 @@ M.funcs = { endif < ]=], + fast = true, name = 'has', params = { { 'feature', 'any' } }, returns = '0|1', @@ -7249,7 +7291,7 @@ M.funcs = { base = 1, desc = [=[ Return a string that indicates the current mode. - If [expr] is supplied and it evaluates to a non-zero Number or + If {expr} is supplied and it evaluates to a non-zero Number or a non-empty String (|non-zero-arg|), then the full mode is returned, otherwise only the first letter is returned. Also see |state()|. @@ -7304,7 +7346,7 @@ M.funcs = { ]=], name = 'mode', params = { { 'expr', 'any' } }, - signature = 'mode([expr])', + signature = 'mode([{expr}])', }, msgpackdump = { args = { 1, 2 }, @@ -7836,6 +7878,9 @@ M.funcs = { echo printf("%1$*2$.*3$f", 1.4142135, 6, 2) < 1.41 + You will get an overflow error |E1510|, when the field-width + or precision will result in a string longer than 6400 chars. + *E1500* You cannot mix positional and non-positional arguments: >vim echo printf("%s%1$s", "One", "Two") @@ -7898,6 +7943,7 @@ M.funcs = { name = 'printf', params = { { 'fmt', 'any' }, { 'expr1', 'any' } }, signature = 'printf({fmt}, {expr1} ...)', + returns = 'string', }, prompt_getprompt = { args = 1, @@ -8746,6 +8792,7 @@ M.funcs = { When a match has been found its line number is returned. If there is no match a 0 is returned and the cursor doesn't move. No error message is given. + To get the matched string, use |matchbufline()|. {flags} is a String, which can contain these character flags: 'b' search Backward instead of forward @@ -9896,10 +9943,11 @@ M.funcs = { Otherwise encloses {string} in single-quotes and replaces all "'" with "'\''". - If {special} is a |non-zero-arg|: - - Special items such as "!", "%", "#" and "<cword>" will be - preceded by a backslash. The backslash will be removed again - by the |:!| command. + The {special} argument adds additional escaping of keywords + used in Vim commands. If it is a |non-zero-arg|: + - Special items such as "!", "%", "#" and "<cword>" (as listed + in |expand()|) will be preceded by a backslash. + The backslash will be removed again by the |:!| command. - The <NL> character is escaped. If 'shell' contains "csh" in the tail: @@ -10481,7 +10529,8 @@ M.funcs = { Similar to using a |slice| "expr[start : end]", but "end" is used exclusive. And for a string the indexes are used as character indexes instead of byte indexes. - Also, composing characters are not counted. + Also, composing characters are treated as a part of the + preceding base character. When {end} is omitted the slice continues to the last item. When {end} is -1 the last item is omitted. Returns an empty value if {start} or {end} are invalid. @@ -10953,8 +11002,8 @@ M.funcs = { of byte index and length. When {skipcc} is omitted or zero, composing characters are counted separately. - When {skipcc} set to 1, Composing characters are ignored, - similar to |slice()|. + When {skipcc} set to 1, composing characters are treated as a + part of the preceding base character, similar to |slice()|. When a character index is used where a character does not exist it is omitted and counted as one character. For example: >vim @@ -10977,7 +11026,7 @@ M.funcs = { in String {string}. When {skipcc} is omitted or zero, composing characters are counted separately. - When {skipcc} set to 1, Composing characters are ignored. + When {skipcc} set to 1, composing characters are ignored. |strcharlen()| always does this. Returns zero on error. @@ -11124,10 +11173,10 @@ M.funcs = { for infinite and NaN floating-point values representations which use |str2float()|. Strings are also dumped literally, only single quote is escaped, which does not allow using YAML - for parsing back binary strings. |eval()| should always work for - strings and floats though and this is the only official - method, use |msgpackdump()| or |json_encode()| if you need to - share data with other application. + for parsing back binary strings. |eval()| should always work + for strings and floats though, and this is the only official + method. Use |msgpackdump()| or |json_encode()| if you need to + share data with other applications. ]=], name = 'string', @@ -11615,6 +11664,10 @@ M.funcs = { synconcealed(lnum, 4) [1, 'X', 2] synconcealed(lnum, 5) [1, 'X', 2] synconcealed(lnum, 6) [0, '', 0] + + Note: Doesn't consider |matchadd()| highlighting items, + since syntax and matching highlighting are two different + mechanisms |syntax-vs-match|. ]=], name = 'synconcealed', params = { { 'lnum', 'integer' }, { 'col', 'integer' } }, @@ -12687,9 +12740,7 @@ M.funcs = { [1, 1], unless there is a tabline, then it is [2, 1]. {nr} can be the window number or the |window-ID|. Use zero for the current window. - Returns [0, 0] if the window cannot be found in the current - tabpage. - + Returns [0, 0] if the window cannot be found. ]=], name = 'win_screenpos', params = { { 'nr', 'integer' } }, @@ -12699,10 +12750,10 @@ M.funcs = { args = { 2, 3 }, base = 1, desc = [=[ - Move the window {nr} to a new split of the window {target}. - This is similar to moving to {target}, creating a new window - using |:split| but having the same contents as window {nr}, and - then closing {nr}. + Temporarily switch to window {target}, then move window {nr} + to a new split adjacent to {target}. + Unlike commands such as |:split|, no new windows are created + (the |window-ID| of window {nr} is unchanged after the move). Both {nr} and {target} can be window numbers or |window-ID|s. Both must be in the current tab page. @@ -12856,7 +12907,9 @@ M.funcs = { # the number of the last accessed window (where |CTRL-W_p| goes to). If there is no previous window or it is in another tab page 0 is - returned. + returned. May refer to the current window in + some cases (e.g. when evaluating 'statusline' + expressions). {N}j the number of the Nth window below the current window (where |CTRL-W_j| goes to). {N}k the number of the Nth window above the current diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c index 7b8f71ef3f..73bfd6db2a 100644 --- a/src/nvim/eval/buffer.c +++ b/src/nvim/eval/buffer.c @@ -197,7 +197,7 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_ && ml_replace(lnum, line, true) == OK) { inserted_bytes(lnum, 0, old_len, (int)strlen(line)); if (is_curbuf && lnum == curwin->w_cursor.lnum) { - check_cursor_col(); + check_cursor_col(curwin); } rettv->vval.v_number = 0; // OK } @@ -229,7 +229,7 @@ static void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, typval_ wp->w_cursor.lnum += (linenr_T)added; } } - check_cursor_col(); + check_cursor_col(curwin); update_topline(curwin); } @@ -469,7 +469,7 @@ void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } } - check_cursor_col(); + check_cursor_col(curwin); deleted_lines_mark(first, count); rettv->vval.v_number = 0; // OK diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index d7237d6443..e3afc1cf54 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -727,7 +727,7 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) return; } - check_cursor(); + check_cursor(curwin); winchanged = true; } @@ -738,7 +738,7 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) if (fp->col == MAXCOL) { // '> can be MAXCOL, get the length of the line then if (fp->lnum <= curbuf->b_ml.ml_line_count) { - col = (colnr_T)strlen(ml_get(fp->lnum)) + 1; + col = ml_get_len(fp->lnum) + 1; } else { col = MAXCOL; } @@ -746,7 +746,7 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) col = fp->col + 1; // col(".") when the cursor is on the NUL at the end of the line // because of "coladd" can be seen as an extra column. - if (virtual_active() && fp == &curwin->w_cursor) { + if (virtual_active(curwin) && fp == &curwin->w_cursor) { char *p = get_cursor_pos_ptr(); if (curwin->w_cursor.coladd >= (colnr_T)win_chartabsize(curwin, p, @@ -1185,13 +1185,14 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) if (lnum > 0) { curwin->w_cursor.lnum = lnum; } - if (col > 0) { - curwin->w_cursor.col = col - 1; + if (col != MAXCOL && --col < 0) { + col = 0; } + curwin->w_cursor.col = col; curwin->w_cursor.coladd = coladd; // Make sure the cursor is in a valid position. - check_cursor(); + check_cursor(curwin); // Correct cursor for multi-byte character. mb_adjust_cursor(); @@ -1361,7 +1362,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fp /// "did_filetype()" function static void f_did_filetype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - rettv->vval.v_number = did_filetype; + rettv->vval.v_number = curbuf->b_did_filetype; } /// "diff_filler()" function @@ -2019,6 +2020,9 @@ static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new, action = tv_get_string_chk(&argvars[2]); if (action == NULL) { + if (is_new) { + tv_dict_unref(d1); + } return; // Type error; error message already given. } size_t i; @@ -2028,6 +2032,9 @@ static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new, } } if (i == 3) { + if (is_new) { + tv_dict_unref(d1); + } semsg(_(e_invarg2), action); return; } @@ -2816,24 +2823,24 @@ static char *block_def2str(struct block_def *bd) return ret; } -/// "getregion()" function -static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2, + bool *const inclusive, MotionType *region_type, oparg_T *oa) + FUNC_ATTR_NONNULL_ALL { tv_list_alloc_ret(rettv, kListLenMayKnow); if (tv_check_for_list_arg(argvars, 0) == FAIL || tv_check_for_list_arg(argvars, 1) == FAIL || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) { - return; + return FAIL; } int fnum1 = -1; int fnum2 = -1; - pos_T p1, p2; - if (list2fpos(&argvars[0], &p1, &fnum1, NULL, false) != OK - || list2fpos(&argvars[1], &p2, &fnum2, NULL, false) != OK + if (list2fpos(&argvars[0], p1, &fnum1, NULL, false) != OK + || list2fpos(&argvars[1], p2, &fnum2, NULL, false) != OK || fnum1 != fnum2) { - return; + return FAIL; } bool is_select_exclusive; @@ -2851,87 +2858,109 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) type = default_type; } - MotionType region_type = kMTUnknown; if (type[0] == 'v' && type[1] == NUL) { - region_type = kMTCharWise; + *region_type = kMTCharWise; } else if (type[0] == 'V' && type[1] == NUL) { - region_type = kMTLineWise; + *region_type = kMTLineWise; } else if (type[0] == Ctrl_V && type[1] == NUL) { - region_type = kMTBlockWise; + *region_type = kMTBlockWise; } else { - return; + semsg(_(e_invargNval), "type", type); + return FAIL; } - buf_T *const save_curbuf = curbuf; + buf_T *findbuf = fnum1 != 0 ? buflist_findnr(fnum1) : curbuf; + if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { + emsg(_(e_buffer_is_not_loaded)); + return FAIL; + } - if (fnum1 != 0) { - buf_T *findbuf = buflist_findnr(fnum1); - // buffer not loaded - if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { - return; - } - curbuf = findbuf; + if (p1->lnum < 1 || p1->lnum > findbuf->b_ml.ml_line_count) { + semsg(_(e_invalid_line_number_nr), p1->lnum); + return FAIL; + } + if (p1->col == MAXCOL) { + p1->col = ml_get_buf_len(findbuf, p1->lnum) + 1; + } else if (p1->col < 1 || p1->col > ml_get_buf_len(findbuf, p1->lnum) + 1) { + semsg(_(e_invalid_column_number_nr), p1->col); + return FAIL; } - const TriState save_virtual = virtual_op; - virtual_op = virtual_active(); + if (p2->lnum < 1 || p2->lnum > findbuf->b_ml.ml_line_count) { + semsg(_(e_invalid_line_number_nr), p2->lnum); + return FAIL; + } + if (p2->col == MAXCOL) { + p2->col = ml_get_buf_len(findbuf, p2->lnum) + 1; + } else if (p2->col < 1 || p2->col > ml_get_buf_len(findbuf, p2->lnum) + 1) { + semsg(_(e_invalid_column_number_nr), p2->col); + return FAIL; + } + + curbuf = findbuf; + curwin->w_buffer = curbuf; + virtual_op = virtual_active(curwin); - // NOTE: Adjust is needed. - p1.col--; - p2.col--; + // NOTE: Adjustment is needed. + p1->col--; + p2->col--; - if (!lt(p1, p2)) { + if (!lt(*p1, *p2)) { // swap position - pos_T p = p1; - p1 = p2; - p2 = p; + pos_T p = *p1; + *p1 = *p2; + *p2 = p; } - oparg_T oa; - bool inclusive = true; - - if (region_type == kMTCharWise) { - // handle 'selection' == "exclusive" - if (is_select_exclusive && !equalpos(p1, p2)) { - if (p2.coladd > 0) { - p2.coladd--; - } else if (p2.col > 0) { - p2.col--; - mark_mb_adjustpos(curbuf, &p2); - } else if (p2.lnum > 1) { - p2.lnum--; - p2.col = (colnr_T)strlen(ml_get(p2.lnum)); - if (p2.col > 0) { - p2.col--; - mark_mb_adjustpos(curbuf, &p2); - } - } + if (*region_type == kMTCharWise) { + // Handle 'selection' == "exclusive". + if (is_select_exclusive && !equalpos(*p1, *p2)) { + // When backing up to previous line, inclusive becomes false. + *inclusive = !unadjust_for_sel_inner(p2); } - // if fp2 is on NUL (empty line) inclusive becomes false - if (*ml_get_pos(&p2) == NUL && !virtual_op) { - inclusive = false; + // If p2 is on NUL (end of line), inclusive becomes false. + if (*inclusive && !virtual_op && *ml_get_pos(p2) == NUL) { + *inclusive = false; } - } else if (region_type == kMTBlockWise) { + } else if (*region_type == kMTBlockWise) { colnr_T sc1, ec1, sc2, ec2; - getvvcol(curwin, &p1, &sc1, NULL, &ec1); - getvvcol(curwin, &p2, &sc2, NULL, &ec2); - oa.motion_type = kMTBlockWise; - oa.inclusive = true; - oa.op_type = OP_NOP; - oa.start = p1; - oa.end = p2; - oa.start_vcol = MIN(sc1, sc2); + getvvcol(curwin, p1, &sc1, NULL, &ec1); + getvvcol(curwin, p2, &sc2, NULL, &ec2); + oa->motion_type = kMTBlockWise; + oa->inclusive = true; + oa->op_type = OP_NOP; + oa->start = *p1; + oa->end = *p2; + oa->start_vcol = MIN(sc1, sc2); if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1) { - oa.end_vcol = sc2 - 1; + oa->end_vcol = sc2 - 1; } else { - oa.end_vcol = MAX(ec1, ec2); + oa->end_vcol = MAX(ec1, ec2); } } // Include the trailing byte of a multi-byte char. - int l = utfc_ptr2len(ml_get_pos(&p2)); + int l = utfc_ptr2len(ml_get_pos(p2)); if (l > 1) { - p2.col += l - 1; + p2->col += l - 1; + } + + return OK; +} + +/// "getregion()" function +static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + buf_T *const save_curbuf = curbuf; + const TriState save_virtual = virtual_op; + + pos_T p1, p2; + bool inclusive = true; + MotionType region_type = kMTUnknown; + oparg_T oa; + + if (getregionpos(argvars, rettv, &p1, &p2, &inclusive, ®ion_type, &oa) == FAIL) { + return; } for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { @@ -2955,10 +2984,127 @@ static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) tv_list_append_allocated_string(rettv->vval.v_list, akt); } - if (curbuf != save_curbuf) { - curbuf = save_curbuf; + // getregionpos() may change curbuf and virtual_op + curbuf = save_curbuf; + curwin->w_buffer = curbuf; + virtual_op = save_virtual; +} + +static void add_regionpos_range(typval_T *rettv, pos_T p1, pos_T p2) +{ + list_T *l1 = tv_list_alloc(2); + tv_list_append_list(rettv->vval.v_list, l1); + + list_T *l2 = tv_list_alloc(4); + tv_list_append_list(l1, l2); + + list_T *l3 = tv_list_alloc(4); + tv_list_append_list(l1, l3); + + tv_list_append_number(l2, curbuf->b_fnum); + tv_list_append_number(l2, p1.lnum); + tv_list_append_number(l2, p1.col); + tv_list_append_number(l2, p1.coladd); + + tv_list_append_number(l3, curbuf->b_fnum); + tv_list_append_number(l3, p2.lnum); + tv_list_append_number(l3, p2.col); + tv_list_append_number(l3, p2.coladd); +} + +/// "getregionpos()" function +static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + buf_T *const save_curbuf = curbuf; + const TriState save_virtual = virtual_op; + + pos_T p1, p2; + bool inclusive = true; + MotionType region_type = kMTUnknown; + bool allow_eol = false; + oparg_T oa; + + if (getregionpos(argvars, rettv, &p1, &p2, &inclusive, ®ion_type, &oa) == FAIL) { + return; + } + + if (argvars[2].v_type == VAR_DICT) { + allow_eol = tv_dict_get_bool(argvars[2].vval.v_dict, "eol", false); + } + + for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { + pos_T ret_p1, ret_p2; + colnr_T line_len = ml_get_len(lnum); + + if (region_type == kMTLineWise) { + ret_p1.col = 1; + ret_p1.coladd = 0; + ret_p2.col = MAXCOL; + ret_p2.coladd = 0; + } else { + struct block_def bd; + + if (region_type == kMTBlockWise) { + block_prep(&oa, &bd, lnum, false); + } else { + charwise_block_prep(p1, p2, &bd, lnum, inclusive); + } + + if (bd.is_oneChar) { // selection entirely inside one char + if (region_type == kMTBlockWise) { + ret_p1.col = bd.textcol; + ret_p1.coladd = bd.start_char_vcols - (bd.start_vcol - oa.start_vcol); + } else { + ret_p1.col = p1.col + 1; + ret_p1.coladd = p1.coladd; + } + } else if (region_type == kMTBlockWise && oa.start_vcol > bd.start_vcol) { + // blockwise selection entirely beyond end of line + ret_p1.col = MAXCOL; + ret_p1.coladd = oa.start_vcol - bd.start_vcol; + bd.is_oneChar = true; + } else if (bd.startspaces > 0) { + ret_p1.col = bd.textcol; + ret_p1.coladd = bd.start_char_vcols - bd.startspaces; + } else { + ret_p1.col = bd.textcol + 1; + ret_p1.coladd = 0; + } + + if (bd.is_oneChar) { // selection entirely inside one char + ret_p2.col = ret_p1.col; + ret_p2.coladd = ret_p1.coladd + bd.startspaces + bd.endspaces; + } else if (bd.endspaces > 0) { + ret_p2.col = bd.textcol + bd.textlen + 1; + ret_p2.coladd = bd.endspaces; + } else { + ret_p2.col = bd.textcol + bd.textlen; + ret_p2.coladd = 0; + } + } + + if (!allow_eol && ret_p1.col > line_len) { + ret_p1.col = 0; + ret_p1.coladd = 0; + } else if (ret_p1.col > line_len + 1) { + ret_p1.col = line_len + 1; + } + + if (!allow_eol && ret_p2.col > line_len) { + ret_p2.col = ret_p1.col == 0 ? 0 : line_len; + ret_p2.coladd = 0; + } else if (ret_p2.col > line_len + 1) { + ret_p2.col = line_len + 1; + } + + ret_p1.lnum = lnum; + ret_p2.lnum = lnum; + add_regionpos_range(rettv, ret_p1, ret_p2); } + // getregionpos() may change curbuf and virtual_op + curbuf = save_curbuf; + curwin->w_buffer = curbuf; virtual_op = save_virtual; } @@ -3416,19 +3562,19 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } else if (STRICMP(name, "syntax_items") == 0) { n = syntax_present(curwin); } else if (STRICMP(name, "clipboard_working") == 0) { - n = eval_has_provider("clipboard"); + n = eval_has_provider("clipboard", true); } else if (STRICMP(name, "pythonx") == 0) { - n = eval_has_provider("python3"); + n = eval_has_provider("python3", true); } else if (STRICMP(name, "wsl") == 0) { n = has_wsl(); #ifdef UNIX } else if (STRICMP(name, "unnamedplus") == 0) { - n = eval_has_provider("clipboard"); + n = eval_has_provider("clipboard", true); #endif } } - if (!n && eval_has_provider(name)) { + if (!n && eval_has_provider(name, true)) { n = true; } @@ -3974,7 +4120,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) FNE_CHECK_START); if (end != NULL && lv.ll_name != NULL) { if (*end != NUL) { - semsg(_(e_trailing_arg), end); + semsg(_(lv.ll_name_len == 0 ? e_invarg2 : e_trailing_arg), end); } else { if (lv.ll_tv == NULL) { dictitem_T *di = find_var(lv.ll_name, lv.ll_name_len, NULL, true); @@ -4630,7 +4776,7 @@ static void f_line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (wp != NULL && tp != NULL) { switchwin_T switchwin; if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { - check_cursor(); + check_cursor(curwin); fp = var2fpos(&argvars[0], true, &fnum, false); } restore_win_noblock(&switchwin, true); @@ -6454,7 +6600,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char *v = os_resolve_shortcut(fname); if (v == NULL) { if (os_is_reparse_point_include(fname)) { - v = os_realpath(fname, v); + v = os_realpath(fname, NULL, MAXPATHL + 1); } } rettv->vval.v_string = (v == NULL ? xstrdup(fname) : v); @@ -6606,7 +6752,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) xfree(buf); } # else - char *v = os_realpath(fname, NULL); + char *v = os_realpath(fname, NULL, MAXPATHL + 1); rettv->vval.v_string = v == NULL ? xstrdup(fname) : v; # endif #endif @@ -6957,12 +7103,13 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) .sa_tm = &tm, }; + const size_t patlen = strlen(pat); int subpatnum; // Repeat until {skip} returns false. while (true) { - subpatnum - = searchit(curwin, curbuf, &pos, NULL, dir, (char *)pat, 1, options, RE_SEARCH, &sia); + subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, (char *)pat, patlen, 1, + options, RE_SEARCH, &sia); // finding the first match again means there is no match where {skip} // evaluates to zero. if (firstpos.lnum != 0 && equalpos(pos, firstpos)) { @@ -7016,7 +7163,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) } // "/$" will put the cursor after the end of the line, may need to // correct that here - check_cursor(); + check_cursor(curwin); } // If 'n' flag is used: restore cursor position. @@ -7517,16 +7664,20 @@ int do_searchpair(const char *spat, const char *mpat, const char *epat, int dir, // Make two search patterns: start/end (pat2, for in nested pairs) and // start/middle/end (pat3, for the top pair). - const size_t pat2_len = strlen(spat) + strlen(epat) + 17; - char *pat2 = xmalloc(pat2_len); - const size_t pat3_len = strlen(spat) + strlen(mpat) + strlen(epat) + 25; - char *pat3 = xmalloc(pat3_len); - snprintf(pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); + const size_t spatlen = strlen(spat); + const size_t epatlen = strlen(epat); + const size_t pat2size = spatlen + epatlen + 17; + char *pat2 = xmalloc(pat2size); + const size_t pat3size = spatlen + strlen(mpat) + epatlen + 25; + char *pat3 = xmalloc(pat3size); + int pat2len = snprintf(pat2, pat2size, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); + int pat3len; if (*mpat == NUL) { STRCPY(pat3, pat2); + pat3len = pat2len; } else { - snprintf(pat3, pat3_len, - "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat); + pat3len = snprintf(pat3, pat3size, + "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat); } if (flags & SP_START) { options |= SEARCH_START; @@ -7543,13 +7694,15 @@ int do_searchpair(const char *spat, const char *mpat, const char *epat, int dir, pos_T foundpos; clearpos(&foundpos); char *pat = pat3; + assert(pat3len >= 0); + size_t patlen = (size_t)pat3len; while (true) { searchit_arg_T sia = { .sa_stop_lnum = lnum_stop, .sa_tm = &tm, }; - int n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1, + int n = searchit(curwin, curbuf, &pos, NULL, dir, pat, patlen, 1, options, RE_SEARCH, &sia); if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) { // didn't find it or found the first match again: FAIL @@ -7778,7 +7931,7 @@ static void set_position(typval_T *argvars, typval_T *rettv, bool charpos) curwin->w_curswant = curswant - 1; curwin->w_set_curswant = false; } - check_cursor(); + check_cursor(curwin); rettv->vval.v_number = 0; } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { // set mark @@ -8283,7 +8436,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData fptr size_t len = 0; if (argvars[0].v_type == VAR_UNKNOWN) { // Find the start and length of the badly spelled word. - len = spell_move_to(curwin, FORWARD, true, true, &attr); + len = spell_move_to(curwin, FORWARD, SMT_ALL, true, &attr); if (len != 0) { word = get_cursor_pos_ptr(); curwin->w_set_curswant = true; @@ -8675,7 +8828,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 && (size_t)col < strlen(ml_get(lnum))) { + && col >= 0 && col < ml_get_len(lnum)) { id = syn_get_id(curwin, lnum, col, trans, NULL, false); } @@ -8798,8 +8951,8 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr CLEAR_FIELD(str); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && (size_t)col <= strlen(ml_get(lnum)) && curwin->w_p_cole > 0) { + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count + && col >= 0 && col <= ml_get_len(lnum) && curwin->w_p_cole > 0) { syn_get_id(curwin, lnum, col, false, NULL, false); syntax_flags = get_syntax_info(&matchid); @@ -8832,10 +8985,8 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const linenr_T lnum = tv_get_lnum(argvars); const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; - if (lnum >= 1 - && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 - && (size_t)col <= strlen(ml_get(lnum))) { + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count + && col >= 0 && col <= ml_get_len(lnum)) { tv_list_alloc_ret(rettv, kListLenMayKnow); syn_get_id(curwin, lnum, col, false, NULL, true); @@ -9193,7 +9344,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) goto theend; } - check_cursor(); + check_cursor(curwin); winchanged = true; } @@ -9205,9 +9356,9 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (fp->col < 0) { fp->col = 0; } else { - const size_t len = strlen(ml_get(fp->lnum)); - if (fp->col > (colnr_T)len) { - fp->col = (colnr_T)len; + const colnr_T len = ml_get_len(fp->lnum); + if (fp->col > len) { + fp->col = len; } } getvvcol(curwin, fp, &vcol_start, NULL, &vcol_end); diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 9328f53dbd..eb8c89c36e 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -859,7 +859,7 @@ int tv_list_slice_or_index(list_T *list, bool range, varnumber_T n1_arg, varnumb // A list index out of range is an error. if (!range) { if (verbose) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)n1); + semsg(_(e_list_index_out_of_range_nr), (int64_t)n1_arg); } return FAIL; } diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h index 0d6ee28adc..e88e6a222a 100644 --- a/src/nvim/eval/typval_defs.h +++ b/src/nvim/eval/typval_defs.h @@ -364,6 +364,7 @@ struct ufunc { struct partial_S { int pt_refcount; ///< Reference count. + int pt_copyID; char *pt_name; ///< Function name; when NULL use pt_func->name. ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with pt_name. bool pt_auto; ///< When true the partial was created by using dict.member diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index d16814ed1e..0ec07399b4 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -345,7 +345,7 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg) char *p = xmalloc(len); ((char **)(newlines.ga_data))[newlines.ga_len++] = p; STRCPY(p, "return "); - xstrlcpy(p + 7, start, (size_t)(end - start) + 1); + xmemcpyz(p + 7, start, (size_t)(end - start)); if (strstr(p + 7, "a:") == NULL) { // No a: variables are used for sure. flags |= FC_NOARGS; @@ -2300,17 +2300,28 @@ void ex_function(exarg_T *eap) arg = fudi.fd_newkey; } if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) { - int j = ((uint8_t)(*arg) == K_SPECIAL) ? 3 : 0; - while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) { - j++; + char *name_base = arg; + if ((uint8_t)(*arg) == K_SPECIAL) { + name_base = vim_strchr(arg, '_'); + if (name_base == NULL) { + name_base = arg + 3; + } else { + name_base++; + } } - if (arg[j] != NUL) { + int i; + for (i = 0; name_base[i] != NUL && (i == 0 + ? eval_isnamec1(name_base[i]) + : eval_isnamec(name_base[i])); i++) {} + if (name_base[i] != NUL) { emsg_funcname(e_invarg2, arg); + goto ret_free; } } // Disallow using the g: dict. if (fudi.fd_dict != NULL && fudi.fd_dict->dv_scope == VAR_DEF_SCOPE) { emsg(_("E862: Cannot use g: here")); + goto ret_free; } } @@ -2360,7 +2371,7 @@ void ex_function(exarg_T *eap) // Read the body of the function, until ":endfunction" is found. if (KeyTyped) { // Check if the function already exists, don't let the user type the - // whole function before telling him it doesn't work! For a script we + // whole function before telling them it doesn't work! For a script we // need to skip the body to be able to find what follows. if (!eap->skip && !eap->forceit) { if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL) { @@ -2404,10 +2415,10 @@ void ex_function(exarg_T *eap) } } else { xfree(line_to_free); - if (eap->getline == NULL) { + if (eap->ea_getline == NULL) { theline = getcmdline(':', 0, indent, do_concat); } else { - theline = eap->getline(':', eap->cookie, indent, do_concat); + theline = eap->ea_getline(':', eap->cookie, indent, do_concat); } line_to_free = theline; } @@ -2428,7 +2439,7 @@ void ex_function(exarg_T *eap) } // Detect line continuation: SOURCING_LNUM increased more than one. - linenr_T sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie); + linenr_T sourcing_lnum_off = get_sourced_lnum(eap->ea_getline, eap->cookie); if (SOURCING_LNUM < sourcing_lnum_off) { sourcing_lnum_off -= SOURCING_LNUM; } else { @@ -2571,11 +2582,13 @@ void ex_function(exarg_T *eap) // and ":let [a, b] =<< [trim] EOF" arg = p; if (checkforcmd(&arg, "let", 2)) { - while (vim_strchr("$@&", *arg) != NULL) { - arg++; + int var_count = 0; + int semicolon = 0; + arg = (char *)skip_var_list(arg, &var_count, &semicolon, true); + if (arg != NULL) { + arg = skipwhite(arg); } - arg = skipwhite(find_name_end(arg, NULL, NULL, FNE_INCL_BR)); - if (arg[0] == '=' && arg[1] == '<' && arg[2] == '<') { + if (arg != NULL && strncmp(arg, "=<<", 3) == 0) { p = skipwhite(arg + 3); while (true) { if (strncmp(p, "trim", 4) == 0) { diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 91ac60d8ea..2eca209ea3 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -57,11 +57,13 @@ typedef int (*ex_unletlock_callback)(lval_T *, char *, exarg_T *, int); #define DICT_MAXNEST 100 // maximum nesting of lists and dicts static const char *e_letunexp = N_("E18: Unexpected characters in :let"); +static const char e_double_semicolon_in_list_of_variables[] + = N_("E452: Double ; in list of variables"); static const char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); static const char e_setting_v_str_to_value_with_wrong_type[] = N_("E963: Setting v:%s to value with wrong type"); -static const char e_cannot_use_heredoc_here[] - = N_("E991: Cannot use =<< here"); +static const char e_missing_end_marker_str[] = N_("E990: Missing end marker '%s'"); +static const char e_cannot_use_heredoc_here[] = N_("E991: Cannot use =<< here"); /// Evaluate one Vim expression {expr} in string "p" and append the /// resulting string to "gap". "p" points to the opening "{". @@ -86,7 +88,7 @@ char *eval_one_expr_in_str(char *p, garray_T *gap, bool evaluate) } if (evaluate) { *block_end = NUL; - char *expr_val = eval_to_string(block_start, true); + char *expr_val = eval_to_string(block_start, false); *block_end = '}'; if (expr_val == NULL) { return NULL; @@ -177,8 +179,15 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) int text_indent_len = 0; char *text_indent = NULL; char dot[] = "."; - - if (eap->getline == NULL) { + bool heredoc_in_string = false; + char *line_arg = NULL; + char *nl_ptr = vim_strchr(cmd, '\n'); + + if (nl_ptr != NULL) { + heredoc_in_string = true; + line_arg = nl_ptr + 1; + *nl_ptr = NUL; + } else if (eap->ea_getline == NULL) { emsg(_(e_cannot_use_heredoc_here)); return NULL; } @@ -214,11 +223,12 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) break; } + const char comment_char = '"'; // The marker is the next word. - if (*cmd != NUL && *cmd != '"') { + if (*cmd != NUL && *cmd != comment_char) { marker = skipwhite(cmd); char *p = skiptowhite(marker); - if (*skipwhite(p) != NUL && *skipwhite(p) != '"') { + if (*skipwhite(p) != NUL && *skipwhite(p) != comment_char) { semsg(_(e_trailing_arg), p); return NULL; } @@ -244,13 +254,34 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) int mi = 0; int ti = 0; - xfree(theline); - theline = eap->getline(NUL, eap->cookie, 0, false); - if (theline == NULL) { - if (!script_get) { - semsg(_("E990: Missing end marker '%s'"), marker); + if (heredoc_in_string) { + // heredoc in a string separated by newlines. Get the next line + // from the string. + + if (*line_arg == NUL) { + if (!script_get) { + semsg(_(e_missing_end_marker_str), marker); + } + break; + } + + theline = line_arg; + char *next_line = vim_strchr(theline, '\n'); + if (next_line == NULL) { + line_arg += strlen(line_arg); + } else { + *next_line = NUL; + line_arg = next_line + 1; + } + } else { + xfree(theline); + theline = eap->ea_getline(NUL, eap->cookie, 0, false); + if (theline == NULL) { + if (!script_get) { + semsg(_(e_missing_end_marker_str), marker); + } + break; } - break; } // with "trim": skip the indent matching the :let line to find the @@ -296,13 +327,17 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) eval_failed = true; continue; } - xfree(theline); - theline = str; + tv_list_append_allocated_string(l, str); + } else { + tv_list_append_string(l, str, -1); } - - tv_list_append_string(l, str, -1); } - xfree(theline); + if (heredoc_in_string) { + // Next command follows the heredoc in the string. + eap->nextcmd = line_arg; + } else { + xfree(theline); + } xfree(text_indent); if (eval_failed) { @@ -341,7 +376,7 @@ void ex_let(exarg_T *eap) const char *argend; int first = true; - argend = skip_var_list(arg, &var_count, &semicolon); + argend = skip_var_list(arg, &var_count, &semicolon, false); if (argend == NULL) { return; } @@ -515,10 +550,11 @@ int ex_let_vars(char *arg_start, typval_T *tv, int copy, int semicolon, int var_ /// Skip over assignable variable "var" or list of variables "[var, var]". /// Used for ":let varvar = expr" and ":for varvar in expr". /// For "[var, var]" increment "*var_count" for each variable. -/// for "[var, var; var]" set "semicolon". +/// for "[var, var; var]" set "semicolon" to 1. +/// If "silent" is true do not give an "invalid argument" error message. /// /// @return NULL for an error. -const char *skip_var_list(const char *arg, int *var_count, int *semicolon) +const char *skip_var_list(const char *arg, int *var_count, int *semicolon, bool silent) { if (*arg == '[') { const char *s; @@ -528,7 +564,9 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon) p = skipwhite(p + 1); // skip whites after '[', ';' or ',' s = skip_var_one(p); if (s == p) { - semsg(_(e_invarg2), p); + if (!silent) { + semsg(_(e_invarg2), p); + } return NULL; } (*var_count)++; @@ -538,12 +576,16 @@ const char *skip_var_list(const char *arg, int *var_count, int *semicolon) break; } else if (*p == ';') { if (*semicolon == 1) { - emsg(_("E452: Double ; in list of variables")); + if (!silent) { + emsg(_(e_double_semicolon_in_list_of_variables)); + } return NULL; } *semicolon = 1; } else if (*p != ',') { - semsg(_(e_invarg2), p); + if (!silent) { + semsg(_(e_invarg2), p); + } return NULL; } } diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index b8aa0c9641..68de40f983 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -14,6 +14,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/window.h" +#include "nvim/ex_getln.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" #include "nvim/gettext_defs.h" @@ -515,7 +516,7 @@ bool win_execute_before(win_execute_T *args, win_T *wp, tabpage_T *tp) } if (switch_win_noblock(&args->switchwin, wp, tp, true) == OK) { - check_cursor(); + check_cursor(curwin); return true; } return false; @@ -539,7 +540,7 @@ void win_execute_after(win_execute_T *args) // In case the command moved the cursor or changed the Visual area, // check it is valid. - check_cursor(); + check_cursor(curwin); if (VIsual_active) { check_pos(curbuf, &VIsual); } @@ -583,9 +584,13 @@ void f_win_getid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int id = (int)tv_get_number(&argvars[0]); + if (curwin->handle == id) { + // Nothing to do. + rettv->vval.v_number = 1; + return; + } - if (cmdwin_type != 0) { - emsg(_(e_cmdwin)); + if (text_or_buf_locked()) { return; } FOR_ALL_TAB_WINDOWS(tp, wp) { @@ -659,55 +664,19 @@ void f_win_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1); } -/// Move the window wp into a new split of targetwin in a given direction -static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags) -{ - int height = wp->w_height; - win_T *oldwin = curwin; - - if (wp == targetwin || is_aucmd_win(wp)) { - return; - } - - // Jump to the target window - if (curwin != targetwin) { - win_goto(targetwin); - } - - // Remove the old window and frame from the tree of frames - int dir; - winframe_remove(wp, &dir, NULL); - win_remove(wp, NULL); - last_status(false); // may need to remove last status line - win_comp_pos(); // recompute window positions - - // Split a window on the desired side and put the old window there - win_split_ins(size, flags, wp, dir); - - // If splitting horizontally, try to preserve height - if (size == 0 && !(flags & WSP_VERT)) { - win_setheight_win(height, wp); - if (p_ea) { - win_equal(wp, true, 'v'); - } - } - - if (oldwin != curwin) { - win_goto(oldwin); - } -} - /// "win_splitmove()" function void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { win_T *wp = find_win_by_nr_or_id(&argvars[0]); win_T *targetwin = find_win_by_nr_or_id(&argvars[1]); + win_T *oldwin = curwin; + + rettv->vval.v_number = -1; if (wp == NULL || targetwin == NULL || wp == targetwin || !win_valid(wp) || !win_valid(targetwin) - || win_float_valid(wp) || win_float_valid(targetwin)) { + || targetwin->w_floating) { emsg(_(e_invalwindow)); - rettv->vval.v_number = -1; return; } @@ -732,7 +701,27 @@ void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) size = (int)tv_dict_get_number(d, "size"); } - win_move_into_split(wp, targetwin, size, flags); + // Check if we're allowed to continue before we bother switching windows. + if (is_aucmd_win(wp) || text_or_buf_locked() || check_split_disallowed(wp) == FAIL) { + return; + } + + if (curwin != targetwin) { + win_goto(targetwin); + } + + // Autocommands may have sent us elsewhere or closed "wp" or "oldwin". + if (curwin == targetwin && win_valid(wp)) { + if (win_splitmove(wp, size, flags) == OK) { + rettv->vval.v_number = 0; + } + } else { + emsg(_(e_auabort)); + } + + if (oldwin != curwin && win_valid(oldwin)) { + win_goto(oldwin); + } } /// "win_gettype(nr)" function @@ -785,7 +774,7 @@ void f_winbufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "wincol()" function void f_wincol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - validate_cursor(); + validate_cursor(curwin); rettv->vval.v_number = curwin->w_wcol + 1; } @@ -822,7 +811,7 @@ void f_winlayout(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "winline()" function void f_winline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - validate_cursor(); + validate_cursor(curwin); rettv->vval.v_number = curwin->w_wrow + 1; } @@ -894,10 +883,10 @@ void f_winrestview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv); } - check_cursor(); + check_cursor(curwin); win_new_height(curwin, curwin->w_height); win_new_width(curwin, curwin->w_width); - changed_window_setting(); + changed_window_setting(curwin); if (curwin->w_topline <= 0) { curwin->w_topline = 1; @@ -967,11 +956,8 @@ int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool n if (tp != NULL) { switchwin->sw_curtab = curtab; if (no_display) { - curtab->tp_firstwin = firstwin; - curtab->tp_lastwin = lastwin; - curtab = tp; - firstwin = curtab->tp_firstwin; - lastwin = curtab->tp_lastwin; + unuse_tabpage(curtab); + use_tabpage(tp); } else { goto_tabpage_tp(tp, false, false); } @@ -998,11 +984,12 @@ void restore_win_noblock(switchwin_T *switchwin, bool no_display) { if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) { if (no_display) { - curtab->tp_firstwin = firstwin; - curtab->tp_lastwin = lastwin; - curtab = switchwin->sw_curtab; - firstwin = curtab->tp_firstwin; - lastwin = curtab->tp_lastwin; + win_T *const old_tp_curwin = curtab->tp_curwin; + + unuse_tabpage(curtab); + // Don't change the curwin of the tabpage we temporarily visited. + curtab->tp_curwin = old_tp_curwin; + use_tabpage(switchwin->sw_curtab); } else { goto_tabpage_tp(switchwin->sw_curtab, false, false); } diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index a7966994e0..f77d686c10 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -90,7 +90,7 @@ int libuv_process_spawn(LibuvProcess *uvproc) int status; if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) { - ELOG("uv_spawn(%s) failed: %s", uvproc->uvopts.file, uv_strerror(status)); + ILOG("uv_spawn(%s) failed: %s", uvproc->uvopts.file, uv_strerror(status)); if (uvproc->uvopts.env) { os_free_fullenv(uvproc->uvopts.env); } diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index 93948d3eaa..e1ebcecbd6 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -39,10 +39,8 @@ void loop_init(Loop *loop, void *data) /// @param ms 0: non-blocking poll. /// > 0: timeout after `ms`. /// < 0: wait forever. -/// @param once true: process at most one `Loop.uv` event. -/// false: process until `ms` timeout (only has effect if `ms` > 0). /// @return true if `ms` > 0 and was reached -bool loop_uv_run(Loop *loop, int64_t ms, bool once) +static bool loop_uv_run(Loop *loop, int64_t ms) { if (loop->recursive++) { abort(); // Should not re-enter uv_run @@ -60,9 +58,7 @@ bool loop_uv_run(Loop *loop, int64_t ms, bool once) mode = UV_RUN_NOWAIT; } - do { - uv_run(&loop->uv, mode); - } while (ms > 0 && !once && !*timeout_expired); + uv_run(&loop->uv, mode); if (ms > 0) { uv_timer_stop(&loop->poll_timer); @@ -83,7 +79,7 @@ bool loop_uv_run(Loop *loop, int64_t ms, bool once) /// @return true if `ms` > 0 and was reached bool loop_poll_events(Loop *loop, int64_t ms) { - bool timeout_expired = loop_uv_run(loop, ms, true); + bool timeout_expired = loop_uv_run(loop, ms); multiqueue_process_events(loop->fast_events); return timeout_expired; } diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index f50c8a0e5a..6b4ab472e4 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -106,7 +106,7 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) // http://docs.libuv.org/en/latest/stream.html#c.uv_read_start. // // We don't need to do anything with the RBuffer because the next call - // to `alloc_cb` will return the same unused pointer(`rbuffer_produced` + // to `alloc_cb` will return the same unused pointer (`rbuffer_produced` // won't be called) if (cnt == UV_ENOBUFS || cnt == 0) { return; diff --git a/src/nvim/event/wstream.c b/src/nvim/event/wstream.c index 406ff1620d..c67a9b96ed 100644 --- a/src/nvim/event/wstream.c +++ b/src/nvim/event/wstream.c @@ -103,7 +103,7 @@ err: /// Creates a WBuffer object for holding output data. Instances of this /// object can be reused across Stream instances, and the memory is freed -/// automatically when no longer needed(it tracks the number of references +/// automatically when no longer needed (it tracks the number of references /// internally) /// /// @param data Data stored by the WBuffer @@ -111,7 +111,7 @@ err: /// @param refcount The number of references for the WBuffer. This will be used /// by Stream instances to decide when a WBuffer should be freed. /// @param cb Pointer to function that will be responsible for freeing -/// the buffer data(passing 'free' will work as expected). +/// the buffer data (passing `xfree` will work as expected). /// @return The allocated WBuffer instance WBuffer *wstream_new_buffer(char *data, size_t size, size_t refcount, wbuffer_data_finalizer cb) FUNC_ATTR_NONNULL_ARG(1) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 74ad8e95a2..834cc6698a 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -541,7 +541,7 @@ void ex_sort(exarg_T *eap) // Also get the longest line length for allocating "sortbuf". for (linenr_T lnum = eap->line1; lnum <= eap->line2; lnum++) { char *s = ml_get(lnum); - int len = (int)strlen(s); + int len = ml_get_len(lnum); if (maxlen < len) { maxlen = len; } @@ -643,8 +643,8 @@ void ex_sort(exarg_T *eap) } char *s = ml_get(get_lnum); - size_t bytelen = strlen(s) + 1; // include EOL in bytelen - old_count += (bcount_t)bytelen; + colnr_T bytelen = ml_get_len(get_lnum) + 1; // include EOL in bytelen + old_count += bytelen; if (!unique || i == 0 || string_compare(s, sortbuf1) != 0) { // Copy the line into a buffer, it may become invalid in // ml_append(). And it's needed for "unique". @@ -652,7 +652,7 @@ void ex_sort(exarg_T *eap) if (ml_append(lnum++, sortbuf1, 0, false) == FAIL) { break; } - new_count += (bcount_t)bytelen; + new_count += bytelen; } fast_breakcheck(); if (got_int) { @@ -740,7 +740,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) return FAIL; } for (extra = 0, l = line1; l <= line2; l++) { - char *str = xstrdup(ml_get(l + extra)); + char *str = xstrnsave(ml_get(l + extra), (size_t)ml_get_len(l + extra)); ml_append(dest + l - line1, str, 0, false); xfree(str); if (dest < line1) { @@ -876,9 +876,8 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) curwin->w_cursor.lnum = n; while (line1 <= line2) { - // need to use xstrdup() because the line will be unlocked within - // ml_append() - char *p = xstrdup(ml_get(line1)); + // need to make a copy because the line will be unlocked within ml_append() + char *p = xstrnsave(ml_get(line1), (size_t)ml_get_len(line1)); ml_append(curwin->w_cursor.lnum, p, 0, false); xfree(p); @@ -2008,6 +2007,10 @@ static int check_readonly(int *forceit, buf_T *buf) /// GETFILE_OPEN_OTHER for successfully opening another file. int getfile(int fnum, char *ffname_arg, char *sfname_arg, bool setpm, linenr_T lnum, bool forceit) { + if (!check_can_set_curbuf_forceit(forceit)) { + return GETFILE_ERROR; + } + char *ffname = ffname_arg; char *sfname = sfname_arg; bool other; @@ -2463,7 +2466,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum // Since we are starting to edit a file, consider the filetype to be // unset. Helps for when an autocommand changes files and expects syntax // highlighting to work in the other file. - did_filetype = false; + curbuf->b_did_filetype = false; // other_file oldbuf // false false re-edit same file, buffer is re-used @@ -2634,14 +2637,14 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum if (newcol >= 0) { // position set by autocommands curwin->w_cursor.lnum = newlnum; curwin->w_cursor.col = newcol; - check_cursor(); + check_cursor(curwin); } else if (newlnum > 0) { // line number from caller or old position curwin->w_cursor.lnum = newlnum; check_cursor_lnum(curwin); if (solcol >= 0 && !p_sol) { // 'sol' is off: Use last known column. curwin->w_cursor.col = solcol; - check_cursor_col(); + check_cursor_col(curwin); curwin->w_cursor.coladd = 0; curwin->w_set_curswant = true; } else { @@ -2780,7 +2783,7 @@ void ex_append(exarg_T *eap) indent = get_indent_lnum(lnum); } } - if (eap->getline == NULL) { + if (eap->ea_getline == NULL) { // No getline() function, use the lines that follow. This ends // when there is no more. if (eap->nextcmd == NULL || *eap->nextcmd == NUL) { @@ -2800,7 +2803,8 @@ void ex_append(exarg_T *eap) // Set State to avoid the cursor shape to be set to MODE_INSERT // state when getline() returns. State = MODE_CMDLINE; - theline = eap->getline(eap->cstack->cs_looplevel > 0 ? -1 : NUL, eap->cookie, indent, true); + theline = eap->ea_getline(eap->cstack->cs_looplevel > 0 ? -1 : NUL, + eap->cookie, indent, true); State = save_State; } lines_left = Rows - 1; @@ -3095,8 +3099,9 @@ void sub_set_replacement(SubReplacementString sub) /// @param[in] save Save pattern to options, history /// /// @returns true if :substitute can be replaced with a join command -static bool sub_joining_lines(exarg_T *eap, char *pat, const char *sub, const char *cmd, bool save) - FUNC_ATTR_NONNULL_ARG(1, 3, 4) +static bool sub_joining_lines(exarg_T *eap, char *pat, size_t patlen, const char *sub, + const char *cmd, bool save) + FUNC_ATTR_NONNULL_ARG(1, 4, 5) { // TODO(vim): find a generic solution to make line-joining operations more // efficient, avoid allocating a string that grows in size. @@ -3134,10 +3139,10 @@ static bool sub_joining_lines(exarg_T *eap, char *pat, const char *sub, const ch if (save) { if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) { - save_re_pat(RE_SUBST, pat, magic_isset()); + save_re_pat(RE_SUBST, pat, patlen, magic_isset()); } // put pattern in history - add_to_history(HIST_SEARCH, pat, true, NUL); + add_to_history(HIST_SEARCH, pat, patlen, true, NUL); } return true; @@ -3296,7 +3301,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n if (nmatch > 1) { \ sub_firstlnum += (linenr_T)nmatch - 1; \ xfree(sub_firstline); \ - sub_firstline = xstrdup(ml_get(sub_firstlnum)); \ + sub_firstline = xstrnsave(ml_get(sub_firstlnum), \ + (size_t)ml_get_len(sub_firstlnum)); \ /* When going beyond the last line, stop substituting. */ \ if (sub_firstlnum <= line2) { \ do_again = true; \ @@ -3327,6 +3333,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n }; char *pat = NULL; char *sub = NULL; // init for GCC + size_t patlen = 0; int delimiter; bool has_second_delim = false; int sublen; @@ -3378,12 +3385,14 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n which_pat = RE_SEARCH; // use last '/' pattern } pat = ""; // empty search pattern + patlen = 0; delimiter = (uint8_t)(*cmd++); // remember delimiter character has_second_delim = true; } else { // find the end of the regexp which_pat = RE_LAST; // use last used regexp delimiter = (uint8_t)(*cmd++); // remember delimiter character pat = cmd; // remember start of search pat + patlen = strlen(pat); cmd = skip_regexp_ex(cmd, delimiter, magic_isset(), &eap->arg, NULL, NULL); if (cmd[0] == delimiter) { // end delimiter found *cmd++ = NUL; // replace it with a NUL @@ -3410,6 +3419,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n return 0; } pat = NULL; // search_regcomp() will use previous pattern + patlen = 0; sub = xstrdup(old_sub.sub); // Vi compatibility quirk: repeating with ":s" keeps the cursor in the @@ -3417,7 +3427,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n endcolumn = (curwin->w_curswant == MAXCOL); } - if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, cmdpreview_ns <= 0)) { + if (sub != NULL && sub_joining_lines(eap, pat, patlen, sub, cmd, cmdpreview_ns <= 0)) { xfree(sub); return 0; } @@ -3472,7 +3482,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n return 0; } - if (search_regcomp(pat, NULL, RE_SUBST, which_pat, + if (search_regcomp(pat, patlen, NULL, RE_SUBST, which_pat, (cmdpreview_ns > 0 ? 0 : SEARCH_HIS), ®match) == FAIL) { if (subflags.do_error) { emsg(_(e_invcmd)); @@ -3624,7 +3634,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n break; } if (sub_firstline == NULL) { - sub_firstline = xstrdup(ml_get(sub_firstlnum)); + sub_firstline = xstrnsave(ml_get(sub_firstlnum), + (size_t)ml_get_len(sub_firstlnum)); } // Save the line number of the last change for the final @@ -3761,7 +3772,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n // really update the line, it would change // what matches. Temporarily replace the line // and change it back afterwards. - orig_line = xstrdup(ml_get(lnum)); + orig_line = xstrnsave(ml_get(lnum), (size_t)ml_get_len(lnum)); char *new_line = concat_str(new_start, sub_firstline + copycol); // Position the cursor relative to the end of the line, the @@ -3783,7 +3794,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n highlight_match = true; update_topline(curwin); - validate_cursor(); + validate_cursor(curwin); redraw_later(curwin, UPD_SOME_VALID); show_cursor_info_later(true); update_screen(); @@ -4243,7 +4254,7 @@ skip: // when interactive leave cursor on the match if (!subflags.do_ask) { if (endcolumn) { - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } else { beginline(BL_WHITE | BL_FIX); } @@ -4274,7 +4285,7 @@ skip: if (subflags.do_ask && hasAnyFolding(curwin)) { // Cursor position may require updating - changed_window_setting(); + changed_window_setting(curwin); } vim_regfree(regmatch.regprog); @@ -4289,7 +4300,7 @@ skip: // Show 'inccommand' preview if there are matched lines. if (cmdpreview_ns > 0 && !aborting()) { if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable. - set_string_option_direct(kOptInccommand, "", 0, SID_NONE); + set_option_direct(kOptInccommand, STATIC_CSTR_AS_OPTVAL(""), 0, SID_NONE); } else if (*p_icm != NUL && pat != NULL) { if (pre_hl_id == 0) { pre_hl_id = syn_check_group(S_LEN("Substitute")); @@ -4384,6 +4395,7 @@ void ex_global(exarg_T *eap) char delim; // delimiter, normally '/' char *pat; + size_t patlen; regmmatch_T regmatch; // When nesting the command works on one line. This allows for @@ -4419,6 +4431,7 @@ void ex_global(exarg_T *eap) } cmd++; pat = ""; + patlen = 0; } else if (*cmd == NUL) { emsg(_("E148: Regular expression missing from global")); return; @@ -4428,6 +4441,7 @@ void ex_global(exarg_T *eap) delim = *cmd; // get the delimiter cmd++; // skip delimiter if there is one pat = cmd; // remember start of pattern + patlen = strlen(pat); cmd = skip_regexp_ex(cmd, delim, magic_isset(), &eap->arg, NULL, NULL); if (cmd[0] == delim) { // end delimiter found *cmd++ = NUL; // replace it with a NUL @@ -4435,7 +4449,7 @@ void ex_global(exarg_T *eap) } char *used_pat; - if (search_regcomp(pat, &used_pat, RE_BOTH, which_pat, + if (search_regcomp(pat, patlen, &used_pat, RE_BOTH, which_pat, SEARCH_HIS, ®match) == FAIL) { emsg(_(e_invcmd)); return; @@ -4510,7 +4524,7 @@ void global_exe(char *cmd) if (global_need_beginline) { beginline(BL_WHITE | BL_FIX); } else { - check_cursor(); // cursor may be beyond the end of the line + check_cursor(curwin); // cursor may be beyond the end of the line } // the cursor may not have moved in the text but a change in a previous @@ -4569,7 +4583,7 @@ bool prepare_tagpreview(bool undo_sync) RESET_BINDING(curwin); // don't take over 'scrollbind' and 'cursorbind' curwin->w_p_diff = false; // no 'diff' - set_string_option_direct(kOptFoldcolumn, "0", 0, SID_NONE); // no 'foldcolumn' + set_option_direct(kOptFoldcolumn, STATIC_CSTR_AS_OPTVAL("0"), 0, SID_NONE); // no 'foldcolumn' return true; } @@ -4588,7 +4602,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i buf_T *cmdpreview_buf = NULL; // disable file info message - set_string_option_direct(kOptShortmess, "F", 0, SID_NONE); + set_option_direct(kOptShortmess, STATIC_CSTR_AS_OPTVAL("F"), 0, SID_NONE); // Place cursor on nearest matching line, to undo do_sub() cursor placement. for (size_t i = 0; i < lines.subresults.size; i++) { @@ -4622,8 +4636,8 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i } char *str = NULL; // construct the line to show in here - size_t old_line_size = 0; - size_t line_size = 0; + colnr_T old_line_size = 0; + colnr_T line_size = 0; linenr_T linenr_preview = 0; // last line added to preview buffer linenr_T linenr_origbuf = 0; // last line added to original buffer linenr_T next_linenr = 0; // next line to show for the match @@ -4662,21 +4676,21 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i line = ""; } else { line = ml_get_buf(orig_buf, next_linenr); - line_size = strlen(line) + (size_t)col_width + 1; + line_size = ml_get_buf_len(orig_buf, next_linenr) + col_width + 1; // Reallocate if line not long enough if (line_size > old_line_size) { - str = xrealloc(str, line_size * sizeof(char)); + str = xrealloc(str, (size_t)line_size * sizeof(char)); old_line_size = line_size; } } // Put "|lnum| line" into `str` and append it to the preview buffer. - snprintf(str, line_size, "|%*" PRIdLINENR "| %s", col_width - 3, + snprintf(str, (size_t)line_size, "|%*" PRIdLINENR "| %s", col_width - 3, next_linenr, line); if (linenr_preview == 0) { ml_replace_buf(cmdpreview_buf, 1, str, true, false); } else { - ml_append_buf(cmdpreview_buf, linenr_preview, str, (colnr_T)line_size, false); + ml_append_buf(cmdpreview_buf, linenr_preview, str, line_size, false); } linenr_preview += 1; } @@ -4689,7 +4703,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i xfree(str); - set_string_option_direct(kOptShortmess, save_shm_p, 0, SID_NONE); + set_option_direct(kOptShortmess, CSTR_AS_OPTVAL(save_shm_p), 0, SID_NONE); xfree(save_shm_p); return preview ? 2 : 1; diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 1318eda5eb..c8574c805c 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -1,6 +1,6 @@ local bit = require 'bit' -local module = {} +local M = {} -- Description of the values below is contained in ex_cmds_defs.h file. -- "EX_" prefix is omitted. @@ -32,14 +32,14 @@ local FILES = bit.bor(XFILE, EXTRA) local WORD1 = bit.bor(EXTRA, NOSPC) local FILE1 = bit.bor(FILES, NOSPC) -module.flags = { +M.flags = { RANGE = RANGE, DFLALL = DFLALL, PREVIEW = PREVIEW, } -- The following table is described in ex_cmds_defs.h file. -module.cmds = { +M.cmds = { { command = 'append', flags = bit.bor(BANG, RANGE, ZEROR, TRLBAR, CMDWIN, LOCK_OK, MODIFY), @@ -337,7 +337,7 @@ module.cmds = { { command = 'caddbuffer', flags = bit.bor(RANGE, WORD1, TRLBAR), - addr_type = 'ADDR_OTHER', + addr_type = 'ADDR_LINES', func = 'ex_cbuffer', }, { @@ -373,7 +373,7 @@ module.cmds = { { command = 'cbuffer', flags = bit.bor(BANG, RANGE, WORD1, TRLBAR), - addr_type = 'ADDR_OTHER', + addr_type = 'ADDR_LINES', func = 'ex_cbuffer', }, { @@ -459,7 +459,7 @@ module.cmds = { { command = 'cgetbuffer', flags = bit.bor(RANGE, WORD1, TRLBAR), - addr_type = 'ADDR_OTHER', + addr_type = 'ADDR_LINES', func = 'ex_cbuffer', }, { @@ -812,7 +812,7 @@ module.cmds = { }, { command = 'drop', - flags = bit.bor(FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR), + flags = bit.bor(BANG, FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR), addr_type = 'ADDR_NONE', func = 'ex_drop', }, @@ -1329,7 +1329,7 @@ module.cmds = { { command = 'laddbuffer', flags = bit.bor(RANGE, WORD1, TRLBAR), - addr_type = 'ADDR_OTHER', + addr_type = 'ADDR_LINES', func = 'ex_cbuffer', }, { @@ -1353,7 +1353,7 @@ module.cmds = { { command = 'lbuffer', flags = bit.bor(BANG, RANGE, WORD1, TRLBAR), - addr_type = 'ADDR_OTHER', + addr_type = 'ADDR_LINES', func = 'ex_cbuffer', }, { @@ -1451,7 +1451,7 @@ module.cmds = { { command = 'lgetbuffer', flags = bit.bor(RANGE, WORD1, TRLBAR), - addr_type = 'ADDR_OTHER', + addr_type = 'ADDR_LINES', func = 'ex_cbuffer', }, { @@ -3359,4 +3359,4 @@ module.cmds = { }, } -return module +return M diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 8016e37ca7..f4a6e61831 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -212,10 +212,24 @@ void dialog_changed(buf_T *buf, bool checkall) } if (ret == VIM_YES) { - if (buf->b_fname != NULL - && check_overwrite(&ea, buf, buf->b_fname, buf->b_ffname, false) == OK) { + bool empty_bufname = buf->b_fname == NULL; + if (empty_bufname) { + buf_set_name(buf->b_fnum, "Untitled"); + } + + if (check_overwrite(&ea, buf, buf->b_fname, buf->b_ffname, false) == OK) { // didn't hit Cancel - buf_write_all(buf, false); + if (buf_write_all(buf, false) == OK) { + return; + } + } + + // restore to empty when write failed + if (empty_bufname) { + XFREE_CLEAR(buf->b_fname); + XFREE_CLEAR(buf->b_ffname); + XFREE_CLEAR(buf->b_sfname); + unchanged(buf, true, false); } } else if (ret == VIM_NO) { unchanged(buf, true, false); @@ -444,6 +458,30 @@ int buf_write_all(buf_T *buf, bool forceit) /// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo" void ex_listdo(exarg_T *eap) { + if (curwin->w_p_wfb) { + if ((eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) && !eap->forceit) { + // Disallow :ldo if 'winfixbuf' is applied + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + return; + } + + if (win_valid(prevwin) && !prevwin->w_p_wfb) { + // 'winfixbuf' is set; attempt to change to a window without it. + win_goto(prevwin); + } + if (curwin->w_p_wfb) { + // Split the window, which will be 'nowinfixbuf', and set curwin to that + (void)win_split(0, 0); + + if (curwin->w_p_wfb) { + // Autocommands set 'winfixbuf' or sent us to another window + // with it set, or we failed to split the window. Give up. + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + return; + } + } + } + char *save_ei = NULL; // Temporarily override SHM_OVER and SHM_OVERALL to avoid that file @@ -582,7 +620,7 @@ void ex_listdo(exarg_T *eap) i++; // execute the command if (execute) { - do_cmdline(eap->arg, eap->getline, eap->cookie, DOCMD_VERBOSE + DOCMD_NOWAIT); + do_cmdline(eap->arg, eap->ea_getline, eap->cookie, DOCMD_VERBOSE + DOCMD_NOWAIT); } if (eap->cmdidx == CMD_bufdo) { @@ -630,7 +668,7 @@ void ex_listdo(exarg_T *eap) } if (eap->cmdidx == CMD_windo && execute) { - validate_cursor(); // cursor may have moved + validate_cursor(curwin); // cursor may have moved // required when 'scrollbind' has been set if (curwin->w_p_scb) { do_check_scrollbind(true); @@ -842,11 +880,13 @@ void ex_drop(exarg_T *eap) const int save_ar = curbuf->b_p_ar; // reload the file if it is newer - curbuf->b_p_ar = 1; + curbuf->b_p_ar = true; buf_check_timestamp(curbuf); curbuf->b_p_ar = save_ar; } - ex_rewind(eap); + if (curbuf->b_ml.ml_flags & ML_EMPTY) { + ex_rewind(eap); + } return; } } diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 827680cbb5..07f92ca169 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -167,8 +167,8 @@ struct exarg { int bad_char; ///< BAD_KEEP, BAD_DROP or replacement byte int useridx; ///< user command index char *errmsg; ///< returned error message - LineGetter getline; ///< Function used to get the next line - void *cookie; ///< argument for getline() + LineGetter ea_getline; ///< function used to get the next line + void *cookie; ///< argument for ea_getline() cstack_T *cstack; ///< condition stack for ":if" etc. }; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 2913f6d4e9..1fcfc505df 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -144,7 +144,7 @@ struct loop_cookie { int current_line; // last read line from growarray int repeating; // true when looping a second time // When "repeating" is false use "getline" and "cookie" to get lines - char *(*getline)(int, void *, int, bool); + LineGetter lc_getline; void *cookie; }; @@ -216,6 +216,38 @@ static void restore_dbg_stuff(struct dbg_stuff *dsp) current_exception = dsp->current_exception; } +/// Check if ffname differs from fnum. +/// fnum is a buffer number. 0 == current buffer, 1-or-more must be a valid buffer ID. +/// ffname is a full path to where a buffer lives on-disk or would live on-disk. +static bool is_other_file(int fnum, char *ffname) +{ + if (fnum != 0) { + if (fnum == curbuf->b_fnum) { + return false; + } + + return true; + } + + if (ffname == NULL) { + return true; + } + + if (*ffname == NUL) { + return false; + } + + if (!curbuf->file_id_valid + && curbuf->b_sfname != NULL + && *curbuf->b_sfname != NUL) { + // This occurs with unsaved buffers. In which case `ffname` + // actually corresponds to curbuf->b_sfname + return path_fnamecmp(ffname, curbuf->b_sfname) != 0; + } + + return otherfile(ffname); +} + /// Repeatedly get commands for Ex mode, until the ":vi" command is given. void do_exmode(void) { @@ -590,7 +622,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) cmd_cookie = (void *)&cmd_loop_cookie; cmd_loop_cookie.lines_gap = &lines_ga; cmd_loop_cookie.current_line = current_line; - cmd_loop_cookie.getline = fgetline; + cmd_loop_cookie.lc_getline = fgetline; cmd_loop_cookie.cookie = cookie; cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len); @@ -971,10 +1003,10 @@ static char *get_loop_line(int c, void *cookie, int indent, bool do_concat) } char *line; // First time inside the ":while"/":for": get line normally. - if (cp->getline == NULL) { + if (cp->lc_getline == NULL) { line = getcmdline(c, 0, indent, do_concat); } else { - line = cp->getline(c, cp->cookie, indent, do_concat); + line = cp->lc_getline(c, cp->cookie, indent, do_concat); } if (line != NULL) { store_loop_line(cp->lines_gap, line); @@ -1011,7 +1043,7 @@ bool getline_equal(LineGetter fgetline, void *cookie, LineGetter func) LineGetter gp = fgetline; struct loop_cookie *cp = (struct loop_cookie *)cookie; while (gp == get_loop_line) { - gp = cp->getline; + gp = cp->lc_getline; cp = cp->cookie; } return gp == func; @@ -1029,7 +1061,7 @@ void *getline_cookie(LineGetter fgetline, void *cookie) LineGetter gp = fgetline; struct loop_cookie *cp = (struct loop_cookie *)cookie; while (gp == get_loop_line) { - gp = cp->getline; + gp = cp->lc_getline; cp = cp->cookie; } return cp; @@ -1456,7 +1488,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, const cha .line2 = 1, .cmd = cmdline, .cmdlinep = &cmdline, - .getline = NULL, + .ea_getline = NULL, .cookie = NULL, }; @@ -1743,8 +1775,8 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) && eap->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. - hasFolding(eap->line1, &eap->line1, NULL); - hasFolding(eap->line2, NULL, &eap->line2); + hasFolding(curwin, eap->line1, &eap->line1, NULL); + hasFolding(curwin, eap->line2, NULL, &eap->line2); } // Use first argument as count when possible @@ -1965,7 +1997,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter // The "ea" structure holds the arguments that can be used. ea.cmd = *cmdlinep; ea.cmdlinep = cmdlinep; - ea.getline = fgetline; + ea.ea_getline = fgetline; ea.cookie = cookie; ea.cstack = cstack; @@ -2213,8 +2245,8 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter && 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. - hasFolding(ea.line1, &ea.line1, NULL); - hasFolding(ea.line2, NULL, &ea.line2); + hasFolding(curwin, ea.line1, &ea.line1, NULL); + hasFolding(curwin, ea.line2, NULL, &ea.line2); } // For the ":make" and ":grep" commands we insert the 'makeprg'/'grepprg' @@ -2440,7 +2472,7 @@ int parse_command_modifiers(exarg_T *eap, const char **errormsg, cmdmod_T *cmod, // in ex mode, an empty line works like :+ if (*eap->cmd == NUL && exmode_active - && getline_equal(eap->getline, eap->cookie, getexline) + && getline_equal(eap->ea_getline, eap->cookie, getexline) && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { eap->cmd = "+"; if (!skip_only) { @@ -2697,7 +2729,7 @@ static void apply_cmdmod(cmdmod_T *cmod) // Set 'eventignore' to "all". // First save the existing option value for restoring it later. cmod->cmod_save_ei = xstrdup(p_ei); - set_string_option_direct(kOptEventignore, "all", 0, SID_NONE); + set_option_direct(kOptEventignore, STATIC_CSTR_AS_OPTVAL("all"), 0, SID_NONE); } } @@ -2717,7 +2749,7 @@ void undo_cmdmod(cmdmod_T *cmod) if (cmod->cmod_save_ei != NULL) { // Restore 'eventignore' to the value before ":noautocmd". - set_string_option_direct(kOptEventignore, cmod->cmod_save_ei, 0, SID_NONE); + set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(cmod->cmod_save_ei), 0, SID_NONE); free_string_option(cmod->cmod_save_ei); cmod->cmod_save_ei = NULL; } @@ -2875,9 +2907,9 @@ int parse_cmd_address(exarg_T *eap, const char **errormsg, bool silent) // (where zero usually means to use the first line). // Check the cursor position before returning. if (eap->line2 > 0) { - check_cursor(); + check_cursor(curwin); } else { - check_cursor_col(); + check_cursor_col(curwin); } need_check_cursor = true; } @@ -2899,7 +2931,7 @@ int parse_cmd_address(exarg_T *eap, const char **errormsg, bool silent) theend: if (need_check_cursor) { - check_cursor(); + check_cursor(curwin); } return ret; } @@ -3474,7 +3506,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, bool } searchcmdlen = 0; flags = silent ? 0 : SEARCH_HIS | SEARCH_MSG; - if (!do_search(NULL, c, c, cmd, 1, flags, NULL)) { + if (!do_search(NULL, c, c, cmd, strlen(cmd), 1, flags, NULL)) { curwin->w_cursor = pos; cmd = NULL; goto error; @@ -3511,7 +3543,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, bool pos.coladd = 0; if (searchit(curwin, curbuf, &pos, NULL, *cmd == '?' ? BACKWARD : FORWARD, - "", 1, SEARCH_MSG, i, NULL) != FAIL) { + "", 0, 1, SEARCH_MSG, i, NULL) != FAIL) { lnum = pos.lnum; } else { cmd = NULL; @@ -3596,7 +3628,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, bool // closed fold after the first address. if (addr_type == ADDR_LINES && (i == '-' || i == '+') && address_count >= 2) { - hasFolding(lnum, NULL, &lnum); + hasFolding(curwin, lnum, NULL, &lnum); } if (i == '-') { lnum -= n; @@ -4050,7 +4082,7 @@ void separate_nextcmd(exarg_T *eap) break; } } else if ( - // Check for '"': start of comment or '|': next command */ + // Check for '"': start of comment or '|': next command // :@" does not start a comment! // :redir @" doesn't either. (*p == '"' @@ -4368,12 +4400,15 @@ static int get_tabpage_arg(exarg_T *eap) tab_number = 0; } else { tab_number = (int)eap->line2; - char *cmdp = eap->cmd; - while (--cmdp > *eap->cmdlinep && (*cmdp == ' ' || ascii_isdigit(*cmdp))) {} - if (!unaccept_arg0 && *cmdp == '-') { - tab_number--; - if (tab_number < unaccept_arg0) { - eap->errmsg = _(e_invrange); + if (!unaccept_arg0) { + char *cmdp = eap->cmd; + while (--cmdp > *eap->cmdlinep + && (ascii_iswhite(*cmdp) || ascii_isdigit(*cmdp))) {} + if (*cmdp == '-') { + tab_number--; + if (tab_number < unaccept_arg0) { + eap->errmsg = _(e_invrange); + } } } } @@ -5334,6 +5369,10 @@ static void ex_resize(exarg_T *eap) /// ":find [+command] <file>" command. static void ex_find(exarg_T *eap) { + if (!check_can_set_curbuf_forceit(eap->forceit)) { + return; + } + char *file_to_find = NULL; char *search_ctx = NULL; char *fname = find_file_in_path(eap->arg, strlen(eap->arg), @@ -5364,6 +5403,16 @@ static void ex_find(exarg_T *eap) /// ":edit", ":badd", ":balt", ":visual". static void ex_edit(exarg_T *eap) { + char *ffname = eap->cmdidx == CMD_enew ? NULL : eap->arg; + + // Exclude commands which keep the window's current buffer + if (eap->cmdidx != CMD_badd + && eap->cmdidx != CMD_balt + // All other commands must obey 'winfixbuf' / ! rules + && (is_other_file(0, ffname) && !check_can_set_curbuf_forceit(eap->forceit))) { + return; + } + do_exedit(eap, NULL); } @@ -5516,8 +5565,6 @@ static void ex_swapname(exarg_T *eap) /// (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>) static void ex_syncbind(exarg_T *eap) { - win_T *save_curwin = curwin; - buf_T *save_curbuf = curbuf; linenr_T topline; int y; linenr_T old_linenr = curwin->w_cursor.lnum; @@ -5544,23 +5591,19 @@ static void ex_syncbind(exarg_T *eap) // Set all scrollbind windows to the same topline. FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - curwin = wp; - if (curwin->w_p_scb) { - curbuf = curwin->w_buffer; - y = topline - curwin->w_topline; + if (wp->w_p_scb) { + y = topline - wp->w_topline; if (y > 0) { - scrollup(y, true); + scrollup(wp, y, true); } else { - scrolldown(-y, true); + scrolldown(wp, -y, true); } - curwin->w_scbind_pos = topline; - redraw_later(curwin, UPD_VALID); - cursor_correct(); - curwin->w_redr_status = true; + wp->w_scbind_pos = topline; + redraw_later(wp, UPD_VALID); + cursor_correct(wp); + wp->w_redr_status = true; } } - curwin = save_curwin; - curbuf = save_curbuf; if (curwin->w_p_scb) { did_syncbind = true; checkpcmark(); @@ -5842,8 +5885,8 @@ static void ex_equal(exarg_T *eap) static void ex_sleep(exarg_T *eap) { - if (cursor_valid()) { - setcursor_mayforce(true); + if (cursor_valid(curwin)) { + setcursor_mayforce(curwin, true); } int64_t len = eap->line2; @@ -5978,7 +6021,7 @@ static void ex_put(exarg_T *eap) eap->forceit = true; } curwin->w_cursor.lnum = eap->line2; - check_cursor_col(); + check_cursor_col(curwin); do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1, PUT_LINE|PUT_CURSLINE); } @@ -6072,7 +6115,7 @@ static void ex_at(exarg_T *eap) int prev_len = typebuf.tb_len; curwin->w_cursor.lnum = eap->line2; - check_cursor_col(); + check_cursor_col(curwin); // Get the register name. No name means use the previous one. int c = (uint8_t)(*eap->arg); @@ -6294,7 +6337,7 @@ static void ex_redraw(exarg_T *eap) RedrawingDisabled = 0; p_lz = false; - validate_cursor(); + validate_cursor(curwin); update_topline(curwin); if (eap->forceit) { redraw_all_later(UPD_NOT_VALID); @@ -6447,10 +6490,10 @@ static void ex_mark(exarg_T *eap) /// Update w_topline, w_leftcol and the cursor position. void update_topline_cursor(void) { - check_cursor(); // put cursor on valid line + check_cursor(curwin); // put cursor on valid line update_topline(curwin); if (!curwin->w_p_wrap) { - validate_cursor(); + validate_cursor(curwin); } update_curswant(); } @@ -6670,7 +6713,7 @@ static void ex_checkpath(exarg_T *eap) { find_pattern_in_path(NULL, 0, 0, false, false, CHECK_PATH, 1, eap->forceit ? ACTION_SHOW_ALL : ACTION_SHOW, - 1, (linenr_T)MAXLNUM); + 1, (linenr_T)MAXLNUM, eap->forceit); } /// ":psearch" @@ -6729,7 +6772,7 @@ static void ex_findpat(exarg_T *eap) if (!eap->skip) { find_pattern_in_path(eap->arg, 0, strlen(eap->arg), whole, !eap->forceit, *eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY, - n, action, eap->line1, eap->line2); + n, action, eap->line1, eap->line2, eap->forceit); } } @@ -6754,7 +6797,7 @@ static void ex_pedit(exarg_T *eap) if (curwin != curwin_save && win_valid(curwin_save)) { // Return cursor to where we were - validate_cursor(); + validate_cursor(curwin); redraw_later(curwin, UPD_VALID); win_enter(curwin_save, true); } @@ -7339,7 +7382,7 @@ void filetype_maybe_enable(void) /// ":setfiletype [FALLBACK] {name}" static void ex_setfiletype(exarg_T *eap) { - if (did_filetype) { + if (curbuf->b_did_filetype) { return; } @@ -7350,7 +7393,7 @@ static void ex_setfiletype(exarg_T *eap) set_option_value_give_err(kOptFiletype, CSTR_AS_OPTVAL(arg), OPT_LOCAL); if (arg != eap->arg) { - did_filetype = false; + curbuf->b_did_filetype = false; } } @@ -7396,7 +7439,7 @@ static void ex_folddo(exarg_T *eap) { // First set the marks for all lines closed/open. for (linenr_T lnum = eap->line1; lnum <= eap->line2; lnum++) { - if (hasFolding(lnum, NULL, NULL) == (eap->cmdidx == CMD_folddoclosed)) { + if (hasFolding(curwin, lnum, NULL, NULL) == (eap->cmdidx == CMD_folddoclosed)) { ml_setmarked(lnum); } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 44a78711d2..f18dc0f747 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -470,7 +470,7 @@ static void may_do_incsearch_highlighting(int firstc, int count, incsearch_state .sa_tm = &tm, }; found = do_search(NULL, firstc == ':' ? '/' : firstc, search_delim, - ccline.cmdbuff + skiplen, count, + ccline.cmdbuff + skiplen, (size_t)patlen, count, search_flags, &sia); ccline.cmdbuff[skiplen + patlen] = next_char; emsg_off--; @@ -510,7 +510,7 @@ static void may_do_incsearch_highlighting(int firstc, int count, incsearch_state s->match_start = curwin->w_cursor; set_search_match(&curwin->w_cursor); - validate_cursor(); + validate_cursor(curwin); end_pos = curwin->w_cursor; s->match_end = end_pos; curwin->w_cursor = save_pos; @@ -530,7 +530,7 @@ static void may_do_incsearch_highlighting(int firstc, int count, incsearch_state ccline.cmdbuff[skiplen + patlen] = next_char; } - validate_cursor(); + validate_cursor(curwin); // May redraw the status line to show the cursor position. if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) { @@ -626,7 +626,7 @@ static void finish_incsearch_highlighting(bool gotesc, incsearch_state_T *s, magic_overruled = s->magic_overruled_save; - validate_cursor(); // needed for TAB + validate_cursor(curwin); // needed for TAB status_redraw_all(); redraw_all_later(UPD_SOME_VALID); if (call_update_screen) { @@ -767,7 +767,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear } setmouse(); - ui_cursor_shape(); // may show different cursor shape + setcursor(); TryState tstate; Error err = ERROR_INIT; @@ -884,11 +884,12 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear && ccline.cmdlen && s->firstc != NUL && (s->some_key_typed || s->histype == HIST_SEARCH)) { - add_to_history(s->histype, ccline.cmdbuff, true, + size_t cmdbufflen = strlen(ccline.cmdbuff); + add_to_history(s->histype, ccline.cmdbuff, cmdbufflen, true, s->histype == HIST_SEARCH ? s->firstc : NUL); if (s->firstc == ':') { xfree(new_last_cmdline); - new_last_cmdline = xstrdup(ccline.cmdbuff); + new_last_cmdline = xstrnsave(ccline.cmdbuff, cmdbufflen); } } @@ -919,7 +920,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear need_wait_return = false; } - set_string_option_direct(kOptInccommand, s->save_p_icm, 0, SID_NONE); + set_option_direct(kOptInccommand, CSTR_AS_OPTVAL(s->save_p_icm), 0, SID_NONE); State = s->save_State; if (cmdpreview != save_cmdpreview) { cmdpreview = save_cmdpreview; // restore preview state @@ -927,7 +928,6 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear } may_trigger_modechanged(); setmouse(); - ui_cursor_shape(); // may show different cursor shape sb_text_end_cmdline(); theend: @@ -1168,6 +1168,7 @@ static int command_line_execute(VimState *state, int key) return -1; // get another key } + disptick_T display_tick_saved = display_tick; CommandLineState *s = (CommandLineState *)state; s->c = key; @@ -1179,6 +1180,10 @@ static int command_line_execute(VimState *state, int key) } else { map_execute_lua(false); } + // Re-apply 'incsearch' highlighting in case it was cleared. + if (display_tick > display_tick_saved && s->is_state.did_incsearch) { + may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); + } // nvim_select_popupmenu_item() can be called from the handling of // K_EVENT, K_COMMAND, or K_LUA. @@ -1447,7 +1452,7 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s pat[patlen] = NUL; int found = searchit(curwin, curbuf, &t, NULL, next_match ? FORWARD : BACKWARD, - pat, count, search_flags, + pat, (size_t)patlen, count, search_flags, RE_SEARCH, NULL); emsg_off--; pat[patlen] = save; @@ -1483,7 +1488,7 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s curwin->w_cursor = s->match_start; changed_cline_bef_curs(curwin); update_topline(curwin); - validate_cursor(); + validate_cursor(curwin); highlight_match = true; save_viewstate(curwin, &s->old_viewstate); redraw_later(curwin, UPD_NOT_VALID); @@ -1964,7 +1969,7 @@ static int command_line_handle_key(CommandLineState *s) return command_line_not_changed(s); // Ignore mouse case K_MIDDLEMOUSE: - cmdline_paste(eval_has_provider("clipboard") ? '*' : 0, true, true); + cmdline_paste(eval_has_provider("clipboard", false) ? '*' : 0, true, true); redrawcmd(); return command_line_changed(s); @@ -2550,7 +2555,7 @@ static bool cmdpreview_may_show(CommandLineState *s) // Open preview buffer if inccommand=split. if (icm_split && (cmdpreview_buf = cmdpreview_open_buf()) == NULL) { // Failed to create preview buffer, so disable preview. - set_string_option_direct(kOptInccommand, "nosplit", 0, SID_NONE); + set_option_direct(kOptInccommand, STATIC_CSTR_AS_OPTVAL("nosplit"), 0, SID_NONE); icm_split = false; } // Setup preview namespace if it's not already set. @@ -3445,9 +3450,11 @@ void cmdline_screen_cleared(void) /// called by ui_flush, do what redraws necessary to keep cmdline updated. void cmdline_ui_flush(void) { - if (!ui_has(kUICmdline)) { + static bool flushing = false; + if (!ui_has(kUICmdline) || flushing) { return; } + flushing = true; int level = ccline.level; CmdlineInfo *line = &ccline; while (level > 0 && line) { @@ -3462,6 +3469,7 @@ void cmdline_ui_flush(void) } line = line->prev_ccline; } + flushing = false; } // Put a character on the command line. Shifts the following text to the @@ -3859,7 +3867,6 @@ void cursorcmd(void) if (ccline.redraw_state < kCmdRedrawPos) { ccline.redraw_state = kCmdRedrawPos; } - setcursor(); return; } @@ -4553,6 +4560,7 @@ static int open_cmdwin(void) State = save_State; may_trigger_modechanged(); setmouse(); + setcursor(); return cmdwin_result; } @@ -4583,7 +4591,7 @@ char *script_get(exarg_T *const eap, size_t *const lenp) { char *cmd = eap->arg; - if (cmd[0] != '<' || cmd[1] != '<' || eap->getline == NULL) { + if (cmd[0] != '<' || cmd[1] != '<' || eap->ea_getline == NULL) { *lenp = strlen(eap->arg); return eap->skip ? NULL : xmemdupz(eap->arg, *lenp); } @@ -4623,6 +4631,6 @@ static void set_search_match(pos_T *t) t->col = search_match_endcol; if (t->lnum > curbuf->b_ml.ml_line_count) { t->lnum = curbuf->b_ml.ml_line_count; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } } diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index fb37bc86f1..0e5d2fe4f5 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -12,6 +12,7 @@ #include "nvim/arglist.h" #include "nvim/arglist_defs.h" #include "nvim/ascii_defs.h" +#include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/eval.h" @@ -1092,6 +1093,8 @@ void ex_mkrc(exarg_T *eap) } xfree(viewFile); + + apply_autocmds(EVENT_SESSIONWRITEPOST, NULL, NULL, false, curbuf); } /// @return the name of the view file for the current buffer. @@ -1135,7 +1138,7 @@ static char *get_view_file(char c) } *s++ = '='; *s++ = c; - xstrlcpy(s, ".vim", 5); + xmemcpyz(s, S_LEN(".vim")); xfree(sname); return retval; diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index c4a34f8019..3236590010 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -206,7 +206,9 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r } } - bool marks_cleared = false; + bool marks_cleared_any = false; + bool marks_cleared_all = l_row == 0 && l_col == 0; + MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, l_row, l_col, itr); while (true) { @@ -214,16 +216,29 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r if (mark.pos.row < 0 || mark.pos.row > u_row || (mark.pos.row == u_row && mark.pos.col > u_col)) { + if (mark.pos.row >= 0) { + marks_cleared_all = false; + } break; } if (mark.ns == ns_id || all_ns) { - marks_cleared = true; + marks_cleared_any = true; extmark_del(buf, itr, mark, true); } else { marktree_itr_next(buf->b_marktree, itr); } } - return marks_cleared; + + if (marks_cleared_all) { + if (all_ns) { + map_destroy(uint32_t, buf->b_extmark_ns); + *buf->b_extmark_ns = (Map(uint32_t, uint32_t)) MAP_INIT; + } else { + map_del(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, NULL); + } + } + + return marks_cleared_any; } /// @return the position of marks between a range, @@ -315,10 +330,6 @@ MTPair extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id) /// free extmarks from the buffer void extmark_free_all(buf_T *buf) { - if (!map_size(buf->b_extmark_ns)) { - return; - } - MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, 0, 0, itr); while (true) { diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index ed4848b402..a8b0dbddee 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -291,7 +291,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i if (!vim_isAbsName(rel_fname) && len + 1 < MAXPATHL) { // Make the start dir an absolute path name. - xstrlcpy(ff_expand_buffer, rel_fname, len + 1); + xmemcpyz(ff_expand_buffer, rel_fname, len); search_ctx->ffsc_start_dir = FullName_save(ff_expand_buffer, false); } else { search_ctx->ffsc_start_dir = xmemdupz(rel_fname, len); diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 3b715e2c0b..df9c4928c9 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -234,7 +234,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip, int using_b_fname; static char *msg_is_a_directory = N_("is a directory"); - au_did_filetype = false; // reset before triggering any autocommands + curbuf->b_au_did_filetype = false; // reset before triggering any autocommands curbuf->b_no_eol_lnum = 0; // in case it was set by the previous read @@ -957,7 +957,7 @@ retry: int tlen = 0; while (true) { p = (uint8_t *)ml_get(read_buf_lnum) + read_buf_col; - int n = (int)strlen((char *)p); + int n = ml_get_len(read_buf_lnum) - read_buf_col; if (tlen + n + 1 > size) { // Filled up to "size", append partial line. // Change NL to NUL to reverse the effect done @@ -1620,7 +1620,7 @@ failed: save_file_ff(curbuf); // If editing a new file: set 'fenc' for the current buffer. // Also for ":read ++edit file". - set_string_option_direct(kOptFileencoding, fenc, OPT_LOCAL, 0); + set_option_direct(kOptFileencoding, CSTR_AS_OPTVAL(fenc), OPT_LOCAL, 0); } if (fenc_alloced) { xfree(fenc); @@ -1854,7 +1854,7 @@ failed: } else if (newfile || (read_buffer && sfname != NULL)) { apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname, false, curbuf, eap); - if (!au_did_filetype && *curbuf->b_p_ft != NUL) { + if (!curbuf->b_au_did_filetype && *curbuf->b_p_ft != NUL) { // EVENT_FILETYPE was not triggered but the buffer already has a // filetype. Trigger EVENT_FILETYPE using the existing filetype. apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname, true, curbuf); @@ -1968,7 +1968,7 @@ void set_forced_fenc(exarg_T *eap) } char *fenc = enc_canonize(eap->cmd + eap->force_enc); - set_string_option_direct(kOptFileencoding, fenc, OPT_LOCAL, 0); + set_option_direct(kOptFileencoding, CSTR_AS_OPTVAL(fenc), OPT_LOCAL, 0); xfree(fenc); } @@ -3151,7 +3151,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options) if (saved == OK) { curbuf->b_flags |= BF_CHECK_RO; // check for RO again - keep_filetype = true; // don't detect 'filetype' + curbuf->b_keep_filetype = true; // don't detect 'filetype' if (readfile(buf->b_ffname, buf->b_fname, 0, 0, (linenr_T)MAXLNUM, &ea, flags, shortmess(SHM_FILEINFO)) != OK) { if (!aborting()) { @@ -3197,9 +3197,9 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options) curwin->w_topline = old_topline; } curwin->w_cursor = old_cursor; - check_cursor(); + check_cursor(curwin); update_topline(curwin); - keep_filetype = false; + curbuf->b_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 c571aaf0a4..59a4dc6aad 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -143,7 +143,7 @@ void copyFoldingState(win_T *wp_from, win_T *wp_to) } // hasAnyFolding() {{{2 -/// @return true if there may be folded lines in the current window. +/// @return true if there may be folded lines in window "win". int hasAnyFolding(win_T *win) { // very simple now, but can become more complex later @@ -155,10 +155,10 @@ int hasAnyFolding(win_T *win) /// When returning true, *firstp and *lastp are set to the first and last /// lnum of the sequence of folded lines (skipped when NULL). /// -/// @return true if line "lnum" in the current window is part of a closed fold. -bool hasFolding(linenr_T lnum, linenr_T *firstp, linenr_T *lastp) +/// @return true if line "lnum" in window "win" is part of a closed fold. +bool hasFolding(win_T *win, linenr_T lnum, linenr_T *firstp, linenr_T *lastp) { - return hasFoldingWin(curwin, lnum, firstp, lastp, true, NULL); + return hasFoldingWin(win, lnum, firstp, lastp, true, NULL); } // hasFoldingWin() {{{2 @@ -398,13 +398,13 @@ void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, bool h // Opening one level only: next fold to open is after the one going to // be opened. if (opening && !recurse) { - hasFolding(lnum, NULL, &lnum_next); + hasFolding(curwin, lnum, NULL, &lnum_next); } setManualFold(temp, opening, recurse, &done); // Closing one level only: next line to close a fold is after just // closed fold. if (!opening && !recurse) { - hasFolding(lnum, NULL, &lnum_next); + hasFolding(curwin, lnum, NULL, &lnum_next); } } if (done == DONE_NOTHING) { @@ -477,7 +477,7 @@ static void newFoldLevelWin(win_T *wp) } wp->w_fold_manual = false; } - changed_window_setting_win(wp); + changed_window_setting(wp); } // foldCheckClose() {{{2 @@ -492,7 +492,7 @@ void foldCheckClose(void) checkupdate(curwin); if (checkCloseRec(&curwin->w_folds, curwin->w_cursor.lnum, (int)curwin->w_p_fdl)) { - changed_window_setting(); + changed_window_setting(curwin); } } @@ -518,7 +518,7 @@ static bool checkCloseRec(garray_T *gap, linenr_T lnum, int level) return retval; } -// foldCreateAllowed() {{{2 +// foldManualAllowed() {{{2 /// @return true if it's allowed to manually create or delete a fold or, /// give an error message and return false if not. int foldManualAllowed(bool create) @@ -661,7 +661,7 @@ void foldCreate(win_T *wp, pos_T start, pos_T end) fp->fd_small = kNone; // redraw - changed_window_setting_win(wp); + changed_window_setting(wp); } } @@ -735,7 +735,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const did_one = true; // redraw window - changed_window_setting_win(wp); + changed_window_setting(wp); } } if (!did_one) { @@ -746,7 +746,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const } } else { // Deleting markers may make cursor column invalid - check_cursor_col_win(wp); + check_cursor_col(wp); } if (last_lnum > 0) { @@ -1009,16 +1009,15 @@ void foldAdjustVisual(void) start = &curwin->w_cursor; end = &VIsual; } - if (hasFolding(start->lnum, &start->lnum, NULL)) { + if (hasFolding(curwin, start->lnum, &start->lnum, NULL)) { start->col = 0; } - if (!hasFolding(end->lnum, NULL, &end->lnum)) { + if (!hasFolding(curwin, end->lnum, NULL, &end->lnum)) { return; } - char *ptr = ml_get(end->lnum); - end->col = (colnr_T)strlen(ptr); + end->col = ml_get_len(end->lnum); if (end->col > 0 && *p_sel == 'o') { end->col--; } @@ -1026,11 +1025,11 @@ void foldAdjustVisual(void) mb_adjust_cursor(); } -// cursor_foldstart() {{{2 +// foldAdjustCursor() {{{2 /// Move the cursor to the first line of a closed fold. -void foldAdjustCursor(void) +void foldAdjustCursor(win_T *wp) { - hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); + hasFolding(wp, wp->w_cursor.lnum, &wp->w_cursor.lnum, NULL); } // Internal functions for "fold_T" {{{1 @@ -1269,7 +1268,7 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, bool opening, bool re } wp->w_fold_manual = true; if (done & DONE_ACTION) { - changed_window_setting_win(wp); + changed_window_setting(wp); } done |= DONE_FOLD; } else if (donep == NULL && wp == curwin) { @@ -1605,7 +1604,7 @@ static void foldAddMarker(buf_T *buf, pos_T pos, const char *marker, size_t mark // Allocate a new line: old-line + 'cms'-start + marker + 'cms'-end char *line = ml_get_buf(buf, lnum); - size_t line_len = strlen(line); + size_t line_len = (size_t)ml_get_buf_len(buf, lnum); size_t added = 0; if (u_save(lnum - 1, lnum + 1) != OK) { @@ -1618,7 +1617,7 @@ static void foldAddMarker(buf_T *buf, pos_T pos, const char *marker, size_t mark STRCPY(newline, line); // Append the marker to the end of the line if (p == NULL || line_is_comment) { - xstrlcpy(newline + line_len, marker, markerlen + 1); + xmemcpyz(newline + line_len, marker, markerlen); added = markerlen; } else { STRCPY(newline + line_len, cms); @@ -1686,7 +1685,7 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char *marker, size_t marker } if (u_save(lnum - 1, lnum + 1) == OK) { // Make new line: text-before-marker + text-after-marker - char *newline = xmalloc(strlen(line) - len + 1); + char *newline = xmalloc((size_t)ml_get_buf_len(buf, lnum) - len + 1); assert(p >= line); memcpy(newline, line, (size_t)(p - line)); STRCPY(newline + (p - line), p + len); @@ -2117,7 +2116,7 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) // If some fold changed, need to redraw and position cursor. if (fold_changed && wp->w_p_fen) { - changed_window_setting_win(wp); + changed_window_setting(wp); } // If we updated folds past "bot", need to redraw more lines. Don't do diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 04b4363e42..62c99ce082 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -259,8 +259,12 @@ put('version') fixdict(1 + #version) for _, item in ipairs(version) do -- NB: all items are mandatory. But any error will be less confusing - -- with placholder vim.NIL (than invalid mpack data) - put(item[1], item[2] or vim.NIL) + -- with placeholder vim.NIL (than invalid mpack data) + local val = item[2] + if val == nil then + val = vim.NIL + end + put(item[1], val) end put('build', version_build) @@ -716,23 +720,6 @@ end -- start building lua output output = assert(io.open(lua_c_bindings_outputf, 'wb')) -output:write([[ -#include <lua.h> -#include <lualib.h> -#include <lauxlib.h> - -#include "nvim/ex_docmd.h" -#include "nvim/ex_getln.h" -#include "nvim/func_attr.h" -#include "nvim/globals.h" -#include "nvim/api/private/defs.h" -#include "nvim/api/private/helpers.h" -#include "nvim/api/private/dispatch.h" -#include "nvim/lua/converter.h" -#include "nvim/lua/executor.h" -#include "nvim/memory.h" - -]]) include_headers(output, headers) output:write('\n') @@ -913,7 +900,7 @@ exit_0: write_shifted_output(string.format( [[ if (lua_gettop(lstate) == 0) { - nlua_push_%s(lstate, %sret, true); + nlua_push_%s(lstate, %sret, kNluaPushSpecial | kNluaPushFreeRefs); } ]], return_type, @@ -927,10 +914,10 @@ exit_0: else local special = (fn.since ~= nil and fn.since < 11) write_shifted_output( - ' nlua_push_%s(lstate, %sret, %s);\n', + ' nlua_push_%s(lstate, %sret, %s | kNluaPushFreeRefs);\n', return_type, ret_mode, - tostring(special) + special and 'kNluaPushSpecial' or '0' ) end @@ -975,8 +962,6 @@ end output:write(string.format( [[ void nlua_add_api_functions(lua_State *lstate) - REAL_FATTR_NONNULL_ALL; -void nlua_add_api_functions(lua_State *lstate) { lua_createtable(lstate, 0, %u); ]], diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index 0808f71daa..516b5ad5ae 100644 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -31,6 +31,10 @@ local function write_signature(output, ev, prefix, notype) end local function write_arglist(output, ev) + if #ev.parameters == 0 then + return + end + output:write(' MAXSIZE_TEMP_ARRAY(args, ' .. #ev.parameters .. ');\n') for j = 1, #ev.parameters do local param = ev.parameters[j] local kind = string.upper(param[1]) @@ -107,14 +111,14 @@ for i = 1, #events do end ev.since = tonumber(ev.since) + local args = #ev.parameters > 0 and 'args' or 'noargs' if not ev.remote_only then if not ev.remote_impl and not ev.noexport then remote_output:write('void remote_ui_' .. ev.name) write_signature(remote_output, ev, 'RemoteUI *ui') remote_output:write('\n{\n') - remote_output:write(' Array args = ui->call_buf;\n') write_arglist(remote_output, ev) - remote_output:write(' push_call(ui, "' .. ev.name .. '", args);\n') + remote_output:write(' push_call(ui, "' .. ev.name .. '", ' .. args .. ');\n') remote_output:write('}\n\n') end end @@ -124,9 +128,8 @@ for i = 1, #events do write_signature(call_output, ev, '') call_output:write('\n{\n') if ev.remote_only then - call_output:write(' Array args = call_buf;\n') write_arglist(call_output, ev) - call_output:write(' ui_call_event("' .. ev.name .. '", args);\n') + call_output:write(' ui_call_event("' .. ev.name .. '", ' .. args .. ');\n') elseif ev.compositor_impl then call_output:write(' ui_comp_' .. ev.name) write_signature(call_output, ev, '', true) diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index 749844e658..0718d965c6 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -164,14 +164,24 @@ local function dump_option(i, o) if o.enable_if then w(get_cond(o.enable_if)) end + + -- An option cannot be both hidden and immutable. + assert(not o.hidden or not o.immutable) + + local has_var = true if o.varname then w(' .var=&' .. o.varname) - -- Immutable options can directly point to the default value. - elseif o.immutable then + elseif o.hidden or o.immutable then + -- Hidden and immutable options can directly point to the default value. w((' .var=&options[%u].def_val'):format(i - 1)) elseif #o.scope == 1 and o.scope[1] == 'window' then w(' .var=VAR_WIN') + else + has_var = false end + -- `enable_if = false` should be present iff there is no variable. + assert((o.enable_if == false) == not has_var) + w(' .hidden=' .. (o.hidden and 'true' or 'false')) w(' .immutable=' .. (o.immutable and 'true' or 'false')) if #o.scope == 1 and o.scope[1] == 'global' then w(' .indir=PV_NONE') diff --git a/src/nvim/generators/nvim_version.lua.in b/src/nvim/generators/nvim_version.lua.in index d0dbf77922..c29141fc68 100644 --- a/src/nvim/generators/nvim_version.lua.in +++ b/src/nvim/generators/nvim_version.lua.in @@ -2,7 +2,7 @@ return { {"major", ${NVIM_VERSION_MAJOR}}, {"minor", ${NVIM_VERSION_MINOR}}, {"patch", ${NVIM_VERSION_PATCH}}, - {"prerelease", "$NVIM_VERSION_PRERELEASE" ~= ""}, + {"prerelease", "${NVIM_VERSION_PRERELEASE}" ~= ""}, {"api_level", ${NVIM_API_LEVEL}}, {"api_compatible", ${NVIM_API_LEVEL_COMPAT}}, {"api_prerelease", ${NVIM_API_PRERELEASE}}, diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 64c9c5a8c3..9b19644b7e 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -64,6 +64,15 @@ #include "nvim/undo.h" #include "nvim/vim_defs.h" +/// State for adding bytes to a recording or 'showcmd'. +typedef struct { + uint8_t buf[MB_MAXBYTES * 3 + 4]; + int prev_c; + size_t buflen; + unsigned pending_special; + unsigned pending_mbyte; +} gotchars_state_T; + /// Index in scriptin static int curscript = -1; /// Streams to read script from @@ -94,6 +103,12 @@ static buffheader_T readbuf1 = { { NULL, { NUL } }, NULL, 0, 0 }; /// Second read ahead buffer. Used for redo. static buffheader_T readbuf2 = { { NULL, { NUL } }, NULL, 0, 0 }; +/// Buffer used to store typed characters for vim.on_key(). +static kvec_withinit_t(char, MAXMAPLEN) on_key_buf = KVI_INITIAL_VALUE(on_key_buf); + +/// Number of following bytes that should not be stored for vim.on_key(). +static size_t no_on_key_len = 0; + static int typeahead_char = 0; ///< typeahead char that's not flushed /// When block_redo is true the redo buffer will not be changed. @@ -255,7 +270,7 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle size_t len; if (buf->bh_space >= (size_t)slen) { len = strlen(buf->bh_curr->b_str); - xstrlcpy(buf->bh_curr->b_str + len, s, (size_t)slen + 1); + xmemcpyz(buf->bh_curr->b_str + len, s, (size_t)slen); buf->bh_space -= (size_t)slen; } else { if (slen < MINIMAL_SIZE) { @@ -265,7 +280,7 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle } buffblock_T *p = xmalloc(offsetof(buffblock_T, b_str) + len + 1); buf->bh_space = len - (size_t)slen; - xstrlcpy(p->b_str, s, (size_t)slen + 1); + xmemcpyz(p->b_str, s, (size_t)slen); p->b_next = buf->bh_curr->b_next; buf->bh_curr->b_next = p; @@ -994,14 +1009,19 @@ int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent) /// Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to /// the char. /// +/// @param no_on_key don't store these bytes for vim.on_key() +/// /// @return the length of what was inserted -int ins_char_typebuf(int c, int modifiers) +int ins_char_typebuf(int c, int modifiers, bool no_on_key) { char buf[MB_MAXBYTES * 3 + 4]; unsigned len = special_to_buf(c, modifiers, true, buf); assert(len < sizeof(buf)); buf[len] = NUL; ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); + if (KeyTyped && no_on_key) { + no_on_key_len += len; + } return (int)len; } @@ -1101,40 +1121,90 @@ void del_typebuf(int len, int offset) } } +/// Add a single byte to a recording or 'showcmd'. +/// Return true if a full key has been received, false otherwise. +static bool gotchars_add_byte(gotchars_state_T *state, uint8_t byte) + FUNC_ATTR_NONNULL_ALL +{ + int c = state->buf[state->buflen++] = byte; + bool retval = false; + const bool in_special = state->pending_special > 0; + const bool in_mbyte = state->pending_mbyte > 0; + + if (in_special) { + state->pending_special--; + } else if (c == K_SPECIAL) { + // When receiving a special key sequence, store it until we have all + // the bytes and we can decide what to do with it. + state->pending_special = 2; + } + + if (state->pending_special > 0) { + goto ret_false; + } + + if (in_mbyte) { + state->pending_mbyte--; + } else { + if (in_special) { + if (state->prev_c == KS_MODIFIER) { + // When receiving a modifier, wait for the modified key. + goto ret_false; + } + c = TO_SPECIAL(state->prev_c, c); + } + // When receiving a multibyte character, store it until we have all + // the bytes, so that it won't be split between two buffer blocks, + // and delete_buff_tail() will work properly. + state->pending_mbyte = MB_BYTE2LEN_CHECK(c) - 1; + } + + if (state->pending_mbyte > 0) { + goto ret_false; + } + + retval = true; +ret_false: + state->prev_c = c; + return retval; +} + /// Write typed characters to script file. -/// If recording is on put the character in the recordbuffer. +/// If recording is on put the character in the record buffer. static void gotchars(const uint8_t *chars, size_t len) FUNC_ATTR_NONNULL_ALL { const uint8_t *s = chars; - static uint8_t buf[4] = { 0 }; - static size_t buflen = 0; size_t todo = len; + static gotchars_state_T state; - while (todo--) { - buf[buflen++] = *s++; - - // When receiving a special key sequence, store it until we have all - // the bytes and we can decide what to do with it. - if (buflen == 1 && buf[0] == K_SPECIAL) { - continue; - } - if (buflen == 2) { + while (todo-- > 0) { + if (!gotchars_add_byte(&state, *s++)) { continue; } // Handle one byte at a time; no translation to be done. - for (size_t i = 0; i < buflen; i++) { - updatescript(buf[i]); + for (size_t i = 0; i < state.buflen; i++) { + updatescript(state.buf[i]); } + state.buf[state.buflen] = NUL; + if (reg_recording != 0) { - buf[buflen] = NUL; - add_buff(&recordbuff, (char *)buf, (ptrdiff_t)buflen); + add_buff(&recordbuff, (char *)state.buf, (ptrdiff_t)state.buflen); // remember how many chars were last recorded - last_recorded_len += buflen; + last_recorded_len += state.buflen; + } + + if (state.buflen > no_on_key_len) { + vim_unescape_ks((char *)state.buf + no_on_key_len); + kvi_concat(on_key_buf, (char *)state.buf + no_on_key_len); + no_on_key_len = 0; + } else { + no_on_key_len -= state.buflen; } - buflen = 0; + + state.buflen = 0; } may_sync_undo(); @@ -1151,6 +1221,7 @@ static void gotchars(const uint8_t *chars, size_t len) void gotchars_ignore(void) { uint8_t nop_buf[3] = { K_SPECIAL, KS_EXTRA, KE_IGNORE }; + no_on_key_len += 3; gotchars(nop_buf, 3); } @@ -1440,6 +1511,61 @@ int merge_modifiers(int c_arg, int *modifiers) return c; } +/// Add a single byte to 'showcmd' for a partially matched mapping. +/// Call add_to_showcmd() if a full key has been received. +static void add_byte_to_showcmd(uint8_t byte) +{ + static gotchars_state_T state; + + if (!p_sc || msg_silent != 0) { + return; + } + + if (!gotchars_add_byte(&state, byte)) { + return; + } + + state.buf[state.buflen] = NUL; + state.buflen = 0; + + int modifiers = 0; + int c = NUL; + + const uint8_t *ptr = state.buf; + if (ptr[0] == K_SPECIAL && ptr[1] == KS_MODIFIER && ptr[2] != NUL) { + modifiers = ptr[2]; + ptr += 3; + } + + if (*ptr != NUL) { + const char *mb_ptr = mb_unescape((const char **)&ptr); + c = mb_ptr != NULL ? utf_ptr2char(mb_ptr) : *ptr++; + if (c <= 0x7f) { + // Merge modifiers into the key to make the result more readable. + int modifiers_after = modifiers; + int mod_c = merge_modifiers(c, &modifiers_after); + if (modifiers_after == 0) { + modifiers = 0; + c = mod_c; + } + } + } + + // TODO(zeertzjq): is there a more readable and yet compact representation of + // modifiers and special keys? + if (modifiers != 0) { + add_to_showcmd(K_SPECIAL); + add_to_showcmd(KS_MODIFIER); + add_to_showcmd(modifiers); + } + if (c != NUL) { + add_to_showcmd(c); + } + while (*ptr != NUL) { + add_to_showcmd(*ptr++); + } +} + /// Get the next input character. /// Can return a special key or a multi-byte character. /// Can return NUL when called recursively, use safe_vgetc() if that's not @@ -1621,9 +1747,13 @@ int vgetc(void) if (!no_mapping && KeyTyped && mod_mask == MOD_MASK_ALT && !(State & MODE_TERMINAL) && !is_mouse_key(c)) { mod_mask = 0; - int len = ins_char_typebuf(c, 0); - ins_char_typebuf(ESC, 0); - ungetchars(len + 3); // K_SPECIAL KS_MODIFIER MOD_MASK_ALT takes 3 more bytes + int len = ins_char_typebuf(c, 0, false); + ins_char_typebuf(ESC, 0, false); + int old_len = len + 3; // K_SPECIAL KS_MODIFIER MOD_MASK_ALT takes 3 more bytes + ungetchars(old_len); + if (on_key_buf.size >= (size_t)old_len) { + on_key_buf.size -= (size_t)old_len; + } continue; } @@ -1639,7 +1769,9 @@ int vgetc(void) may_garbage_collect = false; // Execute Lua on_key callbacks. - nlua_execute_on_key(c); + nlua_execute_on_key(c, on_key_buf.items, on_key_buf.size); + kvi_destroy(on_key_buf); + kvi_init(on_key_buf); // Need to process the character before we know it's safe to do something // else. @@ -2508,7 +2640,7 @@ static int vgetorpeek(bool advance) unshowmode(true); mode_deleted = true; } - validate_cursor(); + validate_cursor(curwin); int old_wcol = curwin->w_wcol; int old_wrow = curwin->w_wrow; @@ -2541,7 +2673,7 @@ static int vgetorpeek(bool advance) curwin->w_wrow = curwin->w_cline_row + curwin->w_wcol / curwin->w_width_inner; curwin->w_wcol %= curwin->w_width_inner; - curwin->w_wcol += curwin_col_off(); + curwin->w_wcol += win_col_off(curwin); col = 0; // no correction needed } else { curwin->w_wcol--; @@ -2664,7 +2796,7 @@ static int vgetorpeek(bool advance) showcmd_idx = typebuf.tb_len - SHOWCMD_COLS; } while (showcmd_idx < typebuf.tb_len) { - add_to_showcmd(typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]); + add_byte_to_showcmd(typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]); } curwin->w_wcol = old_wcol; curwin->w_wrow = old_wrow; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 22f7daa823..83fef1fe75 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -341,8 +341,6 @@ EXTERN bool did_check_timestamps INIT( = false); // did check timestamps // recently EXTERN int no_check_timestamps INIT( = 0); // Don't check timestamps -EXTERN int modified_was_set; // did ":set modified" - // Mouse coordinates, set by handle_mouse_event() EXTERN int mouse_grid; EXTERN int mouse_row; @@ -363,7 +361,7 @@ EXTERN bool sys_menu INIT( = false); // currently active window. EXTERN win_T *firstwin; // first window EXTERN win_T *lastwin; // last window -EXTERN win_T *prevwin INIT( = NULL); // previous window +EXTERN win_T *prevwin INIT( = NULL); // previous window (may equal curwin) #define ONE_WINDOW (firstwin == lastwin) #define FOR_ALL_FRAMES(frp, first_frame) \ for ((frp) = first_frame; (frp) != NULL; (frp) = (frp)->fr_next) @@ -939,6 +937,7 @@ EXTERN const char e_using_float_as_string[] INIT(= N_("E806: Using a Float as a EXTERN const char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now")); EXTERN const char e_using_number_as_bool_nr[] INIT(= N_("E1023: Using a Number as a Bool: %d")); EXTERN const char e_not_callable_type_str[] INIT(= N_("E1085: Not a callable type: %s")); +EXTERN const char e_auabort[] INIT(= N_("E855: Autocommands caused command to abort")); EXTERN const char e_api_error[] INIT(= N_("E5555: API call: %s")); @@ -957,6 +956,7 @@ EXTERN const char e_highlight_group_name_invalid_char[] INIT(= N_("E5248: Invali EXTERN const char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long")); +EXTERN const char e_invalid_column_number_nr[] INIT( = N_("E964: Invalid column number: %ld")); EXTERN const char e_invalid_line_number_nr[] INIT(= N_("E966: Invalid line number: %ld")); EXTERN const char e_stray_closing_curly_str[] @@ -969,6 +969,9 @@ EXTERN const char e_val_too_large[] INIT(= N_("E1510: Value too large: %s")); EXTERN const char e_undobang_cannot_redo_or_move_branch[] INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch")); +EXTERN const char e_winfixbuf_cannot_go_to_buffer[] +INIT(= N_("E1513: Cannot switch buffer. 'winfixbuf' is enabled")); + EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s")); EXTERN const char e_unknown_option2[] INIT(= N_("E355: Unknown option: %s")); diff --git a/src/nvim/grid.c b/src/nvim/grid.c index e386853022..56246bf001 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -1,3 +1,6 @@ +// Low-level functions to manipulate individual character cells on the +// screen grid. +// // Most of the routines in this file perform screen (grid) manipulations. The // given operation is performed physically on the screen. The corresponding // change is also made to the internal screen image. In this way, the editor @@ -338,7 +341,7 @@ static int grid_line_first = INT_MAX; static int grid_line_last = 0; static int grid_line_clear_to = 0; static int grid_line_clear_attr = 0; -static bool grid_line_rl = false; +static int grid_line_flags = 0; /// Start a group of grid_line_puts calls that builds a single grid line. /// @@ -358,7 +361,7 @@ void grid_line_start(ScreenGrid *grid, int row) grid_line_last = 0; grid_line_clear_to = 0; grid_line_clear_attr = 0; - grid_line_rl = false; + grid_line_flags = 0; assert((size_t)grid_line_maxcol <= linebuf_size); @@ -433,15 +436,14 @@ int grid_line_puts(int col, const char *text, int textlen, int attr) ? utfc_ptr2schar_len(ptr, (int)((text + len) - ptr), &firstc) : utfc_ptr2schar(ptr, &firstc); int mbyte_cells = utf_char2cells(firstc); - if (mbyte_cells > 2) { + if (mbyte_cells > 2 || schar == 0) { mbyte_cells = 1; - schar = schar_from_char(0xFFFD); } if (col + mbyte_cells > max_col) { // Only 1 cell left, but character requires 2 cells: - // display a '>' in the last column to avoid wrapping. */ + // display a '>' in the last column to avoid wrapping. schar = schar_from_ascii('>'); mbyte_cells = 1; } @@ -515,7 +517,7 @@ void grid_line_mirror(void) return; } linebuf_mirror(&grid_line_first, &grid_line_last, &grid_line_clear_to, grid_line_maxcol); - grid_line_rl = true; + grid_line_flags |= SLF_RIGHTLEFT; } void linebuf_mirror(int *firstp, int *lastp, int *clearp, int maxcol) @@ -568,7 +570,7 @@ void grid_line_flush(void) } grid_put_linebuf(grid, grid_line_row, grid_line_coloff, grid_line_first, grid_line_last, - grid_line_clear_to, grid_line_rl, grid_line_clear_attr, false); + grid_line_clear_to, grid_line_clear_attr, -1, grid_line_flags); } /// flush grid line but only if on a valid row @@ -619,17 +621,21 @@ static int grid_char_needs_redraw(ScreenGrid *grid, int col, size_t off_to, int /// Move one buffered line to the window grid, but only the characters that /// have actually changed. Handle insert/delete character. -/// "coloff" gives the first column on the grid for this line. -/// "endcol" gives the columns where valid characters are. -/// "clear_width" is the width of the window. It's > 0 if the rest of the line -/// needs to be cleared, negative otherwise. -/// "rl" is true for rightleft text, like a window with 'rightleft' option set -/// When true and "clear_width" > 0, clear columns 0 to "endcol" -/// When false and "clear_width" > 0, clear columns "endcol" to "clear_width" -/// If "wrap" is true, then hint to the UI that "row" contains a line -/// which has wrapped into the next row. +/// +/// @param coloff gives the first column on the grid for this line. +/// @param endcol gives the columns where valid characters are. +/// @param clear_width see SLF_RIGHTLEFT. +/// @param flags can have bits: +/// - SLF_RIGHTLEFT rightleft text, like a window with 'rightleft' option set: +/// - When false, clear columns "endcol" to "clear_width". +/// - When true, clear columns "col" to "endcol". +/// - SLF_WRAP hint to UI that "row" contains a line wrapped into the next row. +/// - SLF_INC_VCOL: +/// - When false, use "last_vcol" for grid->vcols[] of the columns to clear. +/// - When true, use an increasing sequence starting from "last_vcol + 1" for +/// grid->vcols[] of the columns to clear. void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol, int clear_width, - bool rl, int bg_attr, bool wrap) + int bg_attr, colnr_T last_vcol, int flags) { bool redraw_next; // redraw_this for next character bool clear_next = false; @@ -659,7 +665,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol } int clear_start = endcol; - if (rl) { + if (flags & SLF_RIGHTLEFT) { clear_start = col; col = endcol; endcol = clear_width; @@ -743,10 +749,15 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol } int clear_dirty_start = -1, clear_end = -1; + if (flags & SLF_RIGHTLEFT) { + for (col = clear_width - 1; col >= clear_start; col--) { + size_t off = off_to + (size_t)col; + grid->vcols[off] = (flags & SLF_INC_VCOL) ? ++last_vcol : last_vcol; + } + } // blank out the rest of the line // TODO(bfredl): we could cache winline widths - col = clear_start; - while (col < clear_width) { + for (col = clear_start; col < clear_width; col++) { size_t off = off_to + (size_t)col; if (grid->chars[off] != schar_from_ascii(' ') || grid->attrs[off] != bg_attr @@ -758,17 +769,18 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol } clear_end = col + 1; } - grid->vcols[off] = MAXCOL; - col++; + if (!(flags & SLF_RIGHTLEFT)) { + grid->vcols[off] = (flags & SLF_INC_VCOL) ? ++last_vcol : last_vcol; + } } - if (rl && start_dirty != -1 && clear_dirty_start != -1) { + if ((flags & SLF_RIGHTLEFT) && start_dirty != -1 && clear_dirty_start != -1) { if (grid->throttled || clear_dirty_start >= start_dirty - 5) { // cannot draw now or too small to be worth a separate "clear" event start_dirty = clear_dirty_start; } else { ui_line(grid, row, invalid_row, coloff + clear_dirty_start, coloff + clear_dirty_start, - coloff + clear_end, bg_attr, wrap); + coloff + clear_end, bg_attr, flags & SLF_WRAP); } clear_end = end_dirty; } else { @@ -785,7 +797,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol if (clear_end > start_dirty) { if (!grid->throttled) { ui_line(grid, row, invalid_row, coloff + start_dirty, coloff + end_dirty, coloff + clear_end, - bg_attr, wrap); + bg_attr, flags & SLF_WRAP); } else if (grid->dirty_col) { // TODO(bfredl): really get rid of the extra pseudo terminal in message.c // by using a linebuf_char copy for "throttled message line" diff --git a/src/nvim/grid.h b/src/nvim/grid.h index 7506f0fc9d..25144cdc2c 100644 --- a/src/nvim/grid.h +++ b/src/nvim/grid.h @@ -27,8 +27,12 @@ EXTERN sattr_T *linebuf_attr INIT( = NULL); EXTERN colnr_T *linebuf_vcol INIT( = NULL); EXTERN char *linebuf_scratch INIT( = NULL); -// Low-level functions to manipulate individual character cells on the -// screen grid. +/// flags for grid_put_linebuf() +enum { + SLF_RIGHTLEFT = 1, + SLF_WRAP = 2, + SLF_INC_VCOL = 4, +}; /// Put a ASCII character in a screen cell. /// diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index 10a6161171..ac67f42fe0 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -37,7 +37,7 @@ enum { /// attrs[] contains the highlighting attribute for each cell. /// /// vcols[] contains the virtual columns in the line. -1 means not available -/// or before buffer text, MAXCOL means after the end of the line. +/// or before buffer text. /// -2 or -3 means in fold column and a mouse click should: /// -2: open a fold /// -3: close a fold diff --git a/src/nvim/help.c b/src/nvim/help.c index 779772cedf..e9f67ca3e4 100644 --- a/src/nvim/help.c +++ b/src/nvim/help.c @@ -613,7 +613,7 @@ void cleanup_help_tags(int num_file, char **file) void prepare_help_buffer(void) { curbuf->b_help = true; - set_string_option_direct(kOptBuftype, "help", OPT_LOCAL, 0); + set_option_direct(kOptBuftype, STATIC_CSTR_AS_OPTVAL("help"), OPT_LOCAL, 0); // Always set these options after jumping to a help tag, because the // user may have an autocommand that gets in the way. @@ -622,13 +622,13 @@ void prepare_help_buffer(void) // Only set it when needed, buf_init_chartab() is some work. char *p = "!-~,^*,^|,^\",192-255"; if (strcmp(curbuf->b_p_isk, p) != 0) { - set_string_option_direct(kOptIskeyword, p, OPT_LOCAL, 0); + set_option_direct(kOptIskeyword, CSTR_AS_OPTVAL(p), OPT_LOCAL, 0); check_buf_options(curbuf); buf_init_chartab(curbuf, false); } // Don't use the global foldmethod. - set_string_option_direct(kOptFoldmethod, "manual", OPT_LOCAL, 0); + set_option_direct(kOptFoldmethod, STATIC_CSTR_AS_OPTVAL("manual"), OPT_LOCAL, 0); curbuf->b_p_ts = 8; // 'tabstop' is 8. curwin->w_p_list = false; // No list mode. diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 75c23c5bc4..ccb093c116 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -201,8 +201,16 @@ static const char *highlight_init_both[] = { "default link SpecialComment Special", "default link Debug Special", "default link Ignore Normal", - "default link LspInlayHint NonText", - "default link SnippetTabstop Visual", + + // Built-in LSP + "default link LspCodeLens NonText", + "default link LspCodeLensSeparator LspCodeLens", + "default link LspInlayHint NonText", + "default link LspReferenceRead LspReferenceText", + "default link LspReferenceText Visual", + "default link LspReferenceWrite LspReferenceText", + "default link LspSignatureActiveParameter Visual", + "default link SnippetTabstop Visual", // Diagnostic "default link DiagnosticFloatingError DiagnosticError", @@ -223,7 +231,8 @@ static const char *highlight_init_both[] = { "default link DiagnosticUnnecessary Comment", // Treesitter standard groups - "default link @variable.builtin Special", + "default link @variable.builtin Special", + "default link @variable.parameter.builtin Special", "default link @constant Constant", "default link @constant.builtin Special", @@ -248,8 +257,9 @@ static const char *highlight_init_both[] = { "default link @type Type", "default link @type.builtin Special", - "default link @attribute Macro", - "default link @property Identifier", + "default link @attribute Macro", + "default link @attribute.builtin Special", + "default link @property Identifier", "default link @function Function", "default link @function.builtin Special", @@ -282,7 +292,8 @@ static const char *highlight_init_both[] = { "default link @diff.minus Removed", "default link @diff.delta Changed", - "default link @tag Tag", + "default link @tag Tag", + "default link @tag.builtin Special", // LSP semantic tokens "default link @lsp.type.class @type", @@ -290,18 +301,27 @@ static const char *highlight_init_both[] = { "default link @lsp.type.decorator @attribute", "default link @lsp.type.enum @type", "default link @lsp.type.enumMember @constant", + "default link @lsp.type.event @type", "default link @lsp.type.function @function", "default link @lsp.type.interface @type", + "default link @lsp.type.keyword @keyword", "default link @lsp.type.macro @constant.macro", "default link @lsp.type.method @function.method", + "default link @lsp.type.modifier @type.qualifier", "default link @lsp.type.namespace @module", + "default link @lsp.type.number @number", + "default link @lsp.type.operator @operator", "default link @lsp.type.parameter @variable.parameter", "default link @lsp.type.property @property", + "default link @lsp.type.regexp @string.regexp", + "default link @lsp.type.string @string", "default link @lsp.type.struct @type", "default link @lsp.type.type @type", "default link @lsp.type.typeParameter @type.definition", "default link @lsp.type.variable @variable", + "default link @lsp.mod.deprecated DiagnosticDeprecated", + NULL }; @@ -349,7 +369,7 @@ static const char *highlight_init_light[] = { "SpellLocal guisp=NvimDarkGreen gui=undercurl cterm=undercurl", "SpellRare guisp=NvimDarkCyan gui=undercurl cterm=undercurl", "StatusLine guifg=NvimLightGrey3 guibg=NvimDarkGrey3 cterm=reverse", - "StatusLineNC guifg=NvimDarkGrey3 guibg=NvimLightGrey3 cterm=bold", + "StatusLineNC guifg=NvimDarkGrey3 guibg=NvimLightGrey3 cterm=bold,underline", "Title guifg=NvimDarkGrey2 gui=bold cterm=bold", "Visual guibg=NvimLightGrey4 ctermfg=15 ctermbg=0", "WarningMsg guifg=NvimDarkYellow ctermfg=3", @@ -434,7 +454,7 @@ static const char *highlight_init_dark[] = { "SpellLocal guisp=NvimLightGreen gui=undercurl cterm=undercurl", "SpellRare guisp=NvimLightCyan gui=undercurl cterm=undercurl", "StatusLine guifg=NvimDarkGrey3 guibg=NvimLightGrey3 cterm=reverse", - "StatusLineNC guifg=NvimLightGrey3 guibg=NvimDarkGrey3 cterm=bold", + "StatusLineNC guifg=NvimLightGrey3 guibg=NvimDarkGrey3 cterm=bold,underline", "Title guifg=NvimLightGrey2 gui=bold cterm=bold", "Visual guibg=NvimDarkGrey4 ctermfg=0 ctermbg=15", "WarningMsg guifg=NvimLightYellow ctermfg=11", @@ -877,11 +897,13 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) g->sg_link = link_id; g->sg_script_ctx = current_sctx; g->sg_script_ctx.sc_lnum += SOURCING_LNUM; + nlua_set_sctx(&g->sg_script_ctx); g->sg_set |= SG_LINK; if (is_default) { g->sg_deflink = link_id; g->sg_deflink_sctx = current_sctx; g->sg_deflink_sctx.sc_lnum += SOURCING_LNUM; + nlua_set_sctx(&g->sg_deflink_sctx); } } else { g->sg_link = 0; @@ -922,6 +944,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) g->sg_script_ctx = current_sctx; g->sg_script_ctx.sc_lnum += SOURCING_LNUM; + nlua_set_sctx(&g->sg_script_ctx); g->sg_attr = hl_get_syn_attr(0, id, attrs); @@ -1149,9 +1172,8 @@ void do_highlight(const char *line, const bool forceit, const bool init) error = true; break; } - memcpy(key, key_start, key_len); - key[key_len] = NUL; - vim_strup(key); + vim_memcpy_up(key, key_start, key_len); + key[key_len] = '\0'; linep = skipwhite(linep); if (strcmp(key, "NONE") == 0) { @@ -1903,7 +1925,7 @@ static void set_hl_attr(int idx) at_en.cterm_bg_color = (int16_t)sgp->sg_cterm_bg; at_en.rgb_ae_attr = (int16_t)sgp->sg_gui; // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is - // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name + // initialized with 0 (by garray functions), check for sg_rgb_{f,b}g_name // before setting attr_entry->{f,g}g_color to a other than -1 at_en.rgb_fg_color = sgp->sg_rgb_fg_idx != kColorIdxNone ? sgp->sg_rgb_fg : -1; at_en.rgb_bg_color = sgp->sg_rgb_bg_idx != kColorIdxNone ? sgp->sg_rgb_bg : -1; @@ -1941,11 +1963,10 @@ int syn_name2id_len(const char *name, size_t len) return 0; } - // Avoid using stricmp() too much, it's slow on some systems */ + // Avoid using stricmp() too much, it's slow on some systems // Avoid alloc()/free(), these are slow too. - memcpy(name_u, name, len); + vim_memcpy_up(name_u, name, len); name_u[len] = '\0'; - vim_strup(name_u); // map_get(..., int) returns 0 when no key is present, which is // the expected value for missing highlight group. @@ -2462,7 +2483,7 @@ color_name_table_T color_name_table[] = { { "Cyan4", RGB_(0x0, 0x8b, 0x8b) }, { "DarkBlue", RGB_(0x00, 0x00, 0x8b) }, { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) }, - { "DarkGoldenRod", RGB_(0xb8, 0x86, 0x0b) }, + { "DarkGoldenrod", RGB_(0xb8, 0x86, 0x0b) }, { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) }, { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) }, { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) }, @@ -2536,7 +2557,7 @@ color_name_table_T color_name_table[] = { { "Gold2", RGB_(0xee, 0xc9, 0x0) }, { "Gold3", RGB_(0xcd, 0xad, 0x0) }, { "Gold4", RGB_(0x8b, 0x75, 0x0) }, - { "GoldenRod", RGB_(0xda, 0xa5, 0x20) }, + { "Goldenrod", RGB_(0xda, 0xa5, 0x20) }, { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) }, { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) }, { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) }, @@ -2805,7 +2826,7 @@ color_name_table_T color_name_table[] = { { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) }, { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) }, { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) }, - { "LightGoldenRodYellow", RGB_(0xfa, 0xfa, 0xd2) }, + { "LightGoldenrodYellow", RGB_(0xfa, 0xfa, 0xd2) }, { "LightGray", RGB_(0xd3, 0xd3, 0xd3) }, { "LightGreen", RGB_(0x90, 0xee, 0x90) }, { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) }, @@ -2890,6 +2911,10 @@ color_name_table_T color_name_table[] = { // for foreground in light/dark color scheme. { "NvimDarkBlue", RGB_(0x00, 0x4c, 0x73) }, { "NvimDarkCyan", RGB_(0x00, 0x73, 0x73) }, + { "NvimDarkGray1", RGB_(0x07, 0x08, 0x0d) }, + { "NvimDarkGray2", RGB_(0x14, 0x16, 0x1b) }, + { "NvimDarkGray3", RGB_(0x2c, 0x2e, 0x33) }, + { "NvimDarkGray4", RGB_(0x4f, 0x52, 0x58) }, { "NvimDarkGreen", RGB_(0x00, 0x55, 0x23) }, { "NvimDarkGrey1", RGB_(0x07, 0x08, 0x0d) }, { "NvimDarkGrey2", RGB_(0x14, 0x16, 0x1b) }, @@ -2900,6 +2925,10 @@ color_name_table_T color_name_table[] = { { "NvimDarkYellow", RGB_(0x6b, 0x53, 0x00) }, { "NvimLightBlue", RGB_(0xa6, 0xdb, 0xff) }, { "NvimLightCyan", RGB_(0x8c, 0xf8, 0xf7) }, + { "NvimLightGray1", RGB_(0xee, 0xf1, 0xf8) }, + { "NvimLightGray2", RGB_(0xe0, 0xe2, 0xea) }, + { "NvimLightGray3", RGB_(0xc4, 0xc6, 0xcd) }, + { "NvimLightGray4", RGB_(0x9b, 0x9e, 0xa4) }, { "NvimLightGreen", RGB_(0xb3, 0xf6, 0xc0) }, { "NvimLightGrey1", RGB_(0xee, 0xf1, 0xf8) }, { "NvimLightGrey2", RGB_(0xe0, 0xe2, 0xea) }, @@ -2930,7 +2959,7 @@ color_name_table_T color_name_table[] = { { "Orchid2", RGB_(0xee, 0x7a, 0xe9) }, { "Orchid3", RGB_(0xcd, 0x69, 0xc9) }, { "Orchid4", RGB_(0x8b, 0x47, 0x89) }, - { "PaleGoldenRod", RGB_(0xee, 0xe8, 0xaa) }, + { "PaleGoldenrod", RGB_(0xee, 0xe8, 0xaa) }, { "PaleGreen", RGB_(0x98, 0xfb, 0x98) }, { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) }, { "PaleGreen2", RGB_(0x90, 0xee, 0x90) }, diff --git a/src/nvim/highlight_group.h b/src/nvim/highlight_group.h index 47d58d20f2..edf5fbde16 100644 --- a/src/nvim/highlight_group.h +++ b/src/nvim/highlight_group.h @@ -12,7 +12,7 @@ typedef struct { char *name; RgbValue color; } color_name_table_T; -extern color_name_table_T color_name_table[700]; +extern color_name_table_T color_name_table[708]; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "highlight_group.h.generated.h" diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 14247b6d86..d635c7d7bf 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -177,7 +177,7 @@ colnr_T tabstop_start(colnr_T col, int ts, colnr_T *vts) colnr_T tabcol = 0; if (vts == NULL || vts[0] == 0) { - return ((col / ts) * ts); + return col - col % ts; } const int tabcount = vts[0]; @@ -189,7 +189,7 @@ colnr_T tabstop_start(colnr_T col, int ts, colnr_T *vts) } const int excess = (tabcol % vts[tabcount]); - return (excess + ((col - excess) / vts[tabcount]) * vts[tabcount]); + return col - (col - excess) % vts[tabcount]; } /// Find the number of tabs and spaces necessary to get from one column @@ -1105,7 +1105,7 @@ void ex_retab(exarg_T *eap) colnr_T *old_vts_ary = curbuf->b_p_vts_array; if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) { - set_string_option_direct(kOptVartabstop, new_ts_str, OPT_LOCAL, 0); + set_option_direct(kOptVartabstop, CSTR_AS_OPTVAL(new_ts_str), OPT_LOCAL, 0); curbuf->b_p_vts_array = new_vts_array; xfree(old_vts_ary); } else { @@ -1116,7 +1116,7 @@ void ex_retab(exarg_T *eap) } xfree(new_ts_str); } - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); u_clearline(curbuf); } @@ -1160,7 +1160,7 @@ int get_expr_indent(void) curwin->w_cursor = save_pos; curwin->w_curswant = save_curswant; curwin->w_set_curswant = save_set_curswant; - check_cursor(); + check_cursor(curwin); State = save_State; // Reset did_throw, unless 'debug' has "throw" and inside a try/catch. diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index a660c9dead..65b5f46333 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -1197,7 +1197,7 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) s = line; } if (s == line) { - // don't recognize "case (foo):" as a baseclass */ + // don't recognize "case (foo):" as a baseclass if (cin_iscase(s, false)) { break; } diff --git a/src/nvim/input.c b/src/nvim/input.c index 7667c49452..e14bfe7539 100644 --- a/src/nvim/input.c +++ b/src/nvim/input.c @@ -14,6 +14,7 @@ #include "nvim/highlight_defs.h" #include "nvim/input.h" #include "nvim/keycodes.h" +#include "nvim/math.h" #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -21,6 +22,7 @@ #include "nvim/os/input.h" #include "nvim/state_defs.h" #include "nvim/ui.h" +#include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "input.c.generated.h" // IWYU pragma: export @@ -180,10 +182,9 @@ int get_number(int colon, bool *mouse_used) ui_cursor_goto(msg_row, msg_col); int c = safe_vgetc(); if (ascii_isdigit(c)) { - if (n > INT_MAX / 10) { + if (vim_append_digit_int(&n, c - '0') == FAIL) { return 0; } - n = n * 10 + c - '0'; msg_putchar(c); typed++; } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) { diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 41b964323e..b557b9802e 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -253,6 +253,7 @@ static int ctrl_x_mode = CTRL_X_NORMAL; static int compl_matches = 0; ///< number of completion matches static char *compl_pattern = NULL; +static size_t compl_patternlen = 0; static Direction compl_direction = FORWARD; static Direction compl_shows_dir = FORWARD; static int compl_pending = 0; ///< > 1 for postponed CTRL-N @@ -567,6 +568,18 @@ static bool is_first_match(const compl_T *const match) return match == compl_first_match; } +static void do_autocmd_completedone(int c) +{ + save_v_event_T save_v_event; + dict_T *v_event = get_v_event(&save_v_event); + + tv_dict_add_str(v_event, S_LEN("reason"), (c == Ctrl_Y ? "accept" : "cancel")); + tv_dict_set_keys_readonly(v_event); + + ins_apply_autocmds(EVENT_COMPLETEDONE); + restore_v_event(v_event, &save_v_event); +} + /// Check that character "c" is part of the item currently being /// completed. Used to decide whether to abandon complete mode when the menu /// is visible. @@ -1104,11 +1117,11 @@ static dict_T *ins_compl_dict_alloc(compl_T *match) { // { word, abbr, menu, kind, info } dict_T *dict = tv_dict_alloc_lock(VAR_FIXED); - tv_dict_add_str(dict, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); - tv_dict_add_str(dict, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); - tv_dict_add_str(dict, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); - tv_dict_add_str(dict, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); - tv_dict_add_str(dict, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); + tv_dict_add_str(dict, S_LEN("word"), match->cp_str); + tv_dict_add_str(dict, S_LEN("abbr"), match->cp_text[CPT_ABBR]); + tv_dict_add_str(dict, S_LEN("menu"), match->cp_text[CPT_MENU]); + tv_dict_add_str(dict, S_LEN("kind"), match->cp_text[CPT_KIND]); + tv_dict_add_str(dict, S_LEN("info"), match->cp_text[CPT_INFO]); if (match->cp_user_data.v_type == VAR_UNKNOWN) { tv_dict_add_str(dict, S_LEN("user_data"), ""); } else { @@ -1307,26 +1320,30 @@ void ins_compl_show_pum(void) } } -/// used for set or update info -void compl_set_info(int pum_idx) +/// check selected is current match. +/// +/// @param selected the item which is selected. +/// @return bool return true when is current match otherwise is false. +bool compl_match_curr_select(int selected) { - compl_T *comp = compl_first_match; - char *pum_text = compl_match_array[pum_idx].pum_text; - - while (comp != NULL) { - if (pum_text == comp->cp_str - || pum_text == comp->cp_text[CPT_ABBR]) { - comp->cp_text[CPT_INFO] = compl_match_array[pum_idx].pum_info; - - // if comp is current match update completed_item value - if (comp == compl_curr_match) { - dict_T *dict = ins_compl_dict_alloc(compl_curr_match); - set_vim_var_dict(VV_COMPLETED_ITEM, dict); + if (selected < 0) { + return false; + } + compl_T *match = compl_first_match; + int selected_idx = -1, list_idx = 0; + do { + if (!match_at_original_text(match)) { + if (compl_curr_match != NULL + && compl_curr_match->cp_number == match->cp_number) { + selected_idx = list_idx; + break; } - break; + list_idx += 1; } - comp = comp->cp_next; - } + match = match->cp_next; + } while (match != NULL && !is_first_match(match)); + + return selected == selected_idx; } #define DICT_FIRST (1) ///< use just first element in "dict" @@ -1579,6 +1596,7 @@ static char *find_line_end(char *ptr) static void ins_compl_free(void) { XFREE_CLEAR(compl_pattern); + compl_patternlen = 0; XFREE_CLEAR(compl_leader); if (compl_first_match == NULL) { @@ -1613,6 +1631,7 @@ void ins_compl_clear(void) compl_started = false; compl_matches = 0; XFREE_CLEAR(compl_pattern); + compl_patternlen = 0; XFREE_CLEAR(compl_leader); edit_submode_extra = NULL; kv_destroy(compl_orig_extmarks); @@ -2103,7 +2122,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) } // Trigger the CompleteDone event to give scripts a chance to act // upon the end of completion. - ins_apply_autocmds(EVENT_COMPLETEDONE); + do_autocmd_completedone(c); return retval; } @@ -2192,7 +2211,7 @@ bool ins_compl_prep(int c) } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) { // Trigger the CompleteDone event to give scripts a chance to act // upon the (possibly failed) completion. - ins_apply_autocmds(EVENT_COMPLETEDONE); + do_autocmd_completedone(c); } may_trigger_modechanged(); @@ -2435,8 +2454,9 @@ static void expand_by_function(int type, char *base) } textlock--; - curwin->w_cursor = pos; // restore the cursor position - validate_cursor(); + curwin->w_cursor = pos; // restore the cursor position + check_cursor(curwin); // make sure cursor position is valid, just in case + validate_cursor(curwin); if (!equalpos(curwin->w_cursor, pos)) { emsg(_(e_compldel)); goto theend; @@ -2730,72 +2750,6 @@ static void ins_compl_update_sequence_numbers(void) } } -static int info_add_completion_info(list_T *li) -{ - if (compl_first_match == NULL) { - return OK; - } - - bool forward = compl_dir_forward(); - compl_T *match = compl_first_match; - // There are four cases to consider here: - // 1) when just going forward through the menu, - // compl_first_match should point to the initial entry with - // number zero and CP_ORIGINAL_TEXT flag set - // 2) when just going backwards, - // compl-first_match should point to the last entry before - // the entry with the CP_ORIGINAL_TEXT flag set - // 3) when first going forwards and then backwards, e.g. - // pressing C-N, C-P, compl_first_match points to the - // last entry before the entry with the CP_ORIGINAL_TEXT - // flag set and next-entry moves opposite through the list - // compared to case 2, so pretend the direction is forward again - // 4) when first going backwards and then forwards, e.g. - // pressing C-P, C-N, compl_first_match points to the - // first entry with the CP_ORIGINAL_TEXT - // flag set and next-entry moves in opposite direction through the list - // compared to case 1, so pretend the direction is backwards again - // - // But only do this when the 'noselect' option is not active! - - if (!compl_no_select) { - if (forward && !match_at_original_text(match)) { - forward = false; - } else if (!forward && match_at_original_text(match)) { - forward = true; - } - } - - // Skip the element with the CP_ORIGINAL_TEXT flag at the beginning, in case of - // forward completion, or at the end, in case of backward completion. - match = (forward || match->cp_prev == NULL - ? match->cp_next - : (compl_no_select && match_at_original_text(match) - ? match->cp_prev - : match->cp_prev->cp_prev)); - - while (match != NULL && !match_at_original_text(match)) { - dict_T *di = tv_dict_alloc(); - - tv_list_append_dict(li, di); - tv_dict_add_str(di, S_LEN("word"), EMPTY_IF_NULL(match->cp_str)); - tv_dict_add_str(di, S_LEN("abbr"), EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); - tv_dict_add_str(di, S_LEN("menu"), EMPTY_IF_NULL(match->cp_text[CPT_MENU])); - tv_dict_add_str(di, S_LEN("kind"), EMPTY_IF_NULL(match->cp_text[CPT_KIND])); - tv_dict_add_str(di, S_LEN("info"), EMPTY_IF_NULL(match->cp_text[CPT_INFO])); - if (match->cp_user_data.v_type == VAR_UNKNOWN) { - // Add an empty string for backwards compatibility - tv_dict_add_str(di, S_LEN("user_data"), ""); - } else { - tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); - } - - match = forward ? match->cp_next : match->cp_prev; - } - - return OK; -} - /// Get complete information static void get_complete_info(list_T *what_list, dict_T *retdict) { @@ -2839,25 +2793,54 @@ static void get_complete_info(list_T *what_list, dict_T *retdict) ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible()); } - if (ret == OK && (what_flag & CI_WHAT_ITEMS)) { - list_T *li = tv_list_alloc(get_compl_len()); - ret = tv_dict_add_list(retdict, S_LEN("items"), li); - if (ret == OK) { - ret = info_add_completion_info(li); + if (ret == OK && (what_flag & CI_WHAT_ITEMS || what_flag & CI_WHAT_SELECTED)) { + list_T *li = NULL; + int selected_idx = -1; + if (what_flag & CI_WHAT_ITEMS) { + li = tv_list_alloc(kListLenMayKnow); + ret = tv_dict_add_list(retdict, S_LEN("items"), li); } - } - - if (ret == OK && (what_flag & CI_WHAT_SELECTED)) { - if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) { - ins_compl_update_sequence_numbers(); + 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); - win_T *wp = win_float_find_preview(); - if (wp != NULL) { - tv_dict_add_nr(retdict, S_LEN("preview_winid"), wp->handle); - tv_dict_add_nr(retdict, S_LEN("preview_bufnr"), wp->w_buffer->handle); + if (ret == OK && compl_first_match != NULL) { + int list_idx = 0; + compl_T *match = compl_first_match; + do { + if (!match_at_original_text(match)) { + if (what_flag & CI_WHAT_ITEMS) { + dict_T *di = tv_dict_alloc(); + tv_list_append_dict(li, di); + tv_dict_add_str(di, S_LEN("word"), match->cp_str); + tv_dict_add_str(di, S_LEN("abbr"), match->cp_text[CPT_ABBR]); + tv_dict_add_str(di, S_LEN("menu"), match->cp_text[CPT_MENU]); + tv_dict_add_str(di, S_LEN("kind"), match->cp_text[CPT_KIND]); + tv_dict_add_str(di, S_LEN("info"), match->cp_text[CPT_INFO]); + if (match->cp_user_data.v_type == VAR_UNKNOWN) { + // Add an empty string for backwards compatibility + tv_dict_add_str(di, S_LEN("user_data"), ""); + } else { + tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); + } + } + if (compl_curr_match != NULL + && compl_curr_match->cp_number == match->cp_number) { + selected_idx = list_idx; + } + list_idx += 1; + } + match = match->cp_next; + } while (match != NULL && !is_first_match(match)); + } + if (ret == OK && (what_flag & CI_WHAT_SELECTED)) { + ret = tv_dict_add_nr(retdict, S_LEN("selected"), selected_idx); + win_T *wp = win_float_find_preview(); + if (wp != NULL) { + tv_dict_add_nr(retdict, S_LEN("preview_winid"), wp->handle); + tv_dict_add_nr(retdict, S_LEN("preview_bufnr"), wp->w_buffer->handle); + } } } @@ -2937,7 +2920,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar // buffer, so that word at start of buffer is found // correctly. st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count; - st->first_match_pos.col = (colnr_T)strlen(ml_get(st->first_match_pos.lnum)); + st->first_match_pos.col = ml_get_len(st->first_match_pos.lnum); } st->last_match_pos = st->first_match_pos; compl_type = 0; @@ -3023,11 +3006,11 @@ done: static void get_next_include_file_completion(int compl_type) { find_pattern_in_path(compl_pattern, compl_direction, - strlen(compl_pattern), false, false, + compl_patternlen, false, false, ((compl_type == CTRL_X_PATH_DEFINES && !(compl_cont_status & CONT_SOL)) ? FIND_DEFINE : FIND_ANY), - 1, ACTION_EXPAND, 1, MAXLNUM); + 1, ACTION_EXPAND, 1, MAXLNUM, false); } /// Get the next set of words matching "compl_pattern" in dictionary or @@ -3106,8 +3089,7 @@ static void get_next_cmdline_completion(void) char **matches; int num_matches; if (expand_cmdline(&compl_xp, compl_pattern, - (int)strlen(compl_pattern), - &num_matches, &matches) == EXPAND_OK) { + (int)compl_patternlen, &num_matches, &matches) == EXPAND_OK) { ins_compl_add_matches(num_matches, matches, false); } } @@ -3249,8 +3231,8 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_ compl_direction, compl_pattern); } else { found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos, - NULL, compl_direction, compl_pattern, 1, - SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL); + NULL, compl_direction, compl_pattern, compl_patternlen, + 1, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL); } msg_silent--; if (!compl_started || st->set_match_pos) { @@ -3934,7 +3916,8 @@ static bool ins_compl_use_match(int c) /// Get the pattern, column and length for normal completion (CTRL-N CTRL-P /// completion) -/// Sets the global variables: compl_col, compl_length and compl_pattern. +/// Sets the global variables: compl_col, compl_length, compl_pattern and +/// compl_patternlen. /// Uses the global variables: compl_cont_status and ctrl_x_mode static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) { @@ -3951,21 +3934,23 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) } } else if (compl_status_adding()) { char *prefix = "\\<"; + size_t prefixlen = STRLEN_LITERAL("\\<"); // we need up to 2 extra chars for the prefix - compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2); + compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, + compl_length) + prefixlen); if (!vim_iswordp(line + compl_col) || (compl_col > 0 && (vim_iswordp(mb_prevptr(line, line + compl_col))))) { prefix = ""; + prefixlen = 0; } STRCPY(compl_pattern, prefix); - quote_meta(compl_pattern + strlen(prefix), - line + compl_col, compl_length); + quote_meta(compl_pattern + prefixlen, line + compl_col, compl_length); } else if (--startcol < 0 || !vim_iswordp(mb_prevptr(line, line + startcol + 1))) { // Match any word of at least two chars - compl_pattern = xstrdup("\\<\\k\\k"); + compl_pattern = xstrnsave(S_LEN("\\<\\k\\k")); compl_col += curs_col; compl_length = 0; } else { @@ -3997,6 +3982,8 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) } } + compl_patternlen = strlen(compl_pattern); + return OK; } @@ -4016,6 +4003,8 @@ static int get_wholeline_compl_info(char *line, colnr_T curs_col) compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length); } + compl_patternlen = strlen(compl_pattern); + return OK; } @@ -4041,6 +4030,7 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col) compl_col += startcol; compl_length = (int)curs_col - startcol; compl_pattern = addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES); + compl_patternlen = strlen(compl_pattern); return OK; } @@ -4050,7 +4040,8 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col) static int get_cmdline_compl_info(char *line, colnr_T curs_col) { compl_pattern = xstrnsave(line, (size_t)curs_col); - set_cmd_context(&compl_xp, compl_pattern, (int)strlen(compl_pattern), curs_col, false); + compl_patternlen = (size_t)curs_col; + set_cmd_context(&compl_xp, compl_pattern, (int)compl_patternlen, curs_col, false); if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL || compl_xp.xp_context == EXPAND_NOTHING) { // No completion possible, use an empty pattern to get a @@ -4096,7 +4087,8 @@ static int get_userdefined_compl_info(colnr_T curs_col) State = save_State; curwin->w_cursor = pos; // restore the cursor position - validate_cursor(); + check_cursor(curwin); // make sure cursor position is valid, just in case + validate_cursor(curwin); if (!equalpos(curwin->w_cursor, pos)) { emsg(_(e_compldel)); return FAIL; @@ -4135,6 +4127,7 @@ static int get_userdefined_compl_info(colnr_T curs_col) char *line = ml_get(curwin->w_cursor.lnum); compl_length = curs_col - compl_col; compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length); + compl_patternlen = (size_t)compl_length; return OK; } @@ -4160,6 +4153,7 @@ static int get_spell_compl_info(int startcol, colnr_T curs_col) // Need to obtain "line" again, it may have become invalid. char *line = ml_get(curwin->w_cursor.lnum); compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length); + compl_patternlen = (size_t)compl_length; return OK; } @@ -4355,6 +4349,7 @@ static int ins_compl_start(void) if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0, flags, false) != OK) { XFREE_CLEAR(compl_pattern); + compl_patternlen = 0; XFREE_CLEAR(compl_orig_text); kv_destroy(compl_orig_extmarks); return FAIL; @@ -4601,7 +4596,7 @@ void free_insexpand_stuff(void) static void spell_back_to_badword(void) { pos_T tpos = curwin->w_cursor; - spell_bad_len = spell_move_to(curwin, BACKWARD, true, true, NULL); + spell_bad_len = spell_move_to(curwin, BACKWARD, SMT_ALL, true, NULL); if (curwin->w_cursor.col != tpos.col) { start_arrow(&tpos); } diff --git a/src/nvim/lua/api_wrappers.c b/src/nvim/lua/api_wrappers.c new file mode 100644 index 0000000000..2b7b0c6471 --- /dev/null +++ b/src/nvim/lua/api_wrappers.c @@ -0,0 +1,18 @@ +#include <lauxlib.h> +#include <lua.h> +#include <lualib.h> + +#include "nvim/api/private/defs.h" +#include "nvim/api/private/dispatch.h" +#include "nvim/api/private/helpers.h" +#include "nvim/ex_docmd.h" +#include "nvim/ex_getln.h" +#include "nvim/func_attr.h" +#include "nvim/globals.h" +#include "nvim/lua/converter.h" +#include "nvim/lua/executor.h" +#include "nvim/memory.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua_api_c_bindings.generated.h" +#endif diff --git a/src/nvim/lua/base64.c b/src/nvim/lua/base64.c index c1f43a37d7..8fe918493a 100644 --- a/src/nvim/lua/base64.c +++ b/src/nvim/lua/base64.c @@ -45,12 +45,13 @@ static int nlua_base64_decode(lua_State *L) size_t src_len = 0; const char *src = lua_tolstring(L, 1, &src_len); - const char *ret = base64_decode(src, src_len); + size_t out_len = 0; + const char *ret = base64_decode(src, src_len, &out_len); if (ret == NULL) { return luaL_error(L, "Invalid input"); } - lua_pushstring(L, ret); + lua_pushlstring(L, ret, out_len); xfree((void *)ret); return 1; diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index bba771f8a5..38ccb03cfc 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -597,9 +597,9 @@ static bool typval_conv_special = false; /// @param[in] tv typval_T to convert. /// /// @return true in case of success, false otherwise. -bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) +bool nlua_push_typval(lua_State *lstate, typval_T *const tv, int flags) { - typval_conv_special = special; + typval_conv_special = (flags & kNluaPushSpecial); const int initial_size = lua_gettop(lstate); if (!lua_checkstack(lstate, initial_size + 2)) { @@ -662,7 +662,7 @@ static inline void nlua_create_typed_table(lua_State *lstate, const size_t narr, /// Convert given String to lua string /// /// Leaves converted string on top of the stack. -void nlua_push_String(lua_State *lstate, const String s, bool special) +void nlua_push_String(lua_State *lstate, const String s, int flags) FUNC_ATTR_NONNULL_ALL { lua_pushlstring(lstate, s.data, s.size); @@ -671,7 +671,7 @@ void nlua_push_String(lua_State *lstate, const String s, bool special) /// Convert given Integer to lua number /// /// Leaves converted number on top of the stack. -void nlua_push_Integer(lua_State *lstate, const Integer n, bool special) +void nlua_push_Integer(lua_State *lstate, const Integer n, int flags) FUNC_ATTR_NONNULL_ALL { lua_pushnumber(lstate, (lua_Number)n); @@ -680,10 +680,10 @@ void nlua_push_Integer(lua_State *lstate, const Integer n, bool special) /// Convert given Float to lua table /// /// Leaves converted table on top of the stack. -void nlua_push_Float(lua_State *lstate, const Float f, bool special) +void nlua_push_Float(lua_State *lstate, const Float f, int flags) FUNC_ATTR_NONNULL_ALL { - if (special) { + if (flags & kNluaPushSpecial) { nlua_create_typed_table(lstate, 0, 1, kObjectTypeFloat); nlua_push_val_idx(lstate); lua_pushnumber(lstate, (lua_Number)f); @@ -696,7 +696,7 @@ void nlua_push_Float(lua_State *lstate, const Float f, bool special) /// Convert given Float to lua boolean /// /// Leaves converted value on top of the stack. -void nlua_push_Boolean(lua_State *lstate, const Boolean b, bool special) +void nlua_push_Boolean(lua_State *lstate, const Boolean b, int flags) FUNC_ATTR_NONNULL_ALL { lua_pushboolean(lstate, b); @@ -705,21 +705,21 @@ void nlua_push_Boolean(lua_State *lstate, const Boolean b, bool special) /// Convert given Dictionary to lua table /// /// Leaves converted table on top of the stack. -void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special) +void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, int flags) FUNC_ATTR_NONNULL_ALL { - if (dict.size == 0 && special) { + if (dict.size == 0 && (flags & kNluaPushSpecial)) { nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); } else { lua_createtable(lstate, 0, (int)dict.size); - if (dict.size == 0 && !special) { + if (dict.size == 0 && !(flags & kNluaPushSpecial)) { nlua_pushref(lstate, nlua_global_refs->empty_dict_ref); lua_setmetatable(lstate, -2); } } for (size_t i = 0; i < dict.size; i++) { - nlua_push_String(lstate, dict.items[i].key, special); - nlua_push_Object(lstate, &dict.items[i].value, special); + nlua_push_String(lstate, dict.items[i].key, flags); + nlua_push_Object(lstate, &dict.items[i].value, flags); lua_rawset(lstate, -3); } } @@ -727,18 +727,18 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special /// Convert given Array to lua table /// /// Leaves converted table on top of the stack. -void nlua_push_Array(lua_State *lstate, const Array array, bool special) +void nlua_push_Array(lua_State *lstate, const Array array, int flags) FUNC_ATTR_NONNULL_ALL { lua_createtable(lstate, (int)array.size, 0); for (size_t i = 0; i < array.size; i++) { - nlua_push_Object(lstate, &array.items[i], special); + nlua_push_Object(lstate, &array.items[i], flags); lua_rawseti(lstate, -2, (int)i + 1); } } #define GENERATE_INDEX_FUNCTION(type) \ - void nlua_push_##type(lua_State *lstate, const type item, bool special) \ + void nlua_push_##type(lua_State *lstate, const type item, int flags) \ FUNC_ATTR_NONNULL_ALL \ { \ lua_pushnumber(lstate, (lua_Number)(item)); \ @@ -753,12 +753,12 @@ GENERATE_INDEX_FUNCTION(Tabpage) /// Convert given Object to lua value /// /// Leaves converted value on top of the stack. -void nlua_push_Object(lua_State *lstate, Object *obj, bool special) +void nlua_push_Object(lua_State *lstate, Object *obj, int flags) FUNC_ATTR_NONNULL_ALL { switch (obj->type) { case kObjectTypeNil: - if (special) { + if (flags & kNluaPushSpecial) { lua_pushnil(lstate); } else { nlua_pushref(lstate, nlua_global_refs->nil_ref); @@ -766,13 +766,15 @@ void nlua_push_Object(lua_State *lstate, Object *obj, bool special) break; case kObjectTypeLuaRef: { nlua_pushref(lstate, obj->data.luaref); - api_free_luaref(obj->data.luaref); - obj->data.luaref = LUA_NOREF; + if (flags & kNluaPushFreeRefs) { + api_free_luaref(obj->data.luaref); + obj->data.luaref = LUA_NOREF; + } break; } #define ADD_TYPE(type, data_key) \ case kObjectType##type: { \ - nlua_push_##type(lstate, obj->data.data_key, special); \ + nlua_push_##type(lstate, obj->data.data_key, flags); \ break; \ } ADD_TYPE(Boolean, boolean) @@ -784,7 +786,7 @@ void nlua_push_Object(lua_State *lstate, Object *obj, bool special) #undef ADD_TYPE #define ADD_REMOTE_TYPE(type) \ case kObjectType##type: { \ - nlua_push_##type(lstate, (type)obj->data.integer, special); \ + nlua_push_##type(lstate, (type)obj->data.integer, flags); \ break; \ } ADD_REMOTE_TYPE(Buffer) @@ -1380,7 +1382,7 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) lua_pushstring(L, field->str); if (field->type == kObjectTypeNil) { - nlua_push_Object(L, (Object *)mem, false); + nlua_push_Object(L, (Object *)mem, 0); } else if (field->type == kObjectTypeInteger) { lua_pushinteger(L, *(Integer *)mem); } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow @@ -1391,11 +1393,11 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) } else if (field->type == kObjectTypeBoolean) { lua_pushboolean(L, *(Boolean *)mem); } else if (field->type == kObjectTypeString) { - nlua_push_String(L, *(String *)mem, false); + nlua_push_String(L, *(String *)mem, 0); } else if (field->type == kObjectTypeArray) { - nlua_push_Array(L, *(Array *)mem, false); + nlua_push_Array(L, *(Array *)mem, 0); } else if (field->type == kObjectTypeDictionary) { - nlua_push_Dictionary(L, *(Dictionary *)mem, false); + nlua_push_Dictionary(L, *(Dictionary *)mem, 0); } else if (field->type == kObjectTypeLuaRef) { nlua_pushref(L, *(LuaRef *)mem); } else { diff --git a/src/nvim/lua/converter.h b/src/nvim/lua/converter.h index a502df80d9..d1ba61bcee 100644 --- a/src/nvim/lua/converter.h +++ b/src/nvim/lua/converter.h @@ -9,6 +9,12 @@ #define nlua_pop_Window nlua_pop_handle #define nlua_pop_Tabpage nlua_pop_handle +/// Flags for nlua_push_*() functions. +enum { + kNluaPushSpecial = 0x01, ///< Use lua-special-tbl when necessary + kNluaPushFreeRefs = 0x02, ///< Free luarefs to elide an api_luarefs_free_*() later +}; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/converter.h.generated.h" #endif diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 1a9bd026b5..a76b8213e5 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -103,7 +103,7 @@ typedef struct { if (args[i].v_type == VAR_UNKNOWN) { \ lua_pushnil(lstate); \ } else { \ - nlua_push_typval(lstate, &args[i], special); \ + nlua_push_typval(lstate, &args[i], (special) ? kNluaPushSpecial : 0); \ } \ } @@ -325,7 +325,7 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) } ArrayOf(String) ret = runtime_get_named_thread(is_lua, pat, all); - nlua_push_Array(lstate, ret, true); + nlua_push_Array(lstate, ret, kNluaPushSpecial); api_free_array(ret); api_free_array(pat); @@ -1210,7 +1210,7 @@ int nlua_call(lua_State *lstate) }); if (!ERROR_SET(&err)) { - nlua_push_typval(lstate, &rettv, false); + nlua_push_typval(lstate, &rettv, 0); } tv_clear(&rettv); @@ -1261,7 +1261,7 @@ static int nlua_rpc(lua_State *lstate, bool request) ArenaMem res_mem = NULL; Object result = rpc_send_call(chan_id, name, args, &res_mem, &err); if (!ERROR_SET(&err)) { - nlua_push_Object(lstate, &result, false); + nlua_push_Object(lstate, &result, 0); arena_mem_free(res_mem); } } else { @@ -1487,7 +1487,7 @@ static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, const char *name } } -int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name) +void nlua_source_str(const char *code, char *name) { const sctx_T save_current_sctx = current_sctx; current_sctx.sc_sid = SID_STR; @@ -1495,22 +1495,11 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name) current_sctx.sc_lnum = 0; estack_push(ETYPE_SCRIPT, name, 0); - garray_T ga; - char *line = NULL; - - ga_init(&ga, (int)sizeof(char *), 10); - while ((line = fgetline(0, cookie, 0, false)) != NULL) { - GA_APPEND(char *, &ga, line); - } - char *code = ga_concat_strings_sep(&ga, "\n"); size_t len = strlen(code); nlua_typval_exec(code, len, name, NULL, 0, false, NULL); estack_pop(); current_sctx = save_current_sctx; - ga_clear_strings(&ga); - xfree(code); - return OK; } /// Call a LuaCallable given some typvals @@ -1564,7 +1553,7 @@ Object nlua_exec(const String str, const Array args, LuaRetMode mode, Arena *are } for (size_t i = 0; i < args.size; i++) { - nlua_push_Object(lstate, &args.items[i], false); + nlua_push_Object(lstate, &args.items[i], 0); } if (nlua_pcall(lstate, (int)args.size, 1)) { @@ -1611,7 +1600,7 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode, nargs++; } for (size_t i = 0; i < args.size; i++) { - nlua_push_Object(lstate, &args.items[i], false); + nlua_push_Object(lstate, &args.items[i], 0); } if (nlua_pcall(lstate, nargs, 1)) { @@ -1767,7 +1756,7 @@ void ex_luado(exarg_T *const eap) lua_pushvalue(lstate, -1); const char *const old_line = ml_get_buf(curbuf, l); // Get length of old_line here as calling Lua code may free it. - const size_t old_line_len = strlen(old_line); + const colnr_T old_line_len = ml_get_buf_len(curbuf, l); lua_pushstring(lstate, old_line); lua_pushnumber(lstate, (lua_Number)l); if (nlua_pcall(lstate, 2, 1)) { @@ -1791,13 +1780,13 @@ void ex_luado(exarg_T *const eap) } } ml_replace(l, new_line_transformed, false); - inserted_bytes(l, 0, (int)old_line_len, (int)new_line_len); + inserted_bytes(l, 0, old_line_len, (int)new_line_len); } lua_pop(lstate, 1); } lua_pop(lstate, 1); - check_cursor(); + check_cursor(curwin); redraw_curbuf_later(UPD_NOT_VALID); } @@ -1909,6 +1898,9 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, tslua_push_parser); lua_setfield(lstate, -2, "_create_ts_parser"); + lua_pushcfunction(lstate, tslua_push_querycursor); + lua_setfield(lstate, -2, "_create_ts_querycursor"); + lua_pushcfunction(lstate, tslua_add_language); lua_setfield(lstate, -2, "_ts_add_language"); @@ -2061,9 +2053,9 @@ char *nlua_register_table_as_callable(const typval_T *const arg) return name; } -void nlua_execute_on_key(int c) +void nlua_execute_on_key(int c, char *typed_buf, size_t typed_len) { - char buf[NUMBUFLEN]; + char buf[MB_MAXBYTES * 3 + 4]; size_t buf_len = special_to_buf(c, mod_mask, false, buf); lua_State *const lstate = global_lstate; @@ -2082,9 +2074,12 @@ void nlua_execute_on_key(int c) // [ vim, vim._on_key, buf ] lua_pushlstring(lstate, buf, buf_len); + // [ vim, vim._on_key, buf, typed_buf ] + lua_pushlstring(lstate, typed_buf, typed_len); + int save_got_int = got_int; got_int = false; // avoid interrupts when the key typed is Ctrl-C - if (nlua_pcall(lstate, 1, 0)) { + if (nlua_pcall(lstate, 2, 0)) { nlua_error(lstate, _("Error executing vim.on_key Lua callback: %.*s")); } diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index ebcd62122f..32fde3853b 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -12,7 +12,7 @@ #include "nvim/types_defs.h" #include "nvim/usercmd.h" // IWYU pragma: keep -// Generated by msgpack-gen.lua +// Generated by generators/gen_api_dispatch.lua void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; typedef struct { @@ -43,7 +43,7 @@ typedef enum { kRetLuaref, ///< return value becomes a single Luaref, regardless of type (except NIL) } LuaRetMode; -/// To use with kRetNilBool for quick thuthyness check +/// To use with kRetNilBool for quick truthiness check #define LUARET_TRUTHY(res) ((res).type == kObjectTypeBoolean && (res).data.boolean == true) #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 8f58fd1a1a..22ee0a1c98 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -107,15 +107,15 @@ static int regex_match_line(lua_State *lstate) } char *line = ml_get_buf(buf, rownr + 1); - size_t len = strlen(line); + colnr_T len = ml_get_buf_len(buf, rownr + 1); - if (start < 0 || (size_t)start > len) { + if (start < 0 || start > len) { return luaL_error(lstate, "invalid start"); } char save = NUL; if (end >= 0) { - if ((size_t)end > len || end < start) { + if (end > len || end < start) { return luaL_error(lstate, "invalid end"); } save = line[end]; @@ -449,7 +449,7 @@ int nlua_getvar(lua_State *lstate) if (di == NULL) { return 0; // nil } - nlua_push_typval(lstate, &di->di_tv, false); + nlua_push_typval(lstate, &di->di_tv, 0); return 1; } @@ -543,14 +543,27 @@ static int nlua_iconv(lua_State *lstate) return 1; } -// Update foldlevels (e.g., by evaluating 'foldexpr') for all lines in the current window without -// invoking other side effects. Unlike `zx`, it does not close manually opened folds and does not -// open folds under the cursor. +// Update foldlevels (e.g., by evaluating 'foldexpr') for the given line range in the given window, +// without invoking other side effects. Unlike `zx`, it does not close manually opened folds and +// does not open folds under the cursor. static int nlua_foldupdate(lua_State *lstate) { - curwin->w_foldinvalid = true; // recompute folds - foldUpdate(curwin, 1, (linenr_T)MAXLNUM); - curwin->w_foldinvalid = false; + handle_T window = (handle_T)luaL_checkinteger(lstate, 1); + win_T *win = handle_get_window(window); + if (!win) { + return luaL_error(lstate, "invalid window"); + } + // input is zero-based end-exclusive range + linenr_T top = (linenr_T)luaL_checkinteger(lstate, 2) + 1; + if (top < 1 || top > win->w_buffer->b_ml.ml_line_count) { + return luaL_error(lstate, "invalid top"); + } + linenr_T bot = (linenr_T)luaL_checkinteger(lstate, 3); + if (top > bot) { + return luaL_error(lstate, "invalid bot"); + } + + foldUpdate(win, top, bot); return 0; } diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index 25a753b179..e87cf756a8 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -33,15 +33,10 @@ #define TS_META_NODE "treesitter_node" #define TS_META_QUERY "treesitter_query" #define TS_META_QUERYCURSOR "treesitter_querycursor" +#define TS_META_QUERYMATCH "treesitter_querymatch" #define TS_META_TREECURSOR "treesitter_treecursor" typedef struct { - TSQueryCursor *cursor; - int predicated_match; - int max_match_id; -} TSLua_cursor; - -typedef struct { LuaRef cb; lua_State *lstate; bool lex; @@ -56,126 +51,44 @@ typedef struct { # include "lua/treesitter.c.generated.h" #endif -// TSParser -static struct luaL_Reg parser_meta[] = { - { "__gc", parser_gc }, - { "__tostring", parser_tostring }, - { "parse", parser_parse }, - { "reset", parser_reset }, - { "set_included_ranges", parser_set_ranges }, - { "included_ranges", parser_get_ranges }, - { "set_timeout", parser_set_timeout }, - { "timeout", parser_get_timeout }, - { "_set_logger", parser_set_logger }, - { "_logger", parser_get_logger }, - { NULL, NULL } -}; - -// TSTree -static struct luaL_Reg tree_meta[] = { - { "__gc", tree_gc }, - { "__tostring", tree_tostring }, - { "root", tree_root }, - { "edit", tree_edit }, - { "included_ranges", tree_get_ranges }, - { "copy", tree_copy }, - { NULL, NULL } -}; - -// TSNode -static struct luaL_Reg node_meta[] = { - { "__tostring", node_tostring }, - { "__eq", node_eq }, - { "__len", node_child_count }, - { "id", node_id }, - { "range", node_range }, - { "start", node_start }, - { "end_", node_end }, - { "type", node_type }, - { "symbol", node_symbol }, - { "field", node_field }, - { "named", node_named }, - { "missing", node_missing }, - { "extra", node_extra }, - { "has_changes", node_has_changes }, - { "has_error", node_has_error }, - { "sexpr", node_sexpr }, - { "child_count", node_child_count }, - { "named_child_count", node_named_child_count }, - { "child", node_child }, - { "named_child", node_named_child }, - { "descendant_for_range", node_descendant_for_range }, - { "named_descendant_for_range", node_named_descendant_for_range }, - { "parent", node_parent }, - { "iter_children", node_iter_children }, - { "_rawquery", node_rawquery }, - { "next_sibling", node_next_sibling }, - { "prev_sibling", node_prev_sibling }, - { "next_named_sibling", node_next_named_sibling }, - { "prev_named_sibling", node_prev_named_sibling }, - { "named_children", node_named_children }, - { "root", node_root }, - { "tree", node_tree }, - { "byte_length", node_byte_length }, - { "equal", node_equal }, - - { NULL, NULL } -}; - -// TSQuery -static struct luaL_Reg query_meta[] = { - { "__gc", query_gc }, - { "__tostring", query_tostring }, - { "inspect", query_inspect }, - { NULL, NULL } -}; +static PMap(cstr_t) langs = MAP_INIT; -// cursors are not exposed, but still needs garbage collection -static struct luaL_Reg querycursor_meta[] = { - { "__gc", querycursor_gc }, - { NULL, NULL } -}; +// TSLanguage -static struct luaL_Reg treecursor_meta[] = { - { "__gc", treecursor_gc }, - { NULL, NULL } -}; - -static kvec_t(TSQueryCursor *) cursors = KV_INITIAL_VALUE; -static PMap(cstr_t) langs = MAP_INIT; +int tslua_has_language(lua_State *L) +{ + const char *lang_name = luaL_checkstring(L, 1); + lua_pushboolean(L, map_has(cstr_t, &langs, lang_name)); + return 1; +} -static void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) +static TSLanguage *load_language(lua_State *L, const char *path, const char *lang_name, + const char *symbol) { - if (luaL_newmetatable(L, tname)) { // [meta] - luaL_register(L, NULL, meta); + uv_lib_t lib; + if (uv_dlopen(path, &lib)) { + uv_dlclose(&lib); + luaL_error(L, "Failed to load parser for language '%s': uv_dlopen: %s", + lang_name, uv_dlerror(&lib)); + } - lua_pushvalue(L, -1); // [meta, meta] - lua_setfield(L, -2, "__index"); // [meta] + char symbol_buf[128]; + snprintf(symbol_buf, sizeof(symbol_buf), "tree_sitter_%s", symbol); + + TSLanguage *(*lang_parser)(void); + if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) { + uv_dlclose(&lib); + luaL_error(L, "Failed to load parser: uv_dlsym: %s", uv_dlerror(&lib)); } - lua_pop(L, 1); // [] (don't use it now) -} -/// Init the tslua library. -/// -/// All global state is stored in the registry of the lua_State. -void tslua_init(lua_State *L) -{ - // type metatables - build_meta(L, TS_META_PARSER, parser_meta); - build_meta(L, TS_META_TREE, tree_meta); - build_meta(L, TS_META_NODE, node_meta); - build_meta(L, TS_META_QUERY, query_meta); - build_meta(L, TS_META_QUERYCURSOR, querycursor_meta); - build_meta(L, TS_META_TREECURSOR, treecursor_meta); + TSLanguage *lang = lang_parser(); - ts_set_allocator(xmalloc, xcalloc, xrealloc, xfree); -} + if (lang == NULL) { + uv_dlclose(&lib); + luaL_error(L, "Failed to load parser %s: internal error", path); + } -int tslua_has_language(lua_State *L) -{ - const char *lang_name = luaL_checkstring(L, 1); - lua_pushboolean(L, map_has(cstr_t, &langs, lang_name)); - return 1; + return lang; } // Creates the language into the internal language map. @@ -196,34 +109,7 @@ int tslua_add_language(lua_State *L) return 1; } -#define BUFSIZE 128 - char symbol_buf[BUFSIZE]; - snprintf(symbol_buf, BUFSIZE, "tree_sitter_%s", symbol_name); -#undef BUFSIZE - - uv_lib_t lib; - if (uv_dlopen(path, &lib)) { - snprintf(IObuff, IOSIZE, "Failed to load parser for language '%s': uv_dlopen: %s", - lang_name, uv_dlerror(&lib)); - uv_dlclose(&lib); - lua_pushstring(L, IObuff); - return lua_error(L); - } - - TSLanguage *(*lang_parser)(void); - if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) { - snprintf(IObuff, IOSIZE, "Failed to load parser: uv_dlsym: %s", - uv_dlerror(&lib)); - uv_dlclose(&lib); - lua_pushstring(L, IObuff); - return lua_error(L); - } - - TSLanguage *lang = lang_parser(); - if (lang == NULL) { - uv_dlclose(&lib); - return luaL_error(L, "Failed to load parser %s: internal error", path); - } + TSLanguage *lang = load_language(L, path, lang_name, symbol_name); uint32_t lang_version = ts_language_version(lang); if (lang_version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION @@ -254,14 +140,19 @@ int tslua_remove_lang(lua_State *L) return 1; } -int tslua_inspect_lang(lua_State *L) +static TSLanguage *lang_check(lua_State *L, int index) { - const char *lang_name = luaL_checkstring(L, 1); - + const char *lang_name = luaL_checkstring(L, index); TSLanguage *lang = pmap_get(cstr_t)(&langs, lang_name); if (!lang) { - return luaL_error(L, "no such language: %s", lang_name); + luaL_error(L, "no such language: %s", lang_name); } + return lang; +} + +int tslua_inspect_lang(lua_State *L) +{ + TSLanguage *lang = lang_check(L, 1); lua_createtable(L, 0, 2); // [retval] @@ -295,28 +186,38 @@ int tslua_inspect_lang(lua_State *L) lua_setfield(L, -2, "fields"); // [retval] - uint32_t lang_version = ts_language_version(lang); - lua_pushinteger(L, lang_version); // [retval, version] + lua_pushinteger(L, ts_language_version(lang)); // [retval, version] lua_setfield(L, -2, "_abi_version"); return 1; } +// TSParser + +static struct luaL_Reg parser_meta[] = { + { "__gc", parser_gc }, + { "__tostring", parser_tostring }, + { "parse", parser_parse }, + { "reset", parser_reset }, + { "set_included_ranges", parser_set_ranges }, + { "included_ranges", parser_get_ranges }, + { "set_timeout", parser_set_timeout }, + { "timeout", parser_get_timeout }, + { "_set_logger", parser_set_logger }, + { "_logger", parser_get_logger }, + { NULL, NULL } +}; + int tslua_push_parser(lua_State *L) { - // Gather language name - const char *lang_name = luaL_checkstring(L, 1); - - TSLanguage *lang = pmap_get(cstr_t)(&langs, lang_name); - if (!lang) { - return luaL_error(L, "no such language: %s", lang_name); - } + TSLanguage *lang = lang_check(L, 1); TSParser **parser = lua_newuserdata(L, sizeof(TSParser *)); *parser = ts_parser_new(); if (!ts_parser_set_language(*parser, lang)) { ts_parser_delete(*parser); + const char *lang_name = luaL_checkstring(L, 1); return luaL_error(L, "Failed to load language : %s", lang_name); } @@ -325,9 +226,11 @@ int tslua_push_parser(lua_State *L) return 1; } -static TSParser **parser_check(lua_State *L, uint16_t index) +static TSParser *parser_check(lua_State *L, uint16_t index) { - return luaL_checkudata(L, index, TS_META_PARSER); + TSParser **ud = luaL_checkudata(L, index, TS_META_PARSER); + luaL_argcheck(L, *ud, index, "TSParser expected"); + return *ud; } static void logger_gc(TSLogger logger) @@ -343,13 +246,9 @@ static void logger_gc(TSLogger logger) static int parser_gc(lua_State *L) { - TSParser **p = parser_check(L, 1); - if (!p) { - return 0; - } - - logger_gc(ts_parser_logger(*p)); - ts_parser_delete(*p); + TSParser *p = parser_check(L, 1); + logger_gc(ts_parser_logger(p)); + ts_parser_delete(p); return 0; } @@ -371,7 +270,7 @@ static const char *input_cb(void *payload, uint32_t byte_index, TSPoint position return ""; } char *line = ml_get_buf(bp, (linenr_T)position.row + 1); - size_t len = strlen(line); + size_t len = (size_t)ml_get_buf_len(bp, (linenr_T)position.row + 1); if (position.column > len) { *bytes_read = 0; return ""; @@ -422,14 +321,10 @@ static void push_ranges(lua_State *L, const TSRange *ranges, const size_t length static int parser_parse(lua_State *L) { - TSParser **p = parser_check(L, 1); - if (!p || !(*p)) { - return 0; - } - + TSParser *p = parser_check(L, 1); TSTree *old_tree = NULL; if (!lua_isnil(L, 2)) { - TSLuaTree *ud = tree_check(L, 2); + TSLuaTree *ud = luaL_checkudata(L, 2, TS_META_TREE); old_tree = ud ? ud->tree : NULL; } @@ -445,7 +340,7 @@ static int parser_parse(lua_State *L) switch (lua_type(L, 3)) { case LUA_TSTRING: str = lua_tolstring(L, 3, &len); - new_tree = ts_parser_parse_string(*p, old_tree, str, (uint32_t)len); + new_tree = ts_parser_parse_string(p, old_tree, str, (uint32_t)len); break; case LUA_TNUMBER: @@ -461,7 +356,7 @@ static int parser_parse(lua_State *L) } input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8 }; - new_tree = ts_parser_parse(*p, old_tree, input); + new_tree = ts_parser_parse(p, old_tree, input); break; @@ -492,70 +387,14 @@ static int parser_parse(lua_State *L) static int parser_reset(lua_State *L) { - TSParser **p = parser_check(L, 1); - if (p && *p) { - ts_parser_reset(*p); - } - + TSParser *p = parser_check(L, 1); + ts_parser_reset(p); return 0; } -static int tree_copy(lua_State *L) +static void range_err(lua_State *L) { - TSLuaTree *ud = tree_check(L, 1); - if (!ud) { - return 0; - } - - TSTree *copy = ts_tree_copy(ud->tree); - push_tree(L, copy); // [tree] - - return 1; -} - -static int tree_edit(lua_State *L) -{ - if (lua_gettop(L) < 10) { - lua_pushstring(L, "not enough args to tree:edit()"); - return lua_error(L); - } - - TSLuaTree *ud = tree_check(L, 1); - if (!ud) { - return 0; - } - - uint32_t start_byte = (uint32_t)luaL_checkint(L, 2); - uint32_t old_end_byte = (uint32_t)luaL_checkint(L, 3); - uint32_t new_end_byte = (uint32_t)luaL_checkint(L, 4); - TSPoint start_point = { (uint32_t)luaL_checkint(L, 5), (uint32_t)luaL_checkint(L, 6) }; - TSPoint old_end_point = { (uint32_t)luaL_checkint(L, 7), (uint32_t)luaL_checkint(L, 8) }; - TSPoint new_end_point = { (uint32_t)luaL_checkint(L, 9), (uint32_t)luaL_checkint(L, 10) }; - - TSInputEdit edit = { start_byte, old_end_byte, new_end_byte, - start_point, old_end_point, new_end_point }; - - ts_tree_edit(ud->tree, &edit); - - return 0; -} - -static int tree_get_ranges(lua_State *L) -{ - TSLuaTree *ud = tree_check(L, 1); - if (!ud) { - return 0; - } - - bool include_bytes = (lua_gettop(L) >= 2) && lua_toboolean(L, 2); - - uint32_t len; - TSRange *ranges = ts_tree_included_ranges(ud->tree, &len); - - push_ranges(L, ranges, len, include_bytes); - - xfree(ranges); - return 1; + luaL_error(L, "Ranges can only be made from 6 element long tables or nodes."); } // Use the top of the stack (without popping it) to create a TSRange, it can be @@ -567,7 +406,7 @@ static void range_from_lua(lua_State *L, TSRange *range) if (lua_istable(L, -1)) { // should be a table of 6 elements if (lua_objlen(L, -1) != 6) { - goto error; + range_err(L); } lua_rawgeti(L, -1, 1); // [ range, start_row] @@ -606,7 +445,7 @@ static void range_from_lua(lua_State *L, TSRange *range) .start_byte = start_byte, .end_byte = end_byte, }; - } else if (node_check(L, -1, &node)) { + } else if (node_check_opt(L, -1, &node)) { *range = (TSRange) { .start_point = ts_node_start_point(node), .end_point = ts_node_end_point(node), @@ -614,30 +453,19 @@ static void range_from_lua(lua_State *L, TSRange *range) .end_byte = ts_node_end_byte(node) }; } else { - goto error; + range_err(L); } - return; -error: - luaL_error(L, - "Ranges can only be made from 6 element long tables or nodes."); } static int parser_set_ranges(lua_State *L) { if (lua_gettop(L) < 2) { - return luaL_error(L, - "not enough args to parser:set_included_ranges()"); + return luaL_error(L, "not enough args to parser:set_included_ranges()"); } - TSParser **p = parser_check(L, 1); - if (!p) { - return 0; - } + TSParser *p = parser_check(L, 1); - if (!lua_istable(L, 2)) { - return luaL_error(L, - "argument for parser:set_included_ranges() should be a table."); - } + luaL_argcheck(L, lua_istable(L, 2), 2, "table expected."); size_t tbl_len = lua_objlen(L, 2); TSRange *ranges = xmalloc(sizeof(TSRange) * tbl_len); @@ -650,7 +478,7 @@ static int parser_set_ranges(lua_State *L) } // This memcpies ranges, thus we can free it afterwards - ts_parser_set_included_ranges(*p, ranges, (uint32_t)tbl_len); + ts_parser_set_included_ranges(p, ranges, (uint32_t)tbl_len); xfree(ranges); return 0; @@ -658,15 +486,12 @@ static int parser_set_ranges(lua_State *L) static int parser_get_ranges(lua_State *L) { - TSParser **p = parser_check(L, 1); - if (!p) { - return 0; - } + TSParser *p = parser_check(L, 1); bool include_bytes = (lua_gettop(L) >= 2) && lua_toboolean(L, 2); uint32_t len; - const TSRange *ranges = ts_parser_included_ranges(*p, &len); + const TSRange *ranges = ts_parser_included_ranges(p, &len); push_ranges(L, ranges, len, include_bytes); return 1; @@ -674,28 +499,21 @@ static int parser_get_ranges(lua_State *L) static int parser_set_timeout(lua_State *L) { - TSParser **p = parser_check(L, 1); - if (!p) { - return 0; - } + TSParser *p = parser_check(L, 1); if (lua_gettop(L) < 2) { luaL_error(L, "integer expected"); } uint32_t timeout = (uint32_t)luaL_checkinteger(L, 2); - ts_parser_set_timeout_micros(*p, timeout); + ts_parser_set_timeout_micros(p, timeout); return 0; } static int parser_get_timeout(lua_State *L) { - TSParser **p = parser_check(L, 1); - if (!p) { - return 0; - } - - lua_pushinteger(L, (lua_Integer)ts_parser_timeout_micros(*p)); + TSParser *p = parser_check(L, 1); + lua_pushinteger(L, (lua_Integer)ts_parser_timeout_micros(p)); return 1; } @@ -719,22 +537,11 @@ static void logger_cb(void *payload, TSLogType logtype, const char *s) static int parser_set_logger(lua_State *L) { - TSParser **p = parser_check(L, 1); - if (!p) { - return 0; - } + TSParser *p = parser_check(L, 1); - if (!lua_isboolean(L, 2)) { - return luaL_argerror(L, 2, "boolean expected"); - } - - if (!lua_isboolean(L, 3)) { - return luaL_argerror(L, 3, "boolean expected"); - } - - if (!lua_isfunction(L, 4)) { - return luaL_argerror(L, 4, "function expected"); - } + luaL_argcheck(L, lua_isboolean(L, 2), 2, "boolean expected"); + luaL_argcheck(L, lua_isboolean(L, 3), 3, "boolean expected"); + luaL_argcheck(L, lua_isfunction(L, 4), 4, "function expected"); TSLuaLoggerOpts *opts = xmalloc(sizeof(TSLuaLoggerOpts)); lua_pushvalue(L, 4); @@ -752,18 +559,14 @@ static int parser_set_logger(lua_State *L) .log = logger_cb }; - ts_parser_set_logger(*p, logger); + ts_parser_set_logger(p, logger); return 0; } static int parser_get_logger(lua_State *L) { - TSParser **p = parser_check(L, 1); - if (!p) { - return 0; - } - - TSLogger logger = ts_parser_logger(*p); + TSParser *p = parser_check(L, 1); + TSLogger logger = ts_parser_logger(p); if (logger.log) { TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload; lua_rawgeti(L, LUA_REGISTRYINDEX, opts->cb); @@ -774,7 +577,17 @@ static int parser_get_logger(lua_State *L) return 1; } -// Tree methods +// TSTree + +static struct luaL_Reg tree_meta[] = { + { "__gc", tree_gc }, + { "__tostring", tree_tostring }, + { "root", tree_root }, + { "edit", tree_edit }, + { "included_ranges", tree_get_ranges }, + { "copy", tree_copy }, + { NULL, NULL } +}; /// Push tree interface on to the lua stack. /// @@ -804,18 +617,58 @@ static void push_tree(lua_State *L, TSTree *tree) lua_setfenv(L, -2); // [udata] } -static TSLuaTree *tree_check(lua_State *L, int index) +static int tree_copy(lua_State *L) { - TSLuaTree *ud = luaL_checkudata(L, index, TS_META_TREE); - return ud; + TSLuaTree *ud = luaL_checkudata(L, 1, TS_META_TREE); + TSTree *copy = ts_tree_copy(ud->tree); + push_tree(L, copy); // [tree] + + return 1; } -static int tree_gc(lua_State *L) +static int tree_edit(lua_State *L) { - TSLuaTree *ud = tree_check(L, 1); - if (ud) { - ts_tree_delete(ud->tree); + if (lua_gettop(L) < 10) { + lua_pushstring(L, "not enough args to tree:edit()"); + return lua_error(L); } + + TSLuaTree *ud = luaL_checkudata(L, 1, TS_META_TREE); + + uint32_t start_byte = (uint32_t)luaL_checkint(L, 2); + uint32_t old_end_byte = (uint32_t)luaL_checkint(L, 3); + uint32_t new_end_byte = (uint32_t)luaL_checkint(L, 4); + TSPoint start_point = { (uint32_t)luaL_checkint(L, 5), (uint32_t)luaL_checkint(L, 6) }; + TSPoint old_end_point = { (uint32_t)luaL_checkint(L, 7), (uint32_t)luaL_checkint(L, 8) }; + TSPoint new_end_point = { (uint32_t)luaL_checkint(L, 9), (uint32_t)luaL_checkint(L, 10) }; + + TSInputEdit edit = { start_byte, old_end_byte, new_end_byte, + start_point, old_end_point, new_end_point }; + + ts_tree_edit(ud->tree, &edit); + + return 0; +} + +static int tree_get_ranges(lua_State *L) +{ + TSLuaTree *ud = luaL_checkudata(L, 1, TS_META_TREE); + + bool include_bytes = (lua_gettop(L) >= 2) && lua_toboolean(L, 2); + + uint32_t len; + TSRange *ranges = ts_tree_included_ranges(ud->tree, &len); + + push_ranges(L, ranges, len, include_bytes); + + xfree(ranges); + return 1; +} + +static int tree_gc(lua_State *L) +{ + TSLuaTree *ud = luaL_checkudata(L, 1, TS_META_TREE); + ts_tree_delete(ud->tree); return 0; } @@ -827,16 +680,66 @@ static int tree_tostring(lua_State *L) static int tree_root(lua_State *L) { - TSLuaTree *ud = tree_check(L, 1); - if (!ud) { - return 0; - } + TSLuaTree *ud = luaL_checkudata(L, 1, TS_META_TREE); TSNode root = ts_tree_root_node(ud->tree); push_node(L, root, 1); return 1; } -// Node methods +// TSTreeCursor + +static struct luaL_Reg treecursor_meta[] = { + { "__gc", treecursor_gc }, + { NULL, NULL } +}; + +static int treecursor_gc(lua_State *L) +{ + TSTreeCursor *cursor = luaL_checkudata(L, 1, TS_META_TREECURSOR); + ts_tree_cursor_delete(cursor); + return 0; +} + +// TSNode +static struct luaL_Reg node_meta[] = { + { "__tostring", node_tostring }, + { "__eq", node_eq }, + { "__len", node_child_count }, + { "id", node_id }, + { "range", node_range }, + { "start", node_start }, + { "end_", node_end }, + { "type", node_type }, + { "symbol", node_symbol }, + { "field", node_field }, + { "named", node_named }, + { "missing", node_missing }, + { "extra", node_extra }, + { "has_changes", node_has_changes }, + { "has_error", node_has_error }, + { "sexpr", node_sexpr }, + { "child_count", node_child_count }, + { "named_child_count", node_named_child_count }, + { "child", node_child }, + { "named_child", node_named_child }, + { "descendant_for_range", node_descendant_for_range }, + { "named_descendant_for_range", node_named_descendant_for_range }, + { "parent", node_parent }, + { "__has_ancestor", __has_ancestor }, + { "child_containing_descendant", node_child_containing_descendant }, + { "iter_children", node_iter_children }, + { "next_sibling", node_next_sibling }, + { "prev_sibling", node_prev_sibling }, + { "next_named_sibling", node_next_named_sibling }, + { "prev_named_sibling", node_prev_named_sibling }, + { "named_children", node_named_children }, + { "root", node_root }, + { "tree", node_tree }, + { "byte_length", node_byte_length }, + { "equal", node_equal }, + + { NULL, NULL } +}; /// Push node interface on to the Lua stack /// @@ -860,7 +763,7 @@ static void push_node(lua_State *L, TSNode node, int uindex) lua_setfenv(L, -2); // [udata] } -static bool node_check(lua_State *L, int index, TSNode *res) +static bool node_check_opt(lua_State *L, int index, TSNode *res) { TSNode *ud = luaL_checkudata(L, index, TS_META_NODE); if (ud) { @@ -870,12 +773,15 @@ static bool node_check(lua_State *L, int index, TSNode *res) return false; } +static TSNode node_check(lua_State *L, int index) +{ + TSNode *ud = luaL_checkudata(L, index, TS_META_NODE); + return *ud; +} + static int node_tostring(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); lua_pushstring(L, "<node "); lua_pushstring(L, ts_node_type(node)); lua_pushstring(L, ">"); @@ -885,37 +791,22 @@ static int node_tostring(lua_State *L) static int node_eq(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } - - TSNode node2; - if (!node_check(L, 2, &node2)) { - return 0; - } - + TSNode node = node_check(L, 1); + TSNode node2 = node_check(L, 2); lua_pushboolean(L, ts_node_eq(node, node2)); return 1; } static int node_id(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } - + TSNode node = node_check(L, 1); lua_pushlstring(L, (const char *)&node.id, sizeof node.id); return 1; } static int node_range(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); bool include_bytes = (lua_gettop(L) >= 2) && lua_toboolean(L, 2); @@ -941,10 +832,7 @@ static int node_range(lua_State *L) static int node_start(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); TSPoint start = ts_node_start_point(node); uint32_t start_byte = ts_node_start_byte(node); lua_pushinteger(L, start.row); @@ -955,10 +843,7 @@ static int node_start(lua_State *L) static int node_end(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); TSPoint end = ts_node_end_point(node); uint32_t end_byte = ts_node_end_byte(node); lua_pushinteger(L, end.row); @@ -969,10 +854,7 @@ static int node_end(lua_State *L) static int node_child_count(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); uint32_t count = ts_node_child_count(node); lua_pushinteger(L, count); return 1; @@ -980,10 +862,7 @@ static int node_child_count(lua_State *L) static int node_named_child_count(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); uint32_t count = ts_node_named_child_count(node); lua_pushinteger(L, count); return 1; @@ -991,20 +870,14 @@ static int node_named_child_count(lua_State *L) static int node_type(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); lua_pushstring(L, ts_node_type(node)); return 1; } static int node_symbol(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); TSSymbol symbol = ts_node_symbol(node); lua_pushinteger(L, symbol); return 1; @@ -1012,10 +885,7 @@ static int node_symbol(lua_State *L) static int node_field(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); size_t name_len; const char *field_name = luaL_checklstring(L, 2, &name_len); @@ -1042,20 +912,14 @@ static int node_field(lua_State *L) static int node_named(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); lua_pushboolean(L, ts_node_is_named(node)); return 1; } static int node_sexpr(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); char *allocated = ts_node_string(node); lua_pushstring(L, allocated); xfree(allocated); @@ -1064,50 +928,35 @@ static int node_sexpr(lua_State *L) static int node_missing(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); lua_pushboolean(L, ts_node_is_missing(node)); return 1; } static int node_extra(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); lua_pushboolean(L, ts_node_is_extra(node)); return 1; } static int node_has_changes(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); lua_pushboolean(L, ts_node_has_changes(node)); return 1; } static int node_has_error(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); lua_pushboolean(L, ts_node_has_error(node)); return 1; } static int node_child(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); uint32_t num = (uint32_t)lua_tointeger(L, 2); TSNode child = ts_node_child(node, num); @@ -1117,10 +966,7 @@ static int node_child(lua_State *L) static int node_named_child(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); uint32_t num = (uint32_t)lua_tointeger(L, 2); TSNode child = ts_node_named_child(node, num); @@ -1130,10 +976,7 @@ static int node_named_child(lua_State *L) static int node_descendant_for_range(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); TSPoint start = { (uint32_t)lua_tointeger(L, 2), (uint32_t)lua_tointeger(L, 3) }; TSPoint end = { (uint32_t)lua_tointeger(L, 4), @@ -1146,10 +989,7 @@ static int node_descendant_for_range(lua_State *L) static int node_named_descendant_for_range(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); TSPoint start = { (uint32_t)lua_tointeger(L, 2), (uint32_t)lua_tointeger(L, 3) }; TSPoint end = { (uint32_t)lua_tointeger(L, 4), @@ -1162,54 +1002,41 @@ static int node_named_descendant_for_range(lua_State *L) static int node_next_child(lua_State *L) { - TSTreeCursor *ud = luaL_checkudata(L, lua_upvalueindex(1), TS_META_TREECURSOR); - if (!ud) { - return 0; - } - - TSNode source; - if (!node_check(L, lua_upvalueindex(2), &source)) { - return 0; - } + TSTreeCursor *cursor = luaL_checkudata(L, lua_upvalueindex(1), TS_META_TREECURSOR); + TSNode source = node_check(L, lua_upvalueindex(2)); // First call should return first child - if (ts_node_eq(source, ts_tree_cursor_current_node(ud))) { - if (ts_tree_cursor_goto_first_child(ud)) { + if (ts_node_eq(source, ts_tree_cursor_current_node(cursor))) { + if (ts_tree_cursor_goto_first_child(cursor)) { goto push; } else { - goto end; + return 0; } } - if (ts_tree_cursor_goto_next_sibling(ud)) { -push: - push_node(L, - ts_tree_cursor_current_node(ud), - lua_upvalueindex(2)); // [node] + if (!ts_tree_cursor_goto_next_sibling(cursor)) { + return 0; + } - const char *field = ts_tree_cursor_current_field_name(ud); +push: + push_node(L, ts_tree_cursor_current_node(cursor), lua_upvalueindex(2)); // [node] - if (field != NULL) { - lua_pushstring(L, ts_tree_cursor_current_field_name(ud)); - } else { - lua_pushnil(L); - } // [node, field_name_or_nil] - return 2; - } + const char *field = ts_tree_cursor_current_field_name(cursor); -end: - return 0; + if (field != NULL) { + lua_pushstring(L, ts_tree_cursor_current_field_name(cursor)); + } else { + lua_pushnil(L); + } // [node, field_name_or_nil] + return 2; } static int node_iter_children(lua_State *L) { - TSNode source; - if (!node_check(L, 1, &source)) { - return 0; - } + TSNode node = node_check(L, 1); TSTreeCursor *ud = lua_newuserdata(L, sizeof(TSTreeCursor)); // [udata] - *ud = ts_tree_cursor_new(source); + *ud = ts_tree_cursor_new(node); lua_getfield(L, LUA_REGISTRYINDEX, TS_META_TREECURSOR); // [udata, mt] lua_setmetatable(L, -2); // [udata] @@ -1219,30 +1046,60 @@ static int node_iter_children(lua_State *L) return 1; } -static int treecursor_gc(lua_State *L) +static int node_parent(lua_State *L) { - TSTreeCursor *ud = luaL_checkudata(L, 1, TS_META_TREECURSOR); - ts_tree_cursor_delete(ud); - return 0; + TSNode node = node_check(L, 1); + TSNode parent = ts_node_parent(node); + push_node(L, parent, 1); + return 1; } -static int node_parent(lua_State *L) +static int __has_ancestor(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; + TSNode descendant = node_check(L, 1); + if (lua_type(L, 2) != LUA_TTABLE) { + lua_pushboolean(L, false); + return 1; } - TSNode parent = ts_node_parent(node); - push_node(L, parent, 1); + int const pred_len = (int)lua_objlen(L, 2); + + TSNode node = ts_tree_root_node(descendant.tree); + while (!ts_node_is_null(node)) { + char const *node_type = ts_node_type(node); + size_t node_type_len = strlen(node_type); + + for (int i = 3; i <= pred_len; i++) { + lua_rawgeti(L, 2, i); + if (lua_type(L, -1) == LUA_TSTRING) { + size_t check_len; + char const *check_str = lua_tolstring(L, -1, &check_len); + if (node_type_len == check_len && memcmp(node_type, check_str, check_len) == 0) { + lua_pushboolean(L, true); + return 1; + } + } + lua_pop(L, 1); + } + + node = ts_node_child_containing_descendant(node, descendant); + } + + lua_pushboolean(L, false); + return 1; +} + +static int node_child_containing_descendant(lua_State *L) +{ + TSNode node = node_check(L, 1); + TSNode descendant = node_check(L, 2); + TSNode child = ts_node_child_containing_descendant(node, descendant); + push_node(L, child, 1); return 1; } static int node_next_sibling(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); TSNode sibling = ts_node_next_sibling(node); push_node(L, sibling, 1); return 1; @@ -1250,10 +1107,7 @@ static int node_next_sibling(lua_State *L) static int node_prev_sibling(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); TSNode sibling = ts_node_prev_sibling(node); push_node(L, sibling, 1); return 1; @@ -1261,10 +1115,7 @@ static int node_prev_sibling(lua_State *L) static int node_next_named_sibling(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); TSNode sibling = ts_node_next_named_sibling(node); push_node(L, sibling, 1); return 1; @@ -1272,10 +1123,7 @@ static int node_next_named_sibling(lua_State *L) static int node_prev_named_sibling(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } + TSNode node = node_check(L, 1); TSNode sibling = ts_node_prev_named_sibling(node); push_node(L, sibling, 1); return 1; @@ -1283,10 +1131,7 @@ static int node_prev_named_sibling(lua_State *L) static int node_named_children(lua_State *L) { - TSNode source; - if (!node_check(L, 1, &source)) { - return 0; - } + TSNode source = node_check(L, 1); TSTreeCursor cursor = ts_tree_cursor_new(source); lua_newtable(L); @@ -1308,11 +1153,7 @@ static int node_named_children(lua_State *L) static int node_root(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } - + TSNode node = node_check(L, 1); TSNode root = ts_tree_root_node(node.tree); push_node(L, root, 1); return 1; @@ -1320,215 +1161,196 @@ static int node_root(lua_State *L) static int node_tree(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } - + node_check(L, 1); lua_getfenv(L, 1); // [udata, reftable] lua_rawgeti(L, -1, 1); // [udata, reftable, tree_udata] - return 1; } static int node_byte_length(lua_State *L) { - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } - + TSNode node = node_check(L, 1); uint32_t start_byte = ts_node_start_byte(node); uint32_t end_byte = ts_node_end_byte(node); - lua_pushinteger(L, end_byte - start_byte); return 1; } static int node_equal(lua_State *L) { - TSNode node1; - if (!node_check(L, 1, &node1)) { - return 0; - } - - TSNode node2; - if (!node_check(L, 2, &node2)) { - return luaL_error(L, "TSNode expected"); - } - + TSNode node1 = node_check(L, 1); + TSNode node2 = node_check(L, 2); lua_pushboolean(L, ts_node_eq(node1, node2)); return 1; } -/// assumes the match table being on top of the stack -static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx) -{ - // [match] - for (size_t i = 0; i < match->capture_count; i++) { - lua_rawgeti(L, -1, (int)match->captures[i].index + 1); // [match, captures] - if (lua_isnil(L, -1)) { // [match, nil] - lua_pop(L, 1); // [match] - lua_createtable(L, 1, 0); // [match, captures] - } - push_node(L, match->captures[i].node, nodeidx); // [match, captures, node] - lua_rawseti(L, -2, (int)lua_objlen(L, -2) + 1); // [match, captures] - lua_rawseti(L, -2, (int)match->captures[i].index + 1); // [match] - } -} +// TSQueryCursor -static int query_next_match(lua_State *L) -{ - TSLua_cursor *ud = lua_touserdata(L, lua_upvalueindex(1)); - TSQueryCursor *cursor = ud->cursor; - - TSQuery *query = query_check(L, lua_upvalueindex(3)); - TSQueryMatch match; - if (ts_query_cursor_next_match(cursor, &match)) { - lua_pushinteger(L, match.pattern_index + 1); // [index] - lua_createtable(L, (int)ts_query_capture_count(query), 0); // [index, match] - set_match(L, &match, lua_upvalueindex(2)); - return 2; - } - return 0; -} +static struct luaL_Reg querycursor_meta[] = { + { "remove_match", querycursor_remove_match }, + { "next_capture", querycursor_next_capture }, + { "next_match", querycursor_next_match }, + { "__gc", querycursor_gc }, + { NULL, NULL } +}; -static int query_next_capture(lua_State *L) +int tslua_push_querycursor(lua_State *L) { - // Upvalues are: - // [ cursor, node, query, current_match ] - TSLua_cursor *ud = lua_touserdata(L, lua_upvalueindex(1)); - TSQueryCursor *cursor = ud->cursor; - - TSQuery *query = query_check(L, lua_upvalueindex(3)); - - if (ud->predicated_match > -1) { - lua_getfield(L, lua_upvalueindex(4), "active"); - bool active = lua_toboolean(L, -1); - lua_pop(L, 1); - if (!active) { - ts_query_cursor_remove_match(cursor, (uint32_t)ud->predicated_match); - } - ud->predicated_match = -1; - } + TSNode node = node_check(L, 1); - TSQueryMatch match; - uint32_t capture_index; - if (ts_query_cursor_next_capture(cursor, &match, &capture_index)) { - TSQueryCapture capture = match.captures[capture_index]; - - // TODO(vigoux): handle capture quantifiers here - lua_pushinteger(L, capture.index + 1); // [index] - push_node(L, capture.node, lua_upvalueindex(2)); // [index, node] - - // Now check if we need to run the predicates - uint32_t n_pred; - ts_query_predicates_for_pattern(query, match.pattern_index, &n_pred); - - if (n_pred > 0 && (ud->max_match_id < (int)match.id)) { - ud->max_match_id = (int)match.id; - - // Create a new cleared match table - lua_createtable(L, (int)ts_query_capture_count(query), 2); // [index, node, match] - set_match(L, &match, lua_upvalueindex(2)); - lua_pushinteger(L, match.pattern_index + 1); - lua_setfield(L, -2, "pattern"); - - if (match.capture_count > 1) { - ud->predicated_match = (int)match.id; - lua_pushboolean(L, false); - lua_setfield(L, -2, "active"); - } - - // Set current_match to the new match - lua_replace(L, lua_upvalueindex(4)); // [index, node] - lua_pushvalue(L, lua_upvalueindex(4)); // [index, node, match] - return 3; - } - return 2; - } - return 0; -} - -static int node_rawquery(lua_State *L) -{ - TSNode node; - if (!node_check(L, 1, &node)) { - return 0; - } TSQuery *query = query_check(L, 2); - - TSQueryCursor *cursor; - if (kv_size(cursors) > 0) { - cursor = kv_pop(cursors); - } else { - cursor = ts_query_cursor_new(); - } - - ts_query_cursor_set_max_start_depth(cursor, UINT32_MAX); - ts_query_cursor_set_match_limit(cursor, 256); + TSQueryCursor *cursor = ts_query_cursor_new(); ts_query_cursor_exec(cursor, query, node); - bool captures = lua_toboolean(L, 3); - - if (lua_gettop(L) >= 4) { - uint32_t start = (uint32_t)luaL_checkinteger(L, 4); - uint32_t end = lua_gettop(L) >= 5 ? (uint32_t)luaL_checkinteger(L, 5) : MAXLNUM; + if (lua_gettop(L) >= 3) { + uint32_t start = (uint32_t)luaL_checkinteger(L, 3); + uint32_t end = lua_gettop(L) >= 4 ? (uint32_t)luaL_checkinteger(L, 4) : MAXLNUM; ts_query_cursor_set_point_range(cursor, (TSPoint){ start, 0 }, (TSPoint){ end, 0 }); } - if (lua_gettop(L) >= 6 && !lua_isnil(L, 6)) { - if (!lua_istable(L, 6)) { - return luaL_error(L, "table expected"); - } - lua_pushnil(L); - // stack: [dict, ..., nil] - while (lua_next(L, 6)) { - // stack: [dict, ..., key, value] + if (lua_gettop(L) >= 5 && !lua_isnil(L, 5)) { + luaL_argcheck(L, lua_istable(L, 5), 5, "table expected"); + lua_pushnil(L); // [dict, ..., nil] + while (lua_next(L, 5)) { + // [dict, ..., key, value] if (lua_type(L, -2) == LUA_TSTRING) { char *k = (char *)lua_tostring(L, -2); if (strequal("max_start_depth", k)) { uint32_t max_start_depth = (uint32_t)lua_tointeger(L, -1); ts_query_cursor_set_max_start_depth(cursor, max_start_depth); + } else if (strequal("match_limit", k)) { + uint32_t match_limit = (uint32_t)lua_tointeger(L, -1); + ts_query_cursor_set_match_limit(cursor, match_limit); } } - lua_pop(L, 1); // pop the value; lua_next will pop the key. - // stack: [dict, ..., key] + // pop the value; lua_next will pop the key. + lua_pop(L, 1); // [dict, ..., key] } } - TSLua_cursor *ud = lua_newuserdata(L, sizeof(*ud)); // [udata] - ud->cursor = cursor; - ud->predicated_match = -1; - ud->max_match_id = -1; + TSQueryCursor **ud = lua_newuserdata(L, sizeof(*ud)); // [node, query, ..., udata] + *ud = cursor; + lua_getfield(L, LUA_REGISTRYINDEX, TS_META_QUERYCURSOR); // [node, query, ..., udata, meta] + lua_setmetatable(L, -2); // [node, query, ..., udata] - lua_getfield(L, LUA_REGISTRYINDEX, TS_META_QUERYCURSOR); - lua_setmetatable(L, -2); // [udata] - lua_pushvalue(L, 1); // [udata, node] + // Copy the fenv which contains the nodes tree. + lua_getfenv(L, 1); // [udata, reftable] + lua_setfenv(L, -2); // [udata] - // include query separately, as to keep a ref to it for gc - lua_pushvalue(L, 2); // [udata, node, query] + return 1; +} - if (captures) { - // placeholder for match state - lua_createtable(L, (int)ts_query_capture_count(query), 2); // [u, n, q, match] - lua_pushcclosure(L, query_next_capture, 4); // [closure] - } else { - lua_pushcclosure(L, query_next_match, 3); // [closure] +static int querycursor_remove_match(lua_State *L) +{ + TSQueryCursor *cursor = querycursor_check(L, 1); + uint32_t match_id = (uint32_t)luaL_checkinteger(L, 2); + ts_query_cursor_remove_match(cursor, match_id); + return 0; +} + +static int querycursor_next_capture(lua_State *L) +{ + TSQueryCursor *cursor = querycursor_check(L, 1); + TSQueryMatch match; + uint32_t capture_index; + if (!ts_query_cursor_next_capture(cursor, &match, &capture_index)) { + return 0; + } + + TSQueryCapture capture = match.captures[capture_index]; + + // Handle capture quantifiers here + lua_pushinteger(L, capture.index + 1); // [index] + push_node(L, capture.node, 1); // [index, node] + push_querymatch(L, &match, 1); + + return 3; +} + +static int querycursor_next_match(lua_State *L) +{ + TSQueryCursor *cursor = querycursor_check(L, 1); + + TSQueryMatch match; + if (!ts_query_cursor_next_match(cursor, &match)) { + return 0; } + push_querymatch(L, &match, 1); + return 1; } +static TSQueryCursor *querycursor_check(lua_State *L, int index) +{ + TSQueryCursor **ud = luaL_checkudata(L, index, TS_META_QUERYCURSOR); + luaL_argcheck(L, *ud, index, "TSQueryCursor expected"); + return *ud; +} + static int querycursor_gc(lua_State *L) { - TSLua_cursor *ud = luaL_checkudata(L, 1, TS_META_QUERYCURSOR); - kv_push(cursors, ud->cursor); - ud->cursor = NULL; + TSQueryCursor *cursor = querycursor_check(L, 1); + ts_query_cursor_delete(cursor); return 0; } -// Query methods +// TSQueryMatch + +static struct luaL_Reg querymatch_meta[] = { + { "info", querymatch_info }, + { "captures", querymatch_captures }, + { NULL, NULL } +}; + +static void push_querymatch(lua_State *L, TSQueryMatch *match, int uindex) +{ + TSQueryMatch *ud = lua_newuserdata(L, sizeof(TSQueryMatch)); // [udata] + *ud = *match; + lua_getfield(L, LUA_REGISTRYINDEX, TS_META_QUERYMATCH); // [udata, meta] + lua_setmetatable(L, -2); // [udata] + + // Copy the fenv which contains the nodes tree. + lua_getfenv(L, uindex); // [udata, reftable] + lua_setfenv(L, -2); // [udata] +} + +static int querymatch_info(lua_State *L) +{ + TSQueryMatch *match = luaL_checkudata(L, 1, TS_META_QUERYMATCH); + lua_pushinteger(L, match->id); + lua_pushinteger(L, match->pattern_index + 1); + return 2; +} + +static int querymatch_captures(lua_State *L) +{ + TSQueryMatch *match = luaL_checkudata(L, 1, TS_META_QUERYMATCH); + lua_newtable(L); // [match, nodes, captures] + for (size_t i = 0; i < match->capture_count; i++) { + TSQueryCapture capture = match->captures[i]; + int index = (int)capture.index + 1; + + lua_rawgeti(L, -1, index); // [match, node, captures] + if (lua_isnil(L, -1)) { // [match, node, captures, nil] + lua_pop(L, 1); // [match, node, captures] + lua_newtable(L); // [match, node, captures, nodes] + } + push_node(L, capture.node, 1); // [match, node, captures, nodes, node] + lua_rawseti(L, -2, (int)lua_objlen(L, -2) + 1); // [match, node, captures, nodes] + lua_rawseti(L, -2, index); // [match, node, captures] + } + return 1; +} + +// TSQuery + +static struct luaL_Reg query_meta[] = { + { "__gc", query_gc }, + { "__tostring", query_tostring }, + { "inspect", query_inspect }, + { NULL, NULL } +}; int tslua_parse_query(lua_State *L) { @@ -1536,15 +1358,12 @@ int tslua_parse_query(lua_State *L) return luaL_error(L, "string expected"); } - const char *lang_name = lua_tostring(L, 1); - TSLanguage *lang = pmap_get(cstr_t)(&langs, lang_name); - if (!lang) { - return luaL_error(L, "no such language: %s", lang_name); - } + TSLanguage *lang = lang_check(L, 1); size_t len; const char *src = lua_tolstring(L, 2, &len); + tslua_query_parse_count++; uint32_t error_offset; TSQueryError error_type; TSQuery *query = ts_query_new(lang, src, (uint32_t)len, &error_offset, &error_type); @@ -1638,16 +1457,13 @@ static void query_err_string(const char *src, int error_offset, TSQueryError err static TSQuery *query_check(lua_State *L, int index) { TSQuery **ud = luaL_checkudata(L, index, TS_META_QUERY); + luaL_argcheck(L, *ud, index, "TSQuery expected"); return *ud; } static int query_gc(lua_State *L) { TSQuery *query = query_check(L, 1); - if (!query) { - return 0; - } - ts_query_delete(query); return 0; } @@ -1661,9 +1477,6 @@ static int query_tostring(lua_State *L) static int query_inspect(lua_State *L) { TSQuery *query = query_check(L, 1); - if (!query) { - return 0; - } // TSQueryInfo lua_createtable(L, 0, 2); // [retval] @@ -1718,3 +1531,33 @@ static int query_inspect(lua_State *L) return 1; } + +// Library init + +static void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) +{ + if (luaL_newmetatable(L, tname)) { // [meta] + luaL_register(L, NULL, meta); + + lua_pushvalue(L, -1); // [meta, meta] + lua_setfield(L, -2, "__index"); // [meta] + } + lua_pop(L, 1); // [] (don't use it now) +} + +/// Init the tslua library. +/// +/// All global state is stored in the registry of the lua_State. +void tslua_init(lua_State *L) +{ + // type metatables + build_meta(L, TS_META_PARSER, parser_meta); + build_meta(L, TS_META_TREE, tree_meta); + build_meta(L, TS_META_NODE, node_meta); + build_meta(L, TS_META_QUERY, query_meta); + build_meta(L, TS_META_QUERYCURSOR, querycursor_meta); + build_meta(L, TS_META_QUERYMATCH, querymatch_meta); + build_meta(L, TS_META_TREECURSOR, treecursor_meta); + + ts_set_allocator(xmalloc, xcalloc, xrealloc, xfree); +} diff --git a/src/nvim/lua/treesitter.h b/src/nvim/lua/treesitter.h index 4ef9a10602..14df06e184 100644 --- a/src/nvim/lua/treesitter.h +++ b/src/nvim/lua/treesitter.h @@ -1,7 +1,12 @@ #pragma once #include <lua.h> // IWYU pragma: keep +#include <stdint.h> + +#include "nvim/macros_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/treesitter.h.generated.h" #endif + +EXTERN uint64_t tslua_query_parse_count INIT( = 0); diff --git a/src/nvim/main.c b/src/nvim/main.c index ea189aaa0c..30b6b6e86b 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -14,7 +14,9 @@ #include <string.h> #ifdef ENABLE_ASAN_UBSAN # include <sanitizer/asan_interface.h> -# include <sanitizer/ubsan_interface.h> +# ifndef MSWIN +# include <sanitizer/ubsan_interface.h> +# endif #endif #include "auto/config.h" // IWYU pragma: keep @@ -61,6 +63,7 @@ #include "nvim/log.h" #include "nvim/lua/executor.h" #include "nvim/lua/secure.h" +#include "nvim/lua/treesitter.h" #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/mark.h" @@ -353,6 +356,12 @@ int main(int argc, char **argv) ui_client_channel_id = rv; } + if (ui_client_channel_id) { + time_finish(); + ui_client_run(remote_ui); // NORETURN + } + assert(!ui_client_channel_id && !use_builtin_ui); + TIME_MSG("expanding arguments"); if (params.diff_mode && params.window_count == -1) { @@ -395,12 +404,6 @@ int main(int argc, char **argv) input_start(); } - if (ui_client_channel_id) { - time_finish(); - ui_client_run(remote_ui); // NORETURN - } - assert(!ui_client_channel_id && !use_builtin_ui); - // Wait for UIs to set up Nvim or show early messages // and prompts (--cmd, swapfile dialog, …). bool use_remote_ui = (embedded_mode && !headless_mode); @@ -639,7 +642,7 @@ int main(int argc, char **argv) // WORKAROUND(mhi): #3023 if (cb_flags & CB_UNNAMEDMASK) { - eval_has_provider("clipboard"); + eval_has_provider("clipboard", false); } if (params.luaf != NULL) { @@ -1573,7 +1576,7 @@ static void handle_quickfix(mparm_T *paramp) { if (paramp->edit_type == EDIT_QF) { if (paramp->use_ef != NULL) { - set_string_option_direct(kOptErrorfile, paramp->use_ef, 0, SID_CARG); + set_option_direct(kOptErrorfile, CSTR_AS_OPTVAL(paramp->use_ef), 0, SID_CARG); } vim_snprintf(IObuff, IOSIZE, "cfile %s", p_ef); if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) { diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 43a4c10ea7..9320390d68 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -347,7 +347,7 @@ static void set_maparg_rhs(const char *const orig_rhs, const size_t orig_rhs_len if (rhs_lua == LUA_NOREF) { mapargs->orig_rhs_len = orig_rhs_len; mapargs->orig_rhs = xcalloc(mapargs->orig_rhs_len + 1, sizeof(char)); - xstrlcpy(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len + 1); + xmemcpyz(mapargs->orig_rhs, orig_rhs, mapargs->orig_rhs_len); if (STRICMP(orig_rhs, "<nop>") == 0) { // "<Nop>" means nothing mapargs->rhs = xcalloc(1, sizeof(char)); // single NUL-char mapargs->rhs_len = 0; @@ -477,7 +477,7 @@ static int str_to_mapargs(const char *strargs, bool is_unmap, MapArguments *mapa return 1; } char lhs_to_replace[256]; - xstrlcpy(lhs_to_replace, to_parse, orig_lhs_len + 1); + xmemcpyz(lhs_to_replace, to_parse, orig_lhs_len); size_t orig_rhs_len = strlen(rhs_start); if (!set_maparg_lhs_rhs(lhs_to_replace, orig_lhs_len, @@ -1812,8 +1812,7 @@ int makemap(FILE *fd, buf_T *buf) iemsg(_("E228: makemap: Illegal mode")); return FAIL; } - do { - // do this twice if c2 is set, 3 times with c3 */ + do { // do this twice if c2 is set, 3 times with c3 // When outputting <> form, need to make sure that 'cpo' // is set to the Vim default. if (!did_cpo) { diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 34e35a8277..6ce42bb7fe 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -588,7 +588,7 @@ MarkMoveRes mark_move_to(fmark_T *fm, MarkMove flags) } if (res & kMarkSwitchedBuf || res & kMarkChangedCursor) { - check_cursor(); + check_cursor(curwin); } end: return res; @@ -1243,11 +1243,11 @@ void mark_adjust_buf(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount if (win != curwin || by_api) { if (win->w_topline >= line1 && win->w_topline <= line2) { if (amount == MAXLNUM) { // topline is deleted - if (line1 <= 1) { - win->w_topline = 1; + if (by_api && amount_after > line1 - line2 - 1) { + // api: if the deleted region was replaced with new contents, topline will + // get adjusted later as an effect of the adjusted cursor in fix_cursor() } else { - // api: if the deleted region was replaced with new contents, display that - win->w_topline = (by_api && amount_after > line1 - line2 - 1) ? line1 : line1 - 1; + win->w_topline = MAX(line1 - 1, 1); } } else if (win->w_topline > line1) { // keep topline on the same line, unless inserting just @@ -1715,7 +1715,7 @@ void mark_mb_adjustpos(buf_T *buf, pos_T *lp) { if (lp->col > 0 || lp->coladd > 1) { const char *const p = ml_get_buf(buf, lp->lnum); - if (*p == NUL || (int)strlen(p) < lp->col) { + if (*p == NUL || ml_get_buf_len(buf, lp->lnum) < lp->col) { lp->col = 0; } else { lp->col -= utf_head_off(p, p + lp->col); diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 0ebebf409e..34d6cd118f 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -460,7 +460,7 @@ static void meta_describe_key(uint32_t *meta_inc, MTKey k) meta_describe_key_inc(meta_inc, &k); } -// if x is internal, asumes x->meta[..] of children are correct +// if x is internal, assumes x->meta[..] of children are correct static void meta_describe_node(uint32_t *meta_node, MTNode *x) { memset(meta_node, 0, kMTMetaCount * sizeof(meta_node[0])); @@ -1425,7 +1425,7 @@ bool marktree_itr_get_ext(MarkTree *b, MTPos p, MarkTreeIter *itr, bool last, bo } if (meta_filter) { if (!meta_has(itr->x->meta[itr->i], meta_filter)) { - // this takes us to the interal position after the first rejected node + // this takes us to the internal position after the first rejected node break; } } diff --git a/src/nvim/match.c b/src/nvim/match.c index c8837969b6..580d7d1069 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -100,7 +100,7 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in // Build new match. matchitem_T *m = xcalloc(1, sizeof(matchitem_T)); - if (pos_list != NULL) { + if (tv_list_len(pos_list) > 0) { m->mit_pos_array = xcalloc((size_t)tv_list_len(pos_list), sizeof(llpos_T)); m->mit_pos_count = tv_list_len(pos_list); } @@ -533,7 +533,7 @@ void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum) for (shl->first_lnum = lnum; shl->first_lnum > wp->w_topline; shl->first_lnum--) { - if (hasFoldingWin(wp, shl->first_lnum - 1, NULL, NULL, true, NULL)) { + if (hasFolding(wp, shl->first_lnum - 1, NULL, NULL)) { break; } } @@ -1103,7 +1103,7 @@ void f_matchaddpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) list_T *l; l = argvars[1].vval.v_list; - if (l == NULL) { + if (tv_list_len(l) == 0) { return; } diff --git a/src/nvim/math.c b/src/nvim/math.c index 9a0825823c..1ccf4d7806 100644 --- a/src/nvim/math.c +++ b/src/nvim/math.c @@ -1,10 +1,16 @@ // uncrustify:off #include <math.h> // uncrustify:on +#include <limits.h> #include <stdint.h> #include <string.h> +#ifdef HAVE_BITSCANFORWARD64 +# include <intrin.h> // Required for _BitScanForward64 +#endif + #include "nvim/math.h" +#include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "math.c.generated.h" @@ -52,6 +58,10 @@ int xctz(uint64_t x) // Use compiler builtin if possible. #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 4)) return __builtin_ctzll(x); +#elif defined(HAVE_BITSCANFORWARD64) + unsigned long index; + _BitScanForward64(&index, x); + return (int)index; #else int count = 0; // Set x's trailing zeroes to ones and zero the rest. @@ -66,3 +76,31 @@ int xctz(uint64_t x) return count; #endif } + +/// Count number of set bits in bit field. +int popcount(uint64_t x) +{ + // Use compiler builtin if possible. +#if defined(__clang__) || defined(__GNUC__) + return __builtin_popcountll(x); +#else + int count = 0; + for (; x != 0; x >>= 1) { + if (x & 1) { + count++; + } + } + return count; +#endif +} + +/// For overflow detection, add a digit safely to an int value. +int vim_append_digit_int(int *value, int digit) +{ + int x = *value; + if (x > ((INT_MAX - digit) / 10)) { + return FAIL; + } + *value = x * 10 + digit; + return OK; +} diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index c7a56209e4..a345795bbe 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -59,6 +59,7 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/move.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/os.h" @@ -2878,6 +2879,7 @@ void f_setcellwidths(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } xfree(cw_table_save); + changed_window_setting_all(); redraw_all_later(UPD_NOT_VALID); } diff --git a/src/nvim/mbyte_defs.h b/src/nvim/mbyte_defs.h index 8670a0595d..7f2d1ba6ce 100644 --- a/src/nvim/mbyte_defs.h +++ b/src/nvim/mbyte_defs.h @@ -10,7 +10,7 @@ enum { /// character of up to 6 bytes, or one 16-bit character of up to three bytes /// plus six following composing characters of three bytes each. MB_MAXBYTES = 21, - /// max length of an unicode char + /// Maximum length of a Unicode character, excluding composing characters. MB_MAXCHAR = 6, }; diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index fb9f2eb8df..a1713edb66 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -677,7 +677,7 @@ static int mf_trans_add(memfile_T *mfp, bhdr_T *hp) /// The old number When not found. blocknr_T mf_trans_del(memfile_T *mfp, blocknr_T old_nr) { - blocknr_T *num = map_ref(int64_t, int64_t)(&mfp->mf_trans, old_nr, false); + blocknr_T *num = map_ref(int64_t, int64_t)(&mfp->mf_trans, old_nr, NULL); if (num == NULL) { // not found return old_nr; } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 6b2f26b2d8..5acf4f0c37 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -30,6 +30,10 @@ // changed (lines appended/deleted/changed) or when it is flushed it gets a // positive number. Use mf_trans_del() to get the new number, before calling // mf_get(). +// +// "Mom, can we get ropes?" +// "We have ropes at home." +// Ropes at home: #include <assert.h> #include <errno.h> @@ -1174,7 +1178,7 @@ void ml_recover(bool checkext) } else { for (idx = 1; idx <= lnum; idx++) { // Need to copy one line, fetching the other one may flush it. - p = xstrdup(ml_get(idx)); + p = xstrnsave(ml_get(idx), (size_t)ml_get_len(idx)); int i = strcmp(p, ml_get(idx + lnum)); xfree(p); if (i != 0) { @@ -1192,7 +1196,7 @@ void ml_recover(bool checkext) ml_delete(curbuf->b_ml.ml_line_count, false); } curbuf->b_flags |= BF_RECOVERED; - check_cursor(); + check_cursor(curwin); recoverymode = false; if (got_int) { @@ -1834,6 +1838,28 @@ char *ml_get_pos(const pos_T *pos) return ml_get_buf(curbuf, pos->lnum) + pos->col; } +/// @return length (excluding the NUL) of the given line. +colnr_T ml_get_len(linenr_T lnum) +{ + return ml_get_buf_len(curbuf, lnum); +} + +/// @return length (excluding the NUL) of the text after position "pos". +colnr_T ml_get_pos_len(pos_T *pos) +{ + return ml_get_buf_len(curbuf, pos->lnum) - pos->col; +} + +/// @return length (excluding the NUL) of the given line in the given buffer. +colnr_T ml_get_buf_len(buf_T *buf, linenr_T lnum) +{ + if (*ml_get_buf(buf, lnum) == NUL) { + return 0; + } + + return buf->b_ml.ml_line_len - 1; +} + /// @return codepoint at pos. pos must be either valid or have col set to MAXCOL! int gchar_pos(pos_T *pos) FUNC_ATTR_NONNULL_ARG(1) @@ -1865,6 +1891,7 @@ static char *ml_get_buf_impl(buf_T *buf, linenr_T lnum, bool will_change) ml_flush_line(buf, false); errorret: STRCPY(questions, "???"); + buf->b_ml.ml_line_len = 4; buf->b_ml.ml_line_lnum = lnum; return questions; } @@ -1873,6 +1900,7 @@ errorret: } if (buf->b_ml.ml_mfp == NULL) { // there are no lines + buf->b_ml.ml_line_len = 1; return ""; } @@ -1903,8 +1931,14 @@ errorret: DataBlock *dp = hp->bh_data; - char *ptr = (char *)dp + (dp->db_index[lnum - buf->b_ml.ml_locked_low] & DB_INDEX_MASK); - buf->b_ml.ml_line_ptr = ptr; + int idx = lnum - buf->b_ml.ml_locked_low; + unsigned start = (dp->db_index[idx] & DB_INDEX_MASK); + // The text ends where the previous line starts. The first line ends + // at the end of the block. + unsigned end = idx == 0 ? dp->db_txt_end : (dp->db_index[idx - 1] & DB_INDEX_MASK); + + buf->b_ml.ml_line_ptr = (char *)dp + start; + buf->b_ml.ml_line_len = (colnr_T)(end - start); buf->b_ml.ml_line_lnum = lnum; buf->b_ml.ml_flags &= ~(ML_LINE_DIRTY | ML_ALLOCATED); } @@ -1922,7 +1956,8 @@ errorret: #ifdef ML_GET_ALLOC_LINES if ((buf->b_ml.ml_flags & (ML_LINE_DIRTY | ML_ALLOCATED)) == 0) { // make sure the text is in allocated memory - buf->b_ml.ml_line_ptr = xstrdup(buf->b_ml.ml_line_ptr); + buf->b_ml.ml_line_ptr = xmemdup(buf->b_ml.ml_line_ptr, + (size_t)buf->b_ml.ml_line_len); buf->b_ml.ml_flags |= ML_ALLOCATED; if (will_change) { // can't make the change in the data block @@ -2468,6 +2503,7 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy, bool noallo } buf->b_ml.ml_line_ptr = line; + buf->b_ml.ml_line_len = (colnr_T)strlen(line) + 1; buf->b_ml.ml_line_lnum = lnum; buf->b_ml.ml_flags = (buf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY; if (noalloc) { @@ -2765,7 +2801,7 @@ static void ml_flush_line(buf_T *buf, bool noalloc) } else { // text of previous line follows old_len = (int)(dp->db_index[idx - 1] & DB_INDEX_MASK) - start; } - colnr_T new_len = (colnr_T)strlen(new_line) + 1; + colnr_T new_len = buf->b_ml.ml_line_len; int extra = new_len - old_len; // negative if lines gets smaller // if new line fits in data block, replace directly @@ -3456,7 +3492,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ char *const name = xmalloc(name_len); memcpy(name, sw_msg_1, sw_msg_1_len + 1); - home_replace(NULL, fname, &name[sw_msg_1_len], fname_len, true); + home_replace(NULL, fname, name + sw_msg_1_len, fname_len, true); xstrlcat(name, sw_msg_2, name_len); int dialog_result = do_dialog(VIM_WARNING, @@ -3734,7 +3770,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, int len, int updtype) // First line in empty buffer from ml_flush_line() -- reset buf->b_ml.ml_usedchunks = 1; buf->b_ml.ml_chunksize[0].mlcs_numlines = 1; - buf->b_ml.ml_chunksize[0].mlcs_totalsize = (int)strlen(buf->b_ml.ml_line_ptr) + 1; + buf->b_ml.ml_chunksize[0].mlcs_totalsize = buf->b_ml.ml_line_len; return; } @@ -4050,14 +4086,14 @@ void goto_byte(int cnt) if (lnum < 1) { // past the end curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; curwin->w_curswant = MAXCOL; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } else { curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = (colnr_T)boff; curwin->w_cursor.coladd = 0; curwin->w_set_curswant = true; } - check_cursor(); + check_cursor(curwin); // Make sure the cursor is on the first byte of a multi-byte char. mb_adjust_cursor(); @@ -4107,7 +4143,7 @@ int dec(pos_T *lp) if (lp->col == MAXCOL) { // past end of line char *p = ml_get(lp->lnum); - lp->col = (colnr_T)strlen(p); + lp->col = ml_get_len(lp->lnum); lp->col -= utf_head_off(p, p + lp->col); return 0; } @@ -4123,7 +4159,7 @@ int dec(pos_T *lp) // there is a prior line lp->lnum--; char *p = ml_get(lp->lnum); - lp->col = (colnr_T)strlen(p); + lp->col = ml_get_len(lp->lnum); lp->col -= utf_head_off(p, p + lp->col); return 1; } diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h index 1a217c96d4..f675a2b15f 100644 --- a/src/nvim/memline_defs.h +++ b/src/nvim/memline_defs.h @@ -56,6 +56,7 @@ typedef struct { #define ML_ALLOCATED 0x10 // ml_line_ptr is an allocated copy int ml_flags; + colnr_T ml_line_len; // length of the cached line + NUL linenr_T ml_line_lnum; // line number of cached line, 0 if not valid char *ml_line_ptr; // pointer to cached line size_t ml_line_offset; // cached byte offset of ml_line_lnum diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 37e53e4453..789535e270 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -202,7 +202,7 @@ void *xmallocz(size_t size) } void *ret = xmalloc(total_size); - ((char *)ret)[size] = 0; + ((char *)ret)[size] = '\0'; return ret; } @@ -222,6 +222,20 @@ void *xmemdupz(const void *data, size_t len) return memcpy(xmallocz(len), data, len); } +/// Copies `len` bytes of `src` to `dst` and zero terminates it. +/// +/// @see {xstrlcpy} +/// @param[out] dst Buffer to store the result. +/// @param[in] src Buffer to be copied. +/// @param[in] len Number of bytes to be copied. +void *xmemcpyz(void *dst, const void *src, size_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +{ + memcpy(dst, src, len); + ((char *)dst)[len] = '\0'; + return dst; +} + #ifndef HAVE_STRNLEN size_t xstrnlen(const char *s, size_t n) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 4ca2a61ab1..ab28eeca1c 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -1478,11 +1478,11 @@ void execute_menu(const exarg_T *eap, vimmenu_T *menu, int mode_idx) // Activate visual mode VIsual_active = true; VIsual_reselect = true; - check_cursor(); + check_cursor(curwin); VIsual = curwin->w_cursor; curwin->w_cursor = tpos; - check_cursor(); + check_cursor(curwin); // Adjust the cursor to make sure it is in the correct pos // for exclusive mode diff --git a/src/nvim/message.c b/src/nvim/message.c index 175e3b5d03..10b90bde29 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -134,7 +134,7 @@ bool keep_msg_more = false; // keep_msg was set by msgmore() // Extended msg state, currently used for external UIs with ext_messages static const char *msg_ext_kind = NULL; -static Array msg_ext_chunks = ARRAY_DICT_INIT; +static Array *msg_ext_chunks = NULL; static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40); static sattr_T msg_ext_last_attr = -1; static size_t msg_ext_cur_len = 0; @@ -1191,7 +1191,15 @@ void wait_return(int redraw) check_timestamps(false); } - hit_return_msg(); + // if cmdheight=0, we need to scroll in the first line of msg_grid upon the screen + if (p_ch == 0 && !ui_has(kUIMessages) && !msg_scrolled) { + msg_grid_validate(); + msg_scroll_up(false, true); + msg_scrolled++; + cmdline_row = Rows - 1; + } + + hit_return_msg(true); do { // Remember "got_int", if it is set vgetc() probably returns a @@ -1240,7 +1248,7 @@ void wait_return(int redraw) got_int = false; } else if (c != K_IGNORE) { c = K_IGNORE; - hit_return_msg(); + hit_return_msg(false); } } else if (msg_scrolled > Rows - 2 && (c == 'j' || c == 'd' || c == 'f' @@ -1265,7 +1273,7 @@ void wait_return(int redraw) } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) { // Put the character back in the typeahead buffer. Don't use the // stuff buffer, because lmaps wouldn't work. - ins_char_typebuf(vgetc_char, vgetc_mod_mask); + ins_char_typebuf(vgetc_char, vgetc_mod_mask, true); do_redraw = true; // need a redraw even though there is // typeahead } @@ -1313,14 +1321,19 @@ void wait_return(int redraw) } /// Write the hit-return prompt. -static void hit_return_msg(void) +/// +/// @param newline_sb if starting a new line, add it to the scrollback. +static void hit_return_msg(bool newline_sb) { int save_p_more = p_more; - p_more = false; // don't want to see this message when scrolling back + if (!newline_sb) { + p_more = false; + } if (msg_didout) { // start on a new line msg_putchar('\n'); } + p_more = false; // don't want to see this message when scrolling back msg_ext_set_kind("return_prompt"); if (got_int) { msg_puts(_("Interrupt: ")); @@ -2118,6 +2131,9 @@ void msg_printf_attr(const int attr, const char *const fmt, ...) static void msg_ext_emit_chunk(void) { + if (msg_ext_chunks == NULL) { + msg_ext_init_chunks(); + } // Color was changed or a message flushed, end current chunk. if (msg_ext_last_attr == -1) { return; // no chunk @@ -2127,7 +2143,7 @@ static void msg_ext_emit_chunk(void) msg_ext_last_attr = -1; String text = ga_take_string(&msg_ext_last_chunk); ADD(chunk, STRING_OBJ(text)); - ADD(msg_ext_chunks, ARRAY_OBJ(chunk)); + ADD(*msg_ext_chunks, ARRAY_OBJ(chunk)); } /// The display part of msg_puts_len(). @@ -2146,7 +2162,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) msg_ext_last_attr = attr; } // Concat pieces with the same highlight - size_t len = strnlen(str, (size_t)maxlen); + size_t len = maxlen < 0 ? strlen(str) : strnlen(str, (size_t)maxlen); ga_concat_len(&msg_ext_last_chunk, str, len); msg_ext_cur_len += len; return; @@ -2281,7 +2297,7 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) } msg_cursor_goto(msg_row, msg_col); - if (p_more && !recurse && !(s == sb_str + 1 && *sb_str == '\n')) { + if (p_more && !recurse) { store_sb_text(&sb_str, s, attr, &sb_col, false); } @@ -2713,7 +2729,7 @@ static bool do_more_prompt(int typed_char) // If headless mode is enabled and no input is required, this variable // will be true. However If server mode is enabled, the message "--more--" // should be displayed. - bool no_need_more = headless_mode && !embedded_mode; + bool no_need_more = headless_mode && !embedded_mode && !ui_active(); // We get called recursively when a timer callback outputs a message. In // that case don't show another prompt. Also when at the hit-Enter prompt @@ -2968,7 +2984,7 @@ void repeat_message(void) msg_col = 0; msg_clr_eos(); } - hit_return_msg(); + hit_return_msg(false); msg_row = Rows - 1; } } @@ -3043,6 +3059,16 @@ bool msg_end(void) return true; } +/// Clear "msg_ext_chunks" before flushing so that ui_flush() does not re-emit +/// the same message recursively. +static Array *msg_ext_init_chunks(void) +{ + Array *tofree = msg_ext_chunks; + msg_ext_chunks = xcalloc(1, sizeof(*msg_ext_chunks)); + msg_ext_cur_len = 0; + return tofree; +} + void msg_ext_ui_flush(void) { if (!ui_has(kUIMessages)) { @@ -3051,17 +3077,16 @@ void msg_ext_ui_flush(void) } msg_ext_emit_chunk(); - if (msg_ext_chunks.size > 0) { - ui_call_msg_show(cstr_as_string(msg_ext_kind), - msg_ext_chunks, msg_ext_overwrite); + if (msg_ext_chunks->size > 0) { + Array *tofree = msg_ext_init_chunks(); + ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite); + api_free_array(*tofree); + xfree(tofree); if (!msg_ext_overwrite) { msg_ext_visible++; } - msg_ext_kind = NULL; - api_free_array(msg_ext_chunks); - msg_ext_chunks = (Array)ARRAY_DICT_INIT; - msg_ext_cur_len = 0; msg_ext_overwrite = false; + msg_ext_kind = NULL; } } @@ -3071,10 +3096,10 @@ void msg_ext_flush_showmode(void) // separate event. Still reuse the same chunking logic, for simplicity. if (ui_has(kUIMessages)) { msg_ext_emit_chunk(); - ui_call_msg_showmode(msg_ext_chunks); - api_free_array(msg_ext_chunks); - msg_ext_chunks = (Array)ARRAY_DICT_INIT; - msg_ext_cur_len = 0; + Array *tofree = msg_ext_init_chunks(); + ui_call_msg_showmode(*tofree); + api_free_array(*tofree); + xfree(tofree); } } @@ -3390,9 +3415,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt int retval = 0; int i; - if (silent_mode // No dialogs in silent mode ("ex -s") - || !ui_active() // Without a UI Nvim waits for input forever. - ) { + if (silent_mode) { // No dialogs in silent mode ("ex -s") return dfltbutton; // return default option } @@ -3409,6 +3432,12 @@ int do_dialog(int type, const char *title, const char *message, const char *butt char *hotkeys = msg_show_console_dialog(message, buttons, dfltbutton); while (true) { + // Without a UI Nvim waits for input forever. + if (!ui_active() && !input_available()) { + retval = dfltbutton; + break; + } + // Get a typed character directly from the user. int c = get_keystroke(NULL); switch (c) { @@ -3426,7 +3455,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt } if (c == ':' && ex_cmd) { retval = dfltbutton; - ins_char_typebuf(':', 0); + ins_char_typebuf(':', 0, false); break; } diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 506a428243..f393b0fd0f 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -467,7 +467,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent) if (regname == '.') { insert_reg(regname, true); } else { - if (regname == 0 && eval_has_provider("clipboard")) { + if (regname == 0 && eval_has_provider("clipboard", false)) { regname = '*'; } if ((State & REPLACE_FLAG) && !yank_register_mline(regname)) { @@ -771,7 +771,7 @@ popupexit: // move VIsual to the right column start_visual = curwin->w_cursor; // save the cursor pos curwin->w_cursor = end_visual; - coladvance(end_visual.col); + coladvance(curwin, end_visual.col); VIsual = curwin->w_cursor; curwin->w_cursor = start_visual; // restore the cursor } else { @@ -819,7 +819,7 @@ popupexit: // Middle mouse click: Put text before cursor. if (which_button == MOUSE_MIDDLE) { int c2; - if (regname == 0 && eval_has_provider("clipboard")) { + if (regname == 0 && eval_has_provider("clipboard", false)) { regname = '*'; } if (yank_register_mline(regname)) { @@ -1024,7 +1024,7 @@ void do_mousescroll(cmdarg_T *cap) // Vertical scrolling if ((State & MODE_NORMAL) && shift_or_ctrl) { // whole page up or down - onepage(cap->arg ? FORWARD : BACKWARD, 1); + pagescroll(cap->arg ? FORWARD : BACKWARD, 1, false); } else { if (shift_or_ctrl) { // whole page up or down @@ -1430,7 +1430,7 @@ retnomove: break; } first = false; - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin, curwin->w_topline, &curwin->w_topline, NULL); if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { curwin->w_topfill++; } else { @@ -1460,7 +1460,7 @@ retnomove: if (curwin->w_topfill > 0) { curwin->w_topfill--; } else { - if (hasFolding(curwin->w_topline, NULL, &curwin->w_topline) + if (hasFolding(curwin, curwin->w_topline, NULL, &curwin->w_topline) && curwin->w_topline == curbuf->b_ml.ml_line_count) { break; } @@ -1515,7 +1515,7 @@ retnomove: curwin->w_curswant = col; curwin->w_set_curswant = false; // May still have been true - if (coladvance(col) == FAIL) { // Mouse click beyond end of line + if (coladvance(curwin, col) == FAIL) { // Mouse click beyond end of line if (inclusive != NULL) { *inclusive = true; } @@ -1548,7 +1548,7 @@ static bool do_mousescroll_horiz(colnr_T leftcol) // When the line of the cursor is too short, move the cursor to the // longest visible line. - if (!virtual_active() + if (!virtual_active(curwin) && leftcol > scroll_line_len(curwin->w_cursor.lnum)) { curwin->w_cursor.lnum = find_longest_lnum(); curwin->w_cursor.col = 0; @@ -1621,23 +1621,28 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) } if (win->w_skipcol > 0 && lnum == win->w_topline) { - // Adjust for 'smoothscroll' clipping the top screen lines. - // A similar formula is used in curs_columns(). int width1 = win->w_width_inner - win_col_off(win); - int skip_lines = 0; - if (win->w_skipcol > width1) { - skip_lines = (win->w_skipcol - width1) / (width1 + win_col_off2(win)) + 1; - } else if (win->w_skipcol > 0) { - skip_lines = 1; + + if (width1 > 0) { + int skip_lines = 0; + + // Adjust for 'smoothscroll' clipping the top screen lines. + // A similar formula is used in curs_columns(). + if (win->w_skipcol > width1) { + skip_lines = (win->w_skipcol - width1) / (width1 + win_col_off2(win)) + 1; + } else if (win->w_skipcol > 0) { + skip_lines = 1; + } + + count -= skip_lines; } - count -= skip_lines; } if (count > row) { break; // Position is in this buffer line. } - hasFoldingWin(win, lnum, NULL, &lnum, true, NULL); + hasFolding(win, lnum, NULL, &lnum); if (lnum == win->w_buffer->b_ml.ml_line_count) { retval = true; @@ -1883,33 +1888,7 @@ static void mouse_check_grid(colnr_T *vcolp, int *flagsp) const size_t off = gp->line_offset[click_row] + (size_t)click_col; colnr_T col_from_screen = gp->vcols[off]; - if (col_from_screen == MAXCOL) { - // When clicking after end of line, still need to set correct curswant - size_t off_l = gp->line_offset[click_row] + (size_t)start_col; - if (gp->vcols[off_l] < MAXCOL) { - // Binary search to find last char in line - size_t off_r = off; - while (off_l < off_r) { - size_t off_m = (off_l + off_r + 1) / 2; - if (gp->vcols[off_m] < MAXCOL) { - off_l = off_m; - } else { - off_r = off_m - 1; - } - } - colnr_T eol_vcol = gp->vcols[off_r]; - assert(eol_vcol < MAXCOL); - if (eol_vcol < 0) { - // Empty line or whole line before w_leftcol, - // with columns before buffer text - eol_vcol = curwin->w_leftcol - 1; - } - *vcolp = eol_vcol + (int)(off - off_r); - } else { - // Empty line or whole line before w_leftcol - *vcolp = click_col - start_col + curwin->w_leftcol; - } - } else if (col_from_screen >= 0) { + if (col_from_screen >= 0) { // Use the virtual column from vcols[], it is accurate also after // concealed characters. *vcolp = col_from_screen; diff --git a/src/nvim/move.c b/src/nvim/move.c index 551aa1bd4d..418ece09ed 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -36,6 +36,7 @@ #include "nvim/message.h" #include "nvim/mouse.h" #include "nvim/move.h" +#include "nvim/normal.h" #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/plines.h" @@ -77,13 +78,14 @@ int adjust_plines_for_skipcol(win_T *wp) /// Return how many lines "lnum" will take on the screen, taking into account /// whether it is the first line, whether w_skipcol is non-zero and limiting to /// the window height. -static int plines_correct_topline(win_T *wp, linenr_T lnum, linenr_T *nextp, bool *foldedp) +static int plines_correct_topline(win_T *wp, linenr_T lnum, linenr_T *nextp, bool limit_winheight, + bool *foldedp) { int n = plines_win_full(wp, lnum, nextp, foldedp, true, false); if (lnum == wp->w_topline) { n -= adjust_plines_for_skipcol(wp); } - if (n > wp->w_height_inner) { + if (limit_winheight && n > wp->w_height_inner) { return wp->w_height_inner; } return n; @@ -110,7 +112,7 @@ static void comp_botline(win_T *wp) for (; lnum <= wp->w_buffer->b_ml.ml_line_count; lnum++) { linenr_T last = lnum; bool folded; - int n = plines_correct_topline(wp, lnum, &last, &folded); + int n = plines_correct_topline(wp, lnum, &last, true, &folded); if (lnum <= wp->w_cursor.lnum && last >= wp->w_cursor.lnum) { wp->w_cline_row = done; wp->w_cline_height = n; @@ -182,20 +184,23 @@ static void redraw_for_cursorcolumn(win_T *wp) // When current buffer's cursor moves in Visual mode, redraw it with UPD_INVERTED. if (VIsual_active && wp->w_buffer == curbuf) { - redraw_curbuf_later(UPD_INVERTED); + redraw_buf_later(curbuf, UPD_INVERTED); } } /// Calculates how much the 'listchars' "precedes" or 'smoothscroll' "<<<" /// marker overlaps with buffer text for window "wp". /// Parameter "extra2" should be the padding on the 2nd line, not the first -/// line. +/// line. When "extra2" is -1 calculate the padding. /// Returns the number of columns of overlap with buffer text, excluding the /// extra padding on the ledge. int sms_marker_overlap(win_T *wp, int extra2) { + if (extra2 == -1) { + extra2 = win_col_off(wp) - win_col_off2(wp); + } // There is no marker overlap when in showbreak mode, thus no need to - // account for it. See grid_put_linebuf(). + // account for it. See wlv_put_linebuf(). if (*get_showbreak_value(wp) != NUL) { return 0; } @@ -239,7 +244,7 @@ static void reset_skipcol(win_T *wp) redraw_later(wp, UPD_SOME_VALID); } -// Update curwin->w_topline to move the cursor onto the screen. +// Update wp->w_topline to move the cursor onto the screen. void update_topline(win_T *wp) { bool check_botline = false; @@ -281,6 +286,7 @@ void update_topline(win_T *wp) } wp->w_topline = 1; wp->w_botline = 2; + wp->w_skipcol = 0; wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; wp->w_viewport_invalid = true; wp->w_scbind_pos = 1; @@ -294,7 +300,7 @@ void update_topline(win_T *wp) // 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(wp)) { check_topline = true; } else if (wp->w_skipcol > 0 && wp->w_cursor.lnum == wp->w_topline) { colnr_T vcol; @@ -302,7 +308,7 @@ void update_topline(win_T *wp) // Check that the cursor position is visible. Add columns for // the marker displayed in the top-left if needed. getvvcol(wp, &wp->w_cursor, &vcol, NULL, NULL); - int overlap = sms_marker_overlap(wp, win_col_off(wp) - win_col_off2(wp)); + int overlap = sms_marker_overlap(wp, -1); if (wp->w_skipcol + overlap > vcol) { check_topline = true; } @@ -332,7 +338,7 @@ void update_topline(win_T *wp) if (lnum >= wp->w_buffer->b_ml.ml_line_count || n >= halfheight) { break; } - hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); + hasFolding(wp, lnum, NULL, &lnum); } } else { n = wp->w_topline + *so_ptr - wp->w_cursor.lnum; @@ -342,14 +348,14 @@ void update_topline(win_T *wp) // cursor in the middle of the window. Otherwise put the cursor // near the top of the window. if (n >= halfheight) { - scroll_cursor_halfway(false, false); + scroll_cursor_halfway(wp, false, false); } else { - scroll_cursor_top(scrolljump_value(), false); + scroll_cursor_top(wp, scrolljump_value(wp), false); check_botline = true; } } else { // Make sure topline is the first line of a fold. - hasFoldingWin(wp, wp->w_topline, &wp->w_topline, NULL, true, NULL); + hasFolding(wp, wp->w_topline, &wp->w_topline, NULL); check_botline = true; } } @@ -377,7 +383,7 @@ void update_topline(win_T *wp) int n = wp->w_empty_rows; loff.lnum = wp->w_cursor.lnum; // In a fold go to its last line. - hasFoldingWin(wp, loff.lnum, NULL, &loff.lnum, true, NULL); + hasFolding(wp, loff.lnum, NULL, &loff.lnum); loff.fill = 0; n += wp->w_filler_rows; loff.height = 0; @@ -411,15 +417,15 @@ void update_topline(win_T *wp) if (lnum <= 0 || line_count > wp->w_height_inner + 1) { break; } - hasFolding(lnum, &lnum, NULL); + hasFolding(wp, lnum, &lnum, NULL); } } else { line_count = wp->w_cursor.lnum - wp->w_botline + 1 + (int)(*so_ptr); } if (line_count <= wp->w_height_inner + 1) { - scroll_cursor_bot(scrolljump_value(), false); + scroll_cursor_bot(wp, scrolljump_value(wp), false); } else { - scroll_cursor_halfway(false, false); + scroll_cursor_halfway(wp, false, false); } } } @@ -443,40 +449,37 @@ void update_topline(win_T *wp) // May need to set w_skipcol when cursor in w_topline. if (wp->w_cursor.lnum == wp->w_topline) { - validate_cursor(); + validate_cursor(wp); } } *so_ptr = save_so; } -// Return the scrolljump value to use for the current window. -// When 'scrolljump' is positive use it as-is. -// When 'scrolljump' is negative use it as a percentage of the window height. -static int scrolljump_value(void) +/// Return the scrolljump value to use for the window "wp". +/// When 'scrolljump' is positive use it as-is. +/// When 'scrolljump' is negative use it as a percentage of the window height. +static int scrolljump_value(win_T *wp) { - int result = p_sj >= 0 ? (int)p_sj : (curwin->w_height_inner * (int)(-p_sj)) / 100; + int result = p_sj >= 0 ? (int)p_sj : (wp->w_height_inner * (int)(-p_sj)) / 100; return result; } -// Return true when there are not 'scrolloff' lines above the cursor for the -// current window. -static bool check_top_offset(void) +/// Return true when there are not 'scrolloff' lines above the cursor for window "wp". +static bool check_top_offset(win_T *wp) { - int so = get_scrolloff_value(curwin); - if (curwin->w_cursor.lnum < curwin->w_topline + so - || hasAnyFolding(curwin)) { + int so = get_scrolloff_value(wp); + if (wp->w_cursor.lnum < wp->w_topline + so || hasAnyFolding(wp)) { lineoff_T loff; - loff.lnum = curwin->w_cursor.lnum; + loff.lnum = wp->w_cursor.lnum; loff.fill = 0; - int n = curwin->w_topfill; // always have this context + int n = wp->w_topfill; // always have this context // Count the visible screen lines above the cursor line. while (n < so) { - topline_back(curwin, &loff); + topline_back(wp, &loff); // Stop when included a line above the window. - if (loff.lnum < curwin->w_topline - || (loff.lnum == curwin->w_topline - && loff.fill > 0)) { + if (loff.lnum < wp->w_topline + || (loff.lnum == wp->w_topline && loff.fill > 0)) { break; } n += loff.height; @@ -491,7 +494,7 @@ static bool check_top_offset(void) /// Update w_curswant. void update_curswant_force(void) { - validate_virtcol(); + validate_virtcol(curwin); curwin->w_curswant = curwin->w_virtcol; curwin->w_set_curswant = false; } @@ -536,12 +539,7 @@ void check_cursor_moved(win_T *wp) // Call this function when some window settings have changed, which require // the cursor position, botline and topline to be recomputed and the window to // be redrawn. E.g, when changing the 'wrap' option or folding. -void changed_window_setting(void) -{ - changed_window_setting_win(curwin); -} - -void changed_window_setting_win(win_T *wp) +void changed_window_setting(win_T *wp) { wp->w_lines_valid = 0; changed_line_abv_curs_win(wp); @@ -549,13 +547,21 @@ void changed_window_setting_win(win_T *wp) redraw_later(wp, UPD_NOT_VALID); } +/// Call changed_window_setting() for every window. +void changed_window_setting_all(void) +{ + FOR_ALL_TAB_WINDOWS(tp, wp) { + changed_window_setting(wp); + } +} + // Set wp->w_topline to a certain number. void set_topline(win_T *wp, linenr_T lnum) { linenr_T prev_topline = wp->w_topline; // go to first of folded lines - hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFolding(wp, lnum, &lnum, NULL); // Approximate the value of w_botline wp->w_botline += lnum - wp->w_topline; wp->w_topline = lnum; @@ -595,7 +601,7 @@ void changed_line_abv_curs_win(win_T *wp) |VALID_CHEIGHT|VALID_TOPLINE); } -// Make sure the value of curwin->w_botline is valid. +// Make sure the value of wp->w_botline is valid. void validate_botline(win_T *wp) { if (!(wp->w_valid & VALID_BOTLINE)) { @@ -614,21 +620,21 @@ void approximate_botline_win(win_T *wp) wp->w_valid &= ~VALID_BOTLINE; } -// Return true if curwin->w_wrow and curwin->w_wcol are valid. -int cursor_valid(void) +// Return true if wp->w_wrow and wp->w_wcol are valid. +int cursor_valid(win_T *wp) { - check_cursor_moved(curwin); - return (curwin->w_valid & (VALID_WROW|VALID_WCOL)) == (VALID_WROW|VALID_WCOL); + check_cursor_moved(wp); + return (wp->w_valid & (VALID_WROW|VALID_WCOL)) == (VALID_WROW|VALID_WCOL); } // Validate cursor position. Makes sure w_wrow and w_wcol are valid. // w_topline must be valid, you may need to call update_topline() first! -void validate_cursor(void) +void validate_cursor(win_T *wp) { - check_cursor(); - check_cursor_moved(curwin); - if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) { - curs_columns(curwin, true); + check_cursor_lnum(wp); + check_cursor_moved(wp); + if ((wp->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW)) { + curs_columns(wp, true); } } @@ -671,7 +677,7 @@ static void curs_rows(win_T *wp) } else { linenr_T last = lnum; bool folded; - int n = plines_correct_topline(wp, lnum, &last, &folded); + int n = plines_correct_topline(wp, lnum, &last, true, &folded); lnum = last + 1; if (folded && lnum > wp->w_cursor.lnum) { break; @@ -692,26 +698,19 @@ static void curs_rows(win_T *wp) } else if (i > wp->w_lines_valid) { // a line that is too long to fit on the last screen line wp->w_cline_height = 0; - wp->w_cline_folded = hasFoldingWin(wp, wp->w_cursor.lnum, NULL, - NULL, true, NULL); + wp->w_cline_folded = hasFolding(wp, wp->w_cursor.lnum, NULL, NULL); } else { wp->w_cline_height = wp->w_lines[i].wl_size; wp->w_cline_folded = wp->w_lines[i].wl_folded; } } - redraw_for_cursorline(curwin); + redraw_for_cursorline(wp); wp->w_valid |= VALID_CROW|VALID_CHEIGHT; } -// Validate curwin->w_virtcol only. -void validate_virtcol(void) -{ - validate_virtcol_win(curwin); -} - // Validate wp->w_virtcol only. -void validate_virtcol_win(win_T *wp) +void validate_virtcol(win_T *wp) { check_cursor_moved(wp); @@ -724,49 +723,48 @@ void validate_virtcol_win(win_T *wp) wp->w_valid |= VALID_VIRTCOL; } -// Validate curwin->w_cline_height only. -void validate_cheight(void) +// Validate wp->w_cline_height only. +void validate_cheight(win_T *wp) { - check_cursor_moved(curwin); + check_cursor_moved(wp); - if (curwin->w_valid & VALID_CHEIGHT) { + if (wp->w_valid & VALID_CHEIGHT) { return; } - curwin->w_cline_height = plines_win_full(curwin, curwin->w_cursor.lnum, - NULL, &curwin->w_cline_folded, - true, true); - curwin->w_valid |= VALID_CHEIGHT; + wp->w_cline_height = plines_win_full(wp, wp->w_cursor.lnum, + NULL, &wp->w_cline_folded, + true, true); + wp->w_valid |= VALID_CHEIGHT; } // Validate w_wcol and w_virtcol only. -void validate_cursor_col(void) +void validate_cursor_col(win_T *wp) { - validate_virtcol(); + validate_virtcol(wp); - if (curwin->w_valid & VALID_WCOL) { + if (wp->w_valid & VALID_WCOL) { return; } - colnr_T col = curwin->w_virtcol; - colnr_T off = curwin_col_off(); + colnr_T col = wp->w_virtcol; + colnr_T off = win_col_off(wp); col += off; - int width = curwin->w_width_inner - off + curwin_col_off2(); + int width = wp->w_width_inner - off + win_col_off2(wp); - // long line wrapping, adjust curwin->w_wrow - if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_width_inner - && width > 0) { + // long line wrapping, adjust wp->w_wrow + if (wp->w_p_wrap && col >= (colnr_T)wp->w_width_inner && width > 0) { // use same formula as what is used in curs_columns() - col -= ((col - curwin->w_width_inner) / width + 1) * width; + col -= ((col - wp->w_width_inner) / width + 1) * width; } - if (col > (int)curwin->w_leftcol) { - col -= curwin->w_leftcol; + if (col > (int)wp->w_leftcol) { + col -= wp->w_leftcol; } else { col = 0; } - curwin->w_wcol = col; + wp->w_wcol = col; - curwin->w_valid |= VALID_WCOL; + wp->w_valid |= VALID_WCOL; } // Compute offset of a window, occupied by absolute or relative line number, @@ -779,11 +777,6 @@ int win_col_off(win_T *wp) + win_fdccol_count(wp) + (wp->w_scwidth * SIGN_WIDTH); } -int curwin_col_off(void) -{ - return win_col_off(curwin); -} - // Return the difference in column offset for the second screen line of a // wrapped line. It's positive if 'number' or 'relativenumber' is on and 'n' // is in 'cpoptions'. @@ -796,11 +789,6 @@ int win_col_off2(win_T *wp) return 0; } -int curwin_col_off2(void) -{ - return win_col_off2(curwin); -} - // Compute wp->w_wcol and wp->w_virtcol. // Also updates wp->w_wrow and wp->w_cline_row. // Also updates wp->w_leftcol. @@ -896,7 +884,7 @@ void curs_columns(win_T *wp, int may_scroll) // middle of window. int new_leftcol; if (p_ss == 0 || diff >= width1 / 2 || off_right >= off_left) { - new_leftcol = curwin->w_wcol - extra - width1 / 2; + new_leftcol = wp->w_wcol - extra - width1 / 2; } else { if (diff < p_ss) { assert(p_ss <= INT_MAX); @@ -984,9 +972,9 @@ void curs_columns(win_T *wp, int may_scroll) n = plines - wp->w_height_inner + 1; } if (n > 0) { - curwin->w_skipcol = width1 + (n - 1) * width2; + wp->w_skipcol = width1 + (n - 1) * width2; } else { - curwin->w_skipcol = 0; + wp->w_skipcol = 0; } } else if (extra == 1) { // less than 'scrolloff' lines above, decrease skipcol @@ -1063,8 +1051,8 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp, linenr_T lnum = pos->lnum; if (lnum >= wp->w_topline && lnum <= wp->w_botline) { - is_folded = hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); - row = plines_m_win(wp, wp->w_topline, lnum - 1, false); + is_folded = hasFolding(wp, lnum, &lnum, NULL); + row = plines_m_win(wp, wp->w_topline, lnum - 1, INT_MAX); // "row" should be the screen line where line "lnum" begins, which can // be negative if "lnum" is "w_topline" and "w_skipcol" is non-zero. row -= adjust_plines_for_skipcol(wp); @@ -1207,162 +1195,261 @@ void f_virtcol2col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_number = virtcol2col(wp, lnum, screencol); } -/// Scroll the current window down by "line_count" logical lines. "CTRL-Y" +/// Make sure the cursor is in the visible part of the topline after scrolling +/// the screen with 'smoothscroll'. +static void cursor_correct_sms(win_T *wp) +{ + if (!wp->w_p_sms || !wp->w_p_wrap || wp->w_cursor.lnum != wp->w_topline) { + return; + } + + int so = get_scrolloff_value(wp); + int width1 = wp->w_width_inner - win_col_off(wp); + int width2 = width1 + win_col_off2(wp); + int so_cols = so == 0 ? 0 : width1 + (so - 1) * width2; + int space_cols = (wp->w_height_inner - 1) * width2; + int size = so == 0 ? 0 : win_linetabsize(wp, wp->w_topline, + ml_get_buf(wp->w_buffer, wp->w_topline), + (colnr_T)MAXCOL); + + if (wp->w_topline == 1 && wp->w_skipcol == 0) { + so_cols = 0; // Ignore 'scrolloff' at top of buffer. + } else if (so_cols > space_cols / 2) { + so_cols = space_cols / 2; // Not enough room: put cursor in the middle. + } + + // Not enough screen lines in topline: ignore 'scrolloff'. + while (so_cols > size && so_cols - width2 >= width1 && width1 > 0) { + so_cols -= width2; + } + if (so_cols >= width1 && so_cols > size) { + so_cols -= width1; + } + + // If there is no marker or we have non-zero scrolloff, just ignore it. + int overlap = (wp->w_skipcol == 0 || so_cols != 0) ? 0 : sms_marker_overlap(wp, -1); + int top = wp->w_skipcol + overlap + so_cols; + int bot = wp->w_skipcol + width1 + (wp->w_height_inner - 1) * width2 - so_cols; + + validate_virtcol(wp); + colnr_T col = wp->w_virtcol; + + if (col < top) { + if (col < width1) { + col += width1; + } + while (width2 > 0 && col < top) { + col += width2; + } + } else { + while (width2 > 0 && col >= bot) { + col -= width2; + } + } + + if (col != wp->w_virtcol) { + wp->w_curswant = col; + coladvance(wp, wp->w_curswant); + // validate_virtcol() marked various things as valid, but after + // moving the cursor they need to be recomputed + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); + } +} + +/// Scroll "count" lines up or down, and redraw. +void scroll_redraw(int up, linenr_T count) +{ + linenr_T prev_topline = curwin->w_topline; + int prev_skipcol = curwin->w_skipcol; + int prev_topfill = curwin->w_topfill; + linenr_T prev_lnum = curwin->w_cursor.lnum; + + bool moved = up + ? scrollup(curwin, count, true) + : scrolldown(curwin, count, true); + + if (get_scrolloff_value(curwin) > 0) { + // Adjust the cursor position for 'scrolloff'. Mark w_topline as + // valid, otherwise the screen jumps back at the end of the file. + cursor_correct(curwin); + check_cursor_moved(curwin); + curwin->w_valid |= VALID_TOPLINE; + + // If moved back to where we were, at least move the cursor, otherwise + // we get stuck at one position. Don't move the cursor up if the + // first line of the buffer is already on the screen + while (curwin->w_topline == prev_topline + && curwin->w_skipcol == prev_skipcol + && curwin->w_topfill == prev_topfill) { + if (up) { + if (curwin->w_cursor.lnum > prev_lnum + || cursor_down(1L, false) == FAIL) { + break; + } + } else { + if (curwin->w_cursor.lnum < prev_lnum + || prev_topline == 1L + || cursor_up(1L, false) == FAIL) { + break; + } + } + // Mark w_topline as valid, otherwise the screen jumps back at the + // end of the file. + check_cursor_moved(curwin); + curwin->w_valid |= VALID_TOPLINE; + } + } + + if (moved) { + curwin->w_viewport_invalid = true; + } + + cursor_correct_sms(curwin); + if (curwin->w_cursor.lnum != prev_lnum) { + coladvance(curwin, curwin->w_curswant); + } + redraw_later(curwin, UPD_VALID); +} + +/// Scroll a window down by "line_count" logical lines. "CTRL-Y" /// /// @param line_count number of lines to scroll /// @param byfold if true, count a closed fold as one line -bool scrolldown(linenr_T line_count, int byfold) +bool scrolldown(win_T *wp, linenr_T line_count, int byfold) { int done = 0; // total # of physical lines done int width1 = 0; int width2 = 0; - bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; + bool do_sms = wp->w_p_wrap && wp->w_p_sms; if (do_sms) { - width1 = curwin->w_width_inner - curwin_col_off(); - width2 = width1 + curwin_col_off2(); + width1 = wp->w_width_inner - win_col_off(wp); + width2 = width1 + win_col_off2(wp); } // Make sure w_topline is at the first of a sequence of folded lines. - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); - validate_cursor(); // w_wrow needs to be valid + hasFolding(wp, wp->w_topline, &wp->w_topline, NULL); + validate_cursor(wp); // w_wrow needs to be valid for (int todo = line_count; todo > 0; todo--) { - if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline) - && curwin->w_topfill < curwin->w_height_inner - 1) { - curwin->w_topfill++; + if (wp->w_topfill < win_get_fill(wp, wp->w_topline) + && wp->w_topfill < wp->w_height_inner - 1) { + wp->w_topfill++; done++; } else { // break when at the very top - if (curwin->w_topline == 1 && (!do_sms || curwin->w_skipcol < width1)) { + if (wp->w_topline == 1 && (!do_sms || wp->w_skipcol < width1)) { break; } - if (do_sms && curwin->w_skipcol >= width1) { + if (do_sms && wp->w_skipcol >= width1) { // scroll a screen line down - if (curwin->w_skipcol >= width1 + width2) { - curwin->w_skipcol -= width2; + if (wp->w_skipcol >= width1 + width2) { + wp->w_skipcol -= width2; } else { - curwin->w_skipcol -= width1; + wp->w_skipcol -= width1; } - redraw_later(curwin, UPD_NOT_VALID); + redraw_later(wp, UPD_NOT_VALID); done++; } else { // scroll a text line down - curwin->w_topline--; - curwin->w_skipcol = 0; - curwin->w_topfill = 0; + wp->w_topline--; + wp->w_skipcol = 0; + wp->w_topfill = 0; // A sequence of folded lines only counts for one logical line linenr_T first; - if (hasFolding(curwin->w_topline, &first, NULL)) { + if (hasFolding(wp, wp->w_topline, &first, NULL)) { done++; if (!byfold) { - todo -= curwin->w_topline - first - 1; + todo -= wp->w_topline - first - 1; } - curwin->w_botline -= curwin->w_topline - first; - curwin->w_topline = first; + wp->w_botline -= wp->w_topline - first; + wp->w_topline = first; } else { if (do_sms) { - int size = win_linetabsize(curwin, curwin->w_topline, - ml_get(curwin->w_topline), MAXCOL); + int size = win_linetabsize(wp, wp->w_topline, + ml_get_buf(wp->w_buffer, wp->w_topline), MAXCOL); if (size > width1) { - curwin->w_skipcol = width1; + wp->w_skipcol = width1; size -= width1; - redraw_later(curwin, UPD_NOT_VALID); + redraw_later(wp, UPD_NOT_VALID); } while (size > width2) { - curwin->w_skipcol += width2; + wp->w_skipcol += width2; size -= width2; } done++; } else { - done += plines_win_nofill(curwin, curwin->w_topline, true); + done += plines_win_nofill(wp, wp->w_topline, true); } } } } - curwin->w_botline--; // approximate w_botline - invalidate_botline(curwin); + wp->w_botline--; // approximate w_botline + invalidate_botline(wp); } - curwin->w_wrow += done; // keep w_wrow updated - curwin->w_cline_row += done; // keep w_cline_row updated + wp->w_wrow += done; // keep w_wrow updated + wp->w_cline_row += done; // keep w_cline_row updated - if (curwin->w_cursor.lnum == curwin->w_topline) { - curwin->w_cline_row = 0; + if (wp->w_cursor.lnum == wp->w_topline) { + wp->w_cline_row = 0; } - check_topfill(curwin, true); + check_topfill(wp, true); // Compute the row number of the last row of the cursor line // and move the cursor onto the displayed part of the window. - int wrow = curwin->w_wrow; - if (curwin->w_p_wrap && curwin->w_width_inner != 0) { - validate_virtcol(); - validate_cheight(); - wrow += curwin->w_cline_height - 1 - - curwin->w_virtcol / curwin->w_width_inner; + int wrow = wp->w_wrow; + if (wp->w_p_wrap && wp->w_width_inner != 0) { + validate_virtcol(wp); + validate_cheight(wp); + wrow += wp->w_cline_height - 1 - + wp->w_virtcol / wp->w_width_inner; } bool moved = false; - while (wrow >= curwin->w_height_inner && curwin->w_cursor.lnum > 1) { + while (wrow >= wp->w_height_inner && wp->w_cursor.lnum > 1) { linenr_T first; - if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) { + if (hasFolding(wp, wp->w_cursor.lnum, &first, NULL)) { wrow--; if (first == 1) { - curwin->w_cursor.lnum = 1; + wp->w_cursor.lnum = 1; } else { - curwin->w_cursor.lnum = first - 1; + wp->w_cursor.lnum = first - 1; } } else { - wrow -= plines_win(curwin, curwin->w_cursor.lnum--, true); + wrow -= plines_win(wp, wp->w_cursor.lnum--, true); } - curwin->w_valid &= + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); moved = true; } if (moved) { // Move cursor to first line of closed fold. - foldAdjustCursor(); - coladvance(curwin->w_curswant); + foldAdjustCursor(wp); + coladvance(wp, wp->w_curswant); } - - if (curwin->w_cursor.lnum == curwin->w_topline && do_sms) { - int so = get_scrolloff_value(curwin); - colnr_T scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; - - // make sure the cursor is in the visible text - validate_virtcol(); - colnr_T col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols; - int row = 0; - if (col >= width1) { - col -= width1; - row++; - } - if (col > width2 && width2 > 0) { - row += (int)col / width2; - } - if (row >= curwin->w_height_inner) { - curwin->w_curswant = curwin->w_virtcol - (row - curwin->w_height_inner + 1) * width2; - coladvance(curwin->w_curswant); - } + if (wp->w_cursor.lnum < wp->w_topline) { + wp->w_cursor.lnum = wp->w_topline; } + return moved; } -/// Scroll the current window up by "line_count" logical lines. "CTRL-E" +/// Scroll a window up by "line_count" logical lines. "CTRL-E" /// /// @param line_count number of lines to scroll /// @param byfold if true, count a closed fold as one line -bool scrollup(linenr_T line_count, bool byfold) +bool scrollup(win_T *wp, linenr_T line_count, bool byfold) { - linenr_T topline = curwin->w_topline; - linenr_T botline = curwin->w_botline; - bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; + linenr_T topline = wp->w_topline; + linenr_T botline = wp->w_botline; + bool do_sms = wp->w_p_wrap && wp->w_p_sms; - if (do_sms || (byfold && hasAnyFolding(curwin)) || win_may_fill(curwin)) { - int width1 = curwin->w_width_inner - curwin_col_off(); - int width2 = width1 + curwin_col_off2(); + if (do_sms || (byfold && hasAnyFolding(wp)) || win_may_fill(wp)) { + int width1 = wp->w_width_inner - win_col_off(wp); + int width2 = width1 + win_col_off2(wp); int size = 0; - const colnr_T prev_skipcol = curwin->w_skipcol; + const colnr_T prev_skipcol = wp->w_skipcol; if (do_sms) { - size = linetabsize(curwin, curwin->w_topline); + size = linetabsize(wp, wp->w_topline); } // diff mode: first consume "topfill" @@ -1370,120 +1457,80 @@ bool scrollup(linenr_T line_count, bool byfold) // the line, then advance to the next line. // folding: count each sequence of folded lines as one logical line. for (int todo = line_count; todo > 0; todo--) { - if (curwin->w_topfill > 0) { - curwin->w_topfill--; + if (wp->w_topfill > 0) { + wp->w_topfill--; } else { - linenr_T lnum = curwin->w_topline; + linenr_T lnum = wp->w_topline; if (byfold) { // for a closed fold: go to the last line in the fold - hasFolding(lnum, NULL, &lnum); + hasFolding(wp, lnum, NULL, &lnum); } - if (lnum == curwin->w_topline && do_sms) { + if (lnum == wp->w_topline && do_sms) { // 'smoothscroll': increase "w_skipcol" until it goes over // the end of the line, then advance to the next line. - int add = curwin->w_skipcol > 0 ? width2 : width1; - curwin->w_skipcol += add; - if (curwin->w_skipcol >= size) { - if (lnum == curbuf->b_ml.ml_line_count) { + int add = wp->w_skipcol > 0 ? width2 : width1; + wp->w_skipcol += add; + if (wp->w_skipcol >= size) { + if (lnum == wp->w_buffer->b_ml.ml_line_count) { // at the last screen line, can't scroll further - curwin->w_skipcol -= add; + wp->w_skipcol -= add; break; } lnum++; } } else { - if (lnum >= curbuf->b_ml.ml_line_count) { + if (lnum >= wp->w_buffer->b_ml.ml_line_count) { break; } lnum++; } - if (lnum > curwin->w_topline) { + if (lnum > wp->w_topline) { // approximate w_botline - curwin->w_botline += lnum - curwin->w_topline; - curwin->w_topline = lnum; - curwin->w_topfill = win_get_fill(curwin, lnum); - curwin->w_skipcol = 0; + wp->w_botline += lnum - wp->w_topline; + wp->w_topline = lnum; + wp->w_topfill = win_get_fill(wp, lnum); + wp->w_skipcol = 0; if (todo > 1 && do_sms) { - size = linetabsize(curwin, curwin->w_topline); + size = linetabsize(wp, wp->w_topline); } } } } - if (prev_skipcol > 0 || curwin->w_skipcol > 0) { + if (prev_skipcol > 0 || wp->w_skipcol > 0) { // need to redraw more, because wl_size of the (new) topline may // now be invalid - redraw_later(curwin, UPD_NOT_VALID); + redraw_later(wp, UPD_NOT_VALID); } } else { - curwin->w_topline += line_count; - curwin->w_botline += line_count; // approximate w_botline + wp->w_topline += line_count; + wp->w_botline += line_count; // approximate w_botline } - if (curwin->w_topline > curbuf->b_ml.ml_line_count) { - curwin->w_topline = curbuf->b_ml.ml_line_count; + if (wp->w_topline > wp->w_buffer->b_ml.ml_line_count) { + wp->w_topline = wp->w_buffer->b_ml.ml_line_count; } - if (curwin->w_botline > curbuf->b_ml.ml_line_count + 1) { - curwin->w_botline = curbuf->b_ml.ml_line_count + 1; + if (wp->w_botline > wp->w_buffer->b_ml.ml_line_count + 1) { + wp->w_botline = wp->w_buffer->b_ml.ml_line_count + 1; } - check_topfill(curwin, false); + check_topfill(wp, false); - if (hasAnyFolding(curwin)) { + if (hasAnyFolding(wp)) { // Make sure w_topline is at the first of a sequence of folded lines. - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(wp, wp->w_topline, &wp->w_topline, NULL); } - curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); - if (curwin->w_cursor.lnum < curwin->w_topline) { - curwin->w_cursor.lnum = curwin->w_topline; - curwin->w_valid &= + wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); + if (wp->w_cursor.lnum < wp->w_topline) { + wp->w_cursor.lnum = wp->w_topline; + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); - coladvance(curwin->w_curswant); - } - - if (curwin->w_cursor.lnum == curwin->w_topline && do_sms && curwin->w_skipcol > 0) { - int col_off = curwin_col_off(); - int col_off2 = curwin_col_off2(); - - int width1 = curwin->w_width_inner - col_off; - int width2 = width1 + col_off2; - int extra2 = col_off - col_off2; - int so = get_scrolloff_value(curwin); - colnr_T scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; - int space_cols = (curwin->w_height_inner - 1) * width2; - - // If we have non-zero scrolloff, just ignore the marker as we are - // going past it anyway. - int overlap = scrolloff_cols != 0 ? 0 : sms_marker_overlap(curwin, extra2); - - // Make sure the cursor is in a visible part of the line, taking - // 'scrolloff' into account, but using screen lines. - // If there are not enough screen lines put the cursor in the middle. - if (scrolloff_cols > space_cols / 2) { - scrolloff_cols = space_cols / 2; - } - validate_virtcol(); - if (curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols) { - colnr_T col = curwin->w_virtcol; - - if (col < width1) { - col += width1; - } - while (col < curwin->w_skipcol + overlap + scrolloff_cols) { - col += width2; - } - curwin->w_curswant = col; - coladvance(curwin->w_curswant); - - // validate_virtcol() marked various things as valid, but after - // moving the cursor they need to be recomputed - curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); - } + coladvance(wp, wp->w_curswant); } - bool moved = topline != curwin->w_topline || botline != curwin->w_botline; + bool moved = topline != wp->w_topline || botline != wp->w_botline; return moved; } @@ -1496,16 +1543,16 @@ void adjust_skipcol(void) return; } - int width1 = curwin->w_width_inner - curwin_col_off(); + int width1 = curwin->w_width_inner - win_col_off(curwin); if (width1 <= 0) { return; // no text will be displayed } - int width2 = width1 + curwin_col_off2(); + int width2 = width1 + win_col_off2(curwin); int so = get_scrolloff_value(curwin); colnr_T scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2; bool scrolled = false; - validate_cheight(); + validate_cheight(curwin); if (curwin->w_cline_height == curwin->w_height_inner // w_cline_height may be capped at w_height_inner, check there aren't // actually more lines. @@ -1515,8 +1562,8 @@ void adjust_skipcol(void) return; } - validate_virtcol(); - int overlap = sms_marker_overlap(curwin, curwin_col_off() - curwin_col_off2()); + validate_virtcol(curwin); + int overlap = sms_marker_overlap(curwin, -1); while (curwin->w_skipcol > 0 && curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols) { // scroll a screen line down @@ -1528,12 +1575,24 @@ void adjust_skipcol(void) scrolled = true; } if (scrolled) { - validate_virtcol(); + validate_virtcol(curwin); redraw_later(curwin, UPD_NOT_VALID); return; // don't scroll in the other direction now } - colnr_T col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols; int row = 0; + colnr_T col = curwin->w_virtcol + scrolloff_cols; + + // Avoid adjusting for 'scrolloff' beyond the text line height. + if (scrolloff_cols > 0) { + int size = win_linetabsize(curwin, curwin->w_topline, + ml_get(curwin->w_topline), (colnr_T)MAXCOL); + size = width1 + width2 * ((size - width1 + width2 - 1) / width2); + while (col > size) { + col -= width2; + } + } + col -= curwin->w_skipcol; + if (col >= width1) { col -= width1; row++; @@ -1572,22 +1631,7 @@ void check_topfill(win_T *wp, bool down) } } } - win_check_anchored_floats(curwin); -} - -// Use as many filler lines as possible for w_topline. Make sure w_topline -// is still visible. -static void max_topfill(void) -{ - int n = plines_win_nofill(curwin, curwin->w_topline, true); - if (n >= curwin->w_height_inner) { - curwin->w_topfill = 0; - } else { - curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); - if (curwin->w_topfill + n > curwin->w_height_inner) { - curwin->w_topfill = curwin->w_height_inner - n; - } - } + win_check_anchored_floats(wp); } // Scroll the screen one line down, but don't do it if it would move the @@ -1601,7 +1645,7 @@ void scrolldown_clamp(void) return; } - validate_cursor(); // w_wrow needs to be valid + validate_cursor(curwin); // w_wrow needs to be valid // Compute the row number of the last row of the cursor line // and make sure it doesn't go off the screen. Make sure the cursor @@ -1613,8 +1657,8 @@ void scrolldown_clamp(void) end_row += plines_win_nofill(curwin, curwin->w_topline - 1, true); } if (curwin->w_p_wrap && curwin->w_width_inner != 0) { - validate_cheight(); - validate_virtcol(); + validate_cheight(curwin); + validate_virtcol(curwin); end_row += curwin->w_cline_height - 1 - curwin->w_virtcol / curwin->w_width_inner; } @@ -1626,7 +1670,7 @@ void scrolldown_clamp(void) curwin->w_topline--; curwin->w_topfill = 0; } - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); + hasFolding(curwin, curwin->w_topline, &curwin->w_topline, NULL); curwin->w_botline--; // approximate w_botline curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); } @@ -1641,7 +1685,7 @@ void scrollup_clamp(void) return; } - validate_cursor(); // w_wrow needs to be valid + validate_cursor(curwin); // w_wrow needs to be valid // Compute the row number of the first row of the cursor line // and make sure it doesn't go off the screen. Make sure the cursor @@ -1650,14 +1694,14 @@ void scrollup_clamp(void) - plines_win_nofill(curwin, curwin->w_topline, true) - curwin->w_topfill); if (curwin->w_p_wrap && curwin->w_width_inner != 0) { - validate_virtcol(); + validate_virtcol(curwin); start_row -= curwin->w_virtcol / curwin->w_width_inner; } if (start_row >= get_scrolloff_value(curwin)) { if (curwin->w_topfill > 0) { curwin->w_topfill--; } else { - hasFolding(curwin->w_topline, NULL, &curwin->w_topline); + hasFolding(curwin, curwin->w_topline, NULL, &curwin->w_topline); curwin->w_topline++; } curwin->w_botline++; // approximate w_botline @@ -1681,7 +1725,7 @@ static void topline_back_winheight(win_T *wp, lineoff_T *lp, int winheight) lp->fill = 0; if (lp->lnum < 1) { lp->height = MAXCOL; - } else if (hasFolding(lp->lnum, &lp->lnum, NULL)) { + } else if (hasFolding(wp, lp->lnum, &lp->lnum, NULL)) { // Add a closed fold lp->height = 1; } else { @@ -1711,7 +1755,7 @@ static void botline_forw(win_T *wp, lineoff_T *lp) assert(wp->w_buffer != 0); if (lp->lnum > wp->w_buffer->b_ml.ml_line_count) { lp->height = MAXCOL; - } else if (hasFoldingWin(wp, lp->lnum, NULL, &lp->lnum, true, NULL)) { + } else if (hasFolding(wp, lp->lnum, NULL, &lp->lnum)) { // Add a closed fold lp->height = 1; } else { @@ -1720,37 +1764,15 @@ static void botline_forw(win_T *wp, lineoff_T *lp) } } -// Switch from including filler lines below lp->lnum to including filler -// lines above loff.lnum + 1. This keeps pointing to the same line. -// When there are no filler lines nothing changes. -static void botline_topline(lineoff_T *lp) -{ - if (lp->fill > 0) { - lp->lnum++; - lp->fill = win_get_fill(curwin, lp->lnum) - lp->fill + 1; - } -} - -// Switch from including filler lines above lp->lnum to including filler -// lines below loff.lnum - 1. This keeps pointing to the same line. -// When there are no filler lines nothing changes. -static void topline_botline(lineoff_T *lp) -{ - if (lp->fill > 0) { - lp->fill = win_get_fill(curwin, lp->lnum) - lp->fill + 1; - lp->lnum--; - } -} - // Recompute topline to put the cursor at the top of the window. // Scroll at least "min_scroll" lines. // If "always" is true, always set topline (for "zt"). -void scroll_cursor_top(int min_scroll, int always) +void scroll_cursor_top(win_T *wp, int min_scroll, int always) { - linenr_T old_topline = curwin->w_topline; - int old_skipcol = curwin->w_skipcol; - linenr_T old_topfill = curwin->w_topfill; - int off = get_scrolloff_value(curwin); + linenr_T old_topline = wp->w_topline; + int old_skipcol = wp->w_skipcol; + linenr_T old_topfill = wp->w_topfill; + int off = get_scrolloff_value(wp); if (mouse_dragging > 0) { off = mouse_dragging - 1; @@ -1761,54 +1783,54 @@ void scroll_cursor_top(int min_scroll, int always) // - (part of) the cursor line is moved off the screen or // - moved at least 'scrolljump' lines and // - at least 'scrolloff' lines above and below the cursor - validate_cheight(); + validate_cheight(wp); int scrolled = 0; - int used = curwin->w_cline_height; // includes filler lines above - if (curwin->w_cursor.lnum < curwin->w_topline) { + int used = wp->w_cline_height; // includes filler lines above + if (wp->w_cursor.lnum < wp->w_topline) { scrolled = used; } linenr_T top; // just above displayed lines linenr_T bot; // just below displayed lines - if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) { + if (hasFolding(wp, wp->w_cursor.lnum, &top, &bot)) { top--; bot++; } else { - top = curwin->w_cursor.lnum - 1; - bot = curwin->w_cursor.lnum + 1; + top = wp->w_cursor.lnum - 1; + bot = wp->w_cursor.lnum + 1; } linenr_T new_topline = top + 1; // "used" already contains the number of filler lines above, don't add it // again. // Hide filler lines above cursor line by adding them to "extra". - int extra = win_get_fill(curwin, curwin->w_cursor.lnum); + int extra = win_get_fill(wp, wp->w_cursor.lnum); // Check if the lines from "top" to "bot" fit in the window. If they do, // set new_topline and advance "top" and "bot" to include more lines. while (top > 0) { - int i = hasFolding(top, &top, NULL) + int i = hasFolding(wp, top, &top, NULL) ? 1 // count one logical line for a sequence of folded lines - : plines_win_nofill(curwin, top, true); - if (top < curwin->w_topline) { + : plines_win_nofill(wp, top, true); + if (top < wp->w_topline) { scrolled += i; } // If scrolling is needed, scroll at least 'sj' lines. - if ((new_topline >= curwin->w_topline || scrolled > min_scroll) && extra >= off) { + if ((new_topline >= wp->w_topline || scrolled > min_scroll) && extra >= off) { break; } used += i; - if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) { - if (hasFolding(bot, NULL, &bot)) { + if (extra + i <= off && bot < wp->w_buffer->b_ml.ml_line_count) { + if (hasFolding(wp, bot, NULL, &bot)) { // count one logical line for a sequence of folded lines used++; } else { - used += plines_win(curwin, bot, true); + used += plines_win(wp, bot, true); } } - if (used > curwin->w_height_inner) { + if (used > wp->w_height_inner) { break; } @@ -1821,43 +1843,43 @@ void scroll_cursor_top(int min_scroll, int always) // If we don't have enough space, put cursor in the middle. // This makes sure we get the same position when using "k" and "j" // in a small window. - if (used > curwin->w_height_inner) { - scroll_cursor_halfway(false, false); + if (used > wp->w_height_inner) { + scroll_cursor_halfway(wp, false, false); } else { // If "always" is false, only adjust topline to a lower value, higher // value may happen with wrapping lines. - if (new_topline < curwin->w_topline || always) { - curwin->w_topline = new_topline; + if (new_topline < wp->w_topline || always) { + wp->w_topline = new_topline; } - if (curwin->w_topline > curwin->w_cursor.lnum) { - curwin->w_topline = curwin->w_cursor.lnum; + if (wp->w_topline > wp->w_cursor.lnum) { + wp->w_topline = wp->w_cursor.lnum; } - curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); - if (curwin->w_topfill > 0 && extra > off) { - curwin->w_topfill -= extra - off; - if (curwin->w_topfill < 0) { - curwin->w_topfill = 0; + wp->w_topfill = win_get_fill(wp, wp->w_topline); + if (wp->w_topfill > 0 && extra > off) { + wp->w_topfill -= extra - off; + if (wp->w_topfill < 0) { + wp->w_topfill = 0; } } - check_topfill(curwin, false); - if (curwin->w_topline != old_topline) { - reset_skipcol(curwin); - } else if (curwin->w_topline == curwin->w_cursor.lnum) { - validate_virtcol(); - if (curwin->w_skipcol >= curwin->w_virtcol) { + check_topfill(wp, false); + if (wp->w_topline != old_topline) { + reset_skipcol(wp); + } else if (wp->w_topline == wp->w_cursor.lnum) { + validate_virtcol(wp); + if (wp->w_skipcol >= wp->w_virtcol) { // TODO(vim): if the line doesn't fit may optimize w_skipcol instead // of making it zero - reset_skipcol(curwin); + reset_skipcol(wp); } } - if (curwin->w_topline != old_topline - || curwin->w_skipcol != old_skipcol - || curwin->w_topfill != old_topfill) { - curwin->w_valid &= + if (wp->w_topline != old_topline + || wp->w_skipcol != old_skipcol + || wp->w_topfill != old_topfill) { + wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); } - curwin->w_valid |= VALID_TOPLINE; - curwin->w_viewport_invalid = true; + wp->w_valid |= VALID_TOPLINE; + wp->w_viewport_invalid = true; } } @@ -1886,79 +1908,76 @@ void set_empty_rows(win_T *wp, int used) /// When scrolling scroll at least "min_scroll" lines. /// If "set_topbot" is true, set topline and botline first (for "zb"). /// This is messy stuff!!! -void scroll_cursor_bot(int min_scroll, bool set_topbot) +void scroll_cursor_bot(win_T *wp, int min_scroll, bool set_topbot) { lineoff_T loff; - linenr_T old_topline = curwin->w_topline; - int old_skipcol = curwin->w_skipcol; - int old_topfill = curwin->w_topfill; - linenr_T old_botline = curwin->w_botline; - int old_valid = curwin->w_valid; - int old_empty_rows = curwin->w_empty_rows; - linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number - bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; + linenr_T old_topline = wp->w_topline; + int old_skipcol = wp->w_skipcol; + int old_topfill = wp->w_topfill; + linenr_T old_botline = wp->w_botline; + int old_valid = wp->w_valid; + int old_empty_rows = wp->w_empty_rows; + linenr_T cln = wp->w_cursor.lnum; // Cursor Line Number + bool do_sms = wp->w_p_wrap && wp->w_p_sms; if (set_topbot) { - bool set_skipcol = false; - int used = 0; - curwin->w_botline = cln + 1; + wp->w_botline = cln + 1; + loff.lnum = cln + 1; loff.fill = 0; - for (curwin->w_topline = curwin->w_botline; - curwin->w_topline > 1; - curwin->w_topline = loff.lnum) { - loff.lnum = curwin->w_topline; - topline_back_winheight(curwin, &loff, false); + while (true) { + topline_back_winheight(wp, &loff, false); if (loff.height == MAXCOL) { break; } - if (used + loff.height > curwin->w_height_inner) { + if (used + loff.height > wp->w_height_inner) { if (do_sms) { // 'smoothscroll' and 'wrap' are set. The above line is // too long to show in its entirety, so we show just a part // of it. - if (used < curwin->w_height_inner) { - int plines_offset = used + loff.height - curwin->w_height_inner; - used = curwin->w_height_inner; - curwin->w_topfill = loff.fill; - curwin->w_topline = loff.lnum; - curwin->w_skipcol = skipcol_from_plines(curwin, plines_offset); - set_skipcol = true; + if (used < wp->w_height_inner) { + int plines_offset = used + loff.height - wp->w_height_inner; + used = wp->w_height_inner; + wp->w_topfill = loff.fill; + wp->w_topline = loff.lnum; + wp->w_skipcol = skipcol_from_plines(wp, plines_offset); } } break; } + wp->w_topfill = loff.fill; + wp->w_topline = loff.lnum; used += loff.height; - curwin->w_topfill = loff.fill; - } - set_empty_rows(curwin, used); - curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; - if (curwin->w_topline != old_topline - || curwin->w_topfill != old_topfill - || set_skipcol - || curwin->w_skipcol != 0) { - curwin->w_valid &= ~(VALID_WROW|VALID_CROW); - if (set_skipcol) { - redraw_later(curwin, UPD_NOT_VALID); + } + + set_empty_rows(wp, used); + wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; + if (wp->w_topline != old_topline + || wp->w_topfill != old_topfill + || wp->w_skipcol != old_skipcol + || wp->w_skipcol != 0) { + wp->w_valid &= ~(VALID_WROW|VALID_CROW); + if (wp->w_skipcol != old_skipcol) { + redraw_later(wp, UPD_NOT_VALID); } else { - reset_skipcol(curwin); + reset_skipcol(wp); } } } else { - validate_botline(curwin); + validate_botline(wp); } // The lines of the cursor line itself are always used. - int used = plines_win_nofill(curwin, cln, true); + int used = plines_win_nofill(wp, cln, true); int scrolled = 0; // If the cursor is on or below botline, we will at least scroll by the // height of the cursor line, which is "used". Correct for empty lines, // which are really part of botline. - if (cln >= curwin->w_botline) { + if (cln >= wp->w_botline) { scrolled = used; - if (cln == curwin->w_botline) { - scrolled -= curwin->w_empty_rows; + if (cln == wp->w_botline) { + scrolled -= wp->w_empty_rows; } if (do_sms) { // 'smoothscroll' and 'wrap' are set. @@ -1966,21 +1985,23 @@ void scroll_cursor_bot(int min_scroll, bool set_topbot) // occupies. If it is occupying more than the entire window, we // need to scroll the additional clipped lines to scroll past the // top line before we can move on to the other lines. - int top_plines = plines_win_nofill(curwin, curwin->w_topline, false); - int skip_lines = 0; - int width1 = curwin->w_width_inner - curwin_col_off(); + int top_plines = plines_win_nofill(wp, wp->w_topline, false); + int width1 = wp->w_width_inner - win_col_off(wp); + if (width1 > 0) { - int width2 = width1 + curwin_col_off2(); - // similar formula is used in curs_columns() - if (curwin->w_skipcol > width1) { - skip_lines += (curwin->w_skipcol - width1) / width2 + 1; - } else if (curwin->w_skipcol > 0) { + int width2 = width1 + win_col_off2(wp); + int skip_lines = 0; + + // A similar formula is used in curs_columns(). + if (wp->w_skipcol > width1) { + skip_lines += (wp->w_skipcol - width1) / width2 + 1; + } else if (wp->w_skipcol > 0) { skip_lines = 1; } top_plines -= skip_lines; - if (top_plines > curwin->w_height_inner) { - scrolled += (top_plines - curwin->w_height_inner); + if (top_plines > wp->w_height_inner) { + scrolled += (top_plines - wp->w_height_inner); } } } @@ -1992,67 +2013,67 @@ void scroll_cursor_bot(int min_scroll, bool set_topbot) // - scrolled nothing or at least 'sj' lines // - at least 'so' lines below the cursor // - lines between botline and cursor have been counted - if (!hasFolding(curwin->w_cursor.lnum, &loff.lnum, &boff.lnum)) { + if (!hasFolding(wp, wp->w_cursor.lnum, &loff.lnum, &boff.lnum)) { loff.lnum = cln; boff.lnum = cln; } loff.fill = 0; boff.fill = 0; - int fill_below_window = win_get_fill(curwin, curwin->w_botline) - curwin->w_filler_rows; + int fill_below_window = win_get_fill(wp, wp->w_botline) - wp->w_filler_rows; int extra = 0; - int so = get_scrolloff_value(curwin); + int so = get_scrolloff_value(wp); while (loff.lnum > 1) { // Stop when scrolled nothing or at least "min_scroll", found "extra" // context for 'scrolloff' and counted all lines below the window. if ((((scrolled <= 0 || scrolled >= min_scroll) && extra >= (mouse_dragging > 0 ? mouse_dragging - 1 : so)) - || boff.lnum + 1 > curbuf->b_ml.ml_line_count) - && loff.lnum <= curwin->w_botline - && (loff.lnum < curwin->w_botline + || boff.lnum + 1 > wp->w_buffer->b_ml.ml_line_count) + && loff.lnum <= wp->w_botline + && (loff.lnum < wp->w_botline || loff.fill >= fill_below_window)) { break; } // Add one line above - topline_back(curwin, &loff); + topline_back(wp, &loff); if (loff.height == MAXCOL) { used = MAXCOL; } else { used += loff.height; } - if (used > curwin->w_height_inner) { + if (used > wp->w_height_inner) { break; } - if (loff.lnum >= curwin->w_botline - && (loff.lnum > curwin->w_botline + if (loff.lnum >= wp->w_botline + && (loff.lnum > wp->w_botline || loff.fill <= fill_below_window)) { // Count screen lines that are below the window. scrolled += loff.height; - if (loff.lnum == curwin->w_botline + if (loff.lnum == wp->w_botline && loff.fill == 0) { - scrolled -= curwin->w_empty_rows; + scrolled -= wp->w_empty_rows; } } - if (boff.lnum < curbuf->b_ml.ml_line_count) { + if (boff.lnum < wp->w_buffer->b_ml.ml_line_count) { // Add one line below - botline_forw(curwin, &boff); + botline_forw(wp, &boff); used += boff.height; - if (used > curwin->w_height_inner) { + if (used > wp->w_height_inner) { break; } if (extra < (mouse_dragging > 0 ? mouse_dragging - 1 : so) || scrolled < min_scroll) { extra += boff.height; - if (boff.lnum >= curwin->w_botline - || (boff.lnum + 1 == curwin->w_botline - && boff.fill > curwin->w_filler_rows)) { + if (boff.lnum >= wp->w_botline + || (boff.lnum + 1 == wp->w_botline + && boff.fill > wp->w_filler_rows)) { // Count screen lines that are below the window. scrolled += boff.height; - if (boff.lnum == curwin->w_botline + if (boff.lnum == wp->w_botline && boff.fill == 0) { - scrolled -= curwin->w_empty_rows; + scrolled -= wp->w_empty_rows; } } } @@ -2060,77 +2081,82 @@ void scroll_cursor_bot(int min_scroll, bool set_topbot) } linenr_T line_count; - // curwin->w_empty_rows is larger, no need to scroll + // wp->w_empty_rows is larger, no need to scroll if (scrolled <= 0) { line_count = 0; // more than a screenfull, don't scroll but redraw - } else if (used > curwin->w_height_inner) { + } else if (used > wp->w_height_inner) { line_count = used; // scroll minimal number of lines } else { line_count = 0; - boff.fill = curwin->w_topfill; - boff.lnum = curwin->w_topline - 1; + boff.fill = wp->w_topfill; + boff.lnum = wp->w_topline - 1; int i; - for (i = 0; i < scrolled && boff.lnum < curwin->w_botline;) { - botline_forw(curwin, &boff); + for (i = 0; i < scrolled && boff.lnum < wp->w_botline;) { + botline_forw(wp, &boff); i += boff.height; line_count++; } - if (i < scrolled) { // below curwin->w_botline, don't scroll + if (i < scrolled) { // below wp->w_botline, don't scroll line_count = 9999; } } // Scroll up if the cursor is off the bottom of the screen a bit. // Otherwise put it at 1/2 of the screen. - if (line_count >= curwin->w_height_inner && line_count > min_scroll) { - scroll_cursor_halfway(false, true); + if (line_count >= wp->w_height_inner && line_count > min_scroll) { + scroll_cursor_halfway(wp, false, true); } else if (line_count > 0) { if (do_sms) { - scrollup(scrolled, true); // TODO(vim): + scrollup(wp, scrolled, true); // TODO(vim): } else { - scrollup(line_count, true); + scrollup(wp, line_count, true); } } // If topline didn't change we need to restore w_botline and w_empty_rows // (we changed them). // If topline did change, update_screen() will set botline. - if (curwin->w_topline == old_topline && curwin->w_skipcol == old_skipcol && set_topbot) { - curwin->w_botline = old_botline; - curwin->w_empty_rows = old_empty_rows; - curwin->w_valid = old_valid; + if (wp->w_topline == old_topline && wp->w_skipcol == old_skipcol && set_topbot) { + wp->w_botline = old_botline; + wp->w_empty_rows = old_empty_rows; + wp->w_valid = old_valid; + } + wp->w_valid |= VALID_TOPLINE; + wp->w_viewport_invalid = true; + + // Make sure cursor is still visible after adjusting skipcol for "zb". + if (set_topbot) { + cursor_correct_sms(wp); } - curwin->w_valid |= VALID_TOPLINE; - curwin->w_viewport_invalid = true; } /// Recompute topline to put the cursor halfway across the window /// /// @param atend if true, also put the cursor halfway to the end of the file. /// -void scroll_cursor_halfway(bool atend, bool prefer_above) +void scroll_cursor_halfway(win_T *wp, bool atend, bool prefer_above) { - linenr_T old_topline = curwin->w_topline; - lineoff_T loff = { .lnum = curwin->w_cursor.lnum }; - lineoff_T boff = { .lnum = curwin->w_cursor.lnum }; - hasFolding(loff.lnum, &loff.lnum, &boff.lnum); - int used = plines_win_nofill(curwin, loff.lnum, true); + linenr_T old_topline = wp->w_topline; + lineoff_T loff = { .lnum = wp->w_cursor.lnum }; + lineoff_T boff = { .lnum = wp->w_cursor.lnum }; + hasFolding(wp, loff.lnum, &loff.lnum, &boff.lnum); + int used = plines_win_nofill(wp, loff.lnum, true); loff.fill = 0; boff.fill = 0; linenr_T topline = loff.lnum; colnr_T skipcol = 0; int want_height; - bool do_sms = curwin->w_p_wrap && curwin->w_p_sms; + bool do_sms = wp->w_p_wrap && wp->w_p_sms; if (do_sms) { // 'smoothscroll' and 'wrap' are set if (atend) { - want_height = (curwin->w_height_inner - used) / 2; + want_height = (wp->w_height_inner - used) / 2; used = 0; } else { - want_height = curwin->w_height_inner; + want_height = wp->w_height_inner; } } @@ -2139,20 +2165,20 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) // If using smoothscroll, we can precisely scroll to the // exact point where the cursor is halfway down the screen. if (do_sms) { - topline_back_winheight(curwin, &loff, false); + topline_back_winheight(wp, &loff, false); if (loff.height == MAXCOL) { break; } used += loff.height; - if (!atend && boff.lnum < curbuf->b_ml.ml_line_count) { - botline_forw(curwin, &boff); + if (!atend && boff.lnum < wp->w_buffer->b_ml.ml_line_count) { + botline_forw(wp, &boff); used += boff.height; } if (used > want_height) { if (used - loff.height < want_height) { topline = loff.lnum; topfill = loff.fill; - skipcol = skipcol_from_plines(curwin, used - want_height); + skipcol = skipcol_from_plines(wp, used - want_height); } break; } @@ -2176,10 +2202,10 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) ? (round == 2 && below < above) : (round == 1 && below <= above)) { // add a line below the cursor - if (boff.lnum < curbuf->b_ml.ml_line_count) { - botline_forw(curwin, &boff); + if (boff.lnum < wp->w_buffer->b_ml.ml_line_count) { + botline_forw(wp, &boff); used += boff.height; - if (used > curwin->w_height_inner) { + if (used > wp->w_height_inner) { done = true; break; } @@ -2196,13 +2222,13 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) ? (round == 1 && below >= above) : (round == 1 && below > above)) { // add a line above the cursor - topline_back(curwin, &loff); + topline_back(wp, &loff); if (loff.height == MAXCOL) { used = MAXCOL; } else { used += loff.height; } - if (used > curwin->w_height_inner) { + if (used > wp->w_height_inner) { done = true; break; } @@ -2216,51 +2242,51 @@ void scroll_cursor_halfway(bool atend, bool prefer_above) } } - if (!hasFolding(topline, &curwin->w_topline, NULL) - && (curwin->w_topline != topline || skipcol != 0 || curwin->w_skipcol != 0)) { - curwin->w_topline = topline; + if (!hasFolding(wp, topline, &wp->w_topline, NULL) + && (wp->w_topline != topline || skipcol != 0 || wp->w_skipcol != 0)) { + wp->w_topline = topline; if (skipcol != 0) { - curwin->w_skipcol = skipcol; - redraw_later(curwin, UPD_NOT_VALID); + wp->w_skipcol = skipcol; + redraw_later(wp, UPD_NOT_VALID); } else if (do_sms) { - reset_skipcol(curwin); + reset_skipcol(wp); } } - curwin->w_topfill = topfill; - if (old_topline > curwin->w_topline + curwin->w_height_inner) { - curwin->w_botfill = false; + wp->w_topfill = topfill; + if (old_topline > wp->w_topline + wp->w_height_inner) { + wp->w_botfill = false; } - check_topfill(curwin, false); - curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); - curwin->w_valid |= VALID_TOPLINE; + check_topfill(wp, false); + wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); + wp->w_valid |= VALID_TOPLINE; } // Correct the cursor position so that it is in a part of the screen at least // 'so' lines from the top and bottom, if possible. // If not possible, put it at the same position as scroll_cursor_halfway(). // When called topline must be valid! -void cursor_correct(void) +void cursor_correct(win_T *wp) { // 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 = get_scrolloff_value(curwin); - int below_wanted = get_scrolloff_value(curwin); + int above_wanted = get_scrolloff_value(wp); + int below_wanted = get_scrolloff_value(wp); if (mouse_dragging > 0) { above_wanted = mouse_dragging - 1; below_wanted = mouse_dragging - 1; } - if (curwin->w_topline == 1) { + if (wp->w_topline == 1) { above_wanted = 0; - int max_off = curwin->w_height_inner / 2; + int max_off = wp->w_height_inner / 2; if (below_wanted > max_off) { below_wanted = max_off; } } - validate_botline(curwin); - if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1 + validate_botline(wp); + if (wp->w_botline == wp->w_buffer->b_ml.ml_line_count + 1 && mouse_dragging == 0) { below_wanted = 0; - int max_off = (curwin->w_height_inner - 1) / 2; + int max_off = (wp->w_height_inner - 1) / 2; if (above_wanted > max_off) { above_wanted = max_off; } @@ -2268,18 +2294,18 @@ void cursor_correct(void) // If there are sufficient file-lines above and below the cursor, we can // return now. - linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number - if (cln >= curwin->w_topline + above_wanted - && cln < curwin->w_botline - below_wanted - && !hasAnyFolding(curwin)) { + linenr_T cln = wp->w_cursor.lnum; // Cursor Line Number + if (cln >= wp->w_topline + above_wanted + && cln < wp->w_botline - below_wanted + && !hasAnyFolding(wp)) { return; } - if (curwin->w_p_sms && !curwin->w_p_wrap) { + if (wp->w_p_sms && !wp->w_p_wrap) { // 'smoothscroll' is active - if (curwin->w_cline_height == curwin->w_height_inner) { + if (wp->w_cline_height == wp->w_height_inner) { // The cursor line just fits in the window, don't scroll. - reset_skipcol(curwin); + reset_skipcol(wp); return; } // TODO(vim): If the cursor line doesn't fit in the window then only adjust w_skipcol. @@ -2289,419 +2315,236 @@ void cursor_correct(void) // the top and the bottom until: // - the desired context lines are found // - the lines from the top is past the lines from the bottom - linenr_T topline = curwin->w_topline; - linenr_T botline = curwin->w_botline - 1; + linenr_T topline = wp->w_topline; + linenr_T botline = wp->w_botline - 1; // count filler lines as context - int above = curwin->w_topfill; // screen lines above topline - int below = curwin->w_filler_rows; // screen lines below botline + int above = wp->w_topfill; // screen lines above topline + int below = wp->w_filler_rows; // screen lines below botline while ((above < above_wanted || below < below_wanted) && topline < botline) { if (below < below_wanted && (below <= above || above >= above_wanted)) { - if (hasFolding(botline, &botline, NULL)) { + if (hasFolding(wp, botline, &botline, NULL)) { below++; } else { - below += plines_win(curwin, botline, true); + below += plines_win(wp, botline, true); } botline--; } if (above < above_wanted && (above < below || below >= below_wanted)) { - if (hasFolding(topline, NULL, &topline)) { + if (hasFolding(wp, topline, NULL, &topline)) { above++; } else { - above += plines_win_nofill(curwin, topline, true); + above += plines_win_nofill(wp, topline, true); } // Count filler lines below this line as context. if (topline < botline) { - above += win_get_fill(curwin, topline + 1); + above += win_get_fill(wp, topline + 1); } topline++; } } if (topline == botline || botline == 0) { - curwin->w_cursor.lnum = topline; + wp->w_cursor.lnum = topline; } else if (topline > botline) { - curwin->w_cursor.lnum = botline; + wp->w_cursor.lnum = botline; } else { - if (cln < topline && curwin->w_topline > 1) { - curwin->w_cursor.lnum = topline; - curwin->w_valid &= + if (cln < topline && wp->w_topline > 1) { + wp->w_cursor.lnum = topline; + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW); } - if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = botline; - curwin->w_valid &= + if (cln > botline && wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) { + wp->w_cursor.lnum = botline; + wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW); } } - curwin->w_valid |= VALID_TOPLINE; - curwin->w_viewport_invalid = true; + wp->w_valid |= VALID_TOPLINE; + wp->w_viewport_invalid = true; } -/// Move screen "count" pages up ("dir" is BACKWARD) or down ("dir" is FORWARD) -/// and update the screen. +/// Decide how much overlap to use for page-up or page-down scrolling. +/// This is symmetric, so that doing both keeps the same lines displayed. +/// Three lines are examined: /// -/// @return FAIL for failure, OK otherwise. -int onepage(Direction dir, int count) +/// before CTRL-F after CTRL-F / before CTRL-B +/// etc. l1 +/// l1 last but one line ------------ +/// l2 last text line l2 top text line +/// ------------- l3 second text line +/// l3 etc. +static int get_scroll_overlap(Direction dir) { - int retval = OK; lineoff_T loff; - linenr_T old_topline = curwin->w_topline; - int so = get_scrolloff_value(curwin); + int min_height = curwin->w_height_inner - 2; - if (curbuf->b_ml.ml_line_count == 1) { // nothing to do - beep_flush(); - return FAIL; + validate_botline(curwin); + if ((dir == BACKWARD && curwin->w_topline == 1) + || (dir == FORWARD && curwin->w_botline > curbuf->b_ml.ml_line_count)) { + return min_height + 2; // no overlap, still handle 'smoothscroll' } - 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) - : (curwin->w_topline == 1 - && curwin->w_topfill == win_get_fill(curwin, curwin->w_topline))) { - beep_flush(); - retval = FAIL; - break; - } - - loff.fill = 0; - if (dir == FORWARD) { - if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) { - // Vi compatible scrolling - if (p_window <= 2) { - curwin->w_topline++; - } else { - curwin->w_topline += (linenr_T)p_window - 2; - } - if (curwin->w_topline > curbuf->b_ml.ml_line_count) { - curwin->w_topline = curbuf->b_ml.ml_line_count; - } - curwin->w_cursor.lnum = curwin->w_topline; - } else if (curwin->w_botline > curbuf->b_ml.ml_line_count) { - // at end of file - curwin->w_topline = curbuf->b_ml.ml_line_count; - curwin->w_topfill = 0; - curwin->w_valid &= ~(VALID_WROW|VALID_CROW); - } else { - // For the overlap, start with the line just below the window - // and go upwards. - loff.lnum = curwin->w_botline; - loff.fill = win_get_fill(curwin, loff.lnum) - - curwin->w_filler_rows; - get_scroll_overlap(&loff, -1); - curwin->w_topline = loff.lnum; - curwin->w_topfill = loff.fill; - check_topfill(curwin, false); - curwin->w_cursor.lnum = curwin->w_topline; - curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW| - VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); - } - } else { // dir == BACKWARDS - if (curwin->w_topline == 1) { - // Include max number of filler lines - max_topfill(); - continue; - } - if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) { - // Vi compatible scrolling (sort of) - if (p_window <= 2) { - curwin->w_topline--; - } else { - curwin->w_topline -= (linenr_T)p_window - 2; - } - if (curwin->w_topline < 1) { - curwin->w_topline = 1; - } - curwin->w_cursor.lnum = curwin->w_topline + (linenr_T)p_window - 1; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } - continue; - } - - // Find the line at the top of the window that is going to be the - // line at the bottom of the window. Make sure this results in - // the same line as before doing CTRL-F. - loff.lnum = curwin->w_topline - 1; - loff.fill = win_get_fill(curwin, loff.lnum + 1) - curwin->w_topfill; - get_scroll_overlap(&loff, 1); - - if (loff.lnum >= curbuf->b_ml.ml_line_count) { - loff.lnum = curbuf->b_ml.ml_line_count; - loff.fill = 0; - } else { - botline_topline(&loff); - } - curwin->w_cursor.lnum = loff.lnum; - - // Find the line just above the new topline to get the right line - // at the bottom of the window. - int n = 0; - while (n <= curwin->w_height_inner && loff.lnum >= 1) { - topline_back(curwin, &loff); - if (loff.height == MAXCOL) { - n = MAXCOL; - } else { - n += loff.height; - } - } - if (loff.lnum < 1) { // at begin of file - curwin->w_topline = 1; - max_topfill(); - curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); - } else { - // Go two lines forward again. - topline_botline(&loff); - botline_forw(curwin, &loff); - botline_forw(curwin, &loff); - botline_topline(&loff); - // We're at the wrong end of a fold now. - hasFoldingWin(curwin, loff.lnum, &loff.lnum, NULL, true, NULL); - - // Always scroll at least one line. Avoid getting stuck on - // very long lines. - if (loff.lnum >= curwin->w_topline - && (loff.lnum > curwin->w_topline - || loff.fill >= curwin->w_topfill)) { - // First try using the maximum number of filler lines. If - // that's not enough, backup one line. - loff.fill = curwin->w_topfill; - if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { - max_topfill(); - } - if (curwin->w_topfill == loff.fill) { - curwin->w_topline--; - curwin->w_topfill = 0; - curwin->w_valid &= ~(VALID_WROW|VALID_CROW); - } - comp_botline(curwin); - curwin->w_cursor.lnum = curwin->w_botline - 1; - curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|VALID_CROW); - } else { - curwin->w_topline = loff.lnum; - curwin->w_topfill = loff.fill; - check_topfill(curwin, false); - curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE); - } - } - } - } - foldAdjustCursor(); - cursor_correct(); - check_cursor_col(); - if (retval == OK) { - beginline(BL_SOL | BL_FIX); - } - curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL); - - if (retval == OK && dir == FORWARD) { - // Avoid the screen jumping up and down when 'scrolloff' is non-zero. - // But make sure we scroll at least one line (happens with mix of long - // wrapping lines and non-wrapping line). - if (check_top_offset()) { - scroll_cursor_top(1, false); - if (curwin->w_topline <= old_topline - && old_topline < curbuf->b_ml.ml_line_count) { - curwin->w_topline = old_topline + 1; - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); - } - } else if (curwin->w_botline > curbuf->b_ml.ml_line_count) { - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); - } - } + loff.lnum = dir == FORWARD ? curwin->w_botline : curwin->w_topline - 1; + loff.fill = win_get_fill(curwin, loff.lnum + (dir == BACKWARD)) + - (dir == FORWARD ? curwin->w_filler_rows : curwin->w_topfill); + loff.height = loff.fill > 0 ? 1 : plines_win_nofill(curwin, loff.lnum, true); - redraw_later(curwin, UPD_VALID); - return retval; -} - -// Decide how much overlap to use for page-up or page-down scrolling. -// This is symmetric, so that doing both keeps the same lines displayed. -// Three lines are examined: -// -// before CTRL-F after CTRL-F / before CTRL-B -// etc. l1 -// l1 last but one line ------------ -// l2 last text line l2 top text line -// ------------- l3 second text line -// l3 etc. -static void get_scroll_overlap(lineoff_T *lp, int dir) -{ - int min_height = curwin->w_height_inner - 2; - - if (lp->fill > 0) { - lp->height = 1; - } else { - lp->height = plines_win_nofill(curwin, lp->lnum, true); - } - int h1 = lp->height; + int h1 = loff.height; if (h1 > min_height) { - return; // no overlap + return min_height + 2; // no overlap } - lineoff_T loff0 = *lp; - if (dir > 0) { - botline_forw(curwin, lp); + if (dir == FORWARD) { + topline_back(curwin, &loff); } else { - topline_back(curwin, lp); + botline_forw(curwin, &loff); } - int h2 = lp->height; + + int h2 = loff.height; if (h2 == MAXCOL || h2 + h1 > min_height) { - *lp = loff0; // no overlap - return; + return min_height + 2; // no overlap } - - lineoff_T loff1 = *lp; - if (dir > 0) { - botline_forw(curwin, lp); + if (dir == FORWARD) { + topline_back(curwin, &loff); } else { - topline_back(curwin, lp); + botline_forw(curwin, &loff); } - int h3 = lp->height; + + int h3 = loff.height; if (h3 == MAXCOL || h3 + h2 > min_height) { - *lp = loff0; // no overlap - return; + return min_height + 2; // no overlap } - - lineoff_T loff2 = *lp; - if (dir > 0) { - botline_forw(curwin, lp); + if (dir == FORWARD) { + topline_back(curwin, &loff); } else { - topline_back(curwin, lp); + botline_forw(curwin, &loff); } - int h4 = lp->height; + + int h4 = loff.height; if (h4 == MAXCOL || h4 + h3 + h2 > min_height || h3 + h2 + h1 > min_height) { - *lp = loff1; // 1 line overlap + return min_height + 1; // 1 line overlap } else { - *lp = loff2; // 2 lines overlap + return min_height; // 2 lines overlap } } -// Scroll 'scroll' lines up or down. -void halfpage(bool flag, linenr_T Prenum) +/// Scroll "count" lines with 'smoothscroll' in direction "dir". Return true +/// when scrolling happened. Adjust "curscount" for scrolling different amount +/// of lines when 'smoothscroll' is disabled. +static bool scroll_with_sms(Direction dir, int count, int *curscount) { - int scrolled = 0; - int i; - - if (Prenum) { - curwin->w_p_scr = (Prenum > curwin->w_height_inner) ? curwin->w_height_inner - : Prenum; - } - assert(curwin->w_p_scr <= INT_MAX); - int n = curwin->w_p_scr <= curwin->w_height_inner ? (int)curwin->w_p_scr - : curwin->w_height_inner; + int prev_sms = curwin->w_p_sms; + colnr_T prev_skipcol = curwin->w_skipcol; + linenr_T prev_topline = curwin->w_topline; + int prev_topfill = curwin->w_topfill; + + curwin->w_p_sms = true; + scroll_redraw(dir == FORWARD, count); + + // Not actually smoothscrolling but ended up with partially visible line. + // Continue scrolling until skipcol is zero. + if (!prev_sms && curwin->w_skipcol > 0) { + int fixdir = dir; + // Reverse the scroll direction when topline already changed. One line + // extra for scrolling backward so that consuming skipcol is symmetric. + if (labs(curwin->w_topline - prev_topline) > (dir == BACKWARD)) { + fixdir = dir * -1; + } + while (curwin->w_skipcol > 0 + && curwin->w_topline < curbuf->b_ml.ml_line_count) { + scroll_redraw(fixdir == FORWARD, 1); + *curscount += (fixdir == dir ? 1 : -1); + } + } + curwin->w_p_sms = prev_sms; + + return curwin->w_topline == prev_topline + && curwin->w_topfill == prev_topfill + && curwin->w_skipcol == prev_skipcol; +} - update_topline(curwin); - validate_botline(curwin); - int room = curwin->w_empty_rows + curwin->w_filler_rows; - if (flag) { - // scroll the text up - while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count) { - if (curwin->w_topfill > 0) { - i = 1; - n--; - curwin->w_topfill--; - } else { - i = plines_win_nofill(curwin, curwin->w_topline, true); - n -= i; - if (n < 0 && scrolled > 0) { - break; - } - hasFolding(curwin->w_topline, NULL, &curwin->w_topline); - curwin->w_topline++; - curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); - - if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum++; - curwin->w_valid &= - ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL); - } +/// Move screen "count" (half) pages up ("dir" is BACKWARD) or down ("dir" is +/// FORWARD) and update the screen. Handle moving the cursor and not scrolling +/// to reveal end of buffer lines for half-page scrolling with CTRL-D and CTRL-U. +/// +/// @return FAIL for failure, OK otherwise. +int pagescroll(Direction dir, int count, bool half) +{ + int nochange = true; + int buflen = curbuf->b_ml.ml_line_count; + colnr_T prev_col = curwin->w_cursor.col; + colnr_T prev_curswant = curwin->w_curswant; + linenr_T prev_lnum = curwin->w_cursor.lnum; + oparg_T oa = { 0 }; + cmdarg_T ca = { 0 }; + ca.oap = &oa; + + if (half) { + // Scroll [count], 'scroll' or current window height lines. + if (count) { + curwin->w_p_scr = MIN(curwin->w_height_inner, count); + } + count = MIN(curwin->w_height_inner, (int)curwin->w_p_scr); + + int curscount = count; + // Adjust count so as to not reveal end of buffer lines. + if (dir == FORWARD + && (curwin->w_topline + curwin->w_height_inner + count > buflen || hasAnyFolding(curwin))) { + int n = plines_correct_topline(curwin, curwin->w_topline, NULL, false, NULL); + if (n - count < curwin->w_height_inner && curwin->w_topline < buflen) { + n += plines_m_win(curwin, curwin->w_topline + 1, buflen, curwin->w_height_inner + count); } - curwin->w_valid &= ~(VALID_CROW|VALID_WROW); - scrolled += i; - - // Correct w_botline for changed w_topline. - // Won't work when there are filler lines. - if (win_may_fill(curwin)) { - curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP); - } else { - room += i; - do { - i = plines_win(curwin, curwin->w_botline, true); - if (i > room) { - break; - } - hasFolding(curwin->w_botline, NULL, &curwin->w_botline); - curwin->w_botline++; - room -= i; - } while (curwin->w_botline <= curbuf->b_ml.ml_line_count); + if (n < curwin->w_height_inner + count) { + count = n - curwin->w_height_inner; } } - // When hit bottom of the file: move cursor down. - if (n > 0) { - if (hasAnyFolding(curwin)) { - while (--n >= 0 - && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - hasFolding(curwin->w_cursor.lnum, NULL, - &curwin->w_cursor.lnum); - curwin->w_cursor.lnum++; - } - } else { - curwin->w_cursor.lnum += n; - } - check_cursor_lnum(curwin); - } - } else { - // scroll the text down - while (n > 0 && curwin->w_topline > 1) { - if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { - i = 1; - n--; - curwin->w_topfill++; - } else { - i = plines_win_nofill(curwin, curwin->w_topline - 1, true); - n -= i; - if (n < 0 && scrolled > 0) { - break; - } - curwin->w_topline--; - hasFolding(curwin->w_topline, &curwin->w_topline, NULL); - curwin->w_topfill = 0; - } - curwin->w_valid &= ~(VALID_CROW|VALID_WROW| - VALID_BOTLINE|VALID_BOTLINE_AP); - scrolled += i; - if (curwin->w_cursor.lnum > 1) { - curwin->w_cursor.lnum--; - curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL); - } + // (Try to) scroll the window unless already at the end of the buffer. + if (count > 0) { + nochange = scroll_with_sms(dir, count, &curscount); + curwin->w_cursor.lnum = prev_lnum; + curwin->w_cursor.col = prev_col; + curwin->w_curswant = prev_curswant; } - // When hit top of the file: move cursor up. - if (n > 0) { - if (curwin->w_cursor.lnum <= (linenr_T)n) { - curwin->w_cursor.lnum = 1; - } else if (hasAnyFolding(curwin)) { - while (--n >= 0 && curwin->w_cursor.lnum > 1) { - curwin->w_cursor.lnum--; - hasFolding(curwin->w_cursor.lnum, - &curwin->w_cursor.lnum, NULL); - } - } else { - curwin->w_cursor.lnum -= n; - } + // Move the cursor the same amount of screen lines. + if (curwin->w_p_wrap) { + nv_screengo(&oa, dir, curscount); + } else if (dir == FORWARD) { + cursor_down_inner(curwin, curscount); + } else { + cursor_up_inner(curwin, curscount); } + } else { + // Scroll [count] times 'window' or current window height lines. + count *= ((ONE_WINDOW && p_window > 0 && p_window < Rows - 1) + ? MAX(1, (int)p_window - 2) : get_scroll_overlap(dir)); + nochange = scroll_with_sms(dir, count, &count); + + // Place cursor at top or bottom of window. + validate_botline(curwin); + curwin->w_cursor.lnum = (dir == FORWARD ? curwin->w_topline : curwin->w_botline - 1); + } + + if (get_scrolloff_value(curwin) > 0) { + cursor_correct(curwin); } // Move cursor to first line of closed fold. - foldAdjustCursor(); - check_topfill(curwin, !flag); - cursor_correct(); - beginline(BL_SOL | BL_FIX); - redraw_later(curwin, UPD_VALID); + foldAdjustCursor(curwin); + + nochange = nochange + && prev_col == curwin->w_cursor.col + && prev_lnum == curwin->w_cursor.lnum; + + // Error if both the viewport and cursor did not change. + if (nochange) { + beep_flush(); + } else if (!curwin->w_p_sms) { + beginline(BL_SOL | BL_FIX); + } else if (p_sol) { + nv_g_home_m_cmd(&ca); + } + + return nochange; } void do_check_cursorbind(void) @@ -2748,12 +2591,12 @@ void do_check_cursorbind(void) { int restart_edit_save = restart_edit; restart_edit = true; - check_cursor(); + check_cursor(curwin); // Avoid a scroll here for the cursor position, 'scrollbind' is // more important. if (!curwin->w_p_scb) { - validate_cursor(); + validate_cursor(curwin); } restart_edit = restart_edit_save; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index a8fde5a652..5737a0440f 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -67,8 +67,6 @@ static void log_notify(char *dir, uint64_t channel_id, const char *name) # define log_notify(...) #endif -static Set(cstr_t) event_strings = SET_INIT; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/channel.c.generated.h" #endif @@ -111,9 +109,9 @@ static Channel *find_rpc_channel(uint64_t id) return chan; } -/// Publishes an event to a channel. +/// Publishes an event to a channel (emits a notification to method `name`). /// -/// @param id Channel id. 0 means "broadcast to all subscribed channels" +/// @param id Channel id, or 0 to broadcast to all RPC channels. /// @param name Event name (application-defined) /// @param args Array of event arguments /// @return True if the event was sent successfully, false otherwise. @@ -204,41 +202,6 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem return frame.errored ? NIL : frame.result; } -/// Subscribes to event broadcasts -/// -/// @param id The channel id -/// @param event The event type string -void rpc_subscribe(uint64_t id, char *event) -{ - Channel *channel; - - if (!(channel = find_rpc_channel(id))) { - abort(); - } - - const char **key_alloc = NULL; - if (set_put_ref(cstr_t, &event_strings, event, &key_alloc)) { - *key_alloc = xstrdup(event); - } - - set_put(cstr_t, channel->rpc.subscribed_events, *key_alloc); -} - -/// Unsubscribes to event broadcasts -/// -/// @param id The channel id -/// @param event The event type string -void rpc_unsubscribe(uint64_t id, char *event) -{ - Channel *channel; - - if (!(channel = find_rpc_channel(id))) { - abort(); - } - - unsubscribe(channel, event); -} - static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c, void *data, bool eof) { Channel *channel = data; @@ -289,8 +252,9 @@ static void parse_msgpack(Channel *channel) if (p->type == kMessageTypeRedrawEvent) { // When exiting, ui_client_stop() has already been called, so don't handle UI events. if (ui_client_channel_id && !exiting) { - if (p->grid_line_event) { - ui_client_event_raw_line(p->grid_line_event); + if (p->has_grid_line_event) { + ui_client_event_raw_line(&p->grid_line_event); + p->has_grid_line_event = false; } else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) { p->ui_handler.fn(p->result.data.array); } @@ -339,7 +303,7 @@ static void parse_msgpack(Channel *channel) } if (unpacker_closed(p)) { - chan_close_with_error(channel, p->unpack_error.msg, LOGLVL_ERR); + chan_close_with_error(channel, p->unpack_error.msg, LOGLVL_INF); api_clear_error(&p->unpack_error); } } @@ -493,34 +457,24 @@ static void send_error(Channel *chan, MsgpackRpcRequestHandler handler, MessageT api_clear_error(&e); } +/// Broadcasts a notification to all RPC channels. static void broadcast_event(const char *name, Array args) { - kvec_withinit_t(Channel *, 4) subscribed = KV_INITIAL_VALUE; - kvi_init(subscribed); + kvec_withinit_t(Channel *, 4) chans = KV_INITIAL_VALUE; + kvi_init(chans); Channel *channel; map_foreach_value(&channels, channel, { - if (channel->is_rpc - && set_has(cstr_t, channel->rpc.subscribed_events, name)) { - kv_push(subscribed, channel); + if (channel->is_rpc) { + kv_push(chans, channel); } }); - if (kv_size(subscribed)) { - serialize_request(subscribed.items, kv_size(subscribed), 0, name, args); + if (kv_size(chans)) { + serialize_request(chans.items, kv_size(chans), 0, name, args); } - kvi_destroy(subscribed); -} - -static void unsubscribe(Channel *channel, char *event) -{ - if (!set_has(cstr_t, &event_strings, event)) { - WLOG("RPC: ch %" PRIu64 ": tried to unsubscribe unknown event '%s'", - channel->id, event); - return; - } - set_del(cstr_t, channel->rpc.subscribed_events, event); + kvi_destroy(chans); } /// Mark rpc state as closed, and release its reference to the channel. @@ -550,7 +504,6 @@ void rpc_free(Channel *channel) unpacker_teardown(channel->rpc.unpacker); xfree(channel->rpc.unpacker); - set_destroy(cstr_t, channel->rpc.subscribed_events); kv_destroy(channel->rpc.call_stack); api_free_dictionary(channel->rpc.info); } @@ -718,12 +671,6 @@ const char *get_client_info(Channel *chan, const char *key) #ifdef EXITFREE void rpc_free_all_mem(void) { - cstr_t key; - set_foreach(&event_strings, key, { - xfree((void *)key); - }); - set_destroy(cstr_t, &event_strings); - multiqueue_free(ch_before_blocking_events); } #endif diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h index 56dbb332e0..7dc1374964 100644 --- a/src/nvim/msgpack_rpc/channel_defs.h +++ b/src/nvim/msgpack_rpc/channel_defs.h @@ -37,7 +37,6 @@ typedef struct { } RequestEvent; typedef struct { - Set(cstr_t) subscribed_events[1]; bool closed; Unpacker *unpacker; uint32_t next_request_id; diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index dbb30b0c9a..28d27e8268 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -187,6 +187,8 @@ void unpacker_init(Unpacker *p) p->unpack_error = ERROR_INIT; p->arena = (Arena)ARENA_EMPTY; + + p->has_grid_line_event = false; } void unpacker_teardown(Unpacker *p) @@ -303,6 +305,7 @@ error: bool unpacker_advance(Unpacker *p) { assert(p->state >= 0); + p->has_grid_line_event = false; if (p->state == 0) { if (!unpacker_parse_header(p)) { return false; @@ -323,6 +326,7 @@ bool unpacker_advance(Unpacker *p) if (p->state == 16) { // grid_line event already unpacked + p->has_grid_line_event = true; goto done; } else { assert(p->state == 12); @@ -378,7 +382,7 @@ bool unpacker_parse_redraw(Unpacker *p) const char *data = p->read_ptr; size_t size = p->read_size; - GridLineEvent *g = p->grid_line_event; + GridLineEvent *g = &p->grid_line_event; #define NEXT_TYPE(tok, typ) \ result = mpack_rtoken(&data, &size, &tok); \ @@ -420,16 +424,10 @@ bool unpacker_parse_redraw(Unpacker *p) p->read_size = size; if (p->ui_handler.fn != ui_client_event_grid_line) { p->state = 12; - if (p->grid_line_event) { - arena_mem_free(arena_finish(&p->arena)); - p->grid_line_event = NULL; - } return true; } else { p->state = 14; p->arena = (Arena)ARENA_EMPTY; - p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true); - g = p->grid_line_event; } FALLTHROUGH; diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h index 022d778013..ed55fdd4af 100644 --- a/src/nvim/msgpack_rpc/unpacker.h +++ b/src/nvim/msgpack_rpc/unpacker.h @@ -37,7 +37,8 @@ struct Unpacker { int nevents; int ncalls; UIClientHandler ui_handler; - GridLineEvent *grid_line_event; + GridLineEvent grid_line_event; + bool has_grid_line_event; }; // unrecovareble error. unpack_error should be set! diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 8ff47097fa..8ba375f29d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -32,6 +32,7 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" +#include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" @@ -47,6 +48,7 @@ #include "nvim/mapping.h" #include "nvim/mark.h" #include "nvim/mark_defs.h" +#include "nvim/math.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memline_defs.h" @@ -748,7 +750,7 @@ static void normal_get_additional_char(NormalState *s) bool langmap_active = false; // using :lmap mappings if (repl) { State = MODE_REPLACE; // pretend Replace mode - ui_cursor_shape(); // show different cursor shape + ui_cursor_shape_no_check_conceal(); // show different cursor shape } if (lang && curbuf->b_p_iminsert == B_IMODE_LMAP) { // Allow mappings defined with ":lmap". @@ -891,8 +893,8 @@ static bool normal_get_command_count(NormalState *s) // Handle a count before a command and compute ca.count0. // Note that '0' is a command and not the start of a count, but it's // part of a count after other digits. - while ((s->c >= '1' && s->c <= '9') || (s->ca.count0 != 0 - && (s->c == K_DEL || s->c == K_KDEL || s->c == '0'))) { + while ((s->c >= '1' && s->c <= '9') + || (s->ca.count0 != 0 && (s->c == K_DEL || s->c == K_KDEL || s->c == '0'))) { if (s->c == K_DEL || s->c == K_KDEL) { s->ca.count0 /= 10; del_from_showcmd(4); // delete the digit and ~@% @@ -1009,12 +1011,12 @@ normal_end: mb_check_adjust_col(curwin); // #6203 if (curwin->w_p_scb && s->toplevel) { - validate_cursor(); // may need to update w_leftcol + validate_cursor(curwin); // may need to update w_leftcol do_check_scrollbind(true); } if (curwin->w_p_crb && s->toplevel) { - validate_cursor(); // may need to update w_leftcol + validate_cursor(curwin); // may need to update w_leftcol do_check_cursorbind(); } @@ -1078,7 +1080,7 @@ static int normal_execute(VimState *state, int key) // When "restart_edit" is set fake a "d"elete command, Insert mode will restart automatically. // Insert the typed character in the typeahead buffer, so that it can // be mapped in Insert mode. Required for ":lmap" to work. - int len = ins_char_typebuf(vgetc_char, vgetc_mod_mask); + int len = ins_char_typebuf(vgetc_char, vgetc_mod_mask, true); // When recording and gotchars() was called the character will be // recorded again, remove the previous recording. @@ -1343,7 +1345,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(curwin); - validate_cursor(); + validate_cursor(curwin); show_cursor_info_later(false); @@ -1403,6 +1405,12 @@ static int normal_check(VimState *state) normal_check_stuff_buffer(s); normal_check_interrupt(s); + // At the toplevel there is no exception handling. Discard any that + // may be hanging around (e.g. from "interrupt" at the debug prompt). + if (did_throw && !ex_normal_busy) { + discard_current_exception(); + } + if (!exmode_active) { msg_scroll = false; } @@ -1420,7 +1428,7 @@ static int normal_check(VimState *state) // Ensure curwin->w_topline and curwin->w_leftcol are up to date // before triggering a WinScrolled autocommand. update_topline(curwin); - validate_cursor(); + validate_cursor(curwin); normal_check_cursor_moved(s); normal_check_text_changed(s); @@ -1515,7 +1523,7 @@ void end_visual_mode(void) curbuf->b_visual.vi_end = curwin->w_cursor; curbuf->b_visual.vi_curswant = curwin->w_curswant; curbuf->b_visual_mode_eval = VIsual_mode; - if (!virtual_active()) { + if (!virtual_active(curwin)) { curwin->w_cursor.coladd = 0; } @@ -1863,8 +1871,8 @@ void clear_showcmd(void) bot = VIsual.lnum; } // Include closed folds as a whole. - hasFolding(top, &top, NULL); - hasFolding(bot, NULL, &bot); + hasFolding(curwin, top, &top, NULL); + hasFolding(curwin, bot, NULL, &bot); lines = bot - top + 1; if (VIsual_mode == Ctrl_V) { @@ -1960,9 +1968,16 @@ bool add_to_showcmd(int c) } } - char *p = transchar(c); - if (*p == ' ') { - STRCPY(p, "<20>"); + char *p; + char mbyte_buf[MB_MAXCHAR + 1]; + if (c <= 0x7f || !vim_isprintc(c)) { + p = transchar(c); + if (*p == ' ') { + STRCPY(p, "<20>"); + } + } else { + mbyte_buf[utf_char2bytes(c, mbyte_buf)] = NUL; + p = mbyte_buf; } size_t old_len = strlen(showcmd_buf); size_t extra_len = strlen(p); @@ -2028,8 +2043,7 @@ void pop_showcmd(void) static void display_showcmd(void) { - int len = (int)strlen(showcmd_buf); - showcmd_is_clear = (len == 0); + showcmd_is_clear = (showcmd_buf[0] == NUL); if (*p_sloc == 's') { if (showcmd_is_clear) { @@ -2050,14 +2064,11 @@ static void display_showcmd(void) return; } // 'showcmdloc' is "last" or empty - if (p_ch == 0 && !ui_has(kUIMessages)) { - return; - } if (ui_has(kUIMessages)) { MAXSIZE_TEMP_ARRAY(content, 1); MAXSIZE_TEMP_ARRAY(chunk, 2); - if (len > 0) { + if (!showcmd_is_clear) { // placeholder for future highlight support ADD_C(chunk, INTEGER_OBJ(0)); ADD_C(chunk, CSTR_AS_OBJ(showcmd_buf)); @@ -2066,13 +2077,17 @@ static void display_showcmd(void) ui_call_msg_showcmd(content); return; } + if (p_ch == 0) { + return; + } msg_grid_validate(); int showcmd_row = Rows - 1; grid_line_start(&msg_grid_adj, showcmd_row); + int len = 0; if (!showcmd_is_clear) { - grid_line_puts(sc_col, showcmd_buf, -1, HL_ATTR(HLF_MSG)); + len = grid_line_puts(sc_col, showcmd_buf, -1, HL_ATTR(HLF_MSG)); } // clear the rest of an old message by outputting up to SHOWCMD_COLS spaces @@ -2174,14 +2189,14 @@ void check_scrollbind(linenr_T topline_diff, int leftcol_diff) y = topline - curwin->w_topline; if (y > 0) { - scrollup(y, false); + scrollup(curwin, y, false); } else { - scrolldown(-y, false); + scrolldown(curwin, -y, false); } } redraw_later(curwin, UPD_VALID); - cursor_correct(); + cursor_correct(curwin); curwin->w_redr_status = true; } @@ -2258,7 +2273,7 @@ static void nv_page(cmdarg_T *cap) goto_tabpage(cap->count0); } } else { - onepage(cap->arg, cap->count1); + pagescroll(cap->arg, cap->count1, false); } } @@ -2334,13 +2349,15 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar bool incll; int searchflags = flags_arg; - size_t patlen = len + 7; - char *pat = xmalloc(patlen); + size_t patsize = len + 7; + char *pat = xmalloc(patsize); // Put "\V" before the pattern to avoid that the special meaning of "." // and "~" causes trouble. - assert(patlen <= INT_MAX); - snprintf(pat, patlen, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s", (int)len, ptr); + assert(patsize <= INT_MAX); + size_t patlen = (size_t)snprintf(pat, patsize, + vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s", + (int)len, ptr); pos_T old_pos = curwin->w_cursor; bool save_p_ws = p_ws; bool save_p_scs = p_scs; @@ -2367,7 +2384,7 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar clearpos(&found_pos); while (true) { t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD, - pat, 1, searchflags, RE_LAST, NULL); + pat, patlen, 1, searchflags, RE_LAST, NULL); if (curwin->w_cursor.lnum >= old_pos.lnum) { t = false; // match after start is failure too } @@ -2453,7 +2470,7 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar /// 'dist' must be positive. /// /// @return true if able to move cursor, false otherwise. -static bool nv_screengo(oparg_T *oap, int dir, int dist) +bool nv_screengo(oparg_T *oap, int dir, int dist) { int linelen = linetabsize(curwin, curwin->w_cursor.lnum); bool retval = true; @@ -2466,8 +2483,8 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist) oap->motion_type = kMTCharWise; oap->inclusive = (curwin->w_curswant == MAXCOL); - col_off1 = curwin_col_off(); - col_off2 = col_off1 - curwin_col_off2(); + col_off1 = win_col_off(curwin); + col_off2 = col_off1 - win_col_off2(curwin); width1 = curwin->w_width_inner - col_off1; width2 = curwin->w_width_inner - col_off2; @@ -2481,7 +2498,7 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist) // try to stick in the last column of the screen. if (curwin->w_curswant == MAXCOL) { atend = true; - validate_virtcol(); + validate_virtcol(curwin); if (width1 <= 0) { curwin->w_curswant = 0; } else { @@ -2506,7 +2523,7 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist) while (dist--) { if (dir == BACKWARD) { if (curwin->w_curswant >= width1 - && !hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + && !hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { // Move back within the line. This can give a negative value // for w_curswant if width1 < width2 (with cpoptions+=n), // which will get clipped to column 0. @@ -2533,7 +2550,7 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist) n = width1; } if (curwin->w_curswant + width2 < (colnr_T)n - && !hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + && !hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { // move forward within line curwin->w_curswant += width2; } else { @@ -2558,17 +2575,17 @@ static bool nv_screengo(oparg_T *oap, int dir, int dist) } } - if (virtual_active() && atend) { - coladvance(MAXCOL); + if (virtual_active(curwin) && atend) { + coladvance(curwin, MAXCOL); } else { - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) { // Check for landing on a character that got split at the end of the // last line. We want to advance a screenline, not end up in the same // screenline or move two screenlines. - validate_virtcol(); + validate_virtcol(curwin); colnr_T virtcol = curwin->w_virtcol; if (virtcol > (colnr_T)width1 && *get_showbreak_value(curwin) != NUL) { virtcol -= vim_strsize(get_showbreak_value(curwin)); @@ -2607,58 +2624,6 @@ void nv_scroll_line(cmdarg_T *cap) } } -/// Scroll "count" lines up or down, and redraw. -void scroll_redraw(bool up, linenr_T count) -{ - linenr_T prev_topline = curwin->w_topline; - int prev_skipcol = curwin->w_skipcol; - int prev_topfill = curwin->w_topfill; - linenr_T prev_lnum = curwin->w_cursor.lnum; - - bool moved = up - ? scrollup(count, true) - : scrolldown(count, true); - - if (get_scrolloff_value(curwin) > 0) { - // Adjust the cursor position for 'scrolloff'. Mark w_topline as - // valid, otherwise the screen jumps back at the end of the file. - cursor_correct(); - check_cursor_moved(curwin); - curwin->w_valid |= VALID_TOPLINE; - - // If moved back to where we were, at least move the cursor, otherwise - // we get stuck at one position. Don't move the cursor up if the - // first line of the buffer is already on the screen - while (curwin->w_topline == prev_topline - && curwin->w_skipcol == prev_skipcol - && curwin->w_topfill == prev_topfill) { - if (up) { - if (curwin->w_cursor.lnum > prev_lnum - || cursor_down(1, false) == false) { - break; - } - } else { - if (curwin->w_cursor.lnum < prev_lnum - || prev_topline == 1 - || cursor_up(1, false) == false) { - break; - } - } - // Mark w_topline as valid, otherwise the screen jumps back at the - // end of the file. - check_cursor_moved(curwin); - curwin->w_valid |= VALID_TOPLINE; - } - } - if (curwin->w_cursor.lnum != prev_lnum) { - coladvance(curwin->w_curswant); - } - if (moved) { - curwin->w_viewport_invalid = true; - } - redraw_later(curwin, UPD_VALID); -} - /// Get the count specified after a 'z' command. Only the 'z<CR>', 'zl', 'zh', /// 'z<Left>', and 'z<Right>' commands accept a count after 'z'. /// @return true to process the 'z' command and false to skip it. @@ -2684,11 +2649,10 @@ static bool nv_z_get_count(cmdarg_T *cap, int *nchar_arg) if (nchar == K_DEL || nchar == K_KDEL) { n /= 10; } else if (ascii_isdigit(nchar)) { - if (n > INT_MAX / 10) { + if (vim_append_digit_int(&n, nchar - '0') == FAIL) { clearopbeep(cap->oap); break; } - n = n * 10 + (nchar - '0'); } else if (nchar == CAR) { win_setheight(n); break; @@ -2748,7 +2712,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar) // off this fails and find_ident_under_cursor() is // used below. emsg_off++; - len = spell_move_to(curwin, FORWARD, true, true, NULL); + len = spell_move_to(curwin, FORWARD, SMT_ALL, true, NULL); emsg_off--; if (len != 0 && curwin->w_cursor.col <= pos.col) { ptr = ml_get_pos(&curwin->w_cursor); @@ -2803,7 +2767,7 @@ static void nv_zet(cmdarg_T *cap) } else { curwin->w_cursor.lnum = cap->count0; } - check_cursor_col(); + check_cursor_col(curwin); } switch (nchar) { @@ -2826,7 +2790,7 @@ static void nv_zet(cmdarg_T *cap) FALLTHROUGH; case 't': - scroll_cursor_top(0, true); + scroll_cursor_top(curwin, 0, true); redraw_later(curwin, UPD_VALID); set_fraction(curwin); break; @@ -2837,7 +2801,7 @@ static void nv_zet(cmdarg_T *cap) FALLTHROUGH; case 'z': - scroll_cursor_halfway(true, false); + scroll_cursor_halfway(curwin, true, false); redraw_later(curwin, UPD_VALID); set_fraction(curwin); break; @@ -2847,7 +2811,7 @@ static void nv_zet(cmdarg_T *cap) // when <count> is at bottom of window, and puts that one at // bottom of window. if (cap->count0 != 0) { - scroll_cursor_bot(0, true); + scroll_cursor_bot(curwin, 0, true); curwin->w_cursor.lnum = curwin->w_topline; } else if (curwin->w_topline == 1) { curwin->w_cursor.lnum = 1; @@ -2860,7 +2824,7 @@ static void nv_zet(cmdarg_T *cap) FALLTHROUGH; case 'b': - scroll_cursor_bot(0, true); + scroll_cursor_bot(curwin, 0, true); redraw_later(curwin, UPD_VALID); set_fraction(curwin); break; @@ -2895,7 +2859,7 @@ static void nv_zet(cmdarg_T *cap) // "zs" - scroll screen, cursor at the start case 's': if (!curwin->w_p_wrap) { - if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { col = 0; // like the cursor is in col 0 } else { getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL); @@ -2915,12 +2879,12 @@ static void nv_zet(cmdarg_T *cap) // "ze" - scroll screen, cursor at the end case 'e': if (!curwin->w_p_wrap) { - if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { col = 0; // like the cursor is in col 0 } else { getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col); } - int n = curwin->w_width_inner - curwin_col_off(); + int n = curwin->w_width_inner - win_col_off(curwin); if (col + siso < n) { col = 0; } else { @@ -2980,7 +2944,7 @@ static void nv_zet(cmdarg_T *cap) case 'E': if (foldmethodIsManual(curwin)) { clearFolding(curwin); - changed_window_setting(); + changed_window_setting(curwin); } else if (foldmethodIsMarker(curwin)) { deleteFold(curwin, 1, curbuf->b_ml.ml_line_count, true, false); } else { @@ -3005,7 +2969,7 @@ static void nv_zet(cmdarg_T *cap) // "za": open closed fold or close open fold at cursor case 'a': - if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { openFold(curwin->w_cursor, cap->count1); } else { closeFold(curwin->w_cursor, cap->count1); @@ -3015,7 +2979,7 @@ static void nv_zet(cmdarg_T *cap) // "zA": open fold at cursor recursively case 'A': - if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL)) { openFoldRecurse(curwin->w_cursor); } else { closeFoldRecurse(curwin->w_cursor); @@ -3151,11 +3115,11 @@ static void nv_zet(cmdarg_T *cap) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) { wp->w_p_fen = curwin->w_p_fen; - changed_window_setting_win(wp); + changed_window_setting(wp); } } } - changed_window_setting(); + changed_window_setting(curwin); } // Redraw when 'foldlevel' changed. @@ -3223,8 +3187,7 @@ static void nv_colon(cmdarg_T *cap) clearop(cap->oap); } else if (cap->oap->op_type != OP_NOP && (cap->oap->start.lnum > curbuf->b_ml.ml_line_count - || cap->oap->start.col > - (colnr_T)strlen(ml_get(cap->oap->start.lnum)) + || cap->oap->start.col > ml_get_len(cap->oap->start.lnum) || did_emsg)) { // The start of the operator has become invalid by the Ex command. clearopbeep(cap->oap); @@ -3335,21 +3298,22 @@ void do_nv_ident(int c1, int c2) /// 'K' normal-mode command. Get the command to lookup the keyword under the /// cursor. static size_t nv_K_getcmd(cmdarg_T *cap, char *kp, bool kp_help, bool kp_ex, char **ptr_arg, - size_t n, char *buf, size_t buf_size) + size_t n, char *buf, size_t bufsize, size_t *buflen) { if (kp_help) { // in the help buffer STRCPY(buf, "he! "); + *buflen = STRLEN_LITERAL("he! "); return n; } if (kp_ex) { + *buflen = 0; // 'keywordprg' is an ex command if (cap->count0 != 0) { // Send the count to the ex command. - snprintf(buf, buf_size, "%" PRId64, (int64_t)(cap->count0)); + *buflen = (size_t)snprintf(buf, bufsize, "%" PRId64, (int64_t)(cap->count0)); } - STRCAT(buf, kp); - STRCAT(buf, " "); + *buflen += (size_t)snprintf(buf + *buflen, bufsize - *buflen, "%s ", kp); return n; } @@ -3374,21 +3338,19 @@ static size_t nv_K_getcmd(cmdarg_T *cap, char *kp, bool kp_help, bool kp_ex, cha bool isman = (strcmp(kp, "man") == 0); bool isman_s = (strcmp(kp, "man -s") == 0); if (cap->count0 != 0 && !(isman || isman_s)) { - snprintf(buf, buf_size, ".,.+%" PRId64, (int64_t)(cap->count0 - 1)); + *buflen = (size_t)snprintf(buf, bufsize, ".,.+%" PRId64, (int64_t)(cap->count0 - 1)); } do_cmdline_cmd("tabnew"); - STRCAT(buf, "terminal "); + *buflen += (size_t)snprintf(buf + *buflen, bufsize - *buflen, "terminal "); if (cap->count0 == 0 && isman_s) { - STRCAT(buf, "man"); + *buflen += (size_t)snprintf(buf + *buflen, bufsize - *buflen, "man "); } else { - STRCAT(buf, kp); + *buflen += (size_t)snprintf(buf + *buflen, bufsize - *buflen, "%s ", kp); } - STRCAT(buf, " "); if (cap->count0 != 0 && (isman || isman_s)) { - snprintf(buf + strlen(buf), buf_size - strlen(buf), "%" PRId64, - (int64_t)cap->count0); - STRCAT(buf, " "); + *buflen += (size_t)snprintf(buf + *buflen, bufsize - *buflen, + "%" PRId64 " ", (int64_t)cap->count0); } *ptr_arg = ptr; @@ -3451,9 +3413,10 @@ static void nv_ident(cmdarg_T *cap) return; } bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command - size_t buf_size = n * 2 + 30 + strlen(kp); - char *buf = xmalloc(buf_size); + size_t bufsize = n * 2 + 30 + strlen(kp); + char *buf = xmalloc(bufsize); buf[0] = NUL; + size_t buflen = 0; switch (cmdchar) { case '*': @@ -3467,12 +3430,13 @@ static void nv_ident(cmdarg_T *cap) if (!g_cmd && vim_iswordp(ptr)) { STRCPY(buf, "\\<"); + buflen = STRLEN_LITERAL("\\<"); } no_smartcase = true; // don't use 'smartcase' now break; case 'K': - n = nv_K_getcmd(cap, kp, kp_help, kp_ex, &ptr, n, buf, buf_size); + n = nv_K_getcmd(cap, kp, kp_help, kp_ex, &ptr, n, buf, bufsize, &buflen); if (n == 0) { return; } @@ -3481,17 +3445,23 @@ static void nv_ident(cmdarg_T *cap) case ']': tag_cmd = true; STRCPY(buf, "ts "); + buflen = STRLEN_LITERAL("ts "); break; default: tag_cmd = true; if (curbuf->b_help) { STRCPY(buf, "he! "); + buflen = STRLEN_LITERAL("he! "); } else { if (g_cmd) { STRCPY(buf, "tj "); + buflen = STRLEN_LITERAL("tj "); + } else if (cap->count0 == 0) { + STRCPY(buf, "ta "); + buflen = STRLEN_LITERAL("ta "); } else { - snprintf(buf, buf_size, "%" PRId64 "ta ", (int64_t)cap->count0); + buflen = (size_t)snprintf(buf, bufsize, ":%" PRId64 "ta ", (int64_t)cap->count0); } } } @@ -3507,9 +3477,11 @@ static void nv_ident(cmdarg_T *cap) p = vim_strsave_shellescape(ptr, true, true); } xfree(ptr); - char *newbuf = xrealloc(buf, strlen(buf) + strlen(p) + 1); + size_t plen = strlen(p); + char *newbuf = xrealloc(buf, buflen + plen + 1); buf = newbuf; - STRCAT(buf, p); + STRCPY(buf + buflen, p); + buflen += plen; xfree(p); } else { char *aux_ptr; @@ -3528,12 +3500,13 @@ static void nv_ident(cmdarg_T *cap) aux_ptr = "\\|\"\n*?["; } - p = buf + strlen(buf); + p = buf + buflen; while (n-- > 0) { // put a backslash before \ and some others if (vim_strchr(aux_ptr, (uint8_t)(*ptr)) != NULL) { *p++ = '\\'; } + // When current byte is a part of multibyte character, copy all // bytes of that character. const size_t len = (size_t)(utfc_ptr2len(ptr) - 1); @@ -3543,20 +3516,21 @@ static void nv_ident(cmdarg_T *cap) *p++ = *ptr++; } *p = NUL; + buflen = (size_t)(p - buf); } // Execute the command. if (cmdchar == '*' || cmdchar == '#') { - if (!g_cmd - && vim_iswordp(mb_prevptr(get_cursor_line_ptr(), ptr))) { - STRCAT(buf, "\\>"); + if (!g_cmd && vim_iswordp(mb_prevptr(get_cursor_line_ptr(), ptr))) { + STRCPY(buf + buflen, "\\>"); + buflen += STRLEN_LITERAL("\\>"); } // put pattern in search history init_history(); - add_to_history(HIST_SEARCH, buf, true, NUL); + add_to_history(HIST_SEARCH, buf, buflen, true, NUL); - normal_search(cap, cmdchar == '*' ? '/' : '?', buf, 0, NULL); + normal_search(cap, cmdchar == '*' ? '/' : '?', buf, buflen, 0, NULL); } else { g_tag_at_cursor = true; do_cmdline_cmd(buf); @@ -3592,7 +3566,7 @@ bool get_visual_text(cmdarg_T *cap, char **pp, size_t *lenp) } if (VIsual_mode == 'V') { *pp = get_cursor_line_ptr(); - *lenp = strlen(*pp); + *lenp = (size_t)get_cursor_line_len(); } else { if (lt(curwin->w_cursor, VIsual)) { *pp = ml_get_pos(&curwin->w_cursor); @@ -3640,7 +3614,7 @@ static void nv_scroll(cmdarg_T *cap) // Count a fold for one screen line. for (n = cap->count1 - 1; n > 0 && curwin->w_cursor.lnum > curwin->w_topline; n--) { - hasFolding(curwin->w_cursor.lnum, + hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); if (curwin->w_cursor.lnum > curwin->w_topline) { curwin->w_cursor.lnum--; @@ -3669,7 +3643,7 @@ static void nv_scroll(cmdarg_T *cap) if (used >= half) { break; } - if (hasFolding(curwin->w_topline + n, NULL, &lnum)) { + if (hasFolding(curwin, curwin->w_topline + n, NULL, &lnum)) { n = lnum - curwin->w_topline; } } @@ -3682,7 +3656,7 @@ static void nv_scroll(cmdarg_T *cap) // Count a fold for one screen line. lnum = curwin->w_topline; while (n-- > 0 && lnum < curwin->w_botline - 1) { - hasFolding(lnum, NULL, &lnum); + hasFolding(curwin, lnum, NULL, &lnum); lnum++; } n = lnum - curwin->w_topline; @@ -3696,7 +3670,7 @@ static void nv_scroll(cmdarg_T *cap) // Correct for 'so', except when an operator is pending. if (cap->oap->op_type == OP_NOP) { - cursor_correct(); + cursor_correct(curwin); } beginline(BL_SOL | BL_FIX); } @@ -3721,7 +3695,7 @@ static void nv_right(cmdarg_T *cap) // In virtual edit mode, there's no such thing as "past_line", as lines // are (theoretically) infinitely long. - if (virtual_active()) { + if (virtual_active(curwin)) { past_line = false; } @@ -3764,7 +3738,7 @@ static void nv_right(cmdarg_T *cap) break; } else if (past_line) { curwin->w_set_curswant = true; - if (virtual_active()) { + if (virtual_active(curwin)) { oneright(); } else { curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr()); @@ -3806,7 +3780,7 @@ static void nv_left(cmdarg_T *cap) || (cap->cmdchar == K_LEFT && vim_strchr(p_ww, '<') != NULL)) && curwin->w_cursor.lnum > 1) { curwin->w_cursor.lnum--; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); curwin->w_set_curswant = true; // When the NL before the first char has to be deleted we @@ -3897,6 +3871,10 @@ static void nv_gotofile(cmdarg_T *cap) return; } + if (!check_can_set_curbuf_disabled()) { + return; + } + char *ptr = grab_file_name(cap->count1, &lnum); if (ptr != NULL) { @@ -3937,7 +3915,7 @@ static void nv_dollar(cmdarg_T *cap) // In virtual mode when off the edge of a line and an operator // is pending (whew!) keep the cursor where it is. // Otherwise, send it to the end of the line. - if (!virtual_active() || gchar_cursor() != NUL + if (!virtual_active(curwin) || gchar_cursor() != NUL || cap->oap->op_type == OP_NOP) { curwin->w_curswant = MAXCOL; // so we stay at the end } @@ -3973,7 +3951,7 @@ static void nv_search(cmdarg_T *cap) return; } - normal_search(cap, cap->cmdchar, cap->searchbuf, + normal_search(cap, cap->cmdchar, cap->searchbuf, strlen(cap->searchbuf), (cap->arg || !equalpos(save_cursor, curwin->w_cursor)) ? 0 : SEARCH_MARK, NULL); } @@ -3984,14 +3962,14 @@ static void nv_next(cmdarg_T *cap) { pos_T old = curwin->w_cursor; int wrapped = false; - int i = normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, &wrapped); + int i = normal_search(cap, 0, NULL, 0, SEARCH_MARK | cap->arg, &wrapped); if (i == 1 && !wrapped && equalpos(old, curwin->w_cursor)) { // Avoid getting stuck on the current cursor position, which can happen when // an offset is given and the cursor is on the last char in the buffer: // Repeat with count + 1. cap->count1 += 1; - normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, NULL); + normal_search(cap, 0, NULL, 0, SEARCH_MARK | cap->arg, NULL); cap->count1 -= 1; } } @@ -4002,7 +3980,7 @@ static void nv_next(cmdarg_T *cap) /// @param opt extra flags for do_search() /// /// @return 0 for failure, 1 for found, 2 for found and line offset added. -static int normal_search(cmdarg_T *cap, int dir, char *pat, int opt, int *wrapped) +static int normal_search(cmdarg_T *cap, int dir, char *pat, size_t patlen, int opt, int *wrapped) { searchit_arg_T sia; @@ -4012,7 +3990,7 @@ static int normal_search(cmdarg_T *cap, int dir, char *pat, int opt, int *wrappe curwin->w_set_curswant = true; CLEAR_FIELD(sia); - int i = do_search(cap->oap, dir, dir, pat, cap->count1, + int i = do_search(cap->oap, dir, dir, pat, patlen, cap->count1, opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia); if (wrapped != NULL) { *wrapped = sia.sa_wrapped; @@ -4031,7 +4009,8 @@ static int normal_search(cmdarg_T *cap, int dir, char *pat, int opt, int *wrappe // "/$" will put the cursor after the end of the line, may need to // correct that here - check_cursor(); + check_cursor(curwin); + return i; } @@ -4057,7 +4036,7 @@ static void nv_csearch(cmdarg_T *cap) curwin->w_set_curswant = true; // Include a Tab for "tx" and for "dfx". - if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD + if (gchar_cursor() == TAB && virtual_active(curwin) && cap->arg == FORWARD && (t_cmd || cap->oap->op_type != OP_NOP)) { colnr_T scol, ecol; @@ -4233,7 +4212,8 @@ static void nv_brackets(cmdarg_T *cap) (cap->cmdchar == ']' ? curwin->w_cursor.lnum + 1 : 1), - MAXLNUM); + MAXLNUM, + false); xfree(ptr); curwin->w_set_curswant = true; } @@ -4306,12 +4286,15 @@ static void nv_brackets(cmdarg_T *cap) cap->count1) == false) { clearopbeep(cap->oap); } - } else if (cap->nchar == 's' || cap->nchar == 'S') { - // "[s", "[S", "]s" and "]S": move to next spell error. + } else if (cap->nchar == 'r' || cap->nchar == 's' || cap->nchar == 'S') { + // "[r", "[s", "[S", "]r", "]s" and "]S": move to next spell error. setpcmark(); for (n = 0; n < cap->count1; n++) { if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD, - cap->nchar == 's', false, NULL) == 0) { + cap->nchar == 's' + ? SMT_ALL + : cap->nchar == 'r' ? SMT_RARE : SMT_BAD, + false, NULL) == 0) { clearopbeep(cap->oap); break; } @@ -4512,7 +4495,7 @@ static void nv_replace(cmdarg_T *cap) } // Break tabs, etc. - if (virtual_active()) { + if (virtual_active(curwin)) { if (u_save_cursor() == false) { return; } @@ -4527,9 +4510,8 @@ static void nv_replace(cmdarg_T *cap) } // Abort if not enough characters to replace. - char *ptr = get_cursor_pos_ptr(); - if (strlen(ptr) < (unsigned)cap->count1 - || (mb_charlen(ptr) < cap->count1)) { + if ((size_t)get_cursor_pos_len() < (unsigned)cap->count1 + || (mb_charlen(get_cursor_pos_ptr()) < cap->count1)) { clearopbeep(cap->oap); return; } @@ -4625,7 +4607,7 @@ static void v_swap_corners(int cmdchar) pos_T old_cursor = curwin->w_cursor; getvcols(curwin, &old_cursor, &VIsual, &left, &right); curwin->w_cursor.lnum = VIsual.lnum; - coladvance(left); + coladvance(curwin, left); VIsual = curwin->w_cursor; curwin->w_cursor.lnum = old_cursor.lnum; @@ -4635,20 +4617,20 @@ static void v_swap_corners(int cmdchar) if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e') { curwin->w_curswant++; } - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); if (curwin->w_cursor.col == old_cursor.col - && (!virtual_active() + && (!virtual_active(curwin) || curwin->w_cursor.coladd == old_cursor.coladd)) { curwin->w_cursor.lnum = VIsual.lnum; if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e') { right++; } - coladvance(right); + coladvance(curwin, right); VIsual = curwin->w_cursor; curwin->w_cursor.lnum = old_cursor.lnum; - coladvance(left); + coladvance(curwin, left); curwin->w_curswant = left; } } else { @@ -4678,8 +4660,8 @@ static void nv_Replace(cmdarg_T *cap) if (!MODIFIABLE(curbuf)) { emsg(_(e_modifiable)); } else { - if (virtual_active()) { - coladvance(getviscol()); + if (virtual_active(curwin)) { + coladvance(curwin, getviscol()); } invoke_edit(cap, false, cap->arg ? 'V' : 'R', false); } @@ -4713,8 +4695,8 @@ static void nv_vreplace(cmdarg_T *cap) } stuffcharReadbuff(cap->extra_char); stuffcharReadbuff(ESC); - if (virtual_active()) { - coladvance(getviscol()); + if (virtual_active(curwin)) { + coladvance(curwin, getviscol()); } invoke_edit(cap, true, 'v', false); } @@ -4761,7 +4743,7 @@ static void n_swapchar(cmdarg_T *cap) } } - check_cursor(); + check_cursor(curwin); curwin->w_set_curswant = true; if (did_change) { changed_lines(curbuf, startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1, @@ -4893,7 +4875,7 @@ static void nv_gomark(cmdarg_T *cap) move_res = nv_mark_move_to(cap, flags, fm); // May need to clear the coladd that a mark includes. - if (!virtual_active()) { + if (!virtual_active(curwin)) { curwin->w_cursor.coladd = 0; } @@ -5022,7 +5004,7 @@ static void nv_visual(cmdarg_T *cap) // was only one -- webb if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1) { curwin->w_cursor.lnum += resel_VIsual_line_count * cap->count0 - 1; - check_cursor(); + check_cursor(curwin); } VIsual_mode = resel_VIsual_mode; if (VIsual_mode == 'v') { @@ -5036,11 +5018,11 @@ static void nv_visual(cmdarg_T *cap) } else { curwin->w_curswant = resel_VIsual_vcol; } - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } if (resel_VIsual_vcol == MAXCOL) { curwin->w_curswant = MAXCOL; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } else if (VIsual_mode == Ctrl_V) { // Update curswant on the original line, that is where "col" is valid. linenr_T lnum = curwin->w_cursor.lnum; @@ -5049,7 +5031,7 @@ static void nv_visual(cmdarg_T *cap) assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX); curwin->w_curswant += resel_VIsual_vcol * cap->count0 - 1; curwin->w_cursor.lnum = lnum; - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } else { curwin->w_set_curswant = true; } @@ -5101,9 +5083,9 @@ static void n_start_visual_mode(int c) // Corner case: the 0 position in a tab may change when going into // virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting. // - if (c == Ctrl_V && (get_ve_flags() & VE_BLOCK) && gchar_cursor() == TAB) { - validate_virtcol(); - coladvance(curwin->w_virtcol); + if (c == Ctrl_V && (get_ve_flags(curwin) & VE_BLOCK) && gchar_cursor() == TAB) { + validate_virtcol(curwin); + coladvance(curwin, curwin->w_virtcol); } VIsual = curwin->w_cursor; @@ -5191,10 +5173,10 @@ static void nv_gv_cmd(cmdarg_T *cap) // Set Visual to the start and w_cursor to the end of the Visual // area. Make sure they are on an existing character. - check_cursor(); + check_cursor(curwin); VIsual = curwin->w_cursor; curwin->w_cursor = tpos; - check_cursor(); + check_cursor(curwin); update_topline(curwin); // When called from normal "g" command: start Select mode when @@ -5213,7 +5195,7 @@ static void nv_gv_cmd(cmdarg_T *cap) /// "g0", "g^" : Like "0" and "^" but for screen lines. /// "gm": middle of "g0" and "g$". -static void nv_g_home_m_cmd(cmdarg_T *cap) +void nv_g_home_m_cmd(cmdarg_T *cap) { int i; const bool flag = cap->nchar == '^'; @@ -5221,14 +5203,23 @@ static void nv_g_home_m_cmd(cmdarg_T *cap) cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; if (curwin->w_p_wrap && curwin->w_width_inner != 0) { - int width1 = curwin->w_width_inner - curwin_col_off(); - int width2 = width1 + curwin_col_off2(); + int width1 = curwin->w_width_inner - win_col_off(curwin); + int width2 = width1 + win_col_off2(curwin); - validate_virtcol(); + validate_virtcol(curwin); i = 0; if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) { i = (curwin->w_virtcol - width1) / width2 * width2 + width1; } + + // When ending up below 'smoothscroll' marker, move just beyond it so + // that skipcol is not adjusted later. + if (curwin->w_skipcol > 0 && curwin->w_cursor.lnum == curwin->w_topline) { + int overlap = sms_marker_overlap(curwin, -1); + if (overlap > 0 && i == curwin->w_skipcol) { + i += overlap; + } + } } else { i = curwin->w_leftcol; } @@ -5236,10 +5227,10 @@ static void nv_g_home_m_cmd(cmdarg_T *cap) // 'relativenumber' is on and lines are wrapping the middle can be more // to the left. if (cap->nchar == 'm') { - i += (curwin->w_width_inner - curwin_col_off() - + ((curwin->w_p_wrap && i > 0) ? curwin_col_off2() : 0)) / 2; + i += (curwin->w_width_inner - win_col_off(curwin) + + ((curwin->w_p_wrap && i > 0) ? win_col_off2(curwin) : 0)) / 2; } - coladvance((colnr_T)i); + coladvance(curwin, (colnr_T)i); if (flag) { do { i = gchar_cursor(); @@ -5281,7 +5272,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) { oparg_T *oap = cap->oap; int i; - int col_off = curwin_col_off(); + int col_off = win_col_off(curwin); const bool flag = cap->nchar == K_END || cap->nchar == K_KEND; oap->motion_type = kMTCharWise; @@ -5290,14 +5281,14 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) curwin->w_curswant = MAXCOL; // so we stay at the end if (cap->count1 == 1) { int width1 = curwin->w_width_inner - col_off; - int width2 = width1 + curwin_col_off2(); + int width2 = width1 + win_col_off2(curwin); - validate_virtcol(); + validate_virtcol(curwin); i = width1 - 1; if (curwin->w_virtcol >= (colnr_T)width1) { i += ((curwin->w_virtcol - width1) / width2 + 1) * width2; } - coladvance((colnr_T)i); + coladvance(curwin, (colnr_T)i); // Make sure we stick in this column. update_curswant_force(); @@ -5318,7 +5309,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap) cursor_down(cap->count1 - 1, false); } i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1; - coladvance((colnr_T)i); + coladvance(curwin, (colnr_T)i); // if the character doesn't fit move one back if (curwin->w_cursor.col > 0 && utf_ptr2cells(get_cursor_pos_ptr()) > 1) { @@ -5347,9 +5338,9 @@ static void nv_gi_cmd(cmdarg_T *cap) if (curbuf->b_last_insert.mark.lnum != 0) { curwin->w_cursor = curbuf->b_last_insert.mark; check_cursor_lnum(curwin); - int i = (int)strlen(get_cursor_line_ptr()); + int i = (int)get_cursor_line_len(); if (curwin->w_cursor.col > (colnr_T)i) { - if (virtual_active()) { + if (virtual_active(curwin)) { curwin->w_cursor.coladd += curwin->w_cursor.col - i; } curwin->w_cursor.col = i; @@ -5477,9 +5468,9 @@ static void nv_g_cmd(cmdarg_T *cap) oap->inclusive = false; i = linetabsize(curwin, curwin->w_cursor.lnum); if (cap->count0 > 0 && cap->count0 <= 100) { - coladvance((colnr_T)(i * cap->count0 / 100)); + coladvance(curwin, (colnr_T)(i * cap->count0 / 100)); } else { - coladvance((colnr_T)(i / 2)); + coladvance(curwin, (colnr_T)(i / 2)); } curwin->w_set_curswant = true; break; @@ -5702,11 +5693,11 @@ static void n_opencmd(cmdarg_T *cap) if (cap->cmdchar == 'O') { // Open above the first line of a folded sequence of lines - hasFolding(curwin->w_cursor.lnum, + hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); } else { // Open below the last line of a folded sequence of lines - hasFolding(curwin->w_cursor.lnum, + hasFolding(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum); } // trigger TextChangedI for the 'o/O' command @@ -5887,7 +5878,7 @@ static void nv_pipe(cmdarg_T *cap) cap->oap->inclusive = false; beginline(0); if (cap->count0 > 0) { - coladvance((colnr_T)(cap->count0 - 1)); + coladvance(curwin, (colnr_T)(cap->count0 - 1)); curwin->w_curswant = (colnr_T)(cap->count0 - 1); } else { curwin->w_curswant = 0; @@ -5983,8 +5974,8 @@ static void adjust_cursor(oparg_T *oap) // - 'virtualedit' is not "all" and not "onemore". if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL && (!VIsual_active || *p_sel == 'o') - && !virtual_active() - && (get_ve_flags() & VE_ONEMORE) == 0) { + && !virtual_active(curwin) + && (get_ve_flags(curwin) & VE_ONEMORE) == 0) { curwin->w_cursor.col--; // prevent cursor from moving on the trail byte mb_adjust_cursor(); @@ -6023,26 +6014,37 @@ static void adjust_for_sel(cmdarg_T *cap) bool unadjust_for_sel(void) { if (*p_sel == 'e' && !equalpos(VIsual, curwin->w_cursor)) { - pos_T *pp; - if (lt(VIsual, curwin->w_cursor)) { - pp = &curwin->w_cursor; - } else { - pp = &VIsual; - } - if (pp->coladd > 0) { - pp->coladd--; - } else if (pp->col > 0) { - pp->col--; - mark_mb_adjustpos(curbuf, pp); - } else if (pp->lnum > 1) { - pp->lnum--; - pp->col = (colnr_T)strlen(ml_get(pp->lnum)); - return true; - } + return unadjust_for_sel_inner(lt(VIsual, curwin->w_cursor) + ? &curwin->w_cursor : &VIsual); } return false; } +/// Move position "*pp" back one character for 'selection' == "exclusive". +/// +/// @return true when backed up to the previous line. +bool unadjust_for_sel_inner(pos_T *pp) +{ + colnr_T cs, ce; + + if (pp->coladd > 0) { + pp->coladd--; + } else if (pp->col > 0) { + pp->col--; + mark_mb_adjustpos(curbuf, pp); + if (virtual_active(curwin)) { + getvcol(curwin, pp, &cs, NULL, &ce); + pp->coladd = ce - cs; + } + } else if (pp->lnum > 1) { + pp->lnum--; + pp->col = ml_get_len(pp->lnum); + return true; + } + + return false; +} + /// SELECT key in Normal or Visual mode: end of Select mode mapping. static void nv_select(cmdarg_T *cap) { @@ -6147,7 +6149,7 @@ static void nv_esc(cmdarg_T *cap) if (VIsual_active) { end_visual_mode(); // stop Visual - check_cursor_col(); // make sure cursor is not beyond EOL + check_cursor_col(curwin); // make sure cursor is not beyond EOL curwin->w_set_curswant = true; redraw_curbuf_later(UPD_INVERTED); } else if (no_reason) { @@ -6160,12 +6162,12 @@ static void nv_esc(cmdarg_T *cap) void set_cursor_for_append_to_line(void) { curwin->w_set_curswant = true; - if (get_ve_flags() == VE_ALL) { + if (get_ve_flags(curwin) == VE_ALL) { const int save_State = State; // Pretend Insert mode here to allow the cursor on the // character past the end of the line State = MODE_INSERT; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); State = save_State; } else { curwin->w_cursor.col += (colnr_T)strlen(get_cursor_pos_ptr()); @@ -6203,7 +6205,7 @@ static void nv_edit(cmdarg_T *cap) case 'a': // "a"ppend is like "i"nsert on the next character. // increment coladd when in virtual space, increment the // column otherwise, also to append after an unprintable char - if (virtual_active() + if (virtual_active(curwin) && (curwin->w_cursor.coladd > 0 || *get_cursor_pos_ptr() == NUL || *get_cursor_pos_ptr() == TAB)) { @@ -6220,7 +6222,7 @@ static void nv_edit(cmdarg_T *cap) // Pretend Insert mode here to allow the cursor on the // character past the end of the line State = MODE_INSERT; - coladvance(getviscol()); + coladvance(curwin, getviscol()); State = save_State; } @@ -6391,12 +6393,8 @@ static void nv_at(cmdarg_T *cap) /// Handle the CTRL-U and CTRL-D commands. static void nv_halfpage(cmdarg_T *cap) { - if ((cap->cmdchar == Ctrl_U && curwin->w_cursor.lnum == 1) - || (cap->cmdchar == Ctrl_D - && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)) { - clearopbeep(cap->oap); - } else if (!checkclearop(cap->oap)) { - halfpage(cap->cmdchar == Ctrl_D, cap->count0); + if (!checkclearop(cap->oap)) { + pagescroll(cap->cmdchar == Ctrl_D ? FORWARD : BACKWARD, cap->count0, true); } } @@ -6575,7 +6573,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) // line. if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } } auto_format(false, true); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 9b969a2337..6233e446e0 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -283,7 +283,7 @@ void op_shift(oparg_T *oap, bool curs_top, int amount) // Set "'[" and "']" marks. curbuf->b_op_start = oap->start; curbuf->b_op_end.lnum = oap->end.lnum; - curbuf->b_op_end.col = (colnr_T)strlen(ml_get(oap->end.lnum)); + curbuf->b_op_end.col = ml_get_len(oap->end.lnum); if (curbuf->b_op_end.col > 0) { curbuf->b_op_end.col--; } @@ -365,6 +365,7 @@ static void shift_block(oparg_T *oap, int amount) } char *const oldp = get_cursor_line_ptr(); + const int old_line_len = get_cursor_line_len(); int startcol, oldlen, newlen; @@ -414,17 +415,17 @@ static void shift_block(oparg_T *oap, int amount) const int col_pre = bd.pre_whitesp_c - (bd.startspaces != 0); bd.textcol -= col_pre; - const size_t new_line_len // the length of the line after the block shift - = (size_t)bd.textcol + (size_t)tabs + (size_t)spaces + strlen(bd.textstart); - newp = xmalloc(new_line_len + 1); + const int new_line_len // the length of the line after the block shift + = bd.textcol + tabs + spaces + (old_line_len - (int)(bd.textstart - oldp)); + newp = xmalloc((size_t)new_line_len + 1); memmove(newp, oldp, (size_t)bd.textcol); startcol = bd.textcol; oldlen = (int)(bd.textstart - old_textstart) + col_pre; newlen = tabs + spaces; memset(newp + bd.textcol, TAB, (size_t)tabs); memset(newp + bd.textcol + tabs, ' ', (size_t)spaces); - // Note that STRMOVE() copies the trailing NUL. - STRMOVE(newp + bd.textcol + tabs + spaces, bd.textstart); + STRCPY(newp + bd.textcol + tabs + spaces, bd.textstart); + assert(newlen - oldlen == new_line_len - old_line_len); } else { // left char *verbatim_copy_end; // end of the part of the line which is // copied verbatim @@ -491,26 +492,27 @@ static void shift_block(oparg_T *oap, int amount) // part of the line that will be copied, it means we encountered a tab // character, which we will have to partly replace with spaces. assert(destination_col - verbatim_copy_width >= 0); - const size_t fill // nr of spaces that replace a TAB - = (size_t)(destination_col - verbatim_copy_width); + const int fill // nr of spaces that replace a TAB + = destination_col - verbatim_copy_width; assert(verbatim_copy_end - oldp >= 0); - const size_t verbatim_diff = (size_t)(verbatim_copy_end - oldp); + // length of string left of the shift position (ie the string not being shifted) + const int fixedlen = (int)(verbatim_copy_end - oldp); // The replacement line will consist of: // - the beginning of the original line up to "verbatim_copy_end", // - "fill" number of spaces, // - the rest of the line, pointed to by non_white. - const size_t new_line_len // the length of the line after the block shift - = verbatim_diff + fill + strlen(non_white); - - newp = xmalloc(new_line_len + 1); - startcol = (int)verbatim_diff; - oldlen = bd.textcol + (int)(non_white - bd.textstart) - (int)verbatim_diff; - newlen = (int)fill; - memmove(newp, oldp, verbatim_diff); - memset(newp + verbatim_diff, ' ', fill); - // Note that STRMOVE() copies the trailing NUL. - STRMOVE(newp + verbatim_diff + fill, non_white); + const int new_line_len // the length of the line after the block shift + = fixedlen + fill + (old_line_len - (int)(non_white - oldp)); + + newp = xmalloc((size_t)new_line_len + 1); + startcol = fixedlen; + oldlen = bd.textcol + (int)(non_white - bd.textstart) - fixedlen; + newlen = fill; + memmove(newp, oldp, (size_t)fixedlen); + memset(newp + fixedlen, ' ', (size_t)fill); + STRCPY(newp + fixedlen + fill, non_white); + assert(newlen - oldlen == new_line_len - old_line_len); } // replace the line ml_replace(curwin->w_cursor.lnum, newp, false); @@ -525,13 +527,13 @@ static void shift_block(oparg_T *oap, int amount) /// Insert string "s" (b_insert ? before : after) block :AKelly /// Caller must prepare for undo. -static void block_insert(oparg_T *oap, char *s, bool b_insert, struct block_def *bdp) +static void block_insert(oparg_T *oap, const char *s, size_t slen, bool b_insert, + struct block_def *bdp) { int ts_val; int count = 0; // extra spaces to replace a cut TAB int spaces = 0; // non-zero if cutting a TAB colnr_T offset; // pointer along new line - size_t s_len = strlen(s); char *newp, *oldp; // new, old lines int oldstate = State; State = MODE_INSERT; // don't want MODE_REPLACE for State @@ -579,8 +581,8 @@ static void block_insert(oparg_T *oap, char *s, bool b_insert, struct block_def assert(count >= 0); // Make sure the allocated size matches what is actually copied below. - newp = xmalloc(strlen(oldp) + (size_t)spaces + s_len - + (spaces > 0 && !bdp->is_short ? (size_t)ts_val - (size_t)spaces : 0) + newp = xmalloc((size_t)ml_get_len(lnum) + (size_t)spaces + slen + + (spaces > 0 && !bdp->is_short ? (size_t)(ts_val - spaces) : 0) + (size_t)count + 1); // copy up to shifted part @@ -592,8 +594,8 @@ static void block_insert(oparg_T *oap, char *s, bool b_insert, struct block_def memset(newp + offset, ' ', (size_t)spaces); // copy the new text - memmove(newp + offset + spaces, s, s_len); - offset += (int)s_len; + memmove(newp + offset + spaces, s, slen); + offset += (int)slen; int skipped = 0; if (spaces > 0 && !bdp->is_short) { @@ -614,7 +616,7 @@ static void block_insert(oparg_T *oap, char *s, bool b_insert, struct block_def if (spaces > 0) { offset += count; } - STRMOVE(newp + offset, oldp); + STRCPY(newp + offset, oldp); ml_replace(lnum, newp, false); extmark_splice_cols(curbuf, (int)lnum - 1, startcol, @@ -1843,15 +1845,15 @@ int op_delete(oparg_T *oap) // Thus the number of characters may increase! int n = bd.textlen - bd.startspaces - bd.endspaces; char *oldp = ml_get(lnum); - char *newp = xmalloc(strlen(oldp) - (size_t)n + 1); + char *newp = xmalloc((size_t)ml_get_len(lnum) - (size_t)n + 1); // copy up to deleted part memmove(newp, oldp, (size_t)bd.textcol); // insert spaces memset(newp + bd.textcol, ' ', (size_t)bd.startspaces + (size_t)bd.endspaces); // copy the part after the deleted part - oldp += bd.textcol + bd.textlen; - STRMOVE(newp + bd.textcol + bd.startspaces + bd.endspaces, oldp); + STRCPY(newp + bd.textcol + bd.startspaces + bd.endspaces, + oldp + bd.textcol + bd.textlen); // replace the line ml_replace(lnum, newp, false); @@ -1860,7 +1862,7 @@ int op_delete(oparg_T *oap) kExtmarkUndo); } - check_cursor_col(); + check_cursor_col(curwin); changed_lines(curbuf, curwin->w_cursor.lnum, curwin->w_cursor.col, oap->end.lnum + 1, 0, true); oap->line_count = 0; // no lines deleted @@ -1886,15 +1888,8 @@ int op_delete(oparg_T *oap) } else { beginline(0); // cursor in column 0 } - - int old_len = (int)strlen(ml_get(curwin->w_cursor.lnum)); - truncate_line(false); // delete the rest of the line - - extmark_splice_cols(curbuf, - (int)curwin->w_cursor.lnum - 1, curwin->w_cursor.col, - old_len - curwin->w_cursor.col, 0, kExtmarkUndo); - - // leave cursor past last char in line + truncate_line(false); // delete the rest of the line, + // leaving cursor past last char in line if (oap->line_count > 1) { u_clearline(curbuf); // "U" command not possible after "2cc" } @@ -1917,7 +1912,7 @@ int op_delete(oparg_T *oap) coladvance_force(getviscol2(oap->start.col, oap->start.coladd)); oap->start = curwin->w_cursor; if (oap->line_count == 1) { - coladvance(endcol); + coladvance(curwin, endcol); oap->end.col = curwin->w_cursor.col; oap->end.coladd = curwin->w_cursor.coladd; curwin->w_cursor = oap->start; @@ -1959,8 +1954,7 @@ int op_delete(oparg_T *oap) if (virtual_op) { // fix up things for virtualedit-delete: // break the tabs which are going to get in our way - char *curline = get_cursor_line_ptr(); - int len = (int)strlen(curline); + int len = get_cursor_line_len(); if (oap->end.coladd != 0 && (int)oap->end.col >= len - 1 @@ -2120,7 +2114,7 @@ static int op_replace(oparg_T *oap, int c) pos_T vpos; vpos.lnum = curwin->w_cursor.lnum; - getvpos(&vpos, oap->start_vcol); + getvpos(curwin, &vpos, oap->start_vcol); bd.startspaces += vpos.coladd; n = bd.startspaces; } else { @@ -2153,7 +2147,7 @@ static int op_replace(oparg_T *oap, int c) numc *= utf_char2len(c); char *oldp = get_cursor_line_ptr(); - colnr_T oldlen = (int)strlen(oldp); + colnr_T oldlen = get_cursor_line_len(); size_t newp_size = (size_t)bd.textcol + (size_t)bd.startspaces; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { @@ -2217,7 +2211,7 @@ static int op_replace(oparg_T *oap, int c) if (oap->motion_type == kMTLineWise) { oap->start.col = 0; curwin->w_cursor.col = 0; - oap->end.col = (colnr_T)strlen(ml_get(oap->end.lnum)); + oap->end.col = ml_get_len(oap->end.lnum); if (oap->end.col) { oap->end.col--; } @@ -2255,7 +2249,7 @@ static int op_replace(oparg_T *oap, int c) } coladvance_force(getviscol()); if (curwin->w_cursor.lnum == oap->end.lnum) { - getvpos(&oap->end, end_vcol); + getvpos(curwin, &oap->end, end_vcol); } } // with "coladd" set may move to just after a TAB @@ -2298,7 +2292,7 @@ static int op_replace(oparg_T *oap, int c) } curwin->w_cursor = oap->start; - check_cursor(); + check_cursor(curwin); changed_lines(curbuf, oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0, true); if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { @@ -2336,7 +2330,7 @@ void op_tilde(oparg_T *oap) if (oap->motion_type == kMTLineWise) { oap->start.col = 0; pos.col = 0; - oap->end.col = (colnr_T)strlen(ml_get(oap->end.lnum)); + oap->end.col = ml_get_len(oap->end.lnum); if (oap->end.col) { oap->end.col--; } @@ -2351,7 +2345,7 @@ void op_tilde(oparg_T *oap) while (true) { did_change |= swapchars(oap->op_type, &pos, pos.lnum == oap->end.lnum ? oap->end.col + 1 - : (int)strlen(ml_get_pos(&pos))); + : ml_get_pos_len(&pos)); if (ltoreq(oap->end, pos) || inc(&pos) == -1) { break; } @@ -2510,12 +2504,10 @@ void op_insert(oparg_T *oap, int count1) // Get indent information ind_pre_col = (colnr_T)getwhitecols_curline(); ind_pre_vcol = get_indent(); - char *firstline = ml_get(oap->start.lnum) + bd.textcol; - + pre_textlen = ml_get_len(oap->start.lnum) - bd.textcol; if (oap->op_type == OP_APPEND) { - firstline += bd.textlen; + pre_textlen -= bd.textlen; } - pre_textlen = (int)strlen(firstline); } if (oap->op_type == OP_APPEND) { @@ -2540,7 +2532,7 @@ void op_insert(oparg_T *oap, int count1) } } else { curwin->w_cursor = oap->end; - check_cursor_col(); + check_cursor_col(curwin); // Works just like an 'i'nsert on the next character. if (!LINEEMPTY(curwin->w_cursor.lnum) @@ -2642,7 +2634,7 @@ void op_insert(oparg_T *oap, int count1) // Subsequent calls to ml_get() flush the firstline data - take a // copy of the required string. char *firstline = ml_get(oap->start.lnum); - const size_t len = strlen(firstline); + colnr_T len = ml_get_len(oap->start.lnum); colnr_T add = bd.textcol; colnr_T offset = 0; // offset when cursor was moved in insert mode if (oap->op_type == OP_APPEND) { @@ -2659,21 +2651,21 @@ void op_insert(oparg_T *oap, int count1) } } } - if ((size_t)add > len) { - firstline += len; // short line, point to the NUL - } else { - firstline += add; + if (add > len) { + add = len; // short line, point to the NUL } - int ins_len = (int)strlen(firstline) - pre_textlen - offset; + firstline += add; + len -= add; + int ins_len = len - pre_textlen - offset; if (pre_textlen >= 0 && ins_len > 0) { char *ins_text = xmemdupz(firstline, (size_t)ins_len); // block handled here if (u_save(oap->start.lnum, (linenr_T)(oap->end.lnum + 1)) == OK) { - block_insert(oap, ins_text, (oap->op_type == OP_INSERT), &bd); + block_insert(oap, ins_text, (size_t)ins_len, (oap->op_type == OP_INSERT), &bd); } curwin->w_cursor.col = oap->start.col; - check_cursor(); + check_cursor(curwin); xfree(ins_text); } } @@ -2719,7 +2711,7 @@ int op_change(oparg_T *oap) coladvance_force(getviscol()); } firstline = ml_get(oap->start.lnum); - pre_textlen = (int)strlen(firstline); + pre_textlen = ml_get_len(oap->start.lnum); pre_indent = (int)getwhitecols(firstline); bd.textcol = curwin->w_cursor.col; } @@ -2741,7 +2733,6 @@ int op_change(oparg_T *oap) // Don't repeat the insert when Insert mode ended with CTRL-C. if (oap->motion_type == kMTBlockWise && oap->start.lnum != oap->end.lnum && !got_int) { - int ins_len; // Auto-indenting may have changed the indent. If the cursor was past // the indent, exclude that indent change from the inserted text. firstline = ml_get(oap->start.lnum); @@ -2752,12 +2743,12 @@ int op_change(oparg_T *oap) bd.textcol += (colnr_T)(new_indent - pre_indent); } - ins_len = (int)strlen(firstline) - pre_textlen; + int ins_len = ml_get_len(oap->start.lnum) - pre_textlen; if (ins_len > 0) { // Subsequent calls to ml_get() flush the firstline data - take a // copy of the inserted text. char *ins_text = xmalloc((size_t)ins_len + 1); - xstrlcpy(ins_text, firstline + bd.textcol, (size_t)ins_len + 1); + xmemcpyz(ins_text, firstline + bd.textcol, (size_t)ins_len); for (linenr_T linenr = oap->start.lnum + 1; linenr <= oap->end.lnum; linenr++) { block_prep(oap, &bd, linenr, true); @@ -2768,28 +2759,27 @@ int op_change(oparg_T *oap) // initial coladd offset as part of "startspaces" if (bd.is_short) { vpos.lnum = linenr; - getvpos(&vpos, oap->start_vcol); + getvpos(curwin, &vpos, oap->start_vcol); } else { vpos.coladd = 0; } char *oldp = ml_get(linenr); - char *newp = xmalloc(strlen(oldp) + (size_t)vpos.coladd - + (size_t)ins_len + 1); + char *newp = xmalloc((size_t)ml_get_len(linenr) + + (size_t)vpos.coladd + (size_t)ins_len + 1); // copy up to block start memmove(newp, oldp, (size_t)bd.textcol); - int offset = bd.textcol; - memset(newp + offset, ' ', (size_t)vpos.coladd); - offset += vpos.coladd; - memmove(newp + offset, ins_text, (size_t)ins_len); - offset += ins_len; - oldp += bd.textcol; - STRMOVE(newp + offset, oldp); + int newlen = bd.textcol; + memset(newp + newlen, ' ', (size_t)vpos.coladd); + newlen += vpos.coladd; + memmove(newp + newlen, ins_text, (size_t)ins_len); + newlen += ins_len; + STRCPY(newp + newlen, oldp + bd.textcol); ml_replace(linenr, newp, false); extmark_splice_cols(curbuf, (int)linenr - 1, bd.textcol, 0, vpos.coladd + ins_len, kExtmarkUndo); } } - check_cursor(); + check_cursor(curwin); changed_lines(curbuf, oap->start.lnum + 1, 0, oap->end.lnum + 1, 0, true); xfree(ins_text); } @@ -3196,7 +3186,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) bool allocated = false; const pos_T orig_start = curbuf->b_op_start; const pos_T orig_end = curbuf->b_op_end; - unsigned cur_ve_flags = get_ve_flags(); + unsigned cur_ve_flags = get_ve_flags(curwin); if (flags & PUT_FIXINDENT) { orig_indent = get_indent(); @@ -3421,9 +3411,9 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) // Correct line number for closed fold. Don't move the cursor yet, // u_save() uses it. if (dir == BACKWARD) { - hasFolding(lnum, &lnum, NULL); + hasFolding(curwin, lnum, &lnum, NULL); } else { - hasFolding(lnum, NULL, &lnum); + hasFolding(curwin, lnum, NULL, &lnum); } if (dir == FORWARD) { lnum++; @@ -3528,6 +3518,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) } // get the old line and advance to the position to insert at char *oldp = get_cursor_line_ptr(); + colnr_T oldlen = get_cursor_line_len(); CharsizeArg csarg; CSType cstype = init_charsize_arg(&csarg, curwin, curwin->w_cursor.lnum, oldp); @@ -3538,7 +3529,6 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) vcol += incr; ci = utfc_next(ci); } - size_t oldlen = (size_t)(ci.ptr - oldp) + strlen(ci.ptr); char *ptr = ci.ptr; bd.textcol = (colnr_T)(ptr - oldp); @@ -3588,7 +3578,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) totlen = (size_t)count * (size_t)(yanklen + spaces) + (size_t)bd.startspaces + (size_t)bd.endspaces; - char *newp = xmalloc(totlen + oldlen + 1); + char *newp = xmalloc(totlen + (size_t)oldlen + 1); // copy part up to cursor to new line ptr = newp; @@ -3618,7 +3608,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) ptr += bd.endspaces; // move the text after the cursor to the end of the line. - int columns = (int)oldlen - bd.textcol - delcount + 1; + int columns = oldlen - bd.textcol - delcount + 1; assert(columns >= 0); memmove(ptr, oldp + bd.textcol + delcount, (size_t)columns); ml_replace(curwin->w_cursor.lnum, newp, false); @@ -3650,7 +3640,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) curwin->w_cursor.col++; // in Insert mode we might be after the NUL, correct for that - colnr_T len = (colnr_T)strlen(get_cursor_line_ptr()); + colnr_T len = get_cursor_line_len(); if (curwin->w_cursor.col > len) { curwin->w_cursor.col = len; } @@ -3714,22 +3704,22 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) totlen = (size_t)count * (size_t)yanklen; do { char *oldp = ml_get(lnum); - size_t oldlen = strlen(oldp); + colnr_T oldlen = ml_get_len(lnum); if (lnum > start_lnum) { pos_T pos = { .lnum = lnum, }; - if (getvpos(&pos, vcol) == OK) { + if (getvpos(curwin, &pos, vcol) == OK) { col = pos.col; } else { col = MAXCOL; } } - if (VIsual_active && col > (colnr_T)oldlen) { + if (VIsual_active && col > oldlen) { lnum++; continue; } - char *newp = xmalloc(totlen + oldlen + 1); + char *newp = xmalloc(totlen + (size_t)oldlen + 1); memmove(newp, oldp, (size_t)col); char *ptr = newp + col; for (size_t i = 0; i < (size_t)count; i++) { @@ -3786,7 +3776,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) lnum = new_cursor.lnum; char *ptr = ml_get(lnum) + col; totlen = strlen(y_array[y_size - 1]); - char *newp = xmalloc((size_t)(strlen(ptr) + totlen + 1)); + char *newp = xmalloc((size_t)ml_get_len(lnum) - (size_t)col + totlen + 1); STRCPY(newp, y_array[y_size - 1]); STRCAT(newp, ptr); // insert second line @@ -3820,7 +3810,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) curwin->w_cursor.lnum = lnum; char *ptr = ml_get(lnum); if (cnt == count && i == y_size - 1) { - lendiff = (int)strlen(ptr); + lendiff = ml_get_len(lnum); } if (*ptr == '#' && preprocs_left()) { indent = 0; // Leave # lines at start @@ -3837,7 +3827,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) curwin->w_cursor = old_pos; // remember how many chars were removed if (cnt == count && i == y_size - 1) { - lendiff -= (int)strlen(ml_get(lnum)); + lendiff -= ml_get_len(lnum); } } } @@ -3943,7 +3933,7 @@ error: curwin->w_set_curswant = true; // Make sure the cursor is not after the NUL. - int len = (int)strlen(get_cursor_line_ptr()); + int len = get_cursor_line_len(); if (curwin->w_cursor.col > len) { if (cur_ve_flags == VE_ALL) { curwin->w_cursor.coladd = curwin->w_cursor.col - len; @@ -3977,7 +3967,7 @@ end: /// there move it left. void adjust_cursor_eol(void) { - unsigned cur_ve_flags = get_ve_flags(); + unsigned cur_ve_flags = get_ve_flags(curwin); const bool adj_cursor = (curwin->w_cursor.col > 0 && gchar_cursor() == NUL @@ -4439,7 +4429,7 @@ int do_join(size_t count, bool insert_space, bool save_undo, bool use_formatopti // vim: use the column of the last join curwin->w_cursor.col = (vim_strchr(p_cpo, CPO_JOINCOL) != NULL ? currsize : col); - check_cursor_col(); + check_cursor_col(curwin); curwin->w_cursor.coladd = 0; curwin->w_set_curswant = true; @@ -4617,11 +4607,13 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T { colnr_T startcol = 0; colnr_T endcol = MAXCOL; - bool is_oneChar = false; colnr_T cs, ce; char *p = ml_get(lnum); + bdp->startspaces = 0; bdp->endspaces = 0; + bdp->is_oneChar = false; + bdp->start_char_vcols = 0; if (lnum == start.lnum) { startcol = start.col; @@ -4629,7 +4621,8 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T getvcol(curwin, &start, &cs, NULL, &ce); if (ce != cs && start.coladd > 0) { // Part of a tab selected -- but don't double-count it. - bdp->startspaces = (ce - cs + 1) - start.coladd; + bdp->start_char_vcols = ce - cs + 1; + bdp->startspaces = bdp->start_char_vcols - start.coladd; if (bdp->startspaces < 0) { bdp->startspaces = 0; } @@ -4649,7 +4642,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T && utf_head_off(p, p + endcol) == 0)) { if (start.lnum == end.lnum && start.col == end.col) { // Special case: inside a single char - is_oneChar = true; + bdp->is_oneChar = true; bdp->startspaces = end.coladd - start.coladd + inclusive; endcol = startcol; } else { @@ -4660,13 +4653,14 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T } } if (endcol == MAXCOL) { - endcol = (colnr_T)strlen(p); + endcol = ml_get_len(lnum); } - if (startcol > endcol || is_oneChar) { + if (startcol > endcol || bdp->is_oneChar) { bdp->textlen = 0; } else { bdp->textlen = endcol - startcol + inclusive; } + bdp->textcol = startcol; bdp->textstart = p + startcol; } @@ -4717,20 +4711,20 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) } else if (oap->motion_type == kMTLineWise) { curwin->w_cursor.col = 0; pos.col = 0; - length = (colnr_T)strlen(ml_get(pos.lnum)); + length = ml_get_len(pos.lnum); } else { // oap->motion_type == kMTCharWise if (pos.lnum == oap->start.lnum && !oap->inclusive) { dec(&(oap->end)); } - length = (colnr_T)strlen(ml_get(pos.lnum)); + length = ml_get_len(pos.lnum); pos.col = 0; if (pos.lnum == oap->start.lnum) { pos.col += oap->start.col; length -= oap->start.col; } if (pos.lnum == oap->end.lnum) { - length = (int)strlen(ml_get(oap->end.lnum)); + length = ml_get_len(oap->end.lnum); if (oap->end.col >= length) { oap->end.col = length - 1; } @@ -4806,16 +4800,17 @@ bool do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) // "Unsigned" const bool do_unsigned = vim_strchr(curbuf->b_p_nf, 'u') != NULL; - if (virtual_active()) { + if (virtual_active(curwin)) { save_coladd = pos->coladd; pos->coladd = 0; } curwin->w_cursor = *pos; char *ptr = ml_get(pos->lnum); + int linelen = ml_get_len(pos->lnum); int col = pos->col; - if (*ptr == NUL || col + !!save_coladd >= (int)strlen(ptr)) { + if (col + !!save_coladd >= linelen) { goto theend; } @@ -4954,9 +4949,7 @@ bool do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) // get the number value (unsigned) if (visual && VIsual_mode != 'V') { - maxlen = (curbuf->b_visual.vi_curswant == MAXCOL - ? (int)strlen(ptr) - col - : length); + maxlen = curbuf->b_visual.vi_curswant == MAXCOL ? linelen - col : length; } bool overflow = false; @@ -5128,7 +5121,7 @@ theend: curwin->w_cursor = save_cursor; } else if (did_change) { curwin->w_set_curswant = true; - } else if (virtual_active()) { + } else if (virtual_active(curwin)) { curwin->w_cursor.coladd = save_coladd; } @@ -5714,7 +5707,7 @@ void cursor_pos_info(dict_T *dict) switch (l_VIsual_mode) { case Ctrl_V: - virtual_op = virtual_active(); + virtual_op = virtual_active(curwin); block_prep(&oparg, &bd, lnum, false); virtual_op = kNone; s = bd.textstart; @@ -5803,10 +5796,10 @@ void cursor_pos_info(dict_T *dict) } } else { char *p = get_cursor_line_ptr(); - validate_virtcol(); + validate_virtcol(curwin); col_print(buf1, sizeof(buf1), (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); - col_print(buf2, sizeof(buf2), (int)strlen(p), linetabsize_str(p)); + col_print(buf2, sizeof(buf2), get_cursor_line_len(), linetabsize_str(p)); if (char_count_cursor == byte_count_cursor && char_count == byte_count) { @@ -5888,7 +5881,7 @@ static void op_colon(oparg_T *oap) // When using !! on a closed fold the range ".!" works best to operate // on, it will be made the whole closed fold later. linenr_T endOfStartFold = oap->start.lnum; - hasFolding(oap->start.lnum, NULL, &endOfStartFold); + hasFolding(curwin, oap->start.lnum, NULL, &endOfStartFold); if (oap->end.lnum != oap->start.lnum && oap->end.lnum != endOfStartFold) { // Make it a range with the end line. stuffcharReadbuff(','); @@ -5899,7 +5892,7 @@ static void op_colon(oparg_T *oap) } else if (oap->start.lnum == curwin->w_cursor.lnum // do not use ".+number" for a closed fold, it would count // folded lines twice - && !hasFolding(oap->end.lnum, NULL, NULL)) { + && !hasFolding(curwin, oap->end.lnum, NULL, NULL)) { stuffReadbuff(".+"); stuffnumReadbuff(oap->line_count - 1); } else { @@ -6061,11 +6054,11 @@ static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) // (Actually, this does convert column positions into character // positions) curwin->w_cursor.lnum = oap->end.lnum; - coladvance(oap->end_vcol); + coladvance(curwin, oap->end_vcol); oap->end = curwin->w_cursor; curwin->w_cursor = oap->start; - coladvance(oap->start_vcol); + coladvance(curwin, oap->start_vcol); oap->start = curwin->w_cursor; } @@ -6190,7 +6183,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (redo_VIsual.rv_vcol == MAXCOL || VIsual_mode == 'v') { if (VIsual_mode == 'v') { if (redo_VIsual.rv_line_count <= 1) { - validate_virtcol(); + validate_virtcol(curwin); curwin->w_curswant = curwin->w_virtcol + redo_VIsual.rv_vcol - 1; } else { curwin->w_curswant = redo_VIsual.rv_vcol; @@ -6198,7 +6191,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { curwin->w_curswant = MAXCOL; } - coladvance(curwin->w_curswant); + coladvance(curwin, curwin->w_curswant); } cap->count0 = redo_VIsual.rv_count; cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); @@ -6220,11 +6213,10 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) && cap->oap->op_type != OP_DELETE) { if (lt(VIsual, curwin->w_cursor)) { VIsual.col = 0; - curwin->w_cursor.col = - (colnr_T)strlen(ml_get(curwin->w_cursor.lnum)); + curwin->w_cursor.col = ml_get_len(curwin->w_cursor.lnum); } else { curwin->w_cursor.col = 0; - VIsual.col = (colnr_T)strlen(ml_get(VIsual.lnum)); + VIsual.col = ml_get_len(VIsual.lnum); } VIsual_mode = 'v'; } else if (VIsual_mode == 'v') { @@ -6245,15 +6237,15 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (lt(oap->start, curwin->w_cursor)) { // Include folded lines completely. if (!VIsual_active) { - if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { + if (hasFolding(curwin, oap->start.lnum, &oap->start.lnum, NULL)) { oap->start.col = 0; } if ((curwin->w_cursor.col > 0 || oap->inclusive || oap->motion_type == kMTLineWise) - && hasFolding(curwin->w_cursor.lnum, NULL, + && hasFolding(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) { - curwin->w_cursor.col = (colnr_T)strlen(get_cursor_line_ptr()); + curwin->w_cursor.col = get_cursor_line_len(); } } oap->end = curwin->w_cursor; @@ -6266,12 +6258,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { // Include folded lines completely. if (!VIsual_active && oap->motion_type == kMTLineWise) { - if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, + if (hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) { curwin->w_cursor.col = 0; } - if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { - oap->start.col = (colnr_T)strlen(ml_get(oap->start.lnum)); + if (hasFolding(curwin, oap->start.lnum, NULL, &oap->start.lnum)) { + oap->start.col = ml_get_len(oap->start.lnum); } } oap->end = oap->start; @@ -6283,7 +6275,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) oap->line_count = oap->end.lnum - oap->start.lnum + 1; // Set "virtual_op" before resetting VIsual_active. - virtual_op = virtual_active(); + virtual_op = virtual_active(curwin); if (VIsual_active || redo_VIsual_busy) { get_op_vcol(oap, redo_VIsual.rv_vcol, true); @@ -6455,7 +6447,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (inindent(0)) { oap->motion_type = kMTLineWise; } else { - oap->end.col = (colnr_T)strlen(ml_get(oap->end.lnum)); + oap->end.col = ml_get_len(oap->end.lnum); if (oap->end.col) { oap->end.col--; oap->inclusive = true; @@ -6514,7 +6506,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) oap->excl_tr_ws = cap->cmdchar == 'z'; op_yank(oap, !gui_yank); } - check_cursor_col(); + check_cursor_col(curwin); break; case OP_CHANGE: @@ -6588,7 +6580,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { op_tilde(oap); } - check_cursor_col(); + check_cursor_col(curwin); break; case OP_FORMAT: @@ -6706,7 +6698,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) op_addsub(oap, (linenr_T)cap->count1, redo_VIsual.rv_arg); VIsual_active = false; } - check_cursor_col(); + check_cursor_col(curwin); break; default: clearopbeep(oap); @@ -6718,7 +6710,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT || oap->op_type == OP_DELETE)) { reset_lbr(); - coladvance(curwin->w_curswant = old_col); + coladvance(curwin, curwin->w_curswant = old_col); } } else { curwin->w_cursor = old_cursor; @@ -6763,7 +6755,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) goto end; } - if (!eval_has_provider("clipboard")) { + if (!eval_has_provider("clipboard", false)) { if (batch_change_count <= 1 && !quiet && (!clipboard_didwarn || (explicit_cb_reg && !redirecting()))) { clipboard_didwarn = true; @@ -7232,14 +7224,13 @@ bcount_t get_region_bytecount(buf_T *buf, linenr_T start_lnum, linenr_T end_lnum if (start_lnum == end_lnum) { return end_col - start_col; } - const char *first = ml_get_buf(buf, start_lnum); - bcount_t deleted_bytes = (bcount_t)strlen(first) - start_col + 1; + bcount_t deleted_bytes = ml_get_buf_len(buf, start_lnum) - start_col + 1; for (linenr_T i = 1; i <= end_lnum - start_lnum - 1; i++) { if (start_lnum + i > max_lnum) { return deleted_bytes; } - deleted_bytes += (bcount_t)strlen(ml_get_buf(buf, start_lnum + i)) + 1; + deleted_bytes += ml_get_buf_len(buf, start_lnum + i) + 1; } if (end_lnum > max_lnum) { return deleted_bytes; diff --git a/src/nvim/option.c b/src/nvim/option.c index d5df8385f8..22897d4f7e 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -430,10 +430,9 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags) uint32_t flags = opt->flags; if (varp != NULL) { // skip hidden option, nothing to do for it if (option_has_type(opt_idx, kOptValTypeString)) { - // Use set_string_option_direct() for local options to handle freeing and allocating the - // value. + // Use set_option_direct() for local options to handle freeing and allocating the value. if (opt->indir != PV_NONE) { - set_string_option_direct(opt_idx, opt->def_val.string, opt_flags, 0); + set_option_direct(opt_idx, CSTR_AS_OPTVAL(opt->def_val.string), opt_flags, 0); } else { if (flags & P_ALLOCED) { free_string_option(*(char **)(varp)); @@ -765,7 +764,7 @@ static char *stropt_get_default_val(OptIndex opt_idx, uint64_t flags) } /// Copy the new string value into allocated memory for the option. -/// Can't use set_string_option_direct(), because we need to remove the backslashes. +/// Can't use set_option_direct(), because we need to remove the backslashes. static char *stropt_copy_value(char *origval, char **argp, set_op_T op, uint32_t flags FUNC_ATTR_UNUSED) { @@ -1398,7 +1397,8 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char * return; } - *errmsg = set_option(opt_idx, varp, newval, opt_flags, op == OP_NONE, errbuf, errbuflen); + *errmsg = set_option(opt_idx, varp, newval, opt_flags, 0, false, op == OP_NONE, errbuf, + errbuflen); } /// Parse 'arg' for option settings. @@ -1937,7 +1937,7 @@ static const char *did_set_arabic(optset_T *args) // set rightleft mode if (!win->w_p_rl) { win->w_p_rl = true; - changed_window_setting(); + changed_window_setting(curwin); } // Enable Arabic shaping (major part of what Arabic requires) @@ -1968,7 +1968,7 @@ static const char *did_set_arabic(optset_T *args) // reset rightleft mode if (win->w_p_rl) { win->w_p_rl = false; - changed_window_setting(); + changed_window_setting(curwin); } // 'arabicshape' isn't reset, it is a global option and @@ -2024,9 +2024,6 @@ static const char *did_set_cmdheight(optset_T *args) { OptInt old_value = args->os_oldval.number; - if (ui_has(kUIMessages)) { - p_ch = 0; - } if (p_ch > Rows - min_rows() + 1) { p_ch = Rows - min_rows() + 1; } @@ -2247,7 +2244,7 @@ static const char *did_set_modified(optset_T *args) save_file_ff(buf); // Buffer is unchanged } redraw_titles(); - modified_was_set = (int)args->os_newval.boolean; + buf->b_modified_was_set = (int)args->os_newval.boolean; return NULL; } @@ -2901,8 +2898,6 @@ static const char *validate_num_option(OptIndex opt_idx, void *varp, OptInt *new } else if (value > p_wiw) { return e_winwidth; } - } else if (varp == &p_mco) { - *newval = MAX_MCO; } else if (varp == &p_titlelen) { if (value < 0) { return e_positive; @@ -3032,7 +3027,7 @@ void check_redraw_for(buf_T *buf, win_T *win, uint32_t flags) if (flags & P_HLONLY) { redraw_later(win, UPD_NOT_VALID); } else { - changed_window_setting_win(win); + changed_window_setting(win); } } if (flags & P_RBUF) { @@ -3314,38 +3309,6 @@ OptVal object_as_optval(Object o, bool *error) UNREACHABLE; } -/// Unset the local value of an option. The exact semantics of this depend on the option. -/// TODO(famiu): Remove this once we have a dedicated OptVal type for unset local options. -/// -/// @param opt_idx Option index in options[] table. -/// @param[in] varp Pointer to option variable. -/// -/// @return [allocated] Option value equal to the unset value for the option. -static OptVal optval_unset_local(OptIndex opt_idx, void *varp) -{ - vimoption_T *opt = &options[opt_idx]; - // For global-local options, use the unset value of the local value. - if (opt->indir & PV_BOTH) { - // String global-local options always use an empty string for the unset value. - if (option_has_type(opt_idx, kOptValTypeString)) { - return STATIC_CSTR_TO_OPTVAL(""); - } - - if ((int *)varp == &curbuf->b_p_ar) { - return BOOLEAN_OPTVAL(kNone); - } else if ((OptInt *)varp == &curwin->w_p_so || (OptInt *)varp == &curwin->w_p_siso) { - return NUMBER_OPTVAL(-1); - } else if ((OptInt *)varp == &curbuf->b_p_ul) { - return NUMBER_OPTVAL(NO_LOCAL_UNDOLEVEL); - } else { - // This should never happen. - abort(); - } - } - // For options that aren't global-local, just set the local value to the global value. - return get_option_value(opt_idx, OPT_GLOBAL); -} - /// Get an allocated string containing a list of valid types for an option. /// For options with a singular type, it returns the name of the type. For options with multiple /// possible types, it returns a slash separated list of types. For example, if an option can be a @@ -3420,24 +3383,60 @@ vimoption_T *get_option(OptIndex opt_idx) return &options[opt_idx]; } +/// Get option value that represents an unset local value for an option. +/// TODO(famiu): Remove this once we have a dedicated OptVal type for unset local options. +/// +/// @param opt_idx Option index in options[] table. +/// @param[in] varp Pointer to option variable. +/// +/// @return Option value equal to the unset value for the option. +static OptVal get_option_unset_value(OptIndex opt_idx) +{ + assert(opt_idx != kOptInvalid); + vimoption_T *opt = &options[opt_idx]; + + // For global-local options, use the unset value of the local value. + if (opt->indir & PV_BOTH) { + // String global-local options always use an empty string for the unset value. + if (option_has_type(opt_idx, kOptValTypeString)) { + return STATIC_CSTR_AS_OPTVAL(""); + } + + switch (opt_idx) { + case kOptAutoread: + return BOOLEAN_OPTVAL(kNone); + case kOptScrolloff: + case kOptSidescrolloff: + return NUMBER_OPTVAL(-1); + case kOptUndolevels: + return NUMBER_OPTVAL(NO_LOCAL_UNDOLEVEL); + default: + abort(); + } + } + + // For options that aren't global-local, use the global value to represent an unset local value. + return optval_from_varp(opt_idx, get_varp_scope(opt, OPT_GLOBAL)); +} + /// Check if local value of global-local option is unset for current buffer / window. /// Always returns false for options that aren't global-local. /// /// TODO(famiu): Remove this once we have an OptVal type to indicate an unset local value. -static bool is_option_local_value_unset(vimoption_T *opt, buf_T *buf, win_T *win) +static bool is_option_local_value_unset(OptIndex opt_idx) { + vimoption_T *opt = get_option(opt_idx); + // Local value of option that isn't global-local is always considered set. if (!((int)opt->indir & PV_BOTH)) { return false; } - // Get pointer to local value in varp_local, and a pointer to the currently used value in varp. - // If the local value is the one currently being used, that indicates that it's set. - // Otherwise it indicates the local value is unset. - void *varp = get_varp_from(opt, buf, win); - void *varp_local = get_varp_scope_from(opt, OPT_LOCAL, buf, win); + void *varp_local = get_varp_scope(opt, OPT_LOCAL); + OptVal local_value = optval_from_varp(opt_idx, varp_local); + OptVal unset_local_value = get_option_unset_value(opt_idx); - return varp != varp_local; + return optval_equal(local_value, unset_local_value); } /// Handle side-effects of setting an option. @@ -3445,17 +3444,19 @@ static bool is_option_local_value_unset(vimoption_T *opt, buf_T *buf, win_T *win /// @param opt_idx Index in options[] table. Must not be kOptInvalid. /// @param[in] varp Option variable pointer, cannot be NULL. /// @param old_value Old option value. -/// @param new_value New option value. /// @param opt_flags Option flags. -/// @param[out] value_checked Value was checked to be safe, no need to set P_INSECURE. +/// @param set_sid Script ID. Special values: +/// 0: Use current script ID. +/// SID_NONE: Don't set script ID. +/// @param direct Don't process side-effects. /// @param value_replaced Value was replaced completely. /// @param[out] errbuf Buffer for error message. /// @param errbuflen Length of error buffer. /// /// @return NULL on success, an untranslated error message on error. static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value, OptVal new_value, - int opt_flags, bool *value_checked, bool value_replaced, - char *errbuf, // NOLINT(readability-non-const-parameter) + int opt_flags, scid_T set_sid, const bool direct, + const bool value_replaced, char *errbuf, // NOLINT(readability-non-const-parameter) size_t errbuflen) { vimoption_T *opt = &options[opt_idx]; @@ -3463,6 +3464,7 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value bool restore_chartab = false; bool free_oldval = (opt->flags & P_ALLOCED); bool value_changed = false; + bool value_checked = false; optset_T did_set_cb_args = { .os_varp = varp, @@ -3479,8 +3481,11 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value .os_win = curwin }; + if (direct || opt->hidden) { + // Don't do any extra processing if setting directly or if option is hidden. + } // Disallow changing immutable options. - if (opt->immutable && !optval_equal(old_value, new_value)) { + else if (opt->immutable && !optval_equal(old_value, new_value)) { errmsg = e_unsupportedoption; } // Disallow changing some options from secure mode. @@ -3498,30 +3503,41 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value value_changed = did_set_cb_args.os_value_changed; // The 'keymap', 'filetype' and 'syntax' option callback functions may change the // os_value_checked field. - *value_checked = did_set_cb_args.os_value_checked; + value_checked = did_set_cb_args.os_value_checked; // The 'isident', 'iskeyword', 'isprint' and 'isfname' options may change the character table. // On failure, this needs to be restored. restore_chartab = did_set_cb_args.os_restore_chartab; } - // If an error is detected, restore the previous value and don't do any further processing. - if (errmsg != NULL) { + // If option is hidden or if an error is detected, restore the previous value and don't do any + // further processing. + if (opt->hidden || errmsg != NULL) { set_option_varp(opt_idx, varp, old_value, true); // When resetting some values, need to act on it. if (restore_chartab) { buf_init_chartab(curbuf, true); } - // Unset new_value as it is no longer valid. - new_value = NIL_OPTVAL; // NOLINT(clang-analyzer-deadcode.DeadStores) return errmsg; } // Re-assign the new value as its value may get freed or modified by the option callback. new_value = optval_from_varp(opt_idx, varp); - // Remember where the option was set. - set_option_sctx(opt_idx, opt_flags, current_sctx); + if (set_sid != SID_NONE) { + sctx_T script_ctx; + + if (set_sid == 0) { + script_ctx = current_sctx; + } else { + script_ctx.sc_sid = set_sid; + script_ctx.sc_seq = 0; + script_ctx.sc_lnum = 0; + } + // Remember where the option was set. + set_option_sctx(opt_idx, opt_flags, script_ctx); + } + // Free options that are in allocated memory. // Use "free_oldval", because recursiveness may change the flags (esp. init_highlight()). if (free_oldval) { @@ -3529,16 +3545,26 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value } opt->flags |= P_ALLOCED; - if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 && (opt->indir & PV_BOTH)) { - // Global option with local value set to use global value. - // Free the local value and clear it. - void *varp_local = get_varp_scope(opt, OPT_LOCAL); - OptVal local_unset_value = optval_unset_local(opt_idx, varp_local); - set_option_varp(opt_idx, varp_local, local_unset_value, true); - } else if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { - // May set global value for local option. - void *varp_global = get_varp_scope(opt, OPT_GLOBAL); - set_option_varp(opt_idx, varp_global, optval_copy(new_value), true); + const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; + const bool opt_is_global_local = opt->indir & PV_BOTH; + + if (scope_both) { + if (opt_is_global_local) { + // Global option with local value set to use global value. + // Free the local value and clear it. + void *varp_local = get_varp_scope(opt, OPT_LOCAL); + OptVal local_unset_value = get_option_unset_value(opt_idx); + set_option_varp(opt_idx, varp_local, optval_copy(local_unset_value), true); + } else { + // May set global value for local option. + void *varp_global = get_varp_scope(opt, OPT_GLOBAL); + set_option_varp(opt_idx, varp_global, optval_copy(new_value), true); + } + } + + // Don't do anything else if setting the option directly. + if (direct) { + return errmsg; } // Trigger the autocommand only after setting the flags. @@ -3592,91 +3618,105 @@ static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value return errmsg; } -/// Set the value of an option using an OptVal. +/// Validate the new value for an option. /// -/// @param opt_idx Index in options[] table. Must not be kOptInvalid. -/// @param[in] varp Option variable pointer, cannot be NULL. -/// @param value New option value. Might get freed. -/// @param opt_flags Option flags. -/// @param value_replaced Value was replaced completely. -/// @param[out] errbuf Buffer for error message. -/// @param errbuflen Length of error buffer. -/// -/// @return NULL on success, an untranslated error message on error. -static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags, - const bool value_replaced, char *errbuf, size_t errbuflen) +/// @param opt_idx Index in options[] table. Must not be kOptInvalid. +/// @param varp Pointer to option variable. +/// @param newval[in,out] New option value. Might be modified. +static const char *validate_option_value(const OptIndex opt_idx, void *varp, OptVal *newval, + int opt_flags, char *errbuf, size_t errbuflen) { - assert(opt_idx != kOptInvalid && varp != NULL); - const char *errmsg = NULL; - bool value_checked = false; - vimoption_T *opt = &options[opt_idx]; - if (value.type == kOptValTypeNil) { + if (newval->type == kOptValTypeNil) { // Don't try to unset local value if scope is global. // TODO(famiu): Change this to forbid changing all non-local scopes when the API scope bug is // fixed. if (opt_flags == OPT_GLOBAL) { errmsg = _("Cannot unset global option value"); } else { - optval_free(value); - value = optval_unset_local(opt_idx, varp); + optval_free(*newval); + *newval = optval_copy(get_option_unset_value(opt_idx)); } - } else if (!option_has_type(opt_idx, value.type)) { - char *rep = optval_to_cstr(value); + } else if (!option_has_type(opt_idx, newval->type)) { + char *rep = optval_to_cstr(*newval); char *valid_types = option_get_valid_types(opt_idx); snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"), - opt->fullname, valid_types, optval_type_get_name(value.type), rep); + opt->fullname, valid_types, optval_type_get_name(newval->type), rep); xfree(rep); xfree(valid_types); errmsg = errbuf; + } else if (newval->type == kOptValTypeNumber) { + // Validate and bound check num option values. + errmsg = validate_num_option(opt_idx, varp, &newval->data.number, errbuf, errbuflen); } + return errmsg; +} + +/// Set the value of an option using an OptVal. +/// +/// @param opt_idx Index in options[] table. Must not be kOptInvalid. +/// @param[in] varp Option variable pointer, cannot be NULL. +/// @param value New option value. Might get freed. +/// @param opt_flags Option flags. +/// @param set_sid Script ID. Special values: +/// 0: Use current script ID. +/// SID_NONE: Don't set script ID. +/// @param direct Don't process side-effects. +/// @param value_replaced Value was replaced completely. +/// @param[out] errbuf Buffer for error message. +/// @param errbuflen Length of error buffer. +/// +/// @return NULL on success, an untranslated error message on error. +static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, int opt_flags, + scid_T set_sid, const bool direct, const bool value_replaced, + char *errbuf, size_t errbuflen) + FUNC_ATTR_NONNULL_ARG(2) +{ + assert(opt_idx != kOptInvalid); + + const char *errmsg = validate_option_value(opt_idx, varp, &value, opt_flags, errbuf, errbuflen); + if (errmsg != NULL) { - goto err; + optval_free(value); + return errmsg; } + vimoption_T *opt = &options[opt_idx]; + const bool scope_local = opt_flags & OPT_LOCAL; + const bool scope_global = opt_flags & OPT_GLOBAL; + const bool scope_both = !scope_local && !scope_global; + const bool opt_is_global_local = opt->indir & PV_BOTH; + // Whether local value of global-local option is unset. + // NOTE: When this is true, it also implies that opt_is_global_local is true. + const bool is_opt_local_unset = is_option_local_value_unset(opt_idx); + // When using ":set opt=val" for a global option with a local value the local value will be reset, // use the global value here. - if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0 && ((int)opt->indir & PV_BOTH)) { + if (scope_both && opt_is_global_local) { varp = opt->var; } - OptVal old_value = optval_from_varp(opt_idx, varp); - OptVal old_global_value = NIL_OPTVAL; - OptVal old_local_value = NIL_OPTVAL; - - // Save the local and global values before changing anything. This is needed as for a global-only - // option setting the "local value" in fact sets the global value (since there is only one value). - // - // TODO(famiu): This needs to be changed to use the current type of the old value instead of - // value.type, when multi-type options are added. - if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { - old_global_value = optval_from_varp(opt_idx, get_varp_scope(opt, OPT_GLOBAL)); - old_local_value = optval_from_varp(opt_idx, get_varp_scope(opt, OPT_LOCAL)); - - // If local value of global-local option is unset, use global value as local value. - if (is_option_local_value_unset(opt, curbuf, curwin)) { - old_local_value = old_global_value; - } - } + void *varp_local = get_varp_scope(opt, OPT_LOCAL); + void *varp_global = get_varp_scope(opt, OPT_GLOBAL); + OptVal old_value = optval_from_varp(opt_idx, varp); + OptVal old_global_value = optval_from_varp(opt_idx, varp_global); + // If local value of global-local option is unset, use global value as local value. + OptVal old_local_value = is_opt_local_unset + ? old_global_value + : optval_from_varp(opt_idx, varp_local); // Value that's actually being used. - // For local scope of a global-local option, it is equal to the global value. - // In every other case, it is the same as old_value. - const bool oldval_is_global = ((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL); - OptVal used_old_value = oldval_is_global ? optval_from_varp(opt_idx, get_varp(opt)) : old_value; - - if (value.type == kOptValTypeNumber) { - errmsg = validate_num_option(opt_idx, varp, &value.data.number, errbuf, errbuflen); - if (errmsg != NULL) { - goto err; - } - } - - set_option_varp(opt_idx, varp, value, false); - + // For local scope of a global-local option, it's equal to the global value if the local value is + // unset. In every other case, it is the same as old_value. + // This value is used instead of old_value when triggering the OptionSet autocommand. + OptVal used_old_value = (scope_local && is_opt_local_unset) + ? optval_from_varp(opt_idx, get_varp(opt)) + : old_value; + + // Save the old values and the new value in case they get changed. OptVal saved_used_value = optval_copy(used_old_value); OptVal saved_old_global_value = optval_copy(old_global_value); OptVal saved_old_local_value = optval_copy(old_local_value); @@ -3693,23 +3733,20 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, secure = 1; } - errmsg = did_set_option(opt_idx, varp, old_value, value, opt_flags, &value_checked, + // Set option through its variable pointer. + set_option_varp(opt_idx, varp, value, false); + // Process any side effects. + errmsg = did_set_option(opt_idx, varp, old_value, value, opt_flags, set_sid, direct, value_replaced, errbuf, errbuflen); secure = secure_saved; - if (errmsg == NULL) { + if (errmsg == NULL && !direct) { if (!starting) { apply_optionset_autocmd(opt_idx, opt_flags, saved_used_value, saved_old_global_value, saved_old_local_value, saved_new_value, errmsg); } if (opt->flags & P_UI_OPTION) { - // Calculate saved_new_value again as its value might be changed by bound checks. - // NOTE: Currently there are no buffer/window local UI options, but if there ever are buffer - // or window local UI options added in the future, varp might become invalid if the buffer or - // window is closed during an autocommand, and a check would have to be added for it. - optval_free(saved_new_value); - saved_new_value = optval_copy(optval_from_varp(opt_idx, varp)); ui_call_option_set(cstr_as_string(opt->fullname), optval_as_object(saved_new_value)); } } @@ -3719,10 +3756,70 @@ static const char *set_option(const OptIndex opt_idx, void *varp, OptVal value, optval_free(saved_old_local_value); optval_free(saved_old_global_value); optval_free(saved_new_value); + return errmsg; -err: - optval_free(value); - return errmsg; +} + +/// Set option value directly, without processing any side effects. +/// +/// @param opt_idx Option index in options[] table. +/// @param value Option value. +/// @param opt_flags Option flags. +/// @param set_sid Script ID. Special values: +/// 0: Use current script ID. +/// SID_NONE: Don't set script ID. +void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid) +{ + static char errbuf[IOSIZE]; + + vimoption_T *opt = get_option(opt_idx); + + if (opt->var == NULL) { + return; + } + + const bool scope_both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; + void *varp = get_varp_scope(opt, scope_both ? OPT_LOCAL : opt_flags); + + set_option(opt_idx, varp, optval_copy(value), opt_flags, set_sid, true, true, errbuf, + sizeof(errbuf)); +} + +/// Set option value directly for buffer / window, without processing any side effects. +/// +/// @param opt_idx Option index in options[] table. +/// @param value Option value. +/// @param opt_flags Option flags. +/// @param set_sid Script ID. Special values: +/// 0: Use current script ID. +/// SID_NONE: Don't set script ID. +/// @param req_scope Requested option scope. See OptReqScope in option.h. +/// @param[in] from Target buffer/window. +void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid, + OptReqScope req_scope, void *const from) +{ + buf_T *save_curbuf = curbuf; + win_T *save_curwin = curwin; + + // Don't use switch_option_context(), as that calls aucmd_prepbuf(), which may have unintended + // side-effects when setting an option directly. Just change the values of curbuf and curwin if + // needed, no need to properly switch the window / buffer. + switch (req_scope) { + case kOptReqGlobal: + break; + case kOptReqBuf: + curbuf = (buf_T *)from; + break; + case kOptReqWin: + curwin = (win_T *)from; + curbuf = curwin->w_buffer; + break; + } + + set_option_direct(opt_idx, value, opt_flags, set_sid); + + curwin = save_curwin; + curbuf = save_curbuf; } /// Set the value of an option. @@ -3750,7 +3847,8 @@ const char *set_option_value(const OptIndex opt_idx, const OptVal value, int opt return NULL; } - return set_option(opt_idx, varp, optval_copy(value), opt_flags, true, errbuf, sizeof(errbuf)); + return set_option(opt_idx, varp, optval_copy(value), opt_flags, 0, false, true, errbuf, + sizeof(errbuf)); } /// Set the value of an option. Supports TTY options, unlike set_option_value(). @@ -4128,7 +4226,7 @@ static int optval_default(OptIndex opt_idx, void *varp) vimoption_T *opt = &options[opt_idx]; // Hidden or immutable options always use their default value. - if (varp == NULL || opt->immutable) { + if (varp == NULL || opt->hidden || opt->immutable) { return true; } @@ -4629,6 +4727,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) return &(win->w_p_rnu); case PV_NUW: return &(win->w_p_nuw); + case PV_WFB: + return &(win->w_p_wfb); case PV_WFH: return &(win->w_p_wfh); case PV_WFW: @@ -6109,9 +6209,9 @@ char *get_flp_value(buf_T *buf) } /// Get the local or global value of the 'virtualedit' flags. -unsigned get_ve_flags(void) +unsigned get_ve_flags(win_T *wp) { - return (curwin->w_ve_flags ? curwin->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU); + return (wp->w_ve_flags ? wp->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU); } /// Get the local or global value of 'showbreak'. @@ -6208,7 +6308,7 @@ void set_fileformat(int eol_style, int opt_flags) // p is NULL if "eol_style" is EOL_UNKNOWN. if (p != NULL) { - set_string_option_direct(kOptFileformat, p, opt_flags, 0); + set_option_direct(kOptFileformat, CSTR_AS_OPTVAL(p), opt_flags, 0); } // This may cause the buffer to become (un)modified. diff --git a/src/nvim/option.h b/src/nvim/option.h index 7cf880b19b..19764c0121 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -49,7 +49,8 @@ typedef struct { ///< buffer-local option: global value idopt_T indir; ///< global option: PV_NONE; ///< local option: indirect option index - bool immutable; ///< option value cannot be changed from the default value. + bool hidden; ///< option is hidden, any attempt to set its value will be ignored. + bool immutable; ///< option is immutable, trying to set its value will give an error. /// callback function to invoke after an option is modified to validate and /// apply the new value. diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index 69d8f0833d..be78a708ad 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -61,7 +61,7 @@ "-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel," \ "[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb," \ "*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn," \ - "q:QuickFixLine,0:Whitespace,I:NormalNC" + "q:QuickFixLine,g:MsgArea,0:Whitespace,I:NormalNC" // Default values for 'errorformat'. // The "%f|%l| %m" one is used for when the contents of the quickfix window is @@ -129,6 +129,8 @@ #define DFLT_FO_VIM "tcqj" #define FO_ALL "tcro/q2vlb1mMBn,aw]jp" // for do_set() +#define MAX_MCO 6 // fixed value for 'maxcombine' + // characters for the p_cpo option: #define CPO_ALTREAD 'a' // ":read" sets alternate file name #define CPO_ALTWRITE 'A' // ":write" sets alternate file name @@ -549,8 +551,6 @@ EXTERN char *p_mef; ///< 'makeef' EXTERN char *p_mp; ///< 'makeprg' EXTERN char *p_mps; ///< 'matchpairs' EXTERN OptInt p_mat; ///< 'matchtime' -EXTERN OptInt p_mco; ///< 'maxcombine' -#define MAX_MCO 6 // fixed value for 'maxcombine' EXTERN OptInt p_mfd; ///< 'maxfuncdepth' EXTERN OptInt p_mmd; ///< 'maxmapdepth' EXTERN OptInt p_mmp; ///< 'maxmempattern' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 632732d7b7..5173240384 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -7,6 +7,7 @@ --- @field varname? string --- @field pv_name? string --- @field type 'boolean'|'number'|'string' +--- @field hidden? boolean --- @field immutable? boolean --- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma' --- @field scope vim.option_scope[] @@ -90,6 +91,7 @@ return { { abbreviation = 'al', defaults = { if_true = 224 }, + enable_if = false, full_name = 'aleph', scope = { 'global' }, short_desc = N_('ASCII code of the letter Aleph (Hebrew)'), @@ -99,14 +101,13 @@ return { abbreviation = 'ari', defaults = { if_true = false }, desc = [=[ - Allow CTRL-_ in Insert and Command-line mode. This is default off, to - avoid that users that accidentally type CTRL-_ instead of SHIFT-_ get - into reverse Insert mode, and don't know how to get out. See - 'revins'. + Allow CTRL-_ in Insert mode. This is default off, to avoid that users + that accidentally type CTRL-_ instead of SHIFT-_ get into reverse + Insert mode, and don't know how to get out. See 'revins'. ]=], full_name = 'allowrevins', scope = { 'global' }, - short_desc = N_('allow CTRL-_ in Insert and Command-line mode'), + short_desc = N_('allow CTRL-_ in Insert mode'), type = 'boolean', varname = 'p_ari', }, @@ -1323,7 +1324,7 @@ return { defaults = { if_true = '' }, desc = [=[ A template for a comment. The "%s" in the value is replaced with the - comment text. For example, C uses "/*%s*/". Currently only used to + comment text. For example, C uses "/*%s*/". Used for |commenting| and to add markers for folding, see |fold-marker|. ]=], full_name = 'commentstring', @@ -3367,6 +3368,8 @@ return { Format to recognize for the ":grep" command output. This is a scanf-like string that uses the same format as the 'errorformat' option: see |errorformat|. + + If ripgrep ('grepprg') is available, this option defaults to `%f:%l:%c:%m`. ]=], full_name = 'grepformat', list = 'onecomma', @@ -3379,10 +3382,9 @@ return { abbreviation = 'gp', defaults = { condition = 'MSWIN', - if_false = 'grep -n $* /dev/null', + if_false = 'grep -HIn $* /dev/null', if_true = 'findstr /n $* nul', - doc = [["grep -n ", - Unix: "grep -n $* /dev/null"]], + doc = [[see below]], }, desc = [=[ Program to use for the |:grep| command. This option may contain '%' @@ -3390,16 +3392,23 @@ return { line. The placeholder "$*" is allowed to specify where the arguments will be included. Environment variables are expanded |:set_env|. See |option-backslash| about including spaces and backslashes. - When your "grep" accepts the "-H" argument, use this to make ":grep" - also work well with a single file: >vim - set grepprg=grep\ -nH - < Special value: When 'grepprg' is set to "internal" the |:grep| command + Special value: When 'grepprg' is set to "internal" the |:grep| command works like |:vimgrep|, |:lgrep| like |:lvimgrep|, |:grepadd| like |:vimgrepadd| and |:lgrepadd| like |:lvimgrepadd|. See also the section |:make_makeprg|, since most of the comments there apply equally to 'grepprg'. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. + This option defaults to: + - `rg --vimgrep -uu ` if ripgrep is available (|:checkhealth|), + - `grep -HIn $* /dev/null` on Unix, + - `findstr /n $* nul` on Windows. + Ripgrep can perform additional filtering such as using .gitignore rules + and skipping hidden files. This is disabled by default (see the -u option) + to more closely match the behaviour of standard grep. + You can make ripgrep match Vim's case handling using the + -i/--ignore-case and -S/--smart-case options. + An |OptionSet| autocmd can be used to set it up to match automatically. ]=], expand = true, full_name = 'grepprg', @@ -5142,12 +5151,12 @@ return { }, { abbreviation = 'mco', - defaults = { if_true = 6 }, + defaults = { if_true = imacros('MAX_MCO') }, full_name = 'maxcombine', scope = { 'global' }, short_desc = N_('maximum nr of combining characters displayed'), type = 'number', - varname = 'p_mco', + hidden = true, }, { abbreviation = 'mfd', @@ -5459,7 +5468,6 @@ return { When on, the mouse pointer is hidden when characters are typed. The mouse pointer is restored when the mouse is moved. ]=], - enable_if = false, full_name = 'mousehide', redraw = { 'ui_option' }, scope = { 'global' }, @@ -5875,6 +5883,7 @@ return { { abbreviation = 'pt', defaults = { if_true = '' }, + enable_if = false, full_name = 'pastetoggle', scope = { 'global' }, short_desc = N_('No description'), @@ -6573,9 +6582,6 @@ return { top are deleted if new lines exceed this limit. Minimum is 1, maximum is 100000. Only in |terminal| buffers. - - Note: Lines that are not visible and kept in scrollback are not - reflown when the terminal buffer is resized horizontally. ]=], full_name = 'scrollback', redraw = { 'current_buffer' }, @@ -7665,8 +7671,7 @@ return { highlighted with |hl-NonText|. You may also want to add "lastline" to the 'display' option to show as much of the last line as possible. - NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y - and scrolling with the mouse. + NOTE: partly implemented, doesn't work yet for |gj| and |gk|. ]=], full_name = 'smoothscroll', pv_name = 'p_sms', @@ -8406,6 +8411,8 @@ return { "split" when both are present. uselast If included, jump to the previously used window when jumping to errors with |quickfix| commands. + If a window has 'winfixbuf' enabled, 'switchbuf' is currently not + applied to the split window. ]=], expand_cb = 'expand_set_switchbuf', full_name = 'switchbuf', @@ -8530,7 +8537,7 @@ return { appear wrong in many places. The value must be more than 0 and less than 10000. - There are four main ways to use tabs in Vim: + There are five main ways to use tabs in Vim: 1. Always keep 'tabstop' at 8, set 'softtabstop' and 'shiftwidth' to 4 (or 3 or whatever you prefer) and use 'noexpandtab'. Then Vim will use a mix of tabs and spaces, but typing <Tab> and <BS> will @@ -8768,6 +8775,7 @@ return { { abbreviation = 'tenc', defaults = { if_true = '' }, + enable_if = false, full_name = 'termencoding', scope = { 'global' }, short_desc = N_('Terminal encoding'), @@ -9360,6 +9368,7 @@ return { Level Messages ~ ---------------------------------------------------------------------- + 1 Enables Lua tracing (see above). Does not produce messages. 2 When a file is ":source"'ed, or |shada| file is read or written. 3 UI info, terminal capabilities. 4 Shell commands. @@ -9863,8 +9872,8 @@ return { will scroll 'window' minus two lines, with a minimum of one. When 'window' is equal to 'lines' minus one CTRL-F and CTRL-B scroll in a much smarter way, taking care of wrapping lines. - When resizing the Vim window, the value is smaller than 1 or more than - or equal to 'lines' it will be set to 'lines' minus 1. + When resizing the Vim window, and the value is smaller than 1 or more + than or equal to 'lines' it will be set to 'lines' minus 1. Note: Do not confuse this with the height of the Vim window, use 'lines' for that. ]=], @@ -9875,6 +9884,23 @@ return { varname = 'p_window', }, { + abbreviation = 'wfb', + defaults = { if_true = false }, + desc = [=[ + If enabled, the window and the buffer it is displaying are paired. + For example, attempting to change the buffer with |:edit| will fail. + Other commands which change a window's buffer such as |:cnext| will + also skip any window with 'winfixbuf' enabled. However if an Ex + command has a "!" modifier, it can force switching buffers. + ]=], + full_name = 'winfixbuf', + pv_name = 'p_wfb', + redraw = { 'current_window' }, + scope = { 'window' }, + short_desc = N_('pin a window to a specific buffer'), + type = 'boolean', + }, + { abbreviation = 'wfh', defaults = { if_true = false }, desc = [=[ diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 4be08b28f5..29433ddbb5 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -266,113 +266,6 @@ void check_string_option(char **pp) } } -/// Set global value for string option when it's a local option. -/// -/// @param opt option -/// @param varp pointer to option variable -static void set_string_option_global(vimoption_T *opt, char **varp) -{ - char **p; - - // the global value is always allocated - if (opt->var == VAR_WIN) { - p = (char **)GLOBAL_WO(varp); - } else { - p = (char **)opt->var; - } - if (opt->indir != PV_NONE && p != varp) { - char *s = xstrdup(*varp); - free_string_option(*p); - *p = s; - } -} - -/// Set a string option to a new value (without checking the effect). -/// The string is copied into allocated memory. -/// if ("opt_idx" == kOptInvalid) "name" is used, otherwise "opt_idx" is used. -/// When "set_sid" is zero set the scriptID to current_sctx.sc_sid. When -/// "set_sid" is SID_NONE don't set the scriptID. Otherwise set the scriptID to -/// "set_sid". -/// -/// @param opt_flags Option flags. -/// -/// TODO(famiu): Remove this and its win/buf variants. -void set_string_option_direct(OptIndex opt_idx, const char *val, int opt_flags, scid_T set_sid) -{ - vimoption_T *opt = get_option(opt_idx); - - if (opt->var == NULL) { // can't set hidden option - return; - } - - assert(opt->var != &p_shada); - - bool both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; - char *s = xstrdup(val); - char **varp = (char **)get_varp_scope(opt, both ? OPT_LOCAL : opt_flags); - - if (opt->flags & P_ALLOCED) { - free_string_option(*varp); - } - *varp = s; - - // For buffer/window local option may also set the global value. - if (both) { - set_string_option_global(opt, varp); - } - - opt->flags |= P_ALLOCED; - - // When setting both values of a global option with a local value, - // make the local value empty, so that the global value is used. - if ((opt->indir & PV_BOTH) && both) { - free_string_option(*varp); - *varp = empty_string_option; - } - if (set_sid != SID_NONE) { - sctx_T script_ctx; - - if (set_sid == 0) { - script_ctx = current_sctx; - } else { - script_ctx.sc_sid = set_sid; - script_ctx.sc_seq = 0; - script_ctx.sc_lnum = 0; - } - set_option_sctx(opt_idx, opt_flags, script_ctx); - } -} - -/// Like set_string_option_direct(), but for a window-local option in "wp". -/// Blocks autocommands to avoid the old curwin becoming invalid. -void set_string_option_direct_in_win(win_T *wp, OptIndex opt_idx, const char *val, int opt_flags, - int set_sid) -{ - win_T *save_curwin = curwin; - - block_autocmds(); - curwin = wp; - curbuf = curwin->w_buffer; - set_string_option_direct(opt_idx, val, opt_flags, set_sid); - curwin = save_curwin; - curbuf = curwin->w_buffer; - unblock_autocmds(); -} - -/// Like set_string_option_direct(), but for a buffer-local option in "buf". -/// Blocks autocommands to avoid the old curwin becoming invalid. -void set_string_option_direct_in_buf(buf_T *buf, OptIndex opt_idx, const char *val, int opt_flags, - int set_sid) -{ - buf_T *save_curbuf = curbuf; - - block_autocmds(); - curbuf = buf; - set_string_option_direct(opt_idx, val, opt_flags, set_sid); - curbuf = save_curbuf; - unblock_autocmds(); -} - /// Return true if "val" is a valid 'filetype' name. /// Also used for 'syntax' and 'keymap'. static bool valid_filetype(const char *val) @@ -2479,9 +2372,8 @@ const char *did_set_virtualedit(optset_T *args) } else if (strcmp(ve, args->os_oldval.string.data) != 0) { // Recompute cursor position in case the new 've' setting // changes something. - validate_virtcol_win(win); - // XXX: this only works when win == curwin - coladvance(win->w_virtcol); + validate_virtcol(win); + coladvance(win, win->w_virtcol); } } return NULL; diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 5b1cb01976..5a79004c41 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -49,6 +49,7 @@ #endif #ifdef INCLUDE_GENERATED_DECLARATIONS +# include "auto/pathdef.h" # include "os/env.c.generated.h" #endif @@ -473,17 +474,9 @@ void init_homedir(void) 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(os_buf, MAXPATHL) == OK && os_chdir(os_buf) == 0) { - if (!os_chdir(var) && os_dirname(IObuff, IOSIZE) == OK) { - var = IObuff; - } - if (os_chdir(os_buf) != 0) { - emsg(_(e_prev_dir)); - } - } + // Get the actual path. This resolves links. + if (var != NULL && os_realpath(var, IObuff, IOSIZE) != NULL) { + var = IObuff; } // Fall back to current working directory if home is not found @@ -586,9 +579,6 @@ void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool es bool copy_char; bool mustfree; // var was allocated, need to free it later bool at_start = true; // at start of a name -#if defined(BACKSLASH_IN_FILENAME) - char *const save_dst = dst; -#endif int prefix_len = (prefix == NULL) ? 0 : (int)strlen(prefix); @@ -729,7 +719,7 @@ void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool es // with it, skip a character if (after_pathsep(dst, dst + c) #if defined(BACKSLASH_IN_FILENAME) - && (dst == save_dst || dst[-1] != ':') + && dst[c - 1] != ':' #endif && vim_ispathsep(*tail)) { tail++; @@ -1198,7 +1188,7 @@ bool os_setenv_append_path(const char *fname) const char *tail = path_tail_with_sep((char *)fname); size_t dirlen = (size_t)(tail - fname); assert(tail >= fname && dirlen + 1 < sizeof(os_buf)); - xstrlcpy(os_buf, fname, dirlen + 1); + xmemcpyz(os_buf, fname, dirlen); const char *path = os_getenv("PATH"); const size_t pathlen = path ? strlen(path) : 0; const size_t newlen = pathlen + dirlen + 2; diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index ade745df2c..19bdf30311 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -374,8 +374,8 @@ static bool is_executable_in_path(const char *name, char **abspath) char *e = xstrchrnul(p, ENV_SEPCHAR); // Combine the $PATH segment with `name`. - xstrlcpy(buf, p, (size_t)(e - p) + 1); - append_path(buf, name, buf_len); + xmemcpyz(buf, p, (size_t)(e - p)); + (void)append_path(buf, name, buf_len); #ifdef MSWIN if (is_executable_ext(buf, abspath)) { @@ -789,7 +789,7 @@ void os_copy_xattr(const char *from_file, const char *to_file) // get the length of the extended attributes ssize_t size = listxattr((char *)from_file, NULL, 0); // not supported or no attributes to copy - if (errno == ENOTSUP || size <= 0) { + if (size <= 0) { return; } char *xattr_buf = xmalloc((size_t)size); @@ -1320,22 +1320,22 @@ bool os_fileid_equal_fileinfo(const FileID *file_id, const FileInfo *file_info) /// Return the canonicalized absolute pathname. /// /// @param[in] name Filename to be canonicalized. -/// @param[out] buf Buffer to store the canonicalized values. A minimum length -// of MAXPATHL+1 is required. If it is NULL, memory is -// allocated. In that case, the caller should deallocate this -// buffer. +/// @param[out] buf Buffer to store the canonicalized values. +/// If it is NULL, memory is allocated. In that case, the caller +/// should deallocate this buffer. +/// @param[in] len The length of the buffer. /// /// @return pointer to the buf on success, or NULL. -char *os_realpath(const char *name, char *buf) +char *os_realpath(const char *name, char *buf, size_t len) FUNC_ATTR_NONNULL_ARG(1) { uv_fs_t request; int result = uv_fs_realpath(NULL, &request, name, NULL); if (result == kLibuvSuccess) { if (buf == NULL) { - buf = xmallocz(MAXPATHL); + buf = xmalloc(len); } - xstrlcpy(buf, request.ptr, MAXPATHL + 1); + xstrlcpy(buf, request.ptr, len); } uv_fs_req_cleanup(&request); return result == kLibuvSuccess ? buf : NULL; diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index fab360c9af..60b5b48745 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -247,6 +247,13 @@ bool os_isatty(int fd) return uv_guess_handle(fd) == UV_TTY; } +void input_enqueue_raw(String keys) +{ + if (keys.size > 0) { + rbuffer_write(input_buffer, keys.data, keys.size); + } +} + size_t input_enqueue(String keys) { const char *ptr = keys.data; @@ -255,9 +262,9 @@ size_t input_enqueue(String keys) while (rbuffer_space(input_buffer) >= 19 && ptr < end) { // A "<x>" form occupies at least 1 characters, and produces up // to 19 characters (1 + 5 * 3 for the char and 3 for a modifier). - // In the case of K_SPECIAL(0x80), 3 bytes are escaped and needed, + // In the case of K_SPECIAL (0x80), 3 bytes are escaped and needed, // but since the keys are UTF-8, so the first byte cannot be - // K_SPECIAL(0x80). + // K_SPECIAL (0x80). uint8_t buf[19] = { 0 }; // Do not simplify the keys here. Simplification will be done later. unsigned new_size @@ -351,8 +358,7 @@ static uint8_t check_multiclick(int code, int grid, int row, int col) return modifiers; } -// Mouse event handling code(Extract row/col if available and detect multiple -// clicks) +/// Mouse event handling code (extract row/col if available and detect multiple clicks) static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufsize) { int mouse_code = 0; diff --git a/src/nvim/os/nvim.manifest b/src/nvim/os/nvim.manifest index 571b7f4580..f6bd20f66b 100644 --- a/src/nvim/os/nvim.manifest +++ b/src/nvim/os/nvim.manifest @@ -18,8 +18,9 @@ </application> </compatibility> <asmv3:application> - <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings"> - <activeCodePage>UTF-8</activeCodePage> + <asmv3:windowsSettings> + <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage> + <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware> </asmv3:windowsSettings> </asmv3:application> </assembly> diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 46ba13c4cd..2a10510b0f 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -1117,8 +1117,8 @@ static void out_data_ring(char *output, size_t size) /// Continue to append data to last screen line. /// /// @param output Data to append to screen lines. -/// @param remaining Size of data. -/// @param new_line If true, next data output will be on a new line. +/// @param count Size of data. +/// @param eof If true, there will be no more data output. static void out_data_append_to_screen(char *output, size_t *count, bool eof) FUNC_ATTR_NONNULL_ALL { @@ -1168,8 +1168,7 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, rbuffer_consumed(buf, cnt); } - // Move remaining data to start of buffer, so the buffer can never - // wrap around. + // Move remaining data to start of buffer, so the buffer can never wrap around. rbuffer_reset(buf); } diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index ede17bc7c8..e5bdd56fe6 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -2,13 +2,16 @@ #include <stdbool.h> #include <string.h> +#include "klib/kvec.h" #include "nvim/ascii_defs.h" #include "nvim/fileio.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" +#include "nvim/strings.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/stdpaths.c.generated.h" @@ -93,6 +96,46 @@ bool appname_is_valid(void) return true; } +/// Remove duplicate directories in the given XDG directory. +/// @param[in] List of directories possibly with duplicates +/// @param[out] List of directories without duplicates +static char *xdg_remove_duplicate(char *ret, const char *sep) +{ + kvec_t(char *) data = KV_INITIAL_VALUE; + char *saveptr; + + char *token = os_strtok(ret, sep, &saveptr); + while (token != NULL) { + // Check if the directory is not already in the list + bool is_duplicate = false; + for (size_t i = 0; i < data.size; i++) { + if (path_fnamecmp(kv_A(data, i), token) == 0) { + is_duplicate = true; + break; + } + } + // If it's not a duplicate, add it to the list + if (!is_duplicate) { + kv_push(data, token); + } + token = os_strtok(NULL, sep, &saveptr); + } + + StringBuilder result = KV_INITIAL_VALUE; + + for (size_t i = 0; i < data.size; i++) { + if (i == 0) { + kv_printf(result, "%s", kv_A(data, i)); + } else { + kv_printf(result, "%s%s", sep, kv_A(data, i)); + } + } + + kv_destroy(data); + xfree(ret); + return result.items; +} + /// Return XDG variable value /// /// @param[in] idx XDG variable to use. @@ -131,6 +174,10 @@ char *stdpaths_get_xdg_var(const XDGVarType idx) ret = xmemdupz(ret, len >= 2 ? len - 1 : 0); // Trim trailing slash. } + if ((idx == kXDGDataDirs || idx == kXDGConfigDirs) && ret != NULL) { + ret = xdg_remove_duplicate(ret, ENV_SEPSTR); + } + return ret; } @@ -151,7 +198,7 @@ char *get_xdg_home(const XDGVarType idx) assert(appname_len < (IOSIZE - sizeof("-data"))); if (dir) { - xstrlcpy(IObuff, appname, appname_len + 1); + xmemcpyz(IObuff, appname, appname_len); #if defined(MSWIN) if (idx == kXDGDataHome || idx == kXDGStateHome) { xstrlcat(IObuff, "-data", IOSIZE); diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index 8886d6068d..d5a8355470 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -203,7 +203,7 @@ static void init_users(void) os_get_usernames(&ga_users); } -/// Given to ExpandGeneric() to obtain an user names. +/// Given to ExpandGeneric() to obtain user names. char *get_users(expand_T *xp, int idx) { init_users(); diff --git a/src/nvim/path.c b/src/nvim/path.c index 4de18c7530..d782d1a989 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -62,8 +62,8 @@ enum { /// @return Enum of type FileComparison. @see FileComparison. FileComparison path_full_compare(char *const s1, char *const s2, const bool checkname, const bool expandenv) + FUNC_ATTR_NONNULL_ALL { - assert(s1 && s2); char exp1[MAXPATHL]; char full1[MAXPATHL]; char full2[MAXPATHL]; @@ -134,9 +134,8 @@ char *path_tail(const char *fname) /// - `fname` if it contains no path separator. /// - Never NULL. char *path_tail_with_sep(char *fname) + FUNC_ATTR_NONNULL_ALL { - assert(fname != NULL); - // Don't remove the '/' from "c:/file". char *past_head = get_past_head(fname); char *tail = path_tail(fname); @@ -181,8 +180,8 @@ const char *invocation_path_tail(const char *invocation, size_t *len) /// @return Pointer to first found path separator + 1. /// An empty string, if `fname` doesn't contain a path separator, const char *path_next_component(const char *fname) + FUNC_ATTR_NONNULL_ALL { - assert(fname != NULL); while (*fname != NUL && !vim_ispathsep(*fname)) { MB_PTR_ADV(fname); } @@ -212,6 +211,7 @@ int path_head_length(void) /// - True if path begins with a path head /// - False otherwise bool is_path_head(const char *path) + FUNC_ATTR_NONNULL_ALL { #ifdef MSWIN return isalpha((uint8_t)path[0]) && path[1] == ':'; @@ -224,6 +224,7 @@ bool is_path_head(const char *path) /// Unix: after "/"; Win: after "c:\" /// If there is no head, path is returned. char *get_past_head(const char *path) + FUNC_ATTR_NONNULL_ALL { const char *retval = path; @@ -281,6 +282,7 @@ bool vim_ispathlistsep(int c) /// Must be 1 or more. /// It's done in-place. void shorten_dir_len(char *str, int trim_len) + FUNC_ATTR_NONNULL_ALL { char *tail = path_tail(str); char *d = str; @@ -317,6 +319,7 @@ void shorten_dir_len(char *str, int trim_len) /// Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname" /// It's done in-place. void shorten_dir(char *str) + FUNC_ATTR_NONNULL_ALL { shorten_dir_len(str, 1); } @@ -325,6 +328,7 @@ void shorten_dir(char *str) /// Also returns true if there is no directory name. /// "fname" must be writable!. bool dir_of_file_exists(char *fname) + FUNC_ATTR_NONNULL_ALL { char *p = path_tail_with_sep(fname); if (p == fname) { @@ -795,6 +799,7 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in // Moves "*psep" back to the previous path separator in "path". // Returns FAIL is "*psep" ends up at the beginning of "path". static int find_previous_pathsep(char *path, char **psep) + FUNC_ATTR_NONNULL_ALL { // skip the current separator if (*psep > path && vim_ispathsep(**psep)) { @@ -815,6 +820,7 @@ static int find_previous_pathsep(char *path, char **psep) /// Returns true if "maybe_unique" is unique wrt other_paths in "gap". /// "maybe_unique" is the end portion of "((char **)gap->ga_data)[i]". static bool is_unique(char *maybe_unique, garray_T *gap, int i) + FUNC_ATTR_NONNULL_ALL { char **other_paths = gap->ga_data; @@ -844,6 +850,7 @@ static bool is_unique(char *maybe_unique, garray_T *gap, int i) // TODO(vim): handle upward search (;) and path limiter (**N) notations by // expanding each into their equivalent path(s). static void expand_path_option(char *curdir, garray_T *gap) + FUNC_ATTR_NONNULL_ALL { char *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; char *buf = xmalloc(MAXPATHL); @@ -899,6 +906,7 @@ static void expand_path_option(char *curdir, garray_T *gap) // fname: /foo/bar/baz/quux.txt // returns: ^this static char *get_path_cutoff(char *fname, garray_T *gap) + FUNC_ATTR_NONNULL_ALL { int maxlen = 0; char **path_part = gap->ga_data; @@ -935,6 +943,7 @@ static char *get_path_cutoff(char *fname, garray_T *gap) /// that they are unique with respect to each other while conserving the part /// that matches the pattern. Beware, this is at least O(n^2) wrt "gap->ga_len". static void uniquefy_paths(garray_T *gap, char *pattern) + FUNC_ATTR_NONNULL_ALL { char **fnames = gap->ga_data; bool sort_again = false; @@ -999,7 +1008,7 @@ static void uniquefy_paths(garray_T *gap, char *pattern) memmove(path, path_cutoff, strlen(path_cutoff) + 1); } else { // Here all files can be reached without path, so get shortest - // unique path. We start at the end of the path. */ + // unique path. We start at the end of the path. char *pathsep_p = path + len - 1; while (find_previous_pathsep(path, &pathsep_p)) { if (vim_regexec(®match, pathsep_p + 1, 0) @@ -1115,6 +1124,7 @@ const char *gettail_dir(const char *const fname) /// /// @param flags EW_* flags static int expand_in_path(garray_T *const gap, char *const pattern, const int flags) + FUNC_ATTR_NONNULL_ALL { garray_T path_ga; @@ -1147,6 +1157,7 @@ static int expand_in_path(garray_T *const gap, char *const pattern, const int fl /// Return true if "p" contains what looks like an environment variable. /// Allowing for escaping. static bool has_env_var(char *p) + FUNC_ATTR_NONNULL_ALL { for (; *p; MB_PTR_ADV(p)) { if (*p == '\\' && p[1] != NUL) { @@ -1163,6 +1174,7 @@ static bool has_env_var(char *p) // Return true if "p" contains a special wildcard character, one that Vim // cannot expand, requires using a shell. static bool has_special_wildchar(char *p, int flags) + FUNC_ATTR_NONNULL_ALL { for (; *p; MB_PTR_ADV(p)) { // Disallow line break characters. @@ -1356,6 +1368,7 @@ void FreeWild(int count, char **files) /// @return true if we can expand this backtick thing here. static bool vim_backtick(char *p) + FUNC_ATTR_NONNULL_ALL { return *p == '`' && *(p + 1) != NUL && *(p + strlen(p) - 1) == '`'; } @@ -1366,6 +1379,7 @@ static bool vim_backtick(char *p) /// /// @param flags EW_* flags static int expand_backtick(garray_T *gap, char *pat, int flags) + FUNC_ATTR_NONNULL_ALL { char *p; char *buffer; @@ -1419,6 +1433,7 @@ static int expand_backtick(garray_T *gap, char *pat, int flags) /// When 'shellslash' set do it the other way around. /// When the path looks like a URL leave it unmodified. void slash_adjust(char *p) + FUNC_ATTR_NONNULL_ALL { if (path_with_url(p)) { return; @@ -1451,6 +1466,7 @@ void slash_adjust(char *p) /// /// @param f filename void addfile(garray_T *gap, char *f, int flags) + FUNC_ATTR_NONNULL_ALL { bool isdir; FileInfo file_info; @@ -1500,6 +1516,7 @@ void addfile(garray_T *gap, char *f, int flags) // resulting file name is simplified in place and will either be the same // length as that supplied, or shorter. void simplify_filename(char *filename) + FUNC_ATTR_NONNULL_ALL { int components = 0; bool stripping_disabled = false; @@ -1757,6 +1774,7 @@ bool path_has_drive_letter(const char *p) // Also check for ":\\", which MS Internet Explorer accepts, return // URL_BACKSLASH. int path_is_url(const char *p) + FUNC_ATTR_NONNULL_ALL { // In the spec ':' is enough to recognize a scheme // https://url.spec.whatwg.org/#scheme-state @@ -1773,6 +1791,7 @@ int path_is_url(const char *p) /// @param fname is the filename to test /// @return URL_SLASH for "name://", URL_BACKSLASH for "name:\\", zero otherwise. int path_with_url(const char *fname) + FUNC_ATTR_NONNULL_ALL { const char *p; @@ -1802,6 +1821,7 @@ int path_with_url(const char *fname) } bool path_with_extension(const char *path, const char *extension) + FUNC_ATTR_NONNULL_ALL { const char *last_dot = strrchr(path, '.'); if (!last_dot) { @@ -1812,6 +1832,7 @@ bool path_with_extension(const char *path, const char *extension) /// Return true if "name" is a full (absolute) path name or URL. bool vim_isAbsName(const char *name) + FUNC_ATTR_NONNULL_ALL { return path_with_url(name) != 0 || path_is_absolute(name); } @@ -1951,6 +1972,7 @@ void path_fix_case(char *name) /// Takes care of multi-byte characters. /// "b" must point to the start of the file name int after_pathsep(const char *b, const char *p) + FUNC_ATTR_NONNULL_ALL { return p > b && vim_ispathsep(p[-1]) && utf_head_off(b, p - 1) == 0; @@ -2240,6 +2262,7 @@ int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int /// @return true if "fname" matches with an entry in 'suffixes'. bool match_suffix(char *fname) + FUNC_ATTR_NONNULL_ALL { #define MAXSUFLEN 30 // maximum length of a file suffix char suf_buf[MAXSUFLEN]; @@ -2272,14 +2295,22 @@ bool match_suffix(char *fname) /// @param directory Directory name, relative to current directory. /// @return `FAIL` for failure, `OK` for success. int path_full_dir_name(char *directory, char *buffer, size_t len) + FUNC_ATTR_NONNULL_ALL { - int SUCCESS = 0; - int retval = OK; - if (strlen(directory) == 0) { return os_dirname(buffer, len); } + if (os_realpath(directory, buffer, len) != NULL) { + return OK; + } + + // Path does not exist (yet). For a full path fail, will use the path as-is. + if (path_is_absolute(directory)) { + return FAIL; + } + // For a relative path use the current directory and append the file name. + char old_dir[MAXPATHL]; // Get current directory name. @@ -2287,38 +2318,17 @@ int path_full_dir_name(char *directory, char *buffer, size_t len) return FAIL; } - // We have to get back to the current dir at the end, check if that works. - if (os_chdir(old_dir) != SUCCESS) { + xstrlcpy(buffer, old_dir, len); + if (append_path(buffer, directory, len) == FAIL) { return FAIL; } - if (os_chdir(directory) != SUCCESS) { - // Path does not exist (yet). For a full path fail, - // will use the path as-is. For a relative path use - // the current directory and append the file name. - if (path_is_absolute(directory)) { - // Do not return immediately since we may be in the wrong directory. - retval = FAIL; - } else { - xstrlcpy(buffer, old_dir, len); - append_path(buffer, directory, len); - } - } else if (os_dirname(buffer, len) == FAIL) { - // Do not return immediately since we are in the wrong directory. - retval = FAIL; - } - - if (os_chdir(old_dir) != SUCCESS) { - // That shouldn't happen, since we've tested if it works. - retval = FAIL; - emsg(_(e_prev_dir)); - } - - return retval; + return OK; } // Append to_append to path with a slash in between. int append_path(char *path, const char *to_append, size_t max_len) + FUNC_ATTR_NONNULL_ALL { size_t current_length = strlen(path); size_t to_append_length = strlen(to_append); @@ -2358,6 +2368,7 @@ int append_path(char *path, const char *to_append, size_t max_len) /// /// @return FAIL for failure, OK for success. static int path_to_absolute(const char *fname, char *buf, size_t len, int force) + FUNC_ATTR_NONNULL_ALL { const char *p; *buf = NUL; @@ -2395,6 +2406,7 @@ static int path_to_absolute(const char *fname, char *buf, size_t len, int force) /// /// @return `true` if "fname" is absolute. bool path_is_absolute(const char *fname) + FUNC_ATTR_NONNULL_ALL { #ifdef MSWIN if (*fname == NUL) { @@ -2443,7 +2455,7 @@ void path_guess_exepath(const char *argv0, char *buf, size_t bufsize) if (dir_len + 1 > sizeof(NameBuff)) { continue; } - xstrlcpy(NameBuff, dir, dir_len + 1); + xmemcpyz(NameBuff, dir, dir_len); xstrlcat(NameBuff, PATHSEPSTR, sizeof(NameBuff)); xstrlcat(NameBuff, argv0, sizeof(NameBuff)); if (os_can_exe(NameBuff, NULL, false)) { diff --git a/src/nvim/plines.c b/src/nvim/plines.c index eca07f0144..5881d34c48 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -44,6 +44,8 @@ /// @param col /// /// @return Number of cells. +/// +/// @see charsize_nowrap() int win_chartabsize(win_T *wp, char *p, colnr_T col) { buf_T *buf = wp->w_buffer; @@ -55,7 +57,7 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col) /// Like linetabsize_str(), but "s" starts at virtual column "startvcol". /// -/// @param startcol +/// @param startvcol /// @param s /// /// @return Number of cells the string will take on the screen. @@ -74,7 +76,7 @@ int linetabsize_col(int startvcol, char *s) /// screen, taking into account the size of a tab and inline virtual text. int linetabsize(win_T *wp, linenr_T lnum) { - return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), (colnr_T)MAXCOL); + return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), MAXCOL); } static const uint32_t inline_filter[4] = {[kMTMetaInline] = kMTFilterSelect }; @@ -234,7 +236,7 @@ CharSize charsize_regular(CharsizeArg *csarg, char *const cur, colnr_T const vco wcol += col_off_prev; } - if (wcol + size > wp->w_width) { + if (wcol + size > wp->w_width_inner) { // cells taken by 'showbreak'/'breakindent' halfway current char int head_mid = csarg->indent_width; if (head_mid == INT_MIN) { @@ -247,7 +249,7 @@ CharSize charsize_regular(CharsizeArg *csarg, char *const cur, colnr_T const vco } csarg->indent_width = head_mid; } - if (head_mid > 0 && wcol + size > wp->w_width_inner) { + if (head_mid > 0) { // Calculate effective window width. int prev_rem = wp->w_width_inner - wcol; int width = width2 - head_mid; @@ -375,6 +377,20 @@ CharSize charsize_fast(CharsizeArg *csarg, colnr_T const vcol, int32_t const cur return charsize_fast_impl(csarg->win, csarg->use_tabstop, vcol, cur_char); } +/// Get the number of cells taken up on the screen at given virtual column. +/// +/// @see win_chartabsize() +int charsize_nowrap(buf_T *buf, bool use_tabstop, colnr_T vcol, int32_t cur_char) +{ + if (cur_char == TAB && use_tabstop) { + return tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array); + } else if (cur_char < 0) { + return kInvalidByteCells; + } else { + return char2cells(cur_char); + } +} + /// Check that virtual column "vcol" is in the rightmost column of window "wp". /// /// @param wp window @@ -405,48 +421,63 @@ static bool in_win_border(win_T *wp, colnr_T vcol) /// Calculate virtual column until the given "len". /// -/// @param arg Argument to charsize functions. -/// @param vcol Starting virtual column. -/// @param len First byte of the end character, or MAXCOL. +/// @param csarg Argument to charsize functions. +/// @param vcol_arg Starting virtual column. +/// @param len First byte of the end character, or MAXCOL. /// /// @return virtual column before the character at "len", -/// or full size of the line if "len" is MAXCOL. -int linesize_regular(CharsizeArg *const csarg, int vcol, colnr_T const len) +/// or full size of the line if "len" is MAXCOL. +int linesize_regular(CharsizeArg *const csarg, int vcol_arg, colnr_T const len) { char *const line = csarg->line; + int64_t vcol = vcol_arg; StrCharInfo ci = utf_ptr2StrCharInfo(line); while (ci.ptr - line < len && *ci.ptr != NUL) { - vcol += charsize_regular(csarg, ci.ptr, vcol, ci.chr.value).width; + vcol += charsize_regular(csarg, ci.ptr, vcol_arg, ci.chr.value).width; ci = utfc_next(ci); + if (vcol > MAXCOL) { + vcol_arg = MAXCOL; + break; + } else { + vcol_arg = (int)vcol; + } } // Check for inline virtual text after the end of the line. - if (len == MAXCOL && csarg->virt_row >= 0) { - (void)charsize_regular(csarg, ci.ptr, vcol, ci.chr.value); + if (len == MAXCOL && csarg->virt_row >= 0 && *ci.ptr == NUL) { + (void)charsize_regular(csarg, ci.ptr, vcol_arg, ci.chr.value); vcol += csarg->cur_text_width_left + csarg->cur_text_width_right; + vcol_arg = vcol > MAXCOL ? MAXCOL : (int)vcol; } - return vcol; + return vcol_arg; } -/// Like linesize_regular(), but can be used when CStype is kCharsizeFast. +/// Like linesize_regular(), but can be used when CSType is kCharsizeFast. /// /// @see linesize_regular -int linesize_fast(CharsizeArg const *const csarg, int vcol, colnr_T const len) +int linesize_fast(CharsizeArg const *const csarg, int vcol_arg, colnr_T const len) { win_T *const wp = csarg->win; bool const use_tabstop = csarg->use_tabstop; char *const line = csarg->line; + int64_t vcol = vcol_arg; StrCharInfo ci = utf_ptr2StrCharInfo(line); while (ci.ptr - line < len && *ci.ptr != NUL) { - vcol += charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value).width; + vcol += charsize_fast_impl(wp, use_tabstop, vcol_arg, ci.chr.value).width; ci = utfc_next(ci); + if (vcol > MAXCOL) { + vcol_arg = MAXCOL; + break; + } else { + vcol_arg = (int)vcol; + } } - return vcol; + return vcol_arg; } /// Get how many virtual columns inline virtual text should offset the cursor. @@ -540,7 +571,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en if (ci.chr.value == TAB && (State & MODE_NORMAL) && !wp->w_p_list - && !virtual_active() + && !virtual_active(wp) && !(VIsual_active && ((*p_sel == 'e') || ltoreq(*pos, VIsual)))) { // cursor at end *cursor = vcol + incr - 1; @@ -556,7 +587,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en /// /// @param posp /// -/// @retujrn The virtual cursor column. +/// @return The virtual cursor column. colnr_T getvcol_nolist(pos_T *posp) { int list_save = curwin->w_p_list; @@ -583,7 +614,7 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *e { colnr_T col; - if (virtual_active()) { + if (virtual_active(wp)) { // For virtual mode, only want one value getvcol(wp, pos, &col, NULL, NULL); @@ -593,7 +624,7 @@ void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *e // Cannot put the cursor on part of a wide character. char *ptr = ml_get_buf(wp->w_buffer, pos->lnum); - if (pos->col < (colnr_T)strlen(ptr)) { + if (pos->col < ml_get_buf_len(wp->w_buffer, pos->lnum)) { int c = utf_ptr2char(ptr + pos->col); if ((c != TAB) && vim_isprintc(c)) { endadd = (colnr_T)(char2cells(c) - 1); @@ -850,26 +881,29 @@ int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const (lnum == wp->w_topline ? wp->w_topfill : win_get_fill(wp, lnum))); } -/// Get the number of screen lines a range of buffer lines will take in window "wp". -/// This takes care of both folds and topfill. +/// Return number of window lines a physical line range will occupy in window "wp". +/// Takes into account folding, 'wrap', topfill and filler lines beyond the end of the buffer. /// /// XXX: Because of topfill, this only makes sense when first >= wp->w_topline. /// -/// @param first first line number -/// @param last last line number -/// @param limit_winheight when true limit each line to window height +/// @param first first line number +/// @param last last line number +/// @param max number of lines to limit the height to /// /// @see win_text_height -int plines_m_win(win_T *wp, linenr_T first, linenr_T last, bool limit_winheight) +int plines_m_win(win_T *wp, linenr_T first, linenr_T last, int max) { int count = 0; - while (first <= last) { + while (first <= last && count < max) { linenr_T next = first; - count += plines_win_full(wp, first, &next, NULL, false, limit_winheight); + count += plines_win_full(wp, first, &next, NULL, false, false); first = next + 1; } - return count; + if (first == wp->w_buffer->b_ml.ml_line_count + 1) { + count += win_get_fill(wp, first); + } + return MIN(max, count); } /// Get the number of screen lines a range of text will take in window "wp". @@ -902,7 +936,7 @@ int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_ if (start_vcol >= 0) { linenr_T lnum_next = lnum; - const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); + const bool folded = hasFolding(wp, lnum, &lnum, &lnum_next); height_cur_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); height_sum_nofill += height_cur_nofill; const int64_t row_off = (start_vcol < width1 || width2 <= 0) @@ -914,7 +948,7 @@ int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_ while (lnum <= end_lnum) { linenr_T lnum_next = lnum; - const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); + const bool folded = hasFolding(wp, lnum, &lnum, &lnum_next); height_sum_fill += win_get_fill(wp, lnum); height_cur_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); height_sum_nofill += height_cur_nofill; diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po index bdf67933f3..315c860307 100644 --- a/src/nvim/po/ja.po +++ b/src/nvim/po/ja.po @@ -1,9 +1,9 @@ -# Japanese translation for Vim +# Japanese translation for Neovim # # Do ":help uganda" in Vim to read copying and usage conditions. # Do ":help credits" in Vim to see a list of people who contributed. # -# Copyright (C) 2001-2018 MURAOKA Taro <koron.kaoriya@gmail.com>, +# Copyright (C) 2001-2023 MURAOKA Taro <koron.kaoriya@gmail.com>, # vim-jp <http://vim-jp.org/> # # THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. @@ -12,11 +12,11 @@ # msgid "" msgstr "" -"Project-Id-Version: Vim 8.1\n" +"Project-Id-Version: Neovim 0.10\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-07-18 00:43+0900\n" -"PO-Revision-Date: 2017-05-18 00:45+0900\n" -"Last-Translator: MURAOKA Taro <koron.kaoriya@gmail.com>\n" +"POT-Creation-Date: 2024-03-14 19:01+0900\n" +"PO-Revision-Date: 2024-03-14 19:46+0900\n" +"Last-Translator: ite-usagi <https://github.com/ite-usagi>\n" "Language-Team: Japanese <https://github.com/vim-jp/lang-ja>\n" "Language: ja\n" "MIME-Version: 1.0\n" @@ -24,43 +24,1311 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -msgid "E831: bf_key_init() called with empty password" -msgstr "E831: bf_key_init() が空パスワードで呼び出されました" +msgid "(local to window)" +msgstr "(ウィンドウについてローカル)" -msgid "E820: sizeof(uint32_t) != 4" -msgstr "E820: sizeof(uint32_t) != 4" +msgid "(local to buffer)" +msgstr "(バッファについてローカル)" -msgid "E817: Blowfish big/little endian use wrong" -msgstr "E817: Blowfish暗号のビッグ/リトルエンディアンが間違っています" +msgid "(global or local to buffer)" +msgstr "(グローバル/バッファについてローカル)" -msgid "E818: sha256 test failed" -msgstr "E818: sha256のテストに失敗しました" +msgid "" +"\" Each \"set\" line shows the current value of an option (on the left)." +msgstr "\" それぞれの \"set\" 行はオプションの現在の値を(左側に)示しています。" -msgid "E819: Blowfish test failed" -msgstr "E819: Blowfish暗号のテストに失敗しました" +msgid "\" Hit <Enter> on a \"set\" line to execute it." +msgstr "\" \"set\" 行で <Enter> を打つとそれが実行されます。" -msgid "[Location List]" -msgstr "[ロケーションリスト]" +msgid "\" A boolean option will be toggled." +msgstr "\" 切替オプションは切り替えられます。" -msgid "[Quickfix List]" -msgstr "[Quickfixリスト]" +msgid "" +"\" For other options you can edit the value before hitting " +"<Enter>." +msgstr "" +"\" その他のオプションは <Enter> を打つ前に値を編集することができま" +"す。" -msgid "E855: Autocommands caused command to abort" -msgstr "E855: autocommandがコマンドの停止を引き起こしました" +msgid "\" Hit <Enter> on a help line to open a help window on this option." +msgstr "" +"\" ヘルプ行で <Enter> を打つと、このオプションのへルプウィンドウが開きます。" -msgid "E82: Cannot allocate any buffer, exiting..." -msgstr "E82: バッファを1つも作成できないので、終了します..." +msgid "\" Hit <Enter> on an index line to jump there." +msgstr "\" インデックス行で <Enter> を打つと、そこにジャンプします。" -msgid "E83: Cannot allocate buffer, using other one..." -msgstr "E83: バッファを作成できないので、他のを使用します..." +msgid "\" Hit <Space> on a \"set\" line to refresh it." +msgstr "\" \"set\" 行で <Spece> を打つと、最新の値が読込まれます。" + +msgid "important" +msgstr "重要" + +msgid "behave very Vi compatible (not advisable)" +msgstr "Vi との互換性を非常に高くする (望ましくない)" + +msgid "list of flags to specify Vi compatibility" +msgstr "Vi との互換性を指定するフラグのリスト" + +msgid "paste mode, insert typed text literally" +msgstr "paste モード、タイプされたテキストをそのまま挿入する" + +msgid "list of directories used for runtime files and plugins" +msgstr "ランタイムファイルとプラグインに使われるディレクトリのリスト" + +msgid "list of directories used for plugin packages" +msgstr "プラグインパッケージに使われるディレクトリのリスト" + +msgid "name of the main help file" +msgstr "メインのヘルプファイルの名前" + +msgid "moving around, searching and patterns" +msgstr "移動、検索とパターン" + +msgid "list of flags specifying which commands wrap to another line" +msgstr "どのコマンドが行をまたぐかを指定するフラグのリスト" + +msgid "" +"many jump commands move the cursor to the first non-blank\n" +"character of a line" +msgstr "多くのジャンプ命令で、カーソルが行内の最初の非空白文字に移動する" + +msgid "nroff macro names that separate paragraphs" +msgstr "段落を分けるための nroff マクロの名前" + +msgid "nroff macro names that separate sections" +msgstr "章を分けるための nroff マクロの名前" + +msgid "list of directory names used for file searching" +msgstr "ファイルの検索に用いられるディレクトリ名のリスト" + +msgid ":cd without argument goes to the home directory" +msgstr "引数無しの :cd でホームディレクトリに移動する" + +msgid "list of directory names used for :cd" +msgstr ":cd に用いられるディレクトリ名のリスト" + +msgid "change to directory of file in buffer" +msgstr "バッファ内のファイルのディレクトリに変更する" + +msgid "search commands wrap around the end of the buffer" +msgstr "検索コマンドがバッファの末尾/先頭をまたぐ" + +msgid "show match for partly typed search command" +msgstr "部分的に入力された検索コマンドのマッチを表示する" + +msgid "change the way backslashes are used in search patterns" +msgstr "検索パターン内のバックスラッシュの扱いを変更する" + +msgid "select the default regexp engine used" +msgstr "既定で使われる正規表現エンジンを選択する" + +msgid "ignore case when using a search pattern" +msgstr "検索パターンにおいて大文字と小文字を区別しない" + +msgid "override 'ignorecase' when pattern has upper case characters" +msgstr "検索パターンが大文字を含んでいたら 'ignorecase' を上書きする" + +msgid "what method to use for changing case of letters" +msgstr "大文字・小文字を変更する際にどの方法を使うか" + +msgid "maximum amount of memory in Kbyte used for pattern matching" +msgstr "パターンマッチングに使う最大メモリ量 (Kbyte)" + +msgid "pattern for a macro definition line" +msgstr "マクロ定義行のためのパターン" + +msgid "pattern for an include-file line" +msgstr "include 行のためのパターン" + +msgid "expression used to transform an include line to a file name" +msgstr "include 行をファイル名に変換するために使われる式" + +msgid "tags" +msgstr "タグ" + +msgid "use binary searching in tags files" +msgstr "tags ファイル内で二分探索を使う" + +msgid "number of significant characters in a tag name or zero" +msgstr "タグ名で有効になる文字数、あるいはゼロ" + +msgid "list of file names to search for tags" +msgstr "tags を検索するファイル名のリスト" + +msgid "" +"how to handle case when searching in tags files:\n" +"\"followic\" to follow 'ignorecase', \"ignore\" or \"match\"" +msgstr "" +"タグファイル内を検索するときに大文字小文字をどう扱うか:\n" +"'ignorecase' に従うなら \"followic\"、あるいは \"ignore\" か \"match\"" + +msgid "file names in a tags file are relative to the tags file" +msgstr "tags ファイル内のファイル名は tags ファイルからの相対パス" + +msgid "a :tag command will use the tagstack" +msgstr ":tag コマンドはタグスタックを使う" + +msgid "when completing tags in Insert mode show more info" +msgstr "挿入モードでタグを補完するときにより多くの情報を表示する" + +msgid "a function to be used to perform tag searches" +msgstr "タグの検索を実行する際に使われる関数" + +msgid "displaying text" +msgstr "テキストの表示" + +msgid "number of lines to scroll for CTRL-U and CTRL-D" +msgstr "CTRL-U と CTRL-D でスクロールする行数" + +msgid "scroll by screen line" +msgstr "スクリーン行でスクロールする" + +msgid "number of screen lines to show around the cursor" +msgstr "カーソルの上下に表示されるスクリーン行数" + +msgid "long lines wrap" +msgstr "長い行を折り返して表示する" + +msgid "wrap long lines at a character in 'breakat'" +msgstr "'breakat' の文字で長い行を折り返す" + +msgid "preserve indentation in wrapped text" +msgstr "折り返されたテキストでインデントを保持する" + +msgid "adjust breakindent behaviour" +msgstr "breakindent の挙動を調整する" + +msgid "which characters might cause a line break" +msgstr "どの文字のところで行が折り返されるか" + +msgid "string to put before wrapped screen lines" +msgstr "折り返されたスクリーン行の前に表示される文字列" + +msgid "minimal number of columns to scroll horizontally" +msgstr "水平スクロールの最小桁数" + +msgid "minimal number of columns to keep left and right of the cursor" +msgstr "カーソルの左右に表示する最小桁数" + +msgid "" +"include \"lastline\" to show the last line even if it doesn't fit\n" +"include \"uhex\" to show unprintable characters as a hex number" +msgstr "" +"最後の行が収まらない場合でも表示するには \"lastline\" を含めること\n" +"表示できない文字を 16 進数で表示するには \"uhex\" を含めること" + +msgid "characters to use for the status line, folds and filler lines" +msgstr "ステータス行、折畳み、フィラー行に使われる文字" + +msgid "number of lines used for the command-line" +msgstr "コマンドラインに使われる行数" + +msgid "width of the display" +msgstr "画面の幅" + +msgid "number of lines in the display" +msgstr "画面の行数" + +msgid "number of lines to scroll for CTRL-F and CTRL-B" +msgstr "CTRL-F と CTRL-B でスクロールする行数" + +msgid "don't redraw while executing macros" +msgstr "マクロを実行中に再描画しない" + +msgid "timeout for 'hlsearch' and :match highlighting in msec" +msgstr "'hlsearch' と :match のハイライト処理のタイムアウト (ミリ秒)" + +msgid "" +"delay in msec for each char written to the display\n" +"(for debugging)" +msgstr "" +"それぞれの文字が画面に描かれるまでの遅延時間 (ミリ秒)\n" +"(デバッグ用)" + +msgid "show <Tab> as ^I and end-of-line as $" +msgstr "<Tab> を ^I として表示し、改行を $ として表示する" + +msgid "list of strings used for list mode" +msgstr "リストモードで使われる文字列のリスト" + +msgid "show the line number for each line" +msgstr "それぞれの行に行番号を表示する" + +msgid "show the relative line number for each line" +msgstr "それぞれの行に相対行番号を表示する" + +msgid "number of columns to use for the line number" +msgstr "行番号に使われる桁数" + +msgid "controls whether concealable text is hidden" +msgstr "conceal 可能なテキストを隠すかどうかを制御する" + +msgid "modes in which text in the cursor line can be concealed" +msgstr "カーソル行のテキストを conceal 表示するモード" + +msgid "syntax, highlighting and spelling" +msgstr "構文ハイライトとスペルチェック" + +msgid "\"dark\" or \"light\"; the background color brightness" +msgstr "\"dark\" か \"light\"; 背景色の明るさ" + +msgid "type of file; triggers the FileType event when set" +msgstr "ファイルのタイプ; セットされると FileType イベントが発生する" + +msgid "name of syntax highlighting used" +msgstr "使用される構文ハイライトの名前" + +msgid "maximum column to look for syntax items" +msgstr "構文アイテムを検索する最大桁数" + +msgid "which highlighting to use for various occasions" +msgstr "様々な対象に対してどのハイライト表示を使うか" + +msgid "highlight all matches for the last used search pattern" +msgstr "最後の検索パターンに対する全てのマッチをハイライト表示する" + +msgid "use GUI colors for the terminal" +msgstr "端末で GUI カラーを使う" + +msgid "highlight the screen column of the cursor" +msgstr "カーソルのある画面上の桁をハイライト表示する" + +msgid "highlight the screen line of the cursor" +msgstr "カーソルのある画面上の行をハイライト表示する" + +msgid "specifies which area 'cursorline' highlights" +msgstr "'cursorline' がどの領域をハイライト表示するか指定する" + +msgid "columns to highlight" +msgstr "ハイライト表示する桁" + +msgid "highlight spelling mistakes" +msgstr "スペルミスをハイライト表示する" + +msgid "list of accepted languages" +msgstr "受け付ける言語のリスト" + +msgid "file that \"zg\" adds good words to" +msgstr "\"zg\" で正しい単語を追加するファイル" + +msgid "pattern to locate the end of a sentence" +msgstr "文の末尾を見つけるのに使うパターン" + +msgid "flags to change how spell checking works" +msgstr "どのようにスペルチェックが動作するかを変更するフラグ" + +msgid "methods used to suggest corrections" +msgstr "修正を提案する際に使われる方法" + +msgid "amount of memory used by :mkspell before compressing" +msgstr "圧縮の前に :mkspell で使われるメモリ量" + +msgid "multiple windows" +msgstr "複数ウィンドウ" + +msgid "0, 1, 2 or 3; when to use a status line for the last window" +msgstr "0, 1, 2 または 3; 最後のウィンドウのステータス行がいつ使われるか" + +msgid "custom format for the status column" +msgstr "ステータス列に使われる書式" + +msgid "alternate format to be used for a status line" +msgstr "ステータス行に使われる書式" + +msgid "make all windows the same size when adding/removing windows" +msgstr "ウィンドウを追加/削除するときに全ウィンドウのサイズを等しくする" + +msgid "in which direction 'equalalways' works: \"ver\", \"hor\" or \"both\"" +msgstr "どの方向に 'equalalways' が働くか: \"ver\", \"hor\" または \"both\"" + +msgid "minimal number of lines used for the current window" +msgstr "現在のウィンドウに使われる最小行数" + +msgid "minimal number of lines used for any window" +msgstr "任意のウィンドウに使われる最小行数" + +msgid "keep window focused on a single buffer" +msgstr "ウィンドウを単一のバッファにフォーカスする" + +msgid "keep the height of the window" +msgstr "ウィンドウの高さを保つ" + +msgid "keep the width of the window" +msgstr "ウィンドウの幅を保つ" + +msgid "minimal number of columns used for the current window" +msgstr "現在のウィンドウに使われる最小桁数" + +msgid "minimal number of columns used for any window" +msgstr "任意のウィンドウに使われる最小桁数" + +msgid "initial height of the help window" +msgstr "ヘルプウィンドウの開始時の高さ" + +msgid "default height for the preview window" +msgstr "プレビューウィンドウの既定の高さ" + +msgid "identifies the preview window" +msgstr "プレビューウィンドウを識別する" + +msgid "don't unload a buffer when no longer shown in a window" +msgstr "バッファがウィンドウに表示されていないときにアンロードしない" + +msgid "" +"\"useopen\" and/or \"split\"; which window to use when jumping\n" +"to a buffer" +msgstr "" +"\"useopen\" かつ/または \"split\"; バッファにジャンプするときに\n" +"どのウィンドウを使うか" + +msgid "a new window is put below the current one" +msgstr "新しいウィンドウは現在のものの下に置かれる" + +msgid "determines scroll behavior for split windows" +msgstr "ウィンドウ分割のスクロール動作を決める" + +msgid "a new window is put right of the current one" +msgstr "新しいウィンドウは現在のものの右に置かれる" + +msgid "this window scrolls together with other bound windows" +msgstr "このウィンドウは他の同調ウィンドウと一緒にスクロールする" + +msgid "\"ver\", \"hor\" and/or \"jump\"; list of options for 'scrollbind'" +msgstr "" +"\"ver\", \"hor\" かつ/または \"jump\"; 'scrollbind' のオプションの\n" +"リスト" + +msgid "this window's cursor moves together with other bound windows" +msgstr "このウィンドウのカーソルは他の同調ウィンドウと一緒に動く" + +msgid "size of a terminal window" +msgstr "端末ウィンドウのサイズ" + +msgid "key that precedes Vim commands in a terminal window" +msgstr "端末ウィンドウで Vim のコマンドの前に入力するキー" + +msgid "multiple tab pages" +msgstr "複数タブページ" + +msgid "0, 1 or 2; when to use a tab pages line" +msgstr "0, 1 または 2; タブページ行をいつ使うか" + +msgid "maximum number of tab pages to open for -p and \"tab all\"" +msgstr "-p と \"tab all\" で開かれるタブページの最大数" + +msgid "custom tab pages line" +msgstr "カスタムのタブページ行" + +msgid "custom tab page label for the GUI" +msgstr "カスタムの GUI のタブページラベル" + +msgid "custom tab page tooltip for the GUI" +msgstr "カスタムの GUI のタブページツールチップ" + +msgid "terminal" +msgstr "端末" + +msgid "minimal number of lines to scroll at a time" +msgstr "一度にスクロールする最小行数" + +msgid "specifies what the cursor looks like in different modes" +msgstr "それぞれのモード内でのカーソルの外観を指定" + +msgid "show info in the window title" +msgstr "ウィンドウタイトルに情報を表示" + +msgid "percentage of 'columns' used for the window title" +msgstr "ウィンドウタイトルに使われる 'columns' の割合 (パーセント単位)" + +msgid "when not empty, string to be used for the window title" +msgstr "空でないとき、ウィンドウタイトルに使われる文字列" + +msgid "string to restore the title to when exiting Vim" +msgstr "Vim の終了時にタイトルに復元する文字列" + +msgid "set the text of the icon for this window" +msgstr "このウィンドウのアイコンのテキストを設定" + +msgid "when not empty, text for the icon of this window" +msgstr "空でないとき、このウィンドウのアイコンに使われるテキスト" + +msgid "using the mouse" +msgstr "マウスの使用" + +msgid "list of flags for using the mouse" +msgstr "マウスを使うためのフラグのリスト" + +msgid "the window with the mouse pointer becomes the current one" +msgstr "マウスポインタのあるウィンドウがアクティブになる" + +msgid "hide the mouse pointer while typing" +msgstr "文字の入力中にマウスポインタを隠す" + +msgid "" +"\"extend\", \"popup\" or \"popup_setpos\"; what the right\n" +"mouse button is used for" +msgstr "" +"\"extend\", \"popup\" あるいは \"popup_setpos\"; マウスの右ボタンを\n" +"何に使うか" + +msgid "maximum time in msec to recognize a double-click" +msgstr "ダブルクリックとして認識する最大時間 (ミリ秒)" + +msgid "what the mouse pointer looks like in different modes" +msgstr "それぞれのモード内でのマウスポインタの外観を指定" + +msgid "GUI" +msgstr "GUI" + +msgid "list of font names to be used in the GUI" +msgstr "GUI で使われるフォント名のリスト" + +msgid "pair of fonts to be used, for multibyte editing" +msgstr "マルチバイトの編集で使われるフォントのペア" + +msgid "list of font names to be used for double-wide characters" +msgstr "全角文字に使われるフォント名のリスト" + +msgid "list of flags that specify how the GUI works" +msgstr "GUI がどう動くかを指定するフラグのリスト" + +msgid "\"icons\", \"text\" and/or \"tooltips\"; how to show the toolbar" +msgstr "" +"\"icons\", \"text\" かつ/あるいは \"tooltips\"; どのように\n" +"ツールバーを表示するか" + +msgid "size of toolbar icons" +msgstr "ツールバーアイコンのサイズ" + +msgid "" +"\"last\", \"buffer\" or \"current\": which directory used for the file " +"browser" +msgstr "" +"\"last\", \"buffer\" あるいは \"current\": ファイルブラウザでどの\n" +"ディレクトリを使うか" + +msgid "language to be used for the menus" +msgstr "メニューで使われる言語" + +msgid "maximum number of items in one menu" +msgstr "1 個のメニューの最大項目数" + +msgid "\"no\", \"yes\" or \"menu\"; how to use the ALT key" +msgstr "\"no\", \"yes\" または \"menu\"; ALT キーをどう使うか" + +msgid "number of pixel lines to use between characters" +msgstr "行間の幅のピクセル数" + +msgid "delay in milliseconds before a balloon may pop up" +msgstr "バルーン表示が出るまでの時間 (ミリ秒)" + +msgid "use balloon evaluation in the GUI" +msgstr "GUI でバルーン評価を使う" + +msgid "use balloon evaluation in the terminal" +msgstr "端末でバルーン評価を使う" + +msgid "expression to show in balloon eval" +msgstr "バルーン評価に表示する式" + +msgid "messages and info" +msgstr "メッセージと情報" + +msgid "add 's' flag in 'shortmess' (don't show search message)" +msgstr "'s' フラグを 'shortmess' に追加 (検索メッセージを表示しない)" + +msgid "list of flags to make messages shorter" +msgstr "メッセージを短くするためのフラグのリスト" + +msgid "show (partial) command keys in location given by 'showcmdloc'" +msgstr "コマンド (の一部) を 'showcmdloc' で指定された場所に表示" + +msgid "location where to show the (partial) command keys for 'showcmd'" +msgstr "'showcmd' でコマンド (の一部) を表示する場所" + +msgid "display the current mode in the status line" +msgstr "現在のモードをステータス行に表示" + +msgid "show cursor position below each window" +msgstr "カーソル位置をそれぞれのウィンドウの下に表示" + +msgid "alternate format to be used for the ruler" +msgstr "ルーラーに使われる代替書式" + +msgid "threshold for reporting number of changed lines" +msgstr "変更された行の数の報告が出る閾値" + +msgid "the higher the more messages are given" +msgstr "値が大きいほど詳細なメッセージが表示される" + +msgid "file to write messages in" +msgstr "メッセージを書込むファイル" + +msgid "pause listings when the screen is full" +msgstr "画面が一杯になったとき一覧表示を一時停止" + +msgid "start a dialog when a command fails" +msgstr "コマンドが失敗したときにダイアログを開く" + +msgid "ring the bell for error messages" +msgstr "エラーメッセージでベルを鳴らす" + +msgid "use a visual bell instead of beeping" +msgstr "ビープ音の代わりにビジュアルベルを使う" + +msgid "do not ring the bell for these reasons" +msgstr "これらの理由にはベルを鳴らさない" + +msgid "list of preferred languages for finding help" +msgstr "ヘルプを見つける際の望ましい言語のリスト" + +msgid "selecting text" +msgstr "テキスト選択" + +msgid "\"old\", \"inclusive\" or \"exclusive\"; how selecting text behaves" +msgstr "" +"\"old\", \"inclusive\" または \"exclusive\"; テキスト選択がどう振舞うか" + +msgid "" +"\"mouse\", \"key\" and/or \"cmd\"; when to start Select mode\n" +"instead of Visual mode" +msgstr "" +"\"mouse\", \"key\" かつ/または \"cmd\"; いつビジュアルモードでは\n" +"なく選択モードを開始するか" + +msgid "" +"\"unnamed\" to use the * register like unnamed register\n" +"\"autoselect\" to always put selected text on the clipboard" +msgstr "" +"\"unnamed\"; * レジスタを無名レジスタと同じように使う\n" +"\"autoselect\"; 常に選択されたテキストをクリップボードにコピー" + +msgid "\"startsel\" and/or \"stopsel\"; what special keys can do" +msgstr "\"startsel\" かつ/または \"stopsel\"; 特別なキーが何をするか" + +msgid "editing text" +msgstr "テキスト編集" + +msgid "maximum number of changes that can be undone" +msgstr "アンドゥ可能な変更の最大値" + +msgid "automatically save and restore undo history" +msgstr "アンドゥ履歴を自動で保存・復元" + +msgid "list of directories for undo files" +msgstr "アンドゥファイル用のディレクトリのリスト" + +msgid "maximum number lines to save for undo on a buffer reload" +msgstr "バッファのリロード時にアンドゥのために保存する最大行数" + +msgid "changes have been made and not written to a file" +msgstr "変更が行われたがファイルに書込まれていない" + +msgid "buffer is not to be written" +msgstr "バッファは書込まれない" + +msgid "changes to the text are possible" +msgstr "テキストの変更が可能" + +msgid "line length above which to break a line" +msgstr "これより長い行は改行される" + +msgid "margin from the right in which to break a line" +msgstr "改行する際の右からのマージン" + +msgid "specifies what <BS>, CTRL-W, etc. can do in Insert mode" +msgstr "挿入モードで <BS>, CTRL-W 等が何をできるかを指定" + +msgid "definition of what comment lines look like" +msgstr "コメント行がどうなっているかの定義" + +msgid "list of flags that tell how automatic formatting works" +msgstr "自動整形がどのように動作するかを決めるフラグのリスト" + +msgid "pattern to recognize a numbered list" +msgstr "数字付きの箇条書きを認識するパターン" + +msgid "expression used for \"gq\" to format lines" +msgstr "\"gq\" で行を整形するときに使われる式" + +msgid "specifies how Insert mode completion works for CTRL-N and CTRL-P" +msgstr "挿入モード補完が CTRL-N と CTRL-P でどう動作するかを指定" + +msgid "whether to use a popup menu for Insert mode completion" +msgstr "挿入モード補完でポップアップメニューを使うかどうか" + +msgid "maximum height of the popup menu" +msgstr "ポップアップメニューの最大高" + +msgid "minimum width of the popup menu" +msgstr "ポップアップメニューの最大幅" + +msgid "user defined function for Insert mode completion" +msgstr "挿入モード補完用のユーザー定義関数" + +msgid "function for filetype-specific Insert mode completion" +msgstr "ファイルタイプ固有の挿入モード補完用関数" + +msgid "list of dictionary files for keyword completion" +msgstr "キーワード補完用の辞書ファイルのリスト" + +msgid "list of thesaurus files for keyword completion" +msgstr "キーワード補完用の同義語ファイルのリスト" + +msgid "function used for thesaurus completion" +msgstr "同義語補完で使われる関数" + +msgid "adjust case of a keyword completion match" +msgstr "キーワード補完のマッチで大文字小文字を調整" + +msgid "enable entering digraphs with c1 <BS> c2" +msgstr "c1 <BS> c2 でダイグラフを入力可能にする" + +msgid "the \"~\" command behaves like an operator" +msgstr "\"~\" コマンドがオペレータのようにふるまう" + +msgid "function called for the \"g@\" operator" +msgstr "\"g@\" オペレータで呼ばれる関数" + +msgid "when inserting a bracket, briefly jump to its match" +msgstr "括弧を入力したときに、対応する括弧にわずかの間ジャンプ" + +msgid "tenth of a second to show a match for 'showmatch'" +msgstr "'showmatch' で対応を表示する時間 (0.1秒単位)" + +msgid "list of pairs that match for the \"%\" command" +msgstr "\"%\" コマンドでマッチするペアのリスト" + +msgid "use two spaces after '.' when joining a line" +msgstr "行を連結するときに '.' の後に空白を 2 個入れる" + +msgid "" +"\"alpha\", \"octal\", \"hex\", \"bin\" and/or \"unsigned\"; number formats\n" +"recognized for CTRL-A and CTRL-X commands" +msgstr "" +"\"alpha\", \"octal\", \"hex\", \"bin\" かつ/または \"unsigned\";\n" +"CTRL-A と CTRL-X コマンドで認識する数字の書式" + +msgid "tabs and indenting" +msgstr "タブとインデント" -msgid "E931: Buffer cannot be registered" -msgstr "E931: バッファを登録できません" +msgid "number of spaces a <Tab> in the text stands for" +msgstr "1 つの <Tab> に対応する空白の数" + +msgid "number of spaces used for each step of (auto)indent" +msgstr "(自動)インデントの各段階に使われる空白の数" + +msgid "list of number of spaces a tab counts for" +msgstr "1 つのタブが相当する空白の数のリスト" + +msgid "list of number of spaces a soft tabsstop counts for" +msgstr "1 つのソフトタブストップに相当する空白の数のリスト" + +msgid "a <Tab> in an indent inserts 'shiftwidth' spaces" +msgstr "インデント内での <Tab> は 'shiftwidth' 個の空白を挿入" + +msgid "if non-zero, number of spaces to insert for a <Tab>" +msgstr "0 でないとき、1 つの <Tab> で挿入される空白の数" + +msgid "round to 'shiftwidth' for \"<<\" and \">>\"" +msgstr "\"<<\" と \">>\" で 'shiftwidth' に丸める" + +msgid "expand <Tab> to spaces in Insert mode" +msgstr "挿入モードで <Tab> を空白に展開" + +msgid "automatically set the indent of a new line" +msgstr "新しい行のインデントを自動的に設定" + +msgid "do clever autoindenting" +msgstr "賢い自動インデントを行う" + +msgid "enable specific indenting for C code" +msgstr "C コードに特有のインデントを有効にする" + +msgid "options for C-indenting" +msgstr "C インデント処理用のオプション" + +msgid "keys that trigger C-indenting in Insert mode" +msgstr "挿入モードで C インデント処理を引き起こすキー" + +msgid "list of words that cause more C-indent" +msgstr "さらなる C インデントを発生させる単語のリスト" + +msgid "list of scope declaration names used by cino-g" +msgstr "cino-g に用いられるスコープ宣言名のリスト" + +msgid "expression used to obtain the indent of a line" +msgstr "行のインデントを得るために使われる式" + +msgid "keys that trigger indenting with 'indentexpr' in Insert mode" +msgstr "挿入モードで 'indentexpr' によるインデントを引き起こすキー" + +msgid "copy whitespace for indenting from previous line" +msgstr "前の行からインデントの空白をコピー" + +msgid "preserve kind of whitespace when changing indent" +msgstr "インデントを変更するときに空白の種類を保持" + +msgid "enable lisp mode" +msgstr "lisp モードを有効化" + +msgid "words that change how lisp indenting works" +msgstr "lisp インデント処理の動作を変更する単語のリスト" + +msgid "options for Lisp indenting" +msgstr "list インデント処理用のオプション" + +msgid "folding" +msgstr "折畳み" + +msgid "unset to display all folds open" +msgstr "全ての折畳みを開いて表示するにはオフにする" + +msgid "folds with a level higher than this number will be closed" +msgstr "この数値よりもレベルの高い折畳みは閉じられる" + +msgid "value for 'foldlevel' when starting to edit a file" +msgstr "ファイルを編集開始する際の 'foldlevel' の値" + +msgid "width of the column used to indicate folds" +msgstr "折畳みを表示するのに使われる列幅" + +msgid "expression used to display the text of a closed fold" +msgstr "閉じられた折畳みのテキストを表示するのに使われる式" + +msgid "set to \"all\" to close a fold when the cursor leaves it" +msgstr "カーソルが折畳みを離れたときに閉じるには \"all\" に設定" + +msgid "specifies for which commands a fold will be opened" +msgstr "どのコマンドが折畳みを開くかを指定" + +msgid "minimum number of screen lines for a fold to be closed" +msgstr "折畳みが閉じられる画面上の最小行数" + +msgid "template for comments; used to put the marker in" +msgstr "コメント用のテンプレート; マーカーを中に置くために使われる" + +msgid "" +"folding type: \"manual\", \"indent\", \"expr\", \"marker\",\n" +"\"syntax\" or \"diff\"" +msgstr "" +"折畳みの種類: \"manual\", \"indent\", \"expr\", \"marker\",\n" +"\"syntax\" または \"diff\"" + +msgid "expression used when 'foldmethod' is \"expr\"" +msgstr "'foldmethod' が \"expr\" の際に使われる式" + +msgid "used to ignore lines when 'foldmethod' is \"indent\"" +msgstr "'foldmethod' が \"indent\" の際に行を無視するために使われる" + +msgid "markers used when 'foldmethod' is \"marker\"" +msgstr "'foldmethod' が \"marker\" の際に使われるマーカー" + +msgid "maximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\"" +msgstr "" +"'foldmethod' が \"indent\" または \"syntax\" の際の折畳みの\n" +"最大の深さ" + +msgid "diff mode" +msgstr "差分モード" + +msgid "use diff mode for the current window" +msgstr "現在のウィンドウで差分モードを使う" + +msgid "options for using diff mode" +msgstr "差分モードを使うためのオプション" + +msgid "expression used to obtain a diff file" +msgstr "差分ファイルを取得するために使われる式" + +msgid "expression used to patch a file" +msgstr "ファイルにパッチを当てるために使われる式" + +msgid "mapping" +msgstr "マッピング" + +msgid "maximum depth of mapping" +msgstr "マッピングの最大の深さ" + +msgid "allow timing out halfway into a mapping" +msgstr "マッピングの途中でのタイムアウトを許可" + +msgid "allow timing out halfway into a key code" +msgstr "キーコードの途中でのタイムアウトを許可" + +msgid "time in msec for 'timeout'" +msgstr "'timeout' の時間 (ミリ秒)" + +msgid "time in msec for 'ttimeout'" +msgstr "'ttimeout' の時間 (ミリ秒)" + +msgid "reading and writing files" +msgstr "ファイルの読み書き" + +msgid "enable using settings from modelines when reading a file" +msgstr "ファイル読込み時にモードラインからの設定の使用を有効にする" + +msgid "allow setting expression options from a modeline" +msgstr "モードラインから式であるオプションを設定することを許可する" + +msgid "number of lines to check for modelines" +msgstr "モードライン用にチェックする行数" + +msgid "binary file editing" +msgstr "バイナリファイルの編集" + +msgid "last line in the file has an end-of-line" +msgstr "ファイルの最終行に改行がある" + +msgid "last line in the file followed by CTRL-Z" +msgstr "ファイルの最終行が CTRL-Z で終わる" + +msgid "fixes missing end-of-line at end of text file" +msgstr "テキストファイルの末尾に改行がない場合に修正する" + +msgid "prepend a Byte Order Mark to the file" +msgstr "バイト順マーク (BOM) をファイル先頭につける" + +msgid "end-of-line format: \"dos\", \"unix\" or \"mac\"" +msgstr "改行の形式: \"dos\", \"unix\" または \"mac\"" + +msgid "list of file formats to look for when editing a file" +msgstr "ファイル編集時に調べる改行形式のリスト" + +msgid "writing files is allowed" +msgstr "ファイルの書込みが許可されている" + +msgid "write a backup file before overwriting a file" +msgstr "ファイルを上書きする前にバックアップに書込む" + +msgid "keep a backup after overwriting a file" +msgstr "ファイルの上書き後にバックアップを保持" + +msgid "patterns that specify for which files a backup is not made" +msgstr "どのファイルでバックアックが作られないかを指定するパターン" + +msgid "whether to make the backup as a copy or rename the existing file" +msgstr "バックアップを既存のファイルのコピーとするかリネームするか" + +msgid "list of directories to put backup files in" +msgstr "バックアップファイルを置くディレクトリのリスト" + +msgid "file name extension for the backup file" +msgstr "バックアップファイルの拡張子" + +msgid "automatically write a file when leaving a modified buffer" +msgstr "変更されたバッファを離れる際に自動的にファイルに書込む" + +msgid "as 'autowrite', but works with more commands" +msgstr "'autowrite' と同様だが、より多くのコマンドで動作する" + +msgid "always write without asking for confirmation" +msgstr "常に確認無しに書込む" + +msgid "automatically read a file when it was modified outside of Vim" +msgstr "Vim の外でファイルが変更された際に自動的に読込む" + +msgid "keep oldest version of a file; specifies file name extension" +msgstr "ファイルの最古のバージョンを保持; ファイルの拡張子を指定" + +msgid "forcibly sync the file to disk after writing it" +msgstr "ファイルを書込んだ後に強制的にディスクに同期させる" + +msgid "the swap file" +msgstr "スワップファイル" + +msgid "list of directories for the swap file" +msgstr "スワップファイル用のディレクトリのリスト" + +msgid "use a swap file for this buffer" +msgstr "このバッファでスワップファイルを使う" + +msgid "number of characters typed to cause a swap file update" +msgstr "何文字入力したらスワップファイルを更新するか" + +msgid "time in msec after which the swap file will be updated" +msgstr "スワップファイルを更新するまでの時間 (ミリ秒)" + +msgid "command line editing" +msgstr "コマンドライン編集" + +msgid "how many command lines are remembered" +msgstr "コマンドラインを何個まで記憶するか" + +msgid "key that triggers command-line expansion" +msgstr "コマンドライン展開を引き起こすキー" + +msgid "like 'wildchar' but can also be used in a mapping" +msgstr "'wildchar' と同様だがマッピング内でも使用できる" + +msgid "specifies how command line completion works" +msgstr "どのようにコマンドライン補完が動作するかを指定" + +msgid "empty or \"tagfile\" to list file name of matching tags" +msgstr "空、またはマッチしたファイル名を一覧表示するなら \"tagfile\"" + +msgid "list of file name extensions that have a lower priority" +msgstr "低い優先度を持つ拡張子のリスト" + +msgid "list of file name extensions added when searching for a file" +msgstr "ファイル検索時に追加される拡張子のリスト" + +msgid "list of patterns to ignore files for file name completion" +msgstr "ファイル名補完の際に無視するファイルパターンのリスト" + +msgid "ignore case when using file names" +msgstr "ファイル名を使う際に大文字小文字の違いを無視" + +msgid "ignore case when completing file names" +msgstr "ファイル名補完の際に大文字小文字の違いを無視" + +msgid "command-line completion shows a list of matches" +msgstr "コマンドライン補完はマッチの一覧を表示する" + +msgid "key used to open the command-line window" +msgstr "コマンドラインウィンドウを開くためのキー" + +msgid "height of the command-line window" +msgstr "コマンドラインウィンドウの高さ" + +msgid "executing external commands" +msgstr "外部コマンドの実行" + +msgid "name of the shell program used for external commands" +msgstr "外部コマンドに使われるシェルプログラムの名前" + +msgid "character(s) to enclose a shell command in" +msgstr "シェルコマンドを囲む引用符" + +msgid "like 'shellquote' but include the redirection" +msgstr "'shellquote' と同様だがリダイレクトを含む" + +msgid "characters to escape when 'shellxquote' is (" +msgstr "'shellxquote' が ( の時にエスケープされる文字" + +msgid "argument for 'shell' to execute a command" +msgstr "コマンドを実行する際の 'shell' の引数" + +msgid "used to redirect command output to a file" +msgstr "コマンドの出力をファイルにリダイレクトする際に使われる" + +msgid "use a temp file for shell commands instead of using a pipe" +msgstr "シェルコマンドにパイプを使わずに一時ファイルを使う" + +msgid "program used for \"=\" command" +msgstr "\"=\" コマンドに使われるプログラム" + +msgid "program used to format lines with \"gq\" command" +msgstr "\"gq\" コマンドで行を整形する際に使われるプログラム" + +msgid "program used for the \"K\" command" +msgstr "\"K\" コマンドに使われるプログラム" + +msgid "warn when using a shell command and a buffer has changes" +msgstr "バッファに変更がありシェルコマンドが実行された際に警告する" + +msgid "running make and jumping to errors (quickfix)" +msgstr "make の実行とエラーへのジャンプ (quickfix)" + +msgid "name of the file that contains error messages" +msgstr "エラーメッセージが入っているファイルの名前" + +msgid "list of formats for error messages" +msgstr "エラーメッセージの書式のリスト" + +msgid "program used for the \":make\" command" +msgstr "\":make\" コマンドに使われるプログラム" + +msgid "string used to put the output of \":make\" in the error file" +msgstr "\":make\" の出力をエラーファイルに書込むために使われる文字列" + +msgid "name of the errorfile for the 'makeprg' command" +msgstr "'makeprg' コマンド用のエラーファイルの名前" + +msgid "program used for the \":grep\" command" +msgstr "\":grep\" コマンドに使われるプログラム" + +msgid "list of formats for output of 'grepprg'" +msgstr "'grepprg' の出力用の書式のリスト" + +msgid "encoding of the \":make\" and \":grep\" output" +msgstr "\":make\" と \":grep\" の出力のエンコーディング" + +msgid "system specific" +msgstr "システム固有" + +msgid "use forward slashes in file names; for Unix-like shells" +msgstr "ファイル名でスラッシュを使う; Unix ライクなシェル用" + +msgid "specifies slash/backslash used for completion" +msgstr "補完に salsh/backslash のどちらを使うか指定" + +msgid "language specific" +msgstr "言語固有" + +msgid "specifies the characters in a file name" +msgstr "ファイル名に使われる文字を指定" + +msgid "specifies the characters in an identifier" +msgstr "識別子に使われる文字を指定" + +msgid "specifies the characters in a keyword" +msgstr "キーワードに使われる文字を指定" + +msgid "specifies printable characters" +msgstr "表示可能な文字を指定" + +msgid "specifies escape characters in a string" +msgstr "文字列内のエスケープ文字を指定" + +msgid "display the buffer right-to-left" +msgstr "バッファを右から左に表示" + +msgid "when to edit the command-line right-to-left" +msgstr "いつコマンドラインを右から左に編集するか" + +msgid "insert characters backwards" +msgstr "文字を逆方向に挿入" + +msgid "allow CTRL-_ in Insert and Command-line mode to toggle 'revins'" +msgstr "挿入・コマンドラインモードで CTRL-_ で 'revins' の切り替えを許可" + +msgid "the ASCII code for the first letter of the Hebrew alphabet" +msgstr "ヘブライ語アルファベットの最初の文字を表す ASCII コード" + +msgid "use Hebrew keyboard mapping" +msgstr "ヘブライキーボードのマッピングを使用" + +msgid "use phonetic Hebrew keyboard mapping" +msgstr "音声ヘブライキーボードのマッピングを使用" + +msgid "prepare for editing Arabic text" +msgstr "アラビア語のテキストを編集する準備" + +msgid "perform shaping of Arabic characters" +msgstr "アラビア文字の字形処理を行う" + +msgid "terminal will perform bidi handling" +msgstr "端末が双方向 (bidi) の処理を行う" + +msgid "name of a keyboard mapping" +msgstr "キーボードマッピングの名前" + +msgid "list of characters that are translated in Normal mode" +msgstr "ノーマルモードで変換される文字のリスト" + +msgid "apply 'langmap' to mapped characters" +msgstr "'langmap' をマップされた文字に適用" + +msgid "when set never use IM; overrules following IM options" +msgstr "オンのとき IM を全く使わない; 以下の IM 関連オプションに優先する" + +msgid "in Insert mode: 1: use :lmap; 2: use IM; 0: neither" +msgstr "挿入モード時: 1: :lmap を使用; 2: IM を使用; 0: どちらも不使用" + +msgid "entering a search pattern: 1: use :lmap; 2: use IM; 0: neither" +msgstr "" +"検索パターン入力時: 1: :lmap を使用; 2: IM を使用;\n" +"0: どちらも不使用" + +msgid "when set always use IM when starting to edit a command line" +msgstr "オンのときはコマンドライン編集開始時に常に IM を使用" + +msgid "function to obtain IME status" +msgstr "IME の状態を取得するための関数" + +msgid "function to enable/disable IME" +msgstr "IME を有効化/無効化するための関数" + +msgid "multi-byte characters" +msgstr "マルチバイト文字" + +msgid "character encoding used in Nvim: \"utf-8\"" +msgstr "Nvim で使われる文字エンコーディング: \"uft-8\"" + +msgid "character encoding for the current file" +msgstr "現在のファイルの文字エンコーディング" + +msgid "automatically detected character encodings" +msgstr "文字エンコーディングを自動検出" + +msgid "expression used for character encoding conversion" +msgstr "文字エンコーディング変換に使われる式" + +msgid "delete combining (composing) characters on their own" +msgstr "結合文字そのものを削除" + +msgid "maximum number of combining (composing) characters displayed" +msgstr "表示の際の結合文字の最大数" + +msgid "key that activates the X input method" +msgstr "X インプットメソッドを起動するためのキー" + +msgid "width of ambiguous width characters" +msgstr "あいまい幅文字の幅" + +msgid "emoji characters are full width" +msgstr "絵文字の幅は全角" + +msgid "various" +msgstr "その他" + +msgid "" +"when to use virtual editing: \"block\", \"insert\", \"all\"\n" +"and/or \"onemore\"" +msgstr "" +"いつ仮想編集を使うか: \"block\", \"insert\", \"all\"\n" +"かつ/または \"onemore\"" + +msgid "list of autocommand events which are to be ignored" +msgstr "自動コマンドイベントで無視するもののリスト" + +msgid "load plugin scripts when starting up" +msgstr "起動時にプラグインスクリプトを読込む" + +msgid "enable reading .vimrc/.exrc/.gvimrc in the current directory" +msgstr "" +"カレントディレクトリにある .vimrc/.exrc/.gvimrc の読込みを\n" +"有効化" + +msgid "safer working with script files in the current directory" +msgstr "カレントディレクトリのスクリプトファイルを安全に扱う" + +msgid "use the 'g' flag for \":substitute\"" +msgstr "\":substitute\" に 'g' フラグを使う" + +msgid "allow reading/writing devices" +msgstr "デバイスからの読み書きを許可する" + +msgid "maximum depth of function calls" +msgstr "関数呼出しの最大の深さ" + +msgid "list of words that specifies what to put in a session file" +msgstr "セッションファイルに何を保存するかを指定する単語のリスト" + +msgid "list of words that specifies what to save for :mkview" +msgstr ":mkview で何を保存するかを指定する単語のリスト" + +msgid "directory where to store files with :mkview" +msgstr ":mkview でファイルを保存するディレクトリ" + +msgid "list that specifies what to write in the ShaDa file" +msgstr "ShaDa ファイルに何を書くかを指定するリスト" + +msgid "what happens with a buffer when it's no longer in a window" +msgstr "バッファがウィンドウに表示されなくなった時の挙動" + +msgid "empty, \"nofile\", \"nowrite\", \"quickfix\", etc.: type of buffer" +msgstr "空, \"nofile\", \"nowrite\", \"quickfix\" など: バッファの種別" + +msgid "whether the buffer shows up in the buffer list" +msgstr "バッファをバッファ一覧に表示するかどうか" + +msgid "set to \"msg\" to see all error messages" +msgstr "全てのエラーメッセージを見るには \"msg\" に設定" + +msgid "whether to show the signcolumn" +msgstr "目印桁を表示するかどうか" + +msgid "name of the MzScheme dynamic library" +msgstr "MzScheme 動的ライブラリの名前" + +msgid "name of the MzScheme GC dynamic library" +msgstr "MzScheme GC 動的ライブラリの名前" + +msgid "whether to use Python 2 or 3" +msgstr "Python 2 と 3 のどちらを使うか" + +msgid "E249: Window layout changed unexpectedly" +msgstr "E249: 予期せずウィンドウの配置が変わりました" + +msgid "E1156: Cannot change the argument list recursively" +msgstr "E1156: 引数リストを再帰的に変更することはできません" + +msgid "E163: There is only one file to edit" +msgstr "E163: 編集するファイルは1つしかありません" + +msgid "E164: Cannot go before first file" +msgstr "E164: 最初のファイルより前には行けません" + +msgid "E165: Cannot go beyond last file" +msgstr "E165: 最後のファイルを越えて後には行けません" + +msgid "E610: No argument to delete" +msgstr "E610: 削除する引数がありません" + +msgid "E218: Autocommand nesting too deep" +msgstr "E218: 自動コマンドの入れ子が深過ぎます" + +msgid "--Deleted--" +msgstr "--削除済--" + +#, c-format +msgid "auto-removing autocommand: %s <buffer=%d>" +msgstr "自動コマンド: %s <バッファ=%d> が自動的に削除されます" + +#, c-format +msgid "E367: No such group: \"%s\"" +msgstr "E367: そのグループはありません: \"%s\"" + +msgid "E936: Cannot delete the current group" +msgstr "E936: 現在のグループは削除できません" + +msgid "W19: Deleting augroup that is still in use" +msgstr "W19: 使用中の augroup を消そうとしています" + +msgid "" +"\n" +"--- Autocommands ---" +msgstr "" +"\n" +"--- 自動コマンド ---" + +#, c-format +msgid "E680: <buffer=%d>: invalid buffer number " +msgstr "E680: <バッファ=%d>: 無効なバッファ番号です" + +msgid "E217: Can't execute autocommands for ALL events" +msgstr "E217: 全てのイベントに対しての自動コマンドは実行できません" + +#, c-format +msgid "No matching autocommands: %s" +msgstr "該当する自動コマンドは存在しません: %s" + +#, fuzzy, c-format +#~ msgid "%s Autocommands for \"%s\"" +#~ msgstr "%s Autocommands for \"%s\"" + +#, c-format +msgid "Executing %s" +msgstr "%s を実行しています" + +#, c-format +msgid "autocommand %s" +msgstr "自動コマンド %s" + +#, c-format +msgid "E215: Illegal character after *: %s" +msgstr "E215: * の後に不正な文字がありました: %s" + +#, c-format +msgid "E216: No such event: %s" +msgstr "E216: そのようなイベントはありません: %s" + +#, c-format +msgid "E216: No such group or event: %s" +msgstr "E216: そのようなグループもしくはイベントはありません: %s" #, c-format msgid "E937: Attempt to delete a buffer that is in use: %s" msgstr "E937: 使用中のバッファを削除しようと試みました: %s" +msgid "E82: Cannot allocate any buffer, exiting..." +msgstr "E82: バッファを1つも作成できないので、終了します..." + +msgid "E83: Cannot allocate buffer, using other one..." +msgstr "E83: バッファを作成できないので、他のを使用します..." + msgid "E515: No buffers were unloaded" msgstr "E515: 解放されたバッファはありません" @@ -70,26 +1338,20 @@ msgstr "E516: 削除されたバッファはありません" msgid "E517: No buffers were wiped out" msgstr "E517: 破棄されたバッファはありません" -msgid "1 buffer unloaded" -msgstr "1 個のバッファが解放されました" - #, c-format -msgid "%d buffers unloaded" -msgstr "%d 個のバッファが解放されました" - -msgid "1 buffer deleted" -msgstr "1 個のバッファが削除されました" +msgid "%d buffer unloaded" +msgid_plural "%d buffers unloaded" +msgstr[0] "%d 個のバッファが解放されました" #, c-format -msgid "%d buffers deleted" -msgstr "%d 個のバッファが削除されました" - -msgid "1 buffer wiped out" -msgstr "1 個のバッファが破棄されました" +msgid "%d buffer deleted" +msgid_plural "%d buffers deleted" +msgstr[0] "%d 個のバッファが削除されました" #, c-format -msgid "%d buffers wiped out" -msgstr "%d 個のバッファが破棄されました" +msgid "%d buffer wiped out" +msgid_plural "%d buffers wiped out" +msgstr[0] "%d 個のバッファが破棄されました" msgid "E90: Cannot unload last buffer" msgstr "E90: 最後のバッファは解放できません" @@ -107,8 +1369,13 @@ msgid "E88: Cannot go before first buffer" msgstr "E88: 最初のバッファより前へは移動できません" #, c-format -msgid "E89: No write since last change for buffer %ld (add ! to override)" -msgstr "E89: バッファ %ld の変更は保存されていません (! で変更を破棄)" +msgid "" +"E89: No write since last change for buffer %<PRId64> (add ! to override)" +msgstr "E89: バッファ %<PRId64> の変更は保存されていません (! で変更を破棄)" + +#, c-format +msgid "E89: %s will be killed (add ! to override)" +msgstr "E89: \"%s\" が存在します (上書するには ! を追加してください)" msgid "E948: Job still running (add ! to end the job)" msgstr "E948: ジョブはまだ実行中です (! を追加でジョブを終了)" @@ -126,8 +1393,8 @@ msgid "W14: Warning: List of file names overflow" msgstr "W14: 警告: ファイル名のリストが長過ぎます" #, c-format -msgid "E92: Buffer %ld not found" -msgstr "E92: バッファ %ld が見つかりません" +msgid "E92: Buffer %<PRId64> not found" +msgstr "E92: バッファ %<PRId64> が見つかりません" #, c-format msgid "E93: More than one match for %s" @@ -138,8 +1405,8 @@ msgid "E94: No matching buffer for %s" msgstr "E94: %s に該当するバッファはありませんでした" #, c-format -msgid "line %ld" -msgstr "行 %ld" +msgid "line %<PRId64>" +msgstr "行 %<PRId64>" msgid "E95: Buffer with this name already exists" msgstr "E95: この名前のバッファは既にあります" @@ -150,8 +1417,8 @@ msgstr " [変更あり]" msgid "[Not edited]" msgstr "[未編集]" -msgid "[New file]" -msgstr "[新ファイル]" +msgid "[New]" +msgstr "[新]" msgid "[Read errors]" msgstr "[読込エラー]" @@ -163,16 +1430,13 @@ msgid "[readonly]" msgstr "[読込専用]" #, c-format -msgid "1 line --%d%%--" -msgstr "1 行 --%d%%--" - -#, c-format -msgid "%ld lines --%d%%--" -msgstr "%ld 行 --%d%%--" +msgid "%<PRId64> line --%d%%--" +msgid_plural "%<PRId64> lines --%d%%--" +msgstr[0] "%<PRId64> 行 --%d%%--" #, c-format -msgid "line %ld of %ld --%d%%-- col " -msgstr "行 %ld (全体 %ld) --%d%%-- col " +msgid "line %<PRId64> of %<PRId64> --%d%%-- col " +msgstr "行 %<PRId64> (全体 %<PRId64>) --%d%%-- col " msgid "[No Name]" msgstr "[無名]" @@ -180,12 +1444,6 @@ msgstr "[無名]" msgid "help" msgstr "ヘルプ" -msgid "[Help]" -msgstr "[ヘルプ]" - -msgid "[Preview]" -msgstr "[プレビュー]" - msgid "All" msgstr "全て" @@ -195,141 +1453,246 @@ msgstr "末尾" msgid "Top" msgstr "先頭" -msgid "" -"\n" -"# Buffer list:\n" -msgstr "" -"\n" -"# バッファリスト:\n" +#, c-format +msgid "%d%%" +msgstr "%d%%" + +#, c-format +msgid " (%d of %d)" +msgstr " (%d of %d)" + +#, c-format +msgid " ((%d) of %d)" +msgstr " ((%d) of %d)" msgid "E382: Cannot write, 'buftype' option is set" msgstr "E382: 'buftype' オプションが設定されているので書込めません" +msgid "[Command Line]" +msgstr "[コマンドライン]" + msgid "[Prompt]" msgstr "[プロンプト]" msgid "[Scratch]" msgstr "[下書き]" -msgid "" -"\n" -"--- Signs ---" -msgstr "" -"\n" -"--- サイン ---" +msgid "[Location List]" +msgstr "[ロケーションリスト]" -#, c-format -msgid "Signs for %s:" -msgstr "%s のサイン:" +msgid "[Quickfix List]" +msgstr "[Quickfixリスト]" + +msgid "E206: Patchmode: can't touch empty original file" +msgstr "E206: Patchmode: 空の原本ファイルをtouchできません" + +msgid "E513: Write error, conversion failed (make 'fenc' empty to override)" +msgstr "E513: 書込みエラー、変換失敗 (上書するには 'fenc' を空にしてください)" + +#~ msgid "E513: Write error, conversion failed in line %" +#~ msgstr "" + +msgid "E514: Write error (file system full?)" +msgstr "E514: 書込みエラー (ファイルシステムが満杯?)" #, c-format -msgid " line=%ld id=%d name=%s" -msgstr " 行=%ld 識別子=%d 名前=%s" +msgid "E676: No matching autocommands for buftype=%s buffer" +msgstr "E676: buftype=%s バッファの該当する自動コマンドは存在しません" -msgid "E902: Cannot connect to port" -msgstr "E902: ポートに接続できません" +msgid "WARNING: The file has been changed since reading it!!!" +msgstr "警告: 読込んだ後にファイルに変更がありました!!!" -msgid "E901: gethostbyname() in channel_open()" -msgstr "E901: channel_open() 内の gethostbyname() が失敗しました" +msgid "Do you really want to write to it" +msgstr "本当に上書きしますか" -msgid "E898: socket() in channel_open()" -msgstr "E898: channel_open() 内の socket() が失敗しました" +msgid "E203: Autocommands deleted or unloaded buffer to be written" +msgstr "E203: 保存するバッファを自動コマンドが削除もしくは解放しました" -msgid "E903: received command with non-string argument" -msgstr "E903: 非文字列の引数のコマンドを受信しました" +msgid "E204: Autocommand changed number of lines in unexpected way" +msgstr "E204: 自動コマンドが予期せぬ方法で行数を変更しました" -msgid "E904: last argument for expr/call must be a number" -msgstr "E904: expr/call の最後の引数は数字でなければなりません" +msgid "is a directory" +msgstr "はディレクトリです" -msgid "E904: third argument for call must be a list" -msgstr "E904: call の3番目の引数はリスト型でなければなりません" +msgid "is not a file or writable device" +msgstr "はファイルでも書込み可能デバイスでもありません" + +msgid "is read-only (add ! to override)" +msgstr "は読込専用です (強制書込には ! を追加)" #, c-format -msgid "E905: received unknown command: %s" -msgstr "E905: 未知のコマンドを受信しました: %s" +msgid "E303: Unable to create directory \"%s\" for backup file: %s" +msgstr "" +"E303: バックアップファイル用のディレクトリ \"%s\" を作成できませんでした: %s" + +msgid "E509: Cannot create backup file (add ! to override)" +msgstr "E509: バックアップファイルを作れません (! を追加で強制書込)" + +msgid "E510: Can't make backup file (add ! to override)" +msgstr "E510: バックアップに失敗しました (! を追加で強制書込)" -msgid "E906: not an open channel" -msgstr "E906: 開いていないチャネルです" +msgid "E214: Can't find temp file for writing" +msgstr "E214: 保存用一時ファイルが見つかりません" + +msgid "E213: Cannot convert (add ! to write without conversion)" +msgstr "E213: 変換できません (! を追加で変換せずに保存)" + +msgid "E166: Can't open linked file for writing" +msgstr "E166: リンクされたファイルに書込めません" #, c-format -msgid "E630: %s(): write while not connected" -msgstr "E630: %s(): 非接続状態で書き込みました" +msgid "E212: Can't open file for writing: %s" +msgstr "E212: 書込み用にファイルを開けません: %s" #, c-format -msgid "E631: %s(): write failed" -msgstr "E631: %s(): 書き込みに失敗しました" +msgid "E512: Close failed: %s" +msgstr "E512: 閉じることに失敗: %s" + +msgid " CONVERSION ERROR" +msgstr " 変換エラー" #, c-format -msgid "E917: Cannot use a callback with %s()" -msgstr "E917: %s() にコールバックは使えません" +msgid " in line %<PRId64>;" +msgstr " 行 %<PRId64>;" + +msgid "[NOT converted]" +msgstr "[未変換]" + +msgid "[converted]" +msgstr "[変換済]" + +msgid "[Device]" +msgstr "[デバイス]" + +#, fuzzy +#~ msgid "[noeol]" +#~ msgstr "[noeol]" + +msgid " [a]" +msgstr " [a]" + +msgid " appended" +msgstr " 追加" + +msgid " [w]" +msgstr " [w]" + +msgid " written" +msgstr " 書込み" -msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" +msgid "E205: Patchmode: can't save original file" +msgstr "E205: patchmode: 原本ファイルを保存できません" + +msgid "E207: Can't delete backup file" +msgstr "E207: バックアップファイルを消せません" + +msgid "" +"\n" +"WARNING: Original file may be lost or damaged\n" msgstr "" -"E912: raw や nl モードのチャネルに ch_evalexpr()/ch_sendexpr() は使えません" +"\n" +"警告: 原本ファイルが失われたか変更されました\n" -msgid "E920: _io file requires _name to be set" -msgstr "E920: _io ファイルは _name の設定が必要です" +msgid "don't quit the editor until the file is successfully written!" +msgstr "ファイルの保存に成功するまでエディタを終了しないでください!" -msgid "E915: in_io buffer requires in_buf or in_name to be set" -msgstr "E915: in_io バッファは in_buf か in_name の設定が必要です" +msgid "W10: Warning: Changing a readonly file" +msgstr "W10: 警告: 読込専用ファイルを変更します" -#, c-format -msgid "E918: buffer must be loaded: %s" -msgstr "E918: バッファがロードされてなければなりません: %s" +msgid "can only be opened in headless mode" +msgstr "ヘッドレスモードでのみ開くことができます" + +msgid "channel was already open" +msgstr "チャネルは既に開かれています" -msgid "E821: File is encrypted with unknown method" -msgstr "E821: ファイルが未知の方法で暗号化されています" +msgid "Can't send data to closed stream" +msgstr "閉じているストリームにデータを送ることはできません" -msgid "Warning: Using a weak encryption method; see :help 'cm'" -msgstr "警告: 弱い暗号方法を使っています; :help 'cm' を参照してください" +msgid "Can't send raw data to rpc channel" +msgstr "rpcチャネルに生データをに送ることはできません" -msgid "Enter encryption key: " -msgstr "暗号化用のキーを入力してください: " +msgid "tagname" +msgstr "タグ名" -msgid "Enter same key again: " -msgstr "もう一度同じキーを入力してください: " +msgid " kind file\n" +msgstr " ファイル種類\n" -msgid "Keys don't match!" -msgstr "キーが一致しません" +msgid "'history' option is zero" +msgstr "オプション 'history' がゼロです" -msgid "[crypted]" -msgstr "[暗号化]" +msgid "E548: Digit expected" +msgstr "E548: 数値が必要です" + +msgid "E545: Missing colon" +msgstr "E545: コロンがありません" + +msgid "E546: Illegal mode" +msgstr "E546: 不正なモードです" + +msgid "E549: Illegal percentage" +msgstr "E549: 不正なパーセンテージです" + +msgid "Entering Debug mode. Type \"cont\" to continue." +msgstr "デバッグモードに入ります。続けるには \"cont\" と入力してください。" #, c-format -msgid "E720: Missing colon in Dictionary: %s" -msgstr "E720: 辞書型にコロンがありません: %s" +msgid "Oldval = \"%s\"" +msgstr "古い値 = \"%s\"" #, c-format -msgid "E721: Duplicate key in Dictionary: \"%s\"" -msgstr "E721: 辞書型に重複キーがあります: \"%s\"" +msgid "Newval = \"%s\"" +msgstr "新しい値 = \"%s\"" #, c-format -msgid "E722: Missing comma in Dictionary: %s" -msgstr "E722: 辞書型にカンマがありません: %s" +msgid "line %<PRId64>: %s" +msgstr "行 %<PRId64>: %s" #, c-format -msgid "E723: Missing end of Dictionary '}': %s" -msgstr "E723: 辞書型の最後に '}' がありません: %s" +msgid "cmd: %s" +msgstr "コマンド: %s" -msgid "extend() argument" -msgstr "extend() の引数" +msgid "frame is zero" +msgstr "フレームが 0 です" #, c-format -msgid "E737: Key already exists: %s" -msgstr "E737: キーは既に存在します: %s" +msgid "frame at highest level: %d" +msgstr "最高レベルのフレーム: %d" + +#, c-format +msgid "Breakpoint in \"%s%s\" line %<PRId64>" +msgstr "ブレークポイント \"%s%s\" 行 %<PRId64>" + +#, c-format +msgid "E161: Breakpoint not found: %s" +msgstr "E161: ブレークポイントが見つかりません: %s" + +msgid "No breakpoints defined" +msgstr "ブレークポイントが定義されていません" + +#, c-format +msgid "%3d %s %s line %<PRId64>" +msgstr "%3d %s %s 行 %<PRId64>" + +#, c-format +msgid "%3d expr %s" +msgstr "%3d expr %s" #, c-format -msgid "E96: Cannot diff more than %ld buffers" -msgstr "E96: %ld 以上のバッファはdiffできません" +msgid "E96: Cannot diff more than %<PRId64> buffers" +msgstr "E96: %<PRId64> 以上のバッファはdiffできません" + +#, c-format +msgid "Not enough memory to use internal diff for buffer \"%s\"" +msgstr "バッファ \"%s\" 用に内部diffを使うためのメモリが不足しています" msgid "E810: Cannot read or write temp files" -msgstr "E810: 一時ファイルの読込もしくは書込ができません" +msgstr "E810: 一時ファイルの読込みもしくは書込みができません" msgid "E97: Cannot create diffs" msgstr "E97: 差分を作成できません" -msgid "Patch file" -msgstr "パッチファイル" +msgid "E960: Problem creating the internal diff" +msgstr "E960: 内部diff作成時に問題が発生しました" msgid "E816: Cannot read patch output" msgstr "E816: patchの出力を読込めません" @@ -359,173 +1722,236 @@ msgid "E103: Buffer \"%s\" is not in diff mode" msgstr "E103: バッファ \"%s\" は差分モードではありません" msgid "E787: Buffer changed unexpectedly" -msgstr "E787: 予期せずバッファが変更変更されました" +msgstr "E787: 予期せずバッファが変更されました" + +#, c-format +msgid "E1214: Digraph must be just two characters: %s" +msgstr "E1214: ダイグラフはちょうど2文字でなければなりません: %s" + +#, c-format +msgid "E1215: Digraph must be one character: %s" +msgstr "E1215: ダイグラフは1文字でなければなりません: %s" + +msgid "" +"E1216: digraph_setlist() argument must be a list of lists with two items" +msgstr "" +"E1216: digraph_setlist() の引数は2要素のリストのリストでなければなりません" msgid "E104: Escape not allowed in digraph" msgstr "E104: 合字にEscapeは使用できません" -msgid "E544: Keymap file not found" -msgstr "E544: キーマップファイルが見つかりません" +msgid "Custom" +msgstr "カスタム" -msgid "E105: Using :loadkeymap not in a sourced file" -msgstr "E105: :source で取込むファイル以外では :loadkeymap を使えません" +msgid "Latin supplement" +msgstr "ラテン補助" -msgid "E791: Empty keymap entry" -msgstr "E791: 空のキーマップエントリ" +msgid "Greek and Coptic" +msgstr "ギリシャとコプト" -msgid " Keyword completion (^N^P)" -msgstr " キーワード補完 (^N^P)" +msgid "Cyrillic" +msgstr "キリル" -msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" -msgstr " ^X モード (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" +msgid "Hebrew" +msgstr "ヘブライ" -msgid " Whole line completion (^L^N^P)" -msgstr " 行(全体)補完 (^L^N^P)" +msgid "Arabic" +msgstr "アラビア" -msgid " File name completion (^F^N^P)" -msgstr " ファイル名補完 (^F^N^P)" +msgid "Latin extended" +msgstr "ラテン拡張" -msgid " Tag completion (^]^N^P)" -msgstr " タグ補完 (^]^N^P)" +msgid "Greek extended" +msgstr "ギリシャ拡張" -msgid " Path pattern completion (^N^P)" -msgstr " パスパターン補完 (^N^P)" +msgid "Punctuation" +msgstr "句読点" -msgid " Definition completion (^D^N^P)" -msgstr " 定義補完 (^D^N^P)" +msgid "Super- and subscripts" +msgstr "上付き・下付き" -msgid " Dictionary completion (^K^N^P)" -msgstr " 辞書補完 (^K^N^P)" +msgid "Currency" +msgstr "通貨記号" -msgid " Thesaurus completion (^T^N^P)" -msgstr " シソーラス補完 (^T^N^P)" +msgid "Other" +msgstr "その他" -msgid " Command-line completion (^V^N^P)" -msgstr " コマンドライン補完 (^V^N^P)" +msgid "Roman numbers" +msgstr "ローマ数字" -msgid " User defined completion (^U^N^P)" -msgstr " ユーザー定義補完 (^U^N^P)" +msgid "Arrows" +msgstr "矢印" -msgid " Omni completion (^O^N^P)" -msgstr " オムニ補完 (^O^N^P)" +msgid "Mathematical operators" +msgstr "数学記号" -msgid " Spelling suggestion (s^N^P)" -msgstr " 綴り修正候補 (s^N^P)" +msgid "Technical" +msgstr "技術用記号" -msgid " Keyword Local completion (^N^P)" -msgstr " 局所キーワード補完 (^N^P)" +msgid "Box drawing" +msgstr "罫線素片" -msgid "Hit end of paragraph" -msgstr "段落の最後にヒット" +msgid "Block elements" +msgstr "ブロック要素" -msgid "E839: Completion function changed window" -msgstr "E839: 補間関数がウィンドウを変更しました" +msgid "Geometric shapes" +msgstr "幾何学模様" -msgid "E840: Completion function deleted text" -msgstr "E840: 補完関数がテキストを削除しました" +msgid "Symbols" +msgstr "記号" -msgid "'dictionary' option is empty" -msgstr "'dictionary' オプションが空です" +msgid "Dingbats" +msgstr "装飾記号" -msgid "'thesaurus' option is empty" -msgstr "'thesaurus' オプションが空です" +msgid "CJK symbols and punctuation" +msgstr "CJK記号及び句読点" -#, c-format -msgid "Scanning dictionary: %s" -msgstr "辞書をスキャン中: %s" +msgid "Hiragana" +msgstr "平仮名" -msgid " (insert) Scroll (^E/^Y)" -msgstr " (挿入) スクロール(^E/^Y)" +msgid "Katakana" +msgstr "片仮名" -msgid " (replace) Scroll (^E/^Y)" -msgstr " (置換) スクロール (^E/^Y)" +msgid "Bopomofo" +msgstr "注音字母" -#, c-format -msgid "Scanning: %s" -msgstr "スキャン中: %s" +msgid "E544: Keymap file not found" +msgstr "E544: キーマップファイルが見つかりません" -msgid "Scanning tags." -msgstr "タグをスキャン中." +msgid "E105: Using :loadkeymap not in a sourced file" +msgstr "E105: :source で取込むファイル以外では :loadkeymap を使えません" -msgid "match in file" -msgstr "ファイル内のマッチ" +msgid "E791: Empty keymap entry" +msgstr "E791: 空のキーマップエントリ" -msgid " Adding" -msgstr " 追加中" +msgid " TERMINAL" +msgstr "端末" -msgid "-- Searching..." -msgstr "-- 検索中..." +msgid " VREPLACE" +msgstr " 仮想置換" -msgid "Back at original" -msgstr "始めに戻る" +msgid " REPLACE" +msgstr " 置換" -msgid "Word from other line" -msgstr "他の行の単語" +msgid " REVERSE" +msgstr " 反転" -msgid "The only match" -msgstr "唯一の該当" +msgid " INSERT" +msgstr " 挿入" -#, c-format -msgid "match %d of %d" -msgstr "%d 番目の該当 (全該当 %d 個中)" +msgid " (terminal)" +msgstr "(端末)" -#, c-format -msgid "match %d" -msgstr "%d 番目の該当" +msgid " (insert)" +msgstr " (挿入)" -msgid "E18: Unexpected characters in :let" -msgstr "E18: 予期せぬ文字が :let にありました" +msgid " (replace)" +msgstr " (置換)" + +msgid " (vreplace)" +msgstr " (仮想置換)" + +msgid " Arabic" +msgstr " アラビア" + +msgid " (paste)" +msgstr " (貼り付け)" + +msgid " VISUAL" +msgstr " ビジュアル" + +msgid " VISUAL LINE" +msgstr " ビジュアル 行" + +msgid " VISUAL BLOCK" +msgstr " ビジュアル 矩形" + +msgid " SELECT" +msgstr " セレクト" + +msgid " SELECT LINE" +msgstr " 行指向選択" + +msgid " SELECT BLOCK" +msgstr " 矩形選択" + +msgid "recording" +msgstr "記録中" msgid "E111: Missing ']'" msgstr "E111: ']' が見つかりません" -msgid "E719: Cannot use [:] with a Dictionary" -msgstr "E719: [:] を辞書型と組み合わせては使えません" - -msgid "E806: using Float as a String" -msgstr "E806: 浮動小数点数を文字列として扱っています" +#, c-format +msgid "E697: Missing end of List ']': %s" +msgstr "E697: リスト型の最後に ']' がありません: %s" -msgid "E687: Less targets than List items" -msgstr "E687: ターゲットがリスト型内の要素よりも少ないです" +msgid "E719: Cannot slice a Dictionary" +msgstr "E719: 辞書型はスライスできません" -msgid "E688: More targets than List items" -msgstr "E688: ターゲットがリスト型内の要素よりも多いです" +msgid "E909: Cannot index a special variable" +msgstr "E909: 特殊変数はインデックスできません" -msgid "Double ; in list of variables" -msgstr "リスト型の値に2つ以上の ; が検出されました" +msgid "E274: No white space allowed before parenthesis" +msgstr "E274: 丸括弧の前にスペースは許されません" #, c-format -msgid "E738: Can't list variables for %s" -msgstr "E738: %s の値を一覧表示できません" +msgid "E80: Error while writing: %s" +msgstr "E80: 書込み中のエラー: %s" -msgid "E689: Can only index a List or Dictionary" -msgstr "E689: リスト型と辞書型以外はインデックス指定できません" +msgid "E695: Cannot index a Funcref" +msgstr "E695: 関数参照型はインデックスできません" -msgid "E708: [:] must come last" -msgstr "E708: [:] は最後でなければいけません" +msgid "E698: Variable nested too deep for making a copy" +msgstr "E698: コピーを取るには変数の入れ子が深過ぎます" -msgid "E709: [:] requires a List value" -msgstr "E709: [:] にはリスト型の値が必要です" +msgid "E1098: String, List or Blob required" +msgstr "E1098: 文字列型、リスト型またはBlob型が必要です" -msgid "E710: List value has more items than target" -msgstr "E710: リスト型変数にターゲットよりも多い要素があります" +#, c-format +msgid "E1169: Expression too recursive: %s" +msgstr "E1169: 式の再帰が深すぎます: %s" -msgid "E711: List value has not enough items" -msgstr "E711: リスト型変数に十分な数の要素がありません" +#, c-format +msgid "E1203: Dot can only be used on a dictionary: %s" +msgstr "E1203: ドットは辞書型でのみ使用できます: %s" -msgid "E690: Missing \"in\" after :for" -msgstr "E690: :for の後に \"in\" がありません" +msgid "E1192: Empty function name" +msgstr "E1192: 関数名が空です" #, c-format -msgid "E108: No such variable: \"%s\"" -msgstr "E108: その変数はありません: \"%s\"" +msgid "E1250: Argument of %s must be a List, String, Dictionary or Blob" +msgstr "E1250: %s の引数はリスト、文字列、辞書またはBlobでなければなりません" + +msgid "" +"E5700: Expression from 'spellsuggest' must yield lists with exactly two " +"values" +msgstr "" +"E5700: 'spellsuggest'からの式は正確に2つの値を持つリストを返す必要があります" #, c-format -msgid "E940: Cannot lock or unlock variable %s" -msgstr "E940: 変数 %s はロックまたはアンロックできません" +msgid "E121: Undefined variable: %.*s" +msgstr "E121: 未定義の変数です: %.*s" -msgid "E743: variable nested too deep for (un)lock" -msgstr "E743: (アン)ロックするには変数の入れ子が深過ぎます" +msgid "E689: Can only index a List, Dictionary or Blob" +msgstr "E689: リスト型、辞書型またはBlob型のみインデックスできます" + +msgid "E708: [:] must come last" +msgstr "E708: [:] は最後でなければいけません" + +msgid "E713: Cannot use empty key after ." +msgstr "E713: . の後に空のキーを使うことはできません" + +msgid "E709: [:] requires a List or Blob value" +msgstr "E709: [:] にはリスト型かBlob型の値が必要です" + +msgid "E996: Cannot lock a range" +msgstr "E996: 範囲はロックできません" + +msgid "E996: Cannot lock a list or dict" +msgstr "E996: リストあるいは辞書はロックできません" + +msgid "E690: Missing \"in\" after :for" +msgstr "E690: :for の後に \"in\" がありません" msgid "E109: Missing ':' after '?'" msgstr "E109: '?' の後に ':' がありません" @@ -539,12 +1965,6 @@ msgstr "E110: ')' が見つかりません" msgid "E260: Missing name after ->" msgstr "E260: -> の後に名前がありません" -msgid "E695: Cannot index a Funcref" -msgstr "E695: 関数参照型はインデックスできません" - -msgid "E909: Cannot index a special variable" -msgstr "E909: 特殊変数はインデックスできません" - #, c-format msgid "E112: Option name missing: %s" msgstr "E112: オプション名がありません: %s" @@ -553,169 +1973,448 @@ msgstr "E112: オプション名がありません: %s" msgid "E113: Unknown option: %s" msgstr "E113: 未知のオプションです: %s" +msgid "E973: Blob literal should have an even number of hex characters" +msgstr "E973: Blobリテラルは偶数個の16進数文字でなければなりません" + #, c-format msgid "E114: Missing quote: %s" -msgstr "E114: 引用符 (\") がありません: %s" +msgstr "E114: クォートがありません: %s" #, c-format msgid "E115: Missing quote: %s" -msgstr "E115: 引用符 (') がありません: %s" +msgstr "E115: クォートがありません: %s" + +#, c-format +msgid "E696: Missing comma in List: %s" +msgstr "E696: リスト型にコンマがありません: %s" msgid "Not enough memory to set references, garbage collection aborted!" msgstr "" "ガーベッジコレクションを中止しました! 参照を作成するのにメモリが不足しました" -msgid "E724: variable nested too deep for displaying" -msgstr "E724: 表示するには変数の入れ子が深過ぎます" +#, c-format +msgid "E720: Missing colon in Dictionary: %s" +msgstr "E720: 辞書型にコロンがありません: %s" -msgid "E805: Using a Float as a Number" -msgstr "E805: 浮動小数点数を数値として扱っています" +#, c-format +msgid "E721: Duplicate key in Dictionary: \"%s\"" +msgstr "E721: 辞書型に重複キーがあります: \"%s\"" -msgid "E703: Using a Funcref as a Number" -msgstr "E703: 関数参照型を数値として扱っています" +#, c-format +msgid "E722: Missing comma in Dictionary: %s" +msgstr "E722: 辞書型にコンマがありません: %s" -msgid "E745: Using a List as a Number" -msgstr "E745: リスト型を数値として扱っています" +#, c-format +msgid "E723: Missing end of Dictionary '}': %s" +msgstr "E723: 辞書型の最後に '}' がありません: %s" -msgid "E728: Using a Dictionary as a Number" -msgstr "E728: 辞書型を数値として扱っています" +msgid "map() argument" +msgstr "map() の引数" -msgid "E910: Using a Job as a Number" -msgstr "E910: ジョブを数値として扱っています" +msgid "mapnew() argument" +msgstr "mapnew() の引数" -msgid "E913: Using a Channel as a Number" -msgstr "E913: チャネルを数値として扱っています" +msgid "filter() argument" +msgstr "filter() の引数" -msgid "E891: Using a Funcref as a Float" -msgstr "E891: 関数参照型を浮動小数点数として扱っています" +msgid "foreach() argument" +msgstr "foreach() の引数" -msgid "E892: Using a String as a Float" -msgstr "E892: 文字列を浮動小数点数として扱っています" +#, c-format +msgid "E700: Unknown function: %s" +msgstr "E700: 未知の関数です: %s" -msgid "E893: Using a List as a Float" -msgstr "E893: リスト型を浮動小数点数として扱っています" +msgid "E923: Second argument of function() must be a list or a dict" +msgstr "E923: function() の第 2 引数はリスト型または辞書型でなければなりません" -msgid "E894: Using a Dictionary as a Float" -msgstr "E894: 辞書型を浮動小数点数として扱っています" +msgid "E5050: {opts} must be the only argument" +msgstr "E5050: {opts} は唯一の引数でなければなりません" -msgid "E907: Using a special value as a Float" -msgstr "E907: 特殊値を浮動小数点数として扱っています" +#, c-format +msgid "Executing command: \"%s\"" +msgstr "コマンドを実行中: %s" -msgid "E911: Using a Job as a Float" -msgstr "E911: ジョブを浮動小数点数として扱っています" +msgid "E921: Invalid callback argument" +msgstr "E921: 無効なコールバック引数です" -msgid "E914: Using a Channel as a Float" -msgstr "E914: チャネルを浮動小数点数として扱っています" +msgid "" +"\n" +"\tLast set from " +msgstr "" +"\n" +"\t最後にセットしたスクリプト: " -msgid "E729: using Funcref as a String" -msgstr "E729: 関数参照型を文字列として扱っています" +msgid "E977: Can only compare Blob with Blob" +msgstr "E977: Blob型はBlob型としか比較できません" -msgid "E730: using List as a String" -msgstr "E730: リスト型を文字列として扱っています" +msgid "E691: Can only compare List with List" +msgstr "E691: リスト型はリスト型としか比較できません" -msgid "E731: using Dictionary as a String" -msgstr "E731: 辞書型を文字列として扱っています" +msgid "E692: Invalid operation for List" +msgstr "E692: リスト型には無効な操作です" -msgid "E908: using an invalid value as a String" -msgstr "E908: 無効な値を文字列として扱っています" +msgid "E735: Can only compare Dictionary with Dictionary" +msgstr "E735: 辞書型は辞書型としか比較できません" + +msgid "E736: Invalid operation for Dictionary" +msgstr "E736: 辞書型には無効な操作です" + +msgid "E694: Invalid operation for Funcrefs" +msgstr "E694: 関数参照型には無効な操作です" #, c-format -msgid "E795: Cannot delete variable %s" -msgstr "E795: 変数 %s を削除できません" +msgid "E474: Expected comma before list item: %s" +msgstr "E474: リストの要素の前にコンマが必要です: %s" #, c-format -msgid "E704: Funcref variable name must start with a capital: %s" -msgstr "E704: 関数参照型変数名は大文字で始まらなければなりません: %s" +msgid "E474: Expected colon before dictionary value: %s" +msgstr "E474: 辞書型の値の前にコロンが必要です: %s" #, c-format -msgid "E705: Variable name conflicts with existing function: %s" -msgstr "E705: 変数名が既存の関数名と衝突します: %s" +msgid "E474: Expected string key: %s" +msgstr "E474: 文字列のキーが必要です: %s" #, c-format -msgid "E741: Value is locked: %s" -msgstr "E741: 値がロックされています: %s" +msgid "E474: Expected comma before dictionary key: %s" +msgstr "E474: 辞書型のキー前ににコンマが必要です: %s" -msgid "Unknown" -msgstr "不明" +#, c-format +msgid "E474: Unfinished escape sequence: %.*s" +msgstr "E474: 不完全なエスケープシーケンス: %.*s" #, c-format -msgid "E742: Cannot change value of %s" -msgstr "E742: %s の値を変更できません" +msgid "E474: Unfinished unicode escape sequence: %.*s" +msgstr "E474: 不完全な Unicode エスケープシーケンス: %.*s" -msgid "E698: variable nested too deep for making a copy" -msgstr "E698: コピーを取るには変数の入れ子が深過ぎます" +#, c-format +msgid "E474: Expected four hex digits after \\u: %.*s" +msgstr "E474: \\uの後に4桁の16進数が必要です: %.*s" + +#, c-format +msgid "E474: Unknown escape sequence: %.*s" +msgstr "E474: 未知のエスケープシーケンス: %.*s" + +#, c-format +msgid "E474: ASCII control characters cannot be present inside string: %.*s" +msgstr "E474: ASCII 制御文字は文字列内に存在できません: %.*s" +#, c-format +msgid "E474: Only UTF-8 strings allowed: %.*s" +msgstr "E474: UTF-8 文字列のみが使用可能です: %.*s" + +#, c-format msgid "" -"\n" -"# global variables:\n" +"E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: " +"%.*s" msgstr "" -"\n" -"# グローバル変数:\n" +"E474: U+10FFFF までの UTF-8 コードポイントのみがエスケープなしで表示できま" +"す: %.*s" +#, c-format +msgid "E474: Expected string end: %.*s" +msgstr "E474: \" が必要です: %.*s" + +#, c-format +msgid "E474: Leading zeroes are not allowed: %.*s" +msgstr "E474: 先頭にゼロは使用できません: %.*s" + +#, c-format +msgid "E474: Missing number after minus sign: %.*s" +msgstr "E474: マイナス記号の後に数字がありません: %.*s" + +#, c-format +msgid "E474: Missing number after decimal dot: %.*s" +msgstr "E474: 小数点の後に数字がありません: %.*s" + +#, c-format +msgid "E474: Missing exponent: %.*s" +msgstr "E474: 指数がありません: %.*s" + +#, fuzzy, c-format msgid "" -"\n" -"\tLast set from " +"E685: internal error: while converting number \"%.*s\" to float string2float " +"consumed %zu bytes in place of %zu" msgstr "" -"\n" -"\t最後にセットしたスクリプト: " +"E685: 内部エラー: 数値\"%.*s\" をfloat型に変換中 string2float は %zu を %zu " +"バイトの代わりに消費しました" -msgid "E691: Can only compare List with List" -msgstr "E691: リスト型はリスト型としか比較できません" +#, fuzzy, c-format +msgid "" +"E685: internal error: while converting number \"%.*s\" to integer vim_str2nr " +"consumed %i bytes in place of %zu" +msgstr "" +"E685: 内部エラー: 数値\"%.*s\" をint型に変換中 vim_str2nr は %zu の代わりに " +"%i バイトを消費しました" -msgid "E692: Invalid operation for List" -msgstr "E692: リスト型には無効な操作です" +msgid "E474: Attempt to decode a blank string" +msgstr "E474: 空の文字列のデコードを試みました" -msgid "E735: Can only compare Dictionary with Dictionary" -msgstr "E735: 辞書型は辞書型としか比較できません" +#, c-format +msgid "E474: No container to close: %.*s" +msgstr "E474: 閉じるコンテナがありません: %.*s" -msgid "E736: Invalid operation for Dictionary" -msgstr "E736: 辞書型には無効な操作です" +#, c-format +msgid "E474: Closing list with curly bracket: %.*s" +msgstr "E474: リスト型を波括弧で閉じています: %.*s" -msgid "E694: Invalid operation for Funcrefs" -msgstr "E694: 関数参照型には無効な操作です" +#, c-format +msgid "E474: Closing dictionary with square bracket: %.*s" +msgstr "E474: 辞書型を角括弧で閉じています: %.*s" #, c-format -msgid "E686: Argument of %s must be a List" -msgstr "E686: %s の引数はリスト型でなければなりません" +msgid "E474: Trailing comma: %.*s" +msgstr "E474: 余分なコンマが後ろにあります: %.*s" -msgid "E808: Number or Float required" -msgstr "E808: 数値か浮動小数点数が必要です" +#, c-format +msgid "E474: Expected value after colon: %.*s" +msgstr "E474: コロンの後に値が必要です: %.*s" -msgid "E785: complete() can only be used in Insert mode" -msgstr "E785: complete() は挿入モードでしか利用できません" +#, c-format +msgid "E474: Expected value: %.*s" +msgstr "E474: 値が必要です: %.*s" -msgid "&Ok" -msgstr "&Ok" +#, c-format +msgid "E474: Comma not inside container: %.*s" +msgstr "E474: コンマがコンテナ内にありません: %.*s" #, c-format -msgid "E700: Unknown function: %s" -msgstr "E700: 未知の関数です: %s" +msgid "E474: Duplicate comma: %.*s" +msgstr "E474: コンマが重複しています: %.*s" -msgid "E922: expected a dict" -msgstr "E922: 辞書が期待されています" +#, c-format +msgid "E474: Comma after colon: %.*s" +msgstr "E474: コロンの後にコンマがあります: %.*s" -msgid "E923: Second argument of function() must be a list or a dict" -msgstr "E923: function() の第 2 引数はリスト型または辞書型でなければなりません" +#, c-format +msgid "E474: Using comma in place of colon: %.*s" +msgstr "E474: コロンではなくコンマを使用しています: %.*s" + +#, c-format +msgid "E474: Leading comma: %.*s" +msgstr "E474: 余分なコンマが前にあります: %.*s" + +#, c-format +msgid "E474: Colon not inside container: %.*s" +msgstr "E474: コロンがコンテナ内にありません: %.*s" + +#, c-format +msgid "E474: Using colon not in dictionary: %.*s" +msgstr "E474: 辞書にないコロンを使用しています: %.*s" + +#, c-format +msgid "E474: Unexpected colon: %.*s" +msgstr "E474: 予期しないコロンです: %.*s" + +#, c-format +msgid "E474: Colon after comma: %.*s" +msgstr "E474: コンマの後にコロンがあります: %.*s" + +#, c-format +msgid "E474: Duplicate colon: %.*s" +msgstr "E474: コロンが重複しています: %.*s" + +#, c-format +msgid "E474: Expected null: %.*s" +msgstr "E474: null が必要です: %.*s" + +#, c-format +msgid "E474: Expected true: %.*s" +msgstr "E474: true が必要です: %.*s" +#, c-format +msgid "E474: Expected false: %.*s" +msgstr "E474: false が必要です: %.*s" + +#, c-format +msgid "E474: Unidentified byte: %.*s" +msgstr "E474: 未定義のバイトです: %.*s" + +#, c-format +msgid "E474: Trailing characters: %.*s" +msgstr "E474: 余分な文字が後ろにあります: %.*s" + +#, c-format +msgid "E474: Unexpected end of input: %.*s" +msgstr "E474: 予期しない入力の終了: %.*s" + +#, c-format +msgid "key %s" +msgstr "キー %s" + +#, fuzzy, c-format +#~ msgid "key %s at index %i from special map" +#~ msgstr "カスタムマップからのインデックス %i のキー %s" + +#, c-format +msgid "index %i" +msgstr "インデックス %i" + +msgid "partial" +msgstr "部分的" + +#, c-format +msgid "argument %i" +msgstr "引数 %i" + +#, fuzzy +#~ msgid "partial self dictionary" +#~ msgstr "部分的なセルフ辞書" + +#, fuzzy +#~ msgid "itself" +#~ msgstr "自身" + +msgid "E724: unable to correctly dump variable with self-referencing container" +msgstr "E724: 自らを参照するコンテナの変数を正しくダンプできませんでした" + +msgid "E474: Unable to represent NaN value in JSON" +msgstr "E474: JSON は NaN に対応していません" + +msgid "E474: Unable to represent infinity in JSON" +msgstr "E474: JSON は無限大に対応していません" + +#, c-format msgid "" -"&OK\n" -"&Cancel" +"E474: String \"%.*s\" contains byte that does not start any UTF-8 character" msgstr "" -"決定(&O)\n" -"キャンセル(&C)" +"E474: 文字列 \"%.*s\" には UTF-8 文字で始まらないバイトが含まれています" + +#, c-format +msgid "" +"E474: UTF-8 string contains code point which belongs to a surrogate pair: " +"%.*s" +msgstr "" +"E474: UTF-8 文字列にサロゲートペアに属するコードポイントが含まれています: " +"%.*s" + +msgid "E474: Unable to convert EXT string to JSON" +msgstr "E474: EXT 文字列を JSON に変換できません" + +#, c-format +msgid "E474: Error while dumping %s, %s: attempt to dump function reference" +msgstr "" +"E474: %s のダンプ中にエラーが発生しました, %s: 関数参照をダンプしようとしまし" +"た" + +msgid "E474: Invalid key in special dictionary" +msgstr "E474: ユーザー辞書のキーが無効です" + +msgid "encode_tv2string() argument" +msgstr "encode_tv2string() の引数" + +msgid ":echo argument" +msgstr ":echo の引数" + +msgid "encode_tv2json() argument" +msgstr "encode_tv2json() の引数" + +#, c-format +msgid "E5004: Error while dumping %s, %s: attempt to dump function reference" +msgstr "" +"E5004: %s のダンプ中にエラーが発生しました, %s: 関数参照をダンプしようとしま" +"した" + +#, c-format +msgid "E5005: Unable to dump %s: container references itself in %s" +msgstr "E5005: %s をダンプできません: コンテナは %s で自身を参照しています" + +#, c-format +msgid "E684: List index out of range: %<PRId64>" +msgstr "E684: リストのインデックスが範囲外です: %<PRId64>" + +#, c-format +msgid "E899: Argument of %s must be a List or Blob" +msgstr "E899: %s の引数はリスト型またはBlob型でなければなりません" + +msgid "E957: Invalid window number" +msgstr "E957: 無効なウィンドウ番号です" + +#, c-format +msgid "E706: Argument of %s must be a List, String or Dictionary" +msgstr "E706: %s の引数はリスト型、文字列または辞書型でなければなりません" + +#, c-format +msgid "E935: Invalid submatch number: %d" +msgstr "E935: 無効なサブマッチ番号です: %d" + +#, c-format +msgid "E998: Reduce of an empty %s with no initial value" +msgstr "E998: reduce が初期値無しで空の %s で呼ばれました" + +msgid "E1132: Missing function argument" +msgstr "E1132: 引数に関数がありません" + +msgid "add() argument" +msgstr "add() の引数" + +#, c-format +msgid "E158: Invalid buffer name: %s" +msgstr "E158: 無効なバッファ名です: %s" + +#, c-format +msgid "Invalid channel stream \"%s\"" +msgstr "無効なチャネル ストリーム \"%s\"" + +msgid "&Ok" +msgstr "&Ok" + +msgid "Context stack is empty" +msgstr "コンテキストスタックは空です" + +msgid "dictwatcheradd() argument" +msgstr "dictwatcheradd() の引数" + +msgid "E900: maxdepth must be non-negative number" +msgstr "E900: maxdepth は非負数でなければなりません" + +msgid "flatten() argument" +msgstr "flatten() の引数" + +msgid "extend() argument" +msgstr "extend() の引数" + +msgid "extendnew() argument" +msgstr "extendnew() の引数" + +msgid "E5000: Cannot find tab number." +msgstr "E5000: タブ番号が見つかりません" + +#, fuzzy +#~ msgid "E5001: Higher scope cannot be -1 if lower scope is >= 0." +#~ msgstr "E5001: 低いスコープが 0 以上の場合高いスコープは -1 を指定できません" + +msgid "E5002: Cannot find window number." +msgstr "E5002: ウィンドウ番号が見つかりません" msgid "called inputrestore() more often than inputsave()" msgstr "inputrestore() が inputsave() よりも多く呼ばれました" +msgid "insert() argument" +msgstr "insert() の引数" + msgid "E786: Range not allowed" msgstr "E786: 範囲指定は許可されていません" +msgid "E474: Failed to convert list to string" +msgstr "E474: リスト型の文字列への変換に失敗しました" + +#, c-format +msgid "E474: Failed to parse %.*s" +msgstr "E474: 構文解析に失敗しました: %.*s" + msgid "E701: Invalid type for len()" msgstr "E701: len() には無効な型です" +#, fuzzy, c-format +#~ msgid "msgpackdump() argument, index %i" +#~ msgstr "msgpackdump() の引数, インデックス %i" + +msgid "E5070: Character number must not be less than zero" +msgstr "E5070: 文字番号は 0 未満でなければなりません" + #, c-format -msgid "E798: ID is reserved for \":match\": %ld" -msgstr "E798: ID は \":match\" のために予約されています: %ld" +msgid "E5071: Character number must not be greater than INT_MAX (%i)" +msgstr "E5071: 文字番号は INT_MAX (%i) より大きくてはなりません" msgid "E726: Stride is zero" msgstr "E726: ストライド(前進量)が 0 です" @@ -726,490 +2425,730 @@ msgstr "E727: 開始位置が終了位置を越えました" msgid "<empty>" msgstr "<空>" -msgid "E240: No connection to the X server" -msgstr "E240: X サーバーへの接続がありません" +msgid "remove() argument" +msgstr "remove() の引数" -#, c-format -msgid "E241: Unable to send to %s" -msgstr "E241: %s へ送ることができません" +msgid "E655: Too many symbolic links (cycle?)" +msgstr "E655: シンボリックリンクが多過ぎます (循環している可能性があります)" -msgid "E277: Unable to read a server reply" -msgstr "E277: サーバーの応答がありません" +msgid "reverse() argument" +msgstr "reverse() の引数" -msgid "E941: already started a server" -msgstr "E941: サーバーはすでに開始しています" +#, fuzzy, c-format +#~ msgid "E5010: List item %d of the second argument is not a string" +#~ msgstr "E5010: リスト項目 %d の二つ目の引数は文字列ではありません" -msgid "E942: +clientserver feature not available" -msgstr "E942: +clientserver 機能が無効になっています" +#, c-format +msgid "E962: Invalid action: '%s'" +msgstr "E962: 無効な操作です: '%s'" -# Added at 10-Mar-2004. -msgid "E655: Too many symbolic links (cycle?)" -msgstr "E655: シンボリックリンクが多過ぎます (循環している可能性があります)" +#, c-format +msgid "connection failed: %s" +msgstr "接続に失敗しました: %s" -msgid "E258: Unable to send to client" -msgstr "E258: クライアントへ送ることができません" +#, c-format +msgid "E6100: \"%s\" is not a valid stdpath" +msgstr "E6100: \"%s\" は有効な stdpath ではありません" msgid "(Invalid)" msgstr "(無効)" +msgid "Can only call this function in an unmodified buffer" +msgstr "この関数は未変更のバッファ内でのみ呼び出すことができます" + +msgid "writefile() first argument must be a List or a Blob" +msgstr "writefile() の第1引数はリスト型またはBlob型でなければなりません" + #, c-format -msgid "E935: invalid submatch number: %d" -msgstr "E935: 無効なサブマッチ番号: %d" +msgid "E5060: Unknown flag: %s" +msgstr "E5060: 未知のフラグ: %s" -msgid "E921: Invalid callback argument" -msgstr "E921: 無効なコールバック引数です" +msgid "E482: Can't open file with an empty name" +msgstr "E482: 空の名前のファイルは開けません" #, c-format -msgid "<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s" -msgstr "<%s>%s%s %d, 16進数 %02x, 8進数 %03o, ダイグラフ %s" +msgid "E482: Can't open file %s for writing: %s" +msgstr "E482: %s を書込み用として開けません: %s" #, c-format -msgid "<%s>%s%s %d, Hex %02x, Octal %03o" -msgstr "<%s>%s%s %d, 16進数 %02x, 8進数 %03o" +msgid "E80: Error when closing file %s: %s" +msgstr "E80: %s を閉じる時にエラーです: %s" + +msgid "E743: Variable nested too deep for (un)lock" +msgstr "E743: (アン)ロックするには変数の入れ子が深過ぎます" + +msgid "E908: Using an invalid value as a String" +msgstr "E908: 無効な値を文字列として扱っています" #, c-format -msgid "> %d, Hex %04x, Oct %o, Digr %s" -msgstr "> %d, 16進数 %04x, 8進数 %o, ダイグラフ %s" +msgid "E1174: String required for argument %d" +msgstr "E1174: 引数 %d には文字列が必要です" #, c-format -msgid "> %d, Hex %08x, Oct %o, Digr %s" -msgstr "> %d, 16進数 %08x, 8進数 %o, ダイグラフ %s" +msgid "E1175: Non-empty string required for argument %d" +msgstr "E1175: 引数 %d には空ではない文字列が必要です" #, c-format -msgid "> %d, Hex %04x, Octal %o" -msgstr "> %d, 16進数 %04x, 8進数 %o" +msgid "E1206: Dictionary required for argument %d" +msgstr "E1206: 引数 %d には辞書型が必要です" #, c-format -msgid "> %d, Hex %08x, Octal %o" -msgstr "> %d, 16進数 %08x, 8進数 %o" +msgid "E1210: Number required for argument %d" +msgstr "E1210: 引数 %d には数値が必要です" -msgid "E134: Move lines into themselves" -msgstr "E134: 行をそれ自身には移動できません" +#, c-format +msgid "E1211: List required for argument %d" +msgstr "E1211: 引数 %d にはリスト型が必要です" -msgid "1 line moved" -msgstr "1 行が移動されました" +#, c-format +msgid "E1212: Bool required for argument %d" +msgstr "E1212: 引数 %d にはBool型が必要です" #, c-format -msgid "%ld lines moved" -msgstr "%ld 行が移動されました" +msgid "E1219: Float or Number required for argument %d" +msgstr "E1219: 引数 %d には浮動小数点数または数値が必要です" #, c-format -msgid "%ld lines filtered" -msgstr "%ld 行がフィルタ処理されました" +msgid "E1220: String or Number required for argument %d" +msgstr "E1220: 引数 %d には文字列または数値が必要です" -msgid "E135: *Filter* Autocommands must not change current buffer" -msgstr "E135: *フィルタ* autocommandは現在のバッファを変更してはいけません" +#, c-format +msgid "E1222: String or List required for argument %d" +msgstr "E1222: 引数 %d には文字列またはリスト型が必要です" -msgid "[No write since last change]\n" -msgstr "[最後の変更が保存されていません]\n" +#, c-format +msgid "E1226: List or Blob required for argument %d" +msgstr "E1226: 引数 %d にはリスト型またはBlob型が必要です" -msgid "Save As" -msgstr "別名で保存" +#, c-format +msgid "E1238: Blob required for argument %d" +msgstr "E1238: 引数 %d にはBlob型が必要です" -msgid "Write partial file?" -msgstr "ファイルを部分的に保存しますか?" +#, c-format +msgid "E1239: Invalid value for blob: %d" +msgstr "E1239: blobとして無効な値です: %d" -msgid "E140: Use ! to write partial buffer" -msgstr "E140: バッファを部分的に保存するには ! を使ってください" +#, c-format +msgid "E1252: String, List or Blob required for argument %d" +msgstr "E1252: 引数 %d には文字列、リスト型またはBlob型が必要です" #, c-format -msgid "Overwrite existing file \"%s\"?" -msgstr "既存のファイル \"%s\" を上書きしますか?" +msgid "E1256: String or function required for argument %d" +msgstr "E1256: 引数 %d には文字列または関数が必要です" #, c-format -msgid "Swap file \"%s\" exists, overwrite anyway?" -msgstr "スワップファイル \"%s\" が存在します。上書きを強制しますか?" +msgid "E1297: Non-NULL Dictionary required for argument %d" +msgstr "E1297: 引数 %d には非NULLの辞書が必要です" + +msgid "E710: List value has more items than target" +msgstr "E710: リスト型変数にターゲットよりも多い要素があります" + +msgid "E711: List value has not enough items" +msgstr "E711: リスト型変数に十分な数の要素がありません" + +msgid "E702: Sort compare function failed" +msgstr "E702: ソートの比較関数が失敗しました" + +msgid "E882: Uniq compare function failed" +msgstr "E882: Uniq の比較関数が失敗しました" + +msgid "sort() argument" +msgstr "sort() の引数" + +msgid "uniq() argument" +msgstr "uniq() の引数" + +msgid "E6000: Argument is not a function or function name" +msgstr "E6000: 引数は関数または関数名ではありません" #, c-format -msgid "E768: Swap file exists: %s (:silent! overrides)" -msgstr "E768: スワップファイルが存在します: %s (:silent! を追加で上書)" +msgid "E737: Key already exists: %s" +msgstr "E737: キーは既に存在します: %s" + +msgid "E972: Blob value does not have the right number of bytes" +msgstr "E972: Blob値のバイト数が正しくありません" #, c-format -msgid "E141: No file name for buffer %ld" -msgstr "E141: バッファ %ld には名前がありません" +msgid "E741: Value is locked: %.*s" +msgstr "E741: 値がロックされています: %.*s" -msgid "E142: File not written: Writing is disabled by 'write' option" -msgstr "E142: ファイルは保存されませんでした: 'write' オプションにより無効です" +#, c-format +msgid "E742: Cannot change value of %.*s" +msgstr "E742: %.*s の値を変更できません" + +msgid "Unknown" +msgstr "不明" + +msgid "E805: Expected a Number or a String, Float found" +msgstr "E805: 数値か文字列が必要ですが、浮動小数点数が見つかりました" + +msgid "E703: Expected a Number or a String, Funcref found" +msgstr "E703: 数値か文字列が必要ですが、関数参照型が見つかりました" + +msgid "E745: Expected a Number or a String, List found" +msgstr "E745: 数値か文字列が必要ですが、リスト型が見つかりました" + +msgid "E728: Expected a Number or a String, Dictionary found" +msgstr "E728: 数値か文字列が必要ですが、辞書型が見つかりました" + +msgid "E974: Expected a Number or a String, Blob found" +msgstr "E974: 数値か文字列が必要ですが、Blob型が見つかりました" + +msgid "E5299: Expected a Number or a String, Boolean found" +msgstr "E5299: 数値か文字列が必要ですが、論理型が見つかりました" + +msgid "E5300: Expected a Number or a String" +msgstr "E5300: 数値か文字列が必要です" + +msgid "E745: Using a List as a Number" +msgstr "E745: リスト型を数値として扱っています" + +msgid "E728: Using a Dictionary as a Number" +msgstr "E728: 辞書型を数値として扱っています" + +msgid "E805: Using a Float as a Number" +msgstr "E805: 浮動小数点数を数値として扱っています" + +msgid "E974: Using a Blob as a Number" +msgstr "E974: Blob型を数値として扱っています" + +msgid "E685: using an invalid value as a Number" +msgstr "E685: 無効な値を数字として扱っています" + +msgid "E730: Using a List as a String" +msgstr "E730: リスト型を文字列として扱っています" + +msgid "E731: Using a Dictionary as a String" +msgstr "E731: 辞書型を文字列として扱っています" + +msgid "E976: Using a Blob as a String" +msgstr "E976: Blob型を文字列として扱っています" + +msgid "E891: Using a Funcref as a Float" +msgstr "E891: 関数参照型を浮動小数点数として扱っています" + +msgid "E892: Using a String as a Float" +msgstr "E892: 文字列を浮動小数点数として扱っています" + +msgid "E893: Using a List as a Float" +msgstr "E893: リスト型を浮動小数点数として扱っています" + +msgid "E894: Using a Dictionary as a Float" +msgstr "E894: 辞書型を浮動小数点数として扱っています" + +msgid "E362: Using a boolean value as a Float" +msgstr "E362: ブール値を浮動小数点数として扱っています" + +msgid "E907: Using a special value as a Float" +msgstr "E907: 特殊値を浮動小数点数として扱っています" + +msgid "E975: Using a Blob as a Float" +msgstr "E975: Blob型を浮動小数点数として扱っています" + +msgid "E808: Number or Float required" +msgstr "E808: 数値か浮動小数点数が必要です" #, c-format -msgid "" -"'readonly' option is set for \"%s\".\n" -"Do you wish to write anyway?" -msgstr "" -"\"%s\" には 'readonly' オプションが設定されています.\n" -"上書き強制をしますか?" +msgid "E117: Unknown function: %s" +msgstr "E117: 未知の関数です: %s" #, c-format -msgid "" -"File permissions of \"%s\" are read-only.\n" -"It may still be possible to write it.\n" -"Do you wish to try?" -msgstr "" -"ファイル \"%s\" のパーミッションが読込専用です.\n" -"それでも恐らく書き込むことは可能です.\n" -"継続しますか?" +msgid "E122: Function %s already exists, add ! to replace it" +msgstr "E122: 関数 %s は定義済です、再定義するには ! を追加してください" + +msgid "E717: Dictionary entry already exists" +msgstr "E717: 辞書型内にエントリが既に存在します" + +msgid "E718: Funcref required" +msgstr "E718: 関数参照型が要求されます" #, c-format -msgid "E505: \"%s\" is read-only (add ! to override)" -msgstr "E505: \"%s\" は読込専用です (強制書込には ! を追加)" +msgid "E130: Unknown function: %s" +msgstr "E130: 未知の関数です: %s" -msgid "Edit File" -msgstr "ファイルを編集" +msgid "E454: Function list was modified" +msgstr "E454: 関数リストが変更されました" + +msgid "E1058: Function nesting too deep" +msgstr "E1058: 関数の入れ子が深過ぎます" #, c-format -msgid "E143: Autocommands unexpectedly deleted new buffer %s" -msgstr "E143: autocommandが予期せず新しいバッファ %s を削除しました" +msgid "E1068: No white space allowed before '%s': %s" +msgstr "E1068: '%s' の前にスペースは許されません: %s" -msgid "E144: non-numeric argument to :z" -msgstr "E144: 数ではない引数が :z に渡されました" +#, c-format +msgid "E1145: Missing heredoc end marker: %s" +msgstr "E1145: heredocの終端マーカーがありません '%s'" -msgid "E146: Regular expressions can't be delimited by letters" -msgstr "E146: 正規表現は文字で区切ることができません" +msgid "E1300: Cannot use a partial with dictionary for :defer" +msgstr "E1300: :defer で辞書付き部分適用は使用できません" #, c-format -msgid "replace with %s (y/n/a/q/l/^E/^Y)?" -msgstr "%s に置換しますか? (y/n/a/q/l/^E/^Y)" +msgid "E125: Illegal argument: %s" +msgstr "E125: 不正な引数です: %s" -msgid "(Interrupted) " -msgstr "(割込まれました) " +#, c-format +msgid "E853: Duplicate argument name: %s" +msgstr "E853: 引数名が重複しています: %s" -msgid "1 match" -msgstr "1 箇所該当しました" +msgid "E989: Non-default argument follows default argument" +msgstr "E989: 非デフォルト引数がデフォルト引数の後にあります" -msgid "1 substitution" -msgstr "1 箇所置換しました" +#, c-format +msgid "E451: Expected }: %s" +msgstr "E451: } が必要です: %s" #, c-format -msgid "%ld matches" -msgstr "%ld 箇所該当しました" +msgid "E740: Too many arguments for function %s" +msgstr "E740: 関数の引数が多過ぎます: %s" #, c-format -msgid "%ld substitutions" -msgstr "%ld 箇所置換しました" +msgid "E116: Invalid arguments for function %s" +msgstr "E116: 関数の無効な引数です: %s" -msgid " on 1 line" -msgstr " (計 1 行内)" +msgid "E132: Function call depth is higher than 'maxfuncdepth'" +msgstr "E132: 関数呼出しの入れ子数が 'maxfuncdepth' を超えました" #, c-format -msgid " on %ld lines" -msgstr " (計 %ld 行内)" +msgid "calling %s" +msgstr "%s を実行中です" -msgid "E147: Cannot do :global recursive with a range" -msgstr "E147: :global を範囲付きで再帰的には使えません" +#, c-format +msgid "%s aborted" +msgstr "%s が中断されました" -msgid "E148: Regular expression missing from global" -msgstr "E148: globalコマンドに正規表現が指定されていません" +#, c-format +msgid "%s returning #%<PRId64>" +msgstr "%s が #%<PRId64> を返しました" #, c-format -msgid "Pattern found in every line: %s" -msgstr "パターンが全ての行で見つかりました: %s" +msgid "%s returning %s" +msgstr "%s が %s を返しました" #, c-format -msgid "Pattern not found: %s" -msgstr "パターンは見つかりませんでした: %s" +msgid "continuing in %s" +msgstr "%s の実行を継続中です" -msgid "E478: Don't panic!" -msgstr "E478: 慌てないでください" +msgid "E699: Too many arguments" +msgstr "E699: 引数が多過ぎます" #, c-format -msgid "E661: Sorry, no '%s' help for %s" -msgstr "E661: 残念ですが '%s' のヘルプが %s にはありません" +msgid "E276: Cannot use function as a method: %s" +msgstr "E276: 関数をメソッドとして使用できません: %s" #, c-format -msgid "E149: Sorry, no help for %s" -msgstr "E149: 残念ですが %s にはヘルプがありません" +msgid "E933: Function was deleted: %s" +msgstr "E933: 関数は削除されました: %s" #, c-format -msgid "Sorry, help file \"%s\" not found" -msgstr "残念ですがヘルプファイル \"%s\" が見つかりません" +msgid "E120: Using <SID> not in a script context: %s" +msgstr "E120: スクリプト以外で<SID>が使われました: %s" #, c-format -msgid "E151: No match: %s" -msgstr "E151: マッチはありません: %s" +msgid "E725: Calling dict function without Dictionary: %s" +msgstr "E725: 辞書用関数が呼ばれましたが辞書がありません: %s" + +msgid "E129: Function name required" +msgstr "E129: 関数名が要求されます" #, c-format -msgid "E152: Cannot open %s for writing" -msgstr "E152: 書込み用に %s を開けません" +msgid "E128: Function name must start with a capital or \"s:\": %s" +msgstr "E128: 関数名は大文字か \"s:\" で始まらなければなりません: %s" #, c-format -msgid "E153: Unable to open %s for reading" -msgstr "E153: 読込用に %s を開けません" +msgid "E884: Function name cannot contain a colon: %s" +msgstr "E884: 関数名にはコロンは含められません: %s" -# Added at 29-Apr-2004. #, c-format -msgid "E670: Mix of help file encodings within a language: %s" -msgstr "E670: 1つの言語のヘルプファイルに複数のエンコードが混在しています: %s" +msgid "E123: Undefined function: %s" +msgstr "E123: 未定義の関数です: %s" #, c-format -msgid "E154: Duplicate tag \"%s\" in file %s/%s" -msgstr "E154: タグ \"%s\" がファイル %s/%s に重複しています" +msgid "E124: Missing '(': %s" +msgstr "E124: '(' がありません: %s" + +msgid "E862: Cannot use g: here" +msgstr "E862: ここでは g: は使えません" #, c-format -msgid "E150: Not a directory: %s" -msgstr "E150: ディレクトリではありません: %s" +msgid "E932: Closure function should not be at top level: %s" +msgstr "E932: クロージャー関数はトップレベルに記述できません: %s" + +msgid "E126: Missing :endfunction" +msgstr "E126: :endfunction がありません" #, c-format -msgid "E160: Unknown sign command: %s" -msgstr "E160: 未知のsignコマンドです: %s" +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: :endfunction の後に文字があります: %s" -msgid "E156: Missing sign name" -msgstr "E156: sign名がありません" +#, c-format +msgid "E707: Function name conflicts with variable: %s" +msgstr "E707: 関数名が変数名と衝突します: %s" -msgid "E612: Too many signs defined" -msgstr "E612: signの定義が多数見つかりました" +#, c-format +msgid "E127: Cannot redefine function %s: It is in use" +msgstr "E127: 関数 %s を再定義できません: 使用中です" #, c-format -msgid "E239: Invalid sign text: %s" -msgstr "E239: 無効なsignのテキストです: %s" +msgid "E746: Function name does not match script file name: %s" +msgstr "E746: 関数名がスクリプトのファイル名と一致しません: %s" #, c-format -msgid "E155: Unknown sign: %s" -msgstr "E155: 未知のsignです: %s" +msgid "E131: Cannot delete function %s: It is in use" +msgstr "E131: 関数 %s を削除できません: 使用中です" -msgid "E159: Missing sign number" -msgstr "E159: signの番号がありません" +#, c-format +msgid "Cannot delete function %s: It is being used internally" +msgstr "関数 %s を削除できません: 使用中です" + +msgid "E133: :return not inside a function" +msgstr "E133: 関数外に :return がありました" + +msgid "E18: Unexpected characters in :let" +msgstr "E18: 予期せぬ文字が :let にありました" #, c-format -msgid "E158: Invalid buffer name: %s" -msgstr "E158: 無効なバッファ名です: %s" +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: 変数 %s はロックまたはアンロックできません" -msgid "E934: Cannot jump to a buffer that does not have a name" -msgstr "E934: 名前の無いバッファへはジャンプできません" +#, c-format +msgid "E963: Setting v:%s to value with wrong type" +msgstr "E963: v:%s を間違った型の値で設定しています" + +msgid "E991: Cannot use =<< here" +msgstr "E991: ここでは =<< は使えません" + +msgid "E221: Marker cannot start with lower case letter" +msgstr "E221: マーカーは英小文字で始まってはいけません" + +msgid "E172: Missing marker" +msgstr "E172: マーカーがありません" #, c-format -msgid "E157: Invalid sign ID: %ld" -msgstr "E157: 無効なsign識別子です: %ld" +msgid "E990: Missing end marker '%s'" +msgstr "E990: 終端マーカーがありません '%s'" + +msgid "E687: Less targets than List items" +msgstr "E687: ターゲットがリスト型内の要素よりも少ないです" + +msgid "E688: More targets than List items" +msgstr "E688: ターゲットがリスト型内の要素よりも多いです" + +msgid "E452: Double ; in list of variables" +msgstr "E452: リスト型の値に2つ以上の ; が検出されました" #, c-format -msgid "E885: Not possible to change sign %s" -msgstr "E885: 変更できない sign です: %s" +msgid "E738: Can't list variables for %s" +msgstr "E738: %s の値を一覧表示できません" -# Added at 27-Jan-2004. -msgid " (NOT FOUND)" -msgstr " (見つかりません)" +msgid "E996: Cannot lock an environment variable" +msgstr "E996: 環境変数はロックできません" -msgid " (not supported)" -msgstr " (非サポート)" +msgid "E996: Cannot lock an option" +msgstr "E996: オプションはロックできません" -msgid "[Deleted]" -msgstr "[削除済]" +msgid "E996: Cannot lock a register" +msgstr "E996: レジスタはロックできません" -msgid "No old files" -msgstr "古いファイルはありません" +#, c-format +msgid "E108: No such variable: \"%s\"" +msgstr "E108: その変数はありません: \"%s\"" -msgid "Entering Debug mode. Type \"cont\" to continue." -msgstr "デバッグモードに入ります. 続けるには \"cont\" と入力してください." +#, c-format +msgid "E794: Cannot set variable in the sandbox: \"%.*s\"" +msgstr "E794: サンドボックスでは変数 \"%.*s\" に値を設定できません" #, c-format -msgid "Oldval = \"%s\"" -msgstr "古い値 = \"%s\"" +msgid "E1122: Variable is locked: %*s" +msgstr "E1122: 変数がロックされています: %*s" #, c-format -msgid "Newval = \"%s\"" -msgstr "新しい値 = \"%s\"" +msgid "E795: Cannot delete variable %.*s" +msgstr "E795: 変数 %.*s を削除できません" #, c-format -msgid "line %ld: %s" -msgstr "行 %ld: %s" +msgid "E704: Funcref variable name must start with a capital: %s" +msgstr "E704: 関数参照型変数名は大文字で始まらなければなりません: %s" #, c-format -msgid "cmd: %s" -msgstr "コマンド: %s" +msgid "E705: Variable name conflicts with existing function: %s" +msgstr "E705: 変数名が既存の関数名と衝突します: %s" -msgid "frame is zero" -msgstr "フレームが 0 です" +#, c-format +msgid "E521: Number required: &%s = '%s'" +msgstr "E521: 数字が必要です: &%s = '%s'" + +msgid "E1308: Cannot resize a window in another tab page" +msgstr "E1308: 別のタブページのウィンドウをリサイズできません" + +msgid "tcp address must be host:port" +msgstr "TCP アドレスは ホスト名:ポート番号 でなければなりません" + +msgid "failed to lookup host or port" +msgstr "ホストまたはポートの検索に失敗しました" + +msgid "connection refused" +msgstr "接続が拒否されました" + +msgid "E144: Non-numeric argument to :z" +msgstr "E144: 数ではない引数が :z に渡されました" #, c-format -msgid "frame at highest level: %d" -msgstr "最高レベルのフレーム: %d" +msgid "<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s" +msgstr "<%s>%s%s %d, 16進数 %02x, 8進数 %03o, ダイグラフ %s" #, c-format -msgid "Breakpoint in \"%s%s\" line %ld" -msgstr "ブレークポイント \"%s%s\" 行 %ld" +msgid "<%s>%s%s %d, Hex %02x, Octal %03o" +msgstr "<%s>%s%s %d, 16進数 %02x, 8進数 %03o" #, c-format -msgid "E161: Breakpoint not found: %s" -msgstr "E161: ブレークポイントが見つかりません: %s" +msgid "> %d, Hex %04x, Oct %o, Digr %s" +msgstr "> %d, 16進数 %04x, 8進数 %o, ダイグラフ %s" -msgid "No breakpoints defined" -msgstr "ブレークポイントが定義されていません" +#, c-format +msgid "> %d, Hex %08x, Oct %o, Digr %s" +msgstr "> %d, 16進数 %08x, 8進数 %o, ダイグラフ %s" #, c-format -msgid "%3d %s %s line %ld" -msgstr "%3d %s %s 行 %ld" +msgid "> %d, Hex %04x, Octal %o" +msgstr "> %d, 16進数 %04x, 8進数 %o" #, c-format -msgid "%3d expr %s" -msgstr "%3d expr %s" +msgid "> %d, Hex %08x, Octal %o" +msgstr "> %d, 16進数 %08x, 8進数 %o" -msgid "E750: First use \":profile start {fname}\"" -msgstr "E750: 初めに \":profile start {fname}\" を実行してください" +msgid "E134: Cannot move a range of lines into itself" +msgstr "E134: 行の範囲をそれ自身には移動できません" #, c-format -msgid "Save changes to \"%s\"?" -msgstr "変更を \"%s\" に保存しますか?" +msgid "%<PRId64> line moved" +msgid_plural "%<PRId64> lines moved" +msgstr[0] "%<PRId64> 行が移動されました" #, c-format -msgid "E947: Job still running in buffer \"%s\"" -msgstr "E947: ジョブはバッファ \"%s\" でまだ実行中です" +msgid "E482: Can't create file %s" +msgstr "E482: ファイル %s を作成できません" #, c-format -msgid "E162: No write since last change for buffer \"%s\"" -msgstr "E162: バッファ \"%s\" の変更は保存されていません" +msgid "%<PRId64> lines filtered" +msgstr "%<PRId64> 行がフィルタ処理されました" -msgid "Warning: Entered other buffer unexpectedly (check autocommands)" -msgstr "警告: 予期せず他バッファへ移動しました (autocommands を調べてください)" +msgid "E135: *Filter* Autocommands must not change current buffer" +msgstr "E135: *Filter* 自動コマンドは現在のバッファを変更してはいけません" -msgid "E163: There is only one file to edit" -msgstr "E163: 編集するファイルは1つしかありません" +msgid "[No write since last change]\n" +msgstr "[最後の変更が保存されていません]\n" -msgid "E164: Cannot go before first file" -msgstr "E164: 最初のファイルより前には行けません" +#, c-format +msgid "E503: \"%s\" is not a file or writable device" +msgstr "E503: \"%s\" はファイルでも書込み可能デバイスでもありません" -msgid "E165: Cannot go beyond last file" -msgstr "E165: 最後のファイルを越えて後には行けません" +msgid "Write partial file?" +msgstr "ファイルを部分的に保存しますか?" + +msgid "E140: Use ! to write partial buffer" +msgstr "E140: バッファを部分的に保存するには ! を使ってください" #, c-format -msgid "E666: compiler not supported: %s" -msgstr "E666: そのコンパイラには対応していません: %s" +msgid "Overwrite existing file \"%s\"?" +msgstr "既存のファイル \"%s\" を上書きしますか?" #, c-format -msgid "W20: Required python version 2.x not supported, ignoring file: %s" -msgstr "W20: 要求されたpython 2.xは対応していません、ファイルを無視します: %s" +msgid "Swap file \"%s\" exists, overwrite anyway?" +msgstr "スワップファイル \"%s\" が存在します。上書きを強制しますか?" #, c-format -msgid "W21: Required python version 3.x not supported, ignoring file: %s" -msgstr "W21: 要求されたpython 3.xは対応していません、ファイルを無視します: %s" +msgid "E768: Swap file exists: %s (:silent! overrides)" +msgstr "E768: スワップファイルが存在します: %s (:silent! を追加で上書)" #, c-format -msgid "Current %slanguage: \"%s\"" -msgstr "現在の %s言語: \"%s\"" +msgid "E141: No file name for buffer %<PRId64>" +msgstr "E141: バッファ %<PRId64> には名前がありません" + +msgid "E142: File not written: Writing is disabled by 'write' option" +msgstr "E142: ファイルは保存されませんでした: 'write' オプションにより無効です" #, c-format -msgid "E197: Cannot set language to \"%s\"" -msgstr "E197: 言語を \"%s\" に設定できません" +msgid "" +"'readonly' option is set for \"%s\".\n" +"Do you wish to write anyway?" +msgstr "" +"\"%s\" には 'readonly' オプションが設定されています.\n" +"上書き強制をしますか?" -msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." +#, c-format +msgid "" +"File permissions of \"%s\" are read-only.\n" +"It may still be possible to write it.\n" +"Do you wish to try?" msgstr "" -"Exモードに入ります. ノーマルモードに戻るには\"visual\"と入力してください." +"ファイル \"%s\" のパーミッションが読込専用です.\n" +"それでも恐らく書込むことは可能です.\n" +"継続しますか?" -msgid "E501: At end-of-file" -msgstr "E501: ファイルの終了位置" +#, c-format +msgid "E505: \"%s\" is read-only (add ! to override)" +msgstr "E505: \"%s\" は読込専用です (強制書込には ! を追加)" -msgid "E169: Command too recursive" -msgstr "E169: コマンドが再帰的過ぎます" +#, c-format +msgid "E143: Autocommands unexpectedly deleted new buffer %s" +msgstr "E143: 自動コマンドが予期せず新しいバッファ %s を削除しました" + +msgid "E146: Regular expressions can't be delimited by letters" +msgstr "E146: 正規表現は文字で区切ることができません" #, c-format -msgid "E605: Exception not caught: %s" -msgstr "E605: 例外が捕捉されませんでした: %s" +msgid "replace with %s (y/n/a/q/l/^E/^Y)?" +msgstr "%s に置換しますか? (y/n/a/q/l/^E/^Y)" -msgid "End of sourced file" -msgstr "取込ファイルの最後です" +msgid "(Interrupted) " +msgstr "(割込まれました) " -msgid "End of function" -msgstr "関数の最後です" +#, c-format +msgid "%<PRId64> match on %<PRId64> line" +msgid_plural "%<PRId64> matches on %<PRId64> line" +msgstr[0] "%<PRId64> 箇所該当しました (計 %<PRId64> 行内)" -msgid "E464: Ambiguous use of user-defined command" -msgstr "E464: ユーザー定義コマンドのあいまいな使用です" +#, c-format +msgid "%<PRId64> substitution on %<PRId64> line" +msgid_plural "%<PRId64> substitutions on %<PRId64> line" +msgstr[0] "%<PRId64> 箇所置換しました (計 %<PRId64> 行内)" -msgid "E492: Not an editor command" -msgstr "E492: エディタのコマンドではありません" +#, c-format +msgid "%<PRId64> match on %<PRId64> lines" +msgid_plural "%<PRId64> matches on %<PRId64> lines" +msgstr[0] "%<PRId64> 箇所該当しました (計 %<PRId64> 行内)" -msgid "E493: Backwards range given" -msgstr "E493: 逆さまの範囲が指定されました" +#, c-format +msgid "%<PRId64> substitution on %<PRId64> lines" +msgid_plural "%<PRId64> substitutions on %<PRId64> lines" +msgstr[0] "%<PRId64> 箇所置換しました (計 %<PRId64> 行内)" -msgid "Backwards range given, OK to swap" -msgstr "逆さまの範囲が指定されました、入替えますか?" +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: :global を範囲付きで再帰的には使えません" -msgid "E494: Use w or w>>" -msgstr "E494: w もしくは w>> を使用してください" +msgid "E148: Regular expression missing from global" +msgstr "E148: :global に正規表現が指定されていません" -msgid "E943: Command table needs to be updated, run 'make cmdidxs'" -msgstr "" -"E943: コマンドテーブルを更新する必要があります、'make cmdidxs' を実行してくだ" -"さい" +#, c-format +msgid "Pattern found in every line: %s" +msgstr "パターンが全ての行で見つかりました: %s" -msgid "E319: Sorry, the command is not available in this version" -msgstr "E319: このバージョンではこのコマンドは利用できません、ごめんなさい" +#, c-format +msgid "Pattern not found: %s" +msgstr "パターンは見つかりませんでした: %s" -msgid "1 more file to edit. Quit anyway?" -msgstr "編集すべきファイルが 1 個ありますが、終了しますか?" +msgid "No old files" +msgstr "古いファイルはありません" #, c-format -msgid "%d more files to edit. Quit anyway?" -msgstr "編集すべきファイルがあと %d 個ありますが、終了しますか?" +msgid "E666: Compiler not supported: %s" +msgstr "E666: そのコンパイラには対応していません: %s" -msgid "E173: 1 more file to edit" -msgstr "E173: 編集すべきファイルが 1 個あります" +#, c-format +msgid "Save changes to \"%s\"?" +msgstr "変更を \"%s\" に保存しますか?" #, c-format -msgid "E173: %ld more files to edit" -msgstr "E173: 編集すべきファイルがあと %ld 個あります" +msgid "Close \"%s\"?" +msgstr "\"%s\"を閉じますか?" -msgid "E174: Command already exists: add ! to replace it" -msgstr "E174: コマンドが既にあります: 再定義するには ! を追加してください" +#, c-format +msgid "E947: Job still running in buffer \"%s\"" +msgstr "E947: ジョブはバッファ \"%s\" でまだ実行中です" -msgid "" -"\n" -" Name Args Address Complete Definition" -msgstr "" -"\n" -" 名前 引数 アドレス 補完 定義" +#, c-format +msgid "E162: No write since last change for buffer \"%s\"" +msgstr "E162: バッファ \"%s\" の変更は保存されていません" -msgid "No user-defined commands found" -msgstr "ユーザー定義コマンドが見つかりませんでした" +msgid "Warning: Entered other buffer unexpectedly (check autocommands)" +msgstr "警告: 予期せず他バッファへ移動しました (自動コマンドを調べてください)" -msgid "E175: No attribute specified" -msgstr "E175: 属性は定義されていません" +msgid "E464: Ambiguous use of user-defined command" +msgstr "E464: ユーザー定義コマンドのあいまいな使用です" -msgid "E176: Invalid number of arguments" -msgstr "E176: 引数の数が無効です" +msgid "E489: No call stack to substitute for \"<stack>\"" +msgstr "E489: \"<stack>\" を置き換えるコールスタックがありません" -msgid "E177: Count cannot be specified twice" -msgstr "E177: カウントを2重指定することはできません" +msgid "E492: Not an editor command" +msgstr "E492: エディタのコマンドではありません" -msgid "E178: Invalid default value for count" -msgstr "E178: カウントの省略値が無効です" +msgid "E495: No autocommand file name to substitute for \"<afile>\"" +msgstr "E495: \"<afile>\" を置き換える自動コマンドのファイル名がありません" -msgid "E179: argument required for -complete" -msgstr "E179: -complete には引数が必要です" +msgid "E496: No autocommand buffer number to substitute for \"<abuf>\"" +msgstr "E496: \"<abuf>\" を置き換える自動コマンドバッファ番号がありません" -msgid "E179: argument required for -addr" -msgstr "E179: -addr には引数が必要です" +msgid "E497: No autocommand match name to substitute for \"<amatch>\"" +msgstr "E497: \"<amatch>\" を置き換える自動コマンドの該当名がありません" -#, c-format -msgid "E181: Invalid attribute: %s" -msgstr "E181: 無効な属性です: %s" +msgid "E498: No :source file name to substitute for \"<sfile>\"" +msgstr "E498: \"<sfile>\" を置き換える :source 対象ファイル名がありません" -msgid "E182: Invalid command name" -msgstr "E182: 無効なコマンド名です" +msgid "E842: No line number to use for \"<slnum>\"" +msgstr "E842: \"<slnum>\" を置き換える行番号がありません" -msgid "E183: User defined commands must start with an uppercase letter" -msgstr "E183: ユーザー定義コマンドは英大文字で始まらなければなりません" +msgid "E961: No line number to use for \"<sflnum>\"" +msgstr "E961: \"<sflnum>\" を置き換える行番号がありません" -msgid "E841: Reserved name, cannot be used for user defined command" -msgstr "E841: 予約名なので、ユーザー定義コマンドに利用できません" +msgid "E1274: No script file name to substitute for \"<script>\"" +msgstr "E1274: \"<script>\" を置き換えるスクリプトファイル名がありません" -#, c-format -msgid "E184: No such user-defined command: %s" -msgstr "E184: そのユーザー定義コマンドはありません: %s" +msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." +msgstr "" +"Exモードに入ります。ノーマルモードに戻るには \"visual\" と入力してください。" + +msgid "E501: At end-of-file" +msgstr "E501: ファイルの終了位置" #, c-format -msgid "E180: Invalid address type value: %s" -msgstr "E180: 無効なアドレスタイプ値です: %s" +msgid "Executing: %s" +msgstr "実行中: %s" + +#~ msgid "line %" +#~ msgstr "" + +msgid "End of sourced file" +msgstr "取込みファイルの最後です" + +msgid "End of function" +msgstr "関数の最後です" #, c-format -msgid "E180: Invalid complete value: %s" -msgstr "E180: 無効な補完指定です: %s" +msgid "E605: Exception not caught: %s" +msgstr "E605: 例外が捕捉されませんでした: %s" -msgid "E468: Completion argument only allowed for custom completion" -msgstr "E468: 補完引数はカスタム補完でしか使用できません" +msgid "" +"INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX" +msgstr "" +"内部エラー: EX_DFLALL を ADDR_NONE, ADDR_UNSIGNED または ADDR_QUICKFIX と使用" +"できません" -msgid "E467: Custom completion requires a function argument" -msgstr "E467: カスタム補完には引数として関数が必要です" +msgid "E493: Backwards range given" +msgstr "E493: 逆さまの範囲が指定されました" -msgid "unknown" -msgstr "不明" +msgid "Backwards range given, OK to swap" +msgstr "逆さまの範囲が指定されました、入替えますか?" + +msgid "E494: Use w or w>>" +msgstr "E494: w もしくは w>> を使用してください" + +msgid "E943: Command table needs to be updated, run 'make'" +msgstr "" +"E943: コマンドテーブルを更新する必要があります。 'make' を実行してください" + +msgid "E319: The command is not available in this version" +msgstr "E319: このバージョンではこのコマンドは利用できません" + +#, c-format +msgid "%d more file to edit. Quit anyway?" +msgid_plural "%d more files to edit. Quit anyway?" +msgstr[0] "編集すべきファイルがあと %d 個ありますが、終了しますか?" + +#, c-format +msgid "E173: %<PRId64> more file to edit" +msgid_plural "E173: %<PRId64> more files to edit" +msgstr[0] "E173: 編集すべきファイルがあと %<PRId64> 個あります" #, c-format msgid "E185: Cannot find color scheme '%s'" @@ -1224,58 +3163,26 @@ msgstr "E784: 最後のタブページを閉じることはできません" msgid "Already only one tab page" msgstr "既にタブページは1つしかありません" -msgid "Edit File in new tab page" -msgstr "新しいタブページでファイルを編集します" - -msgid "Edit File in new window" -msgstr "新しいウィンドウでファイルを編集します" - #, c-format msgid "Tab page %d" msgstr "タブページ %d" +msgid "E25: Nvim does not have a built-in GUI" +msgstr "E25: Nvim は組み込みの GUI がありません" + msgid "No swap file" msgstr "スワップファイルがありません" -msgid "Append File" -msgstr "追加ファイル" - -msgid "E747: Cannot change directory, buffer is modified (add ! to override)" -msgstr "" -"E747: バッファが修正されているので、ディレクトリを変更できません (! を追加で" -"上書)" - msgid "E186: No previous directory" msgstr "E186: 前のディレクトリはありません" msgid "E187: Unknown" -msgstr "E187: 未知" +msgstr "E187: 不明" msgid "E465: :winsize requires two number arguments" msgstr "E465: :winsize には2つの数値の引数が必要です" #, c-format -msgid "Window position: X %d, Y %d" -msgstr "ウィンドウ位置: X %d, Y %d" - -msgid "E188: Obtaining window position not implemented for this platform" -msgstr "" -"E188: このプラットホームにはウィンドウ位置の取得機能は実装されていません" - -msgid "E466: :winpos requires two number arguments" -msgstr "E466: :winpos には2つの数値の引数が必要です" - -msgid "E930: Cannot use :redir inside execute()" -msgstr "E930: execute() の中では :redir は使えません" - -msgid "Save Redirection" -msgstr "リダイレクトを保存します" - -#, c-format -msgid "E739: Cannot create directory: %s" -msgstr "E739: ディレクトリを作成できません: %s" - -#, c-format msgid "E189: \"%s\" exists (add ! to override)" msgstr "E189: \"%s\" が存在します (上書するには ! を追加してください)" @@ -1289,27 +3196,9 @@ msgstr "E191: 引数は1文字の英字か引用符 (' か `) でなければい msgid "E192: Recursive use of :normal too deep" msgstr "E192: :normal の再帰利用が深くなり過ぎました" -msgid "E809: #< is not available without the +eval feature" -msgstr "E809: #< は +eval 機能が無いと利用できません" - msgid "E194: No alternate file name to substitute for '#'" msgstr "E194: '#'を置き換える副ファイルの名前がありません" -msgid "E495: no autocommand file name to substitute for \"<afile>\"" -msgstr "E495: \"<afile>\"を置き換えるautocommandのファイル名がありません" - -msgid "E496: no autocommand buffer number to substitute for \"<abuf>\"" -msgstr "E496: \"<abuf>\"を置き換えるautocommandバッファ番号がありません" - -msgid "E497: no autocommand match name to substitute for \"<amatch>\"" -msgstr "E497: \"<amatch>\"を置き換えるautocommandの該当名がありません" - -msgid "E498: no :source file name to substitute for \"<sfile>\"" -msgstr "E498: \"<sfile>\"を置き換える :source 対象ファイル名がありません" - -msgid "E842: no line number to use for \"<slnum>\"" -msgstr "E842: \"<slnum>\"を置き換える行番号がありません" - #, no-c-format msgid "E499: Empty file name for '%' or '#', only works with \":p:h\"" msgstr "" @@ -1321,8 +3210,21 @@ msgstr "E500: 空文字列として評価されました" msgid "Untitled" msgstr "無題" -msgid "E196: No digraphs in this version" -msgstr "E196: このバージョンに合字はありません" +msgid "E5009: $VIMRUNTIME is empty or unset" +msgstr "E5009: $VIMRUNTIME が空か未設定です" + +#, c-format +msgid "E5009: Invalid $VIMRUNTIME: %s" +msgstr "E5009:無効な $VIMRUNTIME です: %s" + +msgid "E5009: Invalid 'runtimepath'" +msgstr "E5009:無効な 'runtimepath' です" + +msgid "E583: Multiple :else" +msgstr "E583: 複数の :else があります" + +msgid "E607: Multiple :finally" +msgstr "E607: 複数の :finally があります" msgid "E608: Cannot :throw exceptions with 'Vim' prefix" msgstr "E608: 'Vim' で始まる例外は :throw できません" @@ -1332,16 +3234,16 @@ msgid "Exception thrown: %s" msgstr "例外が発生しました: %s" #, c-format -msgid "Exception finished: %s" -msgstr "例外が収束しました: %s" - -#, c-format msgid "Exception discarded: %s" msgstr "例外が破棄されました: %s" #, c-format -msgid "%s, line %ld" -msgstr "%s, 行 %ld" +msgid "Exception finished: %s" +msgstr "例外が収束しました: %s" + +#, c-format +msgid "%s, line %<PRId64>" +msgstr "%s, 行 %<PRId64>" #, c-format msgid "Exception caught: %s" @@ -1383,9 +3285,6 @@ msgstr "E581: :if のない :else があります" msgid "E582: :elseif without :if" msgstr "E582: :if のない :elseif があります" -msgid "E583: multiple :else" -msgstr "E583: 複数の :else があります" - msgid "E584: :elseif after :else" msgstr "E584: :else の後に :elseif があります" @@ -1416,68 +3315,82 @@ msgstr "E604: :finally の後に :catch があります" msgid "E606: :finally without :try" msgstr "E606: :try のない :finally があります" -msgid "E607: multiple :finally" -msgstr "E607: 複数の :finally があります" - msgid "E602: :endtry without :try" msgstr "E602: :try のない :endtry です" -msgid "E193: :endfunction not inside a function" -msgstr "E193: 関数の外に :endfunction がありました" - -msgid "E788: Not allowed to edit another buffer now" -msgstr "E788: 現在は他のバッファを編集することは許されません" +msgid "E199: Active window or buffer changed or deleted" +msgstr "E199: アクティブなウィンドウかバッファが変更されたか削除されました" msgid "E811: Not allowed to change buffer information now" msgstr "E811: 現在はバッファ情報を変更することは許されません" #, c-format -msgid "" -"\n" -"# %s History (newest to oldest):\n" -msgstr "" -"\n" -"# %s 項目の履歴 (新しいものから古いものへ):\n" +msgid "E5408: Unable to get g:Nvim_color_cmdline callback: %s" +msgstr "E5408: g:Nvim_color_cmdline コールバックを取得できません: %s" + +#, c-format +msgid "E5407: Callback has thrown an exception: %s" +msgstr "E5407: コールバックで例外が発生しました: %s" + +msgid "E5400: Callback should return list" +msgstr "E5400: コールバックはリストを返す必要があります" + +#, c-format +msgid "E5401: List item %i is not a List" +msgstr "E5401: リストの要素 %i はリストではありません" + +#, c-format +msgid "E5402: List item %i has incorrect length: %d /= 3" +msgstr "E5402: リスト項目 %i の長さが正しくありません: %d /= 3" -msgid "Command Line" -msgstr "コマンドライン" +#~ msgid "E5403: Chunk %i start %" +#~ msgstr "" -msgid "Search String" -msgstr "検索文字列" +#~ msgid "E5405: Chunk %i start %" +#~ msgstr "" -msgid "Expression" -msgstr "式" +#~ msgid "E5404: Chunk %i end %" +#~ msgstr "" -msgid "Input Line" -msgstr "入力行" +#~ msgid "E5406: Chunk %i end %" +#~ msgstr "" -msgid "Debug Line" -msgstr "デバッグ行" +msgid "E854: Path too long for completion" +msgstr "E854: パスが長過ぎて補完できません" -msgid "E198: cmd_pchar beyond the command length" -msgstr "E198: cmd_pchar がコマンド長を超えました" +#, c-format +msgid "" +"E343: Invalid path: '**[number]' must be at the end of the path or be " +"followed by '%s'." +msgstr "" +"E343: 無効なパスです: '**[数値]' はpathの最後か '%s' が続いてないといけませ" +"ん。" -msgid "E199: Active window or buffer deleted" -msgstr "E199: アクティブなウィンドウかバッファが削除されました" +#, c-format +msgid "E344: Can't find directory \"%s\" in cdpath" +msgstr "E344: cdpathには \"%s\" というディレクトリがありません" + +#, c-format +msgid "E345: Can't find file \"%s\" in path" +msgstr "E345: pathには \"%s\" というファイルがありません" + +#, c-format +msgid "E346: No more directory \"%s\" found in cdpath" +msgstr "E346: cdpathにはこれ以上 \"%s\" というディレクトリがありません" + +#, c-format +msgid "E347: No more file \"%s\" found in path" +msgstr "E347: パスにはこれ以上 \"%s\" というファイルがありません" msgid "E812: Autocommands changed buffer or buffer name" -msgstr "E812: autocommandがバッファかバッファ名を変更しました" +msgstr "E812: 自動コマンドがバッファかバッファ名を変更しました" msgid "Illegal file name" msgstr "不正なファイル名" -msgid "is a directory" -msgstr "はディレクトリです" - msgid "is not a file" msgstr "はファイルではありません" -msgid "is a device (disabled with 'opendevice' option)" -msgstr "はデバイスです ('opendevice' オプションで回避できます)" - -msgid "[New File]" -msgstr "[新ファイル]" - msgid "[New DIRECTORY]" msgstr "[新規ディレクトリ]" @@ -1488,23 +3401,14 @@ msgid "[Permission Denied]" msgstr "[権限がありません]" msgid "E200: *ReadPre autocommands made the file unreadable" -msgstr "E200: *ReadPre autocommand がファイルを読込不可にしました" +msgstr "E200: *ReadPre 自動コマンド がファイルを読込不可にしました" msgid "E201: *ReadPre autocommands must not change current buffer" -msgstr "E201: *ReadPre autocommand は現在のバッファを変えられません" - -msgid "Vim: Reading from stdin...\n" -msgstr "Vim: 標準入力から読込中...\n" - -msgid "Reading from stdin..." -msgstr "標準入力から読込み中..." +msgstr "E201: *ReadPre 自動コマンド は現在のバッファを変えられません" msgid "E202: Conversion made file unreadable!" msgstr "E202: 変換がファイルを読込不可にしました" -msgid "[fifo/socket]" -msgstr "[FIFO/ソケット]" - msgid "[fifo]" msgstr "[FIFO]" @@ -1520,19 +3424,13 @@ msgstr "[CR無]" msgid "[long lines split]" msgstr "[長行分割]" -msgid "[NOT converted]" -msgstr "[未変換]" - -msgid "[converted]" -msgstr "[変換済]" - #, c-format -msgid "[CONVERSION ERROR in line %ld]" -msgstr "[%ld 行目で変換エラー]" +msgid "[CONVERSION ERROR in line %<PRId64>]" +msgstr "[%<PRId64> 行目で変換エラー]" #, c-format -msgid "[ILLEGAL BYTE in line %ld]" -msgstr "[%ld 行目の不正なバイト]" +msgid "[ILLEGAL BYTE in line %<PRId64>]" +msgstr "[%<PRId64> 行目の不正なバイト]" msgid "[READ ERRORS]" msgstr "[読込エラー]" @@ -1546,151 +3444,24 @@ msgstr "'charconvert' による変換が失敗しました" msgid "can't read output of 'charconvert'" msgstr "'charconvert' の出力を読込めませんでした" -msgid "E676: No matching autocommands for acwrite buffer" -msgstr "E676: acwriteバッファの該当するautocommandは存在しません" - -msgid "E203: Autocommands deleted or unloaded buffer to be written" -msgstr "E203: 保存するバッファをautocommandが削除か解放しました" - -msgid "E204: Autocommand changed number of lines in unexpected way" -msgstr "E204: autocommandが予期せぬ方法で行数を変更しました" - -# Added at 19-Jan-2004. -msgid "NetBeans disallows writes of unmodified buffers" -msgstr "NetBeansは未変更のバッファを上書することは許可していません" - -msgid "Partial writes disallowed for NetBeans buffers" -msgstr "NetBeansバッファの一部を書き出すことはできません" - -msgid "is not a file or writable device" -msgstr "はファイルでも書込み可能デバイスでもありません" - -msgid "writing to device disabled with 'opendevice' option" -msgstr "'opendevice' オプションによりデバイスへの書き込みはできません" - -msgid "is read-only (add ! to override)" -msgstr "は読込専用です (強制書込には ! を追加)" - -msgid "E506: Can't write to backup file (add ! to override)" -msgstr "E506: バックアップファイルを保存できません (! を追加で強制保存)" - -msgid "E507: Close error for backup file (add ! to override)" -msgstr "" -"E507: バックアップファイルを閉じる際にエラーが発生しました (! を追加で強制)" - -msgid "E508: Can't read file for backup (add ! to override)" -msgstr "E508: バックアップ用ファイルを読込めません (! を追加で強制読込)" - -msgid "E509: Cannot create backup file (add ! to override)" -msgstr "E509: バックアップファイルを作れません (! を追加で強制作成)" - -msgid "E510: Can't make backup file (add ! to override)" -msgstr "E510: バックアップファイルを作れません (! を追加で強制作成)" - -msgid "E214: Can't find temp file for writing" -msgstr "E214: 保存用一時ファイルが見つかりません" - -msgid "E213: Cannot convert (add ! to write without conversion)" -msgstr "E213: 変換できません (! を追加で変換せずに保存)" - -msgid "E166: Can't open linked file for writing" -msgstr "E166: リンクされたファイルに書込めません" - -msgid "E212: Can't open file for writing" -msgstr "E212: 書込み用にファイルを開けません" - -msgid "E949: File changed while writing" -msgstr "E949: 書込み中にファイルが変更されました" - -msgid "E512: Close failed" -msgstr "E512: 閉じることに失敗" - -msgid "E513: write error, conversion failed (make 'fenc' empty to override)" -msgstr "E513: 書込みエラー、変換失敗 (上書するには 'fenc' を空にしてください)" - -#, c-format -msgid "" -"E513: write error, conversion failed in line %ld (make 'fenc' empty to " -"override)" -msgstr "" -"E513: 書込みエラー、変換失敗、行数 %ld (上書するには 'fenc' を空にしてくださ" -"い)" - -msgid "E514: write error (file system full?)" -msgstr "E514: 書込みエラー (ファイルシステムが満杯?)" - -msgid " CONVERSION ERROR" -msgstr " 変換エラー" - -#, c-format -msgid " in line %ld;" -msgstr " 行 %ld;" - -msgid "[Device]" -msgstr "[デバイス]" - -msgid "[New]" -msgstr "[新]" - -msgid " [a]" -msgstr " [a]" - -msgid " appended" -msgstr " 追加" - -msgid " [w]" -msgstr " [w]" - -msgid " written" -msgstr " 書込み" - -msgid "" -"\n" -"WARNING: Original file may be lost or damaged\n" -msgstr "" -"\n" -"警告: 原本ファイルが失われたか変更されました\n" - -msgid "don't quit the editor until the file is successfully written!" -msgstr "ファイルの保存に成功するまでエディタを終了しないでください!" - msgid "[dos]" msgstr "[dos]" -msgid "[dos format]" -msgstr "[dosフォーマット]" - msgid "[mac]" msgstr "[mac]" -msgid "[mac format]" -msgstr "[macフォーマット]" - msgid "[unix]" msgstr "[unix]" -msgid "[unix format]" -msgstr "[unixフォーマット]" - -msgid "1 line, " -msgstr "1 行, " - #, c-format -msgid "%ld lines, " -msgstr "%ld 行, " - -msgid "1 character" -msgstr "1 文字" +msgid "%<PRId64> line, " +msgid_plural "%<PRId64> lines, " +msgstr[0] "%<PRId64> 行, " #, c-format -msgid "%lld characters" -msgstr "%lld 文字" - -msgid "[noeol]" -msgstr "[noeol]" - -msgid "[Incomplete last line]" -msgstr "[最終行が不完全]" +msgid "%<PRId64> byte" +msgid_plural "%<PRId64> bytes" +msgstr[0] "%<PRId64> バイト" #, c-format msgid "E208: Error writing to \"%s\"" @@ -1702,10 +3473,10 @@ msgstr "E209: \"%s\" を閉じる時にエラーです" #, c-format msgid "E210: Error reading \"%s\"" -msgstr "E210: \"%s\" を読込中のエラーです" +msgstr "E210: \"%s\" を読込み中のエラーです" msgid "E246: FileChangedShell autocommand deleted buffer" -msgstr "E246: autocommand の FileChangedShell がバッファを削除しました" +msgstr "E246: FileChangedShell 自動コマンドがバッファを削除しました" #, c-format msgid "E211: File \"%s\" no longer available" @@ -1743,10 +3514,12 @@ msgstr "警告" msgid "" "&OK\n" -"&Load File" +"&Load File\n" +"Load File &and Options" msgstr "" "&OK\n" -"ファイル読込(&L)" +"ファイル読込(&L)\n" +"ファイルとオプションを読込(&A)" #, c-format msgid "E462: Could not prepare for reloading \"%s\"" @@ -1756,72 +3529,11 @@ msgstr "E462: \"%s\" をリロードする準備ができませんでした" msgid "E321: Could not reload \"%s\"" msgstr "E321: \"%s\" はリロードできませんでした" -msgid "--Deleted--" -msgstr "--削除済--" - -#, c-format -msgid "auto-removing autocommand: %s <buffer=%d>" -msgstr "autocommand: %s <バッファ=%d> が自動的に削除されます" - -#, c-format -msgid "E367: No such group: \"%s\"" -msgstr "E367: そのグループはありません: \"%s\"" - -msgid "E936: Cannot delete the current group" -msgstr "E936: 現在のグループは削除できません" - -msgid "W19: Deleting augroup that is still in use" -msgstr "W19: 使用中の augroup を消そうとしています" - -#, c-format -msgid "E215: Illegal character after *: %s" -msgstr "E215: * の後に不正な文字がありました: %s" - -#, c-format -msgid "E216: No such event: %s" -msgstr "E216: そのようなイベントはありません: %s" - -#, c-format -msgid "E216: No such group or event: %s" -msgstr "E216: そのようなグループもしくはイベントはありません: %s" - -msgid "" -"\n" -"--- Autocommands ---" -msgstr "" -"\n" -"--- Autocommands ---" - -#, c-format -msgid "E680: <buffer=%d>: invalid buffer number " -msgstr "E680: <バッファ=%d>: 無効なバッファ番号です " - -msgid "E217: Can't execute autocommands for ALL events" -msgstr "E217: 全てのイベントに対してのautocommandは実行できません" - -msgid "No matching autocommands" -msgstr "該当するautocommandは存在しません" - -msgid "E218: autocommand nesting too deep" -msgstr "E218: autocommandの入れ子が深過ぎます" - -#, c-format -msgid "%s Autocommands for \"%s\"" -msgstr "%s Autocommands for \"%s\"" - -#, c-format -msgid "Executing %s" -msgstr "%s を実行しています" - -#, c-format -msgid "autocommand %s" -msgstr "autocommand %s" - msgid "E219: Missing {." -msgstr "E219: { がありません." +msgstr "E219: { がありません。" msgid "E220: Missing }." -msgstr "E220: } がありません." +msgstr "E220: } がありません。" msgid "E490: No fold found" msgstr "E490: 折畳みがありません" @@ -1833,1151 +3545,1084 @@ msgid "E351: Cannot delete fold with current 'foldmethod'" msgstr "E351: 現在の 'foldmethod' では折畳みを削除できません" #, c-format -msgid "+--%3ld line folded " -msgid_plural "+--%3ld lines folded " -msgstr[0] "+--%3ld 行が折畳まれました" +msgid "+--%3d line folded" +msgid_plural "+--%3d lines folded " +msgstr[0] "+--%3d 行が折畳まれました" #, c-format -msgid "+-%s%3ld line: " -msgid_plural "+-%s%3ld lines: " -msgstr[0] "+-%s%3ld 行: " +msgid "+-%s%3d line: " +msgid_plural "+-%s%3d lines: " +msgstr[0] "+-%s%3d 行: " -msgid "E222: Add to read buffer" -msgstr "E222: 読込バッファへ追加" - -msgid "E223: recursive mapping" +msgid "E223: Recursive mapping" msgstr "E223: 再帰的マッピング" -msgid "E851: Failed to create a new process for the GUI" -msgstr "E851: GUI用のプロセスの起動に失敗しました" - -msgid "E852: The child process failed to start the GUI" -msgstr "E852: 子プロセスがGUIの起動に失敗しました" - -msgid "E229: Cannot start the GUI" -msgstr "E229: GUIを開始できません" - -#, c-format -msgid "E230: Cannot read from \"%s\"" -msgstr "E230: \"%s\"から読込むことができません" - -msgid "E665: Cannot start GUI, no valid font found" -msgstr "E665: 有効なフォントが見つからないので、GUIを開始できません" - -msgid "E231: 'guifontwide' invalid" -msgstr "E231: 'guifontwide' が無効です" - -msgid "E599: Value of 'imactivatekey' is invalid" -msgstr "E599: 'imactivatekey' に設定された値が無効です" - -#, c-format -msgid "E254: Cannot allocate color %s" -msgstr "E254: %s の色を割り当てられません" +msgid "E1255: <Cmd> mapping must end with <CR>" +msgstr "E1255: <Cmd> マッピングは <CR> で終わらなければなりません" -msgid "No match at cursor, finding next" -msgstr "カーソルの位置にマッチはありません、次を検索しています" +msgid "E1136: <Cmd> mapping must end with <CR> before second <Cmd>" +msgstr "" +"E1136: <Cmd> マッピングは次の <Cmd> の前に <CR> で終わらなければなりません" -msgid "<cannot open> " -msgstr "<開けません> " +msgid "E222: Add to read buffer" +msgstr "E222: 読み取りバッファに追加" #, c-format -msgid "E616: vim_SelFile: can't get font %s" -msgstr "E616: vim_SelFile: フォント %s を取得できません" - -msgid "E614: vim_SelFile: can't return to current directory" -msgstr "E614: vim_SelFile: 現在のディレクトリに戻れません" - -msgid "Pathname:" -msgstr "パス名:" - -msgid "E615: vim_SelFile: can't get current directory" -msgstr "E615: vim_SelFile: 現在のディレクトリを取得できません" - -msgid "OK" -msgstr "OK" - -msgid "Cancel" -msgstr "キャンセル" - -msgid "Scrollbar Widget: Could not get geometry of thumb pixmap." -msgstr "スクロールバー: 画像を取得できませんでした。" +msgid "Cannot open for reading: \"%s\": %s\n" +msgstr "読込み用として開けません: \"%s\": %s\n" -msgid "Vim dialog" -msgstr "Vim ダイアログ" +msgid "--No lines in buffer--" +msgstr "--バッファに行がありません--" -msgid "E232: Cannot create BalloonEval with both message and callback" -msgstr "E232: メッセージとコールバックのある BalloonEval を作成できません" +msgid "E470: Command aborted" +msgstr "E470: コマンドが中断されました" -msgid "_Cancel" -msgstr "キャンセル(_C)" +msgid "E905: Cannot set this option after startup" +msgstr "E905: 起動後はこのオプションを設定できません" -msgid "_Save" -msgstr "保存(_S)" +#, fuzzy +#~ msgid "E903: Could not spawn API job" +#~ msgstr "E903: API タスクの開始に失敗しました" -msgid "_Open" -msgstr "開く(_O)" +msgid "E471: Argument required" +msgstr "E471: 引数が必要です" -msgid "_OK" -msgstr "_OK" +msgid "E10: \\ should be followed by /, ? or &" +msgstr "E10: \\ の後は / か ? か & でなければなりません" -msgid "" -"&Yes\n" -"&No\n" -"&Cancel" +msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits" msgstr "" -"はい(&Y)\n" -"いいえ(&N)\n" -"キャンセル(&C)" - -msgid "Yes" -msgstr "はい" - -msgid "No" -msgstr "いいえ" - -msgid "Input _Methods" -msgstr "インプットメソッド" - -msgid "VIM - Search and Replace..." -msgstr "VIM - 検索と置換..." - -msgid "VIM - Search..." -msgstr "VIM - 検索..." - -msgid "Find what:" -msgstr "検索文字列:" +"E11: コマンドラインウィンドウでは使用できません。<CR> で実行、CTRL-C で終了し" +"ます。" -msgid "Replace with:" -msgstr "置換文字列:" +#, fuzzy +#~ msgid "E12: Command not allowed in secure mode in current dir or tag search" +#~ msgstr "E12: このコマンドは現在のディレクトリやタグ検索では使用できません" -msgid "Match whole word only" -msgstr "正確に該当するものだけ" +msgid "E169: Command too recursive" +msgstr "E169: コマンドが再帰的過ぎます" -msgid "Match case" -msgstr "大文字/小文字を区別する" +msgid "E681: Buffer is not loaded" +msgstr "E681: バッファは読込まれませんでした" -msgid "Direction" -msgstr "方向" +msgid "E171: Missing :endif" +msgstr "E171: :endif がありません" -msgid "Up" -msgstr "上" +msgid "E600: Missing :endtry" +msgstr "E600: :endtry がありません" -msgid "Down" -msgstr "下" +msgid "E170: Missing :endwhile" +msgstr "E170: :endwhile がありません" -msgid "Find Next" -msgstr "次を検索" +msgid "E170: Missing :endfor" +msgstr "E170: :endfor がありません" -msgid "Replace" -msgstr "置換" +msgid "E588: :endwhile without :while" +msgstr "E588: :while のない :endwhile があります" -msgid "Replace All" -msgstr "全て置換" +msgid "E588: :endfor without :for" +msgstr "E588: :endfor のない :for があります" -msgid "_Close" -msgstr "閉じる(_C)" +msgid "E13: File exists (add ! to override)" +msgstr "E13: ファイルが存在します (! を追加で上書)" -msgid "Vim: Received \"die\" request from session manager\n" -msgstr "Vim: セッションマネージャから \"die\" 要求を受け取りました\n" +msgid "E472: Command failed" +msgstr "E472: コマンドが失敗しました" -msgid "Close tab" -msgstr "タブページを閉じる" +msgid "E473: Internal error" +msgstr "E473: 内部エラーです" -msgid "New tab" -msgstr "新規タブページ" +#, c-format +msgid "E685: Internal error: %s" +msgstr "E685: 内部エラーです: %s" -msgid "Open Tab..." -msgstr "タブページを開く..." +msgid "Interrupted" +msgstr "割込まれました" -msgid "Vim: Main window unexpectedly destroyed\n" -msgstr "Vim: メインウィンドウが不意に破壊されました\n" +msgid "E474: Invalid argument" +msgstr "E474: 無効な引数です" -msgid "&Filter" -msgstr "フィルタ(&F)" +#, c-format +msgid "E475: Invalid argument: %s" +msgstr "E475: 無効な引数です: %s" -msgid "&Cancel" -msgstr "キャンセル(&C)" +#, c-format +msgid "E475: Invalid value for argument %s" +msgstr "E475: 引数 %s に対して無効な値です" -msgid "Directories" -msgstr "ディレクトリ" +#, c-format +msgid "E475: Invalid value for argument %s: %s" +msgstr "E475: 引数 %s に対して無効な値です: %s" -msgid "Filter" -msgstr "フィルタ" +#, c-format +msgid "E983: Duplicate argument: %s" +msgstr "E983: 引数が重複しています: %s" -msgid "&Help" -msgstr "ヘルプ(&H)" +#, c-format +msgid "E15: Invalid expression: \"%s\"" +msgstr "E15: 無効な式です: \"%s\"" -msgid "Files" -msgstr "ファイル" +msgid "E16: Invalid range" +msgstr "E16: 無効な範囲です" -msgid "&OK" -msgstr "&OK" +msgid "E476: Invalid command" +msgstr "E476: 無効なコマンドです" -msgid "Selection" -msgstr "選択" +#, c-format +msgid "E17: \"%s\" is a directory" +msgstr "E17: \"%s\" はディレクトリです" -msgid "Find &Next" -msgstr "次を検索(&N)" +msgid "E756: Spell checking is not possible" +msgstr "E756: スペルチェックは使用できません" -msgid "&Replace" -msgstr "置換(&R)" +msgid "E900: Invalid channel id" +msgstr "E900: 無効なチャネルIDです" -msgid "Replace &All" -msgstr "全て置換(&A)" +msgid "E900: Invalid channel id: not a job" +msgstr "E900: 無効なチャネルIDです: タスクではありません" -msgid "&Undo" -msgstr "アンドゥ(&U)" +msgid "E901: Job table is full" +msgstr "E901: タスクテーブルがフルです" -msgid "Open tab..." -msgstr "タブページを開く" +#, c-format +msgid "E903: Process failed to start: %s: \"%s\"" +msgstr "E903: プロセスの開始に失敗しました: %s: \"%s\"" -msgid "Find string (use '\\\\' to find a '\\')" -msgstr "検索文字列 ('\\' を検索するには '\\\\')" +msgid "E904: channel is not a pty" +msgstr "E904: チャネルは疑似端末ではありません" -msgid "Find & Replace (use '\\\\' to find a '\\')" -msgstr "検索・置換 ('\\' を検索するには '\\\\')" +#, c-format +msgid "E905: Couldn't open stdio channel: %s" +msgstr "E905: 標準出力チャネルを開けませんでした: %s" -msgid "Not Used" -msgstr "使われません" +msgid "E906: invalid stream for channel" +msgstr "E906: チャネルのストリームが無効です" -msgid "Directory\t*.nothing\n" -msgstr "ディレクトリ\t*.nothing\n" +msgid "E906: invalid stream for rpc channel, use 'rpc'" +msgstr "E906: rpc チャネルのストリームが無効です。 rpc を使用してください" #, c-format -msgid "E671: Cannot find window title \"%s\"" -msgstr "E671: タイトルが \"%s\" のウィンドウは見つかりません" +msgid "" +"E5210: dict key '%s' already set for buffered stream in channel %<PRIu64>" +msgstr "" +"E5210: 辞書キー '%s' はチャネル %<PRIu64> のバッファされたストリームにすでに" +"設定されています" #, c-format -msgid "E243: Argument not supported: \"-%s\"; Use the OLE version." -msgstr "E243: 引数はサポートされません: \"-%s\"; OLE版を使用してください." - -msgid "E672: Unable to open window inside MDI application" -msgstr "E672: MDIアプリの中ではウィンドウを開けません" - -msgid "Vim E458: Cannot allocate colormap entry, some colors may be incorrect" -msgstr "Vim E458: 色指定が正しくないのでエントリを割り当てられません" +msgid "E364: Library call failed for \"%s()\"" +msgstr "E364: \"%s()\" のライブラリ呼出しに失敗しました" #, c-format -msgid "E250: Fonts for the following charsets are missing in fontset %s:" -msgstr "E250: 以下の文字セットのフォントがありません %s:" +msgid "E667: Fsync failed: %s" +msgstr "E667: fsync に失敗しました: %s" #, c-format -msgid "E252: Fontset name: %s" -msgstr "E252: フォントセット名: %s" +msgid "E739: Cannot create directory %s: %s" +msgstr "E739: %s ディレクトリを作成できません: %s" -#, c-format -msgid "Font '%s' is not fixed-width" -msgstr "フォント '%s' は固定幅ではありません" +msgid "E19: Mark has invalid line number" +msgstr "E19: マークに無効な行番号が指定されていました" -#, c-format -msgid "E253: Fontset name: %s" -msgstr "E253: フォントセット名: %s" +msgid "E20: Mark not set" +msgstr "E20: マークは設定されていません" -#, c-format -msgid "Font0: %s" -msgstr "フォント0: %s" +msgid "E21: Cannot make changes, 'modifiable' is off" +msgstr "E21: 'modifiable' がオフなので、変更できません" -#, c-format -msgid "Font1: %s" -msgstr "フォント1: %s" +msgid "E22: Scripts nested too deep" +msgstr "E22: スクリプトの入れ子が深過ぎます" -#, c-format -msgid "Font%ld width is not twice that of font0" -msgstr "フォント%ld の幅がフォント0の2倍ではありません" +msgid "E23: No alternate file" +msgstr "E23: 副ファイルはありません" -#, c-format -msgid "Font0 width: %ld" -msgstr "フォント0の幅: %ld" +msgid "E24: No such abbreviation" +msgstr "E24: そのような短縮入力はありません" -#, c-format -msgid "Font1 width: %ld" -msgstr "フォント1の幅: %ld" +msgid "E477: No ! allowed" +msgstr "E477: ! は許可されていません" -msgid "Invalid font specification" -msgstr "無効なフォント指定です" +#, c-format +msgid "E28: No such highlight group name: %s" +msgstr "E28: そのような名のハイライトグループはありません: %s" -msgid "&Dismiss" -msgstr "却下する(&D)" +msgid "E29: No inserted text yet" +msgstr "E29: まだテキストが挿入されていません" -msgid "no specific match" -msgstr "マッチするものがありません" +msgid "E30: No previous command line" +msgstr "E30: 以前にコマンド行がありません" -msgid "Vim - Font Selector" -msgstr "Vim - フォント選択" +msgid "E31: No such mapping" +msgstr "E31: そのようなマッピングはありません" -msgid "Name:" -msgstr "名前:" +msgid "E479: No match" +msgstr "E479: 該当はありません" -msgid "Show size in Points" -msgstr "サイズをポイントで表示する" +#, c-format +msgid "E480: No match: %s" +msgstr "E480: 該当はありません: %s" -msgid "Encoding:" -msgstr "エンコード:" +msgid "E32: No file name" +msgstr "E32: ファイル名がありません" -msgid "Font:" -msgstr "フォント:" +msgid "E33: No previous substitute regular expression" +msgstr "E33: 正規表現置換がまだ実行されていません" -msgid "Style:" -msgstr "スタイル:" +msgid "E34: No previous command" +msgstr "E34: コマンドがまだ実行されていません" -msgid "Size:" -msgstr "サイズ:" +msgid "E35: No previous regular expression" +msgstr "E35: 正規表現がまだ実行されていません" -msgid "E256: Hangul automata ERROR" -msgstr "E256: ハングルオートマトンエラー" +msgid "E481: No range allowed" +msgstr "E481: 範囲指定は許可されていません" -#, c-format -msgid "E799: Invalid ID: %d (must be greater than or equal to 1)" -msgstr "E799: 無効な ID: %d (1 以上でなければなりません)" +msgid "E36: Not enough room" +msgstr "E36: ウィンドウに十分な高さもしくは幅がありません" -#, c-format -msgid "E801: ID already taken: %d" -msgstr "E801: ID はすでに利用中です: %d" +msgid "E483: Can't get temp file name" +msgstr "E483: 一時ファイルの名前を取得できません" #, c-format -msgid "E802: Invalid ID: %d (must be greater than or equal to 1)" -msgstr "E802: 無効な ID: %d (1 以上でなければなりません)" +msgid "E484: Can't open file %s" +msgstr "E484: ファイル %s を開けません" #, c-format -msgid "E803: ID not found: %d" -msgstr "E803: ID はありません: %d" +msgid "E484: Can't open file %s: %s" +msgstr "E484: ファイル %s を開けません: %s" #, c-format -msgid "E798: ID is reserved for \":match\": %d" -msgstr "E798: ID は \":match\" のために予約されています: %d" - -msgid "Add a new database" -msgstr "新データベースを追加" +msgid "E485: Can't read file %s" +msgstr "E485: ファイル %s を読込めません" -msgid "Query for a pattern" -msgstr "パターンのクエリーを追加" +msgid "E38: Null argument" +msgstr "E38: 引数が null です" -msgid "Show this message" -msgstr "このメッセージを表示する" +msgid "E39: Number expected" +msgstr "E39: 数値が要求されています" -msgid "Kill a connection" -msgstr "接続を終了する" +#, c-format +msgid "E40: Can't open errorfile %s" +msgstr "E40: エラーファイル %s を開けません" -msgid "Reinit all connections" -msgstr "全ての接続を再初期化する" +msgid "E41: Out of memory!" +msgstr "E41: メモリが尽き果てました!" -msgid "Show connections" -msgstr "接続を表示する" +msgid "Pattern not found" +msgstr "パターンは見つかりませんでした" #, c-format -msgid "E560: Usage: cs[cope] %s" -msgstr "E560: 使用方法: cs[cope] %s" +msgid "E486: Pattern not found: %s" +msgstr "E486: パターンは見つかりませんでした: %s" -msgid "This cscope command does not support splitting the window.\n" -msgstr "このcscopeコマンドは分割ウィンドウではサポートされません.\n" +msgid "E487: Argument must be positive" +msgstr "E487: 引数は正の値でなければなりません" -msgid "E562: Usage: cstag <ident>" -msgstr "E562: 使用法: cstag <ident>" +msgid "E459: Cannot go back to previous directory" +msgstr "E459: 前のディレクトリに戻れません" -msgid "E257: cstag: tag not found" -msgstr "E257: cstag: タグが見つかりません" +msgid "E42: No Errors" +msgstr "E42: エラーはありません" -#, c-format -msgid "E563: stat(%s) error: %d" -msgstr "E563: stat(%s) エラー: %d" +msgid "E776: No location list" +msgstr "E776: ロケーションリストはありません" -msgid "E563: stat error" -msgstr "E563: stat エラー" +msgid "E43: Damaged match string" +msgstr "E43: マッチした文字列が破損しています" -#, c-format -msgid "E564: %s is not a directory or a valid cscope database" -msgstr "E564: %s はディレクトリ及び有効なcscopeのデータベースではありません" +msgid "E44: Corrupted regexp program" +msgstr "E44: 正規表現プログラムが壊れています" -#, c-format -msgid "Added cscope database %s" -msgstr "cscopeデータベース %s を追加" +msgid "E45: 'readonly' option is set (add ! to override)" +msgstr "E45: 'readonly' オプションが設定されています (! を追加で上書き)" #, c-format -msgid "E262: error reading cscope connection %ld" -msgstr "E262: cscopeの接続 %ld を読込み中のエラーです" - -msgid "E561: unknown cscope search type" -msgstr "E561: 未知のcscope検索型です" +msgid "E734: Wrong variable type for %s=" +msgstr "E734: 異なった型の変数です %s=" -msgid "E566: Could not create cscope pipes" -msgstr "E566: cscopeパイプを作成できませんでした" - -msgid "E622: Could not fork for cscope" -msgstr "E622: cscopeの起動準備(fork)に失敗しました" +#, c-format +msgid "E461: Illegal variable name: %s" +msgstr "E461: 不正な変数名です: %s" -msgid "cs_create_connection setpgid failed" -msgstr "cs_create_connection への setpgid に失敗しました" +msgid "E995: Cannot modify existing variable" +msgstr "E995: 既存の変数を変更できません" -msgid "cs_create_connection exec failed" -msgstr "cs_create_connection の実行に失敗しました" +#, c-format +msgid "E46: Cannot change read-only variable \"%.*s\"" +msgstr "E46: 読取専用変数 \"%.*s\" には値を設定できません" -msgid "cs_create_connection: fdopen for to_fp failed" -msgstr "cs_create_connection: to_fp の fdopen に失敗しました" +msgid "E928: String required" +msgstr "E928: 文字列が必要です" -msgid "cs_create_connection: fdopen for fr_fp failed" -msgstr "cs_create_connection: fr_fp の fdopen に失敗しました" +msgid "E715: Dictionary required" +msgstr "E715: 辞書型が必要です" -msgid "E623: Could not spawn cscope process" -msgstr "E623: cscopeプロセスを起動できませんでした" +#, c-format +msgid "E979: Blob index out of range: %<PRId64>" +msgstr "E979: Blobのインデックスが範囲外です: %<PRId64>" -msgid "E567: no cscope connections" -msgstr "E567: cscope接続に失敗しました" +msgid "E978: Invalid operation for Blob" +msgstr "E978: Blob型には無効な操作です" #, c-format -msgid "E469: invalid cscopequickfix flag %c for %c" -msgstr "E469: 無効な cscopequickfix フラグ %c の %c です" +msgid "E118: Too many arguments for function: %s" +msgstr "E118: 関数の引数が多過ぎます: %s" #, c-format -msgid "E259: no matches found for cscope query %s of %s" -msgstr "E259: cscopeクエリー %s of %s に該当がありませんでした" - -msgid "cscope commands:\n" -msgstr "cscopeコマンド:\n" +msgid "E119: Not enough arguments for function: %s" +msgstr "E119: 関数の引数が足りません: %s" #, c-format -msgid "%-5s: %s%*s (Usage: %s)" -msgstr "%-5s: %s%*s (使用法: %s)" - -msgid "" -"\n" -" a: Find assignments to this symbol\n" -" c: Find functions calling this function\n" -" d: Find functions called by this function\n" -" e: Find this egrep pattern\n" -" f: Find this file\n" -" g: Find this definition\n" -" i: Find files #including this file\n" -" s: Find this C symbol\n" -" t: Find this text string\n" -msgstr "" -"\n" -" a: このシンボルに対する代入を探す\n" -" c: この関数を呼んでいる関数を探す\n" -" d: この関数から呼んでいる関数を探す\n" -" e: このegrepパターンを探す\n" -" f: このファイルを探す\n" -" g: この定義を探す\n" -" i: このファイルを#includeしているファイルを探す\n" -" s: このCシンボルを探す\n" -" t: このテキスト文字列を探す\n" +msgid "E716: Key not present in Dictionary: \"%s\"" +msgstr "E716: 辞書型にキーが存在しません: \"%s\"" #, c-format -msgid "E625: cannot open cscope database: %s" -msgstr "E625: cscopeデータベース: %s を開くことができません" +msgid "E716: Key not present in Dictionary: \"%.*s\"" +msgstr "E716: 辞書型にキーが存在しません: \"%.*s\"" -msgid "E626: cannot get cscope database information" -msgstr "E626: cscopeデータベースの情報を取得できません" +msgid "E714: List required" +msgstr "E714: リスト型が必要です" -msgid "E568: duplicate cscope database not added" -msgstr "E568: 重複するcscopeデータベースは追加されませんでした" +msgid "E897: List or Blob required" +msgstr "E897: リスト型またはBlob型が必要です" #, c-format -msgid "E261: cscope connection %s not found" -msgstr "E261: cscope接続 %s が見つかりませんでした" +msgid "E712: Argument of %s must be a List or Dictionary" +msgstr "E712: %s の引数はリスト型または辞書型でなければなりません" #, c-format -msgid "cscope connection %s closed" -msgstr "cscope接続 %s が閉じられました" - -msgid "E570: fatal error in cs_manage_matches" -msgstr "E570: cs_manage_matches で致命的なエラーです" +msgid "E896: Argument of %s must be a List, Dictionary or Blob" +msgstr "E896: %s の引数はリスト型、辞書型またはBlob型でなければなりません" -#, c-format -msgid "Cscope tag: %s" -msgstr "Cscope タグ: %s" +msgid "E47: Error while reading errorfile" +msgstr "E47: エラーファイルの読込み中にエラーが発生しました" -msgid "" -"\n" -" # line" -msgstr "" -"\n" -" # 行番号" +msgid "E48: Not allowed in sandbox" +msgstr "E48: サンドボックスでは許されません" -msgid "filename / context / line\n" -msgstr "ファイル名 / 文脈 / 行\n" +msgid "E523: Not allowed here" +msgstr "E523: ここでは許可されません" -#, c-format -msgid "E609: Cscope error: %s" -msgstr "E609: cscopeエラー: %s" +msgid "E565: Not allowed to change text or change window" +msgstr "E565: テキストを変更したりウィンドウを変更することは許可されません" -msgid "All cscope databases reset" -msgstr "全てのcscopeデータベースをリセットします" +msgid "E359: Screen mode setting not supported" +msgstr "E359: スクリーンモードの設定には対応していません" -msgid "no cscope connections\n" -msgstr "cscope接続がありません\n" +msgid "E49: Invalid scroll size" +msgstr "E49: 無効なスクロール量です" -msgid " # pid database name prepend path\n" -msgstr " # pid データベース名 prepend パス\n" +msgid "E91: 'shell' option is empty" +msgstr "E91: 'shell' オプションが空です" -msgid "Lua library cannot be loaded." -msgstr "Luaライブラリをロードできません。" +msgid "E255: Couldn't read in sign data!" +msgstr "E255: sign のデータを読込めませんでした" -msgid "cannot save undo information" -msgstr "アンドゥ情報が保存できません" +msgid "E72: Close error on swap file" +msgstr "E72: スワップファイルのクローズ時エラーです" -msgid "" -"E815: Sorry, this command is disabled, the MzScheme libraries could not be " -"loaded." -msgstr "E815: このコマンドは無効です。MzScheme ライブラリをロードできません。" +msgid "E74: Command too complex" +msgstr "E74: コマンドが複雑過ぎます" -msgid "" -"E895: Sorry, this command is disabled, the MzScheme's racket/base module " -"could not be loaded." -msgstr "" -"E895: このコマンドは無効です、ごめんなさい。MzScheme の racket/base モジュー" -"ルがロードできませんでした。" +msgid "E75: Name too long" +msgstr "E75: 名前が長過ぎます" -msgid "invalid expression" -msgstr "無効な式です" +msgid "E76: Too many [" +msgstr "E76: [ が多過ぎます" -msgid "expressions disabled at compile time" -msgstr "式はコンパイル時に無効にされています" +msgid "E77: Too many file names" +msgstr "E77: ファイル名が多過ぎます" -msgid "hidden option" -msgstr "隠しオプション" +msgid "E488: Trailing characters" +msgstr "E488: 余分な文字が後ろにあります" -msgid "unknown option" -msgstr "未知のオプションです" +#, c-format +msgid "E488: Trailing characters: %s" +msgstr "E488: 余分な文字が後ろにあります: %s" -msgid "window index is out of range" -msgstr "範囲外のウィンドウ番号です" +msgid "E78: Unknown mark" +msgstr "E78: 未知のマーク" -msgid "couldn't open buffer" -msgstr "バッファを開けません" +msgid "E79: Cannot expand wildcards" +msgstr "E79: ワイルドカードを展開できません" -msgid "cannot delete line" -msgstr "行を消せません" +msgid "E591: 'winheight' cannot be smaller than 'winminheight'" +msgstr "E591: 'winheight' は 'winminheight' より小さくできません" -msgid "cannot replace line" -msgstr "行を置換できません" +msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'" +msgstr "E592: 'winwidth' は 'winminwidth' より小さくできません" -msgid "cannot insert line" -msgstr "行を挿入できません" +msgid "E80: Error while writing" +msgstr "E80: 書込み中のエラー" -msgid "string cannot contain newlines" -msgstr "文字列には改行文字を含められません" +msgid "E939: Positive count required" +msgstr "E939: 正のカウントが必要です" -msgid "error converting Scheme values to Vim" -msgstr "Scheme値のVimへの変換エラー" +msgid "E81: Using <SID> not in a script context" +msgstr "E81: スクリプト以外で<SID>が使われました" -msgid "Vim error: ~a" -msgstr "Vim エラー: ~a" +#, c-format +msgid "E107: Missing parentheses: %s" +msgstr "E107: カッコ '(' がありません: %s" -msgid "Vim error" -msgstr "Vim エラー" +msgid "E749: Empty buffer" +msgstr "E749: バッファが空です" -msgid "buffer is invalid" -msgstr "バッファは無効です" +#, c-format +msgid "E86: Buffer %<PRId64> does not exist" +msgstr "E86: バッファ %<PRId64> はありません" -msgid "window is invalid" -msgstr "ウィンドウは無効です" +#, c-format +msgid "E193: %s not inside a function" +msgstr "E193: 関数の外に %s がありました" -msgid "linenr out of range" -msgstr "範囲外の行番号です" +msgid "E682: Invalid search pattern or delimiter" +msgstr "E682: 検索パターンか区切り記号が不正です" -msgid "not allowed in the Vim sandbox" -msgstr "サンドボックスでは許されません" +msgid "E139: File is loaded in another buffer" +msgstr "E139: 同じ名前のファイルが他のバッファで読込まれています" #, c-format -msgid "E370: Could not load library %s" -msgstr "E370: ライブラリ %s をロードできませんでした" - -msgid "Sorry, this command is disabled: the Perl library could not be loaded." -msgstr "" -"このコマンドは無効です、ごめんなさい: Perlライブラリをロードできませんでした." - -msgid "E299: Perl evaluation forbidden in sandbox without the Safe module" -msgstr "" -"E299: サンドボックスでは Safe モジュールを使用しないPerlスクリプトは禁じられ" -"ています" +msgid "E764: Option '%s' is not set" +msgstr "E764: オプション '%s' は設定されていません" -msgid "E836: This Vim cannot execute :python after using :py3" -msgstr "E836: このVimでは :py3 を使った後に :python を使えません" +msgid "E850: Invalid register name" +msgstr "E850: 無効なレジスタ名です" -msgid "" -"E263: Sorry, this command is disabled, the Python library could not be " -"loaded." -msgstr "" -"E263: このコマンドは無効です、ごめんなさい: Pythonライブラリをロードできませ" -"んでした。" +#, c-format +msgid "E919: Directory not found in '%s': \"%s\"" +msgstr "E919: ディレクトリが '%s' の中にありません: \"%s\"" -msgid "" -"E887: Sorry, this command is disabled, the Python's site module could not be " -"loaded." -msgstr "" -"E887: このコマンドは無効です、ごめんなさい。Python の site モジュールをロード" -"できませんでした。" +msgid "E952: Autocommand caused recursive behavior" +msgstr "E952: 自動コマンドが再帰を引き起こしました" -# Added at 07-Feb-2004. -msgid "E659: Cannot invoke Python recursively" -msgstr "E659: Python を再帰的に実行することはできません" +msgid "E328: Menu only exists in another mode" +msgstr "E328: メニューは他のモードにだけあります" -msgid "E837: This Vim cannot execute :py3 after using :python" -msgstr "E837: このVimでは :python を使った後に :py3 を使えません" +msgid "E813: Cannot close autocmd window" +msgstr "E813: autocmdウィンドウは閉じられません" -msgid "E265: $_ must be an instance of String" -msgstr "E265: $_ は文字列のインスタンスでなければなりません" +#, c-format +msgid "E686: Argument of %s must be a List" +msgstr "E686: %s の引数はリスト型でなければなりません" -msgid "" -"E266: Sorry, this command is disabled, the Ruby library could not be loaded." -msgstr "" -"E266: このコマンドは無効です、ごめんなさい: Rubyライブラリをロードできません" -"でした。" +msgid "E519: Option not supported" +msgstr "E519: オプションはサポートされていません" -msgid "E267: unexpected return" -msgstr "E267: 予期せぬ return です" +msgid "E856: Filename too long" +msgstr "E856: ファイル名が長過ぎます" -msgid "E268: unexpected next" -msgstr "E268: 予期せぬ next です" +msgid "E806: Using a Float as a String" +msgstr "E806: 浮動小数点数を文字列として扱っています" -msgid "E269: unexpected break" -msgstr "E269: 予期せぬ break です" +msgid "E788: Not allowed to edit another buffer now" +msgstr "E788: 現在は他のバッファを編集することは許されません" -msgid "E270: unexpected redo" -msgstr "E270: 予期せぬ redo です" +#, c-format +msgid "E1023: Using a Number as a Bool: %d" +msgstr "E1023: 数値をBoolとして扱っています: %d" -msgid "E271: retry outside of rescue clause" -msgstr "E271: rescue の外の retry です" +#, c-format +msgid "E1085: Not a callable type: %s" +msgstr "E1085: 呼出し可能な型ではありません: %s" -msgid "E272: unhandled exception" -msgstr "E272: 取り扱われなかった例外があります" +msgid "E855: Autocommands caused command to abort" +msgstr "E855: 自動コマンドがコマンドの停止を引き起こしました" #, c-format -msgid "E273: unknown longjmp status %d" -msgstr "E273: 未知のlongjmp状態: %d" +msgid "E5555: API call: %s" +msgstr "E5555: API の呼び出し: %s" -msgid "invalid buffer number" -msgstr "無効なバッファ番号です" +#, c-format +msgid "E5560: %s must not be called in a lua loop callback" +msgstr "E5560: %s は lua ループコールバックで呼び出されてはなりません" -msgid "not implemented yet" -msgstr "まだ実装されていません" +msgid "E5601: Cannot close window, only floating window would remain" +msgstr "" +"E5601: フローティングウィンドウしか残らないため、ウィンドウは閉じられません" -msgid "cannot set line(s)" -msgstr "行を設定できません" +#, fuzzy +#~ msgid "E5602: Cannot exchange or rotate float" +#~ msgstr "E5602: フローティングウィンドウを交換または回転できません" -msgid "invalid mark name" -msgstr "無効なマーク名です" +msgid "E1155: Cannot define autocommands for ALL events" +msgstr "E1155: 全てのイベントに対しての自動コマンドは定義できません" -msgid "mark not set" -msgstr "マークは設定されていません" +msgid "E1240: Resulting text too long" +msgstr "E1240: テキストが長くなりすぎました" -#, c-format -msgid "row %d column %d" -msgstr "行 %d 列 %d" +msgid "E1247: Line number out of range" +msgstr "E1247: 範囲外の行番号です" -msgid "cannot insert/append line" -msgstr "行の挿入/追加をできません" +msgid "E5248: Invalid character in group name" +msgstr "E5248: グループ名に不正な文字があります" -msgid "line number out of range" -msgstr "範囲外の行番号です" +msgid "E1249: Highlight group name too long" +msgstr "E1249: ハイライトグループ名が長すぎます" -msgid "unknown flag: " -msgstr "未知のフラグ: " +#, c-format +msgid "E964: Invalid column number: %ld" +msgstr "E964: 無効な列番号です: %ld" -msgid "unknown vimOption" -msgstr "未知の vimOption です" +#, c-format +msgid "E966: Invalid line number: %ld" +msgstr "E966: 無効な行番号です: %ld" -msgid "keyboard interrupt" -msgstr "キーボード割込み" +#, c-format +msgid "E1278: Stray '}' without a matching '{': %s" +msgstr "E1278: 対応する '{' がないはぐれた '}' です: %s" -msgid "cannot create buffer/window command: object is being deleted" -msgstr "" -"バッファ/ウィンドウ作成コマンドを作成できません: オブジェクトが消去されていま" -"した" +#, c-format +msgid "E1279: Missing '}': %s" +msgstr "E1279: '}' がありません: %s" -msgid "" -"cannot register callback command: buffer/window is already being deleted" -msgstr "" -"コールバックコマンドを登録できません: バッファ/ウィンドウが既に消去されました" +#, c-format +msgid "E1510: Value too large: %s" +msgstr "E1510: 値が大き過ぎます: %s" -msgid "" -"E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim." -"org" +msgid "E5767: Cannot use :undo! to redo or move to a different undo branch" msgstr "" -"E280: TCL 致命的エラー: reflist 汚染!? vim-dev@vim.org に報告してください" +"E5767: :undo! を使用して redo したり、別の undo ブランチに移動したりすること" +"はできません" -msgid "cannot register callback command: buffer/window reference not found" -msgstr "" -"コールバックコマンドを登録できません: バッファ/ウィンドウの参照が見つかりませ" -"ん" +msgid "E1513: Cannot switch buffer. 'winfixbuf' is enabled" +msgstr "E1513: バッファを切り替えられません。'winfixbuf' が有効になっています" -msgid "" -"E571: Sorry, this command is disabled: the Tcl library could not be loaded." -msgstr "" -"E571: このコマンドは無効です、ごめんなさい: Tclライブラリをロードできませんで" -"した。" +#, fuzzy, c-format +#~ msgid "E5570: Cannot update trust file: %s" +#~ msgstr "E5570: 信頼されたファイルは更新できません: %s" #, c-format -msgid "E572: exit code %d" -msgstr "E572: 終了コード %d" +msgid "E355: Unknown option: %s" +msgstr "E355: 未知のオプションです: %s" -msgid "cannot get line" -msgstr "行を取得できません" +msgid "search hit TOP, continuing at BOTTOM" +msgstr "上まで検索したので下に戻ります" -msgid "Unable to register a command server name" -msgstr "命令サーバーの名前を登録できません" +msgid "search hit BOTTOM, continuing at TOP" +msgstr "下まで検索したので上に戻ります" -msgid "E248: Failed to send command to the destination program" -msgstr "E248: 目的のプログラムへのコマンド送信に失敗しました" +msgid " line " +msgstr " 行 " #, c-format -msgid "E573: Invalid server id used: %s" -msgstr "E573: 無効なサーバーIDが使われました: %s" +msgid "E685: Internal error: hash_add(): duplicate key \"%s\"" +msgstr "E685: 内部エラーです: hash_add(): \"%s\" キーが重複しています" -msgid "E251: VIM instance registry property is badly formed. Deleted!" -msgstr "E251: VIM 実体の登録プロパティが不正です. 消去しました!" +msgid "E478: Don't panic!" +msgstr "E478: 慌てないでください!" #, c-format -msgid "E938: Duplicate key in JSON: \"%s\"" -msgstr "E938: JSONに重複キーがあります: \"%s\"" +msgid "E661: Sorry, no '%s' help for %s" +msgstr "E661: 残念ですが '%s' のヘルプが %s にはありません" #, c-format -msgid "E899: Argument of %s must be a List or Blob" -msgstr "E899: %s の引数はリスト型またはBlob型でなければなりません" +msgid "E149: Sorry, no help for %s" +msgstr "E149: 残念ですが %s にはヘルプがありません" #, c-format -msgid "E696: Missing comma in List: %s" -msgstr "E696: リスト型にカンマがありません: %s" +msgid "Sorry, help file \"%s\" not found" +msgstr "残念ですがヘルプファイル \"%s\" が見つかりません" #, c-format -msgid "E697: Missing end of List ']': %s" -msgstr "E697: リスト型の最後に ']' がありません: %s" +msgid "E151: No match: %s" +msgstr "E151: マッチはありません: %s" -msgid "Unknown option argument" -msgstr "未知のオプション引数です" +#, c-format +msgid "E152: Cannot open %s for writing" +msgstr "E152: 書込み用に %s を開けません" -msgid "Too many edit arguments" -msgstr "編集引数が多過ぎます" +#, c-format +msgid "E153: Unable to open %s for reading" +msgstr "E153: 読込み用に %s を開けません" -msgid "Argument missing after" -msgstr "引数がありません" +#, c-format +msgid "E670: Mix of help file encodings within a language: %s" +msgstr "E670: 1つの言語のヘルプファイルに複数のエンコードが混在しています: %s" -msgid "Garbage after option argument" -msgstr "オプション引数の後にゴミがあります" +#, c-format +msgid "E154: Duplicate tag \"%s\" in file %s/%s" +msgstr "E154: タグ \"%s\" がファイル %s/%s に重複しています" -msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments" -msgstr "\"+command\", \"-c command\", \"--cmd command\" の引数が多過ぎます" +#, c-format +msgid "E150: Not a directory: %s" +msgstr "E150: ディレクトリではありません: %s" -msgid "Invalid argument for" -msgstr "無効な引数です: " +msgid "E424: Too many different highlighting attributes in use" +msgstr "E424: 多くの異なるハイライト属性が使われ過ぎています" #, c-format -msgid "%d files to edit\n" -msgstr "%d 個のファイルが編集を控えています\n" - -msgid "netbeans is not supported with this GUI\n" -msgstr "netbeans はこのGUIでは利用できません\n" +msgid "E411: Highlight group not found: %s" +msgstr "E411: ハイライトグループが見つかりません: %s" -msgid "'-nb' cannot be used: not enabled at compile time\n" -msgstr "'-nb' 使用不可能です: コンパイル時に無効にされています\n" +msgid "E414: Group has settings, highlight link ignored" +msgstr "E414: グループが設定されているのでハイライトリンクは無視されます" -msgid "This Vim was not compiled with the diff feature." -msgstr "このVimにはdiff機能がありません(コンパイル時設定)。" +#, c-format +msgid "E415: Unexpected equal sign: %s" +msgstr "E415: 予期せぬ等号です: %s" -msgid "Attempt to open script file again: \"" -msgstr "スクリプトファイルを再び開こうとしました: \"" +#, c-format +msgid "E416: Missing equal sign: %s" +msgstr "E416: 等号がありません: %s" -msgid "Cannot open for reading: \"" -msgstr "読込用として開けません" +#, c-format +msgid "E417: Missing argument: %s" +msgstr "E417: 引数がありません: %s" -msgid "Cannot open for script output: \"" -msgstr "スクリプト出力用を開けません" +#, c-format +msgid "E412: Not enough arguments: \":highlight link %s\"" +msgstr "E412: 引数が充分ではない: \":highlight link %s\"" -msgid "Vim: Error: Failure to start gvim from NetBeans\n" -msgstr "Vim: エラー: NetBeansからgvimをスタートできません\n" +#, c-format +msgid "E413: Too many arguments: \":highlight link %s\"" +msgstr "E413: 引数が多過ぎます: \":highlight link %s\"" -msgid "Vim: Error: This version of Vim does not run in a Cygwin terminal\n" -msgstr "Vim: エラー: このバージョンのVimはCygwin端末では動作しません\n" +msgid "E423: Illegal argument" +msgstr "E423: 不正な引数です" -msgid "Vim: Warning: Output is not to a terminal\n" -msgstr "Vim: 警告: 端末への出力ではありません\n" +#, c-format +msgid "E418: Illegal value: %s" +msgstr "E418: 不正な値です: %s" -msgid "Vim: Warning: Input is not from a terminal\n" -msgstr "Vim: 警告: 端末からの入力ではありません\n" +msgid "E419: FG color unknown" +msgstr "E419: 未知の前景色です" -msgid "pre-vimrc command line" -msgstr "vimrc前のコマンドライン" +msgid "E420: BG color unknown" +msgstr "E420: 未知の背景色です" #, c-format -msgid "E282: Cannot read from \"%s\"" -msgstr "E282: \"%s\"から読込むことができません" +msgid "E421: Color name or number not recognized: %s" +msgstr "E421: カラー名や番号を認識できません: %s" -msgid "" -"\n" -"More info with: \"vim -h\"\n" -msgstr "" -"\n" -"より詳細な情報は: \"vim -h\"\n" +#, c-format +msgid "E423: Illegal argument: %s" +msgstr "E423: 不正な引数です: %s" -msgid "[file ..] edit specified file(s)" -msgstr "[ファイル..] あるファイルを編集する" +msgid "E669: Unprintable character in group name" +msgstr "E669: グループ名に印刷不可能な文字があります" -msgid "- read text from stdin" -msgstr "- 標準入力からテキストを読込む" +msgid "E849: Too many highlight and syntax groups" +msgstr "E849: ハイライトと構文グループが多過ぎます" -msgid "-t tag edit file where tag is defined" -msgstr "-t タグ タグが定義されたところから編集する" +msgid "Type number and <Enter> or click with the mouse (q or empty cancels): " +msgstr "" +"番号と<Enter>を入力するかマウスでクリックしてください (q か空でキャンセル): " -msgid "-q [errorfile] edit file with first error" -msgstr "-q [errorfile] 最初のエラーで編集する" +msgid "Type number and <Enter> (q or empty cancels): " +msgstr "番号と<Enter>を入力してください (q か空でキャンセル): " -msgid "" -"\n" -"\n" -"Usage:" -msgstr "" -"\n" -"\n" -"使用法:" +msgid " Keyword completion (^N^P)" +msgstr " キーワード補完 (^N^P)" -msgid " vim [arguments] " -msgstr " vim [引数] " +msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" +msgstr " ^X モード (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" -msgid "" -"\n" -" or:" -msgstr "" -"\n" -" もしくは:" +msgid " Whole line completion (^L^N^P)" +msgstr " 行(全体)補完 (^L^N^P)" -msgid "" -"\n" -"Where case is ignored prepend / to make flag upper case" -msgstr "" -"\n" -"大小文字が無視される場合は大文字にするために / を前置してください" +msgid " File name completion (^F^N^P)" +msgstr " ファイル名補完 (^F^N^P)" -msgid "" -"\n" -"\n" -"Arguments:\n" -msgstr "" -"\n" -"\n" -"引数:\n" +msgid " Tag completion (^]^N^P)" +msgstr " タグ補完 (^]^N^P)" -msgid "--\t\t\tOnly file names after this" -msgstr "--\t\t\tこのあとにはファイル名だけ" +msgid " Path pattern completion (^N^P)" +msgstr " パスパターン補完 (^N^P)" -msgid "--literal\t\tDon't expand wildcards" -msgstr "--literal\t\tワイルドカードを展開しない" +msgid " Definition completion (^D^N^P)" +msgstr " 定義補完 (^D^N^P)" -msgid "-register\t\tRegister this gvim for OLE" -msgstr "-register\t\tこのgvimをOLEとして登録する" +msgid " Dictionary completion (^K^N^P)" +msgstr " 辞書補完 (^K^N^P)" -msgid "-unregister\t\tUnregister gvim for OLE" -msgstr "-unregister\t\tgvimのOLE登録を解除する" +msgid " Thesaurus completion (^T^N^P)" +msgstr " シソーラス補完 (^T^N^P)" -msgid "-g\t\t\tRun using GUI (like \"gvim\")" -msgstr "-g\t\t\tGUIで起動する (\"gvim\" と同じ)" +msgid " Command-line completion (^V^N^P)" +msgstr " コマンドライン補完 (^V^N^P)" -msgid "-f or --nofork\tForeground: Don't fork when starting GUI" -msgstr "-f or --nofork\tフォアグラウンド: GUIを始めるときにforkしない" +msgid " User defined completion (^U^N^P)" +msgstr " ユーザー定義補完 (^U^N^P)" -msgid "-v\t\t\tVi mode (like \"vi\")" -msgstr "-v\t\t\tViモード (\"vi\" と同じ)" +msgid " Omni completion (^O^N^P)" +msgstr " オムニ補完 (^O^N^P)" -msgid "-e\t\t\tEx mode (like \"ex\")" -msgstr "-e\t\t\tExモード (\"ex\" と同じ)" +msgid " Spelling suggestion (s^N^P)" +msgstr " 綴り修正候補 (s^N^P)" -msgid "-E\t\t\tImproved Ex mode" -msgstr "-E\t\t\t改良Exモード" +msgid " Keyword Local completion (^N^P)" +msgstr " 局所キーワード補完 (^N^P)" -msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")" -msgstr "-s\t\t\tサイレント(バッチ)モード (\"ex\" 専用)" +msgid "Hit end of paragraph" +msgstr "段落の最後にヒット" -msgid "-d\t\t\tDiff mode (like \"vimdiff\")" -msgstr "-d\t\t\t差分モード (\"vidiff\" と同じ)" +msgid "E840: Completion function deleted text" +msgstr "E840: 補完関数がテキストを削除しました" -msgid "-y\t\t\tEasy mode (like \"evim\", modeless)" -msgstr "-y\t\t\tイージーモード (\"evim\" と同じ、モード無)" +msgid "'dictionary' option is empty" +msgstr "'dictionary' オプションが空です" -msgid "-R\t\t\tReadonly mode (like \"view\")" -msgstr "-R\t\t\t読込専用モード (\"view\" と同じ)" +msgid "'thesaurus' option is empty" +msgstr "'thesaurus' オプションが空です" -msgid "-m\t\t\tModifications (writing files) not allowed" -msgstr "-m\t\t\t変更 (ファイル保存時) をできないようにする" +#, c-format +msgid "Scanning dictionary: %s" +msgstr "辞書をスキャン中: %s" -msgid "-M\t\t\tModifications in text not allowed" -msgstr "-M\t\t\tテキストの編集を行なえないようにする" +msgid " (insert) Scroll (^E/^Y)" +msgstr " (挿入) スクロール(^E/^Y)" -msgid "-b\t\t\tBinary mode" -msgstr "-b\t\t\tバイナリモード" +msgid " (replace) Scroll (^E/^Y)" +msgstr " (置換) スクロール (^E/^Y)" -msgid "-l\t\t\tLisp mode" -msgstr "-l\t\t\tLispモード" +msgid "E785: complete() can only be used in Insert mode" +msgstr "E785: complete() は挿入モードでしか利用できません" -msgid "-C\t\t\tCompatible with Vi: 'compatible'" -msgstr "-C\t\t\tVi互換モード: 'compatible'" +#, c-format +msgid "Scanning: %s" +msgstr "スキャン中: %s" -msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'" -msgstr "-N\t\t\tVi非互換モード: 'nocompatible" +msgid "Scanning tags." +msgstr "タグをスキャン中。" -msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]" -msgstr "-V[N][fname]\t\tログ出力設定 [レベル N] [ログファイル名 fname]" +msgid "match in file" +msgstr "ファイル内のマッチ" -msgid "-D\t\t\tDebugging mode" -msgstr "-D\t\t\tデバッグモード" +msgid " Adding" +msgstr " 追加中" -msgid "-n\t\t\tNo swap file, use memory only" -msgstr "-n\t\t\tスワップファイルを使用せずメモリだけ" +msgid "-- Searching..." +msgstr "-- 検索中..." -msgid "-r\t\t\tList swap files and exit" -msgstr "-r\t\t\tスワップファイルを列挙し終了" +msgid "Back at original" +msgstr "始めに戻る" -msgid "-r (with file name)\tRecover crashed session" -msgstr "-r (ファイル名)\tクラッシュしたセッションを復帰" +msgid "Word from other line" +msgstr "他の行の単語" -msgid "-L\t\t\tSame as -r" -msgstr "-L\t\t\t-rと同じ" +msgid "The only match" +msgstr "唯一の該当" -msgid "-f\t\t\tDon't use newcli to open window" -msgstr "-f\t\t\tウィンドウを開くのに newcli を使用しない" +#, c-format +msgid "match %d of %d" +msgstr "%d 番目の該当 (全該当 %d 個中)" -msgid "-dev <device>\t\tUse <device> for I/O" -msgstr "-dev <device>\t\tI/Oに <device> を使用する" +#, c-format +msgid "match %d" +msgstr "%d 番目の該当" -msgid "-A\t\t\tStart in Arabic mode" -msgstr "-A\t\t\tアラビア語モードで起動する" +#, c-format +msgid "E1502: Lua failed to grow stack to %i" +msgstr "E1502: Lua はスタックを %i に増やすことができませんでした" -msgid "-H\t\t\tStart in Hebrew mode" -msgstr "-H\t\t\tヘブライ語モードで起動する" +msgid "" +"E5100: Cannot convert given lua table: table should contain either only " +"integer keys or only string keys" +msgstr "" +"E5100: 指定された lua テーブルを変換できません: テーブルには整数キーのみまた" +"は文字列キーのみが含まれている必要があります" -msgid "-F\t\t\tStart in Farsi mode" -msgstr "-F\t\t\tペルシア語モードで起動する" +msgid "E5101: Cannot convert given lua type" +msgstr "E5101: 指定された lua タイプを変換できません" -msgid "-T <terminal>\tSet terminal type to <terminal>" -msgstr "-T <terminal>\t端末を <terminal> に設定する" +#, c-format +msgid "E5102: Lua failed to grow stack to %i" +msgstr "E5102: Lua はスタックを %i に増やすことができませんでした" -msgid "--not-a-term\t\tSkip warning for input/output not being a terminal" -msgstr "--not-a-term\t\t入出力が端末でないとの警告をスキップする" +#, c-format +msgid "Error executing vim.schedule lua callback: %.*s" +msgstr "vim.schedule lua コールバックの実行中にエラーが発生しました: %.*s" -msgid "--ttyfail\t\tExit if input or output is not a terminal" -msgstr "--ttyfail\t\t入出力が端末でなければ終了する" +#, c-format +msgid "E970: Failed to initialize lua interpreter\n" +msgstr "E970: lua インタープリターの初期化に失敗しました\n" -msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" -msgstr "-u <vimrc>\t\t.vimrcの代わりに <vimrc> を使う" +#, c-format +msgid "E970: Failed to initialize builtin lua modules\n" +msgstr "E970: 組み込み lua モジュールの初期化に失敗しました\n" -msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc" -msgstr "-U <gvimrc>\t\t.gvimrcの代わりに <gvimrc> を使う" +#, fuzzy, c-format +#~ msgid "E5114: Error while converting print argument #%i: %.*s" +#~ msgstr "E5114: 出力引数 #%i の変換中にエラーが発生しました: %.*s" -msgid "--noplugin\t\tDon't load plugin scripts" -msgstr "--noplugin\t\tプラグインスクリプトをロードしない" +#, fuzzy, c-format +#~ msgid "E5115: Error while loading debug string: %.*s" +#~ msgstr "E5115: デバッグ文字列の読み込み中にエラーが発生しました: %.*s" -msgid "-p[N]\t\tOpen N tab pages (default: one for each file)" -msgstr "-p[N]\t\tN 個タブページを開く(省略値: ファイルにつき1個)" +#, fuzzy, c-format +#~ msgid "E5116: Error while calling debug string: %.*s" +#~ msgstr "E5116: デバッグ文字列の呼び出し中にエラーが発生しました: %.*s" -msgid "-o[N]\t\tOpen N windows (default: one for each file)" -msgstr "-o[N]\t\tN 個ウィンドウを開く(省略値: ファイルにつき1個)" +#, c-format +msgid "E5108: Error executing Lua function: %.*s" +msgstr "E5108: Lua 関数の実行中にエラーが発生しました: %.*s" -msgid "-O[N]\t\tLike -o but split vertically" -msgstr "-O[N]\t\t-oと同じだが垂直分割" +#, c-format +msgid "E5107: Error loading lua %.*s" +msgstr "E5107: lua %.*s の読み込み中にエラーが発生しました" -msgid "+\t\t\tStart at end of file" -msgstr "+\t\t\tファイルの最後からはじめる" +#, c-format +msgid "E5108: Error executing lua %.*s" +msgstr "E5108: lua %.*s の実行中にエラーが発生しました" -msgid "+<lnum>\t\tStart at line <lnum>" -msgstr "+<lnum>\t\t<lnum> 行からはじめる" +#, c-format +msgid "Error executing lua callback: %.*s" +msgstr "lua コールバックの実行中にエラーが発生しました: %.*s" -msgid "--cmd <command>\tExecute <command> before loading any vimrc file" -msgstr "--cmd <command>\tvimrcをロードする前に <command> を実行する" +msgid "cannot save undo information" +msgstr "アンドゥ情報が保存できません" -msgid "-c <command>\t\tExecute <command> after loading the first file" -msgstr "-c <command>\t\t最初のファイルをロード後 <command> を実行する" +#, c-format +msgid "E5109: Error loading lua: %.*s" +msgstr "E5109: lua の読み込み中にエラーが発生しました: %.*s" -msgid "-S <session>\t\tSource file <session> after loading the first file" -msgstr "-S <session>\t\t最初のファイルをロード後ファイル <session> を取込む" +#, c-format +msgid "E5110: Error executing lua: %.*s" +msgstr "E5110: lua の実行中にエラーが発生しました: %.*s" -msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>" -msgstr "-s <scriptin>\tファイル <scriptin> からノーマルコマンドを読込む" +#, c-format +msgid "E5111: Error calling lua: %.*s" +msgstr "E5111: lua の呼び出し中にエラーが発生しました: %.*s" -msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>" -msgstr "-w <scriptout>\t入力した全コマンドをファイル <scriptout> に追加する" +#, c-format +msgid "E5112: Error while creating lua chunk: %.*s" +msgstr "E5112: lua チャンクの作成中にエラーが発生しました: %.*s" -msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>" -msgstr "-W <scriptout>\t入力した全コマンドをファイル <scriptout> に保存する" +#, c-format +msgid "E5113: Error while calling lua chunk: %.*s" +msgstr "E5113: lua チャンクの呼び出し中にエラーが発生しました: %.*s" -msgid "-x\t\t\tEdit encrypted files" -msgstr "-x\t\t\t暗号化されたファイルを編集する" +#, c-format +msgid "Error executing vim._expand_pat: %.*s" +msgstr "vim._expand_pat の実行中にエラーが発生しました: %.*s" -msgid "-display <display>\tConnect Vim to this particular X-server" -msgstr "-display <display>\tvimを指定した X サーバーに接続する" +#, c-format +msgid "Error executing vim.on_key Lua callback: %.*s" +msgstr "vim.on_key Lua コールバックの実行中にエラーが発生しました: %.*s" -msgid "-X\t\t\tDo not connect to X server" -msgstr "-X\t\t\tXサーバーに接続しない" +#, c-format +msgid "Error executing Lua callback: %.*s" +msgstr "Lua コールバックの実行中にエラーが発生しました: %.*s" -msgid "--remote <files>\tEdit <files> in a Vim server if possible" -msgstr "--remote <files>\t可能ならばVimサーバーで <files> を編集する" +#, c-format +msgid "Error executing vim.secure.read: %.*s" +msgstr "vim.secure.read の実行中にエラーが発生しました: %.*s" -msgid "--remote-silent <files> Same, don't complain if there is no server" -msgstr "--remote-silent <files> 同上、サーバーが無くても警告文を出力しない" +#, c-format +msgid "Error executing vim.secure.trust: %.*s" +msgstr "vim.secure.trust の実行中にエラーが発生しました: %.*s" -msgid "" -"--remote-wait <files> As --remote but wait for files to have been edited" -msgstr "--remote-wait <files>\t--remote後 ファイルの編集が終わるのを待つ" +msgid "Argument missing after" +msgstr "引数がありません" -msgid "" -"--remote-wait-silent <files> Same, don't complain if there is no server" -msgstr "" -"--remote-wait-silent <files> 同上、サーバーが無くても警告文を出力しない" +msgid "Garbage after option argument" +msgstr "オプション引数の後にゴミがあります" -msgid "" -"--remote-tab[-wait][-silent] <files> As --remote but use tab page per file" -msgstr "" -"--remote-tab[-wait][-silent] <files> --remoteでファイル1つにつき1つのタブ" -"ページを開く" +msgid "Unknown option argument" +msgstr "未知のオプション引数です" -msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit" -msgstr "--remote-send <keys>\tVimサーバーに <keys> を送信して終了する" +msgid "Too many edit arguments" +msgstr "編集引数が多過ぎます" -msgid "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result" -msgstr "--remote-expr <expr>\tサーバーで <expr> を実行して結果を表示する" +msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments" +msgstr "\"+command\", \"-c command\", \"--cmd command\" の引数が多過ぎます" -msgid "--serverlist\t\tList available Vim server names and exit" -msgstr "--serverlist\t\tVimサーバー名の一覧を表示して終了する" +#, c-format +msgid "Cannot open for script output: \"" +msgstr "スクリプト出力用として開けません: \"" -msgid "--servername <name>\tSend to/become the Vim server <name>" -msgstr "--servername <name>\tVimサーバー <name> に送信/名前設定する" +#, c-format +msgid "E5421: Failed to open stdin: %s" +msgstr "E5421: 標準入力を開けませんでした: %s" -msgid "--startuptime <file>\tWrite startup timing messages to <file>" -msgstr "--startuptime <file>\t起動にかかった時間の詳細を <file> へ出力する" +#, c-format +msgid "Attempt to open script file again: \"%s %s\"\n" +msgstr "スクリプトファイルを再び開こうとしました: \"%s %s\"\n" -msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo" -msgstr "-i <viminfo>\t\t.viminfoの代わりに <viminfo> を使う" +msgid "--embed conflicts with -es/-Es/-l" +msgstr "--embed が -es/-Es/-l と競合しています" -msgid "--clean\t\t'nocompatible', Vim defaults, no plugins, no viminfo" -msgstr "--clean\t\t'nocompatible'、Vimの既定、プラグインなし、viminfoなし" +msgid "pre-vimrc command line" +msgstr "vimrc前のコマンドライン" -msgid "-h or --help\tPrint Help (this message) and exit" -msgstr "-h or --help\tヘルプ(このメッセージ)を表示し終了する" +#, c-format +msgid "E5422: Conflicting configs: \"%s\" \"%s\"" +msgstr "E5422: 設定が競合しています: \"%s\" \"%s\"" -msgid "--version\t\tPrint version information and exit" -msgstr "--version\t\tバージョン情報を表示し終了する" +#, c-format +msgid "E282: Cannot read from \"%s\"" +msgstr "E282: \"%s\" から読込むことができません" +#, c-format msgid "" "\n" -"Arguments recognised by gvim (Motif version):\n" +"More info with \"" msgstr "" "\n" -"gvimによって解釈される引数(Motifバージョン):\n" +"より詳細な情報は: \"" -msgid "" -"\n" -"Arguments recognised by gvim (neXtaw version):\n" -msgstr "" -"\n" -"gvimによって解釈される引数(neXtawバージョン):\n" +#, c-format +msgid "Usage:\n" +msgstr "使用法:\n" +#, c-format +msgid " nvim [options] [file ...]\n" +msgstr " nvim [オプション] [ファイル...]\n" + +#, c-format msgid "" "\n" -"Arguments recognised by gvim (Athena version):\n" +"Options:\n" msgstr "" "\n" -"gvimによって解釈される引数(Athenaバージョン):\n" - -msgid "-display <display>\tRun Vim on <display>" -msgstr "-display <display>\t<display> でvimを実行する" - -msgid "-iconic\t\tStart Vim iconified" -msgstr "-iconic\t\t最小化した状態でvimを起動する" +"オプション:\n" -msgid "-background <color>\tUse <color> for the background (also: -bg)" -msgstr "-background <color>\t背景色に <color> を使う(同義: -bg)" +#, c-format +msgid " --cmd <cmd> Execute <cmd> before any config\n" +msgstr " --cmd <cmd> 設定をロードする前に <cmd> を実行する\n" -msgid "-foreground <color>\tUse <color> for normal text (also: -fg)" -msgstr "-foreground <color>\t前景色に <color> を使う(同義: -fg)" +#, c-format +msgid " +<cmd>, -c <cmd> Execute <cmd> after config and first file\n" +msgstr "" +" +<cmd>, -c <cmd> 最初のファイルと設定をロード後 <cmd> を実行する\n" -msgid "-font <font>\t\tUse <font> for normal text (also: -fn)" -msgstr "-font <font>\t\tテキスト表示に <font> を使う(同義: -fn)" +#, c-format +msgid " -l <script> [args...] Execute Lua <script> (with optional args)\n" +msgstr " -l <script> [引数...] Lua <script> を実行する (任意で引数を指定)\n" -msgid "-boldfont <font>\tUse <font> for bold text" -msgstr "-boldfont <font>\t太字に <font> を使う" +#, c-format +msgid " -S <session> Source <session> after loading the first file\n" +msgstr " -S <session> 最初のファイルをロード後 <session> を取込む\n" -msgid "-italicfont <font>\tUse <font> for italic text" -msgstr "-italicfont <for>\t斜体字に <font> を使う" +#, c-format +msgid " -s <scriptin> Read Normal mode commands from <scriptin>\n" +msgstr " -s <scriptin> <scriptin> からノーマルコマンドを読込む\n" -msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)" -msgstr "-geometry <geom>\t初期配置に <geom> を使う(同義: -geom)" +#, c-format +msgid " -u <config> Use this config file\n" +msgstr " -u <config> この設定ファイルを使用する\n" -msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)" -msgstr "-borderwidth <width>\t境界の幅を <width> にする(同義: -bw)" +#, c-format +msgid " -d Diff mode\n" +msgstr " -d 差分モード\n" -msgid "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)" -msgstr "" -"-scrollbarwidth <width> スクロールバーの幅を <width> にする(同義: -sw)" +#, c-format +msgid " -es, -Es Silent (batch) mode\n" +msgstr " -es, -Es サイレント(バッチ)モード\n" -msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)" -msgstr "-menuheight <height>\tメニューバーの高さを <height> にする(同義: -mh)" +#, c-format +msgid " -h, --help Print this help message\n" +msgstr " -h, --help このヘルプメッセージを表示する\n" -msgid "-reverse\t\tUse reverse video (also: -rv)" -msgstr "-reverse\t\t反転映像を使用する(同義: -rv)" +#, c-format +msgid " -i <shada> Use this shada file\n" +msgstr " -i <shada> この ShaDa ファイルを使用する\n" -msgid "+reverse\t\tDon't use reverse video (also: +rv)" -msgstr "+reverse\t\t反転映像を使用しない(同義: +rv)" +#, c-format +msgid " -n No swap file, use memory only\n" +msgstr " -n スワップファイルを使用せずメモリだけ\n" -msgid "-xrm <resource>\tSet the specified resource" -msgstr "-xrm <resource>\t特定のリソースを使用する" +#, c-format +msgid " -o[N] Open N windows (default: one per file)\n" +msgstr "" +" -o[N] N 個ウィンドウを開く(既定値: ファイルにつき1個)\n" +#, c-format msgid "" -"\n" -"Arguments recognised by gvim (GTK+ version):\n" +" -O[N] Open N vertical windows (default: one per file)\n" msgstr "" -"\n" -"gvimによって解釈される引数(GTK+バージョン):\n" +" -O[N] N 個垂直ウィンドウを開く(既定値: ファイルにつき1個)\n" -msgid "-display <display>\tRun Vim on <display> (also: --display)" -msgstr "-display <display>\t<display> でVimを実行する(同義: --display)" +#, c-format +msgid " -p[N] Open N tab pages (default: one per file)\n" +msgstr "" +" -p[N] N 個タブページを開く(既定値: ファイルにつき1個)\n" -msgid "--role <role>\tSet a unique role to identify the main window" -msgstr "--role <role>\tメインウィンドウを識別する一意な役割(role)を設定する" +#, c-format +msgid " -R Read-only (view) mode\n" +msgstr " -R 読込専用 (view) モード\n" -msgid "--socketid <xid>\tOpen Vim inside another GTK widget" -msgstr "--socketid <xid>\t異なるGTK widgetでVimを開く" +#, c-format +msgid " -v, --version Print version information\n" +msgstr " -v, --version バージョン情報を表示する\n" -msgid "--echo-wid\t\tMake gvim echo the Window ID on stdout" -msgstr "--echo-wid\t\tウィンドウIDを標準出力に出力する" +#, c-format +msgid " -V[N][file] Verbose [level][file]\n" +msgstr " -V[N][ファイル] ログ出力設定 [レベル] [ファイル]\n" -msgid "-P <parent title>\tOpen Vim inside parent application" -msgstr "-P <親のタイトル>\tVimを親アプリケーションの中で起動する" +#, c-format +msgid " -- Only file names after this\n" +msgstr " -- このあとにはファイル名だけ\n" -msgid "--windowid <HWND>\tOpen Vim inside another win32 widget" -msgstr "--windowid <HWND>\t異なるWin32 widgetの内部にVimを開く" +#, c-format +msgid " --api-info Write msgpack-encoded API metadata to stdout\n" +msgstr "" +" --api-info 標準出力にmsgpackにエンコードされたAPIメタデータを出" +"力\n" -msgid "No display" -msgstr "ディスプレイが見つかりません" +#, c-format +msgid "" +" --clean \"Factory defaults\" (skip user config and plugins, " +"shada)\n" +msgstr "" +" --clean \"工場出荷状態\" (設定とプラグイン、shadaをスキッ" +"プ)\n" + +#, c-format +msgid " --embed Use stdin/stdout as a msgpack-rpc channel\n" +msgstr " --embed msgpack-rpc チャネルに標準入出力を使用する\n" + +#, c-format +msgid " --headless Don't start a user interface\n" +msgstr " --headless ユーザーインターフェースを起動しない\n" -msgid ": Send failed.\n" -msgstr ": 送信に失敗しました.\n" +#, c-format +msgid " --listen <address> Serve RPC API from this address\n" +msgstr " --listen <address> このアドレスから RPC API を受け取る\n" -msgid ": Send failed. Trying to execute locally\n" -msgstr ": 送信に失敗しました。ローカルでの実行を試みています\n" +#, c-format +msgid " --remote[-subcommand] Execute commands remotely on a server\n" +msgstr " --remote[-subcommand] サーバー上でコマンドをリモート実行する\n" #, c-format -msgid "%d of %d edited" -msgstr "%d 個 (%d 個中) のファイルを編集しました" +msgid " --server <address> Specify RPC server to send commands to\n" +msgstr " --server <address> コマンドを送信する RPC サーバーを指定する\n" -msgid "No display: Send expression failed.\n" -msgstr "ディスプレイがありません: 式の送信に失敗しました.\n" +#, c-format +msgid " --startuptime <file> Write startup timing messages to <file>\n" +msgstr " --startuptime <file> 起動にかかった時間の詳細を <file> へ出力する\n" -msgid ": Send expression failed.\n" -msgstr ": 式の送信に失敗しました.\n" +#, c-format +msgid "" +"\n" +"See \":help startup-options\" for all options.\n" +msgstr "" +"\n" +"すべてのオプションについては ':help startup-options' を参照してください。\n" #, c-format -msgid "E224: global abbreviation already exists for %s" +msgid "E224: Global abbreviation already exists for %s" msgstr "E224: %s というグローバル短縮入力は既に存在します" #, c-format -msgid "E225: global mapping already exists for %s" +msgid "E225: Global mapping already exists for %s" msgstr "E225: %s というグローバルマッピングは既に存在します" #, c-format -msgid "E226: abbreviation already exists for %s" +msgid "E226: Abbreviation already exists for %s" msgstr "E226: %s という短縮入力は既に存在します" #, c-format -msgid "E227: mapping already exists for %s" +msgid "E227: Mapping already exists for %s" msgstr "E227: %s というマッピングは既に存在します" +msgid "E460: Entries missing in mapset() dict argument" +msgstr "E460: mapset() の辞書引数の要素が足りません" + +#, c-format +msgid "E1276: Illegal map mode string: '%s'" +msgstr "E1276: 不正なマップモード文字列です: '%s'" + msgid "No abbreviation found" msgstr "短縮入力は見つかりませんでした" @@ -2985,7 +4630,7 @@ msgid "No mapping found" msgstr "マッピングは見つかりませんでした" msgid "E228: makemap: Illegal mode" -msgstr "E228: makemap: 不正なモード" +msgstr "E228: makemap: 無効なモード" #, c-format msgid "E357: 'langmap': Matching character missing for %s" @@ -3021,58 +4666,76 @@ msgid "" "change line col text" msgstr "" "\n" -"変更 行 列 テキスト" +"変更 行 列 テキスト" -msgid "" -"\n" -"# File marks:\n" -msgstr "" -"\n" -"# ファイルマーク:\n" +#, c-format +msgid "E799: Invalid ID: %<PRId64> (must be greater than or equal to 1)" +msgstr "E799: 無効な ID: %<PRId64> (1 以上でなければなりません)" -msgid "" -"\n" -"# Jumplist (newest first):\n" -msgstr "" -"\n" -"# ジャンプリスト (新しいものが先):\n" +#, c-format +msgid "E801: ID already taken: %<PRId64>" +msgstr "E801: ID は既に利用中です: %<PRId64>" -msgid "" -"\n" -"# History of marks within files (newest to oldest):\n" -msgstr "" -"\n" -"# ファイル内マークの履歴 (新しいものから古いもの):\n" +#, c-format +msgid "E5030: Empty list at position %d" +msgstr "E5030: 位置 %d に空のリストがあります" + +#, c-format +msgid "E5031: List or number required at position %d" +msgstr "E5031: 位置 %d にはリストまたは数値が必要です" + +#, c-format +msgid "E802: Invalid ID: %<PRId64> (must be greater than or equal to 1)" +msgstr "E802: 無効な ID: %<PRId64> (1 以上でなければなりません)" -msgid "Missing '>'" -msgstr "'>' が見つかりません" +#, c-format +msgid "E803: ID not found: %<PRId64>" +msgstr "E803: ID はありません: %<PRId64>" -msgid "E543: Not a valid codepage" -msgstr "E543: 無効なコードページです" +#, c-format +msgid "E474: List item %d is either not a dictionary or an empty one" +msgstr "E474: リスト要素 %d は辞書でないか、空です。" -msgid "E284: Cannot set IC values" -msgstr "E284: ICの値を設定できません" +#, c-format +msgid "E474: List item %d is missing one of the required keys" +msgstr "E474: リスト要素 %d に必要なキーの 1 つがありません" -msgid "E285: Failed to create input context" -msgstr "E285: インプットコンテキストの作成に失敗しました" +#, c-format +msgid "E798: ID is reserved for \":match\": %<PRId64>" +msgstr "E798: ID は \":match\" のために予約されています: %<PRId64>" -msgid "E286: Failed to open input method" -msgstr "E286: インプットメソッドのオープンに失敗しました" +#, c-format +msgid "E798: ID is reserved for \"match\": %<PRId64>" +msgstr "E798: ID は \":match\" のために予約されています: %<PRId64>" -msgid "E287: Warning: Could not set destroy callback to IM" -msgstr "E287: 警告: IMの破壊コールバックを設定できませんでした" +#, c-format +msgid "E1109: List item %d is not a List" +msgstr "E1109: リストの要素 %d はリストではありません" -msgid "E288: input method doesn't support any style" -msgstr "E288: インプットメソッドはどんなスタイルもサポートしません" +#, c-format +msgid "E1110: List item %d does not contain 3 numbers" +msgstr "E1110: リストの要素 %d は数値を 3 個含んでいません" -msgid "E289: input method doesn't support my preedit type" -msgstr "E289: インプットメソッドは my preedit type をサポートしません" +#, c-format +msgid "E1111: List item %d range invalid" +msgstr "E1111: リストの要素 %d の範囲が不正です" -msgid "E293: block was not locked" +#, c-format +msgid "E1112: List item %d cell width invalid" +msgstr "E1112: リストの要素 %d のセル幅が不正です" + +#, c-format +msgid "E1113: Overlapping ranges for 0x%lx" +msgstr "E1113: 0x%lx の範囲が重複しています" + +msgid "E1114: Only values of 0x80 and higher supported" +msgstr "E1114: 0x80 以上の値しかサポートされていません" + +msgid "E293: Block was not locked" msgstr "E293: ブロックがロックされていません" msgid "E294: Seek error in swap file read" -msgstr "E294: スワップファイル読込時にシークエラーです" +msgstr "E294: スワップファイル読込み時にシークエラーです" msgid "E295: Read error in swap file" msgstr "E295: スワップファイルの読込みエラーです" @@ -3086,17 +4749,45 @@ msgstr "E297: スワップファイルの書込みエラーです" msgid "E300: Swap file already exists (symlink attack?)" msgstr "E300: スワップファイルが既に存在します (symlinkによる攻撃?)" +#, c-format +msgid "E315: ml_get: Invalid lnum: %<PRId64>" +msgstr "E315: ml_get: 無効な lnum: %<PRId64>" + +#, c-format +msgid "E316: ml_get: Cannot find line %<PRId64>in buffer %d %s" +msgstr "E316: ml_get: 行 %<PRId64> がバッファ %d %s に見つかりません" + +msgid "E317: Pointer block id wrong" +msgstr "E317: ポインタブロック ID が間違っています" + +msgid "E317: Pointer block id wrong 2" +msgstr "E317: ポインタブロック ID が間違っています 2" + +msgid "E317: Pointer block id wrong 3" +msgstr "E317: ポインタブロック ID が間違っています 3" + +msgid "E317: Pointer block id wrong 4" +msgstr "E317: ポインタブロック ID が間違っています 4" + +#, c-format +msgid "E322: Line number out of range: %<PRId64> past the end" +msgstr "E322: 行番号が範囲外です: %<PRId64> が末尾を超えています" + +#, c-format +msgid "E323: Line count wrong in block %<PRId64>" +msgstr "E323: ブロック %<PRId64> の行数が間違っています" + +msgid "E1364: Warning: Pointer block corrupted" +msgstr "E1364: 警告: ポインタブロックが壊れています" + msgid "E298: Didn't get block nr 0?" -msgstr "E298: ブロック 0 を取得できません?" +msgstr "E298: ブロック 0 を取得できませんでしたか?" msgid "E298: Didn't get block nr 1?" -msgstr "E298: ブロック 1 を取得できません?" +msgstr "E298: ブロック 1 を取得できませんでしたか?" msgid "E298: Didn't get block nr 2?" -msgstr "E298: ブロック 2 を取得できません?" - -msgid "E843: Error while updating swap file crypt" -msgstr "E843: スワップファイルの暗号を更新中にエラーが発生しました" +msgstr "E298: ブロック 2 を取得できませんでしたか?" msgid "E301: Oops, lost the swap file!!!" msgstr "E301: おっと、スワップファイルが失われました!!!" @@ -3109,7 +4800,7 @@ msgid "E303: Unable to open swap file for \"%s\", recovery impossible" msgstr "E303: \"%s\" のスワップファイルを開けないのでリカバリは不可能です" msgid "E304: ml_upd_block0(): Didn't get block 0??" -msgstr "E304: ml_upd_block0(): ブロック 0 を取得できませんでした??" +msgstr "E304: ml_upd_block0(): ブロック 0 を取得できませんでしたか??" #, c-format msgid "E305: No swap file found for %s" @@ -3155,12 +4846,6 @@ msgstr "" ",\n" "もしくはファイルが損傷しています。" -#, c-format -msgid "" -"E833: %s is encrypted and this version of Vim does not support encryption" -msgstr "" -"E833: %s はこのバージョンのVimでサポートしていない形式で暗号化されています" - msgid " has been damaged (page size is smaller than minimum value).\n" msgstr " は損傷しています (ページサイズが最小値を下回っています).\n" @@ -3176,38 +4861,6 @@ msgid "E308: Warning: Original file may have been changed" msgstr "E308: 警告: 原本ファイルが変更されています" #, c-format -msgid "Swap file is encrypted: \"%s\"" -msgstr "スワップファイルは暗号化されています: \"%s\"" - -msgid "" -"\n" -"If you entered a new crypt key but did not write the text file," -msgstr "" -"\n" -"新しい暗号キーを入力したあとにテキストファイルを保存していない場合は、" - -msgid "" -"\n" -"enter the new crypt key." -msgstr "" -"\n" -"新しい暗号キーを入力してください。" - -msgid "" -"\n" -"If you wrote the text file after changing the crypt key press enter" -msgstr "" -"\n" -"暗号キーを変えたあとにテキストファイルを保存した場合は、テキストファイルと" - -msgid "" -"\n" -"to use the same key for text file and swap file" -msgstr "" -"\n" -"スワップファイルに同じ暗号キーを使うためにenterだけを押してください。" - -#, c-format msgid "E309: Unable to read block 1 from %s" msgstr "E309: %s からブロック 1 を読込めません" @@ -3236,6 +4889,9 @@ msgstr "??? ここから ???END までの行が破壊されているようです msgid "??? from here until ???END lines may have been inserted/deleted" msgstr "??? ここから ???END までの行が挿入か削除されたようです" +msgid "??? lines may be missing" +msgstr "???行がないようです" + msgid "???END" msgstr "???END" @@ -3268,15 +4924,17 @@ msgstr "復元完了。バッファの内容はファイルと同じになりま msgid "" "\n" -"You may want to delete the .swp file now.\n" -"\n" +"You may want to delete the .swp file now." msgstr "" "\n" -"元の.swpファイルは削除しても構いません\n" -"\n" +"元の.swpファイルは削除しても構いません。" -msgid "Using crypt key from swap file for the text file.\n" -msgstr "スワップファイルから取得した暗号キーをテキストファイルに使います.\n" +msgid "" +"\n" +"Note: process STILL RUNNING: " +msgstr "" +"\n" +"注意: プロセスはまだ実行中です: " msgid "Swap files found:" msgstr "スワップファイルが複数見つかりました:" @@ -3293,9 +4951,6 @@ msgstr " ディレクトリ " msgid " -- none --\n" msgstr " -- なし --\n" -msgid "%a %b %d %H:%M:%S %Y" -msgstr "%Y/%m/%d (%a) %H:%M:%S" - msgid " owned by: " msgstr " 所有者: " @@ -3311,6 +4966,10 @@ msgstr " [from Vim version 3.0]" msgid " [does not look like a Vim swap file]" msgstr " [Vimのスワップファイルではないようです]" +#, fuzzy +#~ msgid " [garbled strings (not nul terminated)]" +#~ msgstr " [文字化けした文字列 (nul で終了していない)]" + msgid " file name: " msgstr " ファイル名: " @@ -3351,18 +5010,11 @@ msgstr "" "\n" " プロセスID: " -msgid " (still running)" +msgid " (STILL RUNNING)" msgstr " (まだ実行中)" msgid "" "\n" -" [not usable with this version of Vim]" -msgstr "" -"\n" -" [このVimバージョンでは使用できません]" - -msgid "" -"\n" " [not usable on this computer]" msgstr "" "\n" @@ -3383,53 +5035,25 @@ msgstr "ファイルが維持されます" msgid "E314: Preserve failed" msgstr "E314: 維持に失敗しました" -#, c-format -msgid "E315: ml_get: invalid lnum: %ld" -msgstr "E315: ml_get: 無効なlnumです: %ld" - -#, c-format -msgid "E316: ml_get: cannot find line %ld in buffer %d %s" -msgstr "E316: ml_get: 行 %ld をバッファ %d %s 内に見つけられません" - -msgid "E317: pointer block id wrong 3" -msgstr "E317: ポインタブロックのIDが間違っています 3" - msgid "stack_idx should be 0" msgstr "stack_idx は 0 であるべきです" msgid "E318: Updated too many blocks?" -msgstr "E318: 更新されたブロックが多過ぎるかも?" - -msgid "E317: pointer block id wrong 4" -msgstr "E317: ポインタブロックのIDが間違っています 4" +msgstr "E318: 更新されたブロックが多すぎますか?" msgid "deleted block 1?" msgstr "ブロック 1 は消された?" #, c-format -msgid "E320: Cannot find line %ld" -msgstr "E320: 行 %ld が見つかりません" - -msgid "E317: pointer block id wrong" -msgstr "E317: ポインタブロックのIDが間違っています" +msgid "E320: Cannot find line %<PRId64>" +msgstr "E320: 行 %<PRId64> が見つかりません" msgid "pe_line_count is zero" msgstr "pe_line_count がゼロです" -#, c-format -msgid "E322: line number out of range: %ld past the end" -msgstr "E322: 行番号が範囲外です: %ld 超えています" - -#, c-format -msgid "E323: line count wrong in block %ld" -msgstr "E323: ブロック %ld の行カウントが間違っています" - msgid "Stack size increases" msgstr "スタックサイズが増えます" -msgid "E317: pointer block id wrong 2" -msgstr "E317: ポインタブロックのIDが間違っています 2" - #, c-format msgid "E773: Symlink loop for \"%s\"" msgstr "E773: \"%s\" のシンボリックリンクがループになっています" @@ -3447,6 +5071,9 @@ msgstr "" msgid "While opening file \"" msgstr "次のファイルを開いている最中 \"" +msgid " CANNOT BE FOUND" +msgstr " 見つかりません" + msgid " NEWER than swap file!\n" msgstr " スワップファイルよりも新しいです!\n" @@ -3485,6 +5112,9 @@ msgstr "" "\"\n" " を消せばこのメッセージを回避できます.\n" +msgid "Found a swap file that is not useful, deleting it" +msgstr "不要なスワップファイルが見つかりました。削除します" + msgid "Swap file \"" msgstr "スワップファイル \"" @@ -3494,9 +5124,6 @@ msgstr "\" が既にあります!" msgid "VIM - ATTENTION" msgstr "VIM - 注意" -msgid "Swap file already exists!" -msgstr "スワップファイルが既に存在します!" - msgid "" "&Open Read-Only\n" "&Edit anyway\n" @@ -3528,12 +5155,24 @@ msgstr "" msgid "E326: Too many swap files found" msgstr "E326: スワップファイルが多数見つかりました" +#, c-format +msgid "" +"E303: Unable to create directory \"%s\" for swap file, recovery impossible: " +"%s" +msgstr "" +"E303: スワップファイルの為のディレクトリ \"%s\" を作成できませんでした。リカ" +"バリは不可能です: %s" + +msgid "Vim: Data too large to fit into virtual memory space\n" +msgstr "Vim: データが大きすぎて仮想メモリ空間に収まりません\n" + +#, c-format +msgid "E342: Out of memory! (allocating %<PRIu64> bytes)" +msgstr "E342: メモリが足りません! (%<PRIu64> バイトを割当要求)" + msgid "E327: Part of menu-item path is not sub-menu" msgstr "E327: メニューアイテムのパスの部分がサブメニューではありません" -msgid "E328: Menu only exists in another mode" -msgstr "E328: メニューは他のモードにだけあります" - #, c-format msgid "E329: No menu \"%s\"" msgstr "E329: \"%s\" というメニューはありません" @@ -3557,9 +5196,6 @@ msgstr "" "\n" "--- メニュー ---" -msgid "Tear off this menu" -msgstr "このメニューを切り取る" - #, c-format msgid "E335: Menu not defined for %s mode" msgstr "E335: %s にはメニューが定義されていません" @@ -3581,17 +5217,13 @@ msgstr "E337: メニューが見つかりません - メニュー名を確認し msgid "Error detected while processing %s:" msgstr "%s の処理中にエラーが検出されました:" -#, c-format -msgid "line %4ld:" -msgstr "行 %4ld:" +#~ msgid "line %4" +#~ msgstr "" #, c-format msgid "E354: Invalid register name: '%s'" msgstr "E354: 無効なレジスタ名: '%s'" -msgid "Messages maintainer: Bram Moolenaar <Bram@vim.org>" -msgstr "日本語メッセージ翻訳/監修: 村岡 太郎 <koron.kaoriya@gmail.com>" - msgid "Interrupt: " msgstr "割込み: " @@ -3599,8 +5231,21 @@ msgid "Press ENTER or type command to continue" msgstr "続けるにはENTERを押すかコマンドを入力してください" #, c-format -msgid "%s line %ld" -msgstr "%s 行 %ld" +msgid "%d more line" +msgid_plural "%d more lines" +msgstr[0] "%d 行 追加しました" + +#, c-format +msgid "%d line less" +msgid_plural "%d fewer lines" +msgstr[0] "%d 行 削除しました" + +msgid " (Interrupted)" +msgstr " (割込まれました)" + +#, c-format +msgid "%s line %<PRId64>" +msgstr "%s 行 %<PRId64>" msgid "-- More --" msgstr "-- 継続 --" @@ -3621,6 +5266,15 @@ msgstr "" msgid "" "&Yes\n" "&No\n" +"&Cancel" +msgstr "" +"はい(&Y)\n" +"いいえ(&N)\n" +"キャンセル(&C)" + +msgid "" +"&Yes\n" +"&No\n" "Save &All\n" "&Discard All\n" "&Cancel" @@ -3631,299 +5285,152 @@ msgstr "" "全て放棄(&D)\n" "キャンセル(&C)" -msgid "E766: Insufficient arguments for printf()" -msgstr "E766: printf() の引数が不十分です" - -msgid "E807: Expected Float argument for printf()" -msgstr "E807: printf() の引数には浮動小数点数が期待されています" - -msgid "E767: Too many arguments to printf()" -msgstr "E767: printf() の引数が多過ぎます" - -msgid "Type number and <Enter> or click with mouse (empty cancels): " -msgstr "" -"番号と<Enter>を入力するかマウスでクリックしてください (空でキャンセル): " - -msgid "Type number and <Enter> (empty cancels): " -msgstr "番号と<Enter>を入力してください (空でキャンセル): " - -msgid "1 more line" -msgstr "1 行 追加しました" - -msgid "1 line less" -msgstr "1 行 削除しました" - -#, c-format -msgid "%ld more lines" -msgstr "%ld 行 追加しました" - -#, c-format -msgid "%ld fewer lines" -msgstr "%ld 行 削除しました" - -msgid " (Interrupted)" -msgstr " (割込まれました)" - -msgid "Beep!" -msgstr "ビーッ!" - -#, c-format -msgid "%ld second ago" -msgid_plural "%ld seconds ago" -msgstr[0] "%ld 秒経過しています" - -msgid "ERROR: " -msgstr "エラー: " - -#, c-format -msgid "" -"\n" -"[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\n" -msgstr "" -"\n" -"[メモリ(バイト)] 総割当-解放量 %lu-%lu, 使用量 %lu, ピーク時 %lu\n" - -#, c-format -msgid "" -"[calls] total re/malloc()'s %lu, total free()'s %lu\n" -"\n" -msgstr "" -"[呼出] 総 re/malloc() 回数 %lu, 総 free() 回数 %lu\n" -"\n" - -msgid "E341: Internal error: lalloc(0, )" -msgstr "E341: 内部エラー: lalloc(0, )" - -#, c-format -msgid "E342: Out of memory! (allocating %lu bytes)" -msgstr "E342: メモリが足りません! (%lu バイトを割当要求)" - -#, c-format -msgid "Calling shell to execute: \"%s\"" -msgstr "実行のためにシェルを呼出し中: \"%s\"" - -msgid "E545: Missing colon" -msgstr "E545: コロンがありません" - -msgid "E546: Illegal mode" -msgstr "E546: 不正なモードです" - -msgid "E547: Illegal mouseshape" -msgstr "E547: 不正な 'mouseshape' です" - -msgid "E548: digit expected" -msgstr "E548: 数値が必要です" - -msgid "E549: Illegal percentage" -msgstr "E549: 不正なパーセンテージです" - -msgid "E854: path too long for completion" -msgstr "E854: パスが長過ぎて補完できません" - -#, c-format -msgid "" -"E343: Invalid path: '**[number]' must be at the end of the path or be " -"followed by '%s'." -msgstr "" -"E343: 無効なパスです: '**[数値]' はpathの最後か '%s' が続いてないといけませ" -"ん." - -#, c-format -msgid "E344: Can't find directory \"%s\" in cdpath" -msgstr "E344: cdpathには \"%s\" というファイルがありません" - -#, c-format -msgid "E345: Can't find file \"%s\" in path" -msgstr "E345: pathには \"%s\" というファイルがありません" - -#, c-format -msgid "E346: No more directory \"%s\" found in cdpath" -msgstr "E346: cdpathにはこれ以上 \"%s\" というファイルがありません" - -#, c-format -msgid "E347: No more file \"%s\" found in path" -msgstr "E347: パスにはこれ以上 \"%s\" というファイルがありません" - -#, c-format -msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\"" -msgstr "" -"E668: NetBeansの接続情報ファイルのアクセスモードに問題があります: \"%s\"" - -#, c-format -msgid "E658: NetBeans connection lost for buffer %ld" -msgstr "E658: バッファ %ld の NetBeans 接続が失われました" - -msgid "E838: netbeans is not supported with this GUI" -msgstr "E838: NetBeansはこのGUIには対応していません" - -msgid "E511: netbeans already connected" -msgstr "E511: NetBeansは既に接続しています" +msgid "E664: Changelist is empty" +msgstr "E664: 変更リストが空です" -#, c-format -msgid "E505: %s is read-only (add ! to override)" -msgstr "E505: %s は読込専用です (強制書込には ! を追加)" +msgid "E1292: Command-line window is already open" +msgstr "E1292: コマンドラインウィンドウは既に開かれています" msgid "E349: No identifier under cursor" msgstr "E349: カーソルの位置には識別子がありません" -msgid "Warning: terminal cannot highlight" -msgstr "警告: 使用している端末はハイライトできません" - msgid "E348: No string under cursor" msgstr "E348: カーソルの位置には文字列がありません" msgid "E352: Cannot erase folds with current 'foldmethod'" msgstr "E352: 現在の 'foldmethod' では折畳みを消去できません" -msgid "E664: changelist is empty" -msgstr "E664: 変更リストが空です" - msgid "E662: At start of changelist" msgstr "E662: 変更リストの先頭" msgid "E663: At end of changelist" msgstr "E663: 変更リストの末尾" -msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim" +msgid "Type :qa! and press <Enter> to abandon all changes and exit Nvim" msgstr "" -"すべての変更を破棄し、Vimを終了するには :qa! と入力し <Enter> を押してくだ" +"すべての変更を破棄し、Nvimを終了するには :qa! と入力し <Enter> を押してくだ" "さい" -#, c-format -msgid "1 line %sed 1 time" -msgstr "1 行が %s で 1 回処理されました" +msgid "Type :qa and press <Enter> to exit Nvim" +msgstr "Nvimを終了するには :qa と入力し <Enter> を押してください" -#, c-format -msgid "1 line %sed %d times" -msgstr "1 行が %s で %d 回処理されました" +msgid "" +"E883: Search pattern and expression register may not contain two or more " +"lines" +msgstr "E883: 検索パターンと式レジスタには2行以上を含められません" #, c-format -msgid "%ld lines %sed 1 time" -msgstr "%ld 行が %s で 1 回処理されました" +msgid "%<PRId64> line %sed %d time" +msgid_plural "%<PRId64> line %sed %d times" +msgstr[0] "%<PRId64> 行が %s で %d 回処理されました" #, c-format -msgid "%ld lines %sed %d times" -msgstr "%ld 行が %s で %d 回処理されました" +msgid "%<PRId64> lines %sed %d time" +msgid_plural "%<PRId64> lines %sed %d times" +msgstr[0] "%<PRId64> 行が %s で %d 回処理されました" #, c-format -msgid "%ld lines to indent... " -msgstr "%ld 行がインデントされます... " - -msgid "1 line indented " -msgstr "1 行をインデントしました " +msgid "%<PRId64> lines to indent... " +msgstr "%<PRId64> 行がインデントされます... " #, c-format -msgid "%ld lines indented " -msgstr "%ld 行をインデントしました " - -msgid "cannot yank; delete anyway" -msgstr "ヤンクできません; とにかく消去" +msgid "%<PRId64> line indented " +msgid_plural "%<PRId64> lines indented " +msgstr[0] "%<PRId64> 行をインデントしました " -msgid "1 line changed" -msgstr "1 行が変更されました" +msgid "E748: No previously used register" +msgstr "E748: まだレジスタを使用していません" #, c-format -msgid "%ld lines changed" -msgstr "%ld 行が変更されました" +msgid "%<PRId64> line changed" +msgid_plural "%<PRId64> lines changed" +msgstr[0] "%<PRId64> 行が変更されました" #, c-format -msgid "block of 1 line yanked%s" -msgstr "1 行のブロックが%sヤンクされました" +msgid " into \"%c" +msgstr " \"%c に" #, c-format -msgid "1 line yanked%s" -msgstr "1 行が%sヤンクされました" +msgid "block of %<PRId64> line yanked%s" +msgid_plural "block of %<PRId64> lines yanked%s" +msgstr[0] "%<PRId64> 行のブロックが%sヤンクされました" #, c-format -msgid "block of %ld lines yanked%s" -msgstr "%ld 行のブロックが%sヤンクされました" +msgid "%<PRId64> line yanked%s" +msgid_plural "%<PRId64> lines yanked%s" +msgstr[0] "%<PRId64> 行が%sヤンクされました" #, c-format -msgid "%ld lines yanked%s" -msgstr "%ld 行が%sヤンクされました" - -msgid "" -"\n" -"--- Registers ---" -msgstr "" -"\n" -"--- レジスタ ---" - -msgid "Illegal register name" -msgstr "不正なレジスタ名" +msgid "E353: Nothing in register %s" +msgstr "E353: レジスタ %s には何もありません" msgid "" "\n" -"# Registers:\n" +"Type Name Content" msgstr "" "\n" -"# レジスタ:\n" +"型式 名前 内容" #, c-format -msgid "E574: Unknown register type %d" -msgstr "E574: 未知のレジスタ型 %d です" +msgid "%<PRId64> lines changed" +msgid_plural "%<PRId64> lines changed" +msgstr[0] "%<PRId64> 行が変更されました" #, c-format -msgid "%ld Cols; " -msgstr "%ld 列; " +msgid "%<PRId64> Cols; " +msgstr "%<PRId64> 列; " #, c-format -msgid "Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Bytes" -msgstr "選択 %s%ld / %ld 行; %lld / %lld 単語; %lld / %lld バイト" +msgid "" +"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " +"%<PRId64> of %<PRId64> Bytes" +msgstr "" +"選択 %s%<PRId64> / %<PRId64> 行; %<PRId64> / %<PRId64> 単語; %<PRId64> / " +"%<PRId64> バイト" #, c-format msgid "" -"Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Chars; %lld of " -"%lld Bytes" +"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " +"%<PRId64> of %<PRId64> Chars; %<PRId64> of %<PRId64> Bytes" msgstr "" -"選択 %s%ld / %ld 行; %lld / %lld 単語; %lld / %lld 文字; %lld / %lld バイト" +"選択 %s%<PRId64> / %<PRId64> 行; %<PRId64> / %<PRId64> 単語; %<PRId64> / " +"%<PRId64> 文字; %<PRId64> / %<PRId64> バイト" #, c-format -msgid "Col %s of %s; Line %ld of %ld; Word %lld of %lld; Byte %lld of %lld" -msgstr "列 %s / %s; 行 %ld of %ld; 単語 %lld / %lld; バイト %lld / %lld" +msgid "" +"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte " +"%<PRId64> of %<PRId64>" +msgstr "" +"列 %s / %s; 行 %<PRId64> of %<PRId64>; 単語 %<PRId64> / %<PRId64>; バイト " +"%<PRId64> / %<PRId64>" #, c-format msgid "" -"Col %s of %s; Line %ld of %ld; Word %lld of %lld; Char %lld of %lld; Byte " -"%lld of %lld" +"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char " +"%<PRId64> of %<PRId64>; Byte %<PRId64> of %<PRId64>" msgstr "" -"列 %s / %s; 行 %ld / %ld; 単語 %lld / %lld; 文字 %lld / %lld; バイト %lld of " -"%lld" +"列 %s / %s; 行 %<PRId64> / %<PRId64>; 単語 %<PRId64> / %<PRId64>; 文字 " +"%<PRId64> / %<PRId64>; バイト %<PRId64> of %<PRId64>" #, c-format -msgid "(+%lld for BOM)" -msgstr "(+%lld for BOM)" +msgid "(+%<PRId64> for BOM)" +msgstr "(+%<PRId64> for BOM)" msgid "E774: 'operatorfunc' is empty" msgstr "E774: 'operatorfunc' オプションが空です" -msgid "E775: Eval feature not available" -msgstr "E775: 式評価機能が無効になっています" - msgid "E518: Unknown option" msgstr "E518: 未知のオプションです" -msgid "E519: Option not supported" -msgstr "E519: オプションはサポートされていません" - msgid "E520: Not allowed in a modeline" msgstr "E520: modeline では許可されません" msgid "E992: Not allowed in a modeline when 'modelineexpr' is off" msgstr "E992: 'modelineexpr' がオフの時 modeline では許可されません" -msgid "E846: Key code not set" -msgstr "E846: キーコードが設定されていません" - msgid "E521: Number required after =" msgstr "E521: = の後には数字が必要です" -msgid "E522: Not found in termcap" -msgstr "E522: termcap 内に見つかりません" - -msgid "E946: Cannot make a terminal with running job modifiable" -msgstr "E946: 実行中のジョブがある端末は変更可能にできません" - msgid "E590: A preview window already exists" msgstr "E590: プレビューウィンドウが既に存在します" @@ -3931,9 +5438,6 @@ msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'" msgstr "" "W17: アラビア文字にはUTF-8が必要なので、':set encoding=utf-8' してください" -msgid "E954: 24-bit colors are not supported on this environment" -msgstr "E954: 24bit色はこの環境ではサポートされていません" - #, c-format msgid "E593: Need at least %d lines" msgstr "E593: 最低 %d の行数が必要です" @@ -3942,20 +5446,12 @@ msgstr "E593: 最低 %d の行数が必要です" msgid "E594: Need at least %d columns" msgstr "E594: 最低 %d のカラム幅が必要です" -#, c-format -msgid "E355: Unknown option: %s" -msgstr "E355: 未知のオプションです: %s" +msgid "Cannot unset global option value" +msgstr "グローバルオプションの設定解除はできません" #, c-format -msgid "E521: Number required: &%s = '%s'" -msgstr "E521: 数字が必要です: &%s = '%s'" - -msgid "" -"\n" -"--- Terminal codes ---" -msgstr "" -"\n" -"--- 端末コード ---" +msgid "Invalid value for option '%s': expected %s, got %s %s" +msgstr "'%s' は無効なオプション値です: %s が必要でしたが %s %s でした" msgid "" "\n" @@ -3981,45 +5477,32 @@ msgstr "" msgid "E356: get_varp ERROR" msgstr "E356: get_varp エラー" -#, c-format -msgid "E539: Illegal character <%s>" -msgstr "E539: 不正な文字です <%s>" - -#, c-format -msgid "For option %s" -msgstr "オプション: %s" - msgid "E540: Unclosed expression sequence" msgstr "E540: 式が終了していません" +msgid "E536: Comma required" +msgstr "E536: コンマが必要です" -msgid "E542: unbalanced groups" +msgid "E542: Unbalanced groups" msgstr "E542: グループが釣合いません" -msgid "E529: Cannot set 'term' to empty string" -msgstr "E529: 'term' には空文字列を設定できません" - -msgid "E530: Cannot change term in GUI" -msgstr "E530: GUIでは 'term' を変更できません" - -msgid "E531: Use \":gui\" to start the GUI" -msgstr "E531: GUIをスタートするには \":gui\" を使用してください" - msgid "E589: 'backupext' and 'patchmode' are equal" msgstr "E589: 'backupext' と 'patchmode' が同じです" -msgid "E834: Conflicts with value of 'listchars'" -msgstr "E834: 'listchars'の値に矛盾があります" +msgid "E595: 'showbreak' contains unprintable or wide character" +msgstr "E595: 'showbreak' は表示できない文字かワイド文字を含んでいます" -msgid "E835: Conflicts with value of 'fillchars'" -msgstr "E835: 'fillchars'の値に矛盾があります" +#, c-format +msgid "E1511: Wrong number of characters for field \"%s\"" +msgstr "E1511: フィールド \"%s\" の文字数が間違っています" -msgid "E617: Cannot be changed in the GTK+ 2 GUI" -msgstr "E617: GTK+2 GUIでは変更できません" +#, c-format +msgid "E1512: Wrong character width for field \"%s\"" +msgstr "E1512: フィールド \"%s\" の文字幅が間違っています" #, c-format -msgid "E950: Cannot convert between %s and %s" -msgstr "E950: %s と %s の間で変換できません" +msgid "E539: Illegal character <%s>" +msgstr "E539: 不正な文字です <%s>" msgid "E524: Missing colon" msgstr "E524: コロンがありません" @@ -4028,174 +5511,61 @@ msgid "E525: Zero length string" msgstr "E525: 文字列の長さがゼロです" #, c-format -msgid "E526: Missing number after <%s>" -msgstr "E526: <%s> の後に数字がありません" - -msgid "E527: Missing comma" -msgstr "E527: カンマがありません" - -msgid "E528: Must specify a ' value" -msgstr "E528: ' の値を指定しなければなりません" - -msgid "E595: contains unprintable or wide character" -msgstr "E595: 表示できない文字かワイド文字を含んでいます" - -msgid "E596: Invalid font(s)" -msgstr "E596: 無効なフォントです" - -msgid "E597: can't select fontset" -msgstr "E597: フォントセットを選択できません" - -msgid "E598: Invalid fontset" -msgstr "E598: 無効なフォントセットです" - -msgid "E533: can't select wide font" -msgstr "E533: ワイドフォントを選択できません" - -msgid "E534: Invalid wide font" -msgstr "E534: 無効なワイドフォントです" - -#, c-format -msgid "E535: Illegal character after <%c>" -msgstr "E535: <%c> の後に不正な文字があります" - -msgid "E536: comma required" -msgstr "E536: カンマが必要です" - -#, c-format msgid "E537: 'commentstring' must be empty or contain %s" msgstr "E537: 'commentstring' は空であるか %s を含む必要があります" -msgid "cannot open " -msgstr "開けません " - -msgid "VIM: Can't open window!\n" -msgstr "VIM: ウィンドウを開けません!\n" - -msgid "Need Amigados version 2.04 or later\n" -msgstr "Amigadosのバージョン 2.04かそれ以降が必要です\n" - #, c-format -msgid "Need %s version %ld\n" -msgstr "%s のバージョン %ld が必要です\n" - -msgid "Cannot open NIL:\n" -msgstr "NILを開けません:\n" - -msgid "Cannot create " -msgstr "作成できません " - -#, c-format -msgid "Vim exiting with %d\n" -msgstr "Vimは %d で終了します\n" - -msgid "cannot change console mode ?!\n" -msgstr "コンソールモードを変更できません?!\n" - -msgid "mch_get_shellsize: not a console??\n" -msgstr "mch_get_shellsize: コンソールではない??\n" - -msgid "E360: Cannot execute shell with -f option" -msgstr "E360: -f オプションでシェルを実行できません" - -msgid "Cannot execute " -msgstr "実行できません " - -msgid "shell " -msgstr "シェル " - -msgid " returned\n" -msgstr " 戻りました\n" - -msgid "ANCHOR_BUF_SIZE too small." -msgstr "ANCHOR_BUF_SIZE が小さ過ぎます。" - -msgid "I/O ERROR" -msgstr "入出力エラー" - -msgid "Message" -msgstr "メッセージ" +msgid "E535: Illegal character after <%c>" +msgstr "E535: <%c> の後に不正な文字があります" -msgid "E237: Printer selection failed" -msgstr "E237: プリンタの選択に失敗しました" +msgid "E5080: Digit expected" +msgstr "E5080: 数値が必要です" #, c-format -msgid "to %s on %s" -msgstr "%s へ (%s 上の)" +msgid "E526: Missing number after <%s>" +msgstr "E526: <%s> の後に数字がありません" -#, c-format -msgid "E613: Unknown printer font: %s" -msgstr "E613: 未知のプリンタオプションです: %s" +msgid "E527: Missing comma" +msgstr "E527: コンマがありません" -#, c-format -msgid "E238: Print error: %s" -msgstr "E238: 印刷エラー: %s" +msgid "E528: Must specify a ' value" +msgstr "E528: ' の値を指定しなければなりません" -#, c-format -msgid "Printing '%s'" -msgstr "印刷しています: '%s'" +msgid "E834: Conflicts with value of 'listchars'" +msgstr "E834: 'listchars'の値に矛盾があります" -#, c-format -msgid "E244: Illegal charset name \"%s\" in font name \"%s\"" -msgstr "E244: 文字セット名 \"%s\" は不正です (フォント名 \"%s\")" +msgid "E835: Conflicts with value of 'fillchars'" +msgstr "E835: 'fillchars'の値に矛盾があります" #, c-format -msgid "E244: Illegal quality name \"%s\" in font name \"%s\"" -msgstr "E244: 品質名 \"%s\" は不正です (フォント名 \"%s\")" +msgid "dlerror = \"%s\"" +msgstr "dlerror = \"%s\"" #, c-format -msgid "E245: Illegal char '%c' in font name \"%s\"" -msgstr "E245: '%c' は不正な文字です (フォント名 \"%s\")" +msgid "E5420: Failed to write to file: %s" +msgstr "E5420: ファイルへの書き込みに失敗しました: %s" -#, c-format -msgid "Opening the X display took %ld msec" -msgstr "Xサーバーへの接続に %ld ミリ秒かかりました" +msgid "E1506: Buffer too small to copy xattr value or key" +msgstr "E1506: xattr値またはキーをコピーするのにバッファが小さすぎます" msgid "" -"\n" -"Vim: Got X error\n" -msgstr "" -"\n" -"Vim: X のエラーを検出しましたr\n" - -#, c-format -msgid "restoring display %s" -msgstr "ディスプレイ %s を復元しています" - -msgid "Testing the X display failed" -msgstr "X display のチェックに失敗しました" +"E1508: Size of the extended attribute value is larger than the maximum size " +"allowed" +msgstr "E1508: 拡張属性値のサイズが許可されている最大サイズを超えています" -msgid "Opening the X display timed out" -msgstr "X display の open がタイムアウトしました" - -msgid "" -"\n" -"Could not get security context for " -msgstr "" -"\n" -"セキュリティコンテキストを取得できません " +msgid "E1509: Error occurred when reading or writing extended attribute" +msgstr "E1509: 拡張属性の読込みまたは書込みでエラーが起きました" -msgid "" -"\n" -"Could not set security context for " -msgstr "" -"\n" -"セキュリティコンテキストを設定できません " +msgid "Vim: Error reading input, exiting...\n" +msgstr "Vim: 入力を読込み中のエラーにより終了します...\n" #, c-format -msgid "Could not set security context %s for %s" -msgstr "セキュリティコンテキスト %s を %s に設定できません" +msgid "Current %slanguage: \"%s\"" +msgstr "現在の %s言語: \"%s\"" #, c-format -msgid "Could not get security context %s for %s. Removing it!" -msgstr "セキュリティコンテキスト %s を %s から取得できません。削除します!" - -msgid "" -"\n" -"Cannot execute shell sh\n" -msgstr "" -"\n" -"sh シェルを実行できません\n" +msgid "E197: Cannot set language to \"%s\"" +msgstr "E197: 言語を \"%s\" に設定できません" msgid "" "\n" @@ -4206,98 +5576,31 @@ msgstr "" msgid "" "\n" -"Cannot create pipes\n" -msgstr "" -"\n" -"パイプを作成できません\n" - -msgid "" -"\n" -"Cannot fork\n" -msgstr "" -"\n" -"fork できません\n" - -msgid "" -"\n" -"Cannot execute shell " -msgstr "" -"\n" -"シェルを実行できません " - -msgid "" -"\n" -"Command terminated\n" +"shell failed to start: " msgstr "" "\n" -"コマンドを中断しました\n" - -msgid "XSMP lost ICE connection" -msgstr "XSMP がICE接続を失いました" - -#, c-format -msgid "dlerror = \"%s\"" -msgstr "dlerror = \"%s\"" - -msgid "Opening the X display failed" -msgstr "X display の open に失敗しました" - -msgid "XSMP handling save-yourself request" -msgstr "XSMP がsave-yourself要求を処理しています" - -msgid "XSMP opening connection" -msgstr "XSMP が接続を開始しています" - -msgid "XSMP ICE connection watch failed" -msgstr "XSMP ICE接続が失敗したようです" +"シェルの起動に失敗しました: " #, c-format -msgid "XSMP SmcOpenConnection failed: %s" -msgstr "XSMP SmcOpenConnectionが失敗しました: %s" - -msgid "At line" -msgstr "行" - -msgid "Could not load vim32.dll!" -msgstr "vim32.dll をロードできませんでした" - -msgid "VIM Error" -msgstr "VIMエラー" +msgid "E5677: Error writing input to shell-command: %s" +msgstr "E5677: シェルコマンドへの入力の書き込みエラー: %s" -msgid "Could not fix up function pointers to the DLL!" -msgstr "DLLから関数ポインタを取得できませんでした" +#, no-c-format +msgid "%a %b %d %H:%M:%S %Y" +msgstr "%Y/%m/%d (%a) %H:%M:%S" #, c-format -msgid "Vim: Caught %s event\n" -msgstr "Vim: イベント %s を検知\n" - -msgid "close" -msgstr "閉じる" - -msgid "logoff" -msgstr "ログオフ" - -msgid "shutdown" -msgstr "シャットダウン" - -msgid "E371: Command not found" -msgstr "E371: コマンドがありません" +msgid "E447: Can't find file \"%s\" in path" +msgstr "E447: pathには \"%s\" というファイルがありません" -msgid "" -"VIMRUN.EXE not found in your $PATH.\n" -"External commands will not pause after completion.\n" -"See :help win32-vimrun for more information." -msgstr "" -"VIMRUN.EXEが $PATH の中に見つかりません.\n" -"外部コマンドの終了後に一時停止をしません.\n" -"詳細は :help win32-vimrun を参照してください." +msgid "E750: First use \":profile start {fname}\"" +msgstr "E750: 初めに \":profile start {fname}\" を実行してください" -msgid "Vim Warning" -msgstr "Vimの警告" +msgid "E553: No more items" +msgstr "E553: 要素がもうありません" -#, c-format -msgid "shell returned %d" -msgstr "シェルがコード %d で終了しました" +msgid "E925: Current quickfix list was changed" +msgstr "E925: 現在の quickfix リストが変更されました" msgid "E926: Current location list was changed" msgstr "E926: 現在のロケーションリストが変更されました" @@ -4331,15 +5634,9 @@ msgstr "E378: 'errorformat' にパターンが指定されていません" msgid "E379: Missing or empty directory name" msgstr "E379: ディレクトリ名が無いか空です" -msgid "E553: No more items" -msgstr "E553: 要素がもうありません" - msgid "E924: Current window was closed" msgstr "E924: 現在のウィンドウが閉じられました" -msgid "E925: Current quickfix was changed" -msgstr "E925: 現在の quickfix が変更されました" - #, c-format msgid "(%d of %d)%s%s: " msgstr "(%d of %d)%s%s: " @@ -4349,7 +5646,7 @@ msgstr " (行が削除されました)" #, c-format msgid "%serror list %d of %d; %d errors " -msgstr "%s エラー一覧 %d of %d; %d 個エラー" +msgstr "%s エラー一覧 %d of %d; %d 個エラー " msgid "E380: At bottom of quickfix stack" msgstr "E380: quickfix スタックの末尾です" @@ -4360,9 +5657,6 @@ msgstr "E381: quickfix スタックの先頭です" msgid "No entries" msgstr "エントリがありません" -msgid "Error file" -msgstr "エラーファイル" - msgid "E683: File name missing or invalid pattern" msgstr "E683: ファイル名が無いか無効なパターンです" @@ -4370,17 +5664,35 @@ msgstr "E683: ファイル名が無いか無効なパターンです" msgid "Cannot open file \"%s\"" msgstr "ファイル \"%s\" を開けません" -msgid "E681: Buffer is not loaded" -msgstr "E681: バッファは読み込まれませんでした" +msgid "cannot have both a list and a \"what\" argument" +msgstr "リストと \"what\" 引数の両方を指定することはできません" msgid "E777: String or List expected" msgstr "E777: 文字列かリストが必要です" #, c-format -msgid "E369: invalid item in %s%%[]" +msgid "E927: Invalid action: '%s'" +msgstr "E927: 無効な操作です: '%s'" + +#, c-format +msgid "E59: Invalid character after %s@" +msgstr "E59: %s@ の後に不正な文字がありました" + +msgid "E63: Invalid use of \\_" +msgstr "E63: \\_ の無効な使用方法です" + +msgid "E363: Pattern uses more memory than 'maxmempattern'" +msgstr "E363: パターンが 'maxmempattern' 以上のメモリを使用します" + +#, c-format +msgid "E369: Invalid item in %s%%[]" msgstr "E369: 無効な項目です: %s%%[]" #, c-format +msgid "E654: Missing delimiter after search pattern: %s" +msgstr "E654: 検索パターンのあとに区切りがありません: %s" + +#, c-format msgid "E769: Missing ] after %s[" msgstr "E769: %s[ の後に ] がありません" @@ -4420,6 +5732,21 @@ msgid "E956: Cannot use pattern recursively" msgstr "E956: パターンを再帰的に使うことはできません" #, c-format +msgid "E1204: No Number allowed after .: '\\%%%c'" +msgstr "E1204: . の後に数字は許されません: '\\%%%c'" + +#, c-format +msgid "E1273: (NFA regexp) missing value in '\\%%%c'" +msgstr "E1273: (NFA 正規表現) '\\%%%c' に値がありません" + +#, c-format +msgid "E1281: Atom '\\%%#=%c' must be at the start of the pattern" +msgstr "E1281: アトム '\\%%#=%c' はパターンの先頭になければなりません" + +msgid "E1290: substitute nesting too deep" +msgstr "E1290: 置換の入れ子が深過ぎます" + +#, c-format msgid "E554: Syntax error in %s{...}" msgstr "E554: %s{...} 内に文法エラーがあります" @@ -4427,22 +5754,9 @@ msgstr "E554: %s{...} 内に文法エラーがあります" msgid "E888: (NFA regexp) cannot repeat %s" msgstr "E888: (NFA 正規表現) 繰り返せません %s" -msgid "" -"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " -"used " -msgstr "" -"E864: \\%#= には 0, 1 もしくは 2 のみが続けられます。正規表現エンジンは自動選" -"択されます。" - -msgid "Switching to backtracking RE engine for pattern: " -msgstr "次のパターンにバックトラッキング RE エンジンを適用します: " - msgid "E65: Illegal back reference" msgstr "E65: 不正な後方参照です" -msgid "E63: invalid use of \\_" -msgstr "E63: \\_ の無効な使用方法です" - #, c-format msgid "E64: %s%c follows nothing" msgstr "E64:%s%c の後になにもありません" @@ -4459,10 +5773,6 @@ msgid "E71: Invalid character after %s%%" msgstr "E71: %s%% の後に不正な文字がありました" #, c-format -msgid "E59: invalid character after %s@" -msgstr "E59: %s@ の後に不正な文字がありました" - -#, c-format msgid "E60: Too many complex %s{...}s" msgstr "E60: 複雑な %s{...} が多過ぎます" @@ -4487,6 +5797,7 @@ msgstr "E52: \\z( が釣り合っていません" msgid "E339: Pattern too long" msgstr "E339: パターンが長過ぎます" +#, c-format msgid "External submatches:\n" msgstr "外部の部分該当:\n" @@ -4498,29 +5809,26 @@ msgid "E866: (NFA regexp) Misplaced %c" msgstr "E866: (NFA 正規表現) 位置が誤っています: %c" #, c-format -msgid "E877: (NFA regexp) Invalid character class: %ld" -msgstr "E877: (NFA 正規表現) 無効な文字クラス: %ld" +msgid "E877: (NFA regexp) Invalid character class: %<PRId64>" +msgstr "E877: (NFA 正規表現) 無効な文字クラス: %<PRId64>" + +msgid "E951: \\% value too large" +msgstr "E951: \\% 値が大き過ぎます" #, c-format msgid "E867: (NFA) Unknown operator '\\z%c'" msgstr "E867: (NFA) 未知のオペレータです: '\\z%c'" -msgid "E951: \\% value too large" -msgstr "E951: \\% 値が長過ぎます" - #, c-format msgid "E867: (NFA) Unknown operator '\\%%%c'" msgstr "E867: (NFA) 未知のオペレータです: '\\%%%c'" -msgid "E868: Error building NFA with equivalence class!" -msgstr "E868: 等価クラスを含むNFA構築に失敗しました!" - #, c-format msgid "E869: (NFA) Unknown operator '\\@%c'" msgstr "E869: (NFA) 未知のオペレータです: '\\@%c'" msgid "E870: (NFA regexp) Error reading repetition limits" -msgstr "E870: (NFA 正規表現) 繰り返しの制限回数を読込中にエラー" +msgstr "E870: (NFA 正規表現) 繰り返しの制限回数を読込み中にエラー" msgid "E871: (NFA regexp) Can't have a multi follow a multi" msgstr "E871: (NFA 正規表現) 繰り返し の後に 繰り返し はできません" @@ -4536,112 +5844,134 @@ msgstr "E873: (NFA 正規表現) 終端記号がありません" msgid "Could not open temporary log file for writing, displaying on stderr... " msgstr "" -"NFA正規表現エンジン用のログファイルを書込用として開けません。ログは標準エラー" -"出力に出力します。" +"NFA正規表現エンジン用のログファイルを書込み用として開けません。ログは標準エ" +"ラー出力に出力します。" msgid "E874: (NFA) Could not pop the stack!" msgstr "E874: (NFA) スタックをポップできません!" msgid "" -"E875: (NFA regexp) (While converting from postfix to NFA), too many states " +"E875: (NFA regexp) (While converting from postfix to NFA),too many states " "left on stack" msgstr "" -"E875: (NFA 正規表現) (後置文字列をNFAに変換中に) スタックに残されたステートが" -"多過ぎます" +"E875: (NFA) (後置文字列をNFAに変換中に) スタックに残されたステートが多過ぎま" +"す" msgid "E876: (NFA regexp) Not enough space to store the whole NFA " -msgstr "E876: (NFA 正規表現) NFA全体を保存するには空きスペースが足りません" +msgstr "E876: (NFA) NFA全体を保存するには空きスペースが足りません" -msgid "E878: (NFA) Could not allocate memory for branch traversal!" -msgstr "E878: (NFA) 現在横断中のブランチに十分なメモリを割り当てられません!" +msgid "" +"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " +"used " +msgstr "" +"E864: \\%#= には 0, 1 もしくは 2 のみが続けられます。正規表現エンジンは自動選" +"択されます" + +msgid "Switching to backtracking RE engine for pattern: " +msgstr "次のパターンにバックトラッキング RE エンジンを適用します: " #, c-format -msgid "block of %ld line yanked%s" -msgid_plural "block of %ld lines yanked%s" -msgstr[0] "%ld 行のブロックが%sヤンクされました" +msgid "Searching for \"%s\" under \"%s\" in \"%s\"" +msgstr "\"%s\" を \"%s\" 以下の \"%s\" から検索中" #, c-format -msgid "%ld line yanked%s" -msgid_plural "%ld lines yanked%s" -msgstr[0] "%ld 行が%sヤンクされました" +msgid "Searching for \"%s\" in \"%s\"" +msgstr "\"%s\" を \"%s\" から検索中" -msgid "" -"\n" -"Type Name Content" -msgstr "" -"\n" -"型式 名前 内容" +#, c-format +msgid "Searching for \"%s\"" +msgstr "\"%s\" を検索中" -msgid " VREPLACE" -msgstr " 仮想置換" +#, c-format +msgid "not found in '%s': \"%s\"" +msgstr "'%s' の中にはありません: \"%s\"" -msgid " REPLACE" -msgstr " 置換" +#, c-format +msgid "Searching for \"%s\" in runtime path" +msgstr "ランタイムパスで '%s' を検索しています" -msgid " REVERSE" -msgstr " 反転" +#, c-format +msgid "not found in runtime path: \"%s\"" +msgstr "ランタイムパスに見つかりませんでした: \"%s\"" -msgid " INSERT" -msgstr " 挿入" +#, c-format +msgid "Cannot source a directory: \"%s\"" +msgstr "ディレクトリは取込めません: \"%s\"" -msgid " (insert)" -msgstr " (挿入)" +#, c-format +msgid "could not source \"%s\"" +msgstr "\"%s\" を取込めません" -msgid " (replace)" -msgstr " (置換)" +#, c-format +msgid "line %<PRId64>: could not source \"%s\"" +msgstr "行 %<PRId64>: \"%s\" を取込めません" -msgid " (vreplace)" -msgstr " (仮想置換)" +#, c-format +msgid "sourcing \"%s\"" +msgstr "\"%s\" を取込み中" -msgid " Hebrew" -msgstr " ヘブライ" +#, c-format +msgid "line %<PRId64>: sourcing \"%s\"" +msgstr "行 %<PRId64>: %s を取込み中" -msgid " Arabic" -msgstr " アラビア" +#, c-format +msgid "finished sourcing %s" +msgstr "%s の取込みを完了" -msgid " (paste)" -msgstr " (貼り付け)" +msgid "modeline" +msgstr "モード行" -msgid " VISUAL" -msgstr " ビジュアル" +msgid "--cmd argument" +msgstr "--cmd 引数" -msgid " VISUAL LINE" -msgstr " ビジュアル 行" +msgid "-c argument" +msgstr "-c 引数" -msgid " VISUAL BLOCK" -msgstr " ビジュアル 矩形" +msgid "environment variable" +msgstr "環境変数" -msgid " SELECT" -msgstr " セレクト" +msgid "error handler" +msgstr "エラーハンドラ" -msgid " SELECT LINE" -msgstr " 行指向選択" +msgid "changed window size" +msgstr "ウィンドウサイズが変更されました" -msgid " SELECT BLOCK" -msgstr " 矩形選択" +msgid "Lua" +msgstr "Lua" -msgid "recording" -msgstr "記録中" +#, c-format +msgid "API client (channel id %<PRIu64>)" +msgstr "API クライアント (チャネル ID %<PRIu64>)" -msgid "E984: :scriptversion used outside of a sourced file" -msgstr "E984: :scriptversion が取込スクリプト以外で使用されました" +#, fuzzy +#~ msgid "anonymous :source" +#~ msgstr "匿名の :source" -#, c-format -msgid "E999: scriptversion not supported: %d" -msgstr "E999: scriptversion はサポートされていません: %d" +#, fuzzy, c-format +#~ msgid "anonymous :source (script id %d)" +#~ msgstr "匿名の :source (スクリプト ID %d)" -#, c-format -msgid "E383: Invalid search string: %s" -msgstr "E383: 無効な検索文字列です: %s" +msgid "W15: Warning: Wrong line separator, ^M may be missing" +msgstr "W15: 警告: 行区切が不正です。^M がないのでしょう" + +msgid "E167: :scriptencoding used outside of a sourced file" +msgstr "E167: :scriptencoding が取込みスクリプト以外で使用されました" + +msgid "E168: :finish used outside of a sourced file" +msgstr "E168: :finish が取込みスクリプト以外で使用されました" #, c-format -msgid "E384: search hit TOP without match for: %s" +msgid "E384: Search hit TOP without match for: %s" msgstr "E384: 上まで検索しましたが該当箇所はありません: %s" #, c-format -msgid "E385: search hit BOTTOM without match for: %s" +msgid "E385: Search hit BOTTOM without match for: %s" msgstr "E385: 下まで検索しましたが該当箇所はありません: %s" +#, c-format +msgid "E383: Invalid search string: %s" +msgstr "E383: 無効な検索文字列です: %s" + msgid "E386: Expected '?' or '/' after ';'" msgstr "E386: ';' のあとには '?' か '/' が期待されている" @@ -4655,7 +5985,7 @@ msgid "not found " msgstr "見つかりません " msgid "in path ---\n" -msgstr "パスに ----\n" +msgstr "パスに ---\n" msgid " (Already listed)" msgstr " (既に列挙)" @@ -4686,26 +6016,271 @@ msgstr "E388: 定義を見つけられません" msgid "E389: Couldn't find pattern" msgstr "E389: パターンを見つけられません" -msgid "Substitute " -msgstr "Substitute " +msgid "too few bytes read" +msgstr "読み込まれたバイト数が少なすぎます" + +#, fuzzy, c-format +#~ msgid "System error while skipping in ShaDa file: %s" +#~ msgstr "ShaDa ファイルのスキップ中にシステム エラーが発生しました: %s" + +#, c-format +msgid "" +"Error while reading ShaDa file: last entry specified that it occupies " +"%<PRIu64> bytes, but file ended earlier" +msgstr "" +"ShaDa ファイルの読み取り中にエラーが発生しました: 最後のエントリで %<PRIu64> " +"バイトを占めるように指定されましたが、ファイルはその前に終了しました" + +#, c-format +msgid "System error while closing ShaDa file: %s" +msgstr "ShaDa ファイルを閉じる時にシステムエラーが発生しました: %s" + +#, c-format +msgid "System error while writing ShaDa file: %s" +msgstr "ShaDa ファイルの書き込み中にシステムエラーが発生しました: %s" + +#, c-format +msgid "Reading ShaDa file \"%s\"%s%s%s%s" +msgstr "ShaDaファイル \"%s\"%s%s%s%s を読込み中" + +msgid " info" +msgstr " 情報" + +msgid " marks" +msgstr " マーク" + +msgid " oldfiles" +msgstr " 旧ファイル群" + +msgid " FAILED" +msgstr " 失敗" + +#, c-format +msgid "System error while opening ShaDa file %s for reading: %s" +msgstr "ShaDa ファイル %s を開く時にシステム エラーが発生しました: %s" + +#, fuzzy +#~ msgid "additional elements of ShaDa " +#~ msgstr "ShaDa の追加要素 " + +#, fuzzy +#~ msgid "additional data of ShaDa " +#~ msgstr "ShaDa の追加データ " + +#, c-format +msgid "Failed to write variable %s" +msgstr "変数 %s の書き込みに失敗しました" + +#, c-format +msgid "" +"Failed to parse ShaDa file due to a msgpack parser error at position " +"%<PRIu64>" +msgstr "" +"位置 %<PRIu64> での msgpack 構文解析エラーのため、ShaDa ファイルの解析に失敗" +"しました" + +#, c-format +msgid "" +"Failed to parse ShaDa file: incomplete msgpack string at position %<PRIu64>" +msgstr "" +"ShaDa ファイルの解析に失敗しました: 位置 %<PRIu64> に不完全な msgpack 文字列" +"があります" + +#, c-format +msgid "" +"Failed to parse ShaDa file: extra bytes in msgpack string at position " +"%<PRIu64>" +msgstr "" +"ShaDa ファイルの解析に失敗しました: 位置 %<PRIu64> の msgpack 文字列に余分な" +"バイトがあります" + +#, c-format +msgid "" +"System error while opening ShaDa file %s for reading to merge before writing " +"it: %s" +msgstr "" +"書き込み前にマージを実行するため、読み取り用に ShaDa ファイル %s を開いている" +"ときにシステムエラーが発生しました: %s" + +#, fuzzy, c-format +#~ msgid "E138: All %s.tmp.X files exist, cannot write ShaDa file!" +#~ msgstr "" +#~ "E138: 全ての %s.tmp.X ファイルが存在します。ShaDa ファイルを書き込むことがで" +#~ "きません!" + +#, c-format +msgid "System error while opening temporary ShaDa file %s for writing: %s" +msgstr "" +"書き込みの為一時的に ShaDa ファイル %s を開いているときにシステムエラーが発生" +"しました: %s" + +#, c-format +msgid "Failed to create directory %s for writing ShaDa file: %s" +msgstr "ShaDa ファイルを書き込むためのディレクトリ %s の作成に失敗しました: %s" + +#, c-format +msgid "System error while opening ShaDa file %s for writing: %s" +msgstr "" +"書き込みの為に ShaDa ファイル %s を開いているときにシステムエラーが発生しまし" +"た: %s" + +#, c-format +msgid "Writing ShaDa file \"%s\"" +msgstr "ShaDaファイル \"%s\" を書込み中" + +#, c-format +msgid "E137: ShaDa file is not writable: %s" +msgstr "E137: ShaDaファイルが書込みできません: %s" + +#, c-format +msgid "Failed setting uid and gid for file %s: %s" +msgstr "ファイル %s の uid と gid の設定に失敗しました: %s" + +#, c-format +msgid "Can't rename ShaDa file from %s to %s!" +msgstr "ShaDaファイルの名前を %s から %s へ変更できません!" + +#, c-format +msgid "Did not rename %s because %s does not look like a ShaDa file" +msgstr "" +"%s の名前を変更しませんでした。%s は ShaDa ファイルのように見えないためです" + +#, c-format +msgid "Did not rename %s to %s because there were errors during writing it" +msgstr "書き込み中にエラーが発生した為、%s の名前を %s に変更しませんでした" + +#, c-format +msgid "Do not forget to remove %s or rename it manually to %s." +msgstr "%s を削除するか、手動で名前を %s に変更することを忘れないでください。" + +#, c-format +msgid "System error while reading ShaDa file: %s" +msgstr "ShaDaファイルの読込み中にシステムエラーが発生しました: %s" + +#, c-format +msgid "System error while reading integer from ShaDa file: %s" +msgstr "ShaDa ファイルから整数を読み込む時にシステムエラーが発生しました: %s" + +#, c-format +msgid "" +"Error while reading ShaDa file: expected positive integer at position " +"%<PRIu64>, but got nothing" +msgstr "" +"ShaDa ファイルの読み込み中にエラーが発生しました: 位置 %<PRIu64> に正の整数が" +"必要ですが、何も取得されませんでした" + +#, c-format +msgid "" +"Error while reading ShaDa file: expected positive integer at position " +"%<PRIu64>" +msgstr "" +"ShaDa ファイルの読み込み中にエラーが発生しました: 位置 %<PRIu64> に正の整数が" +"必要です" + +#, c-format +msgid "" +"Error while reading ShaDa file: there is an item at position %<PRIu64> that " +"is stated to be too long" +msgstr "" +"ShaDa ファイルの読み込み中にエラーが発生しました: 位置 %<PRIu64> にある要素が" +"長すぎます" + +#, c-format +msgid "" +"Error while reading ShaDa file: there is an item at position %<PRIu64> that " +"must not be there: Missing items are for internal uses only" +msgstr "" +"ShaDa ファイルの読み込み中にエラーが発生しました: 位置 %<PRIu64> に存在しては" +"いけない要素があります: 欠落している要素は内部使用のみです" + +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry that is not a dictionary" +msgstr "" +"ShaDa ファイルの読み込み中にエラーが発生しました: 位置 %<PRIu64> のバッファリ" +"ストに辞書型ではないエントリが含まれています" + +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry with invalid line number" +msgstr "" +"ShaDa ファイルの読み込み中にエラーが発生しました: 位置 %<PRIu64> のバッファリ" +"ストに無効な行番号を持つエントリが含まれています" + +#, c-format +msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry with invalid column number" +msgstr "" +"ShaDa ファイルの読み込み中にエラーが発生しました: 位置 %<PRIu64> のバッファリ" +"ストに無効な列番号を持つエントリが含まれています" #, c-format msgid "" +"Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +"entry that does not have a file name" +msgstr "" +"ShaDa ファイルの読み込み中にエラーが発生しました: 位置 %<PRIu64> のバッファリ" +"ストにファイル名を持たないエントリが含まれています" + +msgid "" "\n" -"# Last %sSearch Pattern:\n" -"~" +"--- Signs ---" msgstr "" "\n" -"# 最後の %s検索パターン:\n" -"~" +"--- サイン ---" -msgid "E756: Spell checking is not enabled" -msgstr "E756: スペルチェックは無効化されています" +#, c-format +msgid "Signs for %s:" +msgstr "%s のサイン:" #, c-format -msgid "Warning: Cannot find word list \"%s_%s.spl\" or \"%s_ascii.spl\"" -msgstr "" -"警告: 単語リスト \"%s_%s.spl\" および \"%s_ascii.spl\" は見つかりません" +msgid " name=%s" +msgstr " 名前=%s" + +#, c-format +msgid " group=%s" +msgstr " グループ=%s" + +#~ msgid " line=%" +#~ msgstr "" + +#, c-format +msgid "E239: Invalid sign text: %s" +msgstr "E239: 無効なsignのテキストです: %s" + +#, c-format +msgid "E155: Unknown sign: %s" +msgstr "E155: 未知のsignです: %s" + +msgid " (not supported)" +msgstr " (非サポート)" + +#, c-format +msgid "E885: Not possible to change sign %s" +msgstr "E885: 変更できない sign です: %s" + +#, c-format +msgid "E157: Invalid sign ID: %<PRId32>" +msgstr "E157: 無効なsign識別子です: %<PRId32>" + +msgid "E934: Cannot jump to a buffer that does not have a name" +msgstr "E934: 名前の無いバッファへはジャンプできません" + +msgid "E159: Missing sign number" +msgstr "E159: signの番号がありません" + +#, c-format +msgid "E160: Unknown sign command: %s" +msgstr "E160: 未知のsignコマンドです: %s" + +msgid "E156: Missing sign name" +msgstr "E156: sign名がありません" + +msgid "E759: Format error in spell file" +msgstr "E759: スペルファイルのフォーマットエラーです" #, c-format msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\"" @@ -4713,7 +6288,7 @@ msgstr "" "警告: 単語リスト \"%s.%s.spl\" および \"%s.ascii.spl\" は見つかりません" msgid "E797: SpellFileMissing autocommand deleted buffer" -msgstr "E797: autocommand の SpellFileMissing がバッファを削除しました" +msgstr "E797: SpellFileMissing 自動コマンドがバッファを削除しました" #, c-format msgid "Warning: region %s not supported" @@ -4730,6 +6305,16 @@ msgid "E758: Truncated spell file" msgstr "E758: スペルファイルが切取られているようです" #, c-format +msgid "E782: Error while reading .sug file: %s" +msgstr "E782: .sug ファイルの読込み中にエラーが発生しました: %s" + +msgid "E783: Duplicate char in MAP entry" +msgstr "E783: MAP エントリに重複文字が存在します" + +msgid "E1280: Illegal character in word" +msgstr "E1280: 単語内に不正な文字があります" + +#, c-format msgid "Trailing text in %s line %d: %s" msgstr "%s (%d 行目) に続くテキスト: %s" @@ -4737,23 +6322,20 @@ msgstr "%s (%d 行目) に続くテキスト: %s" msgid "Affix name too long in %s line %d: %s" msgstr "%s (%d 行目) の affix 名が長過ぎます: %s" -msgid "E761: Format error in affix file FOL, LOW or UPP" -msgstr "" -"E761: affixファイルの FOL, LOW もしくは UPP のフォーマットにエラーがあります" - -msgid "E762: Character in FOL, LOW or UPP is out of range" -msgstr "E762: FOL, LOW もしくは UPP の文字が範囲外です" - msgid "Compressing word tree..." msgstr "単語ツリーを圧縮しています..." #, c-format msgid "Reading spell file \"%s\"" -msgstr "スペルファイル \"%s\" を読込中" +msgstr "スペルファイル \"%s\" を読込み中" msgid "E757: This does not look like a spell file" msgstr "E757: スペルファイルではないようです" +#, c-format +msgid "E5042: Failed to read spell file %s: %s" +msgstr "E5042: スペルファイル %s の読み込みに失敗しました: %s" + msgid "E771: Old spell file, needs to be updated" msgstr "E771: 古いスペルファイルなので、アップデートしてください" @@ -4780,12 +6362,8 @@ msgid "E781: .sug file doesn't match .spl file: %s" msgstr "E781: .sug ファイルが .spl ファイルと一致しません: %s" #, c-format -msgid "E782: error while reading .sug file: %s" -msgstr "E782: .sug ファイルの読込中にエラーが発生しました: %s" - -#, c-format msgid "Reading affix file %s..." -msgstr "affix ファイル %s を読込中..." +msgstr "affix ファイル %s を読込み中..." #, c-format msgid "Conversion failure for word in %s line %d: %s" @@ -4796,10 +6374,6 @@ msgid "Conversion in %s not supported: from %s to %s" msgstr "%s 内の次の変換はサポートされていません: %s から %s へ" #, c-format -msgid "Conversion in %s not supported" -msgstr "%s 内の変換はサポートされていません" - -#, c-format msgid "Invalid value for FLAG in %s line %d: %s" msgstr "%s 内の %d 行目の FLAG に無効な値があります: %s" @@ -4884,10 +6458,6 @@ msgstr "%s の %d 行目の MAP に重複した文字があります" msgid "Unrecognized or duplicate item in %s line %d: %s" msgstr "%s の %d 行目に 認識できないか重複した項目があります: %s" -#, c-format -msgid "Missing FOL/LOW/UPP line in %s" -msgstr "%s 行目に FOL/LOW/UPP がありません" - msgid "COMPOUNDSYLMAX used without SYLLABLE" msgstr "SYLLABLE が指定されない COMPOUNDSYLMAX" @@ -4929,8 +6499,8 @@ msgid "E760: No word count in %s" msgstr "E760: %s には単語数がありません" #, c-format -msgid "line %6d, word %6ld - %s" -msgstr "行 %6d, 単語 %6ld - %s" +msgid "line %6d, word %6d - %s" +msgstr "行 %6d, 単語 %6d - %s" #, c-format msgid "Duplicate word in %s line %d: %s" @@ -4952,54 +6522,47 @@ msgstr "非ASCII文字を含む %d 個の単語を無視しました (%s 内)" msgid "Reading word file %s..." msgstr "単語ファイル %s を読込み中..." -#, c-format -msgid "Duplicate /encoding= line ignored in %s line %d: %s" -msgstr "%s の %d 行目の 重複した /encoding= 行を無視しました: %s" +#~ msgid "Conversion failure for word in %s line %" +#~ msgstr "" -#, c-format -msgid "/encoding= line after word ignored in %s line %d: %s" -msgstr "%s の %d 行目の 単語の後の /encoding= 行を無視しました: %s" +#~ msgid "Duplicate /encoding= line ignored in %s line %" +#~ msgstr "" -#, c-format -msgid "Duplicate /regions= line ignored in %s line %d: %s" -msgstr "%s の %d 行目の 重複した /regions= 行を無視しました: %s" +#~ msgid "/encoding= line after word ignored in %s line %" +#~ msgstr "" -#, c-format -msgid "Too many regions in %s line %d: %s" -msgstr "%s の %d 行目、範囲指定が多過ぎます: %s" +#~ msgid "Duplicate /regions= line ignored in %s line %" +#~ msgstr "" -#, c-format -msgid "/ line ignored in %s line %d: %s" -msgstr "%s の %d 行目の 重複した / 行を無視しました: %s" +#~ msgid "Too many regions in %s line %" +#~ msgstr "" -#, c-format -msgid "Invalid region nr in %s line %d: %s" -msgstr "%s の %d 行目 無効な nr 領域です: %s" +#~ msgid "/ line ignored in %s line %" +#~ msgstr "" -#, c-format -msgid "Unrecognized flags in %s line %d: %s" -msgstr "%s の %d 行目 認識不能なフラグです: %s" +#~ msgid "Invalid region nr in %s line %" +#~ msgstr "" + +#~ msgid "Unrecognized flags in %s line %" +#~ msgstr "" #, c-format msgid "Ignored %d words with non-ASCII characters" msgstr "非ASCII文字を含む %d 個の単語を無視しました" -msgid "E845: Insufficient memory, word list will be incomplete" -msgstr "E845: メモリが足りないので、単語リストは不完全です" - #, c-format -msgid "Compressed %d of %d nodes; %d (%d%%) remaining" -msgstr "ノード %d 個(全 %d 個中) を圧縮しました; 残り %d (%d%%)" +msgid "Compressed %s of %d nodes; %d (%ld%%) remaining" +msgstr "ノード %s を圧縮 (全 %d 個中); 残り %d (%ld%%)" msgid "Reading back spell file..." -msgstr "スペルファイルを逆読込中" +msgstr "スペルファイルを逆読込み中..." msgid "Performing soundfolding..." msgstr "音声畳込みを実行中..." #, c-format -msgid "Number of words after soundfolding: %ld" -msgstr "音声畳込み後の総単語数: %ld" +msgid "Number of words after soundfolding: %<PRId64>" +msgstr "音声畳込み後の総単語数: %<PRId64>" #, c-format msgid "Total number of words: %d" @@ -5017,8 +6580,8 @@ msgid "E751: Output file name must not have region name" msgstr "E751: 出力ファイル名には範囲名を含められません" #, c-format -msgid "E754: Only up to %ld regions supported" -msgstr "E754: 範囲は %ld 個までしかサポートされていません" +msgid "E754: Only up to %d regions supported" +msgstr "E754: 範囲は %d 個までしかサポートされていません" #, c-format msgid "E755: Invalid region in %s" @@ -5035,13 +6598,16 @@ msgid "Done!" msgstr "実行しました!" #, c-format -msgid "E765: 'spellfile' does not have %ld entries" -msgstr "E765: 'spellfile' には %ld 個のエントリはありません" +msgid "E765: 'spellfile' does not have %<PRId64> entries" +msgstr "E765: 'spellfile' には %<PRId64> 個のエントリはありません" #, c-format msgid "Word '%.*s' removed from %s" msgstr "単語 '%.*s' が %s から削除されました" +msgid "Seek error in spellfile" +msgstr "スワップファイルでシークエラーです" + #, c-format msgid "Word '%.*s' added to %s" msgstr "単語 '%.*s' が %s へ追加されました" @@ -5049,39 +6615,129 @@ msgstr "単語 '%.*s' が %s へ追加されました" msgid "E763: Word characters differ between spell files" msgstr "E763: 単語の文字がスペルファイルと異なります" -msgid "E783: duplicate char in MAP entry" -msgstr "E783: MAP エントリに重複文字が存在します" +msgid "Sorry, no suggestions" +msgstr "残念ですが、修正候補はありません" -msgid "No Syntax items defined for this buffer" -msgstr "このバッファに定義された構文要素はありません" +#, c-format +msgid "Sorry, only %<PRId64> suggestions" +msgstr "残念ですが、修正候補は %<PRId64> 個しかありません" + +#, c-format +msgid "Change \"%.*s\" to:" +msgstr "\"%.*s\" を次へ変換:" + +#, c-format +msgid " < \"%.*s\"" +msgstr " < \"%.*s\"" + +msgid "[Help]" +msgstr "[ヘルプ]" + +msgid "[Preview]" +msgstr "[プレビュー]" + +#, c-format +msgid "E1500: Cannot mix positional and non-positional arguments: %s" +msgstr "E1500: 位置引数と非位置引数を混ぜることはできません: %s" + +#, c-format +msgid "E1501: format argument %d unused in $-style format: %s" +msgstr "" +"E1501: フォーマット引数 %d は $ スタイルフォーマットで使われていません: %s" + +#, c-format +msgid "" +"E1502: Positional argument %d used as field width reused as different type: " +"%s/%s" +msgstr "" +"E1502: フィールド幅として使われている位置引数 %d が異なる型に再利用されていま" +"す: %s/%s" -msgid "syntax conceal on" -msgstr "構文の conceal は現在 on です" +#, c-format +msgid "E1503: Positional argument %d out of bounds: %s" +msgstr "E1503: 位置引数 %d が範囲外です: %s" -msgid "syntax conceal off" -msgstr "構文の conceal は現在 off です" +#, c-format +msgid "E1504: Positional argument %d type used inconsistently: %s/%s" +msgstr "E1504: 位置引数 %d の型が一貫していません: %s/%s" + +#, c-format +msgid "E1505: Invalid format specifier: %s" +msgstr "E1505: 無効なフォーマット指示子です: %s" + +msgid "unknown" +msgstr "不明" + +msgid "int" +msgstr "int" + +msgid "long int" +msgstr "long int" + +msgid "long long int" +msgstr "long long int" + +msgid "signed size_t" +msgstr "signed size_t" + +msgid "unsigned int" +msgstr "unsigned int" + +msgid "unsigned long int" +msgstr "unsigned long int" + +msgid "unsigned long long int" +msgstr "unsigned long long int" + +msgid "size_t" +msgstr "size_t" + +msgid "pointer" +msgstr "pointer" + +msgid "percent" +msgstr "percent" + +msgid "char" +msgstr "char" + +msgid "string" +msgstr "string" + +msgid "float" +msgstr "float" + +msgid "E766: Insufficient arguments for printf()" +msgstr "E766: printf() の引数が不十分です" + +msgid "E807: Expected Float argument for printf()" +msgstr "E807: printf() の引数には浮動小数点数が期待されています" + +msgid "E767: Too many arguments to printf()" +msgstr "E767: printf() の引数が多過ぎます" #, c-format msgid "E390: Illegal argument: %s" msgstr "E390: 不正な引数です: %s" -msgid "syntax case ignore" -msgstr "構文の大文字小文字は現在 ignore です" +msgid "E395: Contains argument not accepted here" +msgstr "E395: この場所では引数containsは許可されていません" -msgid "syntax case match" -msgstr "構文の大文字小文字は現在 match です" +msgid "E844: Invalid cchar value" +msgstr "E844: 無効なccharの値です" -msgid "syntax spell toplevel" -msgstr "構文の spell は現在 toplevel です" +#, c-format +msgid "E890: Trailing char after ']': %s]%s" +msgstr "E890: ']' の後ろに余分な文字があります: %s]%s" -msgid "syntax spell notoplevel" -msgstr "構文の spell は現在 notoplevel です" +msgid "No Syntax items defined for this buffer" +msgstr "このバッファに定義された構文要素はありません" -msgid "syntax spell default" -msgstr "構文の spell は現在 default です" +msgid "'redrawtime' exceeded, syntax highlighting disabled" +msgstr "'redrawtime' を超過したため、構文ハイライトは無効化されます" -msgid "syntax iskeyword " -msgstr "構文用 iskeyword " +msgid "syntax iskeyword not set" +msgstr "構文用 iskeyword はセットされていません" #, c-format msgid "E391: No such syntax cluster: %s" @@ -5093,6 +6749,9 @@ msgstr "C言語風コメントから同期中" msgid "no syncing" msgstr "非同期" +msgid "syncing starts at the first line" +msgstr "最初の行で同期開始" + msgid "syncing starts " msgstr "同期開始 " @@ -5124,11 +6783,14 @@ msgstr "" msgid "E392: No such syntax cluster: %s" msgstr "E392: そのような構文クラスタはありません: %s" +msgid "from the first line" +msgstr "(最初の行から)" + msgid "minimal " -msgstr "minimal " +msgstr "最小 " msgid "maximal " -msgstr "maximal " +msgstr "最大 " msgid "; match " msgstr "; 該当 " @@ -5136,12 +6798,6 @@ msgstr "; 該当 " msgid " line breaks" msgstr " 個の改行" -msgid "E395: contains argument not accepted here" -msgstr "E395: この場所では引数containsは許可されていません" - -msgid "E844: invalid cchar value" -msgstr "E844: 無効なccharの値です" - msgid "E393: group[t]here not accepted here" msgstr "E393: ここではグループは許可されません" @@ -5153,17 +6809,13 @@ msgid "E397: Filename required" msgstr "E397: ファイル名が必要です" msgid "E847: Too many syntax includes" -msgstr "E847: 構文の取り込み(include)が多過ぎます" +msgstr "E847: 構文の取込み(include)が多過ぎます" #, c-format msgid "E789: Missing ']': %s" msgstr "E789: ']' がありません: %s" #, c-format -msgid "E890: trailing char after ']': %s]%s" -msgstr "E890: ']' の後ろに余分な文字があります: %s]%s" - -#, c-format msgid "E398: Missing '=': %s" msgstr "E398: '=' がありません: %s" @@ -5214,31 +6866,37 @@ msgstr "E409: 未知のグループ名: %s" #, c-format msgid "E410: Invalid :syntax subcommand: %s" -msgstr "E410: 無効な :syntax のサブコマンド: %s" +msgstr "E410: 無効な :syntax のサブコマンドです: %s" msgid "" " TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN" msgstr "" " TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN" -msgid "E555: at bottom of tag stack" +msgid "E73: Tag stack empty" +msgstr "E73: タグスタックが空です" + +#, c-format +msgid "E426: Tag not found: %s" +msgstr "E426: タグが見つかりません: %s" + +msgid "E555: At bottom of tag stack" msgstr "E555: タグスタックの末尾です" -msgid "E556: at top of tag stack" +msgid "E556: At top of tag stack" msgstr "E556: タグスタックの先頭です" -msgid "E425: Cannot go before first matching tag" -msgstr "E425: 最初の該当タグを越えて戻ることはできません" +msgid "E986: Cannot modify the tag stack within tagfunc" +msgstr "E986: tagfunc内のタグスタックを変更できません" -#, c-format -msgid "E426: tag not found: %s" -msgstr "E426: タグが見つかりません: %s" +msgid "E987: Invalid return value from tagfunc" +msgstr "E987: tagfuncからの戻り値が無効です" -msgid " # pri kind tag" -msgstr " # pri kind tag" +msgid "E1299: Window unexpectedly closed while searching for tags" +msgstr "E1299: タグを検索中に予期せずウィンドウが閉じられました" -msgid "file\n" -msgstr "ファイル\n" +msgid "E425: Cannot go before first matching tag" +msgstr "E425: 最初の該当タグを越えて戻ることはできません" msgid "E427: There is only one matching tag" msgstr "E427: 該当タグが1つだけしかありません" @@ -5264,6 +6922,12 @@ msgstr " タグを異なるcaseで使用します!" msgid "E429: File \"%s\" does not exist" msgstr "E429: ファイル \"%s\" がありません" +msgid " # pri kind tag" +msgstr " # pri kind tag" + +msgid "file\n" +msgstr "ファイル\n" + msgid "" "\n" " # TO tag FROM line in file/text" @@ -5272,23 +6936,16 @@ msgstr "" " # TO タグ FROM 行 in file/text" #, c-format -msgid "Searching tags file %s" -msgstr "タグファイル %s を検索中" - -#, c-format -msgid "E430: Tag file path truncated for %s\n" -msgstr "E430: タグファイルのパスが %s に切り捨てられました\n" - -msgid "Ignoring long line in tags file" -msgstr "タグファイル内の長い行を無視します" - -#, c-format msgid "E431: Format error in tags file \"%s\"" msgstr "E431: タグファイル \"%s\" のフォーマットにエラーがあります" #, c-format -msgid "Before byte %ld" -msgstr "直前の %ld バイト" +msgid "Before byte %<PRId64>" +msgstr "直前の %<PRId64> バイト" + +#, c-format +msgid "Searching tags file %s" +msgstr "タグファイル %s を検索中" #, c-format msgid "E432: Tags file not sorted: %s" @@ -5307,138 +6964,87 @@ msgstr "E435: タグを見つけられないので単に推測します!" msgid "Duplicate field name: %s" msgstr "重複したフィールド名: %s" -msgid "' not known. Available builtin terminals are:" -msgstr "' は未知です。現行の組み込み端末は次のとおりです:" - -msgid "defaulting to '" -msgstr "省略値を次のように設定します '" - -msgid "E557: Cannot open termcap file" -msgstr "E557: termcapファイルを開けません" - -msgid "E558: Terminal entry not found in terminfo" -msgstr "E558: terminfoに端末エントリを見つけられません" - -msgid "E559: Terminal entry not found in termcap" -msgstr "E559: termcapに端末エントリを見つけられません" - -#, c-format -msgid "E436: No \"%s\" entry in termcap" -msgstr "E436: termcapに \"%s\" のエントリがありません" - -msgid "E437: terminal capability \"cm\" required" -msgstr "E437: 端末に \"cm\" 機能が必要です" - msgid "" -"\n" -"--- Terminal keys ---" +"E856: \"assert_fails()\" second argument must be a string or a list with one " +"or two strings" msgstr "" -"\n" -"--- 端末キー ---" - -msgid "Cannot open $VIMRUNTIME/rgb.txt" -msgstr "$VIMRUNTIME/rgb.txtを開けません" +"E856: \"assert_fails()\" の第2引数は文字列または1個か2個の文字列のリストでな" +"ければなりません" -#, c-format -msgid "Kill job in \"%s\"?" -msgstr "\"%s\" 内のジョブを終了しますか?" +msgid "E1115: \"assert_fails()\" fourth argument must be a number" +msgstr "E1115: \"assert_fails()\" の第4引数は数字でなければなりません" -msgid "Terminal" -msgstr "端末" +msgid "E1116: \"assert_fails()\" fifth argument must be a string" +msgstr "E1116: \"assert_fails()\" の第5引数は文字列でなければなりません" -msgid "Terminal-finished" -msgstr "端末 (終了)" +msgid "E1142: Calling test_garbagecollect_now() while v:testing is not set" +msgstr "" +"E1142: v:testing が設定されていない状態で test_garbagecollect_now() を呼んで" +"います" -msgid "active" -msgstr "アクティブ" +msgid "Beep!" +msgstr "ビーッ!" -msgid "running" -msgstr "実行中" +msgid "E439: Undo list corrupt" +msgstr "E439: Undo リストが壊れています" -msgid "finished" -msgstr "終了" +msgid "E440: Undo line missing" +msgstr "E440: Undo する行がありません" #, c-format -msgid "E953: File exists: %s" -msgstr "E953: ファイルは既に存在します: %s" - -msgid "E955: Not a terminal buffer" -msgstr "E955: 端末バッファではありません" - -msgid "new shell started\n" -msgstr "新しいシェルを起動します\n" - -msgid "Vim: Error reading input, exiting...\n" -msgstr "Vim: 入力を読込み中のエラーにより終了します...\n" - -msgid "Used CUT_BUFFER0 instead of empty selection" -msgstr "空の選択領域のかわりにCUT_BUFFER0が使用されました" +msgid "E829: Write error in undo file: %s" +msgstr "E829: アンドゥファイルの書込みエラーです: %s" msgid "E881: Line count changed unexpectedly" msgstr "E881: 予期せず行カウントが変わりました" -msgid "No undo possible; continue anyway" -msgstr "可能なアンドゥはありません: とりあえず続けます" - #, c-format msgid "E828: Cannot open undo file for writing: %s" msgstr "E828: 書込み用にアンドゥファイルを開けません: %s" #, c-format +msgid "E5003: Unable to create directory \"%s\" for undo file: %s" +msgstr "E5003: アンドゥファイル用のディレクトリ \"%s\" を作成できません: %s" + +#, c-format msgid "E825: Corrupted undo file (%s): %s" msgstr "E825: アンドゥファイルが壊れています (%s): %s" msgid "Cannot write undo file in any directory in 'undodir'" -msgstr "'undodir'のディレクトリにアンドゥファイルを書き込めません" +msgstr "'undodir'のディレクトリにアンドゥファイルを書込めません" #, c-format msgid "Will not overwrite with undo file, cannot read: %s" -msgstr "アンドゥファイルとして読み込めないので上書きしません: %s" +msgstr "アンドゥファイルとして読込めないので上書きしません: %s" #, c-format msgid "Will not overwrite, this is not an undo file: %s" msgstr "アンドゥファイルではないので上書きしません: %s" msgid "Skipping undo file write, nothing to undo" -msgstr "対象がないのでアンドゥファイルの書き込みをスキップします" +msgstr "対象がないのでアンドゥファイルの書込みをスキップします" #, c-format msgid "Writing undo file: %s" -msgstr "アンドゥファイル書き込み中: %s" - -#, c-format -msgid "E829: write error in undo file: %s" -msgstr "E829: アンドゥファイルの書き込みエラーです: %s" +msgstr "アンドゥファイル書込み中: %s" #, c-format msgid "Not reading undo file, owner differs: %s" -msgstr "オーナーが異なるのでアンドゥファイルを読み込みません: %s" +msgstr "オーナーが異なるのでアンドゥファイルを読込みません: %s" #, c-format msgid "Reading undo file: %s" -msgstr "アンドゥファイル読込中: %s" +msgstr "アンドゥファイル読込み中: %s" #, c-format msgid "E822: Cannot open undo file for reading: %s" -msgstr "E822: アンドゥファイルを読込用として開けません: %s" +msgstr "E822: アンドゥファイルを読込み用として開けません: %s" #, c-format msgid "E823: Not an undo file: %s" msgstr "E823: アンドゥファイルではありません: %s" #, c-format -msgid "E832: Non-encrypted file has encrypted undo file: %s" -msgstr "E832: 非暗号化ファイルが暗号化されたアンドゥファイルを使ってます: %s" - -#, c-format -msgid "E826: Undo file decryption failed: %s" -msgstr "E826: 暗号化されたアンドゥファイルの解読に失敗しました: %s" - -#, c-format -msgid "E827: Undo file is encrypted: %s" -msgstr "E827: アンドゥファイルが暗号化されています: %s" - -#, c-format msgid "E824: Incompatible undo file: %s" msgstr "E824: 互換性の無いアンドゥファイルです: %s" @@ -5447,7 +7053,7 @@ msgstr "ファイルの内容が変わっているため、アンドゥ情報を #, c-format msgid "Finished reading undo file %s" -msgstr "アンドゥファイル %s の取込を完了" +msgstr "アンドゥファイル %s の取込みを完了" msgid "Already at oldest change" msgstr "既に一番古い変更です" @@ -5456,8 +7062,8 @@ msgid "Already at newest change" msgstr "既に一番新しい変更です" #, c-format -msgid "E830: Undo number %ld not found" -msgstr "E830: アンドゥ番号 %ld は見つかりません" +msgid "E830: Undo number %<PRId64> not found" +msgstr "E830: アンドゥ番号 %<PRId64> は見つかりません" msgid "E438: u_undo: line numbers wrong" msgstr "E438: u_undo: 行番号が間違っています" @@ -5481,14 +7087,19 @@ msgid "changes" msgstr "箇所変更しました" #, c-format -msgid "%ld %s; %s #%ld %s" -msgstr "%ld %s; %s #%ld %s" +msgid "%<PRId64> %s; %s #%<PRId64> %s" +msgstr "%<PRId64> %s; %s #%<PRId64> %s" + +msgid "after" +msgstr "後方" msgid "before" msgstr "前方" -msgid "after" -msgstr "後方" +#, c-format +msgid "%<PRId64> second ago" +msgid_plural "%<PRId64> seconds ago" +msgstr[0] "%<PRId64> 秒経過しています" msgid "Nothing to undo" msgstr "アンドゥ対象がありません" @@ -5496,411 +7107,110 @@ msgstr "アンドゥ対象がありません" msgid "number changes when saved" msgstr "通番 変更数 変更時期 保存済" -#, c-format -msgid "%ld seconds ago" -msgstr "%ld 秒経過しています" - msgid "E790: undojoin is not allowed after undo" msgstr "E790: undo の直後に undojoin はできません" -msgid "E439: undo list corrupt" -msgstr "E439: アンドゥリストが壊れています" - -msgid "E440: undo line missing" -msgstr "E440: アンドゥ行がありません" - -#, c-format -msgid "E122: Function %s already exists, add ! to replace it" -msgstr "E122: 関数 %s は定義済です、再定義するには ! を追加してください" - -msgid "E717: Dictionary entry already exists" -msgstr "E717: 辞書型内にエントリが既に存在します" - -msgid "E718: Funcref required" -msgstr "E718: 関数参照型が要求されます" - -#, c-format -msgid "E130: Unknown function: %s" -msgstr "E130: 未知の関数です: %s" - -#, c-format -msgid "E125: Illegal argument: %s" -msgstr "E125: 不正な引数です: %s" - -#, c-format -msgid "E853: Duplicate argument name: %s" -msgstr "E853: 引数名が重複しています: %s" - -msgid "E989: Non-default argument follows default argument" -msgstr "E989: 非デフォルト引数がデフォルト引数の後にあります" - -#, c-format -msgid "E740: Too many arguments for function %s" -msgstr "E740: 関数の引数が多過ぎます: %s" - -#, c-format -msgid "E116: Invalid arguments for function %s" -msgstr "E116: 関数の無効な引数です: %s" - -msgid "E132: Function call depth is higher than 'maxfuncdepth'" -msgstr "E132: 関数呼出の入れ子数が 'maxfuncdepth' を超えました" - -#, c-format -msgid "calling %s" -msgstr "%s を実行中です" - -#, c-format -msgid "%s aborted" -msgstr "%s が中断されました" - -#, c-format -msgid "%s returning #%ld" -msgstr "%s が #%ld を返しました" - -#, c-format -msgid "%s returning %s" -msgstr "%s が %s を返しました" - -msgid "E699: Too many arguments" -msgstr "E699: 引数が多過ぎます" - -#, c-format -msgid "E117: Unknown function: %s" -msgstr "E117: 未知の関数です: %s" - -#, c-format -msgid "E276: Cannot use function as a method: %s" -msgstr "E276: 関数をメソッドとして使用できません: %s" - -#, c-format -msgid "E933: Function was deleted: %s" -msgstr "E933: 関数は削除されました: %s" - -#, c-format -msgid "E119: Not enough arguments for function: %s" -msgstr "E119: 関数の引数が足りません: %s" - -#, c-format -msgid "E120: Using <SID> not in a script context: %s" -msgstr "E120: スクリプト以外で<SID>が使われました: %s" - -#, c-format -msgid "E725: Calling dict function without Dictionary: %s" -msgstr "E725: 辞書用関数が呼ばれましたが辞書がありません: %s" - -msgid "E129: Function name required" -msgstr "E129: 関数名が要求されます" - #, c-format -msgid "E128: Function name must start with a capital or \"s:\": %s" -msgstr "E128: 関数名は大文字か \"s:\" で始まらなければなりません: %s" +msgid "E179: Argument required for %s" +msgstr "E179: %s には引数が必要です" #, c-format -msgid "E884: Function name cannot contain a colon: %s" -msgstr "E884: 関数名にはコロンは含められません: %s" - -#, c-format -msgid "E123: Undefined function: %s" -msgstr "E123: 未定義の関数です: %s" - -#, c-format -msgid "E124: Missing '(': %s" -msgstr "E124: '(' がありません: %s" - -msgid "E862: Cannot use g: here" -msgstr "E862: ここでは g: は使えません" - -#, c-format -msgid "E932: Closure function should not be at top level: %s" -msgstr "E932: クロージャー関数はトップレベルに記述できません: %s" - -msgid "E126: Missing :endfunction" -msgstr "E126: :endfunction がありません" - -#, c-format -msgid "W22: Text found after :endfunction: %s" -msgstr "W22: :endfunction の後に文字があります: %s" - -#, c-format -msgid "E707: Function name conflicts with variable: %s" -msgstr "E707: 関数名が変数名と衝突します: %s" - -#, c-format -msgid "E127: Cannot redefine function %s: It is in use" -msgstr "E127: 関数 %s を再定義できません: 使用中です" - -#, c-format -msgid "E746: Function name does not match script file name: %s" -msgstr "E746: 関数名がスクリプトのファイル名と一致しません: %s" - -#, c-format -msgid "E131: Cannot delete function %s: It is in use" -msgstr "E131: 関数 %s を削除できません: 使用中です" +msgid "E184: No such user-defined command: %s" +msgstr "E184: そのユーザー定義コマンドはありません: %s" -msgid "E133: :return not inside a function" -msgstr "E133: 関数外に :return がありました" +msgid "E1208: -complete used without allowing arguments" +msgstr "E1208: -complete が引数を許可されずに使われています" #, c-format -msgid "%s (%s, compiled %s)" -msgstr "%s (%s, compiled %s)" - -msgid "" -"\n" -"MS-Windows 64-bit GUI version" -msgstr "" -"\n" -"MS-Windows 64 ビット GUI 版" - -msgid "" -"\n" -"MS-Windows 32-bit GUI version" -msgstr "" -"\n" -"MS-Windows 32 ビット GUI 版" - -msgid " with OLE support" -msgstr " with OLE サポート" - -msgid "" -"\n" -"MS-Windows 64-bit console version" -msgstr "" -"\n" -"MS-Windows 64 ビット コンソール 版" +msgid "E1237: No such user-defined command in current buffer: %s" +msgstr "E1237: 現在のバッファにそのユーザー定義コマンドはありません: %s" msgid "" "\n" -"MS-Windows 32-bit console version" +" Name Args Address Complete Definition" msgstr "" "\n" -"MS-Windows 32 ビット コンソール 版" +" 名前 引数 アドレス 補完 定義" -msgid "" -"\n" -"macOS version" -msgstr "" -"\n" -"macOS 版" - -msgid "" -"\n" -"macOS version w/o darwin feat." -msgstr "" -"\n" -"macOS 版 (darwin 無し)" - -msgid "" -"\n" -"OpenVMS version" -msgstr "" -"\n" -"OpenVMS 版" - -msgid "" -"\n" -"Included patches: " -msgstr "" -"\n" -"適用済パッチ: " - -msgid "" -"\n" -"Extra patches: " -msgstr "" -"\n" -"追加拡張パッチ: " - -msgid "Modified by " -msgstr "Modified by " - -msgid "" -"\n" -"Compiled " -msgstr "" -"\n" -"Compiled " - -msgid "by " -msgstr "by " - -msgid "" -"\n" -"Huge version " -msgstr "" -"\n" -"Huge 版 " - -msgid "" -"\n" -"Big version " -msgstr "" -"\n" -"Big 版 " - -msgid "" -"\n" -"Normal version " -msgstr "" -"\n" -"通常 版 " - -msgid "" -"\n" -"Small version " -msgstr "" -"\n" -"Small 版 " +msgid "No user-defined commands found" +msgstr "ユーザー定義コマンドが見つかりませんでした" -msgid "" -"\n" -"Tiny version " -msgstr "" -"\n" -"Tiny 版 " +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: 無効なアドレスタイプ値です: %s" -msgid "without GUI." -msgstr "without GUI." +#, c-format +msgid "E180: Invalid complete value: %s" +msgstr "E180: 無効な補完指定です: %s" -msgid "with GTK3 GUI." -msgstr "with GTK3 GUI." +msgid "E468: Completion argument only allowed for custom completion" +msgstr "E468: 補完引数はカスタム補完でしか使用できません" -msgid "with GTK2-GNOME GUI." -msgstr "with GTK2-GNOME GUI." +msgid "E467: Custom completion requires a function argument" +msgstr "E467: カスタム補完には引数として関数が必要です" -msgid "with GTK2 GUI." -msgstr "with GTK2 GUI." +msgid "E175: No attribute specified" +msgstr "E175: 属性は定義されていません" -msgid "with X11-Motif GUI." -msgstr "with X11-Motif GUI." +msgid "E176: Invalid number of arguments" +msgstr "E176: 引数の数が無効です" -msgid "with X11-neXtaw GUI." -msgstr "with X11-neXtaw GUI." +msgid "E177: Count cannot be specified twice" +msgstr "E177: カウントを2重指定することはできません" -msgid "with X11-Athena GUI." -msgstr "with X11-Athena GUI." +msgid "E178: Invalid default value for count" +msgstr "E178: カウントの省略値が無効です" -msgid "with Photon GUI." -msgstr "with Photon GUI." +#, c-format +msgid "E181: Invalid attribute: %s" +msgstr "E181: 無効な属性です: %s" -msgid "with GUI." -msgstr "with GUI." +#, c-format +msgid "E174: Command already exists: add ! to replace it: %s" +msgstr "E174: コマンドが既にあります: 再定義するには ! を追加してください: %s" -msgid "with Carbon GUI." -msgstr "with Carbon GUI." +msgid "E182: Invalid command name" +msgstr "E182: 無効なコマンド名です" -msgid "with Cocoa GUI." -msgstr "with Cocoa GUI." +msgid "E183: User defined commands must start with an uppercase letter" +msgstr "E183: ユーザー定義コマンドは英大文字で始まらなければなりません" -msgid " Features included (+) or not (-):\n" -msgstr " 機能の一覧 有効(+)/無効(-)\n" +msgid "E841: Reserved name, cannot be used for user defined command" +msgstr "E841: 予約名なので、ユーザー定義コマンドに利用できません" msgid " system vimrc file: \"" msgstr " システム vimrc: \"" -msgid " user vimrc file: \"" -msgstr " ユーザー vimrc: \"" - -msgid " 2nd user vimrc file: \"" -msgstr " 第2ユーザー vimrc: \"" - -msgid " 3rd user vimrc file: \"" -msgstr " 第3ユーザー vimrc: \"" - -msgid " user exrc file: \"" -msgstr " ユーザー exrc: \"" - -msgid " 2nd user exrc file: \"" -msgstr " 第2ユーザー exrc: \"" - -msgid " system gvimrc file: \"" -msgstr " システム gvimrc: \"" - -msgid " user gvimrc file: \"" -msgstr " ユーザー gvimrc: \"" - -msgid "2nd user gvimrc file: \"" -msgstr " 第2ユーザー gvimrc: \"" - -msgid "3rd user gvimrc file: \"" -msgstr " 第3ユーザー gvimrc: \"" - -msgid " defaults file: \"" -msgstr " デフォルトファイル: \"" - -msgid " system menu file: \"" -msgstr " システムメニュー: \"" - msgid " fall-back for $VIM: \"" msgstr " 省略時の $VIM: \"" msgid " f-b for $VIMRUNTIME: \"" msgstr "省略時の $VIMRUNTIME: \"" -msgid "Compilation: " -msgstr "コンパイル: " +msgid "Nvim is open source and freely distributable" +msgstr "Nvim はオープンソースであり自由に配布可能です" -msgid "Compiler: " -msgstr "コンパイラ: " +msgid "type :help nvim<Enter> if you are new! " +msgstr "初めての人は :help nvim<Enter> " -msgid "Linking: " -msgstr "リンク: " +msgid "type :checkhealth<Enter> to optimize Nvim" +msgstr "Nvimを最適化するには :checkhealth<Enter> " -msgid " DEBUG BUILD" -msgstr "デバッグビルド" - -msgid "VIM - Vi IMproved" -msgstr "VIM - Vi IMproved" - -msgid "version " -msgstr "version " +msgid "type :q<Enter> to exit " +msgstr "終了するには :q<Enter> " -msgid "by Bram Moolenaar et al." -msgstr "by Bram Moolenaar 他." +msgid "type :help<Enter> for help " +msgstr "ヘルプを見るには :help<Enter> " -msgid "Vim is open source and freely distributable" -msgstr "Vim はオープンソースであり自由に配布可能です" +#, c-format +msgid "type :help news<Enter> to see changes in v%s.%s" +msgstr "v%s.%sの変更点は :help news<Enter> " msgid "Help poor children in Uganda!" msgstr "ウガンダの恵まれない子供たちに援助を!" msgid "type :help iccf<Enter> for information " -msgstr "詳細な情報は :help iccf<Enter> " - -msgid "type :q<Enter> to exit " -msgstr "終了するには :q<Enter> " - -msgid "type :help<Enter> or <F1> for on-line help" -msgstr "オンラインヘルプは :help<Enter> か <F1> " - -msgid "type :help version8<Enter> for version info" -msgstr "バージョン情報は :help version8<Enter> " - -msgid "Running in Vi compatible mode" -msgstr "Vi互換モードで動作中" - -msgid "type :set nocp<Enter> for Vim defaults" -msgstr "Vim推奨値にするには :set nocp<Enter> " - -msgid "type :help cp-default<Enter> for info on this" -msgstr "詳細な情報は :help cp-default<Enter>" - -msgid "menu Help->Orphans for information " -msgstr "詳細はメニューの ヘルプ->孤児 を参照して下さい " - -msgid "Running modeless, typed text is inserted" -msgstr "モード無で実行中。タイプした文字が挿入されます" - -msgid "menu Edit->Global Settings->Toggle Insert Mode " -msgstr "メニューの 編集->全体設定->挿入(初心者)モード切替 " - -msgid " for two modes " -msgstr " でモード有に " - -msgid "menu Edit->Global Settings->Toggle Vi Compatible" -msgstr "メニューの 編集->全体設定->Vi互換モード切替 " - -msgid " for Vim defaults " -msgstr " でVimとして動作 " +msgstr "詳細な情報は :help iccf<Enter> " msgid "Sponsor Vim development!" msgstr "Vimの開発を応援してください!" @@ -5909,772 +7219,201 @@ msgid "Become a registered Vim user!" msgstr "Vimの登録ユーザーになってください!" msgid "type :help sponsor<Enter> for information " -msgstr "詳細な情報は :help sponsor<Enter> " +msgstr "詳細な情報は :help sponsor<Enter> " msgid "type :help register<Enter> for information " -msgstr "詳細な情報は :help register<Enter> " - -msgid "menu Help->Sponsor/Register for information " -msgstr "詳細はメニューの ヘルプ->スポンサー/登録 を参照して下さい" - -msgid "Already only one window" -msgstr "既にウィンドウは1つしかありません" - -msgid "E441: There is no preview window" -msgstr "E441: プレビューウィンドウがありません" - -msgid "E442: Can't split topleft and botright at the same time" -msgstr "E442: 左上と右下を同時に分割することはできません" - -msgid "E443: Cannot rotate when another window is split" -msgstr "E443: 他のウィンドウが分割されている時には順回できません" - -msgid "E444: Cannot close last window" -msgstr "E444: 最後のウィンドウを閉じることはできません" - -msgid "E813: Cannot close autocmd window" -msgstr "E813: autocmdウィンドウは閉じられません" - -msgid "E814: Cannot close window, only autocmd window would remain" -msgstr "E814: autocmdウィンドウしか残らないため、ウィンドウは閉じられません" - -msgid "E445: Other window contains changes" -msgstr "E445: 他のウィンドウには変更があります" - -msgid "E446: No file name under cursor" -msgstr "E446: カーソルの下にファイル名がありません" - -#, c-format -msgid "E447: Can't find file \"%s\" in path" -msgstr "E447: pathには \"%s\" というファイルがありません" - -#, c-format -msgid "E799: Invalid ID: %ld (must be greater than or equal to 1)" -msgstr "E799: 無効な ID: %ld (1 以上でなければなりません)" - -#, c-format -msgid "E801: ID already taken: %ld" -msgstr "E801: ID はすでに利用中です: %ld" - -#, c-format -msgid "E802: Invalid ID: %ld (must be greater than or equal to 1)" -msgstr "E802: 無効な ID: %ld (1 以上でなければなりません)" - -#, c-format -msgid "E803: ID not found: %ld" -msgstr "E803: ID はありません: %ld" - -msgid "Edit with &multiple Vims" -msgstr "複数のVimで編集する (&M)" - -msgid "Edit with single &Vim" -msgstr "1つのVimで編集する (&V)" - -msgid "Diff with Vim" -msgstr "Vimで差分を表示する" - -msgid "Edit with &Vim" -msgstr "Vimで編集する (&V)" - -msgid "Edit with existing Vim - " -msgstr "起動済のVimで編集する - " - -msgid "Edits the selected file(s) with Vim" -msgstr "選択したファイルをVimで編集する" - -msgid "Error creating process: Check if gvim is in your path!" -msgstr "プロセスの作成に失敗: gvimが環境変数PATH上にあるか確認してください!" - -msgid "gvimext.dll error" -msgstr "gvimext.dll エラー" - -msgid "Path length too long!" -msgstr "パスが長過ぎます!" - -msgid "--No lines in buffer--" -msgstr "--バッファに行がありません--" - -msgid "E470: Command aborted" -msgstr "E470: コマンドが中断されました" - -msgid "E471: Argument required" -msgstr "E471: 引数が必要です" - -msgid "E10: \\ should be followed by /, ? or &" -msgstr "E10: \\ の後は / か ? か & でなければなりません" - -msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits" -msgstr "E11: コマンドラインでは無効です; <CR>で実行, CTRL-Cでやめる" - -msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search" -msgstr "" -"E12: 現在のディレクトリやタグ検索ではexrc/vimrcのコマンドは許可されません" - -msgid "E171: Missing :endif" -msgstr "E171: :endif がありません" - -msgid "E600: Missing :endtry" -msgstr "E600: :endtry がありません" - -msgid "E170: Missing :endwhile" -msgstr "E170: :endwhile がありません" - -msgid "E170: Missing :endfor" -msgstr "E170: :endfor がありません" - -msgid "E588: :endwhile without :while" -msgstr "E588: :while のない :endwhile があります" - -msgid "E588: :endfor without :for" -msgstr "E588: :endfor のない :for があります" - -msgid "E13: File exists (add ! to override)" -msgstr "E13: ファイルが存在します (! を追加で上書)" - -msgid "E472: Command failed" -msgstr "E472: コマンドが失敗しました" - -#, c-format -msgid "E234: Unknown fontset: %s" -msgstr "E234: 未知のフォントセット: %s" - -#, c-format -msgid "E235: Unknown font: %s" -msgstr "E235: 未知のフォント: %s" - -#, c-format -msgid "E236: Font \"%s\" is not fixed-width" -msgstr "E236: フォント \"%s\" は固定幅ではありません" - -msgid "E473: Internal error" -msgstr "E473: 内部エラーです" - -#, c-format -msgid "E685: Internal error: %s" -msgstr "E685: 内部エラーです: %s" - -msgid "Interrupted" -msgstr "割込まれました" - -msgid "E474: Invalid argument" -msgstr "E474: 無効な引数です" - -#, c-format -msgid "E475: Invalid argument: %s" -msgstr "E475: 無効な引数です: %s" - -#, c-format -msgid "E475: Invalid value for argument %s" -msgstr "E475: 引数 %s に対して無効な値です" - -#, c-format -msgid "E475: Invalid value for argument %s: %s" -msgstr "E475: 引数 %s に対して無効な値です: %s" - -#, c-format -msgid "E15: Invalid expression: %s" -msgstr "E15: 無効な式です: %s" - -msgid "E16: Invalid range" -msgstr "E16: 無効な範囲です" - -msgid "E476: Invalid command" -msgstr "E476: 無効なコマンドです" - -#, c-format -msgid "E17: \"%s\" is a directory" -msgstr "E17: \"%s\" はディレクトリです" - -#, c-format -msgid "E364: Library call failed for \"%s()\"" -msgstr "E364: \"%s\"() のライブラリ呼出に失敗しました" - -msgid "E667: Fsync failed" -msgstr "E667: fsync に失敗しました" - -#, c-format -msgid "E448: Could not load library function %s" -msgstr "E448: ライブラリの関数 %s をロードできませんでした" - -msgid "E19: Mark has invalid line number" -msgstr "E19: マークに無効な行番号が指定されていました" - -msgid "E20: Mark not set" -msgstr "E20: マークは設定されていません" - -msgid "E21: Cannot make changes, 'modifiable' is off" -msgstr "E21: 'modifiable' がオフなので、変更できません" - -msgid "E22: Scripts nested too deep" -msgstr "E22: スクリプトの入れ子が深過ぎます" - -msgid "E23: No alternate file" -msgstr "E23: 副ファイルはありません" - -msgid "E24: No such abbreviation" -msgstr "E24: そのような短縮入力はありません" - -msgid "E477: No ! allowed" -msgstr "E477: ! は許可されていません" - -msgid "E25: GUI cannot be used: Not enabled at compile time" -msgstr "E25: GUIは使用不可能です: コンパイル時に無効にされています" - -msgid "E26: Hebrew cannot be used: Not enabled at compile time\n" -msgstr "E26: ヘブライ語は使用不可能です: コンパイル時に無効にされています\n" - -msgid "E27: Farsi cannot be used: Not enabled at compile time\n" -msgstr "E27: ペルシア語は使用不可能です: コンパイル時に無効にされています\n" - -msgid "E800: Arabic cannot be used: Not enabled at compile time\n" -msgstr "E800: アラビア語は使用不可能です: コンパイル時に無効にされています\n" - -#, c-format -msgid "E28: No such highlight group name: %s" -msgstr "E28: そのような名のハイライトグループはありません: %s" - -msgid "E29: No inserted text yet" -msgstr "E29: まだテキストが挿入されていません" - -msgid "E30: No previous command line" -msgstr "E30: 以前にコマンド行がありません" - -msgid "E31: No such mapping" -msgstr "E31: そのようなマッピングはありません" - -msgid "E479: No match" -msgstr "E479: 該当はありません" - -#, c-format -msgid "E480: No match: %s" -msgstr "E480: 該当はありません: %s" - -msgid "E32: No file name" -msgstr "E32: ファイル名がありません" - -msgid "E33: No previous substitute regular expression" -msgstr "E33: 正規表現置換がまだ実行されていません" - -msgid "E34: No previous command" -msgstr "E34: コマンドがまだ実行されていません" - -msgid "E35: No previous regular expression" -msgstr "E35: 正規表現がまだ実行されていません" - -msgid "E481: No range allowed" -msgstr "E481: 範囲指定は許可されていません" - -msgid "E36: Not enough room" -msgstr "E36: ウィンドウに十分な高さもしくは幅がありません" +msgstr "詳細な情報は :help register<Enter>" #, c-format -msgid "E247: no registered server named \"%s\"" -msgstr "E247: %s という名前の登録されたサーバーはありません" +msgid "E15: Invalid control character present in input: %.*s" +msgstr "E15: 無効な制御文字が入力されました: %.*s" #, c-format -msgid "E482: Can't create file %s" -msgstr "E482: ファイル %s を作成できません" - -msgid "E483: Can't get temp file name" -msgstr "E483: 一時ファイルの名前を取得できません" +msgid "E112: Option name missing: %.*s" +msgstr "E112: オプション名がありません: %.*s" #, c-format -msgid "E484: Can't open file %s" -msgstr "E484: ファイル \"%s\" を開けません" +msgid "E15: Unexpected EOC character: %.*s" +msgstr "E15: 予期しない EOC 文字: %.*s" #, c-format -msgid "E485: Can't read file %s" -msgstr "E485: ファイル %s を読込めません" - -msgid "E38: Null argument" -msgstr "E38: 引数が空です" +msgid "E15: Unidentified character: %.*s" +msgstr "E15: 識別できない文字: %.*s" -msgid "E39: Number expected" -msgstr "E39: 数値が要求されています" +#, fuzzy, c-format +#~ msgid "E15: Operator is not associative: %.*s" +#~ msgstr "E15: 演算子は結合的ではありません: %.*s" #, c-format -msgid "E40: Can't open errorfile %s" -msgstr "E40: エラーファイル %s を開けません" +msgid "E15: Missing operator: %.*s" +msgstr "E15: 演算子がありません: %.*s" -msgid "E233: cannot open display" -msgstr "E233: ディスプレイを開けません" +#, fuzzy, c-format +#~ msgid "E15: Expected lambda arguments list or arrow: %.*s" +#~ msgstr "E15: ラムダ引数リストかアローが必要です: %.*s" -msgid "E41: Out of memory!" -msgstr "E41: メモリが尽き果てました!" - -msgid "Pattern not found" -msgstr "パターンは見つかりませんでした" +#, fuzzy, c-format +#~ msgid "E15: Expected value part of assignment lvalue: %.*s" +#~ msgstr "E15: 代入左辺値の値部分が必要です: %.*s" #, c-format -msgid "E486: Pattern not found: %s" -msgstr "E486: パターンは見つかりませんでした: %s" +msgid "E15: Expected assignment operator or subscript: %.*s" +msgstr "E15: 代入演算子または添え字が必要です: %.*s" -msgid "E487: Argument must be positive" -msgstr "E487: 引数は正の値でなければなりません" - -msgid "E459: Cannot go back to previous directory" -msgstr "E459: 前のディレクトリに戻れません" - -msgid "E42: No Errors" -msgstr "E42: エラーはありません" - -msgid "E776: No location list" -msgstr "E776: ロケーションリストはありません" - -msgid "E43: Damaged match string" -msgstr "E43: 該当文字列が破損しています" - -msgid "E44: Corrupted regexp program" -msgstr "E44: 不正な正規表現プログラムです" +#, fuzzy +#~ msgid "E15: Unexpected " +#~ msgstr "E15: 予期しない " -msgid "E45: 'readonly' option is set (add ! to override)" -msgstr "E45: 'readonly' オプションが設定されています (! を追加で上書き)" +#, fuzzy, c-format +#~ msgid "E15: Unexpected multiplication-like operator: %.*s" +#~ msgstr "E15: 予期しない乗算のような演算子: %.*s" -msgid "E995: Cannot modify existing variable" -msgstr "E995: 既存の変数を変更できません" +msgid "E15: Environment variable name missing" +msgstr "E15: 環境変数名がありません" #, c-format -msgid "E46: Cannot change read-only variable \"%s\"" -msgstr "E46: 読取専用変数 \"%s\" には値を設定できません" +msgid "E15: Expected value, got comparison operator: %.*s" +msgstr "E15: 値が必要ですが比較演算子を受け取りました: %.*s" #, c-format -msgid "E794: Cannot set variable in the sandbox: \"%s\"" -msgstr "E794: サンドボックスでは変数 \"%s\" に値を設定できません" - -msgid "E713: Cannot use empty key for Dictionary" -msgstr "E713: 辞書型に空のキーを使うことはできません" - -msgid "E715: Dictionary required" -msgstr "E715: 辞書型が必要です" +msgid "E15: Expected value, got comma: %.*s" +msgstr "E15: 値が必要ですがカンマを受け取りました: %.*s" #, c-format -msgid "E684: list index out of range: %ld" -msgstr "E684: リストのインデックスが範囲外です: %ld" +msgid "E15: Comma outside of call, lambda or literal: %.*s" +msgstr "E15: カンマが呼び出し、ラムダ、またはリテラルの外です: %.*s" #, c-format -msgid "E118: Too many arguments for function: %s" -msgstr "E118: 関数の引数が多過ぎます: %s" +msgid "E15: Colon outside of dictionary or ternary operator: %.*s" +msgstr "E15: 辞書外のコロンまたは三項演算子: %.*s" #, c-format -msgid "E716: Key not present in Dictionary: %s" -msgstr "E716: 辞書型にキーが存在しません: %s" - -msgid "E714: List required" -msgstr "E714: リスト型が必要です" +msgid "E15: Expected value, got closing bracket: %.*s" +msgstr "E15: 値が必要ですが閉じ括弧を受け取りました: %.*s" #, c-format -msgid "E712: Argument of %s must be a List or Dictionary" -msgstr "E712: %s の引数はリスト型または辞書型でなければなりません" - -msgid "E47: Error while reading errorfile" -msgstr "E47: エラーファイルの読込中にエラーが発生しました" - -msgid "E48: Not allowed in sandbox" -msgstr "E48: サンドボックスでは許されません" - -msgid "E523: Not allowed here" -msgstr "E523: ここでは許可されません" - -msgid "E359: Screen mode setting not supported" -msgstr "E359: スクリーンモードの設定には対応していません" - -msgid "E49: Invalid scroll size" -msgstr "E49: 無効なスクロール量です" - -msgid "E91: 'shell' option is empty" -msgstr "E91: 'shell' オプションが空です" - -msgid "E255: Couldn't read in sign data!" -msgstr "E255: sign のデータを読込めませんでした" - -msgid "E72: Close error on swap file" -msgstr "E72: スワップファイルのクローズ時エラーです" - -msgid "E73: tag stack empty" -msgstr "E73: タグスタックが空です" - -msgid "E74: Command too complex" -msgstr "E74: コマンドが複雑過ぎます" - -msgid "E75: Name too long" -msgstr "E75: 名前が長過ぎます" - -msgid "E76: Too many [" -msgstr "E76: [ が多過ぎます" - -msgid "E77: Too many file names" -msgstr "E77: ファイル名が多過ぎます" - -msgid "E488: Trailing characters" -msgstr "E488: 余分な文字が後ろにあります" - -msgid "E78: Unknown mark" -msgstr "E78: 未知のマーク" - -msgid "E79: Cannot expand wildcards" -msgstr "E79: ワイルドカードを展開できません" - -msgid "E591: 'winheight' cannot be smaller than 'winminheight'" -msgstr "E591: 'winheight' は 'winminheight' より小さくできません" - -msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'" -msgstr "E592: 'winwidth' は 'winminwidth' より小さくできません" - -msgid "E80: Error while writing" -msgstr "E80: 書込み中のエラー" - -msgid "E939: Positive count required" -msgstr "E939: 正のカウントが必要です" - -msgid "E81: Using <SID> not in a script context" -msgstr "E81: スクリプト以外で<SID>が使われました" +msgid "E475: Unable to assign to empty list: %.*s" +msgstr "E475: 空のリストに割り当てることができません: %.*s" #, c-format -msgid "E107: Missing parentheses: %s" -msgstr "E107: カッコ '(' がありません: %s" - -msgid "E449: Invalid expression received" -msgstr "E449: 無効な式を受け取りました" - -msgid "E463: Region is guarded, cannot modify" -msgstr "E463: 領域が保護されているので、変更できません" - -msgid "E744: NetBeans does not allow changes in read-only files" -msgstr "E744: NetBeans は読込専用ファイルを変更することを許しません" - -msgid "E363: pattern uses more memory than 'maxmempattern'" -msgstr "E363: パターンが 'maxmempattern' 以上のメモリを使用します" - -msgid "E749: empty buffer" -msgstr "E749: バッファが空です" - -#, c-format -msgid "E86: Buffer %ld does not exist" -msgstr "E86: バッファ %ld はありません" - -msgid "E682: Invalid search pattern or delimiter" -msgstr "E682: 検索パターンか区切り記号が不正です" - -msgid "E139: File is loaded in another buffer" -msgstr "E139: 同じ名前のファイルが他のバッファで読込まれています" +msgid "E15: Unexpected closing figure brace: %.*s" +msgstr "E15: 予期しない閉じ波括弧: %.*s" #, c-format -msgid "E764: Option '%s' is not set" -msgstr "E764: オプション '%s' は設定されていません" - -msgid "E850: Invalid register name" -msgstr "E850: 無効なレジスタ名です" +msgid "E475: Nested lists not allowed when assigning: %.*s" +msgstr "E475: 割り当て時に入れ子のリストは許可されていません: %.*s" #, c-format -msgid "E919: Directory not found in '%s': \"%s\"" -msgstr "E919: ディレクトリが '%s' の中にありません: \"%s\"" - -msgid "E952: Autocommand caused recursive behavior" -msgstr "E952: Autocommandが再帰を引き起こしました" - -msgid "search hit TOP, continuing at BOTTOM" -msgstr "上まで検索したので下に戻ります" - -msgid "search hit BOTTOM, continuing at TOP" -msgstr "下まで検索したので上に戻ります" +msgid "E15: Expected value, got closing figure brace: %.*s" +msgstr "E15: 値が必要ですが '}' を受け取りました: %.*s" #, c-format -msgid "Need encryption key for \"%s\"" -msgstr "暗号キーが必要です: \"%s\"" - -msgid "empty keys are not allowed" -msgstr "空のキーは許可されていません" +msgid "E15: Don't know what figure brace means: %.*s" +msgstr "E15: 波括弧の意味がわかりません: %.*s" -msgid "dictionary is locked" -msgstr "辞書はロックされています" +#, fuzzy, c-format +#~ msgid "E15: Unexpected arrow: %.*s" +#~ msgstr "E15: 予期しないアロー: %.*s" -msgid "list is locked" -msgstr "リストはロックされています" +#, fuzzy, c-format +#~ msgid "E15: Arrow outside of lambda: %.*s" +#~ msgstr "E15: アローがラムダの外にあります: %.*s" #, c-format -msgid "failed to add key '%s' to dictionary" -msgstr "辞書にキー '%s' を追加するのに失敗しました" +msgid "E15: Unexpected dot: %.*s" +msgstr "E15: 予期しないドット: %.*s" -#, c-format -msgid "index must be int or slice, not %s" -msgstr "インデックスは %s ではなく整数かスライスにしてください" +#, fuzzy, c-format +#~ msgid "E15: Cannot concatenate in assignments: %.*s" +#~ msgstr "E15: 割り当て内で連結できません: %.*s" #, c-format -msgid "expected str() or unicode() instance, but got %s" -msgstr "str() もしくは unicode() のインスタンスが期待されているのに %s でした" +msgid "E15: Expected value, got parenthesis: %.*s" +msgstr "E15: 値が必要ですが丸括弧を受け取りました: %.*s" #, c-format -msgid "expected bytes() or str() instance, but got %s" -msgstr "bytes() もしくは str() のインスタンスが期待されているのに %s でした" +msgid "E15: Unexpected closing parenthesis: %.*s" +msgstr "E15: 予期しない閉じ丸括弧: %.*s" #, c-format -msgid "" -"expected int(), long() or something supporting coercing to long(), but got %s" -msgstr "long() かそれへ変換可能なものが期待されているのに %s でした" +msgid "E15: Expected value, got question mark: %.*s" +msgstr "E15: 値が必要ですが疑問符を受け取りました: %.*s" #, c-format -msgid "expected int() or something supporting coercing to int(), but got %s" -msgstr "int() かそれへ変換可能なものが期待されているのに %s でした" - -msgid "value is too large to fit into C int type" -msgstr "C言語の int 型としては値が大き過ぎます" - -msgid "value is too small to fit into C int type" -msgstr "C言語の int 型としては値が小さ過ぎます" - -msgid "number must be greater than zero" -msgstr "数値は 0 より大きくなければなりません" - -msgid "number must be greater or equal to zero" -msgstr "数値は 0 かそれ以上でなければなりません" - -msgid "can't delete OutputObject attributes" -msgstr "OutputObject属性を消せません" +msgid "E114: Missing double quote: %.*s" +msgstr "E114: ダブルクォートがありません: %.*s" #, c-format -msgid "invalid attribute: %s" -msgstr "無効な属性です: %s" - -msgid "E264: Python: Error initialising I/O objects" -msgstr "E264: Python: I/Oオブジェクトの初期化エラー" +msgid "E115: Missing single quote: %.*s" +msgstr "E115: シングルクォートがありません: %.*s" -msgid "failed to change directory" -msgstr "辞書の変更に失敗しました" +#, fuzzy, c-format +#~ msgid "E475: Expected closing bracket to end list assignment lvalue: %.*s" +#~ msgstr "E475: リスト割り当ての左辺値を終了するための閉じ括弧が必要です: %.*s" #, c-format -msgid "expected 3-tuple as imp.find_module() result, but got %s" -msgstr "imp.find_module() が %s を返しました (期待値: 3 要素のタプル)" - -#, c-format -msgid "expected 3-tuple as imp.find_module() result, but got tuple of size %d" -msgstr "imp.find_module() が %d 要素のタプルを返しました (期待値: 3)" - -msgid "internal error: imp.find_module returned tuple with NULL" -msgstr "内部エラー: imp.find_module が NULL を含むタプルを返しました" - -msgid "cannot delete vim.Dictionary attributes" -msgstr "vim.Dictionary属性は消せません" - -msgid "cannot modify fixed dictionary" -msgstr "固定された辞書は変更できません" +msgid "E15: Misplaced assignment: %.*s" +msgstr "E15: 割り当てが間違っています: %.*s" #, c-format -msgid "cannot set attribute %s" -msgstr "属性 %s は設定できません" - -msgid "hashtab changed during iteration" -msgstr "イテレーション中に hashtab が変更されました" +msgid "E15: Unexpected assignment: %.*s" +msgstr "E15: 予期しない割り当て: %.*s" #, c-format -msgid "expected sequence element of size 2, but got sequence of size %d" -msgstr "シーケンスの要素数には 2 が期待されていましたが %d でした" - -msgid "list constructor does not accept keyword arguments" -msgstr "リストのコンストラクタはキーワード引数を受け付けません" - -msgid "list index out of range" -msgstr "リスト範囲外のインデックスです" +msgid "E15: Expected value, got EOC: %.*s" +msgstr "E15: 値が必要ですが EOC を受け取りました: %.*s" #, c-format -msgid "internal error: failed to get Vim list item %d" -msgstr "内部エラー: vimのリスト要素 %d の取得に失敗しました" - -msgid "slice step cannot be zero" -msgstr "スライスのステップに 0 は指定できません" +msgid "E116: Missing closing parenthesis for function call: %.*s" +msgstr "E116: 関数の呼び出しに ')' がありません: %.*s" #, c-format -msgid "attempt to assign sequence of size greater than %d to extended slice" -msgstr "長さ %d の拡張スライスに、より長いスライスを割り当てようとしました" +msgid "E110: Missing closing parenthesis for nested expression: %.*s" +msgstr "E110: 入れ子の式に ')' がありません: %.*s" #, c-format -msgid "internal error: no Vim list item %d" -msgstr "内部エラー: vimのリスト要素 %d はありません" - -msgid "internal error: not enough list items" -msgstr "内部エラー: リストに十分な要素がありません" - -msgid "internal error: failed to add item to list" -msgstr "内部エラー: リストへの要素追加に失敗しました" +msgid "E697: Missing end of List ']': %.*s" +msgstr "E697: リスト型の最後に ']' がありません: %.*s" #, c-format -msgid "attempt to assign sequence of size %d to extended slice of size %d" -msgstr "長さ %d のスライスを %d の拡張スライスに割り当てようとしました" - -msgid "failed to add item to list" -msgstr "リストへの要素追加に失敗しました" - -msgid "cannot delete vim.List attributes" -msgstr "vim.List 属性は消せません" - -msgid "cannot modify fixed list" -msgstr "固定されたリストは変更できません" +msgid "E723: Missing end of Dictionary '}': %.*s" +msgstr "E723: 辞書型の最後に '}' がありません: %.*s" #, c-format -msgid "unnamed function %s does not exist" -msgstr "無名関数 %s は存在しません" +msgid "E15: Missing closing figure brace: %.*s" +msgstr "E15: '}' がありません: %.*s" #, c-format -msgid "function %s does not exist" -msgstr "関数 %s がありません" +msgid "E15: Missing closing figure brace for lambda: %.*s" +msgstr "E15: ラムダに '}' がありません: %.*s" #, c-format -msgid "failed to run function %s" -msgstr "関数 %s の実行に失敗しました" - -msgid "unable to get option value" -msgstr "オプションの値は取得できません" +msgid "E109: Missing ':' after '?': %.*s" +msgstr "E109: '?' の後に ':' がありません: %.*s" -msgid "internal error: unknown option type" -msgstr "内部エラー: 未知のオプション型です" - -msgid "problem while switching windows" -msgstr "ウィンドウを切換中に問題が発生しました" - -#, c-format -msgid "unable to unset global option %s" -msgstr "グローバルオプション %s の設定解除はできません" +msgid "E1159: Cannot split a window when closing the buffer" +msgstr "E1159: バッファを閉じている間にウィンドウを分割することはできません" -#, c-format -msgid "unable to unset option %s which does not have global value" -msgstr "グローバルな値の無いオプション %s の設定解除はできません" - -msgid "attempt to refer to deleted tab page" -msgstr "削除されたタブを参照しようとしました" - -msgid "no such tab page" -msgstr "そのようなタブページはありません" - -msgid "attempt to refer to deleted window" -msgstr "削除されたウィンドウを参照しようとしました" - -msgid "readonly attribute: buffer" -msgstr "読込専用属性: バッファー" - -msgid "cursor position outside buffer" -msgstr "カーソル位置がバッファの外側です" - -msgid "no such window" -msgstr "そのようなウィンドウはありません" - -msgid "attempt to refer to deleted buffer" -msgstr "削除されたバッファを参照しようとしました" - -msgid "failed to rename buffer" -msgstr "バッファ名の変更に失敗しました" - -msgid "mark name must be a single character" -msgstr "マーク名は1文字のアルファベットでなければなりません" - -#, c-format -msgid "expected vim.Buffer object, but got %s" -msgstr "vim.Bufferオブジェクトが期待されているのに %s でした" - -#, c-format -msgid "failed to switch to buffer %d" -msgstr "指定されたバッファ %d への切り替えに失敗しました" - -#, c-format -msgid "expected vim.Window object, but got %s" -msgstr "vim.Windowオブジェクトが期待されているのに %s でした" - -msgid "failed to find window in the current tab page" -msgstr "現在のタブには指定されたウィンドウがありませんでした" - -msgid "did not switch to the specified window" -msgstr "指定されたウィンドウに切り替えませんでした" - -#, c-format -msgid "expected vim.TabPage object, but got %s" -msgstr "vim.TabPageオブジェクトが期待されているのに %s でした" - -msgid "did not switch to the specified tab page" -msgstr "指定されたタブページに切り替えませんでした" - -msgid "failed to run the code" -msgstr "コードの実行に失敗しました" - -msgid "E858: Eval did not return a valid python object" -msgstr "E858: 式評価は有効なpythonオブジェクトを返しませんでした" - -msgid "E859: Failed to convert returned python object to a Vim value" -msgstr "E859: 返されたpythonオブジェクトをvimの値に変換できませんでした" - -#, c-format -msgid "unable to convert %s to a Vim dictionary" -msgstr "%s vimの辞書型に変換できません" - -#, c-format -msgid "unable to convert %s to a Vim list" -msgstr "%s をvimのリストに変換できません" - -#, c-format -msgid "unable to convert %s to a Vim structure" -msgstr "%s をvimの構造体に変換できません" - -msgid "internal error: NULL reference passed" -msgstr "内部エラー: NULL参照が渡されました" - -msgid "internal error: invalid value type" -msgstr "内部エラー: 無効な値型です" - -msgid "" -"Failed to set path hook: sys.path_hooks is not a list\n" -"You should now do the following:\n" -"- append vim.path_hook to sys.path_hooks\n" -"- append vim.VIM_SPECIAL_PATH to sys.path\n" -msgstr "" -"パスフックの設定に失敗しました: sys.path_hooks がリストではありません\n" -"すぐに下記を実施してください:\n" -"- vim.path_hooks を sys.path_hooks へ追加\n" -"- vim.VIM_SPECIAL_PATH を sys.path へ追加\n" +msgid "Already only one window" +msgstr "既にウィンドウは1つしかありません" -msgid "" -"Failed to set path: sys.path is not a list\n" -"You should now append vim.VIM_SPECIAL_PATH to sys.path" -msgstr "" -"パスの設定に失敗しました: sys.path がリストではありません\n" -"すぐに vim.VIM_SPECIAL_PATH を sys.path に追加してください" +msgid "E441: There is no preview window" +msgstr "E441: プレビューウィンドウがありません" -msgid "" -"Vim macro files (*.vim)\t*.vim\n" -"All Files (*.*)\t*.*\n" -msgstr "" -"Vimマクロファイル (*.vim)\t*.vim\n" -"すべてのファイル (*.*)\t*.*\n" +msgid "E442: Can't split topleft and botright at the same time" +msgstr "E442: 左上と右下を同時に分割することはできません" -msgid "All Files (*.*)\t*.*\n" -msgstr "すべてのファイル (*.*)\t*.*\n" +msgid "E443: Cannot rotate when another window is split" +msgstr "E443: 他のウィンドウが分割されている時には順回できません" -msgid "" -"All Files (*.*)\t*.*\n" -"C source (*.c, *.h)\t*.c;*.h\n" -"C++ source (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"VB code (*.bas, *.frm)\t*.bas;*.frm\n" -"Vim files (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" -msgstr "" -"すべてのファイル (*.*)\t*.*\n" -"Cソース (*.c, *.h)\t*.c;*.h\n" -"C++ソース (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"VBコード (*.bas, *.frm)\t*.bas;*.frm\n" -"Vimファイル (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" +msgid "E444: Cannot close last window" +msgstr "E444: 最後のウィンドウを閉じることはできません" -msgid "" -"Vim macro files (*.vim)\t*.vim\n" -"All Files (*)\t*\n" -msgstr "" -"Vim マクロファイル (*.vim)\t*.vim\n" -"すべてのファイル (*)\t*\n" +msgid "E814: Cannot close window, only autocmd window would remain" +msgstr "E814: autocmdウィンドウしか残らないため、ウィンドウは閉じられません" -msgid "All Files (*)\t*\n" -msgstr "すべてのファイル (*)\t*\n" +msgid "E445: Other window contains changes" +msgstr "E445: 他のウィンドウには変更があります" -msgid "" -"All Files (*)\t*\n" -"C source (*.c, *.h)\t*.c;*.h\n" -"C++ source (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"Vim files (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" -msgstr "" -"すべてのファイル (*)\t*\n" -"Cソース (*.c, *.h)\t*.c;*.h\n" -"C++ソース (*.cpp, *.hpp)\t*.cpp;*.hpp\n" -"Vimファイル (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" +msgid "E446: No file name under cursor" +msgstr "E446: カーソルの下にファイル名がありません" diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po index 1235111c58..160de6c67e 100644 --- a/src/nvim/po/ru.po +++ b/src/nvim/po/ru.po @@ -4187,9 +4187,9 @@ msgid "" "&No\n" "&Cancel" msgstr "" -"&Да\n" -"&Нет\n" -"О&тмена" +"&Y Да\n" +"&N Нет\n" +"&C Отмена" #: ../message.c:3045 msgid "" diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index e34d6fd97f..86f3611ec5 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -6,6 +6,7 @@ #include <stdbool.h> #include <string.h> +#include "nvim/api/buffer.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" @@ -13,11 +14,16 @@ #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" +#include "nvim/buffer_updates.h" +#include "nvim/change.h" #include "nvim/charset.h" #include "nvim/drawscreen.h" +#include "nvim/edit.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" +#include "nvim/extmark.h" +#include "nvim/extmark_defs.h" #include "nvim/getchar.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" @@ -33,6 +39,7 @@ #include "nvim/menu.h" #include "nvim/message.h" #include "nvim/move.h" +#include "nvim/ops.h" #include "nvim/option.h" #include "nvim/option_defs.h" #include "nvim/option_vars.h" @@ -140,7 +147,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i // to avoid that must_redraw is set when 'cursorcolumn' is on. pum_is_visible = true; pum_is_drawn = true; - validate_cursor_col(); + validate_cursor_col(curwin); int above_row = 0; int below_row = cmdline_row; @@ -273,7 +280,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i context_lines = 0; } else { // Leave two lines of context if possible - validate_cheight(); + validate_cheight(curwin); if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) { context_lines = 3; } else { @@ -665,56 +672,10 @@ void pum_redraw(void) } } -/// create a floating preview window for info -/// @return NULL when no enough room to show -static win_T *pum_create_float_preview(bool enter) -{ - WinConfig config = WIN_CONFIG_INIT; - config.relative = kFloatRelativeEditor; - // when pum_above is SW otherwise is NW - config.anchor = pum_above ? kFloatAnchorSouth : 0; - int col = pum_col + pum_width + pum_scrollbar + 1; - // TODO(glepnir): support config align border by using completepopup - // align menu - config.row = pum_row; - int right_extra = Columns - col; - if (right_extra > 0) { - config.width = right_extra; - config.col = col - 1; - } else if (pum_col - 2 > 0) { - config.width = pum_col - 2; - config.col = pum_col - config.width - 1; - } else { - return NULL; - } - config.height = pum_height; - config.style = kWinStyleMinimal; - config.hide = true; - Error err = ERROR_INIT; - win_T *wp = win_new_float(NULL, true, config, &err); - // TODO(glepnir): remove win_enter usage - if (enter) { - win_enter(wp, false); - } - - // create a new buffer - Buffer b = nvim_create_buf(false, true, &err); - if (!b) { - win_free(wp, NULL); - return NULL; - } - buf_T *buf = find_buffer_by_handle(b, &err); - set_string_option_direct_in_buf(buf, kOptBufhidden, "wipe", OPT_LOCAL, 0); - wp->w_float_is_info = true; - wp->w_p_diff = false; - buf->b_p_bl = false; - win_set_buf(wp, buf, true, &err); - return wp; -} - /// set info text to preview buffer. static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *max_width) { + bcount_t inserted_bytes = 0; for (char *p = info; *p != NUL;) { int text_width = 0; char *e = vim_strchr(p, '\n'); @@ -728,6 +689,7 @@ static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *ma } *e = NUL; ml_append_buf(buf, (*lnum)++, p, (int)(e - p + 1), false); + inserted_bytes += (bcount_t)strlen(p) + 1; text_width = (int)mb_string2cells(p); if (text_width > *max_width) { *max_width = text_width; @@ -737,66 +699,78 @@ static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *ma } // delete the empty last line ml_delete_buf(buf, buf->b_ml.ml_line_count, false); + if (strstr(p_cot, "popup") != NULL) { + extmark_splice(buf, 1, 0, 1, 0, 0, buf->b_ml.ml_line_count, 0, inserted_bytes, kExtmarkNoUndo); + } } -/// adjust floating preview window width and height -static void pum_adjust_float_position(win_T *wp, int height, int width) +/// adjust floating info preview window position +static void pum_adjust_info_position(win_T *wp, int height, int width) { - // when floating window in right and right no enough room to show - // but left has enough room, adjust floating window to left. - if (wp->w_config.width < width && wp->w_config.col > pum_col) { - if ((pum_col - 2) > width) { - wp->w_config.width = width; - wp->w_config.col = pum_col - width - 1; - } + int col = pum_col + pum_width + pum_scrollbar + 1; + // TODO(glepnir): support config align border by using completepopup + // align menu + int right_extra = Columns - col; + int left_extra = pum_col - 2; + + if (right_extra > width) { // place in right + wp->w_config.width = width; + wp->w_config.col = col - 1; + } else if (left_extra > width) { // place in left + wp->w_config.width = width; + wp->w_config.col = pum_col - wp->w_config.width - 1; + } else { // either width is enough just use the biggest one. + const bool place_in_right = right_extra > left_extra; + wp->w_config.width = place_in_right ? right_extra : left_extra; + wp->w_config.col = place_in_right ? col - 1 : pum_col - wp->w_config.width - 1; } - wp->w_config.width = MIN(wp->w_config.width, width); + // when pum_above is SW otherwise is NW + wp->w_config.anchor = pum_above ? kFloatAnchorSouth : 0; + wp->w_config.row = pum_above ? pum_row + height : pum_row; wp->w_config.height = MIN(Rows, height); wp->w_config.hide = false; win_config_float(wp, wp->w_config); } -/// used in nvim_complete_set -win_T *pum_set_info(int pum_idx, char *info) +/// Used for nvim__complete_set +/// +/// @param selected the selected compl item. +/// @parma info Info string. +/// @return a win_T pointer. +win_T *pum_set_info(int selected, char *info) { - if (!pum_is_visible || pum_idx < 0 || pum_idx > pum_size) { - return NULL; - } - pum_array[pum_idx].pum_info = xstrdup(info); - compl_set_info(pum_idx); - bool use_float = strstr(p_cot, "popup") != NULL ? true : false; - if (pum_idx != pum_selected || !use_float) { + if (!pum_is_visible || !compl_match_curr_select(selected)) { return NULL; } - block_autocmds(); RedrawingDisabled++; no_u_sync++; win_T *wp = win_float_find_preview(); if (wp == NULL) { - wp = pum_create_float_preview(false); - // no enough room to show + wp = win_float_create(false, true); if (!wp) { return NULL; } } else { // clean exist buffer + linenr_T count = wp->w_buffer->b_ml.ml_line_count; while (!buf_is_empty(wp->w_buffer)) { ml_delete_buf(wp->w_buffer, 1, false); } + bcount_t deleted_bytes = get_region_bytecount(wp->w_buffer, 1, count, 0, 0); + extmark_splice(wp->w_buffer, 1, 0, count, 0, deleted_bytes, 1, 0, 0, kExtmarkNoUndo); } - no_u_sync--; - RedrawingDisabled--; - linenr_T lnum = 0; int max_info_width = 0; pum_preview_set_text(wp->w_buffer, info, &lnum, &max_info_width); - redraw_later(wp, UPD_SOME_VALID); + no_u_sync--; + RedrawingDisabled--; + redraw_later(wp, UPD_NOT_VALID); if (wp->w_p_wrap) { lnum += plines_win(wp, lnum, true); } - pum_adjust_float_position(wp, lnum, max_info_width); + pum_adjust_info_position(wp, lnum, max_info_width); unblock_autocmds(); return wp; } @@ -820,6 +794,13 @@ static bool pum_set_selected(int n, int repeat) int prev_selected = pum_selected; pum_selected = n; + bool use_float = strstr(p_cot, "popup") != NULL; + // when new leader add and info window is shown and no selected we still + // need use the first index item to update the info float window position. + bool force_select = use_float && pum_selected < 0 && win_float_find_preview(); + if (force_select) { + pum_selected = 0; + } if ((pum_selected >= 0) && (pum_selected < pum_size)) { if (pum_first > pum_selected - 4) { @@ -882,7 +863,6 @@ static bool pum_set_selected(int n, int repeat) && (vim_strchr(p_cot, 'p') != NULL)) { win_T *curwin_save = curwin; tabpage_T *curtab_save = curtab; - bool use_float = strstr(p_cot, "popup") != NULL ? true : false; if (use_float) { block_autocmds(); @@ -907,7 +887,7 @@ static bool pum_set_selected(int n, int repeat) if (wp) { win_enter(wp, false); } else { - wp = pum_create_float_preview(true); + wp = win_float_create(true, true); if (wp) { resized = true; } @@ -978,7 +958,7 @@ static bool pum_set_selected(int n, int repeat) lnum += plines_win(curwin, lnum, true); } // adjust floating window by actually height and max info text width - pum_adjust_float_position(curwin, lnum, max_info_width); + pum_adjust_info_position(curwin, lnum, max_info_width); } if ((curwin != curwin_save && win_valid(curwin_save)) @@ -995,7 +975,7 @@ static bool pum_set_selected(int n, int repeat) } // Return cursor to where we were - validate_cursor(); + validate_cursor(curwin); redraw_later(curwin, UPD_SOME_VALID); // When the preview window was resized we need to @@ -1038,6 +1018,11 @@ static bool pum_set_selected(int n, int repeat) } } + // restore before selected value + if (force_select) { + pum_selected = n; + } + return resized; } @@ -1296,7 +1281,7 @@ void pum_show_popupmenu(vimmenu_T *menu) pum_is_drawn = true; pum_grid.zindex = kZIndexCmdlinePopupMenu; // show above cmdline area #23275 pum_redraw(); - setcursor_mayforce(true); + setcursor_mayforce(curwin, true); int c = vgetc(); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 651ebc9f93..e022184f2f 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -773,9 +773,9 @@ static int qf_get_next_buf_line(qfstate_T *state) return QF_END_OF_INPUT; } char *p_buf = ml_get_buf(state->buf, state->buflnum); + size_t len = (size_t)ml_get_buf_len(state->buf, state->buflnum); state->buflnum += 1; - size_t len = strlen(p_buf); if (len > IOSIZE - 2) { state->linebuf = qf_grow_linebuf(state, len); } else { @@ -2699,7 +2699,8 @@ static void qf_goto_win_with_qfl_file(int qf_fnum) // Didn't find it, go to the window before the quickfix // window, unless 'switchbuf' contains 'uselast': in this case we // try to jump to the previously used window first. - if ((swb_flags & SWB_USELAST) && win_valid(prevwin)) { + if ((swb_flags & SWB_USELAST) && win_valid(prevwin) + && !prevwin->w_p_wfb) { win = prevwin; } else if (altwin != NULL) { win = altwin; @@ -2714,6 +2715,7 @@ static void qf_goto_win_with_qfl_file(int qf_fnum) // Remember a usable window. if (altwin == NULL && !win->w_p_pvw + && !win->w_p_wfb && bt_normal(win->w_buffer)) { altwin = win; } @@ -2802,8 +2804,43 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int ECMD_HIDE + ECMD_SET_HELP, prev_winid == curwin->handle ? curwin : NULL); } else { - retval = buflist_getfile(qf_ptr->qf_fnum, 1, - GETF_SETMARK | GETF_SWITCH, forceit); + int fnum = qf_ptr->qf_fnum; + + if (!forceit && curwin->w_p_wfb && curbuf->b_fnum != fnum) { + if (qi->qfl_type == QFLT_LOCATION) { + // Location lists cannot split or reassign their window + // so 'winfixbuf' windows must fail + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + return FAIL; + } + + if (win_valid(prevwin) && !prevwin->w_p_wfb + && !bt_quickfix(prevwin->w_buffer)) { + // 'winfixbuf' is set; attempt to change to a window without it + // that isn't a quickfix/location list window. + win_goto(prevwin); + } + if (curwin->w_p_wfb) { + // Split the window, which will be 'nowinfixbuf', and set curwin + // to that + if (win_split(0, 0) == OK) { + *opened_window = true; + } + if (curwin->w_p_wfb) { + // Autocommands set 'winfixbuf' or sent us to another window + // with it set, or we failed to split the window. Give up, + // but don't return immediately, as they may have messed + // with the list. + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + retval = FAIL; + } + } + } + + if (retval == OK) { + retval = buflist_getfile(fnum, 1, + GETF_SETMARK | GETF_SWITCH, forceit); + } } // If a location list, check whether the associated window is still // present. @@ -2852,12 +2889,12 @@ static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char if (qf_col > 0) { curwin->w_cursor.coladd = 0; if (qf_viscol == true) { - coladvance(qf_col - 1); + coladvance(curwin, qf_col - 1); } else { curwin->w_cursor.col = qf_col - 1; } curwin->w_set_curswant = true; - check_cursor(); + check_cursor(curwin); } else { beginline(BL_WHITE | BL_FIX); } @@ -2865,7 +2902,7 @@ static void qf_jump_goto_line(linenr_T qf_lnum, int qf_col, char qf_viscol, char // Move the cursor to the first line in the buffer pos_T save_cursor = curwin->w_cursor; curwin->w_cursor.lnum = 0; - if (!do_search(NULL, '/', '/', qf_pattern, 1, SEARCH_KEEP, NULL)) { + if (!do_search(NULL, '/', '/', qf_pattern, strlen(qf_pattern), 1, SEARCH_KEEP, NULL)) { curwin->w_cursor = save_cursor; } } @@ -3794,7 +3831,7 @@ void ex_copen(exarg_T *eap) curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = 0; - check_cursor(); + check_cursor(curwin); update_topline(curwin); // scroll to show the line } @@ -4158,6 +4195,12 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q } } + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == curbuf) { + wp->w_skipcol = 0; + } + } + // Remove all undo information u_clearallandblockfree(curbuf); } @@ -4237,10 +4280,10 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q set_option_value_give_err(kOptFiletype, STATIC_CSTR_AS_OPTVAL("qf"), OPT_LOCAL); curbuf->b_p_ma = false; - keep_filetype = true; // don't detect 'filetype' + curbuf->b_keep_filetype = true; // don't detect 'filetype' apply_autocmds(EVENT_BUFREADPOST, "quickfix", NULL, false, curbuf); apply_autocmds(EVENT_BUFWINENTER, "quickfix", NULL, false, curbuf); - keep_filetype = false; + curbuf->b_keep_filetype = false; curbuf->b_ro_locked--; // make sure it will be redrawn @@ -4297,6 +4340,11 @@ static void qf_jump_first(qf_info_T *qi, unsigned save_qfid, int forceit) if (qf_restore_list(qi, save_qfid) == FAIL) { return; } + + if (!check_can_set_curbuf_forceit(forceit)) { + return; + } + // Autocommands might have cleared the list, check for that if (!qf_list_empty(qf_get_curlist(qi))) { qf_jump(qi, 0, 0, forceit); @@ -5112,7 +5160,7 @@ void ex_cfile(exarg_T *eap) } } if (*eap->arg != NUL) { - set_string_option_direct(kOptErrorfile, eap->arg, 0, 0); + set_option_direct(kOptErrorfile, CSTR_AS_OPTVAL(eap->arg), 0, 0); } char *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc; @@ -5125,7 +5173,7 @@ void ex_cfile(exarg_T *eap) // This function is used by the :cfile, :cgetfile and :caddfile // commands. - // :cfile always creates a new quickfix list and jumps to the + // :cfile always creates a new quickfix list and may jump to the // first error. // :cgetfile creates a new quickfix list but doesn't jump to the // first error. @@ -5314,12 +5362,13 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp break; } col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col); - if (col > (colnr_T)strlen(ml_get_buf(buf, lnum))) { + if (col > ml_get_buf_len(buf, lnum)) { break; } } } else { char *const str = ml_get_buf(buf, lnum); + const colnr_T linelen = ml_get_buf_len(buf, lnum); int score; uint32_t matches[MAX_FUZZY_MATCHES]; const size_t sz = sizeof(matches) / sizeof(matches[0]); @@ -5358,7 +5407,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp break; } col = (colnr_T)matches[pat_len - 1] + col + 1; - if (col > (colnr_T)strlen(str)) { + if (col > linelen) { break; } } @@ -5587,6 +5636,10 @@ theend: /// ":lvimgrepadd {pattern} file(s)" void ex_vimgrep(exarg_T *eap) { + if (!check_can_set_curbuf_forceit(eap->forceit)) { + return; + } + char *au_name = vgr_get_auname(eap->cmdidx); if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, curbuf->b_fname, true, curbuf)) { diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c index ef286b3e22..493c079d4c 100644 --- a/src/nvim/rbuffer.c +++ b/src/nvim/rbuffer.c @@ -123,7 +123,10 @@ char *rbuffer_read_ptr(RBuffer *buf, size_t *read_count) FUNC_ATTR_NONNULL_ALL void rbuffer_consumed(RBuffer *buf, size_t count) FUNC_ATTR_NONNULL_ALL { - assert(count && count <= buf->size); + if (count == 0) { + return; + } + assert(count <= buf->size); buf->read_ptr += count; if (buf->read_ptr >= buf->end_ptr) { diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 86082adbb6..5600d6a2f8 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -45,6 +45,12 @@ #include "nvim/types_defs.h" #include "nvim/vim_defs.h" +typedef enum { + RGLF_LINE = 0x01, + RGLF_LENGTH = 0x02, + RGLF_SUBMATCH = 0x04, +} reg_getline_flags_T; + enum { /// In the NFA engine: how many braces are allowed. /// TODO(RE): Use dynamic memory allocation instead of static, like here @@ -386,6 +392,7 @@ static int re_multi_type(int c) } static char *reg_prev_sub = NULL; +static size_t reg_prev_sublen = 0; // REGEXP_INRANGE contains all characters which are always special in a [] // range after '\'. @@ -420,60 +427,82 @@ static int backslash_trans(int c) return c; } +enum { + CLASS_ALNUM = 0, + CLASS_ALPHA, + CLASS_BLANK, + CLASS_CNTRL, + CLASS_DIGIT, + CLASS_GRAPH, + CLASS_LOWER, + CLASS_PRINT, + CLASS_PUNCT, + CLASS_SPACE, + CLASS_UPPER, + CLASS_XDIGIT, + CLASS_TAB, + CLASS_RETURN, + CLASS_BACKSPACE, + CLASS_ESCAPE, + CLASS_IDENT, + CLASS_KEYWORD, + CLASS_FNAME, + CLASS_NONE = 99, +}; + /// Check for a character class name "[:name:]". "pp" points to the '['. /// Returns one of the CLASS_ items. CLASS_NONE means that no item was /// recognized. Otherwise "pp" is advanced to after the item. static int get_char_class(char **pp) { - static const char *(class_names[]) = { - "alnum:]", -#define CLASS_ALNUM 0 - "alpha:]", -#define CLASS_ALPHA 1 - "blank:]", -#define CLASS_BLANK 2 - "cntrl:]", -#define CLASS_CNTRL 3 - "digit:]", -#define CLASS_DIGIT 4 - "graph:]", -#define CLASS_GRAPH 5 - "lower:]", -#define CLASS_LOWER 6 - "print:]", -#define CLASS_PRINT 7 - "punct:]", -#define CLASS_PUNCT 8 - "space:]", -#define CLASS_SPACE 9 - "upper:]", -#define CLASS_UPPER 10 - "xdigit:]", -#define CLASS_XDIGIT 11 - "tab:]", -#define CLASS_TAB 12 - "return:]", -#define CLASS_RETURN 13 - "backspace:]", -#define CLASS_BACKSPACE 14 - "escape:]", -#define CLASS_ESCAPE 15 - "ident:]", -#define CLASS_IDENT 16 - "keyword:]", -#define CLASS_KEYWORD 17 - "fname:]", -#define CLASS_FNAME 18 + // must be sorted by the 'value' field because it is used by bsearch()! + static keyvalue_T char_class_tab[] = { + KEYVALUE_ENTRY(CLASS_ALNUM, "alnum:]"), + KEYVALUE_ENTRY(CLASS_ALPHA, "alpha:]"), + KEYVALUE_ENTRY(CLASS_BACKSPACE, "backspace:]"), + KEYVALUE_ENTRY(CLASS_BLANK, "blank:]"), + KEYVALUE_ENTRY(CLASS_CNTRL, "cntrl:]"), + KEYVALUE_ENTRY(CLASS_DIGIT, "digit:]"), + KEYVALUE_ENTRY(CLASS_ESCAPE, "escape:]"), + KEYVALUE_ENTRY(CLASS_FNAME, "fname:]"), + KEYVALUE_ENTRY(CLASS_GRAPH, "graph:]"), + KEYVALUE_ENTRY(CLASS_IDENT, "ident:]"), + KEYVALUE_ENTRY(CLASS_KEYWORD, "keyword:]"), + KEYVALUE_ENTRY(CLASS_LOWER, "lower:]"), + KEYVALUE_ENTRY(CLASS_PRINT, "print:]"), + KEYVALUE_ENTRY(CLASS_PUNCT, "punct:]"), + KEYVALUE_ENTRY(CLASS_RETURN, "return:]"), + KEYVALUE_ENTRY(CLASS_SPACE, "space:]"), + KEYVALUE_ENTRY(CLASS_TAB, "tab:]"), + KEYVALUE_ENTRY(CLASS_UPPER, "upper:]"), + KEYVALUE_ENTRY(CLASS_XDIGIT, "xdigit:]") }; -#define CLASS_NONE 99 - int i; - if ((*pp)[1] == ':') { - for (i = 0; i < (int)ARRAY_SIZE(class_names); i++) { - if (strncmp(*pp + 2, class_names[i], strlen(class_names[i])) == 0) { - *pp += strlen(class_names[i]) + 2; - return i; - } + // check that the value of "pp" has a chance of matching + if ((*pp)[1] == ':' && ASCII_ISLOWER((*pp)[2]) + && ASCII_ISLOWER((*pp)[3]) && ASCII_ISLOWER((*pp)[4])) { + // this function can be called repeatedly with the same value for "pp" + // so we cache the last found entry. + static keyvalue_T *last_entry = NULL; + + keyvalue_T target = { + .key = 0, + .value = *pp + 2, + .length = 0, // not used, see cmp_keyvalue_value_n() + }; + + keyvalue_T *entry; + if (last_entry != NULL && cmp_keyvalue_value_n(&target, last_entry) == 0) { + entry = last_entry; + } else { + entry = (keyvalue_T *)bsearch(&target, &char_class_tab, + ARRAY_SIZE(char_class_tab), + sizeof(char_class_tab[0]), cmp_keyvalue_value_n); + } + if (entry != NULL) { + last_entry = entry; + *pp += entry->length + 2; + return entry->key; } } return CLASS_NONE; @@ -745,6 +774,7 @@ char *skip_regexp_ex(char *startp, int dirc, int magic, char **newp, int *droppe { magic_T mymagic; char *p = startp; + size_t startplen = 0; if (magic) { mymagic = MAGIC_ON; @@ -766,14 +796,18 @@ char *skip_regexp_ex(char *startp, int dirc, int magic, char **newp, int *droppe } else if (p[0] == '\\' && p[1] != NUL) { if (dirc == '?' && newp != NULL && p[1] == '?') { // change "\?" to "?", make a copy first. + if (startplen == 0) { + startplen = strlen(startp); + } if (*newp == NULL) { - *newp = xstrdup(startp); + *newp = xstrnsave(startp, startplen); p = *newp + (p - startp); + startp = *newp; } if (dropped != NULL) { (*dropped)++; } - STRMOVE(p, p + 1); + memmove(p, p + 1, startplen - (size_t)((p + 1) - startp) + 1); } else { p++; // skip next character } @@ -1264,19 +1298,89 @@ static bool reg_iswordc(int c) return vim_iswordc_buf(c, rex.reg_buf); } -// Get pointer to the line "lnum", which is relative to "reg_firstlnum". -static char *reg_getline(linenr_T lnum) -{ - // when looking behind for a match/no-match lnum is negative. But we - // can't go before line 1 - if (rex.reg_firstlnum + lnum < 1) { - return NULL; +static bool can_f_submatch = false; ///< true when submatch() can be used + +/// These pointers are used for reg_submatch(). Needed for when the +/// substitution string is an expression that contains a call to substitute() +/// and submatch(). +typedef struct { + regmatch_T *sm_match; + regmmatch_T *sm_mmatch; + linenr_T sm_firstlnum; + linenr_T sm_maxline; + int sm_line_lbr; +} regsubmatch_T; + +static regsubmatch_T rsm; ///< can only be used when can_f_submatch is true + +/// Common code for reg_getline(), reg_getline_len(), reg_getline_submatch() and +/// reg_getline_submatch_len(). +/// +/// @param flags a bitmask that controls what info is to be returned +/// and whether or not submatch is in effect. +static void reg_getline_common(linenr_T lnum, reg_getline_flags_T flags, char **line, + colnr_T *length) +{ + bool get_line = flags & RGLF_LINE; + bool get_length = flags & RGLF_LENGTH; + linenr_T firstlnum; + linenr_T maxline; + + if (flags & RGLF_SUBMATCH) { + firstlnum = rsm.sm_firstlnum + lnum; + maxline = rsm.sm_maxline; + } else { + firstlnum = rex.reg_firstlnum + lnum; + maxline = rex.reg_maxline; + } + + // when looking behind for a match/no-match lnum is negative. but we + // can't go before line 1. + if (firstlnum < 1) { + if (get_line) { + *line = NULL; + } + if (get_length) { + *length = 0; + } + + return; } - if (lnum > rex.reg_maxline) { - // Must have matched the "\n" in the last line. - return ""; + + if (lnum > maxline) { + // must have matched the "\n" in the last line. + if (get_line) { + *line = ""; + } + if (get_length) { + *length = 0; + } + + return; + } + + if (get_line) { + *line = ml_get_buf(rex.reg_buf, firstlnum); + } + if (get_length) { + *length = ml_get_buf_len(rex.reg_buf, firstlnum); } - return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum); +} + +/// Get pointer to the line "lnum", which is relative to "reg_firstlnum". +static char *reg_getline(linenr_T lnum) +{ + char *line; + reg_getline_common(lnum, RGLF_LINE, &line, NULL); + return line; +} + +/// Get length of line "lnum", which is relative to "reg_firstlnum". +static colnr_T reg_getline_len(linenr_T lnum) +{ + colnr_T length; + reg_getline_common(lnum, RGLF_LENGTH, NULL, &length); + return length; } static uint8_t *reg_startzp[NSUBEXP]; // Workspace to mark beginning @@ -1510,7 +1614,7 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e if (clnum == end_lnum) { len = end_col - ccol; } else { - len = (int)strlen(p + ccol); + len = reg_getline_len(clnum) - ccol; } if (cstrncmp(p + ccol, (char *)rex.input, &len) != 0) { @@ -1746,42 +1850,58 @@ static void do_lower(int *d, int c) char *regtilde(char *source, int magic, bool preview) { char *newsub = source; + size_t newsublen = 0; + char tilde[3] = { '~', NUL, NUL }; + size_t tildelen = 1; + bool error = false; + + if (!magic) { + tilde[0] = '\\'; + tilde[1] = '~'; + tilde[2] = NUL; + tildelen = 2; + } + + char *p; + for (p = newsub; *p; p++) { + if (strncmp(p, tilde, tildelen) == 0) { + size_t prefixlen = (size_t)(p - newsub); // not including the tilde + char *postfix = p + tildelen; + size_t postfixlen; + size_t tmpsublen; + + if (newsublen == 0) { + newsublen = strlen(newsub); + } + newsublen -= tildelen; + postfixlen = newsublen - prefixlen; + tmpsublen = prefixlen + reg_prev_sublen + postfixlen; - for (char *p = newsub; *p; p++) { - if ((*p == '~' && magic) || (*p == '\\' && *(p + 1) == '~' && !magic)) { - if (reg_prev_sub != NULL) { - // length = len(newsub) - 1 + len(prev_sub) + 1 + if (tmpsublen > 0 && reg_prev_sub != NULL) { // Avoid making the text longer than MAXCOL, it will cause // trouble at some point. - size_t prevsublen = strlen(reg_prev_sub); - size_t newsublen = strlen(newsub); - if (prevsublen > MAXCOL || newsublen > MAXCOL - || newsublen + prevsublen > MAXCOL) { + if (tmpsublen > MAXCOL) { emsg(_(e_resulting_text_too_long)); + error = true; break; } - char *tmpsub = xmalloc(newsublen + prevsublen); + char *tmpsub = xmalloc(tmpsublen + 1); // copy prefix - size_t prefixlen = (size_t)(p - newsub); // not including ~ memmove(tmpsub, newsub, prefixlen); // interpret tilde - memmove(tmpsub + prefixlen, reg_prev_sub, prevsublen); + memmove(tmpsub + prefixlen, reg_prev_sub, reg_prev_sublen); // copy postfix - if (!magic) { - p++; // back off backslash - } - STRCPY(tmpsub + prefixlen + prevsublen, p + 1); + STRCPY(tmpsub + prefixlen + reg_prev_sublen, postfix); if (newsub != source) { // allocated newsub before xfree(newsub); } newsub = tmpsub; - p = newsub + prefixlen + prevsublen; - } else if (magic) { - STRMOVE(p, p + 1); // remove '~' + newsublen = tmpsublen; + p = newsub + prefixlen + reg_prev_sublen; } else { - STRMOVE(p, p + 2); // remove '\~' + memmove(p, postfix, postfixlen + 1); // remove the tilde (+1 for the NUL) } p--; } else { @@ -1792,32 +1912,31 @@ char *regtilde(char *source, int magic, bool preview) } } + if (error) { + if (newsub != source) { + xfree(newsub); + } + return source; + } + // Only change reg_prev_sub when not previewing. if (!preview) { // Store a copy of newsub in reg_prev_sub. It is always allocated, // because recursive calls may make the returned string invalid. - xfree(reg_prev_sub); - reg_prev_sub = xstrdup(newsub); + // Only store it if there something to store. + newsublen = (size_t)(p - newsub); + if (newsublen == 0) { + XFREE_CLEAR(reg_prev_sub); + } else { + xfree(reg_prev_sub); + reg_prev_sub = xstrnsave(newsub, newsublen); + } + reg_prev_sublen = newsublen; } return newsub; } -static bool can_f_submatch = false; // true when submatch() can be used - -// These pointers are used for reg_submatch(). Needed for when the -// substitution string is an expression that contains a call to substitute() -// and submatch(). -typedef struct { - regmatch_T *sm_match; - regmmatch_T *sm_mmatch; - linenr_T sm_firstlnum; - linenr_T sm_maxline; - int sm_line_lbr; -} regsubmatch_T; - -static regsubmatch_T rsm; // can only be used when can_f_submatch is true - /// Put the submatches in "argv[argskip]" which is a list passed into /// call_func() by vim_regsub_both(). static int fill_submatch_list(int argc FUNC_ATTR_UNUSED, typval_T *argv, int argskip, ufunc_T *fp) @@ -1979,11 +2098,13 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen // "flags & REGSUB_COPY" == 0 to the call with // "flags & REGSUB_COPY" != 0. if (copy) { - size_t reslen = eval_result[nested] != NULL ? strlen(eval_result[nested]) : 0; - if (eval_result[nested] != NULL && reslen < (size_t)destlen) { - STRCPY(dest, eval_result[nested]); - dst += reslen; - XFREE_CLEAR(eval_result[nested]); + if (eval_result[nested] != NULL) { + size_t eval_len = strlen(eval_result[nested]); + if (eval_len < (size_t)destlen) { + STRCPY(dest, eval_result[nested]); + dst += eval_len; + XFREE_CLEAR(eval_result[nested]); + } } } else { const bool prev_can_f_submatch = can_f_submatch; @@ -2218,7 +2339,7 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen len = rex.reg_mmatch->endpos[no].col - rex.reg_mmatch->startpos[no].col; } else { - len = (int)strlen(s); + len = reg_getline_len(clnum) - rex.reg_mmatch->startpos[no].col; } } } else { @@ -2248,7 +2369,7 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen if (rex.reg_mmatch->endpos[no].lnum == clnum) { len = rex.reg_mmatch->endpos[no].col; } else { - len = (int)strlen(s); + len = reg_getline_len(clnum); } } else { break; @@ -2325,23 +2446,18 @@ exit: return (int)((dst - dest) + 1); } -/// Call reg_getline() with the line numbers from the submatch. If a -/// substitute() was used the reg_maxline and other values have been -/// overwritten. static char *reg_getline_submatch(linenr_T lnum) { - char *s; - linenr_T save_first = rex.reg_firstlnum; - linenr_T save_max = rex.reg_maxline; - - rex.reg_firstlnum = rsm.sm_firstlnum; - rex.reg_maxline = rsm.sm_maxline; - - s = reg_getline(lnum); + char *line; + reg_getline_common(lnum, RGLF_LINE | RGLF_SUBMATCH, &line, NULL); + return line; +} - rex.reg_firstlnum = save_first; - rex.reg_maxline = save_max; - return s; +static colnr_T reg_getline_submatch_len(linenr_T lnum) +{ + colnr_T length; + reg_getline_common(lnum, RGLF_LENGTH | RGLF_SUBMATCH, NULL, &length); + return length; } /// Used for the submatch() function: get the string from the n'th submatch in @@ -2379,13 +2495,13 @@ char *reg_submatch(int no) // Within one line: take form start to end col. len = rsm.sm_mmatch->endpos[no].col - rsm.sm_mmatch->startpos[no].col; if (round == 2) { - xstrlcpy(retval, s, (size_t)len + 1); + xmemcpyz(retval, s, (size_t)len); } len++; } else { // Multiple lines: take start line from start col, middle // lines completely and end line up to end col. - len = (ssize_t)strlen(s); + len = reg_getline_submatch_len(lnum) - rsm.sm_mmatch->startpos[no].col; if (round == 2) { STRCPY(retval, s); retval[len] = '\n'; @@ -2393,15 +2509,16 @@ char *reg_submatch(int no) len++; lnum++; while (lnum < rsm.sm_mmatch->endpos[no].lnum) { - s = reg_getline_submatch(lnum++); + s = reg_getline_submatch(lnum); if (round == 2) { STRCPY(retval + len, s); } - len += (ssize_t)strlen(s); + len += reg_getline_submatch_len(lnum); if (round == 2) { retval[len] = '\n'; } len++; + lnum++; } if (round == 2) { strncpy(retval + len, // NOLINT(runtime/printf) @@ -2463,8 +2580,9 @@ list_T *reg_submatch_list(int no) if (slnum == elnum) { tv_list_append_string(list, s, ecol - scol); } else { + int max_lnum = elnum - slnum; tv_list_append_string(list, s, -1); - for (int i = 1; i < elnum - slnum; i++) { + for (int i = 1; i < max_lnum; i++) { s = reg_getline_submatch(slnum + i); tv_list_append_string(list, s, -1); } @@ -4494,7 +4612,7 @@ static uint8_t *regatom(int *flagp) n = n * 10 + (uint32_t)(c - '0'); c = getchr(); } - if (c == '\'' && n == 0) { + if (no_Magic(c) == '\'' && n == 0) { // "\%'m", "\%<'m" and "\%>'m": Mark c = getchr(); ret = regnode(RE_MARK); @@ -5320,7 +5438,7 @@ static regprog_T *bt_regcomp(uint8_t *expr, int re_flags) } // Remember whether this pattern has any \z specials in it. r->reghasz = (uint8_t)re_has_z; - scan = r->program + 1; // First BRANCH. + scan = &r->program[1]; // First BRANCH. if (OP(regnext(scan)) == END) { // Only one top-level choice. scan = OPERAND(scan); @@ -5357,9 +5475,12 @@ static regprog_T *bt_regcomp(uint8_t *expr, int re_flags) longest = NULL; len = 0; for (; scan != NULL; scan = regnext(scan)) { - if (OP(scan) == EXACTLY && strlen((char *)OPERAND(scan)) >= (size_t)len) { - longest = OPERAND(scan); - len = (int)strlen((char *)OPERAND(scan)); + if (OP(scan) == EXACTLY) { + size_t scanlen = strlen((char *)OPERAND(scan)); + if (scanlen >= (size_t)len) { + longest = OPERAND(scan); + len = (int)scanlen; + } } } r->regmust = longest; @@ -6091,7 +6212,7 @@ static bool regmatch(uint8_t *scan, const proftime_T *tm, int *timed_out) pos = &fm->mark; const colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum && pos->col == MAXCOL - ? (colnr_T)strlen(reg_getline(pos->lnum - rex.reg_firstlnum)) + ? reg_getline_len(pos->lnum - rex.reg_firstlnum) : pos->col; if (pos->lnum == rex.lnum + rex.reg_firstlnum @@ -7237,7 +7358,7 @@ static bool regmatch(uint8_t *scan, const proftime_T *tm, int *timed_out) if (rex.line == NULL) { break; } - rex.input = rex.line + strlen((char *)rex.line); + rex.input = rex.line + reg_getline_len(rex.lnum); reg_breakcheck(); } else { MB_PTR_BACK(rex.line, rex.input); @@ -7322,7 +7443,7 @@ static int regtry(bt_regprog_T *prog, colnr_T col, proftime_T *tm, int *timed_ou // Clear the external match subpointers if necessaey. rex.need_clear_zsubexpr = (prog->reghasz == REX_SET); - if (regmatch(prog->program + 1, tm, timed_out) == 0) { + if (regmatch(&prog->program[1], tm, timed_out) == 0) { return 0; } @@ -7664,7 +7785,7 @@ static void regdump(uint8_t *pattern, bt_regprog_T *r) fprintf(f, "-------------------------------------\n\r\nregcomp(%s):\r\n", pattern); - s = r->program + 1; + s = &r->program[1]; // Loop until we find the END that isn't before a referred next (an END // can also appear in a NOMATCH operand). while (op != END || s <= end) { @@ -7735,8 +7856,10 @@ static uint8_t *regprop(uint8_t *op) { char *p; static char buf[50]; + static size_t buflen = 0; STRCPY(buf, ":"); + buflen = 1; switch ((int)OP(op)) { case BOL: @@ -7976,7 +8099,8 @@ static uint8_t *regprop(uint8_t *op) case MOPEN + 7: case MOPEN + 8: case MOPEN + 9: - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "MOPEN%d", OP(op) - MOPEN); + buflen += (size_t)snprintf(buf + buflen, sizeof(buf) - buflen, + "MOPEN%d", OP(op) - MOPEN); p = NULL; break; case MCLOSE + 0: @@ -7991,7 +8115,8 @@ static uint8_t *regprop(uint8_t *op) case MCLOSE + 7: case MCLOSE + 8: case MCLOSE + 9: - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "MCLOSE%d", OP(op) - MCLOSE); + buflen += (size_t)snprintf(buf + buflen, sizeof(buf) - buflen, + "MCLOSE%d", OP(op) - MCLOSE); p = NULL; break; case BACKREF + 1: @@ -8003,7 +8128,8 @@ static uint8_t *regprop(uint8_t *op) case BACKREF + 7: case BACKREF + 8: case BACKREF + 9: - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "BACKREF%d", OP(op) - BACKREF); + buflen += (size_t)snprintf(buf + buflen, sizeof(buf) - buflen, + "BACKREF%d", OP(op) - BACKREF); p = NULL; break; case NOPEN: @@ -8021,7 +8147,8 @@ static uint8_t *regprop(uint8_t *op) case ZOPEN + 7: case ZOPEN + 8: case ZOPEN + 9: - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "ZOPEN%d", OP(op) - ZOPEN); + buflen += (size_t)snprintf(buf + buflen, sizeof(buf) - buflen, + "ZOPEN%d", OP(op) - ZOPEN); p = NULL; break; case ZCLOSE + 1: @@ -8033,7 +8160,8 @@ static uint8_t *regprop(uint8_t *op) case ZCLOSE + 7: case ZCLOSE + 8: case ZCLOSE + 9: - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "ZCLOSE%d", OP(op) - ZCLOSE); + buflen += (size_t)snprintf(buf + buflen, sizeof(buf) - buflen, + "ZCLOSE%d", OP(op) - ZCLOSE); p = NULL; break; case ZREF + 1: @@ -8045,7 +8173,8 @@ static uint8_t *regprop(uint8_t *op) case ZREF + 7: case ZREF + 8: case ZREF + 9: - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "ZREF%d", OP(op) - ZREF); + buflen += (size_t)snprintf(buf + buflen, sizeof(buf) - buflen, + "ZREF%d", OP(op) - ZREF); p = NULL; break; case STAR: @@ -8085,8 +8214,8 @@ static uint8_t *regprop(uint8_t *op) case BRACE_COMPLEX + 7: case BRACE_COMPLEX + 8: case BRACE_COMPLEX + 9: - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "BRACE_COMPLEX%d", - OP(op) - BRACE_COMPLEX); + buflen += (size_t)snprintf(buf + buflen, sizeof(buf) - buflen, + "BRACE_COMPLEX%d", OP(op) - BRACE_COMPLEX); p = NULL; break; case MULTIBYTECODE: @@ -8096,12 +8225,13 @@ static uint8_t *regprop(uint8_t *op) p = "NEWL"; break; default: - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "corrupt %d", OP(op)); + buflen += (size_t)snprintf(buf + buflen, sizeof(buf) - buflen, + "corrupt %d", OP(op)); p = NULL; break; } if (p != NULL) { - STRCAT(buf, p); + STRCPY(buf + buflen, p); } return (uint8_t *)buf; } @@ -10218,7 +10348,7 @@ static int nfa_regatom(void) } EMIT((int)n); break; - } else if (c == '\'' && n == 0) { + } else if (no_Magic(c) == '\'' && n == 0) { // \%'m \%<'m \%>'m EMIT(cmp == '<' ? NFA_MARK_LT : cmp == '>' ? NFA_MARK_GT : NFA_MARK); @@ -10555,7 +10685,7 @@ nfa_do_multibyte: // NFA_END_COMPOSING is the ). Note that right now we are // building the postfix form, not the NFA itself; // a composing char could be: a, b, c, NFA_COMPOSING - // where 'b' and 'c' are chars with codes > 256. */ + // where 'b' and 'c' are chars with codes > 256. while (true) { EMIT(c); if (i > 0) { @@ -13599,7 +13729,7 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T rex.line = (uint8_t *)reg_getline(++rex.lnum); rex.input = rex.line; } else { - rex.input = rex.line + strlen((char *)rex.line); + rex.input = rex.line + reg_getline_len(rex.lnum); } } if ((int)(rex.input - rex.line) >= state->val) { @@ -13845,21 +13975,18 @@ static int skip_to_start(int c, colnr_T *colp) // Returns zero for no match, 1 for a match. static int find_match_text(colnr_T *startcol, int regstart, uint8_t *match_text) { -#define PTR2LEN(x) utf_ptr2len(x) - colnr_T col = *startcol; - int regstart_len = PTR2LEN((char *)rex.line + col); + const int regstart_len = utf_ptr2len((char *)rex.line + col); while (true) { bool match = true; uint8_t *s1 = match_text; uint8_t *s2 = rex.line + col + regstart_len; // skip regstart while (*s1) { - int c1_len = PTR2LEN((char *)s1); + int c1_len = utf_ptr2len((char *)s1); int c1 = utf_ptr2char((char *)s1); - int c2_len = PTR2LEN((char *)s2); + int c2_len = utf_ptr2len((char *)s2); int c2 = utf_ptr2char((char *)s2); - if ((c1 != c2 && (!rex.reg_ic || utf_fold(c1) != utf_fold(c2))) || c1_len != c2_len) { match = false; @@ -13894,8 +14021,6 @@ static int find_match_text(colnr_T *startcol, int regstart, uint8_t *match_text) *startcol = col; return 0L; - -#undef PTR2LEN } static int nfa_did_time_out(void) @@ -15002,7 +15127,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm pos_T *pos = &fm->mark; const colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum && pos->col == MAXCOL - ? (colnr_T)strlen(reg_getline(pos->lnum - rex.reg_firstlnum)) + ? reg_getline_len(pos->lnum - rex.reg_firstlnum) : pos->col; result = pos->lnum == rex.lnum + rex.reg_firstlnum diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 76188499e3..d913d311db 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -69,7 +69,7 @@ /// It is shared between do_source() and getsourceline(). /// This is required, because it needs to be handed to do_cmdline() and /// sourcing can be done recursively. -struct source_cookie { +typedef struct { FILE *fp; ///< opened file for sourcing char *nextline; ///< if not NULL: line that was read ahead linenr_T sourcing_lnum; ///< line number of the source file @@ -83,7 +83,7 @@ struct source_cookie { int dbg_tick; ///< debug_tick when breakpoint was set int level; ///< top nesting level of sourced file vimconv_T conv; ///< type of conversion -}; +} source_cookie_T; typedef struct { char *path; @@ -1628,14 +1628,14 @@ static inline char *add_dir(char *dest, const char *const dir, const size_t dir_ const char *appname = get_appname(); size_t appname_len = strlen(appname); assert(appname_len < (IOSIZE - sizeof("-data"))); - xstrlcpy(IObuff, appname, appname_len + 1); + xmemcpyz(IObuff, appname, appname_len); #if defined(MSWIN) if (type == kXDGDataHome || type == kXDGStateHome) { xstrlcat(IObuff, "-data", IOSIZE); appname_len += 5; } #endif - xstrlcpy(dest, IObuff, appname_len + 1); + xmemcpyz(dest, IObuff, appname_len); dest += appname_len; if (suf1 != NULL) { *dest++ = PATHSEP; @@ -1822,20 +1822,20 @@ void ex_options(exarg_T *eap) /// @return address holding the next breakpoint line for a source cookie linenr_T *source_breakpoint(void *cookie) { - return &((struct source_cookie *)cookie)->breakpoint; + return &((source_cookie_T *)cookie)->breakpoint; } /// @return the address holding the debug tick for a source cookie. int *source_dbg_tick(void *cookie) { - return &((struct source_cookie *)cookie)->dbg_tick; + return &((source_cookie_T *)cookie)->dbg_tick; } /// @return the nesting level for a source cookie. int source_level(void *cookie) FUNC_ATTR_PURE { - return ((struct source_cookie *)cookie)->level; + return ((source_cookie_T *)cookie)->level; } /// Special function to open a file without handle inheritance. @@ -1889,11 +1889,6 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize, c } typedef struct { - linenr_T curr_lnum; - const linenr_T final_lnum; -} GetBufferLineCookie; - -typedef struct { char *buf; size_t offset; } GetStrLineCookie; @@ -2009,15 +2004,15 @@ void cmd_source_buffer(const exarg_T *const eap, bool ex_lua) ga_append(&ga, NL); } ((char *)ga.ga_data)[ga.ga_len - 1] = NUL; - const GetStrLineCookie cookie = { - .buf = ga.ga_data, - .offset = 0, - }; if (ex_lua || strequal(curbuf->b_p_ft, "lua") || (curbuf->b_fname && path_with_extension(curbuf->b_fname, "lua"))) { char *name = ex_lua ? ":{range}lua" : ":source (no file)"; - nlua_source_using_linegetter(get_str_line, (void *)&cookie, name); + nlua_source_str(ga.ga_data, name); } else { + const GetStrLineCookie cookie = { + .buf = ga.ga_data, + .offset = 0, + }; source_using_linegetter((void *)&cookie, get_str_line, ":source (no file)"); } ga_clear(&ga); @@ -2052,7 +2047,7 @@ int do_source_str(const char *cmd, const char *traceback_name) /// If a scriptitem_T was found or created "*ret_sid" is set to the SID. int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid) { - struct source_cookie cookie; + source_cookie_T cookie; uint8_t *firstline = NULL; int retval = FAIL; int save_debug_break_level = debug_break_level; @@ -2392,7 +2387,7 @@ char *get_scriptname(LastSet last_set, bool *should_free) case SID_WINLAYOUT: return _("changed window size"); case SID_LUA: - return _("Lua"); + return _("Lua (run Nvim with -V1 for more details)"); case SID_API_CLIENT: snprintf(IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id); return IObuff; @@ -2440,7 +2435,7 @@ linenr_T get_sourced_lnum(LineGetter fgetline, void *cookie) FUNC_ATTR_PURE { return fgetline == getsourceline - ? ((struct source_cookie *)cookie)->sourcing_lnum + ? ((source_cookie_T *)cookie)->sourcing_lnum : SOURCING_LNUM; } @@ -2546,7 +2541,7 @@ void f_getscriptinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// some error. char *getsourceline(int c, void *cookie, int indent, bool do_concat) { - struct source_cookie *sp = (struct source_cookie *)cookie; + source_cookie_T *sp = (source_cookie_T *)cookie; char *line; // If breakpoints have been added/deleted need to check for it. @@ -2560,8 +2555,8 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat) // Set the current sourcing line number. SOURCING_LNUM = sp->sourcing_lnum + 1; // Get current line. If there is a read-ahead line, use it, otherwise get - // one now. - if (sp->finished) { + // one now. "fp" is NULL if actually using a string. + if (sp->finished || sp->fp == NULL) { line = NULL; } else if (sp->nextline == NULL) { line = get_one_sourceline(sp); @@ -2624,7 +2619,7 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat) return line; } -static char *get_one_sourceline(struct source_cookie *sp) +static char *get_one_sourceline(source_cookie_T *sp) { garray_T ga; int len; @@ -2730,10 +2725,10 @@ retry: /// Without the multi-byte feature it's simply ignored. void ex_scriptencoding(exarg_T *eap) { - struct source_cookie *sp; + source_cookie_T *sp; char *name; - if (!getline_equal(eap->getline, eap->cookie, getsourceline)) { + if (!getline_equal(eap->ea_getline, eap->cookie, getsourceline)) { emsg(_("E167: :scriptencoding used outside of a sourced file")); return; } @@ -2745,7 +2740,7 @@ void ex_scriptencoding(exarg_T *eap) } // Setup for conversion from the specified encoding to 'encoding'. - sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie); + sp = (source_cookie_T *)getline_cookie(eap->ea_getline, eap->cookie); convert_setup(&sp->conv, name, p_enc); if (name != eap->arg) { @@ -2756,7 +2751,7 @@ void ex_scriptencoding(exarg_T *eap) /// ":finish": Mark a sourced file as finished. void ex_finish(exarg_T *eap) { - if (getline_equal(eap->getline, eap->cookie, getsourceline)) { + if (getline_equal(eap->ea_getline, eap->cookie, getsourceline)) { do_finish(eap, false); } else { emsg(_("E168: :finish used outside of a sourced file")); @@ -2769,8 +2764,7 @@ void ex_finish(exarg_T *eap) void do_finish(exarg_T *eap, bool reanimate) { if (reanimate) { - ((struct source_cookie *)getline_cookie(eap->getline, - eap->cookie))->finished = false; + ((source_cookie_T *)getline_cookie(eap->ea_getline, eap->cookie))->finished = false; } // Cleanup (and deactivate) conditionals, but stop when a try conditional @@ -2782,8 +2776,7 @@ void do_finish(exarg_T *eap, bool reanimate) eap->cstack->cs_pending[idx] = CSTP_FINISH; report_make_pending(CSTP_FINISH, NULL); } else { - ((struct source_cookie *)getline_cookie(eap->getline, - eap->cookie))->finished = true; + ((source_cookie_T *)getline_cookie(eap->ea_getline, eap->cookie))->finished = true; } } @@ -2793,7 +2786,7 @@ void do_finish(exarg_T *eap, bool reanimate) bool source_finished(LineGetter fgetline, void *cookie) { return getline_equal(fgetline, cookie, getsourceline) - && ((struct source_cookie *)getline_cookie(fgetline, cookie))->finished; + && ((source_cookie_T *)getline_cookie(fgetline, cookie))->finished; } /// Return the autoload script name for a function or variable name diff --git a/src/nvim/search.c b/src/nvim/search.c index 48e41c290d..746c253708 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -99,9 +99,9 @@ static const char e_search_hit_bottom_without_match_for_str[] static SearchPattern spats[2] = { // Last used search pattern - [0] = { NULL, true, false, 0, { '/', false, false, 0 }, NULL }, + [0] = { NULL, 0, true, false, 0, { '/', false, false, 0 }, NULL }, // Last used substitute pattern - [1] = { NULL, true, false, 0, { '/', false, false, 0 }, NULL } + [1] = { NULL, 0, true, false, 0, { '/', false, false, 0 }, NULL } }; static int last_idx = 0; // index in spats[] for RE_LAST @@ -113,13 +113,15 @@ static char lastc_bytes[MB_MAXBYTES + 1]; static int lastc_bytelen = 1; // >1 for multi-byte char // copy of spats[], for keeping the search patterns while executing autocmds -static SearchPattern saved_spats[2]; +static SearchPattern saved_spats[ARRAY_SIZE(spats)]; static char *saved_mr_pattern = NULL; +static size_t saved_mr_patternlen = 0; static int saved_spats_last_idx = 0; static bool saved_spats_no_hlsearch = false; // allocated copy of pattern used by search_regcomp() static char *mr_pattern = NULL; +static size_t mr_patternlen = 0; // Type used by find_pattern_in_path() to remember which included files have // been searched already. @@ -144,8 +146,8 @@ typedef struct { /// @param regmatch return: pattern and ignore-case flag /// /// @return FAIL if failed, OK otherwise. -int search_regcomp(char *pat, char **used_pat, int pat_save, int pat_use, int options, - regmmatch_T *regmatch) +int search_regcomp(char *pat, size_t patlen, char **used_pat, int pat_save, int pat_use, + int options, regmmatch_T *regmatch) { rc_did_emsg = false; int magic = magic_isset(); @@ -168,10 +170,11 @@ int search_regcomp(char *pat, char **used_pat, int pat_save, int pat_use, int op return FAIL; } pat = spats[i].pat; + patlen = spats[i].patlen; magic = spats[i].magic; no_smartcase = spats[i].no_scs; } else if (options & SEARCH_HIS) { // put new pattern in history - add_to_history(HIST_SEARCH, pat, true, NUL); + add_to_history(HIST_SEARCH, pat, patlen, true, NUL); } if (used_pat) { @@ -182,19 +185,20 @@ int search_regcomp(char *pat, char **used_pat, int pat_save, int pat_use, int op if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { mr_pattern = reverse_text(pat); } else { - mr_pattern = xstrdup(pat); + mr_pattern = xstrnsave(pat, patlen); } + mr_patternlen = patlen; // Save the currently used pattern in the appropriate place, // unless the pattern should not be remembered. if (!(options & SEARCH_KEEP) && (cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) { // search or global command if (pat_save == RE_SEARCH || pat_save == RE_BOTH) { - save_re_pat(RE_SEARCH, pat, magic); + save_re_pat(RE_SEARCH, pat, patlen, magic); } // substitute or global command if (pat_save == RE_SUBST || pat_save == RE_BOTH) { - save_re_pat(RE_SUBST, pat, magic); + save_re_pat(RE_SUBST, pat, patlen, magic); } } @@ -213,14 +217,15 @@ char *get_search_pat(void) return mr_pattern; } -void save_re_pat(int idx, char *pat, int magic) +void save_re_pat(int idx, char *pat, size_t patlen, int magic) { if (spats[idx].pat == pat) { return; } free_spat(&spats[idx]); - spats[idx].pat = xstrdup(pat); + spats[idx].pat = xstrnsave(pat, patlen); + spats[idx].patlen = patlen; spats[idx].magic = magic; spats[idx].no_scs = no_smartcase; spats[idx].timestamp = os_time(); @@ -243,18 +248,19 @@ void save_search_patterns(void) return; } - saved_spats[0] = spats[0]; - if (spats[0].pat != NULL) { - saved_spats[0].pat = xstrdup(spats[0].pat); - } - saved_spats[1] = spats[1]; - if (spats[1].pat != NULL) { - saved_spats[1].pat = xstrdup(spats[1].pat); + for (size_t i = 0; i < ARRAY_SIZE(spats); i++) { + saved_spats[i] = spats[i]; + if (spats[i].pat != NULL) { + saved_spats[i].pat = xstrnsave(spats[i].pat, spats[i].patlen); + saved_spats[i].patlen = spats[i].patlen; + } } if (mr_pattern == NULL) { saved_mr_pattern = NULL; + saved_mr_patternlen = 0; } else { - saved_mr_pattern = xstrdup(mr_pattern); + saved_mr_pattern = xstrnsave(mr_pattern, mr_patternlen); + saved_mr_patternlen = mr_patternlen; } saved_spats_last_idx = last_idx; saved_spats_no_hlsearch = no_hlsearch; @@ -266,13 +272,14 @@ void restore_search_patterns(void) return; } - free_spat(&spats[0]); - spats[0] = saved_spats[0]; + for (size_t i = 0; i < ARRAY_SIZE(spats); i++) { + free_spat(&spats[i]); + spats[i] = saved_spats[i]; + } set_vv_searchforward(); - free_spat(&spats[1]); - spats[1] = saved_spats[1]; xfree(mr_pattern); mr_pattern = saved_mr_pattern; + mr_patternlen = saved_mr_patternlen; last_idx = saved_spats_last_idx; set_no_hlsearch(saved_spats_no_hlsearch); } @@ -286,12 +293,13 @@ static inline void free_spat(SearchPattern *const spat) #if defined(EXITFREE) void free_search_patterns(void) { - free_spat(&spats[0]); - free_spat(&spats[1]); - + for (size_t i = 0; i < ARRAY_SIZE(spats); i++) { + free_spat(&spats[i]); + } CLEAR_FIELD(spats); XFREE_CLEAR(mr_pattern); + mr_patternlen = 0; } #endif @@ -320,7 +328,8 @@ void save_last_search_pattern(void) saved_last_search_spat = spats[RE_SEARCH]; if (spats[RE_SEARCH].pat != NULL) { - saved_last_search_spat.pat = xstrdup(spats[RE_SEARCH].pat); + saved_last_search_spat.pat = xstrnsave(spats[RE_SEARCH].pat, spats[RE_SEARCH].patlen); + saved_last_search_spat.patlen = spats[RE_SEARCH].patlen; } saved_last_idx = last_idx; saved_no_hlsearch = no_hlsearch; @@ -341,6 +350,7 @@ void restore_last_search_pattern(void) xfree(spats[RE_SEARCH].pat); spats[RE_SEARCH] = saved_last_search_spat; saved_last_search_spat.pat = NULL; + saved_last_search_spat.patlen = 0; set_vv_searchforward(); last_idx = saved_last_idx; set_no_hlsearch(saved_no_hlsearch); @@ -487,8 +497,10 @@ void set_last_search_pat(const char *s, int idx, int magic, bool setlast) // An empty string means that nothing should be matched. if (*s == NUL) { spats[idx].pat = NULL; + spats[idx].patlen = 0; } else { - spats[idx].pat = xstrdup(s); + spats[idx].patlen = strlen(s); + spats[idx].pat = xstrnsave(s, spats[idx].patlen); } spats[idx].timestamp = os_time(); spats[idx].additional_data = NULL; @@ -507,8 +519,10 @@ void set_last_search_pat(const char *s, int idx, int magic, bool setlast) saved_spats[idx] = spats[0]; if (spats[idx].pat == NULL) { saved_spats[idx].pat = NULL; + saved_spats[idx].patlen = 0; } else { - saved_spats[idx].pat = xstrdup(spats[idx].pat); + saved_spats[idx].pat = xstrnsave(spats[idx].pat, spats[idx].patlen); + saved_spats[idx].patlen = spats[idx].patlen; } saved_spats_last_idx = last_idx; } @@ -528,7 +542,7 @@ void last_pat_prog(regmmatch_T *regmatch) return; } emsg_off++; // So it doesn't beep if bad expr - search_regcomp("", NULL, 0, last_idx, SEARCH_KEEP, regmatch); + search_regcomp("", 0, NULL, 0, last_idx, SEARCH_KEEP, regmatch); emsg_off--; } @@ -556,7 +570,7 @@ void last_pat_prog(regmmatch_T *regmatch) /// the index of the first matching /// subpattern plus one; one if there was none. int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, char *pat, - int count, int options, int pat_use, searchit_arg_T *extra_arg) + size_t patlen, int count, int options, int pat_use, searchit_arg_T *extra_arg) { int found; linenr_T lnum; // no init to shut up Apollo cc @@ -584,7 +598,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, timed_out = &extra_arg->sa_timed_out; } - if (search_regcomp(pat, NULL, RE_SEARCH, pat_use, + if (search_regcomp(pat, patlen, NULL, RE_SEARCH, pat_use, (options & (SEARCH_HIS + SEARCH_KEEP)), ®match) == FAIL) { if ((options & SEARCH_MSG) && !rc_did_emsg) { semsg(_("E383: Invalid search string: %s"), mr_pattern); @@ -592,6 +606,8 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, return FAIL; } + const bool search_from_match_end = vim_strchr(p_cpo, CPO_SEARCH) != NULL; + // find the string do { // loop for count // When not accepting a match at the start position set "extra_col" to a @@ -604,7 +620,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, && pos->col < MAXCOL - 2) { // Watch out for the "col" being MAXCOL - 2, used in a closed fold. ptr = ml_get_buf(buf, pos->lnum); - if ((int)strlen(ptr) <= pos->col) { + if (ml_get_buf_len(buf, pos->lnum) <= pos->col) { start_char_len = 1; } else { start_char_len = utfc_ptr2len(ptr + pos->col); @@ -699,7 +715,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, // If vi-compatible searching, continue at the end // of the match, otherwise continue one position // forward. - if (vim_strchr(p_cpo, CPO_SEARCH) != NULL) { + if (search_from_match_end) { if (nmatched > 1) { // end is in next line, thus no match in // this line @@ -791,7 +807,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, // If vi-compatible searching, continue at the end // of the match, otherwise continue one position // forward. - if (vim_strchr(p_cpo, CPO_SEARCH) != NULL) { + if (search_from_match_end) { if (nmatched > 1) { break; } @@ -851,7 +867,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, if (endpos.col == 0) { if (pos->lnum > 1) { // just in case pos->lnum--; - pos->col = (colnr_T)strlen(ml_get_buf(buf, pos->lnum)); + pos->col = ml_get_buf_len(buf, pos->lnum); } } else { pos->col--; @@ -969,7 +985,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir, // A pattern like "\n\zs" may go past the last line. if (pos->lnum > buf->b_ml.ml_line_count) { pos->lnum = buf->b_ml.ml_line_count; - pos->col = (int)strlen(ml_get_buf(buf, pos->lnum)); + pos->col = ml_get_buf_len(buf, pos->lnum); if (pos->col > 0) { pos->col--; } @@ -1031,11 +1047,12 @@ static int first_submatch(regmmatch_T *rp) /// @param sia optional arguments or NULL /// /// @return 0 for failure, 1 for found, 2 for found and line offset added. -int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, int options, - searchit_arg_T *sia) +int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, size_t patlen, int count, + int options, searchit_arg_T *sia) { pos_T pos; // position of the last match char *searchstr; + size_t searchstrlen; int retval; // Return value char *p; int64_t c; @@ -1043,9 +1060,11 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in char *strcopy = NULL; char *ps; char *msgbuf = NULL; - size_t len; + size_t msgbuflen = 0; bool has_offset = false; + searchcmdlen = 0; + // A line offset is not remembered, this is vi compatible. if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL) { spats[0].off.line = false; @@ -1076,11 +1095,11 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in // If the cursor is in a closed fold, don't find another match in the same // fold. if (dirc == '/') { - if (hasFolding(pos.lnum, NULL, &pos.lnum)) { + if (hasFolding(curwin, pos.lnum, NULL, &pos.lnum)) { pos.col = MAXCOL - 2; // avoid overflow when adding 1 } } else { - if (hasFolding(pos.lnum, &pos.lnum, NULL)) { + if (hasFolding(curwin, pos.lnum, &pos.lnum, NULL)) { pos.col = 0; } } @@ -1096,19 +1115,23 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in bool show_top_bot_msg = false; searchstr = pat; + searchstrlen = patlen; + dircp = NULL; // use previous pattern if (pat == NULL || *pat == NUL || *pat == search_delim) { if (spats[RE_SEARCH].pat == NULL) { // no previous pattern - searchstr = spats[RE_SUBST].pat; - if (searchstr == NULL) { + if (spats[RE_SUBST].pat == NULL) { emsg(_(e_noprevre)); retval = 0; goto end_do_search; } + searchstr = spats[RE_SUBST].pat; + searchstrlen = spats[RE_SUBST].patlen; } else { // make search_regcomp() use spats[RE_SEARCH].pat searchstr = ""; + searchstrlen = 0; } } @@ -1118,12 +1141,16 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in ps = strcopy; p = skip_regexp_ex(pat, search_delim, magic_isset(), &strcopy, NULL, NULL); if (strcopy != ps) { + size_t len = strlen(strcopy); // made a copy of "pat" to change "\?" to "?" - searchcmdlen += (int)(strlen(pat) - strlen(strcopy)); + searchcmdlen += (int)(patlen - len); pat = strcopy; + patlen = len; searchstr = strcopy; + searchstrlen = len; } if (*p == search_delim) { + searchstrlen = (size_t)(p - pat); dircp = p; // remember where we put the NUL *p++ = NUL; } @@ -1161,12 +1188,13 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in // compute length of search command for get_address() searchcmdlen += (int)(p - pat); + patlen -= (size_t)(p - pat); pat = p; // put pat after search command } + bool show_search_stats = false; if ((options & SEARCH_ECHO) && messaging() && !msg_silent && (!cmd_silent || !shortmess(SHM_SEARCHCOUNT))) { - char *trunc; char off_buf[40]; size_t off_len = 0; @@ -1176,56 +1204,59 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in // Get the offset, so we know how long it is. if (!cmd_silent && (spats[0].off.line || spats[0].off.end || spats[0].off.off)) { - p = off_buf; - *p++ = (char)dirc; + off_buf[off_len++] = (char)dirc; if (spats[0].off.end) { - *p++ = 'e'; + off_buf[off_len++] = 'e'; } else if (!spats[0].off.line) { - *p++ = 's'; + off_buf[off_len++] = 's'; } if (spats[0].off.off > 0 || spats[0].off.line) { - *p++ = '+'; + off_buf[off_len++] = '+'; } - *p = NUL; + off_buf[off_len] = NUL; if (spats[0].off.off != 0 || spats[0].off.line) { - snprintf(p, sizeof(off_buf) - 1 - (size_t)(p - off_buf), - "%" PRId64, spats[0].off.off); + off_len += (size_t)snprintf(off_buf + off_len, sizeof(off_buf) - off_len, + "%" PRId64, spats[0].off.off); } - off_len = strlen(off_buf); } + size_t plen; if (*searchstr == NUL) { p = spats[0].pat; + plen = spats[0].patlen; } else { p = searchstr; + plen = searchstrlen; } + size_t msgbufsize; if (!shortmess(SHM_SEARCHCOUNT) || cmd_silent) { // Reserve enough space for the search pattern + offset + // search stat. Use all the space available, so that the // search state is right aligned. If there is not enough space // msg_strtrunc() will shorten in the middle. if (ui_has(kUIMessages)) { - len = 0; // adjusted below + msgbufsize = 0; // adjusted below } else if (msg_scrolled != 0 && !cmd_silent) { // Use all the columns. - len = (size_t)((Rows - msg_row) * Columns - 1); + msgbufsize = (size_t)((Rows - msg_row) * Columns - 1); } else { // Use up to 'showcmd' column. - len = (size_t)((Rows - msg_row - 1) * Columns + sc_col - 1); + msgbufsize = (size_t)((Rows - msg_row - 1) * Columns + sc_col - 1); } - if (len < strlen(p) + off_len + SEARCH_STAT_BUF_LEN + 3) { - len = strlen(p) + off_len + SEARCH_STAT_BUF_LEN + 3; + if (msgbufsize < plen + off_len + SEARCH_STAT_BUF_LEN + 3) { + msgbufsize = plen + off_len + SEARCH_STAT_BUF_LEN + 3; } } else { // Reserve enough space for the search pattern + offset. - len = strlen(p) + off_len + 3; + msgbufsize = plen + off_len + 3; } xfree(msgbuf); - msgbuf = xmalloc(len); - memset(msgbuf, ' ', len); - msgbuf[len - 1] = NUL; + msgbuf = xmalloc(msgbufsize); + memset(msgbuf, ' ', msgbufsize); + msgbuflen = msgbufsize - 1; + msgbuf[msgbuflen] = NUL; // do not fill the msgbuf buffer, if cmd_silent is set, leave it // empty for the search_stat feature. @@ -1234,18 +1265,19 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in if (utf_iscomposing(utf_ptr2char(p))) { // Use a space to draw the composing char on. msgbuf[1] = ' '; - memmove(msgbuf + 2, p, strlen(p)); + memmove(msgbuf + 2, p, plen); } else { - memmove(msgbuf + 1, p, strlen(p)); + memmove(msgbuf + 1, p, plen); } if (off_len > 0) { - memmove(msgbuf + strlen(p) + 1, off_buf, off_len); + memmove(msgbuf + plen + 1, off_buf, off_len); } - trunc = msg_strtrunc(msgbuf, true); + char *trunc = msg_strtrunc(msgbuf, true); if (trunc != NULL) { xfree(msgbuf); msgbuf = trunc; + msgbuflen = strlen(msgbuf); } // The search pattern could be shown on the right in rightleft @@ -1260,7 +1292,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in while (*r == ' ') { r++; } - size_t pat_len = (size_t)(msgbuf + strlen(msgbuf) - r); + size_t pat_len = (size_t)(msgbuf + msgbuflen - r); memmove(msgbuf, r, pat_len); // overwrite old text if ((size_t)(r - msgbuf) >= pat_len) { @@ -1277,6 +1309,10 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in ui_flush(); msg_nowait = true; // don't wait for this message } + + if (!shortmess(SHM_SEARCHCOUNT)) { + show_search_stats = true; + } } // If there is a character offset, subtract it from the current @@ -1309,7 +1345,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in } c = searchit(curwin, curbuf, &pos, NULL, dirc == '/' ? FORWARD : BACKWARD, - searchstr, count, + searchstr, searchstrlen, count, (spats[0].off.end * SEARCH_END + (options & (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS + SEARCH_MSG @@ -1379,17 +1415,12 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in } // Show [1/15] if 'S' is not in 'shortmess'. - if ((options & SEARCH_ECHO) - && messaging() - && !msg_silent - && c != FAIL - && !shortmess(SHM_SEARCHCOUNT) - && msgbuf != NULL) { + if (show_search_stats) { cmdline_search_stat(dirc, &pos, &curwin->w_cursor, - show_top_bot_msg, msgbuf, + show_top_bot_msg, msgbuf, msgbuflen, (count != 1 || has_offset || (!(fdo_flags & FDO_SEARCH) - && hasFolding(curwin->w_cursor.lnum, NULL, + && hasFolding(curwin, curwin->w_cursor.lnum, NULL, NULL))), SEARCH_STAT_DEF_MAX_COUNT, SEARCH_STAT_DEF_TIMEOUT); @@ -1413,6 +1444,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, int count, in goto end_do_search; } pat++; + patlen--; } if (options & SEARCH_MARK) { @@ -1554,7 +1586,7 @@ int searchc(cmdarg_T *cap, bool t_cmd) char *p = get_cursor_line_ptr(); int col = curwin->w_cursor.col; - int len = (int)strlen(p); + int len = get_cursor_line_len(); while (count--) { while (true) { @@ -1958,7 +1990,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) } linep = ml_get(pos.lnum); - pos.col = (colnr_T)strlen(linep); // pos.col on trailing NUL + pos.col = ml_get_len(pos.lnum); // pos.col on trailing NUL do_quotes = -1; line_breakcheck(); @@ -2105,7 +2137,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) } if (pos.lnum > 1) { ptr = ml_get(pos.lnum - 1); - if (*ptr && *(ptr + strlen(ptr) - 1) == '\\') { + if (*ptr && *(ptr + ml_get_len(pos.lnum - 1) - 1) == '\\') { do_quotes = 1; if (start_in_quotes == kNone) { inquote = at_start; @@ -2418,7 +2450,8 @@ int current_search(int count, bool forward) } // Is the pattern is zero-width?, this time, don't care about the direction - int zero_width = is_zero_width(spats[last_idx].pat, true, &curwin->w_cursor, FORWARD); + int zero_width = is_zero_width(spats[last_idx].pat, spats[last_idx].patlen, + true, &curwin->w_cursor, FORWARD); if (zero_width == -1) { return FAIL; // pattern not found } @@ -2451,7 +2484,7 @@ int current_search(int count, bool forward) result = searchit(curwin, curbuf, &pos, &end_pos, (dir ? FORWARD : BACKWARD), - spats[last_idx].pat, i ? count : 1, + spats[last_idx].pat, spats[last_idx].patlen, i ? count : 1, SEARCH_KEEP | flags, RE_SEARCH, NULL); p_ws = old_p_ws; @@ -2460,7 +2493,7 @@ int current_search(int count, bool forward) // beginning of the file (cursor might be on the search match) // except when Visual mode is active, so that extending the visual // selection works. - if (i == 1 && !result) { // not found, abort */ + if (i == 1 && !result) { // not found, abort curwin->w_cursor = orig_pos; if (VIsual_active) { VIsual = save_VIsual; @@ -2472,7 +2505,7 @@ int current_search(int count, bool forward) } else { // try again from end of buffer // searching backwards, so set pos to last line and col pos.lnum = curwin->w_buffer->b_ml.ml_line_count; - pos.col = (colnr_T)strlen(ml_get(curwin->w_buffer->b_ml.ml_line_count)); + pos.col = ml_get_len(curwin->w_buffer->b_ml.ml_line_count); } } } @@ -2525,7 +2558,8 @@ int current_search(int count, bool forward) /// else from position "cur". /// "direction" is FORWARD or BACKWARD. /// Returns true, false or -1 for failure. -static int is_zero_width(char *pattern, bool move, pos_T *cur, Direction direction) +static int is_zero_width(char *pattern, size_t patternlen, bool move, pos_T *cur, + Direction direction) { regmmatch_T regmatch; int result = -1; @@ -2535,9 +2569,10 @@ static int is_zero_width(char *pattern, bool move, pos_T *cur, Direction directi if (pattern == NULL) { pattern = spats[last_idx].pat; + patternlen = spats[last_idx].patlen; } - if (search_regcomp(pattern, NULL, RE_SEARCH, RE_SEARCH, + if (search_regcomp(pattern, patternlen, NULL, RE_SEARCH, RE_SEARCH, SEARCH_KEEP, ®match) == FAIL) { return -1; } @@ -2552,7 +2587,7 @@ static int is_zero_width(char *pattern, bool move, pos_T *cur, Direction directi // accept a match at the cursor position flag = SEARCH_START; } - if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, 1, + if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, patternlen, 1, SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL) { int nmatched = 0; // Zero-width pattern should match somewhere, then we can check if @@ -2591,7 +2626,8 @@ bool linewhite(linenr_T lnum) /// Add the search count "[3/19]" to "msgbuf". /// See update_search_stat() for other arguments. static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool show_top_bot_msg, - char *msgbuf, bool recompute, int maxcount, int timeout) + char *msgbuf, size_t msgbuflen, bool recompute, int maxcount, + int timeout) { searchstat_T stat; @@ -2602,36 +2638,36 @@ static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool sh } char t[SEARCH_STAT_BUF_LEN]; + size_t len; if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { if (stat.incomplete == 1) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); + len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); } else if (stat.cnt > maxcount && stat.cur > maxcount) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", - maxcount, maxcount); + len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", + maxcount, maxcount); } else if (stat.cnt > maxcount) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]", - maxcount, stat.cur); + len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]", + maxcount, stat.cur); } else { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", - stat.cnt, stat.cur); + len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", + stat.cnt, stat.cur); } } else { if (stat.incomplete == 1) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); + len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]"); } else if (stat.cnt > maxcount && stat.cur > maxcount) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", - maxcount, maxcount); + len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]", + maxcount, maxcount); } else if (stat.cnt > maxcount) { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>%d]", - stat.cur, maxcount); + len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>%d]", + stat.cur, maxcount); } else { - vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", - stat.cur, stat.cnt); + len = (size_t)vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", + stat.cur, stat.cnt); } } - size_t len = strlen(t); if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) { memmove(t + 2, t, len); t[0] = 'W'; @@ -2639,11 +2675,10 @@ static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool sh len += 2; } - size_t msgbuf_len = strlen(msgbuf); - if (len > msgbuf_len) { - len = msgbuf_len; + if (len > msgbuflen) { + len = msgbuflen; } - memmove(msgbuf + msgbuf_len - len, t, len); + memmove(msgbuf + msgbuflen - len, t, len); if (dirc == '?' && stat.cur == maxcount + 1) { stat.cur = -1; @@ -2726,7 +2761,7 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst start = profile_setlimit(timeout); } while (!got_int && searchit(curwin, curbuf, &lastpos, &endpos, - FORWARD, NULL, 1, SEARCH_KEEP, RE_LAST, + FORWARD, NULL, 0, 1, SEARCH_KEEP, RE_LAST, NULL) != FAIL) { done_search = true; // Stop after passing the time limit. @@ -2860,7 +2895,8 @@ void f_searchcount(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) goto the_end; } xfree(spats[last_idx].pat); - spats[last_idx].pat = xstrdup(pattern); + spats[last_idx].patlen = strlen(pattern); + spats[last_idx].pat = xstrnsave(pattern, spats[last_idx].patlen); } if (spats[last_idx].pat == NULL || *spats[last_idx].pat == NUL) { goto the_end; // the previous pattern was never defined @@ -3564,8 +3600,10 @@ static char *get_line_and_copy(linenr_T lnum, char *buf) /// @param action What to do when we find it /// @param start_lnum first line to start searching /// @param end_lnum last line for searching +/// @param forceit If true, always switch to the found path void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool skip_comments, - int type, int count, int action, linenr_T start_lnum, linenr_T end_lnum) + int type, int count, int action, linenr_T start_lnum, linenr_T end_lnum, + int forceit) { SearchedFile *files; // Stack of included files SearchedFile *bigger; // When we need more space @@ -3600,10 +3638,10 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool // when CONT_SOL is set compare "ptr" with the beginning of the // line is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo && !compl_status_sol()) { - size_t patlen = len + 5; - char *pat = xmalloc(patlen); + size_t patsize = len + 5; + char *pat = xmalloc(patsize); assert(len <= INT_MAX); - snprintf(pat, patlen, whole ? "\\<%.*s\\>" : "%.*s", (int)len, ptr); + snprintf(pat, patsize, whole ? "\\<%.*s\\>" : "%.*s", (int)len, ptr); // ignore case according to p_ic, p_scs and pat regmatch.rm_ic = ignorecase(pat); regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0); @@ -3621,8 +3659,7 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool incl_regmatch.rm_ic = false; // don't ignore case in incl. pat. } if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL)) { - def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL - ? p_def : curbuf->b_p_def, + def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL ? p_def : curbuf->b_p_def, magic_isset() ? RE_MAGIC : 0); if (def_regmatch.regprog == NULL) { goto fpip_end; @@ -3675,9 +3712,8 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool true) & kEqualFiles) { if (type != CHECK_PATH && action == ACTION_SHOW_ALL && files[i].matched) { - msg_putchar('\n'); // cursor below last one */ - if (!got_int) { // don't display if 'q' typed at "--more--" - // message + msg_putchar('\n'); // cursor below last one + if (!got_int) { // don't display if 'q' typed at "--more--" message msg_home_replace_hl(new_fname); msg_puts(_(" (includes previously listed match)")); prev_fname = NULL; @@ -4025,17 +4061,17 @@ search_line: break; } if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL, - NULL, true, lnum, false))) { + NULL, true, lnum, forceit))) { break; // failed to jump to file } } else { setpcmark(); } curwin->w_cursor.lnum = lnum; - check_cursor(); + check_cursor(curwin); } else { if (!GETFILE_SUCCESS(getfile(0, files[depth].name, NULL, true, - files[depth].lnum, false))) { + files[depth].lnum, forceit))) { break; // failed to jump to file } // autocommands may have changed the lnum, we don't @@ -4051,7 +4087,7 @@ search_line: if (l_g_do_tagpreview != 0 && curwin != curwin_save && win_valid(curwin_save)) { // Return cursor to where we were - validate_cursor(); + validate_cursor(curwin); redraw_later(curwin, UPD_VALID); win_enter(curwin_save, true); } @@ -4065,7 +4101,7 @@ exit_matched: && action == ACTION_EXPAND && !compl_status_sol() && *startp != NUL - && *(p = startp + utfc_ptr2len(startp)) != NUL) { + && *(startp + utfc_ptr2len(startp)) != NUL) { goto search_line; } } @@ -4164,8 +4200,9 @@ static void show_pat_in_path(char *line, int type, bool did_show, int action, FI if (got_int) { // 'q' typed at "--more--" message return; } + size_t linelen = strlen(line); while (true) { - char *p = line + strlen(line) - 1; + char *p = line + linelen - 1; if (fp != NULL) { // We used fgets(), so get rid of newline at end if (p >= line && *p == '\n') { @@ -4195,12 +4232,14 @@ static void show_pat_in_path(char *line, int type, bool did_show, int action, FI if (vim_fgets(line, LSIZE, fp)) { // end of file break; } + linelen = strlen(line); (*lnum)++; } else { if (++*lnum > curbuf->b_ml.ml_line_count) { break; } line = ml_get(*lnum); + linelen = (size_t)ml_get_len(*lnum); } msg_putchar('\n'); } diff --git a/src/nvim/search.h b/src/nvim/search.h index 09af34d87e..783756b781 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -84,6 +84,7 @@ typedef struct { /// Structure containing last search pattern and its attributes. typedef struct { char *pat; ///< The pattern (in allocated memory) or NULL. + size_t patlen; ///< The length of the patten (0 is pat is NULL). bool magic; ///< Magicness of the pattern. bool no_scs; ///< No smartcase for this pattern. Timestamp timestamp; ///< Time of the last change. diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 0b148993f8..34c36f850f 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1188,6 +1188,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) .off = cur_entry.data.search_pattern.offset, }, .pat = cur_entry.data.search_pattern.pat, + .patlen = strlen(cur_entry.data.search_pattern.pat), .additional_data = cur_entry.data.search_pattern.additional_data, .timestamp = cur_entry.timestamp, }; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index c2091c6bae..d7a6adef58 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -811,7 +811,7 @@ static void find_word(matchinf_T *mip, int mode) if (ptr == mip->mi_word) { spell_casefold(mip->mi_win, ptr, wlen, fword, MAXWLEN); } else { - xstrlcpy(fword, ptr, (size_t)endlen[endidxcnt] + 1); + xmemcpyz(fword, ptr, (size_t)endlen[endidxcnt]); } } if (!can_compound(slang, fword, mip->mi_compflags)) { @@ -1290,11 +1290,11 @@ static inline bool can_syn_spell(win_T *wp, linenr_T lnum, int col) /// to after badly spelled word before the cursor. /// /// @param dir FORWARD or BACKWARD -/// @param allwords true for "[s"/"]s", false for "[S"/"]S" +/// @param behaviour Behaviour of the function /// @param attrp return: attributes of bad word or NULL (only when "dir" is FORWARD) /// /// @return 0 if not found, length of the badly spelled word otherwise. -size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *attrp) +size_t spell_move_to(win_T *wp, int dir, smt_T behaviour, bool curline, hlf_T *attrp) { pos_T found_pos; size_t found_len = 0; @@ -1342,7 +1342,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att while (!got_int) { char *line = ml_get_buf(wp->w_buffer, lnum); - len = strlen(line); + len = (size_t)ml_get_buf_len(wp->w_buffer, lnum); if (buflen < len + MAXWLEN + 2) { xfree(buf); buflen = len + MAXWLEN + 2; @@ -1398,7 +1398,9 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att if (attr != HLF_COUNT) { // We found a bad word. Check the attribute. - if (allwords || attr == HLF_SPB) { + if (behaviour == SMT_ALL + || (behaviour == SMT_BAD && attr == HLF_SPB) + || (behaviour == SMT_RARE && attr == HLF_SPR)) { // When searching forward only accept a bad word after // the cursor. if (dir == BACKWARD @@ -1810,7 +1812,7 @@ void count_common_word(slang_T *lp, char *word, int len, uint8_t count) } else if (len >= MAXWLEN) { return; } else { - xstrlcpy(buf, word, (size_t)len + 1); + xmemcpyz(buf, word, (size_t)len); p = buf; } @@ -1868,7 +1870,7 @@ int init_syl_tab(slang_T *slang) } syl_item_T *syl = GA_APPEND_VIA_PTR(syl_item_T, &slang->sl_syl_items); - xstrlcpy(syl->sy_chars, s, (size_t)l + 1); + xmemcpyz(syl->sy_chars, s, (size_t)l); syl->sy_len = l; } return OK; @@ -2253,7 +2255,7 @@ static void use_midword(slang_T *lp, win_T *wp) char *bp = xstrnsave(wp->w_s->b_spell_ismw_mb, (size_t)n + (size_t)l); xfree(wp->w_s->b_spell_ismw_mb); wp->w_s->b_spell_ismw_mb = bp; - xstrlcpy(bp + n, p, (size_t)l + 1); + xmemcpyz(bp + n, p, (size_t)l); } p += l; } @@ -2663,16 +2665,16 @@ void ex_spellrepall(exarg_T *eap) const size_t repl_to_len = strlen(repl_to); const int addlen = (int)(repl_to_len - repl_from_len); - const size_t frompatlen = repl_from_len + 7; - char *frompat = xmalloc(frompatlen); - snprintf(frompat, frompatlen, "\\V\\<%s\\>", repl_from); + const size_t frompatsize = repl_from_len + 7; + char *frompat = xmalloc(frompatsize); + size_t frompatlen = (size_t)snprintf(frompat, frompatsize, "\\V\\<%s\\>", repl_from); p_ws = false; sub_nsubs = 0; sub_nlines = 0; curwin->w_cursor.lnum = 0; while (!got_int) { - if (do_search(NULL, '/', '/', frompat, 1, SEARCH_KEEP, NULL) == 0 + if (do_search(NULL, '/', '/', frompat, frompatlen, 1, SEARCH_KEEP, NULL) == 0 || u_save_cursor() == FAIL) { break; } @@ -2682,7 +2684,7 @@ void ex_spellrepall(exarg_T *eap) char *line = get_cursor_line_ptr(); if (addlen <= 0 || strncmp(line + curwin->w_cursor.col, repl_to, repl_to_len) != 0) { - char *p = xmalloc(strlen(line) + (size_t)addlen + 1); + char *p = xmalloc((size_t)get_cursor_line_len() + (size_t)addlen + 1); memmove(p, line, (size_t)curwin->w_cursor.col); STRCPY(p + curwin->w_cursor.col, repl_to); STRCAT(p, line + curwin->w_cursor.col + repl_from_len); diff --git a/src/nvim/spell.h b/src/nvim/spell.h index adbdd3705e..85e16d7f6c 100644 --- a/src/nvim/spell.h +++ b/src/nvim/spell.h @@ -21,6 +21,13 @@ extern char *e_format; extern char *repl_from; extern char *repl_to; +/// Values for behaviour in spell_move_to +typedef enum { + SMT_ALL = 0, ///< Move to "all" words + SMT_BAD, ///< Move to "bad" words only + SMT_RARE, ///< Move to "rare" words only +} smt_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "spell.h.generated.h" #endif diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 1c632d2700..046a0a56e5 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -2859,7 +2859,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char *compflags if (flag != 0) { // Find the flag in the hashtable. If it was used before, use // the existing ID. Otherwise add a new entry. - xstrlcpy(key, prevp, (size_t)(p - prevp) + 1); + xmemcpyz(key, prevp, (size_t)(p - prevp)); hashitem_T *hi = hash_find(&aff->af_comp, key); if (!HASHITEM_EMPTY(hi)) { id = HI2CI(hi)->ci_newID; @@ -3263,7 +3263,7 @@ static int get_pfxlist(afffile_T *affile, char *afflist, char *store_afflist) if (get_affitem(affile->af_flagtype, &p) != 0) { // A flag is a postponed prefix flag if it appears in "af_pref" // and its ID is not zero. - xstrlcpy(key, prevp, (size_t)(p - prevp) + 1); + xmemcpyz(key, prevp, (size_t)(p - prevp)); hashitem_T *hi = hash_find(&affile->af_pref, key); if (!HASHITEM_EMPTY(hi)) { int id = HI2AH(hi)->ah_newID; @@ -3293,7 +3293,7 @@ static void get_compflags(afffile_T *affile, char *afflist, char *store_afflist) char *prevp = p; if (get_affitem(affile->af_flagtype, &p) != 0) { // A flag is a compound flag if it appears in "af_comp". - xstrlcpy(key, prevp, (size_t)(p - prevp) + 1); + xmemcpyz(key, prevp, (size_t)(p - prevp)); hashitem_T *hi = hash_find(&affile->af_comp, key); if (!HASHITEM_EMPTY(hi)) { store_afflist[cnt++] = (char)(uint8_t)HI2CI(hi)->ci_newID; @@ -3481,7 +3481,7 @@ static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_ // Obey a "COMPOUNDFORBIDFLAG" of the affix: don't // use the compound flags. if (use_pfxlist != NULL && ae->ae_compforbid) { - xstrlcpy(pfx_pfxlist, use_pfxlist, (size_t)use_pfxlen + 1); + xmemcpyz(pfx_pfxlist, use_pfxlist, (size_t)use_pfxlen); use_pfxlist = pfx_pfxlist; } @@ -5114,13 +5114,12 @@ static void sug_write(spellinfo_T *spin, char *fname) for (linenr_T lnum = 1; lnum <= wcount; lnum++) { // <sugline>: <sugnr> ... NUL char *line = ml_get_buf(spin->si_spellbuf, lnum); - size_t len = strlen(line) + 1; - if (fwrite(line, len, 1, fd) == 0) { + int len = ml_get_buf_len(spin->si_spellbuf, lnum) + 1; + if (fwrite(line, (size_t)len, 1, fd) == 0) { emsg(_(e_write)); goto theend; } - assert((size_t)spin->si_memtot + len <= INT_MAX); - spin->si_memtot += (int)len; + spin->si_memtot += len; } // Write another byte to check for errors. @@ -5577,7 +5576,7 @@ static void init_spellfile(void) if (aspath) { // Use directory of an entry with path, e.g., for // "/dir/lg.utf-8.spl" use "/dir". - xstrlcpy(buf, curbuf->b_s.b_p_spl, (size_t)(lstart - curbuf->b_s.b_p_spl)); + xmemcpyz(buf, curbuf->b_s.b_p_spl, (size_t)(lstart - curbuf->b_s.b_p_spl - 1)); } else { // Copy the path from 'runtimepath' to buf[]. copy_option_part(&rtp, buf, MAXPATHL, ","); @@ -5586,7 +5585,7 @@ static void init_spellfile(void) // Use the first language name from 'spelllang' and the // encoding used in the first loaded .spl file. if (aspath) { - xstrlcpy(buf, curbuf->b_s.b_p_spl, (size_t)(lend - curbuf->b_s.b_p_spl + 1)); + xmemcpyz(buf, curbuf->b_s.b_p_spl, (size_t)(lend - curbuf->b_s.b_p_spl)); } else { // Create the "spell" directory if it doesn't exist yet. l = (int)strlen(buf); diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 5c0e295f88..a7de20d14e 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -480,12 +480,11 @@ void spell_suggest(int count) badlen++; end_visual_mode(); // make sure we don't include the NUL at the end of the line - char *line = get_cursor_line_ptr(); - if (badlen > (int)strlen(line) - (int)curwin->w_cursor.col) { - badlen = (int)strlen(line) - (int)curwin->w_cursor.col; + if (badlen > get_cursor_line_len() - curwin->w_cursor.col) { + badlen = get_cursor_line_len() - curwin->w_cursor.col; } // Find the start of the badly spelled word. - } else if (spell_move_to(curwin, FORWARD, true, true, NULL) == 0 + } else if (spell_move_to(curwin, FORWARD, SMT_ALL, true, NULL) == 0 || curwin->w_cursor.col > prev_cursor.col) { // No bad word or it starts after the cursor: use the word under the // cursor. @@ -514,7 +513,7 @@ void spell_suggest(int count) int need_cap = check_need_cap(curwin, curwin->w_cursor.lnum, curwin->w_cursor.col); // Make a copy of current line since autocommands may free the line. - char *line = xstrdup(get_cursor_line_ptr()); + char *line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len()); spell_suggest_timeout = 5000; // Get the list of suggestions. Limit to 'lines' - 2 or the number in @@ -562,7 +561,8 @@ void spell_suggest(int count) xstrlcpy(wcopy, stp->st_word, MAXWLEN + 1); int el = sug.su_badlen - stp->st_orglen; if (el > 0 && stp->st_wordlen + el <= MAXWLEN) { - xstrlcpy(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen, (size_t)el + 1); + assert(sug.su_badptr != NULL); + xmemcpyz(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen, (size_t)el); } vim_snprintf(IObuff, IOSIZE, "%2d", i + 1); if (cmdmsg_rl) { @@ -734,7 +734,7 @@ static void spell_find_suggest(char *badptr, int badlen, suginfo_T *su, int maxc if (su->su_badlen >= MAXWLEN) { su->su_badlen = MAXWLEN - 1; // just in case } - xstrlcpy(su->su_badword, su->su_badptr, (size_t)su->su_badlen + 1); + xmemcpyz(su->su_badword, su->su_badptr, (size_t)su->su_badlen); spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword, MAXWLEN); @@ -1368,9 +1368,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char *fword, bool soun compflags[sp->ts_complen] = (uint8_t)((unsigned)flags >> 24); compflags[sp->ts_complen + 1] = NUL; - xstrlcpy(preword + sp->ts_prewordlen, + xmemcpyz(preword + sp->ts_prewordlen, tword + sp->ts_splitoff, - (size_t)(sp->ts_twordlen - sp->ts_splitoff) + 1); + (size_t)(sp->ts_twordlen - sp->ts_splitoff)); // Verify CHECKCOMPOUNDPATTERN rules. if (match_checkcompoundpattern(preword, sp->ts_prewordlen, @@ -2647,8 +2647,8 @@ static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char *ba // Add part of the bad word to the good word, so that we soundfold // what replaces the bad word. STRCPY(goodword, stp->st_word); - xstrlcpy(goodword + stp->st_wordlen, - su->su_badptr + su->su_badlen - lendiff, (size_t)lendiff + 1); + xmemcpyz(goodword + stp->st_wordlen, + su->su_badptr + su->su_badlen - lendiff, (size_t)lendiff); pgood = goodword; } else { pgood = stp->st_word; diff --git a/src/nvim/state.c b/src/nvim/state.c index 0df060ecf4..993db255de 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -135,9 +135,9 @@ void state_handle_k_event(void) } /// Return true if in the current mode we need to use virtual. -bool virtual_active(void) +bool virtual_active(win_T *wp) { - unsigned cur_ve_flags = get_ve_flags(); + unsigned cur_ve_flags = get_ve_flags(wp); // While an operator is being executed we return "virtual_op", because // VIsual_active has already been reset, thus we can't check for "block" @@ -289,7 +289,7 @@ static bool is_safe_now(void) && !debug_mode; } -/// Trigger SafeState if currently in s safe state, that is "safe" is TRUE and +/// Trigger SafeState if currently in a safe state, that is "safe" is true and /// there is no typeahead. void may_trigger_safestate(bool safe) { diff --git a/src/nvim/state.h b/src/nvim/state.h index 9002f018d2..8220d90a67 100644 --- a/src/nvim/state.h +++ b/src/nvim/state.h @@ -1,6 +1,7 @@ #pragma once #include "nvim/state_defs.h" // IWYU pragma: keep +#include "nvim/types_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "state.h.generated.h" diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index b403f840ca..ca7083a9e3 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -1002,11 +1002,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // Get the byte value now, in case we need it below. This is more // efficient than making a copy of the line. int byteval; - const size_t len = strlen(line_ptr); - if (wp->w_cursor.col > (colnr_T)len) { + const colnr_T len = ml_get_buf_len(wp->w_buffer, lnum); + if (wp->w_cursor.col > len) { // Line may have changed since checking the cursor column, or the lnum // was adjusted above. - wp->w_cursor.col = (colnr_T)len; + wp->w_cursor.col = len; wp->w_cursor.coladd = 0; byteval = 0; } else { @@ -2187,7 +2187,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // matter? // if (called_emsg > called_emsg_before) if (opt_idx != kOptInvalid && did_emsg > did_emsg_before) { - set_string_option_direct(opt_idx, "", opt_scope, SID_ERROR); + set_option_direct(opt_idx, STATIC_CSTR_AS_OPTVAL(""), opt_scope, SID_ERROR); } // A user function may reset KeyTyped, restore it. diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 01bd610292..16ae35272b 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -299,8 +299,8 @@ char *vim_strsave_shellescape(const char *string, bool do_special, bool do_newli char *vim_strsave_up(const char *string) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char *p1 = xstrdup(string); - vim_strup(p1); + char *p1 = xmalloc(strlen(string) + 1); + vim_strcpy_up(p1, string); return p1; } @@ -309,8 +309,8 @@ char *vim_strsave_up(const char *string) char *vim_strnsave_up(const char *string, size_t len) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char *p1 = xstrnsave(string, len); - vim_strup(p1); + char *p1 = xmalloc(len + 1); + vim_strncpy_up(p1, string, len); return p1; } @@ -324,6 +324,39 @@ void vim_strup(char *p) } } +// strcpy plus vim_strup. +void vim_strcpy_up(char *restrict dst, const char *restrict src) + FUNC_ATTR_NONNULL_ALL +{ + uint8_t c; + while ((c = (uint8_t)(*src++)) != NUL) { + *dst++ = (char)(uint8_t)(c < 'a' || c > 'z' ? c : c - 0x20); + } + *dst = '\0'; +} + +// strncpy (NUL-terminated) plus vim_strup. +void vim_strncpy_up(char *restrict dst, const char *restrict src, size_t n) + FUNC_ATTR_NONNULL_ALL +{ + uint8_t c; + while (n-- && (c = (uint8_t)(*src++)) != NUL) { + *dst++ = (char)(uint8_t)(c < 'a' || c > 'z' ? c : c - 0x20); + } + *dst = '\0'; +} + +// memcpy (does not NUL-terminate) plus vim_strup. +void vim_memcpy_up(char *restrict dst, const char *restrict src, size_t n) + FUNC_ATTR_NONNULL_ALL +{ + uint8_t c; + while (n--) { + c = (uint8_t)(*src++); + *dst++ = (char)(uint8_t)(c < 'a' || c > 'z' ? c : c - 0x20); + } +} + /// Make given string all upper-case or all lower-case /// /// Handles multi-byte characters as good as possible. @@ -910,7 +943,7 @@ static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const { if (*ap_types == NULL || *num_posarg < arg) { const char **new_types = *ap_types == NULL - ? xcalloc(sizeof(const char *), (size_t)arg) + ? xcalloc((size_t)arg, sizeof(const char *)) : xrealloc(*ap_types, (size_t)arg * sizeof(const char *)); for (int idx = *num_posarg; idx < arg; idx++) { @@ -953,6 +986,40 @@ static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const return OK; } +static void format_overflow_error(const char *pstart) +{ + const char *p = pstart; + + while (ascii_isdigit((int)(*p))) { + p++; + } + + size_t arglen = (size_t)(p - pstart); + char *argcopy = xstrnsave(pstart, arglen); + semsg(_(e_val_too_large), argcopy); + xfree(argcopy); +} + +enum { MAX_ALLOWED_STRING_WIDTH = 6400, }; + +static int get_unsigned_int(const char *pstart, const char **p, unsigned *uj) +{ + *uj = (unsigned)(**p - '0'); + (*p)++; + + while (ascii_isdigit((int)(**p)) && *uj < MAX_ALLOWED_STRING_WIDTH) { + *uj = 10 * *uj + (unsigned)(**p - '0'); + (*p)++; + } + + if (*uj > MAX_ALLOWED_STRING_WIDTH) { + format_overflow_error(pstart); + return FAIL; + } + + return OK; +} + static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *fmt, typval_T *tvs) FUNC_ATTR_NONNULL_ARG(1, 2) { @@ -986,6 +1053,7 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * // variable for positional arg int pos_arg = -1; + const char *pstart = p + 1; p++; // skip '%' @@ -1005,11 +1073,12 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * } // Positional argument - unsigned uj = (unsigned)(*p++ - '0'); + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(pstart, &p, &uj) == FAIL) { + goto error; } + pos_arg = (int)uj; any_pos = 1; @@ -1047,10 +1116,10 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * if (ascii_isdigit((int)(*p))) { // Positional argument field width - unsigned uj = (unsigned)(*p++ - '0'); + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(arg + 1, &p, &uj) == FAIL) { + goto error; } if (*p != '$') { @@ -1072,10 +1141,11 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we treat // argument like common implementations do - unsigned uj = (unsigned)(*p++ - '0'); + const char *digstart = p; + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; } if (*p == '$') { @@ -1093,10 +1163,10 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * if (ascii_isdigit((int)(*p))) { // Parse precision - unsigned uj = (unsigned)(*p++ - '0'); + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(arg + 1, &p, &uj) == FAIL) { + goto error; } if (*p == '$') { @@ -1119,10 +1189,11 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we // treat argument like common implementations do - unsigned uj = (unsigned)(*p++ - '0'); + const char *digstart = p; + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; } if (*p == '$') { @@ -1414,11 +1485,13 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st if (*ptype == '$') { // Positional argument - unsigned uj = (unsigned)(*p++ - '0'); + const char *digstart = p; + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; } + pos_arg = (int)uj; p++; @@ -1449,15 +1522,18 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st // parse field width if (*p == '*') { + const char *digstart = p + 1; + p++; if (ascii_isdigit((int)(*p))) { // Positional argument field width - unsigned uj = (unsigned)(*p++ - '0'); + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; } + arg_idx = (int)uj; p++; @@ -1469,6 +1545,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st &arg_cur, fmt), va_arg(ap, int))); + if (j > MAX_ALLOWED_STRING_WIDTH) { + format_overflow_error(digstart); + goto error; + } + if (j >= 0) { min_field_width = (size_t)j; } else { @@ -1478,11 +1559,18 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we treat // argument like common implementations do - unsigned uj = (unsigned)(*p++ - '0'); + const char *digstart = p; + unsigned uj; + + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; + } - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (uj > MAX_ALLOWED_STRING_WIDTH) { + format_overflow_error(digstart); + goto error; } + min_field_width = uj; } @@ -1494,22 +1582,32 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we // treat argument like common implementations do - unsigned uj = (unsigned)(*p++ - '0'); + const char *digstart = p; + unsigned uj; + + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; + } - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (uj > MAX_ALLOWED_STRING_WIDTH) { + format_overflow_error(digstart); + goto error; } + precision = uj; } else if (*p == '*') { + const char *digstart = p; + p++; if (ascii_isdigit((int)(*p))) { // positional argument - unsigned uj = (unsigned)(*p++ - '0'); + unsigned uj; - while (ascii_isdigit((int)(*p))) { - uj = 10 * uj + (unsigned)(*p++ - '0'); + if (get_unsigned_int(digstart, &p, &uj) == FAIL) { + goto error; } + arg_idx = (int)uj; p++; @@ -1521,6 +1619,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st &arg_cur, fmt), va_arg(ap, int))); + if (j > MAX_ALLOWED_STRING_WIDTH) { + format_overflow_error(digstart); + goto error; + } + if (j >= 0) { precision = (size_t)j; } else { @@ -2127,6 +2230,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st emsg(_("E767: Too many arguments to printf()")); } +error: xfree(ap_types); va_end(ap); @@ -3024,3 +3128,39 @@ void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } rettv->vval.v_string = xstrnsave(head, (size_t)(tail - head)); } + +/// compare two keyvalue_T structs by case sensitive value +int cmp_keyvalue_value(const void *a, const void *b) +{ + keyvalue_T *kv1 = (keyvalue_T *)a; + keyvalue_T *kv2 = (keyvalue_T *)b; + + return strcmp(kv1->value, kv2->value); +} + +/// compare two keyvalue_T structs by value with length +int cmp_keyvalue_value_n(const void *a, const void *b) +{ + keyvalue_T *kv1 = (keyvalue_T *)a; + keyvalue_T *kv2 = (keyvalue_T *)b; + + return strncmp(kv1->value, kv2->value, MAX(kv1->length, kv2->length)); +} + +/// compare two keyvalue_T structs by case insensitive value +int cmp_keyvalue_value_i(const void *a, const void *b) +{ + keyvalue_T *kv1 = (keyvalue_T *)a; + keyvalue_T *kv2 = (keyvalue_T *)b; + + return STRICMP(kv1->value, kv2->value); +} + +/// compare two keyvalue_T structs by case insensitive value with length +int cmp_keyvalue_value_ni(const void *a, const void *b) +{ + keyvalue_T *kv1 = (keyvalue_T *)a; + keyvalue_T *kv2 = (keyvalue_T *)b; + + return STRNICMP(kv1->value, kv2->value, MAX(kv1->length, kv2->length)); +} diff --git a/src/nvim/strings.h b/src/nvim/strings.h index 903559b062..f80e85afb0 100644 --- a/src/nvim/strings.h +++ b/src/nvim/strings.h @@ -32,6 +32,18 @@ static inline char *strappend(char *const dst, const char *const src) typedef kvec_t(char) StringBuilder; +// Return the length of a string literal +#define STRLEN_LITERAL(s) (sizeof(s) - 1) + +/// Store a key/value pair +typedef struct { + int key; ///< the key + char *value; ///< the value string + size_t length; ///< length of the value string +} keyvalue_T; + +#define KEYVALUE_ENTRY(k, v) { (k), (v), STRLEN_LITERAL(v) } + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "strings.h.generated.h" #endif diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index ab8ab62b90..b63d2a729d 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -549,8 +549,8 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) // Skip lines that end in a backslash. for (; start_lnum > 1; start_lnum--) { - char *line = ml_get(start_lnum - 1); - if (*line == NUL || *(line + strlen(line) - 1) != '\\') { + char *l = ml_get(start_lnum - 1); + if (*l == NUL || *(l + ml_get_len(start_lnum - 1) - 1) != '\\') { break; } } @@ -2352,7 +2352,6 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_ regmmatch_T regmatch; regmmatch_T best_regmatch; // startpos/endpos of best match lpos_T pos; - char *line; bool had_match = false; char buf_chartab[32]; // chartab array for syn option iskeyword @@ -2457,8 +2456,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_ break; } - line = ml_get_buf(syn_buf, startpos->lnum); - int line_len = (int)strlen(line); + int line_len = ml_get_buf_len(syn_buf, startpos->lnum); // take care of an empty match or negative offset if (pos.col <= matchcol) { @@ -2635,7 +2633,7 @@ static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *s if (result->lnum > syn_buf->b_ml.ml_line_count) { // a "\n" at the end of the pattern may take us below the last line result->lnum = syn_buf->b_ml.ml_line_count; - col = (int)strlen(ml_get_buf(syn_buf, result->lnum)); + col = ml_get_buf_len(syn_buf, result->lnum); } if (off != 0) { base = ml_get_buf(syn_buf, result->lnum); @@ -2735,7 +2733,7 @@ static int check_keyword_id(char *const line, const int startcol, int *const end // Must make a copy of the keyword, so we can add a NUL and make it // lowercase. char keyword[MAXKEYWLEN + 1]; // assume max. keyword len is 80 - xstrlcpy(keyword, kwp, (size_t)kwlen + 1); + xmemcpyz(keyword, kwp, (size_t)kwlen); keyentry_T *kp = NULL; @@ -4951,7 +4949,7 @@ static int get_id_list(char **const arg, const int keylen, int16_t **const list, do { for (end = p; *end && !ascii_iswhite(*end) && *end != ','; end++) {} char *const name = xmalloc((size_t)(end - p) + 3); // leave room for "^$" - xstrlcpy(name + 1, p, (size_t)(end - p) + 1); + xmemcpyz(name + 1, p, (size_t)(end - p)); if (strcmp(name + 1, "ALLBUT") == 0 || strcmp(name + 1, "ALL") == 0 || strcmp(name + 1, "TOP") == 0 diff --git a/src/nvim/tag.c b/src/nvim/tag.c index ab5bfc6773..e7f483dd3d 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -316,11 +316,6 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose) static char **matches = NULL; static int flags; - if (tfu_in_use) { - emsg(_(e_cannot_modify_tag_stack_within_tagfunc)); - return; - } - #ifdef EXITFREE if (type == DT_FREE) { // remove the list of matches @@ -330,6 +325,15 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose) } #endif + if (tfu_in_use) { + emsg(_(e_cannot_modify_tag_stack_within_tagfunc)); + return; + } + + if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) { + return; + } + if (type == DT_HELP) { type = DT_TAG; no_regexp = true; @@ -445,7 +449,7 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose) } curwin->w_cursor.col = saved_fmark.mark.col; curwin->w_set_curswant = true; - check_cursor(); + check_cursor(curwin); if ((fdo_flags & FDO_TAG) && old_KeyTyped) { foldOpenCursor(); } @@ -998,7 +1002,7 @@ static int add_llist_tags(char *tag, int num_matches, char **matches) if (len > 128) { len = 128; } - xstrlcpy(tag_name, tagp.tagname, (size_t)len + 1); + xmemcpyz(tag_name, tagp.tagname, (size_t)len); tag_name[len] = NUL; // Save the tag file name @@ -1464,7 +1468,7 @@ static bool findtags_in_help_init(findtags_state_T *st) // language name in help_lang[]. i = (int)strlen(st->tag_fname); if (i > 3 && st->tag_fname[i - 3] == '-') { - xstrlcpy(st->help_lang, st->tag_fname + i - 2, 3); + xmemcpyz(st->help_lang, st->tag_fname + i - 2, 2); } else { STRCPY(st->help_lang, "en"); } @@ -2011,7 +2015,7 @@ static void findtags_add_match(findtags_state_T *st, tagptrs_T *tagpp, findtags_ if (tagpp->command + 2 < temp_end) { len = (size_t)(temp_end - tagpp->command - 2); mfp = xmalloc(len + 2); - xstrlcpy(mfp, tagpp->command + 2, len + 1); + xmemcpyz(mfp, tagpp->command + 2, len); } else { mfp = NULL; } @@ -2019,7 +2023,7 @@ static void findtags_add_match(findtags_state_T *st, tagptrs_T *tagpp, findtags_ } else { len = (size_t)(tagpp->tagname_end - tagpp->tagname); mfp = xmalloc(sizeof(char) + len + 1); - xstrlcpy(mfp, tagpp->tagname, len + 1); + xmemcpyz(mfp, tagpp->tagname, len); // if wanted, re-read line to get long form too if (State & MODE_INSERT) { @@ -2784,6 +2788,10 @@ static char *tag_full_fname(tagptrs_T *tagp) /// @return OK for success, NOTAGFILE when file not found, FAIL otherwise. static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) { + if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) { + return FAIL; + } + char *pbuf_end; char *tofree_fname = NULL; tagptrs_T tagp; @@ -2934,6 +2942,8 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) str = skip_regexp(pbuf + 1, pbuf[0], false) + 1; } if (str > pbuf_end - 1) { // search command with nothing following + size_t pbuflen = (size_t)(pbuf_end - pbuf); + bool save_p_ws = p_ws; int save_p_ic = p_ic; int save_p_scs = p_scs; @@ -2948,25 +2958,27 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) // start search before first line curwin->w_cursor.lnum = 0; } - if (do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, 1, search_options, NULL)) { + if (do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, pbuflen - 1, 1, + search_options, NULL)) { retval = OK; } else { int found = 1; // try again, ignore case now p_ic = true; - if (!do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, 1, search_options, NULL)) { + if (!do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, pbuflen - 1, 1, + search_options, NULL)) { // Failed to find pattern, take a guess: "^func (" found = 2; test_for_static(&tagp); char cc = *tagp.tagname_end; *tagp.tagname_end = NUL; - snprintf(pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname); - if (!do_search(NULL, '/', '/', pbuf, 1, search_options, NULL)) { + pbuflen = (size_t)snprintf(pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname); + if (!do_search(NULL, '/', '/', pbuf, pbuflen, 1, search_options, NULL)) { // Guess again: "^char * \<func (" - snprintf(pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(", - tagp.tagname); - if (!do_search(NULL, '/', '/', pbuf, 1, search_options, NULL)) { + pbuflen = (size_t)snprintf(pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(", + tagp.tagname); + if (!do_search(NULL, '/', '/', pbuf, pbuflen, 1, search_options, NULL)) { found = 0; } } @@ -2994,7 +3006,7 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) // A search command may have positioned the cursor beyond the end // of the line. May need to correct that here. - check_cursor(); + check_cursor(curwin); } else { const int save_secure = secure; @@ -3039,7 +3051,7 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) if (l_g_do_tagpreview != 0 && curwin != curwin_save && win_valid(curwin_save)) { // Return cursor to where we were - validate_cursor(); + validate_cursor(curwin); redraw_later(curwin, UPD_VALID); win_enter(curwin_save, true); } @@ -3260,7 +3272,7 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char *start if (len > MAXPATHL - 1) { len = MAXPATHL - 1; } - xstrlcpy(buf, start, (size_t)len + 1); + xmemcpyz(buf, start, (size_t)len); } buf[len] = NUL; int retval = tv_dict_add_str(dict, field_name, strlen(field_name), buf); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index b5a3cffe2f..2b05a8047e 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -307,7 +307,8 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) // Set up screen term->vts = vterm_obtain_screen(term->vt); vterm_screen_enable_altscreen(term->vts, true); - vterm_screen_enable_reflow(term->vts, true); + // TODO(clason): reenable when https://github.com/neovim/neovim/issues/23762 is fixed + // vterm_screen_enable_reflow(term->vts, true); // delete empty lines at the end of the buffer vterm_screen_set_callbacks(term->vts, &vterm_screen_callbacks, term); vterm_screen_set_unrecognised_fallbacks(term->vts, &vterm_fallbacks, term); @@ -542,6 +543,9 @@ bool terminal_enter(void) } else { curwin->w_p_cul = false; } + if (curwin->w_p_cuc) { + redraw_later(curwin, UPD_SOME_VALID); + } curwin->w_p_cuc = false; curwin->w_p_so = 0; curwin->w_p_siso = 0; @@ -612,7 +616,7 @@ static void terminal_check_cursor(void) row_to_linenr(term, term->cursor.row)); // Nudge cursor when returning to normal-mode. int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1); - coladvance(MAX(0, term->cursor.col + off)); + coladvance(curwin, MAX(0, term->cursor.col + off)); } // Function executed before each iteration of terminal mode. @@ -626,7 +630,7 @@ static int terminal_check(VimState *state) } terminal_check_cursor(); - validate_cursor(); + validate_cursor(curwin); if (must_redraw) { update_screen(); @@ -1627,7 +1631,10 @@ end: return false; } - ins_char_typebuf(vgetc_char, vgetc_mod_mask); + int len = ins_char_typebuf(vgetc_char, vgetc_mod_mask, true); + if (KeyTyped) { + ungetchars(len); + } return true; } diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c index bfe3ed5972..1722bcc968 100644 --- a/src/nvim/textformat.c +++ b/src/nvim/textformat.c @@ -86,8 +86,7 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on // When 'ai' is off we don't want a space under the cursor to be // deleted. Replace it with an 'x' temporarily. - if (!curbuf->b_p_ai - && !(State & VREPLACE_FLAG)) { + if (!curbuf->b_p_ai && !(State & VREPLACE_FLAG)) { cc = gchar_cursor(); if (ascii_iswhite(cc)) { save_char = (char)cc; @@ -106,14 +105,9 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on colnr_T col; bool did_do_comment = false; - // Cursor is currently at the end of line. No need to format - // if line length is less than textwidth (8 * textwidth for - // utf safety) - if (curwin->w_cursor.col < 8 * textwidth) { - colnr_T virtcol = get_nolist_virtcol() + char2cells(c != NUL ? c : gchar_cursor()); - if (virtcol <= (colnr_T)textwidth) { - break; - } + colnr_T virtcol = get_nolist_virtcol() + char2cells(c != NUL ? c : gchar_cursor()); + if (virtcol <= (colnr_T)textwidth) { + break; } if (no_leader) { @@ -158,19 +152,12 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on } // find column of textwidth border - coladvance((colnr_T)textwidth); + coladvance(curwin, (colnr_T)textwidth); wantcol = curwin->w_cursor.col; - // If startcol is large (a long line), formatting takes too much - // time. The algorithm is O(n^2), it walks from the end of the - // line to textwidth border every time for each line break. - // - // Ceil to 8 * textwidth to optimize. - curwin->w_cursor.col = startcol < 8 * textwidth ? startcol : 8 * textwidth; - + curwin->w_cursor.col = startcol; foundcol = 0; int skip_pos = 0; - bool first_pass = true; // Find position to break at. // Stop at first entered white when 'formatoptions' has 'v' @@ -178,9 +165,8 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on || (flags & INSCHAR_FORMAT) || curwin->w_cursor.lnum != Insstart.lnum || curwin->w_cursor.col >= Insstart.col) { - if (first_pass && c != NUL) { + if (curwin->w_cursor.col == startcol && c != NUL) { cc = c; - first_pass = false; } else { cc = gchar_cursor(); } @@ -446,7 +432,7 @@ void internal_format(int textwidth, int second_indent, int flags, bool format_on // Check if cursor is not past the NUL off the line, cindent // may have added or removed indent. curwin->w_cursor.col += startcol; - colnr_T len = (colnr_T)strlen(get_cursor_line_ptr()); + colnr_T len = get_cursor_line_len(); if (curwin->w_cursor.col > len) { curwin->w_cursor.col = len; } @@ -507,12 +493,11 @@ static int fmt_check_par(linenr_T lnum, int *leader_len, char **leader_flags, bo static bool ends_in_white(linenr_T lnum) { char *s = ml_get(lnum); - size_t l; if (*s == NUL) { return false; } - l = strlen(s) - 1; + colnr_T l = ml_get_len(lnum) - 1; return ascii_iswhite((uint8_t)s[l]); } @@ -545,7 +530,7 @@ static bool same_leader(linenr_T lnum, int leader1_len, char *leader1_flags, int return false; } if (*p == COM_START) { - int line_len = (int)strlen(ml_get(lnum)); + int line_len = ml_get_len(lnum); if (line_len <= leader1_len) { return false; } @@ -648,7 +633,7 @@ void auto_format(bool trailblank, bool prev_line) // in 'formatoptions' and there is a single character before the cursor. // Otherwise the line would be broken and when typing another non-white // next they are not joined back together. - int wasatend = (pos.col == (colnr_T)strlen(old)); + bool wasatend = (pos.col == get_cursor_line_len()); if (*old != NUL && !trailblank && wasatend) { dec_cursor(); int cc = gchar_cursor(); @@ -691,9 +676,9 @@ void auto_format(bool trailblank, bool prev_line) if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { // "cannot happen" curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); } else { - check_cursor_col(); + check_cursor_col(curwin); } // Insert mode: If the cursor is now after the end of the line while it @@ -702,7 +687,7 @@ void auto_format(bool trailblank, bool prev_line) // formatted. if (!wasatend && has_format_option(FO_WHITE_PAR)) { char *linep = get_cursor_line_ptr(); - colnr_T len = (colnr_T)strlen(linep); + colnr_T len = get_cursor_line_len(); if (curwin->w_cursor.col == len) { char *plinep = xstrnsave(linep, (size_t)len + 2); plinep[len] = ' '; @@ -716,7 +701,7 @@ void auto_format(bool trailblank, bool prev_line) } } - check_cursor(); + check_cursor(curwin); } /// When an extra space was added to continue a paragraph for auto-formatting, @@ -840,7 +825,7 @@ void op_format(oparg_T *oap, bool keep_cursor) saved_cursor.lnum = 0; // formatting may have made the cursor position invalid - check_cursor(); + check_cursor(curwin); } if (oap->is_VIsual) { @@ -1064,7 +1049,7 @@ void format_lines(linenr_T line_count, bool avoid_fex) // put cursor on last non-space State = MODE_NORMAL; // don't go past end-of-line - coladvance(MAXCOL); + coladvance(curwin, MAXCOL); while (curwin->w_cursor.col && ascii_isspace(gchar_cursor())) { dec_cursor(); } @@ -1120,7 +1105,7 @@ void format_lines(linenr_T line_count, bool avoid_fex) } first_par_line = false; // If the line is getting long, format it next time - if (strlen(get_cursor_line_ptr()) > (size_t)max_len) { + if (get_cursor_line_len() > max_len) { force_format = true; } else { force_format = false; diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c index d9c2b3b111..3c81a840ea 100644 --- a/src/nvim/textobject.c +++ b/src/nvim/textobject.c @@ -187,7 +187,7 @@ bool findpar(bool *pincl, int dir, int count, int what, bool both) // skip folded lines fold_skipped = false; - if (first && hasFolding(curr, &fold_first, &fold_last)) { + if (first && hasFolding(curwin, curr, &fold_first, &fold_last)) { curr = ((dir > 0) ? fold_last : fold_first) + dir; fold_skipped = true; } @@ -218,7 +218,7 @@ bool findpar(bool *pincl, int dir, int count, int what, bool both) // Put the cursor on the last character in the last line and make the // motion inclusive. - if ((curwin->w_cursor.col = (colnr_T)strlen(line)) != 0) { + if ((curwin->w_cursor.col = ml_get_len(curr)) != 0) { curwin->w_cursor.col--; curwin->w_cursor.col -= utf_head_off(line, line + curwin->w_cursor.col); *pincl = true; @@ -318,8 +318,8 @@ int fwd_word(int count, bool bigword, bool eol) while (--count >= 0) { // When inside a range of folded lines, move to the last char of the // last line. - if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) { - coladvance(MAXCOL); + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) { + coladvance(curwin, MAXCOL); } int sclass = cls(); // starting class @@ -374,7 +374,7 @@ int bck_word(int count, bool bigword, bool stop) while (--count >= 0) { // When inside a range of folded lines, move to the first char of the // first line. - if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) { + if (hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) { curwin->w_cursor.col = 0; } sclass = cls(); @@ -431,8 +431,8 @@ int end_word(int count, bool bigword, bool stop, bool empty) while (--count >= 0) { // When inside a range of folded lines, move to the last char of the // last line. - if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) { - coladvance(MAXCOL); + if (hasFolding(curwin, curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) { + coladvance(curwin, MAXCOL); } sclass = cls(); if (inc_cursor() == -1) { diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 214474ff51..f1594dfcb9 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -442,12 +442,12 @@ static void tk_getkeys(TermInput *input, bool force) forward_modified_utf8(input, &key); } else if (key.type == TERMKEY_TYPE_MOUSE) { forward_mouse_event(input, &key); + } else if (key.type == TERMKEY_TYPE_MODEREPORT) { + handle_modereport(input, &key); } else if (key.type == TERMKEY_TYPE_UNKNOWN_CSI) { handle_unknown_csi(input, &key); } else if (key.type == TERMKEY_TYPE_OSC || key.type == TERMKEY_TYPE_DCS) { handle_term_response(input, &key); - } else if (key.type == TERMKEY_TYPE_MODEREPORT) { - handle_modereport(input, &key); } } @@ -553,6 +553,13 @@ static void handle_term_response(TermInput *input, const TermKeyKey *key) if (termkey_interpret_string(input->tk, key, &str) == TERMKEY_RES_KEY) { assert(str != NULL); + // Handle DECRQSS SGR response for the query from tui_query_extended_underline(). + // Some terminals include "0" in the attribute list unconditionally; others don't. + if (key->type == TERMKEY_TYPE_DCS + && (strnequal(str, S_LEN("1$r4:3m")) || strnequal(str, S_LEN("1$r0;4:3m")))) { + tui_enable_extended_underline(input->tui_data); + } + // Send an event to nvim core. This will update the v:termresponse variable // and fire the TermResponse event MAXSIZE_TEMP_ARRAY(args, 2); diff --git a/src/nvim/tui/terminfo_defs.h b/src/nvim/tui/terminfo_defs.h index 74ba23e4d9..4e8978479d 100644 --- a/src/nvim/tui/terminfo_defs.h +++ b/src/nvim/tui/terminfo_defs.h @@ -12,6 +12,7 @@ // move_insert_mode, // move_standout_mode, // prtr_silent, +// AX, // columns#80, // init_tabs#8, // lines#24, @@ -93,7 +94,7 @@ static const int8_t ansi_terminfo[] = { 26,1,40,0,38,0,16,0,125,1,68,2,97,110,115,105,124,97,110,115,105,47,112,99,45,116,101,114,109,32,99,111,109,112,97,116,105,98,108,101,32,119,105,116,104,32,99,111,108,111,114,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,80,0,8,0,24,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,3,0,0,0,4,0,6,0,-1,-1,8,0,13,0,20,0,24,0,28,0,-1,-1,39,0,56,0,60,0,-1,-1,64,0,-1,-1,-1,-1,68,0,-1,-1,72,0,-1,-1,76,0,80,0,-1,-1,-1,-1,84,0,90,0,95,0,-1,-1,-1,-1,-1,-1,-1,-1,100,0,-1,-1,105,0,110,0,115,0,120,0,-127,0,-121,0,-1,-1,-1,-1,-1,-1,-113,0,-109,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-105,0,-1,-1,-101,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-99,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-95,0,-91,0,-1,-1,-87,0,-1,-1,-1,-1,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-79,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-75,0,-1,-1,-70,0,-61,0,-52,0,-43,0,-34,0,-25,0,-16,0,-7,0,2,1,11,1,-1,-1,-1,-1,-1,-1,-1,-1,20,1,25,1,30,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,50,1,-1,-1,61,1,-1,-1,63,1,-107,1,-1,-1,-104,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-100,1,-1,-1,-37,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-33,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-28,1,-17,1,-12,1,7,2,11,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,20,2,30,2,-1,-1,-1,-1,-1,-1,40,2,44,2,48,2,52,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,56,2,62,2,27,91,90,0,7,0,13,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,68,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,49,109,0,27,91,53,109,0,27,91,49,109,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,49,48,109,0,27,91,48,59,49,48,109,0,27,91,109,0,27,91,109,0,27,91,76,0,8,0,27,91,66,0,27,91,72,0,27,91,76,0,27,91,68,0,27,91,67,0,27,91,65,0,13,27,91,83,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,52,105,0,27,91,53,105,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,91,37,105,37,112,49,37,100,100,0,10,0,27,91,48,59,49,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,55,37,116,59,56,37,59,37,63,37,112,57,37,116,59,49,49,37,59,109,0,27,72,0,27,91,73,0,43,16,44,17,45,24,46,25,48,-37,96,4,97,-79,102,-8,103,-15,104,-80,106,-39,107,-65,108,-38,109,-64,110,-59,111,126,112,-60,113,-60,114,-60,115,95,116,-61,117,-76,118,-63,119,-62,120,-77,121,-13,122,-14,123,-29,124,-40,125,-100,126,-2,0,27,91,90,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,40,66,0,27,41,66,0,27,42,66,0,27,43,66,0,27,91,49,49,109,0,27,91,49,48,109,0,1,0,0,0,0,0,1,0,3,0,1,0,0,0,65,88,0 }; -// conemu|ANIS X3.64 and Xterm 256 colors for ConEmu with libuv, +// conemu|ANSI X3.64 and Xterm 256 colors for ConEmu with libuv, // auto_right_margin, // back_color_erase, // backspaces_with_bs, @@ -101,6 +102,9 @@ static const int8_t ansi_terminfo[] = { // move_insert_mode, // move_standout_mode, // no_pad_char, +// AX, +// XF, +// XT, // columns#80, // init_tabs#8, // lines#24, @@ -289,8 +293,94 @@ static const int8_t ansi_terminfo[] = { // user7@, // user8@, // user9@, +// BD=\E[?2004l, +// BE=\E[?2004h, +// Cr@, +// Cs@, +// E3=\E[3J, +// Ms@, +// PE=\E[201~, +// PS=\E[200~, +// RV=\E[>c, +// Se=\E[2 q, +// Ss=\E[%p1%d q, +// XM@, +// XR=\E[>0q, +// fd=\E[?1004l, +// fe=\E[?1004h, +// kDC3@, +// kDC4@, +// kDC5@, +// kDC6@, +// kDC7@, +// kDN@, +// kDN3@, +// kDN4@, +// kDN5@, +// kDN6@, +// kDN7@, +// kEND3@, +// kEND4@, +// kEND5@, +// kEND6@, +// kEND7@, +// kHOM3@, +// kHOM4@, +// kHOM5@, +// kHOM6@, +// kHOM7@, +// kIC3@, +// kIC4@, +// kIC5@, +// kIC6@, +// kIC7@, +// kLFT3@, +// kLFT4@, +// kLFT5@, +// kLFT6@, +// kLFT7@, +// kNXT3@, +// kNXT4@, +// kNXT5@, +// kNXT6@, +// kNXT7@, +// kPRV3@, +// kPRV4@, +// kPRV5@, +// kPRV6@, +// kPRV7@, +// kRIT3@, +// kRIT4@, +// kRIT5@, +// kRIT6@, +// kRIT7@, +// kUP=\E[1;2A, +// kUP3@, +// kUP4@, +// kUP5@, +// kUP6@, +// kUP7@, +// ka2=\EOx, +// kb1=\EOt, +// kb3=\EOv, +// kc2=\EOr, +// kp5=\EOE, +// kpADD=\EOk, +// kpCMA=\EOl, +// kpDIV=\EOo, +// kpDOT=\EOn, +// kpMUL=\EOj, +// kpSUB=\EOm, +// kpZRO=\EOp, +// kxIN=\E[I, +// kxOUT=\E[O, +// rmxx@, +// rv=\E\[41;[1-6][0-9][0-9];0c, +// smxx@, +// xm@, +// xr=\EP>\|XTerm\([1-9][0-9]+\)\E\\, static const int8_t conemu_terminfo[] = { - 30,2,61,0,38,0,15,0,-99,1,61,3,99,111,110,101,109,117,124,65,78,73,83,32,88,51,46,54,52,32,97,110,100,32,88,116,101,114,109,32,50,53,54,32,99,111,108,111,114,115,32,102,111,114,32,67,111,110,69,109,117,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-2,-1,0,0,2,0,4,0,-2,-1,21,0,29,0,33,0,37,0,-1,-1,48,0,65,0,69,0,73,0,80,0,-1,-1,82,0,89,0,-1,-1,93,0,-2,-1,97,0,101,0,-1,-1,-1,-1,-2,-1,-2,-1,105,0,110,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,119,0,124,0,-127,0,-122,0,-2,-1,-113,0,-108,0,-1,-1,-2,-1,-99,0,-93,0,-2,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-87,0,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-81,0,-1,-1,-76,0,-1,-1,-1,-1,-1,-1,-1,-1,-72,0,-67,0,-61,0,-56,0,-51,0,-46,0,-41,0,-35,0,-29,0,-23,0,-17,0,-12,0,-1,-1,-7,0,-1,-1,-3,0,2,1,7,1,11,1,18,1,-1,-1,25,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,29,1,-1,-1,32,1,41,1,50,1,59,1,68,1,77,1,86,1,95,1,104,1,113,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,122,1,-2,-1,-2,-1,-1,-1,-1,-1,-114,1,-111,1,-100,1,-97,1,-95,1,-92,1,-2,-1,-1,-1,-49,1,-1,-1,-1,-1,-1,-1,-1,-1,-47,1,-43,1,-39,1,-35,1,-31,1,-1,-1,-1,-1,-2,-1,-1,-1,-27,1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-23,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-19,1,-14,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-10,1,-1,-1,-1,-1,-3,1,-1,-1,-1,-1,-1,-1,-1,-1,4,2,11,2,18,2,-1,-1,-1,-1,25,2,-1,-1,32,2,-1,-1,-1,-1,-1,-1,39,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,46,2,52,2,58,2,64,2,70,2,76,2,82,2,88,2,94,2,100,2,106,2,112,2,118,2,124,2,-126,2,-120,2,-114,2,-108,2,-102,2,-96,2,-90,2,-84,2,-78,2,-72,2,-66,2,-60,2,-54,2,-48,2,-42,2,-36,2,-29,2,-23,2,-17,2,-11,2,-5,2,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,1,3,6,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,13,3,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,33,3,47,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,27,91,90,0,27,79,69,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,51,56,59,53,59,37,112,49,37,100,109,0,27,91,52,56,59,53,59,37,112,49,37,100,109,0,0,3,0,0,0,86,0,117,0,121,2,1,1,1,0,0,0,9,0,-2,-1,-2,-1,18,0,-2,-1,23,0,30,0,37,0,42,0,48,0,-2,-1,58,0,64,0,73,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,82,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,89,0,93,0,97,0,101,0,105,0,109,0,113,0,117,0,121,0,125,0,-127,0,-123,0,-119,0,-115,0,-2,-1,-111,0,-2,-1,-2,-1,-86,0,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,45,0,48,0,51,0,54,0,59,0,64,0,69,0,74,0,79,0,83,0,88,0,93,0,98,0,103,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-83,0,-78,0,-73,0,-68,0,-63,0,-57,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,61,1,66,1,71,1,76,1,81,1,86,1,90,1,94,1,98,1,102,1,106,1,112,1,118,1,124,1,-126,1,-120,1,-114,1,-108,1,-103,1,-97,1,-92,1,-89,1,-84,1,-81,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,91,51,74,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,62,99,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,62,48,113,0,27,91,63,49,48,48,52,108,0,27,91,63,49,48,48,52,104,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,27,91,73,0,27,91,79,0,27,92,91,52,49,59,91,49,45,54,93,91,48,45,57,93,91,48,45,57,93,59,48,99,0,27,80,62,92,124,88,84,101,114,109,92,40,91,49,45,57,93,91,48,45,57,93,43,92,41,27,92,92,0,65,88,0,88,70,0,88,84,0,66,68,0,66,69,0,67,114,0,67,115,0,69,51,0,77,115,0,80,69,0,80,83,0,82,86,0,83,101,0,83,115,0,88,77,0,88,82,0,102,100,0,102,101,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,107,120,73,78,0,107,120,79,85,84,0,114,109,120,120,0,114,118,0,115,109,120,120,0,120,109,0,120,114,0 + 30,2,61,0,38,0,15,0,-99,1,61,3,99,111,110,101,109,117,124,65,78,83,73,32,88,51,46,54,52,32,97,110,100,32,88,116,101,114,109,32,50,53,54,32,99,111,108,111,114,115,32,102,111,114,32,67,111,110,69,109,117,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-2,-1,0,0,2,0,4,0,-2,-1,21,0,29,0,33,0,37,0,-1,-1,48,0,65,0,69,0,73,0,80,0,-1,-1,82,0,89,0,-1,-1,93,0,-2,-1,97,0,101,0,-1,-1,-1,-1,-2,-1,-2,-1,105,0,110,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,119,0,124,0,-127,0,-122,0,-2,-1,-113,0,-108,0,-1,-1,-2,-1,-99,0,-93,0,-2,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-87,0,-1,-1,-83,0,-1,-1,-1,-1,-1,-1,-81,0,-1,-1,-76,0,-1,-1,-1,-1,-1,-1,-1,-1,-72,0,-67,0,-61,0,-56,0,-51,0,-46,0,-41,0,-35,0,-29,0,-23,0,-17,0,-12,0,-1,-1,-7,0,-1,-1,-3,0,2,1,7,1,11,1,18,1,-1,-1,25,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,29,1,-1,-1,32,1,41,1,50,1,59,1,68,1,77,1,86,1,95,1,104,1,113,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,122,1,-2,-1,-2,-1,-1,-1,-1,-1,-114,1,-111,1,-100,1,-97,1,-95,1,-92,1,-2,-1,-1,-1,-49,1,-1,-1,-1,-1,-1,-1,-1,-1,-47,1,-43,1,-39,1,-35,1,-31,1,-1,-1,-1,-1,-2,-1,-1,-1,-27,1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-23,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-19,1,-14,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-10,1,-1,-1,-1,-1,-3,1,-1,-1,-1,-1,-1,-1,-1,-1,4,2,11,2,18,2,-1,-1,-1,-1,25,2,-1,-1,32,2,-1,-1,-1,-1,-1,-1,39,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,46,2,52,2,58,2,64,2,70,2,76,2,82,2,88,2,94,2,100,2,106,2,112,2,118,2,124,2,-126,2,-120,2,-114,2,-108,2,-102,2,-96,2,-90,2,-84,2,-78,2,-72,2,-66,2,-60,2,-54,2,-48,2,-42,2,-36,2,-29,2,-23,2,-17,2,-11,2,-5,2,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,1,3,6,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,13,3,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,33,3,47,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,27,91,90,0,27,79,69,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,51,56,59,53,59,37,112,49,37,100,109,0,27,91,52,56,59,53,59,37,112,49,37,100,109,0,0,3,0,0,0,86,0,117,0,121,2,1,1,1,0,0,0,9,0,-2,-1,-2,-1,18,0,-2,-1,23,0,30,0,37,0,42,0,48,0,-2,-1,58,0,64,0,73,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,82,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,89,0,93,0,97,0,101,0,105,0,109,0,113,0,117,0,121,0,125,0,-127,0,-123,0,-119,0,-115,0,-2,-1,-111,0,-2,-1,-2,-1,-86,0,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,45,0,48,0,51,0,54,0,59,0,64,0,69,0,74,0,79,0,83,0,88,0,93,0,98,0,103,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-83,0,-78,0,-73,0,-68,0,-63,0,-57,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,61,1,66,1,71,1,76,1,81,1,86,1,90,1,94,1,98,1,102,1,106,1,112,1,118,1,124,1,-126,1,-120,1,-114,1,-108,1,-103,1,-97,1,-92,1,-89,1,-84,1,-81,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,91,51,74,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,62,99,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,62,48,113,0,27,91,63,49,48,48,52,108,0,27,91,63,49,48,48,52,104,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,27,91,73,0,27,91,79,0,27,92,91,52,49,59,91,49,45,54,93,91,48,45,57,93,91,48,45,57,93,59,48,99,0,27,80,62,92,124,88,84,101,114,109,92,40,91,49,45,57,93,91,48,45,57,93,43,92,41,27,92,92,0,65,88,0,88,70,0,88,84,0,66,68,0,66,69,0,67,114,0,67,115,0,69,51,0,77,115,0,80,69,0,80,83,0,82,86,0,83,101,0,83,115,0,88,77,0,88,82,0,102,100,0,102,101,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,107,120,73,78,0,107,120,79,85,84,0,114,109,120,120,0,114,118,0,115,109,120,120,0,120,109,0,120,114,0 }; // cygwin|ANSI emulation for Cygwin, @@ -672,6 +762,43 @@ static const int8_t interix_8colour_terminfo[] = { // user7=\E[6n, // user8=\E[?%[;0123456789]c, // user9=\E[c, +// BD=\E[?2004l, +// BE=\E[?2004h, +// PE=\E[201~, +// PS=\E[200~, +// TS=\E]2;, +// XM=\E[?1000%?%p1%{1}%=%th%el%;, +// kDN3=\E\E[B, +// kDN4=\E[1;10B, +// kDN5=\E[1;5B, +// kDN6=\E[1;6B, +// kEND3=\E[1;9F, +// kEND4=\E[1;10F, +// kEND5=\E[1;5F, +// kEND6=\E[1;6F, +// kEND7=\E[1;13F, +// kEND8=\E[1;14F, +// kHOM3=\E[1;9H, +// kHOM4=\E[1;10H, +// kHOM5=\E[1;5H, +// kHOM6=\E[1;6H, +// kHOM7=\E[1;13H, +// kHOM8=\E[1;14H, +// kLFT3=\E\E[D, +// kLFT4=\E[1;10D, +// kLFT5=\E[1;5D, +// kLFT6=\E[1;6D, +// kNXT3=\E\E[6~, +// kPRV3=\E\E[5~, +// kRIT3=\E\E[C, +// kRIT4=\E[1;10C, +// kRIT5=\E[1;5C, +// kRIT6=\E[1;6C, +// kUP3=\E\E[A, +// kUP4=\E[1;10A, +// kUP5=\E[1;5A, +// kUP6=\E[1;6A, +// xm=\E[M%?%p4%t%p3%e%{3}%;%' '%+%c%p2%'\041'%+%c%p1%'\041'%+%c, static const int8_t iterm_256colour_terminfo[] = { 30,2,49,0,29,0,15,0,105,1,-29,3,105,84,101,114,109,50,46,97,112,112,124,105,116,101,114,109,50,124,116,101,114,109,105,110,97,108,32,101,109,117,108,97,116,111,114,32,102,111,114,32,77,97,99,32,79,83,32,88,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,1,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,50,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,95,0,-1,-1,99,0,-1,-1,103,0,107,0,111,0,-1,-1,117,0,119,0,124,0,-127,0,-1,-1,-109,0,-104,0,-1,-1,-1,-1,-99,0,-94,0,-89,0,-1,-1,-84,0,-82,0,-77,0,-1,-1,-59,0,-54,0,-48,0,-42,0,-1,-1,-24,0,-1,-1,-1,-1,-1,-1,-1,-1,-22,0,-18,0,-1,-1,-14,0,-1,-1,-1,-1,-1,-1,-12,0,-1,-1,-7,0,-1,-1,-1,-1,-1,-1,-1,-1,-3,0,1,1,7,1,11,1,15,1,19,1,25,1,31,1,37,1,43,1,49,1,-1,-1,-1,-1,53,1,-1,-1,57,1,62,1,67,1,71,1,78,1,-1,-1,85,1,89,1,97,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,105,1,-1,-1,108,1,117,1,126,1,-121,1,-112,1,-103,1,-94,1,-85,1,-76,1,-67,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-58,1,-1,-1,-1,-1,-32,1,-29,1,-18,1,-15,1,-13,1,-10,1,68,2,-1,-1,71,2,73,2,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-1,-1,-1,-1,78,2,-1,-1,-127,2,-1,-1,-1,-1,-123,2,-117,2,-1,-1,-1,-1,-111,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-104,2,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-100,2,-1,-1,-1,-1,-1,-1,-1,-1,-93,2,-1,-1,-86,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-79,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-72,2,-66,2,-60,2,-53,2,-46,2,-39,2,-32,2,-24,2,-16,2,-8,2,0,3,8,3,16,3,24,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,32,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,37,3,48,3,53,3,72,3,76,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,85,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,90,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,96,3,-1,-1,-1,-1,-1,-1,100,3,-93,3,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,50,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,50,48,48,47,62,27,91,63,53,108,0,7,0,27,91,64,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,50,59,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,79,70,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,49,59,50,68,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,0,0,0,0,37,0,74,0,7,2,0,0,9,0,18,0,25,0,32,0,37,0,64,0,69,0,77,0,84,0,91,0,98,0,106,0,113,0,120,0,-128,0,-120,0,-113,0,-105,0,-98,0,-91,0,-83,0,-75,0,-70,0,-62,0,-55,0,-48,0,-42,0,-36,0,-31,0,-23,0,-16,0,-9,0,-4,0,4,1,11,1,18,1,0,0,3,0,6,0,9,0,12,0,15,0,18,0,23,0,28,0,33,0,38,0,44,0,50,0,56,0,62,0,68,0,74,0,80,0,86,0,92,0,98,0,104,0,110,0,116,0,122,0,-128,0,-122,0,-116,0,-110,0,-104,0,-98,0,-92,0,-86,0,-81,0,-76,0,-71,0,-66,0,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,93,50,59,0,27,91,63,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,27,91,66,0,27,91,49,59,49,48,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,57,70,0,27,91,49,59,49,48,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,49,51,70,0,27,91,49,59,49,52,70,0,27,91,49,59,57,72,0,27,91,49,59,49,48,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,49,51,72,0,27,91,49,59,49,52,72,0,27,27,91,68,0,27,91,49,59,49,48,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,27,91,54,126,0,27,27,91,53,126,0,27,27,91,67,0,27,91,49,59,49,48,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,27,91,65,0,27,91,49,59,49,48,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,77,37,63,37,112,52,37,116,37,112,51,37,101,37,123,51,125,37,59,37,39,32,39,37,43,37,99,37,112,50,37,39,33,39,37,43,37,99,37,112,49,37,39,33,39,37,43,37,99,0,66,68,0,66,69,0,80,69,0,80,83,0,84,83,0,88,77,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,69,78,68,56,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,72,79,77,56,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,78,88,84,51,0,107,80,82,86,51,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,120,109,0 }; @@ -685,10 +812,12 @@ static const int8_t iterm_256colour_terminfo[] = { // move_insert_mode, // move_standout_mode, // xon_xoff, +// AX, // init_tabs#8, // max_colors#8, // max_pairs#64, // no_color_video#18, +// U8#1, // acs_chars=++\054\054--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, // bell=^G, // carriage_return=\r, @@ -794,6 +923,8 @@ static const int8_t iterm_256colour_terminfo[] = { // user7=\E[6n, // user8=\E[?6c, // user9=\E[c, +// E3=\E[3J, +// kcbt2=\E[Z, static const int8_t linux_16colour_terminfo[] = { 26,1,20,0,29,0,16,0,125,1,66,3,108,105,110,117,120,124,76,105,110,117,120,32,99,111,110,115,111,108,101,0,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,-1,-1,8,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,18,0,-1,-1,0,0,2,0,4,0,21,0,26,0,33,0,37,0,41,0,-1,-1,52,0,69,0,71,0,75,0,87,0,-1,-1,89,0,101,0,-1,-1,105,0,109,0,121,0,125,0,-1,-1,-1,-1,-127,0,-125,0,-120,0,-1,-1,-1,-1,-115,0,-110,0,-1,-1,-1,-1,-105,0,-100,0,-95,0,-90,0,-81,0,-79,0,-1,-1,-1,-1,-74,0,-69,0,-63,0,-57,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-39,0,-35,0,-1,-1,-31,0,-1,-1,-1,-1,-1,-1,-29,0,-1,-1,-24,0,-1,-1,-1,-1,-1,-1,-1,-1,-20,0,-15,0,-9,0,-4,0,1,1,6,1,11,1,17,1,23,1,29,1,35,1,40,1,-1,-1,45,1,-1,-1,49,1,54,1,59,1,-1,-1,-1,-1,-1,-1,63,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,67,1,-1,-1,70,1,79,1,88,1,97,1,-1,-1,106,1,115,1,124,1,-1,-1,-123,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-114,1,-1,-1,-1,-1,-1,-1,-108,1,-105,1,-94,1,-91,1,-89,1,-86,1,1,2,-1,-1,4,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,6,2,-1,-1,-1,-1,-1,-1,-1,-1,10,2,-1,-1,75,2,-1,-1,-1,-1,78,2,84,2,-1,-1,-1,-1,90,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,94,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,99,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,101,2,107,2,113,2,119,2,125,2,-125,2,-119,2,-113,2,-107,2,-101,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-95,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-90,2,-79,2,-74,2,-68,2,-64,2,-55,2,-51,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,30,3,-1,-1,-1,-1,-1,-1,34,3,44,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,54,3,60,3,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,27,91,63,49,99,0,8,0,27,91,63,50,53,104,27,91,63,48,99,0,27,91,67,0,27,91,65,0,27,91,63,50,53,104,27,91,63,56,99,0,27,91,80,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,109,15,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,50,48,48,47,62,27,91,63,53,108,0,27,91,64,0,27,91,76,0,127,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,99,27,93,82,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,59,49,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,54,37,116,59,49,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,91,71,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,9,0,27,91,63,55,104,0,27,91,63,55,108,0,27,41,48,0,27,91,52,126,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,82,0,27,93,80,37,112,49,37,120,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,48,50,120,0,27,91,77,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,91,49,49,109,0,27,91,49,48,109,0,1,0,1,0,2,0,6,0,24,0,1,0,1,0,0,0,5,0,0,0,3,0,6,0,9,0,27,91,51,74,0,27,91,90,0,65,88,0,85,56,0,69,51,0,107,99,98,116,50,0 }; @@ -807,10 +938,12 @@ static const int8_t linux_16colour_terminfo[] = { // move_insert_mode, // move_standout_mode, // xon_xoff, +// XT, // init_tabs#8, // max_colors#0x100, // max_pairs#0x10000, // no_color_video#22, +// U8#1, // acs_chars=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, // back_tab=\E[Z, // bell=^G, @@ -935,6 +1068,36 @@ static const int8_t linux_16colour_terminfo[] = { // user7=\E[6n, // user8=\E[?6c, // user9=\E[c, +// BD=\E[?2004l, +// BE=\E[?2004h, +// E3=\E[3J, +// PE=\E[201~, +// PS=\E[200~, +// TS=\E]0;, +// XM=\E[?1006;1000%?%p1%{1}%=%th%el%;, +// kDN5=\E[B, +// kLFT5=\E[D, +// kRIT5=\E[C, +// kUP5=\E[A, +// kp1=\EOq, +// kp2=\EOr, +// kp3=\EOs, +// kp4=\EOt, +// kp5=\EOu, +// kp6=\EOv, +// kp7=\EOw, +// kp8=\EOx, +// kp9=\EOy, +// kpADD=\EOl, +// kpDIV=\EOQ, +// kpDOT=\EOn, +// kpMUL=\EOR, +// kpNUM=\EOP, +// kpSUB=\EOS, +// kpZRO=\EOp, +// rmxx=\E[29m, +// smxx=\E[9m, +// xm=\E[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;, static const int8_t putty_256colour_terminfo[] = { 30,2,48,0,29,0,16,0,125,1,-70,4,112,117,116,116,121,45,50,53,54,99,111,108,111,114,124,80,117,84,84,89,32,48,46,53,56,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,1,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,-1,-1,-1,-1,8,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,22,0,0,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,103,0,107,0,111,0,-1,-1,117,0,119,0,124,0,-127,0,-1,-1,-1,-1,-118,0,-1,-1,-1,-1,-113,0,-108,0,-103,0,-98,0,-89,0,-87,0,-82,0,-1,-1,-73,0,-68,0,-62,0,-56,0,-1,-1,-38,0,-1,-1,-36,0,-1,-1,-1,-1,-1,-1,-2,0,-1,-1,2,1,-1,-1,-1,-1,-1,-1,4,1,-1,-1,9,1,-1,-1,-1,-1,-1,-1,13,1,17,1,23,1,29,1,35,1,41,1,47,1,53,1,59,1,65,1,71,1,77,1,82,1,-1,-1,87,1,-1,-1,91,1,96,1,101,1,105,1,109,1,-1,-1,113,1,117,1,125,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-123,1,-1,-1,-120,1,-111,1,-102,1,-1,-1,-93,1,-84,1,-75,1,-66,1,-57,1,-48,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-39,1,-1,-1,-19,1,-1,-1,-1,-1,14,2,17,2,28,2,31,2,33,2,36,2,108,2,-1,-1,111,2,113,2,-1,-1,-1,-1,-1,-1,118,2,122,2,126,2,-126,2,-122,2,-1,-1,-1,-1,-118,2,-1,-1,-67,2,-1,-1,-1,-1,-63,2,-57,2,-1,-1,-1,-1,-51,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-44,2,-39,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-35,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-33,2,-27,2,-21,2,-15,2,-9,2,-3,2,3,3,9,3,15,3,21,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,27,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,32,3,43,3,48,3,54,3,58,3,67,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,71,3,-1,-1,-1,-1,-1,-1,75,3,-118,3,-1,-1,-1,-1,-1,-1,-54,3,-48,3,-42,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-36,3,-82,4,-76,4,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,68,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,7,0,27,55,27,91,114,27,91,109,27,91,63,55,104,27,91,63,49,59,52,59,54,108,27,91,52,108,27,56,27,62,27,93,82,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,121,0,27,91,49,49,126,0,27,91,50,49,126,0,27,91,49,50,126,0,27,91,49,51,126,0,27,91,49,52,126,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,66,0,27,91,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,13,10,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,60,27,91,34,112,27,91,53,48,59,54,34,112,27,99,27,91,63,51,108,27,93,82,27,91,63,49,48,48,48,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,54,37,124,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,48,59,0,27,79,113,0,27,79,115,0,27,79,114,0,27,79,112,0,27,79,110,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,91,52,126,0,27,79,77,0,26,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,54,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,82,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,49,48,109,0,27,91,49,49,109,0,27,91,49,50,109,0,37,63,37,112,49,37,123,56,125,37,61,37,116,27,37,37,71,-30,-105,-104,27,37,37,64,37,101,37,112,49,37,123,49,48,125,37,61,37,116,27,37,37,71,-30,-105,-103,27,37,37,64,37,101,37,112,49,37,123,49,50,125,37,61,37,116,27,37,37,71,-30,-103,-128,27,37,37,64,37,101,37,112,49,37,123,49,51,125,37,61,37,116,27,37,37,71,-30,-103,-86,27,37,37,64,37,101,37,112,49,37,123,49,52,125,37,61,37,116,27,37,37,71,-30,-103,-85,27,37,37,64,37,101,37,112,49,37,123,49,53,125,37,61,37,116,27,37,37,71,-30,-104,-68,27,37,37,64,37,101,37,112,49,37,123,50,55,125,37,61,37,116,27,37,37,71,-30,-122,-112,27,37,37,64,37,101,37,112,49,37,123,49,53,53,125,37,61,37,116,27,37,37,71,-32,-126,-94,27,37,37,64,37,101,37,112,49,37,99,37,59,0,27,91,49,49,109,0,27,91,49,48,109,0,1,0,1,0,30,0,62,0,86,1,1,0,1,0,0,0,0,0,9,0,18,0,23,0,30,0,37,0,42,0,74,0,78,0,82,0,86,0,90,0,94,0,98,0,102,0,106,0,110,0,114,0,118,0,122,0,126,0,-126,0,-122,0,-118,0,-114,0,-110,0,-106,0,-102,0,-96,0,-91,0,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,38,0,44,0,49,0,53,0,57,0,61,0,65,0,69,0,73,0,77,0,81,0,85,0,91,0,97,0,103,0,109,0,115,0,121,0,127,0,-124,0,-119,0,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,91,51,74,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,93,48,59,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,66,0,27,91,68,0,27,91,67,0,27,91,65,0,27,79,113,0,27,79,114,0,27,79,115,0,27,79,116,0,27,79,117,0,27,79,118,0,27,79,119,0,27,79,120,0,27,79,121,0,27,79,108,0,27,79,81,0,27,79,110,0,27,79,82,0,27,79,80,0,27,79,83,0,27,79,112,0,27,91,50,57,109,0,27,91,57,109,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,88,84,0,85,56,0,66,68,0,66,69,0,69,51,0,80,69,0,80,83,0,84,83,0,88,77,0,107,68,78,53,0,107,76,70,84,53,0,107,82,73,84,53,0,107,85,80,53,0,107,112,49,0,107,112,50,0,107,112,51,0,107,112,52,0,107,112,53,0,107,112,54,0,107,112,55,0,107,112,56,0,107,112,57,0,107,112,65,68,68,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,78,85,77,0,107,112,83,85,66,0,107,112,90,82,79,0,114,109,120,120,0,115,109,120,120,0,120,109,0 }; @@ -949,6 +1112,8 @@ static const int8_t putty_256colour_terminfo[] = { // move_insert_mode, // move_standout_mode, // xon_xoff, +// AX, +// XT, // columns#80, // init_tabs#8, // lines#24, @@ -1099,6 +1264,28 @@ static const int8_t putty_256colour_terminfo[] = { // user7=\E[6n, // user8=\E[?1;2c, // user9=\E[c, +// kDC5=\E[3\136, +// kDC6=\E[3@, +// kDN=\E[b, +// kDN5=\EOb, +// kEND5=\E[8\136, +// kEND6=\E[8@, +// kHOM5=\E[7\136, +// kHOM6=\E[7@, +// kIC5=\E[2\136, +// kIC6=\E[2@, +// kLFT5=\EOd, +// kNXT5=\E[6\136, +// kNXT6=\E[6@, +// kPRV5=\E[5\136, +// kPRV6=\E[5@, +// kRIT5=\EOc, +// kUP=\E[a, +// kUP5=\EOa, +// ka2=\EOx, +// kb1=\EOt, +// kb3=\EOv, +// kc2=\EOr, static const int8_t rxvt_256colour_terminfo[] = { 30,2,47,0,38,0,15,0,110,1,-35,4,114,120,118,116,45,50,53,54,99,111,108,111,114,124,114,120,118,116,32,50,46,55,46,57,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,0,1,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,-1,-1,0,0,2,0,4,0,21,0,26,0,34,0,38,0,42,0,-1,-1,53,0,70,0,72,0,76,0,83,0,-1,-1,85,0,92,0,-1,-1,96,0,-1,-1,-1,-1,100,0,-1,-1,-1,-1,104,0,106,0,111,0,116,0,-1,-1,-1,-1,125,0,-1,-1,-1,-1,-126,0,-121,0,-116,0,-1,-1,-111,0,-109,0,-104,0,-1,-1,-91,0,-86,0,-80,0,-74,0,-1,-1,-1,-1,-56,0,-42,0,-1,-1,-1,-1,-1,-1,-8,0,-1,-1,-4,0,-1,-1,-1,-1,-1,-1,-2,0,-1,-1,3,1,-1,-1,7,1,-1,-1,12,1,18,1,24,1,30,1,36,1,42,1,48,1,54,1,60,1,66,1,72,1,78,1,83,1,-1,-1,88,1,-1,-1,92,1,97,1,102,1,106,1,110,1,-1,-1,114,1,118,1,121,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,124,1,-123,1,-114,1,-1,-1,-105,1,-96,1,-87,1,-1,-1,-78,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-69,1,-36,1,-1,-1,-1,-1,14,2,17,2,28,2,31,2,33,2,36,2,103,2,-1,-1,106,2,-1,-1,-1,-1,-1,-1,-1,-1,108,2,112,2,116,2,120,2,124,2,-1,-1,-1,-1,-128,2,-1,-1,-77,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-73,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-66,2,-61,2,-1,-1,-57,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-52,2,-1,-1,-47,2,-42,2,-1,-1,-1,-1,-1,-1,-1,-1,-37,2,-32,2,-27,2,-1,-1,-1,-1,-23,2,-1,-1,-18,2,-1,-1,-1,-1,-1,-1,-13,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-9,2,-3,2,3,3,9,3,15,3,21,3,27,3,33,3,39,3,45,3,51,3,57,3,63,3,69,3,75,3,81,3,87,3,93,3,99,3,105,3,111,3,117,3,123,3,-127,3,-121,3,-115,3,-109,3,-103,3,-97,3,-91,3,-85,3,-79,3,-73,3,-67,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-61,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-56,3,-45,3,-40,3,-32,3,-28,3,-19,3,-12,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,82,4,-1,-1,-1,-1,-1,-1,86,4,-107,4,-1,-1,-1,-1,-1,-1,-43,4,-39,4,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,52,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,63,52,55,108,27,61,27,91,63,49,108,0,27,91,114,27,91,109,27,91,50,74,27,91,72,27,91,63,55,104,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,56,94,0,27,91,50,49,126,0,27,91,49,49,126,0,27,91,50,49,126,0,27,91,49,50,126,0,27,91,49,51,126,0,27,91,49,52,126,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,55,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,97,0,27,91,98,0,27,91,65,0,27,62,0,27,61,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,62,27,91,49,59,51,59,52,59,53,59,54,108,27,91,63,55,104,27,91,109,27,91,114,27,91,50,74,27,91,72,0,27,91,114,27,91,109,27,91,50,74,27,91,72,27,91,63,55,104,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,79,117,0,27,79,113,0,27,79,115,0,96,96,97,97,102,102,103,103,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,56,126,0,27,79,77,0,27,91,49,126,0,27,91,51,36,0,27,91,52,126,0,27,91,56,36,0,27,91,55,36,0,27,91,50,36,0,27,91,100,0,27,91,54,36,0,27,91,53,36,0,27,91,99,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,40,66,0,27,40,48,0,0,2,0,0,0,22,0,46,0,-36,0,1,1,0,0,5,0,10,0,14,0,18,0,23,0,28,0,33,0,38,0,43,0,48,0,52,0,57,0,62,0,67,0,72,0,76,0,80,0,84,0,88,0,92,0,96,0,0,0,3,0,6,0,11,0,16,0,20,0,25,0,31,0,37,0,43,0,49,0,54,0,59,0,65,0,71,0,77,0,83,0,89,0,95,0,99,0,104,0,108,0,112,0,116,0,27,91,51,94,0,27,91,51,64,0,27,91,98,0,27,79,98,0,27,91,56,94,0,27,91,56,64,0,27,91,55,94,0,27,91,55,64,0,27,91,50,94,0,27,91,50,64,0,27,79,100,0,27,91,54,94,0,27,91,54,64,0,27,91,53,94,0,27,91,53,64,0,27,79,99,0,27,91,97,0,27,79,97,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,65,88,0,88,84,0,107,68,67,53,0,107,68,67,54,0,107,68,78,0,107,68,78,53,0,107,69,78,68,53,0,107,69,78,68,54,0,107,72,79,77,53,0,107,72,79,77,54,0,107,73,67,53,0,107,73,67,54,0,107,76,70,84,53,0,107,78,88,84,53,0,107,78,88,84,54,0,107,80,82,86,53,0,107,80,82,86,54,0,107,82,73,84,53,0,107,85,80,0,107,85,80,53,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0 }; @@ -1111,11 +1298,14 @@ static const int8_t rxvt_256colour_terminfo[] = { // has_meta_key, // move_insert_mode, // move_standout_mode, +// AX, +// G0, // columns#80, // init_tabs#8, // lines#24, // max_colors#0x100, // max_pairs#0x10000, +// U8#1, // acs_chars=++\054\054--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, // back_tab=\E[Z, // bell=^G, @@ -1211,6 +1401,8 @@ static const int8_t rxvt_256colour_terminfo[] = { // user7=\E[6n, // user8=\E[?1;2c, // user9=\E[c, +// E0=\E(B, +// S0=\E(%p1%c, static const int8_t screen_256colour_terminfo[] = { 30,2,43,0,43,0,15,0,105,1,41,3,115,99,114,101,101,110,45,50,53,54,99,111,108,111,114,124,71,78,85,32,83,99,114,101,101,110,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,100,0,-1,-1,104,0,107,0,113,0,117,0,-1,-1,-1,-1,121,0,123,0,-128,0,-123,0,-1,-1,-114,0,-109,0,-1,-1,-1,-1,-104,0,-99,0,-94,0,-1,-1,-89,0,-87,0,-82,0,-1,-1,-73,0,-68,0,-62,0,-56,0,-1,-1,-1,-1,-1,-1,-53,0,-1,-1,-1,-1,-1,-1,-49,0,-1,-1,-45,0,-1,-1,-1,-1,-1,-1,-43,0,-1,-1,-38,0,-1,-1,-1,-1,-1,-1,-1,-1,-34,0,-30,0,-24,0,-20,0,-16,0,-12,0,-6,0,0,1,6,1,12,1,18,1,23,1,-1,-1,28,1,-1,-1,32,1,37,1,42,1,-1,-1,-1,-1,-1,-1,46,1,50,1,58,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,66,1,-1,-1,69,1,78,1,87,1,96,1,105,1,114,1,123,1,-124,1,-115,1,-106,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-97,1,-1,-1,-1,-1,-80,1,-77,1,-66,1,-63,1,-61,1,-58,1,26,2,-1,-1,29,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,31,2,-1,-1,96,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,100,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,107,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,112,2,118,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,124,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-127,2,-116,2,-111,2,-103,2,-99,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-90,2,-1,-1,-1,-1,-1,-1,-86,2,-23,2,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,51,52,104,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,51,52,108,0,27,91,80,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,55,109,0,27,91,51,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,51,109,0,27,91,50,52,109,0,27,103,0,27,41,48,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,99,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,49,37,116,59,51,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,52,126,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,0,2,0,1,0,2,0,7,0,27,0,1,1,1,0,0,0,0,0,4,0,0,0,3,0,6,0,9,0,12,0,27,40,66,0,27,40,37,112,49,37,99,0,65,88,0,71,48,0,85,56,0,69,48,0,83,48,0 }; @@ -1224,6 +1416,7 @@ static const int8_t screen_256colour_terminfo[] = { // move_insert_mode, // move_standout_mode, // no_pad_char, +// XT, // columns#80, // init_tabs#8, // lines#24, @@ -1406,6 +1599,28 @@ static const int8_t screen_256colour_terminfo[] = { // user7=\E[6n, // user8=\E[?1;2c, // user9=\E[c, +// BD=\E[?2004l, +// BE=\E[?2004h, +// Ms=\E]52;%p1%s;%p2%s^G, +// PE=\E[201~, +// PS=\E[200~, +// Se=\E[2 q, +// Ss=\E[%p1%d q, +// TS=\E]0;, +// kDN3=\E[1;3B, +// kDN5=\E[1;5B, +// kLFT3=\E[1;3D, +// kLFT5=\E[1;5D, +// kNXT3=\E[6;3~, +// kNXT5=\E[6;5~, +// kPRV3=\E[5;3~, +// kPRV5=\E[5;5~, +// kRIT3=\E[1;3C, +// kRIT5=\E[1;5C, +// kUP3=\E[1;3A, +// kUP5=\E[1;5A, +// rmxx=\E[29m, +// smxx=\E[9m, static const int8_t st_256colour_terminfo[] = { 30,2,55,0,29,0,15,0,105,1,-28,5,115,116,45,50,53,54,99,111,108,111,114,124,115,116,116,101,114,109,45,50,53,54,99,111,108,111,114,124,115,105,109,112,108,101,116,101,114,109,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,104,0,108,0,112,0,-1,-1,118,0,122,0,127,0,-124,0,-1,-1,-115,0,-110,0,-105,0,-1,-1,-100,0,-95,0,-90,0,-85,0,-76,0,-72,0,-67,0,-1,-1,-58,0,-53,0,-47,0,-41,0,-1,-1,-23,0,-1,-1,-21,0,-1,-1,-1,-1,-1,-1,-6,0,-1,-1,-2,0,-1,-1,0,1,-1,-1,7,1,12,1,19,1,23,1,30,1,37,1,-1,-1,44,1,48,1,54,1,58,1,62,1,66,1,72,1,78,1,84,1,90,1,96,1,101,1,106,1,113,1,-1,-1,117,1,122,1,127,1,-125,1,-118,1,-1,-1,-111,1,-107,1,-99,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-91,1,-82,1,-73,1,-64,1,-55,1,-46,1,-37,1,-28,1,-19,1,-10,1,-1,-1,-1,-1,-1,-1,-1,1,3,2,8,2,-1,-1,13,2,16,2,-1,-1,-1,-1,31,2,34,2,45,2,48,2,50,2,53,2,-110,2,-1,-1,-107,2,-105,2,-1,-1,-1,-1,-1,-1,-100,2,-95,2,-90,2,-86,2,-81,2,-1,-1,-1,-1,-76,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-11,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-7,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,2,-1,-1,-1,-1,5,3,-1,-1,-1,-1,-1,-1,-1,-1,12,3,19,3,26,3,-1,-1,-1,-1,33,3,-1,-1,40,3,-1,-1,-1,-1,-1,-1,47,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,54,3,60,3,66,3,73,3,80,3,87,3,94,3,102,3,110,3,118,3,126,3,-122,3,-114,3,-106,3,-98,3,-91,3,-84,3,-77,3,-70,3,-62,3,-54,3,-46,3,-38,3,-30,3,-22,3,-14,3,-6,3,1,4,8,4,15,4,22,4,30,4,38,4,46,4,54,4,62,4,70,4,78,4,86,4,93,4,100,4,107,4,114,4,122,4,-126,4,-118,4,-110,4,-102,4,-94,4,-86,4,-78,4,-71,4,-64,4,-57,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-52,4,-41,4,-36,4,-28,4,-24,4,-15,4,-8,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,86,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,91,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,97,5,-1,-1,-1,-1,-1,-1,101,5,-92,5,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,27,40,48,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,7,0,27,91,52,108,27,62,27,91,63,49,48,51,52,108,0,27,91,76,0,127,0,27,91,51,59,53,126,0,27,91,51,126,0,27,91,51,59,50,126,0,27,79,66,0,27,91,50,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,53,70,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,50,59,53,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,105,0,27,91,52,105,0,27,91,53,105,0,27,99,0,27,91,52,108,27,62,27,91,63,49,48,51,52,108,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,37,63,37,112,57,37,116,27,40,48,37,101,27,40,66,37,59,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,109,0,27,72,0,9,0,27,93,48,59,0,27,91,49,126,0,27,91,53,126,0,27,79,117,0,27,91,52,126,0,27,91,54,126,0,43,67,44,68,45,65,46,66,48,69,96,96,97,97,102,102,103,103,104,70,105,71,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,41,48,0,27,91,52,126,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,1,0,0,0,22,0,45,0,15,1,1,0,0,0,9,0,18,0,36,0,43,0,50,0,56,0,66,0,71,0,78,0,85,0,92,0,99,0,106,0,113,0,120,0,127,0,-122,0,-115,0,-108,0,-101,0,-95,0,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,37,0,43,0,49,0,55,0,61,0,67,0,73,0,79,0,85,0,90,0,95,0,100,0,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,49,59,51,66,0,27,91,49,59,53,66,0,27,91,49,59,51,68,0,27,91,49,59,53,68,0,27,91,54,59,51,126,0,27,91,54,59,53,126,0,27,91,53,59,51,126,0,27,91,53,59,53,126,0,27,91,49,59,51,67,0,27,91,49,59,53,67,0,27,91,49,59,51,65,0,27,91,49,59,53,65,0,27,91,50,57,109,0,27,91,57,109,0,88,84,0,66,68,0,66,69,0,77,115,0,80,69,0,80,83,0,83,101,0,83,115,0,84,83,0,107,68,78,51,0,107,68,78,53,0,107,76,70,84,51,0,107,76,70,84,53,0,107,78,88,84,51,0,107,78,88,84,53,0,107,80,82,86,51,0,107,80,82,86,53,0,107,82,73,84,51,0,107,82,73,84,53,0,107,85,80,51,0,107,85,80,53,0,114,109,120,120,0,115,109,120,120,0 }; @@ -1419,11 +1634,15 @@ static const int8_t st_256colour_terminfo[] = { // has_status_line, // move_insert_mode, // move_standout_mode, +// AX, +// G0, +// XF, // columns#80, // init_tabs#8, // lines#24, // max_colors#0x100, // max_pairs#0x10000, +// U8#1, // acs_chars=++\054\054--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, // back_tab=\E[Z, // bell=^G, @@ -1586,6 +1805,82 @@ static const int8_t st_256colour_terminfo[] = { // user7=\E[6n, // user8=\E[?1;2c, // user9=\E[c, +// BD=\E[?2004l, +// BE=\E[?2004h, +// Cr=\E]112^G, +// Cs=\E]12;%p1%s^G, +// E0=\E(B, +// E3=\E[3J, +// Ms=\E]52;%p1%s;%p2%s^G, +// PE=\E[201~, +// PS=\E[200~, +// RV=\E[>c, +// S0=\E(%p1%c, +// Se=\E[2 q, +// Smulx=\E[4\072%p1%dm, +// Ss=\E[%p1%d q, +// TS=\E]0;, +// XR=\E[>0q, +// fd=\E[?1004l, +// fe=\E[?1004h, +// kDC3=\E[3;3~, +// kDC4=\E[3;4~, +// kDC5=\E[3;5~, +// kDC6=\E[3;6~, +// kDC7=\E[3;7~, +// kDN=\E[1;2B, +// kDN3=\E[1;3B, +// kDN4=\E[1;4B, +// kDN5=\E[1;5B, +// kDN6=\E[1;6B, +// kDN7=\E[1;7B, +// kEND3=\E[1;3F, +// kEND4=\E[1;4F, +// kEND5=\E[1;5F, +// kEND6=\E[1;6F, +// kEND7=\E[1;7F, +// kHOM3=\E[1;3H, +// kHOM4=\E[1;4H, +// kHOM5=\E[1;5H, +// kHOM6=\E[1;6H, +// kHOM7=\E[1;7H, +// kIC3=\E[2;3~, +// kIC4=\E[2;4~, +// kIC5=\E[2;5~, +// kIC6=\E[2;6~, +// kIC7=\E[2;7~, +// kLFT3=\E[1;3D, +// kLFT4=\E[1;4D, +// kLFT5=\E[1;5D, +// kLFT6=\E[1;6D, +// kLFT7=\E[1;7D, +// kNXT3=\E[6;3~, +// kNXT4=\E[6;4~, +// kNXT5=\E[6;5~, +// kNXT6=\E[6;6~, +// kNXT7=\E[6;7~, +// kPRV3=\E[5;3~, +// kPRV4=\E[5;4~, +// kPRV5=\E[5;5~, +// kPRV6=\E[5;6~, +// kPRV7=\E[5;7~, +// kRIT3=\E[1;3C, +// kRIT4=\E[1;4C, +// kRIT5=\E[1;5C, +// kRIT6=\E[1;6C, +// kRIT7=\E[1;7C, +// kUP=\E[1;2A, +// kUP3=\E[1;3A, +// kUP4=\E[1;4A, +// kUP5=\E[1;5A, +// kUP6=\E[1;6A, +// kUP7=\E[1;7A, +// kxIN=\E[I, +// kxOUT=\E[O, +// rmxx=\E[29m, +// rv=\E\[[0-9]+;[0-9]+;[0-9]+c, +// smxx=\E[9m, +// xr=\EP>\|[ -~]+\E\\, static const int8_t tmux_256colour_terminfo[] = { 30,2,35,0,43,0,15,0,105,1,22,5,116,109,117,120,45,50,53,54,99,111,108,111,114,124,116,109,117,120,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,37,0,41,0,45,0,-1,-1,56,0,73,0,75,0,79,0,86,0,-1,-1,88,0,100,0,-1,-1,104,0,107,0,113,0,117,0,121,0,-1,-1,127,0,-127,0,-122,0,-117,0,-1,-1,-108,0,-103,0,-98,0,-1,-1,-93,0,-88,0,-83,0,-1,-1,-78,0,-76,0,-71,0,-1,-1,-62,0,-57,0,-51,0,-45,0,-1,-1,-42,0,-1,-1,-40,0,-1,-1,-1,-1,-1,-1,-36,0,-1,-1,-32,0,-1,-1,-1,-1,-1,-1,-30,0,-1,-1,-25,0,-1,-1,-1,-1,-1,-1,-1,-1,-21,0,-17,0,-11,0,-7,0,-3,0,1,1,7,1,13,1,19,1,25,1,31,1,36,1,-1,-1,41,1,-1,-1,45,1,50,1,55,1,59,1,66,1,-1,-1,73,1,77,1,85,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,93,1,-1,-1,96,1,105,1,114,1,123,1,-124,1,-115,1,-106,1,-97,1,-88,1,-79,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-70,1,-1,-1,-1,-1,-53,1,-50,1,-39,1,-36,1,-34,1,-31,1,58,2,-1,-1,61,2,63,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,68,2,-1,-1,-123,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-119,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-112,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-107,2,-1,-1,-1,-1,-100,2,-1,-1,-1,-1,-1,-1,-1,-1,-93,2,-86,2,-79,2,-1,-1,-1,-1,-72,2,-1,-1,-65,2,-1,-1,-1,-1,-1,-1,-58,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-51,2,-45,2,-39,2,-32,2,-25,2,-18,2,-11,2,-3,2,5,3,13,3,21,3,29,3,37,3,45,3,53,3,60,3,67,3,74,3,81,3,89,3,97,3,105,3,113,3,121,3,-127,3,-119,3,-111,3,-104,3,-97,3,-90,3,-83,3,-75,3,-67,3,-59,3,-51,3,-43,3,-35,3,-27,3,-19,3,-12,3,-5,3,2,4,9,4,17,4,25,4,33,4,41,4,49,4,57,4,65,4,73,4,80,4,87,4,94,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,99,4,110,4,115,4,123,4,127,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-120,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-115,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-109,4,-1,-1,-1,-1,-1,-1,-105,4,-42,4,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,51,52,104,27,91,63,50,53,104,0,27,91,67,0,27,77,0,27,91,51,52,108,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,15,0,27,91,109,15,0,27,91,63,49,48,52,57,108,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,103,0,7,0,27,41,48,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,99,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,93,48,59,0,43,43,44,44,45,45,46,46,48,48,96,96,97,97,102,102,103,103,104,104,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,40,66,27,41,48,0,27,91,52,126,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,49,59,50,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,91,51,109,0,27,91,50,51,109,0,27,91,77,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,3,0,1,0,76,0,-100,0,-70,3,1,1,1,0,1,0,0,0,0,0,9,0,18,0,25,0,37,0,41,0,46,0,64,0,71,0,78,0,83,0,91,0,97,0,108,0,118,0,123,0,-127,0,-118,0,-109,0,-102,0,-95,0,-88,0,-81,0,-74,0,-67,0,-60,0,-53,0,-46,0,-39,0,-32,0,-25,0,-18,0,-11,0,-4,0,3,1,10,1,17,1,24,1,31,1,38,1,45,1,52,1,59,1,66,1,73,1,80,1,87,1,94,1,101,1,108,1,115,1,122,1,-127,1,-120,1,-113,1,-106,1,-99,1,-92,1,-85,1,-78,1,-71,1,-64,1,-57,1,-50,1,-43,1,-36,1,-29,1,-22,1,-15,1,-8,1,-1,1,3,2,7,2,13,2,38,2,43,2,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,45,0,48,0,54,0,57,0,60,0,63,0,66,0,69,0,74,0,79,0,84,0,89,0,94,0,98,0,103,0,108,0,113,0,118,0,123,0,-127,0,-121,0,-115,0,-109,0,-103,0,-97,0,-91,0,-85,0,-79,0,-73,0,-68,0,-63,0,-58,0,-53,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,36,1,42,1,48,1,54,1,60,1,66,1,72,1,76,1,81,1,86,1,91,1,96,1,101,1,106,1,112,1,117,1,120,1,125,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,40,66,0,27,91,51,74,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,62,99,0,27,40,37,112,49,37,99,0,27,91,50,32,113,0,27,91,52,58,37,112,49,37,100,109,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,62,48,113,0,27,91,63,49,48,48,52,108,0,27,91,63,49,48,48,52,104,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,73,0,27,91,79,0,27,91,50,57,109,0,27,92,91,91,48,45,57,93,43,59,91,48,45,57,93,43,59,91,48,45,57,93,43,99,0,27,91,57,109,0,27,80,62,92,124,91,32,45,126,93,43,27,92,92,0,65,88,0,71,48,0,88,70,0,85,56,0,66,68,0,66,69,0,67,114,0,67,115,0,69,48,0,69,51,0,77,115,0,80,69,0,80,83,0,82,86,0,83,48,0,83,101,0,83,109,117,108,120,0,83,115,0,84,83,0,88,82,0,102,100,0,102,101,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,120,73,78,0,107,120,79,85,84,0,114,109,120,120,0,114,118,0,115,109,120,120,0,120,114,0 }; @@ -1598,6 +1893,8 @@ static const int8_t tmux_256colour_terminfo[] = { // eat_newline_glitch, // move_insert_mode, // move_standout_mode, +// AX, +// XT, // columns#80, // init_tabs#8, // lines#24, @@ -1774,11 +2071,80 @@ static const int8_t tmux_256colour_terminfo[] = { // user7=\E[6n, // user8=\E[?%[;0123456789]c, // user9=\E[c, +// BD=\E[?2004l, +// BE=\E[?2004h, +// Cr=\E]112^G, +// Cs=\E]12;%p1%s^G, +// Ms=\E]52;%p1%s;%p2%s^G, +// PE=\E[201~, +// PS=\E[200~, +// Rmol=\E[55m, +// Se=\E[1 q, +// Smol=\E[53m, +// Smulx=\E[4\072%p1%dm, +// Ss=\E[%p1%d q, +// XM=\E[?1006;1000%?%p1%{1}%=%th%el%;, +// kDC3=\E[3;3~, +// kDC4=\E[3;4~, +// kDC5=\E[3;5~, +// kDC6=\E[3;6~, +// kDC7=\E[3;7~, +// kDN=\E[1;2B, +// kDN3=\E[1;3B, +// kDN4=\E[1;4B, +// kDN5=\E[1;5B, +// kDN6=\E[1;6B, +// kDN7=\E[1;7B, +// kEND3=\E[1;3F, +// kEND4=\E[1;4F, +// kEND5=\E[1;5F, +// kEND6=\E[1;6F, +// kEND7=\E[1;7F, +// kHOM3=\E[1;3H, +// kHOM4=\E[1;4H, +// kHOM5=\E[1;5H, +// kHOM6=\E[1;6H, +// kHOM7=\E[1;7H, +// kIC3=\E[2;3~, +// kIC4=\E[2;4~, +// kIC5=\E[2;5~, +// kIC6=\E[2;6~, +// kIC7=\E[2;7~, +// kLFT3=\E[1;3D, +// kLFT4=\E[1;4D, +// kLFT5=\E[1;5D, +// kLFT6=\E[1;6D, +// kLFT7=\E[1;7D, +// kNXT3=\E[6;3~, +// kNXT4=\E[6;4~, +// kNXT5=\E[6;5~, +// kNXT6=\E[6;6~, +// kNXT7=\E[6;7~, +// kPRV3=\E[5;3~, +// kPRV4=\E[5;4~, +// kPRV5=\E[5;5~, +// kPRV6=\E[5;6~, +// kPRV7=\E[5;7~, +// kRIT3=\E[1;3C, +// kRIT4=\E[1;4C, +// kRIT5=\E[1;5C, +// kRIT6=\E[1;6C, +// kRIT7=\E[1;7C, +// kUP=\E[1;2A, +// kUP3=\E[1;3A, +// kUP4=\E[1;4A, +// kUP5=\E[1;5A, +// kUP6=\E[1;6A, +// kUP7=\E[1;7A, +// rmxx=\E[29m, +// setal=\E[58\0722\072\072%p1%{65536}%/%d\072%p1%{256}%/%{255}%&%d\072%p1%{255}%&%dm, +// smxx=\E[9m, +// xm=\E[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;, static const int8_t vte_256colour_terminfo[] = { 30,2,39,0,38,0,15,0,-99,1,7,6,118,116,101,45,50,53,54,99,111,108,111,114,124,86,84,69,32,119,105,116,104,32,120,116,101,114,109,32,50,53,54,45,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,96,0,-1,-1,100,0,-1,-1,104,0,108,0,-1,-1,-1,-1,112,0,114,0,119,0,124,0,-1,-1,-114,0,-109,0,-104,0,-1,-1,-99,0,-94,0,-89,0,-84,0,-75,0,-73,0,-67,0,-1,-1,-49,0,-44,0,-38,0,-32,0,-1,-1,-1,-1,-1,-1,-14,0,-1,-1,-1,-1,-1,-1,19,1,-1,-1,23,1,-1,-1,-1,-1,-1,-1,25,1,-1,-1,30,1,-1,-1,-1,-1,-1,-1,-1,-1,34,1,38,1,44,1,48,1,52,1,56,1,62,1,68,1,74,1,80,1,86,1,90,1,-1,-1,95,1,-1,-1,99,1,104,1,109,1,113,1,120,1,-1,-1,127,1,-125,1,-117,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-109,1,-1,-1,-106,1,-97,1,-88,1,-79,1,-70,1,-61,1,-52,1,-43,1,-34,1,-25,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-16,1,4,2,7,2,-1,-1,-1,-1,58,2,61,2,72,2,75,2,77,2,80,2,-87,2,-1,-1,-84,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-82,2,-1,-1,-1,-1,-1,-1,-1,-1,-78,2,-1,-1,-25,2,-1,-1,-1,-1,-21,2,-15,2,-1,-1,-1,-1,-9,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,2,2,3,-1,-1,6,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,11,3,-1,-1,18,3,23,3,-1,-1,-1,-1,-1,-1,-1,-1,30,3,37,3,44,3,-1,-1,-1,-1,51,3,-1,-1,58,3,-1,-1,-1,-1,-1,-1,65,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,72,3,78,3,84,3,91,3,98,3,105,3,112,3,120,3,-128,3,-120,3,-112,3,-104,3,-96,3,-88,3,-80,3,-73,3,-66,3,-59,3,-52,3,-44,3,-36,3,-28,3,-20,3,-12,3,-4,3,4,4,12,4,19,4,26,4,33,4,40,4,48,4,56,4,64,4,72,4,80,4,88,4,96,4,104,4,111,4,118,4,125,4,-124,4,-116,4,-108,4,-100,4,-92,4,-84,4,-76,4,-68,4,-60,4,-53,4,-46,4,-39,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-34,4,-23,4,-18,4,1,5,5,5,14,5,21,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,115,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,120,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,126,5,-1,-1,-1,-1,-1,-1,-126,5,-63,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,6,4,6,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,14,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,15,0,27,91,48,109,15,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,109,27,91,63,55,104,27,91,52,108,27,62,27,55,27,91,114,27,91,63,49,59,51,59,52,59,54,108,27,56,0,27,91,76,0,127,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,99,0,27,55,27,91,114,27,56,27,91,109,27,91,63,55,104,27,91,33,112,27,91,63,49,59,51,59,52,59,54,108,27,91,52,108,27,62,27,91,63,49,48,48,48,108,27,91,63,50,53,104,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,55,37,116,59,56,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,109,37,63,37,112,57,37,116,14,37,101,15,37,59,0,27,72,0,9,0,27,91,69,0,96,96,97,97,102,102,103,103,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,40,66,27,41,48,0,27,79,70,0,27,79,77,0,27,91,49,126,0,27,91,51,59,50,126,0,27,91,52,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,108,0,27,109,0,0,2,0,0,0,69,0,-116,0,-52,3,1,1,0,0,9,0,18,0,25,0,37,0,55,0,62,0,69,0,75,0,81,0,87,0,98,0,108,0,-116,0,-109,0,-102,0,-95,0,-88,0,-81,0,-74,0,-67,0,-60,0,-53,0,-46,0,-39,0,-32,0,-25,0,-18,0,-11,0,-4,0,3,1,10,1,17,1,24,1,31,1,38,1,45,1,52,1,59,1,66,1,73,1,80,1,87,1,94,1,101,1,108,1,115,1,122,1,-127,1,-120,1,-113,1,-106,1,-99,1,-92,1,-85,1,-78,1,-71,1,-64,1,-57,1,-50,1,-43,1,-36,1,-29,1,-22,1,-15,1,-8,1,-2,1,59,2,64,2,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,32,0,35,0,40,0,46,0,49,0,52,0,57,0,62,0,67,0,72,0,77,0,81,0,86,0,91,0,96,0,101,0,106,0,112,0,118,0,124,0,-126,0,-120,0,-114,0,-108,0,-102,0,-96,0,-90,0,-85,0,-80,0,-75,0,-70,0,-65,0,-59,0,-53,0,-47,0,-41,0,-35,0,-29,0,-23,0,-17,0,-11,0,-5,0,1,1,7,1,13,1,19,1,25,1,31,1,37,1,43,1,49,1,55,1,59,1,64,1,69,1,74,1,79,1,84,1,89,1,95,1,100,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,53,53,109,0,27,91,49,32,113,0,27,91,53,51,109,0,27,91,52,58,37,112,49,37,100,109,0,27,91,37,112,49,37,100,32,113,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,91,50,57,109,0,27,91,53,56,58,50,58,58,37,112,49,37,123,54,53,53,51,54,125,37,47,37,100,58,37,112,49,37,123,50,53,54,125,37,47,37,123,50,53,53,125,37,38,37,100,58,37,112,49,37,123,50,53,53,125,37,38,37,100,109,0,27,91,57,109,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,65,88,0,88,84,0,66,68,0,66,69,0,67,114,0,67,115,0,77,115,0,80,69,0,80,83,0,82,109,111,108,0,83,101,0,83,109,111,108,0,83,109,117,108,120,0,83,115,0,88,77,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,114,109,120,120,0,115,101,116,97,108,0,115,109,120,120,0,120,109,0 }; -// vtpcon|ANIS emulation for console virtual terminal sequence with libuv, +// vtpcon|ANSI emulation for console virtual terminal sequence with libuv, // auto_right_margin, // back_color_erase, // backspaces_with_bs, @@ -1787,6 +2153,9 @@ static const int8_t vte_256colour_terminfo[] = { // move_insert_mode, // move_standout_mode, // no_pad_char, +// AX, +// XF, +// XT, // columns#80, // init_tabs#8, // lines#24, @@ -1978,8 +2347,95 @@ static const int8_t vte_256colour_terminfo[] = { // user7@, // user8@, // user9@, +// BD=\E[?2004l, +// BE=\E[?2004h, +// Cr@, +// Cs@, +// E3=\E[3J, +// Ms@, +// PE=\E[201~, +// PS=\E[200~, +// RV=\E[>c, +// Se=\E[2 q, +// Ss=\E[%p1%d q, +// TS=\E]0;, +// XM@, +// XR=\E[>0q, +// fd=\E[?1004l, +// fe=\E[?1004h, +// kDC3@, +// kDC4@, +// kDC5@, +// kDC6@, +// kDC7@, +// kDN@, +// kDN3@, +// kDN4@, +// kDN5@, +// kDN6@, +// kDN7@, +// kEND3@, +// kEND4@, +// kEND5@, +// kEND6@, +// kEND7@, +// kHOM3@, +// kHOM4@, +// kHOM5@, +// kHOM6@, +// kHOM7@, +// kIC3@, +// kIC4@, +// kIC5@, +// kIC6@, +// kIC7@, +// kLFT3@, +// kLFT4@, +// kLFT5@, +// kLFT6@, +// kLFT7@, +// kNXT3@, +// kNXT4@, +// kNXT5@, +// kNXT6@, +// kNXT7@, +// kPRV3@, +// kPRV4@, +// kPRV5@, +// kPRV6@, +// kPRV7@, +// kRIT3@, +// kRIT4@, +// kRIT5@, +// kRIT6@, +// kRIT7@, +// kUP=\E[1;2A, +// kUP3@, +// kUP4@, +// kUP5@, +// kUP6@, +// kUP7@, +// ka2=\EOx, +// kb1=\EOt, +// kb3=\EOv, +// kc2=\EOr, +// kp5=\EOE, +// kpADD=\EOk, +// kpCMA=\EOl, +// kpDIV=\EOo, +// kpDOT=\EOn, +// kpMUL=\EOj, +// kpSUB=\EOm, +// kpZRO=\EOp, +// kxIN=\E[I, +// kxOUT=\E[O, +// rmxx@, +// rv=\E\[41;[1-6][0-9][0-9];0c, +// smxx@, +// xm@, +// xr=\EP>\|XTerm\([1-9][0-9]+\)\E\\, static const int8_t vtpcon_terminfo[] = { - 30,2,71,0,38,0,15,0,-99,1,64,4,118,116,112,99,111,110,124,65,78,73,83,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,99,111,110,115,111,108,101,32,118,105,114,116,117,97,108,32,116,101,114,109,105,110,97,108,32,115,101,113,117,101,110,99,101,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,-2,-1,25,0,33,0,37,0,41,0,-1,-1,52,0,69,0,73,0,77,0,84,0,-1,-1,86,0,99,0,-1,-1,103,0,-2,-1,107,0,111,0,115,0,-1,-1,121,0,-2,-1,125,0,-126,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,-117,0,-112,0,-107,0,-102,0,-93,0,-89,0,-84,0,-1,-1,-2,-1,-75,0,-69,0,-2,-1,-1,-1,-63,0,-1,-1,-61,0,-1,-1,-1,-1,-1,-1,-51,0,-1,-1,-47,0,-1,-1,-1,-1,-1,-1,-45,0,-1,-1,-40,0,-1,-1,-1,-1,-1,-1,-1,-1,-36,0,-31,0,-25,0,-20,0,-15,0,-10,0,-5,0,1,1,7,1,13,1,19,1,24,1,-1,-1,29,1,-1,-1,33,1,38,1,43,1,47,1,54,1,-1,-1,61,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,65,1,-1,-1,68,1,77,1,86,1,95,1,104,1,113,1,122,1,-125,1,-116,1,-107,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-98,1,-2,-1,-2,-1,-1,-1,-1,-1,-78,1,-75,1,-64,1,-61,1,-59,1,-56,1,-13,1,-1,-1,-10,1,-8,1,-1,-1,-1,-1,-1,-1,-3,1,1,2,5,2,9,2,13,2,-1,-1,-1,-1,17,2,-1,-1,40,2,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,44,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,48,2,53,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,57,2,-1,-1,-1,-1,64,2,-1,-1,-1,-1,-1,-1,-1,-1,71,2,78,2,85,2,-1,-1,-1,-1,92,2,-1,-1,99,2,-1,-1,-1,-1,-1,-1,106,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,113,2,119,2,125,2,-125,2,-119,2,-113,2,-107,2,-101,2,-95,2,-89,2,-83,2,-77,2,-71,2,-65,2,-59,2,-53,2,-47,2,-41,2,-35,2,-29,2,-23,2,-17,2,-11,2,-5,2,1,3,7,3,13,3,19,3,25,3,31,3,38,3,44,3,50,3,56,3,62,3,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,68,3,73,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,80,3,-2,-1,89,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-74,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-69,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-63,3,0,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,27,40,48,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,7,0,27,91,33,112,27,91,63,51,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,27,72,0,9,0,27,93,48,59,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,106,106,107,107,108,108,109,109,110,110,113,113,116,116,117,117,118,118,119,119,120,120,0,27,91,90,0,27,79,69,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,0,27,91,51,109,0,27,91,50,51,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,3,0,0,0,87,0,119,0,-127,2,1,1,1,0,0,0,9,0,-2,-1,-2,-1,18,0,-2,-1,23,0,30,0,37,0,42,0,48,0,58,0,-2,-1,63,0,69,0,78,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,87,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,94,0,98,0,102,0,106,0,110,0,114,0,118,0,122,0,126,0,-126,0,-122,0,-118,0,-114,0,-110,0,-2,-1,-106,0,-2,-1,-2,-1,-81,0,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,45,0,48,0,51,0,54,0,57,0,62,0,67,0,72,0,77,0,82,0,86,0,91,0,96,0,101,0,106,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-109,0,-103,0,-97,0,-91,0,-85,0,-80,0,-75,0,-70,0,-65,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,36,1,42,1,48,1,54,1,60,1,64,1,69,1,74,1,79,1,84,1,89,1,93,1,97,1,101,1,105,1,109,1,115,1,121,1,127,1,-123,1,-117,1,-111,1,-105,1,-100,1,-94,1,-89,1,-86,1,-81,1,-78,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,91,51,74,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,62,99,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,62,48,113,0,27,91,63,49,48,48,52,108,0,27,91,63,49,48,48,52,104,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,27,91,73,0,27,91,79,0,27,92,91,52,49,59,91,49,45,54,93,91,48,45,57,93,91,48,45,57,93,59,48,99,0,27,80,62,92,124,88,84,101,114,109,92,40,91,49,45,57,93,91,48,45,57,93,43,92,41,27,92,92,0,65,88,0,88,70,0,88,84,0,66,68,0,66,69,0,67,114,0,67,115,0,69,51,0,77,115,0,80,69,0,80,83,0,82,86,0,83,101,0,83,115,0,84,83,0,88,77,0,88,82,0,102,100,0,102,101,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,107,120,73,78,0,107,120,79,85,84,0,114,109,120,120,0,114,118,0,115,109,120,120,0,120,109,0,120,114,0 + 30,2,71,0,38,0,15,0,-99,1,64,4,118,116,112,99,111,110,124,65,78,83,73,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,99,111,110,115,111,108,101,32,118,105,114,116,117,97,108,32,116,101,114,109,105,110,97,108,32,115,101,113,117,101,110,99,101,32,119,105,116,104,32,108,105,98,117,118,0,0,1,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,-2,-1,25,0,33,0,37,0,41,0,-1,-1,52,0,69,0,73,0,77,0,84,0,-1,-1,86,0,99,0,-1,-1,103,0,-2,-1,107,0,111,0,115,0,-1,-1,121,0,-2,-1,125,0,-126,0,-1,-1,-2,-1,-2,-1,-2,-1,-1,-1,-117,0,-112,0,-107,0,-102,0,-93,0,-89,0,-84,0,-1,-1,-2,-1,-75,0,-69,0,-2,-1,-1,-1,-63,0,-1,-1,-61,0,-1,-1,-1,-1,-1,-1,-51,0,-1,-1,-47,0,-1,-1,-1,-1,-1,-1,-45,0,-1,-1,-40,0,-1,-1,-1,-1,-1,-1,-1,-1,-36,0,-31,0,-25,0,-20,0,-15,0,-10,0,-5,0,1,1,7,1,13,1,19,1,24,1,-1,-1,29,1,-1,-1,33,1,38,1,43,1,47,1,54,1,-1,-1,61,1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,65,1,-1,-1,68,1,77,1,86,1,95,1,104,1,113,1,122,1,-125,1,-116,1,-107,1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-98,1,-2,-1,-2,-1,-1,-1,-1,-1,-78,1,-75,1,-64,1,-61,1,-59,1,-56,1,-13,1,-1,-1,-10,1,-8,1,-1,-1,-1,-1,-1,-1,-3,1,1,2,5,2,9,2,13,2,-1,-1,-1,-1,17,2,-1,-1,40,2,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,44,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,48,2,53,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,57,2,-1,-1,-1,-1,64,2,-1,-1,-1,-1,-1,-1,-1,-1,71,2,78,2,85,2,-1,-1,-1,-1,92,2,-1,-1,99,2,-1,-1,-1,-1,-1,-1,106,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,113,2,119,2,125,2,-125,2,-119,2,-113,2,-107,2,-101,2,-95,2,-89,2,-83,2,-77,2,-71,2,-65,2,-59,2,-53,2,-47,2,-41,2,-35,2,-29,2,-23,2,-17,2,-11,2,-5,2,1,3,7,3,13,3,19,3,25,3,31,3,38,3,44,3,50,3,56,3,62,3,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,68,3,73,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,80,3,-2,-1,89,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-74,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-69,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-63,3,0,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,80,0,27,91,77,0,27,93,48,59,7,0,27,40,48,0,27,91,49,109,0,27,91,63,49,48,52,57,104,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,91,48,109,0,27,91,63,49,48,52,57,108,0,27,91,50,55,109,0,27,91,50,52,109,0,7,0,27,91,33,112,27,91,63,51,108,0,27,91,76,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,91,65,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,27,91,48,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,27,72,0,9,0,27,93,48,59,0,27,79,119,0,27,79,121,0,27,91,71,0,27,79,113,0,27,79,115,0,106,106,107,107,108,108,109,109,110,110,113,113,116,116,117,117,118,118,119,119,120,120,0,27,91,90,0,27,79,69,0,27,91,52,126,0,27,79,77,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,49,59,54,83,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,51,57,59,52,57,109,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,0,27,91,51,109,0,27,91,50,51,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,3,0,0,0,87,0,119,0,-127,2,1,1,1,0,0,0,9,0,-2,-1,-2,-1,18,0,-2,-1,23,0,30,0,37,0,42,0,48,0,58,0,-2,-1,63,0,69,0,78,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,87,0,-2,-1,-2,-1,-2,-1,-2,-1,-2,-1,94,0,98,0,102,0,106,0,110,0,114,0,118,0,122,0,126,0,-126,0,-122,0,-118,0,-114,0,-110,0,-2,-1,-106,0,-2,-1,-2,-1,-81,0,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,45,0,48,0,51,0,54,0,57,0,62,0,67,0,72,0,77,0,82,0,86,0,91,0,96,0,101,0,106,0,111,0,117,0,123,0,-127,0,-121,0,-115,0,-109,0,-103,0,-97,0,-91,0,-85,0,-80,0,-75,0,-70,0,-65,0,-60,0,-54,0,-48,0,-42,0,-36,0,-30,0,-24,0,-18,0,-12,0,-6,0,0,1,6,1,12,1,18,1,24,1,30,1,36,1,42,1,48,1,54,1,60,1,64,1,69,1,74,1,79,1,84,1,89,1,93,1,97,1,101,1,105,1,109,1,115,1,121,1,127,1,-123,1,-117,1,-111,1,-105,1,-100,1,-94,1,-89,1,-86,1,-81,1,-78,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,91,51,74,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,62,99,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,93,48,59,0,27,91,62,48,113,0,27,91,63,49,48,48,52,108,0,27,91,63,49,48,48,52,104,0,27,91,49,59,50,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,27,91,73,0,27,91,79,0,27,92,91,52,49,59,91,49,45,54,93,91,48,45,57,93,91,48,45,57,93,59,48,99,0,27,80,62,92,124,88,84,101,114,109,92,40,91,49,45,57,93,91,48,45,57,93,43,92,41,27,92,92,0,65,88,0,88,70,0,88,84,0,66,68,0,66,69,0,67,114,0,67,115,0,69,51,0,77,115,0,80,69,0,80,83,0,82,86,0,83,101,0,83,115,0,84,83,0,88,77,0,88,82,0,102,100,0,102,101,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,107,120,73,78,0,107,120,79,85,84,0,114,109,120,120,0,114,118,0,115,109,120,120,0,120,109,0,120,114,0 }; // win32con|ANSI emulation for libuv on legacy console, @@ -2114,6 +2570,8 @@ static const int8_t vtpcon_terminfo[] = { // user7@, // user8@, // user9@, +// Se=\E[0 q, +// Ss=\E[%p1%d q, static const int8_t win32con_terminfo[] = { 26,1,52,0,15,0,15,0,125,1,106,2,119,105,110,51,50,99,111,110,124,65,78,83,73,32,101,109,117,108,97,116,105,111,110,32,102,111,114,32,108,105,98,117,118,32,111,110,32,108,101,103,97,99,121,32,99,111,110,115,111,108,101,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,-1,-1,8,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,8,0,64,0,-1,-1,0,0,2,0,-1,-1,-1,-1,4,0,11,0,15,0,19,0,-1,-1,30,0,47,0,51,0,-1,-1,55,0,-1,-1,-1,-1,57,0,-1,-1,61,0,-1,-1,-2,-1,-2,-1,-1,-1,-1,-1,-2,-1,-1,-1,65,0,70,0,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,79,0,84,0,-2,-1,-1,-1,-2,-1,89,0,94,0,-1,-1,-2,-1,107,0,-2,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-1,-1,113,0,-1,-1,-1,-1,-1,-1,115,0,-1,-1,120,0,-1,-1,-1,-1,-1,-1,-1,-1,124,0,-127,0,-121,0,-116,0,-111,0,-106,0,-101,0,-95,0,-89,0,-83,0,-77,0,-72,0,-1,-1,-67,0,-1,-1,-63,0,-58,0,-53,0,-1,-1,-1,-1,-1,-1,-49,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-45,0,-1,-1,-2,-1,-2,-1,-42,0,-2,-1,-1,-1,-2,-1,-33,0,-24,0,-1,-1,-15,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-6,0,-3,0,8,1,-2,-1,-2,-1,11,1,-1,-1,-1,-1,49,1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,51,1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,55,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,60,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,1,-1,-1,-1,-1,69,1,-1,-1,-1,-1,-1,-1,-1,-1,76,1,83,1,90,1,-1,-1,-1,-1,97,1,-1,-1,104,1,-1,-1,-1,-1,-1,-1,111,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,118,1,124,1,-126,1,-120,1,-114,1,-108,1,-102,1,-96,1,-90,1,-84,1,-78,1,-72,1,-66,1,-60,1,-54,1,-48,1,-42,1,-36,1,-30,1,-24,1,-18,1,-12,1,-6,1,0,2,6,2,12,2,18,2,24,2,30,2,-1,-1,36,2,42,2,48,2,54,2,60,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,66,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-2,-1,-2,-1,-2,-1,71,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,80,2,90,2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,100,2,7,0,13,0,27,91,72,27,91,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,27,91,66,0,27,91,72,0,8,0,27,91,67,0,27,91,65,0,27,91,49,109,0,27,55,27,91,63,52,55,104,0,27,91,55,109,0,27,91,55,109,0,27,91,48,109,0,27,91,50,74,27,91,63,52,55,108,27,56,0,27,91,50,55,109,0,8,0,27,91,51,126,0,27,91,66,0,27,91,91,65,0,27,91,50,49,126,0,27,91,91,66,0,27,91,91,67,0,27,91,91,68,0,27,91,91,69,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,91,49,126,0,27,91,50,126,0,27,91,68,0,27,91,54,126,0,27,91,53,126,0,27,91,67,0,27,91,65,0,13,10,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,65,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,27,91,48,37,63,37,112,49,37,116,59,55,37,59,37,63,37,112,51,37,116,59,55,37,59,37,63,37,112,54,37,116,59,49,37,59,109,0,9,0,27,91,71,0,27,91,52,126,0,26,0,27,91,51,59,50,126,0,27,91,52,59,50,126,0,27,91,49,59,50,126,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,50,53,126,0,27,91,50,54,126,0,27,91,50,56,126,0,27,91,50,57,126,0,27,91,51,49,126,0,27,91,51,50,126,0,27,91,51,51,126,0,27,91,51,52,126,0,27,91,50,51,36,0,27,91,50,52,36,0,27,91,49,49,94,0,27,91,49,50,94,0,27,91,49,51,94,0,27,91,49,52,94,0,27,91,49,53,94,0,27,91,49,55,94,0,27,91,49,56,94,0,27,91,49,57,94,0,27,91,50,48,94,0,27,91,50,49,94,0,27,91,50,51,94,0,27,91,50,52,94,0,27,91,50,53,94,0,27,91,50,54,94,0,27,91,50,56,94,0,27,91,50,57,94,0,27,91,51,49,94,0,27,91,51,50,94,0,27,91,51,51,94,0,27,91,51,52,94,0,27,91,50,51,64,0,27,91,50,52,64,0,27,91,49,75,0,27,91,51,57,59,52,57,109,0,27,91,51,37,112,49,37,100,109,0,27,91,52,37,112,49,37,100,109,0,27,91,49,48,109,0,0,0,0,0,2,0,4,0,22,0,0,0,6,0,0,0,3,0,27,91,48,32,113,0,27,91,37,112,49,37,100,32,113,0,83,101,0,83,115,0 }; @@ -2129,6 +2587,9 @@ static const int8_t win32con_terminfo[] = { // move_standout_mode, // no_pad_char, // prtr_silent, +// AX, +// XF, +// XT, // columns#80, // init_tabs#8, // lines#24, @@ -2317,6 +2778,92 @@ static const int8_t win32con_terminfo[] = { // user7=\E[6n, // user8=\E[?%[;0123456789]c, // user9=\E[c, +// BD=\E[?2004l, +// BE=\E[?2004h, +// Cr=\E]112^G, +// Cs=\E]12;%p1%s^G, +// E3=\E[3J, +// Ms=\E]52;%p1%s;%p2%s^G, +// PE=\E[201~, +// PS=\E[200~, +// RV=\E[>c, +// Se=\E[2 q, +// Ss=\E[%p1%d q, +// XM=\E[?1006;1000%?%p1%{1}%=%th%el%;, +// XR=\E[>0q, +// fd=\E[?1004l, +// fe=\E[?1004h, +// kDC3=\E[3;3~, +// kDC4=\E[3;4~, +// kDC5=\E[3;5~, +// kDC6=\E[3;6~, +// kDC7=\E[3;7~, +// kDN=\E[1;2B, +// kDN3=\E[1;3B, +// kDN4=\E[1;4B, +// kDN5=\E[1;5B, +// kDN6=\E[1;6B, +// kDN7=\E[1;7B, +// kEND3=\E[1;3F, +// kEND4=\E[1;4F, +// kEND5=\E[1;5F, +// kEND6=\E[1;6F, +// kEND7=\E[1;7F, +// kHOM3=\E[1;3H, +// kHOM4=\E[1;4H, +// kHOM5=\E[1;5H, +// kHOM6=\E[1;6H, +// kHOM7=\E[1;7H, +// kIC3=\E[2;3~, +// kIC4=\E[2;4~, +// kIC5=\E[2;5~, +// kIC6=\E[2;6~, +// kIC7=\E[2;7~, +// kLFT3=\E[1;3D, +// kLFT4=\E[1;4D, +// kLFT5=\E[1;5D, +// kLFT6=\E[1;6D, +// kLFT7=\E[1;7D, +// kNXT3=\E[6;3~, +// kNXT4=\E[6;4~, +// kNXT5=\E[6;5~, +// kNXT6=\E[6;6~, +// kNXT7=\E[6;7~, +// kPRV3=\E[5;3~, +// kPRV4=\E[5;4~, +// kPRV5=\E[5;5~, +// kPRV6=\E[5;6~, +// kPRV7=\E[5;7~, +// kRIT3=\E[1;3C, +// kRIT4=\E[1;4C, +// kRIT5=\E[1;5C, +// kRIT6=\E[1;6C, +// kRIT7=\E[1;7C, +// kUP=\E[1;2A, +// kUP3=\E[1;3A, +// kUP4=\E[1;4A, +// kUP5=\E[1;5A, +// kUP6=\E[1;6A, +// kUP7=\E[1;7A, +// ka2=\EOx, +// kb1=\EOt, +// kb3=\EOv, +// kc2=\EOr, +// kp5=\EOE, +// kpADD=\EOk, +// kpCMA=\EOl, +// kpDIV=\EOo, +// kpDOT=\EOn, +// kpMUL=\EOj, +// kpSUB=\EOm, +// kpZRO=\EOp, +// kxIN=\E[I, +// kxOUT=\E[O, +// rmxx=\E[29m, +// rv=\E\[41;[1-6][0-9][0-9];0c, +// smxx=\E[9m, +// xm=\E[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;, +// xr=\EP>\|XTerm\([1-9][0-9]+\)\E\\, static const int8_t xterm_256colour_terminfo[] = { 30,2,37,0,38,0,15,0,-99,1,90,6,120,116,101,114,109,45,50,53,54,99,111,108,111,114,124,120,116,101,114,109,32,119,105,116,104,32,50,53,54,32,99,111,108,111,114,115,0,0,1,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1,1,0,0,0,0,0,0,0,0,1,0,80,0,0,0,8,0,0,0,24,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,0,0,0,0,1,0,0,0,4,0,6,0,8,0,25,0,30,0,38,0,42,0,46,0,-1,-1,57,0,74,0,76,0,80,0,87,0,-1,-1,89,0,102,0,-1,-1,106,0,110,0,120,0,124,0,-1,-1,-1,-1,-128,0,-124,0,-119,0,-114,0,-1,-1,-96,0,-91,0,-86,0,-1,-1,-81,0,-76,0,-71,0,-66,0,-57,0,-53,0,-46,0,-1,-1,-28,0,-23,0,-17,0,-11,0,-1,-1,-1,-1,-1,-1,7,1,-1,-1,-1,-1,-1,-1,25,1,-1,-1,29,1,-1,-1,-1,-1,-1,-1,31,1,-1,-1,36,1,-1,-1,-1,-1,-1,-1,-1,-1,40,1,44,1,50,1,54,1,58,1,62,1,68,1,74,1,80,1,86,1,92,1,96,1,-1,-1,101,1,-1,-1,105,1,110,1,115,1,119,1,126,1,-1,-1,-123,1,-119,1,-111,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-103,1,-94,1,-85,1,-1,-1,-82,1,-73,1,-64,1,-55,1,-46,1,-37,1,-28,1,-19,1,-10,1,-1,1,-1,-1,-1,-1,-1,-1,8,2,12,2,17,2,22,2,42,2,51,2,-1,-1,-1,-1,69,2,72,2,83,2,86,2,88,2,91,2,-72,2,-1,-1,-69,2,-1,-1,-1,-1,-1,-1,-1,-1,-67,2,-63,2,-59,2,-55,2,-51,2,-1,-1,-1,-1,-47,2,-1,-1,6,3,-1,-1,-1,-1,10,3,16,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,22,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,3,30,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,34,3,-1,-1,-1,-1,41,3,-1,-1,-1,-1,-1,-1,-1,-1,48,3,55,3,62,3,-1,-1,-1,-1,69,3,-1,-1,76,3,-1,-1,-1,-1,-1,-1,83,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,90,3,96,3,102,3,109,3,116,3,123,3,-126,3,-118,3,-110,3,-102,3,-94,3,-86,3,-78,3,-70,3,-62,3,-55,3,-48,3,-41,3,-34,3,-26,3,-18,3,-10,3,-2,3,6,4,14,4,22,4,30,4,37,4,44,4,51,4,58,4,66,4,74,4,82,4,90,4,98,4,106,4,114,4,122,4,-127,4,-120,4,-113,4,-106,4,-98,4,-90,4,-82,4,-74,4,-66,4,-58,4,-50,4,-42,4,-35,4,-28,4,-21,4,-16,4,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-9,4,2,5,7,5,26,5,30,5,39,5,46,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-116,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-111,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-105,5,-88,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-70,5,-1,-1,-1,-1,-1,-1,-66,5,-3,5,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,61,6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,84,6,87,6,27,91,90,0,7,0,13,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,114,0,27,91,51,103,0,27,91,72,27,91,50,74,0,27,91,75,0,27,91,74,0,27,91,37,105,37,112,49,37,100,71,0,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,72,0,10,0,27,91,72,0,27,91,63,50,53,108,0,8,0,27,91,63,49,50,108,27,91,63,50,53,104,0,27,91,67,0,27,91,65,0,27,91,63,49,50,59,50,53,104,0,27,91,80,0,27,91,77,0,27,40,48,0,27,91,53,109,0,27,91,49,109,0,27,91,63,49,48,52,57,104,27,91,50,50,59,48,59,48,116,0,27,91,50,109,0,27,91,52,104,0,27,91,56,109,0,27,91,55,109,0,27,91,55,109,0,27,91,52,109,0,27,91,37,112,49,37,100,88,0,27,40,66,0,27,40,66,27,91,109,0,27,91,63,49,48,52,57,108,27,91,50,51,59,48,59,48,116,0,27,91,52,108,0,27,91,50,55,109,0,27,91,50,52,109,0,27,91,63,53,104,36,60,49,48,48,47,62,27,91,63,53,108,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,0,27,91,76,0,8,0,27,91,51,126,0,27,79,66,0,27,79,80,0,27,91,50,49,126,0,27,79,81,0,27,79,82,0,27,79,83,0,27,91,49,53,126,0,27,91,49,55,126,0,27,91,49,56,126,0,27,91,49,57,126,0,27,91,50,48,126,0,27,79,72,0,27,91,50,126,0,27,79,68,0,27,91,54,126,0,27,91,53,126,0,27,79,67,0,27,91,49,59,50,66,0,27,91,49,59,50,65,0,27,79,65,0,27,91,63,49,108,27,62,0,27,91,63,49,104,27,61,0,27,91,63,49,48,51,52,108,0,27,91,63,49,48,51,52,104,0,27,69,0,27,91,37,112,49,37,100,80,0,27,91,37,112,49,37,100,77,0,27,91,37,112,49,37,100,66,0,27,91,37,112,49,37,100,64,0,27,91,37,112,49,37,100,83,0,27,91,37,112,49,37,100,76,0,27,91,37,112,49,37,100,68,0,27,91,37,112,49,37,100,67,0,27,91,37,112,49,37,100,84,0,27,91,37,112,49,37,100,65,0,27,91,105,0,27,91,52,105,0,27,91,53,105,0,37,112,49,37,99,27,91,37,112,50,37,123,49,125,37,45,37,100,98,0,27,99,27,93,49,48,52,7,0,27,91,33,112,27,91,63,51,59,52,108,27,91,52,108,27,62,0,27,56,0,27,91,37,105,37,112,49,37,100,100,0,27,55,0,10,0,27,77,0,37,63,37,112,57,37,116,27,40,48,37,101,27,40,66,37,59,27,91,48,37,63,37,112,54,37,116,59,49,37,59,37,63,37,112,53,37,116,59,50,37,59,37,63,37,112,50,37,116,59,52,37,59,37,63,37,112,49,37,112,51,37,124,37,116,59,55,37,59,37,63,37,112,52,37,116,59,53,37,59,37,63,37,112,55,37,116,59,56,37,59,109,0,27,72,0,9,0,27,79,119,0,27,79,121,0,27,79,117,0,27,79,113,0,27,79,115,0,96,96,97,97,102,102,103,103,105,105,106,106,107,107,108,108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123,123,124,124,125,125,126,126,0,27,91,90,0,27,91,63,55,104,0,27,91,63,55,108,0,27,79,69,0,27,79,70,0,27,79,77,0,27,91,51,59,50,126,0,27,91,49,59,50,70,0,27,91,49,59,50,72,0,27,91,50,59,50,126,0,27,91,49,59,50,68,0,27,91,54,59,50,126,0,27,91,53,59,50,126,0,27,91,49,59,50,67,0,27,91,50,51,126,0,27,91,50,52,126,0,27,91,49,59,50,80,0,27,91,49,59,50,81,0,27,91,49,59,50,82,0,27,91,49,59,50,83,0,27,91,49,53,59,50,126,0,27,91,49,55,59,50,126,0,27,91,49,56,59,50,126,0,27,91,49,57,59,50,126,0,27,91,50,48,59,50,126,0,27,91,50,49,59,50,126,0,27,91,50,51,59,50,126,0,27,91,50,52,59,50,126,0,27,91,49,59,53,80,0,27,91,49,59,53,81,0,27,91,49,59,53,82,0,27,91,49,59,53,83,0,27,91,49,53,59,53,126,0,27,91,49,55,59,53,126,0,27,91,49,56,59,53,126,0,27,91,49,57,59,53,126,0,27,91,50,48,59,53,126,0,27,91,50,49,59,53,126,0,27,91,50,51,59,53,126,0,27,91,50,52,59,53,126,0,27,91,49,59,54,80,0,27,91,49,59,54,81,0,27,91,49,59,54,82,0,27,91,49,59,54,83,0,27,91,49,53,59,54,126,0,27,91,49,55,59,54,126,0,27,91,49,56,59,54,126,0,27,91,49,57,59,54,126,0,27,91,50,48,59,54,126,0,27,91,50,49,59,54,126,0,27,91,50,51,59,54,126,0,27,91,50,52,59,54,126,0,27,91,49,59,51,80,0,27,91,49,59,51,81,0,27,91,49,59,51,82,0,27,91,49,59,51,83,0,27,91,49,53,59,51,126,0,27,91,49,55,59,51,126,0,27,91,49,56,59,51,126,0,27,91,49,57,59,51,126,0,27,91,50,48,59,51,126,0,27,91,50,49,59,51,126,0,27,91,50,51,59,51,126,0,27,91,50,52,59,51,126,0,27,91,49,59,52,80,0,27,91,49,59,52,81,0,27,91,49,59,52,82,0,27,91,49,75,0,27,91,63,54,57,108,0,27,91,37,105,37,100,59,37,100,82,0,27,91,54,110,0,27,91,63,37,91,59,48,49,50,51,52,53,54,55,56,57,93,99,0,27,91,99,0,27,91,51,57,59,52,57,109,0,27,93,49,48,52,7,0,27,93,52,59,37,112,49,37,100,59,114,103,98,58,37,112,50,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,51,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,47,37,112,52,37,123,50,53,53,125,37,42,37,123,49,48,48,48,125,37,47,37,50,46,50,88,27,92,0,27,91,51,109,0,27,91,50,51,109,0,27,91,63,54,57,104,27,91,37,105,37,112,49,37,100,115,0,27,91,63,54,57,104,27,91,37,105,59,37,112,49,37,100,115,0,27,91,60,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,51,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,57,37,112,49,37,123,56,125,37,45,37,100,37,101,51,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,37,63,37,112,49,37,123,56,125,37,60,37,116,52,37,112,49,37,100,37,101,37,112,49,37,123,49,54,125,37,60,37,116,49,48,37,112,49,37,123,56,125,37,45,37,100,37,101,52,56,59,53,59,37,112,49,37,100,37,59,109,0,27,91,63,54,57,104,27,91,37,105,37,112,49,37,100,59,37,112,50,37,100,115,0,27,108,0,27,109,0,3,0,0,0,86,0,-81,0,83,4,1,1,1,0,0,0,9,0,18,0,25,0,37,0,42,0,60,0,67,0,74,0,79,0,85,0,95,0,127,0,-123,0,-114,0,-105,0,-98,0,-91,0,-84,0,-77,0,-70,0,-63,0,-56,0,-49,0,-42,0,-35,0,-28,0,-21,0,-14,0,-7,0,0,1,7,1,14,1,21,1,28,1,35,1,42,1,49,1,56,1,63,1,70,1,77,1,84,1,91,1,98,1,105,1,112,1,119,1,126,1,-123,1,-116,1,-109,1,-102,1,-95,1,-88,1,-81,1,-74,1,-67,1,-60,1,-53,1,-46,1,-39,1,-32,1,-25,1,-18,1,-11,1,-4,1,3,2,7,2,11,2,15,2,19,2,23,2,27,2,31,2,35,2,39,2,43,2,47,2,51,2,55,2,59,2,65,2,90,2,95,2,-124,2,0,0,3,0,6,0,9,0,12,0,15,0,18,0,21,0,24,0,27,0,30,0,33,0,36,0,39,0,42,0,45,0,48,0,51,0,54,0,59,0,64,0,69,0,74,0,79,0,83,0,88,0,93,0,98,0,103,0,108,0,114,0,120,0,126,0,-124,0,-118,0,-112,0,-106,0,-100,0,-94,0,-88,0,-83,0,-78,0,-73,0,-68,0,-63,0,-57,0,-51,0,-45,0,-39,0,-33,0,-27,0,-21,0,-15,0,-9,0,-3,0,3,1,9,1,15,1,21,1,27,1,33,1,39,1,45,1,51,1,57,1,61,1,66,1,71,1,76,1,81,1,86,1,90,1,94,1,98,1,102,1,106,1,112,1,118,1,124,1,-126,1,-120,1,-114,1,-108,1,-103,1,-97,1,-92,1,-89,1,-84,1,-81,1,27,91,63,50,48,48,52,108,0,27,91,63,50,48,48,52,104,0,27,93,49,49,50,7,0,27,93,49,50,59,37,112,49,37,115,7,0,27,91,51,74,0,27,93,53,50,59,37,112,49,37,115,59,37,112,50,37,115,7,0,27,91,50,48,49,126,0,27,91,50,48,48,126,0,27,91,62,99,0,27,91,50,32,113,0,27,91,37,112,49,37,100,32,113,0,27,91,63,49,48,48,54,59,49,48,48,48,37,63,37,112,49,37,123,49,125,37,61,37,116,104,37,101,108,37,59,0,27,91,62,48,113,0,27,91,63,49,48,48,52,108,0,27,91,63,49,48,48,52,104,0,27,91,51,59,51,126,0,27,91,51,59,52,126,0,27,91,51,59,53,126,0,27,91,51,59,54,126,0,27,91,51,59,55,126,0,27,91,49,59,50,66,0,27,91,49,59,51,66,0,27,91,49,59,52,66,0,27,91,49,59,53,66,0,27,91,49,59,54,66,0,27,91,49,59,55,66,0,27,91,49,59,51,70,0,27,91,49,59,52,70,0,27,91,49,59,53,70,0,27,91,49,59,54,70,0,27,91,49,59,55,70,0,27,91,49,59,51,72,0,27,91,49,59,52,72,0,27,91,49,59,53,72,0,27,91,49,59,54,72,0,27,91,49,59,55,72,0,27,91,50,59,51,126,0,27,91,50,59,52,126,0,27,91,50,59,53,126,0,27,91,50,59,54,126,0,27,91,50,59,55,126,0,27,91,49,59,51,68,0,27,91,49,59,52,68,0,27,91,49,59,53,68,0,27,91,49,59,54,68,0,27,91,49,59,55,68,0,27,91,54,59,51,126,0,27,91,54,59,52,126,0,27,91,54,59,53,126,0,27,91,54,59,54,126,0,27,91,54,59,55,126,0,27,91,53,59,51,126,0,27,91,53,59,52,126,0,27,91,53,59,53,126,0,27,91,53,59,54,126,0,27,91,53,59,55,126,0,27,91,49,59,51,67,0,27,91,49,59,52,67,0,27,91,49,59,53,67,0,27,91,49,59,54,67,0,27,91,49,59,55,67,0,27,91,49,59,50,65,0,27,91,49,59,51,65,0,27,91,49,59,52,65,0,27,91,49,59,53,65,0,27,91,49,59,54,65,0,27,91,49,59,55,65,0,27,79,120,0,27,79,116,0,27,79,118,0,27,79,114,0,27,79,69,0,27,79,107,0,27,79,108,0,27,79,111,0,27,79,110,0,27,79,106,0,27,79,109,0,27,79,112,0,27,91,73,0,27,91,79,0,27,91,50,57,109,0,27,92,91,52,49,59,91,49,45,54,93,91,48,45,57,93,91,48,45,57,93,59,48,99,0,27,91,57,109,0,27,91,60,37,105,37,112,51,37,100,59,37,112,49,37,100,59,37,112,50,37,100,59,37,63,37,112,52,37,116,77,37,101,109,37,59,0,27,80,62,92,124,88,84,101,114,109,92,40,91,49,45,57,93,91,48,45,57,93,43,92,41,27,92,92,0,65,88,0,88,70,0,88,84,0,66,68,0,66,69,0,67,114,0,67,115,0,69,51,0,77,115,0,80,69,0,80,83,0,82,86,0,83,101,0,83,115,0,88,77,0,88,82,0,102,100,0,102,101,0,107,68,67,51,0,107,68,67,52,0,107,68,67,53,0,107,68,67,54,0,107,68,67,55,0,107,68,78,0,107,68,78,51,0,107,68,78,52,0,107,68,78,53,0,107,68,78,54,0,107,68,78,55,0,107,69,78,68,51,0,107,69,78,68,52,0,107,69,78,68,53,0,107,69,78,68,54,0,107,69,78,68,55,0,107,72,79,77,51,0,107,72,79,77,52,0,107,72,79,77,53,0,107,72,79,77,54,0,107,72,79,77,55,0,107,73,67,51,0,107,73,67,52,0,107,73,67,53,0,107,73,67,54,0,107,73,67,55,0,107,76,70,84,51,0,107,76,70,84,52,0,107,76,70,84,53,0,107,76,70,84,54,0,107,76,70,84,55,0,107,78,88,84,51,0,107,78,88,84,52,0,107,78,88,84,53,0,107,78,88,84,54,0,107,78,88,84,55,0,107,80,82,86,51,0,107,80,82,86,52,0,107,80,82,86,53,0,107,80,82,86,54,0,107,80,82,86,55,0,107,82,73,84,51,0,107,82,73,84,52,0,107,82,73,84,53,0,107,82,73,84,54,0,107,82,73,84,55,0,107,85,80,0,107,85,80,51,0,107,85,80,52,0,107,85,80,53,0,107,85,80,54,0,107,85,80,55,0,107,97,50,0,107,98,49,0,107,98,51,0,107,99,50,0,107,112,53,0,107,112,65,68,68,0,107,112,67,77,65,0,107,112,68,73,86,0,107,112,68,79,84,0,107,112,77,85,76,0,107,112,83,85,66,0,107,112,90,82,79,0,107,120,73,78,0,107,120,79,85,84,0,114,109,120,120,0,114,118,0,115,109,120,120,0,120,109,0,120,114,0 }; diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 7fae34d33f..2a9530defb 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -23,6 +23,7 @@ #include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/grid_defs.h" +#include "nvim/highlight.h" #include "nvim/highlight_defs.h" #include "nvim/log.h" #include "nvim/macros_defs.h" @@ -165,6 +166,15 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term, bool *rgb) tui->seen_error_exit = 0; tui->loop = &main_loop; tui->url = -1; + // Because setting the default colors is delayed until after startup to avoid + // flickering with the default colorscheme background, any flush that happens + // during startup in turn would result in clearing invalidated regions with + // uninitialized attrs(black). Instead initialize clear_attrs with current + // terminal background so that it is at least not perceived as flickering, even + // though it may be different from the colorscheme that is set during startup. + tui->clear_attrs.rgb_bg_color = normal_bg; + tui->clear_attrs.cterm_bg_color = (int16_t)cterm_normal_bg_color; + kv_init(tui->invalid_regions); kv_init(tui->urlbuf); signal_watcher_init(tui->loop, &tui->winch_handle, tui); @@ -189,36 +199,6 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term, bool *rgb) *rgb = tui->rgb; } -void tui_set_key_encoding(TUIData *tui) - FUNC_ATTR_NONNULL_ALL -{ - switch (tui->input.key_encoding) { - case kKeyEncodingKitty: - out(tui, S_LEN("\x1b[>1u")); - break; - case kKeyEncodingXterm: - out(tui, S_LEN("\x1b[>4;2m")); - break; - case kKeyEncodingLegacy: - break; - } -} - -static void tui_reset_key_encoding(TUIData *tui) - FUNC_ATTR_NONNULL_ALL -{ - switch (tui->input.key_encoding) { - case kKeyEncodingKitty: - out(tui, S_LEN("\x1b[<1u")); - break; - case kKeyEncodingXterm: - out(tui, S_LEN("\x1b[>4;0m")); - break; - case kKeyEncodingLegacy: - break; - } -} - /// Request the terminal's mode (DECRQM). /// /// @see handle_modereport @@ -255,6 +235,26 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state) } } +/// Query the terminal emulator to see if it supports extended underline. +static void tui_query_extended_underline(TUIData *tui) +{ + // Try to set an undercurl using an SGR sequence, followed by a DECRQSS SGR query. + // Reset attributes first, as other code may have set attributes. + out(tui, S_LEN("\x1b[0m\x1b[4:3m\x1bP$qm\x1b\\")); + tui->print_attr_id = -1; +} + +void tui_enable_extended_underline(TUIData *tui) +{ + if (tui->unibi_ext.set_underline_style == -1) { + tui->unibi_ext.set_underline_style = (int)unibi_add_ext_str(tui->ut, "ext.set_underline_style", + "\x1b[4:%p1%dm"); + } + // Only support colon syntax. #9270 + tui->unibi_ext.set_underline_color = (int)unibi_add_ext_str(tui->ut, "ext.set_underline_color", + "\x1b[58:2::%p1%d:%p2%d:%p3%dm"); +} + /// Query the terminal emulator to see if it supports Kitty's keyboard protocol. /// /// Write CSI ? u followed by a primary device attributes request (CSI c). If @@ -270,6 +270,36 @@ static void tui_query_kitty_keyboard(TUIData *tui) out(tui, S_LEN("\x1b[?u\x1b[c")); } +void tui_set_key_encoding(TUIData *tui) + FUNC_ATTR_NONNULL_ALL +{ + switch (tui->input.key_encoding) { + case kKeyEncodingKitty: + out(tui, S_LEN("\x1b[>1u")); + break; + case kKeyEncodingXterm: + out(tui, S_LEN("\x1b[>4;2m")); + break; + case kKeyEncodingLegacy: + break; + } +} + +static void tui_reset_key_encoding(TUIData *tui) + FUNC_ATTR_NONNULL_ALL +{ + switch (tui->input.key_encoding) { + case kKeyEncodingKitty: + out(tui, S_LEN("\x1b[<1u")); + break; + case kKeyEncodingXterm: + out(tui, S_LEN("\x1b[>4;0m")); + break; + case kKeyEncodingLegacy: + break; + } +} + /// Enable the alternate screen and emit other control sequences to start the TUI. /// /// This is also called when the TUI is resumed after being suspended. We reinitialize all state @@ -306,6 +336,7 @@ static void terminfo_start(TUIData *tui) tui->unibi_ext.reset_scroll_region = -1; tui->unibi_ext.set_cursor_style = -1; tui->unibi_ext.reset_cursor_style = -1; + tui->unibi_ext.set_underline_style = -1; tui->unibi_ext.set_underline_color = -1; tui->unibi_ext.sync = -1; tui->out_fd = STDOUT_FILENO; @@ -347,12 +378,16 @@ static void terminfo_start(TUIData *tui) const char *konsolev_env = os_getenv("KONSOLE_VERSION"); int konsolev = konsolev_env ? (int)strtol(konsolev_env, NULL, 10) : (konsole ? 1 : 0); + bool wezterm = strequal(termprg, "WezTerm"); + const char *weztermv = wezterm ? os_getenv("TERM_PROGRAM_VERSION") : NULL; + bool screen = terminfo_is_term_family(term, "screen"); + bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX"); // truecolor support must be checked before patching/augmenting terminfo tui->rgb = term_has_truecolor(tui, colorterm); patch_terminfo_bugs(tui, term, colorterm, vtev, konsolev, iterm_env, nsterm); - augment_terminfo(tui, term, vtev, konsolev, iterm_env, nsterm); + augment_terminfo(tui, term, vtev, konsolev, weztermv, iterm_env, nsterm); tui->can_change_scroll_region = !!unibi_get_str(tui->ut, unibi_change_scroll_region); tui->can_set_lr_margin = @@ -385,8 +420,18 @@ static void terminfo_start(TUIData *tui) // Query support for mode 2026 (Synchronized Output). Some terminals also // support an older DCS sequence for synchronized output, but we will only use - // mode 2026 - tui_request_term_mode(tui, kTermModeSynchronizedOutput); + // mode 2026. + // Some terminals (such as Terminal.app) do not support DECRQM, so skip the query. + if (!nsterm) { + tui_request_term_mode(tui, kTermModeSynchronizedOutput); + } + + // Don't use DECRQSS in screen or tmux, as they behave strangely when receiving it. + // Terminal.app also doesn't support DECRQSS. + if (tui->unibi_ext.set_underline_style == -1 && !(screen || tmux || nsterm)) { + // Query the terminal to see if it supports extended underline. + tui_query_extended_underline(tui); + } // Query the terminal to see if it supports Kitty's keyboard protocol tui_query_kitty_keyboard(tui); @@ -457,10 +502,6 @@ static void terminfo_stop(TUIData *tui) // Disable focus reporting unibi_out_ext(tui, tui->unibi_ext.disable_focus_reporting); - // Disable synchronized output - UNIBI_SET_NUM_VAR(tui->params[0], 0); - unibi_out_ext(tui, tui->unibi_ext.sync); - flush_buf(tui); uv_tty_reset_mode(); uv_close((uv_handle_t *)&tui->output_handle, NULL); @@ -932,17 +973,17 @@ static void print_spaces(TUIData *tui, int width) } } -/// Move cursor to the position given by `row` and `col` and print the character in `cell`. -/// This allows the grid and the host terminal to assume different widths of ambiguous-width chars. +/// Move cursor to the position given by `row` and `col` and print the char in `cell`. +/// Allows grid and host terminal to assume different widths of ambiguous-width chars. /// -/// @param is_doublewidth whether the character is double-width on the grid. -/// If true and the character is ambiguous-width, clear two cells. +/// @param is_doublewidth whether the char is double-width on the grid. +/// If true and the char is ambiguous-width, clear two cells. static void print_cell_at_pos(TUIData *tui, int row, int col, UCell *cell, bool is_doublewidth) { UGrid *grid = &tui->grid; if (grid->row == -1 && cell->data == NUL) { - // If cursor needs to repositioned and there is nothing to print, don't move cursor. + // If cursor needs repositioning and there is nothing to print, don't move cursor. return; } @@ -950,10 +991,14 @@ static void print_cell_at_pos(TUIData *tui, int row, int col, UCell *cell, bool char buf[MAX_SCHAR_SIZE]; schar_get(buf, cell->data); - bool is_ambiwidth = utf_ambiguous_width(utf_ptr2char(buf)); - if (is_ambiwidth && is_doublewidth) { + int c = utf_ptr2char(buf); + bool is_ambiwidth = utf_ambiguous_width(c); + if (is_doublewidth && (is_ambiwidth || utf_char2cells(c) == 1)) { + // If the server used setcellwidths() to treat a single-width char as double-width, + // it needs to be treated like an ambiguous-width char. + is_ambiwidth = true; // Clear the two screen cells. - // If the character is single-width in the host terminal it won't change the second cell. + // If the char is single-width in host terminal it won't change the second cell. update_attrs(tui, cell->attr); print_spaces(tui, 2); cursor_goto(tui, row, col); @@ -962,7 +1007,7 @@ static void print_cell_at_pos(TUIData *tui, int row, int col, UCell *cell, bool print_cell(tui, buf, cell->attr); if (is_ambiwidth) { - // Force repositioning cursor after printing an ambiguous-width character. + // Force repositioning cursor after printing an ambiguous-width char. grid->row = -1; } } @@ -1206,7 +1251,7 @@ static void tui_set_mode(TUIData *tui, ModeShape mode) // We interpret "inverse" as "default" (no termcode for "inverse"...). // Hopefully the user's default cursor color is inverse. unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color); - } else { + } else if (!tui->want_invisible && aep.rgb_bg_color >= 0) { char hexbuf[8]; if (tui->set_cursor_color_as_str) { snprintf(hexbuf, 7 + 1, "#%06x", aep.rgb_bg_color); @@ -1643,7 +1688,7 @@ static void invalidate(TUIData *tui, int top, int bot, int left, int right) static void ensure_space_buf_size(TUIData *tui, size_t len) { if (len > tui->space_buf_len) { - tui->space_buf = xrealloc(tui->space_buf, len * sizeof *tui->space_buf); + tui->space_buf = xrealloc(tui->space_buf, len); memset(tui->space_buf + tui->space_buf_len, ' ', len - tui->space_buf_len); tui->space_buf_len = len; } @@ -2168,7 +2213,7 @@ static void patch_terminfo_bugs(TUIData *tui, const char *term, const char *colo /// This adds stuff that is not in standard terminfo as extended unibilium /// capabilities. static void augment_terminfo(TUIData *tui, const char *term, int vte_version, int konsolev, - bool iterm_env, bool nsterm) + const char *weztermv, bool iterm_env, bool nsterm) { unibi_term *ut = tui->ut; bool xterm = terminfo_is_term_family(term, "xterm") @@ -2327,16 +2372,12 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in if (tui->unibi_ext.set_underline_style == -1) { int ext_bool_Su = unibi_find_ext_bool(ut, "Su"); // used by kitty if (vte_version >= 5102 || konsolev >= 221170 - || (ext_bool_Su != -1 - && unibi_get_ext_bool(ut, (size_t)ext_bool_Su))) { - tui->unibi_ext.set_underline_style = (int)unibi_add_ext_str(ut, "ext.set_underline_style", - "\x1b[4:%p1%dm"); + || (ext_bool_Su != -1 && unibi_get_ext_bool(ut, (size_t)ext_bool_Su)) + || (weztermv != NULL && strcmp(weztermv, "20210203-095643") > 0)) { + tui_enable_extended_underline(tui); } - } - if (tui->unibi_ext.set_underline_style != -1) { - // Only support colon syntax. #9270 - tui->unibi_ext.set_underline_color = (int)unibi_add_ext_str(ut, "ext.set_underline_color", - "\x1b[58:2::%p1%d:%p2%d:%p3%dm"); + } else { + tui_enable_extended_underline(tui); } if (!kitty && (vte_version == 0 || vte_version >= 5400)) { diff --git a/src/nvim/ui.c b/src/nvim/ui.c index bddcf98b53..9bb66b886e 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -71,8 +71,6 @@ static bool has_mouse = false; static int pending_has_mouse = -1; static bool pending_default_colors = false; -static Array call_buf = ARRAY_DICT_INIT; - #ifdef NVIM_LOG_DEBUG static size_t uilog_seen = 0; static const char *uilog_last_event = NULL; @@ -128,14 +126,11 @@ void ui_init(void) default_grid.handle = 1; msg_grid_adj.target = &default_grid; ui_comp_init(); - kv_ensure_space(call_buf, 16); } #ifdef EXITFREE void ui_free_all_mem(void) { - kv_destroy(call_buf); - UIEventCallback *event_cb; map_foreach_value(&ui_event_cbs, event_cb, { free_ui_event_callback(event_cb); @@ -194,23 +189,12 @@ void ui_refresh(void) abort(); } - if (!ui_active()) { - return; - } - - if (updating_screen) { - ui_schedule_refresh(); - return; - } - int width = INT_MAX; int height = INT_MAX; bool ext_widgets[kUIExtCount]; - for (UIExtension i = 0; (int)i < kUIExtCount; i++) { - ext_widgets[i] = true; - } - bool inclusive = ui_override(); + memset(ext_widgets, ui_active(), ARRAY_SIZE(ext_widgets)); + for (size_t i = 0; i < ui_count; i++) { RemoteUI *ui = uis[i]; width = MIN(ui->width, width); @@ -227,13 +211,29 @@ void ui_refresh(void) if (i < kUIGlobalCount) { ext_widgets[i] |= ui_cb_ext[i]; } + // Set 'cmdheight' to zero for all tabpages when ext_messages becomes active. + if (i == kUIMessages && !ui_ext[i] && ext_widgets[i]) { + set_option_value(kOptCmdheight, NUMBER_OPTVAL(0), 0); + command_height(); + FOR_ALL_TABS(tp) { + tp->tp_ch_used = 0; + } + } ui_ext[i] = ext_widgets[i]; if (i < kUIGlobalCount) { - ui_call_option_set(cstr_as_string(ui_ext_names[i]), - BOOLEAN_OBJ(ext_widgets[i])); + ui_call_option_set(cstr_as_string(ui_ext_names[i]), BOOLEAN_OBJ(ext_widgets[i])); } } + if (!ui_active()) { + return; + } + + if (updating_screen) { + ui_schedule_refresh(); + return; + } + ui_default_colors_set(); int save_p_lz = p_lz; @@ -241,10 +241,6 @@ void ui_refresh(void) screen_resize(width, height); p_lz = save_p_lz; - if (ext_widgets[kUIMessages]) { - set_option_value(kOptCmdheight, NUMBER_OPTVAL(0), 0); - command_height(); - } ui_mode_info_set(); pending_mode_update = true; ui_cursor_shape(); @@ -624,7 +620,7 @@ void ui_check_mouse(void) /// Check if current mode has changed. /// /// May update the shape of the cursor. -void ui_cursor_shape(void) +void ui_cursor_shape_no_check_conceal(void) { if (!full_screen) { return; @@ -635,6 +631,15 @@ void ui_cursor_shape(void) ui_mode_idx = new_mode_idx; pending_mode_update = true; } +} + +/// Check if current mode has changed. +/// +/// May update the shape of the cursor. +/// With concealing on, may conceal or unconceal the cursor line. +void ui_cursor_shape(void) +{ + ui_cursor_shape_no_check_conceal(); conceal_check_cursor_line(); } @@ -710,11 +715,14 @@ void ui_call_event(char *name, Array args) map_foreach_value(&ui_event_cbs, event_cb, { Error err = ERROR_INIT; Object res = nlua_call_ref(event_cb->cb, name, args, kRetNilBool, NULL, &err); + // TODO(bfredl/luukvbaal): should this be documented or reconsidered? + // Why does truthy return from Lua callback mean remote UI should not receive + // the event. if (LUARET_TRUTHY(res)) { handled = true; } if (ERROR_SET(&err)) { - ELOG("Error while executing ui_comp_event callback: %s", err.msg); + ELOG("Error executing UI event callback: %s", err.msg); } api_clear_error(&err); }) diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 52b1334b15..8718c7b506 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -14,6 +14,7 @@ #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui.h.generated.h" # include "ui_events_call.h.generated.h" +EXTERN Array noargs INIT(= ARRAY_DICT_INIT); #endif // uncrustify:on diff --git a/src/nvim/ui_defs.h b/src/nvim/ui_defs.h index 4d73cc2321..bbc1655252 100644 --- a/src/nvim/ui_defs.h +++ b/src/nvim/ui_defs.h @@ -63,7 +63,6 @@ typedef struct { PackerBuffer packer; const char *cur_event; ///< name of current event (might get multiple arglists) - Array call_buf; ///< buffer for constructing a single arg list (max 16 elements!) // We start packing the two outermost msgpack arrays before knowing the total // number of elements. Thus track the location where array size will need diff --git a/src/nvim/undo.c b/src/nvim/undo.c index e9170ba858..ba720c9f6a 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -477,7 +477,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, bool r uhp->uh_entry = NULL; uhp->uh_getbot_entry = NULL; uhp->uh_cursor = curwin->w_cursor; // save cursor pos. for undo - if (virtual_active() && curwin->w_cursor.coladd > 0) { + if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { uhp->uh_cursor_vcol = getviscol(); } else { uhp->uh_cursor_vcol = -1; @@ -2488,8 +2488,8 @@ static void u_undoredo(bool undo, bool do_buf_event) if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count) { if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum) { curwin->w_cursor.col = curhead->uh_cursor.col; - if (virtual_active() && curhead->uh_cursor_vcol >= 0) { - coladvance(curhead->uh_cursor_vcol); + if (virtual_active(curwin) && curhead->uh_cursor_vcol >= 0) { + coladvance(curwin, curhead->uh_cursor_vcol); } else { curwin->w_cursor.coladd = 0; } @@ -2506,7 +2506,7 @@ static void u_undoredo(bool undo, bool do_buf_event) } // Make sure the cursor is on an existing line and column. - check_cursor(); + check_cursor(curwin); // Remember where we are for "g-" and ":earlier 10s". curbuf->b_u_seq_cur = curhead->uh_seq; @@ -3073,7 +3073,7 @@ void u_undoline(void) } curwin->w_cursor.col = t; curwin->w_cursor.lnum = curbuf->b_u_line_lnum; - check_cursor_col(); + check_cursor_col(curwin); } /// Allocate memory and copy curbuf line into it. diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 8d41edec8a..e3d9dc5f54 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -1724,7 +1724,7 @@ int do_ucmd(exarg_T *eap, bool preview) save_current_sctx = current_sctx; current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; } - do_cmdline(buf, eap->getline, eap->cookie, + do_cmdline(buf, eap->ea_getline, eap->cookie, DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); // Careful: Do not use "cmd" here, it may have become invalid if a user diff --git a/src/nvim/version.c b/src/nvim/version.c index 038c9701bf..c392362bf4 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -32,7 +32,9 @@ #include "nvim/option_vars.h" #include "nvim/os/os.h" #include "nvim/strings.h" +#include "nvim/ui.h" #include "nvim/version.h" +#include "nvim/window.h" // for ":version", ":intro", and "nvim --version" #ifndef NVIM_VERSION_MEDIUM @@ -1665,7 +1667,7 @@ static const int included_patches[] = { 818, 817, 816, - // 815, + 815, 814, 813, 812, @@ -2710,15 +2712,15 @@ void list_version(void) : "\nRun \":verbose version\" for more info")); } -/// Show the intro message when not editing a file. -void maybe_intro_message(void) +/// Whether it still is not too late to show an intro message +bool may_show_intro(void) { - if (buf_is_empty(curbuf) - && (curbuf->b_fname == NULL) - && (firstwin->w_next == NULL) - && (vim_strchr(p_shm, SHM_INTRO) == NULL)) { - intro_message(false); - } + return (buf_is_empty(curbuf) + && (curbuf->b_fname == NULL) + && (curbuf->handle == 1) + && (curwin->handle == LOWEST_WIN_ID) + && one_window(curwin) + && (vim_strchr(p_shm, SHM_INTRO) == NULL)); } /// Give an introductory message about Vim. @@ -2726,7 +2728,7 @@ void maybe_intro_message(void) /// Or with the ":intro" command (for Sven :-). /// /// @param colon true for ":intro" -void intro_message(int colon) +void intro_message(bool colon) { static char *(lines[]) = { N_(NVIM_VERSION_LONG), @@ -2760,11 +2762,6 @@ void intro_message(int colon) blanklines = 0; } - // Show the sponsor and register message one out of four times, the Uganda - // message two out of four times. - int sponsor = (int)time(NULL); - sponsor = ((sponsor & 2) == 0) - ((sponsor & 4) == 0); - // start displaying the message lines after half of the blank lines int row = blanklines / 2; @@ -2782,16 +2779,6 @@ void intro_message(int colon) mesg = xmallocz((size_t)mesg_size); snprintf(mesg, (size_t)mesg_size + 1, p, STR(NVIM_VERSION_MAJOR), STR(NVIM_VERSION_MINOR)); - } else if (sponsor != 0) { - if (strstr(p, "children") != NULL) { - p = sponsor < 0 - ? N_("Sponsor Vim development!") - : N_("Become a registered Vim user!"); - } else if (strstr(p, "iccf") != NULL) { - p = sponsor < 0 - ? N_("type :help sponsor<Enter> for information ") - : N_("type :help register<Enter> for information "); - } } if (mesg == NULL) { @@ -2803,7 +2790,7 @@ void intro_message(int colon) } if (*mesg != NUL) { - do_intro_line(row, mesg, 0); + do_intro_line(row, mesg, colon); } row++; @@ -2814,7 +2801,7 @@ void intro_message(int colon) } } -static void do_intro_line(int row, char *mesg, int attr) +static void do_intro_line(int row, char *mesg, bool colon) { int l; @@ -2827,7 +2814,12 @@ static void do_intro_line(int row, char *mesg, int attr) col = 0; } - grid_line_start(&default_grid, row); + ScreenGrid *grid = &default_grid; + if (!colon && ui_has(kUIMultigrid)) { + grid = &firstwin->w_grid; + } + + grid_line_start(grid, row); // Split up in parts to highlight <> items differently. for (char *p = mesg; *p != NUL; p += l) { for (l = 0; @@ -2836,7 +2828,7 @@ static void do_intro_line(int row, char *mesg, int attr) l += utfc_ptr2len(p + l) - 1; } assert(row <= INT_MAX && col <= INT_MAX); - col += grid_line_puts(col, p, l, *p == '<' ? HL_ATTR(HLF_8) : attr); + col += grid_line_puts(col, p, l, *p == '<' ? HL_ATTR(HLF_8) : 0); } grid_line_flush(); } diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua index 37cb70c725..2f43f8b32b 100644 --- a/src/nvim/vvars.lua +++ b/src/nvim/vvars.lua @@ -64,9 +64,10 @@ M.vars = { }, completed_item = { desc = [=[ - Dictionary containing the most recent |complete-items| after - |CompleteDone|. Empty if the completion failed, or after - leaving and re-entering insert mode. + Dictionary containing the |complete-items| for the most + recently completed word after |CompleteDone|. Empty if the + completion failed, or after leaving and re-entering insert + mode. Note: Plugins can modify the value to emulate the builtin |CompleteDone| event behavior. ]=], @@ -214,6 +215,7 @@ M.vars = { changed_window Is |v:true| if the event fired while changing window (or tab) on |DirChanged|. status Job status or exit code, -1 means "unknown". |TermClose| + reason Reason for completion being done. |CompleteDone| ]=], }, exception = { diff --git a/src/nvim/window.c b/src/nvim/window.c index e2c4524eaa..1a6c3f7263 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -133,6 +133,35 @@ static void log_frame_layout(frame_T *frame) } #endif +/// Check if the current window is allowed to move to a different buffer. +/// +/// @return If the window has 'winfixbuf', or this function will return false. +bool check_can_set_curbuf_disabled(void) +{ + if (curwin->w_p_wfb) { + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + return false; + } + + return true; +} + +/// Check if the current window is allowed to move to a different buffer. +/// +/// @param forceit If true, do not error. If false and 'winfixbuf' is enabled, error. +/// +/// @return If the window has 'winfixbuf', then forceit must be true +/// or this function will return false. +bool check_can_set_curbuf_forceit(int forceit) +{ + if (!forceit && curwin->w_p_wfb) { + emsg(_(e_winfixbuf_cannot_go_to_buffer)); + return false; + } + + return true; +} + /// @return the current window, unless in the cmdline window and "prevwin" is /// set, then return "prevwin". win_T *prevwin_curwin(void) @@ -455,9 +484,14 @@ newwindow: case 'H': case 'L': CHECK_CMDWIN; - win_totop(Prenum, - ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0) - | ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT)); + if (one_window(curwin)) { + beep_flush(); + } else { + const int dir = ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0) + | ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT); + + win_splitmove(curwin, Prenum, dir); + } break; // make all windows the same width and/or height @@ -592,7 +626,7 @@ wingotofile: ptr = xmemdupz(ptr, len); find_pattern_in_path(ptr, 0, len, true, Prenum == 0, - type, Prenum1, ACTION_SPLIT, 1, MAXLNUM); + type, Prenum1, ACTION_SPLIT, 1, MAXLNUM, false); xfree(ptr); curwin->w_set_curswant = true; break; @@ -701,16 +735,13 @@ static void cmd_with_count(char *cmd, char *bufp, size_t bufsize, int64_t Prenum } } -void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err) +void win_set_buf(win_T *win, buf_T *buf, Error *err) FUNC_ATTR_NONNULL_ALL { tabpage_T *tab = win_find_tabpage(win); // no redrawing and don't set the window title RedrawingDisabled++; - if (noautocmd) { - block_autocmds(); - } switchwin_T switchwin; if (switch_win_noblock(&switchwin, win, tab, true) == FAIL) { @@ -718,10 +749,23 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err) kErrorTypeException, "Failed to switch to window %d", win->handle); + goto cleanup; } try_start(); + + const int save_acd = p_acd; + if (!switchwin.sw_same_win) { + // Temporarily disable 'autochdir' when setting buffer in another window. + p_acd = false; + } + int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0); + + if (!switchwin.sw_same_win) { + p_acd = save_acd; + } + if (!try_end(err) && result == FAIL) { api_set_error(err, kErrorTypeException, @@ -729,14 +773,12 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err) buf->handle); } - // If window is not current, state logic will not validate its cursor. - // So do it now. - validate_cursor(); + // If window is not current, state logic will not validate its cursor. So do it now. + // Still needed if do_buffer returns FAIL (e.g: autocmds abort script after buffer was set). + validate_cursor(curwin); +cleanup: restore_win_noblock(&switchwin, true); - if (noautocmd) { - unblock_autocmds(); - } RedrawingDisabled--; } @@ -785,7 +827,8 @@ void ui_ext_win_position(win_T *wp, bool validate) row += row_off; col += col_off; if (c.bufpos.lnum >= 0) { - pos_T pos = { c.bufpos.lnum + 1, c.bufpos.col, 0 }; + int lnum = MIN(c.bufpos.lnum + 1, win->w_buffer->b_ml.ml_line_count); + pos_T pos = { lnum, c.bufpos.col, 0 }; int trow, tcol, tcolc, tcole; textpos2screenpos(win, &pos, &trow, &tcol, &tcolc, &tcole, true); row += trow - 1; @@ -903,19 +946,35 @@ void ui_ext_win_viewport(win_T *wp) } } -/// If "split_disallowed" is set give an error and return FAIL. +/// If "split_disallowed" is set, or "wp"'s buffer is closing, give an error and return FAIL. /// Otherwise return OK. -static int check_split_disallowed(void) +int check_split_disallowed(const win_T *wp) + FUNC_ATTR_NONNULL_ALL +{ + Error err = ERROR_INIT; + const bool ok = check_split_disallowed_err(wp, &err); + if (ERROR_SET(&err)) { + emsg(_(err.msg)); + api_clear_error(&err); + } + return ok ? OK : FAIL; +} + +/// Like `check_split_disallowed`, but set `err` to the (untranslated) error message on failure and +/// return false. Otherwise return true. +/// @see check_split_disallowed +bool check_split_disallowed_err(const win_T *wp, Error *err) + FUNC_ATTR_NONNULL_ALL { if (split_disallowed > 0) { - emsg(_("E242: Can't split a window while closing another")); - return FAIL; + api_set_error(err, kErrorTypeException, "E242: Can't split a window while closing another"); + return false; } - if (curwin->w_buffer->b_locked_split) { - emsg(_(e_cannot_split_window_when_closing_buffer)); - return FAIL; + if (wp->w_buffer->b_locked_split) { + api_set_error(err, kErrorTypeException, "%s", e_cannot_split_window_when_closing_buffer); + return false; } - return OK; + return true; } // split the current window, implements CTRL-W s and :split @@ -934,7 +993,7 @@ static int check_split_disallowed(void) // return FAIL for failure, OK otherwise int win_split(int size, int flags) { - if (check_split_disallowed() == FAIL) { + if (check_split_disallowed(curwin) == FAIL) { return FAIL; } @@ -958,14 +1017,19 @@ int win_split(int size, int flags) clear_snapshot(curtab, SNAP_HELP_IDX); } - return win_split_ins(size, flags, NULL, 0) == NULL ? FAIL : OK; + return win_split_ins(size, flags, NULL, 0, NULL) == NULL ? FAIL : OK; } /// When "new_wp" is NULL: split the current window in two. /// When "new_wp" is not NULL: insert this window at the far /// top/left/right/bottom. +/// When "to_flatten" is not NULL: flatten this frame before reorganising frames; +/// remains unflattened on failure. +/// +/// On failure, if "new_wp" was not NULL, no changes will have been made to the +/// window layout or sizes. /// @return NULL for failure, or pointer to new window -win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) +win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir, frame_T *to_flatten) { win_T *wp = new_wp; @@ -986,13 +1050,12 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) int need_status = 0; int new_size = size; - bool new_in_layout = (new_wp == NULL || new_wp->w_floating); bool vertical = flags & WSP_VERT; bool toplevel = flags & (WSP_TOP | WSP_BOT); // add a status line when p_ls == 1 and splitting the first window - if (one_nonfloat() && p_ls == 1 && oldwin->w_status_height == 0) { - if (oldwin->w_height <= p_wmh && new_in_layout) { + if (one_window(firstwin) && p_ls == 1 && oldwin->w_status_height == 0) { + if (oldwin->w_height <= p_wmh) { emsg(_(e_noroom)); return NULL; } @@ -1041,7 +1104,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) available = oldwin->w_frame->fr_width; needed += minwidth; } - if (available < needed && new_in_layout) { + if (available < needed) { emsg(_(e_noroom)); return NULL; } @@ -1121,7 +1184,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) available = oldwin->w_frame->fr_height; needed += minheight; } - if (available < needed && new_in_layout) { + if (available < needed) { emsg(_(e_noroom)); return NULL; } @@ -1191,13 +1254,13 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) if (new_wp == NULL) { wp = win_alloc(oldwin, false); } else { - win_append(oldwin, wp); + win_append(oldwin, wp, NULL); } } else { if (new_wp == NULL) { wp = win_alloc(oldwin->w_prev, false); } else { - win_append(oldwin->w_prev, wp); + win_append(oldwin->w_prev, wp, NULL); } } @@ -1211,13 +1274,37 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) // make the contents of the new window the same as the current one win_init(wp, curwin, flags); } else if (wp->w_floating) { - new_frame(wp); + ui_comp_remove_grid(&wp->w_grid_alloc); + if (ui_has(kUIMultigrid)) { + wp->w_pos_changed = true; + } else { + // No longer a float, a non-multigrid UI shouldn't draw it as such + ui_call_win_hide(wp->w_grid_alloc.handle); + win_free_grid(wp, true); + } + + // External windows are independent of tabpages, and may have been the curwin of others. + if (wp->w_config.external) { + FOR_ALL_TABS(tp) { + if (tp != curtab && tp->tp_curwin == wp) { + tp->tp_curwin = tp->tp_firstwin; + } + } + } + wp->w_floating = false; + new_frame(wp); + // non-floating window doesn't store float config or have a border. wp->w_config = WIN_CONFIG_INIT; CLEAR_FIELD(wp->w_border_adj); } + // Going to reorganize frames now, make sure they're flat. + if (to_flatten != NULL) { + frame_flatten(to_flatten); + } + bool before; frame_T *curfrp; @@ -1453,7 +1540,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir) if (!(flags & WSP_NOENTER)) { // make the new window the current window - win_enter_ext(wp, WEE_TRIGGER_NEW_AUTOCMDS | WEE_TRIGGER_ENTER_AUTOCMDS + win_enter_ext(wp, (new_wp == NULL ? WEE_TRIGGER_NEW_AUTOCMDS : 0) | WEE_TRIGGER_ENTER_AUTOCMDS | WEE_TRIGGER_LEAVE_AUTOCMDS); } if (vertical) { @@ -1690,7 +1777,7 @@ static void win_exchange(int Prenum) return; } - if (firstwin == curwin && lastwin_nofloating() == curwin) { + if (one_window(curwin)) { // just one window beep_flush(); return; @@ -1732,13 +1819,13 @@ static void win_exchange(int Prenum) if (wp->w_prev != curwin) { win_remove(curwin, NULL); frame_remove(curwin->w_frame); - win_append(wp->w_prev, curwin); + win_append(wp->w_prev, curwin, NULL); frame_insert(frp, curwin->w_frame); } if (wp != wp2) { win_remove(wp, NULL); frame_remove(wp->w_frame); - win_append(wp2, wp); + win_append(wp2, wp, NULL); if (frp2 == NULL) { frame_insert(wp->w_frame->fr_parent->fr_child, wp->w_frame); } else { @@ -1782,7 +1869,7 @@ static void win_rotate(bool upwards, int count) return; } - if (count <= 0 || (firstwin == curwin && lastwin_nofloating() == curwin)) { + if (count <= 0 || one_window(curwin)) { // nothing to do beep_flush(); return; @@ -1812,7 +1899,7 @@ static void win_rotate(bool upwards, int count) // find last frame and append removed window/frame after it for (; frp->fr_next != NULL; frp = frp->fr_next) {} - win_append(frp->fr_win, wp1); + win_append(frp->fr_win, wp1, NULL); frame_append(frp, wp1->w_frame); wp2 = frp->fr_win; // previously last window @@ -1827,7 +1914,7 @@ static void win_rotate(bool upwards, int count) assert(frp->fr_parent->fr_child); // append the removed window/frame before the first in the list - win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1); + win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1, NULL); frame_insert(frp->fr_parent->fr_child, frp); } @@ -1856,48 +1943,59 @@ static void win_rotate(bool upwards, int count) redraw_all_later(UPD_NOT_VALID); } -// Move the current window to the very top/bottom/left/right of the screen. -static void win_totop(int size, int flags) +/// Move "wp" into a new split in a given direction, possibly relative to the +/// current window. +/// "wp" must be valid in the current tabpage. +/// Returns FAIL for failure, OK otherwise. +int win_splitmove(win_T *wp, int size, int flags) { int dir = 0; - int height = curwin->w_height; + int height = wp->w_height; - if (firstwin == curwin && lastwin_nofloating() == curwin) { - beep_flush(); - return; + if (one_window(wp)) { + return OK; // nothing to do } - if (is_aucmd_win(curwin)) { - return; + if (is_aucmd_win(wp) || check_split_disallowed(wp) == FAIL) { + return FAIL; } - if (check_split_disallowed() == FAIL) { - return; + + frame_T *unflat_altfr = NULL; + if (wp->w_floating) { + win_remove(wp, NULL); + } else { + // Remove the window and frame from the tree of frames. Don't flatten any + // frames yet so we can restore things if win_split_ins fails. + winframe_remove(wp, &dir, NULL, &unflat_altfr); + assert(unflat_altfr != NULL); + win_remove(wp, NULL); + last_status(false); // may need to remove last status line + win_comp_pos(); // recompute window positions } - if (curwin->w_floating) { - ui_comp_remove_grid(&curwin->w_grid_alloc); - if (ui_has(kUIMultigrid)) { - curwin->w_pos_changed = true; - } else { - // No longer a float, a non-multigrid UI shouldn't draw it as such - ui_call_win_hide(curwin->w_grid_alloc.handle); - win_free_grid(curwin, true); + // Split a window on the desired side and put "wp" there. + if (win_split_ins(size, flags, wp, dir, unflat_altfr) == NULL) { + if (!wp->w_floating) { + assert(unflat_altfr != NULL); + // win_split_ins doesn't change sizes or layout if it fails to insert an + // existing window, so just undo winframe_remove. + winframe_restore(wp, dir, unflat_altfr); } - } else { - // Remove the window and frame from the tree of frames. - winframe_remove(curwin, &dir, NULL); + win_append(wp->w_prev, wp, NULL); + return FAIL; } - win_remove(curwin, NULL); - last_status(false); // may need to remove last status line - win_comp_pos(); // recompute window positions - // Split a window on the desired side and put the window there. - win_split_ins(size, flags, curwin, dir); - if (!(flags & WSP_VERT)) { - win_setheight(height); + // If splitting horizontally, try to preserve height. + // Note that win_split_ins autocommands may have immediately closed "wp", or made it floating! + if (size == 0 && !(flags & WSP_VERT) && win_valid(wp) && !wp->w_floating) { + win_setheight_win(height, wp); if (p_ea) { - win_equal(curwin, true, 'v'); + // Equalize windows. Note that win_split_ins autocommands may have + // made a window other than "wp" current. + win_equal(curwin, curwin == wp, 'v'); } } + + return OK; } // Move window "win1" to below/right of "win2" and make "win1" the current @@ -1955,7 +2053,7 @@ void win_move_after(win_T *win1, win_T *win2) } win_remove(win1, NULL); frame_remove(win1->w_frame); - win_append(win2, win1); + win_append(win2, win1, NULL); frame_append(win2->w_frame, win1->w_frame); win_comp_pos(); // recompute w_winrow for all windows @@ -2369,6 +2467,7 @@ void win_init_empty(win_T *wp) wp->w_topline = 1; wp->w_topfill = 0; wp->w_botline = 2; + wp->w_valid = 0; wp->w_s = &wp->w_buffer->b_s; } @@ -2434,37 +2533,13 @@ bool last_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT } /// Check if "win" is the only non-floating window in the current tabpage. -bool one_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (win->w_floating) { - return false; - } - - bool seen_one = false; - - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (!wp->w_floating) { - if (seen_one) { - return false; - } - seen_one = true; - } - } - return true; -} - -/// Like ONE_WINDOW but only considers non-floating windows -bool one_nonfloat(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT -{ - return firstwin->w_next == NULL || firstwin->w_next->w_floating; -} - -/// if wp is the last non-floating window /// -/// always false for a floating window -bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +/// This should be used in place of ONE_WINDOW when necessary, +/// with "firstwin" or the affected window as argument depending on the situation. +bool one_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return wp != NULL && firstwin == wp && !(wp->w_next && !wp->w_floating); + assert(!firstwin->w_floating); + return firstwin == win && (win->w_next == NULL || win->w_next->w_floating); } /// Check if floating windows in the current tab can be closed. @@ -2509,7 +2584,7 @@ bool can_close_in_cmdwin(win_T *win, Error *err) /// @param prev_curtab previous tabpage that will be closed if "win" is the /// last window in the tabpage /// -/// @return true when the window was closed already. +/// @return false if there are other windows and nothing is done, true otherwise. static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev_curtab) FUNC_ATTR_NONNULL_ARG(1) { @@ -2533,10 +2608,6 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev // that below. goto_tabpage_tp(alt_tabpage(), false, true); - // save index for tabclosed event - char prev_idx[NUMBUFLEN]; - snprintf(prev_idx, NUMBUFLEN, "%i", tabpage_index(prev_curtab)); - // Safety check: Autocommands may have closed the window when jumping // to the other tab page. if (valid_tabpage(prev_curtab) && prev_curtab->tp_firstwin == win) { @@ -2630,6 +2701,9 @@ int win_close(win_T *win, bool free_buf, bool force) return FAIL; } } + if (!win_valid_any_tab(win)) { + return FAIL; // window already closed by autocommands + } } else { emsg(e_floatonly); return FAIL; @@ -2714,10 +2788,8 @@ int win_close(win_T *win, bool free_buf, bool force) win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, true); - if (only_one_window() && win_valid(win) && win->w_buffer == NULL - && (last_window(win) || curtab != prev_curtab - || close_last_window_tabpage(win, free_buf, prev_curtab)) - && !win->w_floating) { + if (win_valid(win) && win->w_buffer == NULL + && !win->w_floating && last_window(win)) { // Autocommands have closed all windows, quit now. Restore // curwin->w_buffer, otherwise writing ShaDa file may fail. if (curwin->w_buffer == NULL) { @@ -2729,10 +2801,7 @@ int win_close(win_T *win, bool free_buf, bool force) if (curtab != prev_curtab && win_valid_any_tab(win) && win->w_buffer == NULL) { // Need to close the window anyway, since the buffer is NULL. - // Don't trigger autocmds with a NULL buffer. - block_autocmds(); win_close_othertab(win, false, prev_curtab); - unblock_autocmds(); return FAIL; } @@ -2759,13 +2828,10 @@ int win_close(win_T *win, bool free_buf, bool force) ui_comp_remove_grid(&win->w_grid_alloc); assert(first_tabpage != NULL); // suppress clang "Dereference of NULL pointer" if (win->w_config.external) { - for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) { - if (tp == curtab) { - continue; - } - if (tp->tp_curwin == win) { + FOR_ALL_TABS(tp) { + if (tp != curtab && tp->tp_curwin == win) { // NB: an autocmd can still abort the closing of this window, - // bur carring out this change anyway shouldn't be a catastrophe. + // but carrying out this change anyway shouldn't be a catastrophe. tp->tp_curwin = tp->tp_firstwin; } } @@ -2805,7 +2871,8 @@ int win_close(win_T *win, bool free_buf, bool force) if (wp == curwin) { break; } - if (!wp->w_p_pvw && !bt_quickfix(wp->w_buffer)) { + if (!wp->w_p_pvw && !bt_quickfix(wp->w_buffer) + && !(wp->w_floating && !wp->w_config.focusable)) { curwin = wp; break; } @@ -2816,7 +2883,7 @@ int win_close(win_T *win, bool free_buf, bool force) // The cursor position may be invalid if the buffer changed after last // using the window. - check_cursor(); + check_cursor(curwin); } if (!was_floating) { @@ -2906,10 +2973,14 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) } // Fire WinClosed just before starting to free window-related resources. - do_autocmd_winclosed(win); - // autocmd may have freed the window already. - if (!win_valid_any_tab(win)) { - return; + // If the buffer is NULL, it isn't safe to trigger autocommands, + // and win_close() should have already triggered WinClosed. + if (win->w_buffer != NULL) { + do_autocmd_winclosed(win); + // autocmd may have freed the window already. + if (!win_valid_any_tab(win)) { + return; + } } if (win->w_buffer != NULL) { @@ -3006,24 +3077,11 @@ static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp) if (!win->w_floating) { // Remove the window and its frame from the tree of frames. frame_T *frp = win->w_frame; - wp = winframe_remove(win, dirp, tp); + wp = winframe_remove(win, dirp, tp, NULL); xfree(frp); } else { *dirp = 'h'; // Dummy value. - if (tp == NULL) { - if (win_valid(prevwin) && prevwin != win) { - wp = prevwin; - } else { - wp = firstwin; - } - } else { - assert(tp != curtab); - if (tabpage_win_valid(tp, tp->tp_prevwin) && tp->tp_prevwin != win) { - wp = tp->tp_prevwin; - } else { - wp = tp->tp_firstwin; - } - } + wp = win_float_find_altwin(win, tp); } win_free(win, tp); @@ -3087,9 +3145,64 @@ void win_free_all(void) /// /// @param dirp set to 'v' or 'h' for direction if 'ea' /// @param tp tab page "win" is in, NULL for current +/// @param unflat_altfr if not NULL, set to pointer of frame that got +/// the space, and it is not flattened /// /// @return a pointer to the window that got the freed up space. -win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) +win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp, frame_T **unflat_altfr) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + frame_T *altfr; + win_T *wp = winframe_find_altwin(win, dirp, tp, &altfr); + if (wp == NULL) { + return NULL; + } + + frame_T *frp_close = win->w_frame; + + // Save the position of the containing frame (which will also contain the + // altframe) before we remove anything, to recompute window positions later. + const win_T *const topleft = frame2win(frp_close->fr_parent); + int row = topleft->w_winrow; + int col = topleft->w_wincol; + + // Remove this frame from the list of frames. + frame_remove(frp_close); + + if (*dirp == 'v') { + frame_new_height(altfr, altfr->fr_height + frp_close->fr_height, + altfr == frp_close->fr_next, false); + } else { + assert(*dirp == 'h'); + frame_new_width(altfr, altfr->fr_width + frp_close->fr_width, + altfr == frp_close->fr_next, false); + } + + // If the altframe wasn't adjacent and left/above, resizing it will have + // changed window positions within the parent frame. Recompute them. + if (altfr != frp_close->fr_prev) { + frame_comp_pos(frp_close->fr_parent, &row, &col); + } + + if (unflat_altfr == NULL) { + frame_flatten(altfr); + } else { + *unflat_altfr = altfr; + } + + return wp; +} + +/// Find the window that will get the freed space from a call to `winframe_remove`. +/// Makes no changes to the window layout. +/// +/// @param dirp set to 'v' or 'h' for the direction where "altfr" will be resized +/// to fill the space +/// @param tp tab page "win" is in, NULL for current +/// @param altfr if not NULL, set to pointer of frame that will get the space +/// +/// @return a pointer to the window that will get the freed up space. +win_T *winframe_find_altwin(win_T *win, int *dirp, tabpage_T *tp, frame_T **altfr) FUNC_ATTR_NONNULL_ARG(1, 2) { assert(tp == NULL || tp != curtab); @@ -3101,13 +3214,10 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) frame_T *frp_close = win->w_frame; - // Remove the window from its frame. + // Find the window and frame that gets the space. frame_T *frp2 = win_altframe(win, tp); win_T *wp = frame2win(frp2); - // Remove this frame from the list of frames. - frame_remove(frp_close); - if (frp_close->fr_parent->fr_layout == FR_COL) { // When 'winfixheight' is set, try to find another frame in the column // (as close to the closed frame as possible) to distribute the height @@ -3134,8 +3244,6 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) } } } - frame_new_height(frp2, frp2->fr_height + frp_close->fr_height, - frp2 == frp_close->fr_next, false); *dirp = 'v'; } else { // When 'winfixwidth' is set, try to find another frame in the column @@ -3163,70 +3271,123 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) } } } - frame_new_width(frp2, frp2->fr_width + frp_close->fr_width, - frp2 == frp_close->fr_next, false); *dirp = 'h'; } - // If rows/columns go to a window below/right its positions need to be - // updated. Can only be done after the sizes have been updated. - if (frp2 == frp_close->fr_next) { - int row = win->w_winrow; - int col = win->w_wincol; + assert(wp != win && frp2 != frp_close); + if (altfr != NULL) { + *altfr = frp2; + } + + return wp; +} - frame_comp_pos(frp2, &row, &col); +/// Flatten "frp" into its parent frame if it's the only child, also merging its +/// list with the grandparent if they share the same layout. +/// Frees "frp" if flattened; also "frp->fr_parent" if it has the same layout. +static void frame_flatten(frame_T *frp) + FUNC_ATTR_NONNULL_ALL +{ + if (frp->fr_next != NULL || frp->fr_prev != NULL) { + return; } - if (frp2->fr_next == NULL && frp2->fr_prev == NULL) { - // There is no other frame in this list, move its info to the parent - // and remove it. - frp2->fr_parent->fr_layout = frp2->fr_layout; - frp2->fr_parent->fr_child = frp2->fr_child; - frame_T *frp; - FOR_ALL_FRAMES(frp, frp2->fr_child) { - frp->fr_parent = frp2->fr_parent; - } - frp2->fr_parent->fr_win = frp2->fr_win; - if (frp2->fr_win != NULL) { - frp2->fr_win->w_frame = frp2->fr_parent; + // There is no other frame in this list, move its info to the parent + // and remove it. + frp->fr_parent->fr_layout = frp->fr_layout; + frp->fr_parent->fr_child = frp->fr_child; + frame_T *frp2; + FOR_ALL_FRAMES(frp2, frp->fr_child) { + frp2->fr_parent = frp->fr_parent; + } + frp->fr_parent->fr_win = frp->fr_win; + if (frp->fr_win != NULL) { + frp->fr_win->w_frame = frp->fr_parent; + } + frp2 = frp->fr_parent; + if (topframe->fr_child == frp) { + topframe->fr_child = frp2; + } + xfree(frp); + + frp = frp2->fr_parent; + if (frp != NULL && frp->fr_layout == frp2->fr_layout) { + // The frame above the parent has the same layout, have to merge + // the frames into this list. + if (frp->fr_child == frp2) { + frp->fr_child = frp2->fr_child; + } + assert(frp2->fr_child); + frp2->fr_child->fr_prev = frp2->fr_prev; + if (frp2->fr_prev != NULL) { + frp2->fr_prev->fr_next = frp2->fr_child; + } + for (frame_T *frp3 = frp2->fr_child;; frp3 = frp3->fr_next) { + frp3->fr_parent = frp; + if (frp3->fr_next == NULL) { + frp3->fr_next = frp2->fr_next; + if (frp2->fr_next != NULL) { + frp2->fr_next->fr_prev = frp3; + } + break; + } } - frp = frp2->fr_parent; if (topframe->fr_child == frp2) { topframe->fr_child = frp; } xfree(frp2); + } +} - frp2 = frp->fr_parent; - if (frp2 != NULL && frp2->fr_layout == frp->fr_layout) { - // The frame above the parent has the same layout, have to merge - // the frames into this list. - if (frp2->fr_child == frp) { - frp2->fr_child = frp->fr_child; - } - assert(frp->fr_child); - frp->fr_child->fr_prev = frp->fr_prev; - if (frp->fr_prev != NULL) { - frp->fr_prev->fr_next = frp->fr_child; - } - frame_T *frp3; - for (frp3 = frp->fr_child;; frp3 = frp3->fr_next) { - frp3->fr_parent = frp2; - if (frp3->fr_next == NULL) { - frp3->fr_next = frp->fr_next; - if (frp->fr_next != NULL) { - frp->fr_next->fr_prev = frp3; - } - break; - } - } - if (topframe->fr_child == frp) { - topframe->fr_child = frp2; - } - xfree(frp); +/// Undo changes from a prior call to winframe_remove, also restoring lost +/// vertical separators and statuslines, and changed window positions for +/// windows within "unflat_altfr". +/// Caller must ensure no other changes were made to the layout or window sizes! +void winframe_restore(win_T *wp, int dir, frame_T *unflat_altfr) + FUNC_ATTR_NONNULL_ALL +{ + frame_T *frp = wp->w_frame; + + // Put "wp"'s frame back where it was. + if (frp->fr_prev != NULL) { + frame_append(frp->fr_prev, frp); + } else { + frame_insert(frp->fr_next, frp); + } + + // Vertical separators to the left may have been lost. Restore them. + if (wp->w_vsep_width == 0 && frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) { + frame_add_vsep(frp->fr_prev); + } + + // Statuslines or horizontal separators above may have been lost. Restore them. + if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) { + if (global_stl_height() == 0 && wp->w_status_height == 0) { + frame_add_statusline(frp->fr_prev); + } else if (wp->w_hsep_height == 0) { + frame_add_hsep(frp->fr_prev); } } - return wp; + // Restore the lost room that was redistributed to the altframe. Also + // adjusts window sizes to fit restored statuslines/separators, if needed. + if (dir == 'v') { + frame_new_height(unflat_altfr, unflat_altfr->fr_height - frp->fr_height, + unflat_altfr == frp->fr_next, false); + } else if (dir == 'h') { + frame_new_width(unflat_altfr, unflat_altfr->fr_width - frp->fr_width, + unflat_altfr == frp->fr_next, false); + } + + // Recompute window positions within the parent frame to restore them. + // Positions were unchanged if the altframe was adjacent and left/above. + if (unflat_altfr != frp->fr_prev) { + const win_T *const topleft = frame2win(frp->fr_parent); + int row = topleft->w_winrow; + int col = topleft->w_wincol; + + frame_comp_pos(frp->fr_parent, &row, &col); + } } /// If 'splitbelow' or 'splitright' is set, the space goes above or to the left @@ -3792,7 +3953,7 @@ void close_others(int message, int forceit) return; } - if (one_nonfloat() && !lastwin->w_floating) { + if (one_window(firstwin) && !lastwin->w_floating) { if (message && !autocmd_busy) { msg(_(m_onlyone), 0); @@ -4331,7 +4492,7 @@ static void tabpage_check_windows(tabpage_T *old_curtab) if (wp->w_floating) { if (wp->w_config.external) { win_remove(wp, old_curtab); - win_append(lastwin_nofloating(), wp); + win_append(lastwin_nofloating(), wp, NULL); } else { ui_comp_remove_grid(&wp->w_grid_alloc); } @@ -4767,19 +4928,15 @@ static void win_enter_ext(win_T *const wp, const int flags) if (wp->w_buffer != curbuf) { buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP); } - if (!curwin_invalid) { prevwin = curwin; // remember for CTRL-W p curwin->w_redr_status = true; - } else if (wp == prevwin) { - prevwin = NULL; // don't want it to be the new curwin } - curwin = wp; curbuf = wp->w_buffer; - check_cursor(); - if (!virtual_active()) { + check_cursor(curwin); + if (!virtual_active(curwin)) { curwin->w_cursor.coladd = 0; } if (*p_spk == 'c') { @@ -4790,7 +4947,7 @@ static void win_enter_ext(win_T *const wp, const int flags) win_fix_cursor(get_real_state() & (MODE_NORMAL|MODE_CMDLINE|MODE_TERMINAL)); } - fix_current_dir(); + win_fix_current_dir(); entering_window(curwin); // Careful: autocommands may close the window and make "wp" invalid @@ -4843,7 +5000,7 @@ static void win_enter_ext(win_T *const wp, const int flags) } /// Used after making another window the current one: change directory if needed. -void fix_current_dir(void) +void win_fix_current_dir(void) { // New directory is either the local directory of the window, tab or NULL. char *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->tp_localdir; @@ -4971,7 +5128,14 @@ win_T *win_alloc(win_T *after, bool hidden) block_autocmds(); // link the window in the window list if (!hidden) { - win_append(after, new_wp); + tabpage_T *tp = NULL; + if (after) { + tp = win_find_tabpage(after); + if (tp == curtab) { + tp = NULL; + } + } + win_append(after, new_wp, tp); } new_wp->w_wincol = 0; @@ -5141,21 +5305,29 @@ void win_free_grid(win_T *wp, bool reinit) } } -// Append window "wp" in the window list after window "after". -void win_append(win_T *after, win_T *wp) +/// Append window "wp" in the window list after window "after". +/// +/// @param tp tab page "win" (and "after", if not NULL) is in, NULL for current +void win_append(win_T *after, win_T *wp, tabpage_T *tp) + FUNC_ATTR_NONNULL_ARG(2) { + assert(tp == NULL || tp != curtab); + + win_T **first = tp == NULL ? &firstwin : &tp->tp_firstwin; + win_T **last = tp == NULL ? &lastwin : &tp->tp_lastwin; + // after NULL is in front of the first - win_T *before = after == NULL ? firstwin : after->w_next; + win_T *before = after == NULL ? *first : after->w_next; wp->w_next = before; wp->w_prev = after; if (after == NULL) { - firstwin = wp; + *first = wp; } else { after->w_next = wp; } if (before == NULL) { - lastwin = wp; + *last = wp; } else { before->w_prev = wp; } @@ -5737,10 +5909,6 @@ static void frame_setheight(frame_T *curfrp, int height) if (curfrp->fr_parent == NULL) { // topframe: can only change the command line height - // Avoid doing so with external messages. - if (ui_has(kUIMessages)) { - return; - } if (height > ROWS_AVAIL) { // If height is greater than the available space, try to create space for // the frame by reducing 'cmdheight' if possible, while making sure @@ -6079,12 +6247,6 @@ const char *did_set_winminwidth(optset_T *args FUNC_ATTR_UNUSED) void win_drag_status_line(win_T *dragwin, int offset) { frame_T *fr = dragwin->w_frame; - - // Avoid changing command line height with external messages. - if (fr->fr_next == NULL && ui_has(kUIMessages)) { - return; - } - frame_T *curfr = fr; if (fr != topframe) { // more than one window fr = fr->fr_parent; @@ -6487,7 +6649,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) } } else if (sline > 0) { while (sline > 0 && lnum > 1) { - hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); + hasFolding(wp, lnum, &lnum, NULL); if (lnum == 1) { // first line in buffer is folded line_size = 1; @@ -6507,7 +6669,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) if (sline < 0) { // Line we want at top would go off top of screen. Use next // line instead. - hasFoldingWin(wp, lnum, NULL, &lnum, true, NULL); + hasFolding(wp, lnum, NULL, &lnum); lnum++; wp->w_wrow -= line_size + sline; } else if (sline > 0) { @@ -6548,7 +6710,7 @@ void win_set_inner_size(win_T *wp, bool valid_cursor) if (wp == curwin && *p_spk == 'c') { // w_wrow needs to be valid. When setting 'laststatus' this may // call win_new_height() recursively. - validate_cursor(); + validate_cursor(curwin); } if (wp->w_height_inner != prev_height) { return; // Recursive call already changed the size, bail out. @@ -6590,6 +6752,13 @@ void win_set_inner_size(win_T *wp, bool valid_cursor) wp->w_width_outer = (wp->w_width_inner + win_border_width(wp)); wp->w_winrow_off = wp->w_border_adj[0] + wp->w_winbar_height; wp->w_wincol_off = wp->w_border_adj[3]; + + if (ui_has(kUIMultigrid)) { + ui_call_win_viewport_margins(wp->w_grid_alloc.handle, wp->handle, + wp->w_winrow_off, wp->w_border_adj[2], + wp->w_wincol_off, wp->w_border_adj[1]); + } + wp->w_redr_status = true; } @@ -7058,7 +7227,7 @@ int global_stl_height(void) /// @param morewin pretend there are two or more windows if true. int last_stl_height(bool morewin) { - return (p_ls > 1 || (p_ls == 1 && (morewin || !one_nonfloat()))) ? STATUS_HEIGHT : 0; + return (p_ls > 1 || (p_ls == 1 && (morewin || !one_window(firstwin)))) ? STATUS_HEIGHT : 0; } /// Return the minimal number of rows that is needed on the screen to display diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c index 8fe0315230..e3ca0ff139 100644 --- a/src/nvim/winfloat.c +++ b/src/nvim/winfloat.c @@ -6,7 +6,9 @@ #include "klib/kvec.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" #include "nvim/ascii_defs.h" +#include "nvim/autocmd.h" #include "nvim/buffer_defs.h" #include "nvim/drawscreen.h" #include "nvim/globals.h" @@ -17,6 +19,7 @@ #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/optionstr.h" #include "nvim/pos_defs.h" #include "nvim/strings.h" @@ -41,7 +44,24 @@ win_T *win_new_float(win_T *wp, bool last, WinConfig fconfig, Error *err) { if (wp == NULL) { - wp = win_alloc(last ? lastwin : lastwin_nofloating(), false); + tabpage_T *tp = NULL; + win_T *tp_last = last ? lastwin : lastwin_nofloating(); + if (fconfig.window != 0) { + assert(!last); + win_T *parent_wp = find_window_by_handle(fconfig.window, err); + if (!parent_wp) { + return NULL; + } + tp = win_find_tabpage(parent_wp); + if (!tp) { + return NULL; + } + tp_last = tp->tp_lastwin; + while (tp_last->w_floating && tp_last->w_prev) { + tp_last = tp_last->w_prev; + } + } + wp = win_alloc(tp_last, false); win_init(wp, curwin, 0); } else { assert(!last); @@ -55,13 +75,26 @@ win_T *win_new_float(win_T *wp, bool last, WinConfig fconfig, Error *err) api_set_error(err, kErrorTypeException, "Cannot change window from different tabpage into float"); return NULL; + } else if (cmdwin_win != NULL && !cmdwin_win->w_floating) { + // cmdwin can't become the only non-float. Check for others. + bool other_nonfloat = false; + for (win_T *wp2 = firstwin; wp2 != NULL && !wp2->w_floating; wp2 = wp2->w_next) { + if (wp2 != wp && wp2 != cmdwin_win) { + other_nonfloat = true; + break; + } + } + if (!other_nonfloat) { + api_set_error(err, kErrorTypeException, "%s", e_cmdwin); + return NULL; + } } int dir; - winframe_remove(wp, &dir, NULL); + winframe_remove(wp, &dir, NULL, NULL); XFREE_CLEAR(wp->w_frame); win_comp_pos(); // recompute window positions win_remove(wp, NULL); - win_append(lastwin_nofloating(), wp); + win_append(lastwin_nofloating(), wp, NULL); } wp->w_floating = true; wp->w_status_height = 0; @@ -208,7 +241,7 @@ void win_config_float(win_T *wp, WinConfig fconfig) row += row_off; col += col_off; if (wp->w_config.bufpos.lnum >= 0) { - pos_T pos = { wp->w_config.bufpos.lnum + 1, + pos_T pos = { MIN(wp->w_config.bufpos.lnum + 1, parent->w_buffer->b_ml.ml_line_count), wp->w_config.bufpos.col, 0 }; int trow, tcol, tcolc, tcole; textpos2screenpos(parent, &pos, &trow, &tcol, &tcolc, &tcole, true); @@ -306,3 +339,70 @@ win_T *win_float_find_preview(void) } return NULL; } + +/// Select an alternative window to `win` (assumed floating) in tabpage `tp`. +/// +/// Useful for finding a window to switch to if `win` is the current window, but is then closed or +/// moved to a different tabpage. +/// +/// @param tp `win`'s original tabpage, or NULL for current. +win_T *win_float_find_altwin(const win_T *win, const tabpage_T *tp) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (tp == NULL) { + return (win_valid(prevwin) && prevwin != win) ? prevwin : firstwin; + } + + assert(tp != curtab); + return (tabpage_win_valid(tp, tp->tp_prevwin) && tp->tp_prevwin != win) ? tp->tp_prevwin + : tp->tp_firstwin; +} + +/// create a floating preview window. +/// +/// @param[in] bool enter floating window. +/// @param[in] bool create a new buffer for window. +/// +/// @return win_T +win_T *win_float_create(bool enter, bool new_buf) +{ + WinConfig config = WIN_CONFIG_INIT; + config.col = curwin->w_wcol; + config.row = curwin->w_wrow; + config.relative = kFloatRelativeEditor; + config.focusable = false; + config.anchor = 0; // NW + config.noautocmd = true; + config.hide = true; + config.style = kWinStyleMinimal; + Error err = ERROR_INIT; + + block_autocmds(); + win_T *wp = win_new_float(NULL, false, config, &err); + if (!wp) { + unblock_autocmds(); + return NULL; + } + + if (new_buf) { + Buffer b = nvim_create_buf(false, true, &err); + if (!b) { + win_remove(wp, NULL); + win_free(wp, NULL); + unblock_autocmds(); + return NULL; + } + buf_T *buf = find_buffer_by_handle(b, &err); + buf->b_p_bl = false; // unlist + set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL, 0, kOptReqBuf, + buf); + win_set_buf(wp, buf, &err); + } + unblock_autocmds(); + wp->w_p_diff = false; + wp->w_float_is_info = true; + if (enter) { + win_enter(wp, false); + } + return wp; +} diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg index aaac6e4426..50cee638af 100644 --- a/src/uncrustify.cfg +++ b/src/uncrustify.cfg @@ -1,4 +1,4 @@ -# Uncrustify-0.78.1_f +# Uncrustify-0.79.0_f # # General options @@ -61,7 +61,7 @@ enable_digraphs = false # true/false processing_cmt_as_regex = false # true/false # Add or remove the UTF-8 BOM (recommend 'remove'). -utf8_bom = remove # ignore/add/remove/force/not_defined +utf8_bom = remove # ignore/add/remove/force # If the file contains bytes with values between 128 and 255, but is not # UTF-8, then output as UTF-8. @@ -76,238 +76,235 @@ utf8_force = false # true/false # Add or remove space around non-assignment symbolic operators ('+', '/', '%', # '<<', and so forth). -sp_arith = ignore # ignore/add/remove/force/not_defined +sp_arith = ignore # ignore/add/remove/force # Add or remove space around arithmetic operators '+' and '-'. # # Overrides sp_arith. -sp_arith_additive = force # ignore/add/remove/force/not_defined +sp_arith_additive = force # ignore/add/remove/force # Add or remove space around assignment operator '=', '+=', etc. -sp_assign = force # ignore/add/remove/force/not_defined +sp_assign = force # ignore/add/remove/force # Add or remove space around '=' in C++11 lambda capture specifications. # # Overrides sp_assign. -sp_cpp_lambda_assign = ignore # ignore/add/remove/force/not_defined +sp_cpp_lambda_assign = ignore # ignore/add/remove/force # Add or remove space after the capture specification of a C++11 lambda when # an argument list is present, as in '[] <here> (int x){ ... }'. -sp_cpp_lambda_square_paren = ignore # ignore/add/remove/force/not_defined +sp_cpp_lambda_square_paren = ignore # ignore/add/remove/force # Add or remove space after the capture specification of a C++11 lambda with # no argument list is present, as in '[] <here> { ... }'. -sp_cpp_lambda_square_brace = ignore # ignore/add/remove/force/not_defined +sp_cpp_lambda_square_brace = ignore # ignore/add/remove/force # Add or remove space after the opening parenthesis and before the closing # parenthesis of a argument list of a C++11 lambda, as in # '[]( <here> ){ ... }' # with an empty list. -sp_cpp_lambda_argument_list_empty = ignore # ignore/add/remove/force/not_defined +sp_cpp_lambda_argument_list_empty = ignore # ignore/add/remove/force # Add or remove space after the opening parenthesis and before the closing # parenthesis of a argument list of a C++11 lambda, as in # '[]( <here> int x <here> ){ ... }'. -sp_cpp_lambda_argument_list = ignore # ignore/add/remove/force/not_defined +sp_cpp_lambda_argument_list = ignore # ignore/add/remove/force # Add or remove space after the argument list of a C++11 lambda, as in # '[](int x) <here> { ... }'. -sp_cpp_lambda_paren_brace = ignore # ignore/add/remove/force/not_defined +sp_cpp_lambda_paren_brace = ignore # ignore/add/remove/force # Add or remove space between a lambda body and its call operator of an # immediately invoked lambda, as in '[]( ... ){ ... } <here> ( ... )'. -sp_cpp_lambda_fparen = ignore # ignore/add/remove/force/not_defined +sp_cpp_lambda_fparen = ignore # ignore/add/remove/force # Add or remove space around assignment operator '=' in a prototype. # # If set to ignore, use sp_assign. -sp_assign_default = ignore # ignore/add/remove/force/not_defined +sp_assign_default = ignore # ignore/add/remove/force # Add or remove space before assignment operator '=', '+=', etc. # # Overrides sp_assign. -sp_before_assign = ignore # ignore/add/remove/force/not_defined +sp_before_assign = ignore # ignore/add/remove/force # Add or remove space after assignment operator '=', '+=', etc. # # Overrides sp_assign. -sp_after_assign = ignore # ignore/add/remove/force/not_defined +sp_after_assign = ignore # ignore/add/remove/force # Add or remove space in 'enum {'. # # Default: add -sp_enum_brace = force # ignore/add/remove/force/not_defined +sp_enum_brace = force # ignore/add/remove/force # Add or remove space in 'NS_ENUM ('. -sp_enum_paren = ignore # ignore/add/remove/force/not_defined +sp_enum_paren = ignore # ignore/add/remove/force # Add or remove space around assignment '=' in enum. -sp_enum_assign = ignore # ignore/add/remove/force/not_defined +sp_enum_assign = ignore # ignore/add/remove/force # Add or remove space before assignment '=' in enum. # # Overrides sp_enum_assign. -sp_enum_before_assign = ignore # ignore/add/remove/force/not_defined +sp_enum_before_assign = ignore # ignore/add/remove/force # Add or remove space after assignment '=' in enum. # # Overrides sp_enum_assign. -sp_enum_after_assign = force # ignore/add/remove/force/not_defined +sp_enum_after_assign = force # ignore/add/remove/force # Add or remove space around assignment ':' in enum. -sp_enum_colon = ignore # ignore/add/remove/force/not_defined +sp_enum_colon = ignore # ignore/add/remove/force # Add or remove space around preprocessor '##' concatenation operator. # # Default: add -sp_pp_concat = remove # ignore/add/remove/force/not_defined +sp_pp_concat = remove # ignore/add/remove/force # Add or remove space after preprocessor '#' stringify operator. # Also affects the '#@' charizing operator. -sp_pp_stringify = remove # ignore/add/remove/force/not_defined +sp_pp_stringify = remove # ignore/add/remove/force # Add or remove space before preprocessor '#' stringify operator # as in '#define x(y) L#y'. -sp_before_pp_stringify = ignore # ignore/add/remove/force/not_defined +sp_before_pp_stringify = ignore # ignore/add/remove/force # Add or remove space around boolean operators '&&' and '||'. -sp_bool = force # ignore/add/remove/force/not_defined +sp_bool = force # ignore/add/remove/force # Add or remove space around compare operator '<', '>', '==', etc. -sp_compare = force # ignore/add/remove/force/not_defined +sp_compare = force # ignore/add/remove/force # Add or remove space inside '(' and ')'. -sp_inside_paren = remove # ignore/add/remove/force/not_defined +sp_inside_paren = remove # ignore/add/remove/force # Add or remove space between nested parentheses, i.e. '((' vs. ') )'. -sp_paren_paren = remove # ignore/add/remove/force/not_defined +sp_paren_paren = remove # ignore/add/remove/force # Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('. -sp_cparen_oparen = remove # ignore/add/remove/force/not_defined - -# Whether to balance spaces inside nested parentheses. -sp_balance_nested_parens = false # true/false +sp_cparen_oparen = remove # ignore/add/remove/force # Add or remove space between ')' and '{'. -sp_paren_brace = ignore # ignore/add/remove/force/not_defined +sp_paren_brace = ignore # ignore/add/remove/force # Add or remove space between nested braces, i.e. '{{' vs. '{ {'. -sp_brace_brace = ignore # ignore/add/remove/force/not_defined +sp_brace_brace = ignore # ignore/add/remove/force # Add or remove space before pointer star '*'. -sp_before_ptr_star = force # ignore/add/remove/force/not_defined +sp_before_ptr_star = force # ignore/add/remove/force # Add or remove space before pointer star '*' that isn't followed by a # variable name. If set to ignore, sp_before_ptr_star is used instead. -sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined +sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force # Add or remove space before pointer star '*' that is followed by a qualifier. # If set to ignore, sp_before_unnamed_ptr_star is used instead. -sp_before_qualifier_ptr_star = ignore # ignore/add/remove/force/not_defined +sp_before_qualifier_ptr_star = ignore # ignore/add/remove/force # Add or remove space before pointer star '*' that is followed by 'operator' keyword. # If set to ignore, sp_before_unnamed_ptr_star is used instead. -sp_before_operator_ptr_star = ignore # ignore/add/remove/force/not_defined +sp_before_operator_ptr_star = ignore # ignore/add/remove/force # Add or remove space before pointer star '*' that is followed by # a class scope (as in 'int *MyClass::method()') or namespace scope # (as in 'int *my_ns::func()'). # If set to ignore, sp_before_unnamed_ptr_star is used instead. -sp_before_scope_ptr_star = ignore # ignore/add/remove/force/not_defined +sp_before_scope_ptr_star = ignore # ignore/add/remove/force # Add or remove space before pointer star '*' that is followed by '::', # as in 'int *::func()'. # If set to ignore, sp_before_unnamed_ptr_star is used instead. -sp_before_global_scope_ptr_star = ignore # ignore/add/remove/force/not_defined +sp_before_global_scope_ptr_star = ignore # ignore/add/remove/force # Add or remove space between a qualifier and a pointer star '*' that isn't # followed by a variable name, as in '(char const *)'. If set to ignore, # sp_before_ptr_star is used instead. -sp_qualifier_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined +sp_qualifier_unnamed_ptr_star = ignore # ignore/add/remove/force # Add or remove space between pointer stars '*', as in 'int ***a;'. -sp_between_ptr_star = ignore # ignore/add/remove/force/not_defined +sp_between_ptr_star = ignore # ignore/add/remove/force # Add or remove space between pointer star '*' and reference '&', as in 'int *& a;'. -sp_between_ptr_ref = ignore # ignore/add/remove/force/not_defined +sp_between_ptr_ref = ignore # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a word. # # Overrides sp_type_func. -sp_after_ptr_star = remove # ignore/add/remove/force/not_defined +sp_after_ptr_star = remove # ignore/add/remove/force # Add or remove space after pointer caret '^', if followed by a word. -sp_after_ptr_block_caret = ignore # ignore/add/remove/force/not_defined +sp_after_ptr_block_caret = ignore # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a qualifier. -sp_after_ptr_star_qualifier = ignore # ignore/add/remove/force/not_defined +sp_after_ptr_star_qualifier = ignore # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by a function # prototype or function definition. # # Overrides sp_after_ptr_star and sp_type_func. -sp_after_ptr_star_func = remove # ignore/add/remove/force/not_defined +sp_after_ptr_star_func = remove # ignore/add/remove/force # Add or remove space after a pointer star '*' in the trailing return of a # function prototype or function definition. -sp_after_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined +sp_after_ptr_star_trailing = ignore # ignore/add/remove/force # Add or remove space between the pointer star '*' and the name of the variable # in a function pointer definition. -sp_ptr_star_func_var = ignore # ignore/add/remove/force/not_defined +sp_ptr_star_func_var = ignore # ignore/add/remove/force # Add or remove space between the pointer star '*' and the name of the type # in a function pointer type definition. -sp_ptr_star_func_type = ignore # ignore/add/remove/force/not_defined +sp_ptr_star_func_type = ignore # ignore/add/remove/force # Add or remove space after a pointer star '*', if followed by an open # parenthesis, as in 'void* (*)()'. -sp_ptr_star_paren = ignore # ignore/add/remove/force/not_defined +sp_ptr_star_paren = ignore # ignore/add/remove/force # Add or remove space before a pointer star '*', if followed by a function # prototype or function definition. If set to ignore, sp_before_ptr_star is # used instead. -sp_before_ptr_star_func = ignore # ignore/add/remove/force/not_defined +sp_before_ptr_star_func = ignore # ignore/add/remove/force # Add or remove space between a qualifier and a pointer star '*' followed by # the name of the function in a function prototype or definition, as in # 'char const *foo()`. If set to ignore, sp_before_ptr_star is used instead. -sp_qualifier_ptr_star_func = ignore # ignore/add/remove/force/not_defined +sp_qualifier_ptr_star_func = ignore # ignore/add/remove/force # Add or remove space before a pointer star '*' in the trailing return of a # function prototype or function definition. -sp_before_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined +sp_before_ptr_star_trailing = ignore # ignore/add/remove/force # Add or remove space between a qualifier and a pointer star '*' in the # trailing return of a function prototype or function definition, as in # 'auto foo() -> char const *'. -sp_qualifier_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined +sp_qualifier_ptr_star_trailing = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&'. -sp_before_byref = ignore # ignore/add/remove/force/not_defined +sp_before_byref = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&' that isn't followed by a # variable name. If set to ignore, sp_before_byref is used instead. -sp_before_unnamed_byref = ignore # ignore/add/remove/force/not_defined +sp_before_unnamed_byref = ignore # ignore/add/remove/force # Add or remove space after reference sign '&', if followed by a word. # # Overrides sp_type_func. -sp_after_byref = ignore # ignore/add/remove/force/not_defined +sp_after_byref = ignore # ignore/add/remove/force # Add or remove space after a reference sign '&', if followed by a function # prototype or function definition. # # Overrides sp_after_byref and sp_type_func. -sp_after_byref_func = ignore # ignore/add/remove/force/not_defined +sp_after_byref_func = ignore # ignore/add/remove/force # Add or remove space before a reference sign '&', if followed by a function # prototype or function definition. -sp_before_byref_func = ignore # ignore/add/remove/force/not_defined +sp_before_byref_func = ignore # ignore/add/remove/force # Add or remove space after a reference sign '&', if followed by an open # parenthesis, as in 'char& (*)()'. -sp_byref_paren = ignore # ignore/add/remove/force/not_defined +sp_byref_paren = ignore # ignore/add/remove/force # Add or remove space between type and word. In cases where total removal of # whitespace would be a syntax error, a value of 'remove' is treated the same @@ -320,50 +317,50 @@ sp_byref_paren = ignore # ignore/add/remove/force/not_defined # following word. # # Default: force -sp_after_type = force # ignore/add/remove/force/not_defined +sp_after_type = force # ignore/add/remove/force # Add or remove space between 'decltype(...)' and word, # brace or function call. -sp_after_decltype = ignore # ignore/add/remove/force/not_defined +sp_after_decltype = ignore # ignore/add/remove/force # (D) Add or remove space before the parenthesis in the D constructs # 'template Foo(' and 'class Foo('. -sp_before_template_paren = ignore # ignore/add/remove/force/not_defined +sp_before_template_paren = ignore # ignore/add/remove/force # Add or remove space between 'template' and '<'. # If set to ignore, sp_before_angle is used. -sp_template_angle = ignore # ignore/add/remove/force/not_defined +sp_template_angle = ignore # ignore/add/remove/force # Add or remove space before '<'. -sp_before_angle = ignore # ignore/add/remove/force/not_defined +sp_before_angle = ignore # ignore/add/remove/force # Add or remove space inside '<' and '>'. -sp_inside_angle = ignore # ignore/add/remove/force/not_defined +sp_inside_angle = ignore # ignore/add/remove/force # Add or remove space inside '<>'. # if empty. -sp_inside_angle_empty = ignore # ignore/add/remove/force/not_defined +sp_inside_angle_empty = ignore # ignore/add/remove/force # Add or remove space between '>' and ':'. -sp_angle_colon = ignore # ignore/add/remove/force/not_defined +sp_angle_colon = ignore # ignore/add/remove/force # Add or remove space after '>'. -sp_after_angle = ignore # ignore/add/remove/force/not_defined +sp_after_angle = ignore # ignore/add/remove/force # Add or remove space between '>' and '(' as found in 'new List<byte>(foo);'. -sp_angle_paren = ignore # ignore/add/remove/force/not_defined +sp_angle_paren = ignore # ignore/add/remove/force # Add or remove space between '>' and '()' as found in 'new List<byte>();'. -sp_angle_paren_empty = ignore # ignore/add/remove/force/not_defined +sp_angle_paren_empty = ignore # ignore/add/remove/force # Add or remove space between '>' and a word as in 'List<byte> m;' or # 'template <typename T> static ...'. -sp_angle_word = ignore # ignore/add/remove/force/not_defined +sp_angle_word = ignore # ignore/add/remove/force # Add or remove space between '>' and '>' in '>>' (template stuff). # # Default: add -sp_angle_shift = add # ignore/add/remove/force/not_defined +sp_angle_shift = add # ignore/add/remove/force # (C++11) Permit removal of the space between '>>' in 'foo<bar<int> >'. Note # that sp_angle_shift cannot remove the space without this option. @@ -371,658 +368,662 @@ sp_permit_cpp11_shift = false # true/false # Add or remove space before '(' of control statements ('if', 'for', 'switch', # 'while', etc.). -sp_before_sparen = force # ignore/add/remove/force/not_defined +sp_before_sparen = force # ignore/add/remove/force # Add or remove space inside '(' and ')' of control statements other than # 'for'. -sp_inside_sparen = remove # ignore/add/remove/force/not_defined +sp_inside_sparen = remove # ignore/add/remove/force # Add or remove space after '(' of control statements other than 'for'. # # Overrides sp_inside_sparen. -sp_inside_sparen_open = ignore # ignore/add/remove/force/not_defined +sp_inside_sparen_open = ignore # ignore/add/remove/force # Add or remove space before ')' of control statements other than 'for'. # # Overrides sp_inside_sparen. -sp_inside_sparen_close = ignore # ignore/add/remove/force/not_defined +sp_inside_sparen_close = ignore # ignore/add/remove/force # Add or remove space inside '(' and ')' of 'for' statements. -sp_inside_for = remove # ignore/add/remove/force/not_defined +sp_inside_for = remove # ignore/add/remove/force # Add or remove space after '(' of 'for' statements. # # Overrides sp_inside_for. -sp_inside_for_open = ignore # ignore/add/remove/force/not_defined +sp_inside_for_open = ignore # ignore/add/remove/force # Add or remove space before ')' of 'for' statements. # # Overrides sp_inside_for. -sp_inside_for_close = ignore # ignore/add/remove/force/not_defined +sp_inside_for_close = ignore # ignore/add/remove/force # Add or remove space between '((' or '))' of control statements. -sp_sparen_paren = remove # ignore/add/remove/force/not_defined +sp_sparen_paren = remove # ignore/add/remove/force # Add or remove space after ')' of control statements. -sp_after_sparen = ignore # ignore/add/remove/force/not_defined +sp_after_sparen = ignore # ignore/add/remove/force # Add or remove space between ')' and '{' of control statements. -sp_sparen_brace = force # ignore/add/remove/force/not_defined +sp_sparen_brace = force # ignore/add/remove/force # Add or remove space between 'do' and '{'. -sp_do_brace_open = force # ignore/add/remove/force/not_defined +sp_do_brace_open = force # ignore/add/remove/force # Add or remove space between '}' and 'while'. -sp_brace_close_while = force # ignore/add/remove/force/not_defined +sp_brace_close_while = force # ignore/add/remove/force # Add or remove space between 'while' and '('. Overrides sp_before_sparen. -sp_while_paren_open = ignore # ignore/add/remove/force/not_defined +sp_while_paren_open = ignore # ignore/add/remove/force # (D) Add or remove space between 'invariant' and '('. -sp_invariant_paren = ignore # ignore/add/remove/force/not_defined +sp_invariant_paren = ignore # ignore/add/remove/force # (D) Add or remove space after the ')' in 'invariant (C) c'. -sp_after_invariant_paren = ignore # ignore/add/remove/force/not_defined +sp_after_invariant_paren = ignore # ignore/add/remove/force # Add or remove space before empty statement ';' on 'if', 'for' and 'while'. -sp_special_semi = ignore # ignore/add/remove/force/not_defined +sp_special_semi = ignore # ignore/add/remove/force # Add or remove space before ';'. # # Default: remove -sp_before_semi = remove # ignore/add/remove/force/not_defined +sp_before_semi = remove # ignore/add/remove/force # Add or remove space before ';' in non-empty 'for' statements. -sp_before_semi_for = remove # ignore/add/remove/force/not_defined +sp_before_semi_for = remove # ignore/add/remove/force # Add or remove space before a semicolon of an empty left part of a for # statement, as in 'for ( <here> ; ; )'. -sp_before_semi_for_empty = remove # ignore/add/remove/force/not_defined +sp_before_semi_for_empty = remove # ignore/add/remove/force # Add or remove space between the semicolons of an empty middle part of a for # statement, as in 'for ( ; <here> ; )'. -sp_between_semi_for_empty = remove # ignore/add/remove/force/not_defined +sp_between_semi_for_empty = remove # ignore/add/remove/force # Add or remove space after ';', except when followed by a comment. # # Default: add -sp_after_semi = add # ignore/add/remove/force/not_defined +sp_after_semi = add # ignore/add/remove/force # Add or remove space after ';' in non-empty 'for' statements. # # Default: force -sp_after_semi_for = force # ignore/add/remove/force/not_defined +sp_after_semi_for = force # ignore/add/remove/force # Add or remove space after the final semicolon of an empty part of a for # statement, as in 'for ( ; ; <here> )'. -sp_after_semi_for_empty = remove # ignore/add/remove/force/not_defined +sp_after_semi_for_empty = remove # ignore/add/remove/force # Add or remove space before '[' (except '[]'). -sp_before_square = remove # ignore/add/remove/force/not_defined +sp_before_square = remove # ignore/add/remove/force # Add or remove space before '[' for a variable definition. # # Default: remove -sp_before_vardef_square = remove # ignore/add/remove/force/not_defined +sp_before_vardef_square = remove # ignore/add/remove/force # Add or remove space before '[' for asm block. -sp_before_square_asm_block = ignore # ignore/add/remove/force/not_defined +sp_before_square_asm_block = ignore # ignore/add/remove/force # Add or remove space before '[]'. -sp_before_squares = remove # ignore/add/remove/force/not_defined +sp_before_squares = remove # ignore/add/remove/force + +# Add or remove space before C++17 structured bindings +# after byref. +sp_cpp_before_struct_binding_after_byref = ignore # ignore/add/remove/force # Add or remove space before C++17 structured bindings. -sp_cpp_before_struct_binding = ignore # ignore/add/remove/force/not_defined +sp_cpp_before_struct_binding = ignore # ignore/add/remove/force # Add or remove space inside a non-empty '[' and ']'. -sp_inside_square = remove # ignore/add/remove/force/not_defined +sp_inside_square = remove # ignore/add/remove/force # Add or remove space inside '[]'. # if empty. -sp_inside_square_empty = ignore # ignore/add/remove/force/not_defined +sp_inside_square_empty = ignore # ignore/add/remove/force # (OC) Add or remove space inside a non-empty Objective-C boxed array '@[' and # ']'. If set to ignore, sp_inside_square is used. -sp_inside_square_oc_array = ignore # ignore/add/remove/force/not_defined +sp_inside_square_oc_array = ignore # ignore/add/remove/force # Add or remove space after ',', i.e. 'a,b' vs. 'a, b'. -sp_after_comma = add # ignore/add/remove/force/not_defined +sp_after_comma = add # ignore/add/remove/force # Add or remove space before ',', i.e. 'a,b' vs. 'a ,b'. # # Default: remove -sp_before_comma = remove # ignore/add/remove/force/not_defined +sp_before_comma = remove # ignore/add/remove/force # (C#, Vala) Add or remove space between ',' and ']' in multidimensional array type # like 'int[,,]'. -sp_after_mdatype_commas = ignore # ignore/add/remove/force/not_defined +sp_after_mdatype_commas = ignore # ignore/add/remove/force # (C#, Vala) Add or remove space between '[' and ',' in multidimensional array type # like 'int[,,]'. -sp_before_mdatype_commas = ignore # ignore/add/remove/force/not_defined +sp_before_mdatype_commas = ignore # ignore/add/remove/force # (C#, Vala) Add or remove space between ',' in multidimensional array type # like 'int[,,]'. -sp_between_mdatype_commas = ignore # ignore/add/remove/force/not_defined +sp_between_mdatype_commas = ignore # ignore/add/remove/force # Add or remove space between an open parenthesis and comma, # i.e. '(,' vs. '( ,'. # # Default: force -sp_paren_comma = force # ignore/add/remove/force/not_defined +sp_paren_comma = force # ignore/add/remove/force # Add or remove space between a type and ':'. -sp_type_colon = ignore # ignore/add/remove/force/not_defined +sp_type_colon = ignore # ignore/add/remove/force # Add or remove space after the variadic '...' when preceded by a # non-punctuator. # The value REMOVE will be overridden with FORCE -sp_after_ellipsis = ignore # ignore/add/remove/force/not_defined +sp_after_ellipsis = ignore # ignore/add/remove/force # Add or remove space before the variadic '...' when preceded by a # non-punctuator. # The value REMOVE will be overridden with FORCE -sp_before_ellipsis = ignore # ignore/add/remove/force/not_defined +sp_before_ellipsis = ignore # ignore/add/remove/force # Add or remove space between a type and '...'. -sp_type_ellipsis = ignore # ignore/add/remove/force/not_defined +sp_type_ellipsis = ignore # ignore/add/remove/force # Add or remove space between a '*' and '...'. -sp_ptr_type_ellipsis = ignore # ignore/add/remove/force/not_defined +sp_ptr_type_ellipsis = ignore # ignore/add/remove/force # Add or remove space between ')' and '...'. -sp_paren_ellipsis = ignore # ignore/add/remove/force/not_defined +sp_paren_ellipsis = ignore # ignore/add/remove/force # Add or remove space between '&&' and '...'. -sp_byref_ellipsis = ignore # ignore/add/remove/force/not_defined +sp_byref_ellipsis = ignore # ignore/add/remove/force # Add or remove space between ')' and a qualifier such as 'const'. -sp_paren_qualifier = ignore # ignore/add/remove/force/not_defined +sp_paren_qualifier = ignore # ignore/add/remove/force # Add or remove space between ')' and 'noexcept'. -sp_paren_noexcept = ignore # ignore/add/remove/force/not_defined +sp_paren_noexcept = ignore # ignore/add/remove/force # Add or remove space after class ':'. -sp_after_class_colon = remove # ignore/add/remove/force/not_defined +sp_after_class_colon = remove # ignore/add/remove/force # Add or remove space before class ':'. -sp_before_class_colon = ignore # ignore/add/remove/force/not_defined +sp_before_class_colon = ignore # ignore/add/remove/force # Add or remove space after class constructor ':'. # # Default: add -sp_after_constr_colon = add # ignore/add/remove/force/not_defined +sp_after_constr_colon = add # ignore/add/remove/force # Add or remove space before class constructor ':'. # # Default: add -sp_before_constr_colon = add # ignore/add/remove/force/not_defined +sp_before_constr_colon = add # ignore/add/remove/force # Add or remove space before case ':'. # # Default: remove -sp_before_case_colon = remove # ignore/add/remove/force/not_defined +sp_before_case_colon = remove # ignore/add/remove/force # Add or remove space between 'operator' and operator sign. -sp_after_operator = ignore # ignore/add/remove/force/not_defined +sp_after_operator = ignore # ignore/add/remove/force # Add or remove space between the operator symbol and the open parenthesis, as # in 'operator ++('. -sp_after_operator_sym = ignore # ignore/add/remove/force/not_defined +sp_after_operator_sym = ignore # ignore/add/remove/force # Overrides sp_after_operator_sym when the operator has no arguments, as in # 'operator *()'. -sp_after_operator_sym_empty = ignore # ignore/add/remove/force/not_defined +sp_after_operator_sym_empty = ignore # ignore/add/remove/force # Add or remove space after C/D cast, i.e. 'cast(int)a' vs. 'cast(int) a' or # '(int)a' vs. '(int) a'. -sp_after_cast = remove # ignore/add/remove/force/not_defined +sp_after_cast = remove # ignore/add/remove/force # Add or remove spaces inside cast parentheses. -sp_inside_paren_cast = remove # ignore/add/remove/force/not_defined +sp_inside_paren_cast = remove # ignore/add/remove/force # Add or remove space between the type and open parenthesis in a C++ cast, # i.e. 'int(exp)' vs. 'int (exp)'. -sp_cpp_cast_paren = ignore # ignore/add/remove/force/not_defined +sp_cpp_cast_paren = ignore # ignore/add/remove/force # Add or remove space between 'sizeof' and '('. -sp_sizeof_paren = remove # ignore/add/remove/force/not_defined +sp_sizeof_paren = remove # ignore/add/remove/force # Add or remove space between 'sizeof' and '...'. -sp_sizeof_ellipsis = ignore # ignore/add/remove/force/not_defined +sp_sizeof_ellipsis = ignore # ignore/add/remove/force # Add or remove space between 'sizeof...' and '('. -sp_sizeof_ellipsis_paren = ignore # ignore/add/remove/force/not_defined +sp_sizeof_ellipsis_paren = ignore # ignore/add/remove/force # Add or remove space between '...' and a parameter pack. -sp_ellipsis_parameter_pack = ignore # ignore/add/remove/force/not_defined +sp_ellipsis_parameter_pack = ignore # ignore/add/remove/force # Add or remove space between a parameter pack and '...'. -sp_parameter_pack_ellipsis = ignore # ignore/add/remove/force/not_defined +sp_parameter_pack_ellipsis = ignore # ignore/add/remove/force # Add or remove space between 'decltype' and '('. -sp_decltype_paren = ignore # ignore/add/remove/force/not_defined +sp_decltype_paren = ignore # ignore/add/remove/force # (Pawn) Add or remove space after the tag keyword. -sp_after_tag = ignore # ignore/add/remove/force/not_defined +sp_after_tag = ignore # ignore/add/remove/force # Add or remove space inside enum '{' and '}'. -sp_inside_braces_enum = force # ignore/add/remove/force/not_defined +sp_inside_braces_enum = force # ignore/add/remove/force # Add or remove space inside struct/union '{' and '}'. -sp_inside_braces_struct = ignore # ignore/add/remove/force/not_defined +sp_inside_braces_struct = ignore # ignore/add/remove/force # (OC) Add or remove space inside Objective-C boxed dictionary '{' and '}' -sp_inside_braces_oc_dict = ignore # ignore/add/remove/force/not_defined +sp_inside_braces_oc_dict = ignore # ignore/add/remove/force # Add or remove space after open brace in an unnamed temporary # direct-list-initialization # if statement is a brace_init_lst # works only if sp_brace_brace is set to ignore. -sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force/not_defined +sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force # Add or remove space before close brace in an unnamed temporary # direct-list-initialization # if statement is a brace_init_lst # works only if sp_brace_brace is set to ignore. -sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force/not_defined +sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force # Add or remove space inside an unnamed temporary direct-list-initialization # if statement is a brace_init_lst # works only if sp_brace_brace is set to ignore # works only if sp_before_type_brace_init_lst_close is set to ignore. -sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined +sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force # Add or remove space inside '{' and '}'. -sp_inside_braces = add # ignore/add/remove/force/not_defined +sp_inside_braces = add # ignore/add/remove/force # Add or remove space inside '{}'. # if empty. -sp_inside_braces_empty = remove # ignore/add/remove/force/not_defined +sp_inside_braces_empty = remove # ignore/add/remove/force # Add or remove space around trailing return operator '->'. -sp_trailing_return = ignore # ignore/add/remove/force/not_defined +sp_trailing_return = ignore # ignore/add/remove/force # Add or remove space between return type and function name. A minimum of 1 # is forced except for pointer return types. -sp_type_func = ignore # ignore/add/remove/force/not_defined +sp_type_func = ignore # ignore/add/remove/force # Add or remove space between type and open brace of an unnamed temporary # direct-list-initialization. -sp_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined +sp_type_brace_init_lst = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function declaration. -sp_func_proto_paren = remove # ignore/add/remove/force/not_defined +sp_func_proto_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function declaration # if empty. -sp_func_proto_paren_empty = ignore # ignore/add/remove/force/not_defined +sp_func_proto_paren_empty = ignore # ignore/add/remove/force # Add or remove space between function name and '(' with a typedef specifier. -sp_func_type_paren = ignore # ignore/add/remove/force/not_defined +sp_func_type_paren = ignore # ignore/add/remove/force # Add or remove space between alias name and '(' of a non-pointer function type typedef. -sp_func_def_paren = remove # ignore/add/remove/force/not_defined +sp_func_def_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function definition # if empty. -sp_func_def_paren_empty = ignore # ignore/add/remove/force/not_defined +sp_func_def_paren_empty = ignore # ignore/add/remove/force # Add or remove space inside empty function '()'. # Overrides sp_after_angle unless use_sp_after_angle_always is set to true. -sp_inside_fparens = remove # ignore/add/remove/force/not_defined +sp_inside_fparens = remove # ignore/add/remove/force # Add or remove space inside function '(' and ')'. -sp_inside_fparen = remove # ignore/add/remove/force/not_defined +sp_inside_fparen = remove # ignore/add/remove/force # Add or remove space inside user functor '(' and ')'. -sp_func_call_user_inside_rparen = ignore # ignore/add/remove/force/not_defined +sp_func_call_user_inside_rparen = ignore # ignore/add/remove/force # Add or remove space inside empty functor '()'. # Overrides sp_after_angle unless use_sp_after_angle_always is set to true. -sp_inside_rparens = ignore # ignore/add/remove/force/not_defined +sp_inside_rparens = ignore # ignore/add/remove/force # Add or remove space inside functor '(' and ')'. -sp_inside_rparen = ignore # ignore/add/remove/force/not_defined +sp_inside_rparen = ignore # ignore/add/remove/force # Add or remove space inside the first parentheses in a function type, as in # 'void (*x)(...)'. -sp_inside_tparen = remove # ignore/add/remove/force/not_defined +sp_inside_tparen = remove # ignore/add/remove/force # Add or remove space between the ')' and '(' in a function type, as in # 'void (*x)(...)'. -sp_after_tparen_close = remove # ignore/add/remove/force/not_defined +sp_after_tparen_close = remove # ignore/add/remove/force # Add or remove space between ']' and '(' when part of a function call. -sp_square_fparen = ignore # ignore/add/remove/force/not_defined +sp_square_fparen = ignore # ignore/add/remove/force # Add or remove space between ')' and '{' of function. -sp_fparen_brace = ignore # ignore/add/remove/force/not_defined +sp_fparen_brace = ignore # ignore/add/remove/force # Add or remove space between ')' and '{' of a function call in object # initialization. # # Overrides sp_fparen_brace. -sp_fparen_brace_initializer = ignore # ignore/add/remove/force/not_defined +sp_fparen_brace_initializer = ignore # ignore/add/remove/force # (Java) Add or remove space between ')' and '{{' of double brace initializer. -sp_fparen_dbrace = ignore # ignore/add/remove/force/not_defined +sp_fparen_dbrace = ignore # ignore/add/remove/force # Add or remove space between function name and '(' on function calls. -sp_func_call_paren = remove # ignore/add/remove/force/not_defined +sp_func_call_paren = remove # ignore/add/remove/force # Add or remove space between function name and '()' on function calls without # parameters. If set to ignore (the default), sp_func_call_paren is used. -sp_func_call_paren_empty = ignore # ignore/add/remove/force/not_defined +sp_func_call_paren_empty = ignore # ignore/add/remove/force # Add or remove space between the user function name and '(' on function # calls. You need to set a keyword to be a user function in the config file, # like: # set func_call_user tr _ i18n -sp_func_call_user_paren = ignore # ignore/add/remove/force/not_defined +sp_func_call_user_paren = ignore # ignore/add/remove/force # Add or remove space inside user function '(' and ')'. -sp_func_call_user_inside_fparen = ignore # ignore/add/remove/force/not_defined +sp_func_call_user_inside_fparen = ignore # ignore/add/remove/force # Add or remove space between nested parentheses with user functions, # i.e. '((' vs. '( ('. -sp_func_call_user_paren_paren = ignore # ignore/add/remove/force/not_defined +sp_func_call_user_paren_paren = ignore # ignore/add/remove/force # Add or remove space between a constructor/destructor and the open # parenthesis. -sp_func_class_paren = ignore # ignore/add/remove/force/not_defined +sp_func_class_paren = ignore # ignore/add/remove/force # Add or remove space between a constructor without parameters or destructor # and '()'. -sp_func_class_paren_empty = ignore # ignore/add/remove/force/not_defined +sp_func_class_paren_empty = ignore # ignore/add/remove/force # Add or remove space after 'return'. # # Default: force -sp_return = force # ignore/add/remove/force/not_defined +sp_return = force # ignore/add/remove/force # Add or remove space between 'return' and '('. -sp_return_paren = force # ignore/add/remove/force/not_defined +sp_return_paren = force # ignore/add/remove/force # Add or remove space between 'return' and '{'. -sp_return_brace = ignore # ignore/add/remove/force/not_defined +sp_return_brace = ignore # ignore/add/remove/force # Add or remove space between '__attribute__' and '('. -sp_attribute_paren = remove # ignore/add/remove/force/not_defined +sp_attribute_paren = remove # ignore/add/remove/force # Add or remove space between 'defined' and '(' in '#if defined (FOO)'. -sp_defined_paren = remove # ignore/add/remove/force/not_defined +sp_defined_paren = remove # ignore/add/remove/force # Add or remove space between 'throw' and '(' in 'throw (something)'. -sp_throw_paren = ignore # ignore/add/remove/force/not_defined +sp_throw_paren = ignore # ignore/add/remove/force # Add or remove space between 'throw' and anything other than '(' as in # '@throw [...];'. -sp_after_throw = ignore # ignore/add/remove/force/not_defined +sp_after_throw = ignore # ignore/add/remove/force # Add or remove space between 'catch' and '(' in 'catch (something) { }'. # If set to ignore, sp_before_sparen is used. -sp_catch_paren = ignore # ignore/add/remove/force/not_defined +sp_catch_paren = ignore # ignore/add/remove/force # (OC) Add or remove space between '@catch' and '(' # in '@catch (something) { }'. If set to ignore, sp_catch_paren is used. -sp_oc_catch_paren = ignore # ignore/add/remove/force/not_defined +sp_oc_catch_paren = ignore # ignore/add/remove/force # (OC) Add or remove space before Objective-C protocol list # as in '@protocol Protocol<here><Protocol_A>' or '@interface MyClass : NSObject<here><MyProtocol>'. -sp_before_oc_proto_list = ignore # ignore/add/remove/force/not_defined +sp_before_oc_proto_list = ignore # ignore/add/remove/force # (OC) Add or remove space between class name and '(' # in '@interface className(categoryName)<ProtocolName>:BaseClass' -sp_oc_classname_paren = ignore # ignore/add/remove/force/not_defined +sp_oc_classname_paren = ignore # ignore/add/remove/force # (D) Add or remove space between 'version' and '(' # in 'version (something) { }'. If set to ignore, sp_before_sparen is used. -sp_version_paren = ignore # ignore/add/remove/force/not_defined +sp_version_paren = ignore # ignore/add/remove/force # (D) Add or remove space between 'scope' and '(' # in 'scope (something) { }'. If set to ignore, sp_before_sparen is used. -sp_scope_paren = ignore # ignore/add/remove/force/not_defined +sp_scope_paren = ignore # ignore/add/remove/force # Add or remove space between 'super' and '(' in 'super (something)'. # # Default: remove -sp_super_paren = remove # ignore/add/remove/force/not_defined +sp_super_paren = remove # ignore/add/remove/force # Add or remove space between 'this' and '(' in 'this (something)'. # # Default: remove -sp_this_paren = remove # ignore/add/remove/force/not_defined +sp_this_paren = remove # ignore/add/remove/force # Add or remove space between a macro name and its definition. -sp_macro = ignore # ignore/add/remove/force/not_defined +sp_macro = ignore # ignore/add/remove/force # Add or remove space between a macro function ')' and its definition. -sp_macro_func = ignore # ignore/add/remove/force/not_defined +sp_macro_func = ignore # ignore/add/remove/force # Add or remove space between 'else' and '{' if on the same line. -sp_else_brace = force # ignore/add/remove/force/not_defined +sp_else_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'else' if on the same line. -sp_brace_else = force # ignore/add/remove/force/not_defined +sp_brace_else = force # ignore/add/remove/force # Add or remove space between '}' and the name of a typedef on the same line. -sp_brace_typedef = force # ignore/add/remove/force/not_defined +sp_brace_typedef = force # ignore/add/remove/force # Add or remove space before the '{' of a 'catch' statement, if the '{' and # 'catch' are on the same line, as in 'catch (decl) <here> {'. -sp_catch_brace = ignore # ignore/add/remove/force/not_defined +sp_catch_brace = ignore # ignore/add/remove/force # (OC) Add or remove space before the '{' of a '@catch' statement, if the '{' # and '@catch' are on the same line, as in '@catch (decl) <here> {'. # If set to ignore, sp_catch_brace is used. -sp_oc_catch_brace = ignore # ignore/add/remove/force/not_defined +sp_oc_catch_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'catch' if on the same line. -sp_brace_catch = ignore # ignore/add/remove/force/not_defined +sp_brace_catch = ignore # ignore/add/remove/force # (OC) Add or remove space between '}' and '@catch' if on the same line. # If set to ignore, sp_brace_catch is used. -sp_oc_brace_catch = ignore # ignore/add/remove/force/not_defined +sp_oc_brace_catch = ignore # ignore/add/remove/force # Add or remove space between 'finally' and '{' if on the same line. -sp_finally_brace = ignore # ignore/add/remove/force/not_defined +sp_finally_brace = ignore # ignore/add/remove/force # Add or remove space between '}' and 'finally' if on the same line. -sp_brace_finally = ignore # ignore/add/remove/force/not_defined +sp_brace_finally = ignore # ignore/add/remove/force # Add or remove space between 'try' and '{' if on the same line. -sp_try_brace = ignore # ignore/add/remove/force/not_defined +sp_try_brace = ignore # ignore/add/remove/force # Add or remove space between get/set and '{' if on the same line. -sp_getset_brace = ignore # ignore/add/remove/force/not_defined +sp_getset_brace = ignore # ignore/add/remove/force # Add or remove space between a variable and '{' for C++ uniform # initialization. -sp_word_brace_init_lst = ignore # ignore/add/remove/force/not_defined +sp_word_brace_init_lst = ignore # ignore/add/remove/force # Add or remove space between a variable and '{' for a namespace. # # Default: add -sp_word_brace_ns = add # ignore/add/remove/force/not_defined +sp_word_brace_ns = add # ignore/add/remove/force # Add or remove space before the '::' operator. -sp_before_dc = ignore # ignore/add/remove/force/not_defined +sp_before_dc = ignore # ignore/add/remove/force # Add or remove space after the '::' operator. -sp_after_dc = ignore # ignore/add/remove/force/not_defined +sp_after_dc = ignore # ignore/add/remove/force # (D) Add or remove around the D named array initializer ':' operator. -sp_d_array_colon = ignore # ignore/add/remove/force/not_defined +sp_d_array_colon = ignore # ignore/add/remove/force # Add or remove space after the '!' (not) unary operator. # # Default: remove -sp_not = remove # ignore/add/remove/force/not_defined +sp_not = remove # ignore/add/remove/force # Add or remove space between two '!' (not) unary operators. # If set to ignore, sp_not will be used. -sp_not_not = ignore # ignore/add/remove/force/not_defined +sp_not_not = ignore # ignore/add/remove/force # Add or remove space after the '~' (invert) unary operator. # # Default: remove -sp_inv = remove # ignore/add/remove/force/not_defined +sp_inv = remove # ignore/add/remove/force # Add or remove space after the '&' (address-of) unary operator. This does not # affect the spacing after a '&' that is part of a type. # # Default: remove -sp_addr = ignore # ignore/add/remove/force/not_defined +sp_addr = ignore # ignore/add/remove/force # Add or remove space around the '.' or '->' operators. # # Default: remove -sp_member = remove # ignore/add/remove/force/not_defined +sp_member = remove # ignore/add/remove/force # Add or remove space after the '*' (dereference) unary operator. This does # not affect the spacing after a '*' that is part of a type. # # Default: remove -sp_deref = remove # ignore/add/remove/force/not_defined +sp_deref = remove # ignore/add/remove/force # Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. # # Default: remove -sp_sign = remove # ignore/add/remove/force/not_defined +sp_sign = remove # ignore/add/remove/force # Add or remove space between '++' and '--' the word to which it is being # applied, as in '(--x)' or 'y++;'. # # Default: remove -sp_incdec = remove # ignore/add/remove/force/not_defined +sp_incdec = remove # ignore/add/remove/force # Add or remove space before a backslash-newline at the end of a line. # # Default: add -sp_before_nl_cont = force # ignore/add/remove/force/not_defined +sp_before_nl_cont = force # ignore/add/remove/force # (OC) Add or remove space after the scope '+' or '-', as in '-(void) foo;' # or '+(int) bar;'. -sp_after_oc_scope = ignore # ignore/add/remove/force/not_defined +sp_after_oc_scope = ignore # ignore/add/remove/force # (OC) Add or remove space after the colon in message specs, # i.e. '-(int) f:(int) x;' vs. '-(int) f: (int) x;'. -sp_after_oc_colon = ignore # ignore/add/remove/force/not_defined +sp_after_oc_colon = ignore # ignore/add/remove/force # (OC) Add or remove space before the colon in message specs, # i.e. '-(int) f: (int) x;' vs. '-(int) f : (int) x;'. -sp_before_oc_colon = ignore # ignore/add/remove/force/not_defined +sp_before_oc_colon = ignore # ignore/add/remove/force # (OC) Add or remove space after the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};'. -sp_after_oc_dict_colon = ignore # ignore/add/remove/force/not_defined +sp_after_oc_dict_colon = ignore # ignore/add/remove/force # (OC) Add or remove space before the colon in immutable dictionary expression # 'NSDictionary *test = @{@"foo" :@"bar"};'. -sp_before_oc_dict_colon = ignore # ignore/add/remove/force/not_defined +sp_before_oc_dict_colon = ignore # ignore/add/remove/force # (OC) Add or remove space after the colon in message specs, # i.e. '[object setValue:1];' vs. '[object setValue: 1];'. -sp_after_send_oc_colon = ignore # ignore/add/remove/force/not_defined +sp_after_send_oc_colon = ignore # ignore/add/remove/force # (OC) Add or remove space before the colon in message specs, # i.e. '[object setValue:1];' vs. '[object setValue :1];'. -sp_before_send_oc_colon = ignore # ignore/add/remove/force/not_defined +sp_before_send_oc_colon = ignore # ignore/add/remove/force # (OC) Add or remove space after the (type) in message specs, # i.e. '-(int)f: (int) x;' vs. '-(int)f: (int)x;'. -sp_after_oc_type = ignore # ignore/add/remove/force/not_defined +sp_after_oc_type = ignore # ignore/add/remove/force # (OC) Add or remove space after the first (type) in message specs, # i.e. '-(int) f:(int)x;' vs. '-(int)f:(int)x;'. -sp_after_oc_return_type = ignore # ignore/add/remove/force/not_defined +sp_after_oc_return_type = ignore # ignore/add/remove/force # (OC) Add or remove space between '@selector' and '(', # i.e. '@selector(msgName)' vs. '@selector (msgName)'. # Also applies to '@protocol()' constructs. -sp_after_oc_at_sel = ignore # ignore/add/remove/force/not_defined +sp_after_oc_at_sel = ignore # ignore/add/remove/force # (OC) Add or remove space between '@selector(x)' and the following word, # i.e. '@selector(foo) a:' vs. '@selector(foo)a:'. -sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force/not_defined +sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force # (OC) Add or remove space inside '@selector' parentheses, # i.e. '@selector(foo)' vs. '@selector( foo )'. # Also applies to '@protocol()' constructs. -sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force/not_defined +sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force # (OC) Add or remove space before a block pointer caret, # i.e. '^int (int arg){...}' vs. ' ^int (int arg){...}'. -sp_before_oc_block_caret = ignore # ignore/add/remove/force/not_defined +sp_before_oc_block_caret = ignore # ignore/add/remove/force # (OC) Add or remove space after a block pointer caret, # i.e. '^int (int arg){...}' vs. '^ int (int arg){...}'. -sp_after_oc_block_caret = ignore # ignore/add/remove/force/not_defined +sp_after_oc_block_caret = ignore # ignore/add/remove/force # (OC) Add or remove space between the receiver and selector in a message, # as in '[receiver selector ...]'. -sp_after_oc_msg_receiver = ignore # ignore/add/remove/force/not_defined +sp_after_oc_msg_receiver = ignore # ignore/add/remove/force # (OC) Add or remove space after '@property'. -sp_after_oc_property = ignore # ignore/add/remove/force/not_defined +sp_after_oc_property = ignore # ignore/add/remove/force # (OC) Add or remove space between '@synchronized' and the open parenthesis, # i.e. '@synchronized(foo)' vs. '@synchronized (foo)'. -sp_after_oc_synchronized = ignore # ignore/add/remove/force/not_defined +sp_after_oc_synchronized = ignore # ignore/add/remove/force # Add or remove space around the ':' in 'b ? t : f'. -sp_cond_colon = force # ignore/add/remove/force/not_defined +sp_cond_colon = force # ignore/add/remove/force # Add or remove space before the ':' in 'b ? t : f'. # # Overrides sp_cond_colon. -sp_cond_colon_before = ignore # ignore/add/remove/force/not_defined +sp_cond_colon_before = ignore # ignore/add/remove/force # Add or remove space after the ':' in 'b ? t : f'. # # Overrides sp_cond_colon. -sp_cond_colon_after = ignore # ignore/add/remove/force/not_defined +sp_cond_colon_after = ignore # ignore/add/remove/force # Add or remove space around the '?' in 'b ? t : f'. -sp_cond_question = force # ignore/add/remove/force/not_defined +sp_cond_question = force # ignore/add/remove/force # Add or remove space before the '?' in 'b ? t : f'. # # Overrides sp_cond_question. -sp_cond_question_before = ignore # ignore/add/remove/force/not_defined +sp_cond_question_before = ignore # ignore/add/remove/force # Add or remove space after the '?' in 'b ? t : f'. # # Overrides sp_cond_question. -sp_cond_question_after = ignore # ignore/add/remove/force/not_defined +sp_cond_question_after = ignore # ignore/add/remove/force # In the abbreviated ternary form '(a ?: b)', add or remove space between '?' # and ':'. # # Overrides all other sp_cond_* options. -sp_cond_ternary_short = ignore # ignore/add/remove/force/not_defined +sp_cond_ternary_short = ignore # ignore/add/remove/force # Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make # sense here. -sp_case_label = ignore # ignore/add/remove/force/not_defined +sp_case_label = ignore # ignore/add/remove/force # (D) Add or remove space around the D '..' operator. -sp_range = ignore # ignore/add/remove/force/not_defined +sp_range = ignore # ignore/add/remove/force # Add or remove space after ':' in a Java/C++11 range-based 'for', # as in 'for (Type var : <here> expr)'. -sp_after_for_colon = ignore # ignore/add/remove/force/not_defined +sp_after_for_colon = ignore # ignore/add/remove/force # Add or remove space before ':' in a Java/C++11 range-based 'for', # as in 'for (Type var <here> : expr)'. -sp_before_for_colon = ignore # ignore/add/remove/force/not_defined +sp_before_for_colon = ignore # ignore/add/remove/force # (D) Add or remove space between 'extern' and '(' as in 'extern <here> (C)'. -sp_extern_paren = ignore # ignore/add/remove/force/not_defined +sp_extern_paren = ignore # ignore/add/remove/force # Add or remove space after the opening of a C++ comment, as in '// <here> A'. -sp_cmt_cpp_start = ignore # ignore/add/remove/force/not_defined +sp_cmt_cpp_start = ignore # ignore/add/remove/force # remove space after the '//' and the pvs command '-V1234', # only works with sp_cmt_cpp_start set to add or force. @@ -1038,7 +1039,7 @@ sp_cmt_cpp_lint = false # true/false # with either 'BEGIN' or 'END'. # # Overrides sp_cmt_cpp_start. -sp_cmt_cpp_region = ignore # ignore/add/remove/force/not_defined +sp_cmt_cpp_region = ignore # ignore/add/remove/force # If true, space added with sp_cmt_cpp_start will be added after Doxygen # sequences like '///', '///<', '//!' and '//!<'. @@ -1049,35 +1050,35 @@ sp_cmt_cpp_doxygen = false # true/false sp_cmt_cpp_qttr = false # true/false # Add or remove space between #else or #endif and a trailing comment. -sp_endif_cmt = ignore # ignore/add/remove/force/not_defined +sp_endif_cmt = ignore # ignore/add/remove/force # Add or remove space after 'new', 'delete' and 'delete[]'. -sp_after_new = ignore # ignore/add/remove/force/not_defined +sp_after_new = ignore # ignore/add/remove/force # Add or remove space between 'new' and '(' in 'new()'. -sp_between_new_paren = ignore # ignore/add/remove/force/not_defined +sp_between_new_paren = ignore # ignore/add/remove/force # Add or remove space between ')' and type in 'new(foo) BAR'. -sp_after_newop_paren = ignore # ignore/add/remove/force/not_defined +sp_after_newop_paren = ignore # ignore/add/remove/force # Add or remove space inside parentheses of the new operator # as in 'new(foo) BAR'. -sp_inside_newop_paren = ignore # ignore/add/remove/force/not_defined +sp_inside_newop_paren = ignore # ignore/add/remove/force # Add or remove space after the open parenthesis of the new operator, # as in 'new(foo) BAR'. # # Overrides sp_inside_newop_paren. -sp_inside_newop_paren_open = ignore # ignore/add/remove/force/not_defined +sp_inside_newop_paren_open = ignore # ignore/add/remove/force # Add or remove space before the close parenthesis of the new operator, # as in 'new(foo) BAR'. # # Overrides sp_inside_newop_paren. -sp_inside_newop_paren_close = ignore # ignore/add/remove/force/not_defined +sp_inside_newop_paren_close = ignore # ignore/add/remove/force # Add or remove space before a trailing comment. -sp_before_tr_cmt = add # ignore/add/remove/force/not_defined +sp_before_tr_cmt = add # ignore/add/remove/force # Number of spaces before a trailing comment. sp_num_before_tr_cmt = 2 # unsigned number @@ -1085,7 +1086,7 @@ sp_num_before_tr_cmt = 2 # unsigned number # Add or remove space before an embedded comment. # # Default: force -sp_before_emb_cmt = force # ignore/add/remove/force/not_defined +sp_before_emb_cmt = force # ignore/add/remove/force # Number of spaces before an embedded comment. # @@ -1095,7 +1096,7 @@ sp_num_before_emb_cmt = 1 # unsigned number # Add or remove space after an embedded comment. # # Default: force -sp_after_emb_cmt = force # ignore/add/remove/force/not_defined +sp_after_emb_cmt = force # ignore/add/remove/force # Number of spaces after an embedded comment. # @@ -1103,26 +1104,29 @@ sp_after_emb_cmt = force # ignore/add/remove/force/not_defined sp_num_after_emb_cmt = 1 # unsigned number # (Java) Add or remove space between an annotation and the open parenthesis. -sp_annotation_paren = ignore # ignore/add/remove/force/not_defined +sp_annotation_paren = ignore # ignore/add/remove/force # If true, vbrace tokens are dropped to the previous token and skipped. sp_skip_vbrace_tokens = false # true/false # Add or remove space after 'noexcept'. -sp_after_noexcept = ignore # ignore/add/remove/force/not_defined +sp_after_noexcept = ignore # ignore/add/remove/force # Add or remove space after '_'. -sp_vala_after_translation = ignore # ignore/add/remove/force/not_defined +sp_vala_after_translation = ignore # ignore/add/remove/force # Add or remove space before a bit colon ':'. -sp_before_bit_colon = ignore # ignore/add/remove/force/not_defined +sp_before_bit_colon = ignore # ignore/add/remove/force # Add or remove space after a bit colon ':'. -sp_after_bit_colon = ignore # ignore/add/remove/force/not_defined +sp_after_bit_colon = ignore # ignore/add/remove/force # If true, a <TAB> is inserted after #define. force_tab_after_define = false # true/false +# Add or remove space between two strings. +sp_string_string = force # ignore/add/remove/force + # # Indenting options # @@ -1683,166 +1687,166 @@ nl_for_leave_one_liners = false # true/false nl_oc_msg_leave_one_liner = false # true/false # (OC) Add or remove newline between method declaration and '{'. -nl_oc_mdef_brace = ignore # ignore/add/remove/force/not_defined +nl_oc_mdef_brace = ignore # ignore/add/remove/force # (OC) Add or remove newline between Objective-C block signature and '{'. -nl_oc_block_brace = ignore # ignore/add/remove/force/not_defined +nl_oc_block_brace = ignore # ignore/add/remove/force # (OC) Add or remove blank line before '@interface' statement. -nl_oc_before_interface = ignore # ignore/add/remove/force/not_defined +nl_oc_before_interface = ignore # ignore/add/remove/force # (OC) Add or remove blank line before '@implementation' statement. -nl_oc_before_implementation = ignore # ignore/add/remove/force/not_defined +nl_oc_before_implementation = ignore # ignore/add/remove/force # (OC) Add or remove blank line before '@end' statement. -nl_oc_before_end = ignore # ignore/add/remove/force/not_defined +nl_oc_before_end = ignore # ignore/add/remove/force # (OC) Add or remove newline between '@interface' and '{'. -nl_oc_interface_brace = ignore # ignore/add/remove/force/not_defined +nl_oc_interface_brace = ignore # ignore/add/remove/force # (OC) Add or remove newline between '@implementation' and '{'. -nl_oc_implementation_brace = ignore # ignore/add/remove/force/not_defined +nl_oc_implementation_brace = ignore # ignore/add/remove/force # Add or remove newlines at the start of the file. -nl_start_of_file = ignore # ignore/add/remove/force/not_defined +nl_start_of_file = ignore # ignore/add/remove/force # The minimum number of newlines at the start of the file (only used if # nl_start_of_file is 'add' or 'force'). nl_start_of_file_min = 0 # unsigned number # Add or remove newline at the end of the file. -nl_end_of_file = force # ignore/add/remove/force/not_defined +nl_end_of_file = force # ignore/add/remove/force # The minimum number of newlines at the end of the file (only used if # nl_end_of_file is 'add' or 'force'). nl_end_of_file_min = 1 # unsigned number # Add or remove newline between '=' and '{'. -nl_assign_brace = remove # ignore/add/remove/force/not_defined +nl_assign_brace = remove # ignore/add/remove/force # (D) Add or remove newline between '=' and '['. -nl_assign_square = ignore # ignore/add/remove/force/not_defined +nl_assign_square = ignore # ignore/add/remove/force # Add or remove newline between '[]' and '{'. -nl_tsquare_brace = ignore # ignore/add/remove/force/not_defined +nl_tsquare_brace = ignore # ignore/add/remove/force # (D) Add or remove newline after '= ['. Will also affect the newline before # the ']'. -nl_after_square_assign = ignore # ignore/add/remove/force/not_defined +nl_after_square_assign = ignore # ignore/add/remove/force # Add or remove newline between a function call's ')' and '{', as in # 'list_for_each(item, &list) { }'. -nl_fcall_brace = ignore # ignore/add/remove/force/not_defined +nl_fcall_brace = ignore # ignore/add/remove/force # Add or remove newline between 'enum' and '{'. -nl_enum_brace = remove # ignore/add/remove/force/not_defined +nl_enum_brace = remove # ignore/add/remove/force # Add or remove newline between 'enum' and 'class'. -nl_enum_class = ignore # ignore/add/remove/force/not_defined +nl_enum_class = ignore # ignore/add/remove/force # Add or remove newline between 'enum class' and the identifier. -nl_enum_class_identifier = ignore # ignore/add/remove/force/not_defined +nl_enum_class_identifier = ignore # ignore/add/remove/force # Add or remove newline between 'enum class' type and ':'. -nl_enum_identifier_colon = ignore # ignore/add/remove/force/not_defined +nl_enum_identifier_colon = ignore # ignore/add/remove/force # Add or remove newline between 'enum class identifier :' and type. -nl_enum_colon_type = ignore # ignore/add/remove/force/not_defined +nl_enum_colon_type = ignore # ignore/add/remove/force # Add or remove newline between 'struct and '{'. -nl_struct_brace = remove # ignore/add/remove/force/not_defined +nl_struct_brace = remove # ignore/add/remove/force # Add or remove newline between 'union' and '{'. -nl_union_brace = remove # ignore/add/remove/force/not_defined +nl_union_brace = remove # ignore/add/remove/force # Add or remove newline between 'if' and '{'. -nl_if_brace = remove # ignore/add/remove/force/not_defined +nl_if_brace = remove # ignore/add/remove/force # Add or remove newline between '}' and 'else'. -nl_brace_else = remove # ignore/add/remove/force/not_defined +nl_brace_else = remove # ignore/add/remove/force # Add or remove newline between 'else if' and '{'. If set to ignore, # nl_if_brace is used instead. -nl_elseif_brace = ignore # ignore/add/remove/force/not_defined +nl_elseif_brace = ignore # ignore/add/remove/force # Add or remove newline between 'else' and '{'. -nl_else_brace = remove # ignore/add/remove/force/not_defined +nl_else_brace = remove # ignore/add/remove/force # Add or remove newline between 'else' and 'if'. -nl_else_if = remove # ignore/add/remove/force/not_defined +nl_else_if = remove # ignore/add/remove/force # Add or remove newline before '{' opening brace -nl_before_opening_brace_func_class_def = ignore # ignore/add/remove/force/not_defined +nl_before_opening_brace_func_class_def = ignore # ignore/add/remove/force # Add or remove newline before 'if'/'else if' closing parenthesis. -nl_before_if_closing_paren = remove # ignore/add/remove/force/not_defined +nl_before_if_closing_paren = remove # ignore/add/remove/force # Add or remove newline between '}' and 'finally'. -nl_brace_finally = ignore # ignore/add/remove/force/not_defined +nl_brace_finally = ignore # ignore/add/remove/force # Add or remove newline between 'finally' and '{'. -nl_finally_brace = ignore # ignore/add/remove/force/not_defined +nl_finally_brace = ignore # ignore/add/remove/force # Add or remove newline between 'try' and '{'. -nl_try_brace = ignore # ignore/add/remove/force/not_defined +nl_try_brace = ignore # ignore/add/remove/force # Add or remove newline between get/set and '{'. -nl_getset_brace = ignore # ignore/add/remove/force/not_defined +nl_getset_brace = ignore # ignore/add/remove/force # Add or remove newline between 'for' and '{'. -nl_for_brace = remove # ignore/add/remove/force/not_defined +nl_for_brace = remove # ignore/add/remove/force # Add or remove newline before the '{' of a 'catch' statement, as in # 'catch (decl) <here> {'. -nl_catch_brace = ignore # ignore/add/remove/force/not_defined +nl_catch_brace = ignore # ignore/add/remove/force # (OC) Add or remove newline before the '{' of a '@catch' statement, as in # '@catch (decl) <here> {'. If set to ignore, nl_catch_brace is used. -nl_oc_catch_brace = ignore # ignore/add/remove/force/not_defined +nl_oc_catch_brace = ignore # ignore/add/remove/force # Add or remove newline between '}' and 'catch'. -nl_brace_catch = ignore # ignore/add/remove/force/not_defined +nl_brace_catch = ignore # ignore/add/remove/force # (OC) Add or remove newline between '}' and '@catch'. If set to ignore, # nl_brace_catch is used. -nl_oc_brace_catch = ignore # ignore/add/remove/force/not_defined +nl_oc_brace_catch = ignore # ignore/add/remove/force # Add or remove newline between '}' and ']'. -nl_brace_square = ignore # ignore/add/remove/force/not_defined +nl_brace_square = ignore # ignore/add/remove/force # Add or remove newline between '}' and ')' in a function invocation. -nl_brace_fparen = remove # ignore/add/remove/force/not_defined +nl_brace_fparen = remove # ignore/add/remove/force # Add or remove newline between 'while' and '{'. -nl_while_brace = remove # ignore/add/remove/force/not_defined +nl_while_brace = remove # ignore/add/remove/force # (D) Add or remove newline between 'scope (x)' and '{'. -nl_scope_brace = ignore # ignore/add/remove/force/not_defined +nl_scope_brace = ignore # ignore/add/remove/force # (D) Add or remove newline between 'unittest' and '{'. -nl_unittest_brace = ignore # ignore/add/remove/force/not_defined +nl_unittest_brace = ignore # ignore/add/remove/force # (D) Add or remove newline between 'version (x)' and '{'. -nl_version_brace = ignore # ignore/add/remove/force/not_defined +nl_version_brace = ignore # ignore/add/remove/force # (C#) Add or remove newline between 'using' and '{'. -nl_using_brace = ignore # ignore/add/remove/force/not_defined +nl_using_brace = ignore # ignore/add/remove/force # Add or remove newline between two open or close braces. Due to general # newline/brace handling, REMOVE may not work. -nl_brace_brace = ignore # ignore/add/remove/force/not_defined +nl_brace_brace = ignore # ignore/add/remove/force # Add or remove newline between 'do' and '{'. -nl_do_brace = remove # ignore/add/remove/force/not_defined +nl_do_brace = remove # ignore/add/remove/force # Add or remove newline between '}' and 'while' of 'do' statement. -nl_brace_while = remove # ignore/add/remove/force/not_defined +nl_brace_while = remove # ignore/add/remove/force # Add or remove newline between 'switch' and '{'. -nl_switch_brace = remove # ignore/add/remove/force/not_defined +nl_switch_brace = remove # ignore/add/remove/force # Add or remove newline between 'synchronized' and '{'. -nl_synchronized_brace = ignore # ignore/add/remove/force/not_defined +nl_synchronized_brace = ignore # ignore/add/remove/force # Add a newline between ')' and '{' if the ')' is on a different line than the # if/for/etc. @@ -1853,11 +1857,11 @@ nl_multi_line_cond = false # true/false # Add a newline after '(' if an if/for/while/switch condition spans multiple # lines -nl_multi_line_sparen_open = remove # ignore/add/remove/force/not_defined +nl_multi_line_sparen_open = remove # ignore/add/remove/force # Add a newline before ')' if an if/for/while/switch condition spans multiple # lines. Overrides nl_before_if_closing_paren if both are specified. -nl_multi_line_sparen_close = remove # ignore/add/remove/force/not_defined +nl_multi_line_sparen_close = remove # ignore/add/remove/force # Force a newline in a define after the macro name for multi-line defines. nl_multi_line_define = false # true/false @@ -1872,141 +1876,141 @@ nl_after_case = true # true/false # Add or remove newline between a case ':' and '{'. # # Overrides nl_after_case. -nl_case_colon_brace = remove # ignore/add/remove/force/not_defined +nl_case_colon_brace = remove # ignore/add/remove/force # Add or remove newline between ')' and 'throw'. -nl_before_throw = ignore # ignore/add/remove/force/not_defined +nl_before_throw = ignore # ignore/add/remove/force # Add or remove newline between 'namespace' and '{'. -nl_namespace_brace = ignore # ignore/add/remove/force/not_defined +nl_namespace_brace = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template class. -nl_template_class = ignore # ignore/add/remove/force/not_defined +nl_template_class = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template class declaration. # # Overrides nl_template_class. -nl_template_class_decl = ignore # ignore/add/remove/force/not_defined +nl_template_class_decl = ignore # ignore/add/remove/force # Add or remove newline after 'template<>' of a specialized class declaration. # # Overrides nl_template_class_decl. -nl_template_class_decl_special = ignore # ignore/add/remove/force/not_defined +nl_template_class_decl_special = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template class definition. # # Overrides nl_template_class. -nl_template_class_def = ignore # ignore/add/remove/force/not_defined +nl_template_class_def = ignore # ignore/add/remove/force # Add or remove newline after 'template<>' of a specialized class definition. # # Overrides nl_template_class_def. -nl_template_class_def_special = ignore # ignore/add/remove/force/not_defined +nl_template_class_def_special = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template function. -nl_template_func = ignore # ignore/add/remove/force/not_defined +nl_template_func = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template function # declaration. # # Overrides nl_template_func. -nl_template_func_decl = ignore # ignore/add/remove/force/not_defined +nl_template_func_decl = ignore # ignore/add/remove/force # Add or remove newline after 'template<>' of a specialized function # declaration. # # Overrides nl_template_func_decl. -nl_template_func_decl_special = ignore # ignore/add/remove/force/not_defined +nl_template_func_decl_special = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template function # definition. # # Overrides nl_template_func. -nl_template_func_def = ignore # ignore/add/remove/force/not_defined +nl_template_func_def = ignore # ignore/add/remove/force # Add or remove newline after 'template<>' of a specialized function # definition. # # Overrides nl_template_func_def. -nl_template_func_def_special = ignore # ignore/add/remove/force/not_defined +nl_template_func_def_special = ignore # ignore/add/remove/force # Add or remove newline after 'template<...>' of a template variable. -nl_template_var = ignore # ignore/add/remove/force/not_defined +nl_template_var = ignore # ignore/add/remove/force # Add or remove newline between 'template<...>' and 'using' of a templated # type alias. -nl_template_using = ignore # ignore/add/remove/force/not_defined +nl_template_using = ignore # ignore/add/remove/force # Add or remove newline between 'class' and '{'. -nl_class_brace = ignore # ignore/add/remove/force/not_defined +nl_class_brace = ignore # ignore/add/remove/force # Add or remove newline before or after (depending on pos_class_comma, # may not be IGNORE) each',' in the base class list. -nl_class_init_args = ignore # ignore/add/remove/force/not_defined +nl_class_init_args = ignore # ignore/add/remove/force # Add or remove newline after each ',' in the constructor member # initialization. Related to nl_constr_colon, pos_constr_colon and # pos_constr_comma. -nl_constr_init_args = ignore # ignore/add/remove/force/not_defined +nl_constr_init_args = ignore # ignore/add/remove/force # Add or remove newline before first element, after comma, and after last # element, in 'enum'. -nl_enum_own_lines = ignore # ignore/add/remove/force/not_defined +nl_enum_own_lines = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a function # definition. # might be modified by nl_func_leave_one_liners -nl_func_type_name = remove # ignore/add/remove/force/not_defined +nl_func_type_name = remove # ignore/add/remove/force # Add or remove newline between return type and function name inside a class # definition. If set to ignore, nl_func_type_name or nl_func_proto_type_name # is used instead. -nl_func_type_name_class = ignore # ignore/add/remove/force/not_defined +nl_func_type_name_class = ignore # ignore/add/remove/force # Add or remove newline between class specification and '::' # in 'void A::f() { }'. Only appears in separate member implementation (does # not appear with in-line implementation). -nl_func_class_scope = ignore # ignore/add/remove/force/not_defined +nl_func_class_scope = ignore # ignore/add/remove/force # Add or remove newline between function scope and name, as in # 'void A :: <here> f() { }'. -nl_func_scope_name = ignore # ignore/add/remove/force/not_defined +nl_func_scope_name = ignore # ignore/add/remove/force # Add or remove newline between return type and function name in a prototype. -nl_func_proto_type_name = ignore # ignore/add/remove/force/not_defined +nl_func_proto_type_name = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the # declaration. -nl_func_paren = ignore # ignore/add/remove/force/not_defined +nl_func_paren = ignore # ignore/add/remove/force # Overrides nl_func_paren for functions with no parameters. -nl_func_paren_empty = ignore # ignore/add/remove/force/not_defined +nl_func_paren_empty = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the # definition. -nl_func_def_paren = remove # ignore/add/remove/force/not_defined +nl_func_def_paren = remove # ignore/add/remove/force # Overrides nl_func_def_paren for functions with no parameters. -nl_func_def_paren_empty = ignore # ignore/add/remove/force/not_defined +nl_func_def_paren_empty = ignore # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' in the # call. -nl_func_call_paren = ignore # ignore/add/remove/force/not_defined +nl_func_call_paren = ignore # ignore/add/remove/force # Overrides nl_func_call_paren for functions with no parameters. -nl_func_call_paren_empty = ignore # ignore/add/remove/force/not_defined +nl_func_call_paren_empty = ignore # ignore/add/remove/force # Add or remove newline after '(' in a function declaration. -nl_func_decl_start = remove # ignore/add/remove/force/not_defined +nl_func_decl_start = remove # ignore/add/remove/force # Add or remove newline after '(' in a function definition. -nl_func_def_start = remove # ignore/add/remove/force/not_defined +nl_func_def_start = remove # ignore/add/remove/force # Overrides nl_func_decl_start when there is only one parameter. -nl_func_decl_start_single = ignore # ignore/add/remove/force/not_defined +nl_func_decl_start_single = ignore # ignore/add/remove/force # Overrides nl_func_def_start when there is only one parameter. -nl_func_def_start_single = ignore # ignore/add/remove/force/not_defined +nl_func_def_start_single = ignore # ignore/add/remove/force # Whether to add a newline after '(' in a function declaration if '(' and ')' # are in different lines. If false, nl_func_decl_start is used instead. @@ -2017,13 +2021,13 @@ nl_func_decl_start_multi_line = false # true/false nl_func_def_start_multi_line = false # true/false # Add or remove newline after each ',' in a function declaration. -nl_func_decl_args = remove # ignore/add/remove/force/not_defined +nl_func_decl_args = remove # ignore/add/remove/force # Add or remove newline after each ',' in a function definition. -nl_func_def_args = remove # ignore/add/remove/force/not_defined +nl_func_def_args = remove # ignore/add/remove/force # Add or remove newline after each ',' in a function call. -nl_func_call_args = ignore # ignore/add/remove/force/not_defined +nl_func_call_args = ignore # ignore/add/remove/force # Whether to add a newline after each ',' in a function declaration if '(' # and ')' are in different lines. If false, nl_func_decl_args is used instead. @@ -2034,16 +2038,16 @@ nl_func_decl_args_multi_line = false # true/false nl_func_def_args_multi_line = false # true/false # Add or remove newline before the ')' in a function declaration. -nl_func_decl_end = remove # ignore/add/remove/force/not_defined +nl_func_decl_end = remove # ignore/add/remove/force # Add or remove newline before the ')' in a function definition. -nl_func_def_end = remove # ignore/add/remove/force/not_defined +nl_func_def_end = remove # ignore/add/remove/force # Overrides nl_func_decl_end when there is only one parameter. -nl_func_decl_end_single = ignore # ignore/add/remove/force/not_defined +nl_func_decl_end_single = ignore # ignore/add/remove/force # Overrides nl_func_def_end when there is only one parameter. -nl_func_def_end_single = ignore # ignore/add/remove/force/not_defined +nl_func_def_end_single = ignore # ignore/add/remove/force # Whether to add a newline before ')' in a function declaration if '(' and ')' # are in different lines. If false, nl_func_decl_end is used instead. @@ -2054,20 +2058,20 @@ nl_func_decl_end_multi_line = false # true/false nl_func_def_end_multi_line = false # true/false # Add or remove newline between '()' in a function declaration. -nl_func_decl_empty = ignore # ignore/add/remove/force/not_defined +nl_func_decl_empty = ignore # ignore/add/remove/force # Add or remove newline between '()' in a function definition. -nl_func_def_empty = ignore # ignore/add/remove/force/not_defined +nl_func_def_empty = ignore # ignore/add/remove/force # Add or remove newline between '()' in a function call. -nl_func_call_empty = ignore # ignore/add/remove/force/not_defined +nl_func_call_empty = ignore # ignore/add/remove/force # Whether to add a newline after '(' in a function call, # has preference over nl_func_call_start_multi_line. -nl_func_call_start = remove # ignore/add/remove/force/not_defined +nl_func_call_start = remove # ignore/add/remove/force # Whether to add a newline before ')' in a function call. -nl_func_call_end = remove # ignore/add/remove/force/not_defined +nl_func_call_end = remove # ignore/add/remove/force # Whether to add a newline after '(' in a function call if '(' and ')' are in # different lines. @@ -2103,41 +2107,45 @@ nl_oc_msg_args_min_params = 0 # unsigned number # (OC) Max code width of Objective-C message before applying nl_oc_msg_args. nl_oc_msg_args_max_code_width = 0 # unsigned number +# (OC) Whether to apply nl_oc_msg_args if some of the parameters are already +# on new lines. Overrides nl_oc_msg_args_min_params and nl_oc_msg_args_max_code_width. +nl_oc_msg_args_finish_multi_line = false # true/false + # Add or remove newline between function signature and '{'. -nl_fdef_brace = force # ignore/add/remove/force/not_defined +nl_fdef_brace = force # ignore/add/remove/force # Add or remove newline between function signature and '{', # if signature ends with ')'. Overrides nl_fdef_brace. -nl_fdef_brace_cond = ignore # ignore/add/remove/force/not_defined +nl_fdef_brace_cond = ignore # ignore/add/remove/force # Add or remove newline between C++11 lambda signature and '{'. -nl_cpp_ldef_brace = ignore # ignore/add/remove/force/not_defined +nl_cpp_ldef_brace = ignore # ignore/add/remove/force # Add or remove newline between 'return' and the return expression. -nl_return_expr = remove # ignore/add/remove/force/not_defined +nl_return_expr = remove # ignore/add/remove/force # Add or remove newline between 'throw' and the throw expression. -nl_throw_expr = ignore # ignore/add/remove/force/not_defined +nl_throw_expr = ignore # ignore/add/remove/force # Whether to add a newline after semicolons, except in 'for' statements. nl_after_semicolon = false # true/false # (Java) Add or remove newline between the ')' and '{{' of the double brace # initializer. -nl_paren_dbrace_open = ignore # ignore/add/remove/force/not_defined +nl_paren_dbrace_open = ignore # ignore/add/remove/force # Whether to add a newline after the type in an unnamed temporary # direct-list-initialization, better: # before a direct-list-initialization. -nl_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined +nl_type_brace_init_lst = ignore # ignore/add/remove/force # Whether to add a newline after the open brace in an unnamed temporary # direct-list-initialization. -nl_type_brace_init_lst_open = ignore # ignore/add/remove/force/not_defined +nl_type_brace_init_lst_open = ignore # ignore/add/remove/force # Whether to add a newline before the close brace in an unnamed temporary # direct-list-initialization. -nl_type_brace_init_lst_close = ignore # ignore/add/remove/force/not_defined +nl_type_brace_init_lst_close = ignore # ignore/add/remove/force # Whether to add a newline before '{'. nl_before_brace_open = false # true/false @@ -2168,7 +2176,7 @@ nl_after_vbrace_close = false # true/false # Add or remove newline between the close brace and identifier, # as in 'struct { int a; } <here> b;'. Affects enumerations, unions and # structures. If set to ignore, uses nl_after_brace_close. -nl_brace_struct_var = ignore # ignore/add/remove/force/not_defined +nl_brace_struct_var = ignore # ignore/add/remove/force # Whether to alter newlines in '#define' macros. nl_define_macro = false # true/false @@ -2186,41 +2194,41 @@ nl_squeeze_ifdef = false # true/false nl_squeeze_ifdef_top_level = false # true/false # Add or remove blank line before 'if'. -nl_before_if = ignore # ignore/add/remove/force/not_defined +nl_before_if = ignore # ignore/add/remove/force # Add or remove blank line after 'if' statement. Add/Force work only if the # next token is not a closing brace. -nl_after_if = ignore # ignore/add/remove/force/not_defined +nl_after_if = ignore # ignore/add/remove/force # Add or remove blank line before 'for'. -nl_before_for = ignore # ignore/add/remove/force/not_defined +nl_before_for = ignore # ignore/add/remove/force # Add or remove blank line after 'for' statement. -nl_after_for = ignore # ignore/add/remove/force/not_defined +nl_after_for = ignore # ignore/add/remove/force # Add or remove blank line before 'while'. -nl_before_while = ignore # ignore/add/remove/force/not_defined +nl_before_while = ignore # ignore/add/remove/force # Add or remove blank line after 'while' statement. -nl_after_while = ignore # ignore/add/remove/force/not_defined +nl_after_while = ignore # ignore/add/remove/force # Add or remove blank line before 'switch'. -nl_before_switch = ignore # ignore/add/remove/force/not_defined +nl_before_switch = ignore # ignore/add/remove/force # Add or remove blank line after 'switch' statement. -nl_after_switch = ignore # ignore/add/remove/force/not_defined +nl_after_switch = ignore # ignore/add/remove/force # Add or remove blank line before 'synchronized'. -nl_before_synchronized = ignore # ignore/add/remove/force/not_defined +nl_before_synchronized = ignore # ignore/add/remove/force # Add or remove blank line after 'synchronized' statement. -nl_after_synchronized = ignore # ignore/add/remove/force/not_defined +nl_after_synchronized = ignore # ignore/add/remove/force # Add or remove blank line before 'do'. -nl_before_do = ignore # ignore/add/remove/force/not_defined +nl_before_do = ignore # ignore/add/remove/force # Add or remove blank line after 'do/while' statement. -nl_after_do = ignore # ignore/add/remove/force/not_defined +nl_after_do = ignore # ignore/add/remove/force # Ignore nl_before_{if,for,switch,do,synchronized} if the control # statement is immediately after a case statement. @@ -2237,10 +2245,10 @@ nl_before_return = false # true/false nl_after_return = false # true/false # Whether to put a blank line before a member '.' or '->' operators. -nl_before_member = ignore # ignore/add/remove/force/not_defined +nl_before_member = ignore # ignore/add/remove/force # (Java) Whether to put a blank line after a member '.' or '->' operators. -nl_after_member = ignore # ignore/add/remove/force/not_defined +nl_after_member = ignore # ignore/add/remove/force # Whether to double-space commented-entries in 'struct'/'union'/'enum'. nl_ds_struct_enum_cmt = false # true/false @@ -2251,11 +2259,11 @@ nl_ds_struct_enum_close_brace = false # true/false # Add or remove newline before or after (depending on pos_class_colon) a class # colon, as in 'class Foo <here> : <or here> public Bar'. -nl_class_colon = ignore # ignore/add/remove/force/not_defined +nl_class_colon = ignore # ignore/add/remove/force # Add or remove newline around a class constructor colon. The exact position # depends on nl_constr_init_args, pos_constr_colon and pos_constr_comma. -nl_constr_colon = ignore # ignore/add/remove/force/not_defined +nl_constr_colon = ignore # ignore/add/remove/force # Whether to collapse a two-line namespace, like 'namespace foo\n{ decl; }' # into a single line. If true, prevents other brace newline rules from turning @@ -2509,7 +2517,7 @@ nl_around_cs_property = 0 # unsigned number nl_between_get_set = 0 # unsigned number # (C#) Add or remove newline between property and the '{'. -nl_property_brace = ignore # ignore/add/remove/force/not_defined +nl_property_brace = ignore # ignore/add/remove/force # Whether to remove blank lines after '{'. eat_blanks_after_open_brace = true # true/false @@ -2526,10 +2534,10 @@ nl_remove_extra_newlines = 0 # unsigned number # (Java) Add or remove newline after an annotation statement. Only affects # annotations that are after a newline. -nl_after_annotation = ignore # ignore/add/remove/force/not_defined +nl_after_annotation = ignore # ignore/add/remove/force # (Java) Add or remove newline between two annotations. -nl_between_annotation = ignore # ignore/add/remove/force/not_defined +nl_between_annotation = ignore # ignore/add/remove/force # The number of newlines before a whole-file #ifdef. # @@ -3168,17 +3176,17 @@ cmt_insert_before_ctor_dtor = false # true/false # # Add or remove braces on a single-line 'do' statement. -mod_full_brace_do = add # ignore/add/remove/force/not_defined +mod_full_brace_do = add # ignore/add/remove/force # Add or remove braces on a single-line 'for' statement. -mod_full_brace_for = add # ignore/add/remove/force/not_defined +mod_full_brace_for = add # ignore/add/remove/force # (Pawn) Add or remove braces on a single-line function definition. -mod_full_brace_function = ignore # ignore/add/remove/force/not_defined +mod_full_brace_function = ignore # ignore/add/remove/force # Add or remove braces on a single-line 'if' statement. Braces will not be # removed if the braced statement contains an 'else'. -mod_full_brace_if = add # ignore/add/remove/force/not_defined +mod_full_brace_if = add # ignore/add/remove/force # Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either # have, or do not have, braces. Overrides mod_full_brace_if. @@ -3199,10 +3207,10 @@ mod_full_brace_if_chain = 0 # unsigned number mod_full_brace_if_chain_only = false # true/false # Add or remove braces on single-line 'while' statement. -mod_full_brace_while = add # ignore/add/remove/force/not_defined +mod_full_brace_while = add # ignore/add/remove/force # Add or remove braces on single-line 'using ()' statement. -mod_full_brace_using = ignore # ignore/add/remove/force/not_defined +mod_full_brace_using = ignore # ignore/add/remove/force # Don't remove braces around statements that span N newlines mod_full_brace_nl = 0 # unsigned number @@ -3224,10 +3232,10 @@ mod_full_brace_nl = 0 # unsigned number mod_full_brace_nl_block_rem_mlcond = false # true/false # Add or remove unnecessary parentheses on 'return' statement. -mod_paren_on_return = ignore # ignore/add/remove/force/not_defined +mod_paren_on_return = ignore # ignore/add/remove/force # Add or remove unnecessary parentheses on 'throw' statement. -mod_paren_on_throw = ignore # ignore/add/remove/force/not_defined +mod_paren_on_throw = ignore # ignore/add/remove/force # (Pawn) Whether to change optional semicolons to real semicolons. mod_pawn_semicolon = false # true/false @@ -3323,14 +3331,14 @@ mod_move_case_return = false # true/false # Add or remove braces around a fully braced case statement. Will only remove # braces if there are no variable declarations in the block. -mod_case_brace = ignore # ignore/add/remove/force/not_defined +mod_case_brace = ignore # ignore/add/remove/force # Whether to remove a void 'return;' that appears as the last statement in a # function. mod_remove_empty_return = false # true/false # Add or remove the comma after the last value of an enumeration. -mod_enum_last_comma = add # ignore/add/remove/force/not_defined +mod_enum_last_comma = add # ignore/add/remove/force # Syntax to use for infinite loops. # @@ -3347,28 +3355,28 @@ mod_enum_last_comma = add # ignore/add/remove/force/not_defined mod_infinite_loop = 2 # unsigned number # Add or remove the 'int' keyword in 'int short'. -mod_int_short = remove # ignore/add/remove/force/not_defined +mod_int_short = remove # ignore/add/remove/force # Add or remove the 'int' keyword in 'short int'. -mod_short_int = remove # ignore/add/remove/force/not_defined +mod_short_int = remove # ignore/add/remove/force # Add or remove the 'int' keyword in 'int long'. -mod_int_long = remove # ignore/add/remove/force/not_defined +mod_int_long = remove # ignore/add/remove/force # Add or remove the 'int' keyword in 'long int'. -mod_long_int = remove # ignore/add/remove/force/not_defined +mod_long_int = remove # ignore/add/remove/force # Add or remove the 'int' keyword in 'int signed'. -mod_int_signed = remove # ignore/add/remove/force/not_defined +mod_int_signed = remove # ignore/add/remove/force # Add or remove the 'int' keyword in 'signed int'. -mod_signed_int = remove # ignore/add/remove/force/not_defined +mod_signed_int = remove # ignore/add/remove/force # Add or remove the 'int' keyword in 'int unsigned'. -mod_int_unsigned = remove # ignore/add/remove/force/not_defined +mod_int_unsigned = remove # ignore/add/remove/force # Add or remove the 'int' keyword in 'unsigned int'. -mod_unsigned_int = remove # ignore/add/remove/force/not_defined +mod_unsigned_int = remove # ignore/add/remove/force # If there is a situation where mod_int_* and mod_*_int would result in # multiple int keywords, whether to keep the rightmost int (the default) or the @@ -3418,7 +3426,7 @@ pp_indent_with_tabs = -1 # number # Add or remove indentation of preprocessor directives inside #if blocks # at brace level 0 (file-level). -pp_indent = remove # ignore/add/remove/force/not_defined +pp_indent = remove # ignore/add/remove/force # Whether to indent #if/#else/#endif at the brace level. If false, these are # indented from column 1. @@ -3437,7 +3445,7 @@ pp_indent_at_level0 = false # true/false pp_indent_count = 1 # unsigned number # Add or remove space after # based on pp level of #if blocks. -pp_space_after = force # ignore/add/remove/force/not_defined +pp_space_after = force # ignore/add/remove/force # Sets the number of spaces per level added with pp_space_after. pp_space_count = 1 # unsigned number @@ -3515,8 +3523,12 @@ pp_indent_extern = true # true/false # Default: 1 pp_indent_brace = 1 # number -# Whether to print warning messages for unbalanced #if and #else blocks. -# This will print a message in the following cases: +# Action to perform when unbalanced #if and #else blocks are found. +# 0: do nothing +# 1: print a warning message +# 2: terminate the program with an error (EX_SOFTWARE) +# +# The action will be triggered in the following cases: # - if an #ifdef block ends on a different indent level than # where it started from. Example: # @@ -3535,7 +3547,7 @@ pp_indent_brace = 1 # number # } # int j; # #endif -pp_warn_unbalanced_if = false # true/false +pp_unbalanced_if_action = 0 # unsigned number # # Sort includes options @@ -3649,6 +3661,9 @@ debug_decode_the_flags = false # true/false # Default: true debug_use_the_exit_function_pop = true # true/false +# print (or not) the version in the file defined at the command option -o. +debug_print_version = false # true/false + # insert the number of the line at the beginning of each line set_numbering_for_html_output = false # true/false @@ -3732,5 +3747,5 @@ set CLASS_COLON REAL_FATTR_CONST set CLASS_COLON REAL_FATTR_NONNULL_ALL set CLASS_COLON REAL_FATTR_PURE set CLASS_COLON REAL_FATTR_WARN_UNUSED_RESULT -# option(s) with 'not default' value: 135 +# option(s) with 'not default' value: 136 # diff --git a/test/README.md b/test/README.md index 3aafe1273e..45b3322305 100644 --- a/test/README.md +++ b/test/README.md @@ -37,6 +37,7 @@ Layout - `/test/benchmark` : benchmarks - `/test/functional` : functional tests - `/test/unit` : unit tests +- `/test/old/testdir` : old tests (from Vim) - `/test/config` : contains `*.in` files which are transformed into `*.lua` files using `configure_file` CMake command: this is for accessing CMake variables in lua tests. @@ -44,11 +45,13 @@ Layout parser: normally used to make macros not accessible via this mechanism accessible the other way. - `/test/*/preload.lua` : modules preloaded by busted `--helper` option -- `/test/**/helpers.lua` : common utility functions for test code +- `/test/**/testutil.lua` : common utility functions in the context of the test + runner +- `/test/**/testnvim.lua` : common utility functions in the context of the + test session (RPC channel to the Nvim child process created by clear() for each test) - `/test/*/**/*_spec.lua` : actual tests. Files that do not end with - `_spec.lua` are libraries like `/test/**/helpers.lua`, except that they have + `_spec.lua` are libraries like `/test/**/testutil.lua`, except that they have some common topic. -- `/test/old/testdir` : old tests (from Vim) Running tests @@ -119,7 +122,7 @@ Debugging tests If `$VALGRIND` is also set it will pass `--vgdb=yes` to valgrind instead of starting gdbserver directly. - See [test/functional/helpers.lua](https://github.com/neovim/neovim/blob/9cadbf1d36b63f53f0de48c8c5ff6c752ff05d70/test/functional/helpers.lua#L52-L69) for details. + See `nvim_argv` in https://github.com/neovim/neovim/blob/master/test/functional/testnvim.lua. - Hanging tests can happen due to unexpected "press-enter" prompts. The default screen width is 50 columns. Commands that try to print lines longer @@ -218,7 +221,7 @@ Guidelines - Luajit needs to know about type and constant declarations used in function prototypes. The - [helpers.lua](https://github.com/neovim/neovim/blob/master/test/unit/helpers.lua) + [testutil.lua](https://github.com/neovim/neovim/blob/master/test/unit/testutil.lua) file automatically parses `types.h`, so types used in the tested functions could be moved to it to avoid having to rewrite the declarations in the test files. @@ -252,7 +255,7 @@ by the semantic component they are testing. - _Functional tests_ ([test/functional](https://github.com/neovim/neovim/tree/master/test/functional)) are higher-level (plugins and user input) than unit tests; they are organized - by concept. + by concept. - Try to find an existing `test/functional/*/*_spec.lua` group that makes sense, before creating a new one. @@ -276,9 +279,9 @@ the file). Configuration ============= -Test behaviour is affected by environment variables. Currently supported -(Functional, Unit, Benchmarks) (when Defined; when set to _1_; when defined, -treated as Integer; when defined, treated as String; when defined, treated as +Test behaviour is affected by environment variables. Currently supported +(Functional, Unit, Benchmarks) (when Defined; when set to _1_; when defined, +treated as Integer; when defined, treated as String; when defined, treated as Number; !must be defined to function properly): - `BUSTED_ARGS` (F) (U): arguments forwarded to `busted`. @@ -297,7 +300,7 @@ Number; !must be defined to function properly): - `VALGRIND` (F) (D): makes nvim instances to be run under `valgrind`. Log files are named `valgrind-%p.log` in this case. Note that non-empty valgrind log may fail tests. Valgrind arguments may be seen in - `/test/functional/helpers.lua`. May be used in conjunction with `GDB`. + `/test/functional/testnvim.lua`. May be used in conjunction with `GDB`. - `VALGRIND_LOG` (F) (S): overrides valgrind log file name used for `VALGRIND`. diff --git a/test/benchmark/autocmd_spec.lua b/test/benchmark/autocmd_spec.lua index b3421d86eb..af5fa1c258 100644 --- a/test/benchmark/autocmd_spec.lua +++ b/test/benchmark/autocmd_spec.lua @@ -1,7 +1,7 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local exec_lua = helpers.exec_lua +local clear = n.clear +local exec_lua = n.exec_lua local N = 7500 diff --git a/test/benchmark/bench_regexp_spec.lua b/test/benchmark/bench_regexp_spec.lua index 4a7c50557b..2c2b8dc359 100644 --- a/test/benchmark/bench_regexp_spec.lua +++ b/test/benchmark/bench_regexp_spec.lua @@ -1,8 +1,9 @@ -- Test for benchmarking the RE engine. -local helpers = require('test.functional.helpers')(after_each) -local insert, source = helpers.insert, helpers.source -local clear, command = helpers.clear, helpers.command +local n = require('test.functional.testnvim')() + +local insert, source = n.insert, n.source +local clear, command = n.clear, n.command -- Temporary file for gathering benchmarking results for each regexp engine. local result_file = 'benchmark.out' diff --git a/test/benchmark/extmark_spec.lua b/test/benchmark/extmark_spec.lua new file mode 100644 index 0000000000..0d284b363c --- /dev/null +++ b/test/benchmark/extmark_spec.lua @@ -0,0 +1,45 @@ +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec_lua = n.exec_lua + +describe('extmark perf', function() + before_each(function() + clear() + + exec_lua([[ + out = {} + function start() + ts = vim.uv.hrtime() + end + function stop(name) + out[#out+1] = ('%14.6f ms - %s'):format((vim.uv.hrtime() - ts) / 1000000, name) + end + ]]) + end) + + after_each(function() + for _, line in ipairs(exec_lua([[return out]])) do + print(line) + end + end) + + it('repeatedly calling nvim_buf_clear_namespace #28615', function() + exec_lua([[ + vim.api.nvim_buf_set_lines(0, 0, -1, true, { 'foo', 'bar' }) + local ns0 = vim.api.nvim_create_namespace('ns0') + local ns1 = vim.api.nvim_create_namespace('ns1') + + for _ = 1, 10000 do + vim.api.nvim_buf_set_extmark(0, ns0, 0, 0, {}) + end + vim.api.nvim_buf_set_extmark(0, ns1, 1, 0, {}) + + start() + for _ = 1, 10000 do + vim.api.nvim_buf_clear_namespace(0, ns1, 0, -1) + end + stop('nvim_buf_clear_namespace') + ]]) + end) +end) diff --git a/test/benchmark/preload.lua b/test/benchmark/preload.lua index 1971ef77cc..a7b815ddf8 100644 --- a/test/benchmark/preload.lua +++ b/test/benchmark/preload.lua @@ -1,4 +1,4 @@ -- Modules loaded here will not be cleared and reloaded by Busted. -- Busted started doing this to help provide more isolation. See issue #62 -- for more information about this. -local helpers = require('test.functional.helpers') +local n = require('test.functional.testnvim') diff --git a/test/benchmark/screenpos_spec.lua b/test/benchmark/screenpos_spec.lua index 8a80712dfa..db8f918817 100644 --- a/test/benchmark/screenpos_spec.lua +++ b/test/benchmark/screenpos_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local api = helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local api = n.api + local function rand_utf8(count, seed) math.randomseed(seed) local symbols = { 'i', 'À', 'Ⱡ', '𐀀' } @@ -78,7 +80,7 @@ local N = 10000 local function benchmark(lines, expected_value) local lnum = #lines - local results = helpers.exec_lua( + local results = n.exec_lua( [==[ local N, lnum = ... @@ -99,7 +101,7 @@ local function benchmark(lines, expected_value) ) for _, value in ipairs(results[1]) do - helpers.eq(expected_value, value) + t.eq(expected_value, value) end local stats = results[2] table.sort(stats) @@ -119,7 +121,7 @@ end local function benchmarks(benchmark_results) describe('screenpos() perf', function() - before_each(helpers.clear) + before_each(n.clear) -- no breakindent for li, lines_type in ipairs(benchmark_lines) do @@ -134,7 +136,7 @@ local function benchmarks(benchmark_results) screen:attach() api.nvim_buf_set_lines(0, 0, 1, false, lines) -- for smaller screen expect (last line always different, first line same as others) - helpers.feed('G$') + n.feed('G$') screen:expect(result.screen) benchmark(lines, result.value) end) @@ -153,9 +155,9 @@ local function benchmarks(benchmark_results) local screen = Screen.new(width, height + 1) screen:attach() api.nvim_buf_set_lines(0, 0, 1, false, lines) - helpers.command('set breakindent') + n.command('set breakindent') -- for smaller screen expect (last line always different, first line same as others) - helpers.feed('G$') + n.feed('G$') screen:expect(result.screen) benchmark(lines, result.value) end) diff --git a/test/benchmark/treesitter_spec.lua b/test/benchmark/treesitter_spec.lua index 7c9906a7b2..b13b0fd9da 100644 --- a/test/benchmark/treesitter_spec.lua +++ b/test/benchmark/treesitter_spec.lua @@ -1,7 +1,7 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local exec_lua = helpers.exec_lua +local clear = n.clear +local exec_lua = n.exec_lua describe('treesitter perf', function() setup(function() @@ -9,7 +9,7 @@ describe('treesitter perf', function() end) it('can handle large folds', function() - helpers.command 'edit ./src/nvim/eval.c' + n.command 'edit ./src/nvim/eval.c' exec_lua [[ local parser = vim.treesitter.get_parser(0, "c", {}) vim.treesitter.highlighter.new(parser) diff --git a/test/busted/outputHandlers/TAP.lua b/test/busted/outputHandlers/TAP.lua index 5de48c0ad3..2439547050 100644 --- a/test/busted/outputHandlers/TAP.lua +++ b/test/busted/outputHandlers/TAP.lua @@ -1,13 +1,13 @@ -- Extends the upstream TAP handler, to display the log with suiteEnd. -local global_helpers = require('test.helpers') +local t_global = require('test.testutil') return function(options) local busted = require 'busted' local handler = require 'busted.outputHandlers.TAP'(options) local suiteEnd = function() - io.write(global_helpers.read_nvim_log(nil, true)) + io.write(t_global.read_nvim_log(nil, true)) return nil, true end busted.subscribe({ 'suite', 'end' }, suiteEnd) diff --git a/test/busted/outputHandlers/nvim.lua b/test/busted/outputHandlers/nvim.lua index 28855df261..a090761b5d 100644 --- a/test/busted/outputHandlers/nvim.lua +++ b/test/busted/outputHandlers/nvim.lua @@ -1,5 +1,5 @@ local pretty = require 'pl.pretty' -local global_helpers = require('test.helpers') +local t_global = require('test.testutil') local colors = setmetatable({}, { __index = function() @@ -236,7 +236,7 @@ return function(options) io.write(suiteEndString:format(testCount, tests, fileCount, files, elapsedTime_ms)) io.write(getSummaryString()) if failureCount > 0 or errorCount > 0 then - io.write(global_helpers.read_nvim_log(nil, true)) + io.write(t_global.read_nvim_log(nil, true)) end io.flush() diff --git a/test/client/uv_stream.lua b/test/client/uv_stream.lua index 0540c44eb2..adf002ba1e 100644 --- a/test/client/uv_stream.lua +++ b/test/client/uv_stream.lua @@ -136,7 +136,7 @@ function ChildProcessStream.spawn(argv, env, io_extra) end --- @diagnostic disable-next-line:missing-fields self._proc, self._pid = uv.spawn(prog, { - stdio = { self._child_stdin, self._child_stdout, 2, io_extra }, + stdio = { self._child_stdin, self._child_stdout, 1, io_extra }, args = args, --- @diagnostic disable-next-line:assign-type-mismatch env = env, diff --git a/test/cmakeconfig/paths.lua.in b/test/cmakeconfig/paths.lua.in index a35dbe8901..ce0eb870e0 100644 --- a/test/cmakeconfig/paths.lua.in +++ b/test/cmakeconfig/paths.lua.in @@ -1,22 +1,22 @@ -local module = {} +local M = {} -module.include_paths = {} +M.include_paths = {} for p in ("${TEST_INCLUDE_DIRS}" .. ";"):gmatch("[^;]+") do - table.insert(module.include_paths, p) + table.insert(M.include_paths, p) end -module.test_build_dir = "${CMAKE_BINARY_DIR}" -module.test_source_path = "${CMAKE_SOURCE_DIR}" -module.test_lua_prg = "${LUA_PRG}" -module.test_luajit_prg = "" -if module.test_luajit_prg == '' then - if module.test_lua_prg:sub(-6) == 'luajit' then - module.test_luajit_prg = module.test_lua_prg +M.test_build_dir = "${CMAKE_BINARY_DIR}" +M.test_source_path = "${CMAKE_SOURCE_DIR}" +M.test_lua_prg = "${LUA_PRG}" +M.test_luajit_prg = "" +if M.test_luajit_prg == '' then + if M.test_lua_prg:sub(-6) == 'luajit' then + M.test_luajit_prg = M.test_lua_prg else - module.test_luajit_prg = nil + M.test_luajit_prg = nil end end -table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/include") -table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/src/nvim/auto") +table.insert(M.include_paths, "${CMAKE_BINARY_DIR}/include") +table.insert(M.include_paths, "${CMAKE_BINARY_DIR}/src/nvim/auto") -return module +return M diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua index e89abf6c64..3f9883f43f 100644 --- a/test/functional/api/autocmd_spec.lua +++ b/test/functional/api/autocmd_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local neq = helpers.neq -local exec_lua = helpers.exec_lua -local matches = helpers.matches -local api = helpers.api -local source = helpers.source -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local neq = t.neq +local exec_lua = n.exec_lua +local matches = t.matches +local api = n.api +local source = n.source +local pcall_err = t.pcall_err before_each(clear) @@ -355,6 +356,44 @@ describe('autocmd api', function() test({ 'list' }) test({ foo = 'bar' }) end) + + it('function in arbitrary data is passed to all autocmds #28353', function() + eq( + 1303, + exec_lua([[ + local res = 1 + + local fun = function(m, x) + res = res * m + x + end + + local group = vim.api.nvim_create_augroup('MyTest', { clear = false }) + + vim.api.nvim_create_autocmd('User', { + group = group, + callback = function(payload) + payload.data.fun(10, payload.data.x) + end, + pattern = 'MyEvent', + }) + vim.api.nvim_create_autocmd('User', { + group = group, + callback = function(payload) + payload.data.fun(100, payload.data.x) + end, + pattern = 'MyEvent', + }) + + vim.api.nvim_exec_autocmds('User', { + group = group, + pattern = 'MyEvent', + data = { x = 3, fun = fun }, + }) + + return res + ]]) + ) + end) end) describe('nvim_get_autocmds', function() @@ -610,15 +649,17 @@ describe('autocmd api', function() it('can retrieve a callback from an autocmd', function() local content = 'I Am A Callback' api.nvim_set_var('content', content) - - local result = exec_lua([[ + exec_lua([[ local cb = function() return vim.g.content end vim.api.nvim_create_autocmd("User", { pattern = "TestTrigger", desc = "A test autocommand with a callback", callback = cb, }) - local aus = vim.api.nvim_get_autocmds({ event = 'User', pattern = 'TestTrigger'}) + ]]) + + local result = exec_lua([[ + local aus = vim.api.nvim_get_autocmds({ event = 'User', pattern = 'TestTrigger' }) local first = aus[1] return { cb = { @@ -627,9 +668,14 @@ describe('autocmd api', function() } } ]]) + eq({ cb = { type = 'function', can_retrieve = true } }, result) - eq('function', result.cb.type) - eq(true, result.cb.can_retrieve) + -- Also test with Vimscript + source([[ + let s:aus = nvim_get_autocmds({'event': 'User', 'pattern': 'TestTrigger'}) + let g:result = s:aus[0].callback() + ]]) + eq(content, api.nvim_get_var('result')) end) it( diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 78d220ff57..cf69958fd8 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -1,21 +1,23 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local eq = helpers.eq -local ok = helpers.ok -local describe_lua_and_rpc = helpers.describe_lua_and_rpc(describe) -local api = helpers.api -local fn = helpers.fn -local request = helpers.request -local exc_exec = helpers.exc_exec -local exec_lua = helpers.exec_lua -local feed_command = helpers.feed_command -local insert = helpers.insert + +local clear = n.clear +local eq = t.eq +local ok = t.ok +local describe_lua_and_rpc = n.describe_lua_and_rpc(describe) +local api = n.api +local fn = n.fn +local request = n.request +local exc_exec = n.exc_exec +local exec_lua = n.exec_lua +local feed_command = n.feed_command +local insert = n.insert local NIL = vim.NIL -local command = helpers.command -local feed = helpers.feed -local pcall_err = helpers.pcall_err -local assert_alive = helpers.assert_alive +local command = n.command +local feed = n.feed +local pcall_err = t.pcall_err +local assert_alive = n.assert_alive describe('api/buf', function() before_each(clear) @@ -121,6 +123,65 @@ describe('api/buf', function() eq({ 5, 2 }, api.nvim_win_get_cursor(win2)) end) + it('cursor position is maintained consistently with viewport', function() + local screen = Screen.new(20, 12) + screen:set_default_attr_ids { + [1] = { bold = true, foreground = Screen.colors.Blue1 }, + [2] = { reverse = true, bold = true }, + [3] = { reverse = true }, + } + screen:attach() + + local lines = { 'line1', 'line2', 'line3', 'line4', 'line5', 'line6' } + local buf = api.nvim_get_current_buf() + + api.nvim_buf_set_lines(buf, 0, -1, true, lines) + + command('6') + command('new') + screen:expect { + grid = [[ + ^ | + {1:~ }|*4 + {2:[No Name] }| + line5 | + line6 | + {1:~ }|*2 + {3:[No Name] [+] }| + | + ]], + } + + lines[5] = 'boogalo 5' + api.nvim_buf_set_lines(buf, 0, -1, true, lines) + screen:expect { + grid = [[ + ^ | + {1:~ }|*4 + {2:[No Name] }| + boogalo 5 | + line6 | + {1:~ }|*2 + {3:[No Name] [+] }| + | + ]], + } + + command('wincmd w') + screen:expect { + grid = [[ + | + {1:~ }|*4 + {3:[No Name] }| + boogalo 5 | + ^line6 | + {1:~ }|*2 + {2:[No Name] [+] }| + | + ]], + } + end) + it('line_count has defined behaviour for unloaded buffers', function() -- we'll need to know our bufnr for when it gets unloaded local bufnr = api.nvim_buf_get_number(0) @@ -323,20 +384,20 @@ describe('api/buf', function() ]], } - -- inserting just before topline scrolls up api.nvim_buf_set_lines(buf, 3, 3, true, { 'mmm' }) screen:expect { grid = [[ ^ | {1:~ }|*4 {2:[No Name] }| - mmm | wwweeee | xxx | yyy | + zzz | {3:[No Name] [+] }| | ]], + unchanged = true, } end) @@ -402,7 +463,6 @@ describe('api/buf', function() ]], } - -- inserting just before topline scrolls up api.nvim_buf_set_lines(buf, 3, 3, true, { 'mmm' }) screen:expect { grid = [[ @@ -412,10 +472,10 @@ describe('api/buf', function() mmm | wwweeee | {2:[No Name] [+] }| - mmm | wwweeee | xxx | yyy | + zzz | {3:[No Name] [+] }| | ]], @@ -1316,12 +1376,7 @@ describe('api/buf', function() -- immediate call to nvim_win_get_cursor should have returned the same position eq({ 2, 12 }, cursor) -- coladd should be 0 - eq( - 0, - exec_lua([[ - return vim.fn.winsaveview().coladd - ]]) - ) + eq(0, fn.winsaveview().coladd) end) it('does not change cursor screen column when cursor >EOL and row got shorter', function() @@ -1335,9 +1390,7 @@ describe('api/buf', function() -- turn on virtualedit command('set virtualedit=all') -- move cursor after eol - exec_lua([[ - vim.fn.winrestview({ coladd = 5 }) - ]]) + fn.winrestview({ coladd = 5 }) local cursor = exec_lua([[ vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { @@ -1356,12 +1409,7 @@ describe('api/buf', function() -- immediate call to nvim_win_get_cursor should have returned the same position eq({ 2, 26 }, cursor) -- coladd should be increased so that cursor stays in the same screen column - eq( - 13, - exec_lua([[ - return vim.fn.winsaveview().coladd - ]]) - ) + eq(13, fn.winsaveview().coladd) end) it( @@ -1377,9 +1425,7 @@ describe('api/buf', function() -- turn on virtualedit command('set virtualedit=all') -- move cursor after eol - exec_lua([[ - vim.fn.winrestview({ coladd = 21 }) - ]]) + fn.winrestview({ coladd = 21 }) local cursor = exec_lua([[ vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { @@ -1398,12 +1444,7 @@ describe('api/buf', function() -- immediate call to nvim_win_get_cursor should have returned the same position eq({ 1, 38 }, cursor) -- coladd should be increased so that cursor stays in the same screen column - eq( - 2, - exec_lua([[ - return vim.fn.winsaveview().coladd - ]]) - ) + eq(2, fn.winsaveview().coladd) end ) @@ -1420,9 +1461,7 @@ describe('api/buf', function() -- turn on virtualedit command('set virtualedit=all') -- move cursor after eol just a bit - exec_lua([[ - vim.fn.winrestview({ coladd = 3 }) - ]]) + fn.winrestview({ coladd = 3 }) local cursor = exec_lua([[ vim.api.nvim_buf_set_text(0, 0, 15, 2, 11, { @@ -1441,12 +1480,7 @@ describe('api/buf', function() -- immediate call to nvim_win_get_cursor should have returned the same position eq({ 1, 22 }, cursor) -- coladd should become 0 - eq( - 0, - exec_lua([[ - return vim.fn.winsaveview().coladd - ]]) - ) + eq(0, fn.winsaveview().coladd) end ) @@ -1464,9 +1498,7 @@ describe('api/buf', function() -- turn on virtualedit command('set virtualedit=all') -- move cursor after eol - exec_lua([[ - vim.fn.winrestview({ coladd = 28 }) - ]]) + fn.winrestview({ coladd = 28 }) local cursor = exec_lua([[ vim.api.nvim_buf_set_text(0, 0, 15, 3, 11, { @@ -1485,12 +1517,7 @@ describe('api/buf', function() -- immediate call to nvim_win_get_cursor should have returned the same position eq({ 2, 26 }, cursor) -- coladd should be increased so that cursor stays in the same screen column - eq( - 13, - exec_lua([[ - return vim.fn.winsaveview().coladd - ]]) - ) + eq(13, fn.winsaveview().coladd) end ) end) @@ -1686,12 +1713,11 @@ describe('api/buf', function() api.nvim_buf_set_text(0, 0, 0, 1, 3, { 'XXX', 'YYY' }) screen:expect([[ - XXX | - YYY | - ^ | - ~ | - | - + XXX | + YYY | + ^ | + {1:~ }| + | ]]) end) @@ -2024,6 +2050,37 @@ describe('api/buf', function() eq(1, fn.filereadable(new_name)) os.remove(new_name) end) + + describe("with 'autochdir'", function() + local topdir + local oldbuf + local newbuf + + before_each(function() + command('set shellslash') + topdir = fn.getcwd() + t.mkdir(topdir .. '/Xacd') + + oldbuf = api.nvim_get_current_buf() + command('vnew') + newbuf = api.nvim_get_current_buf() + command('set autochdir') + end) + + after_each(function() + n.rmdir(topdir .. '/Xacd') + end) + + it('does not change cwd with non-current buffer', function() + api.nvim_buf_set_name(oldbuf, topdir .. '/Xacd/foo.txt') + eq(topdir, fn.getcwd()) + end) + + it('changes cwd with current buffer', function() + api.nvim_buf_set_name(newbuf, topdir .. '/Xacd/foo.txt') + eq(topdir .. '/Xacd', fn.getcwd()) + end) + end) end) describe('nvim_buf_is_loaded', function() diff --git a/test/functional/api/buffer_updates_spec.lua b/test/functional/api/buffer_updates_spec.lua index 262ca40e28..e030b45396 100644 --- a/test/functional/api/buffer_updates_spec.lua +++ b/test/functional/api/buffer_updates_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq, ok = helpers.eq, helpers.ok -local fn = helpers.fn -local api = helpers.api -local command, eval, next_msg = helpers.command, helpers.eval, helpers.next_msg -local nvim_prog = helpers.nvim_prog -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq, ok = t.eq, t.ok +local fn = n.fn +local api = n.api +local command, eval, next_msg = n.command, n.eval, n.next_msg +local nvim_prog = n.nvim_prog +local pcall_err = t.pcall_err local sleep = vim.uv.sleep -local write_file = helpers.write_file +local write_file = t.write_file local origlines = { 'original line 1', @@ -34,7 +36,7 @@ local function sendkeys(keys) end local function open(activate, lines) - local filename = helpers.tmpname() + local filename = t.tmpname() write_file(filename, table.concat(lines, '\n') .. '\n', true) command('edit ' .. filename) local b = api.nvim_get_current_buf() @@ -511,11 +513,11 @@ describe('API: buffer events:', function() -- create several new sessions, in addition to our main API local sessions = {} - local pipe = helpers.new_pipename() + local pipe = n.new_pipename() eval("serverstart('" .. pipe .. "')") - sessions[1] = helpers.connect(pipe) - sessions[2] = helpers.connect(pipe) - sessions[3] = helpers.connect(pipe) + sessions[1] = n.connect(pipe) + sessions[2] = n.connect(pipe) + sessions[3] = n.connect(pipe) local function request(sessionnr, method, ...) local status, rv = sessions[sessionnr]:request(method, ...) @@ -814,7 +816,7 @@ describe('API: buffer events:', function() clear() sleep(250) -- response - eq(true, helpers.request('nvim_buf_attach', 0, false, {})) + eq(true, n.request('nvim_buf_attach', 0, false, {})) -- notification eq({ [1] = 'notification', diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index f73b9c8b13..a16c6a88e3 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -1,17 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local NIL = vim.NIL -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local api = helpers.api -local matches = helpers.matches -local source = helpers.source -local pcall_err = helpers.pcall_err -local exec_lua = helpers.exec_lua -local assert_alive = helpers.assert_alive -local feed = helpers.feed -local fn = helpers.fn +local clear = n.clear +local command = n.command +local eq = t.eq +local api = n.api +local matches = t.matches +local source = n.source +local pcall_err = t.pcall_err +local exec_lua = n.exec_lua +local assert_alive = n.assert_alive +local feed = n.feed +local fn = n.fn describe('nvim_get_commands', function() local cmd_dict = { diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 2acfbfc949..7b2fe209ba 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -1,20 +1,21 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local request = helpers.request -local eq = helpers.eq -local ok = helpers.ok -local pcall_err = helpers.pcall_err -local insert = helpers.insert -local feed = helpers.feed -local clear = helpers.clear -local command = helpers.command -local exec = helpers.exec -local api = helpers.api -local assert_alive = helpers.assert_alive +local request = n.request +local eq = t.eq +local ok = t.ok +local pcall_err = t.pcall_err +local insert = n.insert +local feed = n.feed +local clear = n.clear +local command = n.command +local exec = n.exec +local api = n.api +local assert_alive = n.assert_alive local function expect(contents) - return eq(contents, helpers.curbuf_contents()) + return eq(contents, n.curbuf_contents()) end local function set_extmark(ns_id, id, line, col, opts) @@ -460,7 +461,7 @@ describe('API/extmarks', function() -- This shouldn't seg fault screen:expect([[ 12345^ 1 | - ~ |*8 + {1:~ }|*8 | ]]) end) @@ -513,7 +514,7 @@ describe('API/extmarks', function() insert('abc') screen:expect([[ ab^c12345 | - ~ |*8 + {1:~ }|*8 | ]]) local rv = get_extmark_by_id(ns, marks[1]) @@ -1568,7 +1569,7 @@ describe('API/extmarks', function() sign_text = '>>', spell = true, virt_lines = { - { { 'lines', 'Macro' }, { '???' } }, + { { 'lines', 'Macro' }, { '???' }, { ';;;', '' } }, { { 'stack', { 'Type', 'Search' } }, { '!!!' } }, }, virt_lines_above = true, @@ -1603,7 +1604,7 @@ describe('API/extmarks', function() sign_text = '>>', spell = true, virt_lines = { - { { 'lines', 'Macro' }, { '???' } }, + { { 'lines', 'Macro' }, { '???' }, { ';;;', '' } }, { { 'stack', { 'Type', 'Search' } }, { '!!!' } }, }, virt_lines_above = true, @@ -1734,16 +1735,17 @@ describe('API/extmarks', function() command('d2') screen:expect([[ S2^aaa bbb ccc | - aaa bbb ccc |*3 - |*2 + {7: }aaa bbb ccc |*3 + {7: } | + | ]]) -- mark is restored with undo_restore == true command('silent undo') screen:expect([[ - S1 ^aaa bbb ccc | - S1S2aaa bbb ccc | - S2 aaa bbb ccc | - aaa bbb ccc |*2 + S1{7: }^aaa bbb ccc | + S2S1aaa bbb ccc | + S2{7: }aaa bbb ccc | + {7: }aaa bbb ccc |*2 | ]]) -- decor is not removed twice @@ -1894,6 +1896,24 @@ describe('Extmarks buffer api with many marks', function() end eq(ns_marks[ns1], get_marks(ns1)) eq(ns_marks[ns2], get_marks(ns2)) + + api.nvim_buf_clear_namespace(0, ns1, 0, 10) + for id, mark in pairs(ns_marks[ns1]) do + if mark[1] < 10 then + ns_marks[ns1][id] = nil + end + end + eq(ns_marks[ns1], get_marks(ns1)) + eq(ns_marks[ns2], get_marks(ns2)) + + api.nvim_buf_clear_namespace(0, ns1, 20, -1) + for id, mark in pairs(ns_marks[ns1]) do + if mark[1] >= 20 then + ns_marks[ns1][id] = nil + end + end + eq(ns_marks[ns1], get_marks(ns1)) + eq(ns_marks[ns2], get_marks(ns2)) end) it('can delete line', function() @@ -1964,7 +1984,7 @@ describe('API/win_extmark', function() grid = [[ non ui-watched line | ui-watched lin^e | - ~ | + {1:~ }| | ]], extmarks = { @@ -2052,7 +2072,7 @@ describe('API/win_extmark', function() grid = [[ ui-watched linupdat^e| e | - ~ | + {1:~ }| | ]], extmarks = { @@ -2079,9 +2099,9 @@ describe('API/win_extmark', function() grid = [[ ## grid 1 [4:--------------------]|*3 - [No Name] [+] | + {3:[No Name] [+] }| [2:--------------------]|*2 - [No Name] [+] | + {2:[No Name] [+] }| [3:--------------------]| ## grid 2 non ui-watched line | @@ -2091,7 +2111,7 @@ describe('API/win_extmark', function() ## grid 4 non ui-watched line | ui-watched lin^e | - ~ | + {1:~ }| ]], extmarks = { [2] = { @@ -2112,13 +2132,13 @@ describe('API/win_extmark', function() grid = [[ ## grid 1 [4:--------------------]|*3 - [No Name] [+] | + {3:[No Name] [+] }| [2:--------------------]|*2 - [No Name] [+] | + {2:[No Name] [+] }| [3:--------------------]| ## grid 2 non ui-watched line | - ui-watched linupd@@@| + ui-watched linupd{1:@@@}| ## grid 3 | ## grid 4 diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 1973d3e1c7..dd0611f184 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local eq, eval = helpers.eq, helpers.eval -local command = helpers.command -local exec_capture = helpers.exec_capture -local api = helpers.api -local fn = helpers.fn -local pcall_err = helpers.pcall_err -local ok = helpers.ok -local assert_alive = helpers.assert_alive + +local clear = n.clear +local eq, eval = t.eq, n.eval +local command = n.command +local exec_capture = n.exec_capture +local api = n.api +local fn = n.fn +local pcall_err = t.pcall_err +local ok = t.ok +local assert_alive = n.assert_alive describe('API: highlight', function() clear() diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index 0decd710e9..995711507f 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -1,17 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local command = helpers.command -local eq, neq = helpers.eq, helpers.neq -local exec_lua = helpers.exec_lua -local exec = helpers.exec -local feed = helpers.feed -local fn = helpers.fn -local api = helpers.api -local source = helpers.source -local pcall_err = helpers.pcall_err - -local shallowcopy = helpers.shallowcopy +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq, neq = t.eq, t.neq +local exec_lua = n.exec_lua +local exec = n.exec +local feed = n.feed +local fn = n.fn +local api = n.api +local matches = t.matches +local source = n.source +local pcall_err = t.pcall_err + +local shallowcopy = t.shallowcopy local sleep = vim.uv.sleep local sid_api_client = -9 @@ -398,7 +400,9 @@ describe('nvim_get_keymap', function() 0, exec_lua([[ GlobalCount = 0 - vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]]) ) @@ -738,7 +742,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() end end - it('can set mappings containing literal keycodes', function() + it('can set mappings containing C0 control codes', function() api.nvim_set_keymap('n', '\n\r\n', 'rhs', {}) local expected = generate_mapargs('n', '<NL><CR><NL>', 'rhs') eq(expected, get_mapargs('n', '<NL><CR><NL>')) @@ -951,7 +955,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() 0, exec_lua [[ GlobalCount = 0 - vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]] ) @@ -963,34 +969,38 @@ describe('nvim_set_keymap, nvim_del_keymap', function() it(':map command shows lua mapping correctly', function() exec_lua [[ - vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() print('jkl;') end }) + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() print('jkl;') end, + }) ]] - assert.truthy( - string.match( - exec_lua [[return vim.api.nvim_exec2(':nmap asdf', { output = true }).output]], - '^\nn asdf <Lua %d+>' - ) + matches( + '^\nn asdf <Lua %d+>', + exec_lua [[return vim.api.nvim_exec2(':nmap asdf', { output = true }).output]] ) end) it('mapcheck() returns lua mapping correctly', function() exec_lua [[ - vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() print('jkl;') end }) + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() print('jkl;') end, + }) ]] - assert.truthy(string.match(fn.mapcheck('asdf', 'n'), '^<Lua %d+>')) + matches('^<Lua %d+>', fn.mapcheck('asdf', 'n')) end) - it('maparg() returns lua mapping correctly', function() + it('maparg() and maplist() return lua mapping correctly', function() eq( 0, exec_lua([[ GlobalCount = 0 - vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]]) ) - assert.truthy(string.match(fn.maparg('asdf', 'n'), '^<Lua %d+>')) + matches('^<Lua %d+>', fn.maparg('asdf', 'n')) local mapargs = fn.maparg('asdf', 'n', false, true) mapargs.callback = nil @@ -1010,11 +1020,20 @@ describe('nvim_set_keymap, nvim_del_keymap', function() call maparg('asdf', 'n', v:false, v:true).callback() ]]) eq(2, exec_lua([[return GlobalCount]])) + + api.nvim_eval([[ + maplist()->filter({_, m -> m.lhs == 'asdf'})->foreach({_, m -> m.callback()}) + ]]) + eq(3, exec_lua([[return GlobalCount]])) end) it('can make lua expr mappings replacing keycodes', function() exec_lua [[ - vim.api.nvim_set_keymap('n', 'aa', '', {callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, expr = true, replace_keycodes = true }) + vim.api.nvim_set_keymap('n', 'aa', '', { + callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, + expr = true, + replace_keycodes = true, + }) ]] feed('aa') @@ -1024,7 +1043,10 @@ describe('nvim_set_keymap, nvim_del_keymap', function() it('can make lua expr mappings without replacing keycodes', function() exec_lua [[ - vim.api.nvim_set_keymap('i', 'aa', '', {callback = function() return '<space>' end, expr = true }) + vim.api.nvim_set_keymap('i', 'aa', '', { + callback = function() return '<space>' end, + expr = true, + }) ]] feed('iaa<esc>') @@ -1034,7 +1056,10 @@ describe('nvim_set_keymap, nvim_del_keymap', function() it('lua expr mapping returning nil is equivalent to returning an empty string', function() exec_lua [[ - vim.api.nvim_set_keymap('i', 'aa', '', {callback = function() return nil end, expr = true }) + vim.api.nvim_set_keymap('i', 'aa', '', { + callback = function() return nil end, + expr = true, + }) ]] feed('iaa<esc>') @@ -1047,7 +1072,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() 0, exec_lua [[ VisibleCount = 0 - vim.api.nvim_set_keymap('i', '<F2>', '', {callback = function() VisibleCount = VisibleCount + vim.fn.pumvisible() end}) + vim.api.nvim_set_keymap('i', '<F2>', '', { + callback = function() VisibleCount = VisibleCount + vim.fn.pumvisible() end, + }) return VisibleCount ]] ) @@ -1060,7 +1087,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() 0, exec_lua [[ OpCount = 0 - vim.api.nvim_set_keymap('o', '<F2>', '', {callback = function() OpCount = OpCount + 1 end}) + vim.api.nvim_set_keymap('o', '<F2>', '', { + callback = function() OpCount = OpCount + 1 end, + }) return OpCount ]] ) @@ -1075,7 +1104,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() 0, exec_lua [[ GlobalCount = 0 - vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]] ) @@ -1085,7 +1116,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() eq(1, exec_lua [[return GlobalCount]]) exec_lua [[ - vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end }) + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount - 1 end, + }) ]] feed('asdf\n') @@ -1098,7 +1131,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() 0, exec_lua [[ GlobalCount = 0 - vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]] ) @@ -1107,14 +1142,12 @@ describe('nvim_set_keymap, nvim_del_keymap', function() eq(1, exec_lua [[return GlobalCount]]) - exec_lua [[ - vim.api.nvim_del_keymap('n', 'asdf' ) - ]] + exec_lua [[vim.api.nvim_del_keymap('n', 'asdf' )]] feed('asdf\n') eq(1, exec_lua [[return GlobalCount]]) - eq('\nNo mapping found', helpers.exec_capture('nmap asdf')) + eq('\nNo mapping found', n.exec_capture('nmap asdf')) end) it('no double-free when unmapping simplifiable lua mappings', function() @@ -1122,7 +1155,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() 0, exec_lua [[ GlobalCount = 0 - vim.api.nvim_set_keymap('n', '<C-I>', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_set_keymap('n', '<C-I>', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]] ) @@ -1131,29 +1166,30 @@ describe('nvim_set_keymap, nvim_del_keymap', function() eq(1, exec_lua [[return GlobalCount]]) - exec_lua [[ - vim.api.nvim_del_keymap('n', '<C-I>') - ]] + exec_lua [[vim.api.nvim_del_keymap('n', '<C-I>')]] feed('<C-I>\n') eq(1, exec_lua [[return GlobalCount]]) - eq('\nNo mapping found', helpers.exec_capture('nmap <C-I>')) + eq('\nNo mapping found', n.exec_capture('nmap <C-I>')) end) it('can set descriptions on mappings', function() api.nvim_set_keymap('n', 'lhs', 'rhs', { desc = 'map description' }) eq(generate_mapargs('n', 'lhs', 'rhs', { desc = 'map description' }), get_mapargs('n', 'lhs')) - eq('\nn lhs rhs\n map description', helpers.exec_capture('nmap lhs')) + eq('\nn lhs rhs\n map description', n.exec_capture('nmap lhs')) end) it('can define !-mode abbreviations with lua callbacks', function() exec_lua [[ GlobalCount = 0 - vim.api.nvim_set_keymap('!a', 'foo', '', {expr = true, callback = function() - GlobalCount = GlobalCount + 1 - return tostring(GlobalCount) - end}) + vim.api.nvim_set_keymap('!a', 'foo', '', { + expr = true, + callback = function() + GlobalCount = GlobalCount + 1 + return tostring(GlobalCount) + end, + }) ]] feed 'iThe foo and the bar and the foo again<esc>' @@ -1166,10 +1202,13 @@ describe('nvim_set_keymap, nvim_del_keymap', function() it('can define insert mode abbreviations with lua callbacks', function() exec_lua [[ GlobalCount = 0 - vim.api.nvim_set_keymap('ia', 'foo', '', {expr = true, callback = function() - GlobalCount = GlobalCount + 1 - return tostring(GlobalCount) - end}) + vim.api.nvim_set_keymap('ia', 'foo', '', { + expr = true, + callback = function() + GlobalCount = GlobalCount + 1 + return tostring(GlobalCount) + end, + }) ]] feed 'iThe foo and the bar and the foo again<esc>' @@ -1182,10 +1221,13 @@ describe('nvim_set_keymap, nvim_del_keymap', function() it('can define cmdline mode abbreviations with lua callbacks', function() exec_lua [[ GlobalCount = 0 - vim.api.nvim_set_keymap('ca', 'foo', '', {expr = true, callback = function() - GlobalCount = GlobalCount + 1 - return tostring(GlobalCount) - end}) + vim.api.nvim_set_keymap('ca', 'foo', '', { + expr = true, + callback = function() + GlobalCount = GlobalCount + 1 + return tostring(GlobalCount) + end, + }) ]] feed 'iThe foo and the bar and the foo again<esc>' @@ -1290,7 +1332,7 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() it('does not crash when setting mapping in a non-existing buffer #13541', function() pcall_err(api.nvim_buf_set_keymap, 100, '', 'lsh', 'irhs<Esc>', {}) - helpers.assert_alive() + n.assert_alive() end) it('can make lua mappings', function() @@ -1298,7 +1340,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() 0, exec_lua [[ GlobalCount = 0 - vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]] ) @@ -1310,7 +1354,11 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() it('can make lua expr mappings replacing keycodes', function() exec_lua [[ - vim.api.nvim_buf_set_keymap(0, 'n', 'aa', '', {callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, expr = true, replace_keycodes = true }) + vim.api.nvim_buf_set_keymap(0, 'n', 'aa', '', { + callback = function() return '<Insert>π<C-V><M-π>foo<lt><Esc>' end, + expr = true, + replace_keycodes = true, + }) ]] feed('aa') @@ -1320,7 +1368,10 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() it('can make lua expr mappings without replacing keycodes', function() exec_lua [[ - vim.api.nvim_buf_set_keymap(0, 'i', 'aa', '', {callback = function() return '<space>' end, expr = true }) + vim.api.nvim_buf_set_keymap(0, 'i', 'aa', '', { + callback = function() return '<space>' end, + expr = true, + }) ]] feed('iaa<esc>') @@ -1333,7 +1384,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() 0, exec_lua [[ GlobalCount = 0 - vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]] ) @@ -1343,7 +1396,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() eq(1, exec_lua [[return GlobalCount]]) exec_lua [[ - vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount - 1 end }) + vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount - 1 end, + }) ]] feed('asdf\n') @@ -1356,7 +1411,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() 0, exec_lua [[ GlobalCount = 0 - vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_buf_set_keymap(0, 'n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]] ) @@ -1365,14 +1422,12 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() eq(1, exec_lua [[return GlobalCount]]) - exec_lua [[ - vim.api.nvim_buf_del_keymap(0, 'n', 'asdf' ) - ]] + exec_lua [[vim.api.nvim_buf_del_keymap(0, 'n', 'asdf' )]] feed('asdf\n') eq(1, exec_lua [[return GlobalCount]]) - eq('\nNo mapping found', helpers.exec_capture('nmap asdf')) + eq('\nNo mapping found', n.exec_capture('nmap asdf')) end) it('no double-free when unmapping simplifiable lua mappings', function() @@ -1380,7 +1435,9 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() 0, exec_lua [[ GlobalCount = 0 - vim.api.nvim_buf_set_keymap(0, 'n', '<C-I>', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_buf_set_keymap(0, 'n', '<C-I>', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]] ) @@ -1389,13 +1446,11 @@ describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() eq(1, exec_lua [[return GlobalCount]]) - exec_lua [[ - vim.api.nvim_buf_del_keymap(0, 'n', '<C-I>') - ]] + exec_lua [[vim.api.nvim_buf_del_keymap(0, 'n', '<C-I>')]] feed('<C-I>\n') eq(1, exec_lua [[return GlobalCount]]) - eq('\nNo mapping found', helpers.exec_capture('nmap <C-I>')) + eq('\nNo mapping found', n.exec_capture('nmap <C-I>')) end) end) diff --git a/test/functional/api/menu_spec.lua b/test/functional/api/menu_spec.lua index 44b9039393..76eef164c9 100644 --- a/test/functional/api/menu_spec.lua +++ b/test/functional/api/menu_spec.lua @@ -1,9 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed +local clear = n.clear +local command = n.command +local feed = n.feed describe('update_menu notification', function() local screen diff --git a/test/functional/api/proc_spec.lua b/test/functional/api/proc_spec.lua index 50c441792c..d2dd655b17 100644 --- a/test/functional/api/proc_spec.lua +++ b/test/functional/api/proc_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local fn = helpers.fn -local neq = helpers.neq -local nvim_argv = helpers.nvim_argv -local request = helpers.request -local retry = helpers.retry +local clear = n.clear +local eq = t.eq +local fn = n.fn +local neq = t.neq +local nvim_argv = n.nvim_argv +local request = n.request +local retry = t.retry local NIL = vim.NIL -local is_os = helpers.is_os +local is_os = t.is_os describe('API', function() before_each(clear) diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index d1608a951c..7b4c4e8312 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local assert_log = helpers.assert_log -local eq, clear, eval, command, next_msg = - helpers.eq, helpers.clear, helpers.eval, helpers.command, helpers.next_msg -local api = helpers.api -local exec_lua = helpers.exec_lua -local retry = helpers.retry -local assert_alive = helpers.assert_alive +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq, clear, eval, command, next_msg = t.eq, n.clear, n.eval, n.command, n.next_msg +local api = n.api +local exec_lua = n.exec_lua +local retry = t.retry +local assert_alive = n.assert_alive +local check_close = n.check_close local testlog = 'Xtest-server-notify-log' @@ -18,6 +19,7 @@ describe('notify', function() end) after_each(function() + check_close() os.remove(testlog) end) @@ -31,18 +33,18 @@ describe('notify', function() end) end) - describe('passing 0 as the channel id', function() - it('sends the notification/args to all subscribed channels', function() - api.nvim_subscribe('event2') + describe('channel id 0', function() + it('broadcasts the notification/args to all channels', function() eval('rpcnotify(0, "event1", 1, 2, 3)') eval('rpcnotify(0, "event2", 4, 5, 6)') eval('rpcnotify(0, "event2", 7, 8, 9)') + eq({ 'notification', 'event1', { 1, 2, 3 } }, next_msg()) eq({ 'notification', 'event2', { 4, 5, 6 } }, next_msg()) eq({ 'notification', 'event2', { 7, 8, 9 } }, next_msg()) - api.nvim_unsubscribe('event2') - api.nvim_subscribe('event1') + eval('rpcnotify(0, "event2", 10, 11, 12)') eval('rpcnotify(0, "event1", 13, 14, 15)') + eq({ 'notification', 'event2', { 10, 11, 12 } }, next_msg()) eq({ 'notification', 'event1', { 13, 14, 15 } }, next_msg()) end) @@ -75,17 +77,6 @@ describe('notify', function() end) end) - it('unsubscribe non-existing event #8745', function() - clear { env = { - NVIM_LOG_FILE = testlog, - } } - api.nvim_subscribe('event1') - api.nvim_unsubscribe('doesnotexist') - assert_log("tried to unsubscribe unknown event 'doesnotexist'", testlog, 10) - api.nvim_unsubscribe('event1') - assert_alive() - end) - it('cancels stale events on channel close', function() local catchan = eval("jobstart(['cat'], {'rpc': v:true})") local catpath = eval('exepath("cat")') @@ -94,7 +85,6 @@ describe('notify', function() exec_lua( [[ vim.rpcnotify(..., "nvim_call_function", 'chanclose', {..., 'rpc'}) - vim.rpcnotify(..., "nvim_subscribe", "daily_rant") return vim.api.nvim_get_chan_info(...) ]], catchan diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index 298dbac217..bdd340f6c6 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -1,17 +1,18 @@ -- Test server -> client RPC scenarios. Note: unlike `rpcnotify`, to evaluate -- `rpcrequest` calls we need the client event loop to be running. -local helpers = require('test.functional.helpers')(after_each) - -local clear, eval = helpers.clear, helpers.eval -local eq, neq, run, stop = helpers.eq, helpers.neq, helpers.run, helpers.stop -local nvim_prog, command, fn = helpers.nvim_prog, helpers.command, helpers.fn -local source, next_msg = helpers.source, helpers.next_msg -local ok = helpers.ok -local api = helpers.api -local spawn, merge_args = helpers.spawn, helpers.merge_args -local set_session = helpers.set_session -local pcall_err = helpers.pcall_err -local assert_alive = helpers.assert_alive +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eval = n.clear, n.eval +local eq, neq, run, stop = t.eq, t.neq, n.run, n.stop +local nvim_prog, command, fn = n.nvim_prog, n.command, n.fn +local source, next_msg = n.source, n.next_msg +local ok = t.ok +local api = n.api +local spawn, merge_args = n.spawn, n.merge_args +local set_session = n.set_session +local pcall_err = t.pcall_err +local assert_alive = n.assert_alive describe('server -> client', function() local cid @@ -91,19 +92,19 @@ describe('server -> client', function() local function on_request(method, args) eq('rcall', method) - local n = unpack(args) * 2 - if n <= 16 then + local _n = unpack(args) * 2 + if _n <= 16 then local cmd - if n == 4 then - cmd = 'let g:result2 = rpcrequest(' .. cid .. ', "rcall", ' .. n .. ')' - elseif n == 8 then - cmd = 'let g:result3 = rpcrequest(' .. cid .. ', "rcall", ' .. n .. ')' - elseif n == 16 then - cmd = 'let g:result4 = rpcrequest(' .. cid .. ', "rcall", ' .. n .. ')' + if _n == 4 then + cmd = 'let g:result2 = rpcrequest(' .. cid .. ', "rcall", ' .. _n .. ')' + elseif _n == 8 then + cmd = 'let g:result3 = rpcrequest(' .. cid .. ', "rcall", ' .. _n .. ')' + elseif _n == 16 then + cmd = 'let g:result4 = rpcrequest(' .. cid .. ', "rcall", ' .. _n .. ')' end command(cmd) end - return n + return _n end run(on_request, nil, on_setup) end) @@ -259,7 +260,7 @@ describe('server -> client', function() pcall(fn.jobstop, jobid) end) - if helpers.skip(helpers.is_os('win')) then + if t.skip(t.is_os('win')) then return end @@ -280,7 +281,7 @@ describe('server -> client', function() end) describe('connecting to another (peer) nvim', function() - local nvim_argv = merge_args(helpers.nvim_argv, { '--headless' }) + local nvim_argv = merge_args(n.nvim_argv, { '--headless' }) local function connect_test(server, mode, address) local serverpid = fn.getpid() local client = spawn(nvim_argv, false, nil, true) @@ -363,6 +364,24 @@ describe('server -> client', function() server:close() client:close() end) + + it('via stdio, with many small flushes does not crash #23781', function() + source([[ + let chan = jobstart([v:progpath, '--embed', '--headless', '-n', '-u', 'NONE', '-i', 'NONE'], { 'rpc':v:false }) + call chansend(chan, 0Z94) + sleep 50m + call chansend(chan, 0Z00) + call chansend(chan, 0Z01) + call chansend(chan, 0ZAC) + call chansend(chan, 0Z6E76696D5F636F6D6D616E64) + call chansend(chan, 0Z91) + call chansend(chan, 0ZA5) + call chansend(chan, 0Z71616C6C21) + let g:statuses = jobwait([chan]) + ]]) + eq(eval('g:statuses'), { 0 }) + assert_alive() + end) end) describe('connecting to its own pipe address', function() diff --git a/test/functional/api/tabpage_spec.lua b/test/functional/api/tabpage_spec.lua index 36955c4ace..74858475c8 100644 --- a/test/functional/api/tabpage_spec.lua +++ b/test/functional/api/tabpage_spec.lua @@ -1,11 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok -local api = helpers.api -local fn = helpers.fn -local request = helpers.request +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq, ok = n.clear, t.eq, t.ok +local exec = n.exec +local feed = n.feed +local api = n.api +local fn = n.fn +local request = n.request local NIL = vim.NIL -local pcall_err = helpers.pcall_err -local command = helpers.command +local pcall_err = t.pcall_err +local command = n.command describe('api/tabpage', function() before_each(clear) @@ -86,6 +90,30 @@ describe('api/tabpage', function() pcall_err(api.nvim_tabpage_set_win, tab1, win3) ) end) + + it('does not switch window when textlocked or in the cmdwin', function() + local target_win = api.nvim_get_current_win() + feed('q:') + local cur_win = api.nvim_get_current_win() + eq( + 'Vim:E11: Invalid in command-line window; <CR> executes, CTRL-C quits', + pcall_err(api.nvim_tabpage_set_win, 0, target_win) + ) + eq(cur_win, api.nvim_get_current_win()) + command('quit!') + + exec(([[ + new + call setline(1, 'foo') + setlocal debug=throw indentexpr=nvim_tabpage_set_win(0,%d) + ]]):format(target_win)) + cur_win = api.nvim_get_current_win() + eq( + 'Vim(normal):E5555: API call: Vim:E565: Not allowed to change text or change window', + pcall_err(command, 'normal! ==') + ) + eq(cur_win, api.nvim_get_current_win()) + end) end) describe('{get,set,del}_var', function() diff --git a/test/functional/api/ui_spec.lua b/test/functional/api/ui_spec.lua index 3e1f1ec965..2145db7f8a 100644 --- a/test/functional/api/ui_spec.lua +++ b/test/functional/api/ui_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local exec = helpers.exec -local feed = helpers.feed -local api = helpers.api -local request = helpers.request -local pcall_err = helpers.pcall_err + +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local exec = n.exec +local feed = n.feed +local api = n.api +local request = n.request +local pcall_err = t.pcall_err describe('nvim_ui_attach()', function() before_each(function() diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua index c304f1aa88..5dad9978b7 100644 --- a/test/functional/api/version_spec.lua +++ b/test/functional/api/version_spec.lua @@ -1,6 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, fn, eq = helpers.clear, helpers.fn, helpers.eq -local api = helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, fn, eq = n.clear, n.fn, t.eq +local api = n.api local function read_mpack_file(fname) local fd = io.open(fname, 'rb') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 9a4a457637..fd0535aa51 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,42 +1,43 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') local uv = vim.uv local fmt = string.format -local dedent = helpers.dedent -local assert_alive = helpers.assert_alive +local dedent = t.dedent +local assert_alive = n.assert_alive local NIL = vim.NIL -local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq -local command = helpers.command -local command_output = helpers.api.nvim_command_output -local exec = helpers.exec -local exec_capture = helpers.exec_capture -local eval = helpers.eval -local expect = helpers.expect -local fn = helpers.fn -local api = helpers.api -local matches = helpers.matches +local clear, eq, neq = n.clear, t.eq, t.neq +local command = n.command +local command_output = n.api.nvim_command_output +local exec = n.exec +local exec_capture = n.exec_capture +local eval = n.eval +local expect = n.expect +local fn = n.fn +local api = n.api +local matches = t.matches local pesc = vim.pesc -local mkdir_p = helpers.mkdir_p -local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed -local async_meths = helpers.async_meths -local is_os = helpers.is_os -local parse_context = helpers.parse_context -local request = helpers.request -local rmdir = helpers.rmdir -local source = helpers.source -local next_msg = helpers.next_msg -local tmpname = helpers.tmpname -local write_file = helpers.write_file -local exec_lua = helpers.exec_lua -local exc_exec = helpers.exc_exec -local insert = helpers.insert -local skip = helpers.skip - -local pcall_err = helpers.pcall_err +local mkdir_p = n.mkdir_p +local ok, nvim_async, feed = t.ok, n.nvim_async, n.feed +local async_meths = n.async_meths +local is_os = t.is_os +local parse_context = n.parse_context +local request = n.request +local rmdir = n.rmdir +local source = n.source +local next_msg = n.next_msg +local tmpname = t.tmpname +local write_file = t.write_file +local exec_lua = n.exec_lua +local exc_exec = n.exc_exec +local insert = n.insert +local skip = t.skip + +local pcall_err = t.pcall_err local format_string = require('test.format_string').format_string -local intchar2lua = helpers.intchar2lua -local mergedicts_copy = helpers.mergedicts_copy +local intchar2lua = t.intchar2lua +local mergedicts_copy = t.mergedicts_copy local endswith = vim.endswith describe('API', function() @@ -559,6 +560,16 @@ describe('API', function() eq('Vim:E121: Undefined variable: bogus', pcall_err(request, 'nvim_eval', 'bogus expression')) eq('', eval('v:errmsg')) -- v:errmsg was not updated. end) + + it('can return Lua function to Lua code', function() + eq( + [["a string with \"double quotes\" and 'single quotes'"]], + exec_lua([=[ + local fun = vim.api.nvim_eval([[luaeval('string.format')]]) + return fun('%q', [[a string with "double quotes" and 'single quotes']]) + ]=]) + ) + end) end) describe('nvim_call_function', function() @@ -624,6 +635,16 @@ describe('API', function() pcall_err(request, 'nvim_call_function', 'Foo', too_many_args) ) end) + + it('can return Lua function to Lua code', function() + eq( + [["a string with \"double quotes\" and 'single quotes'"]], + exec_lua([=[ + local fun = vim.api.nvim_call_function('luaeval', { 'string.format' }) + return fun('%q', [[a string with "double quotes" and 'single quotes']]) + ]=]) + ) + end) end) describe('nvim_call_dict_function', function() @@ -702,18 +723,18 @@ describe('API', function() end) after_each(function() - helpers.rmdir('Xtestdir') + n.rmdir('Xtestdir') end) it('works', function() api.nvim_set_current_dir('Xtestdir') - eq(fn.getcwd(), start_dir .. helpers.get_pathsep() .. 'Xtestdir') + eq(start_dir .. n.get_pathsep() .. 'Xtestdir', fn.getcwd()) end) it('sets previous directory', function() api.nvim_set_current_dir('Xtestdir') command('cd -') - eq(fn.getcwd(), start_dir) + eq(start_dir, fn.getcwd()) end) end) @@ -1269,7 +1290,7 @@ describe('API', function() api.nvim_paste('', true, 3) screen:expect([[ | - ~ |*2 + {1:~ }|*2 :Foo^ | ]]) end) @@ -1280,8 +1301,8 @@ describe('API', function() api.nvim_paste('normal! \023\022\006\027', true, -1) screen:expect([[ | - ~ |*2 - :normal! ^W^V^F^[^ | + {1:~ }|*2 + :normal! {18:^W^V^F^[}^ | ]]) end) it('crlf=false does not break lines at CR, CRLF', function() @@ -1467,7 +1488,7 @@ describe('API', function() eq(NIL, api.nvim_get_var('Unknown_script_func')) -- Check if autoload works properly - local pathsep = helpers.get_pathsep() + local pathsep = n.get_pathsep() local xconfig = 'Xhome' .. pathsep .. 'Xconfig' local xdata = 'Xhome' .. pathsep .. 'Xdata' local autoload_folder = table.concat({ xconfig, 'nvim', 'autoload' }, pathsep) @@ -1593,14 +1614,12 @@ describe('API', function() api.nvim_set_option_value('equalalways', false, {}) local status, rv = pcall(command_output, 'verbose set equalalways?') eq(true, status) - ok( - nil ~= string.find(rv, 'noequalalways\n' .. '\tLast set from API client %(channel id %d+%)') - ) + matches('noequalalways\n' .. '\tLast set from API client %(channel id %d+%)', rv) api.nvim_exec_lua('vim.api.nvim_set_option_value("equalalways", true, {})', {}) status, rv = pcall(command_output, 'verbose set equalalways?') eq(true, status) - eq(' equalalways\n\tLast set from Lua', rv) + eq(' equalalways\n\tLast set from Lua (run Nvim with -V1 for more details)', rv) end) it('updates whether the option has ever been set #25025', function() @@ -1953,7 +1972,7 @@ describe('API', function() describe('RPC (K_EVENT)', function() it('does not complete ("interrupt") normal-mode operator-pending #6166', function() - helpers.insert([[ + n.insert([[ FIRST LINE SECOND LINE]]) api.nvim_input('gg') @@ -1981,16 +2000,16 @@ describe('API', function() -- Make any RPC request (can be non-async: op-pending does not block). api.nvim_get_current_buf() screen:expect([[ - ^a$ | - b$ | - c$ | + ^a{1:$} | + b{1:$} | + c{1:$} | | ]]) end) it('does not complete ("interrupt") normal-mode map-pending #6166', function() command("nnoremap dd :let g:foo='it worked...'<CR>") - helpers.insert([[ + n.insert([[ FIRST LINE SECOND LINE]]) api.nvim_input('gg') @@ -2002,13 +2021,13 @@ describe('API', function() expect([[ FIRST LINE SECOND LINE]]) - eq('it worked...', helpers.eval('g:foo')) + eq('it worked...', n.eval('g:foo')) end) it('does not complete ("interrupt") insert-mode map-pending #6166', function() command('inoremap xx foo') command('set timeoutlen=9999') - helpers.insert([[ + n.insert([[ FIRST LINE SECOND LINE]]) api.nvim_input('ix') @@ -2155,35 +2174,32 @@ describe('API', function() describe('nvim_replace_termcodes', function() it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function() - eq('\128\254X', helpers.api.nvim_replace_termcodes('\128', true, true, true)) + eq('\128\254X', n.api.nvim_replace_termcodes('\128', true, true, true)) end) it('leaves non-K_SPECIAL string unchanged', function() - eq('abc', helpers.api.nvim_replace_termcodes('abc', true, true, true)) + eq('abc', n.api.nvim_replace_termcodes('abc', true, true, true)) end) it('converts <expressions>', function() - eq('\\', helpers.api.nvim_replace_termcodes('<Leader>', true, true, true)) + eq('\\', n.api.nvim_replace_termcodes('<Leader>', true, true, true)) end) it('converts <LeftMouse> to K_SPECIAL KS_EXTRA KE_LEFTMOUSE', function() -- K_SPECIAL KS_EXTRA KE_LEFTMOUSE -- 0x80 0xfd 0x2c -- 128 253 44 - eq('\128\253\44', helpers.api.nvim_replace_termcodes('<LeftMouse>', true, true, true)) + eq('\128\253\44', n.api.nvim_replace_termcodes('<LeftMouse>', true, true, true)) end) it('converts keycodes', function() - eq( - '\nx\27x\rx<x', - helpers.api.nvim_replace_termcodes('<NL>x<Esc>x<CR>x<lt>x', true, true, true) - ) + eq('\nx\27x\rx<x', n.api.nvim_replace_termcodes('<NL>x<Esc>x<CR>x<lt>x', true, true, true)) end) it('does not convert keycodes if special=false', function() eq( '<NL>x<Esc>x<CR>x<lt>x', - helpers.api.nvim_replace_termcodes('<NL>x<Esc>x<CR>x<lt>x', true, true, false) + n.api.nvim_replace_termcodes('<NL>x<Esc>x<CR>x<lt>x', true, true, false) ) end) @@ -2212,18 +2228,18 @@ describe('API', function() api.nvim_feedkeys(':let x1="…"\n', '', true) -- Both nvim_replace_termcodes and nvim_feedkeys escape \x80 - local inp = helpers.api.nvim_replace_termcodes(':let x2="…"<CR>', true, true, true) + local inp = n.api.nvim_replace_termcodes(':let x2="…"<CR>', true, true, true) api.nvim_feedkeys(inp, '', true) -- escape_ks=true -- nvim_feedkeys with K_SPECIAL escaping disabled - inp = helpers.api.nvim_replace_termcodes(':let x3="…"<CR>', true, true, true) + inp = n.api.nvim_replace_termcodes(':let x3="…"<CR>', true, true, true) api.nvim_feedkeys(inp, '', false) -- escape_ks=false - helpers.stop() + n.stop() end -- spin the loop a bit - helpers.run(nil, nil, on_setup) + n.run(nil, nil, on_setup) eq('…', api.nvim_get_var('x1')) -- Because of the double escaping this is neq @@ -2366,7 +2382,7 @@ describe('API', function() {0:~ }|*6 {1:very fail} | ]]) - helpers.poke_eventloop() + n.poke_eventloop() -- shows up to &cmdheight lines async_meths.nvim_err_write('more fail\ntoo fail\n') @@ -2678,7 +2694,7 @@ describe('API', function() describe('nvim_list_runtime_paths', function() setup(function() - local pathsep = helpers.get_pathsep() + local pathsep = n.get_pathsep() mkdir_p('Xtest' .. pathsep .. 'a') mkdir_p('Xtest' .. pathsep .. 'b') end) @@ -2723,7 +2739,7 @@ describe('API', function() it('can throw exceptions', function() local status, err = pcall(api.nvim_get_option_value, 'invalid-option', {}) eq(false, status) - ok(err:match("Unknown option 'invalid%-option'") ~= nil) + matches("Unknown option 'invalid%-option'", err) end) it('does not truncate error message <1 MB #5984', function() @@ -2736,10 +2752,7 @@ describe('API', function() it('does not leak memory on incorrect argument types', function() local status, err = pcall(api.nvim_set_current_dir, { 'not', 'a', 'dir' }) eq(false, status) - ok( - err:match(': Wrong type for argument 1 when calling nvim_set_current_dir, expecting String') - ~= nil - ) + matches(': Wrong type for argument 1 when calling nvim_set_current_dir, expecting String', err) end) describe('nvim_parse_expression', function() @@ -3135,10 +3148,60 @@ describe('API', function() -- nowadays this works because we don't execute any spurious autocmds at all #24824 assert_alive() end) + + it('no memory leak when autocommands load the buffer immediately', function() + exec([[ + autocmd BufNew * ++once call bufload(expand("<abuf>")->str2nr()) + \| let loaded = bufloaded(expand("<abuf>")->str2nr()) + ]]) + api.nvim_create_buf(false, true) + eq(1, eval('g:loaded')) + end) + + it('creating scratch buffer where autocommands set &swapfile works', function() + exec([[ + autocmd BufNew * ++once execute expand("<abuf>") "buffer" + \| file foobar + \| setlocal swapfile + ]]) + local new_buf = api.nvim_create_buf(false, true) + neq('', fn.swapname(new_buf)) + end) + + it('fires expected autocommands', function() + exec([=[ + " Append the &buftype to check autocommands trigger *after* the buffer was configured to be + " scratch, if applicable. + autocmd BufNew * let fired += [["BufNew", expand("<abuf>")->str2nr(), + \ getbufvar(expand("<abuf>")->str2nr(), "&buftype")]] + autocmd BufAdd * let fired += [["BufAdd", expand("<abuf>")->str2nr(), + \ getbufvar(expand("<abuf>")->str2nr(), "&buftype")]] + + " Don't want to see OptionSet; buffer options set from passing true for "scratch", etc. + " should be configured invisibly, and before autocommands. + autocmd OptionSet * let fired += [["OptionSet", expand("<amatch>")]] + + let fired = [] + ]=]) + local new_buf = api.nvim_create_buf(false, false) + eq({ { 'BufNew', new_buf, '' } }, eval('g:fired')) + + command('let fired = []') + new_buf = api.nvim_create_buf(false, true) + eq({ { 'BufNew', new_buf, 'nofile' } }, eval('g:fired')) + + command('let fired = []') + new_buf = api.nvim_create_buf(true, false) + eq({ { 'BufNew', new_buf, '' }, { 'BufAdd', new_buf, '' } }, eval('g:fired')) + + command('let fired = []') + new_buf = api.nvim_create_buf(true, true) + eq({ { 'BufNew', new_buf, 'nofile' }, { 'BufAdd', new_buf, 'nofile' } }, eval('g:fired')) + end) end) describe('nvim_get_runtime_file', function() - local p = helpers.alter_slashes + local p = n.alter_slashes it('can find files', function() eq({}, api.nvim_get_runtime_file('bork.borkbork', false)) eq({}, api.nvim_get_runtime_file('bork.borkbork', true)) @@ -3365,13 +3428,13 @@ describe('API', function() {desc="(global option, fallback requested) points to global", linenr=9, sid=1, args={'completeopt', {}}}, } - for _, t in pairs(tests) do - it(t.desc, function() + for _, test in pairs(tests) do + it(test.desc, function() -- Switch to the target buffer/window so that curbuf/curwin are used. api.nvim_set_current_win(wins[2]) - local info = api.nvim_get_option_info2(unpack(t.args)) - eq(t.linenr, info.last_set_linenr) - eq(t.sid, info.last_set_sid) + local info = api.nvim_get_option_info2(unpack(test.args)) + eq(test.linenr, info.last_set_linenr) + eq(test.sid, info.last_set_sid) end) end @@ -3492,9 +3555,9 @@ describe('API', function() false, { width = 79, height = 31, row = 1, col = 1, relative = 'editor' } ) - local t = api.nvim_open_term(b, {}) + local term = api.nvim_open_term(b, {}) - api.nvim_chan_send(t, io.open('test/functional/fixtures/smile2.cat', 'r'):read('*a')) + api.nvim_chan_send(term, io.open('test/functional/fixtures/smile2.cat', 'r'):read('*a')) screen:expect { grid = [[ ^ | @@ -3620,7 +3683,7 @@ describe('API', function() api.nvim_buf_set_name(buf, 'mybuf') local mark = api.nvim_get_mark('F', {}) -- Compare the path tail only - assert(string.find(mark[4], 'mybuf$')) + matches('mybuf$', mark[4]) eq({ 2, 2, buf, mark[4] }, mark) end) it('validation', function() @@ -3855,13 +3918,13 @@ describe('API', function() norm 4G ]]) eq({ - str = '││aabb 4 ', + str = '││bbaa 4 ', width = 9, highlights = { { group = 'CursorLineFold', start = 0 }, { group = 'Normal', start = 6 }, - { group = 'IncSearch', start = 6 }, - { group = 'ErrorMsg', start = 8 }, + { group = 'ErrorMsg', start = 6 }, + { group = 'IncSearch', start = 8 }, { group = 'Normal', start = 10 }, }, }, api.nvim_eval_statusline( @@ -4953,4 +5016,218 @@ describe('API', function() eq(false, exec_lua('return _G.success')) end) end) + + it('nvim__redraw', function() + local screen = Screen.new(60, 5) + screen:attach() + local win = api.nvim_get_current_win() + eq('at least one action required', pcall_err(api.nvim__redraw, {})) + eq('at least one action required', pcall_err(api.nvim__redraw, { buf = 0 })) + eq('at least one action required', pcall_err(api.nvim__redraw, { win = 0 })) + eq("cannot use both 'buf' and 'win'", pcall_err(api.nvim__redraw, { buf = 0, win = 0 })) + feed(':echo getchar()<CR>') + fn.setline(1, 'foobar') + command('vnew') + fn.setline(1, 'foobaz') + -- Can flush pending screen updates + api.nvim__redraw({ flush = true }) + screen:expect({ + grid = [[ + foobaz │foobar | + {1:~ }│{1:~ }|*2 + {3:[No Name] [+] }{2:[No Name] [+] }| + ^:echo getchar() | + ]], + }) + -- Can update the grid cursor position #20793 + api.nvim__redraw({ cursor = true }) + screen:expect({ + grid = [[ + ^foobaz │foobar | + {1:~ }│{1:~ }|*2 + {3:[No Name] [+] }{2:[No Name] [+] }| + :echo getchar() | + ]], + }) + -- Also in non-current window + api.nvim__redraw({ cursor = true, win = win }) + screen:expect({ + grid = [[ + foobaz │^foobar | + {1:~ }│{1:~ }|*2 + {3:[No Name] [+] }{2:[No Name] [+] }| + :echo getchar() | + ]], + }) + -- Can update the 'statusline' in a single window + api.nvim_set_option_value('statusline', 'statusline1', { win = 0 }) + api.nvim_set_option_value('statusline', 'statusline2', { win = win }) + api.nvim__redraw({ cursor = true, win = 0, statusline = true }) + screen:expect({ + grid = [[ + ^foobaz │foobar | + {1:~ }│{1:~ }|*2 + {3:statusline1 }{2:[No Name] [+] }| + :echo getchar() | + ]], + }) + api.nvim__redraw({ win = win, statusline = true }) + screen:expect({ + grid = [[ + ^foobaz │foobar | + {1:~ }│{1:~ }|*2 + {3:statusline1 }{2:statusline2 }| + :echo getchar() | + ]], + }) + -- Can update the 'statusline' in all windows + api.nvim_set_option_value('statusline', '', { win = win }) + api.nvim_set_option_value('statusline', 'statusline3', {}) + api.nvim__redraw({ statusline = true }) + screen:expect({ + grid = [[ + ^foobaz │foobar | + {1:~ }│{1:~ }|*2 + {3:statusline3 }{2:statusline3 }| + :echo getchar() | + ]], + }) + -- Can update the 'statuscolumn' + api.nvim_set_option_value('statuscolumn', 'statuscolumn', { win = win }) + api.nvim__redraw({ statuscolumn = true }) + screen:expect({ + grid = [[ + ^foobaz │{8:statuscolumn}foobar | + {1:~ }│{1:~ }|*2 + {3:statusline3 }{2:statusline3 }| + :echo getchar() | + ]], + }) + -- Can update the 'winbar' + api.nvim_set_option_value('winbar', 'winbar', { win = 0 }) + api.nvim__redraw({ win = 0, winbar = true }) + screen:expect({ + grid = [[ + {5:^winbar }│{8:statuscolumn}foobar | + foobaz │{1:~ }| + {1:~ }│{1:~ }| + {3:statusline3 }{2:statusline3 }| + :echo getchar() | + ]], + }) + -- Can update the 'tabline' + api.nvim_set_option_value('showtabline', 2, {}) + api.nvim_set_option_value('tabline', 'tabline', {}) + api.nvim__redraw({ tabline = true }) + screen:expect({ + grid = [[ + {2:^tabline }| + {5:winbar }│{8:statuscolumn}foobar | + foobaz │{1:~ }| + {3:statusline3 }{2:statusline3 }| + :echo getchar() | + ]], + }) + -- Can update multiple status widgets + api.nvim_set_option_value('tabline', 'tabline2', {}) + api.nvim_set_option_value('statusline', 'statusline4', {}) + api.nvim__redraw({ statusline = true, tabline = true }) + screen:expect({ + grid = [[ + {2:^tabline2 }| + {5:winbar }│{8:statuscolumn}foobar | + foobaz │{1:~ }| + {3:statusline4 }{2:statusline4 }| + :echo getchar() | + ]], + }) + -- Can update all status widgets + api.nvim_set_option_value('tabline', 'tabline3', {}) + api.nvim_set_option_value('statusline', 'statusline5', {}) + api.nvim_set_option_value('statuscolumn', 'statuscolumn2', {}) + api.nvim_set_option_value('winbar', 'winbar2', {}) + api.nvim__redraw({ statuscolumn = true, statusline = true, tabline = true, winbar = true }) + screen:expect({ + grid = [[ + {2:^tabline3 }| + {5:winbar2 }│{5:winbar2 }| + {8:statuscolumn2}foobaz │{8:statuscolumn}foobar | + {3:statusline5 }{2:statusline5 }| + :echo getchar() | + ]], + }) + -- Can update status widget for a specific window + feed('<CR><CR>') + command('let g:status=0') + api.nvim_set_option_value('statusline', '%{%g:status%}', { win = 0 }) + command('vsplit') + screen:expect({ + grid = [[ + {2:tabline3 }| + {5:winbar2 }│{5:winbar2 }│{5:winbar2 }| + {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar | + {3:0 }{2:0 statusline5 }| + 13 | + ]], + }) + command('let g:status=1') + api.nvim__redraw({ win = 0, statusline = true }) + screen:expect({ + grid = [[ + {2:tabline3 }| + {5:winbar2 }│{5:winbar2 }│{5:winbar2 }| + {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar | + {3:1 }{2:0 statusline5 }| + 13 | + ]], + }) + -- Can update status widget for a specific buffer + command('let g:status=2') + api.nvim__redraw({ buf = 0, statusline = true }) + screen:expect({ + grid = [[ + {2:tabline3 }| + {5:winbar2 }│{5:winbar2 }│{5:winbar2 }| + {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar | + {3:2 }{2:2 statusline5 }| + 13 | + ]], + }) + -- valid = true does not draw any lines on its own + exec_lua([[ + _G.lines = 0 + ns = vim.api.nvim_create_namespace('') + vim.api.nvim_set_decoration_provider(ns, { + on_win = function() + if _G.do_win then + vim.api.nvim_buf_set_extmark(0, ns, 0, 0, { hl_group = 'IncSearch', end_col = 6 }) + end + end, + on_line = function() + _G.lines = _G.lines + 1 + end, + }) + ]]) + local lines = exec_lua('return lines') + api.nvim__redraw({ buf = 0, valid = true, flush = true }) + eq(lines, exec_lua('return _G.lines')) + -- valid = false does + api.nvim__redraw({ buf = 0, valid = false, flush = true }) + neq(lines, exec_lua('return _G.lines')) + -- valid = true does redraw lines if affected by on_win callback + exec_lua('_G.do_win = true') + api.nvim__redraw({ buf = 0, valid = true, flush = true }) + screen:expect({ + grid = [[ + {2:tabline3 }| + {5:winbar2 }│{5:winbar2 }│{5:winbar2 }| + {8:statuscolumn2}{2:^foobaz} │{8:statuscolumn2}{2:foobaz}│{8:statuscolumn}foobar | + {3:2 }{2:2 statusline5 }| + 13 | + ]], + }) + -- takes buffer line count from correct buffer with "win" and {0, -1} "range" + api.nvim__redraw({ win = 0, range = { 0, -1 } }) + n.assert_alive() + end) end) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 097a546ef2..15b9b0945c 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -1,27 +1,29 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') + local clear, curbuf, curbuf_contents, curwin, eq, neq, matches, ok, feed, insert, eval = - helpers.clear, - helpers.api.nvim_get_current_buf, - helpers.curbuf_contents, - helpers.api.nvim_get_current_win, - helpers.eq, - helpers.neq, - helpers.matches, - helpers.ok, - helpers.feed, - helpers.insert, - helpers.eval -local poke_eventloop = helpers.poke_eventloop -local exec = helpers.exec -local exec_lua = helpers.exec_lua -local fn = helpers.fn -local request = helpers.request + n.clear, + n.api.nvim_get_current_buf, + n.curbuf_contents, + n.api.nvim_get_current_win, + t.eq, + t.neq, + t.matches, + t.ok, + n.feed, + n.insert, + n.eval +local poke_eventloop = n.poke_eventloop +local exec = n.exec +local exec_lua = n.exec_lua +local fn = n.fn +local request = n.request local NIL = vim.NIL -local api = helpers.api -local command = helpers.command -local pcall_err = helpers.pcall_err -local assert_alive = helpers.assert_alive +local api = n.api +local command = n.command +local pcall_err = t.pcall_err +local assert_alive = n.assert_alive describe('API/win', function() before_each(clear) @@ -111,6 +113,44 @@ describe('API/win', function() api.nvim_win_set_buf(new_win, next_buf) eq(next_buf, api.nvim_win_get_buf(new_win)) end) + + describe("with 'autochdir'", function() + local topdir + local otherbuf + local oldwin + local newwin + + before_each(function() + command('set shellslash') + topdir = fn.getcwd() + t.mkdir(topdir .. '/Xacd') + t.mkdir(topdir .. '/Xacd/foo') + otherbuf = api.nvim_create_buf(false, true) + api.nvim_buf_set_name(otherbuf, topdir .. '/Xacd/baz.txt') + + command('set autochdir') + command('edit Xacd/foo/bar.txt') + eq(topdir .. '/Xacd/foo', fn.getcwd()) + + oldwin = api.nvim_get_current_win() + command('vsplit') + newwin = api.nvim_get_current_win() + end) + + after_each(function() + n.rmdir(topdir .. '/Xacd') + end) + + it('does not change cwd with non-current window', function() + api.nvim_win_set_buf(oldwin, otherbuf) + eq(topdir .. '/Xacd/foo', fn.getcwd()) + end) + + it('changes cwd with current window', function() + api.nvim_win_set_buf(newwin, otherbuf) + eq(topdir .. '/Xacd', fn.getcwd()) + end) + end) end) describe('{get,set}_cursor', function() @@ -1147,27 +1187,6 @@ describe('API/win', function() end) describe('open_win', function() - it('noautocmd option works', function() - command('autocmd BufEnter,BufLeave,BufWinEnter * let g:fired = 1') - api.nvim_open_win(api.nvim_create_buf(true, true), true, { - relative = 'win', - row = 3, - col = 3, - width = 12, - height = 3, - noautocmd = true, - }) - eq(0, fn.exists('g:fired')) - api.nvim_open_win(api.nvim_create_buf(true, true), true, { - relative = 'win', - row = 3, - col = 3, - width = 12, - height = 3, - }) - eq(1, fn.exists('g:fired')) - end) - it('disallowed in cmdwin if enter=true or buf=cmdwin_buf', function() local new_buf = api.nvim_create_buf(true, true) feed('q:') @@ -1233,81 +1252,151 @@ describe('API/win', function() eq(wins_before, api.nvim_list_wins()) end) - it('creates a split window', function() - local win = api.nvim_open_win(0, true, { - vertical = false, - }) - eq('', api.nvim_win_get_config(win).relative) + describe('creates a split window above', function() + local function test_open_win_split_above(key, val) + local initial_win = api.nvim_get_current_win() + local win = api.nvim_open_win(0, true, { + [key] = val, + height = 10, + }) + eq('', api.nvim_win_get_config(win).relative) + eq(10, api.nvim_win_get_height(win)) + local layout = fn.winlayout() + eq({ + 'col', + { + { 'leaf', win }, + { 'leaf', initial_win }, + }, + }, layout) + end + + it("with split = 'above'", function() + test_open_win_split_above('split', 'above') + end) + + it("with vertical = false and 'nosplitbelow'", function() + api.nvim_set_option_value('splitbelow', false, {}) + test_open_win_split_above('vertical', false) + end) end) - it('creates split windows in the correct direction', function() - local initial_win = api.nvim_get_current_win() - local win = api.nvim_open_win(0, true, { - vertical = true, - }) - eq('', api.nvim_win_get_config(win).relative) + describe('creates a split window below', function() + local function test_open_win_split_below(key, val) + local initial_win = api.nvim_get_current_win() + local win = api.nvim_open_win(0, true, { + [key] = val, + height = 15, + }) + eq('', api.nvim_win_get_config(win).relative) + eq(15, api.nvim_win_get_height(win)) + local layout = fn.winlayout() + eq({ + 'col', + { + { 'leaf', initial_win }, + { 'leaf', win }, + }, + }, layout) + end - local layout = fn.winlayout() + it("with split = 'below'", function() + test_open_win_split_below('split', 'below') + end) - eq({ - 'row', - { - { 'leaf', win }, - { 'leaf', initial_win }, - }, - }, layout) + it("with vertical = false and 'splitbelow'", function() + api.nvim_set_option_value('splitbelow', true, {}) + test_open_win_split_below('vertical', false) + end) end) - it("respects the 'split' option", function() - local initial_win = api.nvim_get_current_win() - local win = api.nvim_open_win(0, true, { - split = 'below', - }) - eq('', api.nvim_win_get_config(win).relative) + describe('creates a split window to the left', function() + local function test_open_win_split_left(key, val) + local initial_win = api.nvim_get_current_win() + local win = api.nvim_open_win(0, true, { + [key] = val, + width = 25, + }) + eq('', api.nvim_win_get_config(win).relative) + eq(25, api.nvim_win_get_width(win)) + local layout = fn.winlayout() + eq({ + 'row', + { + { 'leaf', win }, + { 'leaf', initial_win }, + }, + }, layout) + end - local layout = fn.winlayout() + it("with split = 'left'", function() + test_open_win_split_left('split', 'left') + end) - eq({ - 'col', - { - { 'leaf', initial_win }, - { 'leaf', win }, - }, - }, layout) + it("with vertical = true and 'nosplitright'", function() + api.nvim_set_option_value('splitright', false, {}) + test_open_win_split_left('vertical', true) + end) end) - it( - "doesn't change tp_curwin when splitting window in non-current tab with enter=false", - function() - local tab1 = api.nvim_get_current_tabpage() - local tab1_win = api.nvim_get_current_win() + describe('creates a split window to the right', function() + local function test_open_win_split_right(key, val) + local initial_win = api.nvim_get_current_win() + local win = api.nvim_open_win(0, true, { + [key] = val, + width = 30, + }) + eq('', api.nvim_win_get_config(win).relative) + eq(30, api.nvim_win_get_width(win)) + local layout = fn.winlayout() + eq({ + 'row', + { + { 'leaf', initial_win }, + { 'leaf', win }, + }, + }, layout) + end + + it("with split = 'right'", function() + test_open_win_split_right('split', 'right') + end) - helpers.command('tabnew') - local tab2 = api.nvim_get_current_tabpage() - local tab2_win = api.nvim_get_current_win() + it("with vertical = true and 'splitright'", function() + api.nvim_set_option_value('splitright', true, {}) + test_open_win_split_right('vertical', true) + end) + end) - eq({ tab1_win, tab2_win }, api.nvim_list_wins()) - eq({ tab1, tab2 }, api.nvim_list_tabpages()) + it("doesn't change tp_curwin when splitting window in another tab with enter=false", function() + local tab1 = api.nvim_get_current_tabpage() + local tab1_win = api.nvim_get_current_win() - api.nvim_set_current_tabpage(tab1) - eq(tab1_win, api.nvim_get_current_win()) + n.command('tabnew') + local tab2 = api.nvim_get_current_tabpage() + local tab2_win = api.nvim_get_current_win() - local tab2_prevwin = fn.tabpagewinnr(tab2, '#') + eq({ tab1_win, tab2_win }, api.nvim_list_wins()) + eq({ tab1, tab2 }, api.nvim_list_tabpages()) - -- split in tab2 whine in tab2, with enter = false - local tab2_win2 = api.nvim_open_win(api.nvim_create_buf(false, true), false, { - win = tab2_win, - split = 'right', - }) - eq(tab1_win, api.nvim_get_current_win()) -- we should still be in the first tp - eq(tab1_win, api.nvim_tabpage_get_win(tab1)) + api.nvim_set_current_tabpage(tab1) + eq(tab1_win, api.nvim_get_current_win()) - eq(tab2_win, api.nvim_tabpage_get_win(tab2)) -- tab2's tp_curwin should not have changed - eq(tab2_prevwin, fn.tabpagewinnr(tab2, '#')) -- tab2's tp_prevwin should not have changed - eq({ tab1_win, tab2_win, tab2_win2 }, api.nvim_list_wins()) - eq({ tab2_win, tab2_win2 }, api.nvim_tabpage_list_wins(tab2)) - end - ) + local tab2_prevwin = fn.tabpagewinnr(tab2, '#') + + -- split in tab2 whine in tab2, with enter = false + local tab2_win2 = api.nvim_open_win(api.nvim_create_buf(false, true), false, { + win = tab2_win, + split = 'right', + }) + eq(tab1_win, api.nvim_get_current_win()) -- we should still be in the first tp + eq(tab1_win, api.nvim_tabpage_get_win(tab1)) + + eq(tab2_win, api.nvim_tabpage_get_win(tab2)) -- tab2's tp_curwin should not have changed + eq(tab2_prevwin, fn.tabpagewinnr(tab2, '#')) -- tab2's tp_prevwin should not have changed + eq({ tab1_win, tab2_win, tab2_win2 }, api.nvim_list_wins()) + eq({ tab2_win, tab2_win2 }, api.nvim_tabpage_list_wins(tab2)) + end) it('creates splits in the correct location', function() local first_win = api.nvim_get_current_win() @@ -1364,6 +1453,400 @@ describe('API/win', function() }, }, layout) end) + + it('opens floating windows in other tabpages', function() + local first_win = api.nvim_get_current_win() + local first_tab = api.nvim_get_current_tabpage() + + command('tabnew') + local new_tab = api.nvim_get_current_tabpage() + local win = api.nvim_open_win(0, false, { + relative = 'win', + win = first_win, + width = 5, + height = 5, + row = 1, + col = 1, + }) + eq(api.nvim_win_get_tabpage(win), first_tab) + eq(api.nvim_get_current_tabpage(), new_tab) + end) + + it('switches to new windows in non-current tabpages when enter=true', function() + local first_win = api.nvim_get_current_win() + local first_tab = api.nvim_get_current_tabpage() + command('tabnew') + local win = api.nvim_open_win(0, true, { + relative = 'win', + win = first_win, + width = 5, + height = 5, + row = 1, + col = 1, + }) + eq(api.nvim_win_get_tabpage(win), first_tab) + eq(api.nvim_get_current_tabpage(), first_tab) + end) + + local function setup_tabbed_autocmd_test() + local info = {} + info.orig_buf = api.nvim_get_current_buf() + info.other_buf = api.nvim_create_buf(true, true) + info.tab1_curwin = api.nvim_get_current_win() + info.tab1 = api.nvim_get_current_tabpage() + command('tab split | split') + info.tab2_curwin = api.nvim_get_current_win() + info.tab2 = api.nvim_get_current_tabpage() + exec([=[ + tabfirst + let result = [] + autocmd TabEnter * let result += [["TabEnter", nvim_get_current_tabpage()]] + autocmd TabLeave * let result += [["TabLeave", nvim_get_current_tabpage()]] + autocmd WinEnter * let result += [["WinEnter", win_getid()]] + autocmd WinLeave * let result += [["WinLeave", win_getid()]] + autocmd WinNew * let result += [["WinNew", win_getid()]] + autocmd WinClosed * let result += [["WinClosed", str2nr(expand("<afile>"))]] + autocmd BufEnter * let result += [["BufEnter", win_getid(), bufnr()]] + autocmd BufLeave * let result += [["BufLeave", win_getid(), bufnr()]] + autocmd BufWinEnter * let result += [["BufWinEnter", win_getid(), bufnr()]] + autocmd BufWinLeave * let result += [["BufWinLeave", win_getid(), bufnr()]] + ]=]) + return info + end + + it('noautocmd option works', function() + local info = setup_tabbed_autocmd_test() + + api.nvim_open_win( + info.other_buf, + true, + { split = 'left', win = info.tab2_curwin, noautocmd = true } + ) + eq({}, eval('result')) + + api.nvim_open_win( + info.orig_buf, + true, + { relative = 'editor', row = 0, col = 0, width = 10, height = 10, noautocmd = true } + ) + eq({}, eval('result')) + end) + + it('fires expected autocmds when creating splits without entering', function() + local info = setup_tabbed_autocmd_test() + + -- For these, don't want BufWinEnter if visiting the same buffer, like :{s}buffer. + -- Same tabpage, same buffer. + local new_win = api.nvim_open_win(0, false, { split = 'left', win = info.tab1_curwin }) + eq({ + { 'WinNew', new_win }, + }, eval('result')) + eq(info.tab1_curwin, api.nvim_get_current_win()) + + -- Other tabpage, same buffer. + command('let result = []') + new_win = api.nvim_open_win(0, false, { split = 'left', win = info.tab2_curwin }) + eq({ + { 'WinNew', new_win }, + }, eval('result')) + eq(info.tab1_curwin, api.nvim_get_current_win()) + + -- Same tabpage, other buffer. + command('let result = []') + new_win = api.nvim_open_win(info.other_buf, false, { split = 'left', win = info.tab1_curwin }) + eq({ + { 'WinNew', new_win }, + { 'BufWinEnter', new_win, info.other_buf }, + }, eval('result')) + eq(info.tab1_curwin, api.nvim_get_current_win()) + + -- Other tabpage, other buffer. + command('let result = []') + new_win = api.nvim_open_win(info.other_buf, false, { split = 'left', win = info.tab2_curwin }) + eq({ + { 'WinNew', new_win }, + { 'BufWinEnter', new_win, info.other_buf }, + }, eval('result')) + eq(info.tab1_curwin, api.nvim_get_current_win()) + end) + + it('fires expected autocmds when creating and entering splits', function() + local info = setup_tabbed_autocmd_test() + + -- Same tabpage, same buffer. + local new_win = api.nvim_open_win(0, true, { split = 'left', win = info.tab1_curwin }) + eq({ + { 'WinNew', new_win }, + { 'WinLeave', info.tab1_curwin }, + { 'WinEnter', new_win }, + }, eval('result')) + + -- Same tabpage, other buffer. + api.nvim_set_current_win(info.tab1_curwin) + command('let result = []') + new_win = api.nvim_open_win(info.other_buf, true, { split = 'left', win = info.tab1_curwin }) + eq({ + { 'WinNew', new_win }, + { 'WinLeave', info.tab1_curwin }, + { 'WinEnter', new_win }, + { 'BufLeave', new_win, info.orig_buf }, + { 'BufEnter', new_win, info.other_buf }, + { 'BufWinEnter', new_win, info.other_buf }, + }, eval('result')) + + -- For these, the other tabpage's prevwin and curwin will change like we switched from its old + -- curwin to the new window, so the extra events near TabEnter reflect that. + -- Other tabpage, same buffer. + api.nvim_set_current_win(info.tab1_curwin) + command('let result = []') + new_win = api.nvim_open_win(0, true, { split = 'left', win = info.tab2_curwin }) + eq({ + { 'WinNew', new_win }, + { 'WinLeave', info.tab1_curwin }, + { 'TabLeave', info.tab1 }, + + { 'WinEnter', info.tab2_curwin }, + { 'TabEnter', info.tab2 }, + { 'WinLeave', info.tab2_curwin }, + { 'WinEnter', new_win }, + }, eval('result')) + + -- Other tabpage, other buffer. + api.nvim_set_current_win(info.tab2_curwin) + api.nvim_set_current_win(info.tab1_curwin) + command('let result = []') + new_win = api.nvim_open_win(info.other_buf, true, { split = 'left', win = info.tab2_curwin }) + eq({ + { 'WinNew', new_win }, + { 'WinLeave', info.tab1_curwin }, + { 'TabLeave', info.tab1 }, + + { 'WinEnter', info.tab2_curwin }, + { 'TabEnter', info.tab2 }, + { 'WinLeave', info.tab2_curwin }, + { 'WinEnter', new_win }, + + { 'BufLeave', new_win, info.orig_buf }, + { 'BufEnter', new_win, info.other_buf }, + { 'BufWinEnter', new_win, info.other_buf }, + }, eval('result')) + + -- Other tabpage, other buffer; but other tabpage's curwin has a new buffer active. + api.nvim_set_current_win(info.tab2_curwin) + local new_buf = api.nvim_create_buf(true, true) + api.nvim_set_current_buf(new_buf) + api.nvim_set_current_win(info.tab1_curwin) + command('let result = []') + new_win = api.nvim_open_win(info.other_buf, true, { split = 'left', win = info.tab2_curwin }) + eq({ + { 'WinNew', new_win }, + { 'BufLeave', info.tab1_curwin, info.orig_buf }, + { 'WinLeave', info.tab1_curwin }, + { 'TabLeave', info.tab1 }, + + { 'WinEnter', info.tab2_curwin }, + { 'TabEnter', info.tab2 }, + { 'BufEnter', info.tab2_curwin, new_buf }, + { 'WinLeave', info.tab2_curwin }, + { 'WinEnter', new_win }, + { 'BufLeave', new_win, new_buf }, + { 'BufEnter', new_win, info.other_buf }, + { 'BufWinEnter', new_win, info.other_buf }, + }, eval('result')) + end) + + it('OK when new window is moved to other tabpage by autocommands', function() + -- Use nvim_win_set_config in the autocommands, as other methods of moving a window to a + -- different tabpage (e.g: wincmd T) actually creates a new window. + local tab0 = api.nvim_get_current_tabpage() + local tab0_win = api.nvim_get_current_win() + command('tabnew') + local new_buf = api.nvim_create_buf(true, true) + local tab1 = api.nvim_get_current_tabpage() + local tab1_parent = api.nvim_get_current_win() + command( + 'tabfirst | autocmd WinNew * ++once call nvim_win_set_config(0, #{split: "left", win: ' + .. tab1_parent + .. '})' + ) + local new_win = api.nvim_open_win(new_buf, true, { split = 'left' }) + eq(tab1, api.nvim_get_current_tabpage()) + eq(new_win, api.nvim_get_current_win()) + eq(new_buf, api.nvim_get_current_buf()) + + -- nvim_win_set_config called after entering. It doesn't follow a curwin that is moved to a + -- different tabpage, but instead moves to the win filling the space, which is tab0_win. + command( + 'tabfirst | autocmd WinEnter * ++once call nvim_win_set_config(0, #{split: "left", win: ' + .. tab1_parent + .. '})' + ) + new_win = api.nvim_open_win(new_buf, true, { split = 'left' }) + eq(tab0, api.nvim_get_current_tabpage()) + eq(tab0_win, api.nvim_get_current_win()) + eq(tab1, api.nvim_win_get_tabpage(new_win)) + eq(new_buf, api.nvim_win_get_buf(new_win)) + + command( + 'tabfirst | autocmd BufEnter * ++once call nvim_win_set_config(0, #{split: "left", win: ' + .. tab1_parent + .. '})' + ) + new_win = api.nvim_open_win(new_buf, true, { split = 'left' }) + eq(tab0, api.nvim_get_current_tabpage()) + eq(tab0_win, api.nvim_get_current_win()) + eq(tab1, api.nvim_win_get_tabpage(new_win)) + eq(new_buf, api.nvim_win_get_buf(new_win)) + end) + + it('does not fire BufWinEnter if win_set_buf fails', function() + exec([[ + set nohidden modified + autocmd WinNew * ++once only! + let fired = v:false + autocmd BufWinEnter * ++once let fired = v:true + ]]) + eq( + 'Failed to set buffer 2', + pcall_err(api.nvim_open_win, api.nvim_create_buf(true, true), false, { split = 'left' }) + ) + eq(false, eval('fired')) + end) + + it('fires Buf* autocommands when `!enter` if window is entered via autocommands', function() + exec([[ + autocmd WinNew * ++once only! + let fired = v:false + autocmd BufEnter * ++once let fired = v:true + ]]) + api.nvim_open_win(api.nvim_create_buf(true, true), false, { split = 'left' }) + eq(true, eval('fired')) + end) + + it('no heap-use-after-free if target buffer deleted by autocommands', function() + local cur_buf = api.nvim_get_current_buf() + local new_buf = api.nvim_create_buf(true, true) + command('autocmd WinNew * ++once call nvim_buf_delete(' .. new_buf .. ', #{force: 1})') + api.nvim_open_win(new_buf, true, { split = 'left' }) + eq(cur_buf, api.nvim_get_current_buf()) + end) + + it('checks if splitting disallowed', function() + command('split | autocmd WinEnter * ++once call nvim_open_win(0, 0, #{split: "right"})') + matches("E242: Can't split a window while closing another$", pcall_err(command, 'quit')) + + command('only | autocmd BufHidden * ++once call nvim_open_win(0, 0, #{split: "left"})') + matches( + 'E1159: Cannot split a window when closing the buffer$', + pcall_err(command, 'new | quit') + ) + + local w = api.nvim_get_current_win() + command( + 'only | new | autocmd BufHidden * ++once call nvim_open_win(0, 0, #{split: "left", win: ' + .. w + .. '})' + ) + matches( + 'E1159: Cannot split a window when closing the buffer$', + pcall_err(api.nvim_win_close, w, true) + ) + + -- OK when using window to different buffer than `win`s. + w = api.nvim_get_current_win() + command( + 'only | autocmd BufHidden * ++once call nvim_open_win(0, 0, #{split: "left", win: ' + .. w + .. '})' + ) + command('new | quit') + end) + + it('restores last known cursor position if BufWinEnter did not move it', function() + -- This test mostly exists to ensure BufWinEnter is executed before enter_buffer's epilogue. + local buf = api.nvim_get_current_buf() + insert([[ + foo + bar baz .etc + i love autocommand bugs! + supercalifragilisticexpialidocious + marvim is actually a human + llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch + ]]) + api.nvim_win_set_cursor(0, { 5, 2 }) + command('set nostartofline | enew') + local new_win = api.nvim_open_win(buf, false, { split = 'left' }) + eq({ 5, 2 }, api.nvim_win_get_cursor(new_win)) + + exec([[ + only! + autocmd BufWinEnter * ++once normal! j6l + ]]) + new_win = api.nvim_open_win(buf, false, { split = 'left' }) + eq({ 2, 6 }, api.nvim_win_get_cursor(new_win)) + end) + + it('does not block all win_set_buf autocommands if !enter and !noautocmd', function() + local new_buf = fn.bufadd('foobarbaz') + exec([[ + let triggered = "" + autocmd BufReadCmd * ++once let triggered = bufname() + ]]) + api.nvim_open_win(new_buf, false, { split = 'left' }) + eq('foobarbaz', eval('triggered')) + end) + + it('sets error when no room', function() + matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)')) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_open_win, 0, true, { split = 'above', win = 0 }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_open_win, 0, true, { split = 'below', win = 0 }) + ) + end) + + describe("with 'autochdir'", function() + local topdir + local otherbuf + + before_each(function() + command('set shellslash') + topdir = fn.getcwd() + t.mkdir(topdir .. '/Xacd') + t.mkdir(topdir .. '/Xacd/foo') + otherbuf = api.nvim_create_buf(false, true) + api.nvim_buf_set_name(otherbuf, topdir .. '/Xacd/baz.txt') + + command('set autochdir') + command('edit Xacd/foo/bar.txt') + eq(topdir .. '/Xacd/foo', fn.getcwd()) + end) + + after_each(function() + n.rmdir(topdir .. '/Xacd') + end) + + it('does not change cwd with enter=false #15280', function() + api.nvim_open_win( + otherbuf, + false, + { relative = 'editor', height = 5, width = 5, row = 5, col = 5 } + ) + eq(topdir .. '/Xacd/foo', fn.getcwd()) + end) + + it('changes cwd with enter=true', function() + api.nvim_open_win( + otherbuf, + true, + { relative = 'editor', height = 5, width = 5, row = 5, col = 5 } + ) + eq(topdir .. '/Xacd', fn.getcwd()) + end) + end) end) describe('set_config', function() @@ -1471,6 +1954,15 @@ describe('API/win', function() config = api.nvim_win_get_config(win) eq('', config.relative) eq('below', config.split) + + eq( + "non-float with 'win' requires at least 'split' or 'vertical'", + pcall_err(api.nvim_win_set_config, 0, { win = 0 }) + ) + eq( + "non-float with 'win' requires at least 'split' or 'vertical'", + pcall_err(api.nvim_win_set_config, 0, { win = 0, relative = '' }) + ) end) it('creates top-level splits', function() @@ -1663,6 +2155,474 @@ describe('API/win', function() }, }, fn.winlayout()) end) + + it('closing new curwin when moving window to other tabpage works', function() + command('split | tabnew') + local t2_win = api.nvim_get_current_win() + command('tabfirst | autocmd WinEnter * ++once quit') + local t1_move_win = api.nvim_get_current_win() + -- win_set_config fails to switch away from "t1_move_win" because the WinEnter autocmd that + -- closed the window we're switched to returns us to "t1_move_win", as it filled the space. + eq( + 'Failed to switch away from window ' .. t1_move_win, + pcall_err(api.nvim_win_set_config, t1_move_win, { win = t2_win, split = 'left' }) + ) + eq(t1_move_win, api.nvim_get_current_win()) + + command('split | split | autocmd WinEnter * ++once quit') + t1_move_win = api.nvim_get_current_win() + -- In this case, we closed the window that we got switched to, but doing so didn't switch us + -- back to "t1_move_win", which is fine. + api.nvim_win_set_config(t1_move_win, { win = t2_win, split = 'left' }) + neq(t1_move_win, api.nvim_get_current_win()) + end) + + it('messing with "win" or "parent" when moving "win" to other tabpage', function() + command('split | tabnew') + local t2 = api.nvim_get_current_tabpage() + local t2_win1 = api.nvim_get_current_win() + command('split') + local t2_win2 = api.nvim_get_current_win() + command('split') + local t2_win3 = api.nvim_get_current_win() + + command('tabfirst | autocmd WinEnter * ++once call nvim_win_close(' .. t2_win1 .. ', 1)') + local cur_win = api.nvim_get_current_win() + eq( + 'Windows to split were closed', + pcall_err(api.nvim_win_set_config, 0, { win = t2_win1, split = 'left' }) + ) + eq(cur_win, api.nvim_get_current_win()) + + command('split | autocmd WinLeave * ++once quit!') + cur_win = api.nvim_get_current_win() + eq( + 'Windows to split were closed', + pcall_err(api.nvim_win_set_config, 0, { win = t2_win2, split = 'left' }) + ) + neq(cur_win, api.nvim_get_current_win()) + + exec([[ + split + autocmd WinLeave * ++once + \ call nvim_win_set_config(0, #{relative:'editor', row:0, col:0, width:5, height:5}) + ]]) + cur_win = api.nvim_get_current_win() + eq( + 'Floating state of windows to split changed', + pcall_err(api.nvim_win_set_config, 0, { win = t2_win3, split = 'left' }) + ) + eq('editor', api.nvim_win_get_config(0).relative) + eq(cur_win, api.nvim_get_current_win()) + + command('autocmd WinLeave * ++once wincmd J') + cur_win = api.nvim_get_current_win() + eq( + 'Floating state of windows to split changed', + pcall_err(api.nvim_win_set_config, 0, { win = t2_win3, split = 'left' }) + ) + eq('', api.nvim_win_get_config(0).relative) + eq(cur_win, api.nvim_get_current_win()) + + -- Try to make "parent" floating. This should give the same error as before, but because + -- changing a split from another tabpage into a float isn't supported yet, check for that + -- error instead for now. + -- Use ":silent!" to avoid the one second delay from printing the error message. + exec(([[ + autocmd WinLeave * ++once silent! + \ call nvim_win_set_config(%d, #{relative:'editor', row:0, col:0, width:5, height:5}) + ]]):format(t2_win3)) + cur_win = api.nvim_get_current_win() + api.nvim_win_set_config(0, { win = t2_win3, split = 'left' }) + matches( + 'Cannot change window from different tabpage into float$', + api.nvim_get_vvar('errmsg') + ) + -- The error doesn't abort moving the window (or maybe it should, if that's wanted?) + neq(cur_win, api.nvim_get_current_win()) + eq(t2, api.nvim_win_get_tabpage(cur_win)) + end) + + it('expected autocmds when moving window to other tabpage', function() + local new_curwin = api.nvim_get_current_win() + command('split') + local win = api.nvim_get_current_win() + command('tabnew') + local parent = api.nvim_get_current_win() + exec([[ + tabfirst + let result = [] + autocmd WinEnter * let result += ["Enter", win_getid()] + autocmd WinLeave * let result += ["Leave", win_getid()] + autocmd WinNew * let result += ["New", win_getid()] + ]]) + api.nvim_win_set_config(0, { win = parent, split = 'left' }) + -- Shouldn't see WinNew, as we're not creating any new windows, just moving existing ones. + eq({ 'Leave', win, 'Enter', new_curwin }, eval('result')) + end) + + it('no autocmds when moving window within same tabpage', function() + local parent = api.nvim_get_current_win() + exec([[ + split + let result = [] + autocmd WinEnter * let result += ["Enter", win_getid()] + autocmd WinLeave * let result += ["Leave", win_getid()] + autocmd WinNew * let result += ["New", win_getid()] + ]]) + api.nvim_win_set_config(0, { win = parent, split = 'left' }) + -- Shouldn't see any of those events, as we remain in the same window. + eq({}, eval('result')) + end) + + it('checks if splitting disallowed', function() + command('split | autocmd WinEnter * ++once call nvim_win_set_config(0, #{split: "right"})') + matches("E242: Can't split a window while closing another$", pcall_err(command, 'quit')) + + command('autocmd BufHidden * ++once call nvim_win_set_config(0, #{split: "left"})') + matches( + 'E1159: Cannot split a window when closing the buffer$', + pcall_err(command, 'new | quit') + ) + + -- OK when using window to different buffer. + local w = api.nvim_get_current_win() + command('autocmd BufHidden * ++once call nvim_win_set_config(' .. w .. ', #{split: "left"})') + command('new | quit') + end) + + --- Returns a function to get information about the window layout, sizes and positions of a + --- tabpage. + local function define_tp_info_function() + exec_lua([[ + function tp_info(tp) + return { + layout = vim.fn.winlayout(vim.api.nvim_tabpage_get_number(tp)), + pos_sizes = vim.tbl_map( + function(w) + local pos = vim.fn.win_screenpos(w) + return { + row = pos[1], + col = pos[2], + width = vim.fn.winwidth(w), + height = vim.fn.winheight(w) + } + end, + vim.api.nvim_tabpage_list_wins(tp) + ) + } + end + ]]) + + return function(tp) + return exec_lua('return tp_info(...)', tp) + end + end + + it('attempt to move window with no room', function() + -- Fill the 2nd tabpage full of windows until we run out of room. + -- Use &laststatus=0 to ensure restoring missing statuslines doesn't affect things. + command('set laststatus=0 | tabnew') + matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)')) + command('vsplit | wincmd | | wincmd p') + local t2 = api.nvim_get_current_tabpage() + local t2_cur_win = api.nvim_get_current_win() + local t2_top_split = fn.win_getid(1) + local t2_bot_split = fn.win_getid(fn.winnr('$')) + local t2_float = api.nvim_open_win( + 0, + false, + { relative = 'editor', row = 0, col = 0, width = 10, height = 10 } + ) + local t2_float_config = api.nvim_win_get_config(t2_float) + local tp_info = define_tp_info_function() + local t2_info = tp_info(t2) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'above' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'below' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'above' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'below' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, t2_float, { win = t2_top_split, split = 'above' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, t2_float, { win = t2_top_split, split = 'below' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, t2_float, { win = t2_bot_split, split = 'above' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, t2_float, { win = t2_bot_split, split = 'below' }) + ) + eq(t2_cur_win, api.nvim_get_current_win()) + eq(t2_info, tp_info(t2)) + eq(t2_float_config, api.nvim_win_get_config(t2_float)) + + -- Try to move windows from the 1st tabpage to the 2nd. + command('tabfirst | split | wincmd _') + local t1 = api.nvim_get_current_tabpage() + local t1_cur_win = api.nvim_get_current_win() + local t1_float = api.nvim_open_win( + 0, + false, + { relative = 'editor', row = 5, col = 3, width = 7, height = 6 } + ) + local t1_float_config = api.nvim_win_get_config(t1_float) + local t1_info = tp_info(t1) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'above' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, 0, { win = t2_top_split, split = 'below' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'above' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, 0, { win = t2_bot_split, split = 'below' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, t1_float, { win = t2_top_split, split = 'above' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, t1_float, { win = t2_top_split, split = 'below' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, t1_float, { win = t2_bot_split, split = 'above' }) + ) + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, t1_float, { win = t2_bot_split, split = 'below' }) + ) + eq(t1_cur_win, api.nvim_get_current_win()) + eq(t1_info, tp_info(t1)) + eq(t1_float_config, api.nvim_win_get_config(t1_float)) + end) + + it('attempt to move window from other tabpage with no room', function() + -- Fill up the 1st tabpage with horizontal splits, then create a 2nd with only a few. Go back + -- to the 1st and try to move windows from the 2nd (while it's non-current) to it. Check that + -- window positions and sizes in the 2nd are unchanged. + command('set laststatus=0') + matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)')) + + command('tab split') + local t2 = api.nvim_get_current_tabpage() + local t2_top = api.nvim_get_current_win() + command('belowright split') + local t2_mid_left = api.nvim_get_current_win() + command('belowright vsplit') + local t2_mid_right = api.nvim_get_current_win() + command('split | wincmd J') + local t2_bot = api.nvim_get_current_win() + local tp_info = define_tp_info_function() + local t2_info = tp_info(t2) + eq({ + 'col', + { + { 'leaf', t2_top }, + { + 'row', + { + { 'leaf', t2_mid_left }, + { 'leaf', t2_mid_right }, + }, + }, + { 'leaf', t2_bot }, + }, + }, t2_info.layout) + + local function try_move_t2_wins_to_t1() + for _, w in ipairs({ t2_bot, t2_mid_left, t2_mid_right, t2_top }) do + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, w, { win = 0, split = 'below' }) + ) + eq(t2_info, tp_info(t2)) + end + end + command('tabfirst') + try_move_t2_wins_to_t1() + -- Go to the 2nd tabpage to ensure nothing changes after win_comp_pos, last_status, .etc. + -- from enter_tabpage. + command('tabnext') + eq(t2_info, tp_info(t2)) + + -- Check things are fine with the global statusline too, for good measure. + -- Set it while the 2nd tabpage is current, so last_status runs for it. + command('set laststatus=3') + t2_info = tp_info(t2) + command('tabfirst') + try_move_t2_wins_to_t1() + end) + + it('handles cmdwin and textlock restrictions', function() + command('tabnew') + local t2 = api.nvim_get_current_tabpage() + local t2_win = api.nvim_get_current_win() + command('tabfirst') + local t1_move_win = api.nvim_get_current_win() + command('split') + + -- Can't move the cmdwin, or its old curwin to a different tabpage. + local old_curwin = api.nvim_get_current_win() + feed('q:') + eq( + 'E11: Invalid in command-line window; <CR> executes, CTRL-C quits', + pcall_err(api.nvim_win_set_config, 0, { split = 'left', win = t2_win }) + ) + eq( + 'E11: Invalid in command-line window; <CR> executes, CTRL-C quits', + pcall_err(api.nvim_win_set_config, old_curwin, { split = 'left', win = t2_win }) + ) + -- But we can move other windows. + api.nvim_win_set_config(t1_move_win, { split = 'left', win = t2_win }) + eq(t2, api.nvim_win_get_tabpage(t1_move_win)) + command('quit!') + + -- Can't configure windows such that the cmdwin would become the only non-float. + command('only!') + feed('q:') + eq( + 'E11: Invalid in command-line window; <CR> executes, CTRL-C quits', + pcall_err( + api.nvim_win_set_config, + old_curwin, + { relative = 'editor', row = 0, col = 0, width = 5, height = 5 } + ) + ) + -- old_curwin is now no longer the only other non-float, so we can make it floating now. + local t1_new_win = api.nvim_open_win( + api.nvim_create_buf(true, true), + false, + { split = 'left', win = old_curwin } + ) + api.nvim_win_set_config( + old_curwin, + { relative = 'editor', row = 0, col = 0, width = 5, height = 5 } + ) + eq('editor', api.nvim_win_get_config(old_curwin).relative) + -- ...which means we shouldn't be able to also make the new window floating too! + eq( + 'E11: Invalid in command-line window; <CR> executes, CTRL-C quits', + pcall_err( + api.nvim_win_set_config, + t1_new_win, + { relative = 'editor', row = 0, col = 0, width = 5, height = 5 } + ) + ) + -- Nothing ought to stop us from making the cmdwin itself floating, though... + api.nvim_win_set_config(0, { relative = 'editor', row = 0, col = 0, width = 5, height = 5 }) + eq('editor', api.nvim_win_get_config(0).relative) + -- We can't make our new window from before floating too, as it's now the only non-float. + eq( + 'Cannot change last window into float', + pcall_err( + api.nvim_win_set_config, + t1_new_win, + { relative = 'editor', row = 0, col = 0, width = 5, height = 5 } + ) + ) + command('quit!') + + -- Can't switch away from window before moving it to a different tabpage during textlock. + exec(([[ + new + call setline(1, 'foo') + setlocal debug=throw indentexpr=nvim_win_set_config(0,#{split:'left',win:%d}) + ]]):format(t2_win)) + local cur_win = api.nvim_get_current_win() + matches( + 'E565: Not allowed to change text or change window$', + pcall_err(command, 'normal! ==') + ) + eq(cur_win, api.nvim_get_current_win()) + end) + + it('updates statusline when moving bottom split', function() + local screen = Screen.new(10, 10) + screen:set_default_attr_ids({ + [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText + [1] = { bold = true, reverse = true }, -- StatusLine + }) + screen:attach() + exec([[ + set laststatus=0 + belowright split + call nvim_win_set_config(0, #{split: 'above', win: win_getid(winnr('#'))}) + ]]) + screen:expect([[ + ^ | + {0:~ }|*3 + {1:[No Name] }| + | + {0:~ }|*3 + | + ]]) + end) + + it("updates tp_curwin of moved window's original tabpage", function() + local t1 = api.nvim_get_current_tabpage() + command('tab split | split') + local t2 = api.nvim_get_current_tabpage() + local t2_alt_win = api.nvim_get_current_win() + command('vsplit') + local t2_cur_win = api.nvim_get_current_win() + command('tabprevious') + eq(t2_cur_win, api.nvim_tabpage_get_win(t2)) + + -- tp_curwin is unchanged when moved within the same tabpage. + api.nvim_win_set_config(t2_cur_win, { split = 'left', win = t2_alt_win }) + eq(t2_cur_win, api.nvim_tabpage_get_win(t2)) + + -- Also unchanged if the move failed. + command('let &winwidth = &columns | let &winminwidth = &columns') + matches( + 'E36: Not enough room$', + pcall_err(api.nvim_win_set_config, t2_cur_win, { split = 'left', win = 0 }) + ) + eq(t2_cur_win, api.nvim_tabpage_get_win(t2)) + command('set winminwidth& winwidth&') + + -- But is changed if successfully moved to a different tabpage. + api.nvim_win_set_config(t2_cur_win, { split = 'left', win = 0 }) + eq(t2_alt_win, api.nvim_tabpage_get_win(t2)) + eq(t1, api.nvim_win_get_tabpage(t2_cur_win)) + + -- Now do it for a float, which has different altwin logic. + command('tabnext') + t2_cur_win = + api.nvim_open_win(0, true, { relative = 'editor', row = 5, col = 5, width = 5, height = 5 }) + eq(t2_alt_win, fn.win_getid(fn.winnr('#'))) + command('tabprevious') + eq(t2_cur_win, api.nvim_tabpage_get_win(t2)) + + api.nvim_win_set_config(t2_cur_win, { split = 'left', win = 0 }) + eq(t2_alt_win, api.nvim_tabpage_get_win(t2)) + eq(t1, api.nvim_win_get_tabpage(t2_cur_win)) + end) end) describe('get_config', function() diff --git a/test/functional/autocmd/autocmd_oldtest_spec.lua b/test/functional/autocmd/autocmd_oldtest_spec.lua index 0243674f2d..1a3b723ac2 100644 --- a/test/functional/autocmd/autocmd_oldtest_spec.lua +++ b/test/functional/autocmd/autocmd_oldtest_spec.lua @@ -1,16 +1,27 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local eq = helpers.eq -local api = helpers.api -local fn = helpers.fn -local exec = helpers.exec -local feed = helpers.feed +local clear = n.clear +local eq = t.eq +local api = n.api +local fn = n.fn +local exec = n.exec +local feed = n.feed +local assert_log = t.assert_log +local check_close = n.check_close +local is_os = t.is_os + +local testlog = 'Xtest_autocmd_oldtest_log' describe('oldtests', function() before_each(clear) + after_each(function() + check_close() + os.remove(testlog) + end) + local exec_lines = function(str) return fn.split(fn.execute(str), '\n') end @@ -49,6 +60,7 @@ describe('oldtests', function() end) it('should fire on unload buf', function() + clear({ env = { NVIM_LOG_FILE = testlog } }) fn.writefile({ 'Test file Xxx1' }, 'Xxx1') fn.writefile({ 'Test file Xxx2' }, 'Xxx2') local fname = 'Xtest_functional_autocmd_unload' @@ -81,6 +93,10 @@ describe('oldtests', function() fn.delete('Xxx2') fn.delete(fname) fn.delete('Xout') + + if is_os('win') then + assert_log('stream write failed. RPC canceled; closing channel', testlog) + end end) -- oldtest: Test_delete_ml_get_errors() diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua index 5fffb70095..5e407a9986 100644 --- a/test/functional/autocmd/autocmd_spec.lua +++ b/test/functional/autocmd/autocmd_spec.lua @@ -1,24 +1,25 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local assert_visible = helpers.assert_visible -local assert_alive = helpers.assert_alive -local dedent = helpers.dedent -local eq = helpers.eq -local neq = helpers.neq -local eval = helpers.eval -local feed = helpers.feed -local clear = helpers.clear -local matches = helpers.matches -local api = helpers.api -local pcall_err = helpers.pcall_err -local fn = helpers.fn -local expect = helpers.expect -local command = helpers.command -local exc_exec = helpers.exc_exec -local exec_lua = helpers.exec_lua -local retry = helpers.retry -local source = helpers.source +local assert_visible = n.assert_visible +local assert_alive = n.assert_alive +local dedent = t.dedent +local eq = t.eq +local neq = t.neq +local eval = n.eval +local feed = n.feed +local clear = n.clear +local matches = t.matches +local api = n.api +local pcall_err = t.pcall_err +local fn = n.fn +local expect = n.expect +local command = n.command +local exc_exec = n.exc_exec +local exec_lua = n.exec_lua +local retry = t.retry +local source = n.source describe('autocmd', function() before_each(clear) @@ -371,6 +372,31 @@ describe('autocmd', function() assert_alive() screen:expect_unchanged() + -- Also check with win_splitmove(). + exec_lua [[ + vim.api.nvim_buf_call(_G.buf, function() + vim.fn.win_splitmove(vim.fn.winnr(), vim.fn.win_getid(1)) + end) + ]] + screen:expect_unchanged() + + -- Also check with nvim_win_set_config(). + matches( + ': Failed to move window %d+ into split$', + pcall_err( + exec_lua, + [[ + vim.api.nvim_buf_call(_G.buf, function() + vim.api.nvim_win_set_config(0, { + vertical = true, + win = vim.fn.win_getid(1) + }) + end) + ]] + ) + ) + screen:expect_unchanged() + -- Ensure splitting still works from inside the aucmd_win. exec_lua [[vim.api.nvim_buf_call(_G.buf, function() vim.cmd "split" end)]] screen:expect [[ @@ -391,12 +417,12 @@ describe('autocmd', function() eq( 'editor', exec_lua [[ - vim.cmd "only" - vim.api.nvim_buf_call(_G.buf, function() - _G.config = vim.api.nvim_win_get_config(0) - end) - return _G.config.relative - ]] + vim.cmd "only" + vim.api.nvim_buf_call(_G.buf, function() + _G.config = vim.api.nvim_win_get_config(0) + end) + return _G.config.relative + ]] ) end) @@ -437,11 +463,11 @@ describe('autocmd', function() pcall_err( exec_lua, [[ - vim.api.nvim_buf_call(_G.buf, function() - local win = vim.api.nvim_get_current_win() - vim.api.nvim_win_close(win, true) - end) - ]] + vim.api.nvim_buf_call(_G.buf, function() + local win = vim.api.nvim_get_current_win() + vim.api.nvim_win_close(win, true) + end) + ]] ) ) matches( @@ -449,12 +475,12 @@ describe('autocmd', function() pcall_err( exec_lua, [[ - vim.api.nvim_buf_call(_G.buf, function() - local win = vim.api.nvim_get_current_win() - vim.cmd('tabnext') - vim.api.nvim_win_close(win, true) - end) - ]] + vim.api.nvim_buf_call(_G.buf, function() + local win = vim.api.nvim_get_current_win() + vim.cmd('tabnext') + vim.api.nvim_win_close(win, true) + end) + ]] ) ) matches( @@ -462,11 +488,11 @@ describe('autocmd', function() pcall_err( exec_lua, [[ - vim.api.nvim_buf_call(_G.buf, function() - local win = vim.api.nvim_get_current_win() - vim.api.nvim_win_hide(win) - end) - ]] + vim.api.nvim_buf_call(_G.buf, function() + local win = vim.api.nvim_get_current_win() + vim.api.nvim_win_hide(win) + end) + ]] ) ) matches( @@ -474,12 +500,12 @@ describe('autocmd', function() pcall_err( exec_lua, [[ - vim.api.nvim_buf_call(_G.buf, function() - local win = vim.api.nvim_get_current_win() - vim.cmd('tabnext') - vim.api.nvim_win_hide(win) - end) - ]] + vim.api.nvim_buf_call(_G.buf, function() + local win = vim.api.nvim_get_current_win() + vim.cmd('tabnext') + vim.api.nvim_win_hide(win) + end) + ]] ) ) end) diff --git a/test/functional/autocmd/bufenter_spec.lua b/test/functional/autocmd/bufenter_spec.lua index af0dd887fa..72e650cae4 100644 --- a/test/functional/autocmd/bufenter_spec.lua +++ b/test/functional/autocmd/bufenter_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local request = helpers.request -local source = helpers.source +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local request = n.request +local source = n.source describe('autocmd BufEnter', function() before_each(clear) @@ -33,7 +34,7 @@ describe('autocmd BufEnter', function() end) it('triggered by ":split normal|:help|:bw"', function() - helpers.add_builddir_to_rtp() + n.add_builddir_to_rtp() command('split normal') command('wincmd j') command('help') diff --git a/test/functional/autocmd/bufmodifiedset_spec.lua b/test/functional/autocmd/bufmodifiedset_spec.lua index 27fe9fcc94..fb8f160b05 100644 --- a/test/functional/autocmd/bufmodifiedset_spec.lua +++ b/test/functional/autocmd/bufmodifiedset_spec.lua @@ -1,10 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local source = helpers.source -local request = helpers.request +local clear = n.clear +local eq = t.eq +local eval = n.eval +local source = n.source +local request = n.request describe('BufModified', function() before_each(clear) diff --git a/test/functional/autocmd/cmdline_spec.lua b/test/functional/autocmd/cmdline_spec.lua index 7428456656..ad3bc3576f 100644 --- a/test/functional/autocmd/cmdline_spec.lua +++ b/test/functional/autocmd/cmdline_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local expect = helpers.expect -local eval = helpers.eval -local next_msg = helpers.next_msg -local feed = helpers.feed -local api = helpers.api +local clear = n.clear +local command = n.command +local eq = t.eq +local expect = n.expect +local eval = n.eval +local next_msg = n.next_msg +local feed = n.feed +local api = n.api describe('cmdline autocommands', function() local channel diff --git a/test/functional/autocmd/completedone_spec.lua b/test/functional/autocmd/completedone_spec.lua new file mode 100644 index 0000000000..33beb16db2 --- /dev/null +++ b/test/functional/autocmd/completedone_spec.lua @@ -0,0 +1,41 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local call = n.call +local feed = n.feed +local eval = n.eval +local eq = t.eq + +describe('CompleteDone', function() + before_each(clear) + + describe('sets v:event.reason', function() + before_each(function() + clear() + command('autocmd CompleteDone * let g:donereason = v:event.reason') + feed('i') + call('complete', call('col', '.'), { 'foo', 'bar' }) + end) + + it('accept', function() + feed('<C-y>') + eq('accept', eval('g:donereason')) + end) + describe('cancel', function() + it('on <C-e>', function() + feed('<C-e>') + eq('cancel', eval('g:donereason')) + end) + it('on non-keyword character', function() + feed('<Esc>') + eq('cancel', eval('g:donereason')) + end) + it('when overriden by another complete()', function() + call('complete', call('col', '.'), { 'bar', 'baz' }) + eq('cancel', eval('g:donereason')) + end) + end) + end) +end) diff --git a/test/functional/autocmd/cursorhold_spec.lua b/test/functional/autocmd/cursorhold_spec.lua index fc2b65f53a..c2815be5c6 100644 --- a/test/functional/autocmd/cursorhold_spec.lua +++ b/test/functional/autocmd/cursorhold_spec.lua @@ -1,12 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local feed = helpers.feed -local retry = helpers.retry -local exec = helpers.source +local clear = n.clear +local eq = t.eq +local feed = n.feed +local retry = t.retry +local exec = n.source local sleep = vim.uv.sleep -local api = helpers.api +local api = n.api before_each(clear) diff --git a/test/functional/autocmd/cursormoved_spec.lua b/test/functional/autocmd/cursormoved_spec.lua index 302afe87b8..2cf02e00f3 100644 --- a/test/functional/autocmd/cursormoved_spec.lua +++ b/test/functional/autocmd/cursormoved_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local api = helpers.api -local source = helpers.source -local command = helpers.command +local clear = n.clear +local eq = t.eq +local eval = n.eval +local api = n.api +local source = n.source +local command = n.command describe('CursorMoved', function() before_each(clear) diff --git a/test/functional/autocmd/dirchanged_spec.lua b/test/functional/autocmd/dirchanged_spec.lua index 7ad529891f..24ac737b5b 100644 --- a/test/functional/autocmd/dirchanged_spec.lua +++ b/test/functional/autocmd/dirchanged_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local request = helpers.request -local is_os = helpers.is_os +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local request = n.request +local is_os = t.is_os describe('autocmd DirChanged and DirChangedPre', function() local curdir = vim.uv.cwd():gsub('\\', '/') @@ -22,12 +23,12 @@ describe('autocmd DirChanged and DirChangedPre', function() setup(function() for _, dir in pairs(dirs) do - helpers.mkdir(dir) + t.mkdir(dir) end end) teardown(function() for _, dir in pairs(dirs) do - helpers.rmdir(dir) + n.rmdir(dir) end end) diff --git a/test/functional/autocmd/filetype_spec.lua b/test/functional/autocmd/filetype_spec.lua index 648f830f27..91843c7910 100644 --- a/test/functional/autocmd/filetype_spec.lua +++ b/test/functional/autocmd/filetype_spec.lua @@ -1,14 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() -local eval = helpers.eval -local clear = helpers.clear -local command = helpers.command +local eval = n.eval +local clear = n.clear +local command = n.command describe('autocmd FileType', function() before_each(clear) it('is triggered by :help only once', function() - helpers.add_builddir_to_rtp() + n.add_builddir_to_rtp() command('let g:foo = 0') command('autocmd FileType help let g:foo = g:foo + 1') command('help help') diff --git a/test/functional/autocmd/focus_spec.lua b/test/functional/autocmd/focus_spec.lua index 4f4a036ba8..5163b576db 100644 --- a/test/functional/autocmd/focus_spec.lua +++ b/test/functional/autocmd/focus_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local thelpers = require('test.functional.terminal.helpers') -local clear = helpers.clear -local feed_command = helpers.feed_command -local feed_data = thelpers.feed_data +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local tt = require('test.functional.terminal.testutil') -if helpers.skip(helpers.is_os('win')) then +local clear = n.clear +local feed_command = n.feed_command +local feed_data = tt.feed_data + +if t.skip(t.is_os('win')) then return end @@ -14,7 +16,7 @@ describe('autoread TUI FocusGained/FocusLost', function() before_each(function() clear() - screen = thelpers.setup_child_nvim({ + screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -39,7 +41,7 @@ describe('autoread TUI FocusGained/FocusLost', function() line 4 ]] - helpers.write_file(path, '') + t.write_file(path, '') local atime = os.time() - 10 vim.uv.fs_utime(path, atime, atime) @@ -75,7 +77,7 @@ describe('autoread TUI FocusGained/FocusLost', function() unchanged = true, } - helpers.write_file(path, expected_addition) + t.write_file(path, expected_addition) feed_data('\027[I') diff --git a/test/functional/autocmd/modechanged_spec.lua b/test/functional/autocmd/modechanged_spec.lua index 8ad914a597..61ce15d91d 100644 --- a/test/functional/autocmd/modechanged_spec.lua +++ b/test/functional/autocmd/modechanged_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq -local feed, command = helpers.feed, helpers.command -local exec_lua = helpers.exec_lua +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eval, eq = n.clear, n.eval, t.eq +local feed, command = n.feed, n.command +local exec_lua = n.exec_lua describe('ModeChanged', function() before_each(function() diff --git a/test/functional/autocmd/recording_spec.lua b/test/functional/autocmd/recording_spec.lua index b9aec774f1..6ee61c5676 100644 --- a/test/functional/autocmd/recording_spec.lua +++ b/test/functional/autocmd/recording_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local source_vim = helpers.source +local clear = n.clear +local eq = t.eq +local eval = n.eval +local source_vim = n.source describe('RecordingEnter', function() before_each(clear) diff --git a/test/functional/autocmd/safestate_spec.lua b/test/functional/autocmd/safestate_spec.lua index b5b7ab2f95..c45a64e373 100644 --- a/test/functional/autocmd/safestate_spec.lua +++ b/test/functional/autocmd/safestate_spec.lua @@ -1,9 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local exec = helpers.exec -local feed = helpers.feed -local api = helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local exec = n.exec +local feed = n.feed +local api = n.api before_each(clear) diff --git a/test/functional/autocmd/searchwrapped_spec.lua b/test/functional/autocmd/searchwrapped_spec.lua index 0705b2d5de..0c0b3595ca 100644 --- a/test/functional/autocmd/searchwrapped_spec.lua +++ b/test/functional/autocmd/searchwrapped_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local command = helpers.command -local api = helpers.api -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local api = n.api +local eq = t.eq +local eval = n.eval +local feed = n.feed describe('autocmd SearchWrapped', function() before_each(function() diff --git a/test/functional/autocmd/show_spec.lua b/test/functional/autocmd/show_spec.lua index 1a9dc8a337..7e1818c4fd 100644 --- a/test/functional/autocmd/show_spec.lua +++ b/test/functional/autocmd/show_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local dedent = helpers.dedent -local eq = helpers.eq -local fn = helpers.fn -local eval = helpers.eval -local exec = helpers.exec -local feed = helpers.feed +local clear = n.clear +local command = n.command +local dedent = t.dedent +local eq = t.eq +local fn = n.fn +local eval = n.eval +local exec = n.exec +local feed = n.feed describe(':autocmd', function() before_each(function() diff --git a/test/functional/autocmd/signal_spec.lua b/test/functional/autocmd/signal_spec.lua index c7087254e7..4416afb3ba 100644 --- a/test/functional/autocmd/signal_spec.lua +++ b/test/functional/autocmd/signal_spec.lua @@ -1,12 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local fn = helpers.fn -local next_msg = helpers.next_msg -local is_os = helpers.is_os -local skip = helpers.skip +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local fn = n.fn +local next_msg = n.next_msg +local is_os = t.is_os +local skip = t.skip if skip(is_os('win'), 'Only applies to POSIX systems') then return diff --git a/test/functional/autocmd/tabclose_spec.lua b/test/functional/autocmd/tabclose_spec.lua index d0b2ac6a8d..fde72cf4d7 100644 --- a/test/functional/autocmd/tabclose_spec.lua +++ b/test/functional/autocmd/tabclose_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eq = helpers.clear, helpers.eq -local api = helpers.api -local command = helpers.command +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq = n.clear, t.eq +local api = n.api +local command = n.command describe('TabClosed', function() before_each(clear) diff --git a/test/functional/autocmd/tabnew_spec.lua b/test/functional/autocmd/tabnew_spec.lua index ad40954f76..80ab5a1944 100644 --- a/test/functional/autocmd/tabnew_spec.lua +++ b/test/functional/autocmd/tabnew_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval describe('autocmd TabNew', function() before_each(clear) diff --git a/test/functional/autocmd/tabnewentered_spec.lua b/test/functional/autocmd/tabnewentered_spec.lua index b888845e3b..fcfebd7af5 100644 --- a/test/functional/autocmd/tabnewentered_spec.lua +++ b/test/functional/autocmd/tabnewentered_spec.lua @@ -1,13 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local command = helpers.command -local dedent = helpers.dedent -local eval = helpers.eval -local eq = helpers.eq -local feed = helpers.feed -local api = helpers.api -local exec_capture = helpers.exec_capture +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local dedent = t.dedent +local eval = n.eval +local eq = t.eq +local feed = n.feed +local api = n.api +local exec_capture = n.exec_capture describe('TabNewEntered', function() describe('au TabNewEntered', function() diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua index 982edfa06a..a63996ae36 100644 --- a/test/functional/autocmd/termxx_spec.lua +++ b/test/functional/autocmd/termxx_spec.lua @@ -1,17 +1,18 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local tt = require('test.functional.terminal.testutil') local uv = vim.uv -local helpers = require('test.functional.helpers')(after_each) -local thelpers = require('test.functional.terminal.helpers') - -local clear, command, testprg = helpers.clear, helpers.command, helpers.testprg -local eval, eq, neq, retry = helpers.eval, helpers.eq, helpers.neq, helpers.retry -local matches = helpers.matches -local ok = helpers.ok -local feed = helpers.feed -local api = helpers.api -local pcall_err = helpers.pcall_err -local assert_alive = helpers.assert_alive -local skip = helpers.skip -local is_os = helpers.is_os + +local clear, command, testprg = n.clear, n.command, n.testprg +local eval, eq, neq, retry = n.eval, t.eq, t.neq, t.retry +local matches = t.matches +local ok = t.ok +local feed = n.feed +local api = n.api +local pcall_err = t.pcall_err +local assert_alive = n.assert_alive +local skip = t.skip +local is_os = t.is_os describe('autocmd TermClose', function() before_each(function() @@ -198,11 +199,11 @@ end) describe('autocmd TextChangedT', function() clear() - local screen = thelpers.screen_setup() + local screen = tt.screen_setup() it('works', function() command('autocmd TextChangedT * ++once let g:called = 1') - thelpers.feed_data('a') + tt.feed_data('a') retry(nil, nil, function() eq(1, api.nvim_get_var('called')) end) @@ -210,7 +211,7 @@ describe('autocmd TextChangedT', function() it('cannot delete terminal buffer', function() command([[autocmd TextChangedT * call nvim_input('<CR>') | bwipe!]]) - thelpers.feed_data('a') + tt.feed_data('a') screen:expect({ any = 'E937: ' }) matches( '^E937: Attempt to delete a buffer that is in use: term://', diff --git a/test/functional/autocmd/textchanged_spec.lua b/test/functional/autocmd/textchanged_spec.lua index d501560dc1..7c679cd4c0 100644 --- a/test/functional/autocmd/textchanged_spec.lua +++ b/test/functional/autocmd/textchanged_spec.lua @@ -1,17 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local exec = helpers.exec -local command = helpers.command -local feed = helpers.feed -local eq = helpers.eq -local neq = helpers.neq -local eval = helpers.eval -local poke_eventloop = helpers.poke_eventloop - -before_each(clear) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec = n.exec +local command = n.command +local feed = n.feed +local eq = t.eq +local neq = t.neq +local eval = n.eval +local poke_eventloop = n.poke_eventloop +local write_file = t.write_file -- oldtest: Test_ChangedP() it('TextChangedI and TextChangedP autocommands', function() + clear() -- The oldtest uses feedkeys() with 'x' flag, which never triggers TextChanged. -- So don't add TextChanged autocommand here. exec([[ @@ -93,6 +95,7 @@ end) -- oldtest: Test_TextChangedI_with_setline() it('TextChangedI with setline()', function() + clear() exec([[ let g:setline_handled = v:false func SetLineOne() @@ -118,76 +121,93 @@ it('TextChangedI with setline()', function() eq('', eval('getline(2)')) end) +-- oldtest: Test_TextChanged_with_norm() +it('TextChanged is triggered after :norm that enters Insert mode', function() + clear() + exec([[ + let g:a = 0 + au TextChanged * let g:a += 1 + ]]) + eq(0, eval('g:a')) + feed(':norm! ia<CR>') + eq(1, eval('g:a')) +end) + -- oldtest: Test_Changed_ChangedI() it('TextChangedI and TextChanged', function() + write_file('XTextChangedI2', 'one\ntwo\nthree') + finally(function() + os.remove('XTextChangedI2') + end) + clear('XTextChangedI2') + exec([[ - let [g:autocmd_i, g:autocmd_n] = ['',''] + let [g:autocmd_n, g:autocmd_i] = ['',''] - func! TextChangedAutocmdI(char) + func TextChangedAutocmd(char) let g:autocmd_{tolower(a:char)} = a:char .. b:changedtick endfunc - augroup Test_TextChanged - au! - au TextChanged <buffer> :call TextChangedAutocmdI('N') - au TextChangedI <buffer> :call TextChangedAutocmdI('I') - augroup END + au TextChanged <buffer> :call TextChangedAutocmd('N') + au TextChangedI <buffer> :call TextChangedAutocmd('I') + + nnoremap <CR> o<Esc> ]]) + -- TextChanged should trigger if a mapping enters and leaves Insert mode. + feed('<CR>') + eq('N4', eval('g:autocmd_n')) + eq('', eval('g:autocmd_i')) + feed('i') - poke_eventloop() + eq('N4', eval('g:autocmd_n')) + eq('', eval('g:autocmd_i')) + -- TextChangedI should trigger if change is done in Insert mode. feed('f') - poke_eventloop() + eq('N4', eval('g:autocmd_n')) + eq('I5', eval('g:autocmd_i')) feed('o') - poke_eventloop() + eq('N4', eval('g:autocmd_n')) + eq('I6', eval('g:autocmd_i')) feed('o') - poke_eventloop() - feed('<esc>') - eq('', eval('g:autocmd_n')) - eq('I5', eval('g:autocmd_i')) + eq('N4', eval('g:autocmd_n')) + eq('I7', eval('g:autocmd_i')) + -- TextChanged shouldn't trigger when leaving Insert mode and TextChangedI + -- has been triggered. + feed('<Esc>') + eq('N4', eval('g:autocmd_n')) + eq('I7', eval('g:autocmd_i')) + -- TextChanged should trigger if change is done in Normal mode. feed('yyp') - eq('N6', eval('g:autocmd_n')) - eq('I5', eval('g:autocmd_i')) - - -- TextChangedI should only trigger if change was done in Insert mode - command([[let g:autocmd_i = '']]) - feed('yypi<esc>') - eq('', eval('g:autocmd_i')) + eq('N8', eval('g:autocmd_n')) + eq('I7', eval('g:autocmd_i')) - -- TextChanged should only trigger if change was done in Normal mode - command([[let g:autocmd_n = '']]) - feed('ibar<esc>') - eq('', eval('g:autocmd_n')) + -- TextChangedI shouldn't trigger if change isn't done in Insert mode. + feed('i') + eq('N8', eval('g:autocmd_n')) + eq('I7', eval('g:autocmd_i')) + feed('<Esc>') + eq('N8', eval('g:autocmd_n')) + eq('I7', eval('g:autocmd_i')) + -- TextChangedI should trigger if change is a mix of Normal and Insert modes. local function validate_mixed_textchangedi(keys) - feed('ifoo<esc>') - command([[let g:autocmd_i = '']]) - command([[let g:autocmd_n = '']]) - for _, s in ipairs(keys) do - feed(s) - poke_eventloop() - end + feed('ifoo<Esc>') + command(":let [g:autocmd_n, g:autocmd_i] = ['', '']") + feed(keys) + eq('', eval('g:autocmd_n')) neq('', eval('g:autocmd_i')) + feed('<Esc>') eq('', eval('g:autocmd_n')) + neq('', eval('g:autocmd_i')) end - validate_mixed_textchangedi({ 'o', '<esc>' }) - validate_mixed_textchangedi({ 'O', '<esc>' }) - validate_mixed_textchangedi({ 'ciw', '<esc>' }) - validate_mixed_textchangedi({ 'cc', '<esc>' }) - validate_mixed_textchangedi({ 'C', '<esc>' }) - validate_mixed_textchangedi({ 's', '<esc>' }) - validate_mixed_textchangedi({ 'S', '<esc>' }) -end) - --- oldtest: Test_TextChanged_with_norm() -it('TextChanged is triggered after :norm that enters Insert mode', function() - exec([[ - let g:a = 0 - au TextChanged * let g:a += 1 - ]]) - eq(0, eval('g:a')) - feed(':norm! ia<CR>') - eq(1, eval('g:a')) + validate_mixed_textchangedi('o') + validate_mixed_textchangedi('O') + validate_mixed_textchangedi('ciw') + validate_mixed_textchangedi('cc') + validate_mixed_textchangedi('C') + validate_mixed_textchangedi('s') + validate_mixed_textchangedi('S') end) diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua index 29cd62f586..cf8cbe25c1 100644 --- a/test/functional/autocmd/textyankpost_spec.lua +++ b/test/functional/autocmd/textyankpost_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq -local feed, command, expect = helpers.feed, helpers.command, helpers.expect -local api, fn, neq = helpers.api, helpers.fn, helpers.neq +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eval, eq = n.clear, n.eval, t.eq +local feed, command, expect = n.feed, n.command, n.expect +local api, fn, neq = n.api, n.fn, t.neq describe('TextYankPost', function() before_each(function() @@ -72,19 +74,19 @@ describe('TextYankPost', function() command('set debug=msg') -- the regcontents should not be changed without copy. local status, err = pcall(command, 'call extend(g:event.regcontents, ["more text"])') - eq(status, false) + eq(false, status) neq(nil, string.find(err, ':E742:')) -- can't mutate keys inside the autocommand command('autocmd! TextYankPost * let v:event.regcontents = 0') status, err = pcall(command, 'normal yy') - eq(status, false) + eq(false, status) neq(nil, string.find(err, ':E46:')) -- can't add keys inside the autocommand command('autocmd! TextYankPost * let v:event.mykey = 0') status, err = pcall(command, 'normal yy') - eq(status, false) + eq(false, status) neq(nil, string.find(err, ':E742:')) end) diff --git a/test/functional/autocmd/win_scrolled_resized_spec.lua b/test/functional/autocmd/win_scrolled_resized_spec.lua index d40dc37103..72bbc1e469 100644 --- a/test/functional/autocmd/win_scrolled_resized_spec.lua +++ b/test/functional/autocmd/win_scrolled_resized_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local exec = helpers.exec -local command = helpers.command -local feed = helpers.feed -local api = helpers.api -local assert_alive = helpers.assert_alive +local clear = n.clear +local eq = t.eq +local eval = n.eval +local exec = n.exec +local command = n.command +local feed = n.feed +local api = n.api +local assert_alive = n.assert_alive before_each(clear) diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 56a2f5a571..a98e190a60 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -1,17 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eq, eval, next_msg, ok, source = - helpers.clear, helpers.eq, helpers.eval, helpers.next_msg, helpers.ok, helpers.source -local command, fn, api = helpers.command, helpers.fn, helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq, eval, next_msg, ok, source = n.clear, t.eq, n.eval, n.next_msg, t.ok, n.source +local command, fn, api = n.command, n.fn, n.api +local matches = t.matches local sleep = vim.uv.sleep -local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv -local get_session, set_session = helpers.get_session, helpers.set_session -local nvim_prog = helpers.nvim_prog -local is_os = helpers.is_os -local retry = helpers.retry -local expect_twostreams = helpers.expect_twostreams -local assert_alive = helpers.assert_alive -local pcall_err = helpers.pcall_err -local skip = helpers.skip +local spawn, nvim_argv = n.spawn, n.nvim_argv +local get_session, set_session = n.get_session, n.set_session +local nvim_prog = n.nvim_prog +local is_os = t.is_os +local retry = t.retry +local expect_twostreams = n.expect_twostreams +local assert_alive = n.assert_alive +local pcall_err = t.pcall_err +local skip = t.skip describe('channels', function() local init = [[ @@ -277,7 +279,7 @@ describe('channels', function() local _, err = pcall(command, "call rpcrequest(id, 'nvim_command', 'call chanclose(v:stderr, \"stdin\")')") - ok(string.find(err, 'E906: invalid stream for channel') ~= nil) + matches('E906: invalid stream for channel', err) eq(1, eval("rpcrequest(id, 'nvim_eval', 'chanclose(v:stderr, \"stderr\")')")) eq({ 'notification', 'stderr', { 3, { '' } } }, next_msg()) diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index d9e3cc3f31..34c3eedbd2 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -1,24 +1,25 @@ -local helpers = require('test.functional.helpers')(after_each) - -local assert_alive = helpers.assert_alive -local command = helpers.command -local feed_command = helpers.feed_command -local feed = helpers.feed -local eval = helpers.eval -local eq = helpers.eq -local run = helpers.run -local fn = helpers.fn -local nvim_prog = helpers.nvim_prog -local pcall_err = helpers.pcall_err -local exec_capture = helpers.exec_capture -local poke_eventloop = helpers.poke_eventloop +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local assert_alive = n.assert_alive +local command = n.command +local feed_command = n.feed_command +local feed = n.feed +local eval = n.eval +local eq = t.eq +local run = n.run +local fn = n.fn +local nvim_prog = n.nvim_prog +local pcall_err = t.pcall_err +local exec_capture = n.exec_capture +local poke_eventloop = n.poke_eventloop describe('v:exiting', function() local cid before_each(function() - helpers.clear() - cid = helpers.api.nvim_get_chan_info(0).id + n.clear() + cid = n.api.nvim_get_chan_info(0).id end) it('defaults to v:null', function() @@ -74,7 +75,7 @@ describe(':cquit', function() end before_each(function() - helpers.clear() + n.clear() end) it('exits with non-zero after :cquit', function() diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua index 928cab525c..5b0be1e83c 100644 --- a/test/functional/core/fileio_spec.lua +++ b/test/functional/core/fileio_spec.lua @@ -1,37 +1,38 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local Screen = require('test.functional.ui.screen') local uv = vim.uv -local helpers = require('test.functional.helpers')(after_each) - -local assert_log = helpers.assert_log -local assert_nolog = helpers.assert_nolog -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local neq = helpers.neq -local ok = helpers.ok -local feed = helpers.feed -local fn = helpers.fn -local nvim_prog = helpers.nvim_prog -local request = helpers.request -local retry = helpers.retry -local rmdir = helpers.rmdir -local matches = helpers.matches -local api = helpers.api -local mkdir = helpers.mkdir + +local assert_log = t.assert_log +local assert_nolog = t.assert_nolog +local clear = n.clear +local command = n.command +local eq = t.eq +local neq = t.neq +local ok = t.ok +local feed = n.feed +local fn = n.fn +local nvim_prog = n.nvim_prog +local request = n.request +local retry = t.retry +local rmdir = n.rmdir +local matches = t.matches +local api = n.api +local mkdir = t.mkdir local sleep = vim.uv.sleep -local read_file = helpers.read_file +local read_file = t.read_file local trim = vim.trim -local currentdir = helpers.fn.getcwd -local assert_alive = helpers.assert_alive -local check_close = helpers.check_close -local expect_exit = helpers.expect_exit -local write_file = helpers.write_file -local Screen = require('test.functional.ui.screen') -local feed_command = helpers.feed_command -local skip = helpers.skip -local is_os = helpers.is_os -local is_ci = helpers.is_ci -local spawn = helpers.spawn -local set_session = helpers.set_session +local currentdir = n.fn.getcwd +local assert_alive = n.assert_alive +local check_close = n.check_close +local expect_exit = n.expect_exit +local write_file = t.write_file +local feed_command = n.feed_command +local skip = t.skip +local is_os = t.is_os +local is_ci = t.is_ci +local spawn = n.spawn +local set_session = n.set_session describe('fileio', function() before_each(function() end) @@ -54,7 +55,7 @@ describe('fileio', function() --- Starts a new nvim session and returns an attached screen. local function startup(extra_args) extra_args = extra_args or {} - local argv = vim.tbl_flatten({ args, '--embed', extra_args }) + local argv = vim.iter({ args, '--embed', extra_args }):flatten():totable() local screen_nvim = spawn(argv) set_session(screen_nvim) local screen = Screen.new(70, 10) @@ -100,7 +101,7 @@ describe('fileio', function() eq('foozubbaz', trim(read_file('Xtest_startup_file1'))) -- 4. Exit caused by deadly signal (+ 'swapfile'). - local j = fn.jobstart(vim.tbl_flatten({ args, '--embed' }), { rpc = true }) + local j = fn.jobstart(vim.iter({ args, '--embed' }):flatten():totable(), { rpc = true }) fn.rpcrequest( j, 'nvim_exec2', @@ -228,7 +229,7 @@ describe('fileio', function() local initial_content = 'foo' local backup_dir = 'Xtest_backupdir' - local sep = helpers.get_pathsep() + local sep = n.get_pathsep() local link_file_name = 'Xtest_startup_file2' local backup_file_name = backup_dir .. sep .. link_file_name .. '~' @@ -329,7 +330,7 @@ describe('tmpdir', function() before_each(function() -- Fake /tmp dir so that we can mess it up. - os_tmpdir = vim.uv.fs_mkdtemp(vim.fs.dirname(helpers.tmpname()) .. '/nvim_XXXXXXXXXX') + os_tmpdir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname()) .. '/nvim_XXXXXXXXXX') end) after_each(function() diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 13f5f9a5e1..e1efc07452 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -1,40 +1,41 @@ -local helpers = require('test.functional.helpers')(after_each) -local thelpers = require('test.functional.terminal.helpers') - -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local exc_exec = helpers.exc_exec -local feed_command = helpers.feed_command -local feed = helpers.feed -local insert = helpers.insert -local neq = helpers.neq -local next_msg = helpers.next_msg -local testprg = helpers.testprg -local ok = helpers.ok -local source = helpers.source -local write_file = helpers.write_file -local mkdir = helpers.mkdir -local rmdir = helpers.rmdir -local assert_alive = helpers.assert_alive -local command = helpers.command -local fn = helpers.fn -local os_kill = helpers.os_kill -local retry = helpers.retry -local api = helpers.api -local NIL = vim.NIL -local poke_eventloop = helpers.poke_eventloop -local get_pathsep = helpers.get_pathsep -local pathroot = helpers.pathroot -local exec_lua = helpers.exec_lua -local nvim_set = helpers.nvim_set -local expect_twostreams = helpers.expect_twostreams -local expect_msg_seq = helpers.expect_msg_seq -local pcall_err = helpers.pcall_err -local matches = helpers.matches +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local skip = helpers.skip -local is_os = helpers.is_os +local tt = require('test.functional.terminal.testutil') + +local clear = n.clear +local eq = t.eq +local eval = n.eval +local exc_exec = n.exc_exec +local feed_command = n.feed_command +local feed = n.feed +local insert = n.insert +local neq = t.neq +local next_msg = n.next_msg +local testprg = n.testprg +local ok = t.ok +local source = n.source +local write_file = t.write_file +local mkdir = t.mkdir +local rmdir = n.rmdir +local assert_alive = n.assert_alive +local command = n.command +local fn = n.fn +local os_kill = n.os_kill +local retry = t.retry +local api = n.api +local NIL = vim.NIL +local poke_eventloop = n.poke_eventloop +local get_pathsep = n.get_pathsep +local pathroot = n.pathroot +local exec_lua = n.exec_lua +local nvim_set = n.nvim_set +local expect_twostreams = n.expect_twostreams +local expect_msg_seq = n.expect_msg_seq +local pcall_err = t.pcall_err +local matches = t.matches +local skip = t.skip +local is_os = t.is_os describe('jobs', function() local channel @@ -73,7 +74,7 @@ describe('jobs', function() command("let j = jobstart('env', g:job_opts)") end end) - ok(string.find(err, 'E475: Invalid argument: env') ~= nil) + matches('E475: Invalid argument: env', err) end) it('append environment #env', function() @@ -227,7 +228,7 @@ describe('jobs', function() command("let j = jobstart('pwd', g:job_opts)") end end) - ok(string.find(err, 'E475: Invalid argument: expected valid directory$') ~= nil) + matches('E475: Invalid argument: expected valid directory$', err) end) it('error on non-executable `cwd`', function() @@ -307,7 +308,7 @@ describe('jobs', function() it('preserves NULs', function() -- Make a file with NULs in it. - local filename = helpers.tmpname() + local filename = t.tmpname() write_file(filename, 'abc\0def\n') command("let j = jobstart(['cat', '" .. filename .. "'], g:job_opts)") @@ -732,7 +733,7 @@ describe('jobs', function() describe('jobwait()', function() before_each(function() if is_os('win') then - helpers.set_shell_powershell() + n.set_shell_powershell() end end) @@ -980,10 +981,7 @@ describe('jobs', function() command('let g:job_opts.pty = v:true') command('let g:job_opts.rpc = v:true') local _, err = pcall(command, "let j = jobstart(['cat', '-'], g:job_opts)") - ok( - string.find(err, "E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set") - ~= nil - ) + matches("E475: Invalid argument: job cannot have both 'pty' and 'rpc' options set", err) end) it('does not crash when repeatedly failing to start shell', function() @@ -1185,7 +1183,7 @@ describe('jobs', function() end) it('does not close the same handle twice on exit #25086', function() - local filename = string.format('%s.lua', helpers.tmpname()) + local filename = string.format('%s.lua', t.tmpname()) write_file( filename, [[ @@ -1198,7 +1196,7 @@ describe('jobs', function() ]] ) - local screen = thelpers.setup_child_nvim({ + local screen = tt.setup_child_nvim({ '--cmd', 'set notermguicolors', '-i', @@ -1233,15 +1231,16 @@ describe('pty process teardown', function() screen:attach() screen:expect([[ ^ | - ~ |*4 + {1:~ }|*4 | ]]) end) it('does not prevent/delay exit. #4798 #4900', function() + skip(fn.executable('sleep') == 0, 'missing "sleep" command') -- Use a nested nvim (in :term) to test without --headless. fn.termopen({ - helpers.nvim_prog, + n.nvim_prog, '-u', 'NONE', '-i', diff --git a/test/functional/core/log_spec.lua b/test/functional/core/log_spec.lua index 1637e683c1..cac61cda2d 100644 --- a/test/functional/core/log_spec.lua +++ b/test/functional/core/log_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local assert_log = helpers.assert_log -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local expect_exit = helpers.expect_exit -local request = helpers.request +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local assert_log = t.assert_log +local clear = n.clear +local command = n.command +local eq = t.eq +local exec_lua = n.exec_lua +local expect_exit = n.expect_exit +local request = n.request describe('log', function() local testlog = 'Xtest_logging' diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index 9d8d64c82d..5e903726db 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -1,17 +1,18 @@ -local uv = vim.uv -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local uv = vim.uv -local eq = helpers.eq -local matches = helpers.matches -local feed = helpers.feed -local eval = helpers.eval -local clear = helpers.clear -local fn = helpers.fn -local nvim_prog_abs = helpers.nvim_prog_abs -local write_file = helpers.write_file -local is_os = helpers.is_os -local skip = helpers.skip +local eq = t.eq +local matches = t.matches +local feed = n.feed +local eval = n.eval +local clear = n.clear +local fn = n.fn +local nvim_prog_abs = n.nvim_prog_abs +local write_file = t.write_file +local is_os = t.is_os +local skip = t.skip describe('command-line option', function() describe('-s', function() @@ -120,9 +121,9 @@ describe('command-line option', function() feed('i:cq<CR>') screen:expect([[ | - [Process exited 1] | + [Process exited 1]{2: } | |*5 - -- TERMINAL -- | + {5:-- TERMINAL --} | ]]) --[=[ Example of incorrect output: screen:expect([[ diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua index e98bfc0d45..d8e274019a 100644 --- a/test/functional/core/path_spec.lua +++ b/test/functional/core/path_spec.lua @@ -1,15 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local fn = helpers.fn -local insert = helpers.insert -local is_os = helpers.is_os -local mkdir = helpers.mkdir -local rmdir = helpers.rmdir -local write_file = helpers.write_file +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local feed = n.feed +local fn = n.fn +local insert = n.insert +local is_os = t.is_os +local mkdir = t.mkdir +local rmdir = n.rmdir +local write_file = t.write_file local function join_path(...) local pathsep = (is_os('win') and '\\' or '/') diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua index caff06f6ab..6cc28ddeef 100644 --- a/test/functional/core/remote_spec.lua +++ b/test/functional/core/remote_spec.lua @@ -1,20 +1,21 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local exec_capture = helpers.exec_capture -local exec_lua = helpers.exec_lua -local expect = helpers.expect -local fn = helpers.fn -local insert = helpers.insert -local nvim_prog = helpers.nvim_prog -local new_argv = helpers.new_argv -local neq = helpers.neq -local set_session = helpers.set_session -local spawn = helpers.spawn -local tmpname = helpers.tmpname -local write_file = helpers.write_file +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local exec_capture = n.exec_capture +local exec_lua = n.exec_lua +local expect = n.expect +local fn = n.fn +local insert = n.insert +local nvim_prog = n.nvim_prog +local new_argv = n.new_argv +local neq = t.neq +local set_session = n.set_session +local spawn = n.spawn +local tmpname = t.tmpname +local write_file = t.write_file describe('Remote', function() local fname, other_fname diff --git a/test/functional/core/spellfile_spec.lua b/test/functional/core/spellfile_spec.lua index 57953b8f80..e2ccb038e7 100644 --- a/test/functional/core/spellfile_spec.lua +++ b/test/functional/core/spellfile_spec.lua @@ -1,13 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local clear = helpers.clear -local api = helpers.api -local exc_exec = helpers.exc_exec -local fn = helpers.fn -local rmdir = helpers.rmdir -local write_file = helpers.write_file -local mkdir = helpers.mkdir +local eq = t.eq +local clear = n.clear +local api = n.api +local exc_exec = n.exc_exec +local fn = n.fn +local rmdir = n.rmdir +local write_file = t.write_file +local mkdir = t.mkdir local testdir = 'Xtest-functional-spell-spellfile.d' diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index cc58226f48..a53625ab1b 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -1,37 +1,41 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local assert_alive = helpers.assert_alive -local assert_log = helpers.assert_log -local clear = helpers.clear -local command = helpers.command -local ok = helpers.ok -local eq = helpers.eq -local matches = helpers.matches -local eval = helpers.eval -local exec = helpers.exec -local exec_capture = helpers.exec_capture -local exec_lua = helpers.exec_lua -local feed = helpers.feed -local fn = helpers.fn +local assert_alive = n.assert_alive +local assert_log = t.assert_log +local clear = n.clear +local command = n.command +local ok = t.ok +local eq = t.eq +local matches = t.matches +local eval = n.eval +local exec = n.exec +local exec_capture = n.exec_capture +local exec_lua = n.exec_lua +local feed = n.feed +local fn = n.fn local pesc = vim.pesc -local mkdir = helpers.mkdir -local mkdir_p = helpers.mkdir_p -local nvim_prog = helpers.nvim_prog -local nvim_set = helpers.nvim_set -local read_file = helpers.read_file -local retry = helpers.retry -local rmdir = helpers.rmdir +local mkdir = t.mkdir +local mkdir_p = n.mkdir_p +local nvim_prog = n.nvim_prog +local nvim_set = n.nvim_set +local read_file = t.read_file +local retry = t.retry +local rmdir = n.rmdir local sleep = vim.uv.sleep local startswith = vim.startswith -local write_file = helpers.write_file -local api = helpers.api -local alter_slashes = helpers.alter_slashes -local is_os = helpers.is_os -local dedent = helpers.dedent +local write_file = t.write_file +local api = n.api +local alter_slashes = n.alter_slashes +local is_os = t.is_os +local dedent = t.dedent local tbl_map = vim.tbl_map local tbl_filter = vim.tbl_filter local endswith = vim.endswith +local check_close = n.check_close + +local testlog = 'Xtest-startupspec-log' describe('startup', function() it('--clean', function() @@ -83,6 +87,8 @@ describe('startup', function() local screen screen = Screen.new(60, 7) screen:attach() + -- not the same colors on windows for some reason + screen._default_attr_ids = nil local id = fn.termopen({ nvim_prog, '-u', @@ -97,15 +103,7 @@ describe('startup', function() VIMRUNTIME = os.getenv('VIMRUNTIME'), }, }) - screen:expect([[ - ^ | - | - Entering Debug mode. Type "cont" to continue. | - nvim_exec2() | - cmd: aunmenu * | - > | - | - ]]) + screen:expect({ any = pesc('Entering Debug mode. Type "cont" to continue.') }) fn.chansend(id, 'cont\n') screen:expect([[ ^ | @@ -119,6 +117,11 @@ end) describe('startup', function() before_each(clear) + after_each(function() + check_close() + os.remove(testlog) + end) + describe('-l Lua', function() local function assert_l_out(expected, nvim_args, lua_args, script, input) local args = { nvim_prog } @@ -326,6 +329,9 @@ describe('startup', function() local screen = Screen.new(25, 3) -- Remote UI connected by --embed. screen:attach() + -- TODO: a lot of tests in this file already use the new default color scheme. + -- once we do the batch update of tests to use it, remove this workarond + screen._default_attr_ids = nil command([[echo has('ttyin') has('ttyout')]]) screen:expect([[ ^ | @@ -337,6 +343,7 @@ describe('startup', function() it('in a TTY: has("ttyin")==1 has("ttyout")==1', function() local screen = Screen.new(25, 4) screen:attach() + screen._default_attr_ids = nil if is_os('win') then command([[set shellcmdflag=/s\ /c shellxquote=\"]]) end @@ -365,6 +372,7 @@ describe('startup', function() end) it('output to pipe: has("ttyin")==1 has("ttyout")==0', function() + clear({ env = { NVIM_LOG_FILE = testlog } }) if is_os('win') then command([[set shellcmdflag=/s\ /c shellxquote=\"]]) end @@ -392,9 +400,13 @@ describe('startup', function() read_file('Xtest_startup_ttyout') ) end) + if is_os('win') then + assert_log('stream write failed. RPC canceled; closing channel', testlog) + end end) it('input from pipe: has("ttyin")==0 has("ttyout")==1', function() + clear({ env = { NVIM_LOG_FILE = testlog } }) if is_os('win') then command([[set shellcmdflag=/s\ /c shellxquote=\"]]) end @@ -423,11 +435,16 @@ describe('startup', function() read_file('Xtest_startup_ttyout') ) end) + if is_os('win') then + assert_log('stream write failed. RPC canceled; closing channel', testlog) + end end) it('input from pipe (implicit) #7679', function() + clear({ env = { NVIM_LOG_FILE = testlog } }) local screen = Screen.new(25, 4) screen:attach() + screen._default_attr_ids = nil if is_os('win') then command([[set shellcmdflag=/s\ /c shellxquote=\"]]) end @@ -450,6 +467,9 @@ describe('startup', function() 0 1 | | ]]) + if not is_os('win') then + assert_log('Failed to get flags on descriptor 3: Bad file descriptor', testlog, 100) + end end) it('input from pipe + file args #7679', function() @@ -589,6 +609,7 @@ describe('startup', function() local screen screen = Screen.new(60, 6) screen:attach() + screen._default_attr_ids = nil local id = fn.termopen({ nvim_prog, '-u', @@ -974,7 +995,7 @@ describe('sysinit', function() local xdgdir = 'Xxdg' local vimdir = 'Xvim' local xhome = 'Xhome' - local pathsep = helpers.get_pathsep() + local pathsep = n.get_pathsep() before_each(function() rmdir(xdgdir) @@ -1035,7 +1056,7 @@ end) describe('user config init', function() local xhome = 'Xhome' - local pathsep = helpers.get_pathsep() + local pathsep = n.get_pathsep() local xconfig = xhome .. pathsep .. 'Xconfig' local xdata = xhome .. pathsep .. 'Xdata' local init_lua_path = table.concat({ xconfig, 'nvim', 'init.lua' }, pathsep) @@ -1123,6 +1144,7 @@ describe('user config init', function() local screen = Screen.new(50, 8) screen:attach() + screen._default_attr_ids = nil fn.termopen({ nvim_prog }, { env = { VIMRUNTIME = os.getenv('VIMRUNTIME'), @@ -1197,7 +1219,7 @@ end) describe('runtime:', function() local xhome = 'Xhome' - local pathsep = helpers.get_pathsep() + local pathsep = n.get_pathsep() local xconfig = xhome .. pathsep .. 'Xconfig' local xdata = xhome .. pathsep .. 'Xdata' local xenv = { XDG_CONFIG_HOME = xconfig, XDG_DATA_HOME = xdata } @@ -1339,7 +1361,7 @@ end) describe('user session', function() local xhome = 'Xhome' - local pathsep = helpers.get_pathsep() + local pathsep = n.get_pathsep() local session_file = table.concat({ xhome, 'session.lua' }, pathsep) before_each(function() diff --git a/test/functional/editor/K_spec.lua b/test/functional/editor/K_spec.lua index 1fbdd1c142..3c58892731 100644 --- a/test/functional/editor/K_spec.lua +++ b/test/functional/editor/K_spec.lua @@ -1,6 +1,7 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq, clear, eval, feed, api, retry = - helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.api, helpers.retry +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq, clear, eval, feed, api, retry = t.eq, n.clear, n.eval, n.feed, n.api, t.retry describe('K', function() local test_file = 'K_spec_out' @@ -13,19 +14,19 @@ describe('K', function() end) it("invokes colon-prefixed 'keywordprg' as Vim command", function() - helpers.source([[ + n.source([[ let @a='fnord' set keywordprg=:put]]) -- K on the text "a" resolves to `:put a`. feed('ia<ESC>K') - helpers.expect([[ + n.expect([[ a fnord]]) end) it("invokes non-prefixed 'keywordprg' as shell command", function() - helpers.source([[ + n.source([[ let @a='fnord' set keywordprg=echo\ fnord>>]]) @@ -43,7 +44,7 @@ describe('K', function() end) it("<esc> kills the buffer for a running 'keywordprg' command", function() - helpers.source('set keywordprg=less') + n.source('set keywordprg=less') eval('writefile(["hello", "world"], "' .. test_file .. '")') feed('i' .. test_file .. '<esc>K') eq('t', eval('mode()')) @@ -57,7 +58,7 @@ describe('K', function() local bufnr = eval('bufnr()') feed('<esc>') eq('n', eval('mode()')) - helpers.neq(bufnr, eval('bufnr()')) + t.neq(bufnr, eval('bufnr()')) end) it('empty string falls back to :help #19298', function() diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua index 33d0d47499..62bb7e19f3 100644 --- a/test/functional/editor/completion_spec.lua +++ b/test/functional/editor/completion_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local assert_alive = helpers.assert_alive -local clear, feed = helpers.clear, helpers.feed -local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq -local feed_command, source, expect = helpers.feed_command, helpers.source, helpers.expect -local fn = helpers.fn -local command = helpers.command -local api = helpers.api -local poke_eventloop = helpers.poke_eventloop + +local assert_alive = n.assert_alive +local clear, feed = n.clear, n.feed +local eval, eq, neq = n.eval, t.eq, t.neq +local feed_command, source, expect = n.feed_command, n.source, n.expect +local fn = n.fn +local command = n.command +local api = n.api +local poke_eventloop = n.poke_eventloop describe('completion', function() local screen diff --git a/test/functional/editor/count_spec.lua b/test/functional/editor/count_spec.lua index 94f741250a..d158497cb8 100644 --- a/test/functional/editor/count_spec.lua +++ b/test/functional/editor/count_spec.lua @@ -1,10 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local clear = helpers.clear -local command = helpers.command +local eq = t.eq +local eval = n.eval +local feed = n.feed +local clear = n.clear +local command = n.command describe('v:count/v:count1', function() before_each(function() diff --git a/test/functional/editor/ctrl_c_spec.lua b/test/functional/editor/ctrl_c_spec.lua index e6a6ea808a..e1258c7df8 100644 --- a/test/functional/editor/ctrl_c_spec.lua +++ b/test/functional/editor/ctrl_c_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, source = helpers.clear, helpers.feed, helpers.source -local command = helpers.command -local poke_eventloop = helpers.poke_eventloop + +local clear, feed, source = n.clear, n.feed, n.source +local command = n.command +local poke_eventloop = n.poke_eventloop local sleep = vim.uv.sleep describe('CTRL-C (mapped)', function() @@ -16,7 +18,7 @@ describe('CTRL-C (mapped)', function() it('interrupts :global', function() -- Crashes luajit. - if helpers.skip_fragile(pending) then + if t.skip_fragile(pending) then return end @@ -66,8 +68,8 @@ describe('CTRL-C (mapped)', function() feed('i') screen:expect([[ ^ | - ~ |*4 - -- INSERT -- | + {1:~ }|*4 + {5:-- INSERT --} | ]]) end) @@ -81,8 +83,8 @@ describe('CTRL-C (mapped)', function() feed('i') screen:expect([[ ^ | - ~ |*4 - -- INSERT -- | + {1:~ }|*4 + {5:-- INSERT --} | ]]) end) end) diff --git a/test/functional/editor/fold_spec.lua b/test/functional/editor/fold_spec.lua index 7950f6aea4..ee3f268a2a 100644 --- a/test/functional/editor/fold_spec.lua +++ b/test/functional/editor/fold_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local insert = helpers.insert -local exec = helpers.exec -local feed = helpers.feed -local expect = helpers.expect -local command = helpers.command -local fn = helpers.fn -local eq = helpers.eq -local neq = helpers.neq +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local insert = n.insert +local exec = n.exec +local feed = n.feed +local expect = n.expect +local command = n.command +local fn = n.fn +local eq = t.eq +local neq = t.neq describe('Folding', function() local tempfname = 'Xtest-fold.txt' @@ -301,7 +302,7 @@ a]], it('updates correctly on :read', function() -- luacheck: ignore 621 - helpers.write_file( + t.write_file( tempfname, [[ a @@ -376,7 +377,7 @@ a]], end) it('splits folds according to >N and <N with foldexpr', function() - helpers.source([[ + n.source([[ function TestFoldExpr(lnum) let thisline = getline(a:lnum) if thisline == 'a' @@ -391,7 +392,7 @@ a]], return 0 endfunction ]]) - helpers.write_file( + t.write_file( tempfname, [[ b diff --git a/test/functional/editor/jump_spec.lua b/test/functional/editor/jump_spec.lua index fe03d82164..880831d9f8 100644 --- a/test/functional/editor/jump_spec.lua +++ b/test/functional/editor/jump_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local dedent = helpers.dedent -local eq = helpers.eq -local fn = helpers.fn -local feed = helpers.feed -local exec_capture = helpers.exec_capture -local write_file = helpers.write_file -local api = helpers.api +local clear = n.clear +local command = n.command +local dedent = t.dedent +local eq = t.eq +local fn = n.fn +local feed = n.feed +local exec_capture = n.exec_capture +local write_file = t.write_file +local api = n.api describe('jumplist', function() local fname1 = 'Xtest-functional-normal-jump' diff --git a/test/functional/editor/lang_spec.lua b/test/functional/editor/lang_spec.lua index ee7cfac057..74d83bcfa8 100644 --- a/test/functional/editor/lang_spec.lua +++ b/test/functional/editor/lang_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, insert, eq = helpers.clear, helpers.insert, helpers.eq -local command, expect = helpers.command, helpers.expect -local feed, eval = helpers.feed, helpers.eval -local exc_exec = helpers.exc_exec +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, insert, eq = n.clear, n.insert, t.eq +local command, expect = n.command, n.expect +local feed, eval = n.feed, n.eval +local exc_exec = n.exc_exec describe('gu and gU', function() before_each(clear) diff --git a/test/functional/editor/langmap_spec.lua b/test/functional/editor/langmap_spec.lua index b2a4b21a89..e50e19a468 100644 --- a/test/functional/editor/langmap_spec.lua +++ b/test/functional/editor/langmap_spec.lua @@ -1,10 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq, neq, call = helpers.eq, helpers.neq, helpers.call -local eval, feed, clear = helpers.eval, helpers.feed, helpers.clear -local command, insert, expect = helpers.command, helpers.insert, helpers.expect -local feed_command = helpers.feed_command -local curwin = helpers.api.nvim_get_current_win +local eq, neq, call = t.eq, t.neq, n.call +local eval, feed, clear = n.eval, n.feed, n.clear +local command, insert, expect = n.command, n.insert, n.expect +local feed_command = n.feed_command +local curwin = n.api.nvim_get_current_win describe("'langmap'", function() before_each(function() @@ -133,7 +134,7 @@ describe("'langmap'", function() hello]]) end) it('command-line CTRL-R', function() - helpers.source([[ + n.source([[ let i_value = 0 let j_value = 0 call setreg('i', 'i_value') @@ -171,7 +172,7 @@ describe("'langmap'", function() end) it('prompt for number', function() command('set langmap=12,21') - helpers.source([[ + n.source([[ let gotten_one = 0 function Map() let answer = inputlist(['a', '1.', '2.', '3.']) @@ -214,10 +215,7 @@ describe("'langmap'", function() end feed('qa' .. command_string .. 'q') expect(expect_string) - eq( - expect_macro or helpers.fn.nvim_replace_termcodes(command_string, true, true, true), - eval('@a') - ) + eq(expect_macro or n.fn.nvim_replace_termcodes(command_string, true, true, true), eval('@a')) if setup_function then setup_function() end diff --git a/test/functional/editor/macro_spec.lua b/test/functional/editor/macro_spec.lua index c97befdf07..27c5eddac8 100644 --- a/test/functional/editor/macro_spec.lua +++ b/test/functional/editor/macro_spec.lua @@ -1,19 +1,21 @@ -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local clear = helpers.clear -local expect = helpers.expect -local command = helpers.command -local fn = helpers.fn -local api = helpers.api -local insert = helpers.insert - -describe('macros', function() +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local eval = n.eval +local feed = n.feed +local clear = n.clear +local expect = n.expect +local command = n.command +local fn = n.fn +local api = n.api +local insert = n.insert + +describe('macros with default mappings', function() before_each(function() clear({ args_rm = { '--cmd' } }) end) + it('can be recorded and replayed', function() feed('qiahello<esc>q') expect('hello') @@ -22,6 +24,7 @@ describe('macros', function() expect('hellohello') eq('ahello', eval('@i')) end) + it('applies maps', function() command('imap x l') command('nmap l a') @@ -34,87 +37,147 @@ describe('macros', function() end) it('can be replayed with Q', function() - insert [[hello + insert [[ +hello hello hello]] feed [[gg]] feed [[qqAFOO<esc>q]] - eq({ 'helloFOO', 'hello', 'hello' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +helloFOO +hello +hello]] feed [[Q]] - eq({ 'helloFOOFOO', 'hello', 'hello' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +helloFOOFOO +hello +hello]] feed [[G3Q]] - eq({ 'helloFOOFOO', 'hello', 'helloFOOFOOFOO' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +helloFOOFOO +hello +helloFOOFOOFOO]] feed [[ggV3jQ]] - eq( - { 'helloFOOFOOFOO', 'helloFOO', 'helloFOOFOOFOOFOO' }, - api.nvim_buf_get_lines(0, 0, -1, false) - ) + expect [[ +helloFOOFOOFOO +helloFOO +helloFOOFOOFOOFOO]] end) - it('can be replayed with @', function() - insert [[hello + it('can be replayed with Q and @@', function() + insert [[ +hello hello hello]] feed [[gg]] feed [[qqAFOO<esc>q]] - eq({ 'helloFOO', 'hello', 'hello' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +helloFOO +hello +hello]] feed [[Q]] - eq({ 'helloFOOFOO', 'hello', 'hello' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +helloFOOFOO +hello +hello]] feed [[G3@@]] - eq({ 'helloFOOFOO', 'hello', 'helloFOOFOOFOO' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +helloFOOFOO +hello +helloFOOFOOFOO]] feed [[ggV2j@@]] - eq( - { 'helloFOOFOOFOO', 'helloFOO', 'helloFOOFOOFOOFOO' }, - api.nvim_buf_get_lines(0, 0, -1, false) - ) + expect [[ +helloFOOFOOFOO +helloFOO +helloFOOFOOFOOFOO]] end) - it('can be replayed with @q and @w', function() - insert [[hello + it('can be replayed with @ in linewise Visual mode', function() + insert [[ +hello hello hello]] feed [[gg]] feed [[qqAFOO<esc>qu]] - eq({ 'hello', 'hello', 'hello' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +hello +hello +hello]] feed [[qwA123<esc>qu]] - eq({ 'hello', 'hello', 'hello' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +hello +hello +hello]] feed [[V3j@q]] - eq({ 'helloFOO', 'helloFOO', 'helloFOO' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +helloFOO +helloFOO +helloFOO]] + + feed [[ggVj@w]] + expect [[ +helloFOO123 +helloFOO123 +helloFOO]] + end) - feed [[gg]] - feed [[Vj@w]] - eq({ 'helloFOO123', 'helloFOO123', 'helloFOO' }, api.nvim_buf_get_lines(0, 0, -1, false)) + it('can be recorded and replayed in Visual mode', function() + insert('foo BAR BAR foo BAR foo BAR BAR BAR foo BAR BAR') + feed('0vqifofRq') + eq({ 0, 1, 7, 0 }, fn.getpos('.')) + eq({ 0, 1, 1, 0 }, fn.getpos('v')) + feed('Q') + eq({ 0, 1, 19, 0 }, fn.getpos('.')) + eq({ 0, 1, 1, 0 }, fn.getpos('v')) + feed('Q') + eq({ 0, 1, 27, 0 }, fn.getpos('.')) + eq({ 0, 1, 1, 0 }, fn.getpos('v')) + feed('@i') + eq({ 0, 1, 43, 0 }, fn.getpos('.')) + eq({ 0, 1, 1, 0 }, fn.getpos('v')) end) - it('can be replayed with @q and @w visual-block', function() - insert [[hello + it('can be replayed with @ in blockwise Visual mode', function() + insert [[ +hello hello hello]] feed [[gg]] feed [[qqAFOO<esc>qu]] - eq({ 'hello', 'hello', 'hello' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +hello +hello +hello]] feed [[qwA123<esc>qu]] - eq({ 'hello', 'hello', 'hello' }, api.nvim_buf_get_lines(0, 0, -1, false)) - - feed [[<C-v>3j@q]] - eq({ 'helloFOO', 'helloFOO', 'helloFOO' }, api.nvim_buf_get_lines(0, 0, -1, false)) + expect [[ +hello +hello +hello]] - feed [[gg]] - feed [[<C-v>j@w]] - eq({ 'helloFOO123', 'helloFOO123', 'helloFOO' }, api.nvim_buf_get_lines(0, 0, -1, false)) + feed [[0<C-v>3jl@q]] + expect [[ +heFOOllo +heFOOllo +heFOOllo]] + + feed [[gg0<C-v>j@w]] + expect [[ +h123eFOOllo +h123eFOOllo +heFOOllo]] end) end) diff --git a/test/functional/editor/mark_spec.lua b/test/functional/editor/mark_spec.lua index 6b20a736c0..69cb95e1c3 100644 --- a/test/functional/editor/mark_spec.lua +++ b/test/functional/editor/mark_spec.lua @@ -1,15 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local api = helpers.api -local clear = helpers.clear -local command = helpers.command -local fn = helpers.fn -local eq = helpers.eq -local feed = helpers.feed -local write_file = helpers.write_file -local pcall_err = helpers.pcall_err + +local api = n.api +local clear = n.clear +local command = n.command +local fn = n.fn +local eq = t.eq +local feed = n.feed +local write_file = t.write_file +local pcall_err = t.pcall_err local cursor = function() - return helpers.api.nvim_win_get_cursor(0) + return n.api.nvim_win_get_cursor(0) end describe('named marks', function() @@ -39,59 +41,59 @@ describe('named marks', function() it('errors when set out of range with :mark', function() command('edit ' .. file1) - local err = pcall_err(helpers.exec_capture, '1000mark x') + local err = pcall_err(n.exec_capture, '1000mark x') eq('nvim_exec2(): Vim(mark):E16: Invalid range: 1000mark x', err) end) it('errors when set out of range with :k', function() command('edit ' .. file1) - local err = pcall_err(helpers.exec_capture, '1000kx') + local err = pcall_err(n.exec_capture, '1000kx') eq('nvim_exec2(): Vim(k):E16: Invalid range: 1000kx', err) end) it('errors on unknown mark name with :mark', function() command('edit ' .. file1) - local err = pcall_err(helpers.exec_capture, 'mark #') + local err = pcall_err(n.exec_capture, 'mark #') eq('nvim_exec2(): Vim(mark):E191: Argument must be a letter or forward/backward quote', err) end) it("errors on unknown mark name with '", function() command('edit ' .. file1) - local err = pcall_err(helpers.exec_capture, "normal! '#") + local err = pcall_err(n.exec_capture, "normal! '#") eq('nvim_exec2(): Vim(normal):E78: Unknown mark', err) end) it('errors on unknown mark name with `', function() command('edit ' .. file1) - local err = pcall_err(helpers.exec_capture, 'normal! `#') + local err = pcall_err(n.exec_capture, 'normal! `#') eq('nvim_exec2(): Vim(normal):E78: Unknown mark', err) end) it("errors when moving to a mark that is not set with '", function() command('edit ' .. file1) - local err = pcall_err(helpers.exec_capture, "normal! 'z") + local err = pcall_err(n.exec_capture, "normal! 'z") eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) - err = pcall_err(helpers.exec_capture, "normal! '.") + err = pcall_err(n.exec_capture, "normal! '.") eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) end) it('errors when moving to a mark that is not set with `', function() command('edit ' .. file1) - local err = pcall_err(helpers.exec_capture, 'normal! `z') + local err = pcall_err(n.exec_capture, 'normal! `z') eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) - err = pcall_err(helpers.exec_capture, 'normal! `>') + err = pcall_err(n.exec_capture, 'normal! `>') eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) end) it("errors when moving to a global mark that is not set with '", function() command('edit ' .. file1) - local err = pcall_err(helpers.exec_capture, "normal! 'Z") + local err = pcall_err(n.exec_capture, "normal! 'Z") eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) end) it('errors when moving to a global mark that is not set with `', function() command('edit ' .. file1) - local err = pcall_err(helpers.exec_capture, 'normal! `Z') + local err = pcall_err(n.exec_capture, 'normal! `Z') eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) end) @@ -166,7 +168,7 @@ describe('named marks', function() feed('mA') command('next') command('bw! ' .. file1) - local err = pcall_err(helpers.exec_capture, "normal! 'A") + local err = pcall_err(n.exec_capture, "normal! 'A") eq('nvim_exec2(): Vim(normal):E92: Buffer 1 not found', err) os.remove(file1) end) @@ -420,12 +422,12 @@ describe('named marks view', function() feed("<C-w>p'a") screen:expect([[ | - ~ |*3 - [No Name] | + {1:~ }|*3 + {2:[No Name] }| 6 line | ^7 line | 8 line | - {MATCH:.*marks} | + {3:<itor-marks }| | ]]) end) @@ -453,7 +455,7 @@ describe('named marks view', function() command('bwipe!') screen:expect([[ ^ | - ~ |*4 + {1:~ }|*4 | ]]) command('rshada!') diff --git a/test/functional/editor/meta_key_spec.lua b/test/functional/editor/meta_key_spec.lua index b57f5c3c35..87fe395608 100644 --- a/test/functional/editor/meta_key_spec.lua +++ b/test/functional/editor/meta_key_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command = helpers.command -local exec_lua = helpers.exec_lua -local eval = helpers.eval -local expect = helpers.expect -local fn = helpers.fn -local eq = helpers.eq +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local command = n.command +local exec_lua = n.exec_lua +local eval = n.eval +local expect = n.expect +local fn = n.fn +local eq = t.eq describe('meta-keys #8226 #13042', function() before_each(function() @@ -141,4 +143,29 @@ describe('meta-keys #8226 #13042', function() // This is some text: bar // This is some text: baz]]) end) + + it('ALT/META with vim.on_key()', function() + feed('ifoo<CR>bar<CR>baz<Esc>gg0') + + exec_lua [[ + keys = {} + typed = {} + + vim.on_key(function(buf, typed_buf) + table.insert(keys, vim.fn.keytrans(buf)) + table.insert(typed, vim.fn.keytrans(typed_buf)) + end) + ]] + + -- <M-"> is reinterpreted as <Esc>" + feed('qrviw"ayc$FOO.<M-">apq') + expect([[ + FOO.foo + bar + baz]]) + + -- vim.on_key() callback should only receive <Esc>" + eq('qrviw"ayc$FOO.<Esc>"apq', exec_lua [[return table.concat(keys, '')]]) + eq('qrviw"ayc$FOO.<Esc>"apq', exec_lua [[return table.concat(typed, '')]]) + end) end) diff --git a/test/functional/editor/mode_cmdline_spec.lua b/test/functional/editor/mode_cmdline_spec.lua index 06efe53718..70bdc5d4c2 100644 --- a/test/functional/editor/mode_cmdline_spec.lua +++ b/test/functional/editor/mode_cmdline_spec.lua @@ -1,12 +1,13 @@ -- Cmdline-mode tests. -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, insert, fn, eq, feed = - helpers.clear, helpers.insert, helpers.fn, helpers.eq, helpers.feed -local eval = helpers.eval -local command = helpers.command -local api = helpers.api + +local clear, insert, fn, eq, feed = n.clear, n.insert, n.fn, t.eq, n.feed +local eval = n.eval +local command = n.command +local api = n.api describe('cmdline', function() before_each(clear) diff --git a/test/functional/editor/mode_insert_spec.lua b/test/functional/editor/mode_insert_spec.lua index e96813b6f7..fb3dda4bf4 100644 --- a/test/functional/editor/mode_insert_spec.lua +++ b/test/functional/editor/mode_insert_spec.lua @@ -1,13 +1,16 @@ -- Insert-mode tests. -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local expect = helpers.expect -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local curbuf_contents = helpers.curbuf_contents + +local clear, feed, insert = n.clear, n.feed, n.insert +local expect = n.expect +local command = n.command +local eq = t.eq +local eval = n.eval +local curbuf_contents = n.curbuf_contents +local api = n.api describe('insert-mode', function() before_each(function() @@ -51,43 +54,34 @@ describe('insert-mode', function() end) it('double quote is removed after hit-enter prompt #22609', function() - local screen = Screen.new(60, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { foreground = Screen.colors.Blue }, -- SpecialKey - [2] = { foreground = Screen.colors.SlateBlue }, - [3] = { bold = true }, -- ModeMsg - [4] = { reverse = true, bold = true }, -- MsgSeparator - [5] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg - [6] = { foreground = Screen.colors.SeaGreen, bold = true }, -- MoreMsg - }) + local screen = Screen.new(50, 6) screen:attach() feed('i<C-R>') screen:expect([[ - {1:^"} | - {0:~ }|*4 - {3:-- INSERT --} | + {18:^"} | + {1:~ }|*4 + {5:-- INSERT --} | ]]) - feed('={}') + feed("=function('add')") screen:expect([[ - {1:"} | - {0:~ }|*4 - ={2:{}}^ | + {18:"} | + {1:~ }|*4 + ={25:function}{16:(}{26:'add'}{16:)}^ | ]]) feed('<CR>') screen:expect([[ - {1:"} | - {0:~ }| - {4: }| - ={2:{}} | - {5:E731: Using a Dictionary as a String} | - {6:Press ENTER or type command to continue}^ | + {18:"} | + {1:~ }| + {3: }| + ={25:function}{16:(}{26:'add'}{16:)} | + {9:E729: Using a Funcref as a String} | + {6:Press ENTER or type command to continue}^ | ]]) feed('<CR>') screen:expect([[ - ^ | - {0:~ }|*4 - {3:-- INSERT --} | + ^ | + {1:~ }|*4 + {5:-- INSERT --} | ]]) end) end) @@ -221,4 +215,146 @@ describe('insert-mode', function() ]], } end) + + describe('backspace', function() + local function set_lines(line_b, line_e, ...) + api.nvim_buf_set_lines(0, line_b, line_e, true, { ... }) + end + local function s(count) + return (' '):rep(count) + end + + local function test_cols(expected_cols) + local cols = { { n.fn.col('.'), n.fn.virtcol('.') } } + for _ = 2, #expected_cols do + feed('<BS>') + table.insert(cols, { n.fn.col('.'), n.fn.virtcol('.') }) + end + eq(expected_cols, cols) + end + + it('works with tabs and spaces', function() + local screen = Screen.new(30, 2) + screen:attach() + command('setl ts=4 sw=4') + set_lines(0, 1, '\t' .. s(4) .. '\t' .. s(9) .. '\t a') + feed('$i') + test_cols({ + { 18, 26 }, + { 17, 25 }, + { 15, 21 }, + { 11, 17 }, + { 7, 13 }, + { 6, 9 }, + { 2, 5 }, + { 1, 1 }, + }) + end) + + it('works with varsofttabstop', function() + local screen = Screen.new(30, 2) + screen:attach() + command('setl vsts=6,2,5,3') + set_lines(0, 1, 'a\t' .. s(4) .. '\t a') + feed('$i') + test_cols({ + { 9, 18 }, + { 8, 17 }, + { 8, 14 }, + { 3, 9 }, + { 7, 7 }, + { 2, 2 }, + { 1, 1 }, + }) + end) + + it('works with tab as ^I', function() + local screen = Screen.new(30, 2) + screen:attach() + command('set list listchars=space:.') + command('setl ts=4 sw=4') + set_lines(0, 1, '\t' .. s(4) .. '\t' .. s(9) .. '\t a') + feed('$i') + test_cols({ + { 18, 21 }, + { 15, 17 }, + { 11, 13 }, + { 7, 9 }, + { 4, 5 }, + { 1, 1 }, + }) + end) + + it('works in replace mode', function() + local screen = Screen.new(50, 2) + screen:attach() + command('setl ts=8 sw=8 sts=8') + set_lines(0, 1, '\t' .. s(4) .. '\t' .. s(9) .. '\t a') + feed('$R') + test_cols({ + { 18, 34 }, + { 17, 33 }, + { 15, 25 }, + { 7, 17 }, + { 2, 9 }, + { 1, 8 }, -- last screen cell of first tab is at vcol 8 + }) + end) + + it('works with breakindent', function() + local screen = Screen.new(17, 4) + screen:attach() + command('setl ts=4 sw=4 bri briopt=min:5') + set_lines(0, 1, '\t' .. s(4) .. '\t' .. s(9) .. '\t a') + feed('$i') + test_cols({ + { 18, 50 }, + { 17, 49 }, + { 15, 33 }, + { 11, 17 }, + { 7, 13 }, + { 6, 9 }, + { 2, 5 }, + { 1, 1 }, + }) + end) + + it('works with inline virtual text', function() + local screen = Screen.new(50, 2) + screen:attach() + command('setl ts=4 sw=4') + set_lines(0, 1, '\t' .. s(4) .. '\t' .. s(9) .. '\t a') + local ns = api.nvim_create_namespace('') + local vt_opts = { virt_text = { { 'text' } }, virt_text_pos = 'inline' } + api.nvim_buf_set_extmark(0, ns, 0, 2, vt_opts) + feed('$i') + test_cols({ + { 18, 30 }, + { 17, 29 }, + { 15, 25 }, + { 11, 21 }, + { 7, 17 }, + { 6, 13 }, + { 2, 9 }, + { 1, 5 }, + }) + end) + + it("works with 'revins'", function() + local screen = Screen.new(30, 3) + screen:attach() + command('setl ts=4 sw=4 revins') + set_lines(0, 1, ('a'):rep(16), s(3) .. '\t' .. s(4) .. '\t a') + feed('j$i') + test_cols({ + { 11, 14 }, + { 10, 13 }, + { 9, 9 }, + { 5, 5 }, + { 1, 1 }, + { 1, 1 }, -- backspace on empty line does nothing + }) + eq(2, api.nvim_win_get_cursor(0)[1]) + end) + end) end) diff --git a/test/functional/editor/mode_normal_spec.lua b/test/functional/editor/mode_normal_spec.lua index 89bab3f6c9..b3ef4866dc 100644 --- a/test/functional/editor/mode_normal_spec.lua +++ b/test/functional/editor/mode_normal_spec.lua @@ -1,11 +1,14 @@ -- Normal mode tests. -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local feed = helpers.feed -local fn = helpers.fn -local command = helpers.command -local eq = helpers.eq +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local Screen = require('test.functional.ui.screen') + +local clear = n.clear +local feed = n.feed +local fn = n.fn +local command = n.command +local eq = t.eq describe('Normal mode', function() before_each(clear) @@ -19,4 +22,23 @@ describe('Normal mode', function() feed('k') eq(pos, fn.getcurpos()) end) + + it('&showcmd does not crash with :startinsert #28419', function() + local screen = Screen.new(60, 17) + screen:attach() + fn.termopen( + { n.nvim_prog, '--clean', '--cmd', 'startinsert' }, + { env = { VIMRUNTIME = os.getenv('VIMRUNTIME') } } + ) + screen:expect({ + grid = [[ + ^ | + ~ |*13 + [No Name] 0,1 All| + -- INSERT -- | + | + ]], + attr_ids = {}, + }) + end) end) diff --git a/test/functional/editor/put_spec.lua b/test/functional/editor/put_spec.lua index 414b289222..0f6936cd31 100644 --- a/test/functional/editor/put_spec.lua +++ b/test/functional/editor/put_spec.lua @@ -1,18 +1,19 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local insert = helpers.insert -local feed = helpers.feed -local expect = helpers.expect -local eq = helpers.eq +local clear = n.clear +local insert = n.insert +local feed = n.feed +local expect = n.expect +local eq = t.eq local map = vim.tbl_map local filter = vim.tbl_filter -local feed_command = helpers.feed_command -local command = helpers.command -local curbuf_contents = helpers.curbuf_contents -local fn = helpers.fn -local dedent = helpers.dedent +local feed_command = n.feed_command +local command = n.command +local curbuf_contents = n.curbuf_contents +local fn = n.fn +local dedent = t.dedent local function reset() command('bwipe! | new') @@ -75,7 +76,7 @@ describe('put command', function() extra_setup() end local orig_dotstr = fn.getreg('.') - helpers.ok(visual_marks_zero()) + t.ok(visual_marks_zero()) -- Make sure every test starts from the same conditions assert_no_change(test.exception_table, false) local was_cli = test.test_action() @@ -890,7 +891,7 @@ describe('put command', function() -- check bell is not set by nvim before the action screen:sleep(50) end - helpers.ok(not screen.bell and not screen.visualbell) + t.ok(not screen.bell and not screen.visualbell) actions() screen:expect { condition = function() diff --git a/test/functional/editor/search_spec.lua b/test/functional/editor/search_spec.lua index 46a3e298b7..770a4f387d 100644 --- a/test/functional/editor/search_spec.lua +++ b/test/functional/editor/search_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local pcall_err = t.pcall_err describe('search (/)', function() before_each(clear) diff --git a/test/functional/editor/tabpage_spec.lua b/test/functional/editor/tabpage_spec.lua index 0cbc2dbf3d..0b26494436 100644 --- a/test/functional/editor/tabpage_spec.lua +++ b/test/functional/editor/tabpage_spec.lua @@ -1,17 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local neq = helpers.neq -local feed = helpers.feed -local eval = helpers.eval -local exec = helpers.exec -local fn = helpers.fn -local api = helpers.api -local curwin = helpers.api.nvim_get_current_win -local assert_alive = helpers.assert_alive +local clear = n.clear +local command = n.command +local eq = t.eq +local neq = t.neq +local feed = n.feed +local eval = n.eval +local exec = n.exec +local fn = n.fn +local api = n.api +local curwin = n.api.nvim_get_current_win +local assert_alive = n.assert_alive describe('tabpage', function() before_each(clear) diff --git a/test/functional/editor/undo_spec.lua b/test/functional/editor/undo_spec.lua index c101bf02a0..12056394d2 100644 --- a/test/functional/editor/undo_spec.lua +++ b/test/functional/editor/undo_spec.lua @@ -1,16 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local command = helpers.command -local eval = helpers.eval -local expect = helpers.expect -local eq = helpers.eq -local feed = helpers.feed -local feed_command = helpers.feed_command -local insert = helpers.insert -local fn = helpers.fn -local exec = helpers.exec -local exec_lua = helpers.exec_lua +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eval = n.eval +local expect = n.expect +local eq = t.eq +local feed = n.feed +local feed_command = n.feed_command +local insert = n.insert +local fn = n.fn +local exec = n.exec +local exec_lua = n.exec_lua local function lastmessage() local messages = fn.split(fn.execute('messages'), '\n') @@ -44,7 +45,7 @@ describe('u CTRL-R g- g+', function() local function undo_and_redo(hist_pos, undo, redo, expect_str) command('enew!') create_history(hist_pos) - local cur_contents = helpers.curbuf_contents() + local cur_contents = n.curbuf_contents() feed(undo) expect(expect_str) feed(redo) diff --git a/test/functional/ex_cmds/append_spec.lua b/test/functional/ex_cmds/append_spec.lua index 5eb8d49c74..80fdcb3134 100644 --- a/test/functional/ex_cmds/append_spec.lua +++ b/test/functional/ex_cmds/append_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local dedent = helpers.dedent -local exec = helpers.exec -local feed = helpers.feed -local clear = helpers.clear -local fn = helpers.fn -local command = helpers.command -local api = helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local eq = t.eq +local dedent = t.dedent +local exec = n.exec +local feed = n.feed +local clear = n.clear +local fn = n.fn +local command = n.command +local api = n.api + local cmdtest = function(cmd, prep, ret1) describe(':' .. cmd, function() before_each(function() diff --git a/test/functional/ex_cmds/arg_spec.lua b/test/functional/ex_cmds/arg_spec.lua index 810b001ec0..9b89823dcc 100644 --- a/test/functional/ex_cmds/arg_spec.lua +++ b/test/functional/ex_cmds/arg_spec.lua @@ -1,7 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq, command, fn = helpers.eq, helpers.command, helpers.fn -local ok = helpers.ok -local clear = helpers.clear +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq, command, fn = t.eq, n.command, n.fn +local ok = t.ok +local matches = t.matches +local clear = n.clear describe(':argument', function() before_each(function() @@ -10,19 +13,19 @@ describe(':argument', function() it('does not restart :terminal buffer', function() command('terminal') - helpers.feed([[<C-\><C-N>]]) + n.feed([[<C-\><C-N>]]) command('argadd') - helpers.feed([[<C-\><C-N>]]) + n.feed([[<C-\><C-N>]]) local bufname_before = fn.bufname('%') local bufnr_before = fn.bufnr('%') - helpers.ok(nil ~= string.find(bufname_before, '^term://')) -- sanity + matches('^term://', bufname_before) -- sanity command('argument 1') - helpers.feed([[<C-\><C-N>]]) + n.feed([[<C-\><C-N>]]) local bufname_after = fn.bufname('%') local bufnr_after = fn.bufnr('%') - eq('[' .. bufname_before .. ']', helpers.eval('trim(execute("args"))')) + eq('[' .. bufname_before .. ']', n.eval('trim(execute("args"))')) ok(fn.line('$') > 1) eq(bufname_before, bufname_after) eq(bufnr_before, bufnr_after) diff --git a/test/functional/ex_cmds/cd_spec.lua b/test/functional/ex_cmds/cd_spec.lua index 1815c672dc..0f95f2aca4 100644 --- a/test/functional/ex_cmds/cd_spec.lua +++ b/test/functional/ex_cmds/cd_spec.lua @@ -1,16 +1,17 @@ -- Specs for :cd, :tcd, :lcd and getcwd() -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local call = helpers.call -local clear = helpers.clear -local command = helpers.command -local exc_exec = helpers.exc_exec -local pathsep = helpers.get_pathsep() -local skip = helpers.skip -local is_os = helpers.is_os -local mkdir = helpers.mkdir +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local call = n.call +local clear = n.clear +local command = n.command +local exc_exec = n.exc_exec +local pathsep = n.get_pathsep() +local skip = t.skip +local is_os = t.is_os +local mkdir = t.mkdir -- These directories will be created for testing local directories = { @@ -289,14 +290,14 @@ describe('getcwd()', function() end) after_each(function() - helpers.rmdir(directories.global) + n.rmdir(directories.global) end) it('returns empty string if working directory does not exist', function() skip(is_os('win')) command('cd ' .. directories.global) command("call delete('../" .. directories.global .. "', 'd')") - eq('', helpers.eval('getcwd()')) + eq('', n.eval('getcwd()')) end) it("works with 'autochdir' after local directory was set (#9892)", function() diff --git a/test/functional/ex_cmds/cmd_map_spec.lua b/test/functional/ex_cmds/cmd_map_spec.lua index cb7d7340e2..6c1ac8eae5 100644 --- a/test/functional/ex_cmds/cmd_map_spec.lua +++ b/test/functional/ex_cmds/cmd_map_spec.lua @@ -1,16 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local feed = helpers.feed -local eq = helpers.eq -local expect = helpers.expect -local eval = helpers.eval -local fn = helpers.fn -local insert = helpers.insert -local write_file = helpers.write_file -local exc_exec = helpers.exc_exec -local command = helpers.command +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local clear = n.clear +local feed = n.feed +local eq = t.eq +local expect = n.expect +local eval = n.eval +local fn = n.fn +local insert = n.insert +local write_file = t.write_file +local exc_exec = n.exc_exec +local command = n.command + describe('mappings with <Cmd>', function() local screen local tmpfile = 'X_ex_cmds_cmd_map' @@ -505,7 +507,7 @@ describe('mappings with <Cmd>', function() feed('"bd<F7>') expect([[ soest text]]) - eq(fn.getreg('b', 1, 1), { 'me short lines', 'of t' }) + eq({ 'me short lines', 'of t' }, fn.getreg('b', 1, 1)) -- startinsert aborts operator feed('d<F8>') @@ -561,7 +563,7 @@ describe('mappings with <Cmd>', function() of stuff test text]]) feed('<F5>') - eq(fn.getreg('a', 1, 1), { 'deed some short little lines', 'of stuff t' }) + eq({ 'deed some short little lines', 'of stuff t' }, fn.getreg('a', 1, 1)) -- still in insert screen:expect([[ diff --git a/test/functional/ex_cmds/debug_spec.lua b/test/functional/ex_cmds/debug_spec.lua index 85327c87e6..ebb3cdd38a 100644 --- a/test/functional/ex_cmds/debug_spec.lua +++ b/test/functional/ex_cmds/debug_spec.lua @@ -1,7 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed = helpers.feed -local clear = helpers.clear + +local feed = n.feed +local clear = n.clear describe(':debug', function() local screen diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index fc7714d16b..b6068a86db 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) -local assert_alive = helpers.assert_alive -local clear, source = helpers.clear, helpers.source -local api = helpers.api -local insert = helpers.insert -local eq, next_msg = helpers.eq, helpers.next_msg -local exc_exec = helpers.exc_exec -local exec_lua = helpers.exec_lua -local command = helpers.command -local eval = helpers.eval +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local assert_alive = n.assert_alive +local clear, source = n.clear, n.source +local api = n.api +local insert = n.insert +local eq, next_msg = t.eq, n.next_msg +local exc_exec = n.exc_exec +local exec_lua = n.exec_lua +local command = n.command +local eval = n.eval describe('Vimscript dictionary notifications', function() local channel diff --git a/test/functional/ex_cmds/digraphs_spec.lua b/test/functional/ex_cmds/digraphs_spec.lua index 24b6f7c53b..fde25c028f 100644 --- a/test/functional/ex_cmds/digraphs_spec.lua +++ b/test/functional/ex_cmds/digraphs_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local clear = n.clear +local command = n.command +local feed = n.feed + describe(':digraphs', function() local screen before_each(function() diff --git a/test/functional/ex_cmds/drop_spec.lua b/test/functional/ex_cmds/drop_spec.lua index cbda5aac98..54ddd79a35 100644 --- a/test/functional/ex_cmds/drop_spec.lua +++ b/test/functional/ex_cmds/drop_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local command = helpers.command +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, feed_command = helpers.clear, helpers.feed, helpers.feed_command + +local command = n.command +local clear, feed, feed_command = n.clear, n.feed, n.feed_command +local exec = n.exec describe(':drop', function() local screen @@ -16,7 +18,7 @@ describe(':drop', function() [2] = { reverse = true }, [3] = { bold = true }, }) - command('set laststatus=2 shortmess-=F') + command('set nohidden laststatus=2 shortmess-=F') end) it('works like :e when called with only one window open', function() @@ -43,7 +45,6 @@ describe(':drop', function() end) it("splits off a new window when a buffer can't be abandoned", function() - command('set nohidden') feed_command('edit tmp1') feed_command('vsplit') feed_command('edit tmp2') @@ -59,4 +60,20 @@ describe(':drop', function() "tmp3" [New] | ]]) end) + + -- oldtest: Test_drop_modified_file() + it('does not cause E37 with modified same file', function() + exec([[ + edit Xdrop_modified.txt + call setline(1, 'The quick brown fox jumped over the lazy dogs') + ]]) + feed_command('drop Xdrop_modified.txt') + screen:expect([[ + ^The quick brown fox jumped over the| + lazy dogs | + {0:~ }|*6 + {1:Xdrop_modified.txt [+] }| + :drop Xdrop_modified.txt | + ]]) + end) end) diff --git a/test/functional/ex_cmds/echo_spec.lua b/test/functional/ex_cmds/echo_spec.lua index e9176a6204..2f0bcb4d9d 100644 --- a/test/functional/ex_cmds/echo_spec.lua +++ b/test/functional/ex_cmds/echo_spec.lua @@ -1,17 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq +local eq = t.eq local NIL = vim.NIL -local eval = helpers.eval -local clear = helpers.clear -local api = helpers.api -local fn = helpers.fn -local source = helpers.source -local dedent = helpers.dedent -local command = helpers.command -local exc_exec = helpers.exc_exec -local exec_capture = helpers.exec_capture -local matches = helpers.matches +local eval = n.eval +local clear = n.clear +local api = n.api +local fn = n.fn +local source = n.source +local dedent = t.dedent +local command = n.command +local exc_exec = n.exc_exec +local exec_capture = n.exec_capture +local matches = t.matches describe(':echo :echon :echomsg :echoerr', function() local fn_tbl = { 'String', 'StringN', 'StringMsg', 'StringErr' } @@ -255,7 +256,7 @@ describe(':echo :echon :echomsg :echoerr', function() eval('add(l, l)') -- Regression: the below line used to crash (add returns original list and -- there was error in dumping partials). Tested explicitly in - -- test/unit/api/private_helpers_spec.lua. + -- test/unit/api/private_t_spec.lua. eval('add(l, function("Test1", l))') eq( dedent( @@ -349,8 +350,8 @@ describe(':echo :echon :echomsg :echoerr', function() end) describe('used to represent special values', function() - local function chr(n) - return ('%c'):format(n) + local function chr(_n) + return ('%c'):format(_n) end local function ctrl(c) return ('%c'):format(c:upper():byte() - 0x40) diff --git a/test/functional/ex_cmds/edit_spec.lua b/test/functional/ex_cmds/edit_spec.lua index b927fa418a..39849a202f 100644 --- a/test/functional/ex_cmds/edit_spec.lua +++ b/test/functional/ex_cmds/edit_spec.lua @@ -1,8 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq, command, fn = helpers.eq, helpers.command, helpers.fn -local ok = helpers.ok -local clear = helpers.clear -local feed = helpers.feed +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq, command, fn = t.eq, n.command, n.fn +local ok = t.ok +local matches = t.matches +local clear = n.clear +local feed = n.feed describe(':edit', function() before_each(function() @@ -14,7 +17,7 @@ describe(':edit', function() feed([[<C-\><C-N>]]) local bufname_before = fn.bufname('%') local bufnr_before = fn.bufnr('%') - helpers.ok(nil ~= string.find(bufname_before, '^term://')) -- sanity + matches('^term://', bufname_before) -- sanity command('edit') diff --git a/test/functional/ex_cmds/encoding_spec.lua b/test/functional/ex_cmds/encoding_spec.lua index 8953fb8eaf..6b882253b9 100644 --- a/test/functional/ex_cmds/encoding_spec.lua +++ b/test/functional/ex_cmds/encoding_spec.lua @@ -1,6 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, feed_command, feed = helpers.clear, helpers.feed_command, helpers.feed -local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, feed_command, feed = n.clear, n.feed_command, n.feed +local eq, neq, eval = t.eq, t.neq, n.eval describe('&encoding', function() before_each(function() diff --git a/test/functional/ex_cmds/excmd_spec.lua b/test/functional/ex_cmds/excmd_spec.lua index d16a52ee62..20ebb2dedb 100644 --- a/test/functional/ex_cmds/excmd_spec.lua +++ b/test/functional/ex_cmds/excmd_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local command = helpers.command -local eq = helpers.eq -local clear = helpers.clear -local fn = helpers.fn -local pcall_err = helpers.pcall_err -local assert_alive = helpers.assert_alive +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local command = n.command +local eq = t.eq +local clear = n.clear +local fn = n.fn +local pcall_err = t.pcall_err +local assert_alive = n.assert_alive describe('Ex cmds', function() before_each(function() diff --git a/test/functional/ex_cmds/file_spec.lua b/test/functional/ex_cmds/file_spec.lua index a48c408600..0c51f1c17b 100644 --- a/test/functional/ex_cmds/file_spec.lua +++ b/test/functional/ex_cmds/file_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local fn = helpers.fn -local rmdir = helpers.rmdir -local mkdir = helpers.mkdir +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local fn = n.fn +local rmdir = n.rmdir +local mkdir = t.mkdir describe(':file', function() local swapdir = vim.uv.cwd() .. '/Xtest-file_spec' diff --git a/test/functional/ex_cmds/grep_spec.lua b/test/functional/ex_cmds/grep_spec.lua index bf81ba2137..f7d0f43c62 100644 --- a/test/functional/ex_cmds/grep_spec.lua +++ b/test/functional/ex_cmds/grep_spec.lua @@ -1,6 +1,7 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, feed_command, feed, ok, eval = - helpers.clear, helpers.feed_command, helpers.feed, helpers.ok, helpers.eval +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, feed_command, feed, ok, eval = n.clear, n.feed_command, n.feed, t.ok, n.eval describe(':grep', function() before_each(clear) diff --git a/test/functional/ex_cmds/help_spec.lua b/test/functional/ex_cmds/help_spec.lua index cee33de1a6..f0a7bea7e4 100644 --- a/test/functional/ex_cmds/help_spec.lua +++ b/test/functional/ex_cmds/help_spec.lua @@ -1,19 +1,20 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local fn = helpers.fn -local api = helpers.api -local mkdir = helpers.mkdir -local rmdir = helpers.rmdir -local write_file = helpers.write_file +local clear = n.clear +local command = n.command +local eq = t.eq +local fn = n.fn +local api = n.api +local mkdir = t.mkdir +local rmdir = n.rmdir +local write_file = t.write_file describe(':help', function() before_each(clear) it('window closed makes cursor return to a valid win/buf #9773', function() - helpers.add_builddir_to_rtp() + n.add_builddir_to_rtp() command('help help') eq(1001, fn.win_getid()) command('quit') diff --git a/test/functional/ex_cmds/highlight_spec.lua b/test/functional/ex_cmds/highlight_spec.lua index 897a2997bc..3cb6cc2579 100644 --- a/test/functional/ex_cmds/highlight_spec.lua +++ b/test/functional/ex_cmds/highlight_spec.lua @@ -1,11 +1,13 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local eq, command = helpers.eq, helpers.command -local clear = helpers.clear -local eval, exc_exec = helpers.eval, helpers.exc_exec -local exec = helpers.exec -local fn = helpers.fn -local api = helpers.api + +local eq, command = t.eq, n.command +local clear = n.clear +local eval, exc_exec = n.eval, n.exc_exec +local exec = n.exec +local fn = n.fn +local api = n.api describe(':highlight', function() local screen diff --git a/test/functional/ex_cmds/ls_spec.lua b/test/functional/ex_cmds/ls_spec.lua index 5f59402d10..aab53f0634 100644 --- a/test/functional/ex_cmds/ls_spec.lua +++ b/test/functional/ex_cmds/ls_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local api = helpers.api -local testprg = helpers.testprg -local retry = helpers.retry +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local feed = n.feed +local api = n.api +local testprg = n.testprg +local retry = t.retry describe(':ls', function() before_each(function() diff --git a/test/functional/ex_cmds/make_spec.lua b/test/functional/ex_cmds/make_spec.lua index dd47bdec58..d6164cec29 100644 --- a/test/functional/ex_cmds/make_spec.lua +++ b/test/functional/ex_cmds/make_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eval = helpers.eval -local has_powershell = helpers.has_powershell -local matches = helpers.matches -local api = helpers.api -local testprg = helpers.testprg +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eval = n.eval +local has_powershell = n.has_powershell +local matches = t.matches +local api = n.api +local testprg = n.testprg describe(':make', function() clear() @@ -18,7 +20,7 @@ describe(':make', function() return end before_each(function() - helpers.set_shell_powershell() + n.set_shell_powershell() end) it('captures stderr & non zero exit code #14349', function() diff --git a/test/functional/ex_cmds/map_spec.lua b/test/functional/ex_cmds/map_spec.lua index d3b027e6f4..9ef0a8ec2e 100644 --- a/test/functional/ex_cmds/map_spec.lua +++ b/test/functional/ex_cmds/map_spec.lua @@ -1,16 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local eq = helpers.eq -local exec = helpers.exec -local exec_capture = helpers.exec_capture -local feed = helpers.feed -local api = helpers.api -local clear = helpers.clear -local command = helpers.command -local expect = helpers.expect -local insert = helpers.insert -local eval = helpers.eval +local eq = t.eq +local exec = n.exec +local exec_capture = n.exec_capture +local feed = n.feed +local api = n.api +local clear = n.clear +local command = n.command +local expect = n.expect +local insert = n.insert +local eval = n.eval describe(':*map', function() before_each(clear) @@ -109,19 +110,19 @@ describe('Screen', function() command('map <expr> x input("> ")') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 | ]]) feed('x') screen:expect([[ | - ~ |*3 + {1:~ }|*3 > ^ | ]]) feed('\n') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 > | ]]) end) @@ -131,20 +132,20 @@ describe('Screen', function() feed('i') screen:expect([[ ^ | - ~ |*3 - -- INSERT -- | + {1:~ }|*3 + {5:-- INSERT --} | ]]) feed('x') screen:expect([[ | - ~ |*3 + {1:~ }|*3 > ^ | ]]) feed('\n') screen:expect([[ ^ | - ~ |*3 - -- INSERT -- | + {1:~ }|*3 + {5:-- INSERT --} | ]]) end) @@ -153,7 +154,7 @@ describe('Screen', function() feed(':<F2>') screen:expect([[ | - ~ |*3 + {1:~ }|*3 :^ | ]]) end) @@ -181,7 +182,7 @@ describe('Screen', function() one | ^two | three | - [on] | + {9:[on] }| | ]]) end) @@ -191,15 +192,16 @@ describe('Screen', function() command('nmap <expr> <F2> execute("throw 42")') feed('<F2>') screen:expect([[ - |*2 - Error detected while processing : | - E605: Exception not caught: 42 | - Press ENTER or type command to continue^ | + | + {3: }| + {9:Error detected while processing :} | + {9:E605: Exception not caught: 42} | + {6:Press ENTER or type command to continue}^ | ]]) feed('<CR>') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 | ]]) end) @@ -210,32 +212,32 @@ describe('Screen', function() feed(':echo "foo') screen:expect([[ | - ~ |*3 + {1:~ }|*3 :echo "foo^ | ]]) feed('<F2>') screen:expect([[ - | + {3: }| :echo "foo | - Error detected while processing : | - E605: Exception not caught: 42 | + {9:Error detected while processing :} | + {9:E605: Exception not caught: 42} | :echo "foo^ | ]]) feed('"') screen:expect([[ - | + {3: }| :echo "foo | - Error detected while processing : | - E605: Exception not caught: 42 | + {9:Error detected while processing :} | + {9:E605: Exception not caught: 42} | :echo "foo"^ | ]]) feed('\n') screen:expect([[ :echo "foo | - Error detected while processing : | - E605: Exception not caught: 42 | + {9:Error detected while processing :} | + {9:E605: Exception not caught: 42} | foo | - Press ENTER or type command to continue^ | + {6:Press ENTER or type command to continue}^ | ]]) end) @@ -246,7 +248,7 @@ describe('Screen', function() feed(': nmap a<CR>') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 n a b | ]]) end) diff --git a/test/functional/ex_cmds/menu_spec.lua b/test/functional/ex_cmds/menu_spec.lua index bb6ef72787..e73f705a29 100644 --- a/test/functional/ex_cmds/menu_spec.lua +++ b/test/functional/ex_cmds/menu_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, command = helpers.clear, helpers.command -local expect, feed = helpers.expect, helpers.feed -local eq, eval = helpers.eq, helpers.eval -local fn = helpers.fn +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, command = n.clear, n.command +local expect, feed = n.expect, n.feed +local eq, eval = t.eq, n.eval +local fn = n.fn describe(':emenu', function() before_each(function() diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua index 6f2e0be3d5..9b24854362 100644 --- a/test/functional/ex_cmds/mksession_spec.lua +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -1,20 +1,21 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local get_pathsep = helpers.get_pathsep -local eq = helpers.eq -local neq = helpers.neq -local fn = helpers.fn -local matches = helpers.matches +local clear = n.clear +local command = n.command +local get_pathsep = n.get_pathsep +local eq = t.eq +local neq = t.neq +local fn = n.fn +local matches = t.matches local pesc = vim.pesc -local rmdir = helpers.rmdir +local rmdir = n.rmdir local sleep = vim.uv.sleep -local api = helpers.api -local skip = helpers.skip -local is_os = helpers.is_os -local mkdir = helpers.mkdir +local api = n.api +local skip = t.skip +local is_os = t.is_os +local mkdir = t.mkdir local file_prefix = 'Xtest-functional-ex_cmds-mksession_spec' diff --git a/test/functional/ex_cmds/mkview_spec.lua b/test/functional/ex_cmds/mkview_spec.lua index de0a4fe0ea..7cc0b1da83 100644 --- a/test/functional/ex_cmds/mkview_spec.lua +++ b/test/functional/ex_cmds/mkview_spec.lua @@ -1,12 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local command = helpers.command -local get_pathsep = helpers.get_pathsep -local eq = helpers.eq -local fn = helpers.fn -local rmdir = helpers.rmdir -local mkdir = helpers.mkdir +local clear = n.clear +local command = n.command +local get_pathsep = n.get_pathsep +local eq = t.eq +local fn = n.fn +local rmdir = n.rmdir +local mkdir = t.mkdir local file_prefix = 'Xtest-functional-ex_cmds-mkview_spec' diff --git a/test/functional/ex_cmds/normal_spec.lua b/test/functional/ex_cmds/normal_spec.lua index 723bfefcf4..ccc649120f 100644 --- a/test/functional/ex_cmds/normal_spec.lua +++ b/test/functional/ex_cmds/normal_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local fn = helpers.fn -local feed = helpers.feed -local expect = helpers.expect -local eq = helpers.eq -local eval = helpers.eval +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local fn = n.fn +local feed = n.feed +local expect = n.expect +local eq = t.eq +local eval = n.eval before_each(clear) diff --git a/test/functional/ex_cmds/oldfiles_spec.lua b/test/functional/ex_cmds/oldfiles_spec.lua index 8d1469f343..927d780181 100644 --- a/test/functional/ex_cmds/oldfiles_spec.lua +++ b/test/functional/ex_cmds/oldfiles_spec.lua @@ -1,13 +1,14 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local expect_exit = helpers.expect_exit -local api, eq, feed_command = helpers.api, helpers.eq, helpers.feed_command -local feed, poke_eventloop = helpers.feed, helpers.poke_eventloop -local ok = helpers.ok -local eval = helpers.eval +local clear = n.clear +local command = n.command +local expect_exit = n.expect_exit +local api, eq, feed_command = n.api, t.eq, n.feed_command +local feed, poke_eventloop = n.feed, n.poke_eventloop +local ok = t.ok +local eval = n.eval local shada_file = 'Xtest.shada' @@ -38,6 +39,7 @@ describe(':oldfiles', function() it('shows most recently used files', function() local screen = Screen.new(100, 5) screen:attach() + screen._default_attr_ids = nil feed_command('edit testfile1') feed_command('edit testfile2') feed_command('wshada') @@ -65,12 +67,12 @@ describe(':oldfiles', function() feed_command('rshada!') local function get_oldfiles(cmd) - local t = eval([[split(execute(']] .. cmd .. [['), "\n")]]) - for i, _ in ipairs(t) do - t[i] = t[i]:gsub('^%d+:%s+', '') + local q = eval([[split(execute(']] .. cmd .. [['), "\n")]]) + for i, _ in ipairs(q) do + q[i] = q[i]:gsub('^%d+:%s+', '') end - table.sort(t) - return t + table.sort(q) + return q end local oldfiles = get_oldfiles('oldfiles') @@ -108,7 +110,7 @@ describe(':browse oldfiles', function() -- Ensure v:oldfiles isn't busted. Since things happen so fast, -- the ordering of v:oldfiles is unstable (it uses qsort() under-the-hood). -- Let's verify the contents and the length of v:oldfiles before moving on. - oldfiles = helpers.api.nvim_get_vvar('oldfiles') + oldfiles = n.api.nvim_get_vvar('oldfiles') eq(2, #oldfiles) ok(filename == oldfiles[1] or filename == oldfiles[2]) ok(filename2 == oldfiles[1] or filename2 == oldfiles[2]) diff --git a/test/functional/ex_cmds/print_commands_spec.lua b/test/functional/ex_cmds/print_commands_spec.lua index ba5ec7d2d1..8c164fd41b 100644 --- a/test/functional/ex_cmds/print_commands_spec.lua +++ b/test/functional/ex_cmds/print_commands_spec.lua @@ -1,5 +1,7 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eq, command, fn = helpers.clear, helpers.eq, helpers.command, helpers.fn +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq, command, fn = n.clear, t.eq, n.command, n.fn describe(':z^', function() before_each(clear) diff --git a/test/functional/ex_cmds/profile_spec.lua b/test/functional/ex_cmds/profile_spec.lua index f85dcc60ff..57e5c6b2dc 100644 --- a/test/functional/ex_cmds/profile_spec.lua +++ b/test/functional/ex_cmds/profile_spec.lua @@ -1,14 +1,15 @@ -require('os') +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local uv = vim.uv +require('os') -local helpers = require('test.functional.helpers')(after_each) -local eval = helpers.eval -local command = helpers.command -local eq, neq = helpers.eq, helpers.neq -local tempfile = helpers.tmpname() -local source = helpers.source -local matches = helpers.matches -local read_file = helpers.read_file +local eval = n.eval +local command = n.command +local eq, neq = t.eq, t.neq +local tempfile = t.tmpname() +local source = n.source +local matches = t.matches +local read_file = t.read_file -- tmpname() also creates the file on POSIX systems. Remove it again. -- We just need the name, ignoring any race conditions. @@ -25,10 +26,10 @@ local function assert_file_exists_not(filepath) end describe(':profile', function() - before_each(helpers.clear) + before_each(n.clear) after_each(function() - helpers.expect_exit(command, 'qall!') + n.expect_exit(command, 'qall!') if uv.fs_stat(tempfile).uid ~= nil then os.remove(tempfile) end diff --git a/test/functional/ex_cmds/quickfix_commands_spec.lua b/test/functional/ex_cmds/quickfix_commands_spec.lua index 5af0198ffe..3df41b015e 100644 --- a/test/functional/ex_cmds/quickfix_commands_spec.lua +++ b/test/functional/ex_cmds/quickfix_commands_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed = helpers.feed -local eq = helpers.eq -local clear = helpers.clear -local fn = helpers.fn -local command = helpers.command -local exc_exec = helpers.exc_exec -local write_file = helpers.write_file -local api = helpers.api -local source = helpers.source +local feed = n.feed +local eq = t.eq +local clear = n.clear +local fn = n.fn +local command = n.command +local exc_exec = n.exc_exec +local write_file = t.write_file +local api = n.api +local source = n.source local file_base = 'Xtest-functional-ex_cmds-quickfix_commands' diff --git a/test/functional/ex_cmds/quit_spec.lua b/test/functional/ex_cmds/quit_spec.lua index 5a1759dab3..83b1c5a540 100644 --- a/test/functional/ex_cmds/quit_spec.lua +++ b/test/functional/ex_cmds/quit_spec.lua @@ -1,5 +1,6 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear +local n = require('test.functional.testnvim')() + +local clear = n.clear describe(':qa', function() before_each(function() diff --git a/test/functional/ex_cmds/script_spec.lua b/test/functional/ex_cmds/script_spec.lua index 4c963c5da7..d69fae11ce 100644 --- a/test/functional/ex_cmds/script_spec.lua +++ b/test/functional/ex_cmds/script_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local neq = helpers.neq -local command = helpers.command -local exec_capture = helpers.exec_capture -local write_file = helpers.write_file -local api = helpers.api -local clear = helpers.clear -local dedent = helpers.dedent -local exc_exec = helpers.exc_exec -local missing_provider = helpers.missing_provider +local eq = t.eq +local neq = t.neq +local command = n.command +local exec_capture = n.exec_capture +local write_file = t.write_file +local api = n.api +local clear = n.clear +local dedent = t.dedent +local exc_exec = n.exc_exec +local missing_provider = n.missing_provider local tmpfile = 'X_ex_cmds_script' diff --git a/test/functional/ex_cmds/sign_spec.lua b/test/functional/ex_cmds/sign_spec.lua index 06de7f23a9..1d1708a41c 100644 --- a/test/functional/ex_cmds/sign_spec.lua +++ b/test/functional/ex_cmds/sign_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eq, assert_alive = helpers.clear, helpers.eq, helpers.assert_alive -local command = helpers.command -local api = helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq, assert_alive = n.clear, t.eq, n.assert_alive +local command = n.command +local api = n.api describe('sign', function() before_each(clear) diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua index 5ce0e395bd..0a6f44fab4 100644 --- a/test/functional/ex_cmds/source_spec.lua +++ b/test/functional/ex_cmds/source_spec.lua @@ -1,23 +1,25 @@ -local helpers = require('test.functional.helpers')(after_each) -local command = helpers.command -local insert = helpers.insert -local eq = helpers.eq -local clear = helpers.clear -local api = helpers.api -local feed = helpers.feed -local feed_command = helpers.feed_command -local write_file = helpers.write_file -local tmpname = helpers.tmpname -local exec = helpers.exec -local exc_exec = helpers.exc_exec -local exec_lua = helpers.exec_lua -local eval = helpers.eval -local exec_capture = helpers.exec_capture -local neq = helpers.neq -local matches = helpers.matches -local mkdir = helpers.mkdir -local rmdir = helpers.rmdir -local is_os = helpers.is_os +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local command = n.command +local insert = n.insert +local eq = t.eq +local clear = n.clear +local api = n.api +local feed = n.feed +local feed_command = n.feed_command +local write_file = t.write_file +local tmpname = t.tmpname +local exec = n.exec +local exc_exec = n.exc_exec +local exec_lua = n.exec_lua +local eval = n.eval +local exec_capture = n.exec_capture +local neq = t.neq +local matches = t.matches +local mkdir = t.mkdir +local rmdir = n.rmdir +local is_os = t.is_os describe(':source', function() before_each(function() diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua index a6fdb919c5..5bb2a0181e 100644 --- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -1,29 +1,31 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) + local uv = vim.uv -local eq, eval, expect, exec = helpers.eq, helpers.eval, helpers.expect, helpers.exec -local assert_alive = helpers.assert_alive -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed -local fn = helpers.fn -local nvim_prog = helpers.nvim_prog -local ok = helpers.ok -local rmdir = helpers.rmdir -local new_argv = helpers.new_argv -local new_pipename = helpers.new_pipename +local eq, eval, expect, exec = t.eq, n.eval, n.expect, n.exec +local assert_alive = n.assert_alive +local clear = n.clear +local command = n.command +local feed = n.feed +local fn = n.fn +local nvim_prog = n.nvim_prog +local ok = t.ok +local rmdir = n.rmdir +local new_argv = n.new_argv +local new_pipename = n.new_pipename local pesc = vim.pesc -local os_kill = helpers.os_kill -local set_session = helpers.set_session -local spawn = helpers.spawn -local async_meths = helpers.async_meths -local expect_msg_seq = helpers.expect_msg_seq -local pcall_err = helpers.pcall_err -local mkdir = helpers.mkdir -local poke_eventloop = helpers.poke_eventloop -local api = helpers.api -local retry = helpers.retry -local write_file = helpers.write_file +local os_kill = n.os_kill +local set_session = n.set_session +local spawn = n.spawn +local async_meths = n.async_meths +local expect_msg_seq = n.expect_msg_seq +local pcall_err = t.pcall_err +local mkdir = t.mkdir +local poke_eventloop = n.poke_eventloop +local api = n.api +local retry = t.retry +local write_file = t.write_file describe(':recover', function() before_each(clear) @@ -110,7 +112,7 @@ describe("preserve and (R)ecover with custom 'directory'", function() end) it('killing TUI process without :preserve #22096', function() - helpers.skip(helpers.is_os('win')) + t.skip(t.is_os('win')) local screen0 = Screen.new() screen0:attach() local child_server = new_pipename() @@ -118,7 +120,7 @@ describe("preserve and (R)ecover with custom 'directory'", function() env = { VIMRUNTIME = os.getenv('VIMRUNTIME') }, }) screen0:expect({ any = pesc('[No Name]') }) -- Wait for the child process to start. - local child_session = helpers.connect(child_server) + local child_session = n.connect(child_server) set_session(child_session) local swappath1 = setup_swapname() set_session(nvim0) @@ -170,6 +172,7 @@ describe('swapfile detection', function() set_session(nvim2) local screen2 = Screen.new(256, 40) screen2:attach() + screen2._default_attr_ids = nil exec(init) command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog). diff --git a/test/functional/ex_cmds/syntax_spec.lua b/test/functional/ex_cmds/syntax_spec.lua index ccdd604c55..35b45fe800 100644 --- a/test/functional/ex_cmds/syntax_spec.lua +++ b/test/functional/ex_cmds/syntax_spec.lua @@ -1,8 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local clear = helpers.clear -local exc_exec = helpers.exc_exec +local eq = t.eq +local clear = n.clear +local exc_exec = n.exc_exec describe(':syntax', function() before_each(clear) diff --git a/test/functional/ex_cmds/trust_spec.lua b/test/functional/ex_cmds/trust_spec.lua index 2997b504fa..76248cf530 100644 --- a/test/functional/ex_cmds/trust_spec.lua +++ b/test/functional/ex_cmds/trust_spec.lua @@ -1,27 +1,28 @@ -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local clear = helpers.clear -local command = helpers.command -local exec_capture = helpers.exec_capture -local matches = helpers.matches -local pathsep = helpers.get_pathsep() -local is_os = helpers.is_os -local fn = helpers.fn +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local clear = n.clear +local command = n.command +local exec_capture = n.exec_capture +local matches = t.matches +local pathsep = n.get_pathsep() +local is_os = t.is_os +local fn = n.fn describe(':trust', function() local xstate = 'Xstate' setup(function() - helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) + n.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) end) teardown(function() - helpers.rmdir(xstate) + n.rmdir(xstate) end) before_each(function() - helpers.write_file('test_file', 'test') + t.write_file('test_file', 'test') clear { env = { XDG_STATE_HOME = xstate } } end) @@ -31,37 +32,37 @@ describe(':trust', function() it('trust then deny then remove a file using current buffer', function() local cwd = fn.getcwd() - local hash = fn.sha256(helpers.read_file('test_file')) + local hash = fn.sha256(t.read_file('test_file')) command('edit test_file') matches('^Allowed ".*test_file" in trust database%.$', exec_capture('trust')) - local trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + local trust = t.read_file(fn.stdpath('state') .. pathsep .. 'trust') eq(string.format('%s %s', hash, cwd .. pathsep .. 'test_file'), vim.trim(trust)) matches('^Denied ".*test_file" in trust database%.$', exec_capture('trust ++deny')) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = t.read_file(fn.stdpath('state') .. pathsep .. 'trust') eq(string.format('! %s', cwd .. pathsep .. 'test_file'), vim.trim(trust)) matches('^Removed ".*test_file" from trust database%.$', exec_capture('trust ++remove')) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = t.read_file(fn.stdpath('state') .. pathsep .. 'trust') eq(string.format(''), vim.trim(trust)) end) it('deny then trust then remove a file using current buffer', function() local cwd = fn.getcwd() - local hash = fn.sha256(helpers.read_file('test_file')) + local hash = fn.sha256(t.read_file('test_file')) command('edit test_file') matches('^Denied ".*test_file" in trust database%.$', exec_capture('trust ++deny')) - local trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + local trust = t.read_file(fn.stdpath('state') .. pathsep .. 'trust') eq(string.format('! %s', cwd .. pathsep .. 'test_file'), vim.trim(trust)) matches('^Allowed ".*test_file" in trust database%.$', exec_capture('trust')) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = t.read_file(fn.stdpath('state') .. pathsep .. 'trust') eq(string.format('%s %s', hash, cwd .. pathsep .. 'test_file'), vim.trim(trust)) matches('^Removed ".*test_file" from trust database%.$', exec_capture('trust ++remove')) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = t.read_file(fn.stdpath('state') .. pathsep .. 'trust') eq(string.format(''), vim.trim(trust)) end) @@ -69,14 +70,14 @@ describe(':trust', function() local cwd = fn.getcwd() matches('^Denied ".*test_file" in trust database%.$', exec_capture('trust ++deny test_file')) - local trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + local trust = t.read_file(fn.stdpath('state') .. pathsep .. 'trust') eq(string.format('! %s', cwd .. pathsep .. 'test_file'), vim.trim(trust)) matches( '^Removed ".*test_file" from trust database%.$', exec_capture('trust ++remove test_file') ) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = t.read_file(fn.stdpath('state') .. pathsep .. 'trust') eq(string.format(''), vim.trim(trust)) end) end) diff --git a/test/functional/ex_cmds/undojoin_spec.lua b/test/functional/ex_cmds/undojoin_spec.lua index 7803906619..06b5973053 100644 --- a/test/functional/ex_cmds/undojoin_spec.lua +++ b/test/functional/ex_cmds/undojoin_spec.lua @@ -1,12 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local clear = helpers.clear -local insert = helpers.insert -local feed = helpers.feed -local expect = helpers.expect -local feed_command = helpers.feed_command -local exc_exec = helpers.exc_exec +local eq = t.eq +local clear = n.clear +local insert = n.insert +local feed = n.feed +local expect = n.expect +local feed_command = n.feed_command +local exc_exec = n.exc_exec describe(':undojoin command', function() before_each(function() diff --git a/test/functional/ex_cmds/verbose_spec.lua b/test/functional/ex_cmds/verbose_spec.lua index 7ceb2460d3..0f99552935 100644 --- a/test/functional/ex_cmds/verbose_spec.lua +++ b/test/functional/ex_cmds/verbose_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local exec = helpers.exec -local exec_capture = helpers.exec_capture -local write_file = helpers.write_file -local call_viml_function = helpers.api.nvim_call_function +local clear = n.clear +local eq = t.eq +local exec = n.exec +local exec_capture = n.exec_capture +local write_file = t.write_file +local call_viml_function = n.api.nvim_call_function local function last_set_tests(cmd) local script_location, script_file @@ -15,7 +16,7 @@ local function last_set_tests(cmd) script_file = 'test_verbose.lua' local current_dir = call_viml_function('getcwd', {}) current_dir = call_viml_function('fnamemodify', { current_dir, ':~' }) - script_location = table.concat { current_dir, helpers.get_pathsep(), script_file } + script_location = table.concat { current_dir, n.get_pathsep(), script_file } write_file( script_file, @@ -32,21 +33,30 @@ vim.api.nvim_exec2("augroup test_group\ augroup END\ ", {}) +vim.api.nvim_create_autocmd('FileType', { + group = 'test_group', + pattern = 'cpp', + command = 'setl cindent', +}) + +vim.api.nvim_exec2(':highlight TestHL1 guibg=Blue', {}) +vim.api.nvim_set_hl(0, 'TestHL2', { bg = 'Green' }) + vim.api.nvim_command("command Bdelete :bd") vim.api.nvim_create_user_command("TestCommand", ":echo 'Hello'", {}) -vim.api.nvim_exec ("\ +vim.api.nvim_exec2 ("\ function Close_Window() abort\ wincmd -\ endfunction\ -", false) +", {}) -local ret = vim.api.nvim_exec ("\ +local ret = vim.api.nvim_exec2 ("\ function! s:return80()\ return 80\ endfunction\ let &tw = s:return80()\ -", true) +", {}) ]] ) exec(cmd .. ' ' .. script_file) @@ -109,7 +119,7 @@ n \key1 * :echo "test"<CR> ) end) - it('"Last set" for mapping set by vim.keymap', function() + it('"Last set" for mapping set by vim.keymap.set', function() local result = exec_capture(':verbose map <leader>key2') eq( string.format( @@ -123,7 +133,7 @@ n \key2 * :echo "test"<CR> ) end) - it('"Last set" for autocmd by vim.api.nvim_exec', function() + it('"Last set" for autocmd set by nvim_exec2', function() local result = exec_capture(':verbose autocmd test_group Filetype c') eq( string.format( @@ -138,6 +148,47 @@ test_group FileType ) end) + it('"Last set" for autocmd set by nvim_create_autocmd', function() + local result = exec_capture(':verbose autocmd test_group Filetype cpp') + eq( + string.format( + [[ +--- Autocommands --- +test_group FileType + cpp setl cindent + Last set from %s line 13]], + script_location + ), + result + ) + end) + + it('"Last set" for highlight group set by nvim_exec2', function() + local result = exec_capture(':verbose highlight TestHL1') + eq( + string.format( + [[ +TestHL1 xxx guibg=Blue + Last set from %s line 19]], + script_location + ), + result + ) + end) + + it('"Last set" for highlight group set by nvim_set_hl', function() + local result = exec_capture(':verbose highlight TestHL2') + eq( + string.format( + [[ +TestHL2 xxx guibg=Green + Last set from %s line 20]], + script_location + ), + result + ) + end) + it('"Last set" for command defined by nvim_command', function() if cmd == 'luafile' then pending('nvim_command does not set the script context') @@ -148,7 +199,7 @@ test_group FileType [[ Name Args Address Complete Definition Bdelete 0 :bd - Last set from %s line 13]], + Last set from %s line 22]], script_location ), result @@ -162,7 +213,7 @@ test_group FileType [[ Name Args Address Complete Definition TestCommand 0 :echo 'Hello' - Last set from %s line 14]], + Last set from %s line 23]], script_location ), result @@ -175,7 +226,7 @@ test_group FileType string.format( [[ function Close_Window() abort - Last set from %s line 16 + Last set from %s line 25 1 wincmd - endfunction]], script_location @@ -190,7 +241,7 @@ test_group FileType string.format( [[ textwidth=80 - Last set from %s line 22]], + Last set from %s line 31]], script_location ), result @@ -230,7 +281,7 @@ describe('lua verbose:', function() eq( [[ nohlsearch - Last set from Lua]], + Last set from Lua (run Nvim with -V1 for more details)]], result ) end) diff --git a/test/functional/ex_cmds/wincmd_spec.lua b/test/functional/ex_cmds/wincmd_spec.lua index 98c6358f45..283f0991cb 100644 --- a/test/functional/ex_cmds/wincmd_spec.lua +++ b/test/functional/ex_cmds/wincmd_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local fn = helpers.fn -local command = helpers.command +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local fn = n.fn +local command = n.command it(':wincmd accepts a count', function() clear() diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index f711731072..bb04bfa08a 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + local eq, eval, clear, write_file, source, insert = - helpers.eq, helpers.eval, helpers.clear, helpers.write_file, helpers.source, helpers.insert -local pcall_err = helpers.pcall_err -local command = helpers.command -local feed_command = helpers.feed_command -local fn = helpers.fn -local api = helpers.api -local skip = helpers.skip -local is_os = helpers.is_os -local is_ci = helpers.is_ci + t.eq, n.eval, n.clear, t.write_file, n.source, n.insert +local pcall_err = t.pcall_err +local command = n.command +local feed_command = n.feed_command +local fn = n.fn +local api = n.api +local skip = t.skip +local is_os = t.is_os +local is_ci = t.is_ci local fname = 'Xtest-functional-ex_cmds-write' local fname_bak = fname .. '~' diff --git a/test/functional/ex_cmds/wundo_spec.lua b/test/functional/ex_cmds/wundo_spec.lua index 78081fa45f..2299f33f06 100644 --- a/test/functional/ex_cmds/wundo_spec.lua +++ b/test/functional/ex_cmds/wundo_spec.lua @@ -1,13 +1,13 @@ -- Specs for :wundo and underlying functions -local helpers = require('test.functional.helpers')(after_each) -local command, clear, eval, spawn, nvim_prog, set_session = - helpers.command, - helpers.clear, - helpers.eval, - helpers.spawn, - helpers.nvim_prog, - helpers.set_session +local n = require('test.functional.testnvim')() + +local command = n.command +local clear = n.clear +local eval = n.eval +local spawn = n.spawn +local nvim_prog = n.nvim_prog +local set_session = n.set_session describe(':wundo', function() before_each(clear) diff --git a/test/functional/ex_cmds/wviminfo_spec.lua b/test/functional/ex_cmds/wviminfo_spec.lua index 23ae1440e6..7c76d92228 100644 --- a/test/functional/ex_cmds/wviminfo_spec.lua +++ b/test/functional/ex_cmds/wviminfo_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command, eq, neq, write_file = helpers.command, helpers.eq, helpers.neq, helpers.write_file -local read_file = helpers.read_file -local is_os = helpers.is_os +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command, eq, neq, write_file = n.command, t.eq, t.neq, t.write_file +local read_file = t.read_file +local is_os = t.is_os describe(':wshada', function() local shada_file = 'wshada_test' diff --git a/test/functional/example_spec.lua b/test/functional/example_spec.lua index 5fc55f4aab..8db5c3d867 100644 --- a/test/functional/example_spec.lua +++ b/test/functional/example_spec.lua @@ -1,12 +1,13 @@ -- To run this test: -- TEST_FILE=test/functional/example_spec.lua make functionaltest -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local feed = helpers.feed +local clear = n.clear +local command = n.command +local eq = t.eq +local feed = n.feed describe('example', function() local screen diff --git a/test/functional/fixtures/api_level_12.mpack b/test/functional/fixtures/api_level_12.mpack Binary files differnew file mode 100644 index 0000000000..bab1b5111c --- /dev/null +++ b/test/functional/fixtures/api_level_12.mpack diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index d9f44da0b4..f806869b40 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -110,7 +110,8 @@ local tests = {} function tests.basic_init() skeleton { - on_init = function(_) + on_init = function(params) + assert_eq(params.workDoneToken, '1') return { capabilities = { textDocumentSync = protocol.TextDocumentSyncKind.None, @@ -983,7 +984,7 @@ local test_name = arg[1] local timeout = arg[2] assert(type(test_name) == 'string', 'test_name must be specified as first arg.') -local kill_timer = vim.uv.new_timer() +local kill_timer = assert(vim.uv.new_timer()) kill_timer:start(timeout or 1e3, 0, function() kill_timer:stop() kill_timer:close() diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index ef9f23e3f9..bd71e7d11b 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -49,6 +49,10 @@ int main(int argc, char **argv) help(); } +#ifdef _MSC_VER + SetConsoleOutputCP(CP_UTF8); +#endif + if (argc >= 2) { if (strcmp(argv[1], "-t") == 0) { if (argc < 3) { diff --git a/test/functional/legacy/002_filename_recognition_spec.lua b/test/functional/legacy/002_filename_recognition_spec.lua index 26a62d92fe..dd1335df94 100644 --- a/test/functional/legacy/002_filename_recognition_spec.lua +++ b/test/functional/legacy/002_filename_recognition_spec.lua @@ -1,9 +1,10 @@ -- Test if URLs are recognized as filenames by commands such as "gf". Here -- we'll use `expand("<cfile>")` since "gf" would need to open the file. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('filename recognition', function() setup(clear) diff --git a/test/functional/legacy/004_bufenter_with_modelines_spec.lua b/test/functional/legacy/004_bufenter_with_modelines_spec.lua index 9b0df024c8..6865153452 100644 --- a/test/functional/legacy/004_bufenter_with_modelines_spec.lua +++ b/test/functional/legacy/004_bufenter_with_modelines_spec.lua @@ -1,9 +1,10 @@ -- Test for autocommand that changes current buffer on BufEnter event. -- Check if modelines are interpreted for the correct buffer. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('BufEnter with modelines', function() setup(clear) diff --git a/test/functional/legacy/005_bufleave_delete_buffer_spec.lua b/test/functional/legacy/005_bufleave_delete_buffer_spec.lua index 8e977aa73e..4d2d06f942 100644 --- a/test/functional/legacy/005_bufleave_delete_buffer_spec.lua +++ b/test/functional/legacy/005_bufleave_delete_buffer_spec.lua @@ -1,10 +1,11 @@ -- Test for autocommand that deletes the current buffer on BufLeave event. -- Also test deleting the last buffer, should give a new, empty buffer. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command, expect = helpers.command, helpers.expect -local poke_eventloop = helpers.poke_eventloop +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local command, expect = n.command, n.expect +local poke_eventloop = n.poke_eventloop describe('test5', function() setup(clear) diff --git a/test/functional/legacy/007_ball_buffer_list_spec.lua b/test/functional/legacy/007_ball_buffer_list_spec.lua index d4e4547c43..15f3b0b9b8 100644 --- a/test/functional/legacy/007_ball_buffer_list_spec.lua +++ b/test/functional/legacy/007_ball_buffer_list_spec.lua @@ -1,8 +1,9 @@ -- Test for autocommand that changes the buffer list, when doing ":ball". -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe(':ball', function() setup(clear) diff --git a/test/functional/legacy/008_autocommands_spec.lua b/test/functional/legacy/008_autocommands_spec.lua index 16f0216bc0..b7b411d631 100644 --- a/test/functional/legacy/008_autocommands_spec.lua +++ b/test/functional/legacy/008_autocommands_spec.lua @@ -1,13 +1,14 @@ -- Test for BufWritePre autocommand that deletes or unloads the buffer. -- Test for BufUnload autocommand that unloads all other buffers. -local helpers = require('test.functional.helpers')(after_each) -local source = helpers.source -local clear, command, expect, eq, eval = - helpers.clear, helpers.command, helpers.expect, helpers.eq, helpers.eval -local write_file, dedent = helpers.write_file, helpers.dedent -local read_file = helpers.read_file -local expect_exit = helpers.expect_exit +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local source = n.source +local clear, command, expect, eq, eval = n.clear, n.command, n.expect, t.eq, n.eval +local write_file, dedent = t.write_file, t.dedent +local read_file = t.read_file +local expect_exit = n.expect_exit describe('autocommands that delete and unload buffers:', function() local test_file = 'Xtest-008_autocommands.out' diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua index eba878b99a..45965d8791 100644 --- a/test/functional/legacy/011_autocommands_spec.lua +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -12,19 +12,14 @@ -- Use a FileChangedShell autocommand to avoid a prompt for "Xtestfile.gz" -- being modified outside of Vim (noticed on Solaris). -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + local clear, feed_command, expect, eq, neq, dedent, write_file, feed = - helpers.clear, - helpers.feed_command, - helpers.expect, - helpers.eq, - helpers.neq, - helpers.dedent, - helpers.write_file, - helpers.feed -local command = helpers.command -local read_file = helpers.read_file -local is_os = helpers.is_os + n.clear, n.feed_command, n.expect, t.eq, t.neq, t.dedent, t.write_file, n.feed +local command = n.command +local read_file = t.read_file +local is_os = t.is_os local function has_gzip() local null = is_os('win') and 'nul' or '/dev/null' diff --git a/test/functional/legacy/012_directory_spec.lua b/test/functional/legacy/012_directory_spec.lua index b428318e3f..5c9185982f 100644 --- a/test/functional/legacy/012_directory_spec.lua +++ b/test/functional/legacy/012_directory_spec.lua @@ -3,19 +3,20 @@ -- - "./dir", in directory relative to file -- - "dir", in directory relative to current dir -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local neq = helpers.neq -local poke_eventloop = helpers.poke_eventloop -local fn = helpers.fn -local api = helpers.api -local clear = helpers.clear -local insert = helpers.insert -local command = helpers.command -local write_file = helpers.write_file -local expect_exit = helpers.expect_exit -local mkdir = helpers.mkdir +local eq = t.eq +local neq = t.neq +local poke_eventloop = n.poke_eventloop +local fn = n.fn +local api = n.api +local clear = n.clear +local insert = n.insert +local command = n.command +local write_file = t.write_file +local expect_exit = n.expect_exit +local mkdir = t.mkdir local function ls_dir_sorted(dirname) local files = {} @@ -44,8 +45,8 @@ describe("'directory' option", function() end) teardown(function() expect_exit(command, 'qall!') - helpers.rmdir('Xtest.je') - helpers.rmdir('Xtest2') + n.rmdir('Xtest.je') + n.rmdir('Xtest2') os.remove('Xtest1') end) diff --git a/test/functional/legacy/015_alignment_spec.lua b/test/functional/legacy/015_alignment_spec.lua index d73ff06972..ac90f3f98b 100644 --- a/test/functional/legacy/015_alignment_spec.lua +++ b/test/functional/legacy/015_alignment_spec.lua @@ -2,9 +2,10 @@ -- Also test formatting a paragraph. -- Also test undo after ":%s" and formatting. -local helpers = require('test.functional.helpers')(after_each) -local feed, insert = helpers.feed, helpers.insert -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, insert = n.feed, n.insert +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('alignment', function() setup(clear) diff --git a/test/functional/legacy/018_unset_smart_indenting_spec.lua b/test/functional/legacy/018_unset_smart_indenting_spec.lua index 94fbb283f4..851b3264d3 100644 --- a/test/functional/legacy/018_unset_smart_indenting_spec.lua +++ b/test/functional/legacy/018_unset_smart_indenting_spec.lua @@ -1,12 +1,12 @@ -- Tests for not doing smart indenting when it isn't set. -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() -local feed = helpers.feed -local clear = helpers.clear -local insert = helpers.insert -local expect = helpers.expect -local feed_command = helpers.feed_command +local feed = n.feed +local clear = n.clear +local insert = n.insert +local expect = n.expect +local feed_command = n.feed_command describe('unset smart indenting', function() before_each(clear) diff --git a/test/functional/legacy/019_smarttab_expandtab_spec.lua b/test/functional/legacy/019_smarttab_expandtab_spec.lua index 7b03ee8e99..3bdc76c220 100644 --- a/test/functional/legacy/019_smarttab_expandtab_spec.lua +++ b/test/functional/legacy/019_smarttab_expandtab_spec.lua @@ -1,9 +1,10 @@ -- Tests for "r<Tab>" with 'smarttab' and 'expandtab' set/not set. -- Also test that dv_ works correctly -local helpers = require('test.functional.helpers')(after_each) -local feed, insert = helpers.feed, helpers.insert -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, insert = n.feed, n.insert +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe([[performing "r<Tab>" with 'smarttab' and 'expandtab' set/not set, and "dv_"]], function() setup(clear) diff --git a/test/functional/legacy/020_blockwise_visual_spec.lua b/test/functional/legacy/020_blockwise_visual_spec.lua index 8d90b1c77d..f8df3dbf7e 100644 --- a/test/functional/legacy/020_blockwise_visual_spec.lua +++ b/test/functional/legacy/020_blockwise_visual_spec.lua @@ -2,9 +2,10 @@ -- First test for undo working properly when executing commands from a register. -- Also test this in an empty buffer. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('blockwise visual', function() setup(clear) diff --git a/test/functional/legacy/021_control_wi_spec.lua b/test/functional/legacy/021_control_wi_spec.lua index 94871433cd..64dbc47201 100644 --- a/test/functional/legacy/021_control_wi_spec.lua +++ b/test/functional/legacy/021_control_wi_spec.lua @@ -1,8 +1,9 @@ -- Tests for [ CTRL-I with a count and CTRL-W CTRL-I with a count -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('CTRL-W CTRL-I', function() setup(clear) diff --git a/test/functional/legacy/023_edit_arguments_spec.lua b/test/functional/legacy/023_edit_arguments_spec.lua index 64b2f6fa95..97dc5b0153 100644 --- a/test/functional/legacy/023_edit_arguments_spec.lua +++ b/test/functional/legacy/023_edit_arguments_spec.lua @@ -1,9 +1,10 @@ -- Tests for complicated + argument to :edit command -local helpers = require('test.functional.helpers')(after_each) -local clear, insert = helpers.clear, helpers.insert -local command, expect = helpers.command, helpers.expect -local poke_eventloop = helpers.poke_eventloop +local n = require('test.functional.testnvim')() + +local clear, insert = n.clear, n.insert +local command, expect = n.command, n.expect +local poke_eventloop = n.poke_eventloop describe(':edit', function() setup(clear) diff --git a/test/functional/legacy/025_jump_tag_hidden_spec.lua b/test/functional/legacy/025_jump_tag_hidden_spec.lua index 33bab05404..b7090662e6 100644 --- a/test/functional/legacy/025_jump_tag_hidden_spec.lua +++ b/test/functional/legacy/025_jump_tag_hidden_spec.lua @@ -1,9 +1,11 @@ -- Test for jumping to a tag with 'hidden' set, with symbolic link in path of tag. -- This only works for Unix, because of the symbolic link. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('jump to a tag with hidden set', function() setup(clear) @@ -23,7 +25,7 @@ describe('jump to a tag with hidden set', function() feed_command('set hidden') -- Create a link from test25.dir to the current directory. - if helpers.is_os('win') then + if t.is_os('win') then feed_command('!rd /q/s test25.dir') feed_command('!mklink /j test25.dir .') else @@ -33,7 +35,7 @@ describe('jump to a tag with hidden set', function() -- Create tags.text, with the current directory name inserted. feed_command('/tags line') - feed_command('r !' .. (helpers.is_os('win') and 'cd' or 'pwd')) + feed_command('r !' .. (t.is_os('win') and 'cd' or 'pwd')) feed('d$/test<cr>') feed('hP:.w! tags.test<cr>') @@ -44,7 +46,7 @@ describe('jump to a tag with hidden set', function() feed('G<C-]> x:yank a<cr>') feed_command("call delete('tags.test')") feed_command("call delete('Xxx')") - if helpers.is_os('win') then + if t.is_os('win') then feed_command('!rd /q test25.dir') else feed_command('!rm -f test25.dir') diff --git a/test/functional/legacy/026_execute_while_if_spec.lua b/test/functional/legacy/026_execute_while_if_spec.lua index ea8abed7ae..143985c726 100644 --- a/test/functional/legacy/026_execute_while_if_spec.lua +++ b/test/functional/legacy/026_execute_while_if_spec.lua @@ -1,11 +1,11 @@ -- Test for :execute, :while and :if -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local expect = helpers.expect -local source = helpers.source -local command = helpers.command +local clear = n.clear +local expect = n.expect +local source = n.source +local command = n.command describe(':execute, :while and :if', function() setup(clear) diff --git a/test/functional/legacy/028_source_ctrl_v_spec.lua b/test/functional/legacy/028_source_ctrl_v_spec.lua index fabf831341..17d4e7cc2e 100644 --- a/test/functional/legacy/028_source_ctrl_v_spec.lua +++ b/test/functional/legacy/028_source_ctrl_v_spec.lua @@ -1,8 +1,9 @@ -- Test for sourcing a file with CTRL-V's at the end of the line -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('CTRL-V at the end of the line', function() setup(clear) diff --git a/test/functional/legacy/029_join_spec.lua b/test/functional/legacy/029_join_spec.lua index c808a21afc..8d4cc70d8f 100644 --- a/test/functional/legacy/029_join_spec.lua +++ b/test/functional/legacy/029_join_spec.lua @@ -1,12 +1,12 @@ -- Test for joining lines with marks in them (and with 'joinspaces' set/reset) -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() -local feed = helpers.feed -local clear = helpers.clear -local insert = helpers.insert -local expect = helpers.expect -local feed_command = helpers.feed_command +local feed = n.feed +local clear = n.clear +local insert = n.insert +local expect = n.expect +local feed_command = n.feed_command describe('joining lines', function() before_each(clear) diff --git a/test/functional/legacy/030_fileformats_spec.lua b/test/functional/legacy/030_fileformats_spec.lua index e88afd9c47..1a44ed6cb6 100644 --- a/test/functional/legacy/030_fileformats_spec.lua +++ b/test/functional/legacy/030_fileformats_spec.lua @@ -1,9 +1,11 @@ -- Test for a lot of variations of the 'fileformats' option -local helpers = require('test.functional.helpers')(after_each) -local feed, clear, command = helpers.feed, helpers.clear, helpers.command -local eq, write_file = helpers.eq, helpers.write_file -local poke_eventloop = helpers.poke_eventloop +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local feed, clear, command = n.feed, n.clear, n.command +local eq, write_file = t.eq, t.write_file +local poke_eventloop = n.poke_eventloop describe('fileformats option', function() setup(function() @@ -254,7 +256,7 @@ describe('fileformats option', function() command('set nobinary ff&') -- Assert buffer contents. This has to be done manually as - -- helpers.expect() calls helpers.dedent() which messes up the white space + -- n.expect() calls t.dedent() which messes up the white space -- and carriage returns. eq( 'unix\n'.. @@ -387,6 +389,6 @@ describe('fileformats option', function() '10\n'.. 'unix\n'.. 'unix', - helpers.curbuf_contents()) + n.curbuf_contents()) end) end) diff --git a/test/functional/legacy/031_close_commands_spec.lua b/test/functional/legacy/031_close_commands_spec.lua index 173ebf1cf4..974df0c21d 100644 --- a/test/functional/legacy/031_close_commands_spec.lua +++ b/test/functional/legacy/031_close_commands_spec.lua @@ -9,15 +9,15 @@ -- :buf -- :edit -local helpers = require('test.functional.helpers')(after_each) - -local feed = helpers.feed -local clear = helpers.clear -local source = helpers.source -local insert = helpers.insert -local expect = helpers.expect -local feed_command = helpers.feed_command -local expect_exit = helpers.expect_exit +local n = require('test.functional.testnvim')() + +local feed = n.feed +local clear = n.clear +local source = n.source +local insert = n.insert +local expect = n.expect +local feed_command = n.feed_command +local expect_exit = n.expect_exit describe('Commands that close windows and/or buffers', function() local function cleanup() diff --git a/test/functional/legacy/033_lisp_indent_spec.lua b/test/functional/legacy/033_lisp_indent_spec.lua index b27de6c16d..e3d06be6bf 100644 --- a/test/functional/legacy/033_lisp_indent_spec.lua +++ b/test/functional/legacy/033_lisp_indent_spec.lua @@ -1,10 +1,11 @@ -- Test for 'lisp' -- If the lisp feature is not enabled, this will fail! -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command, expect = helpers.command, helpers.expect -local poke_eventloop = helpers.poke_eventloop +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local command, expect = n.command, n.expect +local poke_eventloop = n.poke_eventloop describe('lisp indent', function() setup(clear) diff --git a/test/functional/legacy/034_user_function_spec.lua b/test/functional/legacy/034_user_function_spec.lua index c30c7275f2..89bf3b8920 100644 --- a/test/functional/legacy/034_user_function_spec.lua +++ b/test/functional/legacy/034_user_function_spec.lua @@ -3,9 +3,10 @@ -- Also test that a builtin function cannot be replaced. -- Also test for regression when calling arbitrary expression. -local helpers = require('test.functional.helpers')(after_each) -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, insert, source = n.feed, n.insert, n.source +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe( 'user functions, expr-mappings, overwrite protected builtin functions and regression on calling expressions', diff --git a/test/functional/legacy/035_increment_and_decrement_spec.lua b/test/functional/legacy/035_increment_and_decrement_spec.lua index 84eb9c0eee..b6486a8a27 100644 --- a/test/functional/legacy/035_increment_and_decrement_spec.lua +++ b/test/functional/legacy/035_increment_and_decrement_spec.lua @@ -1,9 +1,10 @@ -- Test Ctrl-A and Ctrl-X, which increment and decrement decimal, hexadecimal, -- and octal numbers. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('increment and decrement commands', function() setup(clear) diff --git a/test/functional/legacy/036_regexp_character_classes_spec.lua b/test/functional/legacy/036_regexp_character_classes_spec.lua index ed35b2b245..9c871e159c 100644 --- a/test/functional/legacy/036_regexp_character_classes_spec.lua +++ b/test/functional/legacy/036_regexp_character_classes_spec.lua @@ -1,8 +1,10 @@ -- Test character classes in regexp using regexpengine 0, 1, 2. -local helpers = require('test.functional.helpers')(after_each) -local clear, command, expect = helpers.clear, helpers.command, helpers.expect -local source, write_file = helpers.source, helpers.write_file +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, command, expect = n.clear, n.command, n.expect +local source, write_file = n.source, t.write_file local function sixlines(text) local result = '' @@ -13,14 +15,14 @@ local function sixlines(text) end local function diff(text, nodedent) - local fname = helpers.tmpname() + local fname = t.tmpname() command('w! ' .. fname) - helpers.poke_eventloop() + n.poke_eventloop() local data = io.open(fname):read('*all') if nodedent then - helpers.eq(text, data) + t.eq(text, data) else - helpers.eq(helpers.dedent(text), data) + t.eq(t.dedent(text), data) end os.remove(fname) end diff --git a/test/functional/legacy/038_virtual_replace_spec.lua b/test/functional/legacy/038_virtual_replace_spec.lua index 2f85e7d5a3..b2edc5933f 100644 --- a/test/functional/legacy/038_virtual_replace_spec.lua +++ b/test/functional/legacy/038_virtual_replace_spec.lua @@ -1,8 +1,9 @@ -- Test Virtual replace mode. -local helpers = require('test.functional.helpers')(after_each) -local feed = helpers.feed -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed = n.feed +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('Virtual replace mode', function() setup(clear) diff --git a/test/functional/legacy/039_visual_block_mode_commands_spec.lua b/test/functional/legacy/039_visual_block_mode_commands_spec.lua index bc3fea765c..9fbf5ae774 100644 --- a/test/functional/legacy/039_visual_block_mode_commands_spec.lua +++ b/test/functional/legacy/039_visual_block_mode_commands_spec.lua @@ -1,11 +1,13 @@ -- Test Visual block mode commands -- And test "U" in Visual mode, also on German sharp S. -local helpers = require('test.functional.helpers')(after_each) -local nvim, eq = helpers.api, helpers.eq -local insert, feed = helpers.insert, helpers.feed -local clear, expect = helpers.clear, helpers.expect -local feed_command = helpers.feed_command +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local api, eq = n.api, t.eq +local insert, feed = n.insert, n.feed +local clear, expect = n.clear, n.expect +local feed_command = n.feed_command describe('Visual block mode', function() before_each(function() @@ -204,7 +206,7 @@ describe('Visual block mode', function() feed('G2l') feed('2k<C-v>$gj<ESC>') feed_command([[let cpos=getpos("'>")]]) - local cpos = nvim.nvim_get_var('cpos') + local cpos = api.nvim_get_var('cpos') local expected = { col = 4, off = 0, diff --git a/test/functional/legacy/043_magic_settings_spec.lua b/test/functional/legacy/043_magic_settings_spec.lua index a88ccc2b42..ba4c2dab02 100644 --- a/test/functional/legacy/043_magic_settings_spec.lua +++ b/test/functional/legacy/043_magic_settings_spec.lua @@ -1,8 +1,9 @@ -- Tests for regexp with various magic settings. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('regexp with magic settings', function() setup(clear) diff --git a/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua b/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua index 074ee094b4..99cb64c7e5 100644 --- a/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua +++ b/test/functional/legacy/044_099_regexp_multibyte_magic_spec.lua @@ -3,9 +3,10 @@ -- -- This test contains both "test44" and "test99" from the old test suite. -local helpers = require('test.functional.helpers')(after_each) -local feed, insert = helpers.feed, helpers.insert -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, insert = n.feed, n.insert +local clear, feed_command, expect = n.clear, n.feed_command, n.expect -- Runs the test protocol with the given 'regexpengine' setting. In the old test -- suite the test protocol was duplicated in test44 and test99, the only diff --git a/test/functional/legacy/046_multi_line_regexps_spec.lua b/test/functional/legacy/046_multi_line_regexps_spec.lua index 30ec76ea3e..a103b55581 100644 --- a/test/functional/legacy/046_multi_line_regexps_spec.lua +++ b/test/functional/legacy/046_multi_line_regexps_spec.lua @@ -1,9 +1,10 @@ -- vim: set foldmethod=marker foldmarker=[[,]] : -- Tests for multi-line regexps with ":s" -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local expect = helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local expect = n.expect describe('multi-line regexp', function() setup(clear) diff --git a/test/functional/legacy/054_buffer_local_autocommands_spec.lua b/test/functional/legacy/054_buffer_local_autocommands_spec.lua index c8b9dfa98f..afde6f220a 100644 --- a/test/functional/legacy/054_buffer_local_autocommands_spec.lua +++ b/test/functional/legacy/054_buffer_local_autocommands_spec.lua @@ -1,10 +1,10 @@ -- Some tests for buffer-local autocommands -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local expect = helpers.expect -local command = helpers.command +local clear = n.clear +local expect = n.expect +local command = n.command local fname = 'Xtest-functional-legacy-054' diff --git a/test/functional/legacy/055_list_and_dict_types_spec.lua b/test/functional/legacy/055_list_and_dict_types_spec.lua index e8ae60e350..21d6b07a2d 100644 --- a/test/functional/legacy/055_list_and_dict_types_spec.lua +++ b/test/functional/legacy/055_list_and_dict_types_spec.lua @@ -1,8 +1,9 @@ -- Tests for List and Dictionary types. -local helpers = require('test.functional.helpers')(after_each) -local feed, source = helpers.feed, helpers.source -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, source = n.feed, n.source +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('list and dictionary types', function() before_each(clear) diff --git a/test/functional/legacy/056_script_local_function_spec.lua b/test/functional/legacy/056_script_local_function_spec.lua index 084817ad7a..dd99224edb 100644 --- a/test/functional/legacy/056_script_local_function_spec.lua +++ b/test/functional/legacy/056_script_local_function_spec.lua @@ -1,9 +1,10 @@ -- vim: set foldmethod=marker foldmarker=[[,]] : -- Test for script-local function. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local expect = helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local expect = n.expect describe('source function', function() setup(clear) diff --git a/test/functional/legacy/057_sort_spec.lua b/test/functional/legacy/057_sort_spec.lua index 9b4746591f..8b0664d50c 100644 --- a/test/functional/legacy/057_sort_spec.lua +++ b/test/functional/legacy/057_sort_spec.lua @@ -1,10 +1,11 @@ -- Tests for :sort command. -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local insert, command, clear, expect, eq, poke_eventloop = - helpers.insert, helpers.command, helpers.clear, helpers.expect, helpers.eq, helpers.poke_eventloop -local exc_exec = helpers.exc_exec + n.insert, n.command, n.clear, n.expect, t.eq, n.poke_eventloop +local exc_exec = n.exc_exec describe(':sort', function() local text = [[ diff --git a/test/functional/legacy/060_exists_and_has_functions_spec.lua b/test/functional/legacy/060_exists_and_has_functions_spec.lua index 82fece3e84..ec29b6cf04 100644 --- a/test/functional/legacy/060_exists_and_has_functions_spec.lua +++ b/test/functional/legacy/060_exists_and_has_functions_spec.lua @@ -1,9 +1,11 @@ -- Tests for the exists() and has() functions. -local helpers = require('test.functional.helpers')(after_each) -local source = helpers.source -local clear, expect = helpers.clear, helpers.expect -local write_file = helpers.write_file +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local source = n.source +local clear, expect = n.clear, n.expect +local write_file = t.write_file describe('exists() and has() functions', function() setup(function() diff --git a/test/functional/legacy/061_undo_tree_spec.lua b/test/functional/legacy/061_undo_tree_spec.lua index b5af8f7d52..4177f908a1 100644 --- a/test/functional/legacy/061_undo_tree_spec.lua +++ b/test/functional/legacy/061_undo_tree_spec.lua @@ -1,18 +1,19 @@ -- Tests for undo tree and :earlier and :later. -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local feed_command = helpers.feed_command -local write_file = helpers.write_file -local command = helpers.command -local source = helpers.source -local expect = helpers.expect -local clear = helpers.clear -local feed = helpers.feed -local eval = helpers.eval -local eq = helpers.eq +local feed_command = n.feed_command +local write_file = t.write_file +local command = n.command +local source = n.source +local expect = n.expect +local clear = n.clear +local feed = n.feed +local eval = n.eval +local eq = t.eq local function expect_empty_buffer() - -- The space will be removed by helpers.dedent but is needed because dedent + -- The space will be removed by t.dedent but is needed because dedent -- will fail if it can not find the common indent of the given lines. return expect(' ') end @@ -99,7 +100,7 @@ describe('undo tree:', function() expect_line('123456abc') end - helpers.retry(2, nil, test_earlier_later) + t.retry(2, nil, test_earlier_later) end) it('file-write specifications', function() diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua index 0c2b59932b..7515bcf182 100644 --- a/test/functional/legacy/063_match_and_matchadd_spec.lua +++ b/test/functional/legacy/063_match_and_matchadd_spec.lua @@ -1,10 +1,10 @@ -- Tests for adjusting window and contents -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, command = helpers.clear, helpers.command -local insert = helpers.insert +local clear, command = n.clear, n.command +local insert = n.insert describe('063: Test for ":match", "matchadd()" and related functions', function() setup(clear) @@ -12,10 +12,6 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( it('is working', function() local screen = Screen.new(40, 5) screen:attach() - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, - [1] = { background = Screen.colors.Red }, - }) command('highlight MyGroup1 term=bold ctermbg=red guibg=red') command('highlight MyGroup2 term=italic ctermbg=green guibg=green') @@ -25,8 +21,8 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( insert('abcdefghijklmnopq') command("call matchaddpos('MyGroup1', [[1, 5], [1, 8, 3]], 10, 3)") screen:expect([[ - abcd{1:e}fg{1:hij}klmnop^q | - {0:~ }|*3 + abcd{30:e}fg{30:hij}klmnop^q | + {1:~ }|*3 | ]]) @@ -34,8 +30,8 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( command("call setline(1, 'abcdΣabcdef')") command("call matchaddpos('MyGroup1', [[1, 4, 2], [1, 9, 2]])") screen:expect([[ - abc{1:dΣ}ab{1:cd}e^f | - {0:~ }|*3 + abc{30:dΣ}ab{30:cd}e^f | + {1:~ }|*3 | ]]) end) diff --git a/test/functional/legacy/065_float_and_logic_operators_spec.lua b/test/functional/legacy/065_float_and_logic_operators_spec.lua index ad1b004085..0835a3df52 100644 --- a/test/functional/legacy/065_float_and_logic_operators_spec.lua +++ b/test/functional/legacy/065_float_and_logic_operators_spec.lua @@ -1,8 +1,9 @@ -- Test for floating point and logical operators. -local helpers = require('test.functional.helpers')(after_each) -local insert, source = helpers.insert, helpers.source -local clear, expect = helpers.clear, helpers.expect +local n = require('test.functional.testnvim')() + +local insert, source = n.insert, n.source +local clear, expect = n.clear, n.expect describe('floating point and logical operators', function() setup(clear) diff --git a/test/functional/legacy/066_visual_block_tab_spec.lua b/test/functional/legacy/066_visual_block_tab_spec.lua index f10152d8ea..6e92bd4c65 100644 --- a/test/functional/legacy/066_visual_block_tab_spec.lua +++ b/test/functional/legacy/066_visual_block_tab_spec.lua @@ -1,9 +1,10 @@ -- vim: set foldmethod=marker foldmarker=[[,]] : -- Test for visual block shift and tab characters. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('visual block shift and tab characters', function() setup(clear) diff --git a/test/functional/legacy/067_augroup_exists_spec.lua b/test/functional/legacy/067_augroup_exists_spec.lua index 4a77bf838a..82b9c4bfa3 100644 --- a/test/functional/legacy/067_augroup_exists_spec.lua +++ b/test/functional/legacy/067_augroup_exists_spec.lua @@ -1,9 +1,10 @@ -- Test that groups and patterns are tested correctly when calling exists() for -- autocommands. -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command, expect = helpers.command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command, expect = n.command, n.expect describe('augroup when calling exists()', function() setup(clear) diff --git a/test/functional/legacy/068_text_formatting_spec.lua b/test/functional/legacy/068_text_formatting_spec.lua index 3a1b21bf87..179c15fb0f 100644 --- a/test/functional/legacy/068_text_formatting_spec.lua +++ b/test/functional/legacy/068_text_formatting_spec.lua @@ -1,10 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() -local feed = helpers.feed -local clear = helpers.clear -local insert = helpers.insert -local feed_command = helpers.feed_command -local expect = helpers.expect +local feed = n.feed +local clear = n.clear +local insert = n.insert +local feed_command = n.feed_command +local expect = n.expect describe('text formatting', function() setup(clear) diff --git a/test/functional/legacy/069_multibyte_formatting_spec.lua b/test/functional/legacy/069_multibyte_formatting_spec.lua index 05e6aa1435..24a4692e6c 100644 --- a/test/functional/legacy/069_multibyte_formatting_spec.lua +++ b/test/functional/legacy/069_multibyte_formatting_spec.lua @@ -3,15 +3,11 @@ -- And test "ra" on multibyte characters. -- Also test byteidx() and byteidxcomp() -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + local feed, insert, eq, eval, clear, feed_command, expect = - helpers.feed, - helpers.insert, - helpers.eq, - helpers.eval, - helpers.clear, - helpers.feed_command, - helpers.expect + n.feed, n.insert, t.eq, n.eval, n.clear, n.feed_command, n.expect describe('multibyte text', function() before_each(clear) diff --git a/test/functional/legacy/072_undo_file_spec.lua b/test/functional/legacy/072_undo_file_spec.lua index 80665027c3..f7185dacff 100644 --- a/test/functional/legacy/072_undo_file_spec.lua +++ b/test/functional/legacy/072_undo_file_spec.lua @@ -2,9 +2,10 @@ -- Since this script is sourced we need to explicitly break changes up in -- undo-able pieces. Do that by setting 'undolevels'. -local helpers = require('test.functional.helpers')(after_each) -local feed, insert = helpers.feed, helpers.insert -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, insert = n.feed, n.insert +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('72', function() setup(clear) diff --git a/test/functional/legacy/074_global_var_in_viminfo_spec.lua b/test/functional/legacy/074_global_var_in_viminfo_spec.lua index 0a9ad330c2..d56a2654e3 100644 --- a/test/functional/legacy/074_global_var_in_viminfo_spec.lua +++ b/test/functional/legacy/074_global_var_in_viminfo_spec.lua @@ -1,8 +1,10 @@ -- Tests for storing global variables in the .shada file -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + local clear, command, eq, neq, eval, poke_eventloop = - helpers.clear, helpers.command, helpers.eq, helpers.neq, helpers.eval, helpers.poke_eventloop + n.clear, n.command, t.eq, t.neq, n.eval, n.poke_eventloop describe('storing global variables in ShaDa files', function() local tempname = 'Xtest-functional-legacy-074' diff --git a/test/functional/legacy/078_swapfile_recover_spec.lua b/test/functional/legacy/078_swapfile_recover_spec.lua index dc5b1a8540..fa53482393 100644 --- a/test/functional/legacy/078_swapfile_recover_spec.lua +++ b/test/functional/legacy/078_swapfile_recover_spec.lua @@ -3,8 +3,9 @@ -- restored. We need about 10000 lines of 100 characters to get two levels of -- pointer blocks. -local helpers = require('test.functional.helpers')(after_each) -local clear, expect, source = helpers.clear, helpers.expect, helpers.source +local n = require('test.functional.testnvim')() + +local clear, expect, source = n.clear, n.expect, n.source describe('78', function() setup(clear) diff --git a/test/functional/legacy/081_coptions_movement_spec.lua b/test/functional/legacy/081_coptions_movement_spec.lua index d82c46a3d3..cfa65f5c8a 100644 --- a/test/functional/legacy/081_coptions_movement_spec.lua +++ b/test/functional/legacy/081_coptions_movement_spec.lua @@ -1,8 +1,9 @@ -- Test for t movement command and 'cpo-;' setting -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('coptions', function() setup(clear) diff --git a/test/functional/legacy/082_string_comparison_spec.lua b/test/functional/legacy/082_string_comparison_spec.lua index 311822c34f..3d91dc4b34 100644 --- a/test/functional/legacy/082_string_comparison_spec.lua +++ b/test/functional/legacy/082_string_comparison_spec.lua @@ -1,9 +1,10 @@ -- Tests for case-insensitive UTF-8 comparisons (utf_strnicmp() in mbyte.c) -- Also test "g~ap". -local helpers = require('test.functional.helpers')(after_each) -local feed, source = helpers.feed, helpers.source -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, source = n.feed, n.source +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('case-insensitive string comparison in UTF-8', function() setup(clear) diff --git a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua index 5e9c131c64..05154f660d 100644 --- a/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua +++ b/test/functional/legacy/083_tag_search_with_file_encoding_spec.lua @@ -1,12 +1,14 @@ -- Tests for tag search with !_TAG_FILE_ENCODING. -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + local insert, source, clear, expect, write_file = - helpers.insert, helpers.source, helpers.clear, helpers.expect, helpers.write_file + n.insert, n.source, n.clear, n.expect, t.write_file local function has_iconv() clear() -- ensures session - return 1 == helpers.eval('has("iconv")') + return 1 == n.eval('has("iconv")') end describe('tag search with !_TAG_FILE_ENCODING', function() @@ -31,7 +33,7 @@ describe('tag search with !_TAG_FILE_ENCODING', function() ) -- The last file is very long but repetitive and can be generated on the -- fly. - local text = helpers.dedent([[ + local text = t.dedent([[ !_TAG_FILE_SORTED 1 // !_TAG_FILE_ENCODING cp932 // ]]) diff --git a/test/functional/legacy/090_sha256_spec.lua b/test/functional/legacy/090_sha256_spec.lua index 701b777df1..648eb65550 100644 --- a/test/functional/legacy/090_sha256_spec.lua +++ b/test/functional/legacy/090_sha256_spec.lua @@ -1,8 +1,9 @@ -- Tests for sha256() function. -local helpers = require('test.functional.helpers')(after_each) -local insert, source = helpers.insert, helpers.source -local clear, expect = helpers.clear, helpers.expect +local n = require('test.functional.testnvim')() + +local insert, source = n.insert, n.source +local clear, expect = n.clear, n.expect describe('sha256()', function() setup(clear) diff --git a/test/functional/legacy/091_context_variables_spec.lua b/test/functional/legacy/091_context_variables_spec.lua index 3b9fdf740f..cc3ee6787a 100644 --- a/test/functional/legacy/091_context_variables_spec.lua +++ b/test/functional/legacy/091_context_variables_spec.lua @@ -1,8 +1,9 @@ -- Tests for getbufvar(), getwinvar(), gettabvar() and gettabwinvar(). -local helpers = require('test.functional.helpers')(after_each) -local insert, source = helpers.insert, helpers.source -local clear, expect = helpers.clear, helpers.expect +local n = require('test.functional.testnvim')() + +local insert, source = n.insert, n.source +local clear, expect = n.clear, n.expect describe('context variables', function() setup(clear) diff --git a/test/functional/legacy/092_mksession_cursor_cols_utf8_spec.lua b/test/functional/legacy/092_mksession_cursor_cols_utf8_spec.lua index 3c46c29951..d478545077 100644 --- a/test/functional/legacy/092_mksession_cursor_cols_utf8_spec.lua +++ b/test/functional/legacy/092_mksession_cursor_cols_utf8_spec.lua @@ -3,9 +3,10 @@ -- -- Same as legacy test 93 but using UTF-8 file encoding. -local helpers = require('test.functional.helpers')(after_each) -local feed, insert = helpers.feed, helpers.insert -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, insert = n.feed, n.insert +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('store cursor position in session file in UTF-8', function() setup(clear) diff --git a/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua b/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua index f09fd9a6e5..5e5e465c01 100644 --- a/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua +++ b/test/functional/legacy/093_mksession_cursor_cols_latin1_spec.lua @@ -3,9 +3,10 @@ -- -- Same as legacy test 92 but using Latin-1 file encoding. -local helpers = require('test.functional.helpers')(after_each) -local feed, insert = helpers.feed, helpers.insert -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, insert = n.feed, n.insert +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('store cursor position in session file in Latin-1', function() setup(clear) diff --git a/test/functional/legacy/094_visual_mode_operators_spec.lua b/test/functional/legacy/094_visual_mode_operators_spec.lua index ff1d3e7bec..279c6a432a 100644 --- a/test/functional/legacy/094_visual_mode_operators_spec.lua +++ b/test/functional/legacy/094_visual_mode_operators_spec.lua @@ -4,9 +4,10 @@ -- followed by an operator and those executed via Operator-pending mode. Also -- part of the test are mappings, counts, and repetition with the . command. -local helpers = require('test.functional.helpers')(after_each) -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, insert, source = n.feed, n.insert, n.source +local clear, feed_command, expect = n.clear, n.feed_command, n.expect -- Vim script user functions needed for some of the test cases. local function source_user_functions() diff --git a/test/functional/legacy/095_regexp_multibyte_spec.lua b/test/functional/legacy/095_regexp_multibyte_spec.lua index fad0dc8023..296bf00b58 100644 --- a/test/functional/legacy/095_regexp_multibyte_spec.lua +++ b/test/functional/legacy/095_regexp_multibyte_spec.lua @@ -3,9 +3,10 @@ -- A pattern that gives the expected result produces OK, so that we know it was -- actually tried. -local helpers = require('test.functional.helpers')(after_each) -local insert, source = helpers.insert, helpers.source -local clear, expect = helpers.clear, helpers.expect +local n = require('test.functional.testnvim')() + +local insert, source = n.insert, n.source +local clear, expect = n.clear, n.expect describe('regex with multi-byte', function() setup(clear) diff --git a/test/functional/legacy/096_location_list_spec.lua b/test/functional/legacy/096_location_list_spec.lua index 2817d5d240..27bbe706bb 100644 --- a/test/functional/legacy/096_location_list_spec.lua +++ b/test/functional/legacy/096_location_list_spec.lua @@ -6,9 +6,10 @@ -- C. make sure that the location list window is not reused instead of the window -- it belongs to. -local helpers = require('test.functional.helpers')(after_each) -local source = helpers.source -local clear, command, expect = helpers.clear, helpers.command, helpers.expect +local n = require('test.functional.testnvim')() + +local source = n.source +local clear, command, expect = n.clear, n.command, n.expect describe('location list', function() local test_file = 'Xtest-096_location_list.out' diff --git a/test/functional/legacy/097_glob_path_spec.lua b/test/functional/legacy/097_glob_path_spec.lua index b335b3bf41..4d4bb9e78f 100644 --- a/test/functional/legacy/097_glob_path_spec.lua +++ b/test/functional/legacy/097_glob_path_spec.lua @@ -2,15 +2,17 @@ -- Test whether glob()/globpath() return correct results with certain escaped -- characters. -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command, expect = helpers.command, helpers.expect +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command, expect = n.command, n.expect describe('glob() and globpath()', function() setup(clear) setup(function() - if helpers.is_os('win') then + if t.is_os('win') then os.execute('md sautest\\autoload') os.execute('.>sautest\\autoload\\Test104.vim 2>nul') os.execute('.>sautest\\autoload\\footest.vim 2>nul') @@ -28,7 +30,7 @@ describe('glob() and globpath()', function() -- Consistent sorting of file names command('set nofileignorecase') - if helpers.is_os('win') then + if t.is_os('win') then command([[$put =glob('Xxx{')]]) command([[$put =glob('Xxx$')]]) @@ -72,7 +74,7 @@ describe('glob() and globpath()', function() end) teardown(function() - if helpers.is_os('win') then + if t.is_os('win') then os.execute('del /q/f Xxx{ Xxx$') os.execute('rd /q /s sautest') else diff --git a/test/functional/legacy/101_hlsearch_spec.lua b/test/functional/legacy/101_hlsearch_spec.lua index eff755221c..88f1c31b23 100644 --- a/test/functional/legacy/101_hlsearch_spec.lua +++ b/test/functional/legacy/101_hlsearch_spec.lua @@ -1,8 +1,9 @@ -- Test for v:hlsearch -local helpers = require('test.functional.helpers')(after_each) -local clear, feed = helpers.clear, helpers.feed -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed = n.clear, n.feed +local feed_command, expect = n.feed_command, n.expect describe('v:hlsearch', function() setup(clear) diff --git a/test/functional/legacy/102_fnameescape_spec.lua b/test/functional/legacy/102_fnameescape_spec.lua index 11bdbd7c9c..f0361c2f5a 100644 --- a/test/functional/legacy/102_fnameescape_spec.lua +++ b/test/functional/legacy/102_fnameescape_spec.lua @@ -1,8 +1,9 @@ -- Test if fnameescape is correct for special chars like! -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command, expect = helpers.command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command, expect = n.command, n.expect describe('fnameescape', function() setup(clear) diff --git a/test/functional/legacy/103_visual_mode_reset_spec.lua b/test/functional/legacy/103_visual_mode_reset_spec.lua index f5cd861019..b6fdf8cc68 100644 --- a/test/functional/legacy/103_visual_mode_reset_spec.lua +++ b/test/functional/legacy/103_visual_mode_reset_spec.lua @@ -1,8 +1,9 @@ -- Test for visual mode not being reset causing E315 error. -local helpers = require('test.functional.helpers')(after_each) -local feed, source = helpers.feed, helpers.source -local clear, expect = helpers.clear, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, source = n.feed, n.source +local clear, expect = n.clear, n.expect describe('E315 error', function() setup(clear) diff --git a/test/functional/legacy/106_errorformat_spec.lua b/test/functional/legacy/106_errorformat_spec.lua index 2a83d48c07..4189b568a2 100644 --- a/test/functional/legacy/106_errorformat_spec.lua +++ b/test/functional/legacy/106_errorformat_spec.lua @@ -1,8 +1,9 @@ -- Tests for errorformat. -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command, expect = helpers.command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command, expect = n.command, n.expect describe('errorformat', function() setup(clear) diff --git a/test/functional/legacy/107_adjust_window_and_contents_spec.lua b/test/functional/legacy/107_adjust_window_and_contents_spec.lua index 5a8fdda32d..57ecf76f3a 100644 --- a/test/functional/legacy/107_adjust_window_and_contents_spec.lua +++ b/test/functional/legacy/107_adjust_window_and_contents_spec.lua @@ -1,12 +1,12 @@ -- Tests for adjusting window and contents -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local poke_eventloop = helpers.poke_eventloop -local clear = helpers.clear -local insert = helpers.insert -local command = helpers.command +local poke_eventloop = n.poke_eventloop +local clear = n.clear +local insert = n.insert +local command = n.command describe('107', function() setup(clear) @@ -50,7 +50,7 @@ describe('107', function() [1, '1 '] | [50, '50 '] | ^[59, '59 '] | - ~ |*9 + {1:~ }|*9 3 more lines | ]]) end) diff --git a/test/functional/legacy/108_backtrace_debug_commands_spec.lua b/test/functional/legacy/108_backtrace_debug_commands_spec.lua index ccdd0cd2be..d2ae785813 100644 --- a/test/functional/legacy/108_backtrace_debug_commands_spec.lua +++ b/test/functional/legacy/108_backtrace_debug_commands_spec.lua @@ -1,9 +1,10 @@ -- Tests for backtrace debug commands. -local helpers = require('test.functional.helpers')(after_each) -local command = helpers.command -local feed, clear = helpers.feed, helpers.clear -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local command = n.command +local feed, clear = n.feed, n.clear +local feed_command, expect = n.feed_command, n.expect describe('108', function() before_each(clear) diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index 3e78d7ca5f..ac2a39a381 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -1,11 +1,13 @@ -- Test argument list commands -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, command, eq = helpers.clear, helpers.command, helpers.eq -local expect_exit = helpers.expect_exit -local feed = helpers.feed -local pcall_err = helpers.pcall_err + +local clear, command, eq = n.clear, n.command, t.eq +local expect_exit = n.expect_exit +local feed = n.feed +local pcall_err = t.pcall_err describe('argument list commands', function() before_each(clear) @@ -24,26 +26,26 @@ describe('argument list commands', function() feed(':confirm quit\n') screen:expect([[ | - ~ | - | + {1:~ }| + {3: }| :confirm quit | - 2 more files to edit. Quit anyway? | - [Y]es, (N)o: ^ | + {6:2 more files to edit. Quit anyway?} | + {6:[Y]es, (N)o: }^ | ]]) feed('N') screen:expect([[ ^ | - ~ |*4 + {1:~ }|*4 | ]]) feed(':confirm quit\n') screen:expect([[ | - ~ | - | + {1:~ }| + {3: }| :confirm quit | - 2 more files to edit. Quit anyway? | - [Y]es, (N)o: ^ | + {6:2 more files to edit. Quit anyway?} | + {6:[Y]es, (N)o: }^ | ]]) expect_exit(1000, feed, 'Y') end) diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua index 04c90281a7..47eea5e7d5 100644 --- a/test/functional/legacy/assert_spec.lua +++ b/test/functional/legacy/assert_spec.lua @@ -1,16 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) -local nvim, call = helpers.api, helpers.call -local clear, eq = helpers.clear, helpers.eq -local source, command = helpers.source, helpers.command -local exc_exec = helpers.exc_exec -local eval = helpers.eval +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local api, call = n.api, n.call +local clear, eq = n.clear, t.eq +local source, command = n.source, n.command +local exc_exec = n.exc_exec +local eval = n.eval local function expected_errors(errors) - eq(errors, nvim.nvim_get_vvar('errors')) + eq(errors, api.nvim_get_vvar('errors')) end local function expected_empty() - eq({}, nvim.nvim_get_vvar('errors')) + eq({}, api.nvim_get_vvar('errors')) end describe('assert function:', function() diff --git a/test/functional/legacy/autochdir_spec.lua b/test/functional/legacy/autochdir_spec.lua index e5980f5942..3bfa8bcd1e 100644 --- a/test/functional/legacy/autochdir_spec.lua +++ b/test/functional/legacy/autochdir_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eq, matches = helpers.clear, helpers.eq, helpers.matches -local eval, command, call, api = helpers.eval, helpers.command, helpers.call, helpers.api -local source, exec_capture = helpers.source, helpers.exec_capture -local mkdir = helpers.mkdir +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq, matches = n.clear, t.eq, t.matches +local eval, command, call, api = n.eval, n.command, n.call, n.api +local source, exec_capture = n.source, n.exec_capture +local mkdir = t.mkdir local function expected_empty() eq({}, api.nvim_get_vvar('errors')) @@ -18,7 +20,7 @@ describe('autochdir behavior', function() end) after_each(function() - helpers.rmdir(dir) + n.rmdir(dir) end) -- Tests vim/vim#777 without test_autochdir(). diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua index 9966df263b..9a1ad991f7 100644 --- a/test/functional/legacy/autocmd_option_spec.lua +++ b/test/functional/legacy/autocmd_option_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local nvim = helpers.api -local clear, eq, neq, eval = helpers.clear, helpers.eq, helpers.neq, helpers.eval -local api = helpers.api -local curbuf = helpers.api.nvim_get_current_buf -local curwin = helpers.api.nvim_get_current_win -local exec_capture = helpers.exec_capture -local source, command = helpers.source, helpers.command +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq, neq, eval = n.clear, t.eq, t.neq, n.eval +local api = n.api +local curbuf = n.api.nvim_get_current_buf +local curwin = n.api.nvim_get_current_win +local exec_capture = n.exec_capture +local source, command = n.source, n.command local function declare_hook_function() source([[ @@ -39,7 +40,7 @@ local function init_var() end local function get_result() - local ret = nvim.nvim_get_var('ret') + local ret = api.nvim_get_var('ret') init_var() return ret end @@ -697,24 +698,24 @@ describe('au OptionSet', function() it('should trigger if a boolean option be set globally', function() set_hook('autochdir') - nvim.nvim_set_option_value('autochdir', true, { scope = 'global' }) - eq(true, nvim.nvim_get_option_value('autochdir', { scope = 'global' })) + api.nvim_set_option_value('autochdir', true, { scope = 'global' }) + eq(true, api.nvim_get_option_value('autochdir', { scope = 'global' })) expected_combination({ 'autochdir', false, '', false, true, 'global', 'setglobal' }) end) it('should trigger if a number option be set globally', function() set_hook('cmdheight') - nvim.nvim_set_option_value('cmdheight', 5, { scope = 'global' }) - eq(5, nvim.nvim_get_option_value('cmdheight', { scope = 'global' })) + api.nvim_set_option_value('cmdheight', 5, { scope = 'global' }) + eq(5, api.nvim_get_option_value('cmdheight', { scope = 'global' })) expected_combination({ 'cmdheight', 1, '', 1, 5, 'global', 'setglobal' }) end) it('should trigger if a string option be set globally', function() set_hook('ambiwidth') - nvim.nvim_set_option_value('ambiwidth', 'double', { scope = 'global' }) - eq('double', nvim.nvim_get_option_value('ambiwidth', { scope = 'global' })) + api.nvim_set_option_value('ambiwidth', 'double', { scope = 'global' }) + eq('double', api.nvim_get_option_value('ambiwidth', { scope = 'global' })) expected_combination({ 'ambiwidth', 'single', diff --git a/test/functional/legacy/autocmd_spec.lua b/test/functional/legacy/autocmd_spec.lua index 97051f3d3f..4d82da6312 100644 --- a/test/functional/legacy/autocmd_spec.lua +++ b/test/functional/legacy/autocmd_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local write_file = helpers.write_file -local command = helpers.command -local feed = helpers.feed -local api = helpers.api -local eq = helpers.eq +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local write_file = t.write_file +local command = n.command +local feed = n.feed +local api = n.api +local eq = t.eq before_each(clear) diff --git a/test/functional/legacy/autoformat_join_spec.lua b/test/functional/legacy/autoformat_join_spec.lua index 22b1c258fe..9a240c8bda 100644 --- a/test/functional/legacy/autoformat_join_spec.lua +++ b/test/functional/legacy/autoformat_join_spec.lua @@ -1,9 +1,10 @@ -- Tests for setting the '[,'] marks when joining lines. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command, expect = helpers.command, helpers.expect -local poke_eventloop = helpers.poke_eventloop +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local command, expect = n.command, n.expect +local poke_eventloop = n.poke_eventloop describe('autoformat join', function() setup(clear) diff --git a/test/functional/legacy/breakindent_spec.lua b/test/functional/legacy/breakindent_spec.lua index cf0065f394..8c3d73b17b 100644 --- a/test/functional/legacy/breakindent_spec.lua +++ b/test/functional/legacy/breakindent_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local exec = helpers.exec -local feed = helpers.feed + +local clear = n.clear +local command = n.command +local exec = n.exec +local feed = n.feed before_each(clear) @@ -67,50 +68,43 @@ describe('breakindent', function() setlocal breakindent call setline(1, "\t" .. join(range(100))) ]]) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, -- Visual - [2] = { bold = true, reverse = true }, -- StatusLine - [3] = { reverse = true }, -- StatusLineNC - [4] = { bold = true }, -- ModeMsg - }) screen:attach() feed('v$') screen:expect([[ - {0:<<<} {1: 93 94 95 96 97 98 99}^ | - {2:[No Name] [+] }| + {1:<<<} {17: 93 94 95 96 97 98 99}^ | + {3:[No Name] [+] }| | - {0:~ }| - {3:[No Name] }| - {4:-- VISUAL --} | + {1:~ }| + {2:[No Name] }| + {5:-- VISUAL --} | ]]) command('setlocal showbreak=+++') screen:expect([[ - {0:+++}{1: 90 91 92 93 94 95 96 97 98 99}^ | - {2:[No Name] [+] }| + {1:+++}{17: 90 91 92 93 94 95 96 97 98 99}^ | + {3:[No Name] [+] }| | - {0:~ }| - {3:[No Name] }| - {4:-- VISUAL --} | + {1:~ }| + {2:[No Name] }| + {5:-- VISUAL --} | ]]) command('setlocal breakindentopt+=sbr') screen:expect([[ - {0:+++} {1: 93 94 95 96 97 98 99}^ | - {2:[No Name] [+] }| + {1:+++} {17: 93 94 95 96 97 98 99}^ | + {3:[No Name] [+] }| | - {0:~ }| - {3:[No Name] }| - {4:-- VISUAL --} | + {1:~ }| + {2:[No Name] }| + {5:-- VISUAL --} | ]]) command('setlocal nobreakindent') screen:expect([[ - {0:+++}{1: 98 99}^ | - {2:[No Name] [+] }| + {1:+++}{17: 98 99}^ | + {3:[No Name] [+] }| | - {0:~ }| - {3:[No Name] }| - {4:-- VISUAL --} | + {1:~ }| + {2:[No Name] }| + {5:-- VISUAL --} | ]]) end) end) diff --git a/test/functional/legacy/buffer_spec.lua b/test/functional/legacy/buffer_spec.lua deleted file mode 100644 index b3964540f0..0000000000 --- a/test/functional/legacy/buffer_spec.lua +++ /dev/null @@ -1,59 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, source = helpers.clear, helpers.source -local call, eq, api = helpers.call, helpers.eq, helpers.api - -local function expected_empty() - eq({}, api.nvim_get_vvar('errors')) -end - -describe('buffer', function() - before_each(function() - clear() - api.nvim_ui_attach(80, 24, {}) - api.nvim_set_option_value('hidden', false, {}) - end) - - it('deleting a modified buffer with :confirm', function() - source([[ - func Test_bdel_with_confirm() - new - call setline(1, 'test') - call assert_fails('bdel', 'E89:') - call nvim_input('c') - confirm bdel - call assert_equal(2, winnr('$')) - call assert_equal(1, &modified) - call nvim_input('n') - confirm bdel - call assert_equal(1, winnr('$')) - endfunc - ]]) - call('Test_bdel_with_confirm') - expected_empty() - end) - - it('editing another buffer from a modified buffer with :confirm', function() - source([[ - func Test_goto_buf_with_confirm() - new Xfile - enew - call setline(1, 'test') - call assert_fails('b Xfile', 'E37:') - call nvim_input('c') - call assert_fails('confirm b Xfile', 'E37:') - call assert_equal(1, &modified) - call assert_equal('', @%) - call nvim_input('y') - call assert_fails('confirm b Xfile', 'E37:') - call assert_equal(1, &modified) - call assert_equal('', @%) - call nvim_input('n') - confirm b Xfile - call assert_equal('Xfile', @%) - close! - endfunc - ]]) - call('Test_goto_buf_with_confirm') - expected_empty() - end) -end) diff --git a/test/functional/legacy/changelist_spec.lua b/test/functional/legacy/changelist_spec.lua index b673e74128..e2f87351de 100644 --- a/test/functional/legacy/changelist_spec.lua +++ b/test/functional/legacy/changelist_spec.lua @@ -1,9 +1,10 @@ -- Test changelist position after splitting window -- Set 'undolevels' to make changelist for sourced file -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('changelist', function() setup(clear) diff --git a/test/functional/legacy/charsearch_spec.lua b/test/functional/legacy/charsearch_spec.lua index c1a59c9ee1..da13b61e0f 100644 --- a/test/functional/legacy/charsearch_spec.lua +++ b/test/functional/legacy/charsearch_spec.lua @@ -1,8 +1,9 @@ -- Test for character searches -local helpers = require('test.functional.helpers')(after_each) -local feed, insert = helpers.feed, helpers.insert -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, insert = n.feed, n.insert +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('charsearch', function() setup(clear) diff --git a/test/functional/legacy/close_count_spec.lua b/test/functional/legacy/close_count_spec.lua index 930dae668a..0a08ad9fe6 100644 --- a/test/functional/legacy/close_count_spec.lua +++ b/test/functional/legacy/close_count_spec.lua @@ -1,13 +1,14 @@ -- Tests for :[count]close! and :[count]hide -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local poke_eventloop = helpers.poke_eventloop -local eval = helpers.eval -local feed = helpers.feed -local clear = helpers.clear -local command = helpers.command +local eq = t.eq +local poke_eventloop = n.poke_eventloop +local eval = n.eval +local feed = n.feed +local clear = n.clear +local command = n.command describe('close_count', function() setup(clear) diff --git a/test/functional/legacy/cmdline_spec.lua b/test/functional/legacy/cmdline_spec.lua index 8c94451f9a..2e0e52117d 100644 --- a/test/functional/legacy/cmdline_spec.lua +++ b/test/functional/legacy/cmdline_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed -local feed_command = helpers.feed_command -local exec = helpers.exec -local api = helpers.api + +local clear = n.clear +local command = n.command +local feed = n.feed +local feed_command = n.feed_command +local exec = n.exec +local api = n.api local pesc = vim.pesc describe('cmdline', function() @@ -15,12 +16,6 @@ describe('cmdline', function() it('is cleared when switching tabs', function() local screen = Screen.new(30, 10) screen:attach() - screen:set_default_attr_ids { - [1] = { underline = true, background = Screen.colors.LightGrey }, - [2] = { bold = true }, - [3] = { reverse = true }, - [4] = { bold = true, foreground = Screen.colors.Blue1 }, - } feed_command([[call setline(1, range(30))]]) screen:expect([[ @@ -39,9 +34,9 @@ describe('cmdline', function() feed [[:tabnew<cr>]] screen:expect { grid = [[ - {1: + [No Name] }{2: [No Name] }{3: }{1:X}| + {24: + [No Name] }{5: [No Name] }{2: }{24:X}| ^ | - {4:~ }|*7 + {1:~ }|*7 :tabnew | ]], } @@ -49,9 +44,9 @@ describe('cmdline', function() feed [[<C-w>-<C-w>-]] screen:expect { grid = [[ - {1: + [No Name] }{2: [No Name] }{3: }{1:X}| + {24: + [No Name] }{5: [No Name] }{2: }{24:X}| ^ | - {4:~ }|*5 + {1:~ }|*5 |*3 ]], } @@ -59,7 +54,7 @@ describe('cmdline', function() feed [[gt]] screen:expect { grid = [[ - {2: + [No Name] }{1: [No Name] }{3: }{1:X}| + {5: + [No Name] }{24: [No Name] }{2: }{24:X}| ^0 | 1 | 2 | @@ -74,9 +69,9 @@ describe('cmdline', function() feed [[gt]] screen:expect([[ - {1: + [No Name] }{2: [No Name] }{3: }{1:X}| + {24: + [No Name] }{5: [No Name] }{2: }{24:X}| ^ | - {4:~ }|*5 + {1:~ }|*5 |*3 ]]) end) @@ -93,8 +88,8 @@ describe('cmdline', function() feed_command('DoSomething') screen:expect([[ | - ~ |*2 - | + {1:~ }|*2 + {3: }| Executing: DoSomething | Executing: echo 'hello' |set ts=4 |let v = '123' |echo v | hello | @@ -102,17 +97,13 @@ describe('cmdline', function() Executing: let v = '123' |echo v | Executing: echo v | 123 | - Press ENTER or type command to continue^ | + {6:Press ENTER or type command to continue}^ | ]]) end) -- oldtest: Test_cmdline_redraw_tabline() it('tabline is redrawn on entering cmdline', function() local screen = Screen.new(30, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { reverse = true }, -- TabLineFill - }) screen:attach() exec([[ set showtabline=2 @@ -120,9 +111,9 @@ describe('cmdline', function() ]]) feed(':') screen:expect([[ - {1:foo }| + {2:foo }| | - {0:~ }|*3 + {1:~ }|*3 :^ | ]]) end) @@ -130,9 +121,6 @@ describe('cmdline', function() -- oldtest: Test_redraw_in_autocmd() it('cmdline cursor position is correct after :redraw with cmdheight=2', function() local screen = Screen.new(30, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - }) screen:attach() exec([[ set cmdheight=2 @@ -141,7 +129,7 @@ describe('cmdline', function() feed(':for i in range(3)<CR>') screen:expect([[ | - {0:~ }|*3 + {1:~ }|*3 :for i in range(3) | : ^ | ]]) @@ -149,7 +137,7 @@ describe('cmdline', function() -- Note: this may still be considered broken, ref #18140 screen:expect([[ | - {0:~ }|*3 + {1:~ }|*3 : :let i =^ | | ]]) @@ -157,10 +145,6 @@ describe('cmdline', function() it("setting 'cmdheight' works after outputting two messages vim-patch:9.0.0665", function() local screen = Screen.new(60, 8) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { bold = true, reverse = true }, -- StatusLine - }) screen:attach() exec([[ set cmdheight=1 laststatus=2 @@ -175,15 +159,15 @@ describe('cmdline', function() feed(':call EchoTwo()') screen:expect([[ | - {0:~ }|*5 - {1:[No Name] }| + {1:~ }|*5 + {3:[No Name] }| :call EchoTwo()^ | ]]) feed('<CR>') screen:expect([[ ^ | - {0:~ }|*5 - {1:[No Name] }| + {1:~ }|*5 + {3:[No Name] }| | ]]) end) @@ -191,21 +175,15 @@ describe('cmdline', function() -- oldtest: Test_cmdheight_tabline() it("changing 'cmdheight' when there is a tabline", function() local screen = Screen.new(60, 8) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { bold = true, reverse = true }, -- StatusLine - [2] = { bold = true }, -- TabLineSel - [3] = { reverse = true }, -- TabLineFill - }) screen:attach() api.nvim_set_option_value('laststatus', 2, {}) api.nvim_set_option_value('showtabline', 2, {}) api.nvim_set_option_value('cmdheight', 1, {}) screen:expect([[ - {2: [No Name] }{3: }| + {5: [No Name] }{2: }| ^ | - {0:~ }|*4 - {1:[No Name] }| + {1:~ }|*4 + {3:[No Name] }| | ]]) end) @@ -213,9 +191,6 @@ describe('cmdline', function() -- oldtest: Test_rulerformat_position() it("ruler has correct position with 'rulerformat' set", function() local screen = Screen.new(20, 3) - screen:set_default_attr_ids { - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - } screen:attach() api.nvim_set_option_value('ruler', true, {}) api.nvim_set_option_value('rulerformat', 'longish', {}) @@ -224,7 +199,7 @@ describe('cmdline', function() feed [[<C-W>v<C-W>|<C-W>p]] screen:expect [[ │^ | - {0:~ }│{0:~}| + {1:~ }│{1:~}| longish | ]] end) diff --git a/test/functional/legacy/command_count_spec.lua b/test/functional/legacy/command_count_spec.lua index 8707c0459c..a6be459a4d 100644 --- a/test/functional/legacy/command_count_spec.lua +++ b/test/functional/legacy/command_count_spec.lua @@ -1,8 +1,9 @@ -- Test for user command counts -local helpers = require('test.functional.helpers')(after_each) -local clear, source, expect = helpers.clear, helpers.source, helpers.expect -local feed_command = helpers.feed_command +local n = require('test.functional.testnvim')() + +local clear, source, expect = n.clear, n.source, n.expect +local feed_command = n.feed_command -- luacheck: ignore 613 (Trailing whitespace in a string) describe('command_count', function() diff --git a/test/functional/legacy/comparators_spec.lua b/test/functional/legacy/comparators_spec.lua index 32e830a0af..86df5b1c41 100644 --- a/test/functional/legacy/comparators_spec.lua +++ b/test/functional/legacy/comparators_spec.lua @@ -1,8 +1,10 @@ -- " Test for expression comparators. -local helpers = require('test.functional.helpers')(after_each) -local clear, eq = helpers.clear, helpers.eq -local eval, command = helpers.eval, helpers.command +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq = n.clear, t.eq +local eval, command = n.eval, n.command describe('comparators', function() before_each(clear) diff --git a/test/functional/legacy/conceal_spec.lua b/test/functional/legacy/conceal_spec.lua index 9a23d16c5b..f4c1983bb7 100644 --- a/test/functional/legacy/conceal_spec.lua +++ b/test/functional/legacy/conceal_spec.lua @@ -1,12 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local exec = helpers.exec -local feed = helpers.feed + +local clear = n.clear +local command = n.command +local exec = n.exec +local feed = n.feed +local api = n.api local expect_pos = function(row, col) - return helpers.eq({ row, col }, helpers.eval('[screenrow(), screencol()]')) + return t.eq({ row, col }, n.eval('[screenrow(), screencol()]')) end describe('Conceal', function() @@ -18,12 +21,6 @@ describe('Conceal', function() -- oldtest: Test_conceal_two_windows() it('works', function() local screen = Screen.new(75, 12) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { bold = true, reverse = true }, -- StatusLine - [2] = { reverse = true }, -- StatusLineNC, IncSearch - [3] = { bold = true }, -- ModeMsg - }) screen:attach() exec([[ let lines = ["one one one one one", "two |hidden| here", "three |hidden| three"] @@ -47,12 +44,12 @@ describe('Conceal', function() two ^here | three three | Second window | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| one one one one one | two here | three three | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| /here | ]]) @@ -64,12 +61,12 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| one one one one one | two here | three three | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| /here | ]]) @@ -82,12 +79,12 @@ describe('Conceal', function() two |hidden| ^here | three three | Second window | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| one one one one one | two here | three three | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| /here | ]]) @@ -99,12 +96,12 @@ describe('Conceal', function() two here | three |hidden^| three | Second window | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| one one one one one | two here | three three | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| /here | ]]) @@ -116,13 +113,13 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| ^here | three three | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| /here | ]]) @@ -133,13 +130,13 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two ^here | three three | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| /here | ]]) feed('a') @@ -148,14 +145,14 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| h^ere | three three | - {0:~ }| - {1:[No Name] [+] }| - {3:-- INSERT --} | + {1:~ }| + {3:[No Name] [+] }| + {5:-- INSERT --} | ]]) feed('<Esc>/e') screen:expect([[ @@ -163,13 +160,13 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| h{2:e}re | three three | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| /e^ | ]]) feed('<Esc>v') @@ -178,14 +175,14 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| ^here | three three | - {0:~ }| - {1:[No Name] [+] }| - {3:-- VISUAL --} | + {1:~ }| + {3:[No Name] [+] }| + {5:-- VISUAL --} | ]]) feed('<Esc>') @@ -196,13 +193,13 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| ^here | three three | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| | ]]) feed('a') @@ -211,14 +208,14 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two h^ere | three three | - {0:~ }| - {1:[No Name] [+] }| - {3:-- INSERT --} | + {1:~ }| + {3:[No Name] [+] }| + {5:-- INSERT --} | ]]) feed('<Esc>/e') screen:expect([[ @@ -226,13 +223,13 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| h{2:e}re | three three | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| /e^ | ]]) feed('<Esc>v') @@ -241,14 +238,14 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| ^here | three three | - {0:~ }| - {1:[No Name] [+] }| - {3:-- VISUAL --} | + {1:~ }| + {3:[No Name] [+] }| + {5:-- VISUAL --} | ]]) feed('<Esc>') @@ -259,13 +256,13 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| ^here | three three | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| | ]]) feed('a') @@ -274,14 +271,14 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| h^ere | three three | - {0:~ }| - {1:[No Name] [+] }| - {3:-- INSERT --} | + {1:~ }| + {3:[No Name] [+] }| + {5:-- INSERT --} | ]]) feed('<Esc>/e') screen:expect([[ @@ -289,13 +286,13 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| h{2:e}re | three three | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| /e^ | ]]) feed('<Esc>v') @@ -304,14 +301,14 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two ^here | three three | - {0:~ }| - {1:[No Name] [+] }| - {3:-- VISUAL --} | + {1:~ }| + {3:[No Name] [+] }| + {5:-- VISUAL --} | ]]) feed('<Esc>') @@ -323,14 +320,14 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two |hidden| h^ere | three three | - {0:~ }| - {1:[No Name] [+] }| - {3:-- INSERT --} | + {1:~ }| + {3:[No Name] [+] }| + {5:-- INSERT --} | ]]) feed('<Down>') screen:expect([[ @@ -338,14 +335,14 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two here | three |hidden|^ three | - {0:~ }| - {1:[No Name] [+] }| - {3:-- INSERT --} | + {1:~ }| + {3:[No Name] [+] }| + {5:-- INSERT --} | ]]) feed('<Esc>') @@ -355,13 +352,13 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two here | three |hidden^| three | - {0:~ }| - {1:[No Name] [+] }| + {1:~ }| + {3:[No Name] [+] }| | ]]) feed('o') @@ -370,14 +367,14 @@ describe('Conceal', function() two here | three three | Second window | - {0:~ }| + {1:~ }| {2:[No Name] [+] }| one one one one one | two here | three three | ^ | - {1:[No Name] [+] }| - {3:-- INSERT --} | + {3:[No Name] [+] }| + {5:-- INSERT --} | ]]) feed('<Esc>') end) @@ -433,15 +430,73 @@ describe('Conceal', function() ]]) end) - -- oldtest: Test_conceal_resize_term() - it('resize editor', function() - local screen = Screen.new(75, 6) + -- oldtest: Test_conceal_wrapped_cursorline_wincolor() + it('CursorLine highlight on wrapped lines', function() + local screen = Screen.new(40, 4) + screen:set_default_attr_ids({ + [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText + [1] = { background = Screen.colors.Green }, -- CursorLine (low-priority) + [2] = { foreground = Screen.colors.Red }, -- CursorLine (high-priority) + }) + screen:attach() + exec([[ + call setline(1, 'one one one |hidden| one one one one one one one one') + syntax match test /|hidden|/ conceal + set conceallevel=2 concealcursor=n cursorline + normal! g$ + hi! CursorLine guibg=Green + ]]) + screen:expect([[ + {1:one one one one one one one on^e }| + {1: one one one }| + {0:~ }| + | + ]]) + command('hi! CursorLine guibg=NONE guifg=Red') + screen:expect([[ + {2:one one one one one one one on^e }| + {2: one one one }| + {0:~ }| + | + ]]) + end) + + -- oldtest: Test_conceal_wrapped_cursorline_wincolor_rightleft() + it('CursorLine highlight on wrapped lines with rightleft', function() + local screen = Screen.new(40, 4) screen:set_default_attr_ids({ [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { foreground = Screen.colors.Blue }, -- Comment + [1] = { background = Screen.colors.Green }, -- CursorLine (low-priority) + [2] = { foreground = Screen.colors.Red }, -- CursorLine (high-priority) }) screen:attach() exec([[ + call setline(1, 'one one one |hidden| one one one one one one one one') + syntax match test /|hidden|/ conceal + set conceallevel=2 concealcursor=n cursorline rightleft + normal! g$ + hi! CursorLine guibg=Green + ]]) + screen:expect([[ + {1: ^eno eno eno eno eno eno eno eno}| + {1: eno eno eno }| + {0: ~}| + | + ]]) + command('hi! CursorLine guibg=NONE guifg=Red') + screen:expect([[ + {2: ^eno eno eno eno eno eno eno eno}| + {2: eno eno eno }| + {0: ~}| + | + ]]) + end) + + -- oldtest: Test_conceal_resize_term() + it('resize editor', function() + local screen = Screen.new(75, 6) + screen:attach() + exec([[ call setline(1, '`one` `two` `three` `four` `five`, the backticks should be concealed') setl cocu=n cole=3 syn region CommentCodeSpan matchgroup=Comment start=/`/ end=/`/ concealends @@ -449,14 +504,14 @@ describe('Conceal', function() ]]) screen:expect([[ one two three four five, the ^backticks should be concealed | - {0:~ }|*4 + {1:~ }|*4 | ]]) screen:try_resize(75, 7) screen:expect([[ one two three four five, the ^backticks should be concealed | - {0:~ }|*5 + {1:~ }|*5 | ]]) end) @@ -464,9 +519,6 @@ describe('Conceal', function() -- oldtest: Test_conceal_linebreak() it('with linebreak', function() local screen = Screen.new(75, 8) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - }) screen:attach() exec([[ let &wrap = v:true @@ -486,9 +538,9 @@ describe('Conceal', function() ^xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | - {0:+ }bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | - {0:+ }cccccc | - {0:~ }|*2 + {1:+ }bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | + {1:+ }cccccc | + {1:~ }|*2 | ]]) end) @@ -570,4 +622,210 @@ describe('Conceal', function() feed('$') expect_pos(9, 26) end) + + local function test_conceal_virtualedit_after_eol(wrap) + local screen = Screen.new(60, 3) + screen:set_default_attr_ids({ + [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText + }) + screen:attach() + api.nvim_set_option_value('wrap', wrap, {}) + exec([[ + call setline(1, 'abcdefgh|hidden|ijklmnpop') + syntax match test /|hidden|/ conceal + set conceallevel=2 concealcursor=n virtualedit=all + normal! $ + ]]) + screen:expect([[ + abcdefghijklmnpo^p | + {0:~ }| + | + ]]) + feed('l') + screen:expect([[ + abcdefghijklmnpop^ | + {0:~ }| + | + ]]) + feed('l') + screen:expect([[ + abcdefghijklmnpop ^ | + {0:~ }| + | + ]]) + feed('l') + screen:expect([[ + abcdefghijklmnpop ^ | + {0:~ }| + | + ]]) + feed('rr') + screen:expect([[ + abcdefghijklmnpop ^r | + {0:~ }| + | + ]]) + end + + -- oldtest: Test_conceal_virtualedit_after_eol() + describe('cursor drawn at correct column with virtualedit', function() + it('with wrapping', function() + test_conceal_virtualedit_after_eol(true) + end) + it('without wrapping', function() + test_conceal_virtualedit_after_eol(false) + end) + end) + + local function test_conceal_virtualedit_after_eol_rightleft(wrap) + local screen = Screen.new(60, 3) + screen:set_default_attr_ids({ + [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText + }) + screen:attach() + api.nvim_set_option_value('wrap', wrap, {}) + exec([[ + call setline(1, 'abcdefgh|hidden|ijklmnpop') + syntax match test /|hidden|/ conceal + set conceallevel=2 concealcursor=n virtualedit=all rightleft + normal! $ + ]]) + screen:expect([[ + ^popnmlkjihgfedcba| + {0: ~}| + | + ]]) + feed('h') + screen:expect([[ + ^ popnmlkjihgfedcba| + {0: ~}| + | + ]]) + feed('h') + screen:expect([[ + ^ popnmlkjihgfedcba| + {0: ~}| + | + ]]) + feed('h') + screen:expect([[ + ^ popnmlkjihgfedcba| + {0: ~}| + | + ]]) + feed('rr') + screen:expect([[ + ^r popnmlkjihgfedcba| + {0: ~}| + | + ]]) + end + + -- oldtest: Test_conceal_virtualedit_after_eol_rightleft() + describe('cursor drawn correctly with virtualedit and rightleft', function() + it('with wrapping', function() + test_conceal_virtualedit_after_eol_rightleft(true) + end) + it('without wrapping', function() + test_conceal_virtualedit_after_eol_rightleft(false) + end) + end) + + local function test_conceal_double_width(wrap) + local screen = Screen.new(60, 4) + screen:set_default_attr_ids({ + [0] = { bold = true, foreground = Screen.colors.Blue }, + [1] = { background = Screen.colors.DarkGrey, foreground = Screen.colors.LightGrey }, + [2] = { background = Screen.colors.LightRed }, + }) + screen:attach() + api.nvim_set_option_value('wrap', wrap, {}) + exec([[ + call setline(1, ['aaaaa口=口bbbbb口=口ccccc', 'foobar']) + syntax match test /口=口/ conceal cchar=β + set conceallevel=2 concealcursor=n colorcolumn=30 + normal! $ + ]]) + screen:expect([[ + aaaaa{1:β}bbbbb{1:β}cccc^c {2: } | + foobar {2: } | + {0:~ }| + | + ]]) + feed('gM') + screen:expect([[ + aaaaa{1:β}bb^bbb{1:β}ccccc {2: } | + foobar {2: } | + {0:~ }| + | + ]]) + command('set conceallevel=3') + screen:expect([[ + aaaaabb^bbbccccc {2: } | + foobar {2: } | + {0:~ }| + | + ]]) + feed('$') + screen:expect([[ + aaaaabbbbbcccc^c {2: } | + foobar {2: } | + {0:~ }| + | + ]]) + end + + -- oldtest: Test_conceal_double_width() + describe('cursor drawn correctly when double-width chars are concealed', function() + it('with wrapping', function() + test_conceal_double_width(true) + end) + it('without wrapping', function() + test_conceal_double_width(false) + end) + end) + + -- oldtest: Test_conceal_double_width_wrap() + it('line wraps correctly when double-width chars are concealed', function() + local screen = Screen.new(20, 4) + screen:set_default_attr_ids({ + [0] = { bold = true, foreground = Screen.colors.Blue }, + [1] = { background = Screen.colors.DarkGrey, foreground = Screen.colors.LightGrey }, + [2] = { background = Screen.colors.LightRed }, + }) + screen:attach() + exec([[ + call setline(1, 'aaaaaaaaaa口=口bbbbbbbbbb口=口cccccccccc') + syntax match test /口=口/ conceal cchar=β + set conceallevel=2 concealcursor=n + normal! $ + ]]) + screen:expect([[ + aaaaaaaaaa{1:β}bbbbb | + bbbbb{1:β}ccccccccc^c | + {0:~ }| + | + ]]) + feed('gM') + screen:expect([[ + aaaaaaaaaa{1:β}bbbbb | + ^bbbbb{1:β}cccccccccc | + {0:~ }| + | + ]]) + command('set conceallevel=3') + screen:expect([[ + aaaaaaaaaabbbbb | + ^bbbbbcccccccccc | + {0:~ }| + | + ]]) + feed('$') + screen:expect([[ + aaaaaaaaaabbbbb | + bbbbbccccccccc^c | + {0:~ }| + | + ]]) + end) end) diff --git a/test/functional/legacy/cpoptions_spec.lua b/test/functional/legacy/cpoptions_spec.lua index 288146199a..56b0e72c72 100644 --- a/test/functional/legacy/cpoptions_spec.lua +++ b/test/functional/legacy/cpoptions_spec.lua @@ -1,8 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed + +local clear = n.clear +local command = n.command +local feed = n.feed before_each(clear) @@ -15,13 +16,13 @@ describe('cpoptions', function() feed('c2w') screen:expect([[ ^one tw$ three | - ~ |*4 - -- INSERT -- | + {1:~ }|*4 + {5:-- INSERT --} | ]]) feed('vim<Esc>') screen:expect([[ vi^m three | - ~ |*4 + {1:~ }|*4 | ]]) end) diff --git a/test/functional/legacy/crash_spec.lua b/test/functional/legacy/crash_spec.lua index 094bea253e..04f77c7d4f 100644 --- a/test/functional/legacy/crash_spec.lua +++ b/test/functional/legacy/crash_spec.lua @@ -1,8 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local assert_alive = helpers.assert_alive -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed +local n = require('test.functional.testnvim')() + +local assert_alive = n.assert_alive +local clear = n.clear +local command = n.command +local feed = n.feed before_each(clear) diff --git a/test/functional/legacy/debugger_spec.lua b/test/functional/legacy/debugger_spec.lua index 7ed5e84da6..c6f552ab51 100644 --- a/test/functional/legacy/debugger_spec.lua +++ b/test/functional/legacy/debugger_spec.lua @@ -1,9 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed -local write_file = helpers.write_file + +local clear = n.clear +local command = n.command +local feed = n.feed +local write_file = t.write_file before_each(clear) @@ -12,10 +14,6 @@ describe('debugger', function() before_each(function() screen = Screen.new(999, 10) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, - [1] = { reverse = true, bold = true }, - }) screen:attach() end) @@ -33,7 +31,7 @@ describe('debugger', function() screen:expect { grid = [[ ^let g:Xtest_var += 1{MATCH: *}| - {0:~{MATCH: *}}|*8 + {1:~{MATCH: *}}|*8 :source %{MATCH: *}| ]], } @@ -41,8 +39,8 @@ describe('debugger', function() screen:expect { grid = [[ let g:Xtest_var += 1{MATCH: *}| - {0:~{MATCH: *}}| - {1:{MATCH: *}}| + {1:~{MATCH: *}}| + {3:{MATCH: *}}| Breakpoint in "{MATCH:.*}XdebugBreakExpr.vim" line 1{MATCH: *}| Entering Debug mode. Type "cont" to continue.{MATCH: *}| Oldval = "10"{MATCH: *}| @@ -56,7 +54,7 @@ describe('debugger', function() screen:expect { grid = [[ ^let g:Xtest_var += 1{MATCH: *}| - {0:~{MATCH: *}}|*8 + {1:~{MATCH: *}}|*8 {MATCH: *}| ]], } @@ -64,8 +62,8 @@ describe('debugger', function() screen:expect { grid = [[ let g:Xtest_var += 1{MATCH: *}| - {0:~{MATCH: *}}| - {1:{MATCH: *}}| + {1:~{MATCH: *}}| + {3:{MATCH: *}}| Breakpoint in "{MATCH:.*}XdebugBreakExpr.vim" line 1{MATCH: *}| Entering Debug mode. Type "cont" to continue.{MATCH: *}| Oldval = "11"{MATCH: *}| diff --git a/test/functional/legacy/delete_spec.lua b/test/functional/legacy/delete_spec.lua index 1227065af2..f8e761c7f0 100644 --- a/test/functional/legacy/delete_spec.lua +++ b/test/functional/legacy/delete_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, source = helpers.clear, helpers.source -local eq, eval, command = helpers.eq, helpers.eval, helpers.command -local exc_exec = helpers.exc_exec +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, source = n.clear, n.source +local eq, eval, command = t.eq, n.eval, n.command +local exc_exec = n.exc_exec describe('Test for delete()', function() before_each(clear) @@ -48,7 +50,7 @@ describe('Test for delete()', function() it('symlink directory delete', function() command("call mkdir('Xdir1')") - if helpers.is_os('win') then + if t.is_os('win') then command('silent !mklink /j Xlink Xdir1') else command('silent !ln -s Xdir1 Xlink') diff --git a/test/functional/legacy/digraph_spec.lua b/test/functional/legacy/digraph_spec.lua index 015f144b74..ed1071079c 100644 --- a/test/functional/legacy/digraph_spec.lua +++ b/test/functional/legacy/digraph_spec.lua @@ -1,7 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local feed = helpers.feed + +local clear = n.clear +local feed = n.feed before_each(clear) @@ -9,29 +10,24 @@ describe('digraph', function() -- oldtest: Test_entering_digraph() it('characters displayed on the screen', function() local screen = Screen.new(10, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { foreground = Screen.colors.Blue }, -- SpecialKey - [2] = { bold = true }, -- ModeMsg - }) screen:attach() feed('i<C-K>') screen:expect([[ - {1:^?} | - {0:~ }|*4 - {2:-- INSERT --}| + {18:^?} | + {1:~ }|*4 + {5:-- INSERT --}| ]]) feed('1') screen:expect([[ - {1:^1} | - {0:~ }|*4 - {2:-- INSERT --}| + {18:^1} | + {1:~ }|*4 + {5:-- INSERT --}| ]]) feed('2') screen:expect([[ ½^ | - {0:~ }|*4 - {2:-- INSERT --}| + {1:~ }|*4 + {5:-- INSERT --}| ]]) end) end) diff --git a/test/functional/legacy/display_spec.lua b/test/functional/legacy/display_spec.lua index 153fad2e22..98b9596847 100644 --- a/test/functional/legacy/display_spec.lua +++ b/test/functional/legacy/display_spec.lua @@ -1,10 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) - +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local feed = helpers.feed -local command = helpers.command + +local clear = n.clear +local exec = n.exec +local feed = n.feed +local command = n.command describe('display', function() before_each(clear) @@ -13,9 +13,6 @@ describe('display', function() it('scroll when modified at topline vim-patch:8.2.1488', function() local screen = Screen.new(20, 4) screen:attach() - screen:set_default_attr_ids({ - [1] = { bold = true }, - }) command([[call setline(1, repeat('a', 21))]]) feed('O') @@ -23,7 +20,7 @@ describe('display', function() ^ | aaaaaaaaaaaaaaaaaaaa| a | - {1:-- INSERT --} | + {5:-- INSERT --} | ]]) end) @@ -31,11 +28,6 @@ describe('display', function() it('scrolling when modified at topline in Visual mode vim-patch:8.2.4626', function() local screen = Screen.new(60, 8) screen:attach() - screen:set_default_attr_ids({ - [1] = { bold = true }, -- ModeMsg - [2] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, -- Visual - [3] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue }, -- SignColumn - }) exec([[ set scrolloff=0 @@ -47,9 +39,9 @@ describe('display', function() ]]) feed('VG7kk') screen:expect([[ - {3: }^f{2:oo} | - {3: }foo |*6 - {1:-- VISUAL LINE --} | + {7: }^f{17:oo} | + {7: }foo |*6 + {5:-- VISUAL LINE --} | ]]) end) @@ -149,7 +141,7 @@ describe('display', function() ]]) feed('736|') screen:expect([[ - <<<aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {1:<<<}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|*11 ^aaaaaaaaaaaaaaa | | @@ -157,22 +149,22 @@ describe('display', function() -- The correct part of the last line is moved into view. feed('D') screen:expect([[ - <<<aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {1:<<<}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|*10 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^a| - bbbbb bbbbb bbbbb bbbbb bbbbb bb@@@| + bbbbb bbbbb bbbbb bbbbb bbbbb bb{1:@@@}| | ]]) -- "w_skipcol" does not change because the topline is still long enough -- to maintain the current skipcol. feed('g04l11gkD') screen:expect([[ - <<<^a | + {1:<<<}^a | bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb| bbbbb ccccc ccccc ccccc ccccc cccc| c ccccc ccccc ddddd ddddd ddddd ddd| dd ddddd ddddd ddddd | - ~ |*8 + {1:~ }|*8 | ]]) -- "w_skipcol" is reset to bring the entire topline into view because @@ -183,7 +175,7 @@ describe('display', function() aa^a | bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb| bbbbb ccccc ccccc ccccc ccccc cccc| - c ccccc ccccc ddddd ddddd ddddd @@@| + c ccccc ccccc ddddd ddddd ddddd {1:@@@}| | ]]) end) @@ -197,7 +189,7 @@ describe('display', function() norm $j ]]) screen:expect([[ - <<<bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + {1:<<<}bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|*5 b^b | | @@ -207,7 +199,7 @@ describe('display', function() exec('set number cpo+=n scrolloff=0') feed('$0') screen:expect([[ - <<<b^bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + {1:<<<}b^bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|*6 | ]]) @@ -215,14 +207,14 @@ describe('display', function() exec('set smoothscroll') feed('$b') screen:expect([[ - 2 b ^bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + {8: 2 }b ^bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|*6 | ]]) -- Same for "ge". feed('$ge') screen:expect([[ - 2 ^b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + {8: 2 }^b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|*6 | ]]) diff --git a/test/functional/legacy/edit_spec.lua b/test/functional/legacy/edit_spec.lua index 0762e5e671..f3d18a2541 100644 --- a/test/functional/legacy/edit_spec.lua +++ b/test/functional/legacy/edit_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local expect = helpers.expect -local feed = helpers.feed + +local clear = n.clear +local command = n.command +local expect = n.expect +local feed = n.feed local sleep = vim.uv.sleep before_each(clear) @@ -30,22 +31,17 @@ describe('edit', function() -- oldtest: Test_edit_insert_reg() it('inserting a register using CTRL-R', function() local screen = Screen.new(10, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { foreground = Screen.colors.Blue }, -- SpecialKey - [2] = { bold = true }, -- ModeMsg - }) screen:attach() feed('a<C-R>') screen:expect([[ - {1:^"} | - {0:~ }|*4 - {2:-- INSERT --}| + {18:^"} | + {1:~ }|*4 + {5:-- INSERT --}| ]]) feed('=') screen:expect([[ - {1:"} | - {0:~ }|*4 + {18:"} | + {1:~ }|*4 =^ | ]]) end) @@ -53,51 +49,42 @@ describe('edit', function() -- oldtest: Test_edit_ctrl_r_failed() it('positioning cursor after CTRL-R expression failed', function() local screen = Screen.new(60, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { foreground = Screen.colors.Blue }, -- SpecialKey - [2] = { foreground = Screen.colors.SlateBlue }, - [3] = { bold = true }, -- ModeMsg - [4] = { reverse = true, bold = true }, -- MsgSeparator - [5] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg - [6] = { foreground = Screen.colors.SeaGreen, bold = true }, -- MoreMsg - }) screen:attach() feed('i<C-R>') screen:expect([[ - {1:^"} | - {0:~ }|*4 - {3:-- INSERT --} | + {18:^"} | + {1:~ }|*4 + {5:-- INSERT --} | ]]) - feed('={}') + feed('=0z') screen:expect([[ - {1:"} | - {0:~ }|*4 - ={2:{}}^ | + {18:"} | + {1:~ }|*4 + ={26:0}{9:z}^ | ]]) - -- trying to insert a dictionary produces an error + -- trying to insert a blob produces an error feed('<CR>') screen:expect([[ - {1:"} | - {0:~ }| - {4: }| - ={2:{}} | - {5:E731: Using a Dictionary as a String} | + {18:"} | + {1:~ }| + {3: }| + ={26:0}{9:z} | + {9:E976: Using a Blob as a String} | {6:Press ENTER or type command to continue}^ | ]]) feed(':') screen:expect([[ :^ | - {0:~ }|*4 - {3:-- INSERT --} | + {1:~ }|*4 + {5:-- INSERT --} | ]]) -- ending Insert mode should put the cursor back on the ':' feed('<Esc>') screen:expect([[ ^: | - {0:~ }|*4 + {1:~ }|*4 | ]]) end) diff --git a/test/functional/legacy/erasebackword_spec.lua b/test/functional/legacy/erasebackword_spec.lua index 46057fe599..3042c52784 100644 --- a/test/functional/legacy/erasebackword_spec.lua +++ b/test/functional/legacy/erasebackword_spec.lua @@ -1,7 +1,8 @@ -- Test for CTRL-W in Insert mode -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, expect = helpers.clear, helpers.feed, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, expect = n.clear, n.feed, n.expect describe('CTRL-W in Insert mode', function() setup(clear) diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua index 21d0ce118d..554af9418d 100644 --- a/test/functional/legacy/eval_spec.lua +++ b/test/functional/legacy/eval_spec.lua @@ -1,13 +1,15 @@ -- Test for various eval features. -local helpers = require('test.functional.helpers')(after_each) -local assert_alive = helpers.assert_alive -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, command, expect = helpers.clear, helpers.command, helpers.expect -local eq, eval, write_file = helpers.eq, helpers.eval, helpers.write_file -local poke_eventloop = helpers.poke_eventloop -local exc_exec = helpers.exc_exec -local dedent = helpers.dedent +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local assert_alive = n.assert_alive +local feed, insert, source = n.feed, n.insert, n.source +local clear, command, expect = n.clear, n.command, n.expect +local eq, eval, write_file = t.eq, n.eval, t.write_file +local poke_eventloop = n.poke_eventloop +local exc_exec = n.exc_exec +local dedent = t.dedent describe('eval', function() setup(function() diff --git a/test/functional/legacy/ex_mode_spec.lua b/test/functional/legacy/ex_mode_spec.lua index ae4c4309d1..574c3e4069 100644 --- a/test/functional/legacy/ex_mode_spec.lua +++ b/test/functional/legacy/ex_mode_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local api = helpers.api -local poke_eventloop = helpers.poke_eventloop + +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local feed = n.feed +local api = n.api +local poke_eventloop = n.poke_eventloop before_each(clear) @@ -45,60 +47,55 @@ describe('Ex mode', function() it('substitute confirmation prompt', function() command('set noincsearch nohlsearch inccommand=') local screen = Screen.new(60, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, reverse = true }, -- MsgSeparator - [1] = { foreground = Screen.colors.Brown }, -- LineNr - [2] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - }) screen:attach() command([[call setline(1, ['foo foo', 'foo foo', 'foo foo'])]]) command([[set number]]) feed('gQ') screen:expect([[ - {1: 1 }foo foo | - {1: 2 }foo foo | - {1: 3 }foo foo | - {0: }| + {8: 1 }foo foo | + {8: 2 }foo foo | + {8: 3 }foo foo | + {3: }| Entering Ex mode. Type "visual" to go to Normal mode. | :^ | ]]) feed('%s/foo/bar/gc<CR>') screen:expect([[ - {1: 1 }foo foo | - {0: }| + {8: 1 }foo foo | + {3: }| Entering Ex mode. Type "visual" to go to Normal mode. | :%s/foo/bar/gc | - {1: 1 }foo foo | + {8: 1 }foo foo | ^^^^ | ]]) feed('N<CR>') screen:expect([[ Entering Ex mode. Type "visual" to go to Normal mode. | :%s/foo/bar/gc | - {1: 1 }foo foo | + {8: 1 }foo foo | ^^^N | - {1: 1 }foo foo | + {8: 1 }foo foo | ^^^^ | ]]) feed('n<CR>') screen:expect([[ - {1: 1 }foo foo | + {8: 1 }foo foo | ^^^N | - {1: 1 }foo foo | + {8: 1 }foo foo | ^^^n | - {1: 1 }foo foo | + {8: 1 }foo foo | ^^^^ | ]]) feed('y<CR>') feed('q<CR>') screen:expect([[ - {1: 1 }foo foo | + {8: 1 }foo foo | ^^^y | - {1: 2 }foo foo | + {8: 2 }foo foo | ^^^q | - {1: 2 }foo foo | + {8: 2 }foo foo | :^ | ]]) @@ -106,35 +103,31 @@ describe('Ex mode', function() feed('<CR>') screen:expect([[ ^^^y | - {1: 2 }foo foo | + {8: 2 }foo foo | ^^^q | - {1: 2 }foo foo | - {1: 3 }foo foo | + {8: 2 }foo foo | + {8: 3 }foo foo | :^ | ]]) feed(':vi<CR>') screen:expect([[ - {1: 1 }foo bar | - {1: 2 }foo foo | - {1: 3 }^foo foo | - {2:~ }|*2 + {8: 1 }foo bar | + {8: 2 }foo foo | + {8: 3 }^foo foo | + {1:~ }|*2 | ]]) end) it('pressing Ctrl-C in :append inside a loop in Ex mode does not hang', function() local screen = Screen.new(60, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, reverse = true }, -- MsgSeparator - [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - }) screen:attach() feed('gQ') feed('for i in range(1)<CR>') feed('append<CR>') screen:expect([[ - {0: }| + {3: }| Entering Ex mode. Type "visual" to go to Normal mode. | :for i in range(1) | | diff --git a/test/functional/legacy/excmd_spec.lua b/test/functional/legacy/excmd_spec.lua index 41f14c4645..de3d498f27 100644 --- a/test/functional/legacy/excmd_spec.lua +++ b/test/functional/legacy/excmd_spec.lua @@ -1,18 +1,20 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local exec = helpers.exec -local exec_lua = helpers.exec_lua -local expect_exit = helpers.expect_exit -local feed = helpers.feed -local fn = helpers.fn -local api = helpers.api -local read_file = helpers.read_file -local source = helpers.source -local eq = helpers.eq -local write_file = helpers.write_file -local is_os = helpers.is_os + +local clear = n.clear +local command = n.command +local exec = n.exec +local exec_lua = n.exec_lua +local expect_exit = n.expect_exit +local feed = n.feed +local fn = n.fn +local api = n.api +local read_file = t.read_file +local source = n.source +local eq = t.eq +local write_file = t.write_file +local is_os = t.is_os local function sizeoflong() if not exec_lua('return pcall(require, "ffi")') then @@ -48,12 +50,6 @@ describe(':confirm command dialog', function() local function start_new() clear() screen = Screen.new(75, 20) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { bold = true, reverse = true }, -- StatusLine, MsgSeparator - [2] = { reverse = true }, -- StatusLineNC - [3] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg - }) screen:attach() end @@ -76,17 +72,17 @@ describe(':confirm command dialog', function() feed(':confirm qall\n') screen:expect([[ bar2 | - {0:~ }|*5 + {1:~ }|*5 {2:Xbar [+] }| foo2 | - {0:~ }|*4 + {1:~ }|*4 {2:Xfoo [+] }| | - {0:~ }|*2 - {1: }| + {1:~ }|*2 + {3: }| :confirm qall | - {3:Save changes to "Xbar"?} | - {3:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | + {6:Save changes to "Xbar"?} | + {6:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | ]]) expect_exit(1000, feed, 'A') @@ -106,17 +102,17 @@ describe(':confirm command dialog', function() feed(':confirm qall\n') screen:expect([[ bar3 | - {0:~ }|*5 + {1:~ }|*5 {2:Xbar [+] }| foo3 | - {0:~ }|*4 + {1:~ }|*4 {2:Xfoo [+] }| | - {0:~ }|*2 - {1: }| + {1:~ }|*2 + {3: }| :confirm qall | - {3:Save changes to "Xbar"?} | - {3:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | + {6:Save changes to "Xbar"?} | + {6:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | ]]) expect_exit(1000, feed, 'D') @@ -136,33 +132,33 @@ describe(':confirm command dialog', function() feed(':confirm qall\n') screen:expect([[ bar4 | - {0:~ }|*5 + {1:~ }|*5 {2:Xbar [+] }| foo4 | - {0:~ }|*4 + {1:~ }|*4 {2:Xfoo [+] }| | - {0:~ }|*2 - {1: }| + {1:~ }|*2 + {3: }| :confirm qall | - {3:Save changes to "Xbar"?} | - {3:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | + {6:Save changes to "Xbar"?} | + {6:[Y]es, (N)o, Save (A)ll, (D)iscard All, (C)ancel: }^ | ]]) feed('N') screen:expect([[ bar4 | - {0:~ }|*5 + {1:~ }|*5 {2:Xbar [+] }| foo4 | - {0:~ }|*4 + {1:~ }|*4 {2:Xfoo [+] }| | - {1: }| + {3: }| :confirm qall | - {3:Save changes to "Xbar"?} | + {6:Save changes to "Xbar"?} | | - {3:Save changes to "Xfoo"?} | - {3:[Y]es, (N)o, (C)ancel: }^ | + {6:Save changes to "Xfoo"?} | + {6:[Y]es, (N)o, (C)ancel: }^ | ]]) expect_exit(1000, feed, 'Y') @@ -186,39 +182,39 @@ describe(':confirm command dialog', function() feed(':confirm close\n') screen:expect([[ abc | - {0:~ }|*3 - {1:[No Name] [+] }| + {1:~ }|*3 + {3:[No Name] [+] }| | - {1: }| + {3: }| :confirm close | - {3:Save changes to "Untitled"?} | - {3:[Y]es, (N)o, (C)ancel: }^ | + {6:Save changes to "Untitled"?} | + {6:[Y]es, (N)o, (C)ancel: }^ | ]]) feed('C') screen:expect([[ ^abc | - {0:~ }|*3 - {1:[No Name] [+] }| + {1:~ }|*3 + {3:[No Name] [+] }| | - {0:~ }|*2 + {1:~ }|*2 {2:[No Name] }| | ]]) feed(':confirm close\n') screen:expect([[ abc | - {0:~ }|*3 - {1:[No Name] [+] }| + {1:~ }|*3 + {3:[No Name] [+] }| | - {1: }| + {3: }| :confirm close | - {3:Save changes to "Untitled"?} | - {3:[Y]es, (N)o, (C)ancel: }^ | + {6:Save changes to "Untitled"?} | + {6:[Y]es, (N)o, (C)ancel: }^ | ]]) feed('N') screen:expect([[ ^ | - {0:~ }|*8 + {1:~ }|*8 | ]]) end) @@ -237,16 +233,16 @@ describe(':confirm command dialog', function() feed(':confirm q\n') screen:expect([[ foo | - {0:~ }|*3 - {1: }| + {1:~ }|*3 + {3: }| :confirm q | - {3:Save changes to "Untitled"?} | - {3:[Y]es, (N)o, (C)ancel: }^ | + {6:Save changes to "Untitled"?} | + {6:[Y]es, (N)o, (C)ancel: }^ | ]]) feed('C') screen:expect([[ ^abc | - {0:~ }|*6 + {1:~ }|*6 | ]]) @@ -254,16 +250,16 @@ describe(':confirm command dialog', function() feed(':confirm wq\n') screen:expect([[ foo | - {0:~ }|*3 - {1: }| + {1:~ }|*3 + {3: }| "Xfoo" [noeol] 1L, 3B written | - {3:Save changes to "Untitled"?} | - {3:[Y]es, (N)o, (C)ancel: }^ | + {6:Save changes to "Untitled"?} | + {6:[Y]es, (N)o, (C)ancel: }^ | ]]) feed('C') screen:expect([[ ^abc | - {0:~ }|*6 + {1:~ }|*6 "Xfoo" [noeol] 1L, 3B written | ]]) @@ -286,17 +282,17 @@ describe(':confirm command dialog', function() feed(':set ro | confirm w\n') screen:expect([[ foobar | - {0:~ }|*2 - {1: }| + {1:~ }|*2 + {3: }| :set ro | confirm w | - {3:'readonly' option is set for "Xconfirm_write_ro".} | - {3:Do you wish to write anyway?} | - {3:(Y)es, [N]o: }^ | + {6:'readonly' option is set for "Xconfirm_write_ro".} | + {6:Do you wish to write anyway?} | + {6:(Y)es, [N]o: }^ | ]]) feed('N') screen:expect([[ fooba^r | - {0:~ }|*5 + {1:~ }|*5 | 1,6 All | ]]) @@ -305,35 +301,35 @@ describe(':confirm command dialog', function() feed(':confirm w\n') screen:expect([[ foobar | - {0:~ }|*2 - {1: }| + {1:~ }|*2 + {3: }| :confirm w | - {3:'readonly' option is set for "Xconfirm_write_ro".} | - {3:Do you wish to write anyway?} | - {3:(Y)es, [N]o: }^ | + {6:'readonly' option is set for "Xconfirm_write_ro".} | + {6:Do you wish to write anyway?} | + {6:(Y)es, [N]o: }^ | ]]) feed('Y') if is_os('win') then screen:expect([[ foobar | - {0:~ }| - {1: }| + {1:~ }| + {3: }| :confirm w | - {3:'readonly' option is set for "Xconfirm_write_ro".} | - {3:Do you wish to write anyway?} | + {6:'readonly' option is set for "Xconfirm_write_ro".} | + {6:Do you wish to write anyway?} | "Xconfirm_write_ro" [unix] 1L, 7B written | - {3:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) else screen:expect([[ foobar | - {0:~ }| - {1: }| + {1:~ }| + {3: }| :confirm w | - {3:'readonly' option is set for "Xconfirm_write_ro".} | - {3:Do you wish to write anyway?} | + {6:'readonly' option is set for "Xconfirm_write_ro".} | + {6:Do you wish to write anyway?} | "Xconfirm_write_ro" 1L, 7B written | - {3:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) end eq('foobar\n', read_file('Xconfirm_write_ro')) @@ -344,36 +340,36 @@ describe(':confirm command dialog', function() feed(':set noro | silent undo | confirm w\n') screen:expect([[ foobar | - {0:~ }| - {1: }| + {1:~ }| + {3: }| :set noro | silent undo | confirm w | - {3:File permissions of "Xconfirm_write_ro" are read-only.} | - {3:It may still be possible to write it.} | - {3:Do you wish to try?} | - {3:(Y)es, [N]o: }^ | + {6:File permissions of "Xconfirm_write_ro" are read-only.} | + {6:It may still be possible to write it.} | + {6:Do you wish to try?} | + {6:(Y)es, [N]o: }^ | ]]) feed('Y') if is_os('win') then screen:expect([[ foobar | - {1: }| + {3: }| :set noro | silent undo | confirm w | - {3:File permissions of "Xconfirm_write_ro" are read-only.} | - {3:It may still be possible to write it.} | - {3:Do you wish to try?} | + {6:File permissions of "Xconfirm_write_ro" are read-only.} | + {6:It may still be possible to write it.} | + {6:Do you wish to try?} | "Xconfirm_write_ro" [unix] 1L, 4B written | - {3:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) else screen:expect([[ foobar | - {1: }| + {3: }| :set noro | silent undo | confirm w | - {3:File permissions of "Xconfirm_write_ro" are read-only.} | - {3:It may still be possible to write it.} | - {3:Do you wish to try?} | + {6:File permissions of "Xconfirm_write_ro" are read-only.} | + {6:It may still be possible to write it.} | + {6:Do you wish to try?} | "Xconfirm_write_ro" 1L, 4B written | - {3:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) end eq('foo\n', read_file('Xconfirm_write_ro')) @@ -399,10 +395,10 @@ describe(':confirm command dialog', function() b | c | d | - {1: }| + {3: }| :confirm 2,3w | - {3:Write partial file?} | - {3:(Y)es, [N]o: }^ | + {6:Write partial file?} | + {6:(Y)es, [N]o: }^ | ]]) feed('N') screen:expect([[ @@ -410,7 +406,7 @@ describe(':confirm command dialog', function() b | c | d | - {0:~ }|*2 + {1:~ }|*2 | 1,1 All | ]]) @@ -423,10 +419,10 @@ describe(':confirm command dialog', function() b | c | d | - {1: }| + {3: }| :confirm 2,3w | - {3:Write partial file?} | - {3:(Y)es, [N]o: }^ | + {6:Write partial file?} | + {6:(Y)es, [N]o: }^ | ]]) feed('Y') if is_os('win') then @@ -434,22 +430,22 @@ describe(':confirm command dialog', function() a | b | c | - {1: }| + {3: }| :confirm 2,3w | - {3:Write partial file?} | + {6:Write partial file?} | "Xwrite_partial" [New][unix] 2L, 4B written | - {3:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) else screen:expect([[ a | b | c | - {1: }| + {3: }| :confirm 2,3w | - {3:Write partial file?} | + {6:Write partial file?} | "Xwrite_partial" [New] 2L, 4B written | - {3:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) end eq('b\nc\n', read_file('Xwrite_partial')) diff --git a/test/functional/legacy/filechanged_spec.lua b/test/functional/legacy/filechanged_spec.lua deleted file mode 100644 index 46ecfdcd63..0000000000 --- a/test/functional/legacy/filechanged_spec.lua +++ /dev/null @@ -1,142 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, source = helpers.clear, helpers.source -local call, eq, api = helpers.call, helpers.eq, helpers.api -local is_os = helpers.is_os -local skip = helpers.skip - -local function expected_empty() - eq({}, api.nvim_get_vvar('errors')) -end - -describe('file changed dialog', function() - before_each(function() - clear() - api.nvim_ui_attach(80, 24, {}) - api.nvim_set_option_value('autoread', false, {}) - api.nvim_set_option_value('fsync', true, {}) - end) - - it('works', function() - skip(is_os('win')) - source([[ - func Test_file_changed_dialog() - au! FileChangedShell - - new Xchanged_d - call setline(1, 'reload this') - write - " Need to wait until the timestamp would change by at least a second. - sleep 2 - silent !echo 'extra line' >>Xchanged_d - call nvim_input('L') - checktime - call assert_match('W11:', v:warningmsg) - call assert_equal(2, line('$')) - call assert_equal('reload this', getline(1)) - call assert_equal('extra line', getline(2)) - - " delete buffer, only shows an error, no prompt - silent !rm Xchanged_d - checktime - call assert_match('E211:', v:warningmsg) - call assert_equal(2, line('$')) - call assert_equal('extra line', getline(2)) - let v:warningmsg = 'empty' - - " change buffer, recreate the file and reload - call setline(1, 'buffer is changed') - silent !echo 'new line' >Xchanged_d - call nvim_input('L') - checktime - call assert_match('W12:', v:warningmsg) - call assert_equal(1, line('$')) - call assert_equal('new line', getline(1)) - - " Only mode changed, reload - silent !chmod +x Xchanged_d - call nvim_input('L') - checktime - call assert_match('W16:', v:warningmsg) - call assert_equal(1, line('$')) - call assert_equal('new line', getline(1)) - - " Only time changed, no prompt - sleep 2 - silent !touch Xchanged_d - let v:warningmsg = '' - checktime Xchanged_d - call assert_equal('', v:warningmsg) - call assert_equal(1, line('$')) - call assert_equal('new line', getline(1)) - - " File created after starting to edit it - call delete('Xchanged_d') - new Xchanged_d - call writefile(['one'], 'Xchanged_d') - call nvim_input('L') - checktime Xchanged_d - call assert_equal(['one'], getline(1, '$')) - close! - - bwipe! - call delete('Xchanged_d') - endfunc - ]]) - call('Test_file_changed_dialog') - expected_empty() - end) - - it('works with FileChangedShell', function() - source([[ - func Test_FileChangedShell_edit_dialog() - new Xchanged_r - call setline(1, 'reload this') - set fileformat=unix - silent write " Use :silent to prevent a hit-enter prompt - - " File format changed, reload (content only) via prompt - augroup testreload - au! - au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'ask' - augroup END - call assert_equal(&fileformat, 'unix') - sleep 10m " make the test less flaky in Nvim - call writefile(["line1\r", "line2\r"], 'Xchanged_r') - let g:reason = '' - call nvim_input('L') " load file content only - checktime - call assert_equal('changed', g:reason) - call assert_equal(&fileformat, 'unix') - call assert_equal("line1\r", getline(1)) - call assert_equal("line2\r", getline(2)) - %s/\r - silent write " Use :silent to prevent a hit-enter prompt - - " File format changed, reload (file and options) via prompt - augroup testreload - au! - au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'ask' - augroup END - call assert_equal(&fileformat, 'unix') - sleep 10m " make the test less flaky in Nvim - call writefile(["line1\r", "line2\r"], 'Xchanged_r') - let g:reason = '' - call nvim_input('a') " load file content and options - checktime - call assert_equal('changed', g:reason) - call assert_equal(&fileformat, 'dos') - call assert_equal("line1", getline(1)) - call assert_equal("line2", getline(2)) - set fileformat=unix - silent write " Use :silent to prevent a hit-enter prompt - - au! testreload - bwipe! - call delete(undofile('Xchanged_r')) - call delete('Xchanged_r') - endfunc - ]]) - call('Test_FileChangedShell_edit_dialog') - expected_empty() - end) -end) diff --git a/test/functional/legacy/fixeol_spec.lua b/test/functional/legacy/fixeol_spec.lua index 01b87ac9a0..21f0b9f001 100644 --- a/test/functional/legacy/fixeol_spec.lua +++ b/test/functional/legacy/fixeol_spec.lua @@ -1,8 +1,9 @@ -- Tests for 'fixeol' -local helpers = require('test.functional.helpers')(after_each) -local feed = helpers.feed -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed = n.feed +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('fixeol', function() local function rmtestfiles() diff --git a/test/functional/legacy/fnamemodify_spec.lua b/test/functional/legacy/fnamemodify_spec.lua index 570b523d92..ce8c710a46 100644 --- a/test/functional/legacy/fnamemodify_spec.lua +++ b/test/functional/legacy/fnamemodify_spec.lua @@ -1,11 +1,13 @@ -- Test filename modifiers. -local helpers = require('test.functional.helpers')(after_each) -local clear, source = helpers.clear, helpers.source -local call, eq, nvim = helpers.call, helpers.eq, helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, source = n.clear, n.source +local call, eq, api = n.call, t.eq, n.api local function expected_empty() - eq({}, nvim.nvim_get_vvar('errors')) + eq({}, api.nvim_get_vvar('errors')) end describe('filename modifiers', function() diff --git a/test/functional/legacy/fold_spec.lua b/test/functional/legacy/fold_spec.lua index c39aae87d2..4fea1ef113 100644 --- a/test/functional/legacy/fold_spec.lua +++ b/test/functional/legacy/fold_spec.lua @@ -1,25 +1,19 @@ -- Tests for folding. + +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local feed, insert, feed_command, expect_any = - helpers.feed, helpers.insert, helpers.feed_command, helpers.expect_any -local command = helpers.command -local exec = helpers.exec +local feed, insert, feed_command, expect_any = n.feed, n.insert, n.feed_command, n.expect_any +local command = n.command +local exec = n.exec describe('folding', function() local screen before_each(function() - helpers.clear() + n.clear() screen = Screen.new(45, 8) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [2] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey }, -- Folded - [3] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Grey }, -- FoldColumn - [4] = { foreground = Screen.colors.Brown }, -- LineNr - }) screen:attach() end) @@ -67,7 +61,7 @@ describe('folding', function() feed('kYpj') feed_command('call append("$", foldlevel("."))') - helpers.poke_eventloop() + n.poke_eventloop() screen:expect([[ dd {{{ | ee {{{ }}} | @@ -93,7 +87,7 @@ describe('folding', function() feed_command('call append("$", foldlevel(2))') feed('zR') - helpers.poke_eventloop() + n.poke_eventloop() screen:expect([[ aa | bb | @@ -222,15 +216,15 @@ describe('folding', function() command('call setline(1, ["{{{1", "nline 1", "{{{1", "line 2"])') screen:expect([[ - {3:+ }{4: 0 }{2:^+-- 2 lines: ·························}| - {3:+ }{4: 1 }{2:+-- 2 lines: ·························}| + {7:+ }{8: 0 }{13:^+-- 2 lines: ·························}| + {7:+ }{8: 1 }{13:+-- 2 lines: ·························}| {1:~ }|*5 | ]]) feed('j') screen:expect([[ - {3:+ }{4: 1 }{2:+-- 2 lines: ·························}| - {3:+ }{4: 0 }{2:^+-- 2 lines: ·························}| + {7:+ }{8: 1 }{13:+-- 2 lines: ·························}| + {7:+ }{8: 0 }{13:^+-- 2 lines: ·························}| {1:~ }|*5 | ]]) @@ -246,7 +240,7 @@ describe('folding', function() screen:expect([[ ^one | - {2:+-- 2 lines: two····························}| + {13:+-- 2 lines: two····························}| four | {1:~ }|*4 | @@ -263,7 +257,7 @@ describe('folding', function() feed('4G') screen:expect([[ one | - {2:+-- 2 lines: two····························}| + {13:+-- 2 lines: two····························}| ^four | {1:~ }|*4 | @@ -280,7 +274,7 @@ describe('folding', function() feed('1G') screen:expect([[ ^one | - {2:+-- 2 lines: two····························}| + {13:+-- 2 lines: two····························}| four | {1:~ }|*4 | @@ -297,7 +291,7 @@ describe('folding', function() feed('k') screen:expect([[ ^one | - {2:+-- 2 lines: two····························}| + {13:+-- 2 lines: two····························}| four | {1:~ }|*4 | diff --git a/test/functional/legacy/function_sort_spec.lua b/test/functional/legacy/function_sort_spec.lua index 36128bb0a2..b55c5437ef 100644 --- a/test/functional/legacy/function_sort_spec.lua +++ b/test/functional/legacy/function_sort_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) - -local eq = helpers.eq -local neq = helpers.neq -local eval = helpers.eval -local clear = helpers.clear -local source = helpers.source -local exc_exec = helpers.exc_exec +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local neq = t.neq +local eval = n.eval +local clear = n.clear +local source = n.source +local exc_exec = n.exc_exec describe('sort', function() before_each(clear) diff --git a/test/functional/legacy/getcwd_spec.lua b/test/functional/legacy/getcwd_spec.lua index eae13da528..c95a010c5a 100644 --- a/test/functional/legacy/getcwd_spec.lua +++ b/test/functional/legacy/getcwd_spec.lua @@ -1,14 +1,16 @@ -- Tests for getcwd(), haslocaldir(), and :lcd -local helpers = require('test.functional.helpers')(after_each) -local eq, eval, source = helpers.eq, helpers.eval, helpers.source -local call, clear, command = helpers.call, helpers.clear, helpers.command +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq, eval, source = t.eq, n.eval, n.source +local call, clear, command = n.call, n.clear, n.command describe('getcwd', function() before_each(clear) after_each(function() - helpers.rmdir('Xtopdir') + n.rmdir('Xtopdir') end) it('is working', function() diff --git a/test/functional/legacy/gf_spec.lua b/test/functional/legacy/gf_spec.lua index b51f671bee..4c98edf8e0 100644 --- a/test/functional/legacy/gf_spec.lua +++ b/test/functional/legacy/gf_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local pcall_err = t.pcall_err describe('gf', function() before_each(clear) diff --git a/test/functional/legacy/glob2regpat_spec.lua b/test/functional/legacy/glob2regpat_spec.lua index de304f3e4b..6d08a77386 100644 --- a/test/functional/legacy/glob2regpat_spec.lua +++ b/test/functional/legacy/glob2regpat_spec.lua @@ -1,6 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq, eval = helpers.eq, helpers.eval +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq, eval = t.eq, n.eval describe('glob2regpat()', function() before_each(clear) diff --git a/test/functional/legacy/global_spec.lua b/test/functional/legacy/global_spec.lua index 2c92b7814a..718fd421b0 100644 --- a/test/functional/legacy/global_spec.lua +++ b/test/functional/legacy/global_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local feed = helpers.feed -local poke_eventloop = helpers.poke_eventloop + +local clear = n.clear +local exec = n.exec +local feed = n.feed +local poke_eventloop = n.poke_eventloop before_each(clear) @@ -11,10 +12,6 @@ describe(':global', function() -- oldtest: Test_interrupt_global() it('can be interrupted using Ctrl-C in cmdline mode vim-patch:9.0.0082', function() local screen = Screen.new(75, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, reverse = true }, -- MsgSeparator - [1] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg - }) screen:attach() exec([[ @@ -29,7 +26,7 @@ describe(':global', function() screen:expect([[ ^foo | foo |*4 - {1:Interrupted} | + {9:Interrupted} | ]]) -- Also test in Ex mode @@ -37,11 +34,11 @@ describe(':global', function() poke_eventloop() -- Wait for :sleep to start feed('<C-C>') screen:expect([[ - {0: }| + {3: }| Entering Ex mode. Type "visual" to go to Normal mode. | :g/foo/norm :; | | - {1:Interrupted} | + {9:Interrupted} | :^ | ]]) end) diff --git a/test/functional/legacy/highlight_spec.lua b/test/functional/legacy/highlight_spec.lua index 3d06bf3978..35afd9bb4a 100644 --- a/test/functional/legacy/highlight_spec.lua +++ b/test/functional/legacy/highlight_spec.lua @@ -1,12 +1,14 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local clear, feed = helpers.clear, helpers.feed -local expect = helpers.expect -local eq = helpers.eq -local poke_eventloop = helpers.poke_eventloop -local exc_exec = helpers.exc_exec -local feed_command = helpers.feed_command -local exec = helpers.exec + +local clear, feed = n.clear, n.feed +local expect = n.expect +local eq = t.eq +local poke_eventloop = n.poke_eventloop +local exc_exec = n.exc_exec +local feed_command = n.feed_command +local exec = n.exec before_each(clear) @@ -22,15 +24,15 @@ describe(':highlight', function() -- is discarded resulting in test failure screen:expect([[ :highlight | - SpecialKey xxx ctermfg=4 | - guifg=Blue | - EndOfBuffer xxx links to NonText| + SpecialKey {18:xxx} {18:ctermfg=}4 | + {18:guifg=}Blue | + EndOfBuffer {1:xxx} {18:links to} NonText| | - TermCursor xxx cterm=reverse | - gui=reverse | + TermCursor {2:xxx} {18:cterm=}reverse | + {18:gui=}reverse | TermCursorNC xxx cleared | - NonText xxx ctermfg=12 | - -- More --^ | + NonText {1:xxx} {18:ctermfg=}12 | + {6:-- More --}^ | ]]) feed('q') poke_eventloop() -- wait until we're back to normal @@ -99,11 +101,6 @@ describe('Visual selection highlight', function() -- oldtest: Test_visual_sbr() it("when 'showbreak' is set", function() local screen = Screen.new(60, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, -- Visual - [2] = { bold = true }, -- ModeMsg - }) screen:attach() exec([[ set showbreak=> @@ -112,9 +109,9 @@ describe('Visual selection highlight', function() ]]) feed('v$') screen:expect([[ - {0:>}{1:n, no sea takimata sanctus est Lorem ipsum dolor sit amet.}^ | + {1:>}{17:n, no sea takimata sanctus est Lorem ipsum dolor sit amet.}^ | |*4 - {2:-- VISUAL --} | + {5:-- VISUAL --} | ]]) end) end) diff --git a/test/functional/legacy/increment_spec.lua b/test/functional/legacy/increment_spec.lua index a81044114c..c9c2324c42 100644 --- a/test/functional/legacy/increment_spec.lua +++ b/test/functional/legacy/increment_spec.lua @@ -1,9 +1,11 @@ -- Tests for using Ctrl-A/Ctrl-X on visual selections -local helpers = require('test.functional.helpers')(after_each) -local source, command = helpers.source, helpers.command -local call, clear = helpers.call, helpers.clear -local eq, nvim = helpers.eq, helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local source, command = n.source, n.command +local call, clear = n.call, n.clear +local eq, api = t.eq, n.api describe('Ctrl-A/Ctrl-X on visual selections', function() before_each(function() @@ -743,18 +745,18 @@ describe('Ctrl-A/Ctrl-X on visual selections', function() it('works on Test ' .. id, function() command('set nrformats&vi') -- &vi makes Vim compatible call('Test_visual_increment_' .. id) - eq({}, nvim.nvim_get_vvar('errors')) + eq({}, api.nvim_get_vvar('errors')) end) end it('does not drop leading zeroes', function() command('set nrformats&vi') -- &vi makes Vim compatible call('Test_normal_increment_01') - eq({}, nvim.nvim_get_vvar('errors')) + eq({}, api.nvim_get_vvar('errors')) end) it('maintains correct column after CTRL-A', function() call('Test_normal_increment_02') - eq({}, nvim.nvim_get_vvar('errors')) + eq({}, api.nvim_get_vvar('errors')) end) end) diff --git a/test/functional/legacy/insertcount_spec.lua b/test/functional/legacy/insertcount_spec.lua index e53c9dc6ce..53402c2163 100644 --- a/test/functional/legacy/insertcount_spec.lua +++ b/test/functional/legacy/insertcount_spec.lua @@ -1,8 +1,9 @@ -- Tests for repeating insert and replace. -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect = helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect = n.feed_command, n.expect describe('insertcount', function() setup(clear) diff --git a/test/functional/legacy/join_spec.lua b/test/functional/legacy/join_spec.lua index d683d25eb0..397a714b2f 100644 --- a/test/functional/legacy/join_spec.lua +++ b/test/functional/legacy/join_spec.lua @@ -1,8 +1,10 @@ -- Test for joining lines -local helpers = require('test.functional.helpers')(after_each) -local clear, eq = helpers.clear, helpers.eq -local eval, command = helpers.eval, helpers.command +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq = n.clear, t.eq +local eval, command = n.eval, n.command describe('joining lines', function() before_each(clear) diff --git a/test/functional/legacy/lispwords_spec.lua b/test/functional/legacy/lispwords_spec.lua index efac8775d3..b0130320ed 100644 --- a/test/functional/legacy/lispwords_spec.lua +++ b/test/functional/legacy/lispwords_spec.lua @@ -1,9 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local command = helpers.command -local source = helpers.source +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local eval = n.eval +local command = n.command +local source = n.source describe('lispwords', function() before_each(clear) diff --git a/test/functional/legacy/listchars_spec.lua b/test/functional/legacy/listchars_spec.lua index 746e0550a6..db9ec7fc9d 100644 --- a/test/functional/legacy/listchars_spec.lua +++ b/test/functional/legacy/listchars_spec.lua @@ -1,9 +1,10 @@ -- Tests for 'listchars' display with 'list' and :list. -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed, insert, exec = helpers.feed, helpers.insert, helpers.exec -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect + +local feed, insert, exec = n.feed, n.insert, n.exec +local clear, feed_command, expect = n.clear, n.feed_command, n.expect -- luacheck: ignore 621 (Indentation) describe("'listchars'", function() @@ -102,12 +103,6 @@ describe("'listchars'", function() it('"exceeds" character does not appear in foldcolumn vim-patch:8.2.3121', function() local screen = Screen.new(60, 10) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [2] = { bold = true, reverse = true }, -- StatusLine - [3] = { reverse = true }, -- StatusLineNC - [4] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue }, -- FoldColumn, SignColumn - }) screen:attach() exec([[ call setline(1, ['aaa', '', 'a', 'aaaaaa']) @@ -117,83 +112,83 @@ describe("'listchars'", function() ]]) feed('13<C-W>>') screen:expect([[ - {4: }aaa │{4: }a{1:>}│{4: }^aaa | - {4: } │{4: } │{4: } | - {4: }a │{4: }a │{4: }a | - {4: }aaaaaa │{4: }a{1:>}│{4: }aaaaaa | + {7: }aaa │{7: }a{1:>}│{7: }^aaa | + {7: } │{7: } │{7: } | + {7: }a │{7: }a │{7: }a | + {7: }aaaaaa │{7: }a{1:>}│{7: }aaaaaa | {1:~ }│{1:~ }│{1:~ }|*4 - {3:[No Name] [+] <[+] }{2:[No Name] [+] }| + {2:[No Name] [+] <[+] }{3:[No Name] [+] }| | ]]) feed('<C-W>>') screen:expect([[ - {4: }aaa │{4: }{1:>}│{4: }^aaa | - {4: } │{4: } │{4: } | - {4: }a │{4: }a│{4: }a | - {4: }aaaaaa │{4: }{1:>}│{4: }aaaaaa | + {7: }aaa │{7: }{1:>}│{7: }^aaa | + {7: } │{7: } │{7: } | + {7: }a │{7: }a│{7: }a | + {7: }aaaaaa │{7: }{1:>}│{7: }aaaaaa | {1:~ }│{1:~ }│{1:~ }|*4 - {3:[No Name] [+] <+] }{2:[No Name] [+] }| + {2:[No Name] [+] <+] }{3:[No Name] [+] }| | ]]) feed('<C-W>>') screen:expect([[ - {4: }aaa │{4: }│{4: }^aaa | - {4: } │{4: }│{4: } | - {4: }a │{4: }│{4: }a | - {4: }aaaaaa │{4: }│{4: }aaaaaa | + {7: }aaa │{7: }│{7: }^aaa | + {7: } │{7: }│{7: } | + {7: }a │{7: }│{7: }a | + {7: }aaaaaa │{7: }│{7: }aaaaaa | {1:~ }│{1:~ }│{1:~ }|*4 - {3:[No Name] [+] <] }{2:[No Name] [+] }| + {2:[No Name] [+] <] }{3:[No Name] [+] }| | ]]) feed('<C-W>>') screen:expect([[ - {4: }aaa │{4: }│{4: }^aaa | - {4: } │{4: }│{4: } | - {4: }a │{4: }│{4: }a | - {4: }aaaaaa │{4: }│{4: }aaaaaa | + {7: }aaa │{7: }│{7: }^aaa | + {7: } │{7: }│{7: } | + {7: }a │{7: }│{7: }a | + {7: }aaaaaa │{7: }│{7: }aaaaaa | {1:~ }│{1:~ }│{1:~ }|*4 - {3:[No Name] [+] < }{2:[No Name] [+] }| + {2:[No Name] [+] < }{3:[No Name] [+] }| | ]]) feed('<C-W>>') screen:expect([[ - {4: }aaa │{4: }│{4: }^aaa | - {4: } │{4: }│{4: } | - {4: }a │{4: }│{4: }a | - {4: }aaaaaa │{4: }│{4: }aaaaaa | + {7: }aaa │{7: }│{7: }^aaa | + {7: } │{7: }│{7: } | + {7: }a │{7: }│{7: }a | + {7: }aaaaaa │{7: }│{7: }aaaaaa | {1:~ }│{1:~}│{1:~ }|*4 - {3:[No Name] [+] < }{2:[No Name] [+] }| + {2:[No Name] [+] < }{3:[No Name] [+] }| | ]]) feed('<C-W>h') feed_command('set nowrap foldcolumn=4') screen:expect([[ - {4: }aaa │{4: }^aaa │{4: }aaa | - {4: } │{4: } │{4: } | - {4: }a │{4: }a │{4: }a | - {4: }aaaaaa │{4: }aaaaaa │{4: }aaaaaa | + {7: }aaa │{7: }^aaa │{7: }aaa | + {7: } │{7: } │{7: } | + {7: }a │{7: }a │{7: }a | + {7: }aaaaaa │{7: }aaaaaa │{7: }aaaaaa | {1:~ }│{1:~ }│{1:~ }|*4 - {3:[No Name] [+] }{2:[No Name] [+] }{3:[No Name] [+] }| + {2:[No Name] [+] }{3:[No Name] [+] }{2:[No Name] [+] }| :set nowrap foldcolumn=4 | ]]) feed('15<C-W><lt>') screen:expect([[ - {4: }aaa │{4: }│{4: }aaa | - {4: } │{4: }│{4: } | - {4: }a │{4: }│{4: }a | - {4: }aaaaaa │{4: ^ }│{4: }aaaaaa | + {7: }aaa │{7: }│{7: }aaa | + {7: } │{7: }│{7: } | + {7: }a │{7: }│{7: }a | + {7: }aaaaaa │{7: ^ }│{7: }aaaaaa | {1:~ }│{1:~ }│{1:~ }|*4 - {3:[No Name] [+] }{2:<[+] }{3:[No Name] [+] }| + {2:[No Name] [+] }{3:<[+] }{2:[No Name] [+] }| :set nowrap foldcolumn=4 | ]]) feed('4<C-W><lt>') screen:expect([[ - {4: }aaa │{4: }│{4: }aaa | - {4: } │{4: }│{4: } | - {4: }a │{4: }│{4: }a | - {4: }aaaaaa │{4:^ }│{4: }aaaaaa | + {7: }aaa │{7: }│{7: }aaa | + {7: } │{7: }│{7: } | + {7: }a │{7: }│{7: }a | + {7: }aaaaaa │{7:^ }│{7: }aaaaaa | {1:~ }│{1:~}│{1:~ }|*4 - {3:[No Name] [+] }{2:< }{3:[No Name] [+] }| + {2:[No Name] [+] }{3:< }{2:[No Name] [+] }| :set nowrap foldcolumn=4 | ]]) end) diff --git a/test/functional/legacy/listlbr_spec.lua b/test/functional/legacy/listlbr_spec.lua index 50628e5ef9..da641c3b6f 100644 --- a/test/functional/legacy/listlbr_spec.lua +++ b/test/functional/legacy/listlbr_spec.lua @@ -1,9 +1,10 @@ -- Test for linebreak and list option (non-utf8) -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect + +local feed, insert, source = n.feed, n.insert, n.source +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('listlbr', function() before_each(clear) @@ -204,11 +205,6 @@ describe('listlbr', function() -- oldtest: Test_linebreak_reset_restore() it('cursor position is drawn correctly after operator', function() local screen = Screen.new(60, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, -- Visual - [2] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg - }) screen:attach() -- f_wincol() calls validate_cursor() @@ -220,61 +216,61 @@ describe('listlbr', function() feed('$v$') screen:expect([[ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | - bbbbbbbbbb {1:c}^ | - {0:~ }|*3 + bbbbbbbbbb {17:c}^ | + {1:~ }|*3 2 | ]]) feed('zo') screen:expect([[ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbb ^c | - {0:~ }|*3 - {2:E490: No fold found} | + {1:~ }|*3 + {9:E490: No fold found} | ]]) feed('$v$') screen:expect([[ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | - bbbbbbbbbb {1:c}^ | - {0:~ }|*3 - {2:E490: No fold found} 2 | + bbbbbbbbbb {17:c}^ | + {1:~ }|*3 + {9:E490: No fold found} 2 | ]]) feed('gq') screen:expect([[ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbb ^c | - {0:~ }|*3 - {2:E490: No fold found} | + {1:~ }|*3 + {9:E490: No fold found} | ]]) feed('$<C-V>$') screen:expect([[ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | - bbbbbbbbbb {1:c}^ | - {0:~ }|*3 - {2:E490: No fold found} 1x2 | + bbbbbbbbbb {17:c}^ | + {1:~ }|*3 + {9:E490: No fold found} 1x2 | ]]) feed('I') screen:expect([[ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbb ^c | - {0:~ }|*3 - {2:E490: No fold found} | + {1:~ }|*3 + {9:E490: No fold found} | ]]) feed('<Esc>$v$') screen:expect([[ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | - bbbbbbbbbb {1:c}^ | - {0:~ }|*3 - {2:E490: No fold found} 2 | + bbbbbbbbbb {17:c}^ | + {1:~ }|*3 + {9:E490: No fold found} 2 | ]]) feed('s') screen:expect([[ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | bbbbbbbbbb ^ | - {0:~ }|*3 - {2:E490: No fold found} | + {1:~ }|*3 + {9:E490: No fold found} | ]]) end) end) diff --git a/test/functional/legacy/listlbr_utf8_spec.lua b/test/functional/legacy/listlbr_utf8_spec.lua index 8e5d9b88bc..74cc594cc1 100644 --- a/test/functional/legacy/listlbr_utf8_spec.lua +++ b/test/functional/legacy/listlbr_utf8_spec.lua @@ -1,11 +1,12 @@ -- Test for linebreak and list option in utf-8 mode -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local source = helpers.source -local feed = helpers.feed -local exec = helpers.exec -local clear, expect = helpers.clear, helpers.expect + +local source = n.source +local feed = n.feed +local exec = n.exec +local clear, expect = n.clear, n.expect describe('linebreak', function() before_each(clear) @@ -214,11 +215,6 @@ describe('linebreak', function() -- oldtest: Test_visual_ends_before_showbreak() it("Visual area is correct when it ends before multibyte 'showbreak'", function() local screen = Screen.new(60, 8) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, -- Visual - [2] = { bold = true }, -- ModeMsg - }) screen:attach() exec([[ let &wrap = v:true @@ -229,10 +225,10 @@ describe('linebreak', function() ]]) screen:expect([[ xxxxx | - {0:↪ }{1:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy}^ {1: }| - {0:↪ }zzzz | - {0:~ }|*4 - {2:-- VISUAL --} | + {1:↪ }{17:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy}^ {17: }| + {1:↪ }zzzz | + {1:~ }|*4 + {5:-- VISUAL --} | ]]) end) end) diff --git a/test/functional/legacy/mapping_spec.lua b/test/functional/legacy/mapping_spec.lua index 9eddec40f7..3fc324f66a 100644 --- a/test/functional/legacy/mapping_spec.lua +++ b/test/functional/legacy/mapping_spec.lua @@ -1,9 +1,13 @@ -- Test for mappings and abbreviations -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local expect, poke_eventloop = helpers.expect, helpers.poke_eventloop -local command, eq, eval, api = helpers.command, helpers.eq, helpers.eval, helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local Screen = require('test.functional.ui.screen') + +local clear, feed, insert = n.clear, n.feed, n.insert +local expect, poke_eventloop = n.expect, n.poke_eventloop +local command, eq, eval, api = n.command, t.eq, n.eval, n.api +local exec = n.exec local sleep = vim.uv.sleep describe('mapping', function() @@ -23,6 +27,7 @@ describe('mapping', function() vim ]]) end) + -- oldtest: Test_map_ctrl_c_insert() it('Ctrl-c works in Insert mode', function() -- Mapping of ctrl-c in insert mode command('set cpo-=< cpo-=k') @@ -41,6 +46,7 @@ describe('mapping', function() ]]) end) + -- oldtest: Test_map_ctrl_c_visual() it('Ctrl-c works in Visual mode', function() command([[vnoremap <c-c> :<C-u>$put ='vmap works'<cr>]]) feed('GV') @@ -83,6 +89,7 @@ describe('mapping', function() +]]) end) + -- oldtest: Test_map_feedkeys() it('feedkeys', function() insert([[ a b c d @@ -100,6 +107,7 @@ describe('mapping', function() ]]) end) + -- oldtest: Test_map_cursor() it('i_CTRL-G_U', function() -- <c-g>U<cursor> works only within a single line command('imapclear') @@ -128,7 +136,8 @@ describe('mapping', function() ]]) end) - it('dragging starts Select mode even if coming from mapping vim-patch:8.2.4806', function() + -- oldtest: Test_mouse_drag_mapped_start_select() + it('dragging starts Select mode even if coming from mapping', function() command('set mouse=a') command('set selectmode=mouse') @@ -141,7 +150,8 @@ describe('mapping', function() eq('s', eval('mode()')) end) - it('<LeftDrag> mapping in Insert mode works correctly vim-patch:8.2.4692', function() + -- oldtest: Test_mouse_drag_insert_map() + it('<LeftDrag> mapping in Insert mode works correctly', function() command('set mouse=a') command('inoremap <LeftDrag> <LeftDrag><Cmd>let g:dragged = 1<CR>') @@ -165,7 +175,8 @@ describe('mapping', function() eq('n', eval('mode()')) end) - it('timeout works after an <Nop> mapping is triggered on timeout vim-patch:8.1.0052', function() + -- oldtest: Test_map_after_timed_out_nop() + it('timeout works after an <Nop> mapping is triggered on timeout', function() command('set timeout timeoutlen=400') command('inoremap ab TEST') command('inoremap a <Nop>') @@ -181,4 +192,62 @@ describe('mapping', function() feed('b') expect('TEST') end) + + -- oldtest: Test_showcmd_part_map() + it("'showcmd' with a partial mapping", function() + local screen = Screen.new(60, 6) + screen:attach() + exec([[ + set notimeout showcmd + nnoremap ,a <Ignore> + nnoremap ;a <Ignore> + nnoremap Àa <Ignore> + nnoremap Ëa <Ignore> + nnoremap βa <Ignore> + nnoremap ωa <Ignore> + nnoremap …a <Ignore> + nnoremap <C-W>a <Ignore> + ]]) + + for _, c in ipairs({ ',', ';', 'À', 'Ë', 'β', 'ω', '…' }) do + feed(c) + screen:expect(([[ + ^ | + {1:~ }|*4 + %s | + ]]):format(c)) + feed('a') + screen:expect([[ + ^ | + {1:~ }|*4 + | + ]]) + end + + feed('\23') + screen:expect([[ + ^ | + {1:~ }|*4 + ^W | + ]]) + feed('a') + screen:expect([[ + ^ | + {1:~ }|*4 + | + ]]) + + feed('<C-W>') + screen:expect([[ + ^ | + {1:~ }|*4 + ^W | + ]]) + feed('a') + screen:expect([[ + ^ | + {1:~ }|*4 + | + ]]) + end) end) diff --git a/test/functional/legacy/marks_spec.lua b/test/functional/legacy/marks_spec.lua index 470ea49652..fd968eb355 100644 --- a/test/functional/legacy/marks_spec.lua +++ b/test/functional/legacy/marks_spec.lua @@ -1,6 +1,7 @@ -local helpers = require('test.functional.helpers')(after_each) -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect +local n = require('test.functional.testnvim')() + +local feed, insert, source = n.feed, n.insert, n.source +local clear, feed_command, expect = n.clear, n.feed_command, n.expect describe('marks', function() before_each(function() diff --git a/test/functional/legacy/match_spec.lua b/test/functional/legacy/match_spec.lua index ab791f03e5..0fc8708244 100644 --- a/test/functional/legacy/match_spec.lua +++ b/test/functional/legacy/match_spec.lua @@ -1,8 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local feed = helpers.feed + +local clear = n.clear +local exec = n.exec +local feed = n.feed before_each(clear) @@ -10,28 +11,24 @@ describe('matchaddpos()', function() -- oldtest: Test_matchaddpos_dump() it('can add more than 8 match positions vim-patch:9.0.0620', function() local screen = Screen.new(60, 14) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.Yellow }, -- Search - }) screen:attach() exec([[ call setline(1, ['1234567890123']->repeat(14)) call matchaddpos('Search', range(1, 12)->map({i, v -> [v, v]})) ]]) screen:expect([[ - {1:^1}234567890123 | - 1{1:2}34567890123 | - 12{1:3}4567890123 | - 123{1:4}567890123 | - 1234{1:5}67890123 | - 12345{1:6}7890123 | - 123456{1:7}890123 | - 1234567{1:8}90123 | - 12345678{1:9}0123 | - 123456789{1:0}123 | - 1234567890{1:1}23 | - 12345678901{1:2}3 | + {10:^1}234567890123 | + 1{10:2}34567890123 | + 12{10:3}4567890123 | + 123{10:4}567890123 | + 1234{10:5}67890123 | + 12345{10:6}7890123 | + 123456{10:7}890123 | + 1234567{10:8}90123 | + 12345678{10:9}0123 | + 123456789{10:0}123 | + 1234567890{10:1}23 | + 12345678901{10:2}3 | 1234567890123 | | ]]) @@ -42,10 +39,6 @@ describe('match highlighting', function() -- oldtest: Test_match_in_linebreak() it('does not continue in linebreak vim-patch:8.2.3698', function() local screen = Screen.new(75, 10) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg - }) screen:attach() exec([=[ set breakindent linebreak breakat+=] @@ -53,20 +46,15 @@ describe('match highlighting', function() call matchaddpos('ErrorMsg', [[1, 51]]) ]=]) screen:expect([[ - ^xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx{1:]} | + ^xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx{9:]} | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | - {0:~ }|*7 + {1:~ }|*7 | ]]) end) it('is shown with incsearch vim-patch:8.2.3940', function() local screen = Screen.new(75, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.Yellow }, -- Search - [2] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg - }) screen:attach() exec([[ set incsearch @@ -76,16 +64,16 @@ describe('match highlighting', function() screen:expect([[ ^0 | 1 | - {2:2} | + {9:2} | 3 | 4 | | ]]) feed(':s/0') screen:expect([[ - {1:0} | + {10:0} | 1 | - {2:2} | + {9:2} | 3 | 4 | :s/0^ | @@ -94,10 +82,6 @@ describe('match highlighting', function() it('on a Tab vim-patch:8.2.4062', function() local screen = Screen.new(75, 10) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg - }) screen:attach() exec([[ set linebreak @@ -105,8 +89,8 @@ describe('match highlighting', function() call matchadd('ErrorMsg', '\t') ]]) screen:expect([[ - {1: ^ }ix | - {0:~ }|*8 + {9: ^ }ix | + {1:~ }|*8 | ]]) end) diff --git a/test/functional/legacy/matchparen_spec.lua b/test/functional/legacy/matchparen_spec.lua index b03107deb0..3841761515 100644 --- a/test/functional/legacy/matchparen_spec.lua +++ b/test/functional/legacy/matchparen_spec.lua @@ -1,9 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) - +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local feed = helpers.feed + +local clear = n.clear +local exec = n.exec +local feed = n.feed describe('matchparen', function() before_each(clear) @@ -12,11 +12,6 @@ describe('matchparen', function() it('redraws properly after scrolling with scrolloff=1', function() local screen = Screen.new(30, 7) screen:attach() - screen:set_default_attr_ids({ - [1] = { bold = true }, - [2] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, - }) - exec([[ source $VIMRUNTIME/plugin/matchparen.vim set scrolloff=1 @@ -26,13 +21,13 @@ describe('matchparen', function() feed('V<c-d><c-d>') screen:expect([[ - {2:{} | - {2:}} | - {2:{} | - {2:f} | + {17:{} | + {17:}} | + {17:{} | + {17:f} | ^g | } | - {1:-- VISUAL LINE --} | + {5:-- VISUAL LINE --} | ]]) end) @@ -61,13 +56,15 @@ describe('matchparen', function() set hidden call setline(1, ['()']) normal 0 + + func OtherBuffer() + enew + exe "normal iaa\<Esc>0" + endfunc ]]) screen:expect(screen1) - exec([[ - enew - exe "normal iaa\<Esc>0" - ]]) + exec('call OtherBuffer()') screen:expect(screen2) feed('<C-^>') @@ -77,17 +74,43 @@ describe('matchparen', function() screen:expect(screen2) end) + -- oldtest: Test_matchparen_win_execute() + it('matchparen highlight when switching buffer in win_execute()', function() + local screen = Screen.new(20, 5) + screen:set_default_attr_ids({ + [1] = { background = Screen.colors.Cyan }, + [2] = { reverse = true, bold = true }, + [3] = { reverse = true }, + }) + screen:attach() + + exec([[ + source $VIMRUNTIME/plugin/matchparen.vim + let s:win = win_getid() + call setline(1, '{}') + split + + func SwitchBuf() + call win_execute(s:win, 'enew | buffer #') + endfunc + ]]) + screen:expect([[ + {1:^{}} | + {2:[No Name] [+] }| + {} | + {3:[No Name] [+] }| + | + ]]) + + -- Switching buffer away and back shouldn't change matchparen highlight. + exec('call SwitchBuf()') + screen:expect_unchanged() + end) + -- oldtest: Test_matchparen_pum_clear() it('is cleared when completion popup is shown', function() local screen = Screen.new(30, 9) screen:attach() - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, - [1] = { background = Screen.colors.Plum1 }, - [2] = { background = Screen.colors.Grey }, - [3] = { bold = true }, - [4] = { bold = true, foreground = Screen.colors.SeaGreen }, - }) exec([[ source $VIMRUNTIME/plugin/matchparen.vim @@ -103,11 +126,11 @@ describe('matchparen', function() aaa | aaaa | (aaa^) | - {1: aa }{0: }| - {2: aaa }{0: }| - {1: aaaa }{0: }| - {0:~ }| - {3:-- }{4:match 2 of 3} | + {4: aa }{1: }| + {12: aaa }{1: }| + {4: aaaa }{1: }| + {1:~ }| + {5:-- }{6:match 2 of 3} | ]], } end) diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index a05e9fdf57..87890d4c25 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -1,17 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eval = helpers.eval -local eq = helpers.eq -local feed_command = helpers.feed_command -local retry = helpers.retry -local ok = helpers.ok -local source = helpers.source -local poke_eventloop = helpers.poke_eventloop -local load_adjust = helpers.load_adjust -local write_file = helpers.write_file -local is_os = helpers.is_os -local is_ci = helpers.is_ci -local is_asan = helpers.is_asan +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eval = n.eval +local eq = t.eq +local feed_command = n.feed_command +local retry = t.retry +local ok = t.ok +local source = n.source +local poke_eventloop = n.poke_eventloop +local load_adjust = n.load_adjust +local write_file = t.write_file +local is_os = t.is_os +local is_ci = t.is_ci +local is_asan = n.is_asan clear() if is_asan() then @@ -59,7 +61,7 @@ local monitor_memory_usage = { end table.remove(self.hist, 1) self.last = self.hist[#self.hist] - eq(#result, 1) + eq(1, #result) end) end, dump = function(self) diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua index a87398b158..2f3693b5ad 100644 --- a/test/functional/legacy/messages_spec.lua +++ b/test/functional/legacy/messages_spec.lua @@ -1,12 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local exec = helpers.exec -local feed = helpers.feed -local api = helpers.api -local nvim_dir = helpers.nvim_dir -local assert_alive = helpers.assert_alive + +local clear = n.clear +local command = n.command +local exec = n.exec +local feed = n.feed +local api = n.api +local nvim_dir = n.nvim_dir +local assert_alive = n.assert_alive before_each(clear) @@ -16,12 +17,6 @@ describe('messages', function() -- oldtest: Test_warning_scroll() it('a warning causes scrolling if and only if it has a stacktrace', function() screen = Screen.new(75, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg - [2] = { bold = true, reverse = true }, -- MsgSeparator - [3] = { foreground = Screen.colors.Red }, -- WarningMsg - }) screen:attach() -- When the warning comes from a script, messages are scrolled so that the @@ -35,14 +30,14 @@ describe('messages', function() screen:expect({ grid = [[ | - {0:~ }|*4 - {3:W10: Warning: Changing a readonly file}^ | + {1:~ }|*4 + {19:W10: Warning: Changing a readonly file}^ | ]], timeout = 500, }) screen:expect([[ ^ | - {0:~ }|*4 + {1:~ }|*4 Already at oldest change | ]]) end) @@ -50,10 +45,6 @@ describe('messages', function() -- oldtest: Test_message_not_cleared_after_mode() it('clearing mode does not remove message', function() screen = Screen.new(60, 10) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg - }) screen:attach() exec([[ nmap <silent> gx :call DebugSilent('normal')<CR> @@ -71,7 +62,7 @@ describe('messages', function() ^one | NoSuchFile | three | - {0:~ }|*6 + {1:~ }|*6 from DebugSilent normal | ]]) @@ -81,7 +72,7 @@ describe('messages', function() ^one | NoSuchFile | three | - {0:~ }|*6 + {1:~ }|*6 from DebugSilent visual | ]]) @@ -92,9 +83,9 @@ describe('messages', function() one | NoSuchFil^e | three | - {0:~ }|*5 + {1:~ }|*5 from DebugSilent visual | - {1:E447: Can't find file "NoSuchFile" in path} | + {9:E447: Can't find file "NoSuchFile" in path} | ]]) end) @@ -403,10 +394,6 @@ describe('messages', function() -- oldtest: Test_echo_verbose_system() it('verbose message before echo command', function() screen = Screen.new(60, 10) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg - }) screen:attach() command('cd ' .. nvim_dir) @@ -426,7 +413,7 @@ describe('messages', function() 4: foo | 5: foo | 6: foo | - {1:-- More --}^ | + {6:-- More --}^ | ]]) feed('<Space>') screen:expect([[ @@ -439,7 +426,7 @@ describe('messages', function() 13: foo | 14: foo | 15: foo | - {1:-- More --}^ | + {6:-- More --}^ | ]]) feed('b') screen:expect([[ @@ -452,7 +439,7 @@ describe('messages', function() 4: foo | 5: foo | 6: foo | - {1:-- More --}^ | + {6:-- More --}^ | ]]) -- do the same with 'cmdheight' set to 2 @@ -460,7 +447,7 @@ describe('messages', function() command('set ch=2') screen:expect([[ ^ | - {0:~ }|*7 + {1:~ }|*7 |*2 ]]) feed([[:4 verbose echo system('foo')<CR>]]) @@ -474,7 +461,7 @@ describe('messages', function() 4: foo | 5: foo | 6: foo | - {1:-- More --}^ | + {6:-- More --}^ | ]]) feed('<Space>') screen:expect([[ @@ -487,7 +474,7 @@ describe('messages', function() 13: foo | 14: foo | 15: foo | - {1:-- More --}^ | + {6:-- More --}^ | ]]) feed('b') screen:expect([[ @@ -500,37 +487,32 @@ describe('messages', function() 4: foo | 5: foo | 6: foo | - {1:-- More --}^ | + {6:-- More --}^ | ]]) end) -- oldtest: Test_quit_long_message() it('with control characters can be quit vim-patch:8.2.1844', function() screen = Screen.new(40, 10) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg - [2] = { foreground = Screen.colors.Blue }, -- SpecialKey - }) screen:attach() feed([[:echom range(9999)->join("\x01")<CR>]]) screen:expect([[ - 0{2:^A}1{2:^A}2{2:^A}3{2:^A}4{2:^A}5{2:^A}6{2:^A}7{2:^A}8{2:^A}9{2:^A}10{2:^A}11{2:^A}12| - {2:^A}13{2:^A}14{2:^A}15{2:^A}16{2:^A}17{2:^A}18{2:^A}19{2:^A}20{2:^A}21{2:^A}22| - {2:^A}23{2:^A}24{2:^A}25{2:^A}26{2:^A}27{2:^A}28{2:^A}29{2:^A}30{2:^A}31{2:^A}32| - {2:^A}33{2:^A}34{2:^A}35{2:^A}36{2:^A}37{2:^A}38{2:^A}39{2:^A}40{2:^A}41{2:^A}42| - {2:^A}43{2:^A}44{2:^A}45{2:^A}46{2:^A}47{2:^A}48{2:^A}49{2:^A}50{2:^A}51{2:^A}52| - {2:^A}53{2:^A}54{2:^A}55{2:^A}56{2:^A}57{2:^A}58{2:^A}59{2:^A}60{2:^A}61{2:^A}62| - {2:^A}63{2:^A}64{2:^A}65{2:^A}66{2:^A}67{2:^A}68{2:^A}69{2:^A}70{2:^A}71{2:^A}72| - {2:^A}73{2:^A}74{2:^A}75{2:^A}76{2:^A}77{2:^A}78{2:^A}79{2:^A}80{2:^A}81{2:^A}82| - {2:^A}83{2:^A}84{2:^A}85{2:^A}86{2:^A}87{2:^A}88{2:^A}89{2:^A}90{2:^A}91{2:^A}92| - {1:-- More --}^ | + 0{18:^A}1{18:^A}2{18:^A}3{18:^A}4{18:^A}5{18:^A}6{18:^A}7{18:^A}8{18:^A}9{18:^A}10{18:^A}11{18:^A}12| + {18:^A}13{18:^A}14{18:^A}15{18:^A}16{18:^A}17{18:^A}18{18:^A}19{18:^A}20{18:^A}21{18:^A}22| + {18:^A}23{18:^A}24{18:^A}25{18:^A}26{18:^A}27{18:^A}28{18:^A}29{18:^A}30{18:^A}31{18:^A}32| + {18:^A}33{18:^A}34{18:^A}35{18:^A}36{18:^A}37{18:^A}38{18:^A}39{18:^A}40{18:^A}41{18:^A}42| + {18:^A}43{18:^A}44{18:^A}45{18:^A}46{18:^A}47{18:^A}48{18:^A}49{18:^A}50{18:^A}51{18:^A}52| + {18:^A}53{18:^A}54{18:^A}55{18:^A}56{18:^A}57{18:^A}58{18:^A}59{18:^A}60{18:^A}61{18:^A}62| + {18:^A}63{18:^A}64{18:^A}65{18:^A}66{18:^A}67{18:^A}68{18:^A}69{18:^A}70{18:^A}71{18:^A}72| + {18:^A}73{18:^A}74{18:^A}75{18:^A}76{18:^A}77{18:^A}78{18:^A}79{18:^A}80{18:^A}81{18:^A}82| + {18:^A}83{18:^A}84{18:^A}85{18:^A}86{18:^A}87{18:^A}88{18:^A}89{18:^A}90{18:^A}91{18:^A}92| + {6:-- More --}^ | ]]) feed('q') screen:expect([[ ^ | - {0:~ }|*8 + {1:~ }|*8 | ]]) end) @@ -539,11 +521,6 @@ describe('messages', function() describe('mode is cleared when', function() before_each(function() screen = Screen.new(40, 6) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [2] = { bold = true }, -- ModeMsg - [3] = { bold = true, reverse = true }, -- StatusLine - }) screen:attach() end) @@ -561,7 +538,7 @@ describe('messages', function() ^ | {1:~ }|*3 {3: }| - {2:-- INSERT --} | + {5:-- INSERT --} | ]]) feed('<C-C>') screen:expect([[ @@ -583,7 +560,7 @@ describe('messages', function() ^ | {1:~ }|*3 {3:[No Name] }| - {2:-- INSERT --} | + {5:-- INSERT --} | ]]) feed('<Esc>') screen:expect([[ @@ -600,7 +577,7 @@ describe('messages', function() screen:expect([[ ^ | {1:~ }|*4 - {2:-- (insert) --} | + {5:-- (insert) --} | ]]) feed('<C-C>') screen:expect([[ @@ -614,11 +591,6 @@ describe('messages', function() -- oldtest: Test_ask_yesno() it('y/n prompt works', function() screen = Screen.new(75, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg - [2] = { bold = true, reverse = true }, -- MsgSeparator - }) screen:attach() command('set noincsearch nohlsearch inccommand=') command('call setline(1, range(1, 2))') @@ -627,57 +599,51 @@ describe('messages', function() screen:expect([[ 1 | 2 | - {0:~ }|*3 - {1:Backwards range given, OK to swap (y/n)?}^ | + {1:~ }|*3 + {6:Backwards range given, OK to swap (y/n)?}^ | ]]) feed('n') screen:expect([[ ^1 | 2 | - {0:~ }|*3 - {1:Backwards range given, OK to swap (y/n)?}n | + {1:~ }|*3 + {6:Backwards range given, OK to swap (y/n)?}n | ]]) feed(':2,1s/^/Esc/\n') screen:expect([[ 1 | 2 | - {0:~ }|*3 - {1:Backwards range given, OK to swap (y/n)?}^ | + {1:~ }|*3 + {6:Backwards range given, OK to swap (y/n)?}^ | ]]) feed('<Esc>') screen:expect([[ ^1 | 2 | - {0:~ }|*3 - {1:Backwards range given, OK to swap (y/n)?}n | + {1:~ }|*3 + {6:Backwards range given, OK to swap (y/n)?}n | ]]) feed(':2,1s/^/y/\n') screen:expect([[ 1 | 2 | - {0:~ }|*3 - {1:Backwards range given, OK to swap (y/n)?}^ | + {1:~ }|*3 + {6:Backwards range given, OK to swap (y/n)?}^ | ]]) feed('y') screen:expect([[ y1 | ^y2 | - {0:~ }|*3 - {1:Backwards range given, OK to swap (y/n)?}y | + {1:~ }|*3 + {6:Backwards range given, OK to swap (y/n)?}y | ]]) end) -- oldtest: Test_fileinfo_tabpage_cmdheight() it("fileinfo works when 'cmdheight' has just decreased", function() screen = Screen.new(40, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { bold = true }, -- TabLineSel - [2] = { underline = true, background = Screen.colors.LightGrey }, -- TabLine - [3] = { reverse = true }, -- TabLineFill - }) screen:attach() exec([[ @@ -688,17 +654,17 @@ describe('messages', function() set cmdheight=2 ]]) screen:expect([[ - {2: [No Name] }{1: [No Name] }{3: }{2:X}| + {24: [No Name] }{5: [No Name] }{2: }{24:X}| ^ | - {0:~ }|*2 + {1:~ }|*2 |*2 ]]) feed(':tabprev | edit Xfileinfo.txt<CR>') screen:expect([[ - {1: Xfileinfo.txt }{2: [No Name] }{3: }{2:X}| + {5: Xfileinfo.txt }{24: [No Name] }{2: }{24:X}| ^ | - {0:~ }|*3 + {1:~ }|*3 "Xfileinfo.txt" [New] | ]]) assert_alive() @@ -707,9 +673,6 @@ describe('messages', function() -- oldtest: Test_fileinfo_after_echo() it('fileinfo does not overwrite echo message vim-patch:8.2.4156', function() screen = Screen.new(40, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - }) screen:attach() exec([[ @@ -730,7 +693,7 @@ describe('messages', function() feed('0$') screen:expect([[ ^hi | - {0:~ }|*4 + {1:~ }|*4 'b' written | ]]) os.remove('b.txt') diff --git a/test/functional/legacy/mksession_spec.lua b/test/functional/legacy/mksession_spec.lua index 689d918cd9..28d4637954 100644 --- a/test/functional/legacy/mksession_spec.lua +++ b/test/functional/legacy/mksession_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local fn = helpers.fn -local eq = helpers.eq +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local fn = n.fn +local eq = t.eq describe('mksession', function() before_each(clear) diff --git a/test/functional/legacy/move_spec.lua b/test/functional/legacy/move_spec.lua index 1500d48ad9..c2be8bb3eb 100644 --- a/test/functional/legacy/move_spec.lua +++ b/test/functional/legacy/move_spec.lua @@ -1,8 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local feed = helpers.feed -local fn = helpers.fn + +local clear = n.clear +local feed = n.feed +local fn = n.fn before_each(clear) @@ -10,9 +11,6 @@ describe(':move', function() -- oldtest: Test_move_undo() it('redraws correctly when undone', function() local screen = Screen.new(60, 10) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - }) screen:attach() fn.setline(1, { 'First', 'Second', 'Third', 'Fourth' }) @@ -22,7 +20,7 @@ describe(':move', function() ^First | Third | Fourth | - {0:~ }|*5 + {1:~ }|*5 :move +1 | ]]) @@ -34,7 +32,7 @@ describe(':move', function() Second | Third | Fourth | - {0:~ }|*5 + {1:~ }|*5 | ]]) end) diff --git a/test/functional/legacy/nested_function_spec.lua b/test/functional/legacy/nested_function_spec.lua index 7a2ba1ecf2..5e981b5862 100644 --- a/test/functional/legacy/nested_function_spec.lua +++ b/test/functional/legacy/nested_function_spec.lua @@ -1,8 +1,9 @@ -- Tests for nested function. -local helpers = require('test.functional.helpers')(after_each) -local clear, insert = helpers.clear, helpers.insert -local command, expect, source = helpers.command, helpers.expect, helpers.source +local n = require('test.functional.testnvim')() + +local clear, insert = n.clear, n.insert +local command, expect, source = n.command, n.expect, n.source describe('test_nested_function', function() setup(clear) diff --git a/test/functional/legacy/normal_spec.lua b/test/functional/legacy/normal_spec.lua index 1dddeed033..5158ca3009 100644 --- a/test/functional/legacy/normal_spec.lua +++ b/test/functional/legacy/normal_spec.lua @@ -1,35 +1,106 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -before_each(clear) +local clear = n.clear +local exec = n.exec +local feed = n.feed +local api = n.api +local eq = t.eq +local fn = n.fn describe('normal', function() + local screen + + before_each(function() + clear() + screen = Screen.new(40, 19) + screen:attach() + end) + -- oldtest: Test_normal_j_below_botline() - it( - [["j" does not skip lines when scrolling below botline and 'foldmethod' is not "manual"]], - function() - local screen = Screen.new(40, 19) - screen:attach() - screen:set_default_attr_ids({ { foreground = Screen.colors.Brown } }) - exec([[ + it([[no skipped lines with "j" scrolling below botline and 'foldmethod' not "manual"]], function() + exec([[ set number foldmethod=diff scrolloff=0 call setline(1, map(range(1, 9), 'repeat(v:val, 200)')) norm Lj ]]) - screen:expect([[ - {1: 2 }222222222222222222222222222222222222| - {1: }222222222222222222222222222222222222|*4 - {1: }22222222222222222222 | - {1: 3 }333333333333333333333333333333333333| - {1: }333333333333333333333333333333333333|*4 - {1: }33333333333333333333 | - {1: 4 }^444444444444444444444444444444444444| - {1: }444444444444444444444444444444444444|*4 - {1: }44444444444444444444 | + screen:expect([[ + {8: 2 }222222222222222222222222222222222222| + {8: }222222222222222222222222222222222222|*4 + {8: }22222222222222222222 | + {8: 3 }333333333333333333333333333333333333| + {8: }333333333333333333333333333333333333|*4 + {8: }33333333333333333333 | + {8: 4 }^444444444444444444444444444444444444| + {8: }444444444444444444444444444444444444|*4 + {8: }44444444444444444444 | | ]]) - end - ) + end) + + -- oldtest: Test_single_line_scroll() + it('(Half)-page scroll up or down reveals virtual lines #19605, #27967', function() + fn.setline(1, 'foobar one two three') + exec('set smoothscroll') + local ns = api.nvim_create_namespace('') + api.nvim_buf_set_extmark(0, ns, 0, 0, { + virt_lines = { { { '---', 'IncSearch' } } }, + virt_lines_above = true, + }) + -- Nvim: not actually necessary to scroll down to hide the virtual line. + -- Check topfill instead of skipcol and show the screen state. + feed('<C-E>') + eq(0, fn.winsaveview().topfill) + local s1 = [[ + ^foobar one two three | + {1:~ }|*17 + | + ]] + screen:expect(s1) + feed('<C-B>') + eq(1, fn.winsaveview().topfill) + local s2 = [[ + {2:---} | + ^foobar one two three | + {1:~ }|*16 + | + ]] + screen:expect(s2) + feed('<C-E>') + eq(0, fn.winsaveview().topfill) + screen:expect(s1) + feed('<C-U>') + eq(1, fn.winsaveview().topfill) + screen:expect(s2) + + -- Nvim: also test virt_lines below the last line + feed('yy100pG<C-L>') + api.nvim_buf_set_extmark(0, ns, 100, 0, { virt_lines = { { { '---', 'IncSearch' } } } }) + screen:expect({ + grid = [[ + foobar one two three |*17 + ^foobar one two three | + | + ]], + }) + feed('<C-F>') + screen:expect({ + grid = [[ + ^foobar one two three | + {2:---} | + {1:~ }|*16 + | + ]], + }) + feed('ggG<C-D>') + screen:expect({ + grid = [[ + foobar one two three |*16 + ^foobar one two three | + {2:---} | + | + ]], + }) + end) end) diff --git a/test/functional/legacy/number_spec.lua b/test/functional/legacy/number_spec.lua index c112532eed..0ebd731f65 100644 --- a/test/functional/legacy/number_spec.lua +++ b/test/functional/legacy/number_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local exec = helpers.exec -local feed = helpers.feed + +local clear = n.clear +local command = n.command +local exec = n.exec +local feed = n.feed describe("'number' and 'relativenumber'", function() before_each(clear) @@ -214,10 +215,6 @@ describe("'number' and 'relativenumber'", function() -- oldtest: Test_relativenumber_callback() it('relative line numbers are updated if cursor is moved from timer', function() local screen = Screen.new(50, 8) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.Brown }, -- LineNr - [2] = { bold = true, foreground = Screen.colors.Blue1 }, -- NonText - }) screen:attach() exec([[ call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd']) @@ -232,22 +229,22 @@ describe("'number' and 'relativenumber'", function() ]]) screen:expect({ grid = [[ - {1: 3 }aaaaa | - {1: 2 }bbbbb | - {1: 1 }ccccc | - {1: 0 }^ddddd | - {2:~ }|*3 + {8: 3 }aaaaa | + {8: 2 }bbbbb | + {8: 1 }ccccc | + {8: 0 }^ddddd | + {1:~ }|*3 | ]], timeout = 100, }) screen:expect({ grid = [[ - {1: 0 }^aaaaa | - {1: 1 }bbbbb | - {1: 2 }ccccc | - {1: 3 }ddddd | - {2:~ }|*3 + {8: 0 }^aaaaa | + {8: 1 }bbbbb | + {8: 2 }ccccc | + {8: 3 }ddddd | + {1:~ }|*3 | ]], }) @@ -256,10 +253,6 @@ describe("'number' and 'relativenumber'", function() -- oldtest: Test_number_insert_delete_lines() it('line numbers are updated when deleting/inserting lines', function() local screen = Screen.new(50, 8) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.Brown }, -- LineNr - [2] = { bold = true, foreground = Screen.colors.Blue1 }, -- NonText - }) screen:attach() exec([[ call setline(1, range(1, 7)) @@ -267,37 +260,37 @@ describe("'number' and 'relativenumber'", function() call cursor(2, 1) ]]) local snapshot1 = [[ - {1: 1 }1 | - {1: 2 }^2 | - {1: 3 }3 | - {1: 4 }4 | - {1: 5 }5 | - {1: 6 }6 | - {1: 7 }7 | + {8: 1 }1 | + {8: 2 }^2 | + {8: 3 }3 | + {8: 4 }4 | + {8: 5 }5 | + {8: 6 }6 | + {8: 7 }7 | | ]] screen:expect(snapshot1) feed('dd') screen:expect([[ - {1: 1 }1 | - {1: 2 }^3 | - {1: 3 }4 | - {1: 4 }5 | - {1: 5 }6 | - {1: 6 }7 | - {2:~ }| + {8: 1 }1 | + {8: 2 }^3 | + {8: 3 }4 | + {8: 4 }5 | + {8: 5 }6 | + {8: 6 }7 | + {1:~ }| | ]]) feed('P') screen:expect(snapshot1) feed('2dd') screen:expect([[ - {1: 1 }1 | - {1: 2 }^4 | - {1: 3 }5 | - {1: 4 }6 | - {1: 5 }7 | - {2:~ }|*2 + {8: 1 }1 | + {8: 2 }^4 | + {8: 3 }5 | + {8: 4 }6 | + {8: 5 }7 | + {1:~ }|*2 | ]]) feed('P') diff --git a/test/functional/legacy/options_spec.lua b/test/functional/legacy/options_spec.lua index 2f20b6bd51..e9a3b73cf7 100644 --- a/test/functional/legacy/options_spec.lua +++ b/test/functional/legacy/options_spec.lua @@ -1,11 +1,13 @@ -- See also: test/old/testdir/test_options.vim -local helpers = require('test.functional.helpers')(after_each) -local command, clear = helpers.command, helpers.clear -local source, expect = helpers.source, helpers.expect -local exc_exec = helpers.exc_exec -local matches = helpers.matches +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local command, clear = n.command, n.clear +local source, expect = n.source, n.expect +local exc_exec = n.exc_exec +local matches = t.matches + describe('options', function() setup(clear) @@ -66,11 +68,11 @@ describe('set', function() command('verbose set scroll?') screen:expect([[ | - ~ |*11 - | + {1:~ }|*11 + {3: }| scroll=7 | Last set from changed window size | - Press ENTER or type command to continue^ | + {6:Press ENTER or type command to continue}^ | ]]) end) diff --git a/test/functional/legacy/prompt_buffer_spec.lua b/test/functional/legacy/prompt_buffer_spec.lua index e4810feedb..72ec420b15 100644 --- a/test/functional/legacy/prompt_buffer_spec.lua +++ b/test/functional/legacy/prompt_buffer_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed = helpers.feed -local source = helpers.source -local clear = helpers.clear -local command = helpers.command -local expect = helpers.expect -local poke_eventloop = helpers.poke_eventloop -local api = helpers.api -local eq = helpers.eq -local neq = helpers.neq + +local feed = n.feed +local source = n.source +local clear = n.clear +local command = n.command +local expect = n.expect +local poke_eventloop = n.poke_eventloop +local api = n.api +local eq = t.eq +local neq = t.neq describe('prompt buffer', function() local screen @@ -58,11 +60,11 @@ describe('prompt buffer', function() ]]) screen:expect([[ cmd: ^ | - ~ |*3 - [Prompt] [+] | + {1:~ }|*3 + {3:[Prompt] [+] }| other buffer | - ~ |*3 - -- INSERT -- | + {1:~ }|*3 + {5:-- INSERT --} | ]]) end @@ -79,15 +81,15 @@ describe('prompt buffer', function() Command: "hello" | Result: "hello" | cmd: ^ | - [Prompt] | + {3:[Prompt] }| other buffer | - ~ |*3 - -- INSERT -- | + {1:~ }|*3 + {5:-- INSERT --} | ]]) feed('exit\n') screen:expect([[ ^other buffer | - ~ |*8 + {1:~ }|*8 | ]]) end) @@ -98,43 +100,43 @@ describe('prompt buffer', function() feed('hello<BS><BS>') screen:expect([[ cmd: hel^ | - ~ |*3 - [Prompt] [+] | + {1:~ }|*3 + {3:[Prompt] [+] }| other buffer | - ~ |*3 - -- INSERT -- | + {1:~ }|*3 + {5:-- INSERT --} | ]]) feed('<Left><Left><Left><BS>-') screen:expect([[ cmd: -^hel | - ~ |*3 - [Prompt] [+] | + {1:~ }|*3 + {3:[Prompt] [+] }| other buffer | - ~ |*3 - -- INSERT -- | + {1:~ }|*3 + {5:-- INSERT --} | ]]) feed('<C-O>lz') screen:expect([[ cmd: -hz^el | - ~ |*3 - [Prompt] [+] | + {1:~ }|*3 + {3:[Prompt] [+] }| other buffer | - ~ |*3 - -- INSERT -- | + {1:~ }|*3 + {5:-- INSERT --} | ]]) feed('<End>x') screen:expect([[ cmd: -hzelx^ | - ~ |*3 - [Prompt] [+] | + {1:~ }|*3 + {3:[Prompt] [+] }| other buffer | - ~ |*3 - -- INSERT -- | + {1:~ }|*3 + {5:-- INSERT --} | ]]) feed('<C-U>exit\n') screen:expect([[ ^other buffer | - ~ |*8 + {1:~ }|*8 | ]]) end) @@ -143,32 +145,30 @@ describe('prompt buffer', function() it('switch windows', function() source_script() feed('<C-O>:call SwitchWindows()<CR>') - screen:expect { - grid = [[ + screen:expect([[ cmd: | - ~ |*3 - [Prompt] [+] | + {1:~ }|*3 + {2:[Prompt] [+] }| ^other buffer | - ~ |*3 + {1:~ }|*3 | - ]], - } + ]]) feed('<C-O>:call SwitchWindows()<CR>') screen:expect([[ cmd: ^ | - ~ |*3 - [Prompt] [+] | + {1:~ }|*3 + {3:[Prompt] [+] }| other buffer | - ~ |*3 - -- INSERT -- | + {1:~ }|*3 + {5:-- INSERT --} | ]]) feed('<Esc>') screen:expect([[ cmd:^ | - ~ |*3 - [Prompt] [+] | + {1:~ }|*3 + {3:[Prompt] [+] }| other buffer | - ~ |*3 + {1:~ }|*3 | ]]) end) diff --git a/test/functional/legacy/put_spec.lua b/test/functional/legacy/put_spec.lua index c78946d690..587424da10 100644 --- a/test/functional/legacy/put_spec.lua +++ b/test/functional/legacy/put_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local api = helpers.api -local source = helpers.source -local eq = helpers.eq + +local clear = n.clear +local exec_lua = n.exec_lua +local api = n.api +local source = n.source +local eq = t.eq local function sizeoflong() if not exec_lua('return pcall(require, "ffi")') then @@ -66,8 +68,8 @@ describe('put', function() three more text │ three more text | ^four more text │ four more text | │ | - ~ │~ |*2 - [No Name] [+] [No Name] [+] | + {1:~ }│{1:~ }|*2 + {3:[No Name] [+] }{2:[No Name] [+] }| | ]]) end) diff --git a/test/functional/legacy/qf_title_spec.lua b/test/functional/legacy/qf_title_spec.lua index 9f97eb27b2..26b096d633 100644 --- a/test/functional/legacy/qf_title_spec.lua +++ b/test/functional/legacy/qf_title_spec.lua @@ -1,8 +1,9 @@ -- Tests for quickfix window's title -local helpers = require('test.functional.helpers')(after_each) -local insert, source = helpers.insert, helpers.source -local clear, expect = helpers.clear, helpers.expect +local n = require('test.functional.testnvim')() + +local insert, source = n.insert, n.source +local clear, expect = n.clear, n.expect describe('qf_title', function() setup(clear) diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 8d22c299d6..97578067d5 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local feed = helpers.feed -local assert_alive = helpers.assert_alive + +local clear = n.clear +local exec = n.exec +local feed = n.feed +local assert_alive = n.assert_alive before_each(clear) @@ -23,9 +24,9 @@ describe('smoothscroll', function() set number ]]) feed('<C-Y>') - screen:expect({ any = ' 1 ^one' }) + screen:expect({ any = '{8: 1 }^one' }) feed('<C-E><C-E><C-E>') - screen:expect({ any = ' 2 ^two' }) + screen:expect({ any = '{8: 2 }^two' }) end) -- oldtest: Test_smoothscroll_CtrlE_CtrlY() @@ -43,28 +44,28 @@ describe('smoothscroll', function() long word long word long word | ^line | line |*2 - ~ |*2 + {1:~ }|*2 | ]] local s2 = [[ - <<<d word word word word word word word | + {1:<<<}d word word word word word word word | word word word word | line three | long word long word long word long word | long word long word long word | ^line | line |*2 - ~ |*3 + {1:~ }|*3 | ]] local s3 = [[ - <<<d word word word | + {1:<<<}d word word word | line three | long word long word long word long word | long word long word long word | ^line | line |*2 - ~ |*4 + {1:~ }|*4 | ]] local s4 = [[ @@ -73,28 +74,28 @@ describe('smoothscroll', function() long word long word long word | line |*2 ^line | - ~ |*5 + {1:~ }|*5 | ]] local s5 = [[ - <<<d word word word | + {1:<<<}d word word word | line three | long word long word long word long word | long word long word long word | line |*2 ^line | - ~ |*4 + {1:~ }|*4 | ]] local s6 = [[ - <<<d word word word word word word word | + {1:<<<}d word word word word word word word | word word word word | line three | long word long word long word long word | long word long word long word | line |*2 ^line | - ~ |*3 + {1:~ }|*3 | ]] local s7 = [[ @@ -105,7 +106,7 @@ describe('smoothscroll', function() long word long word long word | line |*2 ^line | - ~ |*2 + {1:~ }|*2 | ]] local s8 = [[ @@ -117,7 +118,7 @@ describe('smoothscroll', function() long word long word long word | line |*2 ^line | - ~ | + {1:~ }| | ]] feed('<C-E>') @@ -160,7 +161,7 @@ describe('smoothscroll', function() ϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛϛ^ϛϛϛϛϛ| ϛϛϛϛϛ | 222222222222222222222222222222222222 | - ~ |*2 + {1:~ }|*2 | ]]) end) @@ -181,134 +182,136 @@ describe('smoothscroll', function() endfunc ]]) screen:expect([[ - 1 one word word word word word word wo| + {8: 1 }one word word word word word word wo| rd word word word word word word word wo| rd word word word word word | - 2 two long word long word long word lo| + {8: 2 }two long word long word long word lo| ng word long word long word long word | - 3 ^line | - 4 line | - 5 line | - ~ |*3 + {8: 3 }^line | + {8: 4 }line | + {8: 5 }line | + {1:~ }|*3 | ]]) feed('<C-E>') screen:expect([[ - <<<word word word word word word word wo| + {1:<<<}word word word word word word word wo| rd word word word word word | - 2 two long word long word long word lo| + {8: 2 }two long word long word long word lo| ng word long word long word long word | - 3 ^line | - 4 line | - 5 line | - ~ |*4 + {8: 3 }^line | + {8: 4 }line | + {8: 5 }line | + {1:~ }|*4 | ]]) feed('<C-E>') screen:expect([[ - <<<word word word word word | - 2 two long word long word long word lo| + {1:<<<}word word word word word | + {8: 2 }two long word long word long word lo| ng word long word long word long word | - 3 ^line | - 4 line | - 5 line | - ~ |*5 + {8: 3 }^line | + {8: 4 }line | + {8: 5 }line | + {1:~ }|*5 | ]]) exec('set cpo-=n') screen:expect([[ - <<< d word word word word word word | - 2 two long word long word long word lo| - ng word long word long word long wor| - d | - 3 ^line | - 4 line | - 5 line | - ~ |*4 + {1:<<<}{8: }d word word word word word word | + {8: 2 }two long word long word long word lo| + {8: }ng word long word long word long wor| + {8: }d | + {8: 3 }^line | + {8: 4 }line | + {8: 5 }line | + {1:~ }|*4 | ]]) feed('<C-Y>') screen:expect([[ - <<< rd word word word word word word wor| - d word word word word word word | - 2 two long word long word long word lo| - ng word long word long word long wor| - d | - 3 ^line | - 4 line | - 5 line | - ~ |*3 + {1:<<<}{8: }rd word word word word word word wor| + {8: }d word word word word word word | + {8: 2 }two long word long word long word lo| + {8: }ng word long word long word long wor| + {8: }d | + {8: 3 }^line | + {8: 4 }line | + {8: 5 }line | + {1:~ }|*3 | ]]) feed('<C-Y>') screen:expect([[ - 1 one word word word word word word wo| - rd word word word word word word wor| - d word word word word word word | - 2 two long word long word long word lo| - ng word long word long word long wor| - d | - 3 ^line | - 4 line | - 5 line | - ~ |*2 + {8: 1 }one word word word word word word wo| + {8: }rd word word word word word word wor| + {8: }d word word word word word word | + {8: 2 }two long word long word long word lo| + {8: }ng word long word long word long wor| + {8: }d | + {8: 3 }^line | + {8: 4 }line | + {8: 5 }line | + {1:~ }|*2 | ]]) exec('botright split') feed('gg') screen:expect([[ - 1 one word word word word word word wo| - rd word word word word word word wor| - d word word word word word word | - 2 two long word long word long word@@@| - [No Name] [+] | - 1 ^one word word word word word word wo| - rd word word word word word word wor| - d word word word word word word | - 2 two long word long word long word lo| - ng word long word long word long @@@| - [No Name] [+] | + {8: 1 }one word word word word word word wo| + {8: }rd word word word word word word wor| + {8: }d word word word word word word | + {8: 2 }two long word long word long word{1:@@@}| + {2:[No Name] [+] }| + {8: 1 }^one word word word word word word wo| + {8: }rd word word word word word word wor| + {8: }d word word word word word word | + {8: 2 }two long word long word long word lo| + {8: }ng word long word long word long {1:@@@}| + {3:[No Name] [+] }| | ]]) + feed('<C-E>') screen:expect([[ - 1 one word word word word word word wo| - rd word word word word word word wor| - d word word word word word word | - 2 two long word long word long word@@@| - [No Name] [+] | - <<< rd word word word word word word wor| - d word word word word word word^ | - 2 two long word long word long word lo| - ng word long word long word long wor| - d | - [No Name] [+] | + {8: 1 }one word word word word word word wo| + {8: }rd word word word word word word wor| + {8: }d word word word word word word | + {8: 2 }two long word long word long word{1:@@@}| + {2:[No Name] [+] }| + {1:<<<}{8: }rd word word word word word word wor| + {8: }d word word word word word word^ | + {8: 2 }two long word long word long word lo| + {8: }ng word long word long word long wor| + {8: }d | + {3:[No Name] [+] }| | ]]) + feed('<C-E>') screen:expect([[ - 1 one word word word word word word wo| - rd word word word word word word wor| - d word word word word word word | - 2 two long word long word long word@@@| - [No Name] [+] | - <<< d word word word word word word^ | - 2 two long word long word long word lo| - ng word long word long word long wor| - d | - 3 line | - [No Name] [+] | + {8: 1 }one word word word word word word wo| + {8: }rd word word word word word word wor| + {8: }d word word word word word word | + {8: 2 }two long word long word long word{1:@@@}| + {2:[No Name] [+] }| + {1:<<<}{8: }d word word word word word word^ | + {8: 2 }two long word long word long word lo| + {8: }ng word long word long word long wor| + {8: }d | + {8: 3 }line | + {3:[No Name] [+] }| | ]]) exec('close') exec('call DoRel()') screen:expect([[ - 2<<<^ong text very long text very long te| - xt very long text very long text ver| - y long text very long text very long| - text very long text very long text | - 1 three | - ~ |*6 + {8:2}{1:<<<}^ong text very long text very long te| + {8: }xt very long text very long text ver| + {8: }y long text very long text very long| + {8: } text very long text very long text | + {8: 1 }three | + {1:~ }|*6 --No lines in buffer-- | ]]) end) @@ -323,22 +326,22 @@ describe('smoothscroll', function() exe "normal 2Gzt\<C-E>" ]]) screen:expect([[ - <<<t very long text very long text very | + {1:<<<}t very long text very long text very | ^long text very long text very long text | very long text very long text very long | - text very long text- | + text very long text{1:-} | three | - ~ |*2 + {1:~ }|*2 | ]]) exec('set listchars+=precedes:#') screen:expect([[ - #ext very long text very long text very | + {1:#}ext very long text very long text very | ^long text very long text very long text | very long text very long text very long | - text very long text- | + text very long text{1:-} | three | - ~ |*2 + {1:~ }|*2 | ]]) end) @@ -356,13 +359,14 @@ describe('smoothscroll', function() set smoothscroll diffthis ]]) + screen:expect([[ - - ^just some text here | - ~ |*2 - [No Name] [+] | - - just some text here | - ~ | - [No Name] [+] | + {7:- }^just some text here | + {1:~ }|*2 + {3:[No Name] [+] }| + {7:- }just some text here | + {1:~ }| + {2:[No Name] [+] }| | ]]) feed('<C-Y>') @@ -380,7 +384,7 @@ describe('smoothscroll', function() :3 ]]) screen:expect([[ - <<<h some text with some text | + {1:<<<}h some text with some text | Line with some text with some text with | some text with some text with some text | with some text with some text | @@ -401,7 +405,7 @@ describe('smoothscroll', function() -- moving cursor up right after the <<< marker - no need to show whole line feed('2gj3l2k') screen:expect([[ - <<<^h some text with some text | + {1:<<<}^h some text with some text | Line with some text with some text with | some text with some text with some text | with some text with some text | @@ -419,7 +423,7 @@ describe('smoothscroll', function() Line with some text with some text with | some text with some text with some text | with some text with some text | - @ | + {1:@ }| | ]]) end) @@ -443,7 +447,7 @@ describe('smoothscroll', function() ]]) feed('<C-E>') screen:expect([[ - <<<th lot^s of text with lots of text wit| + {1:<<<}th lot^s of text with lots of text wit| h lots of text with lots of text with lo| ts of text with lots of text with lots o| f text with lots of text with lots of te| @@ -452,7 +456,7 @@ describe('smoothscroll', function() ]]) feed('5<C-E>') screen:expect([[ - <<< lots ^of text with lots of text with | + {1:<<<} lots ^of text with lots of text with | lots of text with lots of text with lots| of text with lots of text with lots of | text with lots of text with lots of text| @@ -462,7 +466,7 @@ describe('smoothscroll', function() -- scrolling down, cursor moves screen line up feed('5<C-Y>') screen:expect([[ - <<<th lots of text with lots of text wit| + {1:<<<}th lots of text with lots of text wit| h lots of text with lots of text with lo| ts of text with lots of text with lots o| f text with lots of text with lots of te| @@ -482,7 +486,7 @@ describe('smoothscroll', function() exec('set scrolloff=1') feed('10|<C-E>') screen:expect([[ - <<<th lots of text with lots of text wit| + {1:<<<}th lots of text with lots of text wit| h lots of^ text with lots of text with lo| ts of text with lots of text with lots o| f text with lots of text with lots of te| @@ -492,7 +496,7 @@ describe('smoothscroll', function() -- 'scrolloff' set to 1, scrolling down, cursor moves screen line up feed('<C-E>gjgj<C-Y>') screen:expect([[ - <<<th lots of text with lots of text wit| + {1:<<<}th lots of text with lots of text wit| h lots of text with lots of text with lo| ts of text with lots of text with lots o| f text wi^th lots of text with lots of te| @@ -503,7 +507,7 @@ describe('smoothscroll', function() exec('set scrolloff=2') feed('10|<C-E>') screen:expect([[ - <<<th lots of text with lots of text wit| + {1:<<<}th lots of text with lots of text wit| h lots of text with lots of text with lo| ts of tex^t with lots of text with lots o| f text with lots of text with lots of te| @@ -518,7 +522,7 @@ describe('smoothscroll', function() exec('set scrolloff=0') feed('0j') screen:expect([[ - <<<th lots of text with lots of text wit| + {1:<<<}th lots of text with lots of text wit| h lots of text with lots of text with lo| ts of text with lots of text with lots o| f text with lots of text end | @@ -529,20 +533,20 @@ describe('smoothscroll', function() feed('zt') screen:expect([[ ^four | - ~ |*4 + {1:~ }|*4 | ]]) feed('zz') screen:expect([[ - <<<of text with lots of text with lots o| + {1:<<<}of text with lots of text with lots o| f text with lots of text end | ^four | - ~ |*2 + {1:~ }|*2 | ]]) feed('zb') screen:expect([[ - <<<th lots of text with lots of text wit| + {1:<<<}th lots of text with lots of text wit| h lots of text with lots of text with lo| ts of text with lots of text with lots o| f text with lots of text end | @@ -567,7 +571,7 @@ describe('smoothscroll', function() -- screen. feed('3Gzt<C-E>j') screen:expect([[ - <<<th lots of text with lots of text wit| + {1:<<<}th lots of text with lots of text wit| h lots of text with lots of text with lo| ts of text with lots of text with lots o| f text with lots of text end | @@ -588,16 +592,16 @@ describe('smoothscroll', function() lots of text with lots of text with lot| s of text with lots of text with lots of| text | - ~ | + {1:~ }| | ]] screen:expect(s1) feed('<C-E>') screen:expect([[ - <<<ts of text with lots of text with lot| + {1:<<<}ts of text with lots of text with lot| ^s of text with lots of text with lots of| text | - ~ |*2 + {1:~ }|*2 | ]]) feed('0') @@ -612,20 +616,20 @@ describe('smoothscroll', function() exec('set smoothscroll scrolloff=0 showbreak=+++\\ ') local s1 = [[ ^with lots of text in one line with lots | - +++ of text in one line with lots of tex| - +++ t in one line with lots of text in o| - +++ ne line with lots of text in one lin| - +++ e with lots of text in one line | + {1:+++ }of text in one line with lots of tex| + {1:+++ }t in one line with lots of text in o| + {1:+++ }ne line with lots of text in one lin| + {1:+++ }e with lots of text in one line | | ]] screen:expect(s1) feed('<C-E>') screen:expect([[ - +++ ^of text in one line with lots of tex| - +++ t in one line with lots of text in o| - +++ ne line with lots of text in one lin| - +++ e with lots of text in one line | - ~ | + {1:+++ }^of text in one line with lots of tex| + {1:+++ }t in one line with lots of text in o| + {1:+++ }ne line with lots of text in one lin| + {1:+++ }e with lots of text in one line | + {1:~ }| | ]]) feed('0') @@ -642,13 +646,13 @@ describe('smoothscroll', function() screen:expect([[ ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| 口口口口口口口口口口 | - ~ |*3 + {1:~ }|*3 | ]]) feed('<C-E>') screen:expect([[ - <<< 口口口口口口口^口 | - ~ |*4 + {1:<<<} 口口口口口口口^口 | + {1:~ }|*4 | ]]) end) @@ -656,10 +660,6 @@ describe('smoothscroll', function() -- oldtest: Test_smoothscroll_zero_width() it('does not divide by zero with a narrow window', function() screen:try_resize(12, 2) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.Brown }, - [2] = { foreground = Screen.colors.Blue1, bold = true }, - }) exec([[ call setline(1, ['a'->repeat(100)]) set wrap smoothscroll number laststatus=0 @@ -669,12 +669,12 @@ describe('smoothscroll', function() wincmd v ]]) screen:expect([[ - {1: 1^ }│{1: }│{1: }│{1: }│{1: }| + {8: 1^ }│{8: }│{8: }│{8: }│{8: }| | ]]) feed('llllllllll<C-W>o') screen:expect([[ - {2:<<<}{1: }aa^aaaaaa| + {1:<<<}{8: }aa^aaaaaa| | ]]) end) @@ -694,7 +694,7 @@ describe('smoothscroll', function() ]=]) feed('<C-E>gjgk') screen:expect([[ - <<<lots of text in one line^ | + {1:<<<}lots of text in one line^ | line two | line three | line four | @@ -717,7 +717,7 @@ describe('smoothscroll', function() call search('xxx') ]=]) screen:expect([[ - <<<_____________________________________| + {1:<<<}_____________________________________| ________________________________________| ______________________________________^xx| x______________________________________x| @@ -741,10 +741,10 @@ describe('smoothscroll', function() | ]]) exec("call setline(92, 'a'->repeat(100))") - feed('<C-B>G') + feed('<C-L><C-B>G') -- cursor is not placed below window screen:expect([[ - <<<aaaaaaaaaaaaaaaaa | + {1:<<<}aaaaaaaaaaaaaaaaa | |*7 ^ | | @@ -754,12 +754,6 @@ describe('smoothscroll', function() -- oldtest: Test_smoothscroll_incsearch() it('does not reset skipcol when doing incremental search on the same word', function() screen:try_resize(40, 8) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.Brown }, - [2] = { foreground = Screen.colors.Blue1, bold = true }, - [3] = { background = Screen.colors.Yellow1 }, - [4] = { reverse = true }, - }) exec([[ set smoothscroll number scrolloff=0 incsearch call setline(1, repeat([''], 20)) @@ -768,46 +762,46 @@ describe('smoothscroll', function() ]]) feed('/b') screen:expect([[ - {2:<<<}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaa | - {1: 12 } | - {1: 13 } | - {1: 14 }{4:b}{3:bbb} | - {1: 15 } | - {1: 16 } | - {1: 17 } | + {1:<<<}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {8: 12 } | + {8: 13 } | + {8: 14 }{2:b}{10:bbb} | + {8: 15 } | + {8: 16 } | + {8: 17 } | /b^ | ]]) feed('b') screen:expect([[ - {2:<<<}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaa | - {1: 12 } | - {1: 13 } | - {1: 14 }{4:bb}{3:bb} | - {1: 15 } | - {1: 16 } | - {1: 17 } | + {1:<<<}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {8: 12 } | + {8: 13 } | + {8: 14 }{2:bb}{10:bb} | + {8: 15 } | + {8: 16 } | + {8: 17 } | /bb^ | ]]) feed('b') screen:expect([[ - {2:<<<}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaa | - {1: 12 } | - {1: 13 } | - {1: 14 }{4:bbb}b | - {1: 15 } | - {1: 16 } | - {1: 17 } | + {1:<<<}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {8: 12 } | + {8: 13 } | + {8: 14 }{2:bbb}b | + {8: 15 } | + {8: 16 } | + {8: 17 } | /bbb^ | ]]) feed('b') screen:expect([[ - {2:<<<}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaa | - {1: 12 } | - {1: 13 } | - {1: 14 }{4:bbbb} | - {1: 15 } | - {1: 16 } | - {1: 17 } | + {1:<<<}{8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {8: 12 } | + {8: 13 } | + {8: 14 }{2:bbbb} | + {8: 15 } | + {8: 16 } | + {8: 17 } | /bbbb^ | ]]) end) @@ -815,10 +809,6 @@ describe('smoothscroll', function() -- oldtest: Test_smoothscroll_multi_skipcol() it('scrolling multiple lines and stopping at non-zero skipcol', function() screen:try_resize(40, 10) - screen:set_default_attr_ids({ - [0] = { foreground = Screen.colors.Blue, bold = true }, - [1] = { background = Screen.colors.Grey90 }, - }) exec([[ setlocal cursorline scrolloff=0 smoothscroll call setline(1, repeat([''], 8)) @@ -829,7 +819,7 @@ describe('smoothscroll', function() redraw ]]) screen:expect([[ - {1:^ }| + {21:^ }| | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| aaaaaaaaaa | @@ -841,22 +831,22 @@ describe('smoothscroll', function() ]]) feed('3<C-E>') screen:expect([[ - {0:<<<}{1:aaaaaa^a }| + {1:<<<}{21:aaaaaa^a }| aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| aaaaaaaaaa | |*2 bbb | ccc | - {0:~ }|*2 + {1:~ }|*2 | ]]) feed('2<C-E>') screen:expect([[ - {0:<<<}{1:aaaaaa^a }| + {1:<<<}{21:aaaaaa^a }| |*2 bbb | ccc | - {0:~ }|*4 + {1:~ }|*4 | ]]) end) @@ -864,12 +854,6 @@ describe('smoothscroll', function() -- oldtest: Test_smoothscroll_zero_width_scroll_cursor_bot() it('does not divide by zero in zero-width window', function() screen:try_resize(40, 19) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.Brown }, -- LineNr - [2] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [3] = { bold = true, reverse = true }, -- StatusLine - [4] = { reverse = true }, -- StatusLineNC - }) exec([[ silent normal yy silent normal 19p @@ -882,10 +866,10 @@ describe('smoothscroll', function() silent normal 20G ]]) screen:expect([[ - {1: }│ | - {2:@}│ |*15 - {2:^@}│ | - {3:< }{4:[No Name] [+] }| + {8: }│ | + {1:@}│ |*15 + {1:^@}│ | + {3:< }{2:[No Name] [+] }| | ]]) end) @@ -901,15 +885,15 @@ describe('smoothscroll', function() ]]) screen:expect([[ | - [No Name] | + {2:[No Name] }| line1 | line2 | ^line3line3line3line3line3line3line3line3| line3line3line3line3line3line3line3line3| line3line3line3line3 | line4 | - ~ |*2 - [No Name] [+] | + {1:~ }|*2 + {3:[No Name] [+] }| | ]]) end) @@ -929,6 +913,119 @@ describe('smoothscroll', function() assert_alive() end) + -- oldtest: Test_smoothscroll_insert_bottom() + it('works in Insert mode at bottom of window', function() + screen:try_resize(40, 9) + exec([[ + call setline(1, repeat([repeat('A very long line ...', 10)], 5)) + set wrap smoothscroll scrolloff=0 + ]]) + feed('Go123456789<CR>') + screen:expect([[ + {1:<<<}ery long line ...A very long line ...| + A very long line ...A very long line ...|*5 + 123456789 | + ^ | + {5:-- INSERT --} | + ]]) + end) + + -- oldtest: Test_smoothscroll_in_qf_window() + it('works in quickfix window when changing quickfix list', function() + screen:try_resize(60, 20) + exec([[ + set nocompatible display=lastline + copen 5 + setlocal number smoothscroll + let g:l = [{'text': 'foo'}] + repeat([{'text': join(range(30))}], 10) + call setqflist(g:l, 'r') + normal! G + wincmd t + let g:l1 = [{'text': join(range(1000))}] + ]]) + screen:expect([[ + ^ | + {1:~ }|*11 + {3:[No Name] }| + {1:<<<}{8: }21 22 23 24 25 26 27 28 29 | + {8: 10 }|| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | + {8: }21 22 23 24 25 26 27 28 29 | + {8: 11 }|| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | + {8: }21 22 23 24 25 26 27 28 29 | + {2:[Quickfix List] }| + | + ]]) + + feed([[:call setqflist([], 'r')<CR>]]) + local screen_empty = [[ + ^ | + {1:~ }|*11 + {3:[No Name] }| + {8: 1 } | + {1:~ }|*4 + {2:[Quickfix List] }| + :call setqflist([], 'r') | + ]] + screen:expect(screen_empty) + + feed([[:call setqflist(g:l, 'r')<CR>]]) + local screen_l_top = [[ + ^ | + {1:~ }|*11 + {3:[No Name] }| + {8: 1 }{10:|| foo }| + {8: 2 }|| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | + {8: }21 22 23 24 25 26 27 28 29 | + {8: 3 }|| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | + {8: }21 22 23 24 25 26 27 28 29 | + {2:[Quickfix List] }| + :call setqflist(g:l, 'r') | + ]] + screen:expect(screen_l_top) + + feed([[:call setqflist(g:l1, 'r')<CR>]]) + local screen_l1_top = [[ + ^ | + {1:~ }|*11 + {3:[No Name] }| + {8: 1 }{10:|| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 }| + {8: }{10:21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39}| + {8: }{10: 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 5}| + {8: }{10:8 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 }| + {8: }{10:77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95}| + {2:[Quickfix List] }| + :call setqflist(g:l1, 'r') | + ]] + screen:expect(screen_l1_top) + + feed('<C-W>b$<C-W>t') + local screen_l1_bot = [[ + ^ | + {1:~ }|*11 + {3:[No Name] }| + {1:<<<}{8: }{10: 937 938 939 940 941 942 943 944 945 946 947 948 949 950}| + {8: }{10: 951 952 953 954 955 956 957 958 959 960 961 962 963 964}| + {8: }{10: 965 966 967 968 969 970 971 972 973 974 975 976 977 978}| + {8: }{10: 979 980 981 982 983 984 985 986 987 988 989 990 991 992}| + {8: }{10: 993 994 995 996 997 998 999 }| + {2:[Quickfix List] }| + :call setqflist(g:l1, 'r') | + ]] + screen:expect(screen_l1_bot) + + feed([[:call setqflist([], 'r')<CR>]]) + screen:expect(screen_empty) + + feed([[:call setqflist(g:l1, 'r')<CR>]]) + screen:expect(screen_l1_top) + + feed('<C-W>b$<C-W>t') + screen:expect(screen_l1_bot) + + feed([[:call setqflist(g:l, 'r')<CR>]]) + screen:expect(screen_l_top) + end) + it('works with virt_lines above and below', function() screen:try_resize(55, 7) exec([=[ @@ -952,7 +1049,7 @@ describe('smoothscroll', function() ]]) feed('<C-E>') screen:expect([[ - <<<e text with some text with some text with some text | + {1:<<<}e text with some text with some text with some text | virt_below1 | virt_above1 | ^Line with some text with some text with some text with | @@ -977,7 +1074,7 @@ describe('smoothscroll', function() some text with some text with some text with some text | virt_below2 | virt_above2 | - Line with some text with some text with some text wi@@@| + Line with some text with some text with some text wi{1:@@@}| | ]]) feed('<C-E>') @@ -992,124 +1089,199 @@ describe('smoothscroll', function() ]]) feed('<C-E>') screen:expect([[ - <<<e text with some text with some text with some tex^t | + {1:<<<}e text with some text with some text with some tex^t | virt_below2 | virt_above2 | Line with some text with some text with some text with | some text with some text with some text with some text | - ~ | + {1:~ }| | ]]) end) - it('works in Insert mode at bottom of window', function() - screen:try_resize(40, 9) - exec([[ - call setline(1, repeat([repeat('A very long line ...', 10)], 5)) - set wrap smoothscroll scrolloff=0 - ]]) - feed('Go123456789<CR>') - screen:expect([[ - <<<ery long line ...A very long line ...| - A very long line ...A very long line ...|*5 - 123456789 | - ^ | - -- INSERT -- | - ]]) - end) - it('<<< marker shows with tabline, winbar and splits', function() screen:try_resize(40, 12) + screen:set_default_attr_ids({ + [1] = { foreground = Screen.colors.Blue1, bold = true }, + [2] = { reverse = true }, + [3] = { bold = true, reverse = true }, + [4] = { background = Screen.colors.LightMagenta }, + [5] = { bold = true }, + [31] = { foreground = Screen.colors.Fuchsia, bold = true }, + }) exec([[ call setline(1, ['Line' .. (' with some text'->repeat(7))]->repeat(7)) set smoothscroll scrolloff=0 norm sj ]]) screen:expect([[ - <<<e text with some text with some text | + {1:<<<}e text with some text with some text | with some text with some text | Line with some text with some text with | some text with some text with some text | with some text with some text | - [No Name] [+] | - <<<e text with some text with some text | + {2:[No Name] [+] }| + {1:<<<}e text with some text with some text | ^with some text with some text | Line with some text with some text with | - some text with some text with some te@@@| - [No Name] [+] | + some text with some text with some te{1:@@@}| + {3:[No Name] [+] }| | ]]) exec('set showtabline=2') feed('<C-E>') screen:expect([[ - 2+ [No Name] | - <<<e text with some text with some text | + {5: }{31:2}{5:+ [No Name] }{2: }| + {1:<<<}e text with some text with some text | with some text with some text | Line with some text with some text with | some text with some text with some text | with some text with some text | - [No Name] [+] | - <<<e text with some text with some text | + {2:[No Name] [+] }| + {1:<<<}e text with some text with some text | ^with some text with some text | - Line with some text with some text wi@@@| - [No Name] [+] | + Line with some text with some text wi{1:@@@}| + {3:[No Name] [+] }| | ]]) exec('set winbar=winbar') feed('<C-w>k<C-E>') screen:expect([[ - 2+ [No Name] | - winbar | - <<<e text with some text with some text | + {5: }{31:2}{5:+ [No Name] }{2: }| + {5:winbar }| + {1:<<<}e text with some text with some text | ^with some text with some text | Line with some text with some text with | - some text with some text with some te@@@| - [No Name] [+] | - winbar | - <<<e text with some text with some text | + some text with some text with some te{1:@@@}| + {3:[No Name] [+] }| + {5:winbar }| + {1:<<<}e text with some text with some text | with some text with some text | - [No Name] [+] | + {2:[No Name] [+] }| | ]]) end) it('works with very long line', function() - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.Brown }, - [2] = { foreground = Screen.colors.Blue1, bold = true }, - }) exec([[ edit test/functional/fixtures/bigfile_oneline.txt setlocal smoothscroll number ]]) screen:expect([[ - {1: 1 }^0000;<control>;Cc;0;BN;;;;;N;NULL;;;| - {1: }; 0001;<control>;Cc;0;BN;;;;;N;START| - {1: } OF HEADING;;;; 0002;<control>;Cc;0;| - {1: }BN;;;;;N;START OF TEXT;;;; 0003;<con| - {1: }trol>;Cc;0;BN;;;;;N;END OF TEXT;;;; | - {1: }0004;<control>;Cc;0;BN;;;;;N;END OF | - {1: }TRANSMISSION;;;; 0005;<control>;Cc;0| - {1: };BN;;;;;N;ENQUIRY;;;; 0006;<control>| - {1: };Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; 0007;| - {1: }<control>;Cc;0;BN;;;;;N;BELL;;;; 000| - {1: }8;<control>;Cc;0;BN;;;;;N;BACKSPACE;| + {8: 1 }^0000;<control>;Cc;0;BN;;;;;N;NULL;;;| + {8: }; 0001;<control>;Cc;0;BN;;;;;N;START| + {8: } OF HEADING;;;; 0002;<control>;Cc;0;| + {8: }BN;;;;;N;START OF TEXT;;;; 0003;<con| + {8: }trol>;Cc;0;BN;;;;;N;END OF TEXT;;;; | + {8: }0004;<control>;Cc;0;BN;;;;;N;END OF | + {8: }TRANSMISSION;;;; 0005;<control>;Cc;0| + {8: };BN;;;;;N;ENQUIRY;;;; 0006;<control>| + {8: };Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; 0007;| + {8: }<control>;Cc;0;BN;;;;;N;BELL;;;; 000| + {8: }8;<control>;Cc;0;BN;;;;;N;BACKSPACE;| | ]]) feed('j') screen:expect([[ - {2:<<<}{1: }CJK COMPATIBILITY IDEOGRAPH-2F91F;Lo| - {1: };0;L;243AB;;;;N;;;;; 2F920;CJK COMPA| - {1: }TIBILITY IDEOGRAPH-2F920;Lo;0;L;7228| - {1: };;;;N;;;;; 2F921;CJK COMPATIBILITY I| - {1: }DEOGRAPH-2F921;Lo;0;L;7235;;;;N;;;;;| - {1: } 2F922;CJK COMPATIBILITY IDEOGRAPH-2| - {1: }F922;Lo;0;L;7250;;;;N;;;;; | - {1: 2 }^2F923;CJK COMPATIBILITY IDEOGRAPH-2F| - {1: }923;Lo;0;L;24608;;;;N;;;;; | - {1: 3 }2F924;CJK COMPATIBILITY IDEOGRAPH-2F| - {1: }924;Lo;0;L;7280;;;;N;;;;; | + {1:<<<}{8: }CJK COMPATIBILITY IDEOGRAPH-2F91F;Lo| + {8: };0;L;243AB;;;;N;;;;; 2F920;CJK COMPA| + {8: }TIBILITY IDEOGRAPH-2F920;Lo;0;L;7228| + {8: };;;;N;;;;; 2F921;CJK COMPATIBILITY I| + {8: }DEOGRAPH-2F921;Lo;0;L;7235;;;;N;;;;;| + {8: } 2F922;CJK COMPATIBILITY IDEOGRAPH-2| + {8: }F922;Lo;0;L;7250;;;;N;;;;; | + {8: 2 }^2F923;CJK COMPATIBILITY IDEOGRAPH-2F| + {8: }923;Lo;0;L;24608;;;;N;;;;; | + {8: 3 }2F924;CJK COMPATIBILITY IDEOGRAPH-2F| + {8: }924;Lo;0;L;7280;;;;N;;;;; | | ]]) end) + + it('works with very long line and scrolloff', function() + screen:try_resize(40, 8) + exec([[ + set smoothscroll scrolloff=3 + call setline(1, ['one', 'two long '->repeat(100), 'three', 'four', 'five', 'six']) + ]]) + --FIXME: incorrect screen due to reset_skipcol()/curs_columns() shenanigans + feed(':norm j721|<CR>') + screen:expect([[ + two long two long two long two long two | + long two long two long two long two long| + two long two long two long two long two| + ^ long two long two long two long two lon| + g two long two long two long two long tw| + o long two long two long two long two lo| + ng two long two long two long two long t| + :norm j721| | + ]]) + feed('gj') + screen:expect([[ + {1:<<<}two long two long two long two long t| + wo long two long two long two long two l| + ong two long two long two long two long | + two long two long two long two long two | + ^long two long two long two long two long| + two long two long two long two long two| + long two long two long two long two lon| + :norm j721| | + ]]) + feed('gj') + screen:expect([[ + {1:<<<}long two long two long two long two l| + ong two long two long two long two long | + two long two long two long two long two | + long two long two long two long two long| + ^ two long two long two long two long two| + long two long two long two long two lon| + g two long two long | + :norm j721| | + ]]) + feed('gj') + screen:expect([[ + {1:<<<}long two long two long two long two l| + ong two long two long two long two long | + two long two long two long two long two | + long two long two long two long two long| + two long two long two long two long two| + ^ long two long two long two long two lon| + g two long two long | + :norm j721| | + ]]) + feed('gj') + screen:expect([[ + {1:<<<}long two long two long two long two l| + ong two long two long two long two long | + two long two long two long two long two | + long two long two long two long two long| + two long two long two long two long two| + long two long two long two long two lon| + ^g two long two long | + :norm j721| | + ]]) + feed('gj') + screen:expect([[ + {1:<<<} long two long two long two long two | + long two long two long two long two long| + two long two long two long two long two| + long two long two long two long two lon| + g two long two long | + ^three | + four | + :norm j721| | + ]]) + feed('gk') + --FIXME: incorrect screen due to reset_skipcol()/curs_columns() shenanigans + screen:expect([[ + two long two long two long two long two | + long two long two long two long two long| + two long two long two long two long two| + long two long two long two long two lon| + g two long two long two long two long tw| + o long two long two long two long two lo| + ^ng two long two long two long two long t| + :norm j721| | + ]]) + end) end) diff --git a/test/functional/legacy/search_mbyte_spec.lua b/test/functional/legacy/search_mbyte_spec.lua index ef7e41aa30..6457567176 100644 --- a/test/functional/legacy/search_mbyte_spec.lua +++ b/test/functional/legacy/search_mbyte_spec.lua @@ -1,10 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() -local poke_eventloop = helpers.poke_eventloop -local clear = helpers.clear -local insert = helpers.insert -local expect = helpers.expect -local command = helpers.command +local poke_eventloop = n.poke_eventloop +local clear = n.clear +local insert = n.insert +local expect = n.expect +local command = n.command describe('search_mbyte', function() before_each(clear) diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua index 2fda341123..d421a96579 100644 --- a/test/functional/legacy/search_spec.lua +++ b/test/functional/legacy/search_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local fn = helpers.fn -local poke_eventloop = helpers.poke_eventloop -local exec = helpers.exec + +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local feed = n.feed +local fn = n.fn +local poke_eventloop = n.poke_eventloop +local exec = n.exec describe('search cmdline', function() local screen @@ -745,11 +747,6 @@ describe('Search highlight', function() -- oldtest: Test_hlsearch_dump() it('beyond line end vim-patch:8.2.2542', function() local screen = Screen.new(50, 6) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [2] = { background = Screen.colors.Yellow }, -- Search - [3] = { background = Screen.colors.Grey90 }, -- CursorLine - }) screen:attach() exec([[ set hlsearch noincsearch cursorline @@ -759,8 +756,8 @@ describe('Search highlight', function() ]]) feed([[/\_.*<CR>]]) screen:expect([[ - {2:xxx } |*2 - {2:^xxx }{3: }| + {10:xxx } |*2 + {10:^xxx }{21: }| {1:~ }|*2 /\_.* | ]]) diff --git a/test/functional/legacy/search_stat_spec.lua b/test/functional/legacy/search_stat_spec.lua index 378060d316..7779b8bef1 100644 --- a/test/functional/legacy/search_stat_spec.lua +++ b/test/functional/legacy/search_stat_spec.lua @@ -1,6 +1,7 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, exec, command = helpers.clear, helpers.feed, helpers.exec, helpers.command + +local clear, feed, exec, command = n.clear, n.feed, n.exec, n.command describe('search stat', function() local screen diff --git a/test/functional/legacy/signs_spec.lua b/test/functional/legacy/signs_spec.lua index 0e65edbcf8..614673ee3c 100644 --- a/test/functional/legacy/signs_spec.lua +++ b/test/functional/legacy/signs_spec.lua @@ -1,7 +1,8 @@ -- Tests for signs -local helpers = require('test.functional.helpers')(after_each) -local clear, command, expect = helpers.clear, helpers.command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, command, expect = n.clear, n.command, n.expect describe('signs', function() setup(clear) diff --git a/test/functional/legacy/source_spec.lua b/test/functional/legacy/source_spec.lua index 7a19541a77..a910dc3d43 100644 --- a/test/functional/legacy/source_spec.lua +++ b/test/functional/legacy/source_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local feed = helpers.feed -local write_file = helpers.write_file + +local clear = n.clear +local feed = n.feed +local write_file = t.write_file before_each(clear) @@ -16,16 +18,12 @@ describe(':source!', function() ]] ) local screen = Screen.new(75, 6) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg - }) screen:attach() feed(':source! Xscript.vim\n') screen:expect([[ ^ | - {0:~ }|*4 - {1:E22: Scripts nested too deep} | + {1:~ }|*4 + {9:E22: Scripts nested too deep} | ]]) os.remove('Xscript.vim') end) diff --git a/test/functional/legacy/statusline_spec.lua b/test/functional/legacy/statusline_spec.lua index 567e829879..148166fdc3 100644 --- a/test/functional/legacy/statusline_spec.lua +++ b/test/functional/legacy/statusline_spec.lua @@ -1,8 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local feed = helpers.feed + +local clear = n.clear +local exec = n.exec +local feed = n.feed before_each(clear) @@ -15,11 +16,6 @@ describe('statusline', function() end) it('is updated in cmdline mode when using window-local statusline vim-patch:8.2.2737', function() - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [2] = { bold = true, reverse = true }, -- StatusLine - [3] = { reverse = true }, -- StatusLineNC - }) exec([[ setlocal statusline=-%{mode()}- split @@ -28,30 +24,25 @@ describe('statusline', function() screen:expect([[ ^ | {1:~ }| - {2:+n+ }| + {3:+n+ }| | {1:~ }| - {3:-n- }| + {2:-n- }| | ]]) feed(':') screen:expect([[ | {1:~ }| - {2:+c+ }| + {3:+c+ }| | {1:~ }| - {3:-c- }| + {2:-c- }| :^ | ]]) end) it('truncated item does not cause off-by-one highlight vim-patch:8.2.4929', function() - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [2] = { foreground = Screen.colors.Blue }, -- User1 - [3] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- User2 - }) exec([[ set laststatus=2 hi! link User1 Directory @@ -61,20 +52,13 @@ describe('statusline', function() screen:expect([[ ^ | {1:~ }|*4 - {3:<F}{2:GHI }| + {9:<F}{18:GHI }| | ]]) end) -- oldtest: Test_statusline_showcmd() it('showcmdloc=statusline works', function() - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, -- Visual - [2] = { bold = true }, -- MoreMsg - [3] = { bold = true, reverse = true }, -- StatusLine - [5] = { background = Screen.colors.LightGrey, foreground = Screen.colors.DarkBlue }, -- Folded - }) exec([[ func MyStatusLine() return '%S' @@ -92,9 +76,9 @@ describe('statusline', function() feed('g') screen:expect([[ - {5:+-- 2 lines: a···································}| + {13:+-- 2 lines: a···································}| ^c | - {0:~ }|*3 + {1:~ }|*3 {3:g }| | ]]) @@ -105,19 +89,19 @@ describe('statusline', function() ^a | b | c | - {0:~ }|*2 + {1:~ }|*2 {3: }| | ]]) feed('<C-V>Gl') screen:expect([[ - {1:a} | - {1:b} | - {1:c}^ | - {0:~ }|*2 + {17:a} | + {17:b} | + {17:c}^ | + {1:~ }|*2 {3:3x2 }| - {2:-- VISUAL BLOCK --} | + {5:-- VISUAL BLOCK --} | ]]) feed('<Esc>1234') @@ -125,7 +109,7 @@ describe('statusline', function() a | b | ^c | - {0:~ }|*2 + {1:~ }|*2 {3:1234 }| | ]]) @@ -137,7 +121,7 @@ describe('statusline', function() a | b | ^c | - {0:~ }|*2 + {1:~ }|*2 {3:[No Name] [+] 1234 }| : | ]]) diff --git a/test/functional/legacy/substitute_spec.lua b/test/functional/legacy/substitute_spec.lua index b462c10202..647d62782c 100644 --- a/test/functional/legacy/substitute_spec.lua +++ b/test/functional/legacy/substitute_spec.lua @@ -2,12 +2,14 @@ -- Test for submatch() on substitute(). -- Test for *:s%* on :substitute. -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed, insert = helpers.feed, helpers.insert -local exec = helpers.exec -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect -local eq, eval = helpers.eq, helpers.eval + +local feed, insert = n.feed, n.insert +local exec = n.exec +local clear, feed_command, expect = n.clear, n.feed_command, n.expect +local eq, eval = t.eq, n.eval describe('substitute()', function() before_each(clear) @@ -209,11 +211,6 @@ describe(':substitute', function() it('first char is highlighted with confirmation dialog and empty match', function() local screen = Screen.new(60, 8) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { reverse = true }, -- IncSearch - [2] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg - }) screen:attach() exec([[ set nohlsearch noincsearch @@ -221,11 +218,11 @@ describe(':substitute', function() ]]) feed(':%s/^/ /c<CR>') screen:expect([[ - {1:o}ne | + {2:o}ne | two | three | - {0:~ }|*4 - {2:replace with (y/n/a/q/l/^E/^Y)?}^ | + {1:~ }|*4 + {6:replace with (y/n/a/q/l/^E/^Y)?}^ | ]]) end) end) diff --git a/test/functional/legacy/syn_attr_spec.lua b/test/functional/legacy/syn_attr_spec.lua index ec47bdf9af..155eb9ce11 100644 --- a/test/functional/legacy/syn_attr_spec.lua +++ b/test/functional/legacy/syn_attr_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval -- oldtest: Test_missing_attr() describe('synIDattr()', function() diff --git a/test/functional/legacy/tabline_spec.lua b/test/functional/legacy/tabline_spec.lua index 683c7d9bd7..4ce32f2fdd 100644 --- a/test/functional/legacy/tabline_spec.lua +++ b/test/functional/legacy/tabline_spec.lua @@ -1,8 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local feed = helpers.feed + +local clear = n.clear +local exec = n.exec +local feed = n.feed before_each(clear) @@ -16,14 +17,6 @@ describe('tabline', function() -- oldtest: Test_tabline_showcmd() it('showcmdloc=tabline works', function() - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, -- Visual - [2] = { bold = true }, -- MoreMsg, TabLineSel - [3] = { reverse = true }, -- TabLineFill - [4] = { background = Screen.colors.LightGrey, underline = true }, -- TabLine - [5] = { background = Screen.colors.LightGrey, foreground = Screen.colors.DarkBlue }, -- Folded - }) exec([[ func MyTabLine() return '%S' @@ -41,41 +34,41 @@ describe('tabline', function() feed('g') screen:expect([[ - {3:g }| - {5:+-- 2 lines: a···································}| + {2:g }| + {13:+-- 2 lines: a···································}| ^c | - {0:~ }|*3 + {1:~ }|*3 | ]]) -- typing "gg" should open the fold feed('g') screen:expect([[ - {3: }| + {2: }| ^a | b | c | - {0:~ }|*2 + {1:~ }|*2 | ]]) feed('<C-V>Gl') screen:expect([[ - {3:3x2 }| - {1:a} | - {1:b} | - {1:c}^ | - {0:~ }|*2 - {2:-- VISUAL BLOCK --} | + {2:3x2 }| + {17:a} | + {17:b} | + {17:c}^ | + {1:~ }|*2 + {5:-- VISUAL BLOCK --} | ]]) feed('<Esc>1234') screen:expect([[ - {3:1234 }| + {2:1234 }| a | b | ^c | - {0:~ }|*2 + {1:~ }|*2 | ]]) @@ -83,11 +76,11 @@ describe('tabline', function() feed(':<CR>') feed('1234') screen:expect([[ - {2: + [No Name] }{3: }{4:1234}{3: }| + {5: + [No Name] }{2: }{24:1234}{2: }| a | b | ^c | - {0:~ }|*2 + {1:~ }|*2 : | ]]) end) diff --git a/test/functional/legacy/tagcase_spec.lua b/test/functional/legacy/tagcase_spec.lua index f84fc673cf..e5bdd4658a 100644 --- a/test/functional/legacy/tagcase_spec.lua +++ b/test/functional/legacy/tagcase_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local exc_exec = helpers.exc_exec -local expect = helpers.expect -local insert = helpers.insert -local source = helpers.source -local write_file = helpers.write_file +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local eval = n.eval +local exc_exec = n.exc_exec +local expect = n.expect +local insert = n.insert +local source = n.source +local write_file = t.write_file describe("'tagcase' option", function() setup(function() diff --git a/test/functional/legacy/textobjects_spec.lua b/test/functional/legacy/textobjects_spec.lua index 96f655cc41..9ed8716bff 100644 --- a/test/functional/legacy/textobjects_spec.lua +++ b/test/functional/legacy/textobjects_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local call = helpers.call -local clear = helpers.clear -local command = helpers.command -local expect = helpers.expect -local source = helpers.source +local n = require('test.functional.testnvim')() + +local call = n.call +local clear = n.clear +local command = n.command +local expect = n.expect +local source = n.source describe('Text object', function() before_each(function() diff --git a/test/functional/legacy/undolevels_spec.lua b/test/functional/legacy/undolevels_spec.lua index e8badc6864..8453921669 100644 --- a/test/functional/legacy/undolevels_spec.lua +++ b/test/functional/legacy/undolevels_spec.lua @@ -1,6 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local source, clear = helpers.source, helpers.clear -local eq, nvim = helpers.eq, helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local source, clear = n.source, n.clear +local eq, api = t.eq, n.api describe('undolevel', function() setup(clear) @@ -57,6 +59,6 @@ describe('undolevel', function() call Test_global_local_undolevels() ]]) - eq({}, nvim.nvim_get_vvar('errors')) + eq({}, api.nvim_get_vvar('errors')) end) end) diff --git a/test/functional/legacy/utf8_spec.lua b/test/functional/legacy/utf8_spec.lua index 67a4bec4c5..cdb0c2ca58 100644 --- a/test/functional/legacy/utf8_spec.lua +++ b/test/functional/legacy/utf8_spec.lua @@ -1,11 +1,13 @@ -- Tests for Unicode manipulations -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command, expect = helpers.command, helpers.expect -local eq, eval = helpers.eq, helpers.eval -local source = helpers.source -local poke_eventloop = helpers.poke_eventloop +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, feed, insert = n.clear, n.feed, n.insert +local command, expect = n.command, n.expect +local eq, eval = t.eq, n.eval +local source = n.source +local poke_eventloop = n.poke_eventloop describe('utf8', function() before_each(clear) diff --git a/test/functional/legacy/vimscript_spec.lua b/test/functional/legacy/vimscript_spec.lua index 8b0a920a3e..66054810a6 100644 --- a/test/functional/legacy/vimscript_spec.lua +++ b/test/functional/legacy/vimscript_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local feed = helpers.feed -local api = helpers.api + +local clear = n.clear +local exec = n.exec +local feed = n.feed +local api = n.api before_each(clear) diff --git a/test/functional/legacy/visual_spec.lua b/test/functional/legacy/visual_spec.lua index 151e5874e1..ab2213b2fc 100644 --- a/test/functional/legacy/visual_spec.lua +++ b/test/functional/legacy/visual_spec.lua @@ -1,9 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) - +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local feed = helpers.feed -local exec = helpers.exec + +local clear = n.clear +local feed = n.feed +local exec = n.exec before_each(clear) @@ -12,11 +12,6 @@ describe('Visual highlight', function() before_each(function() screen = Screen.new(50, 6) - screen:set_default_attr_ids({ - [0] = { foreground = Screen.colors.Blue, bold = true }, -- NonText - [1] = { bold = true }, -- ModeMsg - [2] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, -- Visual - }) screen:attach() end) @@ -30,20 +25,20 @@ describe('Visual highlight', function() feed('<C-V>gg$') screen:expect([[ - {2:aaaaaa}^ | - {2:bbbb } | - {2:cc } | - {0:~ }|*2 - {1:-- VISUAL BLOCK --} | + {17:aaaaaa}^ | + {17:bbbb } | + {17:cc } | + {1:~ }|*2 + {5:-- VISUAL BLOCK --} | ]]) feed('<Esc>gg<C-V>G$') screen:expect([[ - {2:aaaaaa } | - {2:bbbb } | - {2:cc}^ {2: } | - {0:~ }|*2 - {1:-- VISUAL BLOCK --} | + {17:aaaaaa } | + {17:bbbb } | + {17:cc}^ {17: } | + {1:~ }|*2 + {5:-- VISUAL BLOCK --} | ]]) end) @@ -57,9 +52,9 @@ describe('Visual highlight', function() screen:expect([[ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^a| - {0:+}{2:aaaa}aaaaaa | - {0:~ }|*3 - {1:-- VISUAL --} | + {1:+}{17:aaaa}aaaaaa | + {1:~ }|*3 + {5:-- VISUAL --} | ]]) end) end) diff --git a/test/functional/legacy/window_cmd_spec.lua b/test/functional/legacy/window_cmd_spec.lua index 7fe4ec8eb6..332240d04a 100644 --- a/test/functional/legacy/window_cmd_spec.lua +++ b/test/functional/legacy/window_cmd_spec.lua @@ -1,18 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local exec_lua = helpers.exec_lua -local command = helpers.command -local feed = helpers.feed + +local clear = n.clear +local exec = n.exec +local exec_lua = n.exec_lua +local command = n.command +local feed = n.feed -- oldtest: Test_window_cmd_ls0_split_scrolling() it('scrolling with laststatus=0 and :botright split', function() clear('--cmd', 'set ruler') local screen = Screen.new(40, 10) - screen:set_default_attr_ids({ - [1] = { reverse = true }, -- StatusLineNC - }) screen:attach() exec([[ set laststatus=0 @@ -25,7 +23,7 @@ it('scrolling with laststatus=0 and :botright split', function() 98 | 99 | 100 | - {1:[No Name] [+] 100,1 Bot}| + {2:[No Name] [+] 100,1 Bot}| 97 | 98 | 99 | @@ -65,10 +63,10 @@ describe('splitkeep', function() 99 | ^100 | 101 | - [No Name] [+] | + {3:[No Name] [+] }| 5 | 6 | - [No Name] [+] | + {2:[No Name] [+] }| | ]]) @@ -77,10 +75,10 @@ describe('splitkeep', function() 100 | ^101 | 102 | - [No Name] [+] | + {3:[No Name] [+] }| 5 | 6 | - [No Name] [+] | + {2:[No Name] [+] }| | ]]) @@ -90,10 +88,10 @@ describe('splitkeep', function() 198 | 199 | ^200 | - [No Name] [+] | + {3:[No Name] [+] }| 5 | 6 | - [No Name] [+] | + {2:[No Name] [+] }| | ]]) end) @@ -135,13 +133,13 @@ describe('splitkeep', function() 3 | 4 | 5 | - [No Name] [+] | + {2:[No Name] [+] }| ^7 | 8 | 9 | 10 | 11 | - [No Name] [+] | + {3:[No Name] [+] }| | ]]) feed(':quit<CR>Ht') @@ -152,13 +150,13 @@ describe('splitkeep', function() 3 | 4 | 5 | - [No Name] [+] | + {3:[No Name] [+] }| 7 | 8 | 9 | 10 | 11 | - [No Name] [+] | + {2:[No Name] [+] }| :quit | ]]) feed(':set sb<CR>:quit<CR>Gj') @@ -168,14 +166,14 @@ describe('splitkeep', function() 3 | 4 | ^5 | - [No Name] [+] | + {3:[No Name] [+] }| 7 | 8 | 9 | 10 | 11 | 12 | - [No Name] [+] | + {2:[No Name] [+] }| :quit | ]]) feed(':quit<CR>Gt') @@ -185,14 +183,14 @@ describe('splitkeep', function() 3 | 4 | 5 | - [No Name] [+] | + {2:[No Name] [+] }| 7 | 8 | 9 | 10 | 11 | ^12 | - [No Name] [+] | + {3:[No Name] [+] }| :quit | ]]) end) @@ -213,70 +211,70 @@ describe('splitkeep', function() ]]) feed('L:wincmd s<CR>') screen:expect([[ - 1 +-- 7 lines: int FuncName() {···················| - 8 after fold | - 9 +-- 7 lines: int FuncName() {···················| - 16 after fold | - 17 +-- 7 lines: int FuncName() {···················| - 24 ^after fold | - [No Name] [+] | - 32 after fold | - 33 +-- 7 lines: int FuncName() {···················| - 40 after fold | - 41 +-- 7 lines: int FuncName() {···················| - 48 after fold | - [No Name] [+] | + {8: 1 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 8 }after fold | + {8: 9 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 16 }after fold | + {8: 17 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 24 }^after fold | + {3:[No Name] [+] }| + {8: 32 }after fold | + {8: 33 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 40 }after fold | + {8: 41 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 48 }after fold | + {2:[No Name] [+] }| :wincmd s | ]]) feed(':quit<CR>') screen:expect([[ - 1 +-- 7 lines: int FuncName() {···················| - 8 after fold | - 9 +-- 7 lines: int FuncName() {···················| - 16 after fold | - 17 +-- 7 lines: int FuncName() {···················| - 24 after fold | - 25 +-- 7 lines: int FuncName() {···················| - 32 after fold | - 33 +-- 7 lines: int FuncName() {···················| - 40 after fold | - 41 +-- 7 lines: int FuncName() {···················| - 48 after fold | - 49 ^+-- 7 lines: int FuncName() {···················| + {8: 1 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 8 }after fold | + {8: 9 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 16 }after fold | + {8: 17 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 24 }after fold | + {8: 25 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 32 }after fold | + {8: 33 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 40 }after fold | + {8: 41 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 48 }after fold | + {8: 49 }{13:^+-- 7 lines: int FuncName() {···················}| :quit | ]]) feed('H:below split<CR>') screen:expect([[ - 1 +-- 7 lines: int FuncName() {···················| - 8 after fold | - 9 +-- 7 lines: int FuncName() {···················| - 16 after fold | - 17 +-- 7 lines: int FuncName() {···················| - [No Name] [+] | - 25 ^+-- 7 lines: int FuncName() {···················| - 32 after fold | - 33 +-- 7 lines: int FuncName() {···················| - 40 after fold | - 41 +-- 7 lines: int FuncName() {···················| - 48 after fold | - [No Name] [+] | + {8: 1 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 8 }after fold | + {8: 9 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 16 }after fold | + {8: 17 }{13:+-- 7 lines: int FuncName() {···················}| + {2:[No Name] [+] }| + {8: 25 }{13:^+-- 7 lines: int FuncName() {···················}| + {8: 32 }after fold | + {8: 33 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 40 }after fold | + {8: 41 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 48 }after fold | + {3:[No Name] [+] }| :below split | ]]) feed(':wincmd k<CR>:quit<CR>') screen:expect([[ - 1 +-- 7 lines: int FuncName() {···················| - 8 after fold | - 9 +-- 7 lines: int FuncName() {···················| - 16 after fold | - 17 +-- 7 lines: int FuncName() {···················| - 24 after fold | - 25 ^+-- 7 lines: int FuncName() {···················| - 32 after fold | - 33 +-- 7 lines: int FuncName() {···················| - 40 after fold | - 41 +-- 7 lines: int FuncName() {···················| - 48 after fold | - 49 +-- 7 lines: int FuncName() {···················| + {8: 1 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 8 }after fold | + {8: 9 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 16 }after fold | + {8: 17 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 24 }after fold | + {8: 25 }{13:^+-- 7 lines: int FuncName() {···················}| + {8: 32 }after fold | + {8: 33 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 40 }after fold | + {8: 41 }{13:+-- 7 lines: int FuncName() {···················}| + {8: 48 }after fold | + {8: 49 }{13:+-- 7 lines: int FuncName() {···················}| :quit | ]]) end) @@ -296,13 +294,13 @@ describe('splitkeep', function() a | b | c | - ~ |*4 - [No Name] | + {1:~ }|*4 + {2:[No Name] }| ^a | b | c | - ~ | - [No Name] | + {1:~ }| + {3:[No Name] }| | ]]) end) @@ -317,16 +315,16 @@ describe('splitkeep', function() wincmd s ]]) screen:expect([[ - <<<e line with lots of text in one line | + {1:<<<}e line with lots of text in one line | with lots of text in one line with lots | of text in one line | - ~ | - [No Name] [+] | - <<<e line with lots of text in one line | + {1:~ }| + {2:[No Name] [+] }| + {1:<<<}e line with lots of text in one line | ^with lots of text in one line with lots | of text in one line | - ~ |*2 - [No Name] [+] | + {1:~ }|*2 + {3:[No Name] [+] }| | ]]) end) diff --git a/test/functional/legacy/wordcount_spec.lua b/test/functional/legacy/wordcount_spec.lua index 82021dd98d..85f2a1623c 100644 --- a/test/functional/legacy/wordcount_spec.lua +++ b/test/functional/legacy/wordcount_spec.lua @@ -1,10 +1,12 @@ -- Test for wordcount() function -local helpers = require('test.functional.helpers')(after_each) -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, command = helpers.clear, helpers.command -local eq, eval = helpers.eq, helpers.eval -local poke_eventloop = helpers.poke_eventloop +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local feed, insert, source = n.feed, n.insert, n.source +local clear, command = n.clear, n.command +local eq, eval = t.eq, n.eval +local poke_eventloop = n.poke_eventloop describe('wordcount', function() before_each(clear) diff --git a/test/functional/legacy/writefile_spec.lua b/test/functional/legacy/writefile_spec.lua index 4d54e07d6a..a5a2c3c682 100644 --- a/test/functional/legacy/writefile_spec.lua +++ b/test/functional/legacy/writefile_spec.lua @@ -1,7 +1,8 @@ -- Tests for writefile() -local helpers = require('test.functional.helpers')(after_each) -local clear, command, expect = helpers.clear, helpers.command, helpers.expect +local n = require('test.functional.testnvim')() + +local clear, command, expect = n.clear, n.command, n.expect describe('writefile', function() setup(clear) diff --git a/test/functional/lua/api_spec.lua b/test/functional/lua/api_spec.lua index acd56a0ddb..56969150bd 100644 --- a/test/functional/lua/api_spec.lua +++ b/test/functional/lua/api_spec.lua @@ -1,15 +1,16 @@ -- Test suite for testing interactions with API bindings -local helpers = require('test.functional.helpers')(after_each) - -local exc_exec = helpers.exc_exec -local remove_trace = helpers.remove_trace -local fn = helpers.fn -local clear = helpers.clear -local eval = helpers.eval +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local exc_exec = n.exc_exec +local remove_trace = t.remove_trace +local fn = n.fn +local clear = n.clear +local eval = n.eval local NIL = vim.NIL -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local pcall_err = helpers.pcall_err +local eq = t.eq +local exec_lua = n.exec_lua +local pcall_err = t.pcall_err before_each(clear) diff --git a/test/functional/lua/base64_spec.lua b/test/functional/lua/base64_spec.lua index 21fd536a98..529f5f56e8 100644 --- a/test/functional/lua/base64_spec.lua +++ b/test/functional/lua/base64_spec.lua @@ -1,9 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local pcall_err = helpers.pcall_err -local matches = helpers.matches +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec_lua = n.exec_lua +local eq = t.eq +local pcall_err = t.pcall_err +local matches = t.matches describe('vim.base64', function() before_each(clear) @@ -42,6 +44,7 @@ describe('vim.base64', function() ̦H̬̤̗̤͝e͜ ̜̥̝̻͍̟́w̕h̖̯͓o̝͙̖͎̱̮ ҉̺̙̞̟͈W̷̼̭a̺̪͍į͈͕̭͙̯̜t̶̼̮s̘͙͖̕ ̠̫̠B̻͍͙͉̳ͅe̵h̵̬͇̫͙i̹͓̳̳̮͎̫̕n͟d̴̪̜̖ ̰͉̩͇͙̲͞ͅT͖̼͓̪͢h͏͓̮̻e̬̝̟ͅ ̤̹̝W͙̞̝͔͇͝ͅa͏͓͔̹̼̣l̴͔̰̤̟͔ḽ̫.͕ Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮ ]], + 'Hello\0world', } for _, v in ipairs(values) do diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 714e1b951f..d4af7e4732 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -1,17 +1,19 @@ -- Test suite for testing interactions with API bindings -local helpers = require('test.functional.helpers')(after_each) - -local command = helpers.command -local api = helpers.api -local fn = helpers.fn -local clear = helpers.clear -local eq = helpers.eq -local fail = helpers.fail -local exec_lua = helpers.exec_lua -local feed = helpers.feed -local expect_events = helpers.expect_events -local write_file = helpers.write_file -local dedent = helpers.dedent +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local Screen = require('test.functional.ui.screen') + +local command = n.command +local api = n.api +local fn = n.fn +local clear = n.clear +local eq = t.eq +local fail = t.fail +local exec_lua = n.exec_lua +local feed = n.feed +local expect_events = t.expect_events +local write_file = t.write_file +local dedent = t.dedent local origlines = { 'original line 1', @@ -291,11 +293,11 @@ describe('lua buffer event callbacks: on_lines', function() exec_lua(code) command('q!') - helpers.assert_alive() + n.assert_alive() exec_lua(code) command('bd!') - helpers.assert_alive() + n.assert_alive() end) it('#12718 lnume', function() @@ -312,31 +314,71 @@ describe('lua buffer event callbacks: on_lines', function() feed('G0') feed('p') -- Is the last arg old_byte_size correct? Doesn't matter for this PR - eq(api.nvim_get_var('linesev'), { 'lines', 1, 4, 2, 3, 5, 4 }) + eq({ 'lines', 1, 4, 2, 3, 5, 4 }, api.nvim_get_var('linesev')) feed('2G0') feed('p') - eq(api.nvim_get_var('linesev'), { 'lines', 1, 5, 1, 4, 4, 8 }) + eq({ 'lines', 1, 5, 1, 4, 4, 8 }, api.nvim_get_var('linesev')) feed('1G0') feed('P') - eq(api.nvim_get_var('linesev'), { 'lines', 1, 6, 0, 3, 3, 9 }) + eq({ 'lines', 1, 6, 0, 3, 3, 9 }, api.nvim_get_var('linesev')) end) - it( - 'calling nvim_buf_call() from callback does not cause Normal mode CTRL-A to misbehave #16729', - function() - exec_lua([[ + it('nvim_buf_call() from callback does not cause wrong Normal mode CTRL-A #16729', function() + exec_lua([[ vim.api.nvim_buf_attach(0, false, { on_lines = function(...) vim.api.nvim_buf_call(0, function() end) end, }) ]]) - feed('itest123<Esc><C-A>') - eq('test124', api.nvim_get_current_line()) - end - ) + feed('itest123<Esc><C-A>') + eq('test124', api.nvim_get_current_line()) + end) + + it('setting extmark in on_lines callback works', function() + local screen = Screen.new(40, 6) + screen:attach() + + api.nvim_buf_set_lines(0, 0, -1, true, { 'aaa', 'bbb', 'ccc' }) + exec_lua([[ + local ns = vim.api.nvim_create_namespace('') + vim.api.nvim_buf_attach(0, false, { + on_lines = function(_, _, _, row, _, end_row) + vim.api.nvim_buf_clear_namespace(0, ns, row, end_row) + for i = row, end_row - 1 do + local id = vim.api.nvim_buf_set_extmark(0, ns, i, 0, { + virt_text = {{ 'NEW' .. tostring(i), 'WarningMsg' }}, + }) + end + end, + }) + ]]) + + feed('o') + screen:expect({ + grid = [[ + aaa | + ^ {19:NEW1} | + bbb | + ccc | + {1:~ }| + {5:-- INSERT --} | + ]], + }) + feed('<CR>') + screen:expect({ + grid = [[ + aaa | + {19:NEW1} | + ^ {19:NEW2} | + bbb | + ccc | + {5:-- INSERT --} | + ]], + }) + end) end) describe('lua: nvim_buf_attach on_bytes', function() @@ -426,14 +468,14 @@ describe('lua: nvim_buf_attach on_bytes', function() it('opening lines', function() local check_events = setup_eventcheck(verify, origlines) - -- api.nvim_set_option_value('autoindent', true, {}) + api.nvim_set_option_value('autoindent', false, {}) feed 'Go' check_events { { 'test1', 'bytes', 1, 3, 7, 0, 114, 0, 0, 0, 1, 0, 1 }, } feed '<cr>' check_events { - { 'test1', 'bytes', 1, 5, 7, 0, 114, 0, 0, 0, 1, 0, 1 }, + { 'test1', 'bytes', 1, 4, 7, 0, 114, 0, 0, 0, 1, 0, 1 }, } end) @@ -447,7 +489,7 @@ describe('lua: nvim_buf_attach on_bytes', function() feed '<cr>' check_events { { 'test1', 'bytes', 1, 4, 7, 0, 114, 0, 4, 4, 0, 0, 0 }, - { 'test1', 'bytes', 1, 5, 7, 0, 114, 0, 0, 0, 1, 4, 5 }, + { 'test1', 'bytes', 1, 4, 7, 0, 114, 0, 0, 0, 1, 4, 5 }, } end) @@ -477,7 +519,7 @@ describe('lua: nvim_buf_attach on_bytes', function() api.nvim_set_option_value('filetype', 'c', {}) feed 'A<CR>' check_events { - { 'test1', 'bytes', 1, 4, 0, 10, 10, 0, 0, 0, 1, 3, 4 }, + { 'test1', 'bytes', 1, 3, 0, 10, 10, 0, 0, 0, 1, 3, 4 }, } feed '<ESC>' @@ -493,7 +535,7 @@ describe('lua: nvim_buf_attach on_bytes', function() feed '<CR>' check_events { { 'test1', 'bytes', 1, 6, 1, 2, 13, 0, 1, 1, 0, 0, 0 }, - { 'test1', 'bytes', 1, 7, 1, 2, 13, 0, 0, 0, 1, 3, 4 }, + { 'test1', 'bytes', 1, 6, 1, 2, 13, 0, 0, 0, 1, 3, 4 }, } end) @@ -541,7 +583,7 @@ describe('lua: nvim_buf_attach on_bytes', function() feed 'cc' check_events { - { 'test1', 'bytes', 1, 4, 0, 0, 0, 0, 15, 15, 0, 0, 0 }, + { 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 15, 15, 0, 0, 0 }, } feed '<ESC>' @@ -924,7 +966,7 @@ describe('lua: nvim_buf_attach on_bytes', function() command('e! Xtest-undofile') command('set undodir=. | set undofile') - local ns = helpers.request('nvim_create_namespace', 'ns1') + local ns = n.request('nvim_create_namespace', 'ns1') api.nvim_buf_set_extmark(0, ns, 0, 0, {}) eq({ '12345', 'hello world' }, api.nvim_buf_get_lines(0, 0, -1, true)) @@ -1225,6 +1267,25 @@ describe('lua: nvim_buf_attach on_bytes', function() } end) + it('prompt buffer', function() + local check_events = setup_eventcheck(verify, {}) + api.nvim_set_option_value('buftype', 'prompt', {}) + feed('i') + check_events { + { 'test1', 'bytes', 1, 3, 0, 0, 0, 0, 0, 0, 0, 2, 2 }, + } + feed('<CR>') + check_events { + { 'test1', 'bytes', 1, 4, 1, 0, 3, 0, 0, 0, 1, 0, 1 }, + { 'test1', 'bytes', 1, 5, 1, 0, 3, 0, 0, 0, 0, 2, 2 }, + } + feed('<CR>') + check_events { + { 'test1', 'bytes', 1, 6, 2, 0, 6, 0, 0, 0, 1, 0, 1 }, + { 'test1', 'bytes', 1, 7, 2, 0, 6, 0, 0, 0, 0, 2, 2 }, + } + end) + local function test_lockmarks(mode) local description = (mode ~= '') and mode or '(baseline)' it('test_lockmarks ' .. description .. ' %delete _', function() diff --git a/test/functional/lua/command_line_completion_spec.lua b/test/functional/lua/command_line_completion_spec.lua index b88a38082f..2ba432133b 100644 --- a/test/functional/lua/command_line_completion_spec.lua +++ b/test/functional/lua/command_line_completion_spec.lua @@ -1,8 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local exec_lua = helpers.exec_lua +local clear = n.clear +local eq = t.eq +local exec_lua = n.exec_lua local get_completions = function(input, env) return exec_lua('return {vim._expand_pat(...)}', input, env) diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua index b8d0638ce5..57b084d3d6 100644 --- a/test/functional/lua/commands_spec.lua +++ b/test/functional/lua/commands_spec.lua @@ -1,24 +1,25 @@ -- Test suite for checking :lua* commands -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local eq = helpers.eq +local eq = t.eq local NIL = vim.NIL -local eval = helpers.eval -local feed = helpers.feed -local clear = helpers.clear -local matches = helpers.matches -local api = helpers.api -local exec_lua = helpers.exec_lua -local exec_capture = helpers.exec_capture -local fn = helpers.fn -local source = helpers.source -local dedent = helpers.dedent -local command = helpers.command -local exc_exec = helpers.exc_exec -local pcall_err = helpers.pcall_err -local write_file = helpers.write_file -local remove_trace = helpers.remove_trace +local eval = n.eval +local feed = n.feed +local clear = n.clear +local matches = t.matches +local api = n.api +local exec_lua = n.exec_lua +local exec_capture = n.exec_capture +local fn = n.fn +local source = n.source +local dedent = t.dedent +local command = n.command +local exc_exec = n.exc_exec +local pcall_err = t.pcall_err +local write_file = t.write_file +local remove_trace = t.remove_trace before_each(clear) diff --git a/test/functional/lua/comment_spec.lua b/test/functional/lua/comment_spec.lua new file mode 100644 index 0000000000..bbf061a2ab --- /dev/null +++ b/test/functional/lua/comment_spec.lua @@ -0,0 +1,651 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local api = n.api +local clear = n.clear +local eq = t.eq +local exec_capture = n.exec_capture +local exec_lua = n.exec_lua +local feed = n.feed + +-- Reference text +-- aa +-- aa +-- aa +-- +-- aa +-- aa +-- aa +local example_lines = { 'aa', ' aa', ' aa', '', ' aa', ' aa', 'aa' } + +local set_commentstring = function(commentstring) + api.nvim_set_option_value('commentstring', commentstring, { buf = 0 }) +end + +local get_lines = function(from, to) + from, to = from or 0, to or -1 + return api.nvim_buf_get_lines(0, from, to, false) +end + +local set_lines = function(lines, from, to) + from, to = from or 0, to or -1 + api.nvim_buf_set_lines(0, from, to, false, lines) +end + +local set_cursor = function(row, col) + api.nvim_win_set_cursor(0, { row, col }) +end + +local get_cursor = function() + return api.nvim_win_get_cursor(0) +end + +local setup_treesitter = function() + -- NOTE: This leverages bundled Vimscript and Lua tree-sitter parsers + api.nvim_set_option_value('filetype', 'vim', { buf = 0 }) + exec_lua('vim.treesitter.start()') +end + +before_each(function() + clear({ args_rm = { '--cmd' }, args = { '--clean' } }) +end) + +describe('commenting', function() + before_each(function() + set_lines(example_lines) + set_commentstring('# %s') + end) + + describe('toggle_lines()', function() + local toggle_lines = function(...) + exec_lua('require("vim._comment").toggle_lines(...)', ...) + end + + it('works', function() + toggle_lines(3, 5) + eq(get_lines(2, 5), { ' # aa', ' #', ' # aa' }) + + toggle_lines(3, 5) + eq(get_lines(2, 5), { ' aa', '', ' aa' }) + end) + + it("works with different 'commentstring' options", function() + local validate = function(lines_before, lines_after, lines_again) + set_lines(lines_before) + toggle_lines(1, #lines_before) + eq(get_lines(), lines_after) + toggle_lines(1, #lines_before) + eq(get_lines(), lines_again or lines_before) + end + + -- Single whitespace inside comment parts (main case) + set_commentstring('# %s #') + -- - General case + validate( + { 'aa', ' aa', 'aa ', ' aa ' }, + { '# aa #', '# aa #', '# aa #', '# aa #' } + ) + -- - Tabs + validate( + { 'aa', '\taa', 'aa\t', '\taa\t' }, + { '# aa #', '# \taa #', '# aa\t #', '# \taa\t #' } + ) + -- - With indent + validate({ ' aa', ' aa' }, { ' # aa #', ' # aa #' }) + -- - With blank/empty lines + validate( + { ' aa', '', ' ', '\t' }, + { ' # aa #', ' ##', ' ##', ' ##' }, + { ' aa', '', '', '' } + ) + + set_commentstring('# %s') + validate({ 'aa', ' aa', 'aa ', ' aa ' }, { '# aa', '# aa', '# aa ', '# aa ' }) + validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '# aa', '# \taa', '# aa\t', '# \taa\t' }) + validate({ ' aa', ' aa' }, { ' # aa', ' # aa' }) + validate( + { ' aa', '', ' ', '\t' }, + { ' # aa', ' #', ' #', ' #' }, + { ' aa', '', '', '' } + ) + + set_commentstring('%s #') + validate({ 'aa', ' aa', 'aa ', ' aa ' }, { 'aa #', ' aa #', 'aa #', ' aa #' }) + validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { 'aa #', '\taa #', 'aa\t #', '\taa\t #' }) + validate({ ' aa', ' aa' }, { ' aa #', ' aa #' }) + validate( + { ' aa', '', ' ', '\t' }, + { ' aa #', ' #', ' #', ' #' }, + { ' aa', '', '', '' } + ) + + -- No whitespace in parts + set_commentstring('#%s#') + validate({ 'aa', ' aa', 'aa ', ' aa ' }, { '#aa#', '# aa#', '#aa #', '# aa #' }) + validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '#aa#', '#\taa#', '#aa\t#', '#\taa\t#' }) + validate({ ' aa', ' aa' }, { ' #aa#', ' # aa#' }) + validate( + { ' aa', '', ' ', '\t' }, + { ' #aa#', ' ##', ' ##', ' ##' }, + { ' aa', '', '', '' } + ) + + set_commentstring('#%s') + validate({ 'aa', ' aa', 'aa ', ' aa ' }, { '#aa', '# aa', '#aa ', '# aa ' }) + validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '#aa', '#\taa', '#aa\t', '#\taa\t' }) + validate({ ' aa', ' aa' }, { ' #aa', ' # aa' }) + validate({ ' aa', '', ' ', '\t' }, { ' #aa', ' #', ' #', ' #' }, { ' aa', '', '', '' }) + + set_commentstring('%s#') + validate({ 'aa', ' aa', 'aa ', ' aa ' }, { 'aa#', ' aa#', 'aa #', ' aa #' }) + validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { 'aa#', '\taa#', 'aa\t#', '\taa\t#' }) + validate({ ' aa', ' aa' }, { ' aa#', ' aa#' }) + validate({ ' aa', '', ' ', '\t' }, { ' aa#', ' #', ' #', ' #' }, { ' aa', '', '', '' }) + + -- Extra whitespace inside comment parts + set_commentstring('# %s #') + validate( + { 'aa', ' aa', 'aa ', ' aa ' }, + { '# aa #', '# aa #', '# aa #', '# aa #' } + ) + validate( + { 'aa', '\taa', 'aa\t', '\taa\t' }, + { '# aa #', '# \taa #', '# aa\t #', '# \taa\t #' } + ) + validate({ ' aa', ' aa' }, { ' # aa #', ' # aa #' }) + validate( + { ' aa', '', ' ', '\t' }, + { ' # aa #', ' ##', ' ##', ' ##' }, + { ' aa', '', '', '' } + ) + + set_commentstring('# %s') + validate({ 'aa', ' aa', 'aa ', ' aa ' }, { '# aa', '# aa', '# aa ', '# aa ' }) + validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '# aa', '# \taa', '# aa\t', '# \taa\t' }) + validate({ ' aa', ' aa' }, { ' # aa', ' # aa' }) + validate( + { ' aa', '', ' ', '\t' }, + { ' # aa', ' #', ' #', ' #' }, + { ' aa', '', '', '' } + ) + + set_commentstring('%s #') + validate({ 'aa', ' aa', 'aa ', ' aa ' }, { 'aa #', ' aa #', 'aa #', ' aa #' }) + validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { 'aa #', '\taa #', 'aa\t #', '\taa\t #' }) + validate({ ' aa', ' aa' }, { ' aa #', ' aa #' }) + validate( + { ' aa', '', ' ', '\t' }, + { ' aa #', ' #', ' #', ' #' }, + { ' aa', '', '', '' } + ) + + -- Whitespace outside of comment parts + set_commentstring(' # %s # ') + validate( + { 'aa', ' aa', 'aa ', ' aa ' }, + { ' # aa # ', ' # aa # ', ' # aa # ', ' # aa # ' } + ) + validate( + { 'aa', '\taa', 'aa\t', '\taa\t' }, + { ' # aa # ', ' # \taa # ', ' # aa\t # ', ' # \taa\t # ' } + ) + validate({ ' aa', ' aa' }, { ' # aa # ', ' # aa # ' }) + validate( + { ' aa', '', ' ', '\t' }, + { ' # aa # ', ' ##', ' ##', ' ##' }, + { ' aa', '', '', '' } + ) + + set_commentstring(' # %s ') + validate( + { 'aa', ' aa', 'aa ', ' aa ' }, + { ' # aa ', ' # aa ', ' # aa ', ' # aa ' } + ) + validate( + { 'aa', '\taa', 'aa\t', '\taa\t' }, + { ' # aa ', ' # \taa ', ' # aa\t ', ' # \taa\t ' } + ) + validate({ ' aa', ' aa' }, { ' # aa ', ' # aa ' }) + validate( + { ' aa', '', ' ', '\t' }, + { ' # aa ', ' #', ' #', ' #' }, + { ' aa', '', '', '' } + ) + + set_commentstring(' %s # ') + validate( + { 'aa', ' aa', 'aa ', ' aa ' }, + { ' aa # ', ' aa # ', ' aa # ', ' aa # ' } + ) + validate( + { 'aa', '\taa', 'aa\t', '\taa\t' }, + { ' aa # ', ' \taa # ', ' aa\t # ', ' \taa\t # ' } + ) + validate({ ' aa', ' aa' }, { ' aa # ', ' aa # ' }) + validate( + { ' aa', '', ' ', '\t' }, + { ' aa # ', ' #', ' #', ' #' }, + { ' aa', '', '', '' } + ) + + -- LaTeX + set_commentstring('% %s') + validate({ 'aa', ' aa', 'aa ', ' aa ' }, { '% aa', '% aa', '% aa ', '% aa ' }) + validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '% aa', '% \taa', '% aa\t', '% \taa\t' }) + validate({ ' aa', ' aa' }, { ' % aa', ' % aa' }) + validate( + { ' aa', '', ' ', '\t' }, + { ' % aa', ' %', ' %', ' %' }, + { ' aa', '', '', '' } + ) + end) + + it('respects tree-sitter injections', function() + setup_treesitter() + + local lines = { + 'set background=dark', + 'lua << EOF', + 'print(1)', + 'vim.api.nvim_exec2([[', + ' set background=light', + ']])', + 'EOF', + } + + -- Single line comments + local validate = function(line, ref_output) + set_lines(lines) + toggle_lines(line, line) + eq(get_lines(line - 1, line)[1], ref_output) + end + + validate(1, '"set background=dark') + validate(2, '"lua << EOF') + validate(3, '-- print(1)') + validate(4, '-- vim.api.nvim_exec2([[') + validate(5, ' "set background=light') + validate(6, '-- ]])') + validate(7, '"EOF') + + -- Multiline comments should be computed based on first line 'commentstring' + set_lines(lines) + toggle_lines(1, 3) + local out_lines = get_lines() + eq(out_lines[1], '"set background=dark') + eq(out_lines[2], '"lua << EOF') + eq(out_lines[3], '"print(1)') + end) + + it('correctly computes indent', function() + toggle_lines(2, 4) + eq(get_lines(1, 4), { ' # aa', ' # aa', ' #' }) + end) + + it('correctly detects comment/uncomment', function() + local validate = function(from, to, ref_lines) + set_lines({ '', 'aa', '# aa', '# aa', 'aa', '' }) + toggle_lines(from, to) + eq(get_lines(), ref_lines) + end + + -- It should uncomment only if all non-blank lines are comments + validate(3, 4, { '', 'aa', 'aa', 'aa', 'aa', '' }) + validate(2, 4, { '', '# aa', '# # aa', '# # aa', 'aa', '' }) + validate(3, 5, { '', 'aa', '# # aa', '# # aa', '# aa', '' }) + validate(1, 6, { '#', '# aa', '# # aa', '# # aa', '# aa', '#' }) + + -- Blank lines should be ignored when making a decision + set_lines({ '# aa', '', ' ', '\t', '# aa' }) + toggle_lines(1, 5) + eq(get_lines(), { 'aa', '', ' ', '\t', 'aa' }) + end) + + it('correctly matches comment parts during checking and uncommenting', function() + local validate = function(from, to, ref_lines) + set_lines({ '/*aa*/', '/* aa */', '/* aa */' }) + toggle_lines(from, to) + eq(get_lines(), ref_lines) + end + + -- Should first try to match 'commentstring' parts exactly with their + -- whitespace, with fallback on trimmed parts + set_commentstring('/*%s*/') + validate(1, 3, { 'aa', ' aa ', ' aa ' }) + validate(2, 3, { '/*aa*/', ' aa ', ' aa ' }) + validate(3, 3, { '/*aa*/', '/* aa */', ' aa ' }) + + set_commentstring('/* %s */') + validate(1, 3, { 'aa', 'aa', ' aa ' }) + validate(2, 3, { '/*aa*/', 'aa', ' aa ' }) + validate(3, 3, { '/*aa*/', '/* aa */', ' aa ' }) + + set_commentstring('/* %s */') + validate(1, 3, { 'aa', ' aa ', 'aa' }) + validate(2, 3, { '/*aa*/', ' aa ', 'aa' }) + validate(3, 3, { '/*aa*/', '/* aa */', 'aa' }) + + set_commentstring(' /*%s*/ ') + validate(1, 3, { 'aa', ' aa ', ' aa ' }) + validate(2, 3, { '/*aa*/', ' aa ', ' aa ' }) + validate(3, 3, { '/*aa*/', '/* aa */', ' aa ' }) + end) + + it('uncomments on inconsistent indent levels', function() + set_lines({ '# aa', ' # aa', ' # aa' }) + toggle_lines(1, 3) + eq(get_lines(), { 'aa', ' aa', ' aa' }) + end) + + it('respects tabs', function() + api.nvim_set_option_value('expandtab', false, { buf = 0 }) + set_lines({ '\t\taa', '\t\taa' }) + + toggle_lines(1, 2) + eq(get_lines(), { '\t\t# aa', '\t\t# aa' }) + + toggle_lines(1, 2) + eq(get_lines(), { '\t\taa', '\t\taa' }) + end) + + it('works with trailing whitespace', function() + -- Without right-hand side + set_commentstring('# %s') + set_lines({ ' aa', ' aa ', ' ' }) + toggle_lines(1, 3) + eq(get_lines(), { ' # aa', ' # aa ', ' #' }) + toggle_lines(1, 3) + eq(get_lines(), { ' aa', ' aa ', '' }) + + -- With right-hand side + set_commentstring('%s #') + set_lines({ ' aa', ' aa ', ' ' }) + toggle_lines(1, 3) + eq(get_lines(), { ' aa #', ' aa #', ' #' }) + toggle_lines(1, 3) + eq(get_lines(), { ' aa', ' aa ', '' }) + + -- Trailing whitespace after right side should be preserved for non-blanks + set_commentstring('%s #') + set_lines({ ' aa # ', ' aa #\t', ' # ', ' #\t' }) + toggle_lines(1, 4) + eq(get_lines(), { ' aa ', ' aa\t', '', '' }) + end) + end) + + describe('Operator', function() + it('works in Normal mode', function() + set_cursor(2, 2) + feed('gc', 'ap') + eq(get_lines(), { '# aa', '# aa', '# aa', '#', ' aa', ' aa', 'aa' }) + -- Cursor moves to start line + eq(get_cursor(), { 1, 0 }) + + -- Supports `v:count` + set_lines(example_lines) + set_cursor(2, 0) + feed('2gc', 'ap') + eq(get_lines(), { '# aa', '# aa', '# aa', '#', '# aa', '# aa', '# aa' }) + end) + + it('allows dot-repeat in Normal mode', function() + local doubly_commented = { '# # aa', '# # aa', '# # aa', '# #', '# aa', '# aa', '# aa' } + + set_lines(example_lines) + set_cursor(2, 2) + feed('gc', 'ap') + feed('.') + eq(get_lines(), doubly_commented) + + -- Not immediate dot-repeat + set_lines(example_lines) + set_cursor(2, 2) + feed('gc', 'ap') + set_cursor(7, 0) + feed('.') + eq(get_lines(), doubly_commented) + end) + + it('works in Visual mode', function() + set_cursor(2, 2) + feed('v', 'ap', 'gc') + eq(get_lines(), { '# aa', '# aa', '# aa', '#', ' aa', ' aa', 'aa' }) + + -- Cursor moves to start line + eq(get_cursor(), { 1, 0 }) + end) + + it('allows dot-repeat after initial Visual mode', function() + -- local example_lines = { 'aa', ' aa', ' aa', '', ' aa', ' aa', 'aa' } + + set_lines(example_lines) + set_cursor(2, 2) + feed('vip', 'gc') + eq(get_lines(), { '# aa', '# aa', '# aa', '', ' aa', ' aa', 'aa' }) + eq(get_cursor(), { 1, 0 }) + + -- Dot-repeat after first application in Visual mode should apply to the same + -- relative region + feed('.') + eq(get_lines(), example_lines) + + set_cursor(3, 0) + feed('.') + eq(get_lines(), { 'aa', ' aa', ' # aa', ' #', ' # aa', ' aa', 'aa' }) + end) + + it("respects 'commentstring'", function() + set_commentstring('/*%s*/') + set_cursor(2, 2) + feed('gc', 'ap') + eq(get_lines(), { '/*aa*/', '/* aa*/', '/* aa*/', '/**/', ' aa', ' aa', 'aa' }) + end) + + it("works with empty 'commentstring'", function() + set_commentstring('') + set_cursor(2, 2) + feed('gc', 'ap') + eq(get_lines(), example_lines) + eq(exec_capture('1messages'), [[Option 'commentstring' is empty.]]) + end) + + it('respects tree-sitter injections', function() + setup_treesitter() + + local lines = { + 'set background=dark', + 'lua << EOF', + 'print(1)', + 'vim.api.nvim_exec2([[', + ' set background=light', + ']])', + 'EOF', + } + + -- Single line comments + local validate = function(line, ref_output) + set_lines(lines) + set_cursor(line, 0) + feed('gc_') + eq(get_lines(line - 1, line)[1], ref_output) + end + + validate(1, '"set background=dark') + validate(2, '"lua << EOF') + validate(3, '-- print(1)') + validate(4, '-- vim.api.nvim_exec2([[') + validate(5, ' "set background=light') + validate(6, '-- ]])') + validate(7, '"EOF') + + -- Has proper dot-repeat which recomputes 'commentstring' + set_lines(lines) + + set_cursor(1, 0) + feed('gc_') + eq(get_lines()[1], '"set background=dark') + + set_cursor(3, 0) + feed('.') + eq(get_lines()[3], '-- print(1)') + + -- Multiline comments should be computed based on cursor position + -- which in case of Visual selection means its left part + set_lines(lines) + set_cursor(1, 0) + feed('v2j', 'gc') + local out_lines = get_lines() + eq(out_lines[1], '"set background=dark') + eq(out_lines[2], '"lua << EOF') + eq(out_lines[3], '"print(1)') + end) + + it("recomputes local 'commentstring' based on cursor position", function() + setup_treesitter() + local lines = { + ' print(1)', + 'lua << EOF', + ' print(1)', + 'EOF', + } + set_lines(lines) + + set_cursor(1, 1) + feed('gc_') + eq(get_lines()[1], ' "print(1)') + + set_lines(lines) + set_cursor(3, 2) + feed('.') + eq(get_lines()[3], ' -- print(1)') + end) + + it('preserves marks', function() + set_cursor(2, 0) + -- Set '`<' and '`>' marks + feed('VV') + feed('gc', 'ip') + eq(api.nvim_buf_get_mark(0, '<'), { 2, 0 }) + eq(api.nvim_buf_get_mark(0, '>'), { 2, 2147483647 }) + end) + end) + + describe('Current line', function() + it('works', function() + set_lines(example_lines) + set_cursor(1, 1) + feed('gcc') + eq(get_lines(0, 2), { '# aa', ' aa' }) + + -- Does not comment empty line + set_lines(example_lines) + set_cursor(4, 0) + feed('gcc') + eq(get_lines(2, 5), { ' aa', '', ' aa' }) + + -- Supports `v:count` + set_lines(example_lines) + set_cursor(2, 0) + feed('2gcc') + eq(get_lines(0, 3), { 'aa', ' # aa', ' # aa' }) + end) + + it('allows dot-repeat', function() + set_lines(example_lines) + set_cursor(1, 1) + feed('gcc') + feed('.') + eq(get_lines(), example_lines) + + -- Not immediate dot-repeat + set_lines(example_lines) + set_cursor(1, 1) + feed('gcc') + set_cursor(7, 0) + feed('.') + eq(get_lines(6, 7), { '# aa' }) + end) + + it('respects tree-sitter injections', function() + setup_treesitter() + + local lines = { + 'set background=dark', + 'lua << EOF', + 'print(1)', + 'EOF', + } + set_lines(lines) + + set_cursor(1, 0) + feed('gcc') + eq(get_lines(), { '"set background=dark', 'lua << EOF', 'print(1)', 'EOF' }) + + -- Should work with dot-repeat + set_cursor(3, 0) + feed('.') + eq(get_lines(), { '"set background=dark', 'lua << EOF', '-- print(1)', 'EOF' }) + end) + end) + + describe('Textobject', function() + it('works', function() + set_lines({ 'aa', '# aa', '# aa', 'aa' }) + set_cursor(2, 0) + feed('d', 'gc') + eq(get_lines(), { 'aa', 'aa' }) + end) + + it('allows dot-repeat', function() + set_lines({ 'aa', '# aa', '# aa', 'aa', '# aa' }) + set_cursor(2, 0) + feed('d', 'gc') + set_cursor(3, 0) + feed('.') + eq(get_lines(), { 'aa', 'aa' }) + end) + + it('does nothing when not inside textobject', function() + -- Builtin operators + feed('d', 'gc') + eq(get_lines(), example_lines) + + -- Comment operator + local validate_no_action = function(line, col) + set_lines(example_lines) + set_cursor(line, col) + feed('gc', 'gc') + eq(get_lines(), example_lines) + end + + validate_no_action(1, 1) + validate_no_action(2, 2) + + -- Doesn't work (but should) because both `[` and `]` are set to (1, 0) + -- (instead of more reasonable (1, -1) or (0, 2147483647)). + -- validate_no_action(1, 0) + end) + + it('respects tree-sitter injections', function() + setup_treesitter() + local lines = { + '"set background=dark', + '"set termguicolors', + 'lua << EOF', + '-- print(1)', + '-- print(2)', + 'EOF', + } + set_lines(lines) + + set_cursor(1, 0) + feed('dgc') + eq(get_lines(), { 'lua << EOF', '-- print(1)', '-- print(2)', 'EOF' }) + + -- Should work with dot-repeat + set_cursor(2, 0) + feed('.') + eq(get_lines(), { 'lua << EOF', 'EOF' }) + end) + end) +end) diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index 5802925339..05082bc132 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local NIL = vim.NIL -local command = helpers.command -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local matches = helpers.matches -local api = helpers.api -local pcall_err = helpers.pcall_err +local command = n.command +local clear = n.clear +local exec_lua = n.exec_lua +local eq = t.eq +local matches = t.matches +local api = n.api +local pcall_err = t.pcall_err +local fn = n.fn describe('vim.diagnostic', function() before_each(function() @@ -16,12 +18,12 @@ describe('vim.diagnostic', function() exec_lua [[ require('vim.diagnostic') - function make_diagnostic(msg, x1, y1, x2, y2, severity, source, code) + function make_diagnostic(msg, lnum, col, end_lnum, end_col, severity, source, code) return { - lnum = x1, - col = y1, - end_lnum = x2, - end_col = y2, + lnum = lnum, + col = col, + end_lnum = end_lnum, + end_col = end_col, message = msg, severity = severity, source = source, @@ -29,20 +31,20 @@ describe('vim.diagnostic', function() } end - function make_error(msg, x1, y1, x2, y2, source, code) - return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.ERROR, source, code) + function make_error(msg, lnum, col, end_lnum, end_col, source, code) + return make_diagnostic(msg, lnum, col, end_lnum, end_col, vim.diagnostic.severity.ERROR, source, code) end - function make_warning(msg, x1, y1, x2, y2, source, code) - return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.WARN, source, code) + function make_warning(msg, lnum, col, end_lnum, end_col, source, code) + return make_diagnostic(msg, lnum, col, end_lnum, end_col, vim.diagnostic.severity.WARN, source, code) end - function make_info(msg, x1, y1, x2, y2, source, code) - return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.INFO, source, code) + function make_info(msg, lnum, col, end_lnum, end_col, source, code) + return make_diagnostic(msg, lnum, col, end_lnum, end_col, vim.diagnostic.severity.INFO, source, code) end - function make_hint(msg, x1, y1, x2, y2, source, code) - return make_diagnostic(msg, x1, y1, x2, y2, vim.diagnostic.severity.HINT, source, code) + function make_hint(msg, lnum, col, end_lnum, end_col, source, code) + return make_diagnostic(msg, lnum, col, end_lnum, end_col, vim.diagnostic.severity.HINT, source, code) end function count_diagnostics(bufnr, severity, namespace) @@ -109,7 +111,7 @@ describe('vim.diagnostic', function() 'DiagnosticVirtualTextOk', 'DiagnosticVirtualTextWarn', 'DiagnosticWarn', - }, exec_lua([[return vim.fn.getcompletion('Diagnostic', 'highlight')]])) + }, fn.getcompletion('Diagnostic', 'highlight')) end) it('retrieves diagnostics from all buffers and namespaces', function() @@ -205,7 +207,7 @@ describe('vim.diagnostic', function() diag[1].col = 10000 return vim.diagnostic.get()[1].col == 10000 ]] - eq(result, false) + eq(false, result) end) it('resolves buffer number 0 to the current buffer', function() @@ -328,7 +330,7 @@ describe('vim.diagnostic', function() eq( { 1, 1, 2, 0, 2 }, exec_lua [[ - vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) + vim.diagnostic.enable(false, { bufnr = diagnostic_bufnr, ns_id = diagnostic_ns }) return { count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), @@ -343,7 +345,7 @@ describe('vim.diagnostic', function() eq( all_highlights, exec_lua([[ - vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) + vim.diagnostic.enable(true, { bufnr = diagnostic_bufnr, ns_id = diagnostic_ns }) return { count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.ERROR, diagnostic_ns), count_diagnostics(diagnostic_bufnr, vim.diagnostic.severity.WARN, other_ns), @@ -370,7 +372,7 @@ describe('vim.diagnostic', function() vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, ns_1_diags) vim.diagnostic.set(other_ns, diagnostic_bufnr, ns_2_diags) - vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) + vim.diagnostic.enable(false, { bufnr = diagnostic_bufnr, ns_id = diagnostic_ns }) return { count_extmarks(diagnostic_bufnr, diagnostic_ns), @@ -382,8 +384,8 @@ describe('vim.diagnostic', function() eq( { 4, 0 }, exec_lua [[ - vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) - vim.diagnostic.disable(diagnostic_bufnr, other_ns) + vim.diagnostic.enable(true, { bufnr = diagnostic_bufnr, ns_id = diagnostic_ns }) + vim.diagnostic.enable(false, { bufnr = diagnostic_bufnr, ns_id = other_ns }) return { count_extmarks(diagnostic_bufnr, diagnostic_ns), @@ -477,7 +479,32 @@ describe('vim.diagnostic', function() end) describe('enable() and disable()', function() - it('works without arguments', function() + it('validation', function() + matches('expected boolean, got table', pcall_err(exec_lua, [[vim.diagnostic.enable({})]])) + matches( + 'filter: expected table, got string', + pcall_err(exec_lua, [[vim.diagnostic.enable(false, '')]]) + ) + matches( + 'Invalid buffer id: 42', + pcall_err(exec_lua, [[vim.diagnostic.enable(true, { bufnr = 42 })]]) + ) + matches( + 'expected boolean, got number', + pcall_err(exec_lua, [[vim.diagnostic.enable(42, {})]]) + ) + matches('expected boolean, got table', pcall_err(exec_lua, [[vim.diagnostic.enable({}, 42)]])) + + -- Deprecated signature. + matches('Invalid buffer id: 42', pcall_err(exec_lua, [[vim.diagnostic.enable(42)]])) + -- Deprecated signature. + matches( + 'namespace does not exist or is anonymous', + pcall_err(exec_lua, [[vim.diagnostic.enable(nil, 42)]]) + ) + end) + + it('without arguments', function() local result = exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) @@ -499,7 +526,7 @@ describe('vim.diagnostic', function() table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) - vim.diagnostic.disable() + vim.diagnostic.enable(false) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) @@ -531,7 +558,7 @@ describe('vim.diagnostic', function() eq(4, result[4]) end) - it('works with only a buffer argument', function() + it('with buffer argument', function() local result = exec_lua [[ local other_bufnr = vim.api.nvim_create_buf(true, false) @@ -560,19 +587,19 @@ describe('vim.diagnostic', function() count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) - vim.diagnostic.disable(diagnostic_bufnr) + vim.diagnostic.enable(false, { bufnr = diagnostic_bufnr }) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) - vim.diagnostic.enable(diagnostic_bufnr) + vim.diagnostic.enable(true, { bufnr = diagnostic_bufnr }) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) - vim.diagnostic.disable(other_bufnr) + vim.diagnostic.enable(false, { bufnr = other_bufnr }) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + @@ -587,7 +614,7 @@ describe('vim.diagnostic', function() eq(3, result[4]) end) - it('works with only a namespace argument', function() + it('with a namespace argument', function() local result = exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) @@ -609,17 +636,17 @@ describe('vim.diagnostic', function() table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) - vim.diagnostic.disable(nil, diagnostic_ns) + vim.diagnostic.enable(false, { ns_id = diagnostic_ns }) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) - vim.diagnostic.enable(nil, diagnostic_ns) + vim.diagnostic.enable(true, { ns_id = diagnostic_ns }) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) - vim.diagnostic.disable(nil, other_ns) + vim.diagnostic.enable(false, { ns_id = other_ns }) table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns)) @@ -633,8 +660,11 @@ describe('vim.diagnostic', function() eq(2, result[4]) end) - it('works with both a buffer and a namespace argument', function() - local result = exec_lua [[ + --- @return table + local function test_enable(legacy) + local result = exec_lua( + [[ + local legacy = ... local other_bufnr = vim.api.nvim_create_buf(true, false) vim.api.nvim_win_set_buf(0, diagnostic_bufnr) @@ -662,34 +692,68 @@ describe('vim.diagnostic', function() count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) - vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) + if legacy then + vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) + else + vim.diagnostic.enable(false, { bufnr = diagnostic_bufnr, ns_id = diagnostic_ns }) + end table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) - vim.diagnostic.disable(diagnostic_bufnr, other_ns) + if legacy then + vim.diagnostic.disable(diagnostic_bufnr, other_ns) + else + vim.diagnostic.enable(false, { bufnr = diagnostic_bufnr, ns_id = other_ns }) + end table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) - vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) + if legacy then + vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) + else + vim.diagnostic.enable(true, { bufnr = diagnostic_bufnr, ns_id = diagnostic_ns }) + end table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) - -- Should have no effect - vim.diagnostic.disable(other_bufnr, other_ns) + if legacy then + -- Should have no effect + vim.diagnostic.disable(other_bufnr, other_ns) + else + -- Should have no effect + vim.diagnostic.enable(false, { bufnr = other_bufnr, ns_id = other_ns }) + end table.insert(result, count_extmarks(diagnostic_bufnr, diagnostic_ns) + count_extmarks(diagnostic_bufnr, other_ns) + count_extmarks(other_bufnr, diagnostic_ns)) return result - ]] + ]], + legacy + ) + + return result + end + it('with both buffer and namespace arguments', function() + local result = test_enable(false) + eq(4, result[1]) + eq(2, result[2]) + eq(1, result[3]) + eq(3, result[4]) + eq(3, result[5]) + end) + + it('with both buffer and namespace arguments (deprecated signature)', function() + -- Exercise the legacy/deprecated signature. + local result = test_enable(true) eq(4, result[1]) eq(2, result[2]) eq(1, result[3]) @@ -870,15 +934,112 @@ describe('vim.diagnostic', function() eq( { 4, 0 }, exec_lua [[ - vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { - make_error('Diagnostic #1', 3, 9001, 3, 9001), - make_error('Diagnostic #2', 4, -1, 4, -1), - }) - vim.api.nvim_win_set_buf(0, diagnostic_bufnr) - vim.api.nvim_win_set_cursor(0, {1, 1}) - vim.diagnostic.goto_next { float = false } - return vim.diagnostic.get_next_pos { namespace = diagnostic_ns } - ]] + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 3, 9001, 3, 9001), + make_error('Diagnostic #2', 4, -1, 4, -1), + }) + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {1, 1}) + vim.diagnostic.goto_next { float = false } + return vim.diagnostic.get_next_pos { namespace = diagnostic_ns } + ]] + ) + end) + + it('jumps to diagnostic with highest severity', function() + exec_lua([[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_info('Info', 1, 0, 1, 1), + make_error('Error', 2, 0, 2, 1), + make_warning('Warning', 3, 0, 3, 1), + make_error('Error', 4, 0, 4, 1), + }) + + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {1, 0}) + ]]) + + eq( + { 3, 0 }, + exec_lua([[ + vim.diagnostic.goto_next({_highest = true}) + return vim.api.nvim_win_get_cursor(0) + ]]) + ) + + eq( + { 5, 0 }, + exec_lua([[ + vim.diagnostic.goto_next({_highest = true}) + return vim.api.nvim_win_get_cursor(0) + ]]) + ) + + exec_lua([[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_info('Info', 1, 0, 1, 1), + make_hint('Hint', 2, 0, 2, 1), + make_warning('Warning', 3, 0, 3, 1), + make_hint('Hint', 4, 0, 4, 1), + make_warning('Warning', 5, 0, 5, 1), + }) + + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {1, 0}) + ]]) + + eq( + { 4, 0 }, + exec_lua([[ + vim.diagnostic.goto_next({_highest = true}) + return vim.api.nvim_win_get_cursor(0) + ]]) + ) + + eq( + { 6, 0 }, + exec_lua([[ + vim.diagnostic.goto_next({_highest = true}) + return vim.api.nvim_win_get_cursor(0) + ]]) + ) + end) + + it('jumps to next diagnostic if severity is non-nil', function() + exec_lua([[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_info('Info', 1, 0, 1, 1), + make_error('Error', 2, 0, 2, 1), + make_warning('Warning', 3, 0, 3, 1), + make_error('Error', 4, 0, 4, 1), + }) + + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.api.nvim_win_set_cursor(0, {1, 0}) + ]]) + + eq( + { 2, 0 }, + exec_lua([[ + vim.diagnostic.goto_next() + return vim.api.nvim_win_get_cursor(0) + ]]) + ) + + eq( + { 3, 0 }, + exec_lua([[ + vim.diagnostic.goto_next() + return vim.api.nvim_win_get_cursor(0) + ]]) + ) + + eq( + { 4, 0 }, + exec_lua([[ + vim.diagnostic.goto_next() + return vim.api.nvim_win_get_cursor(0) + ]]) ) end) end) @@ -940,6 +1101,29 @@ describe('vim.diagnostic', function() ]] ) end) + + it('works on blank line #28397', function() + eq( + { 0, 2 }, + exec_lua [[ + local test_bufnr = vim.api.nvim_create_buf(true, false) + vim.api.nvim_buf_set_lines(test_bufnr, 0, -1, false, { + 'first line', + '', + '', + 'end line', + }) + vim.diagnostic.set(diagnostic_ns, test_bufnr, { + make_info('Diagnostic #1', 0, 2, 0, 2), + make_info('Diagnostic #2', 2, 0, 2, 0), + make_info('Diagnostic #3', 2, 0, 2, 0), + }) + vim.api.nvim_win_set_buf(0, test_bufnr) + vim.api.nvim_win_set_cursor(0, {3, 0}) + return vim.diagnostic.get_prev_pos { namespace = diagnostic_ns} + ]] + ) + end) end) describe('get()', function() @@ -1013,13 +1197,13 @@ describe('vim.diagnostic', function() it('allows filtering by line', function() eq( - 1, + 2, exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 3), make_info("Ignored information", 1, 1, 2, 3), - make_error("Error On Other Line", 2, 1, 1, 5), + make_error("Error On Other Line", 3, 1, 3, 5), }) return #vim.diagnostic.get(diagnostic_bufnr, {lnum = 2}) @@ -1129,13 +1313,16 @@ describe('vim.diagnostic', function() it('allows filtering by line', function() eq( - exec_lua [[return { [vim.diagnostic.severity.ERROR] = 1 }]], + exec_lua [[return { + [vim.diagnostic.severity.WARN] = 1, + [vim.diagnostic.severity.INFO] = 1, + }]], exec_lua [[ vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 3), make_info("Ignored information", 1, 1, 2, 3), - make_error("Error On Other Line", 2, 1, 1, 5), + make_error("Error On Other Line", 3, 1, 3, 5), }) return vim.diagnostic.count(diagnostic_bufnr, {lnum = 2}) @@ -1554,7 +1741,7 @@ describe('vim.diagnostic', function() end) describe('set()', function() - it('validates its arguments', function() + it('validation', function() matches( 'expected a list of diagnostics', pcall_err(exec_lua, [[vim.diagnostic.set(1, 0, {lnum = 1, col = 2})]]) @@ -1741,7 +1928,7 @@ describe('vim.diagnostic', function() eq( 0, exec_lua [[ - vim.diagnostic.disable(diagnostic_bufnr, diagnostic_ns) + vim.diagnostic.enable(false, { bufnr = diagnostic_bufnr, ns_id = diagnostic_ns }) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { make_error('Diagnostic From Server 1:1', 1, 1, 1, 1), }) @@ -1752,7 +1939,7 @@ describe('vim.diagnostic', function() eq( 2, exec_lua [[ - vim.diagnostic.enable(diagnostic_bufnr, diagnostic_ns) + vim.diagnostic.enable(true, { bufnr = diagnostic_bufnr, ns_id = diagnostic_ns }) return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]] ) @@ -1825,20 +2012,12 @@ describe('vim.diagnostic', function() it('respects legacy signs placed with :sign define or sign_define #26618', function() -- Legacy signs for diagnostics were deprecated in 0.10 and will be removed in 0.12 - eq(0, helpers.fn.has('nvim-0.12')) + eq(0, n.fn.has('nvim-0.12')) - helpers.command( - 'sign define DiagnosticSignError text= texthl= linehl=ErrorMsg numhl=ErrorMsg' - ) - helpers.command( - 'sign define DiagnosticSignWarn text= texthl= linehl=WarningMsg numhl=WarningMsg' - ) - helpers.command( - 'sign define DiagnosticSignInfo text= texthl= linehl=Underlined numhl=Underlined' - ) - helpers.command( - 'sign define DiagnosticSignHint text= texthl= linehl=Underlined numhl=Underlined' - ) + n.command('sign define DiagnosticSignError text= texthl= linehl=ErrorMsg numhl=ErrorMsg') + n.command('sign define DiagnosticSignWarn text= texthl= linehl=WarningMsg numhl=WarningMsg') + n.command('sign define DiagnosticSignInfo text= texthl= linehl=Underlined numhl=Underlined') + n.command('sign define DiagnosticSignHint text= texthl= linehl=Underlined numhl=Underlined') local result = exec_lua [[ vim.diagnostic.config({ @@ -2451,6 +2630,47 @@ describe('vim.diagnostic', function() ]] ) end) + + it('works for multi-line diagnostics #21949', function() + -- open float failed non diagnostic lnum + eq( + vim.NIL, + exec_lua [[ + local diagnostics = { + make_error("Error in two lines lnum is 1 and end_lnum is 2", 1, 1, 2, 3), + } + local winids = {} + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local _, winnr = vim.diagnostic.open_float(0, { header = false }) + return winnr + ]] + ) + + -- can open a float window on lnum 1 + eq( + { '1. Error in two lines lnum is 1 and end_lnum is 2' }, + exec_lua [[ + vim.api.nvim_win_set_cursor(0, {2, 0}) + local float_bufnr, winnr = vim.diagnostic.open_float(0, { header = false }) + local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]] + ) + + -- can open a float window on end_lnum 2 + eq( + { '1. Error in two lines lnum is 1 and end_lnum is 2' }, + exec_lua [[ + vim.api.nvim_win_set_cursor(0, {3, 0}) + local float_bufnr, winnr = vim.diagnostic.open_float(0, { header = false }) + local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]] + ) + end) end) describe('setloclist()', function() @@ -2718,7 +2938,41 @@ describe('vim.diagnostic', function() ) end) - it('checks if diagnostics are disabled in a buffer', function() + it('is_enabled', function() + eq( + { false, false, false, false, false }, + exec_lua [[ + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic #1', 1, 1, 1, 1), + }) + vim.api.nvim_set_current_buf(diagnostic_bufnr) + vim.diagnostic.enable(false) + return { + vim.diagnostic.is_enabled(), + vim.diagnostic.is_enabled{ bufnr = 0 }, + vim.diagnostic.is_enabled{ bufnr = diagnostic_bufnr }, + vim.diagnostic.is_enabled{ bufnr = diagnostic_bufnr, ns_id = diagnostic_ns }, + vim.diagnostic.is_enabled{ bufnr = 0, ns_id = diagnostic_ns }, + } + ]] + ) + + eq( + { true, true, true, true, true }, + exec_lua [[ + vim.diagnostic.enable() + return { + vim.diagnostic.is_enabled(), + vim.diagnostic.is_enabled{ bufnr = 0 }, + vim.diagnostic.is_enabled{ bufnr = diagnostic_bufnr }, + vim.diagnostic.is_enabled{ bufnr = diagnostic_bufnr, ns_id = diagnostic_ns }, + vim.diagnostic.is_enabled{ bufnr = 0, ns_id = diagnostic_ns }, + } + ]] + ) + end) + + it('is_disabled (deprecated)', function() eq( { true, true, true, true }, exec_lua [[ diff --git a/test/functional/lua/ffi_spec.lua b/test/functional/lua/ffi_spec.lua index c9e8e9d4ca..85ca264107 100644 --- a/test/functional/lua/ffi_spec.lua +++ b/test/functional/lua/ffi_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local clear = helpers.clear +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local exec_lua = n.exec_lua +local clear = n.clear before_each(clear) @@ -13,15 +15,19 @@ describe('ffi.cdef', function() eq( 12, - exec_lua [[ + exec_lua [=[ local ffi = require('ffi') - ffi.cdef('int curwin_col_off(void);') + ffi.cdef [[ + typedef struct window_S win_T; + int win_col_off(win_T *wp); + extern win_T *curwin; + ]] vim.cmd('set number numberwidth=4 signcolumn=yes:4') - return ffi.C.curwin_col_off() - ]] + return ffi.C.win_col_off(ffi.C.curwin) + ]=] ) eq( @@ -30,7 +36,6 @@ describe('ffi.cdef', function() local ffi = require('ffi') ffi.cdef[[ - typedef struct window_S win_T; typedef struct {} stl_hlrec_t; typedef struct {} StlClickRecord; typedef struct {} statuscol_T; diff --git a/test/functional/lua/filetype_spec.lua b/test/functional/lua/filetype_spec.lua index 8b0e0a8beb..7db04e6f6b 100644 --- a/test/functional/lua/filetype_spec.lua +++ b/test/functional/lua/filetype_spec.lua @@ -1,10 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local api = helpers.api -local clear = helpers.clear -local pathroot = helpers.pathroot -local command = helpers.command +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local exec_lua = n.exec_lua +local eq = t.eq +local api = n.api +local clear = n.clear +local pathroot = n.pathroot +local command = n.command +local mkdir = t.mkdir +local rmdir = n.rmdir +local write_file = t.write_file +local uv = vim.uv local root = pathroot() @@ -161,10 +167,30 @@ describe('vim.filetype', function() end) describe('filetype.lua', function() + before_each(function() + mkdir('Xfiletype') + end) + + after_each(function() + rmdir('Xfiletype') + end) + it('does not override user autocommands that set filetype #20333', function() clear({ args = { '--clean', '--cmd', 'autocmd BufRead *.md set filetype=notmarkdown', 'README.md' }, }) eq('notmarkdown', api.nvim_get_option_value('filetype', {})) end) + + it('uses unexpanded path for matching when editing a symlink #27914', function() + mkdir('Xfiletype/.config') + mkdir('Xfiletype/actual') + write_file('Xfiletype/actual/config', '') + uv.fs_symlink(assert(uv.fs_realpath('Xfiletype/actual')), 'Xfiletype/.config/git') + finally(function() + uv.fs_unlink('Xfiletype/.config/git') + end) + clear({ args = { '--clean', 'Xfiletype/.config/git/config' } }) + eq('gitconfig', api.nvim_get_option_value('filetype', {})) + end) end) diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index 6821fe3c5e..aba02ab01b 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -1,16 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local mkdir_p = helpers.mkdir_p -local rmdir = helpers.rmdir -local nvim_dir = helpers.nvim_dir -local test_build_dir = helpers.paths.test_build_dir -local test_source_path = helpers.paths.test_source_path -local nvim_prog = helpers.nvim_prog -local is_os = helpers.is_os -local mkdir = helpers.mkdir +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec_lua = n.exec_lua +local eq = t.eq +local mkdir_p = n.mkdir_p +local rmdir = n.rmdir +local nvim_dir = n.nvim_dir +local command = n.command +local api = n.api +local test_build_dir = t.paths.test_build_dir +local test_source_path = t.paths.test_source_path +local nvim_prog = n.nvim_prog +local is_os = t.is_os +local mkdir = t.mkdir local nvim_prog_basename = is_os('win') and 'nvim.exe' or 'nvim' @@ -36,6 +39,7 @@ local test_basename_dirname_eq = { 'c:/users/foo', 'c:/users/foo/bar.lua', 'c:/users/foo/bar/../', + '~/foo/bar\\baz', } local tests_windows_paths = { @@ -54,7 +58,7 @@ describe('vim.fs', function() it('works', function() local test_dir = nvim_dir .. '/test' mkdir_p(test_dir) - local dirs = {} + local dirs = {} --- @type string[] for dir in vim.fs.parents(test_dir .. '/foo.txt') do dirs[#dirs + 1] = dir if dir == test_build_dir then @@ -70,25 +74,26 @@ describe('vim.fs', function() it('works', function() eq(test_build_dir, vim.fs.dirname(nvim_dir)) - local function test_paths(paths) + ---@param paths string[] + ---@param is_win? boolean + local function test_paths(paths, is_win) + local gsub = is_win and [[:gsub('\\', '/')]] or '' + local code = string.format( + [[ + local path = ... + return vim.fn.fnamemodify(path,':h')%s + ]], + gsub + ) + for _, path in ipairs(paths) do - eq( - exec_lua( - [[ - local path = ... - return vim.fn.fnamemodify(path,':h'):gsub('\\', '/') - ]], - path - ), - vim.fs.dirname(path), - path - ) + eq(exec_lua(code, path), vim.fs.dirname(path), path) end end test_paths(test_basename_dirname_eq) if is_os('win') then - test_paths(tests_windows_paths) + test_paths(tests_windows_paths, true) end end) end) @@ -97,25 +102,26 @@ describe('vim.fs', function() it('works', function() eq(nvim_prog_basename, vim.fs.basename(nvim_prog)) - local function test_paths(paths) + ---@param paths string[] + ---@param is_win? boolean + local function test_paths(paths, is_win) + local gsub = is_win and [[:gsub('\\', '/')]] or '' + local code = string.format( + [[ + local path = ... + return vim.fn.fnamemodify(path,':t')%s + ]], + gsub + ) + for _, path in ipairs(paths) do - eq( - exec_lua( - [[ - local path = ... - return vim.fn.fnamemodify(path,':t'):gsub('\\', '/') - ]], - path - ), - vim.fs.basename(path), - path - ) + eq(exec_lua(code, path), vim.fs.basename(path), path) end end test_paths(test_basename_dirname_eq) if is_os('win') then - test_paths(tests_windows_paths) + test_paths(tests_windows_paths, true) end end) end) @@ -274,6 +280,57 @@ describe('vim.fs', function() end) end) + describe('root()', function() + before_each(function() + command('edit test/functional/fixtures/tty-test.c') + end) + + it('works with a single marker', function() + eq(test_source_path, exec_lua([[return vim.fs.root(0, '.git')]])) + end) + + it('works with multiple markers', function() + local bufnr = api.nvim_get_current_buf() + eq( + vim.fs.joinpath(test_source_path, 'test/functional/fixtures'), + exec_lua([[return vim.fs.root(..., {'CMakeLists.txt', '.git'})]], bufnr) + ) + end) + + it('works with a function', function() + ---@type string + local result = exec_lua([[ + return vim.fs.root(0, function(name, path) + return name:match('%.txt$') + end) + ]]) + eq(vim.fs.joinpath(test_source_path, 'test/functional/fixtures'), result) + end) + + it('works with a filename argument', function() + eq(test_source_path, exec_lua([[return vim.fs.root(..., '.git')]], nvim_prog)) + end) + + it('works with a relative path', function() + eq( + test_source_path, + exec_lua([[return vim.fs.root(..., '.git')]], vim.fs.basename(nvim_prog)) + ) + end) + + it('uses cwd for unnamed buffers', function() + command('new') + eq(test_source_path, exec_lua([[return vim.fs.root(0, '.git')]])) + end) + + it("uses cwd for buffers with non-empty 'buftype'", function() + command('new') + command('set buftype=nofile') + command('file lua://') + eq(test_source_path, exec_lua([[return vim.fs.root(0, '.git')]])) + end) + end) + describe('joinpath()', function() it('works', function() eq('foo/bar/baz', vim.fs.joinpath('foo', 'bar', 'baz')) @@ -282,9 +339,6 @@ describe('vim.fs', function() end) describe('normalize()', function() - it('works with backward slashes', function() - eq('C:/Users/jdoe', vim.fs.normalize('C:\\Users\\jdoe')) - end) it('removes trailing /', function() eq('/home/user', vim.fs.normalize('/home/user/')) end) @@ -292,7 +346,7 @@ describe('vim.fs', function() eq('/', vim.fs.normalize('/')) end) it('works with ~', function() - eq(vim.fs.normalize(vim.uv.os_homedir()) .. '/src/foo', vim.fs.normalize('~/src/foo')) + eq(vim.fs.normalize(assert(vim.uv.os_homedir())) .. '/src/foo', vim.fs.normalize('~/src/foo')) end) it('works with environment variables', function() local xdg_config_home = test_build_dir .. '/.config' @@ -307,10 +361,113 @@ describe('vim.fs', function() ) ) end) - if is_os('win') then - it('Last slash is not truncated from root drive', function() - eq('C:/', vim.fs.normalize('C:/')) + + -- Opts required for testing posix paths and win paths + local posix_opts = is_os('win') and { win = false } or {} + local win_opts = is_os('win') and {} or { win = true } + + it('preserves leading double slashes in POSIX paths', function() + eq('//foo', vim.fs.normalize('//foo', posix_opts)) + eq('//foo/bar', vim.fs.normalize('//foo//bar////', posix_opts)) + eq('/foo', vim.fs.normalize('///foo', posix_opts)) + eq('//', vim.fs.normalize('//', posix_opts)) + eq('/', vim.fs.normalize('///', posix_opts)) + eq('/foo/bar', vim.fs.normalize('/foo//bar////', posix_opts)) + end) + + it('allows backslashes on unix-based os', function() + eq('/home/user/hello\\world', vim.fs.normalize('/home/user/hello\\world', posix_opts)) + end) + + it('preserves / after drive letters', function() + eq('C:/', vim.fs.normalize([[C:\]], win_opts)) + end) + + it('works with UNC and DOS device paths', function() + eq('//server/share/foo/bar', vim.fs.normalize([[\\server\\share\\\foo\bar\\\]], win_opts)) + eq('//system07/C$/', vim.fs.normalize([[\\system07\C$\\\\]], win_opts)) + eq('//./C:/foo/bar', vim.fs.normalize([[\\.\\C:\foo\\\\bar]], win_opts)) + eq('//?/C:/foo/bar', vim.fs.normalize([[\\?\C:\\\foo\bar\\\\]], win_opts)) + eq( + '//?/UNC/server/share/foo/bar', + vim.fs.normalize([[\\?\UNC\server\\\share\\\\foo\\\bar]], win_opts) + ) + eq('//./BootPartition/foo/bar', vim.fs.normalize([[\\.\BootPartition\\foo\bar]], win_opts)) + eq( + '//./Volume{12345678-1234-1234-1234-1234567890AB}/foo/bar', + vim.fs.normalize([[\\.\Volume{12345678-1234-1234-1234-1234567890AB}\\\foo\bar\\]], win_opts) + ) + end) + + it('handles invalid UNC and DOS device paths', function() + eq('//server/share', vim.fs.normalize([[\\server\share]], win_opts)) + eq('//server/', vim.fs.normalize([[\\server\]], win_opts)) + eq('//./UNC/server/share', vim.fs.normalize([[\\.\UNC\server\share]], win_opts)) + eq('//?/UNC/server/', vim.fs.normalize([[\\?\UNC\server\]], win_opts)) + eq('//?/UNC/server/..', vim.fs.normalize([[\\?\UNC\server\..]], win_opts)) + eq('//./', vim.fs.normalize([[\\.\]], win_opts)) + eq('//./foo', vim.fs.normalize([[\\.\foo]], win_opts)) + eq('//./BootPartition', vim.fs.normalize([[\\.\BootPartition]], win_opts)) + end) + + it('converts backward slashes', function() + eq('C:/Users/jdoe', vim.fs.normalize([[C:\Users\jdoe]], win_opts)) + end) + + describe('. and .. component resolving', function() + it('works', function() + -- Windows paths + eq('C:/Users', vim.fs.normalize([[C:\Users\jdoe\Downloads\.\..\..\]], win_opts)) + eq('C:/Users/jdoe', vim.fs.normalize([[C:\Users\jdoe\Downloads\.\..\.\.\]], win_opts)) + eq('C:/', vim.fs.normalize('C:/Users/jdoe/Downloads/./../../../', win_opts)) + eq('C:foo', vim.fs.normalize([[C:foo\bar\.\..\.]], win_opts)) + -- POSIX paths + eq('/home', vim.fs.normalize('/home/jdoe/Downloads/./../..', posix_opts)) + eq('/home/jdoe', vim.fs.normalize('/home/jdoe/Downloads/./../././', posix_opts)) + eq('/', vim.fs.normalize('/home/jdoe/Downloads/./../../../', posix_opts)) + -- OS-agnostic relative paths + eq('foo/bar/baz', vim.fs.normalize('foo/bar/foobar/../baz/./')) + eq('foo/bar', vim.fs.normalize('foo/bar/foobar/../baz/./../../bar/./.')) end) - end + + it('works when relative path reaches current directory', function() + eq('C:', vim.fs.normalize('C:foo/bar/../../.', win_opts)) + + eq('.', vim.fs.normalize('.')) + eq('.', vim.fs.normalize('././././')) + eq('.', vim.fs.normalize('foo/bar/../../.')) + end) + + it('works when relative path goes outside current directory', function() + eq('../../foo/bar', vim.fs.normalize('../../foo/bar')) + eq('../foo', vim.fs.normalize('foo/bar/../../../foo')) + + eq('C:../foo', vim.fs.normalize('C:../foo', win_opts)) + eq('C:../../foo/bar', vim.fs.normalize('C:foo/../../../foo/bar', win_opts)) + end) + + it('.. in root directory resolves to itself', function() + eq('C:/', vim.fs.normalize('C:/../../', win_opts)) + eq('C:/foo', vim.fs.normalize('C:/foo/../../foo', win_opts)) + + eq('//server/share/', vim.fs.normalize([[\\server\share\..\..]], win_opts)) + eq('//server/share/foo', vim.fs.normalize([[\\server\\share\foo\..\..\foo]], win_opts)) + + eq('//./C:/', vim.fs.normalize([[\\.\C:\..\..]], win_opts)) + eq('//?/C:/foo', vim.fs.normalize([[\\?\C:\..\..\foo]], win_opts)) + + eq('//./UNC/server/share/', vim.fs.normalize([[\\.\UNC\\server\share\..\..\]], win_opts)) + eq( + '//?/UNC/server/share/foo', + vim.fs.normalize([[\\?\UNC\server\\share\..\..\foo]], win_opts) + ) + + eq('//?/BootPartition/', vim.fs.normalize([[\\?\BootPartition\..\..]], win_opts)) + eq('//./BootPartition/foo', vim.fs.normalize([[\\.\BootPartition\..\..\foo]], win_opts)) + + eq('/', vim.fs.normalize('/../../', posix_opts)) + eq('/foo', vim.fs.normalize('/foo/../../foo', posix_opts)) + end) + end) end) end) diff --git a/test/functional/lua/glob_spec.lua b/test/functional/lua/glob_spec.lua index 1eac037575..56cd4c9bb5 100644 --- a/test/functional/lua/glob_spec.lua +++ b/test/functional/lua/glob_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local exec_lua = helpers.exec_lua +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local exec_lua = n.exec_lua describe('glob', function() - before_each(helpers.clear) - after_each(helpers.clear) + before_each(n.clear) + after_each(n.clear) local match = function(...) return exec_lua( diff --git a/test/functional/lua/highlight_spec.lua b/test/functional/lua/highlight_spec.lua index 197f3139f3..c9f2d0a47f 100644 --- a/test/functional/lua/highlight_spec.lua +++ b/test/functional/lua/highlight_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local neq = helpers.neq -local eval = helpers.eval -local command = helpers.command -local clear = helpers.clear -local api = helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local exec_lua = n.exec_lua +local eq = t.eq +local neq = t.neq +local eval = n.eval +local command = n.command +local clear = n.clear +local api = n.api describe('vim.highlight.on_yank', function() before_each(function() @@ -19,7 +21,7 @@ describe('vim.highlight.on_yank', function() vim.cmd('bwipeout!') ]]) vim.uv.sleep(10) - helpers.feed('<cr>') -- avoid hang if error message exists + n.feed('<cr>') -- avoid hang if error message exists eq('', eval('v:errmsg')) end) @@ -41,9 +43,9 @@ describe('vim.highlight.on_yank', function() vim.api.nvim_buf_set_mark(0,"]",1,1,{}) vim.highlight.on_yank({timeout = math.huge, on_macro = true, event = {operator = "y"}}) ]]) - neq({}, api.nvim_win_get_ns(0)) + neq({}, api.nvim__win_get_ns(0)) command('wincmd w') - eq({}, api.nvim_win_get_ns(0)) + eq({}, api.nvim__win_get_ns(0)) end) it('removes old highlight if new one is created before old one times out', function() @@ -53,7 +55,7 @@ describe('vim.highlight.on_yank', function() vim.api.nvim_buf_set_mark(0,"]",1,1,{}) vim.highlight.on_yank({timeout = math.huge, on_macro = true, event = {operator = "y"}}) ]]) - neq({}, api.nvim_win_get_ns(0)) + neq({}, api.nvim__win_get_ns(0)) command('wincmd w') exec_lua([[ vim.api.nvim_buf_set_mark(0,"[",1,1,{}) @@ -61,6 +63,6 @@ describe('vim.highlight.on_yank', function() vim.highlight.on_yank({timeout = math.huge, on_macro = true, event = {operator = "y"}}) ]]) command('wincmd w') - eq({}, api.nvim_win_get_ns(0)) + eq({}, api.nvim__win_get_ns(0)) end) end) diff --git a/test/functional/lua/inspector_spec.lua b/test/functional/lua/inspector_spec.lua index ad8b5a45a8..8fadba6ee8 100644 --- a/test/functional/lua/inspector_spec.lua +++ b/test/functional/lua/inspector_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local eval = helpers.eval -local clear = helpers.clear +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local exec_lua = n.exec_lua +local eq = t.eq +local eval = n.eval +local clear = n.clear describe('vim.inspect_pos', function() before_each(function() diff --git a/test/functional/lua/iter_spec.lua b/test/functional/lua/iter_spec.lua index 8d6cf1264b..79e92e6a7d 100644 --- a/test/functional/lua/iter_spec.lua +++ b/test/functional/lua/iter_spec.lua @@ -1,7 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local matches = helpers.matches -local pcall_err = helpers.pcall_err +local t = require('test.testutil') + +local eq = t.eq +local matches = t.matches +local pcall_err = t.pcall_err describe('vim.iter', function() it('new() on iterable class instance', function() @@ -18,12 +19,12 @@ describe('vim.iter', function() return v % 2 ~= 0 end - local t = { 1, 2, 3, 4, 5 } - eq({ 1, 3, 5 }, vim.iter(t):filter(odd):totable()) + local q = { 1, 2, 3, 4, 5 } + eq({ 1, 3, 5 }, vim.iter(q):filter(odd):totable()) eq( { 2, 4 }, vim - .iter(t) + .iter(q) :filter(function(v) return not odd(v) end) @@ -32,7 +33,7 @@ describe('vim.iter', function() eq( {}, vim - .iter(t) + .iter(q) :filter(function(v) return v > 5 end) @@ -40,7 +41,7 @@ describe('vim.iter', function() ) do - local it = vim.iter(ipairs(t)) + local it = vim.iter(ipairs(q)) it:filter(function(i, v) return i > 1 and v < 5 end) @@ -60,11 +61,11 @@ describe('vim.iter', function() end) it('map()', function() - local t = { 1, 2, 3, 4, 5 } + local q = { 1, 2, 3, 4, 5 } eq( { 2, 4, 6, 8, 10 }, vim - .iter(t) + .iter(q) :map(function(v) return 2 * v end) @@ -96,10 +97,10 @@ describe('vim.iter', function() end) it('for loops', function() - local t = { 1, 2, 3, 4, 5 } + local q = { 1, 2, 3, 4, 5 } local acc = 0 for v in - vim.iter(t):map(function(v) + vim.iter(q):map(function(v) return v * 3 end) do @@ -116,6 +117,9 @@ describe('vim.iter', function() eq({ { 1, 1 }, { 2, 4 }, { 3, 9 } }, it:totable()) end + -- Holes in array-like tables are removed + eq({ 1, 2, 3 }, vim.iter({ 1, nil, 2, nil, 3 }):totable()) + do local it = vim.iter(string.gmatch('1,4,lol,17,blah,2,9,3', '%d+')):map(tonumber) eq({ 1, 4, 17, 2, 9, 3 }, it:totable()) @@ -141,18 +145,18 @@ describe('vim.iter', function() eq({ 3, 2, 1 }, vim.iter({ 1, 2, 3 }):rev():totable()) local it = vim.iter(string.gmatch('abc', '%w')) - matches('rev%(%) requires a list%-like table', pcall_err(it.rev, it)) + matches('rev%(%) requires an array%-like table', pcall_err(it.rev, it)) end) it('skip()', function() do - local t = { 4, 3, 2, 1 } - eq(t, vim.iter(t):skip(0):totable()) - eq({ 3, 2, 1 }, vim.iter(t):skip(1):totable()) - eq({ 2, 1 }, vim.iter(t):skip(2):totable()) - eq({ 1 }, vim.iter(t):skip(#t - 1):totable()) - eq({}, vim.iter(t):skip(#t):totable()) - eq({}, vim.iter(t):skip(#t + 1):totable()) + local q = { 4, 3, 2, 1 } + eq(q, vim.iter(q):skip(0):totable()) + eq({ 3, 2, 1 }, vim.iter(q):skip(1):totable()) + eq({ 2, 1 }, vim.iter(q):skip(2):totable()) + eq({ 1 }, vim.iter(q):skip(#q - 1):totable()) + eq({}, vim.iter(q):skip(#q):totable()) + eq({}, vim.iter(q):skip(#q + 1):totable()) end do @@ -168,44 +172,44 @@ describe('vim.iter', function() end end) - it('skipback()', function() + it('rskip()', function() do - local t = { 4, 3, 2, 1 } - eq(t, vim.iter(t):skipback(0):totable()) - eq({ 4, 3, 2 }, vim.iter(t):skipback(1):totable()) - eq({ 4, 3 }, vim.iter(t):skipback(2):totable()) - eq({ 4 }, vim.iter(t):skipback(#t - 1):totable()) - eq({}, vim.iter(t):skipback(#t):totable()) - eq({}, vim.iter(t):skipback(#t + 1):totable()) + local q = { 4, 3, 2, 1 } + eq(q, vim.iter(q):rskip(0):totable()) + eq({ 4, 3, 2 }, vim.iter(q):rskip(1):totable()) + eq({ 4, 3 }, vim.iter(q):rskip(2):totable()) + eq({ 4 }, vim.iter(q):rskip(#q - 1):totable()) + eq({}, vim.iter(q):rskip(#q):totable()) + eq({}, vim.iter(q):rskip(#q + 1):totable()) end local it = vim.iter(vim.gsplit('a|b|c|d', '|')) - matches('skipback%(%) requires a list%-like table', pcall_err(it.skipback, it, 0)) + matches('rskip%(%) requires an array%-like table', pcall_err(it.rskip, it, 0)) end) it('slice()', function() - local t = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } - eq({ 3, 4, 5, 6, 7 }, vim.iter(t):slice(3, 7):totable()) - eq({}, vim.iter(t):slice(6, 5):totable()) - eq({}, vim.iter(t):slice(0, 0):totable()) - eq({ 1 }, vim.iter(t):slice(1, 1):totable()) - eq({ 1, 2 }, vim.iter(t):slice(1, 2):totable()) - eq({ 10 }, vim.iter(t):slice(10, 10):totable()) - eq({ 8, 9, 10 }, vim.iter(t):slice(8, 11):totable()) + local q = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } + eq({ 3, 4, 5, 6, 7 }, vim.iter(q):slice(3, 7):totable()) + eq({}, vim.iter(q):slice(6, 5):totable()) + eq({}, vim.iter(q):slice(0, 0):totable()) + eq({ 1 }, vim.iter(q):slice(1, 1):totable()) + eq({ 1, 2 }, vim.iter(q):slice(1, 2):totable()) + eq({ 10 }, vim.iter(q):slice(10, 10):totable()) + eq({ 8, 9, 10 }, vim.iter(q):slice(8, 11):totable()) local it = vim.iter(vim.gsplit('a|b|c|d', '|')) - matches('slice%(%) requires a list%-like table', pcall_err(it.slice, it, 1, 3)) + matches('slice%(%) requires an array%-like table', pcall_err(it.slice, it, 1, 3)) end) it('nth()', function() do - local t = { 4, 3, 2, 1 } - eq(nil, vim.iter(t):nth(0)) - eq(4, vim.iter(t):nth(1)) - eq(3, vim.iter(t):nth(2)) - eq(2, vim.iter(t):nth(3)) - eq(1, vim.iter(t):nth(4)) - eq(nil, vim.iter(t):nth(5)) + local q = { 4, 3, 2, 1 } + eq(nil, vim.iter(q):nth(0)) + eq(4, vim.iter(q):nth(1)) + eq(3, vim.iter(q):nth(2)) + eq(2, vim.iter(q):nth(3)) + eq(1, vim.iter(q):nth(4)) + eq(nil, vim.iter(q):nth(5)) end do @@ -221,35 +225,41 @@ describe('vim.iter', function() end end) - it('nthback()', function() + it('nth(-x) advances in reverse order starting from end', function() do - local t = { 4, 3, 2, 1 } - eq(nil, vim.iter(t):nthback(0)) - eq(1, vim.iter(t):nthback(1)) - eq(2, vim.iter(t):nthback(2)) - eq(3, vim.iter(t):nthback(3)) - eq(4, vim.iter(t):nthback(4)) - eq(nil, vim.iter(t):nthback(5)) + local q = { 4, 3, 2, 1 } + eq(nil, vim.iter(q):nth(0)) + eq(1, vim.iter(q):nth(-1)) + eq(2, vim.iter(q):nth(-2)) + eq(3, vim.iter(q):nth(-3)) + eq(4, vim.iter(q):nth(-4)) + eq(nil, vim.iter(q):nth(-5)) end local it = vim.iter(vim.gsplit('a|b|c|d', '|')) - matches('skipback%(%) requires a list%-like table', pcall_err(it.nthback, it, 1)) + matches('rskip%(%) requires an array%-like table', pcall_err(it.nth, it, -1)) end) it('take()', function() do - local t = { 4, 3, 2, 1 } - eq({}, vim.iter(t):take(0):totable()) - eq({ 4 }, vim.iter(t):take(1):totable()) - eq({ 4, 3 }, vim.iter(t):take(2):totable()) - eq({ 4, 3, 2 }, vim.iter(t):take(3):totable()) - eq({ 4, 3, 2, 1 }, vim.iter(t):take(4):totable()) - eq({ 4, 3, 2, 1 }, vim.iter(t):take(5):totable()) + local q = { 4, 3, 2, 1 } + eq({}, vim.iter(q):take(0):totable()) + eq({ 4 }, vim.iter(q):take(1):totable()) + eq({ 4, 3 }, vim.iter(q):take(2):totable()) + eq({ 4, 3, 2 }, vim.iter(q):take(3):totable()) + eq({ 4, 3, 2, 1 }, vim.iter(q):take(4):totable()) + eq({ 4, 3, 2, 1 }, vim.iter(q):take(5):totable()) + end + + do + local q = { 4, 3, 2, 1 } + eq({ 1, 2, 3 }, vim.iter(q):rev():take(3):totable()) + eq({ 2, 3, 4 }, vim.iter(q):take(3):rev():totable()) end do - local t = { 4, 3, 2, 1 } - local it = vim.iter(t) + local q = { 4, 3, 2, 1 } + local it = vim.iter(q) eq({ 4, 3 }, it:take(2):totable()) -- tail is already set from the previous take() eq({ 4, 3 }, it:take(3):totable()) @@ -269,13 +279,13 @@ describe('vim.iter', function() end do - local t = { 4, 8, 9, 10 } - eq(true, vim.iter(t):any(odd)) + local q = { 4, 8, 9, 10 } + eq(true, vim.iter(q):any(odd)) end do - local t = { 4, 8, 10 } - eq(false, vim.iter(t):any(odd)) + local q = { 4, 8, 10 } + eq(false, vim.iter(q):any(odd)) end do @@ -300,13 +310,13 @@ describe('vim.iter', function() end do - local t = { 3, 5, 7, 9 } - eq(true, vim.iter(t):all(odd)) + local q = { 3, 5, 7, 9 } + eq(true, vim.iter(q):all(odd)) end do - local t = { 3, 5, 7, 10 } - eq(false, vim.iter(t):all(odd)) + local q = { 3, 5, 7, 10 } + eq(false, vim.iter(q):all(odd)) end do @@ -349,23 +359,23 @@ describe('vim.iter', function() do local it = vim.iter(vim.gsplit('hi', '')) - matches('peek%(%) requires a list%-like table', pcall_err(it.peek, it)) + matches('peek%(%) requires an array%-like table', pcall_err(it.peek, it)) end end) it('find()', function() - local t = { 3, 6, 9, 12 } - eq(12, vim.iter(t):find(12)) - eq(nil, vim.iter(t):find(15)) + local q = { 3, 6, 9, 12 } + eq(12, vim.iter(q):find(12)) + eq(nil, vim.iter(q):find(15)) eq( 12, - vim.iter(t):find(function(v) + vim.iter(q):find(function(v) return v % 4 == 0 end) ) do - local it = vim.iter(t) + local it = vim.iter(q) local pred = function(v) return v % 3 == 0 end @@ -389,16 +399,16 @@ describe('vim.iter', function() end) it('rfind()', function() - local t = { 1, 2, 3, 2, 1 } + local q = { 1, 2, 3, 2, 1 } do - local it = vim.iter(t) + local it = vim.iter(q) eq(1, it:rfind(1)) eq(1, it:rfind(1)) eq(nil, it:rfind(1)) end do - local it = vim.iter(t):enumerate() + local it = vim.iter(q):enumerate() local pred = function(i) return i % 2 ~= 0 end @@ -410,52 +420,52 @@ describe('vim.iter', function() do local it = vim.iter(vim.gsplit('AbCdE', '')) - matches('rfind%(%) requires a list%-like table', pcall_err(it.rfind, it, 'E')) + matches('rfind%(%) requires an array%-like table', pcall_err(it.rfind, it, 'E')) end end) - it('nextback()', function() + it('pop()', function() do local it = vim.iter({ 1, 2, 3, 4 }) - eq(4, it:nextback()) - eq(3, it:nextback()) - eq(2, it:nextback()) - eq(1, it:nextback()) - eq(nil, it:nextback()) - eq(nil, it:nextback()) + eq(4, it:pop()) + eq(3, it:pop()) + eq(2, it:pop()) + eq(1, it:pop()) + eq(nil, it:pop()) + eq(nil, it:pop()) end do local it = vim.iter(vim.gsplit('hi', '')) - matches('nextback%(%) requires a list%-like table', pcall_err(it.nextback, it)) + matches('pop%(%) requires an array%-like table', pcall_err(it.pop, it)) end end) - it('peekback()', function() + it('rpeek()', function() do local it = vim.iter({ 1, 2, 3, 4 }) - eq(4, it:peekback()) - eq(4, it:peekback()) - eq(4, it:nextback()) + eq(4, it:rpeek()) + eq(4, it:rpeek()) + eq(4, it:pop()) end do local it = vim.iter(vim.gsplit('hi', '')) - matches('peekback%(%) requires a list%-like table', pcall_err(it.peekback, it)) + matches('rpeek%(%) requires an array%-like table', pcall_err(it.rpeek, it)) end end) it('fold()', function() - local t = { 1, 2, 3, 4, 5 } + local q = { 1, 2, 3, 4, 5 } eq( 115, - vim.iter(t):fold(100, function(acc, v) + vim.iter(q):fold(100, function(acc, v) return acc + v end) ) eq( { 5, 4, 3, 2, 1 }, - vim.iter(t):fold({}, function(acc, v) + vim.iter(q):fold({}, function(acc, v) table.insert(acc, 1, v) return acc end) @@ -463,30 +473,32 @@ describe('vim.iter', function() end) it('flatten()', function() - local t = { { 1, { 2 } }, { { { { 3 } } }, { 4 } }, { 5 } } + local q = { { 1, { 2 } }, { { { { 3 } } }, { 4 } }, { 5 } } - eq(t, vim.iter(t):flatten(-1):totable()) - eq(t, vim.iter(t):flatten(0):totable()) - eq({ 1, { 2 }, { { { 3 } } }, { 4 }, 5 }, vim.iter(t):flatten():totable()) - eq({ 1, 2, { { 3 } }, 4, 5 }, vim.iter(t):flatten(2):totable()) - eq({ 1, 2, { 3 }, 4, 5 }, vim.iter(t):flatten(3):totable()) - eq({ 1, 2, 3, 4, 5 }, vim.iter(t):flatten(4):totable()) + eq(q, vim.iter(q):flatten(-1):totable()) + eq(q, vim.iter(q):flatten(0):totable()) + eq({ 1, { 2 }, { { { 3 } } }, { 4 }, 5 }, vim.iter(q):flatten():totable()) + eq({ 1, 2, { { 3 } }, 4, 5 }, vim.iter(q):flatten(2):totable()) + eq({ 1, 2, { 3 }, 4, 5 }, vim.iter(q):flatten(3):totable()) + eq({ 1, 2, 3, 4, 5 }, vim.iter(q):flatten(4):totable()) local m = { a = 1, b = { 2, 3 }, d = { 4 } } local it = vim.iter(m) - local flat_err = 'flatten%(%) requires a list%-like table' + local flat_err = 'flatten%(%) requires an array%-like table' matches(flat_err, pcall_err(it.flatten, it)) -- cases from the documentation local simple_example = { 1, { 2 }, { { 3 } } } eq({ 1, 2, { 3 } }, vim.iter(simple_example):flatten():totable()) - local not_list_like = vim.iter({ [2] = 2 }) - matches(flat_err, pcall_err(not_list_like.flatten, not_list_like)) + local not_list_like = { [2] = 2 } + eq({ 2 }, vim.iter(not_list_like):flatten():totable()) + + local also_not_list_like = { nil, 2 } + eq({ 2 }, vim.iter(also_not_list_like):flatten():totable()) - local also_not_list_like = vim.iter({ nil, 2 }) - matches(flat_err, pcall_err(not_list_like.flatten, also_not_list_like)) + eq({ 1, 2, 3 }, vim.iter({ nil, { 1, nil, 2 }, 3 }):flatten():totable()) local nested_non_lists = vim.iter({ 1, { { a = 2 } }, { { nil } }, { 3 } }) eq({ 1, { a = 2 }, { nil }, 3 }, nested_non_lists:flatten():totable()) @@ -501,11 +513,11 @@ describe('vim.iter', function() end end) - local t = it:fold({}, function(t, k, v) - t[k] = v - return t + local q = it:fold({}, function(q, k, v) + q[k] = v + return q end) - eq({ A = 2, C = 6 }, t) + eq({ A = 2, C = 6 }, q) end) it('handles table values mid-pipeline', function() diff --git a/test/functional/lua/json_spec.lua b/test/functional/lua/json_spec.lua index d348e2de3c..a6e814d739 100644 --- a/test/functional/lua/json_spec.lua +++ b/test/functional/lua/json_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec_lua = n.exec_lua +local eq = t.eq +local pcall_err = t.pcall_err describe('vim.json.decode()', function() before_each(function() @@ -30,6 +32,18 @@ describe('vim.json.decode()', function() baz = vim.NIL, foo = { a = 'b' }, }, exec_lua([[return vim.json.decode(..., {})]], jsonstr)) + eq( + { + arr = { 1, 2, vim.NIL }, + bar = { 3, 7 }, + baz = vim.NIL, + foo = { a = 'b' }, + }, + exec_lua( + [[return vim.json.decode(..., { luanil = { array = false, object = false } })]], + jsonstr + ) + ) eq({ arr = { 1, 2, vim.NIL }, bar = { 3, 7 }, diff --git a/test/functional/lua/loader_spec.lua b/test/functional/lua/loader_spec.lua index 4e42a18405..f13e6664c5 100644 --- a/test/functional/lua/loader_spec.lua +++ b/test/functional/lua/loader_spec.lua @@ -1,10 +1,11 @@ -- Test suite for testing interactions with API bindings -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local exec_lua = helpers.exec_lua -local command = helpers.command -local clear = helpers.clear -local eq = helpers.eq +local exec_lua = n.exec_lua +local command = n.command +local clear = n.clear +local eq = t.eq describe('vim.loader', function() before_each(clear) @@ -35,7 +36,7 @@ describe('vim.loader', function() vim.loader.enable() ]] - local tmp = helpers.tmpname() + local tmp = t.tmpname() command('edit ' .. tmp) eq( @@ -73,15 +74,15 @@ describe('vim.loader', function() vim.loader.enable() ]] - local tmp1, tmp2 = (function(t) - assert(os.remove(t)) - assert(helpers.mkdir(t)) - assert(helpers.mkdir(t .. '/%')) - return t .. '/%/x', t .. '/%%x' - end)(helpers.tmpname()) + local tmp = t.tmpname() + assert(os.remove(tmp)) + assert(t.mkdir(tmp)) + assert(t.mkdir(tmp .. '/%')) + local tmp1 = tmp .. '/%/x' + local tmp2 = tmp .. '/%%x' - helpers.write_file(tmp1, 'return 1', true) - helpers.write_file(tmp2, 'return 2', true) + t.write_file(tmp1, 'return 1', true) + t.write_file(tmp2, 'return 2', true) vim.uv.fs_utime(tmp1, 0, 0) vim.uv.fs_utime(tmp2, 0, 0) eq(1, exec_lua('return loadfile(...)()', tmp1)) diff --git a/test/functional/lua/loop_spec.lua b/test/functional/lua/loop_spec.lua index 71eaf29009..566a171a84 100644 --- a/test/functional/lua/loop_spec.lua +++ b/test/functional/lua/loop_spec.lua @@ -1,16 +1,18 @@ -- Test suite for testing interactions with API bindings -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local fn = helpers.fn -local api = helpers.api -local clear = helpers.clear + +local fn = n.fn +local api = n.api +local clear = n.clear local sleep = vim.uv.sleep -local feed = helpers.feed -local eq = helpers.eq -local eval = helpers.eval -local matches = helpers.matches -local exec_lua = helpers.exec_lua -local retry = helpers.retry +local feed = n.feed +local eq = t.eq +local eval = n.eval +local matches = t.matches +local exec_lua = n.exec_lua +local retry = t.retry before_each(clear) @@ -131,6 +133,20 @@ describe('vim.uv', function() {5:-- INSERT --} | ]]) eq({ blocking = false, mode = 'n' }, exec_lua('return _G.mode')) + + exec_lua([[ + local timer = vim.uv.new_timer() + timer:start(20, 0, function () + _G.is_fast = vim.in_fast_event() + timer:close() + _G.value = vim.fn.has("nvim-0.5") + _G.unvalue = vim.fn.has("python3") + end) + ]]) + + screen:expect({ any = [[{3:Vim:E5560: Vimscript function must not be called i}]] }) + feed('<cr>') + eq({ 1, nil }, exec_lua('return {_G.value, _G.unvalue}')) end) it("is equal to require('luv')", function() diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index b28cfa4dd2..3f62cd8325 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -1,19 +1,20 @@ -- Test suite for testing luaeval() function -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local pcall_err = helpers.pcall_err -local exc_exec = helpers.exc_exec -local remove_trace = helpers.remove_trace -local exec_lua = helpers.exec_lua -local command = helpers.command -local api = helpers.api -local fn = helpers.fn -local clear = helpers.clear -local eval = helpers.eval -local feed = helpers.feed +local pcall_err = t.pcall_err +local exc_exec = n.exc_exec +local remove_trace = t.remove_trace +local exec_lua = n.exec_lua +local command = n.command +local api = n.api +local fn = n.fn +local clear = n.clear +local eval = n.eval +local feed = n.feed local NIL = vim.NIL -local eq = helpers.eq +local eq = t.eq before_each(clear) @@ -38,8 +39,8 @@ describe('luaeval()', function() describe('second argument', function() it('is successfully received', function() - local t = {t=true, f=false, --[[n=NIL,]] d={l={'string', 42, 0.42}}} - eq(t, fn.luaeval("_A", t)) + local q = {t=true, f=false, --[[n=NIL,]] d={l={'string', 42, 0.42}}} + eq(q, fn.luaeval("_A", q)) -- Not tested: nil, funcrefs, returned object identity: behaviour will -- most likely change. end) diff --git a/test/functional/lua/mpack_spec.lua b/test/functional/lua/mpack_spec.lua index 0b6a6d60bd..efd69d4607 100644 --- a/test/functional/lua/mpack_spec.lua +++ b/test/functional/lua/mpack_spec.lua @@ -1,9 +1,10 @@ -- Test suite for testing interactions with API bindings -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local exec_lua = helpers.exec_lua +local clear = n.clear +local eq = t.eq +local exec_lua = n.exec_lua describe('lua vim.mpack', function() before_each(clear) @@ -22,7 +23,7 @@ describe('lua vim.mpack', function() { { {}, 'foo', {} }, true, false }, exec_lua [[ local var = vim.mpack.decode(vim.mpack.encode({{}, "foo", vim.empty_dict()})) - return {var, vim.tbl_islist(var[1]), vim.tbl_islist(var[3])} + return {var, vim.islist(var[1]), vim.islist(var[3])} ]] ) end) diff --git a/test/functional/lua/overrides_spec.lua b/test/functional/lua/overrides_spec.lua index ecbdde3bfd..849978f080 100644 --- a/test/functional/lua/overrides_spec.lua +++ b/test/functional/lua/overrides_spec.lua @@ -1,19 +1,20 @@ -- Test for Vim overrides of lua built-ins -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local eq = helpers.eq +local eq = t.eq local NIL = vim.NIL -local feed = helpers.feed -local clear = helpers.clear -local fn = helpers.fn -local api = helpers.api -local command = helpers.command -local write_file = helpers.write_file -local exec_capture = helpers.exec_capture -local exec_lua = helpers.exec_lua -local pcall_err = helpers.pcall_err -local is_os = helpers.is_os +local feed = n.feed +local clear = n.clear +local fn = n.fn +local api = n.api +local command = n.command +local write_file = t.write_file +local exec_capture = n.exec_capture +local exec_lua = n.exec_lua +local pcall_err = t.pcall_err +local is_os = t.is_os local fname = 'Xtest-functional-lua-overrides-luafile' @@ -195,7 +196,7 @@ describe('print', function() end) describe('debug.debug', function() - local screen + local screen --- @type test.functional.ui.screen before_each(function() screen = Screen.new() diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua index 6f36ccfb9e..4adce42c3e 100644 --- a/test/functional/lua/runtime_spec.lua +++ b/test/functional/lua/runtime_spec.lua @@ -1,17 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local exec = helpers.exec -local fn = helpers.fn -local mkdir_p = helpers.mkdir_p -local rmdir = helpers.rmdir -local write_file = helpers.write_file +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local eval = n.eval +local exec = n.exec +local fn = n.fn +local mkdir_p = n.mkdir_p +local rmdir = n.rmdir +local write_file = t.write_file describe('runtime:', function() local plug_dir = 'Test_Plugin' - local sep = helpers.get_pathsep() + local sep = n.get_pathsep() local init = 'dummy_init.lua' setup(function() diff --git a/test/functional/lua/secure_spec.lua b/test/functional/lua/secure_spec.lua index 7aed711b23..c58fd689b7 100644 --- a/test/functional/lua/secure_spec.lua +++ b/test/functional/lua/secure_spec.lua @@ -1,18 +1,21 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local eq = helpers.eq -local clear = helpers.clear -local command = helpers.command -local pathsep = helpers.get_pathsep() -local is_os = helpers.is_os -local api = helpers.api -local exec_lua = helpers.exec_lua -local feed_command = helpers.feed_command -local feed = helpers.feed -local fn = helpers.fn -local pcall_err = helpers.pcall_err -local matches = helpers.matches +local eq = t.eq +local clear = n.clear +local command = n.command +local pathsep = n.get_pathsep() +local is_os = t.is_os +local api = n.api +local exec_lua = n.exec_lua +local feed_command = n.feed_command +local feed = n.feed +local fn = n.fn +local stdpath = fn.stdpath +local pcall_err = t.pcall_err +local matches = t.matches +local read_file = t.read_file describe('vim.secure', function() describe('read()', function() @@ -20,8 +23,8 @@ describe('vim.secure', function() setup(function() clear { env = { XDG_STATE_HOME = xstate } } - helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) - helpers.write_file( + n.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) + t.write_file( 'Xfile', [[ let g:foobar = 42 @@ -31,7 +34,7 @@ describe('vim.secure', function() teardown(function() os.remove('Xfile') - helpers.rmdir(xstate) + n.rmdir(xstate) end) it('works', function() @@ -71,11 +74,11 @@ describe('vim.secure', function() ]], } - local trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + local trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(string.format('! %s', cwd .. pathsep .. 'Xfile'), vim.trim(trust)) eq(vim.NIL, exec_lua([[return vim.secure.read('Xfile')]])) - os.remove(fn.stdpath('state') .. pathsep .. 'trust') + os.remove(stdpath('state') .. pathsep .. 'trust') feed_command([[lua vim.secure.read('Xfile')]]) screen:expect { @@ -100,12 +103,12 @@ describe('vim.secure', function() ]], } - local hash = fn.sha256(helpers.read_file('Xfile')) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + local hash = fn.sha256(read_file('Xfile')) + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(string.format('%s %s', hash, cwd .. pathsep .. 'Xfile'), vim.trim(trust)) eq(vim.NIL, exec_lua([[vim.secure.read('Xfile')]])) - os.remove(fn.stdpath('state') .. pathsep .. 'trust') + os.remove(stdpath('state') .. pathsep .. 'trust') feed_command([[lua vim.secure.read('Xfile')]]) screen:expect { @@ -131,7 +134,7 @@ describe('vim.secure', function() } -- Trust database is not updated - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(nil, trust) feed_command([[lua vim.secure.read('Xfile')]]) @@ -165,7 +168,7 @@ describe('vim.secure', function() } -- Trust database is not updated - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(nil, trust) -- Cannot write file @@ -179,15 +182,15 @@ describe('vim.secure', function() setup(function() clear { env = { XDG_STATE_HOME = xstate } } - helpers.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) + n.mkdir_p(xstate .. pathsep .. (is_os('win') and 'nvim-data' or 'nvim')) end) teardown(function() - helpers.rmdir(xstate) + n.rmdir(xstate) end) before_each(function() - helpers.write_file('test_file', 'test') + t.write_file('test_file', 'test') end) after_each(function() @@ -210,70 +213,70 @@ describe('vim.secure', function() it('trust then deny then remove a file using bufnr', function() local cwd = fn.getcwd() - local hash = fn.sha256(helpers.read_file('test_file')) + local hash = fn.sha256(read_file('test_file')) local full_path = cwd .. pathsep .. 'test_file' command('edit test_file') eq({ true, full_path }, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) - local trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + local trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(string.format('%s %s', hash, full_path), vim.trim(trust)) eq({ true, full_path }, exec_lua([[return {vim.secure.trust({action='deny', bufnr=0})}]])) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(string.format('! %s', full_path), vim.trim(trust)) eq({ true, full_path }, exec_lua([[return {vim.secure.trust({action='remove', bufnr=0})}]])) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq('', vim.trim(trust)) end) it('deny then trust then remove a file using bufnr', function() local cwd = fn.getcwd() - local hash = fn.sha256(helpers.read_file('test_file')) + local hash = fn.sha256(read_file('test_file')) local full_path = cwd .. pathsep .. 'test_file' command('edit test_file') eq({ true, full_path }, exec_lua([[return {vim.secure.trust({action='deny', bufnr=0})}]])) - local trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + local trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(string.format('! %s', full_path), vim.trim(trust)) eq({ true, full_path }, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(string.format('%s %s', hash, full_path), vim.trim(trust)) eq({ true, full_path }, exec_lua([[return {vim.secure.trust({action='remove', bufnr=0})}]])) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq('', vim.trim(trust)) end) it('trust using bufnr then deny then remove a file using path', function() local cwd = fn.getcwd() - local hash = fn.sha256(helpers.read_file('test_file')) + local hash = fn.sha256(read_file('test_file')) local full_path = cwd .. pathsep .. 'test_file' command('edit test_file') eq({ true, full_path }, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) - local trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + local trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(string.format('%s %s', hash, full_path), vim.trim(trust)) eq( { true, full_path }, exec_lua([[return {vim.secure.trust({action='deny', path='test_file'})}]]) ) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(string.format('! %s', full_path), vim.trim(trust)) eq( { true, full_path }, exec_lua([[return {vim.secure.trust({action='remove', path='test_file'})}]]) ) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq('', vim.trim(trust)) end) it('deny then trust then remove a file using bufnr', function() local cwd = fn.getcwd() - local hash = fn.sha256(helpers.read_file('test_file')) + local hash = fn.sha256(read_file('test_file')) local full_path = cwd .. pathsep .. 'test_file' command('edit test_file') @@ -281,18 +284,18 @@ describe('vim.secure', function() { true, full_path }, exec_lua([[return {vim.secure.trust({action='deny', path='test_file'})}]]) ) - local trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + local trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(string.format('! %s', full_path), vim.trim(trust)) eq({ true, full_path }, exec_lua([[return {vim.secure.trust({action='allow', bufnr=0})}]])) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq(string.format('%s %s', hash, full_path), vim.trim(trust)) eq( { true, full_path }, exec_lua([[return {vim.secure.trust({action='remove', path='test_file'})}]]) ) - trust = helpers.read_file(fn.stdpath('state') .. pathsep .. 'trust') + trust = read_file(stdpath('state') .. pathsep .. 'trust') eq('', vim.trim(trust)) end) diff --git a/test/functional/lua/snippet_spec.lua b/test/functional/lua/snippet_spec.lua index e981bc6261..413aa93994 100644 --- a/test/functional/lua/snippet_spec.lua +++ b/test/functional/lua/snippet_spec.lua @@ -1,15 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) - -local buf_lines = helpers.buf_lines -local clear = helpers.clear -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local feed = helpers.feed -local fn = helpers.fn -local matches = helpers.matches -local pcall_err = helpers.pcall_err -local poke_eventloop = helpers.poke_eventloop -local retry = helpers.retry +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local buf_lines = n.buf_lines +local clear = n.clear +local eq = t.eq +local exec_lua = n.exec_lua +local feed = n.feed +local api = n.api +local fn = n.fn +local matches = t.matches +local pcall_err = t.pcall_err +local poke_eventloop = n.poke_eventloop +local retry = t.retry describe('vim.snippet', function() before_each(function() @@ -95,9 +97,9 @@ describe('vim.snippet', function() it('does not jump outside snippet range', function() test_expand_success({ 'function $1($2)', ' $0', 'end' }, { 'function ()', ' ', 'end' }) - eq(false, exec_lua('return vim.snippet.jumpable(-1)')) + eq(false, exec_lua('return vim.snippet.active({ direction = -1 })')) feed('<Tab><Tab>i') - eq(false, exec_lua('return vim.snippet.jumpable(1)')) + eq(false, exec_lua('return vim.snippet.active( { direction = 1 })')) end) it('navigates backwards', function() @@ -230,7 +232,7 @@ describe('vim.snippet', function() end) it('updates snippet state when built-in completion menu is visible', function() - test_expand_success({ '$1 = function($2)\n$3\nend' }, { ' = function()', '', 'end' }) + test_expand_success({ '$1 = function($2)\nend' }, { ' = function()', 'end' }) -- Show the completion menu. feed('<C-n>') -- Make sure no item is selected. @@ -238,6 +240,50 @@ describe('vim.snippet', function() -- Jump forward (the 2nd tabstop). exec_lua('vim.snippet.jump(1)') feed('foo') - eq({ ' = function(foo)', '', 'end' }, buf_lines(0)) + eq({ ' = function(foo)', 'end' }, buf_lines(0)) + end) + + it('correctly indents with newlines', function() + local curbuf = api.nvim_get_current_buf() + test_expand_success( + { 'function($2)\n\t$3\nend' }, + { 'function()', ' ', 'end' }, + [[ + vim.opt.sw = 2 + vim.opt.expandtab = true + ]] + ) + api.nvim_buf_set_lines(curbuf, 0, -1, false, {}) + test_expand_success( + { 'function($2)\n$3\nend' }, + { 'function()', '', 'end' }, + [[ + vim.opt.sw = 2 + vim.opt.expandtab = true + ]] + ) + api.nvim_buf_set_lines(curbuf, 0, -1, false, {}) + test_expand_success( + { 'func main() {\n\t$1\n}' }, + { 'func main() {', '\t', '}' }, + [[ + vim.opt.sw = 4 + vim.opt.ts = 4 + vim.opt.expandtab = false + ]] + ) + api.nvim_buf_set_lines(curbuf, 0, -1, false, {}) + test_expand_success( + { '${1:name} :: ${2}\n${1:name} ${3}= ${0:undefined}' }, + { + 'name :: ', + 'name = undefined', + }, + [[ + vim.opt.sw = 4 + vim.opt.ts = 4 + vim.opt.expandtab = false + ]] + ) end) end) diff --git a/test/functional/lua/spell_spec.lua b/test/functional/lua/spell_spec.lua index e82dd7b4a0..af7d08b390 100644 --- a/test/functional/lua/spell_spec.lua +++ b/test/functional/lua/spell_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec_lua = n.exec_lua +local eq = t.eq +local pcall_err = t.pcall_err describe('vim.spell', function() before_each(function() diff --git a/test/functional/lua/system_spec.lua b/test/functional/lua/system_spec.lua index cb561f0771..e72a009d2e 100644 --- a/test/functional/lua/system_spec.lua +++ b/test/functional/lua/system_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local eq = helpers.eq +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec_lua = n.exec_lua +local eq = t.eq local function system_sync(cmd, opts) return exec_lua( diff --git a/test/functional/lua/text_spec.lua b/test/functional/lua/text_spec.lua index e31aa63768..9e77953c8c 100644 --- a/test/functional/lua/text_spec.lua +++ b/test/functional/lua/text_spec.lua @@ -1,6 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq describe('vim.text', function() before_each(clear) diff --git a/test/functional/lua/thread_spec.lua b/test/functional/lua/thread_spec.lua index c1981e19d4..780057b580 100644 --- a/test/functional/lua/thread_spec.lua +++ b/test/functional/lua/thread_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local assert_alive = helpers.assert_alive -local clear = helpers.clear -local feed = helpers.feed -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local next_msg = helpers.next_msg + +local assert_alive = n.assert_alive +local clear = n.clear +local feed = n.feed +local eq = t.eq +local exec_lua = n.exec_lua +local next_msg = n.next_msg local NIL = vim.NIL -local pcall_err = helpers.pcall_err +local pcall_err = t.pcall_err describe('thread', function() local screen @@ -166,7 +168,7 @@ describe('thread', function() ]] local msg = next_msg() - eq(msg[1], 'notification') + eq('notification', msg[1]) assert(tonumber(msg[2]) >= 72961) end) @@ -327,7 +329,7 @@ describe('threadpool', function() ]] local msg = next_msg() - eq(msg[1], 'notification') + eq('notification', msg[1]) assert(tonumber(msg[2]) >= 72961) end) diff --git a/test/functional/lua/ui_event_spec.lua b/test/functional/lua/ui_event_spec.lua index 3e46018682..1e80c88403 100644 --- a/test/functional/lua/ui_event_spec.lua +++ b/test/functional/lua/ui_event_spec.lua @@ -1,10 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local clear = helpers.clear -local feed = helpers.feed -local fn = helpers.fn + +local eq = t.eq +local exec_lua = n.exec_lua +local clear = n.clear +local feed = n.feed +local fn = n.fn +local assert_log = t.assert_log +local check_close = n.check_close + +local testlog = 'Xtest_lua_ui_event_log' describe('vim.ui_attach', function() local screen @@ -108,7 +114,7 @@ describe('vim.ui_attach', function() it('does not crash on exit', function() fn.system({ - helpers.nvim_prog, + n.nvim_prog, '-u', 'NONE', '-i', @@ -120,7 +126,7 @@ describe('vim.ui_attach', function() '--cmd', 'quitall!', }) - eq(0, helpers.eval('v:shell_error')) + eq(0, n.eval('v:shell_error')) end) it('can receive accurate message kinds even if they are history', function() @@ -149,4 +155,56 @@ describe('vim.ui_attach', function() }, }, actual, vim.inspect(actual)) end) + + it('ui_refresh() activates correct capabilities without remote UI', function() + screen:detach() + exec_lua('vim.ui_attach(ns, { ext_cmdline = true }, on_event)') + eq(1, n.api.nvim_get_option_value('cmdheight', {})) + exec_lua('vim.ui_detach(ns)') + exec_lua('vim.ui_attach(ns, { ext_messages = true }, on_event)') + n.api.nvim_set_option_value('cmdheight', 1, {}) + screen:attach() + eq(1, n.api.nvim_get_option_value('cmdheight', {})) + end) + + it("ui_refresh() sets 'cmdheight' for all open tabpages with ext_messages", function() + exec_lua('vim.cmd.tabnew()') + exec_lua('vim.ui_attach(ns, { ext_messages = true }, on_event)') + exec_lua('vim.cmd.tabnext()') + eq(0, n.api.nvim_get_option_value('cmdheight', {})) + end) + + it('avoids recursive flushing and invalid memory access with :redraw', function() + exec_lua([[ + _G.cmdline = 0 + vim.ui_attach(ns, { ext_messages = true }, function(ev) + vim.cmd.redraw() + _G.cmdline = _G.cmdline + (ev == 'cmdline_show' and 1 or 0) + end + )]]) + feed(':') + eq(1, exec_lua('return _G.cmdline')) + n.assert_alive() + feed('version<CR><CR>v<Esc>') + n.assert_alive() + end) +end) + +describe('vim.ui_attach', function() + after_each(function() + check_close() + os.remove(testlog) + end) + + it('error in callback is logged', function() + clear({ env = { NVIM_LOG_FILE = testlog } }) + local screen = Screen.new() + screen:attach() + exec_lua([[ + local ns = vim.api.nvim_create_namespace('testspace') + vim.ui_attach(ns, { ext_popupmenu = true }, function() error(42) end) + ]]) + feed('ifoo<CR>foobar<CR>fo<C-X><C-N>') + assert_log('Error executing UI event callback: Error executing lua: .*: 42', testlog, 100) + end) end) diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua index e769843b19..d69e893c96 100644 --- a/test/functional/lua/ui_spec.lua +++ b/test/functional/lua/ui_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local matches = helpers.matches -local exec_lua = helpers.exec_lua -local clear = helpers.clear -local feed = helpers.feed -local eval = helpers.eval -local is_ci = helpers.is_ci -local is_os = helpers.is_os -local poke_eventloop = helpers.poke_eventloop +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local ok = t.ok +local exec_lua = n.exec_lua +local clear = n.clear +local feed = n.feed +local eval = n.eval +local is_ci = t.is_ci +local is_os = t.is_os +local poke_eventloop = n.poke_eventloop describe('vim.ui', function() before_each(function() @@ -138,13 +140,12 @@ describe('vim.ui', function() describe('open()', function() it('validation', function() if is_os('win') or not is_ci('github') then - exec_lua [[vim.system = function() return { wait=function() return { code=3} end } end]] + exec_lua [[vim.system = function() return { wait=function() return { code=3 } end } end]] end if not is_os('bsd') then - matches( - 'vim.ui.open: command failed %(%d%): { "[^"]+", .*"non%-existent%-file" }', - exec_lua [[local _, err = vim.ui.open('non-existent-file') ; return err]] - ) + local rv = + exec_lua [[local cmd = vim.ui.open('non-existent-file'); return cmd:wait(100).code]] + ok(type(rv) == 'number' and rv ~= 0, 'nonzero exit code', rv) end exec_lua [[ @@ -152,7 +153,7 @@ describe('vim.ui', function() vim.fn.executable = function() return 0 end ]] eq( - 'vim.ui.open: no handler found (tried: explorer.exe, xdg-open)', + 'vim.ui.open: no handler found (tried: wslview, explorer.exe, xdg-open)', exec_lua [[local _, err = vim.ui.open('foo') ; return err]] ) end) diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua index dacaf95867..553afb35d3 100644 --- a/test/functional/lua/uri_spec.lua +++ b/test/functional/lua/uri_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local is_os = helpers.is_os -local skip = helpers.skip -local write_file = require('test.helpers').write_file +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec_lua = n.exec_lua +local eq = t.eq +local is_os = t.is_os +local skip = t.skip +local write_file = t.write_file describe('URI methods', function() before_each(function() @@ -206,7 +208,7 @@ describe('URI methods', function() it('Windows paths should not be treated as uris', function() skip(not is_os('win'), 'Not applicable on non-Windows') - local file = helpers.tmpname() + local file = t.tmpname() write_file(file, 'Test content') local test_case = string.format( [[ diff --git a/test/functional/lua/version_spec.lua b/test/functional/lua/version_spec.lua index 3bc9e26d41..4ce8fb8dfe 100644 --- a/test/functional/lua/version_spec.lua +++ b/test/functional/lua/version_spec.lua @@ -1,10 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local ok = helpers.ok -local exec_lua = helpers.exec_lua -local matches = helpers.matches -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local ok = t.ok +local exec_lua = n.exec_lua +local matches = t.matches +local pcall_err = t.pcall_err +local fn = n.fn local function v(ver) return vim.version._version(ver) @@ -17,7 +20,7 @@ describe('version', function() end) it('version() returns Nvim version', function() - local expected = exec_lua('return vim.fn.api_info().version') + local expected = fn.api_info().version local actual = exec_lua('return vim.version()') eq(expected.major, actual.major) eq(expected.minor, actual.minor) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index a262d239e8..c8f94c6ffa 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -1,32 +1,33 @@ -- Test suite for testing interactions with API bindings -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local nvim_prog = helpers.nvim_prog -local fn = helpers.fn -local api = helpers.api -local command = helpers.command -local dedent = helpers.dedent -local insert = helpers.insert -local clear = helpers.clear -local eq = helpers.eq -local ok = helpers.ok +local nvim_prog = n.nvim_prog +local fn = n.fn +local api = n.api +local command = n.command +local dedent = t.dedent +local insert = n.insert +local clear = n.clear +local eq = t.eq +local ok = t.ok local pesc = vim.pesc -local eval = helpers.eval -local feed = helpers.feed -local pcall_err = helpers.pcall_err -local exec_lua = helpers.exec_lua -local matches = helpers.matches -local exec = helpers.exec +local eval = n.eval +local feed = n.feed +local pcall_err = t.pcall_err +local exec_lua = n.exec_lua +local matches = t.matches +local exec = n.exec local NIL = vim.NIL -local retry = helpers.retry -local next_msg = helpers.next_msg -local remove_trace = helpers.remove_trace -local mkdir_p = helpers.mkdir_p -local rmdir = helpers.rmdir -local write_file = helpers.write_file -local poke_eventloop = helpers.poke_eventloop -local assert_alive = helpers.assert_alive +local retry = t.retry +local next_msg = n.next_msg +local remove_trace = t.remove_trace +local mkdir_p = n.mkdir_p +local rmdir = n.rmdir +local write_file = t.write_file +local poke_eventloop = n.poke_eventloop +local assert_alive = n.assert_alive describe('lua stdlib', function() before_each(clear) @@ -128,63 +129,62 @@ describe('lua stdlib', function() eq(1, fn.luaeval('vim.stricmp("\\0C\\0", "\\0B\\0")')) end) - local function test_vim_deprecate(current_version) + --- @param prerel string | nil + local function test_vim_deprecate(prerel) -- vim.deprecate(name, alternative, version, plugin, backtrace) -- See MAINTAIN.md for the soft/hard deprecation policy - describe(('vim.deprecate [current_version = %s]'):format(current_version), function() - before_each(function() - -- mock vim.version() behavior, should be pinned for consistent testing - exec_lua( - [[ - local current_version_mock = vim.version.parse(...) - getmetatable(vim.version).__call = function() - return current_version_mock - end - ]], - current_version - ) - end) + describe(('vim.deprecate prerel=%s,'):format(prerel or 'nil'), function() + local curver = exec_lua('return vim.version()') --[[@as {major:number, minor:number}]] + -- "0.10" or "0.10-dev+xxx" + local curstr = ('%s.%s%s'):format(curver.major, curver.minor, prerel or '') + -- "0.10" or "0.11" + local nextver = ('%s.%s'):format(curver.major, curver.minor + (prerel and 0 or 1)) + local was_removed = prerel and 'was removed' or 'will be removed' - it('when plugin = nil', function() + it('plugin=nil, same message skipped', function() eq( - dedent [[ - foo.bar() is deprecated, use zub.wooo{ok=yay} instead. :help deprecated - This feature will be removed in Nvim version 0.10]], - exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', '0.10') + dedent( + [[ + foo.bar() is deprecated. Run ":checkhealth vim.deprecated" for more information]] + ):format(curstr), + exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr) ) - -- Same message, skipped. - eq(vim.NIL, exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', '0.10')) + -- Same message as above; skipped this time. + eq(vim.NIL, exec_lua('return vim.deprecate(...)', 'foo.bar()', 'zub.wooo{ok=yay}', curstr)) + end) - -- Don't show error if not hard-deprecated (only soft-deprecated) + it('plugin=nil, no error if soft-deprecated', function() eq( vim.NIL, - exec_lua('return vim.deprecate(...)', 'foo.baz()', 'foo.better_baz()', '0.12.0') + exec_lua('return vim.deprecate(...)', 'foo.baz()', 'foo.better_baz()', '0.99.0') ) + end) - -- Show error if hard-deprecated + it('plugin=nil, show error if hard-deprecated', function() eq( - dedent [[ - foo.hard_dep() is deprecated, use vim.new_api() instead. :help deprecated - This feature will be removed in Nvim version 0.11]], - exec_lua('return vim.deprecate(...)', 'foo.hard_dep()', 'vim.new_api()', '0.11') + dedent( + [[ + foo.hard_dep() is deprecated. Run ":checkhealth vim.deprecated" for more information]] + ):format(was_removed, nextver), + exec_lua('return vim.deprecate(...)', 'foo.hard_dep()', 'vim.new_api()', nextver) ) + end) - -- To be deleted in the next major version (1.0) + it('plugin=nil, to be deleted in the next major version (1.0)', function() eq( dedent [[ - foo.baz() is deprecated. :help deprecated - This feature will be removed in Nvim version 1.0]], + foo.baz() is deprecated. Run ":checkhealth vim.deprecated" for more information]], exec_lua [[ return vim.deprecate('foo.baz()', nil, '1.0') ]] ) end) - it('when plugin is specified', function() + it('plugin specified', function() -- When `plugin` is specified, don't show ":help deprecated". #22235 eq( dedent [[ foo.bar() is deprecated, use zub.wooo{ok=yay} instead. - This feature will be removed in my-plugin.nvim version 0.3.0]], + Feature will be removed in my-plugin.nvim 0.3.0]], exec_lua( 'return vim.deprecate(...)', 'foo.bar()', @@ -199,7 +199,7 @@ describe('lua stdlib', function() eq( dedent [[ foo.bar() is deprecated, use zub.wooo{ok=yay} instead. - This feature will be removed in my-plugin.nvim version 0.11.0]], + Feature will be removed in my-plugin.nvim 0.11.0]], exec_lua( 'return vim.deprecate(...)', 'foo.bar()', @@ -213,8 +213,8 @@ describe('lua stdlib', function() end) end - test_vim_deprecate('0.10') - test_vim_deprecate('0.10-dev+g0000000') + test_vim_deprecate() + test_vim_deprecate('-dev+g0000000') it('vim.startswith', function() eq(true, fn.luaeval('vim.startswith("123", "1")')) @@ -592,8 +592,8 @@ describe('lua stdlib', function() { 'x*yz*oo*l', '*', true, false, { 'x', 'yz', 'oo', 'l' } }, } - for _, t in ipairs(tests) do - eq(t[5], vim.split(t[1], t[2], { plain = t[3], trimempty = t[4] }), t[1]) + for _, q in ipairs(tests) do + eq(q[5], vim.split(q[1], q[2], { plain = q[3], trimempty = q[4] }), q[1]) end -- Test old signature @@ -603,8 +603,8 @@ describe('lua stdlib', function() { 'abc', '.-' }, } - for _, t in ipairs(loops) do - matches('Infinite loop detected', pcall_err(vim.split, t[1], t[2])) + for _, q in ipairs(loops) do + matches('Infinite loop detected', pcall_err(vim.split, q[1], q[2])) end -- Validates args. @@ -626,8 +626,8 @@ describe('lua stdlib', function() { 'r\n', 'r' }, } - for _, t in ipairs(trims) do - assert(t[2], trim(t[1])) + for _, q in ipairs(trims) do + assert(q[2], trim(q[1])) end -- Validates args. @@ -636,8 +636,8 @@ describe('lua stdlib', function() it('vim.inspect', function() -- just make sure it basically works, it has its own test suite - local inspect = function(t, opts) - return exec_lua('return vim.inspect(...)', t, opts) + local inspect = function(q, opts) + return exec_lua('return vim.inspect(...)', q, opts) end eq('2', inspect(2)) @@ -670,21 +670,21 @@ describe('lua stdlib', function() local a = {} local b = vim.deepcopy(a) - return vim.tbl_islist(b) and vim.tbl_count(b) == 0 and tostring(a) ~= tostring(b) + return vim.islist(b) and vim.tbl_count(b) == 0 and tostring(a) ~= tostring(b) ]])) ok(exec_lua([[ local a = vim.empty_dict() local b = vim.deepcopy(a) - return not vim.tbl_islist(b) and vim.tbl_count(b) == 0 + return not vim.islist(b) and vim.tbl_count(b) == 0 ]])) ok(exec_lua([[ local a = {x = vim.empty_dict(), y = {}} local b = vim.deepcopy(a) - return not vim.tbl_islist(b.x) and vim.tbl_islist(b.y) + return not vim.islist(b.x) and vim.islist(b.y) and vim.tbl_count(b) == 2 and tostring(a) ~= tostring(b) ]])) @@ -822,30 +822,30 @@ describe('lua stdlib', function() ) end) - it('vim.tbl_isarray', function() - eq(true, exec_lua('return vim.tbl_isarray({})')) - eq(false, exec_lua('return vim.tbl_isarray(vim.empty_dict())')) - eq(true, exec_lua("return vim.tbl_isarray({'a', 'b', 'c'})")) - eq(false, exec_lua("return vim.tbl_isarray({'a', '32', a='hello', b='baz'})")) - eq(false, exec_lua("return vim.tbl_isarray({1, a='hello', b='baz'})")) - eq(false, exec_lua("return vim.tbl_isarray({a='hello', b='baz', 1})")) - eq(false, exec_lua("return vim.tbl_isarray({1, 2, nil, a='hello'})")) - eq(true, exec_lua('return vim.tbl_isarray({1, 2, nil, 4})')) - eq(true, exec_lua('return vim.tbl_isarray({nil, 2, 3, 4})')) - eq(false, exec_lua('return vim.tbl_isarray({1, [1.5]=2, [3]=3})')) + it('vim.isarray', function() + eq(true, exec_lua('return vim.isarray({})')) + eq(false, exec_lua('return vim.isarray(vim.empty_dict())')) + eq(true, exec_lua("return vim.isarray({'a', 'b', 'c'})")) + eq(false, exec_lua("return vim.isarray({'a', '32', a='hello', b='baz'})")) + eq(false, exec_lua("return vim.isarray({1, a='hello', b='baz'})")) + eq(false, exec_lua("return vim.isarray({a='hello', b='baz', 1})")) + eq(false, exec_lua("return vim.isarray({1, 2, nil, a='hello'})")) + eq(true, exec_lua('return vim.isarray({1, 2, nil, 4})')) + eq(true, exec_lua('return vim.isarray({nil, 2, 3, 4})')) + eq(false, exec_lua('return vim.isarray({1, [1.5]=2, [3]=3})')) end) - it('vim.tbl_islist', function() - eq(true, exec_lua('return vim.tbl_islist({})')) - eq(false, exec_lua('return vim.tbl_islist(vim.empty_dict())')) - eq(true, exec_lua("return vim.tbl_islist({'a', 'b', 'c'})")) - eq(false, exec_lua("return vim.tbl_islist({'a', '32', a='hello', b='baz'})")) - eq(false, exec_lua("return vim.tbl_islist({1, a='hello', b='baz'})")) - eq(false, exec_lua("return vim.tbl_islist({a='hello', b='baz', 1})")) - eq(false, exec_lua("return vim.tbl_islist({1, 2, nil, a='hello'})")) - eq(false, exec_lua('return vim.tbl_islist({1, 2, nil, 4})')) - eq(false, exec_lua('return vim.tbl_islist({nil, 2, 3, 4})')) - eq(false, exec_lua('return vim.tbl_islist({1, [1.5]=2, [3]=3})')) + it('vim.islist', function() + eq(true, exec_lua('return vim.islist({})')) + eq(false, exec_lua('return vim.islist(vim.empty_dict())')) + eq(true, exec_lua("return vim.islist({'a', 'b', 'c'})")) + eq(false, exec_lua("return vim.islist({'a', '32', a='hello', b='baz'})")) + eq(false, exec_lua("return vim.islist({1, a='hello', b='baz'})")) + eq(false, exec_lua("return vim.islist({a='hello', b='baz', 1})")) + eq(false, exec_lua("return vim.islist({1, 2, nil, a='hello'})")) + eq(false, exec_lua('return vim.islist({1, 2, nil, 4})')) + eq(false, exec_lua('return vim.islist({nil, 2, 3, 4})')) + eq(false, exec_lua('return vim.islist({1, [1.5]=2, [3]=3})')) end) it('vim.tbl_isempty', function() @@ -918,7 +918,7 @@ describe('lua stdlib', function() local b = {} local c = vim.tbl_extend("keep", a, b) - return not vim.tbl_islist(c) and vim.tbl_count(c) == 0 + return not vim.islist(c) and vim.tbl_count(c) == 0 ]])) ok(exec_lua([[ @@ -926,7 +926,7 @@ describe('lua stdlib', function() local b = vim.empty_dict() local c = vim.tbl_extend("keep", a, b) - return vim.tbl_islist(c) and vim.tbl_count(c) == 0 + return vim.islist(c) and vim.tbl_count(c) == 0 ]])) ok(exec_lua([[ @@ -1026,7 +1026,7 @@ describe('lua stdlib', function() local count = 0 for _ in pairs(c) do count = count + 1 end - return not vim.tbl_islist(c) and count == 0 + return not vim.islist(c) and count == 0 ]])) ok(exec_lua([[ @@ -1037,7 +1037,7 @@ describe('lua stdlib', function() local count = 0 for _ in pairs(c) do count = count + 1 end - return vim.tbl_islist(c) and count == 0 + return vim.islist(c) and count == 0 ]])) eq( @@ -1282,7 +1282,7 @@ describe('lua stdlib', function() vim.rpcrequest(chan, 'nvim_exec', 'let xx = {}\nlet yy = []', false) local dict = vim.rpcrequest(chan, 'nvim_eval', 'xx') local list = vim.rpcrequest(chan, 'nvim_eval', 'yy') - return {dict, list, vim.tbl_islist(dict), vim.tbl_islist(list)} + return {dict, list, vim.islist(dict), vim.islist(list)} ]]) ) @@ -1355,7 +1355,7 @@ describe('lua stdlib', function() vim.api.nvim_set_var('dicty', vim.empty_dict()) local listy = vim.fn.eval("listy") local dicty = vim.fn.eval("dicty") - return {vim.tbl_islist(listy), vim.tbl_islist(dicty), next(listy) == nil, next(dicty) == nil} + return {vim.islist(listy), vim.islist(dicty), next(listy) == nil, next(dicty) == nil} ]]) ) @@ -1569,7 +1569,7 @@ describe('lua stdlib', function() eq(NIL, exec_lua([[return vim.g.Unknown_script_func]])) -- Check if autoload works properly - local pathsep = helpers.get_pathsep() + local pathsep = n.get_pathsep() local xconfig = 'Xhome' .. pathsep .. 'Xconfig' local xdata = 'Xhome' .. pathsep .. 'Xdata' local autoload_folder = table.concat({ xconfig, 'nvim', 'autoload' }, pathsep) @@ -2016,7 +2016,7 @@ describe('lua stdlib', function() vim.opt.scrolloff = 10 return vim.o.scrolloff ]] - eq(scrolloff, 10) + eq(10, scrolloff) end) pending('should handle STUPID window things', function() @@ -2037,7 +2037,7 @@ describe('lua stdlib', function() vim.opt.wildignore = { 'hello', 'world' } return vim.o.wildignore ]] - eq(wildignore, 'hello,world') + eq('hello,world', wildignore) end) it('should allow setting tables with shortnames', function() @@ -2045,7 +2045,7 @@ describe('lua stdlib', function() vim.opt.wig = { 'hello', 'world' } return vim.o.wildignore ]] - eq(wildignore, 'hello,world') + eq('hello,world', wildignore) end) it('should error when you attempt to set string values to numeric options', function() @@ -2451,13 +2451,13 @@ describe('lua stdlib', function() vim.opt.wildignore = 'foo' return vim.o.wildignore ]] - eq(wildignore, 'foo') + eq('foo', wildignore) wildignore = exec_lua [[ vim.opt.wildignore = vim.opt.wildignore + { 'bar', 'baz' } return vim.o.wildignore ]] - eq(wildignore, 'foo,bar,baz') + eq('foo,bar,baz', wildignore) end) it('should handle adding duplicates', function() @@ -2465,19 +2465,19 @@ describe('lua stdlib', function() vim.opt.wildignore = 'foo' return vim.o.wildignore ]] - eq(wildignore, 'foo') + eq('foo', wildignore) wildignore = exec_lua [[ vim.opt.wildignore = vim.opt.wildignore + { 'bar', 'baz' } return vim.o.wildignore ]] - eq(wildignore, 'foo,bar,baz') + eq('foo,bar,baz', wildignore) wildignore = exec_lua [[ vim.opt.wildignore = vim.opt.wildignore + { 'bar', 'baz' } return vim.o.wildignore ]] - eq(wildignore, 'foo,bar,baz') + eq('foo,bar,baz', wildignore) end) it('should allow adding multiple times', function() @@ -2486,7 +2486,7 @@ describe('lua stdlib', function() vim.opt.wildignore = vim.opt.wildignore + 'bar' + 'baz' return vim.o.wildignore ]] - eq(wildignore, 'foo,bar,baz') + eq('foo,bar,baz', wildignore) end) it('should remove values when you use minus', function() @@ -2494,19 +2494,19 @@ describe('lua stdlib', function() vim.opt.wildignore = 'foo' return vim.o.wildignore ]] - eq(wildignore, 'foo') + eq('foo', wildignore) wildignore = exec_lua [[ vim.opt.wildignore = vim.opt.wildignore + { 'bar', 'baz' } return vim.o.wildignore ]] - eq(wildignore, 'foo,bar,baz') + eq('foo,bar,baz', wildignore) wildignore = exec_lua [[ vim.opt.wildignore = vim.opt.wildignore - 'bar' return vim.o.wildignore ]] - eq(wildignore, 'foo,baz') + eq('foo,baz', wildignore) end) it('should prepend values when using ^', function() @@ -2521,7 +2521,7 @@ describe('lua stdlib', function() vim.opt.wildignore = vim.opt.wildignore ^ 'super_first' return vim.o.wildignore ]] - eq(wildignore, 'super_first,first,foo') + eq('super_first,first,foo', wildignore) end) it('should not remove duplicates from wildmode: #14708', function() @@ -2530,7 +2530,7 @@ describe('lua stdlib', function() return vim.o.wildmode ]] - eq(wildmode, 'full,list,full') + eq('full,list,full', wildmode) end) describe('option types', function() @@ -2738,7 +2738,7 @@ describe('lua stdlib', function() return vim.go.whichwrap ]] - eq(ww, 'b,s') + eq('b,s', ww) eq( 'b,s,<,>,[,]', exec_lua [[ @@ -3007,25 +3007,65 @@ describe('lua stdlib', function() end) describe('vim.on_key', function() - it('tracks keystrokes', function() + it('tracks Unicode input', function() insert([[hello world ]]) exec_lua [[ keys = {} + typed = {} - vim.on_key(function(buf) + vim.on_key(function(buf, typed_buf) if buf:byte() == 27 then buf = "<ESC>" end + if typed_buf:byte() == 27 then + typed_buf = "<ESC>" + end table.insert(keys, buf) + table.insert(typed, typed_buf) end) ]] - insert([[next 🤦 lines å ]]) + insert([[next 🤦 lines å …]]) -- It has escape in the keys pressed - eq('inext 🤦 lines å <ESC>', exec_lua [[return table.concat(keys, '')]]) + eq('inext 🤦 lines å …<ESC>', exec_lua [[return table.concat(keys, '')]]) + eq('inext 🤦 lines å …<ESC>', exec_lua [[return table.concat(typed, '')]]) + end) + + it('tracks input with modifiers', function() + exec_lua [[ + keys = {} + typed = {} + + vim.on_key(function(buf, typed_buf) + table.insert(keys, vim.fn.keytrans(buf)) + table.insert(typed, vim.fn.keytrans(typed_buf)) + end) + ]] + + feed([[i<C-V><C-;><C-V><C-…><Esc>]]) + + eq('i<C-V><C-;><C-V><C-…><Esc>', exec_lua [[return table.concat(keys, '')]]) + eq('i<C-V><C-;><C-V><C-…><Esc>', exec_lua [[return table.concat(typed, '')]]) + end) + + it('works with character find and Select mode', function() + insert('12345') + + exec_lua [[ + typed = {} + + vim.cmd('snoremap # @') + + vim.on_key(function(buf, typed_buf) + table.insert(typed, vim.fn.keytrans(typed_buf)) + end) + ]] + + feed('F3gHβγδεζ<Esc>gH…<Esc>gH#$%^') + eq('F3gHβγδεζ<Esc>gH…<Esc>gH#$%^', exec_lua [[return table.concat(typed, '')]]) end) it('allows removing on_key listeners', function() @@ -3087,23 +3127,29 @@ describe('lua stdlib', function() eq('inext l', exec_lua [[ return table.concat(keys, '') ]]) end) - it('processes mapped keys, not unmapped keys', function() + it('argument 1 is keys after mapping, argument 2 is typed keys', function() exec_lua [[ keys = {} + typed = {} vim.cmd("inoremap hello world") - vim.on_key(function(buf) + vim.on_key(function(buf, typed_buf) if buf:byte() == 27 then buf = "<ESC>" end + if typed_buf:byte() == 27 then + typed_buf = "<ESC>" + end table.insert(keys, buf) + table.insert(typed, typed_buf) end) ]] insert('hello') eq('iworld<ESC>', exec_lua [[return table.concat(keys, '')]]) + eq('ihello<ESC>', exec_lua [[return table.concat(typed, '')]]) end) it('can call vim.fn functions on Ctrl-C #17273', function() @@ -3664,6 +3710,20 @@ describe('lua stdlib', function() ]] ) end) + + it('layout in current tabpage does not affect windows in others', function() + command('tab split') + local t2_move_win = api.nvim_get_current_win() + command('vsplit') + local t2_other_win = api.nvim_get_current_win() + command('tabprevious') + matches('E36: Not enough room$', pcall_err(command, 'execute "split|"->repeat(&lines)')) + command('vsplit') + + -- Without vim-patch:8.2.3862, this gives E36, despite just the 1st tabpage being full. + exec_lua('vim.api.nvim_win_call(..., function() vim.cmd.wincmd "J" end)', t2_move_win) + eq({ 'col', { { 'leaf', t2_other_win }, { 'leaf', t2_move_win } } }, fn.winlayout(2)) + end) end) describe('vim.iconv', function() @@ -4011,7 +4071,7 @@ describe('vim.keymap', function() feed('asdf\n') eq(1, exec_lua [[return GlobalCount]]) - eq('\nNo mapping found', helpers.exec_capture('nmap asdf')) + eq('\nNo mapping found', n.exec_capture('nmap asdf')) end) it('works with buffer-local mappings', function() @@ -4035,7 +4095,7 @@ describe('vim.keymap', function() feed('asdf\n') eq(1, exec_lua [[return GlobalCount]]) - eq('\nNo mapping found', helpers.exec_capture('nmap asdf')) + eq('\nNo mapping found', n.exec_capture('nmap asdf')) end) it('does not mutate the opts parameter', function() diff --git a/test/functional/lua/watch_spec.lua b/test/functional/lua/watch_spec.lua index 115fee8091..bd8faadf5b 100644 --- a/test/functional/lua/watch_spec.lua +++ b/test/functional/lua/watch_spec.lua @@ -1,15 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local clear = helpers.clear -local is_ci = helpers.is_ci -local is_os = helpers.is_os -local skip = helpers.skip +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local exec_lua = n.exec_lua +local clear = n.clear +local is_ci = t.is_ci +local is_os = t.is_os +local skip = t.skip -- Create a file via a rename to avoid multiple -- events which can happen with some backends on some platforms local function touch(path) - local tmp = helpers.tmpname() + local tmp = t.tmpname() io.open(tmp, 'w'):close() assert(vim.uv.fs_rename(tmp, path)) end @@ -22,15 +24,13 @@ describe('vim._watch', function() local function run(watchfunc) it('detects file changes (watchfunc=' .. watchfunc .. '())', function() if watchfunc == 'fswatch' then - skip(is_os('mac'), 'flaky test on mac') - skip( - not is_ci() and helpers.fn.executable('fswatch') == 0, - 'fswatch not installed and not on CI' - ) skip(is_os('win'), 'not supported on windows') + skip(is_os('mac'), 'flaky test on mac') + skip(not is_ci() and n.fn.executable('fswatch') == 0, 'fswatch not installed and not on CI') end if watchfunc == 'watch' then + skip(is_os('mac'), 'flaky test on mac') skip(is_os('bsd'), 'Stopped working on bsd after 3ca967387c49c754561c3b11a574797504d40f38') else skip( @@ -39,7 +39,7 @@ describe('vim._watch', function() ) end - local root_dir = vim.uv.fs_mkdtemp(vim.fs.dirname(helpers.tmpname()) .. '/nvim_XXXXXXXXXX') + local root_dir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname()) .. '/nvim_XXXXXXXXXX') local expected_events = 0 diff --git a/test/functional/lua/xdiff_spec.lua b/test/functional/lua/xdiff_spec.lua index c21309c2e4..d5589c1f13 100644 --- a/test/functional/lua/xdiff_spec.lua +++ b/test/functional/lua/xdiff_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec_lua = n.exec_lua +local eq = t.eq +local pcall_err = t.pcall_err describe('xdiff bindings', function() before_each(function() diff --git a/test/functional/options/autochdir_spec.lua b/test/functional/options/autochdir_spec.lua index 11f71912a9..c490ab67a9 100644 --- a/test/functional/options/autochdir_spec.lua +++ b/test/functional/options/autochdir_spec.lua @@ -1,9 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local fn = helpers.fn -local command = helpers.command -local mkdir = helpers.mkdir +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local fn = n.fn +local command = n.command +local mkdir = t.mkdir describe("'autochdir'", function() it('given on the shell gets processed properly', function() @@ -16,7 +18,7 @@ describe("'autochdir'", function() -- With 'autochdir' on, we should get the directory of tty-test.c. clear('--cmd', 'set autochdir', targetdir .. '/tty-test.c') - eq(helpers.is_os('win') and expected:gsub('/', '\\') or expected, fn.getcwd()) + eq(t.is_os('win') and expected:gsub('/', '\\') or expected, fn.getcwd()) end) it('is not overwritten by getwinvar() call #17609', function() @@ -38,7 +40,7 @@ describe("'autochdir'", function() eq(dir_a, fn.getcwd()) fn.getwinvar(2, 'foo') eq(dir_a, fn.getcwd()) - helpers.rmdir(dir_a) - helpers.rmdir(dir_b) + n.rmdir(dir_a) + n.rmdir(dir_b) end) end) diff --git a/test/functional/options/chars_spec.lua b/test/functional/options/chars_spec.lua index e9c20b5da9..8e63e07e09 100644 --- a/test/functional/options/chars_spec.lua +++ b/test/functional/options/chars_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, command = helpers.clear, helpers.command -local pcall_err = helpers.pcall_err -local eval = helpers.eval -local eq = helpers.eq -local insert = helpers.insert -local feed = helpers.feed -local api = helpers.api + +local clear, command = n.clear, n.command +local pcall_err = t.pcall_err +local eval = n.eval +local eq = t.eq +local insert = n.insert +local feed = n.feed +local api = n.api describe("'fillchars'", function() local screen @@ -22,7 +24,7 @@ describe("'fillchars'", function() eq('', eval('&fillchars')) screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 | ]]) end) @@ -30,13 +32,14 @@ describe("'fillchars'", function() it('supports whitespace', function() screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 | ]]) command('set fillchars=eob:\\ ') screen:expect([[ ^ | - |*4 + {1: }|*3 + | ]]) end) @@ -44,7 +47,7 @@ describe("'fillchars'", function() command('set fillchars=eob:ñ') screen:expect([[ ^ | - ñ |*3 + {1:ñ }|*3 | ]]) end) @@ -53,7 +56,7 @@ describe("'fillchars'", function() command('set fillchars=eob:å̲') screen:expect([[ ^ | - å̲ |*3 + {1:å̲ }|*3 | ]]) end) @@ -112,8 +115,8 @@ describe("'fillchars'", function() command('vsplit') command('set fillchars=fold:x') screen:expect([[ - ^+-- 2 lines: fooxxxxxxxx│+-- 2 lines: fooxxxxxxx| - ~ │~ |*3 + {13:^+-- 2 lines: fooxxxxxxxx}│{13:+-- 2 lines: fooxxxxxxx}| + {1:~ }│{1:~ }|*3 | ]]) end) @@ -126,8 +129,8 @@ describe("'fillchars'", function() command('vsplit') command('setl fillchars=fold:x') screen:expect([[ - ^+-- 2 lines: fooxxxxxxxx│+-- 2 lines: foo·······| - ~ │~ |*3 + {13:^+-- 2 lines: fooxxxxxxxx}│{13:+-- 2 lines: foo·······}| + {1:~ }│{1:~ }|*3 | ]]) end) @@ -141,8 +144,8 @@ describe("'fillchars'", function() command('vsplit') command('set fillchars&') screen:expect([[ - ^+-- 2 lines: foo········│+-- 2 lines: fooxxxxxxx| - ~ │~ |*3 + {13:^+-- 2 lines: foo········}│{13:+-- 2 lines: fooxxxxxxx}| + {1:~ }│{1:~ }|*3 | ]]) end) @@ -163,8 +166,8 @@ describe("'listchars'", function() command('vsplit') command('set listchars=tab:<->') screen:expect([[ - <------><------>^<------> │<------><------><------>| - ~ │~ |*3 + {1:<------><------>^<------>} │{1:<------><------><------>}| + {1:~ }│{1:~ }|*3 | ]]) end) @@ -176,8 +179,8 @@ describe("'listchars'", function() command('vsplit') command('setl listchars<') screen:expect([[ - > > ^> │<------><------><------>| - ~ │~ |*3 + {1:> > ^> } │{1:<------><------><------>}| + {1:~ }│{1:~ }|*3 | ]]) end) @@ -189,8 +192,8 @@ describe("'listchars'", function() command('vsplit') command('set listchars=tab:>-,eol:$') screen:expect([[ - >------->-------^>-------$│<------><------><------>| - ~ │~ |*3 + {1:>------->-------^>-------$}│{1:<------><------><------>}| + {1:~ }│{1:~ }|*3 | ]]) end) diff --git a/test/functional/options/cursorbind_spec.lua b/test/functional/options/cursorbind_spec.lua index cafdc83de2..19551b928f 100644 --- a/test/functional/options/cursorbind_spec.lua +++ b/test/functional/options/cursorbind_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local exec = helpers.exec -local feed = helpers.feed + +local clear = n.clear +local command = n.command +local exec = n.exec +local feed = n.feed before_each(clear) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index d27fa375ee..f61139d92d 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -1,26 +1,27 @@ -local helpers = require('test.functional.helpers')(after_each) - +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local assert_alive = helpers.assert_alive -local assert_log = helpers.assert_log -local api = helpers.api -local command = helpers.command -local clear = helpers.clear -local exc_exec = helpers.exc_exec -local exec_lua = helpers.exec_lua -local eval = helpers.eval -local eq = helpers.eq -local ok = helpers.ok -local fn = helpers.fn -local insert = helpers.insert -local neq = helpers.neq -local mkdir = helpers.mkdir -local rmdir = helpers.rmdir -local alter_slashes = helpers.alter_slashes +local assert_alive = n.assert_alive +local assert_log = t.assert_log +local api = n.api +local command = n.command +local clear = n.clear +local exc_exec = n.exc_exec +local exec_lua = n.exec_lua +local eval = n.eval +local eq = t.eq +local ok = t.ok +local fn = n.fn +local insert = n.insert +local neq = t.neq +local mkdir = t.mkdir +local rmdir = n.rmdir +local alter_slashes = n.alter_slashes local tbl_contains = vim.tbl_contains -local expect_exit = helpers.expect_exit -local is_os = helpers.is_os +local expect_exit = n.expect_exit +local check_close = n.check_close +local is_os = t.is_os local testlog = 'Xtest-defaults-log' @@ -32,7 +33,7 @@ describe('startup defaults', function() command('filetype') screen:expect([[ ^ | - ~ |*2 + {1:~ }|*2 ]] .. expected) end @@ -132,9 +133,9 @@ describe('startup defaults', function() command('vsp') screen:expect([[ 1 │1 | - ^+-- 2 lines: 2··········│+-- 2 lines: 2·········| + {13:^+-- 2 lines: 2··········}│{13:+-- 2 lines: 2·········}| 4 │4 | - ~ │~ | + {1:~ }│{1:~ }| | ]]) @@ -142,9 +143,9 @@ describe('startup defaults', function() command('set ambiwidth=double') screen:expect([[ 1 |1 | - ^+-- 2 lines: 2----------|+-- 2 lines: 2---------| + {13:^+-- 2 lines: 2----------}|{13:+-- 2 lines: 2---------}| 4 |4 | - ~ |~ | + {1:~ }|{1:~ }| | ]]) @@ -152,9 +153,9 @@ describe('startup defaults', function() fn.setcellwidths({ { 0x2502, 0x2502, 1 } }) screen:expect([[ 1 │1 | - ^+-- 2 lines: 2----------│+-- 2 lines: 2---------| + {13:^+-- 2 lines: 2----------}│{13:+-- 2 lines: 2---------}| 4 │4 | - ~ │~ | + {1:~ }│{1:~ }| | ]]) @@ -162,9 +163,9 @@ describe('startup defaults', function() fn.setcellwidths({ { 0x2502, 0x2502, 2 } }) screen:expect([[ 1 |1 | - ^+-- 2 lines: 2----------|+-- 2 lines: 2---------| + {13:^+-- 2 lines: 2----------}|{13:+-- 2 lines: 2---------}| 4 |4 | - ~ |~ | + {1:~ }|{1:~ }| | ]]) @@ -172,9 +173,9 @@ describe('startup defaults', function() command('set ambiwidth=single') screen:expect([[ 1 |1 | - ^+-- 2 lines: 2··········|+-- 2 lines: 2·········| + {13:^+-- 2 lines: 2··········}|{13:+-- 2 lines: 2·········}| 4 |4 | - ~ |~ | + {1:~ }|{1:~ }| | ]]) end) @@ -274,6 +275,7 @@ describe('XDG defaults', function() -- Do not put before_each() here for the same reasons. after_each(function() + check_close() os.remove(testlog) end) @@ -378,7 +380,7 @@ describe('XDG defaults', function() .. root_path .. ('/b'):rep(2048) .. '/nvim' - .. (',' .. root_path .. '/c/nvim'):rep(512) + .. (',' .. root_path .. '/c/nvim') .. ',' .. root_path .. ('/X'):rep(4096) @@ -393,12 +395,12 @@ describe('XDG defaults', function() .. root_path .. ('/B'):rep(2048) .. '/nvim/site' - .. (',' .. root_path .. '/C/nvim/site'):rep(512) + .. (',' .. root_path .. '/C/nvim/site') .. ',' .. vimruntime .. ',' .. libdir - .. (',' .. root_path .. '/C/nvim/site/after'):rep(512) + .. (',' .. root_path .. '/C/nvim/site/after') .. ',' .. root_path .. ('/B'):rep(2048) @@ -413,7 +415,7 @@ describe('XDG defaults', function() .. '/' .. data_dir .. '/site/after' - .. (',' .. root_path .. '/c/nvim/after'):rep(512) + .. (',' .. root_path .. '/c/nvim/after') .. ',' .. root_path .. ('/b'):rep(2048) @@ -449,7 +451,7 @@ describe('XDG defaults', function() .. root_path .. ('/b'):rep(2048) .. '/nvim' - .. (',' .. root_path .. '/c/nvim'):rep(512) + .. (',' .. root_path .. '/c/nvim') .. ',' .. root_path .. ('/X'):rep(4096) @@ -464,12 +466,12 @@ describe('XDG defaults', function() .. root_path .. ('/B'):rep(2048) .. '/nvim/site' - .. (',' .. root_path .. '/C/nvim/site'):rep(512) + .. (',' .. root_path .. '/C/nvim/site') .. ',' .. vimruntime .. ',' .. libdir - .. (',' .. root_path .. '/C/nvim/site/after'):rep(512) + .. (',' .. root_path .. '/C/nvim/site/after') .. ',' .. root_path .. ('/B'):rep(2048) @@ -484,7 +486,7 @@ describe('XDG defaults', function() .. '/' .. data_dir .. '/site/after' - .. (',' .. root_path .. '/c/nvim/after'):rep(512) + .. (',' .. root_path .. '/c/nvim/after') .. ',' .. root_path .. ('/b'):rep(2048) @@ -865,6 +867,11 @@ describe('XDG defaults', function() end) describe('stdpath()', function() + after_each(function() + check_close() + os.remove(testlog) + end) + -- Windows appends 'nvim-data' instead of just 'nvim' to prevent collisions -- due to XDG_CONFIG_HOME, XDG_DATA_HOME and XDG_STATE_HOME being the same. local function maybe_data(name) @@ -890,7 +897,7 @@ describe('stdpath()', function() it('reacts to $NVIM_APPNAME', function() local appname = 'NVIM_APPNAME_TEST' .. ('_'):rep(106) - clear({ env = { NVIM_APPNAME = appname } }) + clear({ env = { NVIM_APPNAME = appname, NVIM_LOG_FILE = testlog } }) eq(appname, fn.fnamemodify(fn.stdpath('config'), ':t')) eq(appname, fn.fnamemodify(fn.stdpath('cache'), ':t')) eq(maybe_data(appname), fn.fnamemodify(fn.stdpath('log'), ':t')) @@ -928,6 +935,9 @@ describe('stdpath()', function() -- Valid appnames: test_appname('a/b', 0) test_appname('a/b\\c', 0) + if not is_os('win') then + assert_log('Failed to start server: no such file or directory:', testlog) + end end) describe('returns a String', function() @@ -1220,6 +1230,8 @@ describe('stdpath()', function() end) describe('errors', function() + before_each(clear) + it('on unknown strings', function() eq('Vim(call):E6100: "capybara" is not a valid stdpath', exc_exec('call stdpath("capybara")')) eq('Vim(call):E6100: "" is not a valid stdpath', exc_exec('call stdpath("")')) @@ -1235,7 +1247,8 @@ end) describe('autocommands', function() it('closes terminal with default shell on success', function() - api.nvim_set_option_value('shell', helpers.testprg('shell-test'), {}) + clear() + api.nvim_set_option_value('shell', n.testprg('shell-test'), {}) command('set shellcmdflag=EXIT shellredir= shellpipe= shellquote= shellxquote=') -- Should not block other events @@ -1243,11 +1256,11 @@ describe('autocommands', function() command('au BufEnter * let g:n = g:n + 1') command('terminal') - eq(eval('get(g:, "n", 0)'), 1) + eq(1, eval('get(g:, "n", 0)')) - helpers.retry(nil, 1000, function() - neq(api.nvim_get_option_value('buftype', { buf = 0 }), 'terminal') - eq(eval('get(g:, "n", 0)'), 2) + t.retry(nil, 1000, function() + neq('terminal', api.nvim_get_option_value('buftype', { buf = 0 })) + eq(2, eval('get(g:, "n", 0)')) end) end) end) diff --git a/test/functional/options/keymap_spec.lua b/test/functional/options/keymap_spec.lua index 7be58888bc..f76525699f 100644 --- a/test/functional/options/keymap_spec.lua +++ b/test/functional/options/keymap_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, feed, eq = helpers.clear, helpers.feed, helpers.eq -local expect, command, eval = helpers.expect, helpers.command, helpers.eval -local insert, call = helpers.insert, helpers.call -local exec_capture, dedent = helpers.exec_capture, helpers.dedent +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, feed, eq = n.clear, n.feed, t.eq +local expect, command, eval = n.expect, n.command, n.eval +local insert, call = n.insert, n.call +local exec_capture, dedent = n.exec_capture, t.dedent -- First test it's implemented using the :lmap and :lnoremap commands, then -- check those mappings behave as expected. diff --git a/test/functional/options/modified_spec.lua b/test/functional/options/modified_spec.lua new file mode 100644 index 0000000000..956deb70ff --- /dev/null +++ b/test/functional/options/modified_spec.lua @@ -0,0 +1,20 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local api = n.api + +describe("'modified'", function() + before_each(function() + clear() + end) + + it("can be unset after changing 'fileformat'", function() + for _, ff in ipairs({ 'unix', 'dos', 'mac' }) do + api.nvim_set_option_value('fileformat', ff, {}) + api.nvim_set_option_value('modified', false, {}) + eq(false, api.nvim_get_option_value('modified', {}), 'fileformat=' .. ff) + end + end) +end) diff --git a/test/functional/options/mousescroll_spec.lua b/test/functional/options/mousescroll_spec.lua index 96af8987b8..2ccf934fd4 100644 --- a/test/functional/options/mousescroll_spec.lua +++ b/test/functional/options/mousescroll_spec.lua @@ -1,21 +1,23 @@ -local helpers = require('test.functional.helpers')(after_each) -local command = helpers.command -local clear = helpers.clear -local eval = helpers.eval -local eq = helpers.eq -local exc_exec = helpers.exc_exec -local feed = helpers.feed +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local command = n.command +local clear = n.clear +local eval = n.eval +local eq = t.eq +local exc_exec = n.exc_exec +local feed = n.feed local scroll = function(direction) - return helpers.request('nvim_input_mouse', 'wheel', direction, '', 0, 2, 2) + return n.request('nvim_input_mouse', 'wheel', direction, '', 0, 2, 2) end local screenrow = function() - return helpers.call('screenrow') + return n.call('screenrow') end local screencol = function() - return helpers.call('screencol') + return n.call('screencol') end describe("'mousescroll'", function() diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua index 0614bcf814..3ac6f3f121 100644 --- a/test/functional/options/num_options_spec.lua +++ b/test/functional/options/num_options_spec.lua @@ -1,8 +1,9 @@ -- Tests for :setlocal and :setglobal -local helpers = require('test.functional.helpers')(after_each) -local clear, feed_command, eval, eq, api = - helpers.clear, helpers.feed_command, helpers.eval, helpers.eq, helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, feed_command, eval, eq, api = n.clear, n.feed_command, n.eval, t.eq, n.api local function should_fail(opt, value, errmsg) feed_command('setglobal ' .. opt .. '=' .. value) @@ -12,7 +13,7 @@ local function should_fail(opt, value, errmsg) eq(errmsg, eval('v:errmsg'):match('E%d*')) feed_command('let v:errmsg = ""') local status, err = pcall(api.nvim_set_option_value, opt, value, {}) - eq(status, false) + eq(false, status) eq(errmsg, err:match('E%d*')) eq('', eval('v:errmsg')) end diff --git a/test/functional/options/shortmess_spec.lua b/test/functional/options/shortmess_spec.lua index 6bc00ca1c5..dcbf9d15e0 100644 --- a/test/functional/options/shortmess_spec.lua +++ b/test/functional/options/shortmess_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed + +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local feed = n.feed describe("'shortmess'", function() local screen @@ -22,7 +24,7 @@ describe("'shortmess'", function() feed(':edit foo<CR>') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 "foo" [New] | ]]) eq(1, eval('bufnr("%")')) @@ -31,7 +33,7 @@ describe("'shortmess'", function() feed(':edit bar<CR>') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 :edit bar | ]]) eq(2, eval('bufnr("%")')) @@ -43,21 +45,21 @@ describe("'shortmess'", function() feed(':edit foo<CR>') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 "foo" [New] | ]]) eq(1, eval('bufnr("%")')) feed(':edit bar<CR>') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 "bar" [New] | ]]) eq(2, eval('bufnr("%")')) feed(':bprevious<CR>') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 "foo" [New] --No lines in buffer-- | ]]) eq(1, eval('bufnr("%")')) @@ -66,14 +68,14 @@ describe("'shortmess'", function() feed(':bnext<CR>') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 :bnext | ]]) eq(2, eval('bufnr("%")')) feed(':bprevious<CR>') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 :bprevious | ]]) eq(1, eval('bufnr("%")')) diff --git a/test/functional/options/tabstop_spec.lua b/test/functional/options/tabstop_spec.lua index 9070db8257..4ddbb13a0e 100644 --- a/test/functional/options/tabstop_spec.lua +++ b/test/functional/options/tabstop_spec.lua @@ -1,8 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() -local assert_alive = helpers.assert_alive -local clear = helpers.clear -local feed = helpers.feed +local assert_alive = n.assert_alive +local clear = n.clear +local feed = n.feed describe("'tabstop' option", function() before_each(function() diff --git a/test/functional/options/winfixbuf_spec.lua b/test/functional/options/winfixbuf_spec.lua new file mode 100644 index 0000000000..124f194b5a --- /dev/null +++ b/test/functional/options/winfixbuf_spec.lua @@ -0,0 +1,55 @@ +local n = require('test.functional.testnvim')() + +local clear = n.clear +local exec_lua = n.exec_lua + +describe("Nvim API calls with 'winfixbuf'", function() + before_each(function() + clear() + end) + + it("Calling vim.api.nvim_win_set_buf with 'winfixbuf'", function() + local results = exec_lua([[ + local function _setup_two_buffers() + local buffer = vim.api.nvim_create_buf(true, true) + + vim.api.nvim_create_buf(true, true) -- Make another buffer + + local current_window = 0 + vim.api.nvim_set_option_value("winfixbuf", true, {win=current_window}) + + return buffer + end + + local other_buffer = _setup_two_buffers() + local current_window = 0 + local results, _ = pcall(vim.api.nvim_win_set_buf, current_window, other_buffer) + + return results + ]]) + + assert(results == false) + end) + + it("Calling vim.api.nvim_set_current_buf with 'winfixbuf'", function() + local results = exec_lua([[ + local function _setup_two_buffers() + local buffer = vim.api.nvim_create_buf(true, true) + + vim.api.nvim_create_buf(true, true) -- Make another buffer + + local current_window = 0 + vim.api.nvim_set_option_value("winfixbuf", true, {win=current_window}) + + return buffer + end + + local other_buffer = _setup_two_buffers() + local results, _ = pcall(vim.api.nvim_set_current_buf, other_buffer) + + return results + ]]) + + assert(results == false) + end) +end) diff --git a/test/functional/plugin/ccomplete_spec.lua b/test/functional/plugin/ccomplete_spec.lua index 903f16fc73..4c4c5e9444 100644 --- a/test/functional/plugin/ccomplete_spec.lua +++ b/test/functional/plugin/ccomplete_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local write_file = helpers.write_file +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local feed = n.feed +local write_file = t.write_file describe('ccomplete#Complete', function() setup(function() diff --git a/test/functional/plugin/cfilter_spec.lua b/test/functional/plugin/cfilter_spec.lua index 37261d59df..220404a934 100644 --- a/test/functional/plugin/cfilter_spec.lua +++ b/test/functional/plugin/cfilter_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local fn = helpers.fn +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local fn = n.fn describe('cfilter.lua', function() before_each(function() diff --git a/test/functional/plugin/editorconfig_spec.lua b/test/functional/plugin/editorconfig_spec.lua index 115c28fbf6..839a723405 100644 --- a/test/functional/plugin/editorconfig_spec.lua +++ b/test/functional/plugin/editorconfig_spec.lua @@ -1,14 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local pathsep = helpers.get_pathsep() -local fn = helpers.fn -local api = helpers.api -local exec_lua = helpers.exec_lua +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local command = n.command +local eq = t.eq +local pathsep = n.get_pathsep() +local fn = n.fn +local api = n.api +local exec_lua = n.exec_lua local testdir = 'Xtest-editorconfig' +--- @param name string +--- @param expected table<string,any> local function test_case(name, expected) local filename = testdir .. pathsep .. name command('edit ' .. filename) @@ -18,8 +22,8 @@ local function test_case(name, expected) end setup(function() - helpers.mkdir_p(testdir) - helpers.write_file( + n.mkdir_p(testdir) + t.write_file( testdir .. pathsep .. '.editorconfig', [[ root = true @@ -94,7 +98,7 @@ setup(function() end) teardown(function() - helpers.rmdir(testdir) + n.rmdir(testdir) end) describe('editorconfig', function() @@ -176,18 +180,18 @@ But not this one -- luacheck: pop local trimmed = untrimmed:gsub('%s+\n', '\n') - helpers.write_file(filename, untrimmed) + t.write_file(filename, untrimmed) command('edit ' .. filename) command('write') command('bdelete') - eq(trimmed, helpers.read_file(filename)) + eq(trimmed, t.read_file(filename)) filename = testdir .. pathsep .. 'no_trim.txt' - helpers.write_file(filename, untrimmed) + t.write_file(filename, untrimmed) command('edit ' .. filename) command('write') command('bdelete') - eq(untrimmed, helpers.read_file(filename)) + eq(untrimmed, t.read_file(filename)) end) it('sets textwidth', function() diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua index 8564ec7c9b..9c7c953fb0 100644 --- a/test/functional/plugin/health_spec.lua +++ b/test/functional/plugin/health_spec.lua @@ -1,14 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local curbuf_contents = helpers.curbuf_contents -local command = helpers.command -local eq, neq, matches = helpers.eq, helpers.neq, helpers.matches -local getcompletion = helpers.fn.getcompletion -local insert = helpers.insert -local source = helpers.source -local exec_lua = helpers.exec_lua +local clear = n.clear +local curbuf_contents = n.curbuf_contents +local command = n.command +local eq, matches = t.eq, t.matches +local getcompletion = n.fn.getcompletion +local insert = n.insert +local exec_lua = n.exec_lua +local source = n.source +local assert_alive = n.assert_alive +local fn = n.fn +local api = n.api describe(':checkhealth', function() it('detects invalid $VIMRUNTIME', function() @@ -19,6 +23,7 @@ describe(':checkhealth', function() eq(false, status) eq('Invalid $VIMRUNTIME: bogus', string.match(err, 'Invalid.*')) end) + it("detects invalid 'runtimepath'", function() clear() command('set runtimepath=bogus') @@ -26,19 +31,29 @@ describe(':checkhealth', function() eq(false, status) eq("Invalid 'runtimepath'", string.match(err, 'Invalid.*')) end) + it('detects invalid $VIM', function() clear() -- Do this after startup, otherwise it just breaks $VIMRUNTIME. command("let $VIM='zub'") - command('checkhealth nvim') + command('checkhealth vim.health') matches('ERROR $VIM .* zub', curbuf_contents()) end) + it('completions can be listed via getcompletion()', function() clear() - eq('nvim', getcompletion('nvim', 'checkhealth')[1]) - eq('provider.clipboard', getcompletion('prov', 'checkhealth')[1]) + eq('vim.deprecated', getcompletion('vim', 'checkhealth')[1]) + eq('vim.provider', getcompletion('vim.prov', 'checkhealth')[1]) eq('vim.lsp', getcompletion('vim.ls', 'checkhealth')[1]) - neq('vim', getcompletion('^vim', 'checkhealth')[1]) -- should not complete vim.health + end) + + it('completion checks for vim.health._complete() return type #28456', function() + clear() + exec_lua([[vim.health._complete = function() return 1 end]]) + eq({}, getcompletion('', 'checkhealth')) + exec_lua([[vim.health._complete = function() return { 1 } end]]) + eq({}, getcompletion('', 'checkhealth')) + assert_alive() end) end) @@ -52,7 +67,7 @@ describe('health.vim', function() describe(':checkhealth', function() it('functions report_*() render correctly', function() command('checkhealth full_render') - helpers.expect([[ + n.expect([[ ============================================================================== test_plug.full_render: require("test_plug.full_render.health").check() @@ -75,7 +90,7 @@ describe('health.vim', function() it('concatenates multiple reports', function() command('checkhealth success1 success2 test_plug') - helpers.expect([[ + n.expect([[ ============================================================================== test_plug: require("test_plug.health").check() @@ -105,7 +120,7 @@ describe('health.vim', function() it('lua plugins submodules', function() command('checkhealth test_plug.submodule') - helpers.expect([[ + n.expect([[ ============================================================================== test_plug.submodule: require("test_plug.submodule.health").check() @@ -120,7 +135,7 @@ describe('health.vim', function() it('... including empty reports', function() command('checkhealth test_plug.submodule_empty') - helpers.expect([[ + n.expect([[ ============================================================================== test_plug.submodule_empty: require("test_plug.submodule_empty.health").check() @@ -158,28 +173,10 @@ describe('health.vim', function() } end) - it('fold healthchecks', function() - local screen = Screen.new(50, 7) - screen:attach() - command('checkhealth foo success1') - command('set nowrap laststatus=0') - screen:expect { - grid = [[ - ^ | - ──────────────────────────────────────────────────| - +WE 4 lines: foo: ·······························| - ──────────────────────────────────────────────────| - +-- 8 lines: test_plug.success1: require("test_pl| - ~ | - | - ]], - } - end) - it('gracefully handles invalid healthcheck', function() command('checkhealth non_existent_healthcheck') -- luacheck: ignore 613 - helpers.expect([[ + n.expect([[ ============================================================================== non_existent_healthcheck: @@ -191,7 +188,7 @@ describe('health.vim', function() it('does not use vim.health as a healtcheck', function() -- vim.health is not a healthcheck command('checkhealth vim') - helpers.expect([[ + n.expect([[ ERROR: No healthchecks found.]]) end) end) @@ -218,6 +215,12 @@ describe(':checkhealth window', function() it('opens directly if no buffer created', function() local screen = Screen.new(50, 12) + screen:set_default_attr_ids { + [1] = { foreground = Screen.colors.Blue, bold = true }, + [14] = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray }, + [31] = { foreground = tonumber('0x6a0dad') }, + [32] = { foreground = Screen.colors.PaleGreen2 }, + } screen:attach({ ext_multigrid = true }) command('checkhealth success1') screen:expect { @@ -227,16 +230,16 @@ describe(':checkhealth window', function() [3:--------------------------------------------------]| ## grid 2 ^ | - ──────────────────────────────────────────────────| - ──────────────────────────── | - test_plug.success1: require("test_plug.success1. | - health").check() | + {14:──────────────────────────────────────────────────}| + {14:────────────────────────────} | + {31:test_plug.success1: require("test_plug.success1. }| + {31:health").check()} | | - report 1 | - - OK everything is fine | + {31:report 1} | + - {32:OK} everything is fine | | - report 2 | - - OK nothing to see here | + {31:report 2} | + - {32:OK} nothing to see here | ## grid 3 | ]], @@ -245,6 +248,12 @@ describe(':checkhealth window', function() local function test_health_vsplit(left, emptybuf, mods) local screen = Screen.new(50, 20) + screen:set_default_attr_ids { + [1] = { foreground = Screen.colors.Blue, bold = true }, + [14] = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray }, + [31] = { foreground = tonumber('0x6a0dad') }, + [32] = { foreground = Screen.colors.PaleGreen2 }, + } screen:attach({ ext_multigrid = true }) if not emptybuf then insert('hello') @@ -257,24 +266,24 @@ describe(':checkhealth window', function() [3:--------------------------------------------------]| ## grid 2 %s | - ~ |*18 + {1:~ }|*18 ## grid 3 | ## grid 4 ^ | - ─────────────────────────|*3 - ─── | - test_plug.success1: | - require("test_plug. | - success1.health").check()| + {14:─────────────────────────}|*3 + {14:───} | + {31:test_plug.success1: }| + {31:require("test_plug. }| + {31:success1.health").check()}| | - report 1 | - - OK everything is fine | + {31:report 1} | + - {32:OK} everything is fine | | - report 2 | - - OK nothing to see here | + {31:report 2} | + - {32:OK} nothing to see here | | - ~ |*4 + {1:~ }|*4 ]]):format( left and '[4:-------------------------]│[2:------------------------]|*19' or '[2:------------------------]│[4:-------------------------]|*19', @@ -304,6 +313,7 @@ describe(':checkhealth window', function() local function test_health_split(top, emptybuf, mods) local screen = Screen.new(50, 25) screen:attach({ ext_multigrid = true }) + screen._default_attr_ids = nil if not emptybuf then insert('hello') end @@ -368,7 +378,7 @@ describe(':checkhealth window', function() it('opens in tab', function() -- create an empty buffer called "my_buff" - exec_lua 'vim.api.nvim_create_buf(false, true)' + api.nvim_create_buf(false, true) command('file my_buff') command('checkhealth success1') -- define a function that collects all buffers in each tab @@ -387,7 +397,7 @@ describe(':checkhealth window', function() return buffs endfunction ]]) - local buffers_per_tab = exec_lua('return vim.fn.CollectBuffersPerTab()') + local buffers_per_tab = fn.CollectBuffersPerTab() eq(buffers_per_tab, { tab1 = { 'my_buff' }, tab2 = { 'health://' } }) end) end) diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua index 29daf7a066..cd20e95dd1 100644 --- a/test/functional/plugin/lsp/codelens_spec.lua +++ b/test/functional/plugin/lsp/codelens_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local exec_lua = helpers.exec_lua -local eq = helpers.eq +local exec_lua = n.exec_lua +local eq = t.eq describe('vim.lsp.codelens', function() before_each(function() - helpers.clear() + n.clear() exec_lua('require("vim.lsp")') end) - after_each(helpers.clear) + after_each(n.clear) it('on_codelens_stores_and_displays_lenses', function() local fake_uri = 'file:///fake/uri' diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua index 655eb76be6..2798d57381 100644 --- a/test/functional/plugin/lsp/completion_spec.lua +++ b/test/functional/plugin/lsp/completion_spec.lua @@ -1,7 +1,9 @@ ---@diagnostic disable: no-unknown -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local exec_lua = helpers.exec_lua +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local exec_lua = n.exec_lua --- Convert completion results. --- @@ -41,7 +43,7 @@ local function complete(line, candidates, lnum) end describe('vim.lsp._completion', function() - before_each(helpers.clear) + before_each(n.clear) -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion it('prefers textEdit over label as word', function() diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua index 705c182df7..c5e14ffdc2 100644 --- a/test/functional/plugin/lsp/diagnostic_spec.lua +++ b/test/functional/plugin/lsp/diagnostic_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) -local lsp_helpers = require('test.functional.plugin.lsp.helpers') +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local neq = require('test.helpers').neq +local t_lsp = require('test.functional.plugin.lsp.testutil') -local create_server_definition = lsp_helpers.create_server_definition +local clear = n.clear +local exec_lua = n.exec_lua +local eq = t.eq +local neq = t.neq + +local create_server_definition = t_lsp.create_server_definition describe('vim.lsp.diagnostic', function() local fake_uri @@ -257,7 +259,7 @@ describe('vim.lsp.diagnostic', function() }, {client_id=client_id}) return vim.fn.bufnr(vim.uri_to_fname("file:///fake/uri2")) ]] - eq(bufnr, -1) + eq(-1, bufnr) -- Create buffer on diagnostics bufnr = exec_lua [[ @@ -269,8 +271,8 @@ describe('vim.lsp.diagnostic', function() }, {client_id=client_id}) return vim.fn.bufnr(vim.uri_to_fname("file:///fake/uri2")) ]] - neq(bufnr, -1) - eq(exec_lua([[return #vim.diagnostic.get(...)]], bufnr), 1) + neq(-1, bufnr) + eq(1, exec_lua([[return #vim.diagnostic.get(...)]], bufnr)) -- Clear diagnostics after buffer was created bufnr = exec_lua [[ @@ -280,8 +282,8 @@ describe('vim.lsp.diagnostic', function() }, {client_id=client_id}) return vim.fn.bufnr(vim.uri_to_fname("file:///fake/uri2")) ]] - neq(bufnr, -1) - eq(exec_lua([[return #vim.diagnostic.get(...)]], bufnr), 0) + neq(-1, bufnr) + eq(0, exec_lua([[return #vim.diagnostic.get(...)]], bufnr)) end) end) diff --git a/test/functional/plugin/lsp/handler_spec.lua b/test/functional/plugin/lsp/handler_spec.lua index 56e29e7337..013a5fb5e7 100644 --- a/test/functional/plugin/lsp/handler_spec.lua +++ b/test/functional/plugin/lsp/handler_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local pcall_err = helpers.pcall_err -local matches = helpers.matches +local eq = t.eq +local exec_lua = n.exec_lua +local pcall_err = t.pcall_err +local matches = t.matches describe('lsp-handlers', function() describe('vim.lsp._with_extend', function() diff --git a/test/functional/plugin/lsp/incremental_sync_spec.lua b/test/functional/plugin/lsp/incremental_sync_spec.lua index bd1842ceb5..238b90b57d 100644 --- a/test/functional/plugin/lsp/incremental_sync_spec.lua +++ b/test/functional/plugin/lsp/incremental_sync_spec.lua @@ -1,11 +1,12 @@ -- Test suite for testing interactions with the incremental sync algorithms powering the LSP client -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local api = helpers.api -local clear = helpers.clear -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local feed = helpers.feed +local api = n.api +local clear = n.clear +local eq = t.eq +local exec_lua = n.exec_lua +local feed = n.feed before_each(function() clear() diff --git a/test/functional/plugin/lsp/inlay_hint_spec.lua b/test/functional/plugin/lsp/inlay_hint_spec.lua index 192797b312..d3b5ae0e4e 100644 --- a/test/functional/plugin/lsp/inlay_hint_spec.lua +++ b/test/functional/plugin/lsp/inlay_hint_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) -local lsp_helpers = require('test.functional.plugin.lsp.helpers') +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local t_lsp = require('test.functional.plugin.lsp.testutil') -local eq = helpers.eq -local dedent = helpers.dedent -local exec_lua = helpers.exec_lua -local insert = helpers.insert +local eq = t.eq +local dedent = t.dedent +local exec_lua = n.exec_lua +local insert = n.insert +local api = n.api -local clear_notrace = lsp_helpers.clear_notrace -local create_server_definition = lsp_helpers.create_server_definition +local clear_notrace = t_lsp.clear_notrace +local create_server_definition = t_lsp.create_server_definition local text = dedent([[ auto add(int a, int b) { return a + b; } @@ -41,12 +43,12 @@ local grid_without_inlay_hints = [[ ]] local grid_with_inlay_hints = [[ - auto add(int a, int b)-> int { return a + b; } | + auto add(int a, int b){1:-> int} { return a + b; } | | int main() { | int x = 1; | int y = 2; | - return add(a: x,b: y); | + return add({1:a:} x,{1:b:} y); | } | ^} | | @@ -68,8 +70,8 @@ before_each(function() inlayHintProvider = true, }, handlers = { - ['textDocument/inlayHint'] = function() - return vim.json.decode(response) + ['textDocument/inlayHint'] = function(_, _, callback) + callback(nil, vim.json.decode(response)) end, } }) @@ -83,12 +85,12 @@ before_each(function() ) insert(text) - exec_lua([[vim.lsp.inlay_hint.enable(bufnr)]]) + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) screen:expect({ grid = grid_with_inlay_hints }) end) after_each(function() - exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") + api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) end) describe('vim.lsp.inlay_hint', function() @@ -104,13 +106,13 @@ describe('vim.lsp.inlay_hint', function() inlayHintProvider = true, }, handlers = { - ['textDocument/inlayHint'] = function() - return {} + ['textDocument/inlayHint'] = function(_, _, callback) + callback(nil, {}) end, } }) client2 = vim.lsp.start({ name = 'dummy2', cmd = server2.cmd }) - vim.lsp.inlay_hint.enable(bufnr) + vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) ]]) exec_lua([[ vim.lsp.stop_client(client2) ]]) @@ -118,18 +120,66 @@ describe('vim.lsp.inlay_hint', function() end) describe('enable()', function() - it('clears/applies inlay hints when passed false/true/nil', function() - exec_lua([[vim.lsp.inlay_hint.enable(bufnr, false)]]) - screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + it('validation', function() + t.matches( + 'enable: expected boolean, got table', + t.pcall_err(exec_lua, [[vim.lsp.inlay_hint.enable({}, { bufnr = bufnr })]]) + ) + t.matches( + 'enable: expected boolean, got number', + t.pcall_err(exec_lua, [[vim.lsp.inlay_hint.enable(42)]]) + ) + t.matches( + 'filter: expected table, got number', + t.pcall_err(exec_lua, [[vim.lsp.inlay_hint.enable(true, 42)]]) + ) + end) + + describe('clears/applies inlay hints when passed false/true/nil', function() + before_each(function() + exec_lua([[ + bufnr2 = vim.api.nvim_create_buf(true, false) + vim.lsp.buf_attach_client(bufnr2, client_id) + vim.api.nvim_win_set_buf(0, bufnr2) + ]]) + insert(text) + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr2 })]]) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]]) + screen:expect({ grid = grid_with_inlay_hints }) + end) + + it('for one single buffer', function() + exec_lua([[ + vim.lsp.inlay_hint.enable(false, { bufnr = bufnr }) + vim.api.nvim_win_set_buf(0, bufnr2) + ]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]]) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) - exec_lua([[vim.lsp.inlay_hint.enable(bufnr, true)]]) - screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) - exec_lua([[vim.lsp.inlay_hint.enable(bufnr, not vim.lsp.inlay_hint.is_enabled(bufnr))]]) - screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + exec_lua( + [[vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }), { bufnr = bufnr })]] + ) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) - exec_lua([[vim.lsp.inlay_hint.enable(bufnr)]]) - screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua([[vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + end) + + it('for all buffers', function() + exec_lua([[vim.lsp.inlay_hint.enable(false)]]) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr2)]]) + screen:expect({ grid = grid_without_inlay_hints, unchanged = true }) + + exec_lua([[vim.lsp.inlay_hint.enable(true)]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + exec_lua([[vim.api.nvim_win_set_buf(0, bufnr)]]) + screen:expect({ grid = grid_with_inlay_hints, unchanged = true }) + end) end) end) @@ -156,25 +206,25 @@ describe('vim.lsp.inlay_hint', function() inlayHintProvider = true, }, handlers = { - ['textDocument/inlayHint'] = function() - return { expected2 } + ['textDocument/inlayHint'] = function(_, _, callback) + callback(nil, { expected2 }) end, } }) client2 = vim.lsp.start({ name = 'dummy2', cmd = server2.cmd }) - vim.lsp.inlay_hint.enable(bufnr) + vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) ]], expected2 ) --- @type vim.lsp.inlay_hint.get.ret local res = exec_lua([[return vim.lsp.inlay_hint.get()]]) - eq(res, { + eq({ { bufnr = 1, client_id = 1, inlay_hint = expected[1] }, { bufnr = 1, client_id = 1, inlay_hint = expected[2] }, { bufnr = 1, client_id = 1, inlay_hint = expected[3] }, { bufnr = 1, client_id = 2, inlay_hint = expected2 }, - }) + }, res) --- @type vim.lsp.inlay_hint.get.ret res = exec_lua([[return vim.lsp.inlay_hint.get({ @@ -183,9 +233,9 @@ describe('vim.lsp.inlay_hint', function() ["end"] = { line = 2, character = 10 }, }, })]]) - eq(res, { + eq({ { bufnr = 1, client_id = 2, inlay_hint = expected2 }, - }) + }, res) --- @type vim.lsp.inlay_hint.get.ret res = exec_lua([[return vim.lsp.inlay_hint.get({ @@ -195,16 +245,16 @@ describe('vim.lsp.inlay_hint', function() ["end"] = { line = 5, character = 17 }, }, })]]) - eq(res, { + eq({ { bufnr = 1, client_id = 1, inlay_hint = expected[2] }, { bufnr = 1, client_id = 1, inlay_hint = expected[3] }, - }) + }, res) --- @type vim.lsp.inlay_hint.get.ret res = exec_lua([[return vim.lsp.inlay_hint.get({ bufnr = vim.api.nvim_get_current_buf() + 1, })]]) - eq(res, {}) + eq({}, res) end) end) end) diff --git a/test/functional/plugin/lsp/semantic_tokens_spec.lua b/test/functional/plugin/lsp/semantic_tokens_spec.lua index 77e39c81c8..7908c5d2e7 100644 --- a/test/functional/plugin/lsp/semantic_tokens_spec.lua +++ b/test/functional/plugin/lsp/semantic_tokens_spec.lua @@ -1,25 +1,27 @@ -local helpers = require('test.functional.helpers')(after_each) -local lsp_helpers = require('test.functional.plugin.lsp.helpers') +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local t_lsp = require('test.functional.plugin.lsp.testutil') -local command = helpers.command -local dedent = helpers.dedent -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local feed = helpers.feed -local feed_command = helpers.feed_command -local insert = helpers.insert -local matches = helpers.matches +local command = n.command +local dedent = t.dedent +local eq = t.eq +local exec_lua = n.exec_lua +local feed = n.feed +local feed_command = n.feed_command +local insert = n.insert +local matches = t.matches +local api = n.api -local clear_notrace = lsp_helpers.clear_notrace -local create_server_definition = lsp_helpers.create_server_definition +local clear_notrace = t_lsp.clear_notrace +local create_server_definition = t_lsp.create_server_definition before_each(function() clear_notrace() end) after_each(function() - exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") + api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) end) describe('semantic token highlighting', function() @@ -37,6 +39,8 @@ describe('semantic token highlighting', function() [7] = { bold = true, foreground = Screen.colors.DarkCyan }, [8] = { bold = true, foreground = Screen.colors.SlateBlue }, [9] = { bold = true, foreground = tonumber('0x6a0dad') }, + [10] = { bold = true, foreground = Screen.colors.Brown }, + [11] = { foreground = Screen.colors.Magenta1 }, } command([[ hi link @lsp.type.namespace Type ]]) command([[ hi link @lsp.type.function Special ]]) @@ -91,11 +95,11 @@ describe('semantic token highlighting', function() }, }, handlers = { - ['textDocument/semanticTokens/full'] = function() - return vim.fn.json_decode(response) + ['textDocument/semanticTokens/full'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(response)) end, - ['textDocument/semanticTokens/full/delta'] = function() - return vim.fn.json_decode(edit_response) + ['textDocument/semanticTokens/full/delta'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(edit_response)) end, } }) @@ -269,6 +273,63 @@ describe('semantic token highlighting', function() end ) + it('highlights start and stop when using "0" for current buffer', function() + exec_lua([[ + bufnr = vim.api.nvim_get_current_buf() + vim.api.nvim_win_set_buf(0, bufnr) + client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + ]]) + + insert(text) + + exec_lua([[ + vim.notify = function() end + vim.lsp.semantic_tokens.stop(0, client_id) + ]]) + + screen:expect { + grid = [[ + #include <iostream> | + | + int main() | + { | + int x; | + #ifdef __cplusplus | + std::cout << x << "\n"; | + #else | + printf("%d\n", x); | + #endif | + } | + ^} | + {1:~ }|*3 + | + ]], + } + + exec_lua([[ + vim.lsp.semantic_tokens.start(0, client_id) + ]]) + + screen:expect { + grid = [[ + #include <iostream> | + | + int {8:main}() | + { | + int {7:x}; | + #ifdef {5:__cplusplus} | + {4:std}::{2:cout} << {2:x} << "\n"; | + {6:#else} | + {6: printf("%d\n", x);} | + {6:#endif} | + } | + ^} | + {1:~ }|*3 + | + ]], + } + end) + it('buffer is re-highlighted when force refreshed', function() exec_lua([[ bufnr = vim.api.nvim_get_current_buf() @@ -499,11 +560,11 @@ describe('semantic token highlighting', function() }, }, handlers = { - ['textDocument/semanticTokens/full'] = function() - return nil + ['textDocument/semanticTokens/full'] = function(_, _, callback) + callback(nil, nil) end, ['textDocument/semanticTokens/full/delta'] = function() - return nil + callback(nil, nil) end, } }) @@ -547,11 +608,11 @@ describe('semantic token highlighting', function() }, }, handlers = { - ['textDocument/semanticTokens/full'] = function() - return vim.fn.json_decode(response) + ['textDocument/semanticTokens/full'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(response)) end, - ['textDocument/semanticTokens/full/delta'] = function() - return vim.fn.json_decode(edit_response) + ['textDocument/semanticTokens/full/delta'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(edit_response)) end, } }) @@ -837,11 +898,13 @@ b = "as"]], { it = 'rust-analyzer', text = [[pub fn main() { - break rust; - /// what? + println!("Hello world!"); + break rust; + /// what? } ]], - response = [[{"data": [0, 0, 3, 1, 0, 0, 4, 2, 1, 0, 0, 3, 4, 14, 524290, 0, 4, 1, 45, 0, 0, 1, 1, 45, 0, 0, 2, 1, 26, 0, 1, 4, 5, 1, 8192, 0, 6, 4, 52, 0, 0, 4, 1, 48, 0, 1, 4, 9, 0, 1, 1, 0, 1, 26, 0], "resultId": "1"}]], + response = [[{"data": [0, 0, 3, 1, 0, 0, 4, 2, 1, 0, 0, 3, 4, 14, 524290, 0, 4, 1, 45, 0, 0, 1, 1, 45, 0, 0, 2, 1, 26, 0, 1, 4, 8, 17, 0, 0, 8, 1, 45, 0, 0, 1, 14, 2, 0, 0, 14, 1, 45, 0, 0, 1, 1, 48, 0, 1, 4, 5, 1, 8192, 0, 6, 4, 52, 0, 0, 4, 1, 48, 0, 1, 4, 9, 0, 1, 1, 0, 1, 26, 0 ], "resultId": "1"}]], + legend = [[{ "tokenTypes": [ "comment", "keyword", "string", "number", "regexp", "operator", "namespace", "type", "struct", "class", "interface", "enum", "enumMember", "typeParameter", "function", "method", "property", "macro", "variable", @@ -904,6 +967,46 @@ b = "as"]], }, { line = 1, + modifiers = {}, + start_col = 4, + end_col = 12, + type = 'macro', -- println! + marked = true, + }, + { + line = 1, + modifiers = {}, + start_col = 12, + end_col = 13, + type = 'parenthesis', + marked = true, + }, + { + line = 1, + modifiers = {}, + start_col = 13, + end_col = 27, + type = 'string', -- "Hello world!" + marked = true, + }, + { + line = 1, + modifiers = {}, + start_col = 27, + end_col = 28, + type = 'parenthesis', + marked = true, + }, + { + line = 1, + modifiers = {}, + start_col = 28, + end_col = 29, + type = 'semicolon', + marked = true, + }, + { + line = 2, modifiers = { controlFlow = true }, start_col = 4, end_col = 9, -- break @@ -911,31 +1014,31 @@ b = "as"]], marked = true, }, { - line = 1, + line = 2, modifiers = {}, start_col = 10, - end_col = 13, -- rust + end_col = 14, -- rust type = 'unresolvedReference', marked = true, }, { - line = 1, + line = 2, modifiers = {}, - start_col = 13, - end_col = 13, + start_col = 14, + end_col = 15, type = 'semicolon', marked = true, }, { - line = 2, + line = 3, modifiers = { documentation = true }, start_col = 4, - end_col = 11, + end_col = 13, type = 'comment', -- /// what? marked = true, }, { - line = 3, + line = 4, modifiers = {}, start_col = 0, end_col = 1, @@ -946,12 +1049,13 @@ b = "as"]], expected_screen = function() screen:expect { grid = [[ - pub fn {8:main}() { | - break rust; | - //{6:/ what?} | + {10:pub} {10:fn} {8:main}() { | + {5:println!}({11:"Hello world!"}); | + {10:break} rust; | + {6:/// what?} | } | ^ | - {1:~ }|*10 + {1:~ }|*9 | ]], } @@ -971,8 +1075,8 @@ b = "as"]], }, }, handlers = { - ['textDocument/semanticTokens/full'] = function() - return vim.fn.json_decode(resp) + ['textDocument/semanticTokens/full'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(resp)) end, } }) @@ -1357,11 +1461,11 @@ int main() }, }, handlers = { - ['textDocument/semanticTokens/full'] = function() - return vim.fn.json_decode(resp1) + ['textDocument/semanticTokens/full'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(resp1)) end, - ['textDocument/semanticTokens/full/delta'] = function() - return vim.fn.json_decode(resp2) + ['textDocument/semanticTokens/full/delta'] = function(_, _, callback) + callback(nil, vim.fn.json_decode(resp2)) end, } }) diff --git a/test/functional/plugin/lsp/snippet_spec.lua b/test/functional/plugin/lsp/snippet_spec.lua index ba8bc7fe04..e60c36cd23 100644 --- a/test/functional/plugin/lsp/snippet_spec.lua +++ b/test/functional/plugin/lsp/snippet_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + local snippet = require('vim.lsp._snippet_grammar') local type = snippet.NodeType -local eq = helpers.eq -local exec_lua = helpers.exec_lua +local eq = t.eq +local exec_lua = n.exec_lua describe('vim.lsp._snippet_grammar', function() - before_each(helpers.clear) - after_each(helpers.clear) + before_each(n.clear) + after_each(n.clear) local parse = function(...) local res = exec_lua('return require("vim.lsp._snippet_grammar").parse(...)', ...) diff --git a/test/functional/plugin/lsp/helpers.lua b/test/functional/plugin/lsp/testutil.lua index 97fa108500..3430a1e1a3 100644 --- a/test/functional/plugin/lsp/helpers.lua +++ b/test/functional/plugin/lsp/testutil.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(nil) +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local run = helpers.run -local stop = helpers.stop +local clear = n.clear +local exec_lua = n.exec_lua +local run = n.run +local stop = n.stop +local api = n.api local NIL = vim.NIL local M = {} @@ -38,8 +39,7 @@ M.create_server_definition = [[ }) local handler = handlers[method] if handler then - local response, err = handler(method, params) - callback(err, response) + handler(method, params, callback) elseif method == 'initialize' then callback(nil, { capabilities = opts.capabilities or {} @@ -105,6 +105,11 @@ local function fake_lsp_server_setup(test_name, timeout_ms, options, settings) uri = 'file://' .. vim.uv.cwd(), name = 'test_folder', }}; + before_init = function(params, config) + vim.schedule(function() + vim.rpcrequest(1, "setup") + end) + end, on_init = function(client, result) TEST_RPC_CLIENT = client vim.rpcrequest(1, "init", result) @@ -128,6 +133,18 @@ local function fake_lsp_server_setup(test_name, timeout_ms, options, settings) ) end +--- @class test.lsp.Config +--- @field test_name string +--- @field timeout_ms? integer +--- @field options? table +--- @field settings? table +--- +--- @field on_setup? fun() +--- @field on_init? fun(client: vim.lsp.Client, ...) +--- @field on_handler? fun(...) +--- @field on_exit? fun(code: integer, signal: integer) + +--- @param config test.lsp.Config function M.test_rpc_server(config) if config.test_name then M.clear_notrace() @@ -158,8 +175,15 @@ function M.test_rpc_server(config) end end, }) + --- @type integer, integer local code, signal local function on_request(method, args) + if method == 'setup' then + if config.on_setup then + config.on_setup() + end + return NIL + end if method == 'init' then if config.on_init then config.on_init(client, unpack(args)) @@ -180,14 +204,14 @@ function M.test_rpc_server(config) end end -- TODO specify timeout? - -- run(on_request, on_notify, config.on_setup, 1000) - run(on_request, on_notify, config.on_setup) + -- run(on_request, on_notify, nil, 1000) + run(on_request, on_notify, nil) if config.on_exit then config.on_exit(code, signal) end stop() if config.test_name then - exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") + api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) end end diff --git a/test/functional/plugin/lsp/utils_spec.lua b/test/functional/plugin/lsp/utils_spec.lua index bb9cdb8390..6c6dec0667 100644 --- a/test/functional/plugin/lsp/utils_spec.lua +++ b/test/functional/plugin/lsp/utils_spec.lua @@ -1,12 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed = helpers.feed -local eq = helpers.eq -local exec_lua = helpers.exec_lua +local feed = n.feed +local eq = t.eq +local exec_lua = n.exec_lua describe('vim.lsp.util', function() - before_each(helpers.clear) + before_each(n.clear) describe('stylize_markdown', function() local stylize_markdown = function(content, opts) @@ -142,7 +143,7 @@ describe('vim.lsp.util', function() local screen before_each(function() - helpers.clear() + n.clear() screen = Screen.new(80, 80) screen:attach() feed('79i<CR><Esc>') -- fill screen with empty lines diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 4826153edb..c95a96baca 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1,36 +1,38 @@ -local helpers = require('test.functional.helpers')(after_each) -local lsp_helpers = require('test.functional.plugin.lsp.helpers') - -local assert_log = helpers.assert_log -local buf_lines = helpers.buf_lines -local clear = helpers.clear -local command = helpers.command -local dedent = helpers.dedent -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local eval = helpers.eval -local matches = helpers.matches -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local t_lsp = require('test.functional.plugin.lsp.testutil') + +local assert_log = t.assert_log +local buf_lines = n.buf_lines +local clear = n.clear +local command = n.command +local dedent = t.dedent +local exec_lua = n.exec_lua +local eq = t.eq +local eval = n.eval +local matches = t.matches +local pcall_err = t.pcall_err local pesc = vim.pesc -local insert = helpers.insert -local fn = helpers.fn -local retry = helpers.retry -local stop = helpers.stop +local insert = n.insert +local fn = n.fn +local retry = t.retry +local stop = n.stop local NIL = vim.NIL -local read_file = require('test.helpers').read_file -local write_file = require('test.helpers').write_file -local is_ci = helpers.is_ci -local api = helpers.api -local is_os = helpers.is_os -local skip = helpers.skip -local mkdir = helpers.mkdir -local tmpname = helpers.tmpname - -local clear_notrace = lsp_helpers.clear_notrace -local create_server_definition = lsp_helpers.create_server_definition -local fake_lsp_code = lsp_helpers.fake_lsp_code -local fake_lsp_logfile = lsp_helpers.fake_lsp_logfile -local test_rpc_server = lsp_helpers.test_rpc_server +local read_file = t.read_file +local write_file = t.write_file +local is_ci = t.is_ci +local api = n.api +local is_os = t.is_os +local skip = t.skip +local mkdir = t.mkdir +local tmpname = t.tmpname + +local clear_notrace = t_lsp.clear_notrace +local create_server_definition = t_lsp.create_server_definition +local fake_lsp_code = t_lsp.fake_lsp_code +local fake_lsp_logfile = t_lsp.fake_lsp_logfile +local test_rpc_server = t_lsp.test_rpc_server local function get_buf_option(name, bufnr) bufnr = bufnr or 'BUFFER' @@ -83,7 +85,7 @@ describe('LSP', function() end) after_each(function() - exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") + api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) -- exec_lua("lsp.stop_all_clients(true)") end) @@ -146,7 +148,7 @@ describe('LSP', function() after_each(function() stop() exec_lua('lsp.stop_client(lsp.get_clients(), true)') - exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") + api.nvim_exec_autocmds('VimLeavePre', { modeline = false }) end) it('should run correctly', function() @@ -249,7 +251,7 @@ describe('LSP', function() if is_ci() then pending('hangs the build on CI #14028, re-enable with freeze timeout #14204') return - elseif helpers.skip_fragile(pending) then + elseif t.skip_fragile(pending) then return end local expected_handlers = { @@ -279,7 +281,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'finish', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_finish', on_setup = function() @@ -312,7 +314,7 @@ describe('LSP', function() end) it('should fire autocommands on attach and detach', function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_init', on_setup = function() @@ -349,7 +351,7 @@ describe('LSP', function() end) it('should set default options on attach', function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'set_defaults_all_capabilities', on_init = function(_client) @@ -397,7 +399,7 @@ describe('LSP', function() end) it('should overwrite options set by ftplugins', function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'set_defaults_all_capabilities', on_init = function(_client) @@ -437,7 +439,7 @@ describe('LSP', function() end) it('should not overwrite user-defined options', function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'set_defaults_all_capabilities', on_init = function(_client) @@ -473,6 +475,12 @@ describe('LSP', function() local server = _create_server() local bufnr = vim.api.nvim_create_buf(false, true) vim.api.nvim_set_current_buf(bufnr) + local detach_called = false + vim.api.nvim_create_autocmd("LspDetach", { + callback = function() + detach_called = true + end + }) local client_id = vim.lsp.start({ name = 'detach-dummy', cmd = server.cmd }) assert(client_id, "lsp.start must return client_id") local client = vim.lsp.get_client_by_id(client_id) @@ -484,11 +492,42 @@ describe('LSP', function() client_id = client_id, num_attached_before = num_attached_before, num_attached_after = num_attached_after, + detach_called = detach_called, } ]]) eq(true, result ~= nil, 'exec_lua must return result') eq(1, result.num_attached_before) eq(0, result.num_attached_after) + eq(true, result.detach_called) + end) + + it('should not re-attach buffer if it was deleted in on_init #28575', function() + clear() + exec_lua(create_server_definition) + exec_lua([[ + local server = _create_server({ + handlers = { + initialize = function(method, params, callback) + vim.schedule(function() + callback(nil, { capabilities = {} }) + end) + end + } + }) + local bufnr = vim.api.nvim_create_buf(false, true) + local on_init_called = false + local client_id = vim.lsp.start({ + name = 'detach-dummy', + cmd = server.cmd, + on_init = function() + vim.api.nvim_buf_delete(bufnr, {}) + on_init_called = true + end + }) + vim.lsp.buf_attach_client(bufnr, client_id) + local ok = vim.wait(1000, function() return on_init_called end) + assert(ok, "on_init was not called") + ]]) end) it('client should return settings via workspace/configuration handler', function() @@ -508,7 +547,7 @@ describe('LSP', function() }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_workspace_configuration', on_init = function(_client) @@ -598,7 +637,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'text_document_sync_save_bool', on_init = function(c) @@ -641,11 +680,11 @@ describe('LSP', function() vim.lsp.stop_client(client_id) return server.messages ]]) - eq(#messages, 4) - eq(messages[1].method, 'initialize') - eq(messages[2].method, 'initialized') - eq(messages[3].method, 'shutdown') - eq(messages[4].method, 'exit') + eq(4, #messages) + eq('initialize', messages[1].method) + eq('initialized', messages[2].method) + eq('shutdown', messages[3].method) + eq('exit', messages[4].method) end) it('BufWritePre sends willSave / willSaveWaitUntil, applies textEdits', function() @@ -660,7 +699,7 @@ describe('LSP', function() } }, handlers = { - ['textDocument/willSaveWaitUntil'] = function() + ['textDocument/willSaveWaitUntil'] = function(_, _, callback) local text_edit = { range = { start = { line = 0, character = 0 }, @@ -668,7 +707,7 @@ describe('LSP', function() }, newText = 'Hello' } - return { text_edit, } + callback(nil, { text_edit, }) end }, }) @@ -692,7 +731,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server({ test_name = 'text_document_save_did_open', on_init = function(c) @@ -732,7 +771,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'text_document_sync_save_includeText', on_init = function(c) @@ -808,14 +847,12 @@ describe('LSP', function() BUFFER = vim.api.nvim_get_current_buf() lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID) vim.lsp.handlers['textDocument/typeDefinition'] = function() end + vim.cmd(BUFFER.."bwipeout") ]=]) end, on_init = function(client) client.stop() exec_lua('vim.lsp.buf.type_definition()') - exec_lua [[ - vim.api.nvim_command(BUFFER.."bwipeout") - ]] end, on_exit = function(code, signal) eq(0, code, 'exit code') @@ -859,7 +896,7 @@ describe('LSP', function() local expected_handlers = { { NIL, {}, { method = 'finish', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_forward_request_cancelled', on_init = function(_client) @@ -885,7 +922,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { { code = -32801 }, NIL, { method = 'error_code_test', bufnr = 1, client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_forward_content_modified', on_init = function(_client) @@ -915,7 +952,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_pending_request_tracked', on_init = function(_client) @@ -949,7 +986,7 @@ describe('LSP', function() local expected_handlers = { { NIL, {}, { method = 'finish', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_cancel_request_tracked', on_init = function(_client) @@ -982,7 +1019,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_tracked_requests_cleared', on_init = function(_client) @@ -1021,7 +1058,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'check_tracked_requests_cleared', on_init = function(_client) @@ -1057,7 +1094,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'finish', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_finish', on_setup = function() @@ -1067,12 +1104,10 @@ describe('LSP', function() "testing"; "123"; }) - ]] - eq(1, exec_lua('return TEST_RPC_CLIENT_ID')) - eq(true, exec_lua('return lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)')) - eq(true, exec_lua('return lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)')) - exec_lua [[ - vim.api.nvim_command(BUFFER.."bwipeout") + assert(TEST_RPC_CLIENT_ID == 1) + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + assert(lsp.buf_is_attached(BUFFER, TEST_RPC_CLIENT_ID)) + vim.cmd(BUFFER.."bwipeout") ]] end, on_init = function(_client) @@ -1101,7 +1136,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open', on_setup = function() @@ -1147,7 +1182,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open', on_setup = function() @@ -1190,7 +1225,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change', on_setup = function() @@ -1238,7 +1273,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_noeol', on_setup = function() @@ -1305,7 +1340,7 @@ describe('LSP', function() }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'inlay_hint', on_setup = function() @@ -1332,7 +1367,7 @@ describe('LSP', function() on_handler = function(err, result, ctx) if ctx.method == 'start' then exec_lua [[ - vim.lsp.inlay_hint.enable(BUFFER) + vim.lsp.inlay_hint.enable(true, { bufnr = BUFFER }) ]] end if ctx.method == 'textDocument/inlayHint' then @@ -1352,7 +1387,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_incremental', options = { @@ -1403,7 +1438,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_incremental', options = { @@ -1457,7 +1492,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_incremental_editing', on_setup = function() @@ -1485,7 +1520,7 @@ describe('LSP', function() end, on_handler = function(err, result, ctx) if ctx.method == 'start' then - helpers.command('normal! 1Go') + n.command('normal! 1Go') client.notify('finish') end eq(table.remove(expected_handlers), { err, result, ctx }, 'expected handler') @@ -1502,7 +1537,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_multi', on_setup = function() @@ -1553,7 +1588,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_check_buffer_open_and_change_multi_and_close', on_setup = function() @@ -1607,7 +1642,7 @@ describe('LSP', function() { NIL, {}, { method = 'finish', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'invalid_header', on_setup = function() end, @@ -1640,7 +1675,7 @@ describe('LSP', function() }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'decode_nil', on_setup = function() @@ -1807,7 +1842,7 @@ describe('LSP', function() make_edit(1, 0, 2, 5, 'foobar'), make_edit(4, 0, 5, 0, 'barfoo'), } - eq(true, exec_lua('return vim.api.nvim_buf_set_mark(1, "a", 2, 1, {})')) + eq(true, api.nvim_buf_set_mark(1, 'a', 2, 1, {})) exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') eq({ 'First line of text', @@ -1815,7 +1850,7 @@ describe('LSP', function() 'Fourth line of text', 'barfoo', }, buf_lines(1)) - local mark = exec_lua('return vim.api.nvim_buf_get_mark(1, "a")') + local mark = api.nvim_buf_get_mark(1, 'a') eq({ 2, 1 }, mark) end) @@ -1824,7 +1859,7 @@ describe('LSP', function() make_edit(1, 0, 2, 15, 'foobar'), make_edit(4, 0, 5, 0, 'barfoo'), } - eq(true, exec_lua('return vim.api.nvim_buf_set_mark(1, "a", 2, 10, {})')) + eq(true, api.nvim_buf_set_mark(1, 'a', 2, 10, {})) exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') eq({ 'First line of text', @@ -1832,7 +1867,7 @@ describe('LSP', function() 'Fourth line of text', 'barfoo', }, buf_lines(1)) - local mark = exec_lua('return vim.api.nvim_buf_get_mark(1, "a")') + local mark = api.nvim_buf_get_mark(1, 'a') eq({ 2, 9 }, mark) end) @@ -1841,19 +1876,19 @@ describe('LSP', function() make_edit(1, 0, 4, 5, 'foobar'), make_edit(4, 0, 5, 0, 'barfoo'), } - eq(true, exec_lua('return vim.api.nvim_buf_set_mark(1, "a", 4, 1, {})')) + eq(true, api.nvim_buf_set_mark(1, 'a', 4, 1, {})) exec_lua('vim.lsp.util.apply_text_edits(...)', edits, 1, 'utf-16') eq({ 'First line of text', 'foobaro', }, buf_lines(1)) - local mark = exec_lua('return vim.api.nvim_buf_get_mark(1, "a")') + local mark = api.nvim_buf_get_mark(1, 'a') eq({ 2, 1 }, mark) end) describe('cursor position', function() it("don't fix the cursor if the range contains the cursor", function() - fn.nvim_win_set_cursor(0, { 2, 6 }) + api.nvim_win_set_cursor(0, { 2, 6 }) local edits = { make_edit(1, 0, 1, 19, 'Second line of text'), } @@ -1865,11 +1900,11 @@ describe('LSP', function() 'Fourth line of text', 'å å ɧ 汉语 ↥ 🤦 🦄', }, buf_lines(1)) - eq({ 2, 6 }, fn.nvim_win_get_cursor(0)) + eq({ 2, 6 }, api.nvim_win_get_cursor(0)) end) it('fix the cursor to the valid col if the content was removed', function() - fn.nvim_win_set_cursor(0, { 2, 6 }) + api.nvim_win_set_cursor(0, { 2, 6 }) local edits = { make_edit(1, 0, 1, 6, ''), make_edit(1, 6, 1, 19, ''), @@ -1882,11 +1917,11 @@ describe('LSP', function() 'Fourth line of text', 'å å ɧ 汉语 ↥ 🤦 🦄', }, buf_lines(1)) - eq({ 2, 0 }, fn.nvim_win_get_cursor(0)) + eq({ 2, 0 }, api.nvim_win_get_cursor(0)) end) it('fix the cursor to the valid row if the content was removed', function() - fn.nvim_win_set_cursor(0, { 2, 6 }) + api.nvim_win_set_cursor(0, { 2, 6 }) local edits = { make_edit(1, 0, 1, 6, ''), make_edit(0, 18, 5, 0, ''), @@ -1895,11 +1930,11 @@ describe('LSP', function() eq({ 'First line of text', }, buf_lines(1)) - eq({ 1, 17 }, fn.nvim_win_get_cursor(0)) + eq({ 1, 17 }, api.nvim_win_get_cursor(0)) end) it('fix the cursor row', function() - fn.nvim_win_set_cursor(0, { 3, 0 }) + api.nvim_win_set_cursor(0, { 3, 0 }) local edits = { make_edit(1, 0, 2, 0, ''), } @@ -1910,14 +1945,14 @@ describe('LSP', function() 'Fourth line of text', 'å å ɧ 汉语 ↥ 🤦 🦄', }, buf_lines(1)) - eq({ 2, 0 }, fn.nvim_win_get_cursor(0)) + eq({ 2, 0 }, api.nvim_win_get_cursor(0)) end) it('fix the cursor col', function() -- append empty last line. See #22636 - exec_lua('vim.api.nvim_buf_set_lines(...)', 1, -1, -1, true, { '' }) + api.nvim_buf_set_lines(1, -1, -1, true, { '' }) - fn.nvim_win_set_cursor(0, { 2, 11 }) + api.nvim_win_set_cursor(0, { 2, 11 }) local edits = { make_edit(1, 7, 1, 11, ''), } @@ -1930,11 +1965,11 @@ describe('LSP', function() 'å å ɧ 汉语 ↥ 🤦 🦄', '', }, buf_lines(1)) - eq({ 2, 7 }, fn.nvim_win_get_cursor(0)) + eq({ 2, 7 }, api.nvim_win_get_cursor(0)) end) it('fix the cursor row and col', function() - fn.nvim_win_set_cursor(0, { 2, 12 }) + api.nvim_win_set_cursor(0, { 2, 12 }) local edits = { make_edit(0, 11, 1, 12, ''), } @@ -1945,7 +1980,7 @@ describe('LSP', function() 'Fourth line of text', 'å å ɧ 汉语 ↥ 🤦 🦄', }, buf_lines(1)) - eq({ 1, 11 }, fn.nvim_win_get_cursor(0)) + eq({ 1, 11 }, api.nvim_win_get_cursor(0)) end) end) @@ -2021,7 +2056,7 @@ describe('LSP', function() end) describe('apply_text_document_edit', function() - local target_bufnr + local target_bufnr --- @type integer local text_document_edit = function(editVersion) return { edits = { @@ -2267,7 +2302,7 @@ describe('LSP', function() }, } exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) + eq(true, vim.uv.fs_stat(tmpfile) ~= nil) end) it( 'Supports file creation in folder that needs to be created with CreateFile payload', @@ -2285,7 +2320,7 @@ describe('LSP', function() }, } exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) + eq(true, vim.uv.fs_stat(tmpfile) ~= nil) end ) it('createFile does not touch file if it exists and ignoreIfExists is set', function() @@ -2304,7 +2339,7 @@ describe('LSP', function() }, } exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) + eq(true, vim.uv.fs_stat(tmpfile) ~= nil) eq('Dummy content', read_file(tmpfile)) end) it('createFile overrides file if overwrite is set', function() @@ -2324,7 +2359,7 @@ describe('LSP', function() }, } exec_lua('vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16') - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) + eq(true, vim.uv.fs_stat(tmpfile) ~= nil) eq('', read_file(tmpfile)) end) it('DeleteFile delete file and buffer', function() @@ -2348,8 +2383,8 @@ describe('LSP', function() }, } eq(true, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit, 'utf-16')) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) - eq(false, exec_lua('return vim.api.nvim_buf_is_loaded(vim.fn.bufadd(...))', tmpfile)) + eq(false, vim.uv.fs_stat(tmpfile) ~= nil) + eq(false, api.nvim_buf_is_loaded(fn.bufadd(tmpfile))) end) it('DeleteFile fails if file does not exist and ignoreIfNotExists is false', function() local tmpfile = tmpname() @@ -2367,12 +2402,12 @@ describe('LSP', function() }, } eq(false, pcall(exec_lua, 'vim.lsp.util.apply_workspace_edit(...)', edit)) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', tmpfile)) + eq(false, vim.uv.fs_stat(tmpfile) ~= nil) end) end) describe('lsp.util.rename', function() - local pathsep = helpers.get_pathsep() + local pathsep = n.get_pathsep() it('Can rename an existing file', function() local old = tmpname() @@ -2395,9 +2430,9 @@ describe('LSP', function() new ) eq({ 'Test content' }, lines) - local exists = exec_lua('return vim.uv.fs_stat(...) ~= nil', old) + local exists = vim.uv.fs_stat(old) ~= nil eq(false, exists) - exists = exec_lua('return vim.uv.fs_stat(...) ~= nil', new) + exists = vim.uv.fs_stat(new) ~= nil eq(true, exists) os.remove(new) end) @@ -2408,7 +2443,7 @@ describe('LSP', function() os.remove(old_dir) os.remove(new_dir) - helpers.mkdir_p(old_dir) + n.mkdir_p(old_dir) local file = 'file.txt' write_file(old_dir .. pathsep .. file, 'Test content') @@ -2433,9 +2468,9 @@ describe('LSP', function() file ) eq({ 'Test content' }, lines) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', old_dir)) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', new_dir)) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', new_dir .. pathsep .. file)) + eq(false, vim.uv.fs_stat(old_dir) ~= nil) + eq(true, vim.uv.fs_stat(new_dir) ~= nil) + eq(true, vim.uv.fs_stat(new_dir .. pathsep .. file) ~= nil) os.remove(new_dir) end) @@ -2444,7 +2479,7 @@ describe('LSP', function() local new = tmpname() os.remove(old) os.remove(new) - helpers.mkdir_p(old) + n.mkdir_p(old) local result = exec_lua( [[ @@ -2499,7 +2534,7 @@ describe('LSP', function() new ) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', old)) + eq(true, vim.uv.fs_stat(old) ~= nil) eq('New file', read_file(new)) exec_lua( @@ -2513,7 +2548,7 @@ describe('LSP', function() new ) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', old)) + eq(true, vim.uv.fs_stat(old) ~= nil) eq('New file', read_file(new)) end ) @@ -2543,8 +2578,8 @@ describe('LSP', function() old, new ) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', old)) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', new)) + eq(false, vim.uv.fs_stat(old) ~= nil) + eq(true, vim.uv.fs_stat(new) ~= nil) eq(true, undo_kept) end) it('Maintains undo information for unloaded buffer', function() @@ -2570,8 +2605,8 @@ describe('LSP', function() old, new ) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', old)) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', new)) + eq(false, vim.uv.fs_stat(old) ~= nil) + eq(true, vim.uv.fs_stat(new) ~= nil) eq(true, undo_kept) end) it('Does not rename file when it conflicts with a buffer without file', function() @@ -2615,8 +2650,8 @@ describe('LSP', function() new ) - eq(false, exec_lua('return vim.uv.fs_stat(...) ~= nil', old)) - eq(true, exec_lua('return vim.uv.fs_stat(...) ~= nil', new)) + eq(false, vim.uv.fs_stat(old) ~= nil) + eq(true, vim.uv.fs_stat(new) ~= nil) eq('Old file', read_file(new)) end) end) @@ -2697,6 +2732,7 @@ describe('LSP', function() eq(expected, actual) end) end) + describe('lsp.util.symbols_to_items', function() describe('convert DocumentSymbol[] to items', function() it('DocumentSymbol has children', function() @@ -2971,7 +3007,7 @@ describe('LSP', function() end) describe('lsp.util.jump_to_location', function() - local target_bufnr + local target_bufnr --- @type integer before_each(function() target_bufnr = exec_lua [[ @@ -2994,10 +3030,10 @@ describe('LSP', function() local jump = function(msg) eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg, 'utf-16')) - eq(target_bufnr, exec_lua [[return vim.fn.bufnr('%')]]) + eq(target_bufnr, fn.bufnr('%')) return { - line = exec_lua [[return vim.fn.line('.')]], - col = exec_lua [[return vim.fn.col('.')]], + line = fn.line('.'), + col = fn.col('.'), } end @@ -3027,25 +3063,25 @@ describe('LSP', function() local pos = jump(location(1, 2, 1, 2)) eq(2, pos.line) eq(4, pos.col) - eq('å', exec_lua [[return vim.fn.expand('<cword>')]]) + eq('å', fn.expand('<cword>')) end) it('adds current position to jumplist before jumping', function() - fn.nvim_win_set_buf(0, target_bufnr) - local mark = fn.nvim_buf_get_mark(target_bufnr, "'") + api.nvim_win_set_buf(0, target_bufnr) + local mark = api.nvim_buf_get_mark(target_bufnr, "'") eq({ 1, 0 }, mark) - fn.nvim_win_set_cursor(0, { 2, 3 }) + api.nvim_win_set_cursor(0, { 2, 3 }) jump(location(0, 9, 0, 9)) - mark = fn.nvim_buf_get_mark(target_bufnr, "'") + mark = api.nvim_buf_get_mark(target_bufnr, "'") eq({ 2, 3 }, mark) end) end) describe('lsp.util.show_document', function() - local target_bufnr - local target_bufnr2 + local target_bufnr --- @type integer + local target_bufnr2 --- @type integer before_each(function() target_bufnr = exec_lua([[ @@ -3084,11 +3120,11 @@ describe('LSP', function() ) ) if focus == true or focus == nil then - eq(target_bufnr, exec_lua([[return vim.fn.bufnr('%')]])) + eq(target_bufnr, fn.bufnr('%')) end return { - line = exec_lua([[return vim.fn.line('.')]]), - col = exec_lua([[return vim.fn.col('.')]]), + line = fn.line('.'), + col = fn.col('.'), } end @@ -3132,101 +3168,101 @@ describe('LSP', function() end) it('does not add current position to jumplist if not focus', function() - fn.nvim_win_set_buf(0, target_bufnr) - local mark = fn.nvim_buf_get_mark(target_bufnr, "'") + api.nvim_win_set_buf(0, target_bufnr) + local mark = api.nvim_buf_get_mark(target_bufnr, "'") eq({ 1, 0 }, mark) - fn.nvim_win_set_cursor(0, { 2, 3 }) + api.nvim_win_set_cursor(0, { 2, 3 }) show_document(location(0, 9, 0, 9), false, true) show_document(location(0, 9, 0, 9, true), false, true) - mark = fn.nvim_buf_get_mark(target_bufnr, "'") + mark = api.nvim_buf_get_mark(target_bufnr, "'") eq({ 1, 0 }, mark) end) it('does not change cursor position if not focus and not reuse_win', function() - fn.nvim_win_set_buf(0, target_bufnr) - local cursor = fn.nvim_win_get_cursor(0) + api.nvim_win_set_buf(0, target_bufnr) + local cursor = api.nvim_win_get_cursor(0) show_document(location(0, 9, 0, 9), false, false) - eq(cursor, fn.nvim_win_get_cursor(0)) + eq(cursor, api.nvim_win_get_cursor(0)) end) it('does not change window if not focus', function() - fn.nvim_win_set_buf(0, target_bufnr) - local win = fn.nvim_get_current_win() + api.nvim_win_set_buf(0, target_bufnr) + local win = api.nvim_get_current_win() -- same document/bufnr show_document(location(0, 9, 0, 9), false, true) - eq(win, fn.nvim_get_current_win()) + eq(win, api.nvim_get_current_win()) -- different document/bufnr, new window/split show_document(location(0, 9, 0, 9, true), false, true) - eq(2, #fn.nvim_list_wins()) - eq(win, fn.nvim_get_current_win()) + eq(2, #api.nvim_list_wins()) + eq(win, api.nvim_get_current_win()) end) it("respects 'reuse_win' parameter", function() - fn.nvim_win_set_buf(0, target_bufnr) + api.nvim_win_set_buf(0, target_bufnr) -- does not create a new window if the buffer is already open show_document(location(0, 9, 0, 9), false, true) - eq(1, #fn.nvim_list_wins()) + eq(1, #api.nvim_list_wins()) -- creates a new window even if the buffer is already open show_document(location(0, 9, 0, 9), false, false) - eq(2, #fn.nvim_list_wins()) + eq(2, #api.nvim_list_wins()) end) it('correctly sets the cursor of the split if range is given without focus', function() - fn.nvim_win_set_buf(0, target_bufnr) + api.nvim_win_set_buf(0, target_bufnr) show_document(location(0, 9, 0, 9, true), false, true) - local wins = fn.nvim_list_wins() + local wins = api.nvim_list_wins() eq(2, #wins) table.sort(wins) - eq({ 1, 0 }, fn.nvim_win_get_cursor(wins[1])) - eq({ 1, 9 }, fn.nvim_win_get_cursor(wins[2])) + eq({ 1, 0 }, api.nvim_win_get_cursor(wins[1])) + eq({ 1, 9 }, api.nvim_win_get_cursor(wins[2])) end) it('does not change cursor of the split if not range and not focus', function() - fn.nvim_win_set_buf(0, target_bufnr) - fn.nvim_win_set_cursor(0, { 2, 3 }) + api.nvim_win_set_buf(0, target_bufnr) + api.nvim_win_set_cursor(0, { 2, 3 }) exec_lua([[vim.cmd.new()]]) - fn.nvim_win_set_buf(0, target_bufnr2) - fn.nvim_win_set_cursor(0, { 2, 3 }) + api.nvim_win_set_buf(0, target_bufnr2) + api.nvim_win_set_cursor(0, { 2, 3 }) show_document({ uri = 'file:///fake/uri2' }, false, true) - local wins = fn.nvim_list_wins() + local wins = api.nvim_list_wins() eq(2, #wins) - eq({ 2, 3 }, fn.nvim_win_get_cursor(wins[1])) - eq({ 2, 3 }, fn.nvim_win_get_cursor(wins[2])) + eq({ 2, 3 }, api.nvim_win_get_cursor(wins[1])) + eq({ 2, 3 }, api.nvim_win_get_cursor(wins[2])) end) it('respects existing buffers', function() - fn.nvim_win_set_buf(0, target_bufnr) - local win = fn.nvim_get_current_win() + api.nvim_win_set_buf(0, target_bufnr) + local win = api.nvim_get_current_win() exec_lua([[vim.cmd.new()]]) - fn.nvim_win_set_buf(0, target_bufnr2) - fn.nvim_win_set_cursor(0, { 2, 3 }) - local split = fn.nvim_get_current_win() + api.nvim_win_set_buf(0, target_bufnr2) + api.nvim_win_set_cursor(0, { 2, 3 }) + local split = api.nvim_get_current_win() -- reuse win for open document/bufnr if called from split show_document(location(0, 9, 0, 9, true), false, true) - eq({ 1, 9 }, fn.nvim_win_get_cursor(split)) - eq(2, #fn.nvim_list_wins()) + eq({ 1, 9 }, api.nvim_win_get_cursor(split)) + eq(2, #api.nvim_list_wins()) - fn.nvim_set_current_win(win) + api.nvim_set_current_win(win) -- reuse win for open document/bufnr if called outside the split show_document(location(0, 9, 0, 9, true), false, true) - eq({ 1, 9 }, fn.nvim_win_get_cursor(split)) - eq(2, #fn.nvim_list_wins()) + eq({ 1, 9 }, api.nvim_win_get_cursor(split)) + eq(2, #api.nvim_list_wins()) end) end) @@ -3464,6 +3500,442 @@ describe('LSP', function() end) end) + describe('vim.lsp.buf.typehierarchy subtypes', function() + it('does nothing for an empty response', function() + local qflist_count = exec_lua([=[ + require'vim.lsp.handlers'['typeHierarchy/subtypes'](nil, nil, {}) + return #vim.fn.getqflist() + ]=]) + eq(0, qflist_count) + end) + + it('opens the quickfix list with the right subtypes', function() + clear() + exec_lua(create_server_definition) + local qflist = exec_lua([=[ + local clangd_response = { { + data = { + parents = { { + parents = { { + parents = { { + parents = {}, + symbolID = "62B3D268A01B9978" + } }, + symbolID = "DC9B0AD433B43BEC" + } }, + symbolID = "06B5F6A19BA9F6A8" + } }, + symbolID = "EDC336589C09ABB2" + }, + kind = 5, + name = "D2", + range = { + ["end"] = { + character = 8, + line = 9 + }, + start = { + character = 6, + line = 9 + } + }, + selectionRange = { + ["end"] = { + character = 8, + line = 9 + }, + start = { + character = 6, + line = 9 + } + }, + uri = "file:///home/jiangyinzuo/hello.cpp" + }, { + data = { + parents = { { + parents = { { + parents = { { + parents = {}, + symbolID = "62B3D268A01B9978" + } }, + symbolID = "DC9B0AD433B43BEC" + } }, + symbolID = "06B5F6A19BA9F6A8" + } }, + symbolID = "AFFCAED15557EF08" + }, + kind = 5, + name = "D1", + range = { + ["end"] = { + character = 8, + line = 8 + }, + start = { + character = 6, + line = 8 + } + }, + selectionRange = { + ["end"] = { + character = 8, + line = 8 + }, + start = { + character = 6, + line = 8 + } + }, + uri = "file:///home/jiangyinzuo/hello.cpp" + } } + + local server = _create_server({ + capabilities = { + positionEncoding = "utf-8" + }, + }) + local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + local handler = require'vim.lsp.handlers'['typeHierarchy/subtypes'] + handler(nil, clangd_response, { client_id = client_id, bufnr = 1 }) + return vim.fn.getqflist() + ]=]) + + local expected = { + { + bufnr = 2, + col = 7, + end_col = 0, + end_lnum = 0, + lnum = 10, + module = '', + nr = 0, + pattern = '', + text = 'D2', + type = '', + valid = 1, + vcol = 0, + }, + { + bufnr = 2, + col = 7, + end_col = 0, + end_lnum = 0, + lnum = 9, + module = '', + nr = 0, + pattern = '', + text = 'D1', + type = '', + valid = 1, + vcol = 0, + }, + } + + eq(expected, qflist) + end) + + it('opens the quickfix list with the right subtypes and details', function() + clear() + exec_lua(create_server_definition) + local qflist = exec_lua([=[ + local jdtls_response = { + { + data = { element = '=hello-java_ed323c3c/_<{Main.java[Main[A' }, + detail = '', + kind = 5, + name = 'A', + range = { + ['end'] = { character = 26, line = 3 }, + start = { character = 1, line = 3 }, + }, + selectionRange = { + ['end'] = { character = 8, line = 3 }, + start = { character = 7, line = 3 }, + }, + tags = {}, + uri = 'file:///home/jiangyinzuo/hello-java/Main.java', + }, + { + data = { element = '=hello-java_ed323c3c/_<mylist{MyList.java[MyList[Inner' }, + detail = 'mylist', + kind = 5, + name = 'MyList$Inner', + range = { + ['end'] = { character = 37, line = 3 }, + start = { character = 1, line = 3 }, + }, + selectionRange = { + ['end'] = { character = 19, line = 3 }, + start = { character = 14, line = 3 }, + }, + tags = {}, + uri = 'file:///home/jiangyinzuo/hello-java/mylist/MyList.java', + }, + } + + local server = _create_server({ + capabilities = { + positionEncoding = "utf-8" + }, + }) + local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + local handler = require'vim.lsp.handlers'['typeHierarchy/subtypes'] + handler(nil, jdtls_response, { client_id = client_id, bufnr = 1 }) + return vim.fn.getqflist() + ]=]) + + local expected = { + { + bufnr = 2, + col = 2, + end_col = 0, + end_lnum = 0, + lnum = 4, + module = '', + nr = 0, + pattern = '', + text = 'A', + type = '', + valid = 1, + vcol = 0, + }, + { + bufnr = 3, + col = 2, + end_col = 0, + end_lnum = 0, + lnum = 4, + module = '', + nr = 0, + pattern = '', + text = 'MyList$Inner mylist', + type = '', + valid = 1, + vcol = 0, + }, + } + eq(expected, qflist) + end) + end) + + describe('vim.lsp.buf.typehierarchy supertypes', function() + it('does nothing for an empty response', function() + local qflist_count = exec_lua([=[ + require'vim.lsp.handlers'['typeHierarchy/supertypes'](nil, nil, {}) + return #vim.fn.getqflist() + ]=]) + eq(0, qflist_count) + end) + + it('opens the quickfix list with the right supertypes', function() + clear() + exec_lua(create_server_definition) + local qflist = exec_lua([=[ + local clangd_response = { { + data = { + parents = { { + parents = { { + parents = { { + parents = {}, + symbolID = "62B3D268A01B9978" + } }, + symbolID = "DC9B0AD433B43BEC" + } }, + symbolID = "06B5F6A19BA9F6A8" + } }, + symbolID = "EDC336589C09ABB2" + }, + kind = 5, + name = "D2", + range = { + ["end"] = { + character = 8, + line = 9 + }, + start = { + character = 6, + line = 9 + } + }, + selectionRange = { + ["end"] = { + character = 8, + line = 9 + }, + start = { + character = 6, + line = 9 + } + }, + uri = "file:///home/jiangyinzuo/hello.cpp" + }, { + data = { + parents = { { + parents = { { + parents = { { + parents = {}, + symbolID = "62B3D268A01B9978" + } }, + symbolID = "DC9B0AD433B43BEC" + } }, + symbolID = "06B5F6A19BA9F6A8" + } }, + symbolID = "AFFCAED15557EF08" + }, + kind = 5, + name = "D1", + range = { + ["end"] = { + character = 8, + line = 8 + }, + start = { + character = 6, + line = 8 + } + }, + selectionRange = { + ["end"] = { + character = 8, + line = 8 + }, + start = { + character = 6, + line = 8 + } + }, + uri = "file:///home/jiangyinzuo/hello.cpp" + } } + + local server = _create_server({ + capabilities = { + positionEncoding = "utf-8" + }, + }) + local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + local handler = require'vim.lsp.handlers'['typeHierarchy/supertypes'] + handler(nil, clangd_response, { client_id = client_id, bufnr = 1 }) + return vim.fn.getqflist() + ]=]) + + local expected = { + { + bufnr = 2, + col = 7, + end_col = 0, + end_lnum = 0, + lnum = 10, + module = '', + nr = 0, + pattern = '', + text = 'D2', + type = '', + valid = 1, + vcol = 0, + }, + { + bufnr = 2, + col = 7, + end_col = 0, + end_lnum = 0, + lnum = 9, + module = '', + nr = 0, + pattern = '', + text = 'D1', + type = '', + valid = 1, + vcol = 0, + }, + } + + eq(expected, qflist) + end) + + it('opens the quickfix list with the right supertypes and details', function() + clear() + exec_lua(create_server_definition) + local qflist = exec_lua([=[ + local jdtls_response = { + { + data = { element = '=hello-java_ed323c3c/_<{Main.java[Main[A' }, + detail = '', + kind = 5, + name = 'A', + range = { + ['end'] = { character = 26, line = 3 }, + start = { character = 1, line = 3 }, + }, + selectionRange = { + ['end'] = { character = 8, line = 3 }, + start = { character = 7, line = 3 }, + }, + tags = {}, + uri = 'file:///home/jiangyinzuo/hello-java/Main.java', + }, + { + data = { element = '=hello-java_ed323c3c/_<mylist{MyList.java[MyList[Inner' }, + detail = 'mylist', + kind = 5, + name = 'MyList$Inner', + range = { + ['end'] = { character = 37, line = 3 }, + start = { character = 1, line = 3 }, + }, + selectionRange = { + ['end'] = { character = 19, line = 3 }, + start = { character = 14, line = 3 }, + }, + tags = {}, + uri = 'file:///home/jiangyinzuo/hello-java/mylist/MyList.java', + }, + } + + local server = _create_server({ + capabilities = { + positionEncoding = "utf-8" + }, + }) + local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + local handler = require'vim.lsp.handlers'['typeHierarchy/supertypes'] + handler(nil, jdtls_response, { client_id = client_id, bufnr = 1 }) + return vim.fn.getqflist() + ]=]) + + local expected = { + { + bufnr = 2, + col = 2, + end_col = 0, + end_lnum = 0, + lnum = 4, + module = '', + nr = 0, + pattern = '', + text = 'A', + type = '', + valid = 1, + vcol = 0, + }, + { + bufnr = 3, + col = 2, + end_col = 0, + end_lnum = 0, + lnum = 4, + module = '', + nr = 0, + pattern = '', + text = 'MyList$Inner mylist', + type = '', + valid = 1, + vcol = 0, + }, + } + eq(expected, qflist) + end) + end) + describe('vim.lsp.buf.rename', function() for _, test in ipairs({ { @@ -3504,7 +3976,7 @@ describe('LSP', function() }, }) do it(test.it, function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = test.name, on_init = function(_client) @@ -3554,7 +4026,7 @@ describe('LSP', function() describe('vim.lsp.buf.code_action', function() it('Calls client side command if available', function() - local client + local client --- @type vim.lsp.Client local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, @@ -3592,7 +4064,7 @@ describe('LSP', function() } end) it('Calls workspace/executeCommand if no client side command', function() - local client + local client --- @type vim.lsp.Client local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, { @@ -3632,7 +4104,7 @@ describe('LSP', function() }) end) it('Filters and automatically applies action if requested', function() - local client + local client --- @type vim.lsp.Client local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, @@ -3701,8 +4173,8 @@ describe('LSP', function() } }, handlers = { - ["textDocument/codeAction"] = function() - return { + ["textDocument/codeAction"] = function(_, _, callback) + callback(nil, { { title = "Code Action 1", command = { @@ -3710,10 +4182,10 @@ describe('LSP', function() command = "command:1", } } - } + }) end, - ["codeAction/resolve"] = function() - return nil, "resolve failed" + ["codeAction/resolve"] = function(_, _, callback) + callback("resolve failed", nil) end, } }) @@ -3732,6 +4204,7 @@ describe('LSP', function() eq('command:1', result[5].params.command) end) end) + describe('vim.lsp.commands', function() it('Accepts only string keys', function() matches( @@ -3746,9 +4219,10 @@ describe('LSP', function() ) end) end) + describe('vim.lsp.codelens', function() it('uses client commands', function() - local client + local client --- @type vim.lsp.Client local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, @@ -3802,7 +4276,7 @@ describe('LSP', function() end) it('releases buffer refresh lock', function() - local client + local client --- @type vim.lsp.Client local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, @@ -3879,11 +4353,97 @@ describe('LSP', function() end, } end) + + it('refresh multiple buffers', function() + local lens_title_per_fake_uri = { + ['file:///fake/uri1'] = 'Lens1', + ['file:///fake/uri2'] = 'Lens2', + } + clear() + exec_lua(create_server_definition) + + -- setup lsp + exec_lua( + [[ + local lens_title_per_fake_uri = ... + local server = _create_server({ + capabilities = { + codeLensProvider = { + resolveProvider = true + }, + }, + handlers = { + ["textDocument/codeLens"] = function(method, params, callback) + local lenses = { + { + range = { + start = { line = 0, character = 0 }, + ['end'] = { line = 0, character = 0 }, + }, + command = { + title = lens_title_per_fake_uri[params.textDocument.uri], + command = 'Dummy', + }, + }, + } + callback(nil, lenses) + end, + } + }) + + CLIENT_ID = vim.lsp.start({ + name = "dummy", + cmd = server.cmd, + }) + ]], + lens_title_per_fake_uri + ) + + -- create buffers and setup handler + exec_lua( + [[ + local lens_title_per_fake_uri = ... + local default_buf = vim.api.nvim_get_current_buf() + for fake_uri, _ in pairs(lens_title_per_fake_uri) do + local bufnr = vim.uri_to_bufnr(fake_uri) + vim.api.nvim_set_current_buf(bufnr) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'Some contents'}) + vim.lsp.buf_attach_client(bufnr, CLIENT_ID) + end + vim.api.nvim_buf_delete(default_buf, {force = true}) + + REQUEST_COUNT = vim.tbl_count(lens_title_per_fake_uri) + RESPONSES = {} + local on_codelens = vim.lsp.codelens.on_codelens + vim.lsp.codelens.on_codelens = function (err, result, ctx, ...) + table.insert(RESPONSES, { err = err, result = result, ctx = ctx }) + return on_codelens(err, result, ctx, ...) + end + ]], + lens_title_per_fake_uri + ) + + -- call codelens refresh + local cmds = exec_lua([[ + RESPONSES = {} + vim.lsp.codelens.refresh() + vim.wait(100, function () return #RESPONSES >= REQUEST_COUNT end) + + local cmds = {} + for _, resp in ipairs(RESPONSES) do + local uri = resp.ctx.params.textDocument.uri + cmds[uri] = resp.result[1].command + end + return cmds + ]]) + eq({ command = 'Dummy', title = 'Lens1' }, cmds['file:///fake/uri1']) + eq({ command = 'Dummy', title = 'Lens2' }, cmds['file:///fake/uri2']) + end) end) describe('vim.lsp.buf.format', function() it('Aborts with notify if no client matches filter', function() - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_init', on_init = function(c) @@ -3912,7 +4472,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_formatting', on_init = function(c) @@ -3945,7 +4505,7 @@ describe('LSP', function() { NIL, {}, { method = 'shutdown', client_id = 1 } }, { NIL, {}, { method = 'start', client_id = 1 } }, } - local client + local client --- @type vim.lsp.Client test_rpc_server { test_name = 'basic_formatting', on_init = function(c) @@ -4068,6 +4628,9 @@ describe('LSP', function() end ]]) local fail_msg = '[LSP] Format request failed, no matching language servers.' + --- @param name string + --- @param formatting boolean + --- @param range_formatting boolean local function check_notify(name, formatting, range_formatting) local timeout_msg = '[LSP][' .. name .. '] timeout' exec_lua( @@ -4131,13 +4694,13 @@ describe('LSP', function() }, handlers = { ---@return lsp.Location[] - ['textDocument/definition'] = function() - return { _G.mock_locations[1] } + ['textDocument/definition'] = function(_, _, callback) + callback(nil, { _G.mock_locations[1] }) end, ---@return lsp.WorkspaceSymbol[] - ['workspace/symbol'] = function(_, request) + ['workspace/symbol'] = function(_, request, callback) assert(request.query == 'foobar') - return { + callback(nil, { { name = 'foobar', kind = 13, ---@type lsp.SymbolKind @@ -4148,7 +4711,7 @@ describe('LSP', function() kind = 12, ---@type lsp.SymbolKind location = _G.mock_locations[2], } - } + }) end, }, }) @@ -4221,10 +4784,10 @@ describe('LSP', function() server:shutdown() return vim.json.decode(init) ]] - eq(result.method, 'initialize') + eq('initialize', result.method) end) - it('can connect to lsp server via rpc.domain_socket_connect', function() - local tmpfile + it('can connect to lsp server via pipe or domain_socket', function() + local tmpfile --- @type string if is_os('win') then tmpfile = '\\\\.\\\\pipe\\pipe.test' else @@ -4248,7 +4811,7 @@ describe('LSP', function() client:close() end)) end) - vim.lsp.start({ name = "dummy", cmd = vim.lsp.rpc.domain_socket_connect(SOCK) }) + vim.lsp.start({ name = "dummy", cmd = vim.lsp.rpc.connect(SOCK) }) vim.wait(1000, function() return init ~= nil end) assert(init, "server must receive `initialize` request") server:close() @@ -4257,7 +4820,7 @@ describe('LSP', function() ]], tmpfile ) - eq(result.method, 'initialize') + eq('initialize', result.method) end) end) @@ -4449,18 +5012,26 @@ describe('LSP', function() string.format('sends notifications when files change (watchfunc=%s)', watchfunc), function() if watchfunc == 'fswatch' then + skip(is_os('win'), 'not supported on windows') + skip(is_os('mac'), 'flaky test on mac') skip( not is_ci() and fn.executable('fswatch') == 0, 'fswatch not installed and not on CI' ) - skip(is_os('win'), 'not supported on windows') - skip(is_os('mac'), 'flaky') end - skip( - is_os('bsd'), - 'kqueue only reports events on watched folder itself, not contained files #26110' - ) + if watchfunc == 'watch' then + skip(is_os('mac'), 'flaky test on mac') + skip( + is_os('bsd'), + 'Stopped working on bsd after 3ca967387c49c754561c3b11a574797504d40f38' + ) + else + skip( + is_os('bsd'), + 'kqueue only reports events on watched folder itself, not contained files #26110' + ) + end local root_dir = tmpname() os.remove(root_dir) @@ -4679,14 +5250,7 @@ describe('LSP', function() ) local function watched_uri(fname) - return exec_lua( - [[ - local root_dir, fname = ... - return vim.uri_from_fname(root_dir .. '/' .. fname) - ]], - root_dir, - fname - ) + return vim.uri_from_fname(root_dir .. '/' .. fname) end eq(4, #result) @@ -4792,13 +5356,7 @@ describe('LSP', function() ) local function watched_uri(fname) - return exec_lua( - [[ - local fname = ... - return vim.uri_from_fname('/dir/' .. fname) - ]], - fname - ) + return vim.uri_from_fname('/dir/' .. fname) end eq(3, #result) @@ -4923,30 +5481,21 @@ describe('LSP', function() root_dir ) - local function watched_uri(fname) - return exec_lua( - [[ - return vim.uri_from_fname(...) - ]], - fname - ) - end - eq(3, #result) eq('workspace/didChangeWatchedFiles', result[3].method) eq({ changes = { { type = exec_lua([[return vim.lsp.protocol.FileChangeType.Created]]), - uri = watched_uri('file1'), + uri = vim.uri_from_fname('file1'), }, { type = exec_lua([[return vim.lsp.protocol.FileChangeType.Changed]]), - uri = watched_uri('file1'), + uri = vim.uri_from_fname('file1'), }, { type = exec_lua([[return vim.lsp.protocol.FileChangeType.Created]]), - uri = watched_uri('file2'), + uri = vim.uri_from_fname('file2'), }, }, }, result[3].params) @@ -5009,7 +5558,7 @@ describe('LSP', function() ) end - eq(true, check_registered(nil)) -- start{_client}() defaults to make_client_capabilities(). + eq(is_os('mac') or is_os('win'), check_registered(nil)) -- start{_client}() defaults to make_client_capabilities(). eq(false, check_registered(vim.empty_dict())) eq( false, diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua index 5bfa566729..978178191c 100644 --- a/test/functional/plugin/man_spec.lua +++ b/test/functional/plugin/man_spec.lua @@ -1,17 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local command, rawfeed = helpers.command, helpers.rawfeed -local clear = helpers.clear -local exec_lua = helpers.exec_lua -local fn = helpers.fn -local nvim_prog = helpers.nvim_prog -local matches = helpers.matches -local write_file = helpers.write_file -local tmpname = helpers.tmpname -local eq = helpers.eq + +local command, feed = n.command, n.feed +local clear = n.clear +local exec_lua = n.exec_lua +local fn = n.fn +local nvim_prog = n.nvim_prog +local matches = t.matches +local write_file = t.write_file +local tmpname = t.tmpname +local eq = t.eq local pesc = vim.pesc -local skip = helpers.skip -local is_ci = helpers.is_ci +local skip = t.skip +local is_ci = t.is_ci -- Collects all names passed to find_path() after attempting ":Man foo". local function get_search_history(name) @@ -44,7 +46,7 @@ describe(':Man', function() end) describe('man.lua: highlight_line()', function() - local screen + local screen --- @type test.functional.ui.screen before_each(function() command('syntax on') @@ -64,7 +66,7 @@ describe(':Man', function() end) it('clears backspaces from text and adds highlights', function() - rawfeed( + feed( [[ ithis i<C-v><C-h>is<C-v><C-h>s a<C-v><C-h>a test with _<C-v><C-h>o_<C-v><C-h>v_<C-v><C-h>e_<C-v><C-h>r_<C-v><C-h>s_<C-v><C-h>t_<C-v><C-h>r_<C-v><C-h>u_<C-v><C-h>c_<C-v><C-h>k text<ESC>]] @@ -90,7 +92,7 @@ describe(':Man', function() end) it('clears escape sequences from text and adds highlights', function() - rawfeed( + feed( [[ ithis <C-v><ESC>[1mis <C-v><ESC>[3ma <C-v><ESC>[4mtest<C-v><ESC>[0m <C-v><ESC>[4mwith<C-v><ESC>[24m <C-v><ESC>[4mescaped<C-v><ESC>[24m <C-v><ESC>[4mtext<C-v><ESC>[24m<ESC>]] @@ -116,7 +118,7 @@ describe(':Man', function() end) it('highlights multibyte text', function() - rawfeed( + feed( [[ ithis i<C-v><C-h>is<C-v><C-h>s あ<C-v><C-h>あ test with _<C-v><C-h>ö_<C-v><C-h>v_<C-v><C-h>e_<C-v><C-h>r_<C-v><C-h>s_<C-v><C-h>t_<C-v><C-h>r_<C-v><C-h>u_<C-v><C-h>̃_<C-v><C-h>c_<C-v><C-h>k te<C-v><ESC>[3mxt¶<C-v><ESC>[0m<ESC>]] @@ -132,7 +134,7 @@ describe(':Man', function() end) it('highlights underscores based on context', function() - rawfeed( + feed( [[ i_<C-v><C-h>_b<C-v><C-h>be<C-v><C-h>eg<C-v><C-h>gi<C-v><C-h>in<C-v><C-h>ns<C-v><C-h>s m<C-v><C-h>mi<C-v><C-h>id<C-v><C-h>d_<C-v><C-h>_d<C-v><C-h>dl<C-v><C-h>le<C-v><C-h>e @@ -150,7 +152,7 @@ describe(':Man', function() end) it('highlights various bullet formats', function() - rawfeed([[ + feed([[ i· ·<C-v><C-h>· +<C-v><C-h>o +<C-v><C-h>+<C-v><C-h>o<C-v><C-h>o double<ESC>]]) @@ -166,7 +168,7 @@ describe(':Man', function() end) it('handles : characters in input', function() - rawfeed([[ + feed([[ i<C-v><C-[>[40m 0 <C-v><C-[>[41m 1 <C-v><C-[>[42m 2 <C-v><C-[>[43m 3 <C-v><C-[>[44m 4 <C-v><C-[>[45m 5 <C-v><C-[>[46m 6 <C-v><C-[>[47m 7 <C-v><C-[>[100m 8 <C-v><C-[>[101m 9 <C-v><C-[>[102m 10 <C-v><C-[>[103m 11 <C-v><C-[>[104m 12 <C-v><C-[>[105m 13 <C-v><C-[>[106m 14 <C-v><C-[>[107m 15 @@ -190,6 +192,7 @@ describe(':Man', function() '--headless', '+autocmd VimLeave * echo "quit works!!"', '+Man!', + '+tag ls', '+call nvim_input("q")', } matches('quit works!!', fn.system(args, { 'manpage contents' })) diff --git a/test/functional/plugin/matchparen_spec.lua b/test/functional/plugin/matchparen_spec.lua index 530afd16e4..ae718ac1bd 100644 --- a/test/functional/plugin/matchparen_spec.lua +++ b/test/functional/plugin/matchparen_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local api = helpers.api -local feed = helpers.feed -local eq = helpers.eq +local clear = n.clear +local command = n.command +local api = n.api +local feed = n.feed +local eq = t.eq describe('matchparen', function() - local screen + local screen --- @type test.functional.ui.screen before_each(function() clear { args = { '-u', 'NORC' } } diff --git a/test/functional/plugin/msgpack_spec.lua b/test/functional/plugin/msgpack_spec.lua index 8511e6c703..1d5d20ec02 100644 --- a/test/functional/plugin/msgpack_spec.lua +++ b/test/functional/plugin/msgpack_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local api = helpers.api -local eq = helpers.eq -local nvim_eval = helpers.eval -local nvim_command = helpers.command -local exc_exec = helpers.exc_exec -local ok = helpers.ok +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local api = n.api +local eq = t.eq +local nvim_eval = n.eval +local nvim_command = n.command +local exc_exec = n.exc_exec +local ok = t.ok local NIL = vim.NIL describe('autoload/msgpack.vim', function() diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index 1c20548321..1c2bcbd497 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear +local t_shada = require('test.functional.shada.testutil') + +local clear = n.clear local eq, api, nvim_eval, nvim_command, exc_exec, fn, nvim_feed = - helpers.eq, helpers.api, helpers.eval, helpers.command, helpers.exc_exec, helpers.fn, helpers.feed -local neq = helpers.neq -local read_file = helpers.read_file + t.eq, n.api, n.eval, n.command, n.exc_exec, n.fn, n.feed +local neq = t.neq +local read_file = t.read_file -local shada_helpers = require('test.functional.shada.helpers') -local get_shada_rw = shada_helpers.get_shada_rw +local get_shada_rw = t_shada.get_shada_rw local function reset(shada_file) clear { args = { '-u', 'NORC', '-i', shada_file or 'NONE' } } @@ -2612,7 +2614,7 @@ end) describe('plugin/shada.vim', function() local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) - local eol = helpers.is_os('win') and '\r\n' or '\n' + local eol = t.is_os('win') and '\r\n' or '\n' before_each(function() -- Note: reset() is called explicitly in each test. os.remove(fname) diff --git a/test/functional/plugin/tohtml_spec.lua b/test/functional/plugin/tohtml_spec.lua index 2ac0fe1fa3..200a5f34b2 100644 --- a/test/functional/plugin/tohtml_spec.lua +++ b/test/functional/plugin/tohtml_spec.lua @@ -1,13 +1,14 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local exec = helpers.exec -local exec_lua = helpers.exec_lua -local eq = helpers.eq -local fn = helpers.fn -local api = helpers.api -local insert = helpers.insert +local clear = n.clear +local exec = n.exec +local exec_lua = n.exec_lua +local eq = t.eq +local fn = n.fn +local api = n.api +local insert = n.insert local function html_syntax_match() local styles = diff --git a/test/functional/plugin/tutor_spec.lua b/test/functional/plugin/tutor_spec.lua index 99538e1db0..9f381d45db 100644 --- a/test/functional/plugin/tutor_spec.lua +++ b/test/functional/plugin/tutor_spec.lua @@ -1,18 +1,20 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed -local is_os = helpers.is_os + +local clear = n.clear +local command = n.command +local feed = n.feed +local is_os = t.is_os describe(':Tutor', function() - local screen + local screen --- @type test.functional.ui.screen before_each(function() clear({ args = { '--clean' } }) command('set cmdheight=0') command('Tutor') - screen = Screen.new(80, 30) + screen = Screen.new(81, 30) screen:set_default_attr_ids({ [0] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray }, [1] = { bold = true }, @@ -20,6 +22,7 @@ describe(':Tutor', function() [3] = { foreground = Screen.colors.SlateBlue }, [4] = { bold = true, foreground = Screen.colors.Brown }, [5] = { bold = true, foreground = Screen.colors.Magenta1 }, + [6] = { italic = true }, }) screen:attach() end) @@ -27,71 +30,109 @@ describe(':Tutor', function() it('applies {unix:…,win:…} transform', function() local expected = is_os('win') and [[ - {0: }^ | - {0: } 3. To verify that a file was retrieved, cursor back and notice that there | - {0: } are now two copies of Lesson 5.3, the original and the retrieved version. | - {0: } | - {0: }{1:NOTE}: You can also read the output of an external command. For example, | - {0: } | - {0: } :r {4:!}dir | - {0: } | - {0: } reads the output of the ls command and puts it below the cursor. | - {0: } | - {0: }{3:#}{5: Lesson 5 SUMMARY} | - {0: } | - {0: } 1. {2::!command} executes an external command. | - {0: } | - {0: } Some useful examples are: | - {0: } :{4:!}dir - shows a directory listing | - {0: } :{4:!}del FILENAME - removes file FILENAME | - {0: } | - {0: } 2. {2::w} FILENAME writes the current Neovim file to disk with | - {0: } name FILENAME. | - {0: } | - {0: } 3. {2:v} motion :w FILENAME saves the Visually selected lines in file | - {0: } FILENAME. | - {0: } | - {0: } 4. {2::r} FILENAME retrieves disk file FILENAME and puts it | - {0: } below the cursor position. | - {0: } | - {0: } 5. {2::r !dir} reads the output of the dir command and | - {0: } puts it below the cursor position. | - {0: } | + {0: }^ | + {0: } 3. To verify that a file was retrieved, cursor back and notice that there | + {0: } are now two copies of Lesson 5.3, the original and the retrieved version. | + {0: } | + {0: }{1:NOTE}: You can also read the output of an external command. For example, | + {0: } | + {0: } :r {4:!}dir | + {0: } | + {0: } reads the output of the ls command and puts it below the cursor. | + {0: } | + {0: }{3:#}{5: Lesson 5 SUMMARY} | + {0: } | + {0: } 1. {2::!command} executes an external command. | + {0: } | + {0: } Some useful examples are: | + {0: } :{4:!}dir - shows a directory listing | + {0: } :{4:!}del FILENAME - removes file FILENAME | + {0: } | + {0: } 2. {2::w} FILENAME writes the current Neovim file to disk with | + {0: } name FILENAME. | + {0: } | + {0: } 3. {2:v} motion :w FILENAME saves the Visually selected lines in file | + {0: } FILENAME. | + {0: } | + {0: } 4. {2::r} FILENAME retrieves disk file FILENAME and puts it | + {0: } below the cursor position. | + {0: } | + {0: } 5. {2::r !dir} reads the output of the dir command and | + {0: } puts it below the cursor position. | + {0: } | ]] or [[ - {0: }^ | - {0: } 3. To verify that a file was retrieved, cursor back and notice that there | - {0: } are now two copies of Lesson 5.3, the original and the retrieved version. | - {0: } | - {0: }{1:NOTE}: You can also read the output of an external command. For example, | - {0: } | - {0: } :r {4:!}ls | - {0: } | - {0: } reads the output of the ls command and puts it below the cursor. | - {0: } | - {0: }{3:#}{5: Lesson 5 SUMMARY} | - {0: } | - {0: } 1. {2::!command} executes an external command. | - {0: } | - {0: } Some useful examples are: | - {0: } :{4:!}ls - shows a directory listing | - {0: } :{4:!}rm FILENAME - removes file FILENAME | - {0: } | - {0: } 2. {2::w} FILENAME writes the current Neovim file to disk with | - {0: } name FILENAME. | - {0: } | - {0: } 3. {2:v} motion :w FILENAME saves the Visually selected lines in file | - {0: } FILENAME. | - {0: } | - {0: } 4. {2::r} FILENAME retrieves disk file FILENAME and puts it | - {0: } below the cursor position. | - {0: } | - {0: } 5. {2::r !ls} reads the output of the ls command and | - {0: } puts it below the cursor position. | - {0: } | + {0: }^ | + {0: } 3. To verify that a file was retrieved, cursor back and notice that there | + {0: } are now two copies of Lesson 5.3, the original and the retrieved version. | + {0: } | + {0: }{1:NOTE}: You can also read the output of an external command. For example, | + {0: } | + {0: } :r {4:!}ls | + {0: } | + {0: } reads the output of the ls command and puts it below the cursor. | + {0: } | + {0: }{3:#}{5: Lesson 5 SUMMARY} | + {0: } | + {0: } 1. {2::!command} executes an external command. | + {0: } | + {0: } Some useful examples are: | + {0: } :{4:!}ls - shows a directory listing | + {0: } :{4:!}rm FILENAME - removes file FILENAME | + {0: } | + {0: } 2. {2::w} FILENAME writes the current Neovim file to disk with | + {0: } name FILENAME. | + {0: } | + {0: } 3. {2:v} motion :w FILENAME saves the Visually selected lines in file | + {0: } FILENAME. | + {0: } | + {0: } 4. {2::r} FILENAME retrieves disk file FILENAME and puts it | + {0: } below the cursor position. | + {0: } | + {0: } 5. {2::r !ls} reads the output of the ls command and | + {0: } puts it below the cursor position. | + {0: } | ]] feed(':700<CR>zt') screen:expect(expected) end) + + it('applies hyperlink highlighting', function() + local expected = [[ + {0: }^ | + {0: }{3:#}{5: CONCLUSION} | + {0: } | + {0: }This was intended to give a brief overview of the Neovim editor, just enough to| + {0: }allow you to use it fairly easily. It is far from complete as Neovim has | + {0: }many many more commands. Consult the help often. | + {0: }There are also countless great tutorials and videos to be found online. | + {0: }Here's a bunch of them: | + {0: } | + {0: }- {6:Learn Vim Progressively}: | + {0: } {2:https://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/} | + {0: }- {6:Learning Vim in 2014}: | + {0: } {2:https://benmccormick.org/learning-vim-in-2014/} | + {0: }- {6:Vimcasts}: | + {0: } {2:http://vimcasts.org/} | + {0: }- {6:Vim Video-Tutorials by Derek Wyatt}: | + {0: } {2:http://derekwyatt.org/vim/tutorials/} | + {0: }- {6:Learn Vimscript the Hard Way}: | + {0: } {2:https://learnvimscriptthehardway.stevelosh.com/} | + {0: }- {6:7 Habits of Effective Text Editing}: | + {0: } {2:https://www.moolenaar.net/habits.html} | + {0: }- {6:vim-galore}: | + {0: } {2:https://github.com/mhinz/vim-galore} | + {0: } | + {0: }If you prefer a book, {6:Practical Vim} by Drew Neil is recommended often | + {0: }(the sequel, {6:Modern Vim}, includes material specific to Neovim). | + {0: } | + {0: }This tutorial was written by Michael C. Pierce and Robert K. Ware, Colorado | + {0: }School of Mines using ideas supplied by Charles Smith, Colorado State | + {0: }University. E-mail: {2:bware@mines.colorado.edu}. | + ]] + + feed(':960<CR>zt') + screen:expect(expected) + end) end) diff --git a/test/functional/plugin/vim_syntax_spec.lua b/test/functional/plugin/vim_syntax_spec.lua index 9396bbce5c..d99a69eab9 100644 --- a/test/functional/plugin/vim_syntax_spec.lua +++ b/test/functional/plugin/vim_syntax_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local api = helpers.api + +local clear = n.clear +local exec = n.exec +local api = n.api describe('Vimscript syntax highlighting', function() - local screen + local screen --- @type test.functional.ui.screen before_each(function() clear() - helpers.add_builddir_to_rtp() + n.add_builddir_to_rtp() exec([[ setfiletype vim syntax on diff --git a/test/functional/preload.lua b/test/functional/preload.lua index 49f1eff0da..e524ab34aa 100644 --- a/test/functional/preload.lua +++ b/test/functional/preload.lua @@ -1,12 +1,10 @@ -- Modules loaded here will NOT be cleared and reloaded by Busted. -- Busted started doing this to help provide more isolation. See issue #62 -- for more information about this. -local helpers = require('test.functional.helpers')(nil) +local t = require('test.testutil') require('test.functional.ui.screen') -local busted = require('busted') -local is_os = helpers.is_os -if is_os('win') then +if t.is_os('win') then local ffi = require('ffi') ffi.cdef [[ typedef int errno_t; @@ -14,26 +12,3 @@ if is_os('win') then ]] ffi.C._set_fmode(0x8000) end - -local testid = (function() - local id = 0 - return function() - id = id + 1 - return id - end -end)() - --- Global before_each. https://github.com/Olivine-Labs/busted/issues/613 -local function before_each(_element, _parent) - local id = ('T%d'):format(testid()) - _G._nvim_test_id = id - return nil, true -end -busted.subscribe({ 'test', 'start' }, before_each, { - -- Ensure our --helper is handled before --output (see busted/runner.lua). - priority = 1, - -- Don't generate a test-id for skipped tests. /shrug - predicate = function(element, _, status) - return not (element.descriptor == 'pending' or status == 'pending') - end, -}) diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua index 0c4fd7aaa0..9e7df0ba6b 100644 --- a/test/functional/provider/clipboard_spec.lua +++ b/test/functional/provider/clipboard_spec.lua @@ -1,12 +1,13 @@ -- Test clipboard provider support -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local feed_command, expect, eq, eval, source = - helpers.feed_command, helpers.expect, helpers.eq, helpers.eval, helpers.source -local command = helpers.command -local api = helpers.api + +local clear, feed, insert = n.clear, n.feed, n.insert +local feed_command, expect, eq, eval, source = n.feed_command, n.expect, t.eq, n.eval, n.source +local command = n.command +local api = n.api local function basic_register_test(noblock) insert('some words') diff --git a/test/functional/provider/define_spec.lua b/test/functional/provider/define_spec.lua index 657f1a0d8a..3945486d98 100644 --- a/test/functional/provider/define_spec.lua +++ b/test/functional/provider/define_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local eval, command = helpers.eval, helpers.command -local eq, run, stop = helpers.eq, helpers.run, helpers.stop -local clear = helpers.clear -local api = helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eval, command = n.eval, n.command +local eq, run, stop = t.eq, n.run, n.stop +local clear = n.clear +local api = n.api local function get_prefix(sync) if sync then @@ -28,7 +30,7 @@ local function runx(sync, handler, on_setup) local function setup_cb(...) on_setup(...) -- need to stop on setup callback because there's two session:request - -- calls in `request/helpers.lua`. The second call will always return + -- calls in `request/testnvim.lua`. The second call will always return -- after pending notification/request callbacks are processed stop() end @@ -324,7 +326,7 @@ local function function_specs_for(fn, sync, first_arg_factory, init) end) it('with range', function() - helpers.insert([[ + n.insert([[ foo bar baz diff --git a/test/functional/provider/nodejs_spec.lua b/test/functional/provider/nodejs_spec.lua index 1769239cb0..a67ad36f79 100644 --- a/test/functional/provider/nodejs_spec.lua +++ b/test/functional/provider/nodejs_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq, clear = helpers.eq, helpers.clear -local missing_provider = helpers.missing_provider -local command = helpers.command -local write_file = helpers.write_file -local eval = helpers.eval -local retry = helpers.retry +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq, clear = t.eq, n.clear +local missing_provider = n.missing_provider +local command = n.command +local write_file = t.write_file +local eval = n.eval +local retry = t.retry do clear() diff --git a/test/functional/provider/perl_spec.lua b/test/functional/provider/perl_spec.lua index e9a031eb07..ea4aaa9ee9 100644 --- a/test/functional/provider/perl_spec.lua +++ b/test/functional/provider/perl_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq, clear = helpers.eq, helpers.clear -local missing_provider = helpers.missing_provider -local command = helpers.command -local write_file = helpers.write_file -local eval = helpers.eval -local retry = helpers.retry -local api = helpers.api -local insert = helpers.insert -local expect = helpers.expect -local feed = helpers.feed +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq, clear = t.eq, n.clear +local missing_provider = n.missing_provider +local command = n.command +local write_file = t.write_file +local eval = n.eval +local retry = t.retry +local api = n.api +local insert = n.insert +local expect = n.expect +local feed = n.feed do clear() diff --git a/test/functional/provider/provider_spec.lua b/test/functional/provider/provider_spec.lua index cccd1a1184..caed30ab0b 100644 --- a/test/functional/provider/provider_spec.lua +++ b/test/functional/provider/provider_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eval = helpers.clear, helpers.eval -local command = helpers.command -local eq = helpers.eq -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eval = n.clear, n.eval +local command = n.command +local eq = t.eq +local pcall_err = t.pcall_err describe('providers', function() before_each(function() diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua index 80b3552e82..71a067d3d2 100644 --- a/test/functional/provider/python3_spec.lua +++ b/test/functional/provider/python3_spec.lua @@ -1,15 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) -local assert_alive = helpers.assert_alive -local eval, command, feed = helpers.eval, helpers.command, helpers.feed -local eq, clear, insert = helpers.eq, helpers.clear, helpers.insert -local expect, write_file = helpers.expect, helpers.write_file -local feed_command = helpers.feed_command -local source = helpers.source -local missing_provider = helpers.missing_provider -local matches = helpers.matches -local pcall_err = helpers.pcall_err -local fn = helpers.fn -local dedent = helpers.dedent +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local assert_alive = n.assert_alive +local eval, command, feed = n.eval, n.command, n.feed +local eq, clear, insert = t.eq, n.clear, n.insert +local expect, write_file = n.expect, t.write_file +local feed_command = n.feed_command +local source = n.source +local missing_provider = n.missing_provider +local matches = t.matches +local pcall_err = t.pcall_err +local fn = n.fn +local dedent = t.dedent do clear() @@ -153,7 +155,7 @@ describe('python3 provider', function() end) it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function() - helpers.add_builddir_to_rtp() + n.add_builddir_to_rtp() source([=[ python3 << EOF import vim diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua index 9b2531a23c..716ef296be 100644 --- a/test/functional/provider/ruby_spec.lua +++ b/test/functional/provider/ruby_spec.lua @@ -1,20 +1,21 @@ -local helpers = require('test.functional.helpers')(after_each) - -local assert_alive = helpers.assert_alive -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local exc_exec = helpers.exc_exec -local expect = helpers.expect -local feed = helpers.feed -local feed_command = helpers.feed_command -local fn = helpers.fn -local insert = helpers.insert -local api = helpers.api -local missing_provider = helpers.missing_provider -local matches = helpers.matches -local write_file = helpers.write_file -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local assert_alive = n.assert_alive +local clear = n.clear +local command = n.command +local eq = t.eq +local exc_exec = n.exc_exec +local expect = n.expect +local feed = n.feed +local feed_command = n.feed_command +local fn = n.fn +local insert = n.insert +local api = n.api +local missing_provider = n.missing_provider +local matches = t.matches +local write_file = t.write_file +local pcall_err = t.pcall_err do clear() @@ -103,7 +104,7 @@ end) describe('ruby provider', function() it('RPC call to expand("<afile>") during BufDelete #5245 #5617', function() - helpers.add_builddir_to_rtp() + n.add_builddir_to_rtp() command([=[autocmd BufDelete * ruby VIM::evaluate('expand("<afile>")')]=]) feed_command('help help') assert_alive() diff --git a/test/functional/script/luacats_grammar_spec.lua b/test/functional/script/luacats_grammar_spec.lua index c3ac9fe722..6d444e1888 100644 --- a/test/functional/script/luacats_grammar_spec.lua +++ b/test/functional/script/luacats_grammar_spec.lua @@ -1,5 +1,6 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq +local t = require('test.testutil') + +local eq = t.eq local grammar = require('scripts/luacats_grammar') @@ -151,4 +152,11 @@ describe('luacats grammar', function() name = '[1]', type = 'integer', }) + + test('@param type `T` this is a generic type', { + desc = 'this is a generic type', + kind = 'param', + name = 'type', + type = '`T`', + }) end) diff --git a/test/functional/script/luacats_parser_spec.lua b/test/functional/script/luacats_parser_spec.lua index e10aa81003..bc87b38eca 100644 --- a/test/functional/script/luacats_parser_spec.lua +++ b/test/functional/script/luacats_parser_spec.lua @@ -1,5 +1,6 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq +local t = require('test.testutil') + +local eq = t.eq local parser = require('scripts/luacats_parser') diff --git a/test/functional/script/text_utils_spec.lua b/test/functional/script/text_utils_spec.lua index 190c617e1d..176c2ef816 100644 --- a/test/functional/script/text_utils_spec.lua +++ b/test/functional/script/text_utils_spec.lua @@ -1,6 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local exec_lua = helpers.exec_lua -local eq = helpers.eq +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local exec_lua = n.exec_lua +local eq = t.eq local function md_to_vimdoc(text, start_indent, indent, text_width) return exec_lua( @@ -28,7 +30,7 @@ end describe('md_to_vimdoc', function() before_each(function() - helpers.clear() + n.clear() end) test('can render para after fenced code', { diff --git a/test/functional/shada/buffers_spec.lua b/test/functional/shada/buffers_spec.lua index 9fead98fed..07b0bf4217 100644 --- a/test/functional/shada/buffers_spec.lua +++ b/test/functional/shada/buffers_spec.lua @@ -1,10 +1,12 @@ -- shada buffer list saving/reading support -local helpers = require('test.functional.helpers')(after_each) -local nvim_command, fn, eq, api = helpers.command, helpers.fn, helpers.eq, helpers.api -local expect_exit = helpers.expect_exit +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local t_shada = require('test.functional.shada.testutil') -local shada_helpers = require('test.functional.shada.helpers') -local reset, clear = shada_helpers.reset, shada_helpers.clear +local nvim_command, fn, eq, api = n.command, n.fn, t.eq, n.api +local expect_exit = n.expect_exit + +local reset, clear = t_shada.reset, t_shada.clear describe('shada support code', function() local testfilename = 'Xtestfile-functional-shada-buffers' diff --git a/test/functional/shada/compatibility_spec.lua b/test/functional/shada/compatibility_spec.lua index bc4e9675c6..98d3884098 100644 --- a/test/functional/shada/compatibility_spec.lua +++ b/test/functional/shada/compatibility_spec.lua @@ -1,18 +1,18 @@ -- ShaDa compatibility support -local helpers = require('test.functional.helpers')(after_each) -local nvim_command, fn, eq = helpers.command, helpers.fn, helpers.eq -local exc_exec = helpers.exc_exec +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local t_shada = require('test.functional.shada.testutil') -local shada_helpers = require('test.functional.shada.helpers') -local reset, clear, get_shada_rw = - shada_helpers.reset, shada_helpers.clear, shada_helpers.get_shada_rw -local read_shada_file = shada_helpers.read_shada_file +local nvim_command, fn, eq = n.command, n.fn, t.eq +local exc_exec = n.exc_exec +local reset, clear, get_shada_rw = t_shada.reset, t_shada.clear, t_shada.get_shada_rw +local read_shada_file = t_shada.read_shada_file local wshada, sdrcmd, shada_fname = get_shada_rw('Xtest-functional-shada-compatibility.shada') local mock_file_path = '/a/b/' local mock_file_path2 = '/d/e/' -if helpers.is_os('win') then +if t.is_os('win') then mock_file_path = 'C:/a/' mock_file_path2 = 'C:/d/' end diff --git a/test/functional/shada/errors_spec.lua b/test/functional/shada/errors_spec.lua index 233f03e5c0..a9084da929 100644 --- a/test/functional/shada/errors_spec.lua +++ b/test/functional/shada/errors_spec.lua @@ -1,10 +1,10 @@ -- ShaDa errors handling support -local helpers = require('test.functional.helpers')(after_each) -local nvim_command, eq, exc_exec = helpers.command, helpers.eq, helpers.exc_exec +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local t_shada = require('test.functional.shada.testutil') -local shada_helpers = require('test.functional.shada.helpers') -local reset, clear, get_shada_rw = - shada_helpers.reset, shada_helpers.clear, shada_helpers.get_shada_rw +local nvim_command, eq, exc_exec = n.command, t.eq, n.exc_exec +local reset, clear, get_shada_rw = t_shada.reset, t_shada.clear, t_shada.get_shada_rw local wshada, sdrcmd, shada_fname, clean = get_shada_rw('Xtest-functional-shada-errors.shada') diff --git a/test/functional/shada/history_spec.lua b/test/functional/shada/history_spec.lua index c8a19bb082..39a5f8df7e 100644 --- a/test/functional/shada/history_spec.lua +++ b/test/functional/shada/history_spec.lua @@ -1,12 +1,13 @@ -- ShaDa history saving/reading support -local helpers = require('test.functional.helpers')(after_each) -local nvim_command, fn, api, nvim_feed, eq = - helpers.command, helpers.fn, helpers.api, helpers.feed, helpers.eq -local assert_alive = helpers.assert_alive -local expect_exit = helpers.expect_exit +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local t_shada = require('test.functional.shada.testutil') -local shada_helpers = require('test.functional.shada.helpers') -local reset, clear = shada_helpers.reset, shada_helpers.clear +local nvim_command, fn, api, nvim_feed, eq = n.command, n.fn, n.api, n.feed, t.eq +local assert_alive = n.assert_alive +local expect_exit = n.expect_exit + +local reset, clear = t_shada.reset, t_shada.clear describe('ShaDa support code', function() before_each(reset) @@ -115,6 +116,12 @@ describe('ShaDa support code', function() nvim_feed('gg0n') eq({ 0, 2, 3, 0 }, fn.getpos('.')) eq(1, api.nvim_get_vvar('searchforward')) + -- Autocommands shouldn't cause search pattern to change + nvim_command('autocmd User * :') + nvim_command('doautocmd User') + nvim_feed('gg0n') + eq({ 0, 2, 3, 0 }, fn.getpos('.')) + eq(1, api.nvim_get_vvar('searchforward')) end) it('dumps and loads last search pattern with offset and backward direction', function() @@ -129,6 +136,12 @@ describe('ShaDa support code', function() nvim_feed('G$n') eq({ 0, 2, 3, 0 }, fn.getpos('.')) eq(0, api.nvim_get_vvar('searchforward')) + -- Autocommands shouldn't cause search pattern to change + nvim_command('autocmd User * :') + nvim_command('doautocmd User') + nvim_feed('G$n') + eq({ 0, 2, 3, 0 }, fn.getpos('.')) + eq(0, api.nvim_get_vvar('searchforward')) end) it('saves v:hlsearch=1', function() diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua index 3f29a02506..d680f0b83b 100644 --- a/test/functional/shada/marks_spec.lua +++ b/test/functional/shada/marks_spec.lua @@ -1,12 +1,14 @@ -- ShaDa marks saving/reading support -local helpers = require('test.functional.helpers')(after_each) -local api, nvim_command, fn, eq = helpers.api, helpers.command, helpers.fn, helpers.eq -local feed = helpers.feed -local exc_exec, exec_capture = helpers.exc_exec, helpers.exec_capture -local expect_exit = helpers.expect_exit +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local t_shada = require('test.functional.shada.testutil') -local shada_helpers = require('test.functional.shada.helpers') -local reset, clear = shada_helpers.reset, shada_helpers.clear +local api, nvim_command, fn, eq = n.api, n.command, n.fn, t.eq +local feed = n.feed +local exc_exec, exec_capture = n.exc_exec, n.exec_capture +local expect_exit = n.expect_exit + +local reset, clear = t_shada.reset, t_shada.clear local nvim_current_line = function() return api.nvim_win_get_cursor(0)[1] @@ -163,17 +165,17 @@ describe('ShaDa support code', function() eq({ 2, 0 }, api.nvim_win_get_cursor(0)) end) - it('is able to dump and restore jump list with different times (slow!)', function() + it('is able to dump and restore jump list with different times', function() nvim_command('edit ' .. testfilename_2) - nvim_command('sleep 2') + nvim_command('sleep 10m') nvim_command('normal! G') - nvim_command('sleep 2') + nvim_command('sleep 10m') nvim_command('normal! gg') - nvim_command('sleep 2') + nvim_command('sleep 10m') nvim_command('edit ' .. testfilename) - nvim_command('sleep 2') + nvim_command('sleep 10m') nvim_command('normal! G') - nvim_command('sleep 2') + nvim_command('sleep 10m') nvim_command('normal! gg') expect_exit(nvim_command, 'qall') reset() @@ -216,7 +218,7 @@ describe('ShaDa support code', function() -- -c temporary sets lnum to zero to make `+/pat` work, so calling setpcmark() -- during -c used to add item with zero lnum to jump list. it('does not create incorrect file for non-existent buffers when writing from -c', function() - local argv = helpers.new_argv { + local argv = n.new_argv { args_rm = { '-i', '--embed', -- no --embed @@ -235,7 +237,7 @@ describe('ShaDa support code', function() end) it('does not create incorrect file for non-existent buffers opened from -c', function() - local argv = helpers.new_argv { + local argv = n.new_argv { args_rm = { '-i', '--embed', -- no --embed diff --git a/test/functional/shada/merging_spec.lua b/test/functional/shada/merging_spec.lua index 1b5c0eab5d..c98678ccbf 100644 --- a/test/functional/shada/merging_spec.lua +++ b/test/functional/shada/merging_spec.lua @@ -1,18 +1,19 @@ -- ShaDa merging data support -local helpers = require('test.functional.helpers')(after_each) -local nvim_command, fn, eq = helpers.command, helpers.fn, helpers.eq -local exc_exec, exec_capture = helpers.exc_exec, helpers.exec_capture -local api = helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local t_shada = require('test.functional.shada.testutil') -local shada_helpers = require('test.functional.shada.helpers') -local reset, clear, get_shada_rw = - shada_helpers.reset, shada_helpers.clear, shada_helpers.get_shada_rw -local read_shada_file = shada_helpers.read_shada_file +local nvim_command, fn, eq = n.command, n.fn, t.eq +local exc_exec, exec_capture = n.exc_exec, n.exec_capture +local api = n.api + +local reset, clear, get_shada_rw = t_shada.reset, t_shada.clear, t_shada.get_shada_rw +local read_shada_file = t_shada.read_shada_file local wshada, sdrcmd, shada_fname = get_shada_rw('Xtest-functional-shada-merging.shada') local mock_file_path = '/a/b/' -if helpers.is_os('win') then +if t.is_os('win') then mock_file_path = 'C:/a/' end @@ -698,9 +699,9 @@ describe('ShaDa marks support code', function() for _, v in ipairs(read_shada_file(shada_fname)) do if v.type == 7 then local name = ('%c'):format(v.value.n) - local t = found[name] or {} - t[v.value.f] = (t[v.value.f] or 0) + 1 - found[name] = t + local _t = found[name] or {} + _t[v.value.f] = (_t[v.value.f] or 0) + 1 + found[name] = _t end end eq({ ['0'] = { [mock_file_path .. '-'] = 1 }, A = { [mock_file_path .. '?'] = 1 } }, found) @@ -1003,7 +1004,7 @@ describe('ShaDa jumps support code', function() eq(jumps[found].line, v.value.l) end end - eq(found, #jumps) + eq(#jumps, found) end) it('merges JUMPLISTSIZE jumps when writing', function() @@ -1018,14 +1019,14 @@ describe('ShaDa jumps support code', function() eq(0, exc_exec(sdrcmd())) shada = '' for i = 1, 101 do - local t = i * 2 + local _t = i * 2 shada = shada .. ('\008\204%c\019\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161l\204%c'):format( - t, - t + _t, + _t ) - jumps[(t > #jumps + 1) and (#jumps + 1) or t] = - { file = '' .. mock_file_path .. 'c', line = t } + jumps[(_t > #jumps + 1) and (#jumps + 1) or _t] = + { file = '' .. mock_file_path .. 'c', line = _t } end wshada(shada) eq(0, exc_exec('wshada ' .. shada_fname)) @@ -1041,7 +1042,7 @@ describe('ShaDa jumps support code', function() eq(jumps[found].line, v.value.l) end end - eq(found, 100) + eq(100, found) end) end) @@ -1132,7 +1133,7 @@ describe('ShaDa changes support code', function() eq(changes[found].line, v.value.l or 1) end end - eq(found, #changes) + eq(#changes, found) end) it('merges JUMPLISTSIZE changes when writing', function() @@ -1149,13 +1150,13 @@ describe('ShaDa changes support code', function() eq(0, exc_exec(sdrcmd())) shada = '' for i = 1, 101 do - local t = i * 2 + local _t = i * 2 shada = shada .. ('\011\204%c\019\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161l\204%c'):format( - t, - t + _t, + _t ) - changes[(t > #changes + 1) and (#changes + 1) or t] = { line = t } + changes[(_t > #changes + 1) and (#changes + 1) or _t] = { line = _t } end wshada(shada) eq(0, exc_exec('wshada ' .. shada_fname)) @@ -1170,7 +1171,7 @@ describe('ShaDa changes support code', function() eq(changes[found].line, v.value.l) end end - eq(found, 100) + eq(100, found) end) it('merges JUMPLISTSIZE changes when writing, with new items between old', function() @@ -1178,11 +1179,11 @@ describe('ShaDa changes support code', function() nvim_command('keepjumps call setline(1, range(202))') local shada = '' for i = 1, 101 do - local t = i * 2 + local _t = i * 2 shada = shada .. ('\011\204%c\019\131\162mX\195\161f\196\006' .. mock_file_path .. 'c\161l\204%c'):format( - t, - t + _t, + _t ) end wshada(shada) @@ -1197,8 +1198,8 @@ describe('ShaDa changes support code', function() changes[i] = { line = i } end for i = 1, 101 do - local t = i * 2 - changes[(t > #changes + 1) and (#changes + 1) or t] = { line = t } + local _t = i * 2 + changes[(_t > #changes + 1) and (#changes + 1) or _t] = { line = _t } end wshada(shada) eq(0, exc_exec('wshada ' .. shada_fname)) @@ -1213,6 +1214,6 @@ describe('ShaDa changes support code', function() eq(changes[found].line, v.value.l) end end - eq(found, 100) + eq(100, found) end) end) diff --git a/test/functional/shada/registers_spec.lua b/test/functional/shada/registers_spec.lua index ef15ab9a05..1e6249807e 100644 --- a/test/functional/shada/registers_spec.lua +++ b/test/functional/shada/registers_spec.lua @@ -1,10 +1,11 @@ -- ShaDa registers saving/reading support -local helpers = require('test.functional.helpers')(after_each) -local nvim_command, fn, eq = helpers.command, helpers.fn, helpers.eq +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local t_shada = require('test.functional.shada.testutil') -local shada_helpers = require('test.functional.shada.helpers') -local reset, clear = shada_helpers.reset, shada_helpers.clear -local expect_exit = helpers.expect_exit +local nvim_command, fn, eq = n.command, n.fn, t.eq +local reset, clear = t_shada.reset, t_shada.clear +local expect_exit = n.expect_exit local setreg = function(name, contents, typ) if type(contents) == 'string' then diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua index 6eb318015d..5debdc6c77 100644 --- a/test/functional/shada/shada_spec.lua +++ b/test/functional/shada/shada_spec.lua @@ -1,18 +1,18 @@ -- Other ShaDa tests -local helpers = require('test.functional.helpers')(after_each) -local api, nvim_command, fn, eq = helpers.api, helpers.command, helpers.fn, helpers.eq -local write_file, spawn, set_session, nvim_prog, exc_exec = - helpers.write_file, helpers.spawn, helpers.set_session, helpers.nvim_prog, helpers.exc_exec -local is_os = helpers.is_os -local skip = helpers.skip - +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local t_shada = require('test.functional.shada.testutil') local uv = vim.uv -local paths = helpers.paths +local paths = t.paths + +local api, nvim_command, fn, eq = n.api, n.command, n.fn, t.eq +local write_file, spawn, set_session, nvim_prog, exc_exec = + t.write_file, n.spawn, n.set_session, n.nvim_prog, n.exc_exec +local is_os = t.is_os +local skip = t.skip -local shada_helpers = require('test.functional.shada.helpers') -local reset, clear, get_shada_rw = - shada_helpers.reset, shada_helpers.clear, shada_helpers.get_shada_rw -local read_shada_file = shada_helpers.read_shada_file +local reset, clear, get_shada_rw = t_shada.reset, t_shada.clear, t_shada.get_shada_rw +local read_shada_file = t_shada.read_shada_file local wshada, _, shada_fname, clean = get_shada_rw('Xtest-functional-shada-shada.shada') diff --git a/test/functional/shada/helpers.lua b/test/functional/shada/testutil.lua index baa27889f3..d9252af5f0 100644 --- a/test/functional/shada/helpers.lua +++ b/test/functional/shada/testutil.lua @@ -1,9 +1,11 @@ -local helpers = require('test.functional.helpers')(nil) -local api = helpers.api -local write_file = helpers.write_file -local concat_tables = helpers.concat_tables +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local tmpname = helpers.tmpname() +local api = n.api +local write_file = t.write_file +local concat_tables = t.concat_tables + +local tmpname = t.tmpname() -- o={ -- args=…, @@ -24,7 +26,7 @@ local function reset(o) elseif o.args then args = concat_tables(args, o.args) end - helpers.clear { + n.clear { args_rm = args_rm, args = args, } @@ -32,7 +34,7 @@ local function reset(o) end local clear = function() - helpers.expect_exit(helpers.command, 'qall!') + n.expect_exit(n.command, 'qall!') os.remove(tmpname) end diff --git a/test/functional/shada/variables_spec.lua b/test/functional/shada/variables_spec.lua index d70f5deded..2fea7278ef 100644 --- a/test/functional/shada/variables_spec.lua +++ b/test/functional/shada/variables_spec.lua @@ -1,11 +1,11 @@ -- ShaDa variables saving/reading support -local helpers = require('test.functional.helpers')(after_each) -local api, fn, nvim_command, eq, eval = - helpers.api, helpers.fn, helpers.command, helpers.eq, helpers.eval -local expect_exit = helpers.expect_exit +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local t_shada = require('test.functional.shada.testutil') -local shada_helpers = require('test.functional.shada.helpers') -local reset, clear = shada_helpers.reset, shada_helpers.clear +local api, fn, nvim_command, eq, eval = n.api, n.fn, n.command, t.eq, n.eval +local expect_exit = n.expect_exit +local reset, clear = t_shada.reset, t_shada.clear describe('ShaDa support code', function() before_each(reset) diff --git a/test/functional/terminal/altscreen_spec.lua b/test/functional/terminal/altscreen_spec.lua index c3be9ec6ca..12c8615799 100644 --- a/test/functional/terminal/altscreen_spec.lua +++ b/test/functional/terminal/altscreen_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) -local thelpers = require('test.functional.terminal.helpers') -local clear, eq, api = helpers.clear, helpers.eq, helpers.api -local feed = helpers.feed -local feed_data = thelpers.feed_data -local enter_altscreen = thelpers.enter_altscreen -local exit_altscreen = thelpers.exit_altscreen +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -if helpers.skip(helpers.is_os('win')) then +local tt = require('test.functional.terminal.testutil') +local clear, eq, api = n.clear, t.eq, n.api +local feed = n.feed +local feed_data = tt.feed_data +local enter_altscreen = tt.enter_altscreen +local exit_altscreen = tt.exit_altscreen + +if t.skip(t.is_os('win')) then return end @@ -15,7 +17,7 @@ describe(':terminal altscreen', function() before_each(function() clear() - screen = thelpers.screen_setup() + screen = tt.screen_setup() feed_data({ 'line1', 'line2', diff --git a/test/functional/terminal/api_spec.lua b/test/functional/terminal/api_spec.lua index 79cc5016da..1f10dda551 100644 --- a/test/functional/terminal/api_spec.lua +++ b/test/functional/terminal/api_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local child_session = require('test.functional.terminal.helpers') -local ok = helpers.ok +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -if helpers.skip(helpers.is_os('win')) then +local tt = require('test.functional.terminal.testutil') +local ok = t.ok + +if t.skip(t.is_os('win')) then return end @@ -11,9 +13,9 @@ describe('api', function() local socket_name = './Xtest_functional_api.sock' before_each(function() - helpers.clear() + n.clear() os.remove(socket_name) - screen = child_session.setup_child_nvim({ + screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -21,7 +23,7 @@ describe('api', function() '--cmd', 'colorscheme vim', '--cmd', - helpers.nvim_set .. ' notermguicolors', + n.nvim_set .. ' notermguicolors', }) end) after_each(function() @@ -39,7 +41,7 @@ describe('api', function() } -- Start the socket from the child nvim. - child_session.feed_data(":echo serverstart('" .. socket_name .. "')\n") + tt.feed_data(":echo serverstart('" .. socket_name .. "')\n") -- Wait for socket creation. screen:expect([[ @@ -49,10 +51,10 @@ describe('api', function() {3:-- TERMINAL --} | ]]) - local socket_session1 = helpers.connect(socket_name) - local socket_session2 = helpers.connect(socket_name) + local socket_session1 = n.connect(socket_name) + local socket_session2 = n.connect(socket_name) - child_session.feed_data('i[tui] insert-mode') + tt.feed_data('i[tui] insert-mode') -- Wait for stdin to be processed. screen:expect([[ [tui] insert-mode{1: } | diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 376b7b849e..96abd9f543 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -1,24 +1,27 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local thelpers = require('test.functional.terminal.helpers') -local assert_alive = helpers.assert_alive -local feed, clear = helpers.feed, helpers.clear -local poke_eventloop = helpers.poke_eventloop -local nvim_prog = helpers.nvim_prog -local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source -local pcall_err = helpers.pcall_err -local eq, neq = helpers.eq, helpers.neq -local api = helpers.api -local retry = helpers.retry -local write_file = helpers.write_file -local command = helpers.command -local exc_exec = helpers.exc_exec -local matches = helpers.matches -local exec_lua = helpers.exec_lua +local tt = require('test.functional.terminal.testutil') + +local assert_alive = n.assert_alive +local feed, clear = n.feed, n.clear +local poke_eventloop = n.poke_eventloop +local nvim_prog = n.nvim_prog +local eval, feed_command, source = n.eval, n.feed_command, n.source +local pcall_err = t.pcall_err +local eq, neq = t.eq, t.neq +local api = n.api +local retry = t.retry +local testprg = n.testprg +local write_file = t.write_file +local command = n.command +local exc_exec = n.exc_exec +local matches = t.matches +local exec_lua = n.exec_lua local sleep = vim.uv.sleep -local fn = helpers.fn -local is_os = helpers.is_os -local skip = helpers.skip +local fn = n.fn +local is_os = t.is_os +local skip = t.skip describe(':terminal buffer', function() local screen @@ -26,7 +29,7 @@ describe(':terminal buffer', function() before_each(function() clear() command('set modifiable swapfile undolevels=20') - screen = thelpers.screen_setup() + screen = tt.screen_setup() end) it('terminal-mode forces various options', function() @@ -54,7 +57,7 @@ describe(':terminal buffer', function() eq({ 0, 'both' }, eval('[&l:cursorline, &l:cursorlineopt]')) end) - it('terminal-mode disables cursorline when cursorlineopt is only set to "line', function() + it('terminal-mode disables cursorline when cursorlineopt is only set to "line"', function() feed([[<C-\><C-N>]]) command('setlocal cursorline cursorlineopt=line') feed('i') @@ -205,22 +208,14 @@ describe(':terminal buffer', function() eq(tbuf, eval('bufnr("%")')) end) - it('term_close() use-after-free #4393', function() - feed_command('terminal yes') - feed([[<C-\><C-n>]]) - feed_command('bdelete!') - end) - describe('handles confirmations', function() it('with :confirm', function() - feed_command('terminal') feed('<c-\\><c-n>') feed_command('confirm bdelete') screen:expect { any = 'Close "term://' } end) it('with &confirm', function() - feed_command('terminal') feed('<c-\\><c-n>') feed_command('bdelete') screen:expect { any = 'E89' } @@ -273,7 +268,7 @@ describe(':terminal buffer', function() it('does not segfault when pasting empty register #13955', function() feed('<c-\\><c-n>') feed_command('put a') -- register a is empty - helpers.assert_alive() + n.assert_alive() end) it([[can use temporary normal mode <c-\><c-o>]], function() @@ -317,13 +312,22 @@ describe(':terminal buffer', function() pcall_err(command, 'write test/functional/fixtures/tty-test.c') ) end) +end) + +describe(':terminal buffer', function() + before_each(clear) + + it('term_close() use-after-free #4393', function() + command('terminal yes') + feed('<Ignore>') -- Add input to separate two RPC requests + command('bdelete!') + end) it('emits TermRequest events #26972', function() - command('new') local term = api.nvim_open_term(0, {}) local termbuf = api.nvim_get_current_buf() - -- Test that autocommand buffer is associated with the terminal buffer, not the current buffer + -- Test that <abuf> is the terminal buffer, not the current buffer command('au TermRequest * let g:termbuf = +expand("<abuf>")') command('wincmd p') @@ -337,7 +341,6 @@ describe(':terminal buffer', function() end) it('TermReqeust synchronization #27572', function() - command('new') command('autocmd! nvim_terminal TermRequest') local term = exec_lua([[ _G.input = {} @@ -364,20 +367,13 @@ describe(':terminal buffer', function() '\027[0n', }, exec_lua('return _G.input')) end) -end) - -describe('No heap-buffer-overflow when using', function() - local testfilename = 'Xtestfile-functional-terminal-buffers_spec' - before_each(function() + it('no heap-buffer-overflow when using termopen(echo) #3161', function() + local testfilename = 'Xtestfile-functional-terminal-buffers_spec' write_file(testfilename, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa') - end) - - after_each(function() - os.remove(testfilename) - end) - - it('termopen(echo) #3161', function() + finally(function() + os.remove(testfilename) + end) feed_command('edit ' .. testfilename) -- Move cursor away from the beginning of the line feed('$') @@ -386,15 +382,36 @@ describe('No heap-buffer-overflow when using', function() assert_alive() feed_command('bdelete!') end) -end) -describe('No heap-buffer-overflow when', function() - it('set nowrap and send long line #11548', function() + it('no heap-buffer-overflow when sending long line with nowrap #11548', function() feed_command('set nowrap') feed_command('autocmd TermOpen * startinsert') feed_command('call feedkeys("4000ai\\<esc>:terminal!\\<cr>")') assert_alive() end) + + it('truncates number of composing characters to 5', function() + local chan = api.nvim_open_term(0, {}) + local composing = ('a̳'):sub(2) + api.nvim_chan_send(chan, 'a' .. composing:rep(8)) + retry(nil, nil, function() + eq('a' .. composing:rep(5), api.nvim_get_current_line()) + end) + end) + + it('handles split UTF-8 sequences #16245', function() + local screen = Screen.new(50, 7) + screen:attach() + fn.termopen({ testprg('shell-test'), 'UTF-8' }) + screen:expect([[ + ^å | + ref: å̲ | + 1: å̲ | + 2: å̲ | + 3: å̲ | + |*2 + ]]) + end) end) describe('on_lines does not emit out-of-bounds line indexes when', function() @@ -431,16 +448,6 @@ describe('on_lines does not emit out-of-bounds line indexes when', function() end) end) -it('terminal truncates number of composing characters to 5', function() - clear() - local chan = api.nvim_open_term(0, {}) - local composing = ('a̳'):sub(2) - api.nvim_chan_send(chan, 'a' .. composing:rep(8)) - retry(nil, nil, function() - eq('a' .. composing:rep(5), api.nvim_get_current_line()) - end) -end) - describe('terminal input', function() before_each(function() clear() @@ -468,7 +475,7 @@ end) describe('terminal input', function() it('sends various special keys with modifiers', function() clear() - local screen = thelpers.setup_child_nvim({ + local screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -477,18 +484,16 @@ describe('terminal input', function() 'colorscheme vim', '--cmd', 'set notermguicolors', - '--cmd', - 'startinsert', + '-c', + 'while 1 | redraw | echo keytrans(getcharstr()) | endwhile', }) - screen:expect { - grid = [[ + screen:expect([[ {1: } | {4:~ }|*3 - {5:[No Name] 0,1 All}| - {3:-- INSERT --} | + {5:[No Name] 0,0-1 All}| + | {3:-- TERMINAL --} | - ]], - } + ]]) for _, key in ipairs({ '<M-Tab>', '<M-CR>', @@ -538,10 +543,14 @@ describe('terminal input', function() '<ScrollWheelLeft>', '<ScrollWheelRight>', }) do - feed('<CR><C-V>' .. key) - retry(nil, nil, function() - eq(key, api.nvim_get_current_line()) - end) + feed(key) + screen:expect(([[ + | + {4:~ }|*3 + {5:[No Name] 0,0-1 All}| + %s{1: }{MATCH: *}| + {3:-- TERMINAL --} | + ]]):format(key)) end end) end) @@ -555,7 +564,7 @@ if is_os('win') then feed_command('set modifiable swapfile undolevels=20') poke_eventloop() local cmd = { 'cmd.exe', '/K', 'PROMPT=$g$s' } - screen = thelpers.screen_setup(nil, cmd) + screen = tt.screen_setup(nil, cmd) end) it('"put" operator sends data normally', function() diff --git a/test/functional/terminal/channel_spec.lua b/test/functional/terminal/channel_spec.lua index 9615534c87..774ff01291 100644 --- a/test/functional/terminal/channel_spec.lua +++ b/test/functional/terminal/channel_spec.lua @@ -1,17 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local command = helpers.command -local pcall_err = helpers.pcall_err -local feed = helpers.feed -local poke_eventloop = helpers.poke_eventloop -local is_os = helpers.is_os -local api = helpers.api -local async_meths = helpers.async_meths -local testprg = helpers.testprg -local assert_alive = helpers.assert_alive + +local clear = n.clear +local eq = t.eq +local eval = n.eval +local command = n.command +local pcall_err = t.pcall_err +local feed = n.feed +local poke_eventloop = n.poke_eventloop +local is_os = t.is_os +local api = n.api +local async_meths = n.async_meths +local testprg = n.testprg +local assert_alive = n.assert_alive describe('terminal channel is closed and later released if', function() local screen @@ -121,6 +123,7 @@ it('chansend sends lines to terminal channel in proper order', function() clear({ args = { '--cmd', 'set laststatus=2' } }) local screen = Screen.new(100, 20) screen:attach() + screen._default_attr_ids = nil local shells = is_os('win') and { 'cmd.exe', 'pwsh.exe -nop', 'powershell.exe -nop' } or { 'sh' } for _, sh in ipairs(shells) do command([[let id = termopen(']] .. sh .. [[')]]) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index 73fd97203e..51c6b12e62 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -1,26 +1,28 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local thelpers = require('test.functional.terminal.helpers') -local feed, clear = helpers.feed, helpers.clear -local testprg, command = helpers.testprg, helpers.command -local eq, eval = helpers.eq, helpers.eval -local matches = helpers.matches -local poke_eventloop = helpers.poke_eventloop -local hide_cursor = thelpers.hide_cursor -local show_cursor = thelpers.show_cursor -local is_os = helpers.is_os -local skip = helpers.skip +local tt = require('test.functional.terminal.testutil') + +local feed, clear = n.feed, n.clear +local testprg, command = n.testprg, n.command +local eq, eval = t.eq, n.eval +local matches = t.matches +local poke_eventloop = n.poke_eventloop +local hide_cursor = tt.hide_cursor +local show_cursor = tt.show_cursor +local is_os = t.is_os +local skip = t.skip describe(':terminal cursor', function() local screen before_each(function() clear() - screen = thelpers.screen_setup() + screen = tt.screen_setup() end) it('moves the screen cursor when focused', function() - thelpers.feed_data('testing cursor') + tt.feed_data('testing cursor') screen:expect([[ tty ready | testing cursor{1: } | @@ -66,7 +68,7 @@ describe(':terminal cursor', function() :set number | ]]) feed('i') - helpers.poke_eventloop() + n.poke_eventloop() screen:expect([[ {7: 1 }tty ready | {7: 2 }rows: 6, cols: 46 | @@ -152,7 +154,7 @@ describe('buffer cursor position is correct in terminal without number column', local screen local function setup_ex_register(str) - screen = thelpers.setup_child_nvim({ + screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -189,7 +191,7 @@ describe('buffer cursor position is correct in terminal without number column', before_each(clear) - describe('in a line with no multibyte characters or trailing spaces,', function() + describe('in a line with no multibyte chars or trailing spaces,', function() before_each(function() setup_ex_register('aaaaaaaa') end) @@ -252,7 +254,7 @@ describe('buffer cursor position is correct in terminal without number column', end) end) - describe('in a line with single-cell multibyte characters and no trailing spaces,', function() + describe('in a line with single-cell multibyte chars and no trailing spaces,', function() before_each(function() setup_ex_register('µµµµµµµµ') end) @@ -315,81 +317,72 @@ describe('buffer cursor position is correct in terminal without number column', end) end) - describe( - 'in a line with single-cell composed multibyte characters and no trailing spaces,', - function() - if skip(is_os('win'), 'Encoding problem?') then - return - end - - before_each(function() - setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳') - end) + describe('in a line with single-cell composed multibyte chars and no trailing spaces,', function() + before_each(function() + setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳') + end) - it('at the end', function() - feed('<C-R>r') - screen:expect([[ + it('at the end', function() + feed('<C-R>r') + screen:expect([[ |*4 Entering Ex mode. Type "visual" to go to Normal mode. | :µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{2:^ } | {3:-- TERMINAL --} | ]]) - eq({ 6, 33 }, eval('nvim_win_get_cursor(0)')) - feed([[<C-\><C-N>]]) - screen:expect([[ + eq({ 6, 33 }, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ |*4 Entering Ex mode. Type "visual" to go to Normal mode. | :µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳{4: } | | ]]) - eq({ 6, 29 }, eval('nvim_win_get_cursor(0)')) - end) + eq({ 6, 29 }, eval('nvim_win_get_cursor(0)')) + end) - it('near the end', function() - feed('<C-R>r<C-X><C-X>') - screen:expect([[ + it('near the end', function() + skip(is_os('win')) + feed('<C-R>r<C-X><C-X>') + screen:expect([[ |*4 Entering Ex mode. Type "visual" to go to Normal mode. | :µ̳µ̳µ̳µ̳µ̳µ̳{2:^µ̳}µ̳ | {3:-- TERMINAL --} | ]]) - eq({ 6, 25 }, eval('nvim_win_get_cursor(0)')) - feed([[<C-\><C-N>]]) - screen:expect([[ + eq({ 6, 25 }, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ |*4 Entering Ex mode. Type "visual" to go to Normal mode. | :µ̳µ̳µ̳µ̳µ̳^µ̳{4:µ̳}µ̳ | | ]]) - eq({ 6, 21 }, eval('nvim_win_get_cursor(0)')) - end) + eq({ 6, 21 }, eval('nvim_win_get_cursor(0)')) + end) - it('near the start', function() - feed('<C-R>r<C-B><C-O>') - screen:expect([[ + it('near the start', function() + skip(is_os('win')) + feed('<C-R>r<C-B><C-O>') + screen:expect([[ |*4 Entering Ex mode. Type "visual" to go to Normal mode. | :µ̳{2:^µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | {3:-- TERMINAL --} | ]]) - eq({ 6, 5 }, eval('nvim_win_get_cursor(0)')) - feed([[<C-\><C-N>]]) - screen:expect([[ + eq({ 6, 5 }, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ |*4 Entering Ex mode. Type "visual" to go to Normal mode. | :^µ̳{4:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | | ]]) - eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) - end) - end - ) - - describe('in a line with double-cell multibyte characters and no trailing spaces,', function() - if skip(is_os('win'), 'Encoding problem?') then - return - end + eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) + end) + end) + describe('in a line with double-cell multibyte chars and no trailing spaces,', function() before_each(function() setup_ex_register('哦哦哦哦哦哦哦哦') end) @@ -478,7 +471,7 @@ describe('buffer cursor position is correct in terminal with number column', fun local screen local function setup_ex_register(str) - screen = thelpers.setup_child_nvim({ + screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -522,7 +515,7 @@ describe('buffer cursor position is correct in terminal with number column', fun command('set number') end) - describe('in a line with no multibyte characters or trailing spaces,', function() + describe('in a line with no multibyte chars or trailing spaces,', function() before_each(function() setup_ex_register('aaaaaaaa') end) @@ -603,7 +596,7 @@ describe('buffer cursor position is correct in terminal with number column', fun end) end) - describe('in a line with single-cell multibyte characters and no trailing spaces,', function() + describe('in a line with single-cell multibyte chars and no trailing spaces,', function() before_each(function() setup_ex_register('µµµµµµµµ') end) @@ -684,20 +677,14 @@ describe('buffer cursor position is correct in terminal with number column', fun end) end) - describe( - 'in a line with single-cell composed multibyte characters and no trailing spaces,', - function() - if skip(is_os('win'), 'Encoding problem?') then - return - end - - before_each(function() - setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳') - end) + describe('in a line with single-cell composed multibyte chars and no trailing spaces,', function() + before_each(function() + setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳') + end) - it('at the end', function() - feed('<C-R>r') - screen:expect([[ + it('at the end', function() + feed('<C-R>r') + screen:expect([[ {7: 1 } | {7: 2 } | {7: 3 } | @@ -706,9 +693,9 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{2:^ } | {3:-- TERMINAL --} | ]]) - eq({ 6, 33 }, eval('nvim_win_get_cursor(0)')) - feed([[<C-\><C-N>]]) - screen:expect([[ + eq({ 6, 33 }, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ {7: 1 } | {7: 2 } | {7: 3 } | @@ -717,12 +704,13 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳{4: } | | ]]) - eq({ 6, 29 }, eval('nvim_win_get_cursor(0)')) - end) + eq({ 6, 29 }, eval('nvim_win_get_cursor(0)')) + end) - it('near the end', function() - feed('<C-R>r<C-X><C-X>') - screen:expect([[ + it('near the end', function() + skip(is_os('win')) + feed('<C-R>r<C-X><C-X>') + screen:expect([[ {7: 1 } | {7: 2 } | {7: 3 } | @@ -731,9 +719,9 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳{2:^µ̳}µ̳ | {3:-- TERMINAL --} | ]]) - eq({ 6, 25 }, eval('nvim_win_get_cursor(0)')) - feed([[<C-\><C-N>]]) - screen:expect([[ + eq({ 6, 25 }, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ {7: 1 } | {7: 2 } | {7: 3 } | @@ -742,12 +730,13 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 6 }:µ̳µ̳µ̳µ̳µ̳^µ̳{4:µ̳}µ̳ | | ]]) - eq({ 6, 21 }, eval('nvim_win_get_cursor(0)')) - end) + eq({ 6, 21 }, eval('nvim_win_get_cursor(0)')) + end) - it('near the start', function() - feed('<C-R>r<C-B><C-O>') - screen:expect([[ + it('near the start', function() + skip(is_os('win')) + feed('<C-R>r<C-B><C-O>') + screen:expect([[ {7: 1 } | {7: 2 } | {7: 3 } | @@ -756,9 +745,9 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 6 }:µ̳{2:^µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | {3:-- TERMINAL --} | ]]) - eq({ 6, 5 }, eval('nvim_win_get_cursor(0)')) - feed([[<C-\><C-N>]]) - screen:expect([[ + eq({ 6, 5 }, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ {7: 1 } | {7: 2 } | {7: 3 } | @@ -767,16 +756,11 @@ describe('buffer cursor position is correct in terminal with number column', fun {7: 6 }:^µ̳{4:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | | ]]) - eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) - end) - end - ) - - describe('in a line with double-cell multibyte characters and no trailing spaces,', function() - if skip(is_os('win'), 'Encoding problem?') then - return - end + eq({ 6, 1 }, eval('nvim_win_get_cursor(0)')) + end) + end) + describe('in a line with double-cell multibyte chars and no trailing spaces,', function() before_each(function() setup_ex_register('哦哦哦哦哦哦哦哦') end) diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua index f7ceb0a68b..24c5eb28e5 100644 --- a/test/functional/terminal/edit_spec.lua +++ b/test/functional/terminal/edit_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + local screen = require('test.functional.ui.screen') -local testprg = helpers.testprg -local command = helpers.command -local fn = helpers.fn -local api = helpers.api -local clear = helpers.clear -local eq = helpers.eq -local matches = helpers.matches +local testprg = n.testprg +local command = n.command +local fn = n.fn +local api = n.api +local clear = n.clear +local eq = t.eq +local matches = t.matches local pesc = vim.pesc describe(':edit term://*', function() diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 92d37fc04a..1039572210 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -1,18 +1,20 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local assert_alive = helpers.assert_alive -local clear, poke_eventloop = helpers.clear, helpers.poke_eventloop -local testprg, source, eq = helpers.testprg, helpers.source, helpers.eq -local feed = helpers.feed -local feed_command, eval = helpers.feed_command, helpers.eval -local fn = helpers.fn -local api = helpers.api -local retry = helpers.retry -local ok = helpers.ok -local command = helpers.command -local skip = helpers.skip -local is_os = helpers.is_os -local is_ci = helpers.is_ci + +local assert_alive = n.assert_alive +local clear, poke_eventloop = n.clear, n.poke_eventloop +local testprg, source, eq = n.testprg, n.source, t.eq +local feed = n.feed +local feed_command, eval = n.feed_command, n.eval +local fn = n.fn +local api = n.api +local retry = t.retry +local ok = t.ok +local command = n.command +local skip = t.skip +local is_os = t.is_os +local is_ci = t.is_ci describe(':terminal', function() local screen @@ -21,6 +23,7 @@ describe(':terminal', function() clear() screen = Screen.new(50, 4) screen:attach({ rgb = false }) + screen._default_attr_ids = nil end) it('does not interrupt Press-ENTER prompt #2748', function() @@ -168,6 +171,7 @@ local function test_terminal_with_fake_shell(backslash) clear() screen = Screen.new(50, 4) screen:attach({ rgb = false }) + screen._default_attr_ids = nil api.nvim_set_option_value('shell', shell_path, {}) api.nvim_set_option_value('shellcmdflag', 'EXE', {}) api.nvim_set_option_value('shellxquote', '', {}) -- win: avoid extra quotes diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua index ec057c6766..4f3d010d02 100644 --- a/test/functional/terminal/highlight_spec.lua +++ b/test/functional/terminal/highlight_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local thelpers = require('test.functional.terminal.helpers') -local feed, clear = helpers.feed, helpers.clear -local api = helpers.api -local testprg, command = helpers.testprg, helpers.command -local nvim_prog_abs = helpers.nvim_prog_abs -local eq, eval = helpers.eq, helpers.eval -local fn = helpers.fn -local nvim_set = helpers.nvim_set -local is_os = helpers.is_os -local skip = helpers.skip +local tt = require('test.functional.terminal.testutil') + +local feed, clear = n.feed, n.clear +local api = n.api +local testprg, command = n.testprg, n.command +local nvim_prog_abs = n.nvim_prog_abs +local fn = n.fn +local nvim_set = n.nvim_set +local is_os = t.is_os +local skip = t.skip describe(':terminal highlight', function() local screen @@ -52,9 +53,9 @@ describe(':terminal highlight', function() describe(title, function() before_each(function() set_attrs_fn() - thelpers.feed_data('text') - thelpers.clear_attrs() - thelpers.feed_data('text') + tt.feed_data('text') + tt.clear_attrs() + tt.feed_data('text') end) local function pass_attrs() @@ -77,7 +78,7 @@ describe(':terminal highlight', function() table.insert(lines, 'line' .. tostring(i)) end table.insert(lines, '') - thelpers.feed_data(lines) + tt.feed_data(lines) screen:expect([[ line4 | line5 | @@ -102,28 +103,28 @@ describe(':terminal highlight', function() end descr('foreground', 1, function() - thelpers.set_fg(45) + tt.set_fg(45) end) descr('background', 2, function() - thelpers.set_bg(46) + tt.set_bg(46) end) descr('foreground and background', 3, function() - thelpers.set_fg(45) - thelpers.set_bg(46) + tt.set_fg(45) + tt.set_bg(46) end) descr('bold, italics, underline and strikethrough', 4, function() - thelpers.set_bold() - thelpers.set_italic() - thelpers.set_underline() - thelpers.set_strikethrough() + tt.set_bold() + tt.set_italic() + tt.set_underline() + tt.set_strikethrough() end) descr('bold and underdouble', 12, function() - thelpers.set_bold() - thelpers.set_underdouble() + tt.set_bold() + tt.set_underdouble() end) descr('italics and undercurl', 13, function() - thelpers.set_italic() - thelpers.set_undercurl() + tt.set_italic() + tt.set_undercurl() end) end) @@ -191,6 +192,109 @@ it(':terminal highlight has lower precedence than editor #9964', function() ]]) end) +it('CursorLine and CursorColumn work in :terminal buffer in Normal mode', function() + clear() + local screen = Screen.new(50, 7) + screen:set_default_attr_ids({ + [1] = { background = Screen.colors.Grey90 }, -- CursorLine, CursorColumn + [2] = { reverse = true }, -- TermCursor + [3] = { bold = true }, -- ModeMsg + [4] = { background = Screen.colors.Grey90, reverse = true }, + [5] = { background = Screen.colors.Red }, + }) + screen:attach() + command(("enew | call termopen(['%s'])"):format(testprg('tty-test'))) + screen:expect([[ + ^tty ready | + |*6 + ]]) + tt.feed_data((' foobar'):rep(30)) + screen:expect([[ + ^tty ready | + foobar foobar foobar foobar foobar foobar foobar | + foobar foobar foobar foobar foobar foobar foobar f| + oobar foobar foobar foobar foobar foobar foobar fo| + obar foobar foobar foobar foobar foobar foobar foo| + bar foobar | + | + ]]) + command('set cursorline cursorcolumn') + feed('j10w') + screen:expect([[ + tty ready {1: } | + foobar foobar{1: }foobar foobar foobar foobar foobar | + {1:foobar foobar ^foobar foobar foobar foobar foobar f}| + oobar foobar f{1:o}obar foobar foobar foobar foobar fo| + obar foobar fo{1:o}bar foobar foobar foobar foobar foo| + bar foobar {1: } | + | + ]]) + -- Entering terminal mode disables 'cursorline' and 'cursorcolumn'. + feed('i') + screen:expect([[ + tty ready | + foobar foobar foobar foobar foobar foobar foobar | + foobar foobar foobar foobar foobar foobar foobar f| + oobar foobar foobar foobar foobar foobar foobar fo| + obar foobar foobar foobar foobar foobar foobar foo| + bar foobar{2: } | + {3:-- TERMINAL --} | + ]]) + -- Leaving terminal mode restores old values. + feed([[<C-\><C-N>]]) + screen:expect([[ + tty ready{1: } | + foobar f{1:o}obar foobar foobar foobar foobar foobar | + foobar fo{1:o}bar foobar foobar foobar foobar foobar f| + oobar foo{1:b}ar foobar foobar foobar foobar foobar fo| + obar foob{1:a}r foobar foobar foobar foobar foobar foo| + {1:bar fooba^r }| + | + ]]) + -- CursorLine and CursorColumn are combined with TermCursorNC. + command('highlight TermCursorNC gui=reverse') + screen:expect([[ + tty ready{1: } | + foobar f{1:o}obar foobar foobar foobar foobar foobar | + foobar fo{1:o}bar foobar foobar foobar foobar foobar f| + oobar foo{1:b}ar foobar foobar foobar foobar foobar fo| + obar foob{1:a}r foobar foobar foobar foobar foobar foo| + {1:bar fooba^r}{4: }{1: }| + | + ]]) + feed('2gg11|') + screen:expect([[ + tty ready {1: } | + {1: foobar fo^obar foobar foobar foobar foobar foobar }| + foobar foo{1:b}ar foobar foobar foobar foobar foobar f| + oobar foob{1:a}r foobar foobar foobar foobar foobar fo| + obar fooba{1:r} foobar foobar foobar foobar foobar foo| + bar foobar{4: } | + | + ]]) + -- TermCursorNC has higher precedence. + command('highlight TermCursorNC gui=NONE guibg=Red') + screen:expect([[ + tty ready {1: } | + {1: foobar fo^obar foobar foobar foobar foobar foobar }| + foobar foo{1:b}ar foobar foobar foobar foobar foobar f| + oobar foob{1:a}r foobar foobar foobar foobar foobar fo| + obar fooba{1:r} foobar foobar foobar foobar foobar foo| + bar foobar{5: } | + | + ]]) + feed('G$') + screen:expect([[ + tty ready{1: } | + foobar f{1:o}obar foobar foobar foobar foobar foobar | + foobar fo{1:o}bar foobar foobar foobar foobar foobar f| + oobar foo{1:b}ar foobar foobar foobar foobar foobar fo| + obar foob{1:a}r foobar foobar foobar foobar foobar foo| + {1:bar fooba^r}{5: }{1: }| + | + ]]) +end) + describe(':terminal highlight forwarding', function() local screen @@ -217,12 +321,12 @@ describe(':terminal highlight forwarding', function() it('will handle cterm and rgb attributes', function() skip(is_os('win')) - thelpers.set_fg(3) - thelpers.feed_data('text') - thelpers.feed_termcode('[38:2:255:128:0m') - thelpers.feed_data('color') - thelpers.clear_attrs() - thelpers.feed_data('text') + tt.set_fg(3) + tt.feed_data('text') + tt.feed_termcode('[38:2:255:128:0m') + tt.feed_data('color') + tt.clear_attrs() + tt.feed_data('text') screen:expect { grid = [[ tty ready | @@ -264,10 +368,10 @@ describe(':terminal highlight with custom palette', function() it('will use the custom color', function() skip(is_os('win')) - thelpers.set_fg(3) - thelpers.feed_data('text') - thelpers.clear_attrs() - thelpers.feed_data('text') + tt.set_fg(3) + tt.feed_data('text') + tt.clear_attrs() + tt.feed_data('text') screen:expect([[ tty ready | {1:text}text{7: } | @@ -276,115 +380,3 @@ describe(':terminal highlight with custom palette', function() ]]) end) end) - -describe('synIDattr()', function() - local screen - before_each(function() - clear() - screen = Screen.new(50, 7) - command('highlight Normal ctermfg=252 guifg=#ff0000 guibg=Black') - -- Salmon #fa8072 Maroon #800000 - command( - 'highlight Keyword ctermfg=79 guifg=Salmon guisp=Maroon cterm=strikethrough gui=strikethrough' - ) - end) - - it('returns cterm-color if RGB-capable UI is _not_ attached', function() - eq('252', eval('synIDattr(hlID("Normal"), "fg")')) - eq('252', eval('synIDattr(hlID("Normal"), "fg#")')) - eq('', eval('synIDattr(hlID("Normal"), "bg")')) - eq('', eval('synIDattr(hlID("Normal"), "bg#")')) - eq('79', eval('synIDattr(hlID("Keyword"), "fg")')) - eq('79', eval('synIDattr(hlID("Keyword"), "fg#")')) - eq('', eval('synIDattr(hlID("Keyword"), "sp")')) - eq('', eval('synIDattr(hlID("Keyword"), "sp#")')) - end) - - it('returns gui-color if "gui" arg is passed', function() - eq('Black', eval('synIDattr(hlID("Normal"), "bg", "gui")')) - eq('Maroon', eval('synIDattr(hlID("Keyword"), "sp", "gui")')) - end) - - it('returns gui-color if RGB-capable UI is attached', function() - screen:attach({ rgb = true }) - eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg")')) - eq('Black', eval('synIDattr(hlID("Normal"), "bg")')) - eq('Salmon', eval('synIDattr(hlID("Keyword"), "fg")')) - eq('Maroon', eval('synIDattr(hlID("Keyword"), "sp")')) - end) - - it('returns #RRGGBB value for fg#/bg#/sp#', function() - screen:attach({ rgb = true }) - eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg#")')) - eq('#000000', eval('synIDattr(hlID("Normal"), "bg#")')) - eq('#fa8072', eval('synIDattr(hlID("Keyword"), "fg#")')) - eq('#800000', eval('synIDattr(hlID("Keyword"), "sp#")')) - end) - - it('returns color number if non-GUI', function() - screen:attach({ rgb = false }) - eq('252', eval('synIDattr(hlID("Normal"), "fg")')) - eq('79', eval('synIDattr(hlID("Keyword"), "fg")')) - end) - - it('returns "1" if group has given highlight attribute', function() - local hl_attrs = { - 'underline', - 'undercurl', - 'underdouble', - 'underdotted', - 'underdashed', - 'strikethrough', - } - for _, hl_attr in ipairs(hl_attrs) do - local context = 'using ' .. hl_attr .. ' attr' - command('highlight Keyword cterm=' .. hl_attr .. ' gui=' .. hl_attr) - eq('', eval('synIDattr(hlID("Normal"), "' .. hl_attr .. '")'), context) - eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '")'), context) - eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '", "gui")'), context) - end - end) -end) - -describe('fg/bg special colors', function() - local screen - before_each(function() - clear() - screen = Screen.new(50, 7) - command('highlight Normal ctermfg=145 ctermbg=16 guifg=#ff0000 guibg=Black') - command('highlight Visual ctermfg=bg ctermbg=fg guifg=bg guibg=fg guisp=bg') - end) - - it('resolve to "Normal" values', function() - eq(eval('synIDattr(hlID("Normal"), "bg")'), eval('synIDattr(hlID("Visual"), "fg")')) - eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "fg#")')) - eq(eval('synIDattr(hlID("Normal"), "fg")'), eval('synIDattr(hlID("Visual"), "bg")')) - eq(eval('synIDattr(hlID("Normal"), "fg#")'), eval('synIDattr(hlID("Visual"), "bg#")')) - eq('bg', eval('synIDattr(hlID("Visual"), "fg", "gui")')) - eq('bg', eval('synIDattr(hlID("Visual"), "fg#", "gui")')) - eq('fg', eval('synIDattr(hlID("Visual"), "bg", "gui")')) - eq('fg', eval('synIDattr(hlID("Visual"), "bg#", "gui")')) - eq('bg', eval('synIDattr(hlID("Visual"), "sp", "gui")')) - eq('bg', eval('synIDattr(hlID("Visual"), "sp#", "gui")')) - end) - - it('resolve to "Normal" values in RGB-capable UI', function() - screen:attach({ rgb = true }) - eq('bg', eval('synIDattr(hlID("Visual"), "fg")')) - eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "fg#")')) - eq('fg', eval('synIDattr(hlID("Visual"), "bg")')) - eq(eval('synIDattr(hlID("Normal"), "fg#")'), eval('synIDattr(hlID("Visual"), "bg#")')) - eq('bg', eval('synIDattr(hlID("Visual"), "sp")')) - eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "sp#")')) - end) - - it('resolve after the "Normal" group is modified', function() - screen:attach({ rgb = true }) - local new_guibg = '#282c34' - local new_guifg = '#abb2bf' - command('highlight Normal guifg=' .. new_guifg .. ' guibg=' .. new_guibg) - eq(new_guibg, eval('synIDattr(hlID("Visual"), "fg#")')) - eq(new_guifg, eval('synIDattr(hlID("Visual"), "bg#")')) - eq(new_guibg, eval('synIDattr(hlID("Visual"), "sp#")')) - end) -end) diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua index 0395d5ee16..ad98dfc6c3 100644 --- a/test/functional/terminal/mouse_spec.lua +++ b/test/functional/terminal/mouse_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) -local thelpers = require('test.functional.terminal.helpers') -local clear, eq, eval = helpers.clear, helpers.eq, helpers.eval -local feed, api, command = helpers.feed, helpers.api, helpers.command -local feed_data = thelpers.feed_data -local is_os = helpers.is_os -local skip = helpers.skip +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local tt = require('test.functional.terminal.testutil') +local clear, eq, eval = n.clear, t.eq, n.eval +local feed, api, command = n.feed, n.api, n.command +local feed_data = tt.feed_data +local is_os = t.is_os +local skip = t.skip describe(':terminal mouse', function() local screen @@ -15,7 +17,7 @@ describe(':terminal mouse', function() command('highlight StatusLine cterm=NONE') command('highlight StatusLineNC cterm=NONE') command('highlight VertSplit cterm=NONE') - screen = thelpers.screen_setup() + screen = tt.screen_setup() local lines = {} for i = 1, 30 do table.insert(lines, 'line' .. tostring(i)) @@ -41,6 +43,7 @@ describe(':terminal mouse', function() end) it('will exit focus and trigger Normal mode mapping on mouse click', function() + feed([[<C-\><C-N>qri]]) command('let g:got_leftmouse = 0') command('nnoremap <LeftMouse> <Cmd>let g:got_leftmouse = 1<CR>') eq('t', eval('mode(1)')) @@ -48,9 +51,12 @@ describe(':terminal mouse', function() feed('<LeftMouse>') eq('nt', eval('mode(1)')) eq(1, eval('g:got_leftmouse')) + feed('q') + eq('i<LeftMouse>', eval('keytrans(@r)')) end) it('will exit focus and trigger Normal mode mapping on mouse click with modifier', function() + feed([[<C-\><C-N>qri]]) command('let g:got_ctrl_leftmouse = 0') command('nnoremap <C-LeftMouse> <Cmd>let g:got_ctrl_leftmouse = 1<CR>') eq('t', eval('mode(1)')) @@ -58,6 +64,8 @@ describe(':terminal mouse', function() feed('<C-LeftMouse>') eq('nt', eval('mode(1)')) eq(1, eval('g:got_ctrl_leftmouse')) + feed('q') + eq('i<C-LeftMouse>', eval('keytrans(@r)')) end) it('will exit focus on <C-\\> + mouse-scroll', function() @@ -89,8 +97,8 @@ describe(':terminal mouse', function() describe('with mouse events enabled by the program', function() before_each(function() - thelpers.enable_mouse() - thelpers.feed_data('mouse enabled\n') + tt.enable_mouse() + tt.feed_data('mouse enabled\n') screen:expect([[ line27 | line28 | @@ -417,8 +425,8 @@ describe(':terminal mouse', function() ]]) -- enabling mouse won't affect interaction with other windows - thelpers.enable_mouse() - thelpers.feed_data('mouse enabled\n') + tt.enable_mouse() + tt.feed_data('mouse enabled\n') screen:expect([[ {7: 27 }line │line30 | {7: 28 }line │rows: 5, cols: 25 | diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index 858e23984d..229a169996 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -1,26 +1,28 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local thelpers = require('test.functional.terminal.helpers') -local clear, eq = helpers.clear, helpers.eq -local feed, testprg = helpers.feed, helpers.testprg -local eval = helpers.eval -local command = helpers.command -local poke_eventloop = helpers.poke_eventloop -local retry = helpers.retry -local api = helpers.api -local feed_data = thelpers.feed_data -local pcall_err = helpers.pcall_err -local exec_lua = helpers.exec_lua -local assert_alive = helpers.assert_alive -local skip = helpers.skip -local is_os = helpers.is_os +local tt = require('test.functional.terminal.testutil') + +local clear, eq = n.clear, t.eq +local feed, testprg = n.feed, n.testprg +local eval = n.eval +local command = n.command +local poke_eventloop = n.poke_eventloop +local retry = t.retry +local api = n.api +local feed_data = tt.feed_data +local pcall_err = t.pcall_err +local exec_lua = n.exec_lua +local assert_alive = n.assert_alive +local skip = t.skip +local is_os = t.is_os describe(':terminal scrollback', function() local screen before_each(function() clear() - screen = thelpers.screen_setup(nil, nil, 30) + screen = tt.screen_setup(nil, nil, 30) end) describe('when the limit is exceeded', function() @@ -361,16 +363,19 @@ describe(':terminal prints more lines than the screen height and exits', functio line8 | line9 | | - [Process exited 0] | - -- TERMINAL -- | + [Process exited 0]{2: } | + {5:-- TERMINAL --} | ]]) feed('<cr>') -- closes the buffer correctly after pressing a key - screen:expect([[ + screen:expect { + grid = [[ ^ | - ~ |*5 + {1:~ }|*5 | - ]]) + ]], + attr_ids = { [1] = { foreground = 12 } }, + } end) end) @@ -394,9 +399,9 @@ describe("'scrollback' option", function() it('set to 0 behaves as 1', function() local screen if is_os('win') then - screen = thelpers.screen_setup(nil, { 'cmd.exe' }, 30) + screen = tt.screen_setup(nil, { 'cmd.exe' }, 30) else - screen = thelpers.screen_setup(nil, { 'sh' }, 30) + screen = tt.screen_setup(nil, { 'sh' }, 30) end api.nvim_set_option_value('scrollback', 0, {}) @@ -411,10 +416,10 @@ describe("'scrollback' option", function() local screen if is_os('win') then command([[let $PROMPT='$$']]) - screen = thelpers.screen_setup(nil, { 'cmd.exe' }, 30) + screen = tt.screen_setup(nil, { 'cmd.exe' }, 30) else command('let $PS1 = "$"') - screen = thelpers.screen_setup(nil, { 'sh' }, 30) + screen = tt.screen_setup(nil, { 'sh' }, 30) end api.nvim_set_option_value('scrollback', 200, {}) @@ -476,7 +481,7 @@ describe("'scrollback' option", function() it('deletes extra lines immediately', function() -- Scrollback is 10 on screen_setup - local screen = thelpers.screen_setup(nil, nil, 30) + local screen = tt.screen_setup(nil, nil, 30) local lines = {} for i = 1, 30 do table.insert(lines, 'line' .. tostring(i)) diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/testutil.lua index 05db1b3c8c..f3fc5d3f93 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/testutil.lua @@ -1,12 +1,13 @@ -- To test tui/input.c, this module spawns `nvim` inside :terminal and sends --- bytes via jobsend(). Note: the functional/helpers.lua test-session methods +-- bytes via jobsend(). Note: the functional/testutil.lua test-session methods -- operate on the _host_ session, _not_ the child session. -local helpers = require('test.functional.helpers')(nil) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local testprg = helpers.testprg -local exec_lua = helpers.exec_lua -local api = helpers.api -local nvim_prog = helpers.nvim_prog + +local testprg = n.testprg +local exec_lua = n.exec_lua +local api = n.api +local nvim_prog = n.nvim_prog local function feed_data(data) if type(data) == 'table' then @@ -30,7 +31,7 @@ local function make_lua_executor(session) end end --- some helpers for controlling the terminal. the codes were taken from +-- some t for controlling the terminal. the codes were taken from -- infocmp xterm-256color which is less what libvterm understands -- civis/cnorm local function hide_cursor() diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 94690524d3..d4628ea626 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -4,33 +4,38 @@ -- "bracketed paste" terminal feature: -- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode -local helpers = require('test.functional.helpers')(after_each) -local thelpers = require('test.functional.terminal.helpers') +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local eq = helpers.eq -local feed_data = thelpers.feed_data -local clear = helpers.clear -local command = helpers.command -local dedent = helpers.dedent -local exec = helpers.exec -local exec_lua = helpers.exec_lua -local testprg = helpers.testprg -local retry = helpers.retry -local nvim_prog = helpers.nvim_prog -local nvim_set = helpers.nvim_set -local ok = helpers.ok -local read_file = helpers.read_file -local fn = helpers.fn -local api = helpers.api -local is_ci = helpers.is_ci -local is_os = helpers.is_os -local new_pipename = helpers.new_pipename -local spawn_argv = helpers.spawn_argv -local set_session = helpers.set_session -local write_file = helpers.write_file -local eval = helpers.eval - -if helpers.skip(is_os('win')) then +local tt = require('test.functional.terminal.testutil') + +local eq = t.eq +local feed_data = tt.feed_data +local clear = n.clear +local command = n.command +local dedent = t.dedent +local exec = n.exec +local exec_lua = n.exec_lua +local testprg = n.testprg +local retry = t.retry +local nvim_prog = n.nvim_prog +local nvim_set = n.nvim_set +local ok = t.ok +local read_file = t.read_file +local fn = n.fn +local api = n.api +local is_ci = t.is_ci +local is_os = t.is_os +local new_pipename = n.new_pipename +local spawn_argv = n.spawn_argv +local set_session = n.set_session +local write_file = t.write_file +local eval = n.eval +local assert_log = t.assert_log + +local testlog = 'Xtest-tui-log' + +if t.skip(is_os('win')) then return end @@ -42,7 +47,7 @@ describe('TUI', function() before_each(function() clear() local child_server = new_pipename() - screen = thelpers.setup_child_nvim({ + screen = tt.setup_child_nvim({ '--listen', child_server, '-u', @@ -61,8 +66,8 @@ describe('TUI', function() | {3:-- TERMINAL --} | ]]) - child_session = helpers.connect(child_server) - child_exec_lua = thelpers.make_lua_executor(child_session) + child_session = n.connect(child_server) + child_exec_lua = tt.make_lua_executor(child_session) end) -- Wait for mode in the child Nvim (avoid "typeahead race" #10826). @@ -83,7 +88,6 @@ describe('TUI', function() end it('rapid resize #7572 #7628', function() - helpers.skip(helpers.is_asan(), 'Test extra unstable with ASAN. See #23762') -- Need buffer rows to provoke the behavior. feed_data(':edit test/functional/fixtures/bigfile.txt\n') screen:expect([[ @@ -1345,15 +1349,15 @@ describe('TUI', function() it('paste: big burst of input', function() feed_data(':set ruler\n') - local t = {} + local q = {} for i = 1, 3000 do - t[i] = 'item ' .. tostring(i) + q[i] = 'item ' .. tostring(i) end feed_data('i') wait_for_mode('i') -- "bracketed paste" - feed_data('\027[200~' .. table.concat(t, '\n') .. '\027[201~') - expect_child_buf_lines(t) + feed_data('\027[200~' .. table.concat(q, '\n') .. '\027[201~') + expect_child_buf_lines(q) feed_data(' end') screen:expect([[ item 2997 | @@ -1486,7 +1490,8 @@ describe('TUI', function() feed_data('\027[201~') -- phase 3 screen:expect_unchanged() local _, rv = child_session:request('nvim_exec_lua', [[return _G.paste_phases]], {}) - eq({ 1, 2, 3 }, rv) + -- In rare cases there may be multiple chunks of phase 2 because of timing. + eq({ 1, 2, 3 }, { rv[1], rv[2], rv[#rv] }) end) it('allows termguicolors to be set at runtime', function() @@ -1588,6 +1593,35 @@ describe('TUI', function() } end) + -- Note: libvterm doesn't support colored underline or undercurl. + it('supports undercurl and underdouble when run in :terminal', function() + screen:set_default_attr_ids({ + [1] = { reverse = true }, + [2] = { bold = true, reverse = true }, + [3] = { bold = true }, + [4] = { foreground = 12 }, + [5] = { undercurl = true }, + [6] = { underdouble = true }, + }) + child_session:request('nvim_set_hl', 0, 'Visual', { undercurl = true }) + feed_data('ifoobar\027V') + screen:expect([[ + {5:fooba}{1:r} | + {4:~ }|*3 + {2:[No Name] [+] }| + {3:-- VISUAL LINE --} | + {3:-- TERMINAL --} | + ]]) + child_session:request('nvim_set_hl', 0, 'Visual', { underdouble = true }) + screen:expect([[ + {6:fooba}{1:r} | + {4:~ }|*3 + {2:[No Name] [+] }| + {3:-- VISUAL LINE --} | + {3:-- TERMINAL --} | + ]]) + end) + it('in nvim_list_uis()', function() -- $TERM in :terminal. local exp_term = is_os('bsd') and 'builtin_xterm' or 'xterm-256color' @@ -1618,7 +1652,7 @@ describe('TUI', function() eq(expected, rv) end) - it('allows grid to assume wider ambiguous-width characters than host terminal #19686', function() + it('allows grid to assume wider ambiwidth chars than host terminal', function() child_session:request( 'nvim_buf_set_lines', 0, @@ -1662,8 +1696,52 @@ describe('TUI', function() screen:expect(singlewidth_screen) end) + it('allows grid to assume wider non-ambiwidth chars than host terminal', function() + child_session:request( + 'nvim_buf_set_lines', + 0, + 0, + -1, + true, + { ('✓'):rep(60), ('✓'):rep(60) } + ) + child_session:request('nvim_set_option_value', 'cursorline', true, {}) + child_session:request('nvim_set_option_value', 'list', true, {}) + child_session:request('nvim_set_option_value', 'listchars', 'eol:$', { win = 0 }) + feed_data('gg') + local singlewidth_screen = [[ + {13:✓}{12:✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓}| + {12:✓✓✓✓✓✓✓✓✓✓}{15:$}{12: }| + ✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓✓| + ✓✓✓✓✓✓✓✓✓✓{4:$} | + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]] + -- When grid assumes "✓" to be double-width but host terminal assumes it to be single-width, + -- the second cell of "✓" is a space and the attributes of the "✓" are applied to it. + local doublewidth_screen = [[ + {13:✓}{12: ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ }| + {12:✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ }| + {12:✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ }{15:$}{12: }| + ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ {4:@@@@}| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]] + screen:expect(singlewidth_screen) + child_session:request('nvim_set_option_value', 'ambiwidth', 'double', {}) + screen:expect_unchanged() + child_session:request('nvim_call_function', 'setcellwidths', { { { 0x2713, 0x2713, 2 } } }) + screen:expect(doublewidth_screen) + child_session:request('nvim_set_option_value', 'ambiwidth', 'single', {}) + screen:expect_unchanged() + child_session:request('nvim_call_function', 'setcellwidths', { { { 0x2713, 0x2713, 1 } } }) + screen:expect(singlewidth_screen) + end) + it('draws correctly when cursor_address overflows #21643', function() - helpers.skip(is_os('mac'), 'FIXME: crashes/errors on macOS') + t.skip(is_os('mac'), 'FIXME: crashes/errors on macOS') screen:try_resize(77, 855) retry(nil, nil, function() eq({ true, 852 }, { child_session:request('nvim_win_get_height', 0) }) @@ -1925,6 +2003,39 @@ describe('TUI', function() ]]) end) + it('invalidated regions are cleared with terminal background attr', function() + local screen = Screen.new(50, 10) + screen:set_default_attr_ids({ [1] = { foreground = Screen.colors.Black } }) + screen:attach() + fn.termopen({ + nvim_prog, + '--clean', + '--cmd', + 'set termguicolors', + '--cmd', + 'sleep 10', + }, { + env = { + VIMRUNTIME = os.getenv('VIMRUNTIME'), + }, + }) + screen:expect({ + grid = [[ + {1:^ }| + {1: }|*8 + | + ]], + }) + screen:try_resize(51, 11) + screen:expect({ + grid = [[ + {1:^ }| + {1: }|*9 + | + ]], + }) + end) + it('argv[0] can be overridden #23953', function() if not exec_lua('return pcall(require, "ffi")') then pending('missing LuaJIT FFI') @@ -1941,7 +2052,7 @@ describe('TUI', function() finally(function() os.remove(script_file) end) - local screen = thelpers.setup_child_nvim({ '--clean', '-l', script_file }) + local screen = tt.setup_child_nvim({ '--clean', '-l', script_file }) screen:expect { grid = [[ {1: } | @@ -1969,7 +2080,7 @@ describe('TUI', function() finally(function() os.remove('testF') end) - local screen = thelpers.screen_setup( + local screen = tt.screen_setup( 0, ('"%s" -u NONE -i NONE --cmd "set noswapfile noshowcmd noruler" --cmd "normal iabc" > /dev/null 2>&1 && cat testF && rm testF'):format( nvim_prog @@ -1990,7 +2101,7 @@ describe('TUI', function() end) it('<C-h> #10134', function() - local screen = thelpers.setup_child_nvim({ + local screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -2023,7 +2134,7 @@ describe('TUI', function() end) it('draws line with many trailing spaces correctly #24955', function() - local screen = thelpers.setup_child_nvim({ + local screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -2062,7 +2173,7 @@ describe('TUI', function() it('no heap-buffer-overflow when changing &columns', function() -- Set a different bg colour and change $TERM to something dumber so the `print_spaces()` -- codepath in `clear_region()` is hit. - local screen = thelpers.setup_child_nvim({ + local screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -2105,7 +2216,7 @@ end) describe('TUI UIEnter/UILeave', function() it('fires exactly once, after VimEnter', function() clear() - local screen = thelpers.setup_child_nvim({ + local screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -2152,7 +2263,7 @@ describe('TUI FocusGained/FocusLost', function() before_each(function() clear() local child_server = new_pipename() - screen = thelpers.setup_child_nvim({ + screen = tt.setup_child_nvim({ '--listen', child_server, '-u', @@ -2172,7 +2283,7 @@ describe('TUI FocusGained/FocusLost', function() | {3:-- TERMINAL --} | ]]) - child_session = helpers.connect(child_server) + child_session = n.connect(child_server) child_session:request( 'nvim_exec2', [[ @@ -2364,14 +2475,14 @@ describe('TUI FocusGained/FocusLost', function() end) end) --- These tests require `thelpers` because --headless/--embed +-- These tests require `tt` because --headless/--embed -- does not initialize the TUI. describe("TUI 't_Co' (terminal colors)", function() local screen local function assert_term_colors(term, colorterm, maxcolors) clear({ env = { TERM = term }, args = {} }) - screen = thelpers.setup_child_nvim({ + screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -2647,14 +2758,14 @@ describe("TUI 't_Co' (terminal colors)", function() end) end) --- These tests require `thelpers` because --headless/--embed +-- These tests require `tt` because --headless/--embed -- does not initialize the TUI. describe("TUI 'term' option", function() local screen local function assert_term(term_envvar, term_expected) clear() - screen = thelpers.setup_child_nvim({ + screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -2702,7 +2813,7 @@ describe("TUI 'term' option", function() end) end) --- These tests require `thelpers` because --headless/--embed +-- These tests require `tt` because --headless/--embed -- does not initialize the TUI. describe('TUI', function() local screen @@ -2714,7 +2825,7 @@ describe('TUI', function() -- Runs (child) `nvim` in a TTY (:terminal), to start the builtin TUI. local function nvim_tui(extra_args) clear() - screen = thelpers.setup_child_nvim({ + screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -2795,7 +2906,7 @@ describe('TUI', function() ]]) local child_server = new_pipename() - screen = thelpers.setup_child_nvim({ + screen = tt.setup_child_nvim({ '--listen', child_server, '-u', @@ -2815,7 +2926,7 @@ describe('TUI', function() screen:expect({ any = '%[No Name%]' }) - local child_session = helpers.connect(child_server) + local child_session = n.connect(child_server) retry(nil, 1000, function() eq({ Tc = true, @@ -2845,7 +2956,7 @@ describe('TUI', function() ]]) local child_server = new_pipename() - screen = thelpers.setup_child_nvim({ + screen = tt.setup_child_nvim({ '--listen', child_server, -- Use --clean instead of -u NONE to load the osc52 plugin @@ -2861,7 +2972,7 @@ describe('TUI', function() screen:expect({ any = '%[No Name%]' }) - local child_session = helpers.connect(child_server) + local child_session = n.connect(child_server) retry(nil, 1000, function() eq('Ms', eval("get(g:, 'xtgettcap', '')")) eq({ true, 'OSC 52' }, { child_session:request('nvim_eval', 'g:clipboard.name') }) @@ -2876,7 +2987,7 @@ describe('TUI bg color', function() command('highlight clear Normal') command('set background=dark') -- set outer Nvim background local child_server = new_pipename() - local screen = thelpers.setup_child_nvim({ + local screen = tt.setup_child_nvim({ '--listen', child_server, '-u', @@ -2889,7 +3000,7 @@ describe('TUI bg color', function() 'set noswapfile', }) screen:expect({ any = '%[No Name%]' }) - local child_session = helpers.connect(child_server) + local child_session = n.connect(child_server) retry(nil, nil, function() eq({ true, 'dark' }, { child_session:request('nvim_eval', '&background') }) end) @@ -2899,7 +3010,7 @@ describe('TUI bg color', function() command('highlight clear Normal') command('set background=light') -- set outer Nvim background local child_server = new_pipename() - local screen = thelpers.setup_child_nvim({ + local screen = tt.setup_child_nvim({ '--listen', child_server, '-u', @@ -2912,7 +3023,7 @@ describe('TUI bg color', function() 'set noswapfile', }) screen:expect({ any = '%[No Name%]' }) - local child_session = helpers.connect(child_server) + local child_session = n.connect(child_server) retry(nil, nil, function() eq({ true, 'light' }, { child_session:request('nvim_eval', '&background') }) end) @@ -2930,7 +3041,7 @@ describe('TUI bg color', function() end, }) ]]) - thelpers.setup_child_nvim({ + tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -2946,7 +3057,7 @@ describe('TUI bg color', function() end) it('triggers OptionSet from automatic background processing', function() - local screen = thelpers.setup_child_nvim({ + local screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -2968,16 +3079,20 @@ describe('TUI bg color', function() end) end) --- These tests require `thelpers` because --headless/--embed +-- These tests require `tt` because --headless/--embed -- does not initialize the TUI. describe('TUI as a client', function() + after_each(function() + os.remove(testlog) + end) + it('connects to remote instance (with its own TUI)', function() local server_super = spawn_argv(false) -- equivalent to clear() local client_super = spawn_argv(true) set_session(server_super) local server_pipe = new_pipename() - local screen_server = thelpers.setup_child_nvim({ + local screen_server = tt.setup_child_nvim({ '--listen', server_pipe, '-u', @@ -3012,7 +3127,7 @@ describe('TUI as a client', function() } set_session(client_super) - local screen_client = thelpers.setup_child_nvim({ + local screen_client = tt.setup_child_nvim({ '--server', server_pipe, '--remote-ui', @@ -3048,7 +3163,7 @@ describe('TUI as a client', function() it('connects to remote instance (--headless)', function() local server = spawn_argv(false) -- equivalent to clear() - local client_super = spawn_argv(true) + local client_super = spawn_argv(true, { env = { NVIM_LOG_FILE = testlog } }) set_session(server) local server_pipe = api.nvim_get_vvar('servername') @@ -3056,7 +3171,7 @@ describe('TUI as a client', function() server:request('nvim_command', 'set notermguicolors') set_session(client_super) - local screen_client = thelpers.setup_child_nvim({ + local screen_client = tt.setup_child_nvim({ '--server', server_pipe, '--remote-ui', @@ -3091,11 +3206,14 @@ describe('TUI as a client', function() client_super:close() server:close() + if is_os('mac') then + assert_log('uv_tty_set_mode failed: Unknown system error %-102', testlog) + end end) it('throws error when no server exists', function() clear() - local screen = thelpers.setup_child_nvim({ + local screen = tt.setup_child_nvim({ '--server', '127.0.0.1:2436546', '--remote-ui', @@ -3116,7 +3234,7 @@ describe('TUI as a client', function() set_session(server_super) local server_pipe = new_pipename() - local screen_server = thelpers.setup_child_nvim({ + local screen_server = tt.setup_child_nvim({ '--listen', server_pipe, '-u', @@ -3160,7 +3278,7 @@ describe('TUI as a client', function() } set_session(client_super) - local screen_client = thelpers.setup_child_nvim({ + local screen_client = tt.setup_child_nvim({ '--server', server_pipe, '--remote-ui', diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua index 3781933cad..f85e26a66d 100644 --- a/test/functional/terminal/window_spec.lua +++ b/test/functional/terminal/window_spec.lua @@ -1,21 +1,23 @@ -local helpers = require('test.functional.helpers')(after_each) -local thelpers = require('test.functional.terminal.helpers') -local feed_data = thelpers.feed_data -local feed, clear = helpers.feed, helpers.clear -local poke_eventloop = helpers.poke_eventloop -local command = helpers.command -local retry = helpers.retry -local eq = helpers.eq -local eval = helpers.eval -local skip = helpers.skip -local is_os = helpers.is_os +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local tt = require('test.functional.terminal.testutil') +local feed_data = tt.feed_data +local feed, clear = n.feed, n.clear +local poke_eventloop = n.poke_eventloop +local command = n.command +local retry = t.retry +local eq = t.eq +local eval = n.eval +local skip = t.skip +local is_os = t.is_os describe(':terminal window', function() local screen before_each(function() clear() - screen = thelpers.screen_setup() + screen = tt.screen_setup() end) it('sets topline correctly #8556', function() @@ -35,6 +37,7 @@ describe(':terminal window', function() describe("with 'number'", function() it('wraps text', function() + skip(is_os('win')) -- todo(clason): unskip when reenabling reflow feed([[<C-\><C-N>]]) feed([[:set numberwidth=1 number<CR>i]]) screen:expect([[ @@ -64,7 +67,7 @@ describe(':terminal window', function() {7: 1 }tty ready | {7: 2 }rows: 6, cols: 48 | {7: 3 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO| - {7: 4 }PQRSTUVWXYZrows: 6, cols: 41 | + {7: 4 }WXYZrows: 6, cols: 41 | {7: 5 }{1: } | {7: 6 } | {3:-- TERMINAL --} | @@ -74,7 +77,7 @@ describe(':terminal window', function() {7: 1 }tty ready | {7: 2 }rows: 6, cols: 48 | {7: 3 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO| - {7: 4 }PQRSTUVWXYZrows: 6, cols: 41 | + {7: 4 }WXYZrows: 6, cols: 41 | {7: 5 } abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN| {7: 6 }OPQRSTUVWXYZ{1: } | {3:-- TERMINAL --} | @@ -84,6 +87,7 @@ describe(':terminal window', function() describe("with 'statuscolumn'", function() it('wraps text', function() + skip(is_os('win')) -- todo(clason): unskip when reenabling reflow command([[set number statuscolumn=++%l\ \ ]]) screen:expect([[ {7:++1 }tty ready | @@ -108,9 +112,9 @@ describe(':terminal window', function() screen:expect([[ {7:++7 } | {7:++8 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR| - {7:++9 }STUVWXYZ | + {7:++9 }TUVWXYZ | {7:++10 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR| - {7:++11 }STUVWXYZrows: 6, cols: 44 | + {7:++11 }TUVWXYZrows: 6, cols: 44 | {7:++12 }{1: } | {3:-- TERMINAL --} | ]]) @@ -174,7 +178,7 @@ describe(':terminal with multigrid', function() before_each(function() clear() - screen = thelpers.screen_setup(0, nil, 50, nil, { ext_multigrid = true }) + screen = tt.screen_setup(0, nil, 50, nil, { ext_multigrid = true }) end) it('resizes to requested size', function() diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua index 17411e2724..04d2e0bca7 100644 --- a/test/functional/terminal/window_split_tab_spec.lua +++ b/test/functional/terminal/window_split_tab_spec.lua @@ -1,16 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) -local thelpers = require('test.functional.terminal.helpers') -local assert_alive = helpers.assert_alive -local clear = helpers.clear -local feed = helpers.feed -local feed_command = helpers.feed_command -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local api = helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local tt = require('test.functional.terminal.testutil') +local assert_alive = n.assert_alive +local clear = n.clear +local feed = n.feed +local feed_command = n.feed_command +local command = n.command +local eq = t.eq +local eval = n.eval +local api = n.api local sleep = vim.uv.sleep -local retry = helpers.retry -local is_os = helpers.is_os +local retry = t.retry +local is_os = t.is_os describe(':terminal', function() local screen @@ -23,7 +25,7 @@ describe(':terminal', function() command('highlight StatusLine cterm=NONE') command('highlight StatusLineNC cterm=NONE') command('highlight VertSplit cterm=NONE') - screen = thelpers.screen_setup(3) + screen = tt.screen_setup(3) end) after_each(function() diff --git a/test/functional/helpers.lua b/test/functional/testnvim.lua index d1d26919a0..6b858e4d69 100644 --- a/test/functional/helpers.lua +++ b/test/functional/testnvim.lua @@ -1,32 +1,33 @@ local uv = vim.uv -local global_helpers = require('test.helpers') +local t = require('test.testutil') local Session = require('test.client.session') local uv_stream = require('test.client.uv_stream') local SocketStream = uv_stream.SocketStream local ChildProcessStream = uv_stream.ChildProcessStream -local check_cores = global_helpers.check_cores -local check_logs = global_helpers.check_logs -local dedent = global_helpers.dedent -local eq = global_helpers.eq -local is_os = global_helpers.is_os -local ok = global_helpers.ok +local check_cores = t.check_cores +local check_logs = t.check_logs +local dedent = t.dedent +local eq = t.eq +local is_os = t.is_os +local ok = t.ok local sleep = uv.sleep -local fail = global_helpers.fail -local module = {} +--- This module uses functions from the context of the test session, i.e. in the context of the +--- nvim being tests. +local M = {} local runtime_set = 'set runtimepath^=./build/lib/nvim/' -module.nvim_prog = (os.getenv('NVIM_PRG') or global_helpers.paths.test_build_dir .. '/bin/nvim') +M.nvim_prog = (os.getenv('NVIM_PRG') or t.paths.test_build_dir .. '/bin/nvim') -- Default settings for the test session. -module.nvim_set = ( +M.nvim_set = ( 'set shortmess+=IS background=light termguicolors noswapfile noautoindent startofline' .. ' laststatus=1 undodir=. directory=. viewdir=. backupdir=.' .. ' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid' ) -module.nvim_argv = { - module.nvim_prog, +M.nvim_argv = { + M.nvim_prog, '-u', 'NONE', '-i', @@ -35,10 +36,10 @@ module.nvim_argv = { '--cmd', runtime_set, '--cmd', - module.nvim_set, - -- Remove default mappings. + M.nvim_set, + -- Remove default user commands and mappings. '--cmd', - 'mapclear | mapclear!', + 'comclear | mapclear | mapclear!', -- Make screentest work after changing to the new default color scheme -- Source 'vim' color scheme without side effects -- TODO: rewrite tests @@ -50,9 +51,9 @@ module.nvim_argv = { } -- Directory containing nvim. -module.nvim_dir = module.nvim_prog:gsub('[/\\][^/\\]+$', '') -if module.nvim_dir == module.nvim_prog then - module.nvim_dir = '.' +M.nvim_dir = M.nvim_prog:gsub('[/\\][^/\\]+$', '') +if M.nvim_dir == M.nvim_prog then + M.nvim_dir = '.' end local prepend_argv --- @type string[]? @@ -84,11 +85,11 @@ if prepend_argv then for i = 1, len do new_nvim_argv[i] = prepend_argv[i] end - for i = 1, #module.nvim_argv do - new_nvim_argv[i + len] = module.nvim_argv[i] + for i = 1, #M.nvim_argv do + new_nvim_argv[i + len] = M.nvim_argv[i] end - module.nvim_argv = new_nvim_argv - module.prepend_argv = prepend_argv + M.nvim_argv = new_nvim_argv + M.prepend_argv = prepend_argv end local session --- @type test.Session? @@ -103,19 +104,19 @@ if not is_os('win') then end) end -function module.get_session() +function M.get_session() return session end -function module.set_session(s) +function M.set_session(s) session = s end --- @param method string --- @param ... any --- @return any -function module.request(method, ...) - assert(session) +function M.request(method, ...) + assert(session, 'no Nvim session') local status, rv = session:request(method, ...) if not status then if loop_running then @@ -132,21 +133,21 @@ end --- @param method string --- @param ... any --- @return any -function module.request_lua(method, ...) - return module.exec_lua([[return vim.api[...](select(2, ...))]], method, ...) +function M.request_lua(method, ...) + return M.exec_lua([[return vim.api[...](select(2, ...))]], method, ...) end --- @param timeout? integer --- @return string? -function module.next_msg(timeout) +function M.next_msg(timeout) assert(session) return session:next_message(timeout or 10000) end -function module.expect_twostreams(msgs1, msgs2) +function M.expect_twostreams(msgs1, msgs2) local pos1, pos2 = 1, 1 while pos1 <= #msgs1 or pos2 <= #msgs2 do - local msg = module.next_msg() + local msg = M.next_msg() if pos1 <= #msgs1 and pcall(eq, msgs1[pos1], msg) then pos1 = pos1 + 1 elseif pos2 <= #msgs2 then @@ -169,7 +170,7 @@ end -- -- ignore: List of ignored event names. -- seqs: List of one or more potential event sequences. -function module.expect_msg_seq(...) +function M.expect_msg_seq(...) if select('#', ...) < 1 then error('need at least 1 argument') end @@ -196,12 +197,12 @@ function module.expect_msg_seq(...) end return string.format('%s\n%s\n%s', err1, string.rep('=', 78), err2) end - local msg_timeout = module.load_adjust(10000) -- Big timeout for ASAN/valgrind. + local msg_timeout = M.load_adjust(10000) -- Big timeout for ASAN/valgrind. for anum = 1, #seqs do local expected_seq = seqs[anum] -- Collect enough messages to compare the next expected sequence. while #actual_seq < #expected_seq do - local msg = module.next_msg(msg_timeout) + local msg = M.next_msg(msg_timeout) local msg_type = msg and msg[2] or nil if msg == nil then error( @@ -246,7 +247,7 @@ local function call_and_stop_on_error(lsession, ...) return result end -function module.set_method_error(err) +function M.set_method_error(err) method_error = err end @@ -256,7 +257,7 @@ end --- @param setup_cb function? --- @param timeout integer --- @return {[1]: integer, [2]: string} -function module.run_session(lsession, request_cb, notification_cb, setup_cb, timeout) +function M.run_session(lsession, request_cb, notification_cb, setup_cb, timeout) local on_request --- @type function? local on_notification --- @type function? local on_setup --- @type function? @@ -296,35 +297,35 @@ function module.run_session(lsession, request_cb, notification_cb, setup_cb, tim return lsession.eof_err end -function module.run(request_cb, notification_cb, setup_cb, timeout) +function M.run(request_cb, notification_cb, setup_cb, timeout) assert(session) - return module.run_session(session, request_cb, notification_cb, setup_cb, timeout) + return M.run_session(session, request_cb, notification_cb, setup_cb, timeout) end -function module.stop() +function M.stop() assert(session):stop() end -function module.nvim_prog_abs() +function M.nvim_prog_abs() -- system(['build/bin/nvim']) does not work for whatever reason. It must -- be executable searched in $PATH or something starting with / or ./. - if module.nvim_prog:match('[/\\]') then - return module.request('nvim_call_function', 'fnamemodify', { module.nvim_prog, ':p' }) + if M.nvim_prog:match('[/\\]') then + return M.request('nvim_call_function', 'fnamemodify', { M.nvim_prog, ':p' }) else - return module.nvim_prog + return M.nvim_prog end end -- Use for commands which expect nvim to quit. -- The first argument can also be a timeout. -function module.expect_exit(fn_or_timeout, ...) +function M.expect_exit(fn_or_timeout, ...) local eof_err_msg = 'EOF was received from Nvim. Likely the Nvim process crashed.' if type(fn_or_timeout) == 'function' then - eq(eof_err_msg, module.pcall_err(fn_or_timeout, ...)) + eq(eof_err_msg, t.pcall_err(fn_or_timeout, ...)) else eq( eof_err_msg, - module.pcall_err(function(timeout, fn, ...) + t.pcall_err(function(timeout, fn, ...) fn(...) assert(session) while session:next_message(timeout) do @@ -342,8 +343,8 @@ end --- @param name string --- @param ... any --- @return any -function module.call_lua(name, ...) - return module.exec_lua([[return vim.call(...)]], name, ...) +function M.call_lua(name, ...) + return M.exec_lua([[return vim.call(...)]], name, ...) end --- Sends user input to Nvim. @@ -351,9 +352,9 @@ end --- @param input string local function nvim_feed(input) while #input > 0 do - local written = module.request('nvim_input', input) + local written = M.request('nvim_input', input) if written == nil then - module.assert_alive() + M.assert_alive() error('crash? (nvim_input returned nil)') end input = input:sub(written + 1) @@ -361,14 +362,7 @@ local function nvim_feed(input) end --- @param ... string -function module.feed(...) - for _, v in ipairs({ ... }) do - nvim_feed(dedent(v)) - end -end - ---- @param ... string -function module.rawfeed(...) +function M.feed(...) for _, v in ipairs({ ... }) do nvim_feed(dedent(v)) end @@ -376,7 +370,7 @@ end ---@param ... string[]? ---@return string[] -function module.merge_args(...) +function M.merge_args(...) local i = 1 local argv = {} --- @type string[] for anum = 1, select('#', ...) do @@ -438,7 +432,7 @@ local function remove_args(args, args_rm) return new_args end -function module.check_close() +function M.check_close() if not session then return end @@ -465,18 +459,18 @@ end --- @param keep boolean --- @param io_extra uv.uv_pipe_t? used for stdin_fd, see :help ui-option --- @return test.Session -function module.spawn(argv, merge, env, keep, io_extra) +function M.spawn(argv, merge, env, keep, io_extra) if not keep then - module.check_close() + M.check_close() end local child_stream = - ChildProcessStream.spawn(merge and module.merge_args(prepend_argv, argv) or argv, env, io_extra) + ChildProcessStream.spawn(merge and M.merge_args(prepend_argv, argv) or argv, env, io_extra) return Session.new(child_stream) end -- Creates a new Session connected by domain socket (named pipe) or TCP. -function module.connect(file_or_address) +function M.connect(file_or_address) local addr, port = string.match(file_or_address, '(.*):(%d+)') local stream = (addr and port) and SocketStream.connect(addr, port) or SocketStream.open(file_or_address) @@ -495,17 +489,17 @@ end -- Example: -- clear('-e') -- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}} -function module.clear(...) - module.set_session(module.spawn_argv(false, ...)) - return module.get_session() +function M.clear(...) + M.set_session(M.spawn_argv(false, ...)) + return M.get_session() end --- same params as clear, but does returns the session instead --- of replacing the default session --- @return test.Session -function module.spawn_argv(keep, ...) - local argv, env, io_extra = module.new_argv(...) - return module.spawn(argv, nil, env, keep, io_extra) +function M.spawn_argv(keep, ...) + local argv, env, io_extra = M.new_argv(...) + return M.spawn(argv, nil, env, keep, io_extra) end --- @class test.new_argv.Opts @@ -521,8 +515,8 @@ end --- @return string[] --- @return string[]? --- @return uv.uv_pipe_t? -function module.new_argv(...) - local args = { unpack(module.nvim_argv) } +function M.new_argv(...) + local args = { unpack(M.nvim_argv) } table.insert(args, '--headless') if _G._nvim_test_id then -- Set the server name to the test-id for logging. #8519 @@ -579,11 +573,11 @@ function module.new_argv(...) end --- @param ... string -function module.insert(...) +function M.insert(...) nvim_feed('i') for _, v in ipairs({ ... }) do local escaped = v:gsub('<', '<lt>') - module.rawfeed(escaped) + M.feed(escaped) end nvim_feed('<ESC>') end @@ -591,7 +585,7 @@ end --- Executes an ex-command by user input. Because nvim_input() is used, Vimscript --- errors will not manifest as client (lua) errors. Use command() for that. --- @param ... string -function module.feed_command(...) +function M.feed_command(...) for _, v in ipairs({ ... }) do if v:sub(1, 1) ~= '/' then -- not a search command, prefix with colon @@ -603,12 +597,12 @@ function module.feed_command(...) end -- @deprecated use nvim_exec2() -function module.source(code) - module.exec(dedent(code)) +function M.source(code) + M.exec(dedent(code)) end -function module.has_powershell() - return module.eval('executable("' .. (is_os('win') and 'powershell' or 'pwsh') .. '")') == 1 +function M.has_powershell() + return M.eval('executable("' .. (is_os('win') and 'powershell' or 'pwsh') .. '")') == 1 end --- Sets Nvim shell to powershell. @@ -616,12 +610,12 @@ end --- @param fake (boolean) If true, a fake will be used if powershell is not --- found on the system. --- @returns true if powershell was found on the system, else false. -function module.set_shell_powershell(fake) - local found = module.has_powershell() +function M.set_shell_powershell(fake) + local found = M.has_powershell() if not fake then assert(found) end - local shell = found and (is_os('win') and 'powershell' or 'pwsh') or module.testprg('pwsh-test') + local shell = found and (is_os('win') and 'powershell' or 'pwsh') or M.testprg('pwsh-test') local cmd = 'Remove-Item -Force ' .. table.concat( is_os('win') and { 'alias:cat', 'alias:echo', 'alias:sleep', 'alias:sort', 'alias:tee' } @@ -629,7 +623,7 @@ function module.set_shell_powershell(fake) ',' ) .. ';' - module.exec([[ + M.exec([[ let &shell = ']] .. shell .. [[' set shellquote= shellxquote= let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ' @@ -644,7 +638,7 @@ end ---@param func function ---@return table<string,function> -function module.create_callindex(func) +function M.create_callindex(func) return setmetatable({}, { --- @param tbl table<any,function> --- @param arg1 string @@ -661,7 +655,7 @@ end --- @param method string --- @param ... any -function module.nvim_async(method, ...) +function M.nvim_async(method, ...) assert(session):notify(method, ...) end @@ -670,27 +664,27 @@ end --- @param name string --- @param ... any --- @return any -function module.call(name, ...) - return module.request('nvim_call_function', name, { ... }) +function M.call(name, ...) + return M.request('nvim_call_function', name, { ... }) end -module.async_meths = module.create_callindex(module.nvim_async) +M.async_meths = M.create_callindex(M.nvim_async) -module.rpc = { - fn = module.create_callindex(module.call), - api = module.create_callindex(module.request), +M.rpc = { + fn = M.create_callindex(M.call), + api = M.create_callindex(M.request), } -module.lua = { - fn = module.create_callindex(module.call_lua), - api = module.create_callindex(module.request_lua), +M.lua = { + fn = M.create_callindex(M.call_lua), + api = M.create_callindex(M.request_lua), } -module.describe_lua_and_rpc = function(describe) +M.describe_lua_and_rpc = function(describe) return function(what, tests) local function d(flavour) describe(string.format('%s (%s)', what, flavour), function(...) - return tests(module[flavour].api, ...) + return tests(M[flavour].api, ...) end) end @@ -700,85 +694,64 @@ module.describe_lua_and_rpc = function(describe) end --- add for typing. The for loop after will overwrite this -module.api = vim.api -module.fn = vim.fn +M.api = vim.api +M.fn = vim.fn -for name, fns in pairs(module.rpc) do +for name, fns in pairs(M.rpc) do --- @diagnostic disable-next-line:no-unknown - module[name] = fns + M[name] = fns end -- Executes an ex-command. Vimscript errors manifest as client (lua) errors, but -- v:errmsg will not be updated. -module.command = module.api.nvim_command +M.command = M.api.nvim_command -- Evaluates a Vimscript expression. -- Fails on Vimscript error, but does not update v:errmsg. -module.eval = module.api.nvim_eval +M.eval = M.api.nvim_eval -function module.poke_eventloop() +function M.poke_eventloop() -- Execute 'nvim_eval' (a deferred function) to -- force at least one main_loop iteration - module.api.nvim_eval('1') + M.api.nvim_eval('1') end -function module.buf_lines(bufnr) - return module.exec_lua('return vim.api.nvim_buf_get_lines((...), 0, -1, false)', bufnr) +function M.buf_lines(bufnr) + return M.exec_lua('return vim.api.nvim_buf_get_lines((...), 0, -1, false)', bufnr) end ---@see buf_lines() -function module.curbuf_contents() - module.poke_eventloop() -- Before inspecting the buffer, do whatever. - return table.concat(module.api.nvim_buf_get_lines(0, 0, -1, true), '\n') +function M.curbuf_contents() + M.poke_eventloop() -- Before inspecting the buffer, do whatever. + return table.concat(M.api.nvim_buf_get_lines(0, 0, -1, true), '\n') end -function module.expect(contents) - return eq(dedent(contents), module.curbuf_contents()) +function M.expect(contents) + return eq(dedent(contents), M.curbuf_contents()) end -function module.expect_any(contents) +function M.expect_any(contents) contents = dedent(contents) - return ok(nil ~= string.find(module.curbuf_contents(), contents, 1, true)) -end - ---- @param expected any[] ---- @param received any[] ---- @param kind string ---- @return any -function module.expect_events(expected, received, kind) - if not pcall(eq, expected, received) then - local msg = 'unexpected ' .. kind .. ' received.\n\n' - - msg = msg .. 'received events:\n' - for _, e in ipairs(received) do - msg = msg .. ' ' .. vim.inspect(e) .. ';\n' - end - msg = msg .. '\nexpected events:\n' - for _, e in ipairs(expected) do - msg = msg .. ' ' .. vim.inspect(e) .. ';\n' - end - fail(msg) - end - return received + return ok(nil ~= string.find(M.curbuf_contents(), contents, 1, true)) end -- Checks that the Nvim session did not terminate. -function module.assert_alive() - assert(2 == module.eval('1+1'), 'crash? request failed') +function M.assert_alive() + assert(2 == M.eval('1+1'), 'crash? request failed') end -- Asserts that buffer is loaded and visible in the current tabpage. -function module.assert_visible(bufnr, visible) +function M.assert_visible(bufnr, visible) assert(type(visible) == 'boolean') - eq(visible, module.api.nvim_buf_is_loaded(bufnr)) + eq(visible, M.api.nvim_buf_is_loaded(bufnr)) if visible then assert( - -1 ~= module.fn.bufwinnr(bufnr), + -1 ~= M.fn.bufwinnr(bufnr), 'expected buffer to be visible in current tabpage: ' .. tostring(bufnr) ) else assert( - -1 == module.fn.bufwinnr(bufnr), + -1 == M.fn.bufwinnr(bufnr), 'expected buffer NOT visible in current tabpage: ' .. tostring(bufnr) ) end @@ -796,7 +769,7 @@ local function do_rmdir(path) for file in vim.fs.dir(path) do if file ~= '.' and file ~= '..' then local abspath = path .. '/' .. file - if global_helpers.isdir(abspath) then + if t.isdir(abspath) then do_rmdir(abspath) -- recurse else local ret, err = os.remove(abspath) @@ -806,7 +779,7 @@ local function do_rmdir(path) else -- Try Nvim delete(): it handles `readonly` attribute on Windows, -- and avoids Lua cross-version/platform incompatibilities. - if -1 == module.call('delete', abspath) then + if -1 == M.call('delete', abspath) then local hint = (is_os('win') and ' (hint: try :%bwipeout! before rmdir())' or '') error('delete() failed' .. hint .. ': ' .. abspath) end @@ -823,12 +796,12 @@ end local start_dir = uv.cwd() -function module.rmdir(path) +function M.rmdir(path) local ret, _ = pcall(do_rmdir, path) if not ret and is_os('win') then -- Maybe "Permission denied"; try again after changing the nvim -- process to the top-level directory. - module.command([[exe 'cd '.fnameescape(']] .. start_dir .. "')") + M.command([[exe 'cd '.fnameescape(']] .. start_dir .. "')") ret, _ = pcall(do_rmdir, path) end -- During teardown, the nvim process may not exit quickly enough, then rmdir() @@ -839,94 +812,65 @@ function module.rmdir(path) end end -function module.exc_exec(cmd) - module.command(([[ +function M.exc_exec(cmd) + M.command(([[ try execute "%s" catch let g:__exception = v:exception endtry ]]):format(cmd:gsub('\n', '\\n'):gsub('[\\"]', '\\%0'))) - local ret = module.eval('get(g:, "__exception", 0)') - module.command('unlet! g:__exception') + local ret = M.eval('get(g:, "__exception", 0)') + M.command('unlet! g:__exception') return ret end ---- @param cond boolean ---- @param reason string ---- @return boolean -function module.skip(cond, reason) - if cond then - --- @type fun(reason: string) - local pending = getfenv(2).pending - pending(reason or 'FIXME') - return true - 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) - if pending_fn == nil or type(pending_fn) ~= type(function() end) then - error('invalid pending_fn') - end - if cond then - pending_fn('skipped (test is fragile on this system)', function() end) - return true - elseif os.getenv('TEST_SKIP_FRAGILE') then - pending_fn('skipped (TEST_SKIP_FRAGILE)', function() end) - return true - end - return false -end - -function module.exec(code) - module.api.nvim_exec2(code, {}) +function M.exec(code) + M.api.nvim_exec2(code, {}) end --- @param code string --- @return string -function module.exec_capture(code) - return module.api.nvim_exec2(code, { output = true }).output +function M.exec_capture(code) + return M.api.nvim_exec2(code, { output = true }).output end --- @param code string --- @return any -function module.exec_lua(code, ...) - return module.api.nvim_exec_lua(code, { ... }) +function M.exec_lua(code, ...) + return M.api.nvim_exec_lua(code, { ... }) end -function module.get_pathsep() +function M.get_pathsep() return is_os('win') and '\\' or '/' end --- Gets the filesystem root dir, namely "/" or "C:/". -function module.pathroot() +function M.pathroot() local pathsep = package.config:sub(1, 1) - return is_os('win') and (module.nvim_dir:sub(1, 2) .. pathsep) or '/' + return is_os('win') and (M.nvim_dir:sub(1, 2) .. pathsep) or '/' end --- Gets the full `…/build/bin/{name}` path of a test program produced by --- `test/functional/fixtures/CMakeLists.txt`. --- --- @param name (string) Name of the test program. -function module.testprg(name) - local ext = module.is_os('win') and '.exe' or '' - return ('%s/%s%s'):format(module.nvim_dir, name, ext) +function M.testprg(name) + local ext = is_os('win') and '.exe' or '' + return ('%s/%s%s'):format(M.nvim_dir, name, ext) end -function module.is_asan() - local version = module.eval('execute("verbose version")') +function M.is_asan() + local version = M.eval('execute("verbose version")') return version:match('-fsanitize=[a-z,]*address') end -- Returns a valid, platform-independent Nvim listen address. -- Useful for communicating with child instances. -function module.new_pipename() +function M.new_pipename() -- HACK: Start a server temporarily, get the name, then stop it. - local pipename = module.eval('serverstart()') - module.fn.serverstop(pipename) + local pipename = M.eval('serverstart()') + M.fn.serverstop(pipename) -- Remove the pipe so that trying to connect to it without a server listening -- will be an error instead of a hang. os.remove(pipename) @@ -935,24 +879,24 @@ end --- @param provider string --- @return string|boolean? -function module.missing_provider(provider) +function M.missing_provider(provider) if provider == 'ruby' or provider == 'perl' then --- @type string? - local e = module.exec_lua("return {require('vim.provider." .. provider .. "').detect()}")[2] + local e = M.exec_lua("return {require('vim.provider." .. provider .. "').detect()}")[2] return e ~= '' and e or false elseif provider == 'node' then --- @type string? - local e = module.fn['provider#node#Detect']()[2] + local e = M.fn['provider#node#Detect']()[2] return e ~= '' and e or false elseif provider == 'python' then - return module.exec_lua([[return {require('vim.provider.python').detect_by_module('neovim')}]])[2] + return M.exec_lua([[return {require('vim.provider.python').detect_by_module('neovim')}]])[2] end assert(false, 'Unknown provider: ' .. provider) end --- @param obj string|table --- @return any -function module.alter_slashes(obj) +function M.alter_slashes(obj) if not is_os('win') then return obj end @@ -963,7 +907,7 @@ function module.alter_slashes(obj) --- @cast obj table<any,any> local ret = {} --- @type table<any,any> for k, v in pairs(obj) do - ret[k] = module.alter_slashes(v) + ret[k] = M.alter_slashes(v) end return ret end @@ -971,28 +915,28 @@ function module.alter_slashes(obj) end local load_factor = 1 -if global_helpers.is_ci() then +if t.is_ci() then -- Compute load factor only once (but outside of any tests). - module.clear() - module.request('nvim_command', 'source test/old/testdir/load.vim') - load_factor = module.request('nvim_eval', 'g:test_load_factor') + M.clear() + M.request('nvim_command', 'source test/old/testdir/load.vim') + load_factor = M.request('nvim_eval', 'g:test_load_factor') end --- @param num number --- @return number -function module.load_adjust(num) +function M.load_adjust(num) return math.ceil(num * load_factor) end --- @param ctx table<string,any> --- @return table -function module.parse_context(ctx) +function M.parse_context(ctx) local parsed = {} --- @type table<string,any> for _, item in ipairs({ 'regs', 'jumps', 'bufs', 'gvars' }) do --- @param v any parsed[item] = vim.tbl_filter(function(v) return type(v) == 'table' - end, module.call('msgpackparse', ctx[item])) + end, M.call('msgpackparse', ctx[item])) end parsed['bufs'] = parsed['bufs'][1] --- @param v any @@ -1004,15 +948,15 @@ function module.parse_context(ctx) end, parsed) end -function module.add_builddir_to_rtp() +function M.add_builddir_to_rtp() -- Add runtime from build dir for doc/tags (used with :help). - module.command(string.format([[set rtp+=%s/runtime]], module.paths.test_build_dir)) + M.command(string.format([[set rtp+=%s/runtime]], t.paths.test_build_dir)) end --- Kill (reap) a process by PID. --- @param pid string --- @return boolean? -function module.os_kill(pid) +function M.os_kill(pid) return os.execute( ( is_os('win') and 'taskkill /f /t /pid ' .. pid .. ' > nul' @@ -1024,15 +968,33 @@ end --- Create folder with non existing parents --- @param path string --- @return boolean? -function module.mkdir_p(path) +function M.mkdir_p(path) return os.execute((is_os('win') and 'mkdir ' .. path or 'mkdir -p ' .. path)) end ---- @class test.functional.helpers: test.helpers -module = vim.tbl_extend('error', module, global_helpers) +local testid = (function() + local id = 0 + return function() + id = id + 1 + return id + end +end)() + +return function() + local g = getfenv(2) + + --- @type function? + local before_each = g.before_each + --- @type function? + local after_each = g.after_each + + if before_each then + before_each(function() + local id = ('T%d'):format(testid()) + _G._nvim_test_id = id + end) + end ---- @return test.functional.helpers -return function(after_each) if after_each then after_each(function() check_logs() @@ -1047,5 +1009,5 @@ return function(after_each) end end) end - return module + return M end diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua index 9428432f66..a7f278aa01 100644 --- a/test/functional/treesitter/fold_spec.lua +++ b/test/functional/treesitter/fold_spec.lua @@ -1,13 +1,15 @@ -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 command = helpers.command -local feed = helpers.feed -local poke_eventloop = helpers.poke_eventloop +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local clear = n.clear +local eq = t.eq +local insert = n.insert +local exec_lua = n.exec_lua +local command = n.command +local feed = n.feed +local poke_eventloop = n.poke_eventloop + before_each(clear) describe('treesitter foldexpr', function() @@ -404,6 +406,28 @@ t3]]) }, get_fold_levels()) end) + it('handles quantified patterns', function() + insert([[ +import hello +import hello +import hello +import hello +import hello +import hello]]) + + exec_lua([[vim.treesitter.query.set('python', 'folds', '(import_statement)+ @fold')]]) + parse('python') + + eq({ + [1] = '>1', + [2] = '1', + [3] = '1', + [4] = '1', + [5] = '1', + [6] = '1', + }, get_fold_levels()) + end) + it('updates folds in all windows', function() local screen = Screen.new(60, 48) screen:attach() diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 2bf230fe69..69984b3233 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -1,13 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() 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 command = helpers.command -local api = helpers.api -local eq = helpers.eq +local clear = n.clear +local insert = n.insert +local exec_lua = n.exec_lua +local feed = n.feed +local command = n.command +local api = n.api +local eq = t.eq before_each(clear) @@ -483,7 +484,7 @@ describe('treesitter highlighting (C)', function() exec_lua [[ vim.treesitter.language.register("c", "foo") local parser = vim.treesitter.get_parser(0, "c", { - injections = {c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "fOO")) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "fOO"))'} + injections = {c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "foo")) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "foo"))'} }) local highlighter = vim.treesitter.highlighter test_hl = highlighter.new(parser, {queries = {c = hl_query}}) @@ -681,6 +682,12 @@ describe('treesitter highlighting (C)', function() ((identifier) @Identifier (#set! conceal "") (#eq? @Identifier "lstate")) + + ((call_expression + function: (identifier) @function + arguments: (argument_list) @arguments) + (#eq? @function "multiqueue_put") + (#set! @function conceal "V")) ]]}}) ]=] @@ -697,7 +704,7 @@ describe('treesitter highlighting (C)', function() | LuaRef cb = nlua_ref(, 1); | | - multiqueue_put(main_loop.events, nlua_schedule_event, | + {11:V}(main_loop.events, nlua_schedule_event, | 1, (void *)(ptrdiff_t)cb); | return 0; | ^} | @@ -756,6 +763,75 @@ describe('treesitter highlighting (C)', function() ]], } end) + + it('gives higher priority to more specific captures #27895', function() + insert([[ + void foo(int *bar); + ]]) + + local query = [[ + "*" @operator + + (parameter_declaration + declarator: (pointer_declarator) @variable.parameter) + ]] + + exec_lua( + [[ + local query = ... + vim.treesitter.query.set('c', 'highlights', query) + vim.treesitter.highlighter.new(vim.treesitter.get_parser(0, 'c')) + ]], + query + ) + + screen:expect { + grid = [[ + void foo(int {4:*}{11:bar}); | + ^ | + {1:~ }|*15 + | + ]], + } + end) +end) + +describe('treesitter highlighting (lua)', function() + local screen + + before_each(function() + screen = Screen.new(65, 18) + screen:attach() + screen:set_default_attr_ids { + [1] = { bold = true, foreground = Screen.colors.Blue }, + [2] = { foreground = Screen.colors.DarkCyan }, + [3] = { foreground = Screen.colors.Magenta }, + [4] = { foreground = Screen.colors.SlateBlue }, + [5] = { bold = true, foreground = Screen.colors.Brown }, + } + end) + + it('supports language injections', function() + insert [[ + local ffi = require('ffi') + ffi.cdef("int (*fun)(int, char *);") + ]] + + exec_lua [[ + vim.bo.filetype = 'lua' + vim.treesitter.start() + ]] + + screen:expect { + grid = [[ + {5:local} {2:ffi} {5:=} {4:require(}{3:'ffi'}{4:)} | + {2:ffi}{4:.}{2:cdef}{4:(}{3:"}{4:int}{3: }{4:(}{5:*}{3:fun}{4:)(int,}{3: }{4:char}{3: }{5:*}{4:);}{3:"}{4:)} | + ^ | + {1:~ }|*14 + | + ]], + } + end) end) describe('treesitter highlighting (help)', function() @@ -788,7 +864,7 @@ describe('treesitter highlighting (help)', function() screen:expect { grid = [[ - {1:>ruby} | + {1:>}{3:ruby} | {1: -- comment} | {1: local this_is = 'actually_lua'} | {1:<} | @@ -797,11 +873,11 @@ describe('treesitter highlighting (help)', function() ]], } - helpers.api.nvim_buf_set_text(0, 0, 1, 0, 5, { 'lua' }) + n.api.nvim_buf_set_text(0, 0, 1, 0, 5, { 'lua' }) screen:expect { grid = [[ - {1:>lua} | + {1:>}{3:lua} | {1: -- comment} | {1: }{3:local}{1: }{4:this_is}{1: }{3:=}{1: }{5:'actually_lua'} | {1:<} | @@ -810,11 +886,11 @@ describe('treesitter highlighting (help)', function() ]], } - helpers.api.nvim_buf_set_text(0, 0, 1, 0, 4, { 'ruby' }) + n.api.nvim_buf_set_text(0, 0, 1, 0, 4, { 'ruby' }) screen:expect { grid = [[ - {1:>ruby} | + {1:>}{3:ruby} | {1: -- comment} | {1: local this_is = 'actually_lua'} | {1:<} | @@ -823,6 +899,40 @@ describe('treesitter highlighting (help)', function() ]], } end) + + it('correctly redraws injections subpriorities', function() + -- The top level string node will be highlighted first + -- with an extmark spanning multiple lines. + -- When the next line is drawn, which includes an injection, + -- make sure the highlight appears above the base tree highlight + + insert([=[ + local s = [[ + local also = lua + ]] + ]=]) + + exec_lua [[ + parser = vim.treesitter.get_parser(0, "lua", { + injections = { + lua = '(string content: (_) @injection.content (#set! injection.language lua))' + } + }) + + vim.treesitter.highlighter.new(parser) + ]] + + screen:expect { + grid = [=[ + {3:local} {4:s} {3:=} {5:[[} | + {5: }{3:local}{5: }{4:also}{5: }{3:=}{5: }{4:lua} | + {5:]]} | + ^ | + {2:~ }| + | + ]=], + } + end) end) describe('treesitter highlighting (nested injections)', function() @@ -891,3 +1001,46 @@ vim.cmd([[ } end) end) + +describe('treesitter highlighting (markdown)', function() + local screen + + before_each(function() + screen = Screen.new(40, 6) + screen:attach() + screen:set_default_attr_ids { + [1] = { foreground = Screen.colors.Blue1 }, + [2] = { bold = true, foreground = Screen.colors.Blue1 }, + [3] = { bold = true, foreground = Screen.colors.Brown }, + [4] = { foreground = Screen.colors.Cyan4 }, + [5] = { foreground = Screen.colors.Magenta1 }, + } + end) + + it('supports hyperlinks', function() + local url = 'https://example.com' + insert(string.format('[This link text](%s) is a hyperlink.', url)) + exec_lua([[ + vim.bo.filetype = 'markdown' + vim.treesitter.start() + ]]) + + screen:expect { + grid = [[ + {4:[}{6:This link text}{4:](}{7:https://example.com}{4:)} is| + a hyperlink^. | + {2:~ }|*3 + | + ]], + attr_ids = { + [1] = { foreground = Screen.colors.Blue1 }, + [2] = { bold = true, foreground = Screen.colors.Blue1 }, + [3] = { bold = true, foreground = Screen.colors.Brown }, + [4] = { foreground = Screen.colors.Cyan4 }, + [5] = { foreground = Screen.colors.Magenta }, + [6] = { foreground = Screen.colors.Cyan4, url = url }, + [7] = { underline = true, foreground = Screen.colors.SlateBlue }, + }, + } + end) +end) diff --git a/test/functional/treesitter/inspect_tree_spec.lua b/test/functional/treesitter/inspect_tree_spec.lua index a3d44ff906..f5acfe7c4a 100644 --- a/test/functional/treesitter/inspect_tree_spec.lua +++ b/test/functional/treesitter/inspect_tree_spec.lua @@ -1,17 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local insert = helpers.insert -local dedent = helpers.dedent -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local feed = helpers.feed +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local insert = n.insert +local dedent = t.dedent +local eq = t.eq +local exec_lua = n.exec_lua +local feed = n.feed describe('vim.treesitter.inspect_tree', function() before_each(clear) local expect_tree = function(x) local expected = vim.split(vim.trim(dedent(x)), '\n') - local actual = helpers.buf_lines(0) ---@type string[] + local actual = n.buf_lines(0) ---@type string[] eq(expected, actual) end diff --git a/test/functional/treesitter/language_spec.lua b/test/functional/treesitter/language_spec.lua index 65d9e0e81c..40c974beee 100644 --- a/test/functional/treesitter/language_spec.lua +++ b/test/functional/treesitter/language_spec.lua @@ -1,12 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local command = helpers.command -local exec_lua = helpers.exec_lua -local pcall_err = helpers.pcall_err -local matches = helpers.matches -local insert = helpers.insert +local clear = n.clear +local eq = t.eq +local command = n.command +local exec_lua = n.exec_lua +local pcall_err = t.pcall_err +local matches = t.matches +local insert = n.insert before_each(clear) @@ -120,6 +121,20 @@ describe('treesitter language API', function() eq('<node translation_unit>', exec_lua('return tostring(tree:root())')) end) + it('retrieve the tree given a range when range is out of bounds relative to buffer', function() + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + langtree = vim.treesitter.get_parser(0, "c") + tree = langtree:tree_for_range({10, 10, 10, 10}) + ]]) + + eq('<node translation_unit>', exec_lua('return tostring(tree:root())')) + end) + it('retrieve the node given a range', function() insert([[ int main() { diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua index f114d36823..96579f296b 100644 --- a/test/functional/treesitter/node_spec.lua +++ b/test/functional/treesitter/node_spec.lua @@ -1,10 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local insert = helpers.insert -local assert_alive = helpers.assert_alive +local clear = n.clear +local eq = t.eq +local exec_lua = n.exec_lua +local insert = n.insert +local assert_alive = n.assert_alive before_each(clear) @@ -142,4 +143,30 @@ describe('treesitter node API', function() eq(28, lua_eval('root:byte_length()')) eq(3, lua_eval('child:byte_length()')) end) + + it('child_containing_descendant() works', function() + insert([[ + int main() { + int x = 3; + }]]) + + exec_lua([[ + tree = vim.treesitter.get_parser(0, "c"):parse()[1] + root = tree:root() + main = root:child(0) + body = main:child(2) + statement = body:child(1) + declarator = statement:child(1) + value = declarator:child(1) + ]]) + + eq(lua_eval('main:type()'), lua_eval('root:child_containing_descendant(value):type()')) + eq(lua_eval('body:type()'), lua_eval('main:child_containing_descendant(value):type()')) + eq(lua_eval('statement:type()'), lua_eval('body:child_containing_descendant(value):type()')) + eq( + lua_eval('declarator:type()'), + lua_eval('statement:child_containing_descendant(value):type()') + ) + eq(vim.NIL, lua_eval('declarator:child_containing_descendant(value)')) + end) end) diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index 875b772fec..dbd6bb3c23 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -1,15 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local dedent = helpers.dedent -local eq = helpers.eq -local insert = helpers.insert -local exec_lua = helpers.exec_lua -local pcall_err = helpers.pcall_err -local feed = helpers.feed -local is_os = helpers.is_os -local api = helpers.api -local fn = helpers.fn +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local dedent = t.dedent +local eq = t.eq +local insert = n.insert +local exec_lua = n.exec_lua +local pcall_err = t.pcall_err +local feed = n.feed describe('treesitter parser API', function() before_each(function() @@ -121,7 +119,7 @@ void ui_refresh(void) res = {} for node, field in func_node:iter_children() do - table.insert(res, {node:type(), field}) + table.insert(res, { node:type(), field }) end return res ]]) @@ -157,7 +155,7 @@ void ui_refresh(void) local res = {} for _, node in ipairs(func_node:field("type")) do - table.insert(res, {node:type(), node:range()}) + table.insert(res, { node:type(), node:range() }) end return res ]]) @@ -173,185 +171,6 @@ void ui_refresh(void) assert(res_fail) end) - local test_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() - local ret = exec_lua [[ - return vim.treesitter.query.get("c", "highlights").captures[1] - ]] - - eq('variable', ret) - end) - - it('supports caching queries', function() - local long_query = test_query:rep(100) - local function q(n) - return exec_lua( - [[ - local query, n = ... - local before = vim.uv.hrtime() - for i=1,n,1 do - cquery = vim.treesitter.query.parse("c", ...) - end - local after = vim.uv.hrtime() - return after - before - ]], - long_query, - n - ) - end - - local firstrun = q(1) - local manyruns = q(100) - - -- First run should be at least 200x slower than an 100 subsequent runs. - local factor = is_os('win') and 100 or 200 - assert( - factor * manyruns < firstrun, - ('firstrun: %f ms, manyruns: %f ms'):format(firstrun / 1e6, manyruns / 1e6) - ) - end) - - it('support query and iter by capture', function() - insert(test_text) - - local res = exec_lua( - [[ - cquery = vim.treesitter.query.parse("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 - ]], - test_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() - insert(test_text) - - local res = exec_lua( - [[ - cquery = vim.treesitter.query.parse("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, { all = true }) do - -- can't transmit node over RPC. just check the name and range - local mrepr = {} - for cid, nodes in pairs(match) do - for _, node in ipairs(nodes) do - table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) - end - end - table.insert(res, {pattern, mrepr}) - end - return res - ]], - test_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('support query and iter by capture for quantifiers', function() - insert(test_text) - - local res = exec_lua( - [[ - cquery = vim.treesitter.query.parse("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 - ]], - '(expression_statement (assignment_expression (call_expression)))+ @funccall' - ) - - eq({ - { 'funccall', 'expression_statement', 11, 4, 11, 34 }, - { 'funccall', 'expression_statement', 12, 4, 12, 37 }, - { 'funccall', 'expression_statement', 13, 4, 13, 34 }, - }, res) - end) - - it('support query and iter by match for quantifiers', function() - insert(test_text) - - local res = exec_lua( - [[ - cquery = vim.treesitter.query.parse("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, { all = true }) do - -- can't transmit node over RPC. just check the name and range - local mrepr = {} - for cid, nodes in pairs(match) do - for _, node in ipairs(nodes) do - table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) - end - end - table.insert(res, {pattern, mrepr}) - end - return res - ]], - '(expression_statement (assignment_expression (call_expression)))+ @funccall' - ) - - eq({ - { - 1, - { - { 'funccall', 'expression_statement', 11, 4, 11, 34 }, - { 'funccall', 'expression_statement', 12, 4, 12, 37 }, - { 'funccall', 'expression_statement', 13, 4, 13, 34 }, - }, - }, - }, res) - end) - it('supports getting text of multiline node', function() insert(test_text) local res = exec_lua([[ @@ -369,7 +188,7 @@ void ui_refresh(void) eq('void', res2) end) - it('support getting text where start of node is one past EOF', function() + it('supports getting text where start of node is one past EOF', function() local text = [[ def run a = <<~E @@ -378,25 +197,25 @@ end]] eq( '', exec_lua [[ - local fake_node = {} - function fake_node:start() - return 3, 0, 23 - end - function fake_node:end_() - return 3, 0, 23 - end - function fake_node:range(bytes) - if bytes then - return 3, 0, 23, 3, 0, 23 + local fake_node = {} + function fake_node:start() + return 3, 0, 23 end - return 3, 0, 3, 0 - end - return vim.treesitter.get_node_text(fake_node, 0) - ]] + function fake_node:end_() + return 3, 0, 23 + end + function fake_node:range(bytes) + if bytes then + return 3, 0, 23, 3, 0, 23 + end + return 3, 0, 3, 0 + end + return vim.treesitter.get_node_text(fake_node, 0) + ]] ) end) - it('support getting empty text if node range is zero width', function() + it('supports getting empty text if node range is zero width', function() local text = [[ ```lua {} @@ -418,387 +237,6 @@ end]] eq(true, result) end) - it('can match special regex characters like \\ * + ( with `vim-match?`', function() - insert('char* astring = "\\n"; (1 + 1) * 2 != 2;') - - local res = exec_lua([[ - cquery = vim.treesitter.query.parse("c", '([_] @plus (#vim-match? @plus "^\\\\+$"))'.. - '([_] @times (#vim-match? @times "^\\\\*$"))'.. - '([_] @paren (#vim-match? @paren "^\\\\($"))'.. - '([_] @escape (#vim-match? @escape "^\\\\\\\\n$"))'.. - '([_] @string (#vim-match? @string "^\\"\\\\\\\\n\\"$"))') - parser = vim.treesitter.get_parser(0, "c") - tree = parser:parse()[1] - res = {} - for pattern, match in cquery:iter_matches(tree:root(), 0, 0, -1, { all = true }) do - -- can't transmit node over RPC. just check the name and range - local mrepr = {} - for cid, nodes in pairs(match) do - for _, node in ipairs(nodes) do - table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) - end - end - table.insert(res, {pattern, mrepr}) - end - return res - ]]) - - eq({ - { 2, { { 'times', '*', 0, 4, 0, 5 } } }, - { 5, { { 'string', 'string_literal', 0, 16, 0, 20 } } }, - { 4, { { 'escape', 'escape_sequence', 0, 17, 0, 19 } } }, - { 3, { { 'paren', '(', 0, 22, 0, 23 } } }, - { 1, { { 'plus', '+', 0, 25, 0, 26 } } }, - { 2, { { 'times', '*', 0, 30, 0, 31 } } }, - }, res) - end) - - it('supports builtin query predicate any-of?', function() - insert([[ - #include <stdio.h> - - int main(void) { - int i; - for(i=1; i<=100; i++) { - if(((i%3)||(i%5))== 0) - printf("number= %d FizzBuzz\n", i); - else if((i%3)==0) - printf("number= %d Fizz\n", i); - else if((i%5)==0) - printf("number= %d Buzz\n", i); - else - printf("number= %d\n",i); - } - return 0; - } - ]]) - exec_lua([[ - function get_query_result(query_text) - cquery = vim.treesitter.query.parse("c", query_text) - parser = vim.treesitter.get_parser(0, "c") - tree = parser:parse()[1] - res = {} - for cid, node in cquery:iter_captures(tree:root(), 0) do - -- can't transmit node over RPC. just check the name, range, and text - local text = vim.treesitter.get_node_text(node, 0) - local range = {node:range()} - table.insert(res, {cquery.captures[cid], node:type(), range, text}) - end - return res - end - ]]) - - local res0 = exec_lua( - [[return get_query_result(...)]], - [[((primitive_type) @c-keyword (#any-of? @c-keyword "int" "float"))]] - ) - eq({ - { 'c-keyword', 'primitive_type', { 2, 2, 2, 5 }, 'int' }, - { 'c-keyword', 'primitive_type', { 3, 4, 3, 7 }, 'int' }, - }, res0) - - local res1 = exec_lua( - [[return get_query_result(...)]], - [[ - ((string_literal) @fizzbuzz-strings (#any-of? @fizzbuzz-strings - "\"number= %d FizzBuzz\\n\"" - "\"number= %d Fizz\\n\"" - "\"number= %d Buzz\\n\"" - )) - ]] - ) - eq({ - { 'fizzbuzz-strings', 'string_literal', { 6, 15, 6, 38 }, '"number= %d FizzBuzz\\n"' }, - { 'fizzbuzz-strings', 'string_literal', { 8, 15, 8, 34 }, '"number= %d Fizz\\n"' }, - { 'fizzbuzz-strings', 'string_literal', { 10, 15, 10, 34 }, '"number= %d Buzz\\n"' }, - }, res1) - end) - - it( - 'allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', - function() - insert('char* astring = "Hello World!";') - - local res = exec_lua([[ - cquery = vim.treesitter.query.parse("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, { all = true }) do - -- can't transmit node over RPC. just check the name and range - local mrepr = {} - for cid, nodes in pairs(match) do - for _, node in ipairs(nodes) do - table.insert(mrepr, {cquery.captures[cid], node:type(), node:range()}) - end - 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))' - - do - local res = exec_lua( - [[ - local query = vim.treesitter.query - - local function is_main(match, pattern, bufnr, predicate) - local nodes = match[ predicate[2] ] - for _, node in ipairs(nodes) do - if vim.treesitter.get_node_text(node, bufnr) == 'main' then - return true - end - end - return false - end - - local parser = vim.treesitter.get_parser(0, "c") - - -- Time bomb: update this in 0.12 - if vim.fn.has('nvim-0.12') == 1 then - return 'Update this test to remove this message and { all = true } from add_predicate' - end - query.add_predicate("is-main?", is_main, { all = true }) - - local query = query.parse("c", ...) - - local nodes = {} - for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do - table.insert(nodes, {node:range()}) - end - - return nodes - ]], - custom_query - ) - - eq({ { 0, 4, 0, 8 } }, res) - end - - -- Once with the old API. Remove this whole 'do' block in 0.12 - do - local res = exec_lua( - [[ - local query = vim.treesitter.query - - local function is_main(match, pattern, bufnr, predicate) - local node = match[ predicate[2] ] - - return vim.treesitter.get_node_text(node, bufnr) == 'main' - end - - local parser = vim.treesitter.get_parser(0, "c") - - query.add_predicate("is-main?", is_main, true) - - local query = query.parse("c", ...) - - local nodes = {} - for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do - table.insert(nodes, {node:range()}) - end - - return nodes - ]], - custom_query - ) - - -- Remove this 'do' block in 0.12 - eq(0, fn.has('nvim-0.12')) - eq({ { 0, 4, 0, 8 } }, res) - end - - do - local res = exec_lua [[ - local query = vim.treesitter.query - - local t = {} - for _, v in ipairs(query.list_predicates()) do - t[v] = true - end - - return t - ]] - - eq(true, res['is-main?']) - end - end) - - it('supports "all" and "any" semantics for predicates on quantified captures #24738', function() - local query_all = [[ - (((comment (comment_content))+) @bar - (#lua-match? @bar "Yes")) - ]] - - local query_any = [[ - (((comment (comment_content))+) @bar - (#any-lua-match? @bar "Yes")) - ]] - - local function test(input, query) - api.nvim_buf_set_lines(0, 0, -1, true, vim.split(dedent(input), '\n')) - return exec_lua( - [[ - local parser = vim.treesitter.get_parser(0, "lua") - local query = vim.treesitter.query.parse("lua", ...) - local nodes = {} - for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do - nodes[#nodes+1] = { node:range() } - end - return nodes - ]], - query - ) - end - - eq( - {}, - test( - [[ - -- Yes - -- No - -- Yes - ]], - query_all - ) - ) - - eq( - { - { 0, 2, 0, 8 }, - { 1, 2, 1, 8 }, - { 2, 2, 2, 8 }, - }, - test( - [[ - -- Yes - -- Yes - -- Yes - ]], - query_all - ) - ) - - eq( - {}, - test( - [[ - -- No - -- No - -- No - ]], - query_any - ) - ) - - eq( - { - { 0, 2, 0, 7 }, - { 1, 2, 1, 8 }, - { 2, 2, 2, 7 }, - }, - test( - [[ - -- No - -- Yes - -- No - ]], - query_any - ) - ) - end) - - it('supports any- prefix to match any capture when using quantifiers #24738', function() - insert([[ - -- Comment - -- Comment - -- Comment - ]]) - - local query = [[ - (((comment (comment_content))+) @bar - (#lua-match? @bar "Comment")) - ]] - - local result = exec_lua( - [[ - local parser = vim.treesitter.get_parser(0, "lua") - local query = vim.treesitter.query.parse("lua", ...) - local nodes = {} - for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do - nodes[#nodes+1] = { node:range() } - end - return nodes - ]], - query - ) - - eq({ - { 0, 2, 0, 12 }, - { 1, 2, 1, 12 }, - { 2, 2, 2, 12 }, - }, result) - end) - - it('supports the old broken version of iter_matches #24738', function() - -- Delete this test in 0.12 when iter_matches is removed - eq(0, fn.has('nvim-0.12')) - - insert(test_text) - local res = exec_lua( - [[ - cquery = vim.treesitter.query.parse("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 - 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 - ]], - test_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('allows to set simple ranges', function() insert(test_text) @@ -828,7 +266,7 @@ end]] return parser:included_regions() ]] - eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } }) + eq({ { { 0, 0, 0, 17, 1, 508 } } }, range_tbl) end) it('allows to set complex ranges', function() @@ -882,56 +320,31 @@ end]] local ret = exec_lua( [[ - local str = ... - local parser = vim.treesitter.get_string_parser(str, "c") + local str = ... + local parser = vim.treesitter.get_string_parser(str, "c") - local nodes = {} - local query = vim.treesitter.query.parse("c", '((identifier) @id (#eq? @id "foo"))') + local nodes = {} + local query = vim.treesitter.query.parse("c", '((identifier) @id (#eq? @id "foo"))') - for _, node in query:iter_captures(parser:parse()[1]:root(), str) do - table.insert(nodes, { node:range() }) - end + for _, node in query:iter_captures(parser:parse()[1]:root(), str) do + table.insert(nodes, { node:range() }) + end - return nodes]], + return nodes + ]], txt ) eq({ { 0, 10, 0, 13 } }, ret) end) - it('should use node range when omitted', 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.query.parse("c", '((identifier) @foo)') - local first_child = parser:parse()[1]:root():child(1) - - for _, node in query:iter_captures(first_child, str) do - table.insert(nodes, { node:range() }) - end - - return nodes]], - txt - ) - - eq({ { 1, 10, 1, 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 - ]]) + return exec_lua [[ + local result = {} + parser:for_each_tree(function(tree) table.insert(result, {tree:root():range()}) end) + return result + ]] end before_each(function() @@ -948,10 +361,14 @@ int x = INT_MAX; describe('when parsing regions independently', function() it('should inject a language', function() exec_lua([[ - parser = vim.treesitter.get_parser(0, "c", { - injections = { - c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c")) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c"))'}}) - parser:parse(true) + parser = vim.treesitter.get_parser(0, "c", { + injections = { + c = ( + '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c")) ' .. + '(preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c"))' + ) + }}) + parser:parse(true) ]]) eq('table', exec_lua('return type(parser:children().c)')) @@ -965,7 +382,7 @@ int x = INT_MAX; { 2, 29, 2, 66 }, -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) }, get_ranges()) - helpers.feed('ggo<esc>') + n.feed('ggo<esc>') eq(5, exec_lua('return #parser:children().c:trees()')) eq({ { 0, 0, 8, 0 }, -- root tree @@ -981,10 +398,14 @@ int x = INT_MAX; describe('when parsing regions combined', function() it('should inject a language', function() exec_lua([[ - parser = vim.treesitter.get_parser(0, "c", { - injections = { - c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c") (#set! injection.combined)) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c") (#set! injection.combined))'}}) - parser:parse(true) + parser = vim.treesitter.get_parser(0, "c", { + injections = { + c = ( + '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c") (#set! injection.combined)) ' .. + '(preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c") (#set! injection.combined))' + ) + }}) + parser:parse(true) ]]) eq('table', exec_lua('return type(parser:children().c)')) @@ -998,7 +419,7 @@ int x = INT_MAX; -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) }, get_ranges()) - helpers.feed('ggo<esc>') + n.feed('ggo<esc>') eq('table', exec_lua('return type(parser:children().c)')) eq(2, exec_lua('return #parser:children().c:trees()')) eq({ @@ -1010,7 +431,7 @@ int x = INT_MAX; -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) }, get_ranges()) - helpers.feed('7ggI//<esc>') + n.feed('7ggI//<esc>') exec_lua([[parser:parse({6, 7})]]) eq('table', exec_lua('return type(parser:children().c)')) eq(2, exec_lua('return #parser:children().c:trees()')) @@ -1027,10 +448,14 @@ int x = INT_MAX; describe('when using injection.self', function() it('should inject the source language', function() exec_lua([[ - parser = vim.treesitter.get_parser(0, "c", { - injections = { - c = '(preproc_def (preproc_arg) @injection.content (#set! injection.self)) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.self))'}}) - parser:parse(true) + parser = vim.treesitter.get_parser(0, "c", { + injections = { + c = ( + '(preproc_def (preproc_arg) @injection.content (#set! injection.self)) ' .. + '(preproc_function_def value: (preproc_arg) @injection.content (#set! injection.self))' + ) + }}) + parser:parse(true) ]]) eq('table', exec_lua('return type(parser:children().c)')) @@ -1044,7 +469,7 @@ int x = INT_MAX; { 2, 29, 2, 66 }, -- READ_STRING_OK(x, y) (char *)read_string((x), (size_t)(y)) }, get_ranges()) - helpers.feed('ggo<esc>') + n.feed('ggo<esc>') eq(5, exec_lua('return #parser:children().c:trees()')) eq({ { 0, 0, 8, 0 }, -- root tree @@ -1060,10 +485,14 @@ int x = INT_MAX; 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", { - injections = { - c = '(preproc_def ((preproc_arg) @injection.content (#set! injection.language "c") (#offset! @injection.content 0 2 0 -1))) (preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c"))'}}) - parser:parse(true) + parser = vim.treesitter.get_parser(0, "c", { + injections = { + c = ( + '(preproc_def ((preproc_arg) @injection.content (#set! injection.language "c") (#offset! @injection.content 0 2 0 -1))) ' .. + '(preproc_function_def value: (preproc_arg) @injection.content (#set! injection.language "c"))' + ) + }}) + parser:parse(true) ]]) eq('table', exec_lua('return type(parser:children().c)')) @@ -1078,13 +507,13 @@ int x = INT_MAX; end) it('should list all directives', function() local res_list = exec_lua [[ - local query = vim.treesitter.query + local query = vim.treesitter.query - local list = query.list_directives() + local list = query.list_directives() - table.sort(list) + table.sort(list) - return list + return list ]] eq({ 'gsub!', 'offset!', 'set!', 'trim!' }, res_list) @@ -1102,16 +531,49 @@ int x = INT_MAX; it('should return the correct language tree', function() local result = exec_lua([[ - parser = vim.treesitter.get_parser(0, "c", { - injections = { c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c"))'}}) - parser:parse(true) + parser = vim.treesitter.get_parser(0, "c", { + injections = { + c = '(preproc_def (preproc_arg) @injection.content (#set! injection.language "c"))' + } + }) + parser:parse(true) + + local sub_tree = parser:language_for_range({1, 18, 1, 19}) + + return sub_tree == parser:children().c + ]]) - local sub_tree = parser:language_for_range({1, 18, 1, 19}) + eq(true, result) + end) + end) - return sub_tree == parser:children().c + describe('when setting the node for an injection', function() + before_each(function() + insert([[ +print() ]]) + end) - eq(result, true) + it('ignores optional captures #23100', function() + local result = exec_lua([[ + parser = vim.treesitter.get_parser(0, "lua", { + injections = { + lua = ( + '(function_call ' .. + '(arguments ' .. + '(string)? @injection.content ' .. + '(number)? @injection.content ' .. + '(#offset! @injection.content 0 1 0 -1) ' .. + '(#set! injection.language "c")))' + ) + } + }) + parser:parse(true) + + return parser:is_valid() + ]]) + + eq(true, result) end) end) @@ -1135,7 +597,7 @@ int x = INT_MAX; return result ]]) - eq(result, 'value') + eq('value', result) end) describe('when setting a key on a capture', function() @@ -1158,7 +620,7 @@ int x = INT_MAX; end ]]) - eq(result, 'value') + eq('value', result) end) it('it should not overwrite the nested table', function() @@ -1208,15 +670,15 @@ int x = INT_MAX; local function run_query() return exec_lua( [[ - local query = vim.treesitter.query.parse("c", ...) - parser = vim.treesitter.get_parser() - tree = parser:parse()[1] - res = {} - for id, node in query:iter_captures(tree:root()) do - table.insert(res, {query.captures[id], node:range()}) - end - return res - ]], + local query = vim.treesitter.query.parse("c", ...) + parser = vim.treesitter.get_parser() + tree = parser:parse()[1] + res = {} + for id, node in query:iter_captures(tree:root()) do + table.insert(res, {query.captures[id], node:range()}) + end + return res + ]], query0 ) end @@ -1226,7 +688,7 @@ int x = INT_MAX; { 'declaration', 1, 2, 1, 12 }, }, run_query()) - helpers.command 'normal ggO' + n.command 'normal ggO' insert('int a;') eq({ @@ -1258,14 +720,14 @@ int x = INT_MAX; local r = exec_lua( [[ - local parser = vim.treesitter.get_string_parser(..., 'lua') - parser:parse(true) - local ranges = {} - parser:for_each_tree(function(tstree, tree) - ranges[tree:lang()] = { tstree:root():range(true) } - end) - return ranges - ]], + local parser = vim.treesitter.get_string_parser(..., 'lua') + parser:parse(true) + local ranges = {} + parser:for_each_tree(function(tstree, tree) + ranges[tree:lang()] = { tstree:root():range(true) } + end) + return ranges + ]], source ) @@ -1281,14 +743,14 @@ int x = INT_MAX; local rb = exec_lua( [[ - local r, source = ... - local add_bytes = require('vim.treesitter._range').add_bytes - for lang, range in pairs(r) do - r[lang] = {range[1], range[2], range[4], range[5]} - r[lang] = add_bytes(source, r[lang]) - end - return r - ]], + local r, source = ... + local add_bytes = require('vim.treesitter._range').add_bytes + for lang, range in pairs(r) do + r[lang] = {range[1], range[2], range[4], range[5]} + r[lang] = add_bytes(source, r[lang]) + end + return r + ]], r, source ) @@ -1402,53 +864,7 @@ int x = INT_MAX; ) end) - it('fails to load queries', function() - local function test(exp, cquery) - eq(exp, pcall_err(exec_lua, "vim.treesitter.query.parse('c', ...)", cquery)) - end - - -- Invalid node type - test( - '.../query.lua:0: Query error at 1:2. Invalid node type "dentifier":\n' - .. '(dentifier) @variable\n' - .. ' ^', - '(dentifier) @variable' - ) - - -- Impossible pattern - test( - '.../query.lua:0: Query error at 1:13. Impossible pattern:\n' - .. '(identifier (identifier) @variable)\n' - .. ' ^', - '(identifier (identifier) @variable)' - ) - - -- Invalid syntax - test( - '.../query.lua:0: Query error at 1:13. Invalid syntax:\n' - .. '(identifier @variable\n' - .. ' ^', - '(identifier @variable' - ) - - -- Invalid field name - test( - '.../query.lua:0: Query error at 1:15. Invalid field name "invalid_field":\n' - .. '((identifier) invalid_field: (identifier))\n' - .. ' ^', - '((identifier) invalid_field: (identifier))' - ) - - -- Invalid capture name - test( - '.../query.lua:0: Query error at 3:2. Invalid capture name "ok.capture":\n' - .. '@ok.capture\n' - .. ' ^', - '((identifier) @id \n(#eq? @id\n@ok.capture\n))' - ) - end) - - describe('is_valid()', function() + describe('languagetree is_valid()', function() before_each(function() insert(dedent [[ Treesitter integration *treesitter* diff --git a/test/functional/treesitter/query_spec.lua b/test/functional/treesitter/query_spec.lua new file mode 100644 index 0000000000..c3a376cd71 --- /dev/null +++ b/test/functional/treesitter/query_spec.lua @@ -0,0 +1,808 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local dedent = t.dedent +local eq = t.eq +local insert = n.insert +local exec_lua = n.exec_lua +local pcall_err = t.pcall_err +local api = n.api +local fn = n.fn + +local get_query_result_code = [[ + function get_query_result(query_text) + cquery = vim.treesitter.query.parse("c", query_text) + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + res = {} + for cid, node in cquery:iter_captures(tree:root(), 0) do + -- can't transmit node over RPC. just check the name, range, and text + local text = vim.treesitter.get_node_text(node, 0) + local range = {node:range()} + table.insert(res, { cquery.captures[cid], node:type(), range, text }) + end + return res + end +]] + +describe('treesitter query API', function() + before_each(function() + clear() + exec_lua [[ + vim.g.__ts_debug = 1 + ]] + 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); + } + } +}]] + + local test_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() + ---@type string[] + local ret = exec_lua [[ + return vim.treesitter.query.get("c", "highlights").captures + ]] + + -- see $VIMRUNTIME/queries/c/highlights.scm + eq('variable', ret[1]) + eq('keyword', ret[2]) + end) + + it('supports caching queries', function() + local long_query = test_query:rep(100) + ---@return number + local function q(_n) + return exec_lua( + [[ + local query, n = ... + local before = vim.api.nvim__stats().ts_query_parse_count + collectgarbage("stop") + for i=1, n, 1 do + cquery = vim.treesitter.query.parse("c", ...) + end + collectgarbage("restart") + collectgarbage("collect") + local after = vim.api.nvim__stats().ts_query_parse_count + return after - before + ]], + long_query, + _n + ) + end + + eq(1, q(1)) + -- cache is cleared by garbage collection even if valid "cquery" reference is kept around + eq(1, q(100)) + end) + + it('supports query and iter by capture (iter_captures)', function() + insert(test_text) + + local res = exec_lua( + [[ + cquery = vim.treesitter.query.parse("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 + ]], + test_query + ) + + eq({ + { '@type', 'primitive_type', 8, 2, 8, 6 }, -- bool + { '@keyword', 'for', 9, 2, 9, 5 }, -- for + { '@type', 'primitive_type', 9, 7, 9, 13 }, -- size_t + { '@minfunc', 'identifier', 11, 12, 11, 15 }, -- "MIN"(ui->width, width); + { '@fieldarg', 'identifier', 11, 16, 11, 18 }, -- ui + { '@min_id', 'identifier', 11, 27, 11, 32 }, -- width + { '@minfunc', 'identifier', 12, 13, 12, 16 }, -- "MIN"(ui->height, height); + { '@fieldarg', 'identifier', 12, 17, 12, 19 }, -- ui + { '@min_id', 'identifier', 12, 29, 12, 35 }, -- height + { '@fieldarg', 'identifier', 13, 14, 13, 16 }, -- ui ; in BAR(..) + }, res) + end) + + it('supports query and iter by match (iter_matches)', function() + insert(test_text) + + ---@type table + local res = exec_lua( + [[ + cquery = vim.treesitter.query.parse("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, { all = true }) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid, nodes in pairs(match) do + for _, node in ipairs(nodes) do + table.insert(mrepr, { '@' .. cquery.captures[cid], node:type(), node:range() }) + end + end + table.insert(res, { pattern, mrepr }) + end + return res + ]], + test_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('supports query and iter by capture for quantifiers', function() + insert(test_text) + + local res = exec_lua( + [[ + cquery = vim.treesitter.query.parse("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 + ]], + '(expression_statement (assignment_expression (call_expression)))+ @funccall' + ) + + eq({ + { '@funccall', 'expression_statement', 11, 4, 11, 34 }, + { '@funccall', 'expression_statement', 12, 4, 12, 37 }, + { '@funccall', 'expression_statement', 13, 4, 13, 34 }, + }, res) + end) + + it('supports query and iter by match for quantifiers', function() + insert(test_text) + + local res = exec_lua( + [[ + cquery = vim.treesitter.query.parse("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, { all = true }) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid, nodes in pairs(match) do + for _, node in ipairs(nodes) do + table.insert(mrepr, { '@' .. cquery.captures[cid], node:type(), node:range() }) + end + end + table.insert(res, {pattern, mrepr}) + end + return res + ]], + '(expression_statement (assignment_expression (call_expression)))+ @funccall' + ) + + eq({ + { + 1, + { + { '@funccall', 'expression_statement', 11, 4, 11, 34 }, + { '@funccall', 'expression_statement', 12, 4, 12, 37 }, + { '@funccall', 'expression_statement', 13, 4, 13, 34 }, + }, + }, + }, res) + end) + + it('can match special regex characters like \\ * + ( with `vim-match?`', function() + insert('char* astring = "\\n"; (1 + 1) * 2 != 2;') + + ---@type table + local res = exec_lua([[ + query = ( + '([_] @plus (#vim-match? @plus "^\\\\+$"))' .. + '([_] @times (#vim-match? @times "^\\\\*$"))' .. + '([_] @paren (#vim-match? @paren "^\\\\($"))' .. + '([_] @escape (#vim-match? @escape "^\\\\\\\\n$"))' .. + '([_] @string (#vim-match? @string "^\\"\\\\\\\\n\\"$"))' + ) + cquery = vim.treesitter.query.parse("c", query) + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + res = {} + for pattern, match in cquery:iter_matches(tree:root(), 0, 0, -1, { all = true }) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid, nodes in pairs(match) do + for _, node in ipairs(nodes) do + table.insert(mrepr, { '@' .. cquery.captures[cid], node:type(), node:range() }) + end + end + table.insert(res, { pattern, mrepr }) + end + return res + ]]) + + eq({ + { 2, { { '@times', '*', 0, 4, 0, 5 } } }, + { 5, { { '@string', 'string_literal', 0, 16, 0, 20 } } }, + { 4, { { '@escape', 'escape_sequence', 0, 17, 0, 19 } } }, + { 3, { { '@paren', '(', 0, 22, 0, 23 } } }, + { 1, { { '@plus', '+', 0, 25, 0, 26 } } }, + { 2, { { '@times', '*', 0, 30, 0, 31 } } }, + }, res) + end) + + it('supports builtin query predicate any-of?', function() + insert([[ + #include <stdio.h> + + int main(void) { + int i; + for(i=1; i<=100; i++) { + if(((i%3)||(i%5))== 0) + printf("number= %d FizzBuzz\n", i); + else if((i%3)==0) + printf("number= %d Fizz\n", i); + else if((i%5)==0) + printf("number= %d Buzz\n", i); + else + printf("number= %d\n",i); + } + return 0; + } + ]]) + exec_lua(get_query_result_code) + + local res0 = exec_lua( + [[return get_query_result(...)]], + [[((primitive_type) @c-keyword (#any-of? @c-keyword "int" "float"))]] + ) + eq({ + { 'c-keyword', 'primitive_type', { 2, 2, 2, 5 }, 'int' }, + { 'c-keyword', 'primitive_type', { 3, 4, 3, 7 }, 'int' }, + }, res0) + + local res1 = exec_lua( + [[return get_query_result(...)]], + [[ + ((string_literal) @fizzbuzz-strings (#any-of? @fizzbuzz-strings + "\"number= %d FizzBuzz\\n\"" + "\"number= %d Fizz\\n\"" + "\"number= %d Buzz\\n\"" + )) + ]] + ) + eq({ + { 'fizzbuzz-strings', 'string_literal', { 6, 15, 6, 38 }, '"number= %d FizzBuzz\\n"' }, + { 'fizzbuzz-strings', 'string_literal', { 8, 15, 8, 34 }, '"number= %d Fizz\\n"' }, + { 'fizzbuzz-strings', 'string_literal', { 10, 15, 10, 34 }, '"number= %d Buzz\\n"' }, + }, res1) + end) + + it('supports builtin predicate has-ancestor?', function() + insert([[ + int x = 123; + enum C { y = 124 }; + int main() { int z = 125; }]]) + exec_lua(get_query_result_code) + + local result = exec_lua( + [[return get_query_result(...)]], + [[((number_literal) @literal (#has-ancestor? @literal "function_definition"))]] + ) + eq({ { 'literal', 'number_literal', { 2, 21, 2, 24 }, '125' } }, result) + + result = exec_lua( + [[return get_query_result(...)]], + [[((number_literal) @literal (#has-ancestor? @literal "function_definition" "enum_specifier"))]] + ) + eq({ + { 'literal', 'number_literal', { 1, 13, 1, 16 }, '124' }, + { 'literal', 'number_literal', { 2, 21, 2, 24 }, '125' }, + }, result) + + result = exec_lua( + [[return get_query_result(...)]], + [[((number_literal) @literal (#not-has-ancestor? @literal "enum_specifier"))]] + ) + eq({ + { 'literal', 'number_literal', { 0, 8, 0, 11 }, '123' }, + { 'literal', 'number_literal', { 2, 21, 2, 24 }, '125' }, + }, result) + end) + + it('allows loading query with escaped quotes and capture them `#{lua,vim}-match`?', function() + insert('char* astring = "Hello World!";') + + local res = exec_lua([[ + cquery = vim.treesitter.query.parse("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, { all = true }) do + -- can't transmit node over RPC. just check the name and range + local mrepr = {} + for cid, nodes in pairs(match) do + for _, node in ipairs(nodes) do + table.insert(mrepr, { '@' .. cquery.captures[cid], node:type(), node:range() }) + end + 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))' + + do + local res = exec_lua( + [[ + local query = vim.treesitter.query + + local function is_main(match, pattern, bufnr, predicate) + local nodes = match[ predicate[2] ] + for _, node in ipairs(nodes) do + if vim.treesitter.get_node_text(node, bufnr) == 'main' then + return true + end + end + return false + end + + local parser = vim.treesitter.get_parser(0, "c") + + -- Time bomb: update this in 0.12 + if vim.fn.has('nvim-0.12') == 1 then + return 'Update this test to remove this message and { all = true } from add_predicate' + end + query.add_predicate("is-main?", is_main, { all = true }) + + local query = query.parse("c", ...) + + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do + table.insert(nodes, {node:range()}) + end + + return nodes + ]], + custom_query + ) + + eq({ { 0, 4, 0, 8 } }, res) + end + + -- Once with the old API. Remove this whole 'do' block in 0.12 + do + local res = exec_lua( + [[ + local query = vim.treesitter.query + + local function is_main(match, pattern, bufnr, predicate) + local node = match[ predicate[2] ] + + return vim.treesitter.get_node_text(node, bufnr) == 'main' + end + + local parser = vim.treesitter.get_parser(0, "c") + + query.add_predicate("is-main?", is_main, true) + + local query = query.parse("c", ...) + + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do + table.insert(nodes, {node:range()}) + end + + return nodes + ]], + custom_query + ) + + -- Remove this 'do' block in 0.12 + eq(0, fn.has('nvim-0.12')) + eq({ { 0, 4, 0, 8 } }, res) + end + + do + local res = exec_lua [[ + local query = vim.treesitter.query + + local t = {} + for _, v in ipairs(query.list_predicates()) do + t[v] = true + end + + return t + ]] + + eq(true, res['is-main?']) + end + end) + + it('supports "all" and "any" semantics for predicates on quantified captures #24738', function() + local query_all = [[ + (((comment (comment_content))+) @bar + (#lua-match? @bar "Yes")) + ]] + + local query_any = [[ + (((comment (comment_content))+) @bar + (#any-lua-match? @bar "Yes")) + ]] + + local function test(input, query) + api.nvim_buf_set_lines(0, 0, -1, true, vim.split(dedent(input), '\n')) + return exec_lua( + [[ + local parser = vim.treesitter.get_parser(0, "lua") + local query = vim.treesitter.query.parse("lua", ...) + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do + nodes[#nodes+1] = { node:range() } + end + return nodes + ]], + query + ) + end + + eq( + {}, + test( + [[ + -- Yes + -- No + -- Yes + ]], + query_all + ) + ) + + eq( + { + { 0, 2, 0, 8 }, + { 1, 2, 1, 8 }, + { 2, 2, 2, 8 }, + }, + test( + [[ + -- Yes + -- Yes + -- Yes + ]], + query_all + ) + ) + + eq( + {}, + test( + [[ + -- No + -- No + -- No + ]], + query_any + ) + ) + + eq( + { + { 0, 2, 0, 7 }, + { 1, 2, 1, 8 }, + { 2, 2, 2, 7 }, + }, + test( + [[ + -- No + -- Yes + -- No + ]], + query_any + ) + ) + end) + + it('supports any- prefix to match any capture when using quantifiers #24738', function() + insert([[ + -- Comment + -- Comment + -- Comment + ]]) + + local query = [[ + (((comment (comment_content))+) @bar + (#lua-match? @bar "Comment")) + ]] + + local result = exec_lua( + [[ + local parser = vim.treesitter.get_parser(0, "lua") + local query = vim.treesitter.query.parse("lua", ...) + local nodes = {} + for _, node in query:iter_captures(parser:parse()[1]:root(), 0) do + nodes[#nodes+1] = { node:range() } + end + return nodes + ]], + query + ) + + eq({ + { 0, 2, 0, 12 }, + { 1, 2, 1, 12 }, + { 2, 2, 2, 12 }, + }, result) + end) + + it('supports the old broken version of iter_matches #24738', function() + -- Delete this test in 0.12 when iter_matches is removed + eq(0, fn.has('nvim-0.12')) + + insert(test_text) + local res = exec_lua( + [[ + cquery = vim.treesitter.query.parse("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 + 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 + ]], + test_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('should use node range when omitted', 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.query.parse("c", '((identifier) @foo)') + local first_child = parser:parse()[1]:root():child(1) + + for _, node in query:iter_captures(first_child, str) do + table.insert(nodes, { node:range() }) + end + + return nodes + ]], + txt + ) + + eq({ { 1, 10, 1, 13 } }, ret) + end) + + it('fails to load queries', function() + local function test(exp, cquery) + eq(exp, pcall_err(exec_lua, "vim.treesitter.query.parse('c', ...)", cquery)) + end + + -- Invalid node type + test( + '.../query.lua:0: Query error at 1:2. Invalid node type "dentifier":\n' + .. '(dentifier) @variable\n' + .. ' ^', + '(dentifier) @variable' + ) + + -- Impossible pattern + test( + '.../query.lua:0: Query error at 1:13. Impossible pattern:\n' + .. '(identifier (identifier) @variable)\n' + .. ' ^', + '(identifier (identifier) @variable)' + ) + + -- Invalid syntax + test( + '.../query.lua:0: Query error at 1:13. Invalid syntax:\n' + .. '(identifier @variable\n' + .. ' ^', + '(identifier @variable' + ) + + -- Invalid field name + test( + '.../query.lua:0: Query error at 1:15. Invalid field name "invalid_field":\n' + .. '((identifier) invalid_field: (identifier))\n' + .. ' ^', + '((identifier) invalid_field: (identifier))' + ) + + -- Invalid capture name + test( + '.../query.lua:0: Query error at 3:2. Invalid capture name "ok.capture":\n' + .. '@ok.capture\n' + .. ' ^', + '((identifier) @id \n(#eq? @id\n@ok.capture\n))' + ) + end) + + describe('Query:iter_captures', function() + it('includes metadata for all captured nodes #23664', function() + insert([[ + const char *sql = "SELECT * FROM Students WHERE name = 'Robert'); DROP TABLE Students;--"; + ]]) + + local query = [[ + (declaration + type: (_) + declarator: (init_declarator + declarator: (pointer_declarator + declarator: (identifier)) @_id + value: (string_literal + (string_content) @injection.content)) + (#set! injection.language "sql") + (#contains? @_id "sql")) + ]] + + local result = exec_lua( + [=[ + local query = vim.treesitter.query.parse("c", ...) + local parser = vim.treesitter.get_parser(0, "c") + local root = parser:parse()[1]:root() + local t = {} + for id, node, metadata in query:iter_captures(root, 0) do + t[query.captures[id]] = metadata + end + return t + ]=], + query + ) + + eq({ + ['_id'] = { ['injection.language'] = 'sql' }, + ['injection.content'] = { ['injection.language'] = 'sql' }, + }, result) + end) + + it('only evaluates predicates once per match', function() + insert([[ + void foo(int x, int y); + ]]) + local query = [[ + (declaration + type: (_) + declarator: (function_declarator + declarator: (identifier) @function.name + parameters: (parameter_list + (parameter_declaration + type: (_) + declarator: (identifier) @argument))) + (#eq? @function.name "foo")) + ]] + + local result = exec_lua( + [[ + local query = vim.treesitter.query.parse("c", ...) + local match_preds = query.match_preds + local called = 0 + function query:match_preds(...) + called = called + 1 + return match_preds(self, ...) + end + local parser = vim.treesitter.get_parser(0, "c") + local root = parser:parse()[1]:root() + local captures = {} + for id, node in query:iter_captures(root, 0) do + captures[#captures + 1] = id + end + return { called, captures } + ]], + query + ) + + eq({ 2, { 1, 1, 2, 2 } }, result) + end) + end) +end) diff --git a/test/functional/treesitter/utils_spec.lua b/test/functional/treesitter/utils_spec.lua index 2734c22499..bca0aca0cb 100644 --- a/test/functional/treesitter/utils_spec.lua +++ b/test/functional/treesitter/utils_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local insert = helpers.insert -local eq = helpers.eq -local exec_lua = helpers.exec_lua +local clear = n.clear +local insert = n.insert +local eq = t.eq +local exec_lua = n.exec_lua before_each(clear) diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua index 896f75a681..5590db5bc4 100644 --- a/test/functional/ui/bufhl_spec.lua +++ b/test/functional/ui/bufhl_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command, neq = helpers.command, helpers.neq -local api = helpers.api -local eq = helpers.eq -local pcall_err = helpers.pcall_err +local clear, feed, insert = n.clear, n.feed, n.insert +local command, neq = n.command, t.neq +local api = n.api +local eq = t.eq +local pcall_err = t.pcall_err local set_virtual_text = api.nvim_buf_set_virtual_text describe('Buffer highlighting', function() diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 6c4000ba41..1c6f19245a 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local eq = helpers.eq -local feed = helpers.feed -local clear = helpers.clear -local api = helpers.api -local fn = helpers.fn -local source = helpers.source -local exec_capture = helpers.exec_capture -local dedent = helpers.dedent -local command = helpers.command +local eq = t.eq +local feed = n.feed +local clear = n.clear +local api = n.api +local fn = n.fn +local source = n.source +local exec_capture = n.exec_capture +local dedent = t.dedent +local command = n.command local screen diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 0eb5770819..6edfb4a49c 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -1,15 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed = helpers.clear, helpers.feed -local source = helpers.source -local command = helpers.command -local assert_alive = helpers.assert_alive -local poke_eventloop = helpers.poke_eventloop -local exec = helpers.exec -local eval = helpers.eval -local eq = helpers.eq -local is_os = helpers.is_os -local api = helpers.api + +local clear, feed = n.clear, n.feed +local source = n.source +local command = n.command +local assert_alive = n.assert_alive +local poke_eventloop = n.poke_eventloop +local exec = n.exec +local eval = n.eval +local eq = t.eq +local is_os = t.is_os +local api = n.api local function new_screen(opt) local screen = Screen.new(25, 5) @@ -365,7 +367,7 @@ local function test_cmdline(linegrid) grid = [[ | {2:[No Name] }| - {1::}mak^e | + {1::}make^ | {3:[Command Line] }| | ]], @@ -377,7 +379,7 @@ local function test_cmdline(linegrid) grid = [[ | {2:[No Name] }| - {1::}mak^e | + {1::}make^ | {3:[Command Line] }| | ]], @@ -396,7 +398,7 @@ local function test_cmdline(linegrid) grid = [[ | {2:[No Name] }| - {1::}mak^e | + {1::}make^ | {3:[Command Line] }| | ]], @@ -416,7 +418,7 @@ local function test_cmdline(linegrid) grid = [[ | {2:[No Name] }| - {1::}mak^e | + {1::}make^ | {3:[Command Line] }| | ]], @@ -824,6 +826,30 @@ local function test_cmdline(linegrid) | ]]) end) + + it('does not move cursor to curwin #20309', function() + local win = api.nvim_get_current_win() + command('norm icmdlinewin') + command('new') + command('norm icurwin') + feed(':') + api.nvim_win_set_cursor(win, { 1, 7 }) + api.nvim__redraw({ win = win, cursor = true }) + screen:expect { + grid = [[ + curwin | + {3:[No Name] [+] }| + cmdline^win | + {2:[No Name] [+] }| + | + ]], + cmdline = { { + content = { { '' } }, + firstc = ':', + pos = 0, + } }, + } + end) end -- the representation of cmdline and cmdline_block contents changed with ext_linegrid @@ -1242,15 +1268,6 @@ describe('cmdheight=0', function() before_each(function() clear() screen = Screen.new(25, 5) - screen:set_default_attr_ids { - [1] = { bold = true, foreground = Screen.colors.Blue }, - [2] = { bold = true, reverse = true }, - [3] = { bold = true }, - [4] = { foreground = Screen.colors.White, background = Screen.colors.Red }, - [5] = { foreground = Screen.colors.SeaGreen4, bold = true }, - [6] = { reverse = true }, - [7] = { background = Screen.colors.Yellow }, - } screen:attach() end) @@ -1279,7 +1296,7 @@ describe('cmdheight=0', function() grid = [[ ^ | {1:~ }|*2 - {2:[No Name] }| + {3:[No Name] }| | ]], } @@ -1291,7 +1308,7 @@ describe('cmdheight=0', function() grid = [[ ^ | {1:~ }|*3 - {2:[No Name] }| + {3:[No Name] }| ]], } end) @@ -1341,7 +1358,7 @@ describe('cmdheight=0', function() grid = [[ ^ | {1:~ }|*3 - {3:-- INSERT --} | + {5:-- INSERT --} | ]], } feed('<Esc>') @@ -1377,7 +1394,7 @@ describe('cmdheight=0', function() grid = [[ | {1:~ }| - {2: }| + {3: }| :call input("foo >") | foo >^ | ]], @@ -1399,26 +1416,26 @@ describe('cmdheight=0', function() feed(':split<CR>') screen:expect { grid = [[ - {2: }| + {3: }| :split | - {4:E36: Not enough room} | - {5:Press ENTER or type comma}| - {5:nd to continue}^ | + {9:E36: Not enough room} | + {6:Press ENTER or type comma}| + {6:nd to continue}^ | ]], } feed('<CR>') screen:expect { grid = [[ - {3:foo }| + {5:foo }| ^ | {1:~ }|*2 - {2:[No Name] }| + {3:[No Name] }| ]], } feed(':') screen:expect { grid = [[ - {3:foo }| + {5:foo }| | {1:~ }|*2 :^ | @@ -1427,10 +1444,10 @@ describe('cmdheight=0', function() feed('<Esc>') screen:expect { grid = [[ - {3:foo }| + {5:foo }| ^ | {1:~ }|*2 - {2:[No Name] }| + {3:[No Name] }| ]], showmode = {}, } @@ -1465,16 +1482,16 @@ describe('cmdheight=0', function() grid = [[ fo^o | {1:~ }|*3 - {2:[No Name] [+] }| + {3:[No Name] [+] }| ]], } feed(':%s/foo/bar/gc<CR>') screen:expect { grid = [[ - {6:foo} | + {2:foo} | {1:~ }|*3 - {5:replace wi...q/l/^E/^Y)?}^ | + {6:replace wi...q/l/^E/^Y)?}^ | ]], } @@ -1483,7 +1500,7 @@ describe('cmdheight=0', function() grid = [[ ^bar | {1:~ }|*3 - {2:[No Name] [+] }| + {3:[No Name] [+] }| ]], } @@ -1502,7 +1519,7 @@ describe('cmdheight=0', function() feed('iaabbaa<esc>/aa<cr>') screen:expect { grid = [[ - {7:^aa}bb{7:aa} | + {10:^aa}bb{10:aa} | {1:~ }|*4 ]], } @@ -1561,7 +1578,7 @@ describe('cmdheight=0', function() [2:-------------------------]|*4 [3:-------------------------]| ## grid 2 - {6:p} | + {2:p} | {1:~ }|*4 ## grid 3 /p^ | @@ -1572,7 +1589,7 @@ describe('cmdheight=0', function() topline = 0, botline = 2, curline = 0, - curcol = 0, + curcol = 1, linecount = 1, sum_scroll_delta = 0, }, @@ -1587,7 +1604,7 @@ describe('cmdheight=0', function() ]]) feed(':') screen:expect([[ - {3:c :}| + {5:c :}| | {1:~ }|*2 :^ | @@ -1595,14 +1612,14 @@ describe('cmdheight=0', function() feed('echo') -- not redrawn yet screen:expect([[ - {3:c :}| + {5:c :}| | {1:~ }|*2 :echo^ | ]]) command('redrawstatus') screen:expect([[ - {3:c :echo}| + {5:c :echo}| | {1:~ }|*2 :echo^ | @@ -1623,7 +1640,7 @@ describe('cmdheight=0', function() screen:expect([[ ^ │ | {1:~ }│{1:~ }|*3 - {2:[No Name] }│{1:~ }| + {3:[No Name] }│{1:~ }| │{1:~ }| {1:~ }│{1:~ }|*3 ]]) @@ -1631,7 +1648,7 @@ describe('cmdheight=0', function() screen:expect([[ ^ │ | {1:~ }│{1:~ }| - {2:[No Name] }│{1:~ }| + {3:[No Name] }│{1:~ }| │{1:~ }| {1:~ }│{1:~ }|*5 ]]) @@ -1641,7 +1658,7 @@ describe('cmdheight=0', function() screen:expect([[ ^ │ | {1:~ }│{1:~ }|*3 - {2:[No Name] }│{1:~ }| + {3:[No Name] }│{1:~ }| │{1:~ }| {1:~ }│{1:~ }|*3 ]]) @@ -1663,7 +1680,7 @@ describe('cmdheight=0', function() screen:expect([[ ^ | {1:~ }|*2 - {2:[No Name] }| + {3:[No Name] }| | ]]) command('set cmdheight=0') @@ -1671,7 +1688,7 @@ describe('cmdheight=0', function() grid = [[ ^ | {1:~ }|*3 - {2:[No Name] }| + {3:[No Name] }| ]], } command('resize -1') @@ -1679,7 +1696,7 @@ describe('cmdheight=0', function() grid = [[ ^ | {1:~ }|*2 - {2:[No Name] }| + {3:[No Name] }| | ]], } @@ -1687,23 +1704,28 @@ describe('cmdheight=0', function() screen:expect([[ ^ | {1:~ }|*3 - {2:[No Name] }| + {3:[No Name] }| ]]) end) - it('cannot be resized at all with external messages', function() + it('can be resized with external messages', function() clear() screen = new_screen({ rgb = true, ext_messages = true }) command('set laststatus=2 mouse=a') command('resize -1') screen:expect([[ ^ | - {1:~ }|*3 + {1:~ }|*2 {3:[No Name] }| + | ]]) - api.nvim_input_mouse('left', 'press', '', 0, 6, 10) + api.nvim_input_mouse('left', 'press', '', 0, 3, 10) poke_eventloop() - api.nvim_input_mouse('left', 'drag', '', 0, 5, 10) - screen:expect_unchanged() + api.nvim_input_mouse('left', 'drag', '', 0, 4, 10) + screen:expect([[ + ^ | + {1:~ }|*3 + {3:[No Name] }| + ]]) end) end) diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 871e9a0790..8bfceb8cce 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -1,8 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, api = helpers.clear, helpers.api -local eq = helpers.eq -local command = helpers.command + +local clear, api = n.clear, n.api +local eq = t.eq +local command = n.command describe('ui/cursor', function() local screen @@ -204,7 +206,7 @@ describe('ui/cursor', function() screen:expect { grid = [[ ^ | - ~ |*3 + {1:~ }|*3 test | ]], condition = function() @@ -213,8 +215,8 @@ describe('ui/cursor', function() } -- Change the cursor style. - helpers.command('hi Cursor guibg=DarkGray') - helpers.command( + n.command('hi Cursor guibg=DarkGray') + n.command( 'set guicursor=n-v-c:block,i-ci-ve:ver25,r-cr-o:hor20' .. ',a:blinkwait700-blinkoff400-blinkon250-Cursor/lCursor' .. ',sm:block-blinkwait175-blinkoff150-blinkon175' @@ -260,8 +262,8 @@ describe('ui/cursor', function() end) -- Change hl groups only, should update the styles - helpers.command('hi Cursor guibg=Red') - helpers.command('hi lCursor guibg=Green') + n.command('hi Cursor guibg=Red') + n.command('hi lCursor guibg=Green') -- Update the expected values. for _, m in ipairs(expected_mode_info) do @@ -280,7 +282,7 @@ describe('ui/cursor', function() end) -- update the highlight again to hide cursor - helpers.command('hi Cursor blend=100') + n.command('hi Cursor blend=100') for _, m in ipairs(expected_mode_info) do if m.hl_id then @@ -290,7 +292,7 @@ describe('ui/cursor', function() screen:expect { grid = [[ ^ | - ~ |*3 + {1:~ }|*3 test | ]], condition = function() diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index e57e719192..746bfb3262 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1,18 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local feed = helpers.feed -local insert = helpers.insert -local exec_lua = helpers.exec_lua -local exec = helpers.exec -local expect_events = helpers.expect_events -local api = helpers.api -local fn = helpers.fn -local command = helpers.command -local eq = helpers.eq -local assert_alive = helpers.assert_alive -local pcall_err = helpers.pcall_err +local clear = n.clear +local feed = n.feed +local insert = n.insert +local exec_lua = n.exec_lua +local exec = n.exec +local expect_events = t.expect_events +local api = n.api +local fn = n.fn +local command = n.command +local eq = t.eq +local assert_alive = n.assert_alive +local pcall_err = t.pcall_err describe('decorations providers', function() local screen @@ -667,6 +668,33 @@ describe('decorations providers', function() ]]) end) + it('on_line is invoked only for buffer lines', function() + insert(mulholland) + command('vnew') + insert(mulholland) + feed('dd') + command('windo diffthis') + + exec_lua([[ + out_of_bound = false + ]]) + setup_provider([[ + local function on_do(kind, _, bufnr, row) + if kind == 'line' then + if not api.nvim_buf_get_lines(bufnr, row, row + 1, false)[1] then + out_of_bound = true + end + end + end + ]]) + + feed('<C-e>') + + exec_lua([[ + assert(out_of_bound == false) + ]]) + end) + it('errors gracefully', function() insert(mulholland) @@ -696,48 +724,7 @@ describe('decorations providers', function() end ]] - helpers.assert_alive() - end) - - it('supports subpriorities (order of definitions in a query file #27131)', function() - insert(mulholland) - setup_provider [[ - local test_ns = api.nvim_create_namespace('mulholland') - function on_do(event, ...) - if event == "line" then - local win, buf, line = ... - api.nvim_buf_set_extmark(buf, test_ns, line, 0, { - end_row = line + 1, - hl_eol = true, - hl_group = 'Comment', - ephemeral = true, - priority = 100, - _subpriority = 20, - }) - - -- This extmark is set last but has a lower subpriority, so the first extmark "wins" - api.nvim_buf_set_extmark(buf, test_ns, line, 0, { - end_row = line + 1, - hl_eol = true, - hl_group = 'String', - ephemeral = true, - priority = 100, - _subpriority = 10, - }) - end - end - ]] - - screen:expect{grid=[[ - {4:// just to see if there was an accident }| - {4:// on Mulholland Drive }| - {4:try_start(); }| - {4:bufref_T save_buf; }| - {4:switch_buffer(&save_buf, buf); }| - {4:posp = getmark(mark, false); }| - {4:restore_buffer(&save_buf);^ }| - | - ]]} + n.assert_alive() end) it('is not invoked repeatedly in Visual mode with vim.schedule() #20235', function() @@ -2301,8 +2288,21 @@ describe('extmark decorations', function() ]]} end) + it('virtual text does not crash with blend, conceal and wrap #27836', function() + screen:try_resize(50, 3) + insert(('a'):rep(45) .. '|hidden|' .. ('b'):rep(45)) + command('syntax match test /|hidden|/ conceal') + command('set conceallevel=2 concealcursor=n') + api.nvim_buf_set_extmark(0, ns, 0, 0, {virt_text = {{'FOO'}}, virt_text_pos='right_align', hl_mode='blend'}) + screen:expect{grid=[[ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa FOO| + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb^b | + | + ]]} + end) + it('works with both hl_group and sign_hl_group', function() - screen:try_resize(screen._width, 3) + screen:try_resize(50, 3) insert('abcdefghijklmn') api.nvim_buf_set_extmark(0, ns, 0, 0, {sign_text='S', sign_hl_group='NonText', hl_group='Error', end_col=14}) screen:expect{grid=[[ @@ -2343,10 +2343,9 @@ describe('extmark decorations', function() local url = 'https://example.com' - screen:set_default_attr_ids({ - e = { bold = true, foreground = Screen.colors.Blue }, - u = { url = url }, - }) + screen:add_extra_attr_ids { + u = { url = "https://example.com" }, + } api.nvim_buf_set_extmark(0, ns, 1, 4, { end_col = 14, @@ -2366,8 +2365,8 @@ describe('extmark decorations', function() colpos = colpos+1 | end | en^d | - {e:~ }| - {e:~ }| + {1:~ }| + {1:~ }| | ]]} end) @@ -2399,7 +2398,7 @@ describe('extmark decorations', function() | ]]} - helpers.assert_alive() + n.assert_alive() end) it('priority ordering of overlay or win_col virtual text at same position', function() @@ -4042,16 +4041,8 @@ describe('decorations: virtual lines', function() clear() screen = Screen.new(50, 12) screen:attach() - screen:set_default_attr_ids { - [1] = {bold=true, foreground=Screen.colors.Blue}; - [2] = {foreground = Screen.colors.DarkCyan}; - [3] = {background = Screen.colors.Yellow1}; - [4] = {bold = true}; - [5] = {background = Screen.colors.Yellow, foreground = Screen.colors.Blue}; - [6] = {foreground = Screen.colors.Blue}; - [7] = {foreground = Screen.colors.SlateBlue}; - [8] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue}; - [9] = {foreground = Screen.colors.Brown}; + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.Blue, background = Screen.colors.Yellow }, } ns = api.nvim_create_namespace 'test' @@ -4092,7 +4083,7 @@ if (h->n_buckets < new_n_buckets) { // expand screen:expect{grid=[[ if (h->n_buckets < new_n_buckets) { // expand | - {1:>> }{2:krealloc}: change the size of an allocation | + {1:>> }{25:krealloc}: change the size of an allocation | ^khkey_t *new_keys = (khkey_t *)krealloc((void *)| h->keys, new_n_buckets * sizeof(khkey_t)); | h->keys = new_keys; | @@ -4108,12 +4099,12 @@ if (h->n_buckets < new_n_buckets) { // expand feed '/krealloc<cr>' screen:expect{grid=[[ if (h->n_buckets < new_n_buckets) { // expand | - {1:>> }{2:krealloc}: change the size of an allocation | - khkey_t *new_keys = (khkey_t *){3:^krealloc}((void *)| + {1:>> }{25:krealloc}: change the size of an allocation | + khkey_t *new_keys = (khkey_t *){10:^krealloc}((void *)| h->keys, new_n_buckets * sizeof(khkey_t)); | h->keys = new_keys; | if (kh_is_map && val_size) { | - char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + char *new_vals = {10:krealloc}( h->vals_buf, new_n_| buckets * val_size); | h->vals_buf = new_vals; | } | @@ -4126,28 +4117,28 @@ if (h->n_buckets < new_n_buckets) { // expand screen:expect{grid=[[ if (h->n_buckets < new_n_buckets) { // expand | khkey_t *new_keys = (khkey_t *) | - {1:>> }{2:krealloc}: change the size of an allocation | - {3:^krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + {1:>> }{25:krealloc}: change the size of an allocation | + {10:^krealloc}((void *)h->keys, new_n_buckets * sizeof(k| hkey_t)); | h->keys = new_keys; | if (kh_is_map && val_size) { | - char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + char *new_vals = {10:krealloc}( h->vals_buf, new_n_| buckets * val_size); | h->vals_buf = new_vals; | } | - {4:-- INSERT --} | + {5:-- INSERT --} | ]]} feed '<esc>3+' screen:expect{grid=[[ if (h->n_buckets < new_n_buckets) { // expand | khkey_t *new_keys = (khkey_t *) | - {1:>> }{2:krealloc}: change the size of an allocation | - {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + {1:>> }{25:krealloc}: change the size of an allocation | + {10:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| hkey_t)); | h->keys = new_keys; | if (kh_is_map && val_size) { | - ^char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + ^char *new_vals = {10:krealloc}( h->vals_buf, new_n_| buckets * val_size); | h->vals_buf = new_vals; | } | @@ -4160,14 +4151,14 @@ if (h->n_buckets < new_n_buckets) { // expand screen:expect{grid=[[ if (h->n_buckets < new_n_buckets) { // expand | khkey_t *new_keys = (khkey_t *) | - {1:>> }{2:krealloc}: change the size of an allocation | - {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + {1:>> }{25:krealloc}: change the size of an allocation | + {10:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| hkey_t)); | h->keys = new_keys; | if (kh_is_map && val_size) { | - ^char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + ^char *new_vals = {10:krealloc}( h->vals_buf, new_n_| buckets * val_size); | - {5:^^ REVIEW:}{6: new_vals variable seems unnecessary?} | + {100:^^ REVIEW:}{18: new_vals variable seems unnecessary?} | h->vals_buf = new_vals; | | ]]} @@ -4176,11 +4167,11 @@ if (h->n_buckets < new_n_buckets) { // expand screen:expect{grid=[[ if (h->n_buckets < new_n_buckets) { // expand | khkey_t *new_keys = (khkey_t *) | - {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + {10:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| hkey_t)); | h->keys = new_keys; | if (kh_is_map && val_size) { | - ^char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + ^char *new_vals = {10:krealloc}( h->vals_buf, new_n_| buckets * val_size); | h->vals_buf = new_vals; | } | @@ -4235,9 +4226,9 @@ if (h->n_buckets < new_n_buckets) { // expand feed '<c-b>' screen:expect{grid=[[ - {7:refactor(khash): }take size of values as parameter | - Author: Dev Devsson, {6:Tue Aug 31 10:13:37 2021} | - ^if (h->n_buckets < new_n_buckets) { // expand | + {16:refactor(khash): }take size of values as parameter | + Author: Dev Devsson, {18:Tue Aug 31 10:13:37 2021} | + if (h->n_buckets < new_n_buckets) { // expand | khkey_t *new_keys = (khkey_t *)krealloc((void *)| h->keys, new_n_buckets * sizeof(khkey_t)); | h->keys = new_keys; | @@ -4245,7 +4236,7 @@ if (h->n_buckets < new_n_buckets) { // expand char *new_vals = krealloc( h->vals_buf, new_n_| buckets * val_size); | h->vals_buf = new_vals; | - } | + ^} | | ]]} end) @@ -4461,7 +4452,7 @@ if (h->n_buckets < new_n_buckets) { // expand api.nvim_buf_set_extmark(0, ns, 0, 0, {virt_lines = {{{'bar', 'Comment'}}}}) screen:expect([[ fo^o | - {6:bar} | + {18:bar} | {1:~ }|*9 | ]]) @@ -4520,7 +4511,7 @@ if (h->n_buckets < new_n_buckets) { // expand ff | gg | they see me | - {7:scrolling} | + {16:scrolling} | | ]]} @@ -4530,7 +4521,7 @@ if (h->n_buckets < new_n_buckets) { // expand ff | gg | they see me | - {7:scrolling} | + {16:scrolling} | they | | ]]} @@ -4540,9 +4531,9 @@ if (h->n_buckets < new_n_buckets) { // expand ^ff | gg | they see me | - {7:scrolling} | + {16:scrolling} | they | - {7:hatin'} | + {16:hatin'} | | ]]} @@ -4550,9 +4541,9 @@ if (h->n_buckets < new_n_buckets) { // expand screen:expect{grid=[[ ^gg | they see me | - {7:scrolling} | + {16:scrolling} | they | - {7:hatin'} | + {16:hatin'} | hh | | ]]} @@ -4560,9 +4551,9 @@ if (h->n_buckets < new_n_buckets) { // expand feed '<c-e>' screen:expect{grid=[[ they see me | - {7:scrolling} | + {16:scrolling} | they | - {7:hatin'} | + {16:hatin'} | ^hh | {1:~ }| | @@ -4570,9 +4561,9 @@ if (h->n_buckets < new_n_buckets) { // expand feed '<c-e>' screen:expect{grid=[[ - {7:scrolling} | + {16:scrolling} | they | - {7:hatin'} | + {16:hatin'} | ^hh | {1:~ }|*2 | @@ -4581,7 +4572,7 @@ if (h->n_buckets < new_n_buckets) { // expand feed '<c-e>' screen:expect{grid=[[ they | - {7:hatin'} | + {16:hatin'} | ^hh | {1:~ }|*3 | @@ -4589,7 +4580,7 @@ if (h->n_buckets < new_n_buckets) { // expand feed '<c-e>' screen:expect{grid=[[ - {7:hatin'} | + {16:hatin'} | ^hh | {1:~ }|*4 | @@ -4608,17 +4599,17 @@ if (h->n_buckets < new_n_buckets) { // expand feed 'gg' command 'set number signcolumn=yes' screen:expect{grid=[[ - {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| - {8: }{9: }d | - {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| - {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| - {8: }{9: }t)); | - {8: }{9: 3 } h->keys = new_keys; | - {8: }{9: 4 } if (kh_is_map && val_size) { | - {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | - {8: }{9: }new_n_buckets * val_size); | - {8: }{9: 6 } h->vals_buf = new_vals; | - {8: }{9: 7 } } | + {7: }{8: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {7: }{8: }d | + {7: }{8: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {7: }{8: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {7: }{8: }t)); | + {7: }{8: 3 } h->keys = new_keys; | + {7: }{8: 4 } if (kh_is_map && val_size) { | + {7: }{8: 5 } char *new_vals = krealloc( h->vals_buf, | + {7: }{8: }new_n_buckets * val_size); | + {7: }{8: 6 } h->vals_buf = new_vals; | + {7: }{8: 7 } } | | ]]} @@ -4630,17 +4621,17 @@ if (h->n_buckets < new_n_buckets) { // expand }) screen:expect{grid=[[ - {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| - {8: }{9: }d | - {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| - {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| - {8: }{9: }t)); | - {8: }{9: 3 } h->keys = new_keys; | - {8: }{9: }{7:Some special} | - {8: }{9: }{6:remark about codes} | - {8: }{9: 4 } if (kh_is_map && val_size) { | - {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | - {8: }{9: }new_n_buckets * val_size); | + {7: }{8: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {7: }{8: }d | + {7: }{8: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {7: }{8: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {7: }{8: }t)); | + {7: }{8: 3 } h->keys = new_keys; | + {7: }{8: }{16:Some special} | + {7: }{8: }{18:remark about codes} | + {7: }{8: 4 } if (kh_is_map && val_size) { | + {7: }{8: 5 } char *new_vals = krealloc( h->vals_buf, | + {7: }{8: }new_n_buckets * val_size); | | ]]} @@ -4653,17 +4644,17 @@ if (h->n_buckets < new_n_buckets) { // expand id=markid; }) screen:expect{grid=[[ - {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| - {8: }{9: }d | - {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| - {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| - {8: }{9: }t)); | - {8: }{9: 3 } h->keys = new_keys; | - {7:Some special} | - {6:remark about codes} | - {8: }{9: 4 } if (kh_is_map && val_size) { | - {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | - {8: }{9: }new_n_buckets * val_size); | + {7: }{8: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {7: }{8: }d | + {7: }{8: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {7: }{8: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {7: }{8: }t)); | + {7: }{8: 3 } h->keys = new_keys; | + {16:Some special} | + {18:remark about codes} | + {7: }{8: 4 } if (kh_is_map && val_size) { | + {7: }{8: 5 } char *new_vals = krealloc( h->vals_buf, | + {7: }{8: }new_n_buckets * val_size); | | ]]} end) @@ -4679,7 +4670,7 @@ if (h->n_buckets < new_n_buckets) { // expand ^if (h->n_buckets < new_n_buckets) { // expand | khkey_t *new_keys = (khkey_t *)krealloc((void *)| h->keys, new_n_buckets * sizeof(khkey_t)); | - {1:>>}{2: very tabby}text with tabs | + {1:>>}{25: very tabby}text with tabs | h->keys = new_keys; | if (kh_is_map && val_size) { | char *new_vals = krealloc( h->vals_buf, new_n_| @@ -4695,7 +4686,7 @@ if (h->n_buckets < new_n_buckets) { // expand ^if (h->n_buckets < new_n_buckets) { // expand | khkey_t *new_keys = (khkey_t *)krealloc((void *)| h->keys, new_n_buckets * sizeof(khkey_t)); | - {1:>>}{2: very tabby}text with tabs | + {1:>>}{25: very tabby}text with tabs | h->keys = new_keys; | if (kh_is_map && val_size) { | char *new_vals = krealloc( h->vals_buf, new_n_| @@ -4708,33 +4699,33 @@ if (h->n_buckets < new_n_buckets) { // expand command 'set number' screen:expect{grid=[[ - {9: 1 }^if (h->n_buckets < new_n_buckets) { // expand | - {9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((voi| - {9: }d *)h->keys, new_n_buckets * sizeof(khkey_t));| - {9: }{1:>>}{2: very tabby}text with tabs | - {9: 3 } h->keys = new_keys; | - {9: 4 } if (kh_is_map && val_size) { | - {9: 5 } char *new_vals = krealloc( h->vals_buf, ne| - {9: }w_n_buckets * val_size); | - {9: 6 } h->vals_buf = new_vals; | - {9: 7 } } | - {9: 8 }} | + {8: 1 }^if (h->n_buckets < new_n_buckets) { // expand | + {8: 2 } khkey_t *new_keys = (khkey_t *)krealloc((voi| + {8: }d *)h->keys, new_n_buckets * sizeof(khkey_t));| + {8: }{1:>>}{25: very tabby}text with tabs | + {8: 3 } h->keys = new_keys; | + {8: 4 } if (kh_is_map && val_size) { | + {8: 5 } char *new_vals = krealloc( h->vals_buf, ne| + {8: }w_n_buckets * val_size); | + {8: 6 } h->vals_buf = new_vals; | + {8: 7 } } | + {8: 8 }} | | ]]} command 'set tabstop&' screen:expect{grid=[[ - {9: 1 }^if (h->n_buckets < new_n_buckets) { // expand | - {9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((voi| - {9: }d *)h->keys, new_n_buckets * sizeof(khkey_t));| - {9: }{1:>>}{2: very tabby}text with tabs | - {9: 3 } h->keys = new_keys; | - {9: 4 } if (kh_is_map && val_size) { | - {9: 5 } char *new_vals = krealloc( h->vals_buf, ne| - {9: }w_n_buckets * val_size); | - {9: 6 } h->vals_buf = new_vals; | - {9: 7 } } | - {9: 8 }} | + {8: 1 }^if (h->n_buckets < new_n_buckets) { // expand | + {8: 2 } khkey_t *new_keys = (khkey_t *)krealloc((voi| + {8: }d *)h->keys, new_n_buckets * sizeof(khkey_t));| + {8: }{1:>>}{25: very tabby}text with tabs | + {8: 3 } h->keys = new_keys; | + {8: 4 } if (kh_is_map && val_size) { | + {8: 5 } char *new_vals = krealloc( h->vals_buf, ne| + {8: }w_n_buckets * val_size); | + {8: 6 } h->vals_buf = new_vals; | + {8: 7 } } | + {8: 8 }} | | ]]} end) @@ -4771,12 +4762,12 @@ if (h->n_buckets < new_n_buckets) { // expand api.nvim_buf_set_extmark(0, ns, 0, 0, {virt_lines = {{{'VIRT LINE 1', 'NonText'}}}, virt_lines_leftcol = true}) api.nvim_buf_set_extmark(0, ns, 3, 0, {virt_lines = {{{'VIRT LINE 2', 'NonText'}}}}) screen:expect{grid=[[ - aaa{9: 1 }| + aaa{8: 1 }| {1:1 ENIL TRIV}| - bbb{9: 2 }| - ccc{9: 3 }| - ^ddd{9: 4 }| - {1:2 ENIL TRIV}{9: }| + bbb{8: 2 }| + ccc{8: 3 }| + ^ddd{8: 4 }| + {1:2 ENIL TRIV}{8: }| {1: ~}| | ]]} @@ -4847,6 +4838,104 @@ if (h->n_buckets < new_n_buckets) { // expand ]]) end) + it('does not break cursor position with concealcursor #27887', function() + command('vsplit') + insert('\n') + api.nvim_set_option_value('conceallevel', 2, {}) + api.nvim_set_option_value('concealcursor', 'niv', {}) + api.nvim_buf_set_extmark(0, ns, 0, 0, { virt_lines = {{{'VIRT1'}}, {{'VIRT2'}}} }) + screen:expect([[ + │ | + VIRT1 │VIRT1 | + VIRT2 │VIRT2 | + ^ │ | + {1:~ }│{1:~ }|*6 + {3:[No Name] [+] }{2:[No Name] [+] }| + | + ]]) + end) + + it('works with full page scrolling #28290', function() + screen:try_resize(20, 8) + command('call setline(1, range(20))') + api.nvim_buf_set_extmark(0, ns, 10, 0, { virt_lines = {{{'VIRT1'}}, {{'VIRT2'}}} }) + screen:expect([[ + ^0 | + 1 | + 2 | + 3 | + 4 | + 5 | + 6 | + | + ]]) + feed('<C-F>') + screen:expect([[ + ^5 | + 6 | + 7 | + 8 | + 9 | + 10 | + VIRT1 | + | + ]]) + feed('<C-F>') + screen:expect([[ + ^10 | + VIRT1 | + VIRT2 | + 11 | + 12 | + 13 | + 14 | + | + ]]) + feed('<C-F>') + screen:expect([[ + ^13 | + 14 | + 15 | + 16 | + 17 | + 18 | + 19 | + | + ]]) + feed('<C-B>') + screen:expect([[ + 10 | + VIRT1 | + VIRT2 | + 11 | + 12 | + 13 | + ^14 | + | + ]]) + feed('<C-B>') + screen:expect([[ + 5 | + 6 | + 7 | + 8 | + 9 | + ^10 | + VIRT1 | + | + ]]) + feed('<C-B>') + screen:expect([[ + 0 | + 1 | + 2 | + 3 | + 4 | + 5 | + ^6 | + | + ]]) + end) end) describe('decorations: signs', function() @@ -4855,11 +4944,8 @@ describe('decorations: signs', function() clear() screen = Screen.new(50, 10) screen:attach() - screen:set_default_attr_ids { - [1] = {foreground = Screen.colors.Blue4, background = Screen.colors.Grey}; - [2] = {foreground = Screen.colors.Blue1, bold = true}; - [3] = {background = Screen.colors.Yellow1, foreground = Screen.colors.Blue1}; - [4] = {foreground = Screen.colors.Gray100, background = Screen.colors.Red}; + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.Blue, background = Screen.colors.Yellow }, } ns = api.nvim_create_namespace 'test' @@ -4881,13 +4967,13 @@ l5 api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S'}) screen:expect{grid=[[ - {1: }^l1 | + {7: }^l1 | S l2 | - {1: }l3 | - {1: }l4 | - {1: }l5 | - {1: } | - {2:~ }|*3 + {7: }l3 | + {7: }l4 | + {7: }l5 | + {7: } | + {1:~ }|*3 | ]]} end) @@ -4899,13 +4985,13 @@ l5 api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row=1}) screen:expect{grid=[[ - {1: }^l1 | + {7: }^l1 | S l2 | - {1: }l3 | - {1: }l4 | - {1: }l5 | - {1: } | - {2:~ }|*3 + {7: }l3 | + {7: }l4 | + {7: }l5 | + {7: } | + {1:~ }|*3 | ]]} end) @@ -4916,13 +5002,13 @@ l5 api.nvim_buf_set_extmark(0, ns, 1, 0, {sign_text='S', hl_group='Todo', end_col=1}) screen:expect{grid=[[ - {1: }^l1 | - S {3:l}2 | - {1: }l3 | - {1: }l4 | - {1: }l5 | - {1: } | - {2:~ }|*3 + {7: }^l1 | + S {100:l}2 | + {7: }l3 | + {7: }l4 | + {7: }l5 | + {7: } | + {1:~ }|*3 | ]]} @@ -4936,13 +5022,13 @@ l5 api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row = 2}) screen:expect{grid=[[ - {1: }^l1 | + {7: }^l1 | S l2 | S l3 | - {1: }l4 | - {1: }l5 | - {1: } | - {2:~ }|*3 + {7: }l4 | + {7: }l5 | + {7: } | + {1:~ }|*3 | ]]} end) @@ -4955,13 +5041,13 @@ l5 api.nvim_buf_set_extmark(0, ns, 3, -1, {sign_text='S2', end_row = 4}) screen:expect{grid=[[ - {1: }^l1 | + {7: }^l1 | S1l2 | - {1: }l3 | + {7: }l3 | S2l4 | S2l5 | - {1: } | - {2:~ }|*3 + {7: } | + {1:~ }|*3 | ]]} end) @@ -4973,13 +5059,13 @@ l5 api.nvim_buf_set_extmark(0, ns, 3, -1, {sign_text='S1'}) api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S2', end_row = 3}) screen:expect{grid=[[ - {1: }^l1 | - S2{1: }l2 | - S2{1: }l3 | - S1S2l4 | - {1: }l5 | - {1: } | - {2:~ }|*3 + {7: }^l1 | + S2{7: }l2 | + S2{7: }l3 | + S2S1l4 | + {7: }l5 | + {7: } | + {1:~ }|*3 | ]]} end) @@ -4993,13 +5079,13 @@ l5 api.nvim_buf_set_extmark(0, ns, 2, -1, {sign_text='S2', end_row=3}) screen:expect{grid=[[ - {1: }^l1 | - S1{1: }l2 | - S1S2l3 | - S2{1: }l4 | - {1: }l5 | - {1: } | - {2:~ }|*3 + {7: }^l1 | + S1{7: }l2 | + S2S1l3 | + S2{7: }l4 | + {7: }l5 | + {7: } | + {1:~ }|*3 | ]]} end) @@ -5014,11 +5100,11 @@ l5 screen:expect{grid=[[ S1^l1 | S2l2 | - {1: }l3 | - {1: }l4 | - {1: }l5 | - {1: } | - {2:~ }|*3 + {7: }l3 | + {7: }l4 | + {7: }l5 | + {7: } | + {1:~ }|*3 | ]]} end) @@ -5027,8 +5113,8 @@ l5 insert(example_test3) feed 'gg' - helpers.command('sign define Oldsign text=x') - helpers.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]]) + n.command('sign define Oldsign text=x') + n.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]]) api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S1'}) api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S2'}) @@ -5036,13 +5122,13 @@ l5 api.nvim_buf_set_extmark(0, ns, 2, -1, {sign_text='S5'}) screen:expect{grid=[[ - S1S4^l1 | - x S2l2 | - S5{1: }l3 | - {1: }l4 | - {1: }l5 | - {1: } | - {2:~ }|*3 + S4S1^l1 | + S2x l2 | + S5{7: }l3 | + {7: }l4 | + {7: }l5 | + {7: } | + {1:~ }|*3 | ]]} end) @@ -5051,8 +5137,8 @@ l5 insert(example_test3) feed 'gg' - helpers.command('sign define Oldsign text=x') - helpers.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]]) + n.command('sign define Oldsign text=x') + n.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]]) api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S1'}) api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='S2'}) @@ -5061,13 +5147,13 @@ l5 api.nvim_buf_set_extmark(0, ns, 2, -1, {sign_text='S5'}) screen:expect{grid=[[ - S1S3S4^l1 | - x S2S3l2 | - S3S5{1: }l3 | - S3{1: }l4 | - S3{1: }l5 | - {1: } | - {2:~ }|*3 + S4S3S1^l1 | + S3S2x l2 | + S5S3{7: }l3 | + S3{7: }l4 | + S3{7: }l5 | + {7: } | + {1:~ }|*3 | ]]} end) @@ -5081,11 +5167,11 @@ l5 api.nvim_buf_set_extmark(0, ns, 1, -1, {sign_text='X', end_row=3}) screen:expect{grid=[[ - X {1: }^l3 | - X {1: }l4 | - {1: }l5 | - {1: } | - {2:~ }|*5 + X {7: }^l3 | + X {7: }l4 | + {7: }l5 | + {7: } | + {1:~ }|*5 | ]]} end) @@ -5110,8 +5196,8 @@ l5 end screen:expect{grid=[[ - W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |*8 - W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:^h} | + Z Y X W {100:a} {100:b} {100:c} {100:d} {100:e} {100:f} {100:g} {100:h} |*8 + Z Y X W {100:a} {100:b} {100:c} {100:d} {100:e} {100:f} {100:g} {100:^h} | | ]]} end) @@ -5130,8 +5216,8 @@ l5 api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S1', priority=1}) screen:expect{grid=[[ - S1S2O3S4S5^l1 | - {1: }l2 | + S5S4O3S2S1^l1 | + {7: }l2 | | ]]} @@ -5140,7 +5226,7 @@ l5 screen:expect{grid=[[ S5^l1 | - {1: }l2 | + {7: }l2 | | ]]} end) @@ -5161,7 +5247,7 @@ l5 command([[exe 'sign place 09 line=1 name=Oldsign priority=10 buffer=' . bufnr('')]]) screen:expect{grid=[[ O3O3O3O3O3O3O3O3O3^ | - {2:~ }| + {1:~ }| | ]]} @@ -5170,8 +5256,8 @@ l5 api.nvim_buf_set_extmark(0, ns, 0, -1, {sign_text='S5', priority=200}) screen:expect{grid=[[ - O3O3O3O3O3O3O3O3S5^ | - {2:~ }| + S5O3O3O3O3O3O3O3O3^ | + {1:~ }| | ]]} @@ -5216,34 +5302,34 @@ l5 feed('gg') local s1 = [[ - S1S2^l1 | - S2S3l2 | - S2S3l3 | + S2S1^l1 | + S3S2l2 | + S3S2l3 | | ]] screen:expect{grid=s1} -- Correct width when :move'ing a line with signs command('move2') screen:expect{grid=[[ - S3{1: }l2 | - S1S2S3^l1 | - {1: }l3 | + S3{7: }l2 | + S3S2S1^l1 | + {7: }l3 | | ]]} command('silent undo') screen:expect{grid=s1} command('d') screen:expect{grid=[[ - S1S2S3^l2 | - S2S3{1: }l3 | - {1: }l4 | + S3S2S1^l2 | + S3S2{7: }l3 | + {7: }l4 | | ]]} command('d') screen:expect{grid=[[ - S1S2S3^l3 | - {1: }l4 | - {1: }l5 | + S3S2S1^l3 | + {7: }l4 | + {7: }l5 | | ]]} end) @@ -5289,9 +5375,9 @@ l5 norm 4Gdd ]]) screen:expect{grid=[[ - {1: }l3 | - S1S2l5 | - {1: }^ | + {7: }l3 | + S2S1l5 | + {7: }^ | | ]]} end) @@ -5306,7 +5392,7 @@ l5 screen:expect{grid=[[ S1l | S2^1 | - {1: }l2 | + {7: }l2 | | ]]} end) @@ -5319,8 +5405,8 @@ l5 api.nvim_buf_set_extmark(buf, ns, 0, 0, { sign_text = 'h' }) screen:expect{grid=[[ h ^l1 | - {1: }l2 | - {1: }l3 | + {7: }l2 | + {7: }l3 | | ]]} api.nvim_win_set_buf(0, api.nvim_create_buf(false, true)) @@ -5329,7 +5415,7 @@ l5 api.nvim_win_set_buf(0, buf) screen:expect{grid=[[ ^ | - {2:~ }|*2 + {1:~ }|*2 | ]]} end) @@ -5370,8 +5456,8 @@ l5 api.nvim_buf_set_extmark(0, ns, 0, 0, {sign_text = 'S1'}) screen:expect{grid=[[ - S1{4:^a} | - {2:~ }|*2 + S1{9:^a} | + {1:~ }|*2 | ]]} end) @@ -5382,8 +5468,8 @@ l5 api.nvim_buf_set_extmark(0, ns2, 0, 0, {sign_text = 'S2', id = 1}) screen:expect{grid=[[ - S1S2^ | - {2:~ }|*8 + S2S1^ | + {1:~ }|*8 | ]]} end) @@ -5403,9 +5489,9 @@ l5 command('0d29') screen:expect{grid=[[ - S1S2S3S4{4:^foo} | - S5{1: }{4:foo} | - {2:~ }|*7 + S4S3S2S1{9:^foo} | + S5{7: }{9:foo} | + {1:~ }|*7 29 fewer lines | ]]} @@ -5420,11 +5506,6 @@ describe('decorations: virt_text', function() clear() screen = Screen.new(50, 10) screen:attach() - screen:set_default_attr_ids { - [1] = {foreground = Screen.colors.Brown}; - [2] = {foreground = Screen.colors.Fuchsia}; - [3] = {bold = true, foreground = Screen.colors.Blue1}; - } end) it('avoids regression in #17638', function() @@ -5444,12 +5525,12 @@ describe('decorations: virt_text', function() }) screen:expect{grid=[[ - {1: 4 } | - {1: 3 }hello | - {1: 2 }hello {2:hello} | - {1: 1 }hello | - {1:5 }helloVIRTUA^L | - {3:~ }|*4 + {8: 4 } | + {8: 3 }hello | + {8: 2 }hello {26:hello} | + {8: 1 }hello | + {8:5 }helloVIRTUA^L | + {1:~ }|*4 | ]]} @@ -5457,12 +5538,12 @@ describe('decorations: virt_text', function() feed('k') screen:expect{grid=[[ - {1: 3 } | - {1: 2 }hello | - {1: 1 }hello {2:hello} | - {1:4 }hell^o | - {1: 1 }helloVIRTUAL | - {3:~ }|*4 + {8: 3 } | + {8: 2 }hello | + {8: 1 }hello {26:hello} | + {8:4 }hell^o | + {8: 1 }helloVIRTUAL | + {1:~ }|*4 | ]]} end) @@ -5474,7 +5555,7 @@ describe('decorations: virt_text', function() | hello |*4 hell^o | - {3:~ }|*3 + {1:~ }|*3 | ]]} @@ -5487,7 +5568,7 @@ describe('decorations: virt_text', function() | hello |*4 hell^o world | - {3:~ }|*3 + {1:~ }|*3 | ]]} end) @@ -5495,13 +5576,14 @@ end) describe('decorations: window scoped', function() local screen, ns + local url = 'https://example.com' before_each(function() clear() screen = Screen.new(20, 10) screen:attach() - screen:set_default_attr_ids { - [1] = { foreground = Screen.colors.Blue1 }, - [2] = { foreground = Screen.colors.Blue1, bold = true }, + screen:add_extra_attr_ids { + [100] = { special = Screen.colors.Red, undercurl = true }, + [101] = { url = "https://example.com" }, } ns = api.nvim_create_namespace 'test' @@ -5512,7 +5594,7 @@ describe('decorations: window scoped', function() local noextmarks = { grid = [[ 1234^5 | - {2:~ }|*8 + {1:~ }|*8 | ]]} @@ -5528,12 +5610,12 @@ describe('decorations: window scoped', function() screen:expect(noextmarks) - api.nvim_win_add_ns(0, ns) + api.nvim__win_add_ns(0, ns) screen:expect { grid = [[ - {1:123}4^5 | - {2:~ }|*8 + {18:123}4^5 | + {1:~ }|*8 | ]]} @@ -5563,12 +5645,12 @@ describe('decorations: window scoped', function() screen:expect(noextmarks) - api.nvim_win_add_ns(0, ns) + api.nvim__win_add_ns(0, ns) screen:expect { grid = [[ - 1{1:c}34^5{1:b} {1:a} {1:d}| - {2:~ }|*8 + 1{18:c}34^5{18:b} {18:a} {18:d}| + {1:~ }|*8 | ]]} @@ -5585,13 +5667,13 @@ describe('decorations: window scoped', function() screen:expect(noextmarks) - api.nvim_win_add_ns(0, ns) + api.nvim__win_add_ns(0, ns) screen:expect { grid = [[ 1234^5 | - {1:a} | - {2:~ }|*7 + {18:a} | + {1:~ }|*7 | ]]} @@ -5609,17 +5691,17 @@ describe('decorations: window scoped', function() screen:expect(noextmarks) - api.nvim_win_add_ns(0, ns) + api.nvim__win_add_ns(0, ns) screen:expect { grid = [[ - 12{1:bbbbbbbbbbbbbbbbbb}| + 12{18:bbbbbbbbbbbbbbbbbb}| 34^5 | - {2:~ }|*7 + {1:~ }|*7 | ]]} - api.nvim_win_remove_ns(0, ns) + api.nvim__win_del_ns(0, ns) screen:expect(noextmarks) end) @@ -5634,7 +5716,7 @@ describe('decorations: window scoped', function() screen:expect(noextmarks) - api.nvim_win_add_ns(0, ns) + api.nvim__win_add_ns(0, ns) screen:expect { grid = [[ @@ -5650,12 +5732,6 @@ describe('decorations: window scoped', function() end) it('statuscolumn hl group', function() - local attrs = screen:get_default_attr_ids() - table.insert(attrs, { - foreground = Screen.colors.Brown, - }) - screen:set_default_attr_ids(attrs) - set_scoped_extmark(0, 0, { number_hl_group='comment', }) @@ -5667,17 +5743,17 @@ describe('decorations: window scoped', function() screen:expect { grid = [[ - {3: 1 }1234^5 | - {2:~ }|*8 + {8: 1 }1234^5 | + {1:~ }|*8 | ]]} - api.nvim_win_add_ns(0, ns) + api.nvim__win_add_ns(0, ns) screen:expect { grid = [[ - {1: 1 1234^5 }| - {2:~ }|*8 + {18: 1 1234^5 }| + {1:~ }|*8 | ]]} @@ -5686,18 +5762,13 @@ describe('decorations: window scoped', function() screen:expect { grid = [[ - {3: 1 }1234^5 | - {2:~ }|*8 + {8: 1 }1234^5 | + {1:~ }|*8 | ]]} end) it('spell', function() - local attrs = screen:get_default_attr_ids() - table.insert(attrs, { - special = Screen.colors.Red, undercurl = true - }) - screen:set_default_attr_ids(attrs) api.nvim_buf_set_lines(0,0,-1,true,{'aa'}) set_scoped_extmark(0, 0, { @@ -5712,16 +5783,16 @@ describe('decorations: window scoped', function() screen:expect { grid = [[ a^a | - {2:~ }|*8 + {1:~ }|*8 | ]]} - api.nvim_win_add_ns(0, ns) + api.nvim__win_add_ns(0, ns) screen:expect { grid = [[ - {3:a^a} | - {2:~ }|*8 + {100:a^a} | + {1:~ }|*8 | ]]} @@ -5731,19 +5802,12 @@ describe('decorations: window scoped', function() screen:expect { grid = [[ a^a | - {2:~ }|*8 + {1:~ }|*8 | ]]} end) it('url', function() - local url = 'https://example.com' - local attrs = screen:get_default_attr_ids() - table.insert(attrs, { - url = url, - }) - screen:set_default_attr_ids(attrs) - set_scoped_extmark(0, 0, { end_col=3, url=url, @@ -5751,12 +5815,12 @@ describe('decorations: window scoped', function() screen:expect(noextmarks) - api.nvim_win_add_ns(0, ns) + api.nvim__win_add_ns(0, ns) screen:expect { grid = [[ - {3:123}4^5 | - {2:~ }|*8 + {101:123}4^5 | + {1:~ }|*8 | ]]} @@ -5772,12 +5836,12 @@ describe('decorations: window scoped', function() end_col = 3, }) - api.nvim_win_add_ns(0, ns) + api.nvim__win_add_ns(0, ns) screen:expect { grid = [[ - {1:123}4^5 | - {2:~ }|*8 + {18:123}4^5 | + {1:~ }|*8 | ]]} @@ -5795,8 +5859,8 @@ describe('decorations: window scoped', function() screen:expect { grid = [[ - {1:123}4^5 | - {2:~ }|*8 + {18:123}4^5 | + {1:~ }|*8 | ]]} @@ -5816,34 +5880,34 @@ describe('decorations: window scoped', function() end_col = 3, }) - eq(true, api.nvim_win_add_ns(0, ns)) - eq({ ns }, api.nvim_win_get_ns(0)) + eq(true, api.nvim__win_add_ns(0, ns)) + eq({ ns }, api.nvim__win_get_ns(0)) screen:expect { grid = [[ - {1:123}4^5 | - {2:~ }|*8 + {18:123}4^5 | + {1:~ }|*8 | ]]} command 'split' command 'only' - eq({}, api.nvim_win_get_ns(0)) + eq({}, api.nvim__win_get_ns(0)) screen:expect(noextmarks) - eq(true, api.nvim_win_add_ns(0, ns)) - eq({ ns }, api.nvim_win_get_ns(0)) + eq(true, api.nvim__win_add_ns(0, ns)) + eq({ ns }, api.nvim__win_get_ns(0)) screen:expect { grid = [[ - {1:123}4^5 | - {2:~ }|*8 + {18:123}4^5 | + {1:~ }|*8 | ]]} - eq(true, api.nvim_win_remove_ns(0, ns)) - eq({}, api.nvim_win_get_ns(0)) + eq(true, api.nvim__win_del_ns(0, ns)) + eq({}, api.nvim__win_get_ns(0)) screen:expect(noextmarks) end) diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index e0dfde35f2..e79621f364 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed = helpers.feed -local clear = helpers.clear -local command = helpers.command -local insert = helpers.insert -local write_file = helpers.write_file -local dedent = helpers.dedent -local exec = helpers.exec -local eq = helpers.eq -local api = helpers.api +local feed = n.feed +local clear = n.clear +local command = n.command +local insert = n.insert +local write_file = t.write_file +local dedent = t.dedent +local exec = n.exec +local eq = t.eq +local api = n.api before_each(clear) @@ -40,17 +41,6 @@ describe('Diff mode screen', function() screen = Screen.new(40, 16) screen:attach() - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray }, - [2] = { background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1 }, - [3] = { reverse = true }, - [4] = { background = Screen.colors.LightBlue }, - [5] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey }, - [6] = { bold = true, foreground = Screen.colors.Blue1 }, - [7] = { bold = true, reverse = true }, - [8] = { bold = true, background = Screen.colors.Red }, - [9] = { background = Screen.colors.LightMagenta }, - }) end) it('Add a line in beginning of file 2', function() @@ -60,31 +50,31 @@ describe('Diff mode screen', function() feed(':set diffopt=filler<cr>') screen:expect([[ - {1: }{2:------------------}│{1: }{4:0 }| - {1: }^1 │{1: }1 | - {1: }2 │{1: }2 | - {1: }3 │{1: }3 | - {1: }4 │{1: }4 | - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1:+ }{5:+-- 4 lines: 7···}│{1:+ }{5:+-- 4 lines: 7··}| - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }{23:------------------}│{7: }{22:0 }| + {7: }^1 │{7: }1 | + {7: }2 │{7: }2 | + {7: }3 │{7: }3 | + {7: }4 │{7: }4 | + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7:+ }{13:+-- 4 lines: 7···}│{7:+ }{13:+-- 4 lines: 7··}| + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler | ]]) feed(':set diffopt+=internal<cr>') screen:expect([[ - {1: }{2:------------------}│{1: }{4:0 }| - {1: }^1 │{1: }1 | - {1: }2 │{1: }2 | - {1: }3 │{1: }3 | - {1: }4 │{1: }4 | - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1:+ }{5:+-- 4 lines: 7···}│{1:+ }{5:+-- 4 lines: 7··}| - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }{23:------------------}│{7: }{22:0 }| + {7: }^1 │{7: }1 | + {7: }2 │{7: }2 | + {7: }3 │{7: }3 | + {7: }4 │{7: }4 | + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7:+ }{13:+-- 4 lines: 7···}│{7:+ }{13:+-- 4 lines: 7··}| + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) end) @@ -96,31 +86,31 @@ describe('Diff mode screen', function() feed(':set diffopt=filler<cr>') screen:expect([[ - {1: }{4:^0 }│{1: }{2:-----------------}| - {1: }1 │{1: }1 | - {1: }2 │{1: }2 | - {1: }3 │{1: }3 | - {1: }4 │{1: }4 | - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1:+ }{5:+-- 4 lines: 7···}│{1:+ }{5:+-- 4 lines: 7··}| - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }{22:^0 }│{7: }{23:-----------------}| + {7: }1 │{7: }1 | + {7: }2 │{7: }2 | + {7: }3 │{7: }3 | + {7: }4 │{7: }4 | + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7:+ }{13:+-- 4 lines: 7···}│{7:+ }{13:+-- 4 lines: 7··}| + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler | ]]) feed(':set diffopt+=internal<cr>') screen:expect([[ - {1: }{4:^0 }│{1: }{2:-----------------}| - {1: }1 │{1: }1 | - {1: }2 │{1: }2 | - {1: }3 │{1: }3 | - {1: }4 │{1: }4 | - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1:+ }{5:+-- 4 lines: 7···}│{1:+ }{5:+-- 4 lines: 7··}| - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }{22:^0 }│{7: }{23:-----------------}| + {7: }1 │{7: }1 | + {7: }2 │{7: }2 | + {7: }3 │{7: }3 | + {7: }4 │{7: }4 | + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7:+ }{13:+-- 4 lines: 7···}│{7:+ }{13:+-- 4 lines: 7··}| + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) end) @@ -132,44 +122,44 @@ describe('Diff mode screen', function() feed(':set diffopt=filler<cr>') screen:expect([[ - {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}| - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1: }7 │{1: }7 | - {1: }8 │{1: }8 | - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }{2:------------------}│{1: }{4:11 }| - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7:+ }{13:^+-- 4 lines: 1···}│{7:+ }{13:+-- 4 lines: 1··}| + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7: }7 │{7: }7 | + {7: }8 │{7: }8 | + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }{23:------------------}│{7: }{22:11 }| + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler | ]]) feed(':set diffopt+=internal<cr>') screen:expect([[ - {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}| - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1: }7 │{1: }7 | - {1: }8 │{1: }8 | - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }{2:------------------}│{1: }{4:11 }| - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7:+ }{13:^+-- 4 lines: 1···}│{7:+ }{13:+-- 4 lines: 1··}| + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7: }7 │{7: }7 | + {7: }8 │{7: }8 | + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }{23:------------------}│{7: }{22:11 }| + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) screen:try_resize(40, 9) screen:expect([[ - {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}| - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1: }7 │{1: }7 | - {1: }8 │{1: }8 | - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7:+ }{13:^+-- 4 lines: 1···}│{7:+ }{13:+-- 4 lines: 1··}| + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7: }7 │{7: }7 | + {7: }8 │{7: }8 | + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| | ]]) end) @@ -181,44 +171,44 @@ describe('Diff mode screen', function() feed(':set diffopt=filler<cr>') screen:expect([[ - {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}| - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1: }7 │{1: }7 | - {1: }8 │{1: }8 | - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }{4:11 }│{1: }{2:-----------------}| - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7:+ }{13:^+-- 4 lines: 1···}│{7:+ }{13:+-- 4 lines: 1··}| + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7: }7 │{7: }7 | + {7: }8 │{7: }8 | + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }{22:11 }│{7: }{23:-----------------}| + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler | ]]) feed(':set diffopt+=internal<cr>') screen:expect([[ - {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}| - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1: }7 │{1: }7 | - {1: }8 │{1: }8 | - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }{4:11 }│{1: }{2:-----------------}| - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7:+ }{13:^+-- 4 lines: 1···}│{7:+ }{13:+-- 4 lines: 1··}| + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7: }7 │{7: }7 | + {7: }8 │{7: }8 | + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }{22:11 }│{7: }{23:-----------------}| + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) screen:try_resize(40, 9) screen:expect([[ - {1:+ }{5:^+-- 4 lines: 1···}│{1:+ }{5:+-- 4 lines: 1··}| - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1: }7 │{1: }7 | - {1: }8 │{1: }8 | - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7:+ }{13:^+-- 4 lines: 1···}│{7:+ }{13:+-- 4 lines: 1··}| + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7: }7 │{7: }7 | + {7: }8 │{7: }8 | + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| | ]]) end) @@ -230,39 +220,39 @@ describe('Diff mode screen', function() feed(':set diffopt=filler<cr>') screen:expect([[ - {1: }^1 │{1: }1 | - {1: }2 │{1: }2 | - {1: }3 │{1: }3 | - {1: }4 │{1: }4 | - {1: }{2:------------------}│{1: }{4:4 }| - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1: }7 │{1: }7 | - {1: }8 │{1: }8 | - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }{4:11 }│{1: }{2:-----------------}| - {6:~ }│{6:~ }|*2 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^1 │{7: }1 | + {7: }2 │{7: }2 | + {7: }3 │{7: }3 | + {7: }4 │{7: }4 | + {7: }{23:------------------}│{7: }{22:4 }| + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7: }7 │{7: }7 | + {7: }8 │{7: }8 | + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }{22:11 }│{7: }{23:-----------------}| + {1:~ }│{1:~ }|*2 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler | ]]) feed(':set diffopt+=internal<cr>') screen:expect([[ - {1: }^1 │{1: }1 | - {1: }2 │{1: }2 | - {1: }3 │{1: }3 | - {1: }4 │{1: }4 | - {1: }{2:------------------}│{1: }{4:4 }| - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1: }7 │{1: }7 | - {1: }8 │{1: }8 | - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }{4:11 }│{1: }{2:-----------------}| - {6:~ }│{6:~ }|*2 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^1 │{7: }1 | + {7: }2 │{7: }2 | + {7: }3 │{7: }3 | + {7: }4 │{7: }4 | + {7: }{23:------------------}│{7: }{22:4 }| + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7: }7 │{7: }7 | + {7: }8 │{7: }8 | + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }{22:11 }│{7: }{23:-----------------}| + {1:~ }│{1:~ }|*2 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) end) @@ -274,39 +264,39 @@ describe('Diff mode screen', function() feed(':set diffopt=filler<cr>') screen:expect([[ - {1: }^1 │{1: }1 | - {1: }2 │{1: }2 | - {1: }3 │{1: }3 | - {1: }4 │{1: }4 | - {1: }{4:4 }│{1: }{2:-----------------}| - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1: }7 │{1: }7 | - {1: }8 │{1: }8 | - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }{2:------------------}│{1: }{4:11 }| - {6:~ }│{6:~ }|*2 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^1 │{7: }1 | + {7: }2 │{7: }2 | + {7: }3 │{7: }3 | + {7: }4 │{7: }4 | + {7: }{22:4 }│{7: }{23:-----------------}| + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7: }7 │{7: }7 | + {7: }8 │{7: }8 | + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }{23:------------------}│{7: }{22:11 }| + {1:~ }│{1:~ }|*2 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler | ]]) feed(':set diffopt+=internal<cr>') screen:expect([[ - {1: }^1 │{1: }1 | - {1: }2 │{1: }2 | - {1: }3 │{1: }3 | - {1: }4 │{1: }4 | - {1: }{4:4 }│{1: }{2:-----------------}| - {1: }5 │{1: }5 | - {1: }6 │{1: }6 | - {1: }7 │{1: }7 | - {1: }8 │{1: }8 | - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }{2:------------------}│{1: }{4:11 }| - {6:~ }│{6:~ }|*2 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^1 │{7: }1 | + {7: }2 │{7: }2 | + {7: }3 │{7: }3 | + {7: }4 │{7: }4 | + {7: }{22:4 }│{7: }{23:-----------------}| + {7: }5 │{7: }5 | + {7: }6 │{7: }6 | + {7: }7 │{7: }7 | + {7: }8 │{7: }8 | + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }{23:------------------}│{7: }{22:11 }| + {1:~ }│{1:~ }|*2 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) end) @@ -372,41 +362,41 @@ int main(int argc, char **argv) reread() feed(':set diffopt=internal,filler<cr>') screen:expect([[ - {1: }^#include <stdio.h>│{1: }#include <stdio.h| - {1: } │{1: } | - {1: }{8:// Frobs foo heart}│{1: }{8:int fib(int n)}{9: }| - {1: }{4:int frobnitz(int f}│{1: }{2:-----------------}| - {1: }{ │{1: }{ | - {1: }{9: i}{8:nt i;}{9: }│{1: }{9: i}{8:f(n > 2)}{9: }| - {1: }{4: for(i = 0; i <}│{1: }{2:-----------------}| - {1: } { │{1: } { | - {1: }{9: }{8:printf("Yo}│{1: }{9: }{8:return fi}| - {1: }{4: printf("%d}│{1: }{2:-----------------}| - {1: } } │{1: } } | - {1: }{2:------------------}│{1: }{4: return 1; }| - {1: }} │{1: }} | - {1: } │{1: } | - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^#include <stdio.h>│{7: }#include <stdio.h| + {7: } │{7: } | + {7: }{27:// Frobs foo heart}│{7: }{27:int fib(int n)}{4: }| + {7: }{22:int frobnitz(int f}│{7: }{23:-----------------}| + {7: }{ │{7: }{ | + {7: }{4: i}{27:nt i;}{4: }│{7: }{4: i}{27:f(n > 2)}{4: }| + {7: }{22: for(i = 0; i <}│{7: }{23:-----------------}| + {7: } { │{7: } { | + {7: }{4: }{27:printf("Yo}│{7: }{4: }{27:return fi}| + {7: }{22: printf("%d}│{7: }{23:-----------------}| + {7: } } │{7: } } | + {7: }{23:------------------}│{7: }{22: return 1; }| + {7: }} │{7: }} | + {7: } │{7: } | + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=internal,filler | ]]) feed('G') screen:expect([[ - {1: }{2:------------------}│{1: }{4:int frobnitz(int }| - {1: }{ │{1: }{ | - {1: }{9: i}{8:f(n > 1)}{9: }│{1: }{9: i}{8:nt i;}{9: }| - {1: }{2:------------------}│{1: }{4: for(i = 0; i }| - {1: } { │{1: } { | - {1: }{9: }{8:return fac}│{1: }{9: }{8:printf("%}| - {1: } } │{1: } } | - {1: }{4: return 1; }│{1: }{2:-----------------}| - {1: }} │{1: }} | - {1: } │{1: } | - {1: }int main(int argc,│{1: }int main(int argc| - {1: }{ │{1: }{ | - {1: }{9: frobnitz(f}{8:act}{9:(}│{1: }{9: frobnitz(f}{8:ib}{9:(}| - {1: }^} │{1: }} | - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }{23:------------------}│{7: }{22:int frobnitz(int }| + {7: }{ │{7: }{ | + {7: }{4: i}{27:f(n > 1)}{4: }│{7: }{4: i}{27:nt i;}{4: }| + {7: }{23:------------------}│{7: }{22: for(i = 0; i }| + {7: } { │{7: } { | + {7: }{4: }{27:return fac}│{7: }{4: }{27:printf("%}| + {7: } } │{7: } } | + {7: }{22: return 1; }│{7: }{23:-----------------}| + {7: }} │{7: }} | + {7: } │{7: } | + {7: }int main(int argc,│{7: }int main(int argc| + {7: }{ │{7: }{ | + {7: }{4: frobnitz(f}{27:act}{4:(}│{7: }{4: frobnitz(f}{27:ib}{4:(}| + {7: }^} │{7: }} | + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=internal,filler | ]]) end) @@ -415,41 +405,41 @@ int main(int argc, char **argv) reread() feed(':set diffopt=internal,filler,algorithm:patience<cr>') screen:expect([[ - {1: }^#include <stdio.h>│{1: }#include <stdio.h| - {1: } │{1: } | - {1: }{2:------------------}│{1: }{4:int fib(int n) }| - {1: }{2:------------------}│{1: }{4:{ }| - {1: }{2:------------------}│{1: }{4: if(n > 2) }| - {1: }{2:------------------}│{1: }{4: { }| - {1: }{2:------------------}│{1: }{4: return fi}| - {1: }{2:------------------}│{1: }{4: } }| - {1: }{2:------------------}│{1: }{4: return 1; }| - {1: }{2:------------------}│{1: }{4:} }| - {1: }{2:------------------}│{1: }{4: }| - {1: }// Frobs foo heart│{1: }// Frobs foo hear| - {1: }int frobnitz(int f│{1: }int frobnitz(int | - {1: }{ │{1: }{ | - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^#include <stdio.h>│{7: }#include <stdio.h| + {7: } │{7: } | + {7: }{23:------------------}│{7: }{22:int fib(int n) }| + {7: }{23:------------------}│{7: }{22:{ }| + {7: }{23:------------------}│{7: }{22: if(n > 2) }| + {7: }{23:------------------}│{7: }{22: { }| + {7: }{23:------------------}│{7: }{22: return fi}| + {7: }{23:------------------}│{7: }{22: } }| + {7: }{23:------------------}│{7: }{22: return 1; }| + {7: }{23:------------------}│{7: }{22:} }| + {7: }{23:------------------}│{7: }{22: }| + {7: }// Frobs foo heart│{7: }// Frobs foo hear| + {7: }int frobnitz(int f│{7: }int frobnitz(int | + {7: }{ │{7: }{ | + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| | ]]) feed('G') screen:expect([[ - {1: } │{1: } | - {1: }{4:int fact(int n) }│{1: }{2:-----------------}| - {1: }{4:{ }│{1: }{2:-----------------}| - {1: }{4: if(n > 1) }│{1: }{2:-----------------}| - {1: }{4: { }│{1: }{2:-----------------}| - {1: }{4: return fac}│{1: }{2:-----------------}| - {1: }{4: } }│{1: }{2:-----------------}| - {1: }{4: return 1; }│{1: }{2:-----------------}| - {1: }{4:} }│{1: }{2:-----------------}| - {1: }{4: }│{1: }{2:-----------------}| - {1: }int main(int argc,│{1: }int main(int argc| - {1: }{ │{1: }{ | - {1: }{9: frobnitz(f}{8:act}{9:(}│{1: }{9: frobnitz(f}{8:ib}{9:(}| - {1: }^} │{1: }} | - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: } │{7: } | + {7: }{22:int fact(int n) }│{7: }{23:-----------------}| + {7: }{22:{ }│{7: }{23:-----------------}| + {7: }{22: if(n > 1) }│{7: }{23:-----------------}| + {7: }{22: { }│{7: }{23:-----------------}| + {7: }{22: return fac}│{7: }{23:-----------------}| + {7: }{22: } }│{7: }{23:-----------------}| + {7: }{22: return 1; }│{7: }{23:-----------------}| + {7: }{22:} }│{7: }{23:-----------------}| + {7: }{22: }│{7: }{23:-----------------}| + {7: }int main(int argc,│{7: }int main(int argc| + {7: }{ │{7: }{ | + {7: }{4: frobnitz(f}{27:act}{4:(}│{7: }{4: frobnitz(f}{27:ib}{4:(}| + {7: }^} │{7: }} | + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| | ]]) end) @@ -458,41 +448,41 @@ int main(int argc, char **argv) reread() feed(':set diffopt=internal,filler,algorithm:histogram<cr>') screen:expect([[ - {1: }^#include <stdio.h>│{1: }#include <stdio.h| - {1: } │{1: } | - {1: }{2:------------------}│{1: }{4:int fib(int n) }| - {1: }{2:------------------}│{1: }{4:{ }| - {1: }{2:------------------}│{1: }{4: if(n > 2) }| - {1: }{2:------------------}│{1: }{4: { }| - {1: }{2:------------------}│{1: }{4: return fi}| - {1: }{2:------------------}│{1: }{4: } }| - {1: }{2:------------------}│{1: }{4: return 1; }| - {1: }{2:------------------}│{1: }{4:} }| - {1: }{2:------------------}│{1: }{4: }| - {1: }// Frobs foo heart│{1: }// Frobs foo hear| - {1: }int frobnitz(int f│{1: }int frobnitz(int | - {1: }{ │{1: }{ | - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^#include <stdio.h>│{7: }#include <stdio.h| + {7: } │{7: } | + {7: }{23:------------------}│{7: }{22:int fib(int n) }| + {7: }{23:------------------}│{7: }{22:{ }| + {7: }{23:------------------}│{7: }{22: if(n > 2) }| + {7: }{23:------------------}│{7: }{22: { }| + {7: }{23:------------------}│{7: }{22: return fi}| + {7: }{23:------------------}│{7: }{22: } }| + {7: }{23:------------------}│{7: }{22: return 1; }| + {7: }{23:------------------}│{7: }{22:} }| + {7: }{23:------------------}│{7: }{22: }| + {7: }// Frobs foo heart│{7: }// Frobs foo hear| + {7: }int frobnitz(int f│{7: }int frobnitz(int | + {7: }{ │{7: }{ | + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| | ]]) feed('G') screen:expect([[ - {1: } │{1: } | - {1: }{4:int fact(int n) }│{1: }{2:-----------------}| - {1: }{4:{ }│{1: }{2:-----------------}| - {1: }{4: if(n > 1) }│{1: }{2:-----------------}| - {1: }{4: { }│{1: }{2:-----------------}| - {1: }{4: return fac}│{1: }{2:-----------------}| - {1: }{4: } }│{1: }{2:-----------------}| - {1: }{4: return 1; }│{1: }{2:-----------------}| - {1: }{4:} }│{1: }{2:-----------------}| - {1: }{4: }│{1: }{2:-----------------}| - {1: }int main(int argc,│{1: }int main(int argc| - {1: }{ │{1: }{ | - {1: }{9: frobnitz(f}{8:act}{9:(}│{1: }{9: frobnitz(f}{8:ib}{9:(}| - {1: }^} │{1: }} | - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: } │{7: } | + {7: }{22:int fact(int n) }│{7: }{23:-----------------}| + {7: }{22:{ }│{7: }{23:-----------------}| + {7: }{22: if(n > 1) }│{7: }{23:-----------------}| + {7: }{22: { }│{7: }{23:-----------------}| + {7: }{22: return fac}│{7: }{23:-----------------}| + {7: }{22: } }│{7: }{23:-----------------}| + {7: }{22: return 1; }│{7: }{23:-----------------}| + {7: }{22:} }│{7: }{23:-----------------}| + {7: }{22: }│{7: }{23:-----------------}| + {7: }int main(int argc,│{7: }int main(int argc| + {7: }{ │{7: }{ | + {7: }{4: frobnitz(f}{27:act}{4:(}│{7: }{4: frobnitz(f}{27:ib}{4:(}| + {7: }^} │{7: }} | + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| | ]]) end) @@ -525,17 +515,17 @@ int main(int argc, char **argv) reread() feed(':set diffopt=internal,filler<cr>') screen:expect([[ - {1: }^def finalize(value│{1: }def finalize(valu| - {1: } │{1: } | - {1: } values.each do |│{1: } values.each do | - {1: }{2:------------------}│{1: }{4: v.prepare }| - {1: }{2:------------------}│{1: }{4: end }| - {1: }{2:------------------}│{1: }{4: }| - {1: }{2:------------------}│{1: }{4: values.each do }| - {1: } v.finalize │{1: } v.finalize | - {1: } end │{1: } end | - {6:~ }│{6:~ }|*5 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^def finalize(value│{7: }def finalize(valu| + {7: } │{7: } | + {7: } values.each do |│{7: } values.each do | + {7: }{23:------------------}│{7: }{22: v.prepare }| + {7: }{23:------------------}│{7: }{22: end }| + {7: }{23:------------------}│{7: }{22: }| + {7: }{23:------------------}│{7: }{22: values.each do }| + {7: } v.finalize │{7: } v.finalize | + {7: } end │{7: } end | + {1:~ }│{1:~ }|*5 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=internal,filler | ]]) end) @@ -544,17 +534,17 @@ int main(int argc, char **argv) reread() feed(':set diffopt=internal,filler,indent-heuristic<cr>') screen:expect([[ - {1: }^def finalize(value│{1: }def finalize(valu| - {1: } │{1: } | - {1: }{2:------------------}│{1: }{4: values.each do }| - {1: }{2:------------------}│{1: }{4: v.prepare }| - {1: }{2:------------------}│{1: }{4: end }| - {1: }{2:------------------}│{1: }{4: }| - {1: } values.each do |│{1: } values.each do | - {1: } v.finalize │{1: } v.finalize | - {1: } end │{1: } end | - {6:~ }│{6:~ }|*5 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^def finalize(value│{7: }def finalize(valu| + {7: } │{7: } | + {7: }{23:------------------}│{7: }{22: values.each do }| + {7: }{23:------------------}│{7: }{22: v.prepare }| + {7: }{23:------------------}│{7: }{22: end }| + {7: }{23:------------------}│{7: }{22: }| + {7: } values.each do |│{7: } values.each do | + {7: } v.finalize │{7: } v.finalize | + {7: } end │{7: } end | + {1:~ }│{1:~ }|*5 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| | ]]) end) @@ -564,17 +554,17 @@ int main(int argc, char **argv) feed(':set diffopt=internal,filler,indent-heuristic,algorithm:patience<cr>') feed(':<cr>') screen:expect([[ - {1: }^def finalize(value│{1: }def finalize(valu| - {1: } │{1: } | - {1: }{2:------------------}│{1: }{4: values.each do }| - {1: }{2:------------------}│{1: }{4: v.prepare }| - {1: }{2:------------------}│{1: }{4: end }| - {1: }{2:------------------}│{1: }{4: }| - {1: } values.each do |│{1: } values.each do | - {1: } v.finalize │{1: } v.finalize | - {1: } end │{1: } end | - {6:~ }│{6:~ }|*5 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^def finalize(value│{7: }def finalize(valu| + {7: } │{7: } | + {7: }{23:------------------}│{7: }{22: values.each do }| + {7: }{23:------------------}│{7: }{22: v.prepare }| + {7: }{23:------------------}│{7: }{22: end }| + {7: }{23:------------------}│{7: }{22: }| + {7: } values.each do |│{7: } values.each do | + {7: } v.finalize │{7: } v.finalize | + {7: } end │{7: } end | + {1:~ }│{1:~ }|*5 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| : | ]]) end) @@ -587,17 +577,17 @@ int main(int argc, char **argv) feed(':set diffopt=filler<cr>') screen:expect([[ - {1:+ }{5:^+-- 10 lines: 1···}│{1:+ }{5:+-- 10 lines: 1··}| - {6:~ }│{6:~ }|*13 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7:+ }{13:^+-- 10 lines: 1···}│{7:+ }{13:+-- 10 lines: 1··}| + {1:~ }│{1:~ }|*13 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler | ]]) feed(':set diffopt+=internal<cr>') screen:expect([[ - {1:+ }{5:^+-- 10 lines: 1···}│{1:+ }{5:+-- 10 lines: 1··}| - {6:~ }│{6:~ }|*13 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7:+ }{13:^+-- 10 lines: 1···}│{7:+ }{13:+-- 10 lines: 1··}| + {1:~ }│{1:~ }|*13 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) end) @@ -609,17 +599,17 @@ int main(int argc, char **argv) feed(':set diffopt=filler<cr>') screen:expect([[ - {1:- }^ │{1:- } | - {6:~ }│{6:~ }|*13 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7:- }^ │{7:- } | + {1:~ }│{1:~ }|*13 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler | ]]) feed(':set diffopt+=internal<cr>') screen:expect([[ - {1:- }^ │{1:- } | - {6:~ }│{6:~ }|*13 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7:- }^ │{7:- } | + {1:~ }│{1:~ }|*13 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) end) @@ -631,21 +621,21 @@ int main(int argc, char **argv) feed(':set diffopt=filler,icase<cr>') screen:expect([[ - {1: }^a │{1: }A | - {1: }b │{1: }b | - {1: }{9:cd }│{1: }{9:cD}{8:e}{9: }| - {6:~ }│{6:~ }|*11 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^a │{7: }A | + {7: }b │{7: }b | + {7: }{4:cd }│{7: }{4:cD}{27:e}{4: }| + {1:~ }│{1:~ }|*11 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler,icase | ]]) feed(':set diffopt+=internal<cr>') screen:expect([[ - {1: }^a │{1: }A | - {1: }b │{1: }b | - {1: }{9:cd }│{1: }{9:cD}{8:e}{9: }| - {6:~ }│{6:~ }|*11 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^a │{7: }A | + {7: }b │{7: }b | + {7: }{4:cd }│{7: }{4:cD}{27:e}{4: }| + {1:~ }│{1:~ }|*11 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) end) @@ -664,16 +654,16 @@ int main(int argc, char **argv) reread() feed(':set diffopt=filler,iwhite<cr>') screen:expect([[ - {1: }^int main() │{1: }int main() | - {1: }{ │{1: }{ | - {1: }{2:------------------}│{1: }{4: if (0) }| - {1: }{2:------------------}│{1: }{4: { }| - {1: } printf("Hello, │{1: } printf("Hel| - {1: } return 0; │{1: } return 0; | - {1: }{2:------------------}│{1: }{4: } }| - {1: }} │{1: }} | - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^int main() │{7: }int main() | + {7: }{ │{7: }{ | + {7: }{23:------------------}│{7: }{22: if (0) }| + {7: }{23:------------------}│{7: }{22: { }| + {7: } printf("Hello, │{7: } printf("Hel| + {7: } return 0; │{7: } return 0; | + {7: }{23:------------------}│{7: }{22: } }| + {7: }} │{7: }} | + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler,iwhite | ]]) end) @@ -682,16 +672,16 @@ int main(int argc, char **argv) reread() feed(':set diffopt=filler,iwhite,internal<cr>') screen:expect([[ - {1: }^int main() │{1: }int main() | - {1: }{ │{1: }{ | - {1: }{2:------------------}│{1: }{4: if (0) }| - {1: }{2:------------------}│{1: }{4: { }| - {1: } printf("Hello, │{1: } printf("Hel| - {1: } return 0; │{1: } return 0; | - {1: }{2:------------------}│{1: }{4: } }| - {1: }} │{1: }} | - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^int main() │{7: }int main() | + {7: }{ │{7: }{ | + {7: }{23:------------------}│{7: }{22: if (0) }| + {7: }{23:------------------}│{7: }{22: { }| + {7: } printf("Hello, │{7: } printf("Hel| + {7: } return 0; │{7: } return 0; | + {7: }{23:------------------}│{7: }{22: } }| + {7: }} │{7: }} | + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=filler,iwhite,internal | ]]) end) @@ -708,14 +698,14 @@ int main(int argc, char **argv) reread() feed(':set diffopt=internal,filler,iblank<cr>') screen:expect([[ - {1: }^a │{1: }a | - {1: }{4: }│{1: }{2:-----------------}|*2 - {1: }cd │{1: }cd | - {1: }ef │{1: } | - {1: }{8:xxx}{9: }│{1: }ef | - {6:~ }│{1: }{8:yyy}{9: }| - {6:~ }│{6:~ }|*7 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^a │{7: }a | + {7: }{22: }│{7: }{23:-----------------}|*2 + {7: }cd │{7: }cd | + {7: }ef │{7: } | + {7: }{27:xxx}{4: }│{7: }ef | + {1:~ }│{7: }{27:yyy}{4: }| + {1:~ }│{1:~ }|*7 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=internal,filler,iblank | ]]) end) @@ -725,14 +715,14 @@ int main(int argc, char **argv) feed(':set diffopt=internal,filler,iblank,iwhite<cr>') feed(':<cr>') screen:expect([[ - {1: }^a │{1: }a | - {1: } │{1: }cd | - {1: } │{1: } | - {1: }cd │{1: }ef | - {1: }ef │{1: }{8:yyy}{9: }| - {1: }{8:xxx}{9: }│{6:~ }| - {6:~ }│{6:~ }|*8 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^a │{7: }a | + {7: } │{7: }cd | + {7: } │{7: } | + {7: }cd │{7: }ef | + {7: }ef │{7: }{27:yyy}{4: }| + {7: }{27:xxx}{4: }│{1:~ }| + {1:~ }│{1:~ }|*8 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| : | ]]) end) @@ -742,14 +732,14 @@ int main(int argc, char **argv) feed(':set diffopt=internal,filler,iblank,iwhiteall<cr>') feed(':<cr>') screen:expect([[ - {1: }^a │{1: }a | - {1: } │{1: }cd | - {1: } │{1: } | - {1: }cd │{1: }ef | - {1: }ef │{1: }{8:yyy}{9: }| - {1: }{8:xxx}{9: }│{6:~ }| - {6:~ }│{6:~ }|*8 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^a │{7: }a | + {7: } │{7: }cd | + {7: } │{7: } | + {7: }cd │{7: }ef | + {7: }ef │{7: }{27:yyy}{4: }| + {7: }{27:xxx}{4: }│{1:~ }| + {1:~ }│{1:~ }|*8 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| : | ]]) end) @@ -759,14 +749,14 @@ int main(int argc, char **argv) feed(':set diffopt=internal,filler,iblank,iwhiteeol<cr>') feed(':<cr>') screen:expect([[ - {1: }^a │{1: }a | - {1: } │{1: }cd | - {1: } │{1: } | - {1: }cd │{1: }ef | - {1: }ef │{1: }{8:yyy}{9: }| - {1: }{8:xxx}{9: }│{6:~ }| - {6:~ }│{6:~ }|*8 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^a │{7: }a | + {7: } │{7: }cd | + {7: } │{7: } | + {7: }cd │{7: }ef | + {7: }ef │{7: }{27:yyy}{4: }| + {7: }{27:xxx}{4: }│{1:~ }| + {1:~ }│{1:~ }|*8 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| : | ]]) end) @@ -784,16 +774,16 @@ int main(int argc, char **argv) feed(':set diffopt=internal,filler,iwhiteeol<cr>') feed(':<cr>') screen:expect([[ - {1: }^a │{1: }a | - {1: }x │{1: }x | - {1: }{9:cd }│{1: }{9:c}{8: }{9:d }| - {1: }{9:ef }│{1: }{8: }{9:ef }| - {1: }{9:xx }{8: }{9:xx }│{1: }{9:xx xx }| - {1: }foo │{1: }foo | - {1: }{2:------------------}│{1: }{4: }| - {1: }bar │{1: }bar | - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^a │{7: }a | + {7: }x │{7: }x | + {7: }{4:cd }│{7: }{4:c}{27: }{4:d }| + {7: }{4:ef }│{7: }{27: }{4:ef }| + {7: }{4:xx }{27: }{4:xx }│{7: }{4:xx xx }| + {7: }foo │{7: }foo | + {7: }{23:------------------}│{7: }{22: }| + {7: }bar │{7: }bar | + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| : | ]]) end) @@ -803,16 +793,16 @@ int main(int argc, char **argv) feed(':set diffopt=internal,filler,iwhiteall<cr>') feed(':<cr>') screen:expect([[ - {1: }^a │{1: }a | - {1: }x │{1: }x | - {1: }cd │{1: }c d | - {1: }ef │{1: } ef | - {1: }xx xx │{1: }xx xx | - {1: }foo │{1: }foo | - {1: }{2:------------------}│{1: }{4: }| - {1: }bar │{1: }bar | - {6:~ }│{6:~ }|*6 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^a │{7: }a | + {7: }x │{7: }x | + {7: }cd │{7: }c d | + {7: }ef │{7: } ef | + {7: }xx xx │{7: }xx xx | + {7: }foo │{7: }foo | + {7: }{23:------------------}│{7: }{22: }| + {7: }bar │{7: }bar | + {1:~ }│{1:~ }|*6 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| : | ]]) end) @@ -883,32 +873,32 @@ int main(int argc, char **argv) feed('<C-W><C-W>jjjj') screen:expect([[ - {1: }line 1 │{1: }line 1 | - {1: }line 2 │{1: }line 2 | - {1: }line 3 │{1: }line 3 | - {1: }line 4 │{1: }line 4 | - {1: } │{1: }^ | - {1: }{2:-----------------}│{1: }{4:Lorem }| - {1: }{2:-----------------}│{1: }{4:ipsum }| - {1: }{2:-----------------}│{1: }{4:dolor }| - {1: }{2:-----------------}│{1: }{4:sit }| - {1: }{2:-----------------}│{1: }{4:amet, }| - {3:<nal-diff-screen-1 }{7:<al-diff-screen-1.2 }| + {7: }line 1 │{7: }line 1 | + {7: }line 2 │{7: }line 2 | + {7: }line 3 │{7: }line 3 | + {7: }line 4 │{7: }line 4 | + {7: } │{7: }^ | + {7: }{23:-----------------}│{7: }{22:Lorem }| + {7: }{23:-----------------}│{7: }{22:ipsum }| + {7: }{23:-----------------}│{7: }{22:dolor }| + {7: }{23:-----------------}│{7: }{22:sit }| + {7: }{23:-----------------}│{7: }{22:amet, }| + {2:<nal-diff-screen-1 }{3:<al-diff-screen-1.2 }| :e | ]]) feed('j') screen:expect([[ - {1: }line 1 │{1: }line 1 | - {1: }line 2 │{1: }line 2 | - {1: }line 3 │{1: }line 3 | - {1: }line 4 │{1: }line 4 | - {1: } │{1: } | - {1: }{2:-----------------}│{1: }{4:^Lorem }| - {1: }{2:-----------------}│{1: }{4:ipsum }| - {1: }{2:-----------------}│{1: }{4:dolor }| - {1: }{2:-----------------}│{1: }{4:sit }| - {1: }{2:-----------------}│{1: }{4:amet, }| - {3:<nal-diff-screen-1 }{7:<al-diff-screen-1.2 }| + {7: }line 1 │{7: }line 1 | + {7: }line 2 │{7: }line 2 | + {7: }line 3 │{7: }line 3 | + {7: }line 4 │{7: }line 4 | + {7: } │{7: } | + {7: }{23:-----------------}│{7: }{22:^Lorem }| + {7: }{23:-----------------}│{7: }{22:ipsum }| + {7: }{23:-----------------}│{7: }{22:dolor }| + {7: }{23:-----------------}│{7: }{22:sit }| + {7: }{23:-----------------}│{7: }{22:amet, }| + {2:<nal-diff-screen-1 }{3:<al-diff-screen-1.2 }| :e | ]]) end) @@ -942,38 +932,38 @@ int main(int argc, char **argv) reread() feed(':set diffopt=internal,filler<cr>') screen:expect([[ - {1: }^if __name__ == "__│{1: }if __name__ == "_| - {1: } import sys │{1: } import sys | - {1: }{9: }{8:app = QWidgets}│{1: }{9: }{8:comment these}| - {1: }{9: }{8:MainWindow = Q}│{1: }{9: }{8:#app = QWidge}| - {1: }{9: }{8:ui = UI_}{9:MainWi}│{1: }{9: }{8:#MainWindow =}| - {1: }{2:------------------}│{1: }{4: add a complet}| - {1: }{2:------------------}│{1: }{4: #ui = UI_Main}| - {1: }{2:------------------}│{1: }{4: add another n}| - {1: } ui.setupUI(Mai│{1: } ui.setupUI(Ma| - {1: } MainWindow.sho│{1: } MainWindow.sh| - {1: } sys.exit(app.e│{1: } sys.exit(app.| - {6:~ }│{6:~ }|*3 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^if __name__ == "__│{7: }if __name__ == "_| + {7: } import sys │{7: } import sys | + {7: }{4: }{27:app = QWidgets}│{7: }{4: }{27:comment these}| + {7: }{4: }{27:MainWindow = Q}│{7: }{4: }{27:#app = QWidge}| + {7: }{4: }{27:ui = UI_}{4:MainWi}│{7: }{4: }{27:#MainWindow =}| + {7: }{23:------------------}│{7: }{22: add a complet}| + {7: }{23:------------------}│{7: }{22: #ui = UI_Main}| + {7: }{23:------------------}│{7: }{22: add another n}| + {7: } ui.setupUI(Mai│{7: } ui.setupUI(Ma| + {7: } MainWindow.sho│{7: } MainWindow.sh| + {7: } sys.exit(app.e│{7: } sys.exit(app.| + {1:~ }│{1:~ }|*3 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt=internal,filler | ]]) feed('G') feed(':set diffopt+=linematch:20<cr>') screen:expect([[ - {1: }if __name__ == "__│{1: }if __name__ == "_| - {1: } import sys │{1: } import sys | - {1: }{2:------------------}│{1: }{4: comment these}| - {1: }{9: app = QWidgets}│{1: }{9: }{8:#}{9:app = QWidge}| - {1: }{9: MainWindow = Q}│{1: }{9: }{8:#}{9:MainWindow =}| - {1: }{2:------------------}│{1: }{4: add a complet}| - {1: }{9: ui = UI_MainWi}│{1: }{9: }{8:#}{9:ui = UI_Main}| - {1: }{2:------------------}│{1: }{4: add another n}| - {1: } ui.setupUI(Mai│{1: } ui.setupUI(Ma| - {1: } MainWindow.sho│{1: } MainWindow.sh| - {1: } ^sys.exit(app.e│{1: } sys.exit(app.| - {6:~ }│{6:~ }|*3 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }if __name__ == "__│{7: }if __name__ == "_| + {7: } import sys │{7: } import sys | + {7: }{23:------------------}│{7: }{22: comment these}| + {7: }{4: app = QWidgets}│{7: }{4: }{27:#}{4:app = QWidge}| + {7: }{4: MainWindow = Q}│{7: }{4: }{27:#}{4:MainWindow =}| + {7: }{23:------------------}│{7: }{22: add a complet}| + {7: }{4: ui = UI_MainWi}│{7: }{4: }{27:#}{4:ui = UI_Main}| + {7: }{23:------------------}│{7: }{22: add another n}| + {7: } ui.setupUI(Mai│{7: } ui.setupUI(Ma| + {7: } MainWindow.sho│{7: } MainWindow.sh| + {7: } ^sys.exit(app.e│{7: } sys.exit(app.| + {1:~ }│{1:~ }|*3 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=linematch:20 | ]]) end) @@ -993,20 +983,20 @@ ccca]] reread() feed(':set diffopt=internal,filler,linematch:20<cr>') screen:expect([[ - {1: }^DDD │{1: }DDD | - {1: }{2:------------------}│{1: }{4:AAA }| - {1: }{8:_a}{9:a }│{1: }{8:ccc}{9:a }| - {6:~ }│{6:~ }|*11 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^DDD │{7: }DDD | + {7: }{23:------------------}│{7: }{22:AAA }| + {7: }{27:_a}{4:a }│{7: }{27:ccc}{4:a }| + {1:~ }│{1:~ }|*11 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| | ]]) feed(':set diffopt+=icase<cr>') screen:expect([[ - {1: }^DDD │{1: }DDD | - {1: }{8:_}{9:aa }│{1: }{8:A}{9:AA }| - {1: }{2:------------------}│{1: }{4:ccca }| - {6:~ }│{6:~ }|*11 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^DDD │{7: }DDD | + {7: }{27:_}{4:aa }│{7: }{27:A}{4:AA }| + {7: }{23:------------------}│{7: }{22:ccca }| + {1:~ }│{1:~ }|*11 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=icase | ]]) end) @@ -1027,22 +1017,22 @@ AAAB]] feed(':set diffopt=internal,filler,linematch:20<cr>') screen:expect { grid = [[ - {1: }^BB │{1: }BB | - {1: }{9: AA}{8:A}{9: }│{1: }{9: AA}{8:B}{9: }| - {1: }{2:------------------}│{1: }{4:AAAB }| - {6:~ }│{6:~ }|*11 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^BB │{7: }BB | + {7: }{4: AA}{27:A}{4: }│{7: }{4: AA}{27:B}{4: }| + {7: }{23:------------------}│{7: }{22:AAAB }| + {1:~ }│{1:~ }|*11 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| | ]], } feed(':set diffopt+=iwhiteall<cr>') screen:expect { grid = [[ - {1: }^BB │{1: }BB | - {1: }{2:------------------}│{1: }{4: AAB }| - {1: }{9: AAA }│{1: }{9:AAA}{8:B}{9: }| - {6:~ }│{6:~ }|*11 - {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + {7: }^BB │{7: }BB | + {7: }{23:------------------}│{7: }{22: AAB }| + {7: }{4: AAA }│{7: }{4:AAA}{27:B}{4: }| + {1:~ }│{1:~ }|*11 + {3:<onal-diff-screen-1 }{2:<l-diff-screen-1.2 }| :set diffopt+=iwhiteall | ]], } @@ -1057,16 +1047,16 @@ AAAB]] command('botright new') screen:expect { grid = [[ - {1: }aaa │{1: }aaa | - {1: }bbb │{1: }bbb | - {1: }ccc │{1: }ccc | - {1: } │{1: } | - {1: }{8:xx}{9: }│{1: }{8:yy}{9: }| - {6:~ }│{6:~ }| - {3:<onal-diff-screen-1 <l-diff-screen-1.2 }| + {7: }aaa │{7: }aaa | + {7: }bbb │{7: }bbb | + {7: }ccc │{7: }ccc | + {7: } │{7: } | + {7: }{27:xx}{4: }│{7: }{27:yy}{4: }| + {1:~ }│{1:~ }| + {2:<onal-diff-screen-1 <l-diff-screen-1.2 }| ^ | - {6:~ }|*6 - {7:[No Name] }| + {1:~ }|*6 + {3:[No Name] }| :e | ]], } @@ -1074,16 +1064,16 @@ AAAB]] api.nvim_buf_set_lines(buf, 1, 2, true, { 'BBB' }) screen:expect { grid = [[ - {1: }aaa │{1: }aaa | - {1: }{8:BBB}{9: }│{1: }{8:bbb}{9: }| - {1: }ccc │{1: }ccc | - {1: } │{1: } | - {1: }{8:xx}{9: }│{1: }{8:yy}{9: }| - {6:~ }│{6:~ }| - {3:<-diff-screen-1 [+] <l-diff-screen-1.2 }| + {7: }aaa │{7: }aaa | + {7: }{27:BBB}{4: }│{7: }{27:bbb}{4: }| + {7: }ccc │{7: }ccc | + {7: } │{7: } | + {7: }{27:xx}{4: }│{7: }{27:yy}{4: }| + {1:~ }│{1:~ }| + {2:<-diff-screen-1 [+] <l-diff-screen-1.2 }| ^ | - {6:~ }|*6 - {7:[No Name] }| + {1:~ }|*6 + {3:[No Name] }| :e | ]], } @@ -1097,20 +1087,20 @@ AAAB]] command('botright split | diffoff') screen:expect { grid = [[ - {1: }aaa │{1: }aaa | - {1: }bbb │{1: }bbb | - {1: }ccc │{1: }ccc | - {1: } │{1: } | - {1: }{8:xx}{9: }│{1: }{8:yy}{9: }| - {6:~ }│{6:~ }| - {3:<onal-diff-screen-1 <l-diff-screen-1.2 }| + {7: }aaa │{7: }aaa | + {7: }bbb │{7: }bbb | + {7: }ccc │{7: }ccc | + {7: } │{7: } | + {7: }{27:xx}{4: }│{7: }{27:yy}{4: }| + {1:~ }│{1:~ }| + {2:<onal-diff-screen-1 <l-diff-screen-1.2 }| ^aaa | bbb | ccc | | xx | - {6:~ }|*2 - {7:Xtest-functional-diff-screen-1 }| + {1:~ }|*2 + {3:Xtest-functional-diff-screen-1 }| :e | ]], } @@ -1118,20 +1108,20 @@ AAAB]] api.nvim_buf_set_lines(buf, 1, 2, true, { 'BBB' }) screen:expect { grid = [[ - {1: }aaa │{1: }aaa | - {1: }{8:BBB}{9: }│{1: }{8:bbb}{9: }| - {1: }ccc │{1: }ccc | - {1: } │{1: } | - {1: }{8:xx}{9: }│{1: }{8:yy}{9: }| - {6:~ }│{6:~ }| - {3:<-diff-screen-1 [+] <l-diff-screen-1.2 }| + {7: }aaa │{7: }aaa | + {7: }{27:BBB}{4: }│{7: }{27:bbb}{4: }| + {7: }ccc │{7: }ccc | + {7: } │{7: } | + {7: }{27:xx}{4: }│{7: }{27:yy}{4: }| + {1:~ }│{1:~ }| + {2:<-diff-screen-1 [+] <l-diff-screen-1.2 }| ^aaa | BBB | ccc | | xx | - {6:~ }|*2 - {7:Xtest-functional-diff-screen-1 [+] }| + {1:~ }|*2 + {3:Xtest-functional-diff-screen-1 [+] }| :e | ]], } @@ -1220,19 +1210,6 @@ end) it('diff updates line numbers below filler lines', function() local screen = Screen.new(40, 14) screen:attach() - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray }, - [2] = { background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1 }, - [3] = { reverse = true }, - [4] = { background = Screen.colors.LightBlue }, - [5] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey }, - [6] = { bold = true, foreground = Screen.colors.Blue1 }, - [7] = { bold = true, reverse = true }, - [8] = { bold = true, background = Screen.colors.Red }, - [9] = { background = Screen.colors.LightMagenta }, - [10] = { bold = true, foreground = Screen.colors.Brown }, - [11] = { foreground = Screen.colors.Brown }, - }) exec([[ call setline(1, ['a', 'a', 'a', 'y', 'b', 'b', 'b', 'b', 'b']) vnew @@ -1241,50 +1218,50 @@ it('diff updates line numbers below filler lines', function() setlocal number rnu cursorline cursorlineopt=number foldcolumn=0 ]]) screen:expect([[ - {1: }a │{10:1 }^a | - {1: }a │{11: 1 }a | - {1: }a │{11: 2 }a | - {1: }{8:x}{9: }│{11: 3 }{8:y}{9: }| - {1: }{4:x }│{11: }{2:----------------}|*2 - {1: }b │{11: 4 }b | - {1: }b │{11: 5 }b | - {1: }b │{11: 6 }b | - {1: }b │{11: 7 }b | - {1: }b │{11: 8 }b | - {6:~ }│{6:~ }| - {3:[No Name] [+] }{7:[No Name] [+] }| + {7: }a │{15:1 }^a | + {7: }a │{8: 1 }a | + {7: }a │{8: 2 }a | + {7: }{27:x}{4: }│{8: 3 }{27:y}{4: }| + {7: }{22:x }│{8: }{23:----------------}|*2 + {7: }b │{8: 4 }b | + {7: }b │{8: 5 }b | + {7: }b │{8: 6 }b | + {7: }b │{8: 7 }b | + {7: }b │{8: 8 }b | + {1:~ }│{1:~ }| + {2:[No Name] [+] }{3:[No Name] [+] }| | ]]) feed('j') screen:expect([[ - {1: }a │{11: 1 }a | - {1: }a │{10:2 }^a | - {1: }a │{11: 1 }a | - {1: }{8:x}{9: }│{11: 2 }{8:y}{9: }| - {1: }{4:x }│{11: }{2:----------------}|*2 - {1: }b │{11: 3 }b | - {1: }b │{11: 4 }b | - {1: }b │{11: 5 }b | - {1: }b │{11: 6 }b | - {1: }b │{11: 7 }b | - {6:~ }│{6:~ }| - {3:[No Name] [+] }{7:[No Name] [+] }| + {7: }a │{8: 1 }a | + {7: }a │{15:2 }^a | + {7: }a │{8: 1 }a | + {7: }{27:x}{4: }│{8: 2 }{27:y}{4: }| + {7: }{22:x }│{8: }{23:----------------}|*2 + {7: }b │{8: 3 }b | + {7: }b │{8: 4 }b | + {7: }b │{8: 5 }b | + {7: }b │{8: 6 }b | + {7: }b │{8: 7 }b | + {1:~ }│{1:~ }| + {2:[No Name] [+] }{3:[No Name] [+] }| | ]]) feed('j') screen:expect([[ - {1: }a │{11: 2 }a | - {1: }a │{11: 1 }a | - {1: }a │{10:3 }^a | - {1: }{8:x}{9: }│{11: 1 }{8:y}{9: }| - {1: }{4:x }│{11: }{2:----------------}|*2 - {1: }b │{11: 2 }b | - {1: }b │{11: 3 }b | - {1: }b │{11: 4 }b | - {1: }b │{11: 5 }b | - {1: }b │{11: 6 }b | - {6:~ }│{6:~ }| - {3:[No Name] [+] }{7:[No Name] [+] }| + {7: }a │{8: 2 }a | + {7: }a │{8: 1 }a | + {7: }a │{15:3 }^a | + {7: }{27:x}{4: }│{8: 1 }{27:y}{4: }| + {7: }{22:x }│{8: }{23:----------------}|*2 + {7: }b │{8: 2 }b | + {7: }b │{8: 3 }b | + {7: }b │{8: 4 }b | + {7: }b │{8: 5 }b | + {7: }b │{8: 6 }b | + {1:~ }│{1:~ }| + {2:[No Name] [+] }{3:[No Name] [+] }| | ]]) end) @@ -1293,16 +1270,6 @@ end) it('Align the filler lines when changing text in diff mode', function() local screen = Screen.new(40, 20) screen:attach() - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray }, - [2] = { background = Screen.colors.LightCyan, foreground = Screen.colors.Blue1, bold = true }, - [3] = { reverse = true }, - [4] = { background = Screen.colors.LightBlue }, - [5] = { background = Screen.colors.LightMagenta }, - [6] = { background = Screen.colors.Red, bold = true }, - [7] = { foreground = Screen.colors.Blue1, bold = true }, - [8] = { reverse = true, bold = true }, - }) exec([[ call setline(1, range(1, 15)) vnew @@ -1313,54 +1280,54 @@ it('Align the filler lines when changing text in diff mode', function() ]]) screen:expect { grid = [[ - {1: }{2:------------------}│{1: }{4:6 }| - {1: }{2:------------------}│{1: }{4:7 }| - {1: }{2:------------------}│{1: }{4:8 }| - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }11 │{1: }11 | - {1: }12 │{1: }12 | - {1: }13 │{1: }13 | - {1: }14 │{1: }14 | - {1:- }1^5 │{1:- }15 | - {7:~ }│{7:~ }|*8 - {8:[No Name] [+] }{3:[No Name] [+] }| + {7: }{23:------------------}│{7: }{22:6 }| + {7: }{23:------------------}│{7: }{22:7 }| + {7: }{23:------------------}│{7: }{22:8 }| + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }11 │{7: }11 | + {7: }12 │{7: }12 | + {7: }13 │{7: }13 | + {7: }14 │{7: }14 | + {7:- }1^5 │{7:- }15 | + {1:~ }│{1:~ }|*8 + {3:[No Name] [+] }{2:[No Name] [+] }| | ]], } feed('ax<Esc>') screen:expect { grid = [[ - {1: }{2:------------------}│{1: }{4:6 }| - {1: }{2:------------------}│{1: }{4:7 }| - {1: }{2:------------------}│{1: }{4:8 }| - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }11 │{1: }11 | - {1: }12 │{1: }12 | - {1: }13 │{1: }13 | - {1: }14 │{1: }14 | - {1: }{5:15}{6:^x}{5: }│{1: }{5:15 }| - {7:~ }│{7:~ }|*8 - {8:[No Name] [+] }{3:[No Name] [+] }| + {7: }{23:------------------}│{7: }{22:6 }| + {7: }{23:------------------}│{7: }{22:7 }| + {7: }{23:------------------}│{7: }{22:8 }| + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }11 │{7: }11 | + {7: }12 │{7: }12 | + {7: }13 │{7: }13 | + {7: }14 │{7: }14 | + {7: }{4:15}{27:^x}{4: }│{7: }{4:15 }| + {1:~ }│{1:~ }|*8 + {3:[No Name] [+] }{2:[No Name] [+] }| | ]], } feed('<C-W>lay<Esc>') screen:expect { grid = [[ - {1: }{2:-----------------}│{1: }{4:6 }| - {1: }{2:-----------------}│{1: }{4:7 }| - {1: }{2:-----------------}│{1: }{4:8 }| - {1: }9 │{1: }9 | - {1: }10 │{1: }10 | - {1: }11 │{1: }11 | - {1: }12 │{1: }12 | - {1: }13 │{1: }13 | - {1: }14 │{1: }14 | - {1: }{5:15}{6:x}{5: }│{1: }{5:15}{6:^y}{5: }| - {7:~ }│{7:~ }|*8 - {3:[No Name] [+] }{8:[No Name] [+] }| + {7: }{23:-----------------}│{7: }{22:6 }| + {7: }{23:-----------------}│{7: }{22:7 }| + {7: }{23:-----------------}│{7: }{22:8 }| + {7: }9 │{7: }9 | + {7: }10 │{7: }10 | + {7: }11 │{7: }11 | + {7: }12 │{7: }12 | + {7: }13 │{7: }13 | + {7: }14 │{7: }14 | + {7: }{4:15}{27:x}{4: }│{7: }{4:15}{27:^y}{4: }| + {1:~ }│{1:~ }|*8 + {2:[No Name] [+] }{3:[No Name] [+] }| | ]], } @@ -1368,15 +1335,12 @@ end) it("diff mode doesn't restore invalid 'foldcolumn' value #21647", function() local screen = Screen.new(60, 6) - screen:set_default_attr_ids({ - [0] = { foreground = Screen.colors.Blue, bold = true }, - }) screen:attach() eq('0', api.nvim_get_option_value('foldcolumn', {})) command('diffsplit | bd') screen:expect([[ ^ | - {0:~ }|*4 + {1:~ }|*4 | ]]) eq('0', api.nvim_get_option_value('foldcolumn', {})) @@ -1385,16 +1349,9 @@ end) -- oldtest: Test_diff_binary() it('diff mode works properly if file contains NUL bytes vim-patch:8.2.3925', function() local screen = Screen.new(40, 20) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray }, - [2] = { reverse = true }, - [3] = { background = Screen.colors.LightBlue }, - [4] = { background = Screen.colors.LightMagenta }, - [5] = { background = Screen.colors.Red, bold = true }, - [6] = { foreground = Screen.colors.Blue, bold = true }, - [7] = { background = Screen.colors.Red, foreground = Screen.colors.Blue, bold = true }, - [8] = { reverse = true, bold = true }, - }) + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.Blue, bold = true, background = Screen.colors.Red }, + } screen:attach() exec([[ call setline(1, ['a', 'b', "c\n", 'd', 'e', 'f', 'g']) @@ -1408,15 +1365,15 @@ it('diff mode works properly if file contains NUL bytes vim-patch:8.2.3925', fun -- Test using internal diff screen:expect([[ - {1: }{5:^A}{4: }│{1: }{5:a}{4: }| - {1: }b │{1: }b | - {1: }{4:c }│{1: }{4:c}{7:^@}{4: }| - {1: }d │{1: }d | - {1: }{5:E}{4: }│{1: }{5:e}{4: }| - {1: }f │{1: }f | - {1: }g │{1: }g | - {6:~ }│{6:~ }|*11 - {8:[No Name] [+] }{2:[No Name] [+] }| + {7: }{27:^A}{4: }│{7: }{27:a}{4: }| + {7: }b │{7: }b | + {7: }{4:c }│{7: }{4:c}{100:^@}{4: }| + {7: }d │{7: }d | + {7: }{27:E}{4: }│{7: }{27:e}{4: }| + {7: }f │{7: }f | + {7: }g │{7: }g | + {1:~ }│{1:~ }|*11 + {3:[No Name] [+] }{2:[No Name] [+] }| | ]]) @@ -1424,15 +1381,15 @@ it('diff mode works properly if file contains NUL bytes vim-patch:8.2.3925', fun command('set diffopt+=icase') feed('<C-L>') screen:expect([[ - {1: }^A │{1: }a | - {1: }b │{1: }b | - {1: }{4:c }│{1: }{4:c}{7:^@}{4: }| - {1: }d │{1: }d | - {1: }E │{1: }e | - {1: }f │{1: }f | - {1: }g │{1: }g | - {6:~ }│{6:~ }|*11 - {8:[No Name] [+] }{2:[No Name] [+] }| + {7: }^A │{7: }a | + {7: }b │{7: }b | + {7: }{4:c }│{7: }{4:c}{100:^@}{4: }| + {7: }d │{7: }d | + {7: }E │{7: }e | + {7: }f │{7: }f | + {7: }g │{7: }g | + {1:~ }│{1:~ }|*11 + {3:[No Name] [+] }{2:[No Name] [+] }| | ]]) @@ -1440,15 +1397,15 @@ it('diff mode works properly if file contains NUL bytes vim-patch:8.2.3925', fun command('set diffopt=filler') feed('<C-L>') screen:expect([[ - {1: }{5:^A}{4: }│{1: }{5:a}{4: }| - {1: }b │{1: }b | - {1: }{4:c }│{1: }{4:c}{7:^@}{4: }| - {1: }d │{1: }d | - {1: }{5:E}{4: }│{1: }{5:e}{4: }| - {1: }f │{1: }f | - {1: }g │{1: }g | - {6:~ }│{6:~ }|*11 - {8:[No Name] [+] }{2:[No Name] [+] }| + {7: }{27:^A}{4: }│{7: }{27:a}{4: }| + {7: }b │{7: }b | + {7: }{4:c }│{7: }{4:c}{100:^@}{4: }| + {7: }d │{7: }d | + {7: }{27:E}{4: }│{7: }{27:e}{4: }| + {7: }f │{7: }f | + {7: }g │{7: }g | + {1:~ }│{1:~ }|*11 + {3:[No Name] [+] }{2:[No Name] [+] }| | ]]) @@ -1456,15 +1413,15 @@ it('diff mode works properly if file contains NUL bytes vim-patch:8.2.3925', fun command('set diffopt+=filler,icase') feed('<C-L>') screen:expect([[ - {1: }^A │{1: }a | - {1: }b │{1: }b | - {1: }{4:c }│{1: }{4:c}{7:^@}{4: }| - {1: }d │{1: }d | - {1: }E │{1: }e | - {1: }f │{1: }f | - {1: }g │{1: }g | - {6:~ }│{6:~ }|*11 - {8:[No Name] [+] }{2:[No Name] [+] }| + {7: }^A │{7: }a | + {7: }b │{7: }b | + {7: }{4:c }│{7: }{4:c}{100:^@}{4: }| + {7: }d │{7: }d | + {7: }E │{7: }e | + {7: }f │{7: }f | + {7: }g │{7: }g | + {1:~ }│{1:~ }|*11 + {3:[No Name] [+] }{2:[No Name] [+] }| | ]]) end) @@ -1473,12 +1430,6 @@ end) it("diff mode draws 'breakindent' correctly after filler lines", function() local screen = Screen.new(45, 8) screen:attach() - screen:set_default_attr_ids({ - [1] = { background = Screen.colors.Grey, foreground = Screen.colors.DarkBlue }, - [2] = { background = Screen.colors.LightBlue }, - [3] = { background = Screen.colors.LightCyan, bold = true, foreground = Screen.colors.Blue }, - [4] = { foreground = Screen.colors.Blue, bold = true }, - }) exec([[ set laststatus=0 diffopt+=followwrap breakindent breakindentopt=min:0 call setline(1, ['a', ' ' .. repeat('c', 50)]) @@ -1488,11 +1439,11 @@ it("diff mode draws 'breakindent' correctly after filler lines", function() norm! G$ ]]) screen:expect([[ - {1: }a │{1: }a | - {1: }{2:b }│{1: }{3:--------------------}| - {1: } cccccccccccccccccc│{1: } cccccccccccccccccc|*2 - {1: } cccccccccccccc │{1: } ccccccccccccc^c | - {4:~ }│{4:~ }|*2 + {7: }a │{7: }a | + {7: }{22:b }│{7: }{23:--------------------}| + {7: } cccccccccccccccccc│{7: } cccccccccccccccccc|*2 + {7: } cccccccccccccc │{7: } ccccccccccccc^c | + {1:~ }│{1:~ }|*2 | ]]) end) diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua index 0445476780..e1abd43e20 100644 --- a/test/functional/ui/embed_spec.lua +++ b/test/functional/ui/embed_spec.lua @@ -1,18 +1,23 @@ -local uv = vim.uv - -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local uv = vim.uv -local api = helpers.api -local feed = helpers.feed -local eq = helpers.eq -local neq = helpers.neq -local clear = helpers.clear -local ok = helpers.ok -local fn = helpers.fn -local nvim_prog = helpers.nvim_prog -local retry = helpers.retry -local write_file = helpers.write_file +local api = n.api +local feed = n.feed +local eq = t.eq +local neq = t.neq +local clear = n.clear +local ok = t.ok +local fn = n.fn +local nvim_prog = n.nvim_prog +local retry = t.retry +local write_file = t.write_file +local assert_log = t.assert_log +local check_close = n.check_close +local is_os = t.is_os + +local testlog = 'Xtest-embed-log' local function test_embed(ext_linegrid) local screen @@ -53,7 +58,7 @@ local function test_embed(ext_linegrid) end) it("doesn't erase output when setting color scheme", function() - if helpers.is_os('openbsd') then + if t.is_os('openbsd') then pending('FIXME #10804') end startup('--cmd', 'echoerr "foo"', '--cmd', 'color default', '--cmd', 'echoerr "bar"') @@ -93,13 +98,18 @@ describe('--embed UI on startup (ext_linegrid=false)', function() end) describe('--embed UI', function() + after_each(function() + check_close() + os.remove(testlog) + end) + it('can pass stdin', function() local pipe = assert(uv.pipe()) local writer = assert(uv.new_pipe(false)) writer:open(pipe.write) - clear { args_rm = { '--headless' }, io_extra = pipe.read } + clear { args_rm = { '--headless' }, io_extra = pipe.read, env = { NVIM_LOG_FILE = testlog } } -- attach immediately after startup, for early UI local screen = Screen.new(40, 8) @@ -131,6 +141,10 @@ describe('--embed UI', function() {1:~ }|*4 {2:-- INSERT --} | ]] + + if not is_os('win') then + assert_log('Failed to get flags on descriptor 3: Bad file descriptor', testlog, 100) + end end) it('can pass stdin to -q - #17523', function() @@ -219,7 +233,7 @@ describe('--embed UI', function() } eq({ [16777215] = true }, seen) - -- NB: by accident how functional/helpers.lua currently handles the default color scheme, the + -- NB: by accident how functional/testutil.lua currently handles the default color scheme, the -- above is sufficient to test the behavior. But in case that workaround is removed, we need -- a test with an explicit override like below, so do it to remain safe. startup('--cmd', 'hi NORMAL guibg=#FF00FF') @@ -239,44 +253,44 @@ describe('--embed UI', function() screen:expect { condition = function() - eq(helpers.paths.test_source_path, screen.pwd) + eq(t.paths.test_source_path, screen.pwd) end, } -- Change global cwd - helpers.command(string.format('cd %s/src/nvim', helpers.paths.test_source_path)) + n.command(string.format('cd %s/src/nvim', t.paths.test_source_path)) screen:expect { condition = function() - eq(string.format('%s/src/nvim', helpers.paths.test_source_path), screen.pwd) + eq(string.format('%s/src/nvim', t.paths.test_source_path), screen.pwd) end, } -- Split the window and change the cwd in the split - helpers.command('new') - helpers.command(string.format('lcd %s/test', helpers.paths.test_source_path)) + n.command('new') + n.command(string.format('lcd %s/test', t.paths.test_source_path)) screen:expect { condition = function() - eq(string.format('%s/test', helpers.paths.test_source_path), screen.pwd) + eq(string.format('%s/test', t.paths.test_source_path), screen.pwd) end, } -- Move to the original window - helpers.command('wincmd p') + n.command('wincmd p') screen:expect { condition = function() - eq(string.format('%s/src/nvim', helpers.paths.test_source_path), screen.pwd) + eq(string.format('%s/src/nvim', t.paths.test_source_path), screen.pwd) end, } -- Change global cwd again - helpers.command(string.format('cd %s', helpers.paths.test_source_path)) + n.command(string.format('cd %s', t.paths.test_source_path)) screen:expect { condition = function() - eq(helpers.paths.test_source_path, screen.pwd) + eq(t.paths.test_source_path, screen.pwd) end, } end) @@ -284,9 +298,9 @@ end) describe('--embed --listen UI', function() it('waits for connection on listening address', function() - helpers.skip(helpers.is_os('win')) + t.skip(t.is_os('win')) clear() - local child_server = assert(helpers.new_pipename()) + local child_server = assert(n.new_pipename()) fn.jobstart({ nvim_prog, '--embed', @@ -300,7 +314,7 @@ describe('--embed --listen UI', function() neq(nil, uv.fs_stat(child_server)) end) - local child_session = helpers.connect(child_server) + local child_session = n.connect(child_server) local info_ok, api_info = child_session:request('nvim_get_api_info') ok(info_ok) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index cdb3b79963..248220e28b 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -1,24 +1,26 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') local os = require('os') -local clear, feed = helpers.clear, helpers.feed -local assert_alive = helpers.assert_alive -local command, feed_command = helpers.command, helpers.feed_command -local eval = helpers.eval -local eq = helpers.eq -local neq = helpers.neq -local expect = helpers.expect -local exec = helpers.exec -local exec_lua = helpers.exec_lua -local insert = helpers.insert -local api = helpers.api -local fn = helpers.fn -local run = helpers.run -local pcall_err = helpers.pcall_err + +local clear, feed = n.clear, n.feed +local assert_alive = n.assert_alive +local command, feed_command = n.command, n.feed_command +local eval = n.eval +local eq = t.eq +local neq = t.neq +local expect = n.expect +local exec = n.exec +local exec_lua = n.exec_lua +local insert = n.insert +local api = n.api +local fn = n.fn +local run = n.run +local pcall_err = t.pcall_err local tbl_contains = vim.tbl_contains -local curbuf = helpers.api.nvim_get_current_buf -local curwin = helpers.api.nvim_get_current_win -local curtab = helpers.api.nvim_get_current_tabpage +local curbuf = n.api.nvim_get_current_buf +local curwin = n.api.nvim_get_current_win +local curtab = n.api.nvim_get_current_tabpage local NIL = vim.NIL describe('float window', function() @@ -325,6 +327,27 @@ describe('float window', function() eq(12, pos[2]) end) + it('error message when reconfig missing relative field', function() + local bufnr = api.nvim_create_buf(false, true) + local opts = { + width = 10, + height = 10, + col = 5, + row = 5, + relative = 'editor', + style = 'minimal', + } + local win_id = api.nvim_open_win(bufnr, true, opts) + eq( + "Missing 'relative' field when reconfiguring floating window 1001", + pcall_err(api.nvim_win_set_config, win_id, { + width = 3, + height = 3, + row = 10, + col = 10, + })) + end) + it('is not operated on by windo when non-focusable #15374', function() command([[ let winids = [] @@ -413,6 +436,25 @@ describe('float window', function() eq(winid, eval('win_getid()')) end) + it('is not active after closing window when non-focusable #28454', function() + command('copen') + local winid = exec_lua([[ + local bufnr = vim.api.nvim_create_buf(false, true) + local opts = { + relative = 'editor', + focusable = false, + height = 5, + width = 5, + col = 5, + row = 5, + } + return vim.api.nvim_open_win(bufnr, false, opts) + ]]) + command('wincmd t') + command('wincmd q') + neq(winid, curwin()) + end) + it('supports windo with focusable and non-focusable floats', function() local winids = exec_lua([[ local result = {vim.api.nvim_get_current_win()} @@ -490,7 +532,10 @@ describe('float window', function() local closed_win = api.nvim_get_current_win() command('close') local buf = api.nvim_create_buf(false,false) - api.nvim_open_win(buf, true, {relative='win', win=closed_win, width=1, height=1, bufpos={0,0}}) + eq( + 'Invalid window id: ' .. closed_win, + pcall_err(api.nvim_open_win, buf, true, {relative='win', win=closed_win, width=1, height=1, bufpos={0,0}}) + ) assert_alive() end) @@ -550,6 +595,43 @@ describe('float window', function() eq({ w0 }, api.nvim_list_wins()) end) + it('win_splitmove() can move float into a split', function() + command('split') + eq({'col', {{'leaf', 1001}, {'leaf', 1000}}}, fn.winlayout()) + + local win1 = api.nvim_open_win(0, true, {relative = 'editor', row = 1, col = 1, width = 5, height = 5}) + fn.win_splitmove(win1, 1001, {vertical = true}) + eq({'col', {{'row', {{'leaf', win1}, {'leaf', 1001}}}, {'leaf', 1000}}}, fn.winlayout()) + eq('', api.nvim_win_get_config(win1).relative) + + -- Should be unable to create a split relative to a float, though. + local win2 = api.nvim_open_win(0, true, {relative = 'editor', row = 1, col = 1, width = 5, height = 5}) + eq('Vim:E957: Invalid window number', pcall_err(fn.win_splitmove, win1, win2, {vertical = true})) + end) + + it('tp_curwin updated if external window is moved into split', function() + local screen = Screen.new(20, 7) + screen:attach { ext_multigrid = true } + + command('tabnew') + local external_win = api.nvim_open_win(0, true, {external = true, width = 5, height = 5}) + eq(external_win, api.nvim_get_current_win()) + eq(2, fn.tabpagenr()) + command('tabfirst') + api.nvim_set_current_win(external_win) + eq(external_win, api.nvim_get_current_win()) + eq(1, fn.tabpagenr()) + + command('wincmd J') + eq(external_win, api.nvim_get_current_win()) + eq(false, api.nvim_win_get_config(external_win).external) + command('tabnext') + eq(2, fn.tabpagenr()) + neq(external_win, api.nvim_get_current_win()) + + screen:detach() + end) + describe('with only one tabpage,', function() local float_opts = {relative = 'editor', row = 1, col = 1, width = 1, height = 1} local old_buf, old_win @@ -836,6 +918,57 @@ describe('float window', function() end) end) + describe(':close on non-float with floating windows', function() + -- XXX: it isn't really clear whether this should quit Nvim, as if the autocommand + -- here is BufUnload then it does quit Nvim. + -- But with BufWinLeave, this doesn't quit Nvim if there are no floating windows, + -- so it shouldn't quit Nvim if there are floating windows. + it('does not quit Nvim if BufWinLeave makes it the only non-float', function() + exec([[ + let g:buf = bufnr() + new + let s:midwin = win_getid() + new + setlocal bufhidden=wipe + call nvim_win_set_config(s:midwin, + \ #{relative: 'editor', row: 5, col: 5, width: 5, height: 5}) + autocmd BufWinLeave * ++once exe g:buf .. 'bwipe!' + ]]) + eq('Vim(close):E855: Autocommands caused command to abort', pcall_err(command, 'close')) + assert_alive() + end) + + pending('does not crash if BufUnload makes it the only non-float in tabpage', function() + exec([[ + tabnew + let g:buf = bufnr() + new + let s:midwin = win_getid() + new + setlocal bufhidden=wipe + call nvim_win_set_config(s:midwin, + \ #{relative: 'editor', row: 5, col: 5, width: 5, height: 5}) + autocmd BufUnload * ++once exe g:buf .. 'bwipe!' + ]]) + command('close') + assert_alive() + end) + + it('does not crash if WinClosed from floating window closes it', function() + exec([[ + tabnew + new + let s:win = win_getid() + call nvim_win_set_config(s:win, + \ #{relative: 'editor', row: 5, col: 5, width: 5, height: 5}) + wincmd t + exe $"autocmd WinClosed {s:win} 1close" + ]]) + command('close') + assert_alive() + end) + end) + local function with_ext_multigrid(multigrid) local screen, attrs before_each(function() @@ -1448,7 +1581,12 @@ describe('float window', function() }, win_viewport={ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; - }} + }, + win_viewport_margins={ + [2] = {win = 1000, top = 0, bottom = 0, left = 0, right = 0}; + [4] = {win = 1001, top = 1, bottom = 1, left = 1, right = 1}; + } + } else screen:expect{grid=[[ ^ | @@ -1648,7 +1786,12 @@ describe('float window', function() }, win_viewport={ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; - }} + }, + win_viewport_margins={ + [2] = {win = 1000, top = 0, bottom = 0, left = 0, right = 0}; + [4] = {win = 1001, top = 0, bottom = 0, left = 1, right = 1}; + } + } else screen:expect{grid=[[ ^ | @@ -1681,6 +1824,10 @@ describe('float window', function() }, win_viewport={ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }, + win_viewport_margins={ + [2] = {win = 1000, top = 0, bottom = 0, left = 0, right = 0}; + [4] = {win = 1001, top = 1, bottom = 1, left = 0, right = 0}; }} else screen:expect{grid=[[ @@ -1726,6 +1873,10 @@ describe('float window', function() }, win_viewport={ [2] = {win = 1000, topline = 0, botline = 6, curline = 5, curcol = 0, linecount = 6, sum_scroll_delta = 0}; [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }, + win_viewport_margins={ + [2] = {win = 1000, top = 0, bottom = 0, left = 0, right = 0}; + [4] = {win = 1001, top = 0, bottom = 1, left = 0, right = 1}; }} else screen:expect{grid=[[ @@ -2256,6 +2407,7 @@ describe('float window', function() command('hi B0 guibg=Red guifg=Black') command('hi B1 guifg=White') + api.nvim_win_set_config(win, { title = {{"🦄"}, {"BB", {"B0", "B1"}}}, title_pos = "right", footer= {{"🦄"}, {"BB", {"B0", "B1"}}}, footer_pos = "right", @@ -2292,6 +2444,47 @@ describe('float window', function() | ]]} end + eq({{"🦄"}, {"BB", {"B0", "B1"}}}, api.nvim_win_get_config(win).title) + eq({{"🦄"}, {"BB", {"B0", "B1"}}}, api.nvim_win_get_config(win).footer) + + api.nvim_win_set_config(win, { + title = {{"🦄", ""}, {"BB", {"B0", "B1", ""}}}, title_pos = "left", + footer= {{"🦄", ""}, {"BB", {"B0", "B1", ""}}}, footer_pos = "left", + }) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]|*6 + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }|*5 + ## grid 3 + | + ## grid 4 + {5:╔}🦄{7:BB}{5:═════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚}🦄{7:BB}{5:═════╝}| + ]], float_pos={ + [4] = { 1001, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2, sum_scroll_delta = 0}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:╔}🦄{7:BB}{5:═════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚}🦄{7:BB}{5:═════╝}{0: }| + | + ]]} + end + eq({{"🦄", ""}, {"BB", {"B0", "B1", ""}}}, api.nvim_win_get_config(win).title) + eq({{"🦄", ""}, {"BB", {"B0", "B1", ""}}}, api.nvim_win_get_config(win).footer) end) it('terminates border on edge of viewport when window extends past viewport', function() @@ -3974,7 +4167,7 @@ describe('float window', function() if multigrid then pending("supports second UI without multigrid", function() - local session2 = helpers.connect(eval('v:servername')) + local session2 = n.connect(eval('v:servername')) print(session2:request("nvim_eval", "2+2")) local screen2 = Screen.new(40,7) screen2:attach(nil, session2) @@ -6169,7 +6362,7 @@ describe('float window', function() run(on_request, nil, on_setup) os.remove('Xtest_written') os.remove('Xtest_written2') - eq(exited, true) + eq(true, exited) end) it(':quit two floats in a row', function() @@ -7872,7 +8065,7 @@ describe('float window', function() end) it("correctly redraws when overlaid windows are resized #13991", function() - helpers.source([[ + n.source([[ let popup_config = {"relative" : "editor", \ "width" : 7, \ "height" : 3, @@ -7936,7 +8129,7 @@ describe('float window', function() ]]) end - helpers.source([[ + n.source([[ let new_popup_config = {"width" : 1, "height" : 3} let new_border_config = {"width" : 3, "height" : 5} @@ -7951,7 +8144,7 @@ describe('float window', function() nnoremap zz <cmd>call Resize()<cr> ]]) - helpers.feed("zz") + n.feed("zz") if multigrid then screen:expect{grid=[[ ## grid 1 @@ -8273,6 +8466,10 @@ describe('float window', function() }, win_viewport={ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + }, + win_viewport_margins={ + [2] = {win = 1000, top = 0, bottom = 0, left = 0, right = 0}; + [4] = {win = 1001, top = 2, bottom = 1, left = 1, right = 1}; }} else screen:expect{grid=[[ @@ -9101,6 +9298,67 @@ describe('float window', function() ]]} end end) + + it('attempt to turn into split with no room', function() + eq('Vim(split):E36: Not enough room', pcall_err(command, 'execute "split |"->repeat(&lines)')) + command('vsplit | wincmd | | wincmd p') + api.nvim_open_win(0, true, {relative = "editor", row = 0, col = 0, width = 5, height = 5}) + local config = api.nvim_win_get_config(0) + eq('editor', config.relative) + + local layout = fn.winlayout() + local restcmd = fn.winrestcmd() + eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd K')) + eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd J')) + eq(layout, fn.winlayout()) + eq(restcmd, fn.winrestcmd()) + eq(config, api.nvim_win_get_config(0)) + end) + + it("error when relative to itself", function() + local buf = api.nvim_create_buf(false, true) + local config = { relative='win', width=5, height=2, row=3, col=3 } + local winid = api.nvim_open_win(buf, false, config) + api.nvim_set_current_win(winid) + eq("floating window cannot be relative to itself", pcall_err(api.nvim_win_set_config, winid, config)) + end) + + it("bufpos out of range", function() + local buf = api.nvim_create_buf(false, true) + api.nvim_buf_set_lines(0, 0, -1, false, {}) + local config = { relative='win', width=5, height=2, row=0, col=0, bufpos = { 3, 3 } } + api.nvim_open_win(buf, false, config) + if multigrid then + screen:expect({ + grid = [[ + ## grid 1 + [2:----------------------------------------]|*6 + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }|*5 + ## grid 3 + | + ## grid 4 + {1: }| + {2:~ }| + ]], float_pos={ + [4] = {1001, "NW", 2, 0, 0, true, 50}; + }, win_viewport={ + [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + }}) + else + screen:expect({ + grid = [[ + {1:^ } | + {2:~ }{0: }| + {0:~ }|*4 + | + ]] + }) + end + end) end describe('with ext_multigrid', function() diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 7f13b6bd03..2712e5ff48 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, eq = helpers.clear, helpers.feed, helpers.eq -local command = helpers.command -local feed_command = helpers.feed_command -local insert = helpers.insert -local fn = helpers.fn -local api = helpers.api -local exec = helpers.exec -local assert_alive = helpers.assert_alive + +local clear, feed, eq = n.clear, n.feed, t.eq +local command = n.command +local feed_command = n.feed_command +local insert = n.insert +local fn = n.fn +local api = n.api +local exec = n.exec +local assert_alive = n.assert_alive local content1 = [[ This is a diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 727dc38829..b7b46ddfae 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') local os = require('os') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command, exec = helpers.command, helpers.exec -local eval = helpers.eval -local feed_command, eq = helpers.feed_command, helpers.eq -local fn = helpers.fn -local api = helpers.api -local exec_lua = helpers.exec_lua + +local clear, feed, insert = n.clear, n.feed, n.insert +local command, exec = n.command, n.exec +local eval = n.eval +local feed_command, eq = n.feed_command, t.eq +local fn = n.fn +local api = n.api +local exec_lua = n.exec_lua describe('colorscheme compatibility', function() before_each(function() @@ -32,11 +34,6 @@ describe('highlight: `:syntax manual`', function() screen = Screen.new(20, 5) screen:attach() -- syntax highlight for vimscript's "echo" - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, - [1] = { bold = true, foreground = Screen.colors.Brown }, - [2] = { foreground = Screen.colors.Magenta1 }, - }) end) after_each(function() @@ -57,8 +54,8 @@ describe('highlight: `:syntax manual`', function() command('bn') feed_command('bp') screen:expect([[ - {1:^echo} {2:1} | - {0:~ }|*3 + {15:^echo} {26:1} | + {1:~ }|*3 :bp | ]]) end) @@ -79,8 +76,8 @@ describe('highlight: `:syntax manual`', function() feed_command('silent bp') eq('Xtest-functional-ui-highlight.tmp.vim', eval("fnamemodify(bufname('%'), ':t')")) screen:expect([[ - {1:^echo} {2:1} | - {0:~ }|*3 + {15:^echo} {26:1} | + {1:~ }|*3 :silent bp | ]]) end) @@ -92,17 +89,9 @@ describe('highlight defaults', function() before_each(function() clear() screen = Screen.new() - screen:set_default_attr_ids { - [0] = { bold = true, foreground = Screen.colors.Blue }, - [1] = { reverse = true, bold = true }, - [2] = { reverse = true }, - [3] = { bold = true }, - [4] = { bold = true, foreground = Screen.colors.SeaGreen }, - [5] = { foreground = Screen.colors.Red1, background = Screen.colors.WebGreen }, - [6] = { background = Screen.colors.Red1, foreground = Screen.colors.Grey100 }, - [7] = { foreground = Screen.colors.Red }, - [8] = { foreground = Screen.colors.Blue }, - [9] = { italic = true }, + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.Red, background = Screen.colors.WebGreen }, + [101] = { italic = true }, } screen:attach() end) @@ -111,10 +100,10 @@ describe('highlight defaults', function() feed_command('sp', 'vsp', 'vsp') screen:expect([[ ^ │ │ | - {0:~ }│{0:~ }│{0:~ }|*5 - {1:[No Name] }{2:[No Name] [No Name] }| + {1:~ }│{1:~ }│{1:~ }|*5 + {3:[No Name] }{2:[No Name] [No Name] }| | - {0:~ }|*4 + {1:~ }|*4 {2:[No Name] }| :vsp | ]]) @@ -122,11 +111,11 @@ describe('highlight defaults', function() feed('<c-w>j') screen:expect([[ │ │ | - {0:~ }│{0:~ }│{0:~ }|*5 + {1:~ }│{1:~ }│{1:~ }|*5 {2:[No Name] [No Name] [No Name] }| ^ | - {0:~ }|*4 - {1:[No Name] }| + {1:~ }|*4 + {3:[No Name] }| :vsp | ]]) -- note that when moving to a window with small width nvim will increase @@ -135,30 +124,30 @@ describe('highlight defaults', function() feed('<c-w>k<c-w>l') screen:expect([[ │^ │ | - {0:~ }│{0:~ }│{0:~ }|*5 - {2:[No Name] }{1:[No Name] }{2:[No Name] }| + {1:~ }│{1:~ }│{1:~ }|*5 + {2:[No Name] }{3:[No Name] }{2:[No Name] }| | - {0:~ }|*4 + {1:~ }|*4 {2:[No Name] }| :vsp | ]]) feed('<c-w>l') screen:expect([[ │ │^ | - {0:~ }│{0:~ }│{0:~ }|*5 - {2:[No Name] [No Name] }{1:[No Name] }| + {1:~ }│{1:~ }│{1:~ }|*5 + {2:[No Name] [No Name] }{3:[No Name] }| | - {0:~ }|*4 + {1:~ }|*4 {2:[No Name] }| :vsp | ]]) feed('<c-w>h<c-w>h') screen:expect([[ ^ │ │ | - {0:~ }│{0:~ }│{0:~ }|*5 - {1:[No Name] }{2:[No Name] [No Name] }| + {1:~ }│{1:~ }│{1:~ }|*5 + {3:[No Name] }{2:[No Name] [No Name] }| | - {0:~ }|*4 + {1:~ }|*4 {2:[No Name] }| :vsp | ]]) @@ -169,8 +158,8 @@ describe('highlight defaults', function() screen:try_resize(53, 4) screen:expect([[ ^ | - {0:~ }|*2 - {3:-- INSERT --} | + {1:~ }|*2 + {5:-- INSERT --} | ]]) end) @@ -178,7 +167,7 @@ describe('highlight defaults', function() screen:try_resize(53, 4) screen:expect([[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]]) end) @@ -187,10 +176,10 @@ describe('highlight defaults', function() screen:try_resize(53, 4) feed(':ls<cr>') screen:expect([[ - {1: }| + {3: }| :ls | 1 %a "[No Name]" line 1 | - {4:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) feed('<cr>') -- skip the "Press ENTER..." state or tests will hang end) @@ -201,7 +190,7 @@ describe('highlight defaults', function() feed('i') screen:expect([[ ^ | - {0:~ }|*2 + {1:~ }|*2 -- INSERT -- | ]]) feed('<esc>') @@ -210,8 +199,8 @@ describe('highlight defaults', function() feed('i') screen:expect([[ ^ | - {0:~ }|*2 - {5:-- INSERT --} | + {1:~ }|*2 + {100:-- INSERT --} | ]]) end) @@ -221,8 +210,8 @@ describe('highlight defaults', function() feed_command('hi link TmpKeyword ErrorMsg') insert('neovim') screen:expect([[ - {6:neovi^m} | - {0:~ }|*2 + {9:neovi^m} | + {1:~ }|*2 | ]]) feed_command( @@ -231,7 +220,7 @@ describe('highlight defaults', function() ) screen:expect([[ neovi^m | - {0:~ }|*2 + {1:~ }|*2 | ]]) end) @@ -240,19 +229,16 @@ describe('highlight defaults', function() screen:try_resize(53, 4) screen:expect([[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]]) feed_command('hi NonTextAlt guifg=Red') feed_command('hi! link NonText NonTextAlt') - screen:expect( - [[ + screen:expect([[ ^ | - {0:~ }|*2 + {19:~ }|*2 :hi! link NonText NonTextAlt | - ]], - { [0] = { foreground = Screen.colors.Red } } - ) + ]]) end) it('Cursor after `:hi clear|syntax reset` #6508', function() @@ -266,47 +252,48 @@ describe('highlight defaults', function() feed_command('set listchars=space:.,tab:>-,trail:*,eol:¬ list') insert(' ne \t o\tv im ') screen:expect([[ - ne{7:.>----.}o{7:>-----}v{7:..}im{7:*^*¬} | - {7:~ }|*2 + ne{19:.>----.}o{19:>-----}v{19:..}im{19:*^*¬} | + {19:~ }|*2 | ]]) feed_command('highlight Whitespace gui=NONE guifg=#0000FF') screen:expect([[ - ne{8:.>----.}o{8:>-----}v{8:..}im{8:*^*}{7:¬} | - {7:~ }|*2 + ne{18:.>----.}o{18:>-----}v{18:..}im{18:*^*}{19:¬} | + {19:~ }|*2 :highlight Whitespace gui=NONE guifg=#0000FF | ]]) end) it('are sent to UIs', function() screen:try_resize(53, 4) + screen:expect { grid = [[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], - hl_groups = { EndOfBuffer = 0, MsgSeparator = 1 }, + hl_groups = { EndOfBuffer = 1, MsgSeparator = 3 }, } command('highlight EndOfBuffer gui=italic') screen:expect { grid = [[ ^ | - {9:~ }|*2 + {101:~ }|*2 | ]], - hl_groups = { EndOfBuffer = 9, MsgSeparator = 1 }, + hl_groups = { EndOfBuffer = 101, MsgSeparator = 3 }, } command('highlight clear EndOfBuffer') screen:expect { grid = [[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], - hl_groups = { EndOfBuffer = 0, MsgSeparator = 1 }, + hl_groups = { EndOfBuffer = 1, MsgSeparator = 3 }, } end) end) @@ -317,14 +304,6 @@ describe('highlight', function() it('Visual', function() local screen = Screen.new(45, 5) screen:attach() - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.Black, background = Screen.colors.LightGrey }, - [2] = { bold = true, foreground = Screen.colors.Blue }, - [3] = { bold = true }, - [4] = { reverse = true, bold = true }, - [5] = { reverse = true }, - [6] = { background = Screen.colors.Grey90 }, - }) insert([[ line1 foo bar abcdefghijklmnopqrs @@ -337,137 +316,136 @@ describe('highlight', function() command('set guicursor=a:block-blinkon0') feed('V') screen:expect([[ - {1: }^l{1:ine1 foo bar} │{1: line1 foo bar} | + {17: }^l{17:ine1 foo bar} │{17: line1 foo bar} | abcdefghijklmnopqrs │abcdefghijklmnopqrs | ABCDEFGHIJKLMNOPQRS │ABCDEFGHIJKLMNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL LINE --} | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL LINE --} | ]]) feed('<Esc>$vhhh') screen:expect([[ - line1 foo^ {1:bar} │ line1 foo{1: bar} | + line1 foo^ {17:bar} │ line1 foo{17: bar} | abcdefghijklmnopqrs │abcdefghijklmnopqrs | ABCDEFGHIJKLMNOPQRS │ABCDEFGHIJKLMNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL --} | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL --} | ]]) -- Vertical cursor: highlights char-at-cursor. #8983 command('set guicursor=a:block-blinkon175') screen:expect([[ - line1 foo{1:^ bar} │ line1 foo{1: bar} | + line1 foo{17:^ bar} │ line1 foo{17: bar} | abcdefghijklmnopqrs │abcdefghijklmnopqrs | ABCDEFGHIJKLMNOPQRS │ABCDEFGHIJKLMNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL --} | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL --} | ]]) command('set selection=exclusive') screen:expect([[ - line1 foo{1:^ ba}r │ line1 foo{1: ba}r | + line1 foo{17:^ ba}r │ line1 foo{17: ba}r | abcdefghijklmnopqrs │abcdefghijklmnopqrs | ABCDEFGHIJKLMNOPQRS │ABCDEFGHIJKLMNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL --} | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL --} | ]]) feed('o') screen:expect([[ - line1 foo{1: ba}^r │ line1 foo{1: ba}r | + line1 foo{17: ba}^r │ line1 foo{17: ba}r | abcdefghijklmnopqrs │abcdefghijklmnopqrs | ABCDEFGHIJKLMNOPQRS │ABCDEFGHIJKLMNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL --} | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL --} | ]]) feed('V') screen:expect([[ - {1: line1 foo ba^r} │{1: line1 foo bar} | + {17: line1 foo ba^r} │{17: line1 foo bar} | abcdefghijklmnopqrs │abcdefghijklmnopqrs | ABCDEFGHIJKLMNOPQRS │ABCDEFGHIJKLMNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL LINE --} | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL LINE --} | ]]) command('set cursorcolumn') feed('<C-V>') screen:expect([[ - line1 foo{1: ba}^r │ line1 foo{1: ba}r | - abcdefghijklmn{6:o}pqrs │abcdefghijklmnopqrs | - ABCDEFGHIJKLMN{6:O}PQRS │ABCDEFGHIJKLMNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL BLOCK --} | + line1 foo{17: ba}^r │ line1 foo{17: ba}r | + abcdefghijklmn{21:o}pqrs │abcdefghijklmnopqrs | + ABCDEFGHIJKLMN{21:O}PQRS │ABCDEFGHIJKLMNOPQRS | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL BLOCK --} | ]]) command('set selection&') screen:expect([[ - line1 foo{1: ba^r} │ line1 foo{1: bar} | - abcdefghijklmn{6:o}pqrs │abcdefghijklmnopqrs | - ABCDEFGHIJKLMN{6:O}PQRS │ABCDEFGHIJKLMNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL BLOCK --} | + line1 foo{17: ba^r} │ line1 foo{17: bar} | + abcdefghijklmn{21:o}pqrs │abcdefghijklmnopqrs | + ABCDEFGHIJKLMN{21:O}PQRS │ABCDEFGHIJKLMNOPQRS | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL BLOCK --} | ]]) feed('^') screen:expect([[ - {1:^line1 foo }bar │ {1:line1 foo }bar | - ab{6:c}defghijklmnopqrs │abcdefghijklmnopqrs | - AB{6:C}DEFGHIJKLMNOPQRS │ABCDEFGHIJKLMNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL BLOCK --} | + {17:^line1 foo }bar │ {17:line1 foo }bar | + ab{21:c}defghijklmnopqrs │abcdefghijklmnopqrs | + AB{21:C}DEFGHIJKLMNOPQRS │ABCDEFGHIJKLMNOPQRS | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL BLOCK --} | ]]) feed('2j') screen:expect([[ - {1:line1 foo }bar │ {1:line1 foo }bar | - ab{1:cdefghijkl}mnopqrs │ab{1:cdefghijkl}mnopqrs | - AB{1:^CDEFGHIJKL}MNOPQRS │AB{1:CDEFGHIJKL}MNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL BLOCK --} | + {17:line1 foo }bar │ {17:line1 foo }bar | + ab{17:cdefghijkl}mnopqrs │ab{17:cdefghijkl}mnopqrs | + AB{17:^CDEFGHIJKL}MNOPQRS │AB{17:CDEFGHIJKL}MNOPQRS | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL BLOCK --} | ]]) command('set nocursorcolumn') feed('O') screen:expect([[ - {1:line1 foo }bar │ {1:line1 foo }bar | - ab{1:cdefghijkl}mnopqrs │ab{1:cdefghijkl}mnopqrs | - AB{1:CDEFGHIJK^L}MNOPQRS │AB{1:CDEFGHIJKL}MNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL BLOCK --} | + {17:line1 foo }bar │ {17:line1 foo }bar | + ab{17:cdefghijkl}mnopqrs │ab{17:cdefghijkl}mnopqrs | + AB{17:CDEFGHIJK^L}MNOPQRS │AB{17:CDEFGHIJKL}MNOPQRS | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL BLOCK --} | ]]) command('set selection=exclusive') screen:expect([[ - {1:line1 foo} bar │ {1:line1 foo} bar | - ab{1:cdefghijk}lmnopqrs │ab{1:cdefghijk}lmnopqrs | - AB{1:CDEFGHIJK}^LMNOPQRS │AB{1:CDEFGHIJK}LMNOPQRS | - {4:[No Name] [+] }{5:[No Name] [+] }| - {3:-- VISUAL BLOCK --} | + {17:line1 foo} bar │ {17:line1 foo} bar | + ab{17:cdefghijk}lmnopqrs │ab{17:cdefghijk}lmnopqrs | + AB{17:CDEFGHIJK}^LMNOPQRS │AB{17:CDEFGHIJK}LMNOPQRS | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- VISUAL BLOCK --} | ]]) end) it('cterm=standout gui=standout', function() local screen = Screen.new(20, 5) screen:attach() - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue1 }, - [2] = { - standout = true, + screen:add_extra_attr_ids { + [100] = { + foreground = Screen.colors.Blue1, bold = true, + background = Screen.colors.Grey90, underline = true, - background = Screen.colors.Gray90, - foreground = Screen.colors.Blue1, + standout = true, }, - [3] = { standout = true, underline = true, background = Screen.colors.Gray90 }, - }) + [101] = { underline = true, standout = true, background = Screen.colors.Grey90 }, + } feed_command('hi CursorLine cterm=standout,underline gui=standout,underline') feed_command('set cursorline') feed_command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list') feed('i\t abcd <cr>\t abcd <cr><esc>k') screen:expect([[ {1:>-------.}abcd{1:*¬} | - {2:^>-------.}{3:abcd}{2:*¬}{3: }| + {100:^>-------.}{101:abcd}{100:*¬}{101: }| {1:¬} | {1:~ }| | @@ -504,16 +482,13 @@ describe('highlight', function() it('nocombine', function() local screen = Screen.new(25, 6) - screen:set_default_attr_ids { - [1] = { foreground = Screen.colors.SlateBlue, underline = true }, - [2] = { bold = true, foreground = Screen.colors.Blue1 }, - [3] = { underline = true, reverse = true, foreground = Screen.colors.SlateBlue }, - [4] = { - background = Screen.colors.Yellow, - reverse = true, + screen:add_extra_attr_ids { + [100] = { underline = true, reverse = true, foreground = Screen.colors.SlateBlue }, + [101] = { foreground = Screen.colors.SlateBlue, + reverse = true, + background = Screen.colors.Yellow, }, - [5] = { foreground = Screen.colors.Red }, } screen:attach() feed_command('syntax on') @@ -526,9 +501,9 @@ describe('highlight', function() ]]) screen:expect { grid = [[ - {1:foobar} |*2 + {28:foobar} |*2 ^ | - {2:~ }|*2 + {1:~ }|*2 | ]], } @@ -536,21 +511,21 @@ describe('highlight', function() feed('/foo') screen:expect { grid = [[ - {3:foo}{1:bar} | - {4:foo}{1:bar} | + {100:foo}{28:bar} | + {101:foo}{28:bar} | | - {2:~ }|*2 + {1:~ }|*2 /foo^ | ]], } feed('<cr>') screen:expect { grid = [[ - {4:^foo}{1:bar} | - {4:foo}{1:bar} | + {101:^foo}{28:bar} | + {101:foo}{28:bar} | | - {2:~ }|*2 - {5:search hit...uing at TOP} | + {1:~ }|*2 + {19:search hit...uing at TOP} | ]], } end) @@ -660,44 +635,40 @@ describe("'listchars' highlight", function() end) it("'cursorline' and 'cursorcolumn'", function() - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, - [1] = { background = Screen.colors.Grey90 }, - }) feed_command('highlight clear ModeMsg') feed_command('set cursorline') feed('i') screen:expect([[ - {1:^ }| - {0:~ }|*3 + {21:^ }| + {1:~ }|*3 -- INSERT -- | ]]) feed('abcdefg<cr>kkasdf') screen:expect([[ abcdefg | - {1:kkasdf^ }| - {0:~ }|*2 + {21:kkasdf^ }| + {1:~ }|*2 -- INSERT -- | ]]) feed('<esc>') screen:expect([[ abcdefg | - {1:kkasd^f }| - {0:~ }|*2 + {21:kkasd^f }| + {1:~ }|*2 | ]]) feed_command('set nocursorline') screen:expect([[ abcdefg | kkasd^f | - {0:~ }|*2 + {1:~ }|*2 :set nocursorline | ]]) feed('k') screen:expect([[ abcde^fg | kkasdf | - {0:~ }|*2 + {1:~ }|*2 :set nocursorline | ]]) feed('jjji<cr><cr><cr><esc>') @@ -711,42 +682,27 @@ describe("'listchars' highlight", function() feed_command('set cursorcolumn') feed('kkiabcdefghijk<esc>hh') screen:expect([[ - kkasd {1: } | - {1:abcdefgh^ijk }| - {1: } | - f {1: } | + kkasd {21: } | + {21:abcdefgh^ijk }| + {21: } | + f {21: } | | ]]) feed('khh') screen:expect([[ - {1:kk^asd }| - ab{1:c}defghijk | - {1: } | - f {1: } | + {21:kk^asd }| + ab{21:c}defghijk | + {21: } | + f {21: } | | ]]) end) it("'cursorline' and with 'listchars' option", function() - screen:set_default_attr_ids({ - [1] = { background = Screen.colors.Grey90 }, - [2] = { - foreground = Screen.colors.Red, - background = Screen.colors.Grey90, - }, - [3] = { - background = Screen.colors.Grey90, - foreground = Screen.colors.Blue, - bold = true, - }, - [4] = { - foreground = Screen.colors.Blue, - bold = true, - }, - [5] = { - foreground = Screen.colors.Red, - }, - }) + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.Red, background = Screen.colors.Grey90 }, + [101] = { foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.Grey90 }, + } feed_command('highlight clear ModeMsg') feed_command('highlight Whitespace guifg=#FF0000') feed_command('set cursorline') @@ -754,73 +710,70 @@ describe("'listchars' highlight", function() feed_command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list') feed('i\t abcd <cr>\t abcd <cr><esc>k') screen:expect([[ - {5:>-------.}abcd{5:*}{4:¬} | - {2:^>-------.}{1:abcd}{2:*}{3:¬}{1: }| - {4:¬} | - {4:~ }| + {19:>-------.}abcd{19:*}{1:¬} | + {100:^>-------.}{21:abcd}{100:*}{101:¬}{21: }| + {1:¬} | + {1:~ }| | ]]) feed('k') screen:expect([[ - {2:^>-------.}{1:abcd}{2:*}{3:¬}{1: }| - {5:>-------.}abcd{5:*}{4:¬} | - {4:¬} | - {4:~ }| + {100:^>-------.}{21:abcd}{100:*}{101:¬}{21: }| + {19:>-------.}abcd{19:*}{1:¬} | + {1:¬} | + {1:~ }| | ]]) feed_command('set nocursorline') screen:expect([[ - {5:^>-------.}abcd{5:*}{4:¬} | - {5:>-------.}abcd{5:*}{4:¬} | - {4:¬} | - {4:~ }| + {19:^>-------.}abcd{19:*}{1:¬} | + {19:>-------.}abcd{19:*}{1:¬} | + {1:¬} | + {1:~ }| :set nocursorline | ]]) feed_command('set nowrap') feed('ALorem ipsum dolor sit amet<ESC>0') screen:expect([[ - {5:^>-------.}abcd{5:.}Lorem{4:>}| - {5:>-------.}abcd{5:*}{4:¬} | - {4:¬} | - {4:~ }| + {19:^>-------.}abcd{19:.}Lorem{1:>}| + {19:>-------.}abcd{19:*}{1:¬} | + {1:¬} | + {1:~ }| | ]]) feed_command('set cursorline') screen:expect([[ - {2:^>-------.}{1:abcd}{2:.}{1:Lorem}{3:>}| - {5:>-------.}abcd{5:*}{4:¬} | - {4:¬} | - {4:~ }| + {100:^>-------.}{21:abcd}{100:.}{21:Lorem}{101:>}| + {19:>-------.}abcd{19:*}{1:¬} | + {1:¬} | + {1:~ }| :set cursorline | ]]) feed('$') screen:expect([[ - {3:<}{1:r}{2:.}{1:sit}{2:.}{1:ame^t}{3:¬}{1: }| - {4:<} |*2 - {4:~ }| + {101:<}{21:r}{100:.}{21:sit}{100:.}{21:ame^t}{101:¬}{21: }| + {1:<} |*2 + {1:~ }| :set cursorline | ]]) feed('G') screen:expect([[ - {5:>-------.}abcd{5:.}Lorem{4:>}| - {5:>-------.}abcd{5:*}{4:¬} | - {3:^¬}{1: }| - {4:~ }| + {19:>-------.}abcd{19:.}Lorem{1:>}| + {19:>-------.}abcd{19:*}{1:¬} | + {101:^¬}{21: }| + {1:~ }| :set cursorline | ]]) end) it("'listchar' with wrap", function() - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, - }) feed_command('set wrap') feed_command('set listchars=eol:¬,precedes:< list') feed('90ia<esc>') screen:expect([[ - {0:<}aaaaaaaaaaaaaaaaaaa| + {1:<}aaaaaaaaaaaaaaaaaaa| aaaaaaaaaaaaaaaaaaaa|*2 - aaaaaaaaa^a{0:¬} | + aaaaaaaaa^a{1:¬} | | ]]) feed('0') @@ -832,16 +785,16 @@ describe("'listchars' highlight", function() end) it("'listchar' in visual mode", function() - screen:set_default_attr_ids({ - [1] = { background = Screen.colors.Grey90 }, - [2] = { foreground = Screen.colors.Red, background = Screen.colors.Grey90 }, - [3] = { background = Screen.colors.Grey90, foreground = Screen.colors.Blue, bold = true }, - [4] = { foreground = Screen.colors.Blue, bold = true }, - [5] = { foreground = Screen.colors.Red }, - [6] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, - [7] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Red }, - [8] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Blue, bold = true }, - }) + screen:add_extra_attr_ids { + [100] = { + foreground = Screen.colors.Blue1, + bold = true, + background = Screen.colors.LightGray, + }, + [101] = { foreground = Screen.colors.Red, background = Screen.colors.Grey90 }, + [102] = { foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.Grey90 }, + [103] = { foreground = Screen.colors.Red, background = Screen.colors.LightGray }, + } command('highlight clear ModeMsg') command('highlight Whitespace guifg=#FF0000') command('set cursorline') @@ -850,45 +803,42 @@ describe("'listchars' highlight", function() command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list') feed('i\t abcd <cr>\t abcd Lorem ipsum dolor sit amet<cr><esc>kkk0') screen:expect([[ - {2:^>-------.}{1:abcd}{2:*}{3:¬}{1: }| - {5:>-------.}abcd{5:.}Lorem{4:>}| - {4:¬} | - {4:~ }| + {101:^>-------.}{21:abcd}{101:*}{102:¬}{21: }| + {19:>-------.}abcd{19:.}Lorem{1:>}| + {1:¬} | + {1:~ }| | ]]) feed('lllvj') screen:expect([[ - {5:>-------.}a{6:bcd}{7:*}{8:¬} | - {7:>-------.}{6:a}^bcd{5:.}Lorem{4:>}| - {4:¬} | - {4:~ }| + {19:>-------.}a{17:bcd}{103:*}{100:¬} | + {103:>-------.}{17:a}^bcd{19:.}Lorem{1:>}| + {1:¬} | + {1:~ }| -- VISUAL -- | ]]) feed('<esc>V') screen:expect([[ - {5:>-------.}abcd{5:*}{4:¬} | - {7:>-------.}{6:a}^b{6:cd}{7:.}{6:Lorem}{4:>}| - {4:¬} | - {4:~ }| + {19:>-------.}abcd{19:*}{1:¬} | + {103:>-------.}{17:a}^b{17:cd}{103:.}{17:Lorem}{1:>}| + {1:¬} | + {1:~ }| -- VISUAL LINE -- | ]]) feed('<esc>$') screen:expect([[ - {4:<} | - {3:<}{1:r}{2:.}{1:sit}{2:.}{1:ame^t}{3:¬}{1: }| - {4:<} | - {4:~ }| + {1:<} | + {102:<}{21:r}{101:.}{21:sit}{101:.}{21:ame^t}{102:¬}{21: }| + {1:<} | + {1:~ }| | ]]) end) it("'cursorline' with :match", function() - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, - [1] = { background = Screen.colors.Grey90 }, - [2] = { foreground = Screen.colors.Red }, - [3] = { foreground = Screen.colors.X11Green, background = Screen.colors.Red1 }, - }) + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.Lime, background = Screen.colors.Red }, + } feed_command('highlight clear ModeMsg') feed_command('highlight Whitespace guifg=#FF0000') feed_command('highlight Error guifg=#00FF00') @@ -896,19 +846,19 @@ describe("'listchars' highlight", function() feed('ia \t bc \t <esc>') screen:expect([[ a bc ^ | - {0:~ }|*3 + {1:~ }|*3 | ]]) feed_command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list') screen:expect([[ - a{2:.>-----.}bc{2:*>---*^*}{0:¬} | - {0:~ }|*3 + a{19:.>-----.}bc{19:*>---*^*}{1:¬} | + {1:~ }|*3 | ]]) feed_command('match Error /\\s\\+$/') screen:expect([[ - a{2:.>-----.}bc{3:*>---*^*}{0:¬} | - {0:~ }|*3 + a{19:.>-----.}bc{100:*>---*^*}{1:¬} | + {1:~ }|*3 | ]]) end) @@ -919,15 +869,10 @@ describe('CursorLine and CursorLineNr highlights', function() it('overridden by Error, ColorColumn if fg not set', function() local screen = Screen.new(50, 5) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.SlateBlue }, - [2] = { bold = true, foreground = Screen.colors.Brown }, - [3] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, - [4] = { foreground = Screen.colors.SlateBlue, background = Screen.colors.Gray90 }, - [5] = { background = Screen.colors.Gray90 }, - [6] = { bold = true, foreground = Screen.colors.Blue1 }, - [7] = { background = Screen.colors.LightRed }, - }) + screen:add_extra_attr_ids { + [100] = { background = Screen.colors.LightRed }, + [101] = { foreground = Screen.colors.SlateBlue, background = Screen.colors.Grey90 }, + } screen:attach() command('filetype on') @@ -935,32 +880,32 @@ describe('CursorLine and CursorLineNr highlights', function() command('set cursorline ft=json') feed('i{<cr>"a" : abc // 10;<cr>}<cr><esc>') screen:expect([[ - {1:{} | - "{2:a}" : {3:abc} {3:// 10;} | - {1:}} | - {5:^ }| + {16:{} | + "{15:a}" : {9:abc} {9:// 10;} | + {16:}} | + {21:^ }| | ]]) command('set colorcolumn=3') feed('i <esc>') screen:expect([[ - {1:{} {7: } | - "{2:a}{7:"} : {3:abc} {3:// 10;} | - {1:}} {7: } | - {5: ^ }{7: }{5: }| + {16:{} {100: } | + "{15:a}{100:"} : {9:abc} {9:// 10;} | + {16:}} {100: } | + {21: ^ }{100: }{21: }| | ]]) end) it("overridden by NonText in 'showbreak' characters", function() local screen = Screen.new(20, 5) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.Yellow, background = Screen.colors.Blue }, - [2] = { foreground = Screen.colors.Black, background = Screen.colors.White }, - [3] = { foreground = Screen.colors.Yellow, background = Screen.colors.White }, - [4] = { foreground = Screen.colors.Yellow }, - }) + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.Yellow1 }, + [101] = { foreground = Screen.colors.Yellow1, background = Screen.colors.Blue }, + [102] = { foreground = Screen.colors.Grey0, background = Screen.colors.Grey100 }, + [103] = { foreground = Screen.colors.Yellow1, background = Screen.colors.Grey100 }, + } screen:attach() feed_command('set wrap cursorline') @@ -973,46 +918,45 @@ describe('CursorLine and CursorLineNr highlights', function() feed('30iø<esc>o<esc>30ia<esc>') screen:expect([[ øøøøøøøøøøøøøøøøøøøø| - {1:>>>}øøøøøøøøøø | - {2:aaaaaaaaaaaaaaaaaaaa}| - {1:>>>}{2:aaaaaaaaa^a }| + {101:>>>}øøøøøøøøøø | + {102:aaaaaaaaaaaaaaaaaaaa}| + {101:>>>}{102:aaaaaaaaa^a }| | ]]) feed('k') screen:expect([[ - {2:øøøøøøøøøøøøøøøøøøøø}| - {1:>>>}{2:øøøøøøøøø^ø }| + {102:øøøøøøøøøøøøøøøøøøøø}| + {101:>>>}{102:øøøøøøøøø^ø }| aaaaaaaaaaaaaaaaaaaa| - {1:>>>}aaaaaaaaaa | + {101:>>>}aaaaaaaaaa | | ]]) feed_command('highlight NonText guibg=NONE') screen:expect([[ - {2:øøøøøøøøøøøøøøøøøøøø}| - {3:>>>}{2:øøøøøøøøø^ø }| + {102:øøøøøøøøøøøøøøøøøøøø}| + {103:>>>}{102:øøøøøøøøø^ø }| aaaaaaaaaaaaaaaaaaaa| - {4:>>>}aaaaaaaaaa | + {100:>>>}aaaaaaaaaa | | ]]) feed_command('set nocursorline') screen:expect([[ øøøøøøøøøøøøøøøøøøøø| - {4:>>>}øøøøøøøøø^ø | + {100:>>>}øøøøøøøøø^ø | aaaaaaaaaaaaaaaaaaaa| - {4:>>>}aaaaaaaaaa | + {100:>>>}aaaaaaaaaa | :set nocursorline | ]]) end) it("'cursorlineopt' screenline", function() local screen = Screen.new(20, 5) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.Black, background = Screen.colors.White }, - [2] = { foreground = Screen.colors.Yellow }, - [3] = { foreground = Screen.colors.Red, background = Screen.colors.Green }, - [4] = { foreground = Screen.colors.Green, background = Screen.colors.Red }, - [5] = { bold = true }, -- ModeMsg - }) + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.Yellow }, + [101] = { foreground = Screen.colors.Red, background = Screen.colors.WebGreen }, + [102] = { foreground = Screen.colors.Black, background = Screen.colors.Grey100 }, + [103] = { foreground = Screen.colors.WebGreen, background = Screen.colors.Red }, + } screen:attach() command('set wrap cursorline cursorlineopt=screenline') @@ -1029,107 +973,107 @@ describe('CursorLine and CursorLineNr highlights', function() -- CursorLine should not apply to 'showbreak' when 'cursorlineopt' contains "screenline" screen:expect([[ øøøøøøøøøøøøøøøøøøøø| - {2:>>>}øøøøøøøøøø | + {100:>>>}øøøøøøøøøø | aaaaaaaaaaaaaaaaaaaa| - {2:>>>}{1:aaaaaaaaa^a }| + {100:>>>}{102:aaaaaaaaa^a }| | ]]) feed('gk') screen:expect([[ øøøøøøøøøøøøøøøøøøøø| - {2:>>>}øøøøøøøøøø | - {1:aaaaaaaaaaaa^aaaaaaaa}| - {2:>>>}aaaaaaaaaa | + {100:>>>}øøøøøøøøøø | + {102:aaaaaaaaaaaa^aaaaaaaa}| + {100:>>>}aaaaaaaaaa | | ]]) feed('k') screen:expect([[ - {1:øøøøøøøøøøøø^øøøøøøøø}| - {2:>>>}øøøøøøøøøø | + {102:øøøøøøøøøøøø^øøøøøøøø}| + {100:>>>}øøøøøøøøøø | aaaaaaaaaaaaaaaaaaaa| - {2:>>>}aaaaaaaaaa | + {100:>>>}aaaaaaaaaa | | ]]) -- CursorLineNr should not apply to line number when 'cursorlineopt' does not contain "number" command('set relativenumber numberwidth=2') screen:expect([[ - {3:0 }{1:øøøøøøøøøøøø^øøøøøø}| - {3: }{2:>>>}øøøøøøøøøøøø | - {3:1 }aaaaaaaaaaaaaaaaaa| - {3: }{2:>>>}aaaaaaaaaaaa | + {101:0 }{102:øøøøøøøøøøøø^øøøøøø}| + {101: }{100:>>>}øøøøøøøøøøøø | + {101:1 }aaaaaaaaaaaaaaaaaa| + {101: }{100:>>>}aaaaaaaaaaaa | | ]]) -- CursorLineNr should apply to line number when 'cursorlineopt' contains "number" command('set cursorlineopt+=number') screen:expect([[ - {4:0 }{1:øøøøøøøøøøøø^øøøøøø}| - {3: }{2:>>>}øøøøøøøøøøøø | - {3:1 }aaaaaaaaaaaaaaaaaa| - {3: }{2:>>>}aaaaaaaaaaaa | + {103:0 }{102:øøøøøøøøøøøø^øøøøøø}| + {101: }{100:>>>}øøøøøøøøøøøø | + {101:1 }aaaaaaaaaaaaaaaaaa| + {101: }{100:>>>}aaaaaaaaaaaa | | ]]) feed('gj') screen:expect([[ - {4:0 }øøøøøøøøøøøøøøøøøø| - {3: }{2:>>>}{1:øøøøøøøøø^øøø }| - {3:1 }aaaaaaaaaaaaaaaaaa| - {3: }{2:>>>}aaaaaaaaaaaa | + {103:0 }øøøøøøøøøøøøøøøøøø| + {101: }{100:>>>}{102:øøøøøøøøø^øøø }| + {101:1 }aaaaaaaaaaaaaaaaaa| + {101: }{100:>>>}aaaaaaaaaaaa | | ]]) feed('gj') screen:expect([[ - {3:1 }øøøøøøøøøøøøøøøøøø| - {3: }{2:>>>}øøøøøøøøøøøø | - {4:0 }{1:aaaaaaaaaaaa^aaaaaa}| - {3: }{2:>>>}aaaaaaaaaaaa | + {101:1 }øøøøøøøøøøøøøøøøøø| + {101: }{100:>>>}øøøøøøøøøøøø | + {103:0 }{102:aaaaaaaaaaaa^aaaaaa}| + {101: }{100:>>>}aaaaaaaaaaaa | | ]]) feed('gj') screen:expect([[ - {3:1 }øøøøøøøøøøøøøøøøøø| - {3: }{2:>>>}øøøøøøøøøøøø | - {4:0 }aaaaaaaaaaaaaaaaaa| - {3: }{2:>>>}{1:aaaaaaaaa^aaa }| + {101:1 }øøøøøøøøøøøøøøøøøø| + {101: }{100:>>>}øøøøøøøøøøøø | + {103:0 }aaaaaaaaaaaaaaaaaa| + {101: }{100:>>>}{102:aaaaaaaaa^aaa }| | ]]) -- updated in Insert mode feed('I') screen:expect([[ - {3:1 }øøøøøøøøøøøøøøøøøø| - {3: }{2:>>>}øøøøøøøøøøøø | - {4:0 }{1:^aaaaaaaaaaaaaaaaaa}| - {3: }{2:>>>}aaaaaaaaaaaa | + {101:1 }øøøøøøøøøøøøøøøøøø| + {101: }{100:>>>}øøøøøøøøøøøø | + {103:0 }{102:^aaaaaaaaaaaaaaaaaa}| + {101: }{100:>>>}aaaaaaaaaaaa | {5:-- INSERT --} | ]]) feed('<Esc>gg') screen:expect([[ - {4:0 }{1:^øøøøøøøøøøøøøøøøøø}| - {3: }{2:>>>}øøøøøøøøøøøø | - {3:1 }aaaaaaaaaaaaaaaaaa| - {3: }{2:>>>}aaaaaaaaaaaa | + {103:0 }{102:^øøøøøøøøøøøøøøøøøø}| + {101: }{100:>>>}øøøøøøøøøøøø | + {101:1 }aaaaaaaaaaaaaaaaaa| + {101: }{100:>>>}aaaaaaaaaaaa | | ]]) command('inoremap <F2> <Cmd>call cursor(1, 1)<CR>') feed('A') screen:expect([[ - {4:0 }øøøøøøøøøøøøøøøøøø| - {3: }{2:>>>}{1:øøøøøøøøøøøø^ }| - {3:1 }aaaaaaaaaaaaaaaaaa| - {3: }{2:>>>}aaaaaaaaaaaa | + {103:0 }øøøøøøøøøøøøøøøøøø| + {101: }{100:>>>}{102:øøøøøøøøøøøø^ }| + {101:1 }aaaaaaaaaaaaaaaaaa| + {101: }{100:>>>}aaaaaaaaaaaa | {5:-- INSERT --} | ]]) feed('<F2>') screen:expect([[ - {4:0 }{1:^øøøøøøøøøøøøøøøøøø}| - {3: }{2:>>>}øøøøøøøøøøøø | - {3:1 }aaaaaaaaaaaaaaaaaa| - {3: }{2:>>>}aaaaaaaaaaaa | + {103:0 }{102:^øøøøøøøøøøøøøøøøøø}| + {101: }{100:>>>}øøøøøøøøøøøø | + {101:1 }aaaaaaaaaaaaaaaaaa| + {101: }{100:>>>}aaaaaaaaaaaa | {5:-- INSERT --} | ]]) end) @@ -1137,22 +1081,16 @@ describe('CursorLine and CursorLineNr highlights', function() -- oldtest: Test_cursorline_after_yank() it('always updated. vim-patch:8.1.0849', function() local screen = Screen.new(50, 5) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.SlateBlue }, - [2] = { bold = true, foreground = Screen.colors.Brown }, - [3] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, - [4] = { foreground = Screen.colors.SlateBlue, background = Screen.colors.Gray90 }, - [5] = { background = Screen.colors.Gray90 }, - [6] = { bold = true, foreground = Screen.colors.Blue1 }, - [7] = { background = Screen.colors.LightRed }, - [8] = { foreground = Screen.colors.Brown }, - }) + screen:add_extra_attr_ids { + [100] = { background = Screen.colors.LightRed }, + [101] = { foreground = Screen.colors.SlateBlue, background = Screen.colors.Grey90 }, + } screen:attach() command('set cursorline relativenumber') command('call setline(1, ["","1","2","3",""])') feed('Gy3k') screen:expect([[ - {2: 0 }{5:^1 }| + {15: 0 }{21:^1 }| {8: 1 }2 | {8: 2 }3 | {8: 3 } | @@ -1162,7 +1100,7 @@ describe('CursorLine and CursorLineNr highlights', function() screen:expect([[ {8: 2 }1 | {8: 1 }2 | - {2: 0 }{5:^3 }| + {15: 0 }{21:^3 }| {8: 1 } | 4 lines yanked | ]]) @@ -1171,37 +1109,25 @@ describe('CursorLine and CursorLineNr highlights', function() -- oldtest: Test_cursorline_with_visualmode() it('with visual area. vim-patch:8.1.1001', function() local screen = Screen.new(50, 5) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.SlateBlue }, - [2] = { bold = true, foreground = Screen.colors.Brown }, - [3] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, - [4] = { foreground = Screen.colors.SlateBlue, background = Screen.colors.Gray90 }, - [5] = { background = Screen.colors.Gray90 }, - [6] = { bold = true, foreground = Screen.colors.Blue1 }, - [7] = { background = Screen.colors.LightRed }, - [8] = { foreground = Screen.colors.Brown }, - [9] = { foreground = Screen.colors.Black, background = Screen.colors.LightGrey }, - [10] = { bold = true }, - }) + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.SlateBlue, background = Screen.colors.Grey90 }, + [101] = { background = Screen.colors.LightRed }, + } screen:attach() command('set cursorline') command('call setline(1, repeat(["abc"], 50))') feed('V<C-f>zbkkjk') screen:expect([[ - {9:abc} | - ^a{9:bc} | + {17:abc} | + ^a{17:bc} | abc |*2 - {10:-- VISUAL LINE --} | + {5:-- VISUAL LINE --} | ]]) end) -- oldtest: Test_cursorline_callback() it('is updated if cursor is moved up from timer vim-patch:8.2.4591', function() local screen = Screen.new(50, 8) - screen:set_default_attr_ids({ - [1] = { background = Screen.colors.Gray90 }, -- CursorLine - [2] = { bold = true, foreground = Screen.colors.Blue1 }, -- NonText - }) screen:attach() exec([[ call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd']) @@ -1219,8 +1145,8 @@ describe('CursorLine and CursorLineNr highlights', function() aaaaa | bbbbb | ccccc | - {1:^ddddd }| - {2:~ }|*3 + {21:^ddddd }| + {1:~ }|*3 | ]], timeout = 100, @@ -1228,10 +1154,10 @@ describe('CursorLine and CursorLineNr highlights', function() screen:expect({ grid = [[ aaaaa | - {1:^bbbbb }| + {21:^bbbbb }| ccccc | ddddd | - {2:~ }|*3 + {1:~ }|*3 | ]], }) @@ -1239,17 +1165,10 @@ describe('CursorLine and CursorLineNr highlights', function() it('with split windows in diff mode', function() local screen = Screen.new(50, 12) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray }, - [2] = { bold = true, background = Screen.colors.Red }, - [3] = { background = Screen.colors.LightMagenta }, - [4] = { reverse = true }, - [5] = { background = Screen.colors.LightBlue }, - [6] = { background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1 }, - [7] = { background = Screen.colors.Red, foreground = Screen.colors.White }, - [8] = { bold = true, foreground = Screen.colors.Blue1 }, - [9] = { bold = true, reverse = true }, - }) + screen:add_extra_attr_ids { + [100] = { background = Screen.colors.Plum1, underline = true }, + [101] = { background = Screen.colors.Red1, bold = true, underline = true }, + } screen:attach() command('hi CursorLine ctermbg=red ctermfg=white guibg=red guifg=white') @@ -1264,26 +1183,26 @@ describe('CursorLine and CursorLineNr highlights', function() feed('<esc>gg') command('windo diffthis') screen:expect([[ - {1: }{7:line 1 some text }│{1: }{7:^line 1 some text }| - {1: }{3:line 2 mo}{2:Re text!}{3: }│{1: }{3:line 2 mo}{2:re text}{3: }| - {1: }{5:extra line! }│{1: }{6:----------------------}| - {1: }extra line! │{1: }extra line! |*2 - {1: }last line ... │{1: }last line ... | - {1: } │{1: } | - {8:~ }│{8:~ }|*3 - {4:[No Name] [+] }{9:[No Name] [+] }| + {7: }{9:line 1 some text }│{7: }{9:^line 1 some text }| + {7: }{4:line 2 mo}{27:Re text!}{4: }│{7: }{4:line 2 mo}{27:re text}{4: }| + {7: }{22:extra line! }│{7: }{23:----------------------}| + {7: }extra line! │{7: }extra line! |*2 + {7: }last line ... │{7: }last line ... | + {7: } │{7: } | + {1:~ }│{1:~ }|*3 + {2:[No Name] [+] }{3:[No Name] [+] }| | ]]) feed('jjjjj') screen:expect([[ - {1: }line 1 some text │{1: }line 1 some text | - {1: }{3:line 2 mo}{2:Re text!}{3: }│{1: }{3:line 2 mo}{2:re text}{3: }| - {1: }{5:extra line! }│{1: }{6:----------------------}| - {1: }extra line! │{1: }extra line! |*2 - {1: }last line ... │{1: }last line ... | - {1: }{7: }│{1: }{7:^ }| - {8:~ }│{8:~ }|*3 - {4:[No Name] [+] }{9:[No Name] [+] }| + {7: }line 1 some text │{7: }line 1 some text | + {7: }{4:line 2 mo}{27:Re text!}{4: }│{7: }{4:line 2 mo}{27:re text}{4: }| + {7: }{22:extra line! }│{7: }{23:----------------------}| + {7: }extra line! │{7: }extra line! |*2 + {7: }last line ... │{7: }last line ... | + {7: }{9: }│{7: }{9:^ }| + {1:~ }│{1:~ }|*3 + {2:[No Name] [+] }{3:[No Name] [+] }| | ]]) @@ -1291,53 +1210,25 @@ describe('CursorLine and CursorLineNr highlights', function() -- Rendered as underline in a diff-line. #9028 command('hi CursorLine ctermbg=red ctermfg=NONE guibg=red guifg=NONE') feed('kkkk') - screen:expect( - [[ - {1: }line 1 some text │{1: }line 1 some text | - {1: }{11:line 2 mo}{12:Re text!}{11: }│{1: }{11:^line 2 mo}{12:re text}{11: }| - {1: }{5:extra line! }│{1: }{6:----------------------}| - {1: }extra line! │{1: }extra line! |*2 - {1: }last line ... │{1: }last line ... | - {1: } │{1: } | - {8:~ }│{8:~ }|*3 - {4:[No Name] [+] }{9:[No Name] [+] }| + screen:expect([[ + {7: }line 1 some text │{7: }line 1 some text | + {7: }{100:line 2 mo}{101:Re text!}{100: }│{7: }{100:^line 2 mo}{101:re text}{100: }| + {7: }{22:extra line! }│{7: }{23:----------------------}| + {7: }extra line! │{7: }extra line! |*2 + {7: }last line ... │{7: }last line ... | + {7: } │{7: } | + {1:~ }│{1:~ }|*3 + {2:[No Name] [+] }{3:[No Name] [+] }| | - ]], - { - [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray }, - [2] = { bold = true, background = Screen.colors.Red }, - [3] = { background = Screen.colors.LightMagenta }, - [4] = { reverse = true }, - [5] = { background = Screen.colors.LightBlue }, - [6] = { - background = Screen.colors.LightCyan1, - bold = true, - foreground = Screen.colors.Blue1, - }, - [7] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, - [8] = { bold = true, foreground = Screen.colors.Blue1 }, - [9] = { bold = true, reverse = true }, - [10] = { bold = true }, - [11] = { underline = true, background = Screen.colors.LightMagenta }, - [12] = { bold = true, underline = true, background = Screen.colors.Red }, - } - ) + ]]) end) -- oldtest: Test_diff_with_cursorline_number() it('CursorLineNr shows correctly just below filler lines', function() local screen = Screen.new(50, 12) - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray }, - [2] = { background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1 }, - [3] = { reverse = true }, - [4] = { background = Screen.colors.LightBlue }, - [5] = { background = Screen.colors.Red, foreground = Screen.colors.White }, - [6] = { background = Screen.colors.White, bold = true, foreground = Screen.colors.Black }, - [7] = { bold = true, foreground = Screen.colors.Blue1 }, - [8] = { bold = true, reverse = true }, - [9] = { foreground = Screen.colors.Brown }, - }) + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.Black, bold = true, background = Screen.colors.Grey100 }, + } screen:attach() command('hi CursorLine guibg=red guifg=white') @@ -1350,22 +1241,22 @@ describe('CursorLine and CursorLineNr highlights', function() command('windo diffthis') command('1wincmd w') screen:expect([[ - {1: }{9: }{2:-------------------}│{1: }{9: 1 }{4:baz }| - {1: }{6: 1 }{5:^foo }│{1: }{6: 2 }{5:foo }| - {1: }{9: 2 }foo │{1: }{9: 3 }foo | - {1: }{9: 3 }bar │{1: }{9: 4 }bar | - {7:~ }│{7:~ }|*6 - {8:[No Name] [+] }{3:[No Name] [+] }| + {7: }{8: }{23:-------------------}│{7: }{8: 1 }{22:baz }| + {7: }{100: 1 }{9:^foo }│{7: }{100: 2 }{9:foo }| + {7: }{8: 2 }foo │{7: }{8: 3 }foo | + {7: }{8: 3 }bar │{7: }{8: 4 }bar | + {1:~ }│{1:~ }|*6 + {3:[No Name] [+] }{2:[No Name] [+] }| | ]]) command('set cursorlineopt=number') screen:expect([[ - {1: }{9: }{2:-------------------}│{1: }{9: 1 }{4:baz }| - {1: }{6: 1 }^foo │{1: }{6: 2 }{5:foo }| - {1: }{9: 2 }foo │{1: }{9: 3 }foo | - {1: }{9: 3 }bar │{1: }{9: 4 }bar | - {7:~ }│{7:~ }|*6 - {8:[No Name] [+] }{3:[No Name] [+] }| + {7: }{8: }{23:-------------------}│{7: }{8: 1 }{22:baz }| + {7: }{100: 1 }^foo │{7: }{100: 2 }{9:foo }| + {7: }{8: 2 }foo │{7: }{8: 3 }foo | + {7: }{8: 3 }bar │{7: }{8: 4 }bar | + {1:~ }│{1:~ }|*6 + {3:[No Name] [+] }{2:[No Name] [+] }| | ]]) end) @@ -1376,11 +1267,9 @@ describe('CursorColumn highlight', function() before_each(function() clear() screen = Screen.new(50, 8) - screen:set_default_attr_ids({ - [1] = { background = Screen.colors.Gray90 }, -- CursorColumn - [2] = { bold = true, foreground = Screen.colors.Blue1 }, -- NonText - [3] = { bold = true }, -- ModeMsg - }) + screen:add_extra_attr_ids { + [100] = { background = Screen.colors.Blue1 }, + } screen:attach() end) @@ -1391,31 +1280,31 @@ describe('CursorColumn highlight', function() call cursor(2, 2) ]]) screen:expect([[ - 1234567{1:8}9 | + 1234567{21:8}9 | a ^ b | - {2:~ }|*5 + {1:~ }|*5 | ]]) feed('i') screen:expect([[ - 1{1:2}3456789 | + 1{21:2}3456789 | a^ b | - {2:~ }|*5 - {3:-- INSERT --} | + {1:~ }|*5 + {5:-- INSERT --} | ]]) feed('<C-O>') screen:expect([[ - 1234567{1:8}9 | + 1234567{21:8}9 | a ^ b | - {2:~ }|*5 - {3:-- (insert) --} | + {1:~ }|*5 + {5:-- (insert) --} | ]]) feed('i') screen:expect([[ - 1{1:2}3456789 | + 1{21:2}3456789 | a^ b | - {2:~ }|*5 - {3:-- INSERT --} | + {1:~ }|*5 + {5:-- INSERT --} | ]]) end) @@ -1434,11 +1323,11 @@ describe('CursorColumn highlight', function() ]]) screen:expect({ grid = [[ - aaaa{1:a} | - bbbb{1:b} | - cccc{1:c} | + aaaa{21:a} | + bbbb{21:b} | + cccc{21:c} | dddd^d | - {2:~ }|*3 + {1:~ }|*3 | ]], timeout = 100, @@ -1446,14 +1335,55 @@ describe('CursorColumn highlight', function() screen:expect({ grid = [[ ^aaaaa | - {1:b}bbbb | - {1:c}cccc | - {1:d}dddd | - {2:~ }|*3 + {21:b}bbbb | + {21:c}cccc | + {21:d}dddd | + {1:~ }|*3 | ]], }) end) + + it('is not shown on current line with virtualedit', function() + exec([[ + hi! CursorColumn guibg=Red + hi! CursorLine guibg=Blue + set virtualedit=all cursorline cursorcolumn + ]]) + insert('line 1\nline 2\nline 3') + feed('k') + screen:expect([[ + line {30:1} | + {100:line ^2 }| + line {30:3} | + {1:~ }|*4 + | + ]]) + feed('l') + screen:expect([[ + line 1{30: } | + {100:line 2^ }| + line 3{30: } | + {1:~ }|*4 + | + ]]) + feed('l') + screen:expect([[ + line 1 {30: } | + {100:line 2 ^ }| + line 3 {30: } | + {1:~ }|*4 + | + ]]) + feed('l') + screen:expect([[ + line 1 {30: } | + {100:line 2 ^ }| + line 3 {30: } | + {1:~ }|*4 + | + ]]) + end) end) describe('ColorColumn highlight', function() @@ -1462,17 +1392,11 @@ describe('ColorColumn highlight', function() before_each(function() clear() screen = Screen.new(40, 15) - screen:set_default_attr_ids({ - [1] = { background = Screen.colors.LightRed }, -- ColorColumn - [2] = { background = Screen.colors.Grey90 }, -- CursorLine - [3] = { foreground = Screen.colors.Brown }, -- LineNr - [4] = { foreground = Screen.colors.Brown, bold = true }, -- CursorLineNr - [5] = { foreground = Screen.colors.Blue, bold = true }, -- NonText - [6] = { foreground = Screen.colors.Blue, background = Screen.colors.LightRed, bold = true }, - [7] = { reverse = true, bold = true }, -- StatusLine - [8] = { reverse = true }, -- StatusLineNC - [9] = { background = Screen.colors.Grey90, foreground = Screen.colors.Red }, - }) + screen:add_extra_attr_ids { + [100] = { foreground = Screen.colors.Red, background = Screen.colors.Grey90 }, + [101] = { background = Screen.colors.LightRed }, + [102] = { foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.LightRed }, + } screen:attach() end) @@ -1490,16 +1414,16 @@ describe('ColorColumn highlight', function() buf X ]]) screen:expect([[ - {4: 1 }11{1:1}11111{1:1}1 | - {3: 2 }22{1:2}22222{1:2}22 | - {3: 3 }33{1:3}33333{1:3}3 | - {5:~ }|*3 - {8:X }| - {4: 1 }^11{1:1}11111{1:1}1 | - {3: 2 }22{1:2}22222{1:2}22 | - {3: 3 }33{1:3}33333{1:3}3 | - {5:~ }|*3 - {7:X }| + {15: 1 }11{101:1}11111{101:1}1 | + {8: 2 }22{101:2}22222{101:2}22 | + {8: 3 }33{101:3}33333{101:3}3 | + {1:~ }|*3 + {2:X }| + {15: 1 }^11{101:1}11111{101:1}1 | + {8: 2 }22{101:2}22222{101:2}22 | + {8: 3 }33{101:3}33333{101:3}3 | + {1:~ }|*3 + {3:X }| | ]]) end) @@ -1511,9 +1435,9 @@ describe('ColorColumn highlight', function() set co=40 linebreak bri briopt=shift:2 cc=40,41,43 ]]) screen:expect([[ - ^The quick brown fox jumped over the {1: }| - {1: } {1:l}azy dogs | - {5:~ }|*12 + ^The quick brown fox jumped over the {101: }| + {101: } {101:l}azy dogs | + {1:~ }|*12 | ]]) end) @@ -1525,9 +1449,9 @@ describe('ColorColumn highlight', function() set co=40 showbreak=+++>\\ cc=40,41,43 ]]) screen:expect([[ - ^The quick brown fox jumped over the laz{1:y}| - {6:+}{5:+}{6:+}{5:>\} dogs | - {5:~ }|*12 + ^The quick brown fox jumped over the laz{101:y}| + {102:+}{1:+}{102:+}{1:>\} dogs | + {1:~ }|*12 | ]]) end) @@ -1536,17 +1460,17 @@ describe('ColorColumn highlight', function() screen:try_resize(40, 2) command('set colorcolumn=30 cursorline') screen:expect([[ - {2:^ }{1: }{2: }| + {21:^ }{101: }{21: }| | ]]) command('hi clear ColorColumn') screen:expect([[ - {2:^ }| + {21:^ }| | ]]) command('hi ColorColumn guifg=Red') screen:expect([[ - {2:^ }{9: }{2: }| + {21:^ }{100: }{21: }| | ]]) end) @@ -2274,7 +2198,7 @@ describe("'winhighlight' highlight", function() ]], } - helpers.assert_alive() + n.assert_alive() end) it('can redraw statusline on cursor movement', function() @@ -2494,3 +2418,115 @@ describe('highlight namespaces', function() } end) end) + +describe('synIDattr()', function() + local screen + before_each(function() + clear() + screen = Screen.new(50, 7) + command('highlight Normal ctermfg=252 guifg=#ff0000 guibg=Black') + -- Salmon #fa8072 Maroon #800000 + command( + 'highlight Keyword ctermfg=79 guifg=Salmon guisp=Maroon cterm=strikethrough gui=strikethrough' + ) + end) + + it('returns cterm-color if RGB-capable UI is _not_ attached', function() + eq('252', eval('synIDattr(hlID("Normal"), "fg")')) + eq('252', eval('synIDattr(hlID("Normal"), "fg#")')) + eq('', eval('synIDattr(hlID("Normal"), "bg")')) + eq('', eval('synIDattr(hlID("Normal"), "bg#")')) + eq('79', eval('synIDattr(hlID("Keyword"), "fg")')) + eq('79', eval('synIDattr(hlID("Keyword"), "fg#")')) + eq('', eval('synIDattr(hlID("Keyword"), "sp")')) + eq('', eval('synIDattr(hlID("Keyword"), "sp#")')) + end) + + it('returns gui-color if "gui" arg is passed', function() + eq('Black', eval('synIDattr(hlID("Normal"), "bg", "gui")')) + eq('Maroon', eval('synIDattr(hlID("Keyword"), "sp", "gui")')) + end) + + it('returns gui-color if RGB-capable UI is attached', function() + screen:attach({ rgb = true }) + eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg")')) + eq('Black', eval('synIDattr(hlID("Normal"), "bg")')) + eq('Salmon', eval('synIDattr(hlID("Keyword"), "fg")')) + eq('Maroon', eval('synIDattr(hlID("Keyword"), "sp")')) + end) + + it('returns #RRGGBB value for fg#/bg#/sp#', function() + screen:attach({ rgb = true }) + eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg#")')) + eq('#000000', eval('synIDattr(hlID("Normal"), "bg#")')) + eq('#fa8072', eval('synIDattr(hlID("Keyword"), "fg#")')) + eq('#800000', eval('synIDattr(hlID("Keyword"), "sp#")')) + end) + + it('returns color number if non-GUI', function() + screen:attach({ rgb = false }) + eq('252', eval('synIDattr(hlID("Normal"), "fg")')) + eq('79', eval('synIDattr(hlID("Keyword"), "fg")')) + end) + + it('returns "1" if group has given highlight attribute', function() + local hl_attrs = { + 'underline', + 'undercurl', + 'underdouble', + 'underdotted', + 'underdashed', + 'strikethrough', + } + for _, hl_attr in ipairs(hl_attrs) do + local context = 'using ' .. hl_attr .. ' attr' + command('highlight Keyword cterm=' .. hl_attr .. ' gui=' .. hl_attr) + eq('', eval('synIDattr(hlID("Normal"), "' .. hl_attr .. '")'), context) + eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '")'), context) + eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '", "gui")'), context) + end + end) +end) + +describe('fg/bg special colors', function() + local screen + before_each(function() + clear() + screen = Screen.new(50, 7) + command('highlight Normal ctermfg=145 ctermbg=16 guifg=#ff0000 guibg=Black') + command('highlight Visual ctermfg=bg ctermbg=fg guifg=bg guibg=fg guisp=bg') + end) + + it('resolve to "Normal" values', function() + eq(eval('synIDattr(hlID("Normal"), "bg")'), eval('synIDattr(hlID("Visual"), "fg")')) + eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "fg#")')) + eq(eval('synIDattr(hlID("Normal"), "fg")'), eval('synIDattr(hlID("Visual"), "bg")')) + eq(eval('synIDattr(hlID("Normal"), "fg#")'), eval('synIDattr(hlID("Visual"), "bg#")')) + eq('bg', eval('synIDattr(hlID("Visual"), "fg", "gui")')) + eq('bg', eval('synIDattr(hlID("Visual"), "fg#", "gui")')) + eq('fg', eval('synIDattr(hlID("Visual"), "bg", "gui")')) + eq('fg', eval('synIDattr(hlID("Visual"), "bg#", "gui")')) + eq('bg', eval('synIDattr(hlID("Visual"), "sp", "gui")')) + eq('bg', eval('synIDattr(hlID("Visual"), "sp#", "gui")')) + end) + + it('resolve to "Normal" values in RGB-capable UI', function() + screen:attach({ rgb = true }) + eq('bg', eval('synIDattr(hlID("Visual"), "fg")')) + eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "fg#")')) + eq('fg', eval('synIDattr(hlID("Visual"), "bg")')) + eq(eval('synIDattr(hlID("Normal"), "fg#")'), eval('synIDattr(hlID("Visual"), "bg#")')) + eq('bg', eval('synIDattr(hlID("Visual"), "sp")')) + eq(eval('synIDattr(hlID("Normal"), "bg#")'), eval('synIDattr(hlID("Visual"), "sp#")')) + end) + + it('resolve after the "Normal" group is modified', function() + screen:attach({ rgb = true }) + local new_guibg = '#282c34' + local new_guifg = '#abb2bf' + command('highlight Normal guifg=' .. new_guifg .. ' guibg=' .. new_guibg) + eq(new_guibg, eval('synIDattr(hlID("Visual"), "fg#")')) + eq(new_guifg, eval('synIDattr(hlID("Visual"), "bg#")')) + eq(new_guibg, eval('synIDattr(hlID("Visual"), "sp#")')) + end) +end) diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua index 8b36ad5431..8d14c9f73d 100644 --- a/test/functional/ui/hlstate_spec.lua +++ b/test/functional/ui/hlstate_spec.lua @@ -1,13 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local tt = require('test.functional.terminal.testutil') -local clear, insert = helpers.clear, helpers.insert -local command = helpers.command -local api = helpers.api -local testprg = helpers.testprg -local thelpers = require('test.functional.terminal.helpers') -local skip = helpers.skip -local is_os = helpers.is_os +local clear, insert = n.clear, n.insert +local command = n.command +local api = n.api +local testprg = n.testprg +local skip = t.skip +local is_os = t.is_os describe('ext_hlstate detailed highlights', function() local screen @@ -236,11 +237,11 @@ describe('ext_hlstate detailed highlights', function() {7: }| ]]) - thelpers.feed_data('x ') - thelpers.set_fg(45) - thelpers.feed_data('y ') - thelpers.set_bold() - thelpers.feed_data('z\n') + tt.feed_data('x ') + tt.set_fg(45) + tt.feed_data('y ') + tt.set_bold() + tt.feed_data('z\n') -- TODO(bfredl): check if this distinction makes sense if is_os('win') then screen:expect([[ @@ -260,8 +261,8 @@ describe('ext_hlstate detailed highlights', function() ]]) end - thelpers.feed_termcode('[A') - thelpers.feed_termcode('[2C') + tt.feed_termcode('[A') + tt.feed_termcode('[2C') if is_os('win') then screen:expect([[ ^tty ready | @@ -381,7 +382,7 @@ describe('ext_hlstate detailed highlights', function() }, } - helpers.feed('3ggV2jd') + n.feed('3ggV2jd') --screen:redraw_debug() screen:expect { grid = [[ @@ -478,7 +479,7 @@ describe('ext_hlstate detailed highlights', function() }, } - helpers.feed('3ggV2jd') + n.feed('3ggV2jd') --screen:redraw_debug() screen:expect { grid = [[ @@ -512,7 +513,7 @@ describe('ext_hlstate detailed highlights', function() end insert('last line') - helpers.feed('gg') + n.feed('gg') screen:expect { grid = [[ ^first line | @@ -555,7 +556,7 @@ describe('ext_hlstate detailed highlights', function() }, } - helpers.feed(string.format('3ggV%ijd', num_lines - 2)) + n.feed(string.format('3ggV%ijd', num_lines - 2)) --screen:redraw_debug(nil, nil, 100000) local expected_ids = {} diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 29c8c43ca1..c11e009fef 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -1,22 +1,24 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local expect = helpers.expect -local feed = helpers.feed -local insert = helpers.insert -local fn = helpers.fn -local api = helpers.api -local neq = helpers.neq -local ok = helpers.ok -local retry = helpers.retry -local source = helpers.source -local poke_eventloop = helpers.poke_eventloop + +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local expect = n.expect +local feed = n.feed +local insert = n.insert +local fn = n.fn +local api = n.api +local neq = t.neq +local ok = t.ok +local retry = t.retry +local source = n.source +local poke_eventloop = n.poke_eventloop local sleep = vim.uv.sleep -local testprg = helpers.testprg -local assert_alive = helpers.assert_alive +local testprg = n.testprg +local assert_alive = n.assert_alive local default_text = [[ Inc substitution on @@ -65,26 +67,12 @@ local function common_setup(screen, inccommand, text) command('set nohlsearch') command('hi Substitute guifg=red guibg=yellow') screen:attach() - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.Fuchsia }, - [2] = { foreground = Screen.colors.Brown, bold = true }, - [3] = { foreground = Screen.colors.SlateBlue }, - [4] = { bold = true, foreground = Screen.colors.SlateBlue }, - [5] = { foreground = Screen.colors.DarkCyan }, - [6] = { bold = true }, - [7] = { underline = true, bold = true, foreground = Screen.colors.SlateBlue }, - [8] = { foreground = Screen.colors.Slateblue, underline = true }, - [9] = { background = Screen.colors.Yellow }, - [10] = { reverse = true }, - [11] = { reverse = true, bold = true }, - [12] = { foreground = Screen.colors.Red, background = Screen.colors.Yellow }, - [13] = { bold = true, foreground = Screen.colors.SeaGreen }, - [14] = { foreground = Screen.colors.White, background = Screen.colors.Red }, - [15] = { bold = true, foreground = Screen.colors.Blue }, - [16] = { background = Screen.colors.Grey90 }, -- cursorline - [17] = { foreground = Screen.colors.Blue1 }, + + screen:add_extra_attr_ids { + [100] = { underline = true }, + [101] = { underline = true, foreground = Screen.colors.SlateBlue, bold = true }, vis = { background = Screen.colors.LightGrey }, - }) + } end command('set inccommand=' .. (inccommand or '')) @@ -142,13 +130,13 @@ describe(":substitute, 'inccommand' preserves", function() screen:expect([[ BAC | - {15:~ }|*3 - {11: }| + {1:~ }|*3 + {3: }| :ls | 1 %a + "[No Name]" | line 1 | - {13:Press ENTER or type command to}| - {13: continue}^ | + {6:Press ENTER or type command to}| + {6: continue}^ | ]]) end) @@ -204,8 +192,8 @@ describe(":substitute, 'inccommand' preserves", function() feed(':%s/as/glork/') poke_eventloop() feed('<enter>') - eq(api.nvim_get_option_value('undolevels', { scope = 'global' }), 139) - eq(api.nvim_get_option_value('undolevels', { buf = 0 }), 34) + eq(139, api.nvim_get_option_value('undolevels', { scope = 'global' })) + eq(34, api.nvim_get_option_value('undolevels', { buf = 0 })) end) end @@ -577,7 +565,7 @@ describe(":substitute, 'inccommand' preserves undo", function() Inc substitution on | two lines | ^ | - {15:~ }|*6 + {1:~ }|*6 | ]]) command('set undolevels=1') @@ -613,7 +601,7 @@ describe(":substitute, 'inccommand' preserves undo", function() Inc substitution on | ^MOo lines | | - {15:~ }|*6 + {1:~ }|*6 Already ...t change | ]]) else @@ -621,7 +609,7 @@ describe(":substitute, 'inccommand' preserves undo", function() Inc substitution on | ^MOo lines | | - {15:~ }|*6 + {1:~ }|*6 Already ...t change | ]]) end @@ -655,7 +643,7 @@ describe(":substitute, 'inccommand' preserves undo", function() Inc substitution on | two line^s | | - {15:~ }|*6 + {1:~ }|*6 Already ...t change | ]]) else @@ -663,7 +651,7 @@ describe(":substitute, 'inccommand' preserves undo", function() Inc substitution on | two line^s | | - {15:~ }|*6 + {1:~ }|*6 Already ...t change | ]]) end @@ -693,7 +681,7 @@ describe(":substitute, 'inccommand' preserves undo", function() Inc substitution on | ^MOo lines | | - {15:~ }|*6 + {1:~ }|*6 Already ...t change | ]]) else @@ -701,7 +689,7 @@ describe(":substitute, 'inccommand' preserves undo", function() Inc substitution on | ^MOo lines | | - {15:~ }|*6 + {1:~ }|*6 Already ...t change | ]]) end @@ -725,7 +713,7 @@ describe(":substitute, 'inccommand' preserves undo", function() Inc substitution on | ^MOo lines | | - {15:~ }|*6 + {1:~ }|*6 Already ...t change | ]]) else @@ -733,7 +721,7 @@ describe(":substitute, 'inccommand' preserves undo", function() Inc substitution on | ^MOo lines | | - {15:~ }|*6 + {1:~ }|*6 Already ...t change | ]]) end @@ -754,7 +742,7 @@ describe(":substitute, 'inccommand' preserves undo", function() ^LInc substitution on| two lines | | - {15:~ }|*6 + {1:~ }|*6 Already ...t change | ]]) end @@ -775,15 +763,15 @@ describe(':substitute, inccommand=split', function() feed(':%s/tw') screen:expect([[ Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | | - {11:[No Name] }| - |2| {12:tw}o lines | - |4| {12:tw}o lines | - {15:~ }|*5 - {10:[Preview] }| + {3:[No Name] }| + |2| {20:tw}o lines | + |4| {20:tw}o lines | + {1:~ }|*5 + {2:[Preview] }| :%s/tw^ | ]]) feed([[<C-\><C-N>]]) -- Cancel the :substitute command. @@ -793,13 +781,13 @@ describe(':substitute, inccommand=split', function() it('shows preview when cmd modifiers are present', function() -- one modifier feed(':keeppatterns %s/tw/to') - screen:expect { any = [[{12:to}o lines]] } + screen:expect { any = [[{20:to}o lines]] } feed('<Esc>') screen:expect { any = [[two lines]] } -- multiple modifiers feed(':keeppatterns silent %s/tw/to') - screen:expect { any = [[{12:to}o lines]] } + screen:expect { any = [[{20:to}o lines]] } feed('<Esc>') screen:expect { any = [[two lines]] } @@ -811,14 +799,14 @@ describe(':substitute, inccommand=split', function() Inc substitution on | two lines | | - {15:~ }|*9 + {1:~ }|*9 :silent tabedit %s/tw/to^ | ]]) feed('<Esc>') -- leading colons feed(':::%s/tw/to') - screen:expect { any = [[{12:to}o lines]] } + screen:expect { any = [[{20:to}o lines]] } feed('<Esc>') screen:expect { any = [[two lines]] } end) @@ -828,15 +816,15 @@ describe(':substitute, inccommand=split', function() feed(':topleft %s/tw/to') screen:expect([[ Inc substitution on | - {12:to}o lines | + {20:to}o lines | Inc substitution on | - {12:to}o lines | + {20:to}o lines | | - {11:[No Name] [+] }| - |2| {12:to}o lines | - |4| {12:to}o lines | - {15:~ }|*5 - {10:[Preview] }| + {3:[No Name] [+] }| + |2| {20:to}o lines | + |4| {20:to}o lines | + {1:~ }|*5 + {2:[Preview] }| :topleft %s/tw/to^ | ]]) feed('<Esc>') @@ -846,15 +834,15 @@ describe(':substitute, inccommand=split', function() feed(':topleft vert %s/tw/to') screen:expect([[ Inc substitution on | - {12:to}o lines | + {20:to}o lines | Inc substitution on | - {12:to}o lines | + {20:to}o lines | | - {11:[No Name] [+] }| - |2| {12:to}o lines | - |4| {12:to}o lines | - {15:~ }|*5 - {10:[Preview] }| + {3:[No Name] [+] }| + |2| {20:to}o lines | + |4| {20:to}o lines | + {1:~ }|*5 + {2:[Preview] }| :topleft vert %s/tw/to^ | ]]) feed('<Esc>') @@ -865,15 +853,15 @@ describe(':substitute, inccommand=split', function() feed(':%s/tw') screen:expect([[ Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | | - {11:[No Name] [+] }| - |2| {12:tw}o lines | - |4| {12:tw}o lines | - {15:~ }|*5 - {10:[Preview] }| + {3:[No Name] [+] }| + |2| {20:tw}o lines | + |4| {20:tw}o lines | + {1:~ }|*5 + {2:[Preview] }| :%s/tw^ | ]]) end) @@ -886,26 +874,26 @@ describe(':substitute, inccommand=split', function() Inc substitution on | o lines | | - {11:[No Name] [+] }| + {3:[No Name] [+] }| |2| o lines | |4| o lines | - {15:~ }|*5 - {10:[Preview] }| + {1:~ }|*5 + {2:[Preview] }| :%s/tw/^ | ]]) feed('x') screen:expect([[ Inc substitution on | - {12:x}o lines | + {20:x}o lines | Inc substitution on | - {12:x}o lines | + {20:x}o lines | | - {11:[No Name] [+] }| - |2| {12:x}o lines | - |4| {12:x}o lines | - {15:~ }|*5 - {10:[Preview] }| + {3:[No Name] [+] }| + |2| {20:x}o lines | + |4| {20:x}o lines | + {1:~ }|*5 + {2:[Preview] }| :%s/tw/x^ | ]]) @@ -916,11 +904,11 @@ describe(':substitute, inccommand=split', function() Inc substitution on | o lines | | - {11:[No Name] [+] }| + {3:[No Name] [+] }| |2| o lines | |4| o lines | - {15:~ }|*5 - {10:[Preview] }| + {1:~ }|*5 + {2:[Preview] }| :%s/tw/^ | ]]) end) @@ -929,15 +917,15 @@ describe(':substitute, inccommand=split', function() feed(':%s/tw/XX') screen:expect([[ Inc substitution on | - {12:XX}o lines | + {20:XX}o lines | Inc substitution on | - {12:XX}o lines | + {20:XX}o lines | | - {11:[No Name] [+] }| - |2| {12:XX}o lines | - |4| {12:XX}o lines | - {15:~ }|*5 - {10:[Preview] }| + {3:[No Name] [+] }| + |2| {20:XX}o lines | + |4| {20:XX}o lines | + {1:~ }|*5 + {2:[Preview] }| :%s/tw/XX^ | ]]) end) @@ -947,11 +935,11 @@ describe(':substitute, inccommand=split', function() feed(':s/tw') screen:expect([[ Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | Inc substitution on | two lines | | - {15:~ }|*9 + {1:~ }|*9 :s/tw^ | ]]) end) @@ -962,12 +950,12 @@ describe(':substitute, inccommand=split', function() -- Assert that 'cursorline' is active. screen:expect([[ - {16:^Inc substitution on }| + {21:^Inc substitution on }| two lines | Inc substitution on | two lines | | - {15:~ }|*9 + {1:~ }|*9 | ]]) @@ -975,15 +963,15 @@ describe(':substitute, inccommand=split', function() -- 'cursorline' is NOT active during preview. screen:expect([[ Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | | - {11:[No Name] [+] }| - |2| {12:tw}o lines | - |4| {12:tw}o lines | - {15:~ }|*5 - {10:[Preview] }| + {3:[No Name] [+] }| + |2| {20:tw}o lines | + |4| {20:tw}o lines | + {1:~ }|*5 + {2:[Preview] }| :%s/tw^ | ]]) end) @@ -993,15 +981,15 @@ describe(':substitute, inccommand=split', function() feed('M M M<esc>') feed(':%s/M/123/g') screen:expect([[ - {12:123} {12:123} {12:123} | + {20:123} {20:123} {20:123} | Inc substitution on | two lines | Inc substitution on | two lines | - {11:[No Name] [+] }| - |1| {12:123} {12:123} {12:123} | - {15:~ }|*6 - {10:[Preview] }| + {3:[No Name] [+] }| + |1| {20:123} {20:123} {20:123} | + {1:~ }|*6 + {2:[Preview] }| :%s/M/123/g^ | ]]) end) @@ -1015,10 +1003,10 @@ describe(':substitute, inccommand=split', function() Inc substitution on | two lines | | - {11:[No Name] [+] }| + {3:[No Name] [+] }| | - {15:~ }|*6 - {10:[Preview] }| + {1:~ }|*6 + {2:[Preview] }| :%s/Inx^ | ]]) end) @@ -1030,12 +1018,12 @@ describe(':substitute, inccommand=split', function() insert(string.rep('abc abc abc\n', 20)) feed(':%s/abc/MMM/g') screen:expect([[ - {12:MMM} {12:MMM} {12:MMM} |*9 - {11:[No Name] [+] }| - | 1| {12:MMM} {12:MMM} {12:MMM} | - | 2| {12:MMM} {12:MMM} {12:MMM} | - | 3| {12:MMM} {12:MMM} {12:MMM} | - {10:[Preview] }| + {20:MMM} {20:MMM} {20:MMM} |*9 + {3:[No Name] [+] }| + | 1| {20:MMM} {20:MMM} {20:MMM} | + | 2| {20:MMM} {20:MMM} {20:MMM} | + | 3| {20:MMM} {20:MMM} {20:MMM} | + {2:[Preview] }| :%s/abc/MMM/g^ | ]]) end) @@ -1051,7 +1039,7 @@ describe(':substitute, inccommand=split', function() Inc substitution on | ^XXo lines | | - {15:~ }|*9 + {1:~ }|*9 :%s/tw/XX/g | ]]) end) @@ -1067,17 +1055,17 @@ describe(':substitute, inccommand=split', function() Inc substitution on | BBo lines | Inc substitution on | - {12:X}o lines | + {20:X}o lines | Inc substitution on | - {11:[No Name] [+] }| - |1001| {12:X}o lines | - |1003| {12:X}o lines | - |1005| {12:X}o lines | - |1007| {12:X}o lines | - |1009| {12:X}o lines | - |1011| {12:X}o lines | - |1013| {12:X}o lines | - {10:[Preview] }| + {3:[No Name] [+] }| + |1001| {20:X}o lines | + |1003| {20:X}o lines | + |1005| {20:X}o lines | + |1007| {20:X}o lines | + |1009| {20:X}o lines | + |1011| {20:X}o lines | + |1013| {20:X}o lines | + {2:[Preview] }| :%s/tw/X^ | ]]) end) @@ -1111,7 +1099,7 @@ describe(':substitute, inccommand=split', function() Inc substitution on | two lines | ^ | - {15:~ }|*9 + {1:~ }|*9 2 matches on 2 lines | ]]) end) @@ -1196,16 +1184,16 @@ describe(':substitute, inccommand=split', function() feed(':1,2s/t/X') screen:expect([[ - Inc subs{12:X}itution on | - {12:X}wo lines | + Inc subs{20:X}itution on | + {20:X}wo lines | Inc substitution on | two lines | | - {11:[No Name] [+] }| - |1| Inc subs{12:X}itution on | - |2| {12:X}wo lines | - {15:~ }|*5 - {10:[Preview] }| + {3:[No Name] [+] }| + |1| Inc subs{20:X}itution on | + |2| {20:X}wo lines | + {1:~ }|*5 + {2:[Preview] }| :1,2s/t/X^ | ]]) @@ -1218,7 +1206,7 @@ describe(':substitute, inccommand=split', function() Inc substitution on | two lines | | - {15:~ }|*9 + {1:~ }|*9 :echo 'foo'^ | ]]) end) @@ -1227,65 +1215,65 @@ describe(':substitute, inccommand=split', function() feed('gg') feed(':1,2s/t/X') screen:expect([[ - Inc subs{12:X}itution on | - {12:X}wo lines | + Inc subs{20:X}itution on | + {20:X}wo lines | Inc substitution on | two lines | | - {11:[No Name] [+] }| - |1| Inc subs{12:X}itution on | - |2| {12:X}wo lines | - {15:~ }|*5 - {10:[Preview] }| + {3:[No Name] [+] }| + |1| Inc subs{20:X}itution on | + |2| {20:X}wo lines | + {1:~ }|*5 + {2:[Preview] }| :1,2s/t/X^ | ]]) feed([[<C-R>='Y']]) -- preview should be unchanged during c_CTRL-R_= editing screen:expect([[ - Inc subs{12:X}itution on | - {12:X}wo lines | + Inc subs{20:X}itution on | + {20:X}wo lines | Inc substitution on | two lines | | - {11:[No Name] [+] }| - |1| Inc subs{12:X}itution on | - |2| {12:X}wo lines | - {15:~ }|*5 - {10:[Preview] }| - ={1:'Y'}^ | + {3:[No Name] [+] }| + |1| Inc subs{20:X}itution on | + |2| {20:X}wo lines | + {1:~ }|*5 + {2:[Preview] }| + ={26:'Y'}^ | ]]) feed('<CR>') -- preview should be changed by the result of the expression screen:expect([[ - Inc subs{12:XY}itution on | - {12:XY}wo lines | + Inc subs{20:XY}itution on | + {20:XY}wo lines | Inc substitution on | two lines | | - {11:[No Name] [+] }| - |1| Inc subs{12:XY}itution on | - |2| {12:XY}wo lines | - {15:~ }|*5 - {10:[Preview] }| + {3:[No Name] [+] }| + |1| Inc subs{20:XY}itution on | + |2| {20:XY}wo lines | + {1:~ }|*5 + {2:[Preview] }| :1,2s/t/XY^ | ]]) feed([[<C-\>e'echo']]) -- preview should be unchanged during c_CTRL-\_e editing screen:expect([[ - Inc subs{12:XY}itution on | - {12:XY}wo lines | + Inc subs{20:XY}itution on | + {20:XY}wo lines | Inc substitution on | two lines | | - {11:[No Name] [+] }| - |1| Inc subs{12:XY}itution on | - |2| {12:XY}wo lines | - {15:~ }|*5 - {10:[Preview] }| - ={1:'echo'}^ | + {3:[No Name] [+] }| + |1| Inc subs{20:XY}itution on | + |2| {20:XY}wo lines | + {1:~ }|*5 + {2:[Preview] }| + ={26:'echo'}^ | ]]) feed('<CR>') @@ -1296,7 +1284,7 @@ describe(':substitute, inccommand=split', function() Inc substitution on | two lines | | - {15:~ }|*9 + {1:~ }|*9 :echo^ | ]]) end) @@ -1321,8 +1309,8 @@ describe('inccommand=nosplit', function() two lines | Inc substitution on | two lines | - Line *.{12:X} | - {15:~ }|*4 + Line *.{20:X} | + {1:~ }|*4 :%smagic/3.*/X^ | ]]) @@ -1333,8 +1321,8 @@ describe('inccommand=nosplit', function() two lines | Inc substitution on | two lines | - Line *.{12:X} here | - {15:~ }|*4 + Line *.{20:X} here | + {1:~ }|*4 :%snomagic/3.*/X^ | ]]) end) @@ -1342,13 +1330,13 @@ describe('inccommand=nosplit', function() it('shows preview when cmd modifiers are present', function() -- one modifier feed(':keeppatterns %s/tw/to') - screen:expect { any = [[{12:to}o lines]] } + screen:expect { any = [[{20:to}o lines]] } feed('<Esc>') screen:expect { any = [[two lines]] } -- multiple modifiers feed(':keeppatterns silent %s/tw/to') - screen:expect { any = [[{12:to}o lines]] } + screen:expect { any = [[{20:to}o lines]] } feed('<Esc>') screen:expect { any = [[two lines]] } @@ -1360,8 +1348,8 @@ describe('inccommand=nosplit', function() Inc substitution on | two lines | | - {15:~ }|*2 - {11: }| + {1:~ }|*2 + {3: }| :silent tabedit %s/t| w/to^ | ]]) @@ -1378,11 +1366,11 @@ describe('inccommand=nosplit', function() poke_eventloop() screen:expect([[ Inc substitution on | - {12:OKOK}o lines | + {20:OKOK}o lines | Inc substitution on | - {12:OKOK}o lines | + {20:OKOK}o lines | | - {15:~ }|*4 + {1:~ }|*4 :%s/tw/OKOK^ | ]]) end) @@ -1393,33 +1381,33 @@ describe('inccommand=nosplit', function() feed(':%s/tw') screen:expect([[ Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | | - {15:~ }|*4 + {1:~ }|*4 :%s/tw^ | ]]) feed('/BM') screen:expect([[ Inc substitution on | - {12:BM}o lines | + {20:BM}o lines | Inc substitution on | - {12:BM}o lines | + {20:BM}o lines | | - {15:~ }|*4 + {1:~ }|*4 :%s/tw/BM^ | ]]) feed('/') screen:expect([[ Inc substitution on | - {12:BM}o lines | + {20:BM}o lines | Inc substitution on | - {12:BM}o lines | + {20:BM}o lines | | - {15:~ }|*4 + {1:~ }|*4 :%s/tw/BM/^ | ]]) @@ -1430,7 +1418,7 @@ describe('inccommand=nosplit', function() Inc substitution on | ^BMo lines | | - {15:~ }|*4 + {1:~ }|*4 :%s/tw/BM/ | ]]) end) @@ -1442,12 +1430,12 @@ describe('inccommand=nosplit', function() feed(':1,2s/t/X') screen:expect([[ - Inc subs{12:X}itution on | - {12:X}wo lines | + Inc subs{20:X}itution on | + {20:X}wo lines | Inc substitution on | two lines | | - {15:~ }|*4 + {1:~ }|*4 :1,2s/t/X^ | ]]) @@ -1460,7 +1448,7 @@ describe('inccommand=nosplit', function() Inc substitution on | two lines | | - {15:~ }|*4 + {1:~ }|*4 :echo 'foo'^ | ]]) end) @@ -1469,11 +1457,11 @@ describe('inccommand=nosplit', function() feed(':%s/two/three/g|q!') screen:expect([[ Inc substitution on | - {12:three} lines | + {20:three} lines | Inc substitution on | - {12:three} lines | + {20:three} lines | | - {15:~ }|*4 + {1:~ }|*4 :%s/two/three/g|q!^ | ]]) eq(eval('v:null'), eval('v:exiting')) @@ -1494,7 +1482,7 @@ describe('inccommand=nosplit', function() Inc substitution on | two lines | | - {15:~ }|*4 + {1:~ }|*4 :%s/^ | ]]) end) @@ -1546,7 +1534,7 @@ describe(":substitute, 'inccommand' with a failing expression", function() Inc substitution on | two lines | | - {15:~ }|*6 + {1:~ }|*6 :100s/^ | ]]) @@ -1555,8 +1543,8 @@ describe(":substitute, 'inccommand' with a failing expression", function() Inc substitution on | two lines | ^ | - {15:~ }|*6 - {14:E16: Invalid range} | + {1:~ }|*6 + {9:E16: Invalid range} | ]]) end end) @@ -1649,7 +1637,7 @@ describe("'inccommand' and :cnoremap", function() command("cnoremap <expr> x execute('bwipeout!')[-1].'x'") feed(':%s/tw/tox<enter>') - screen:expect { any = [[{14:^E565:]] } + screen:expect { any = [[{9:^E565:]] } feed('<c-c>') -- error thrown b/c of the mapping @@ -1736,9 +1724,9 @@ describe("'inccommand' autocommands", function() CmdwinLeave = {}, } - local function bufferlist(t) + local function bufferlist(q) local s = '' - for _, buffer in pairs(t) do + for _, buffer in pairs(q) do s = s .. ', ' .. tostring(buffer) end return s @@ -1807,18 +1795,18 @@ describe("'inccommand' split windows", function() feed(':%s/tw') screen:expect([[ Inc substitution on │Inc substitution on| - {12:tw}o lines │{12:tw}o lines | + {20:tw}o lines │{20:tw}o lines | │ | - {15:~ }│{15:~ }|*11 - {11:[No Name] [+] }│{15:~ }| - Inc substitution on │{15:~ }| - {12:tw}o lines │{15:~ }| - │{15:~ }| - {15:~ }│{15:~ }|*2 - {10:[No Name] [+] [No Name] [+] }| - |2| {12:tw}o lines | - {15:~ }|*6 - {10:[Preview] }| + {1:~ }│{1:~ }|*11 + {3:[No Name] [+] }│{1:~ }| + Inc substitution on │{1:~ }| + {20:tw}o lines │{1:~ }| + │{1:~ }| + {1:~ }│{1:~ }|*2 + {2:[No Name] [+] [No Name] [+] }| + |2| {20:tw}o lines | + {1:~ }|*6 + {2:[Preview] }| :%s/tw^ | ]]) @@ -1830,18 +1818,18 @@ describe("'inccommand' split windows", function() feed(':%s/tw') screen:expect([[ Inc substitution on │Inc substitution on| - {12:tw}o lines │{12:tw}o lines | + {20:tw}o lines │{20:tw}o lines | │ | - {15:~ }│{15:~ }|*11 - {11:[No Name] [+] }{10:[No Name] [+] }| + {1:~ }│{1:~ }|*11 + {3:[No Name] [+] }{2:[No Name] [+] }| Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | | - {15:~ }|*2 - {10:[No Name] [+] }| - |2| {12:tw}o lines | - {15:~ }|*6 - {10:[Preview] }| + {1:~ }|*2 + {2:[No Name] [+] }| + |2| {20:tw}o lines | + {1:~ }|*6 + {2:[Preview] }| :%s/tw^ | ]]) end) @@ -1864,13 +1852,13 @@ describe("'inccommand' split windows", function() screen:expect([[ Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | | - {15:~ }|*17 - {11:[No Name] [+] }| - |2| {12:tw}o lines | - {15:~ }|*6 - {10:[Preview] }| + {1:~ }|*17 + {3:[No Name] [+] }| + |2| {20:tw}o lines | + {1:~ }|*6 + {2:[Preview] }| :%s/tw^ | ]]) end @@ -1882,7 +1870,7 @@ describe("'inccommand' split windows", function() feed('gg:%s/tw') screen:expect([[ Inc substitution on | - {12:tw}o lines | + {20:tw}o lines | :%s/tw^ | ]]) end) @@ -1931,10 +1919,10 @@ describe("'inccommand' with 'gdefault'", function() feed(':%s/\\%1c/a/') screen:expect([[ - {12:a}Inc substitution on | - {12:a}two lines | - {12:a} | - {15:~ }| + {20:a}Inc substitution on | + {20:a}two lines | + {20:a} | + {1:~ }| :%s/\%1c/a/^ | ]]) @@ -1943,7 +1931,7 @@ describe("'inccommand' with 'gdefault'", function() Inc substitution on | two lines | ^ | - {15:~ }| + {1:~ }| | ]]) end) @@ -1962,47 +1950,47 @@ describe(':substitute', function() feed(':%s/2\\_.*X') screen:expect([[ - 1 {12:2 3} | - {12:A B C} | - {12:4 5 6} | - {12:X} Y Z | + 1 {20:2 3} | + {20:A B C} | + {20:4 5 6} | + {20:X} Y Z | 7 8 9 | - {11:[No Name] [+] }| - |1| 1 {12:2 3} | - |2|{12: A B C} | - |3|{12: 4 5 6} | - |4|{12: X} Y Z | - {15:~ }|*3 - {10:[Preview] }| + {3:[No Name] [+] }| + |1| 1 {20:2 3} | + |2|{20: A B C} | + |3|{20: 4 5 6} | + |4|{20: X} Y Z | + {1:~ }|*3 + {2:[Preview] }| :%s/2\_.*X^ | ]]) feed('/MMM') screen:expect([[ - 1 {12:MMM} Y Z | + 1 {20:MMM} Y Z | 7 8 9 | | - {15:~ }|*2 - {11:[No Name] [+] }| - |1| 1 {12:MMM} Y Z | - {15:~ }|*6 - {10:[Preview] }| + {1:~ }|*2 + {3:[No Name] [+] }| + |1| 1 {20:MMM} Y Z | + {1:~ }|*6 + {2:[Preview] }| :%s/2\_.*X/MMM^ | ]]) feed('\\rK\\rLLL') screen:expect([[ - 1 {12:MMM} | - {12:K} | - {12:LLL} Y Z | + 1 {20:MMM} | + {20:K} | + {20:LLL} Y Z | 7 8 9 | | - {11:[No Name] [+] }| - |1| 1 {12:MMM} | - |2|{12: K} | - |3|{12: LLL} Y Z | - {15:~ }|*4 - {10:[Preview] }| + {3:[No Name] [+] }| + |1| 1 {20:MMM} | + |2|{20: K} | + |3|{20: LLL} Y Z | + {1:~ }|*4 + {2:[Preview] }| :%s/2\_.*X/MMM\rK\rLLL^ | ]]) end) @@ -2013,21 +2001,21 @@ describe(':substitute', function() feed(':%s/2\\_.*X/MMM') screen:expect([[ - 1 {12:MMM} Y Z | + 1 {20:MMM} Y Z | 7 8 9 | | - {15:~ }|*11 + {1:~ }|*11 :%s/2\_.*X/MMM^ | ]]) feed('\\rK\\rLLL') screen:expect([[ - 1 {12:MMM} | - {12:K} | - {12:LLL} Y Z | + 1 {20:MMM} | + {20:K} | + {20:LLL} Y Z | 7 8 9 | | - {15:~ }|*9 + {1:~ }|*9 :%s/2\_.*X/MMM\rK\rLLL^ | ]]) end) @@ -2039,15 +2027,15 @@ describe(':substitute', function() feed(':%s/a/XLK') screen:expect([[ - {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:XLK} r| + {20:XLK} bdc e{20:XLK}e {20:XLK} fgl lzi{20:XLK} r| x | | - {15:~ }|*2 - {11:[No Name] [+] }| - |1| {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:X}| - {12:LK} r | - {15:~ }|*5 - {10:[Preview] }| + {1:~ }|*2 + {3:[No Name] [+] }| + |1| {20:XLK} bdc e{20:XLK}e {20:XLK} fgl lzi{20:X}| + {20:LK} r | + {1:~ }|*5 + {2:[Preview] }| :%s/a/XLK^ | ]]) end) @@ -2059,10 +2047,10 @@ describe(':substitute', function() feed(':%s/a/XLK') screen:expect([[ - {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:XLK} r| + {20:XLK} bdc e{20:XLK}e {20:XLK} fgl lzi{20:XLK} r| x | | - {15:~ }|*11 + {1:~ }|*11 :%s/a/XLK^ | ]]) end) @@ -2073,18 +2061,18 @@ describe(':substitute', function() feed(':%s/[0-9]\\n\\zs[A-Z]/OKO') screen:expect([[ - {12:OKO} B C | + {20:OKO} B C | 4 5 6 | - {12:OKO} Y Z | + {20:OKO} Y Z | 7 8 9 | | - {11:[No Name] [+] }| + {3:[No Name] [+] }| |1| 1 2 3 | - |2| {12:OKO} B C | + |2| {20:OKO} B C | |3| 4 5 6 | - |4| {12:OKO} Y Z | - {15:~ }|*3 - {10:[Preview] }| + |4| {20:OKO} Y Z | + {1:~ }|*3 + {2:[Preview] }| :%s/[0-9]\n\zs[A-Z]/OKO^ | ]]) end) @@ -2096,12 +2084,12 @@ describe(':substitute', function() feed(':%s/[0-9]\\n\\zs[A-Z]/OKO') screen:expect([[ 1 2 3 | - {12:OKO} B C | + {20:OKO} B C | 4 5 6 | - {12:OKO} Y Z | + {20:OKO} Y Z | 7 8 9 | | - {15:~ }|*8 + {1:~ }|*8 :%s/[0-9]\n\zs[A-Z]/OKO^ | ]]) end) @@ -2111,14 +2099,14 @@ describe(':substitute', function() feed(':%s/T\\([0-9]\\+\\)/\\1\\1/g') screen:expect([[ - T {12:123123} {12:22}T TTT {12:090804090804} | + T {20:123123} {20:22}T TTT {20:090804090804} | x | - {15:~ }|*3 - {11:[No Name] [+] }| - |1| T {12:123123} {12:22}T TTT {12:090804090}| - {12:804} | - {15:~ }|*5 - {10:[Preview] }| + {1:~ }|*3 + {3:[No Name] [+] }| + |1| T {20:123123} {20:22}T TTT {20:090804090}| + {20:804} | + {1:~ }|*5 + {2:[Preview] }| :%s/T\([0-9]\+\)/\1\1/g^ | ]]) end) @@ -2128,9 +2116,9 @@ describe(':substitute', function() feed(':%s/T\\([0-9]\\+\\)/\\1\\1/g') screen:expect([[ - T {12:123123} {12:22}T TTT {12:090804090804} | + T {20:123123} {20:22}T TTT {20:090804090804} | x | - {15:~ }|*12 + {1:~ }|*12 :%s/T\([0-9]\+\)/\1\1/g^ | ]]) end) @@ -2147,17 +2135,17 @@ describe(':substitute', function() common_setup(screen, 'split', text) feed(':%s/[QR]\\n') screen:expect([[ - afa {12:Q} | - adf la;lkd {12:R} | + afa {20:Q} | + adf la;lkd {20:R} | alx | | - {15:~ }| - {11:[No Name] [+] }| - |3| afa {12:Q} | - |4|{12: }adf la;lkd {12:R} | - |5|{12: }alx | - {15:~ }|*4 - {10:[Preview] }| + {1:~ }| + {3:[No Name] [+] }| + |3| afa {20:Q} | + |4|{20: }adf la;lkd {20:R} | + |5|{20: }alx | + {1:~ }|*4 + {2:[Preview] }| :%s/[QR]\n^ | ]]) @@ -2165,13 +2153,13 @@ describe(':substitute', function() screen:expect([[ T T123 T T123 T2T TT T23423424| x | - afa {12:KKK}adf la;lkd {12:KKK}alx | + afa {20:KKK}adf la;lkd {20:KKK}alx | | - {15:~ }| - {11:[No Name] [+] }| - |3| afa {12:KKK}adf la;lkd {12:KKK}alx | - {15:~ }|*6 - {10:[Preview] }| + {1:~ }| + {3:[No Name] [+] }| + |3| afa {20:KKK}adf la;lkd {20:KKK}alx | + {1:~ }|*6 + {2:[Preview] }| :%s/[QR]\n/KKK^ | ]]) end) @@ -2190,9 +2178,9 @@ describe(':substitute', function() screen:expect([[ T T123 T T123 T2T TT T23423424| x | - afa {12:KKK}adf la;lkd {12:KKK}alx | + afa {20:KKK}adf la;lkd {20:KKK}alx | | - {15:~ }|*10 + {1:~ }|*10 :%s/[QR]\n/KKK^ | ]]) end) @@ -2218,16 +2206,16 @@ describe(':substitute', function() BBB BB | CCC CC | | - {11:[No Name] [+] }| + {3:[No Name] [+] }| | 1| AAA AA | - | 2|{12: }BBB BB | - | 3|{12: }CCC CC | - | 4|{12: }AAA AA | - | 5|{12: }BBB BB | - | 6|{12: }CCC CC | - | 7|{12: }AAA AA | - {10:[Preview] }| - :%s/\n\n/{17:^M}/g^ | + | 2|{20: }BBB BB | + | 3|{20: }CCC CC | + | 4|{20: }AAA AA | + | 5|{20: }BBB BB | + | 6|{20: }CCC CC | + | 7|{20: }AAA AA | + {2:[Preview] }| + :%s/\n\n/{18:^M}/g^ | ]], } assert_alive() @@ -2261,7 +2249,7 @@ describe(':substitute', function() BBB BB | CCC CC | | - :%s/\n\n/{17:^M}/g^ | + :%s/\n\n/{18:^M}/g^ | ]], } assert_alive() @@ -2271,36 +2259,36 @@ describe(':substitute', function() common_setup(screen, 'split', multibyte_text) feed(':%s/£.*ѫ/X¥¥') screen:expect([[ - a{12:X¥¥}¥KOL | + a{20:X¥¥}¥KOL | £ ¥ libm | £ ¥ | | - {15:~ }| - {11:[No Name] [+] }| - |1| {12:X¥¥} PEPPERS | - |2| {12:X¥¥} | - |3| a{12:X¥¥}¥KOL | - {15:~ }|*4 - {10:[Preview] }| + {1:~ }| + {3:[No Name] [+] }| + |1| {20:X¥¥} PEPPERS | + |2| {20:X¥¥} | + |3| a{20:X¥¥}¥KOL | + {1:~ }|*4 + {2:[Preview] }| :%s/£.*ѫ/X¥¥^ | ]]) feed('\\ra££ ¥') screen:expect([[ - a{12:X¥¥} | - {12:a££ ¥}¥KOL | + a{20:X¥¥} | + {20:a££ ¥}¥KOL | £ ¥ libm | £ ¥ | | - {11:[No Name] [+] }| - |1| {12:X¥¥} | - |2|{12: a££ ¥} PEPPERS | - |3| {12:X¥¥} | - |4|{12: a££ ¥} | - |5| a{12:X¥¥} | - |6|{12: a££ ¥}¥KOL | - {15:~ }| - {10:[Preview] }| + {3:[No Name] [+] }| + |1| {20:X¥¥} | + |2|{20: a££ ¥} PEPPERS | + |3| {20:X¥¥} | + |4|{20: a££ ¥} | + |5| a{20:X¥¥} | + |6|{20: a££ ¥}¥KOL | + {1:~ }| + {2:[Preview] }| :%s/£.*ѫ/X¥¥\ra££ ¥^ | ]]) end) @@ -2309,28 +2297,28 @@ describe(':substitute', function() common_setup(screen, 'nosplit', multibyte_text) feed(':%s/£.*ѫ/X¥¥') screen:expect([[ - {12:X¥¥} PEPPERS | - {12:X¥¥} | - a{12:X¥¥}¥KOL | + {20:X¥¥} PEPPERS | + {20:X¥¥} | + a{20:X¥¥}¥KOL | £ ¥ libm | £ ¥ | | - {15:~ }|*8 + {1:~ }|*8 :%s/£.*ѫ/X¥¥^ | ]]) feed('\\ra££ ¥') screen:expect([[ - {12:X¥¥} | - {12:a££ ¥} PEPPERS | - {12:X¥¥} | - {12:a££ ¥} | - a{12:X¥¥} | - {12:a££ ¥}¥KOL | + {20:X¥¥} | + {20:a££ ¥} PEPPERS | + {20:X¥¥} | + {20:a££ ¥} | + a{20:X¥¥} | + {20:a££ ¥}¥KOL | £ ¥ libm | £ ¥ | | - {15:~ }|*5 + {1:~ }|*5 :%s/£.*ѫ/X¥¥\ra££ ¥^ | ]]) end) @@ -2344,17 +2332,17 @@ describe(':substitute', function() X Y Z | 7 8 9 | K L M | - {12:a} b c | - {12:d} e f | - {12:q} r s | - {12:x} y z | - £ {12:m} n | - {12:t} œ ¥ | + {20:a} b c | + {20:d} e f | + {20:q} r s | + {20:x} y z | + £ {20:m} n | + {20:t} œ ¥ | | - {11:[No Name] [+] }| - | 7| {12:a} b c | - | 8| {12:d} e f | - {10:[Preview] }| + {3:[No Name] [+] }| + | 7| {20:a} b c | + | 8| {20:d} e f | + {2:[Preview] }| :%s/[a-z]^ | ]]) @@ -2363,17 +2351,17 @@ describe(':substitute', function() X Y Z | 7 8 9 | K L M | - {12:JLKR £} b c | - {12:JLKR £} e f | - {12:JLKR £} r s | - {12:JLKR £} y z | - £ {12:JLKR £} n | - {12:JLKR £} œ ¥ | + {20:JLKR £} b c | + {20:JLKR £} e f | + {20:JLKR £} r s | + {20:JLKR £} y z | + £ {20:JLKR £} n | + {20:JLKR £} œ ¥ | | - {11:[No Name] [+] }| - | 7| {12:JLKR £} b c | - | 8| {12:JLKR £} e f | - {10:[Preview] }| + {3:[No Name] [+] }| + | 7| {20:JLKR £} b c | + | 8| {20:JLKR £} e f | + {2:[Preview] }| :%s/[a-z]/JLKR £^ | ]]) @@ -2381,17 +2369,17 @@ describe(':substitute', function() screen:expect([[ 7 8 9 | K L M | - {12:JLKR £} | - {12:ѫ ab } | - {12:XXXX} b c | - {12:JLKR £} | - {12:ѫ ab } | - {12:XXXX} e f | - {12:JLKR £} | - {12:ѫ ab } | - {11:[No Name] [+] }| - | 7| {12:JLKR £} | - {11: }| + {20:JLKR £} | + {20:ѫ ab } | + {20:XXXX} b c | + {20:JLKR £} | + {20:ѫ ab } | + {20:XXXX} e f | + {20:JLKR £} | + {20:ѫ ab } | + {3:[No Name] [+] }| + | 7| {20:JLKR £} | + {3: }| :%s/[a-z]/JLKR £\rѫ ab \rXXX| X^ | ]]) @@ -2403,58 +2391,58 @@ describe(':substitute', function() feed(':%s/. .$') screen:expect([[ - t {12:œ ¥} | - {11:[No Name] [+] }| - | 1| 1 {12:2 3} | - | 2| A {12:B C} | - | 3| 4 {12:5 6} | - | 4| X {12:Y Z} | - | 5| 7 {12:8 9} | - | 6| K {12:L M} | - | 7| a {12:b c} | - | 8| d {12:e f} | - | 9| q {12:r s} | - |10| x {12:y z} | - |11| £ {12:m n} | - {10:[Preview] }| + t {20:œ ¥} | + {3:[No Name] [+] }| + | 1| 1 {20:2 3} | + | 2| A {20:B C} | + | 3| 4 {20:5 6} | + | 4| X {20:Y Z} | + | 5| 7 {20:8 9} | + | 6| K {20:L M} | + | 7| a {20:b c} | + | 8| d {20:e f} | + | 9| q {20:r s} | + |10| x {20:y z} | + |11| £ {20:m n} | + {2:[Preview] }| :%s/. .$^ | ]]) feed('/ YYY') screen:expect([[ - t {12: YYY} | - {11:[No Name] [+] }| - | 1| 1 {12: YYY} | - | 2| A {12: YYY} | - | 3| 4 {12: YYY} | - | 4| X {12: YYY} | - | 5| 7 {12: YYY} | - | 6| K {12: YYY} | - | 7| a {12: YYY} | - | 8| d {12: YYY} | - | 9| q {12: YYY} | - |10| x {12: YYY} | - |11| £ {12: YYY} | - {10:[Preview] }| + t {20: YYY} | + {3:[No Name] [+] }| + | 1| 1 {20: YYY} | + | 2| A {20: YYY} | + | 3| 4 {20: YYY} | + | 4| X {20: YYY} | + | 5| 7 {20: YYY} | + | 6| K {20: YYY} | + | 7| a {20: YYY} | + | 8| d {20: YYY} | + | 9| q {20: YYY} | + |10| x {20: YYY} | + |11| £ {20: YYY} | + {2:[Preview] }| :%s/. .$/ YYY^ | ]]) feed('\\r KKK') screen:expect([[ - a {12: YYY} | - {11:[No Name] [+] }| - | 1| 1 {12: YYY} | - | 2|{12: KKK} | - | 3| A {12: YYY} | - | 4|{12: KKK} | - | 5| 4 {12: YYY} | - | 6|{12: KKK} | - | 7| X {12: YYY} | - | 8|{12: KKK} | - | 9| 7 {12: YYY} | - |10|{12: KKK} | - |11| K {12: YYY} | - {10:[Preview] }| + a {20: YYY} | + {3:[No Name] [+] }| + | 1| 1 {20: YYY} | + | 2|{20: KKK} | + | 3| A {20: YYY} | + | 4|{20: KKK} | + | 5| 4 {20: YYY} | + | 6|{20: KKK} | + | 7| X {20: YYY} | + | 8|{20: KKK} | + | 9| 7 {20: YYY} | + |10|{20: KKK} | + |11| K {20: YYY} | + {2:[Preview] }| :%s/. .$/ YYY\r KKK^ | ]]) end) @@ -2463,14 +2451,14 @@ describe(':substitute', function() common_setup(screen, 'split', 'something\neverything\nsomeone') feed([[:%s/\(some\)\@<lt>=thing/one/]]) screen:expect([[ - some{12:one} | + some{20:one} | everything | someone | - {15:~ }|*2 - {11:[No Name] [+] }| - |1| some{12:one} | - {15:~ }|*6 - {10:[Preview] }| + {1:~ }|*2 + {3:[No Name] [+] }| + |1| some{20:one} | + {1:~ }|*6 + {2:[Preview] }| :%s/\(some\)\@<=thing/one/^ | ]]) @@ -2480,13 +2468,13 @@ describe(':substitute', function() feed([[:%s/\(some\)\@<lt>!thing/one/]]) screen:expect([[ something | - every{12:one} | + every{20:one} | someone | - {15:~ }|*2 - {11:[No Name] [+] }| - |2| every{12:one} | - {15:~ }|*6 - {10:[Preview] }| + {1:~ }|*2 + {3:[No Name] [+] }| + |2| every{20:one} | + {1:~ }|*6 + {2:[Preview] }| :%s/\(some\)\@<!thing/one/^ | ]]) @@ -2494,14 +2482,14 @@ describe(':substitute', function() poke_eventloop() feed([[:%s/some\(thing\)\@=/every/]]) screen:expect([[ - {12:every}thing | + {20:every}thing | everything | someone | - {15:~ }|*2 - {11:[No Name] [+] }| - |1| {12:every}thing | - {15:~ }|*6 - {10:[Preview] }| + {1:~ }|*2 + {3:[No Name] [+] }| + |1| {20:every}thing | + {1:~ }|*6 + {2:[Preview] }| :%s/some\(thing\)\@=/every/^ | ]]) @@ -2511,12 +2499,12 @@ describe(':substitute', function() screen:expect([[ something | everything | - {12:every}one | - {15:~ }|*2 - {11:[No Name] [+] }| - |3| {12:every}one | - {15:~ }|*6 - {10:[Preview] }| + {20:every}one | + {1:~ }|*2 + {3:[No Name] [+] }| + |3| {20:every}one | + {1:~ }|*6 + {2:[Preview] }| :%s/some\(thing\)\@!/every/^ | ]]) end) @@ -2528,23 +2516,23 @@ describe(':substitute', function() -- substitution preview should have been made, without prompting screen:expect([[ - {12:MO}o lines | - {11:[No Name] [+] }| - |2| {12:MO}o lines | - {15:~ }|*3 - {10:[Preview] }| + {20:MO}o lines | + {3:[No Name] [+] }| + |2| {20:MO}o lines | + {1:~ }|*3 + {2:[Preview] }| :2,1s/tw/MO/g^ | ]]) -- but should be prompted on hitting enter feed('<CR>') screen:expect([[ - {12:MO}o lines | - {11:[No Name] [+] }| - |2| {12:MO}o lines | - {15:~ }|*3 - {10:[Preview] }| - {13:Backwards range given, OK to swap (y/n)?}^ | + {20:MO}o lines | + {3:[No Name] [+] }| + |2| {20:MO}o lines | + {1:~ }|*3 + {2:[Preview] }| + {6:Backwards range given, OK to swap (y/n)?}^ | ]]) feed('y') @@ -2552,14 +2540,14 @@ describe(':substitute', function() Inc substitution on | ^MOo lines | | - {15:~ }|*4 - {13:Backwards range given, OK to swap (y/n)?}y | + {1:~ }|*4 + {6:Backwards range given, OK to swap (y/n)?}y | ]]) end) end) it(':substitute with inccommand during :terminal activity', function() - if helpers.skip_fragile(pending) then + if t.skip_fragile(pending) then return end retry(2, 40000, function() @@ -2595,17 +2583,23 @@ it(':substitute with inccommand, timer-induced :redraw #9777', function() feed(':%s/foo/ZZZ') sleep(20) -- Allow some timer activity. screen:expect([[ - {12:ZZZ} bar baz | + {20:ZZZ} bar baz | bar baz fox | - bar {12:ZZZ} baz | - {15:~ }|*3 - {11:[No Name] [+] }| - |1| {12:ZZZ} bar baz | - |3| bar {12:ZZZ} baz | - {15:~ }| - {10:[Preview] }| + bar {20:ZZZ} baz | + {1:~ }|*3 + {3:[No Name] [+] }| + |1| {20:ZZZ} bar baz | + |3| bar {20:ZZZ} baz | + {1:~ }| + {2:[Preview] }| :%s/foo/ZZZ^ | ]]) + + -- Also with nvim__redraw() + command('call timer_start(10, {-> nvim__redraw(#{flush:1})}, {"repeat":-1})') + command('call timer_start(10, {-> nvim__redraw(#{statusline:1})}, {"repeat":-1})') + sleep(20) -- Allow some timer activity. + screen:expect_unchanged() end) it(':substitute with inccommand, allows :redraw before first separator is typed #18857', function() @@ -2627,8 +2621,8 @@ it(':substitute with inccommand, allows :redraw before first separator is typed foo bar baz | bar baz fox | bar foo baz | - {16: }{15: }| - {15:~ }| + {21: }{1: }| + {1:~ }| :^ | ]]) feed('%s') @@ -2636,8 +2630,8 @@ it(':substitute with inccommand, allows :redraw before first separator is typed foo bar baz | bar baz fox | bar foo baz | - {16: }{15: }| - {15:~ }| + {21: }{1: }| + {1:~ }| :%s^ | ]]) api.nvim_buf_set_lines(float_buf, 0, -1, true, { 'foo' }) @@ -2646,8 +2640,8 @@ it(':substitute with inccommand, allows :redraw before first separator is typed foo bar baz | bar baz fox | bar foo baz | - {16:foo }{15: }| - {15:~ }| + {21:foo }{1: }| + {1:~ }| :%s^ | ]]) end) @@ -2659,7 +2653,7 @@ it(':substitute with inccommand, does not crash if range contains invalid marks' feed([[:'a,'bs]]) screen:expect([[ test | - {15:~ }|*4 + {1:~ }|*4 :'a,'bs^ | ]]) -- v:errmsg shouldn't be set either before the first separator is typed @@ -2667,7 +2661,7 @@ it(':substitute with inccommand, does not crash if range contains invalid marks' feed('/') screen:expect([[ test | - {15:~ }|*4 + {1:~ }|*4 :'a,'bs/^ | ]]) end) @@ -2679,18 +2673,18 @@ it(':substitute with inccommand, no unnecessary redraw if preview is not shown', feed(':ls<CR>') screen:expect([[ test | - {15:~ }| - {11: }| + {1:~ }| + {3: }| :ls | 1 %a + "[No Name]" line 1 | - {13:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) feed(':s') -- no unnecessary redraw, so messages are still shown screen:expect([[ test | - {15:~ }| - {11: }| + {1:~ }| + {3: }| :ls | 1 %a + "[No Name]" line 1 | :s^ | @@ -2698,8 +2692,8 @@ it(':substitute with inccommand, no unnecessary redraw if preview is not shown', feed('o') screen:expect([[ test | - {15:~ }| - {11: }| + {1:~ }| + {3: }| :ls | 1 %a + "[No Name]" line 1 | :so^ | @@ -2707,8 +2701,8 @@ it(':substitute with inccommand, no unnecessary redraw if preview is not shown', feed('<BS>') screen:expect([[ test | - {15:~ }| - {11: }| + {1:~ }| + {3: }| :ls | 1 %a + "[No Name]" line 1 | :s^ | @@ -2716,8 +2710,8 @@ it(':substitute with inccommand, no unnecessary redraw if preview is not shown', feed('/test') -- now inccommand is shown, so screen is redrawn screen:expect([[ - {12:test} | - {15:~ }|*4 + {20:test} | + {1:~ }|*4 :s/test^ | ]]) end) @@ -2733,8 +2727,8 @@ it(":substitute doesn't crash with inccommand, if undo is empty #12932", functio sleep(100) feed('f') screen:expect([[ - {12:f} | - {15:~ }|*3 + {20:f} | + {1:~ }|*3 :%s/test/f^ | ]]) assert_alive() @@ -2756,11 +2750,11 @@ it(':substitute with inccommand works properly if undo is not synced #20029', fu ]]) feed('hjkl') screen:expect([[ - aaaaa {12:hjkl} | + aaaaa {20:hjkl} | foo | bar | baz | - bbbbb {12:hjkl} | + bbbbb {20:hjkl} | :'<,'>s/asdf/hjkl^ | ]]) feed('<CR>') @@ -2784,25 +2778,25 @@ it(':substitute with inccommand does not unexpectedly change viewport #25697', f command('vnew | tabnew | tabclose') screen:expect([[ ^ │£ m n | - {15:~ }│t œ ¥ | - {15:~ }│ | - {11:[No Name] }{10:[No Name] [+] }| + {1:~ }│t œ ¥ | + {1:~ }│ | + {3:[No Name] }{2:[No Name] [+] }| | ]]) feed(':s/') screen:expect([[ │£ m n | - {15:~ }│t œ ¥ | - {15:~ }│ | - {11:[No Name] }{10:[No Name] [+] }| + {1:~ }│t œ ¥ | + {1:~ }│ | + {3:[No Name] }{2:[No Name] [+] }| :s/^ | ]]) feed('<Esc>') screen:expect([[ ^ │£ m n | - {15:~ }│t œ ¥ | - {15:~ }│ | - {11:[No Name] }{10:[No Name] [+] }| + {1:~ }│t œ ¥ | + {1:~ }│ | + {3:[No Name] }{2:[No Name] [+] }| | ]]) end) @@ -2836,7 +2830,7 @@ it('long :%s/ with inccommand does not collapse cmdline', function() ) screen:expect([[ | - {11: }| + {3: }| :%s/AAAAAAAA| AAAAAAAAAAAA| AAAAAAA^ | @@ -2850,7 +2844,7 @@ it("with 'inccommand' typing invalid `={expr}` does not show error", function() feed(':edit `=`') screen:expect([[ | - {15:~ }|*4 + {1:~ }|*4 :edit `=`^ | ]]) end) @@ -2903,26 +2897,26 @@ it("'inccommand' value can be changed multiple times #27086", function() for _ = 1, 3 do feed(':%s/foo/bar') screen:expect([[ - {12:bar}1 | - {12:bar}2 | - {12:bar}3 | - {15:~ }|*7 - {11:[No Name] [+] }| - |1| {12:bar}1 | - |2| {12:bar}2 | - |3| {12:bar}3 | - {15:~ }|*4 - {10:[Preview] }| + {20:bar}1 | + {20:bar}2 | + {20:bar}3 | + {1:~ }|*7 + {3:[No Name] [+] }| + |1| {20:bar}1 | + |2| {20:bar}2 | + |3| {20:bar}3 | + {1:~ }|*4 + {2:[Preview] }| :%s/foo/bar^ | ]]) feed('<Esc>') command('set inccommand=nosplit') feed(':%s/foo/bar') screen:expect([[ - {12:bar}1 | - {12:bar}2 | - {12:bar}3 | - {15:~ }|*16 + {20:bar}1 | + {20:bar}2 | + {20:bar}3 | + {1:~ }|*16 :%s/foo/bar^ | ]]) feed('<Esc>') @@ -2938,10 +2932,10 @@ it("'inccommand' disables preview if preview buffer can't be created #27086", fu eq('split', api.nvim_get_option_value('inccommand', {})) feed(':%s/foo/bar') screen:expect([[ - {12:bar}1 | - {12:bar}2 | - {12:bar}3 | - {15:~ }|*16 + {20:bar}1 | + {20:bar}2 | + {20:bar}3 | + {1:~ }|*16 :%s/foo/bar^ | ]]) eq('nosplit', api.nvim_get_option_value('inccommand', {})) diff --git a/test/functional/ui/inccommand_user_spec.lua b/test/functional/ui/inccommand_user_spec.lua index a714df72b7..12f3640b54 100644 --- a/test/functional/ui/inccommand_user_spec.lua +++ b/test/functional/ui/inccommand_user_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local api = helpers.api -local clear = helpers.clear -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local insert = helpers.insert -local feed = helpers.feed -local command = helpers.command -local assert_alive = helpers.assert_alive + +local api = n.api +local clear = n.clear +local eq = t.eq +local exec_lua = n.exec_lua +local insert = n.insert +local feed = n.feed +local command = n.command +local assert_alive = n.assert_alive -- Implements a :Replace command that works like :substitute and has multibuffer support. local setup_replace_cmd = [[ @@ -237,13 +239,6 @@ describe("'inccommand' for user commands", function() before_each(function() clear() screen = Screen.new(40, 17) - screen:set_default_attr_ids({ - [1] = { background = Screen.colors.Yellow1 }, - [2] = { foreground = Screen.colors.Blue1, bold = true }, - [3] = { reverse = true }, - [4] = { reverse = true, bold = true }, - [5] = { foreground = Screen.colors.Blue }, - }) screen:attach() exec_lua(setup_replace_cmd) command('set cmdwinheight=5') @@ -263,16 +258,16 @@ describe("'inccommand' for user commands", function() command('set inccommand=nosplit') feed(':Replace text cats') screen:expect([[ - {1:cats} on line 1 | - more {1:cats} on line 2 | - oh no, even more {1:cats} | - will the {1:cats} ever stop | + {10:cats} on line 1 | + more {10:cats} on line 2 | + oh no, even more {10:cats} | + will the {10:cats} ever stop | oh well | - did the {1:cats} stop | + did the {10:cats} stop | why won't it stop | - make the {1:cats} stop | + make the {10:cats} stop | | - {2:~ }|*7 + {1:~ }|*7 :Replace text cats^ | ]]) end) @@ -281,22 +276,22 @@ describe("'inccommand' for user commands", function() command('set inccommand=split') feed(':Replace text cats') screen:expect([[ - {1:cats} on line 1 | - more {1:cats} on line 2 | - oh no, even more {1:cats} | - will the {1:cats} ever stop | + {10:cats} on line 1 | + more {10:cats} on line 2 | + oh no, even more {10:cats} | + will the {10:cats} ever stop | oh well | - did the {1:cats} stop | + did the {10:cats} stop | why won't it stop | - make the {1:cats} stop | + make the {10:cats} stop | | - {4:[No Name] [+] }| - |1| {1:cats} on line 1 | - |2| more {1:cats} on line 2 | - |3| oh no, even more {1:cats} | - |4| will the {1:cats} ever stop | - |6| did the {1:cats} stop | - {3:[Preview] }| + {3:[No Name] [+] }| + |1| {10:cats} on line 1 | + |2| more {10:cats} on line 2 | + |3| oh no, even more {10:cats} | + |4| will the {10:cats} ever stop | + |6| did the {10:cats} stop | + {2:[Preview] }| :Replace text cats^ | ]]) end) @@ -314,7 +309,7 @@ describe("'inccommand' for user commands", function() why won't it stop | make the text stop | ^ | - {2:~ }|*7 + {1:~ }|*7 | ]]) end) @@ -332,7 +327,7 @@ describe("'inccommand' for user commands", function() why won't it stop | make the cats stop | ^ | - {2:~ }|*7 + {1:~ }|*7 :Replace text cats | ]]) end) @@ -341,7 +336,7 @@ describe("'inccommand' for user commands", function() command('set inccommand=split') feed('gg:.Replace text cats') screen:expect([[ - {1:cats} on line 1 | + {10:cats} on line 1 | more text on line 2 | oh no, even more text | will the text ever stop | @@ -350,7 +345,7 @@ describe("'inccommand' for user commands", function() why won't it stop | make the text stop | | - {2:~ }|*7 + {1:~ }|*7 :.Replace text cats^ | ]]) end) @@ -394,7 +389,7 @@ describe("'inccommand' for user commands", function() ]]) feed(':C') screen:expect([[ - {1: cats on line 1} | + {10: cats on line 1} | more cats on line 2 | oh no, even more cats | will the cats ever stop | @@ -403,7 +398,7 @@ describe("'inccommand' for user commands", function() why won't it stop | make the cats stop | | - {2:~ }|*7 + {1:~ }|*7 :C^ | ]]) assert_alive() @@ -453,7 +448,7 @@ describe("'inccommand' for user commands", function() why won't it stop | make the text stop | a.a.a.a. | - {2:~ }|*7 + {1:~ }|*7 :Test a.a.a.a.^ | ]]) feed('<C-V><Esc>u') @@ -467,8 +462,8 @@ describe("'inccommand' for user commands", function() why won't it stop | make the text stop | a.a.a. | - {2:~ }|*7 - :Test a.a.a.a.{5:^[}u^ | + {1:~ }|*7 + :Test a.a.a.a.{18:^[}u^ | ]]) feed('<Esc>') screen:expect([[ @@ -481,7 +476,7 @@ describe("'inccommand' for user commands", function() why won't it stop | make the text stop | ^ | - {2:~ }|*7 + {1:~ }|*7 | ]]) end @@ -521,12 +516,6 @@ describe("'inccommand' with multiple buffers", function() before_each(function() clear() screen = Screen.new(40, 17) - screen:set_default_attr_ids({ - [1] = { background = Screen.colors.Yellow1 }, - [2] = { foreground = Screen.colors.Blue1, bold = true }, - [3] = { reverse = true }, - [4] = { reverse = true, bold = true }, - }) screen:attach() exec_lua(setup_replace_cmd) command('set cmdwinheight=10') @@ -547,12 +536,12 @@ describe("'inccommand' with multiple buffers", function() command('set inccommand=nosplit') feed(':Replace foo bar') screen:expect([[ - bar baz {1:bar} │ {1:bar} bar baz | - baz {1:bar} bar │ bar baz {1:bar} | - {1:bar} bar baz │ baz {1:bar} bar | + bar baz {10:bar} │ {10:bar} bar baz | + baz {10:bar} bar │ bar baz {10:bar} | + {10:bar} bar baz │ baz {10:bar} bar | │ | - {2:~ }│{2:~ }|*11 - {4:[No Name] [+] }{3:[No Name] [+] }| + {1:~ }│{1:~ }|*11 + {3:[No Name] [+] }{2:[No Name] [+] }| :Replace foo bar^ | ]]) feed('<CR>') @@ -561,8 +550,8 @@ describe("'inccommand' with multiple buffers", function() baz bar bar │ bar baz bar | bar bar baz │ baz bar bar | ^ │ | - {2:~ }│{2:~ }|*11 - {4:[No Name] [+] }{3:[No Name] [+] }| + {1:~ }│{1:~ }|*11 + {3:[No Name] [+] }{2:[No Name] [+] }| :Replace foo bar | ]]) end) @@ -571,22 +560,22 @@ describe("'inccommand' with multiple buffers", function() command('set inccommand=split') feed(':Replace foo bar') screen:expect([[ - bar baz {1:bar} │ {1:bar} bar baz | - baz {1:bar} bar │ bar baz {1:bar} | - {1:bar} bar baz │ baz {1:bar} bar | + bar baz {10:bar} │ {10:bar} bar baz | + baz {10:bar} bar │ bar baz {10:bar} | + {10:bar} bar baz │ baz {10:bar} bar | │ | - {4:[No Name] [+] }{3:[No Name] [+] }| + {3:[No Name] [+] }{2:[No Name] [+] }| Buffer #1: | - |1| {1:bar} bar baz | - |2| bar baz {1:bar} | - |3| baz {1:bar} bar | + |1| {10:bar} bar baz | + |2| bar baz {10:bar} | + |3| baz {10:bar} bar | Buffer #2: | - |1| bar baz {1:bar} | - |2| baz {1:bar} bar | - |3| {1:bar} bar baz | + |1| bar baz {10:bar} | + |2| baz {10:bar} bar | + |3| {10:bar} bar baz | | - {2:~ }| - {3:[Preview] }| + {1:~ }| + {2:[Preview] }| :Replace foo bar^ | ]]) feed('<CR>') @@ -595,8 +584,8 @@ describe("'inccommand' with multiple buffers", function() baz bar bar │ bar baz bar | bar bar baz │ baz bar bar | ^ │ | - {2:~ }│{2:~ }|*11 - {4:[No Name] [+] }{3:[No Name] [+] }| + {1:~ }│{1:~ }|*11 + {3:[No Name] [+] }{2:[No Name] [+] }| :Replace foo bar | ]]) end) diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index b2899bf82d..f377939458 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -1,16 +1,18 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, feed_command = helpers.clear, helpers.feed_command -local feed, next_msg, eq = helpers.feed, helpers.next_msg, helpers.eq -local command = helpers.command -local expect = helpers.expect -local curbuf_contents = helpers.curbuf_contents -local api = helpers.api -local exec_lua = helpers.exec_lua -local write_file = helpers.write_file -local fn = helpers.fn -local eval = helpers.eval +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') +local clear, feed_command = n.clear, n.feed_command +local feed, next_msg, eq = n.feed, n.next_msg, t.eq +local command = n.command +local expect = n.expect +local curbuf_contents = n.curbuf_contents +local api = n.api +local exec_lua = n.exec_lua +local write_file = t.write_file +local fn = n.fn +local eval = n.eval + before_each(clear) describe('mappings', function() @@ -280,21 +282,16 @@ end) it('typing a simplifiable key at hit-enter prompt triggers mapping vim-patch:8.2.0839', function() local screen = Screen.new(60, 8) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [2] = { bold = true, reverse = true }, -- MsgSeparator - [3] = { bold = true, foreground = Screen.colors.SeaGreen }, -- MoreMsg - }) screen:attach() command([[nnoremap <C-6> <Cmd>echo 'hit ctrl-6'<CR>]]) feed_command('ls') screen:expect([[ | {1:~ }|*3 - {2: }| + {3: }| :ls | 1 %a "[No Name]" line 1 | - {3:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) feed('<C-6>') screen:expect([[ @@ -331,12 +328,6 @@ describe('input non-printable chars', function() it("doesn't crash when echoing them back", function() write_file('Xtest-overwrite', [[foobar]]) local screen = Screen.new(60, 8) - screen:set_default_attr_ids { - [1] = { bold = true, foreground = Screen.colors.Blue1 }, - [2] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, - [3] = { bold = true, foreground = Screen.colors.SeaGreen4 }, - [4] = { bold = true, reverse = true }, - } screen:attach() command('set shortmess-=F') @@ -347,52 +338,52 @@ describe('input non-printable chars', function() "Xtest-overwrite" [noeol] 1L, 6B | ]]) - -- The timestamp is in second resolution, wait two seconds to be sure. - screen:sleep(2000) + -- Wait for some time so that the timestamp changes. + vim.uv.sleep(10) write_file('Xtest-overwrite', [[smurf]]) feed_command('w') screen:expect([[ foobar | {1:~ }|*3 - {4: }| + {3: }| "Xtest-overwrite" | - {2:WARNING: The file has been changed since reading it!!!} | - {3:Do you really want to write to it (y/n)?}^ | + {9:WARNING: The file has been changed since reading it!!!} | + {6:Do you really want to write to it (y/n)?}^ | ]]) feed('u') screen:expect([[ foobar | {1:~ }|*2 - {4: }| + {3: }| "Xtest-overwrite" | - {2:WARNING: The file has been changed since reading it!!!} | - {3:Do you really want to write to it (y/n)?}u | - {3:Do you really want to write to it (y/n)?}^ | + {9:WARNING: The file has been changed since reading it!!!} | + {6:Do you really want to write to it (y/n)?}u | + {6:Do you really want to write to it (y/n)?}^ | ]]) feed('\005') screen:expect([[ foobar | {1:~ }| - {4: }| + {3: }| "Xtest-overwrite" | - {2:WARNING: The file has been changed since reading it!!!} | - {3:Do you really want to write to it (y/n)?}u | - {3:Do you really want to write to it (y/n)?} | - {3:Do you really want to write to it (y/n)?}^ | + {9:WARNING: The file has been changed since reading it!!!} | + {6:Do you really want to write to it (y/n)?}u | + {6:Do you really want to write to it (y/n)?} | + {6:Do you really want to write to it (y/n)?}^ | ]]) feed('n') screen:expect([[ foobar | - {4: }| + {3: }| "Xtest-overwrite" | - {2:WARNING: The file has been changed since reading it!!!} | - {3:Do you really want to write to it (y/n)?}u | - {3:Do you really want to write to it (y/n)?} | - {3:Do you really want to write to it (y/n)?}n | - {3:Press ENTER or type command to continue}^ | + {9:WARNING: The file has been changed since reading it!!!} | + {6:Do you really want to write to it (y/n)?}u | + {6:Do you really want to write to it (y/n)?} | + {6:Do you really want to write to it (y/n)?}n | + {6:Press ENTER or type command to continue}^ | ]]) feed('<cr>') @@ -437,10 +428,6 @@ describe('display is updated', function() local screen before_each(function() screen = Screen.new(60, 8) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue1 }, -- NonText - [2] = { bold = true }, -- ModeMsg - }) screen:attach() end) @@ -452,7 +439,7 @@ describe('display is updated', function() abc | ^ | {1:~ }|*5 - {2:-- INSERT --} | + {5:-- INSERT --} | ]]) end) @@ -464,7 +451,7 @@ describe('display is updated', function() abc | ^ | {1:~ }|*5 - {2:-- INSERT --} | + {5:-- INSERT --} | ]]) end) end) diff --git a/test/functional/ui/linematch_spec.lua b/test/functional/ui/linematch_spec.lua index 40df5cadf1..03eed5a49c 100644 --- a/test/functional/ui/linematch_spec.lua +++ b/test/functional/ui/linematch_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed = helpers.feed -local clear = helpers.clear -local write_file = helpers.write_file +local feed = n.feed +local clear = n.clear +local write_file = t.write_file describe('Diff mode screen with 3 diffs open', function() local fname = 'Xtest-functional-diff-screen-1' @@ -38,18 +39,6 @@ describe('Diff mode screen with 3 diffs open', function() screen = Screen.new(100, 16) screen:attach() - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray }, - [2] = { foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.LightCyan1 }, - [3] = { reverse = true }, - [4] = { background = Screen.colors.LightBlue }, - [5] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray }, - [6] = { foreground = Screen.colors.Blue1, bold = true }, - [7] = { reverse = true, bold = true }, - [8] = { background = Screen.colors.Red1, bold = true }, - [10] = { foreground = Screen.colors.Brown }, - [9] = { background = Screen.colors.Plum1 }, - }) feed('<c-w>=') feed(':windo set nu!<cr>') end) @@ -96,20 +85,20 @@ describe('Diff mode screen with 3 diffs open', function() feed('1<c-w>w') feed(':2,6diffget screen-1.2<cr>') screen:expect([[ - {1: }{10: 1 }^ │{1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }{9:<<<<<<< HEAD }│{1: }{10: 3 }{9:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}| - {1: }{10: 4 } AAA │{1: }{10: 4 } AAA │{1: }{10: 3 } AAA | - {1: }{10: 5 } AAA │{1: }{10: 5 } AAA │{1: }{10: 4 } AAA | - {1: }{10: 6 } AAA │{1: }{10: 6 } AAA │{1: }{10: 5 } AAA | - {1: }{10: 7 }{9:======= }│{1: }{10: 7 }{9:======= }│{1: }{10: }{2:---------------------------}| - {1: }{10: 8 }{9: BBB }│{1: }{10: 8 }{9: BBB }│{1: }{10: }{2:---------------------------}| - {1: }{10: 9 }{9: BBB }│{1: }{10: 9 }{9: BBB }│{1: }{10: }{2:---------------------------}| - {1: }{10: 10 }{9: BBB }│{1: }{10: 10 }{9: BBB }│{1: }{10: }{2:---------------------------}| - {1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: }{2:---------------------------}| - {1: }{10: 12 } │{1: }{10: 12 } │{1: }{10: 6 } | - {6:~ }│{6:~ }│{6:~ }|*2 - {7:<-functional-diff-screen-1.3 [+] }{3:<est-functional-diff-screen-1.2 Xtest-functional-diff-screen-1 }| + {7: }{8: 1 }^ │{7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }{4:<<<<<<< HEAD }│{7: }{8: 3 }{4:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}| + {7: }{8: 4 } AAA │{7: }{8: 4 } AAA │{7: }{8: 3 } AAA | + {7: }{8: 5 } AAA │{7: }{8: 5 } AAA │{7: }{8: 4 } AAA | + {7: }{8: 6 } AAA │{7: }{8: 6 } AAA │{7: }{8: 5 } AAA | + {7: }{8: 7 }{4:======= }│{7: }{8: 7 }{4:======= }│{7: }{8: }{23:---------------------------}| + {7: }{8: 8 }{4: BBB }│{7: }{8: 8 }{4: BBB }│{7: }{8: }{23:---------------------------}| + {7: }{8: 9 }{4: BBB }│{7: }{8: 9 }{4: BBB }│{7: }{8: }{23:---------------------------}| + {7: }{8: 10 }{4: BBB }│{7: }{8: 10 }{4: BBB }│{7: }{8: }{23:---------------------------}| + {7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: }{23:---------------------------}| + {7: }{8: 12 } │{7: }{8: 12 } │{7: }{8: 6 } | + {1:~ }│{1:~ }│{1:~ }|*2 + {3:<-functional-diff-screen-1.3 [+] }{2:<est-functional-diff-screen-1.2 Xtest-functional-diff-screen-1 }| :2,6diffget screen-1.2 | ]]) end) @@ -118,17 +107,17 @@ describe('Diff mode screen with 3 diffs open', function() feed('2<c-w>w') feed(':5,7diffget screen-1.3<cr>') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 }^ │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}| - {1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }| - {1: }{10: 3 }{9: BBB }│{1: }{10: 5 }{9: BBB }│{1: }{10: }{2:---------------------------}| - {1: }{10: 4 }{9: }{8:BBB}{9: }│{1: }{10: 6 }{9: }{8:BBB}{9: }│{1: }{10: 4 }{9: }{8:AAA}{9: }| - {1: }{10: 5 }{9: }{8:BBB}{9: }│{1: }{10: 7 }{9: }{8:BBB}{9: }│{1: }{10: 5 }{9: }{8:AAA}{9: }| - {1: }{10: }{2:---------------------------}│{1: }{10: 8 }{4:>>>>>>> branch1 }│{1: }{10: }{2:---------------------------}| - {1: }{10: 6 } │{1: }{10: 9 } │{1: }{10: 6 } | - {6:~ }│{6:~ }│{6:~ }|*5 - {3:<test-functional-diff-screen-1.3 }{7:<functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 } │{7: }{8: 1 }^ │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: }{23:---------------------------}│{7: }{8: 3 }{22:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}| + {7: }{8: }{23:---------------------------}│{7: }{8: 4 }{4: AAA }│{7: }{8: 3 }{4: AAA }| + {7: }{8: 3 }{4: BBB }│{7: }{8: 5 }{4: BBB }│{7: }{8: }{23:---------------------------}| + {7: }{8: 4 }{4: }{27:BBB}{4: }│{7: }{8: 6 }{4: }{27:BBB}{4: }│{7: }{8: 4 }{4: }{27:AAA}{4: }| + {7: }{8: 5 }{4: }{27:BBB}{4: }│{7: }{8: 7 }{4: }{27:BBB}{4: }│{7: }{8: 5 }{4: }{27:AAA}{4: }| + {7: }{8: }{23:---------------------------}│{7: }{8: 8 }{22:>>>>>>> branch1 }│{7: }{8: }{23:---------------------------}| + {7: }{8: 6 } │{7: }{8: 9 } │{7: }{8: 6 } | + {1:~ }│{1:~ }│{1:~ }|*5 + {2:<test-functional-diff-screen-1.3 }{3:<functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :5,7diffget screen-1.3 | ]]) end) @@ -137,20 +126,20 @@ describe('Diff mode screen with 3 diffs open', function() feed('3<c-w>w') feed(':5,6diffget screen-1.2<cr>') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } │{1: }{10: 1 }^ | - {1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}| - {1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }| - {1: }{10: }{2:---------------------------}│{1: }{10: 5 }{9: AAA }│{1: }{10: 4 }{9: AAA }| - {1: }{10: }{2:---------------------------}│{1: }{10: 6 }{9: AAA }│{1: }{10: 5 }{9: AAA }| - {1: }{10: }{2:---------------------------}│{1: }{10: 7 }{9:======= }│{1: }{10: 6 }{9:======= }| - {1: }{10: 3 } BBB │{1: }{10: 8 } BBB │{1: }{10: 7 } BBB | - {1: }{10: 4 } BBB │{1: }{10: 9 } BBB │{1: }{10: 8 } BBB | - {1: }{10: 5 } BBB │{1: }{10: 10 } BBB │{1: }{10: 9 } BBB | - {1: }{10: }{2:---------------------------}│{1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: 10 }{9:>>>>>>> branch1 }| - {1: }{10: 6 } │{1: }{10: 12 } │{1: }{10: 11 } | - {6:~ }│{6:~ }│{6:~ }|*2 - {3:<test-functional-diff-screen-1.3 <est-functional-diff-screen-1.2 }{7:<st-functional-diff-screen-1 [+] }| + {7: }{8: 1 } │{7: }{8: 1 } │{7: }{8: 1 }^ | + {7: }{8: 2 }common line │{7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: }{23:---------------------------}│{7: }{8: 3 }{22:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}| + {7: }{8: }{23:---------------------------}│{7: }{8: 4 }{4: AAA }│{7: }{8: 3 }{4: AAA }| + {7: }{8: }{23:---------------------------}│{7: }{8: 5 }{4: AAA }│{7: }{8: 4 }{4: AAA }| + {7: }{8: }{23:---------------------------}│{7: }{8: 6 }{4: AAA }│{7: }{8: 5 }{4: AAA }| + {7: }{8: }{23:---------------------------}│{7: }{8: 7 }{4:======= }│{7: }{8: 6 }{4:======= }| + {7: }{8: 3 } BBB │{7: }{8: 8 } BBB │{7: }{8: 7 } BBB | + {7: }{8: 4 } BBB │{7: }{8: 9 } BBB │{7: }{8: 8 } BBB | + {7: }{8: 5 } BBB │{7: }{8: 10 } BBB │{7: }{8: 9 } BBB | + {7: }{8: }{23:---------------------------}│{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: 10 }{4:>>>>>>> branch1 }| + {7: }{8: 6 } │{7: }{8: 12 } │{7: }{8: 11 } | + {1:~ }│{1:~ }│{1:~ }|*2 + {2:<test-functional-diff-screen-1.3 <est-functional-diff-screen-1.2 }{3:<st-functional-diff-screen-1 [+] }| :5,6diffget screen-1.2 | ]]) end) @@ -159,20 +148,20 @@ describe('Diff mode screen with 3 diffs open', function() feed('2<c-w>w') feed(':6,8diffput screen-1<cr>') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 }^ │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}| - {1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }| - {1: }{10: }{2:---------------------------}│{1: }{10: 5 }{9: AAA }│{1: }{10: 4 }{9: AAA }| - {1: }{10: }{2:---------------------------}│{1: }{10: 6 }{9: AAA }│{1: }{10: 5 }{9: AAA }| - {1: }{10: }{2:---------------------------}│{1: }{10: 7 }{9:======= }│{1: }{10: 6 }{9:======= }| - {1: }{10: 3 }{9: BBB }│{1: }{10: 8 }{9: BBB }│{1: }{10: }{2:---------------------------}| - {1: }{10: 4 }{9: BBB }│{1: }{10: 9 }{9: BBB }│{1: }{10: }{2:---------------------------}| - {1: }{10: 5 } BBB │{1: }{10: 10 } BBB │{1: }{10: 7 } BBB | - {1: }{10: }{2:---------------------------}│{1: }{10: 11 }{4:>>>>>>> branch1 }│{1: }{10: }{2:---------------------------}| - {1: }{10: 6 } │{1: }{10: 12 } │{1: }{10: 8 } | - {6:~ }│{6:~ }│{6:~ }|*2 - {3:<test-functional-diff-screen-1.3 }{7:<est-functional-diff-screen-1.2 }{3:<st-functional-diff-screen-1 [+] }| + {7: }{8: 1 } │{7: }{8: 1 }^ │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: }{23:---------------------------}│{7: }{8: 3 }{22:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}| + {7: }{8: }{23:---------------------------}│{7: }{8: 4 }{4: AAA }│{7: }{8: 3 }{4: AAA }| + {7: }{8: }{23:---------------------------}│{7: }{8: 5 }{4: AAA }│{7: }{8: 4 }{4: AAA }| + {7: }{8: }{23:---------------------------}│{7: }{8: 6 }{4: AAA }│{7: }{8: 5 }{4: AAA }| + {7: }{8: }{23:---------------------------}│{7: }{8: 7 }{4:======= }│{7: }{8: 6 }{4:======= }| + {7: }{8: 3 }{4: BBB }│{7: }{8: 8 }{4: BBB }│{7: }{8: }{23:---------------------------}| + {7: }{8: 4 }{4: BBB }│{7: }{8: 9 }{4: BBB }│{7: }{8: }{23:---------------------------}| + {7: }{8: 5 } BBB │{7: }{8: 10 } BBB │{7: }{8: 7 } BBB | + {7: }{8: }{23:---------------------------}│{7: }{8: 11 }{22:>>>>>>> branch1 }│{7: }{8: }{23:---------------------------}| + {7: }{8: 6 } │{7: }{8: 12 } │{7: }{8: 8 } | + {1:~ }│{1:~ }│{1:~ }|*2 + {2:<test-functional-diff-screen-1.3 }{3:<est-functional-diff-screen-1.2 }{2:<st-functional-diff-screen-1 [+] }| :6,8diffput screen-1 | ]]) end) @@ -180,20 +169,20 @@ describe('Diff mode screen with 3 diffs open', function() feed('2<c-w>w') feed(':6,11diffput screen-1<cr>') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 }^ │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: }{2:---------------------------}│{1: }{10: 3 }{4:<<<<<<< HEAD }│{1: }{10: }{2:---------------------------}| - {1: }{10: }{2:---------------------------}│{1: }{10: 4 }{9: AAA }│{1: }{10: 3 }{9: AAA }| - {1: }{10: }{2:---------------------------}│{1: }{10: 5 }{9: AAA }│{1: }{10: 4 }{9: AAA }| - {1: }{10: }{2:---------------------------}│{1: }{10: 6 }{9: AAA }│{1: }{10: 5 }{9: AAA }| - {1: }{10: }{2:---------------------------}│{1: }{10: 7 }{9:======= }│{1: }{10: 6 }{9:======= }| - {1: }{10: 3 } BBB │{1: }{10: 8 } BBB │{1: }{10: 7 } BBB | - {1: }{10: 4 } BBB │{1: }{10: 9 } BBB │{1: }{10: 8 } BBB | - {1: }{10: 5 } BBB │{1: }{10: 10 } BBB │{1: }{10: 9 } BBB | - {1: }{10: }{2:---------------------------}│{1: }{10: 11 }{9:>>>>>>> branch1 }│{1: }{10: 10 }{9:>>>>>>> branch1 }| - {1: }{10: 6 } │{1: }{10: 12 } │{1: }{10: 11 } | - {6:~ }│{6:~ }│{6:~ }|*2 - {3:<test-functional-diff-screen-1.3 }{7:<est-functional-diff-screen-1.2 }{3:<st-functional-diff-screen-1 [+] }| + {7: }{8: 1 } │{7: }{8: 1 }^ │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: }{23:---------------------------}│{7: }{8: 3 }{22:<<<<<<< HEAD }│{7: }{8: }{23:---------------------------}| + {7: }{8: }{23:---------------------------}│{7: }{8: 4 }{4: AAA }│{7: }{8: 3 }{4: AAA }| + {7: }{8: }{23:---------------------------}│{7: }{8: 5 }{4: AAA }│{7: }{8: 4 }{4: AAA }| + {7: }{8: }{23:---------------------------}│{7: }{8: 6 }{4: AAA }│{7: }{8: 5 }{4: AAA }| + {7: }{8: }{23:---------------------------}│{7: }{8: 7 }{4:======= }│{7: }{8: 6 }{4:======= }| + {7: }{8: 3 } BBB │{7: }{8: 8 } BBB │{7: }{8: 7 } BBB | + {7: }{8: 4 } BBB │{7: }{8: 9 } BBB │{7: }{8: 8 } BBB | + {7: }{8: 5 } BBB │{7: }{8: 10 } BBB │{7: }{8: 9 } BBB | + {7: }{8: }{23:---------------------------}│{7: }{8: 11 }{4:>>>>>>> branch1 }│{7: }{8: 10 }{4:>>>>>>> branch1 }| + {7: }{8: 6 } │{7: }{8: 12 } │{7: }{8: 11 } | + {1:~ }│{1:~ }│{1:~ }|*2 + {2:<test-functional-diff-screen-1.3 }{3:<est-functional-diff-screen-1.2 }{2:<st-functional-diff-screen-1 [+] }| :6,11diffput screen-1 | ]]) end) @@ -229,18 +218,6 @@ describe('Diff mode screen with 2 diffs open', function() screen = Screen.new(100, 20) screen:attach() - screen:set_default_attr_ids({ - [1] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.Gray }, - [2] = { foreground = Screen.colors.Blue1, bold = true, background = Screen.colors.LightCyan1 }, - [3] = { reverse = true }, - [4] = { background = Screen.colors.LightBlue }, - [5] = { foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray }, - [6] = { foreground = Screen.colors.Blue1, bold = true }, - [7] = { reverse = true, bold = true }, - [8] = { background = Screen.colors.Red1, bold = true }, - [10] = { foreground = Screen.colors.Brown }, - [9] = { background = Screen.colors.Plum1 }, - }) feed('<c-w>=') feed(':windo set nu!<cr>') end) @@ -290,20 +267,20 @@ something feed('1<c-w>w') feed(':5,9diffget<cr>') screen:expect([[ - {1:+ }{10: 1 }{5:^+-- 7 lines: common line··················}│{1:+ }{10: 1 }{5:+-- 7 lines: common line···················}| - {1: }{10: 8 }xyz │{1: }{10: 8 }xyz | - {1: }{10: 9 }DEFabc │{1: }{10: 9 }DEFabc | - {1: }{10: 10 }DEFabc │{1: }{10: 10 }DEFabc | - {1: }{10: 11 }DEFabc │{1: }{10: 11 }DEFabc | - {1: }{10: 12 }common line │{1: }{10: 12 }common line | - {1: }{10: 13 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 14 }common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 15 }something │{1: }{10: 17 }something | - {1: }{10: 16 } │{1: }{10: 18 } | - {6:~ }│{6:~ }|*6 - {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| + {7:+ }{8: 1 }{13:^+-- 7 lines: common line··················}│{7:+ }{8: 1 }{13:+-- 7 lines: common line···················}| + {7: }{8: 8 }xyz │{7: }{8: 8 }xyz | + {7: }{8: 9 }DEFabc │{7: }{8: 9 }DEFabc | + {7: }{8: 10 }DEFabc │{7: }{8: 10 }DEFabc | + {7: }{8: 11 }DEFabc │{7: }{8: 11 }DEFabc | + {7: }{8: 12 }common line │{7: }{8: 12 }common line | + {7: }{8: 13 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 14 }common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 15 }something │{7: }{8: 17 }something | + {7: }{8: 16 } │{7: }{8: 18 } | + {1:~ }│{1:~ }|*6 + {3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :5,9diffget | ]]) end) @@ -311,23 +288,23 @@ something feed('2<c-w>w') feed(':5,10diffget<cr>') screen:expect([[ - {1:- }{10: 1 } │{1:- }{10: 1 }^ | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }ABCabc │{1: }{10: 5 }ABCabc | - {1: }{10: 6 }ABCabc │{1: }{10: 6 }ABCabc | - {1: }{10: 7 }ABCabc │{1: }{10: 7 }ABCabc | - {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 8 }{8:DEF}{9:abc }| - {1: }{10: 9 }common line │{1: }{10: 9 }common line | - {1: }{10: 10 }common line │{1: }{10: 10 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 11 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 12 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 13 }{4:DEF }| - {1: }{10: 12 }something │{1: }{10: 14 }something | - {1: }{10: 13 } │{1: }{10: 15 } | - {6:~ }│{6:~ }|*3 - {3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }| + {7:- }{8: 1 } │{7:- }{8: 1 }^ | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }ABCabc │{7: }{8: 5 }ABCabc | + {7: }{8: 6 }ABCabc │{7: }{8: 6 }ABCabc | + {7: }{8: 7 }ABCabc │{7: }{8: 7 }ABCabc | + {7: }{8: 8 }{27:ABC}{4:abc }│{7: }{8: 8 }{27:DEF}{4:abc }| + {7: }{8: 9 }common line │{7: }{8: 9 }common line | + {7: }{8: 10 }common line │{7: }{8: 10 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 11 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 12 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 13 }{22:DEF }| + {7: }{8: 12 }something │{7: }{8: 14 }something | + {7: }{8: 13 } │{7: }{8: 15 } | + {1:~ }│{1:~ }|*3 + {2:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| :5,10diffget | ]]) end) @@ -335,21 +312,21 @@ something feed('2<c-w>w') feed(':4,17diffget<cr>') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 }^ | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }ABCabc │{1: }{10: 5 }ABCabc | - {1: }{10: 6 }ABCabc │{1: }{10: 6 }ABCabc | - {1: }{10: 7 }ABCabc │{1: }{10: 7 }ABCabc | - {1: }{10: 8 }ABCabc │{1: }{10: 8 }ABCabc | - {1: }{10: 9 }common line │{1: }{10: 9 }common line | - {1: }{10: 10 }common line │{1: }{10: 10 }common line | - {1: }{10: 11 }common line │{1: }{10: 11 }common line | - {1: }{10: 12 }something │{1: }{10: 12 }something | - {1: }{10: 13 } │{1: }{10: 13 } | - {6:~ }│{6:~ }|*5 - {3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }| + {7: }{8: 1 } │{7: }{8: 1 }^ | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }ABCabc │{7: }{8: 5 }ABCabc | + {7: }{8: 6 }ABCabc │{7: }{8: 6 }ABCabc | + {7: }{8: 7 }ABCabc │{7: }{8: 7 }ABCabc | + {7: }{8: 8 }ABCabc │{7: }{8: 8 }ABCabc | + {7: }{8: 9 }common line │{7: }{8: 9 }common line | + {7: }{8: 10 }common line │{7: }{8: 10 }common line | + {7: }{8: 11 }common line │{7: }{8: 11 }common line | + {7: }{8: 12 }something │{7: }{8: 12 }something | + {7: }{8: 13 } │{7: }{8: 13 } | + {1:~ }│{1:~ }|*5 + {2:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| :4,17diffget | ]]) end) @@ -357,25 +334,25 @@ something feed('1<c-w>w') feed(':4,12diffget<cr>') screen:expect([[ - {1: }{10: 1 }^ │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }DEFabc │{1: }{10: 5 }DEFabc | - {1: }{10: 6 }xyz │{1: }{10: 6 }xyz | - {1: }{10: 7 }xyz │{1: }{10: 7 }xyz | - {1: }{10: 8 }xyz │{1: }{10: 8 }xyz | - {1: }{10: 9 }DEFabc │{1: }{10: 9 }DEFabc | - {1: }{10: 10 }DEFabc │{1: }{10: 10 }DEFabc | - {1: }{10: 11 }DEFabc │{1: }{10: 11 }DEFabc | - {1: }{10: 12 }common line │{1: }{10: 12 }common line | - {1: }{10: 13 }common line │{1: }{10: 13 }common line | - {1: }{10: 14 }DEF │{1: }{10: 14 }DEF | - {1: }{10: 15 }common line │{1: }{10: 15 }common line | - {1: }{10: 16 }DEF │{1: }{10: 16 }DEF | - {1: }{10: 17 }something │{1: }{10: 17 }something | - {1: }{10: 18 } │{1: }{10: 18 } | - {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 }^ │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }DEFabc │{7: }{8: 5 }DEFabc | + {7: }{8: 6 }xyz │{7: }{8: 6 }xyz | + {7: }{8: 7 }xyz │{7: }{8: 7 }xyz | + {7: }{8: 8 }xyz │{7: }{8: 8 }xyz | + {7: }{8: 9 }DEFabc │{7: }{8: 9 }DEFabc | + {7: }{8: 10 }DEFabc │{7: }{8: 10 }DEFabc | + {7: }{8: 11 }DEFabc │{7: }{8: 11 }DEFabc | + {7: }{8: 12 }common line │{7: }{8: 12 }common line | + {7: }{8: 13 }common line │{7: }{8: 13 }common line | + {7: }{8: 14 }DEF │{7: }{8: 14 }DEF | + {7: }{8: 15 }common line │{7: }{8: 15 }common line | + {7: }{8: 16 }DEF │{7: }{8: 16 }DEF | + {7: }{8: 17 }something │{7: }{8: 17 }something | + {7: }{8: 18 } │{7: }{8: 18 } | + {3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :4,12diffget | ]]) end) @@ -384,25 +361,25 @@ something feed('5gg') feed('do') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }^DEFabc │{1: }{10: 5 }DEFabc | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }| - {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }| - {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }| - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 12 }something │{1: }{10: 17 }something | - {1: }{10: 13 } │{1: }{10: 18 } | - {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }^DEFabc │{7: }{8: 5 }DEFabc | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }{27:ABC}{4:abc }│{7: }{8: 9 }{27:DEF}{4:abc }| + {7: }{8: 7 }{27:ABC}{4:abc }│{7: }{8: 10 }{27:DEF}{4:abc }| + {7: }{8: 8 }{27:ABC}{4:abc }│{7: }{8: 11 }{27:DEF}{4:abc }| + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 12 }something │{7: }{8: 17 }something | + {7: }{8: 13 } │{7: }{8: 18 } | + {3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -411,25 +388,25 @@ something feed('6gg') feed('do') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }^DEFabc │{1: }{10: 9 }DEFabc | - {1: }{10: 7 }DEFabc │{1: }{10: 10 }DEFabc | - {1: }{10: 8 }DEFabc │{1: }{10: 11 }DEFabc | - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 12 }something │{1: }{10: 17 }something | - {1: }{10: 13 } │{1: }{10: 18 } | - {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }^DEFabc │{7: }{8: 9 }DEFabc | + {7: }{8: 7 }DEFabc │{7: }{8: 10 }DEFabc | + {7: }{8: 8 }DEFabc │{7: }{8: 11 }DEFabc | + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 12 }something │{7: }{8: 17 }something | + {7: }{8: 13 } │{7: }{8: 18 } | + {3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -438,25 +415,25 @@ something feed('7gg') feed('do') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }DEFabc │{1: }{10: 9 }DEFabc | - {1: }{10: 7 }^DEFabc │{1: }{10: 10 }DEFabc | - {1: }{10: 8 }DEFabc │{1: }{10: 11 }DEFabc | - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 12 }something │{1: }{10: 17 }something | - {1: }{10: 13 } │{1: }{10: 18 } | - {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }DEFabc │{7: }{8: 9 }DEFabc | + {7: }{8: 7 }^DEFabc │{7: }{8: 10 }DEFabc | + {7: }{8: 8 }DEFabc │{7: }{8: 11 }DEFabc | + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 12 }something │{7: }{8: 17 }something | + {7: }{8: 13 } │{7: }{8: 18 } | + {3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -465,25 +442,25 @@ something feed('11gg') feed('do') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }| - {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }| - {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }| - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: 11 }DEF │{1: }{10: 14 }DEF | - {1: }{10: 12 }^common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 13 }something │{1: }{10: 17 }something | - {1: }{10: 14 } │{1: }{10: 18 } | - {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }{27:ABC}{4:abc }│{7: }{8: 9 }{27:DEF}{4:abc }| + {7: }{8: 7 }{27:ABC}{4:abc }│{7: }{8: 10 }{27:DEF}{4:abc }| + {7: }{8: 8 }{27:ABC}{4:abc }│{7: }{8: 11 }{27:DEF}{4:abc }| + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: 11 }DEF │{7: }{8: 14 }DEF | + {7: }{8: 12 }^common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 13 }something │{7: }{8: 17 }something | + {7: }{8: 14 } │{7: }{8: 18 } | + {3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -492,25 +469,25 @@ something feed('12gg') feed('do') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }| - {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }| - {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }| - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 15 }common line | - {1: }{10: 12 }DEF │{1: }{10: 16 }DEF | - {1: }{10: 13 }^something │{1: }{10: 17 }something | - {1: }{10: 14 } │{1: }{10: 18 } | - {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }{27:ABC}{4:abc }│{7: }{8: 9 }{27:DEF}{4:abc }| + {7: }{8: 7 }{27:ABC}{4:abc }│{7: }{8: 10 }{27:DEF}{4:abc }| + {7: }{8: 8 }{27:ABC}{4:abc }│{7: }{8: 11 }{27:DEF}{4:abc }| + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 15 }common line | + {7: }{8: 12 }DEF │{7: }{8: 16 }DEF | + {7: }{8: 13 }^something │{7: }{8: 17 }something | + {7: }{8: 14 } │{7: }{8: 18 } | + {3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -519,25 +496,25 @@ something feed('5gg') feed('dp') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }^ABCabc │{1: }{10: 5 }ABCabc | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }| - {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }| - {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }| - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 12 }something │{1: }{10: 17 }something | - {1: }{10: 13 } │{1: }{10: 18 } | - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }^ABCabc │{7: }{8: 5 }ABCabc | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }{27:ABC}{4:abc }│{7: }{8: 9 }{27:DEF}{4:abc }| + {7: }{8: 7 }{27:ABC}{4:abc }│{7: }{8: 10 }{27:DEF}{4:abc }| + {7: }{8: 8 }{27:ABC}{4:abc }│{7: }{8: 11 }{27:DEF}{4:abc }| + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 12 }something │{7: }{8: 17 }something | + {7: }{8: 13 } │{7: }{8: 18 } | + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 [+] }| :e | ]]) end) @@ -546,25 +523,25 @@ something feed('6gg') feed('dp') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }^ABCabc │{1: }{10: 9 }ABCabc | - {1: }{10: 7 }ABCabc │{1: }{10: 10 }ABCabc | - {1: }{10: 8 }ABCabc │{1: }{10: 11 }ABCabc | - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 12 }something │{1: }{10: 17 }something | - {1: }{10: 13 } │{1: }{10: 18 } | - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }^ABCabc │{7: }{8: 9 }ABCabc | + {7: }{8: 7 }ABCabc │{7: }{8: 10 }ABCabc | + {7: }{8: 8 }ABCabc │{7: }{8: 11 }ABCabc | + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 12 }something │{7: }{8: 17 }something | + {7: }{8: 13 } │{7: }{8: 18 } | + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 [+] }| :e | ]]) end) @@ -573,25 +550,25 @@ something feed('7gg') feed('dp') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }ABCabc │{1: }{10: 9 }ABCabc | - {1: }{10: 7 }^ABCabc │{1: }{10: 10 }ABCabc | - {1: }{10: 8 }ABCabc │{1: }{10: 11 }ABCabc | - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 12 }something │{1: }{10: 17 }something | - {1: }{10: 13 } │{1: }{10: 18 } | - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }ABCabc │{7: }{8: 9 }ABCabc | + {7: }{8: 7 }^ABCabc │{7: }{8: 10 }ABCabc | + {7: }{8: 8 }ABCabc │{7: }{8: 11 }ABCabc | + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 12 }something │{7: }{8: 17 }something | + {7: }{8: 13 } │{7: }{8: 18 } | + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 [+] }| :e | ]]) end) @@ -600,25 +577,25 @@ something feed('11gg') feed('dp') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }| - {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }| - {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }| - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: 11 }^common line │{1: }{10: 14 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 15 }{4:DEF }| - {1: }{10: 12 }something │{1: }{10: 16 }something | - {1: }{10: 13 } │{1: }{10: 17 } | - {6:~ }│{6:~ }| - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }{27:ABC}{4:abc }│{7: }{8: 9 }{27:DEF}{4:abc }| + {7: }{8: 7 }{27:ABC}{4:abc }│{7: }{8: 10 }{27:DEF}{4:abc }| + {7: }{8: 8 }{27:ABC}{4:abc }│{7: }{8: 11 }{27:DEF}{4:abc }| + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: 11 }^common line │{7: }{8: 14 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 15 }{22:DEF }| + {7: }{8: 12 }something │{7: }{8: 16 }something | + {7: }{8: 13 } │{7: }{8: 17 } | + {1:~ }│{1:~ }| + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 [+] }| :e | ]]) end) @@ -627,25 +604,25 @@ something feed('12gg') feed('dp') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }| - {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }| - {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }| - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 15 }common line | - {1: }{10: 12 }^something │{1: }{10: 16 }something | - {1: }{10: 13 } │{1: }{10: 17 } | - {6:~ }│{6:~ }| - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }{27:ABC}{4:abc }│{7: }{8: 9 }{27:DEF}{4:abc }| + {7: }{8: 7 }{27:ABC}{4:abc }│{7: }{8: 10 }{27:DEF}{4:abc }| + {7: }{8: 8 }{27:ABC}{4:abc }│{7: }{8: 11 }{27:DEF}{4:abc }| + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 15 }common line | + {7: }{8: 12 }^something │{7: }{8: 16 }something | + {7: }{8: 13 } │{7: }{8: 17 } | + {1:~ }│{1:~ }| + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 [+] }| :e | ]]) end) @@ -654,25 +631,25 @@ something feed('6gg') feed('dp') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: 6 }xyz │{1: }{10: 6 }^xyz | - {1: }{10: 7 }xyz │{1: }{10: 7 }xyz | - {1: }{10: 8 }xyz │{1: }{10: 8 }xyz | - {1: }{10: 9 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }| - {1: }{10: 10 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }| - {1: }{10: 11 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }| - {1: }{10: 12 }common line │{1: }{10: 12 }common line | - {1: }{10: 13 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 14 }common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 15 }something │{1: }{10: 17 }something | - {1: }{10: 16 } │{1: }{10: 18 } | - {3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: 6 }xyz │{7: }{8: 6 }^xyz | + {7: }{8: 7 }xyz │{7: }{8: 7 }xyz | + {7: }{8: 8 }xyz │{7: }{8: 8 }xyz | + {7: }{8: 9 }{27:ABC}{4:abc }│{7: }{8: 9 }{27:DEF}{4:abc }| + {7: }{8: 10 }{27:ABC}{4:abc }│{7: }{8: 10 }{27:DEF}{4:abc }| + {7: }{8: 11 }{27:ABC}{4:abc }│{7: }{8: 11 }{27:DEF}{4:abc }| + {7: }{8: 12 }common line │{7: }{8: 12 }common line | + {7: }{8: 13 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 14 }common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 15 }something │{7: }{8: 17 }something | + {7: }{8: 16 } │{7: }{8: 18 } | + {2:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -681,25 +658,25 @@ something feed('8gg') feed('dp') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: 6 }xyz │{1: }{10: 6 }xyz | - {1: }{10: 7 }xyz │{1: }{10: 7 }xyz | - {1: }{10: 8 }xyz │{1: }{10: 8 }^xyz | - {1: }{10: 9 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }| - {1: }{10: 10 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }| - {1: }{10: 11 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }| - {1: }{10: 12 }common line │{1: }{10: 12 }common line | - {1: }{10: 13 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 14 }common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 15 }something │{1: }{10: 17 }something | - {1: }{10: 16 } │{1: }{10: 18 } | - {3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: 6 }xyz │{7: }{8: 6 }xyz | + {7: }{8: 7 }xyz │{7: }{8: 7 }xyz | + {7: }{8: 8 }xyz │{7: }{8: 8 }^xyz | + {7: }{8: 9 }{27:ABC}{4:abc }│{7: }{8: 9 }{27:DEF}{4:abc }| + {7: }{8: 10 }{27:ABC}{4:abc }│{7: }{8: 10 }{27:DEF}{4:abc }| + {7: }{8: 11 }{27:ABC}{4:abc }│{7: }{8: 11 }{27:DEF}{4:abc }| + {7: }{8: 12 }common line │{7: }{8: 12 }common line | + {7: }{8: 13 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 14 }common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 15 }something │{7: }{8: 17 }something | + {7: }{8: 16 } │{7: }{8: 18 } | + {2:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -708,25 +685,25 @@ something feed('9gg') feed('dp') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }DEFabc │{1: }{10: 9 }^DEFabc | - {1: }{10: 7 }DEFabc │{1: }{10: 10 }DEFabc | - {1: }{10: 8 }DEFabc │{1: }{10: 11 }DEFabc | - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 15 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 16 }{4:DEF }| - {1: }{10: 12 }something │{1: }{10: 17 }something | - {1: }{10: 13 } │{1: }{10: 18 } | - {3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }DEFabc │{7: }{8: 9 }^DEFabc | + {7: }{8: 7 }DEFabc │{7: }{8: 10 }DEFabc | + {7: }{8: 8 }DEFabc │{7: }{8: 11 }DEFabc | + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 15 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 16 }{22:DEF }| + {7: }{8: 12 }something │{7: }{8: 17 }something | + {7: }{8: 13 } │{7: }{8: 18 } | + {2:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -735,25 +712,25 @@ something feed('17gg') feed('dp') screen:expect([[ - {1: }{10: 1 } │{1: }{10: 1 } | - {1: }{10: 2 }common line │{1: }{10: 2 }common line | - {1: }{10: 3 }common line │{1: }{10: 3 }common line | - {1: }{10: 4 } │{1: }{10: 4 } | - {1: }{10: 5 }{8:ABC}{9:abc }│{1: }{10: 5 }{8:DEF}{9:abc }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 6 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 7 }{4:xyz }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 8 }{4:xyz }| - {1: }{10: 6 }{8:ABC}{9:abc }│{1: }{10: 9 }{8:DEF}{9:abc }| - {1: }{10: 7 }{8:ABC}{9:abc }│{1: }{10: 10 }{8:DEF}{9:abc }| - {1: }{10: 8 }{8:ABC}{9:abc }│{1: }{10: 11 }{8:DEF}{9:abc }| - {1: }{10: 9 }common line │{1: }{10: 12 }common line | - {1: }{10: 10 }common line │{1: }{10: 13 }common line | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 14 }{4:DEF }| - {1: }{10: 11 }common line │{1: }{10: 15 }common line | - {1: }{10: 12 }DEF │{1: }{10: 16 }DEF | - {1: }{10: 13 }something │{1: }{10: 17 }^something | - {1: }{10: 14 } │{1: }{10: 18 } | - {3:Xtest-functional-diff-screen-1.2 [+] }{7:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 } │{7: }{8: 1 } | + {7: }{8: 2 }common line │{7: }{8: 2 }common line | + {7: }{8: 3 }common line │{7: }{8: 3 }common line | + {7: }{8: 4 } │{7: }{8: 4 } | + {7: }{8: 5 }{27:ABC}{4:abc }│{7: }{8: 5 }{27:DEF}{4:abc }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 6 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 7 }{22:xyz }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 8 }{22:xyz }| + {7: }{8: 6 }{27:ABC}{4:abc }│{7: }{8: 9 }{27:DEF}{4:abc }| + {7: }{8: 7 }{27:ABC}{4:abc }│{7: }{8: 10 }{27:DEF}{4:abc }| + {7: }{8: 8 }{27:ABC}{4:abc }│{7: }{8: 11 }{27:DEF}{4:abc }| + {7: }{8: 9 }common line │{7: }{8: 12 }common line | + {7: }{8: 10 }common line │{7: }{8: 13 }common line | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 14 }{22:DEF }| + {7: }{8: 11 }common line │{7: }{8: 15 }common line | + {7: }{8: 12 }DEF │{7: }{8: 16 }DEF | + {7: }{8: 13 }something │{7: }{8: 17 }^something | + {7: }{8: 14 } │{7: }{8: 18 } | + {2:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -778,13 +755,13 @@ d it('display results', function() screen:expect([[ - {1: }{10: 1 }{4:^ }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 2 }{9:abc d }│{1: }{10: 1 }{8:// }{9:abc d }| - {1: }{10: 3 }{9:d }│{1: }{10: 2 }{8:// }{9:d }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 3 }{4:// d }| - {1: }{10: 4 } │{1: }{10: 4 } | - {6:~ }│{6:~ }|*13 - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 }{22:^ }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 2 }{4:abc d }│{7: }{8: 1 }{27:// }{4:abc d }| + {7: }{8: 3 }{4:d }│{7: }{8: 2 }{27:// }{4:d }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 3 }{22:// d }| + {7: }{8: 4 } │{7: }{8: 4 } | + {1:~ }│{1:~ }|*13 + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -813,15 +790,15 @@ void testFunction () { it('display results', function() screen:expect([[ - {1: }{10: 1 }^void testFunction () { │{1: }{10: 1 }void testFunction () { | - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 2 }{4: for (int i = 0; i < 10; i++) { }| - {1: }{10: 2 }{9: }{8:// for (int j = 0; j < 10; i}{9:++) { }│{1: }{10: 3 }{9: }{8:for (int j = 0; j < 10; j}{9:++) { }| - {1: }{10: 3 }{9: }{8:// }{9:} }│{1: }{10: 4 }{9: } }| - {1: }{10: }{2:-------------------------------------------}│{1: }{10: 5 }{4: } }| - {1: }{10: 4 }} │{1: }{10: 6 }} | - {1: }{10: 5 } │{1: }{10: 7 } | - {6:~ }│{6:~ }|*11 - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 }^void testFunction () { │{7: }{8: 1 }void testFunction () { | + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 2 }{22: for (int i = 0; i < 10; i++) { }| + {7: }{8: 2 }{4: }{27:// for (int j = 0; j < 10; i}{4:++) { }│{7: }{8: 3 }{4: }{27:for (int j = 0; j < 10; j}{4:++) { }| + {7: }{8: 3 }{4: }{27:// }{4:} }│{7: }{8: 4 }{4: } }| + {7: }{8: }{23:-------------------------------------------}│{7: }{8: 5 }{22: } }| + {7: }{8: 4 }} │{7: }{8: 6 }} | + {7: }{8: 5 } │{7: }{8: 7 } | + {1:~ }│{1:~ }|*11 + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -851,17 +828,17 @@ void testFunction () { it('display results', function() screen:expect([[ - {1: }{10: 1 }{4:^?Z }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 2 }{8:?}{9:A }│{1: }{10: 1 }{8:!}{9:A }| - {1: }{10: 3 }{8:?}{9:B }│{1: }{10: 2 }{8:!}{9:B }| - {1: }{10: 4 }{8:?}{9:C }│{1: }{10: 3 }{8:!}{9:C }| - {1: }{10: 5 }{4:?A }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 6 }{4:?B }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 7 }{4:?B }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 8 }{4:?C }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 9 } │{1: }{10: 4 } | - {6:~ }│{6:~ }|*9 - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 }{22:^?Z }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 2 }{27:?}{4:A }│{7: }{8: 1 }{27:!}{4:A }| + {7: }{8: 3 }{27:?}{4:B }│{7: }{8: 2 }{27:!}{4:B }| + {7: }{8: 4 }{27:?}{4:C }│{7: }{8: 3 }{27:!}{4:C }| + {7: }{8: 5 }{22:?A }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 6 }{22:?B }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 7 }{22:?B }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 8 }{22:?C }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 9 } │{7: }{8: 4 } | + {1:~ }│{1:~ }|*9 + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -891,17 +868,17 @@ void testFunction () { it('display results', function() screen:expect([[ - {1: }{10: 1 }{4:^?A }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 2 }{4:?Z }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 3 }{4:?B }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 4 }{4:?C }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 5 }{8:?}{9:A }│{1: }{10: 1 }{8:!}{9:A }| - {1: }{10: 6 }{8:?}{9:B }│{1: }{10: 2 }{8:!}{9:B }| - {1: }{10: 7 }{8:?}{9:C }│{1: }{10: 3 }{8:!}{9:C }| - {1: }{10: 8 }{4:?C }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 9 } │{1: }{10: 4 } | - {6:~ }│{6:~ }|*9 - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 }{22:^?A }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 2 }{22:?Z }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 3 }{22:?B }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 4 }{22:?C }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 5 }{27:?}{4:A }│{7: }{8: 1 }{27:!}{4:A }| + {7: }{8: 6 }{27:?}{4:B }│{7: }{8: 2 }{27:!}{4:B }| + {7: }{8: 7 }{27:?}{4:C }│{7: }{8: 3 }{27:!}{4:C }| + {7: }{8: 8 }{22:?C }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 9 } │{7: }{8: 4 } | + {1:~ }│{1:~ }|*9 + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }| :e | ]]) end) @@ -953,50 +930,50 @@ something feed('1<c-w>w') -- linematch is disabled for the longest diff because it's combined line length is over 10 screen:expect([[ - {1: }{10: 1 }^common line │{1: }{10: 1 }common line | - {1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }| - {1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 5 } │{1: }{10: 3 } | - {1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }| - {1: }{10: 7 }{8:xyz}{9: }│{1: }{10: 5 }{8:aABCabc}{9: }| - {1: }{10: 8 }{8:xyz}{9: }│{1: }{10: 6 }{8:aABCabc}{9: }| - {1: }{10: 9 }{8:xyz}{9: }│{1: }{10: 7 }{8:aABCabc}{9: }| - {1: }{10: 10 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 11 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 12 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 13 }common line │{1: }{10: 8 }common line | - {1: }{10: 14 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 15 }{8:GHI}{9: }│{1: }{10: 9 }{8:HIL}{9: }| - {1: }{10: 16 }{4:something else }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 17 }common line │{1: }{10: 10 }common line | - {1: }{10: 18 }something │{1: }{10: 11 }something | - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 }^common line │{7: }{8: 1 }common line | + {7: }{8: 2 }{22:DEF }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 3 }{27:GHI}{4: }│{7: }{8: 2 }{27:HIL}{4: }| + {7: }{8: 4 }{22:something }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 5 } │{7: }{8: 3 } | + {7: }{8: 6 }{4:a}{27:DEF}{4:abc }│{7: }{8: 4 }{4:a}{27:ABC}{4:abc }| + {7: }{8: 7 }{27:xyz}{4: }│{7: }{8: 5 }{27:aABCabc}{4: }| + {7: }{8: 8 }{27:xyz}{4: }│{7: }{8: 6 }{27:aABCabc}{4: }| + {7: }{8: 9 }{27:xyz}{4: }│{7: }{8: 7 }{27:aABCabc}{4: }| + {7: }{8: 10 }{22:aDEFabc }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 11 }{22:aDEFabc }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 12 }{22:aDEFabc }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 13 }common line │{7: }{8: 8 }common line | + {7: }{8: 14 }{22:DEF }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 15 }{27:GHI}{4: }│{7: }{8: 9 }{27:HIL}{4: }| + {7: }{8: 16 }{22:something else }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 17 }common line │{7: }{8: 10 }common line | + {7: }{8: 18 }something │{7: }{8: 11 }something | + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }| :e | ]]) -- enable it by increasing the number feed(':set diffopt-=linematch:10<cr>') feed(':set diffopt+=linematch:30<cr>') screen:expect([[ - {1: }{10: 1 }^common line │{1: }{10: 1 }common line | - {1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }| - {1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 5 } │{1: }{10: 3 } | - {1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }| - {1: }{10: 7 }{4:xyz }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 8 }{4:xyz }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 9 }{4:xyz }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 10 }{9:a}{8:DEF}{9:abc }│{1: }{10: 5 }{9:a}{8:ABC}{9:abc }| - {1: }{10: 11 }{9:a}{8:DEF}{9:abc }│{1: }{10: 6 }{9:a}{8:ABC}{9:abc }| - {1: }{10: 12 }{9:a}{8:DEF}{9:abc }│{1: }{10: 7 }{9:a}{8:ABC}{9:abc }| - {1: }{10: 13 }common line │{1: }{10: 8 }common line | - {1: }{10: 14 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 15 }{8:GHI}{9: }│{1: }{10: 9 }{8:HIL}{9: }| - {1: }{10: 16 }{4:something else }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 17 }common line │{1: }{10: 10 }common line | - {1: }{10: 18 }something │{1: }{10: 11 }something | - {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 }^common line │{7: }{8: 1 }common line | + {7: }{8: 2 }{22:DEF }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 3 }{27:GHI}{4: }│{7: }{8: 2 }{27:HIL}{4: }| + {7: }{8: 4 }{22:something }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 5 } │{7: }{8: 3 } | + {7: }{8: 6 }{4:a}{27:DEF}{4:abc }│{7: }{8: 4 }{4:a}{27:ABC}{4:abc }| + {7: }{8: 7 }{22:xyz }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 8 }{22:xyz }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 9 }{22:xyz }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 10 }{4:a}{27:DEF}{4:abc }│{7: }{8: 5 }{4:a}{27:ABC}{4:abc }| + {7: }{8: 11 }{4:a}{27:DEF}{4:abc }│{7: }{8: 6 }{4:a}{27:ABC}{4:abc }| + {7: }{8: 12 }{4:a}{27:DEF}{4:abc }│{7: }{8: 7 }{4:a}{27:ABC}{4:abc }| + {7: }{8: 13 }common line │{7: }{8: 8 }common line | + {7: }{8: 14 }{22:DEF }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 15 }{27:GHI}{4: }│{7: }{8: 9 }{27:HIL}{4: }| + {7: }{8: 16 }{22:something else }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 17 }common line │{7: }{8: 10 }common line | + {7: }{8: 18 }something │{7: }{8: 11 }something | + {3:Xtest-functional-diff-screen-1.2 }{2:Xtest-functional-diff-screen-1 }| :set diffopt+=linematch:30 | ]]) end @@ -1005,25 +982,25 @@ something feed('2<c-w>w') feed(':1,12diffget<cr>') screen:expect([[ - {1: }{10: 1 }common line │{1: }{10: 1 }^common line | - {1: }{10: 2 }DEF │{1: }{10: 2 }DEF | - {1: }{10: 3 }GHI │{1: }{10: 3 }GHI | - {1: }{10: 4 }something │{1: }{10: 4 }something | - {1: }{10: 5 } │{1: }{10: 5 } | - {1: }{10: 6 }aDEFabc │{1: }{10: 6 }aDEFabc | - {1: }{10: 7 }xyz │{1: }{10: 7 }xyz | - {1: }{10: 8 }xyz │{1: }{10: 8 }xyz | - {1: }{10: 9 }xyz │{1: }{10: 9 }xyz | - {1: }{10: 10 }aDEFabc │{1: }{10: 10 }aDEFabc | - {1: }{10: 11 }aDEFabc │{1: }{10: 11 }aDEFabc | - {1: }{10: 12 }aDEFabc │{1: }{10: 12 }aDEFabc | - {1: }{10: 13 }common line │{1: }{10: 13 }common line | - {1: }{10: 14 }DEF │{1: }{10: 14 }DEF | - {1: }{10: 15 }GHI │{1: }{10: 15 }GHI | - {1: }{10: 16 }something else │{1: }{10: 16 }something else | - {1: }{10: 17 }common line │{1: }{10: 17 }common line | - {1: }{10: 18 }something │{1: }{10: 18 }something | - {3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }| + {7: }{8: 1 }common line │{7: }{8: 1 }^common line | + {7: }{8: 2 }DEF │{7: }{8: 2 }DEF | + {7: }{8: 3 }GHI │{7: }{8: 3 }GHI | + {7: }{8: 4 }something │{7: }{8: 4 }something | + {7: }{8: 5 } │{7: }{8: 5 } | + {7: }{8: 6 }aDEFabc │{7: }{8: 6 }aDEFabc | + {7: }{8: 7 }xyz │{7: }{8: 7 }xyz | + {7: }{8: 8 }xyz │{7: }{8: 8 }xyz | + {7: }{8: 9 }xyz │{7: }{8: 9 }xyz | + {7: }{8: 10 }aDEFabc │{7: }{8: 10 }aDEFabc | + {7: }{8: 11 }aDEFabc │{7: }{8: 11 }aDEFabc | + {7: }{8: 12 }aDEFabc │{7: }{8: 12 }aDEFabc | + {7: }{8: 13 }common line │{7: }{8: 13 }common line | + {7: }{8: 14 }DEF │{7: }{8: 14 }DEF | + {7: }{8: 15 }GHI │{7: }{8: 15 }GHI | + {7: }{8: 16 }something else │{7: }{8: 16 }something else | + {7: }{8: 17 }common line │{7: }{8: 17 }common line | + {7: }{8: 18 }something │{7: }{8: 18 }something | + {2:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| :1,12diffget | ]]) end) @@ -1031,20 +1008,20 @@ something feed('1<c-w>w') feed(':1,19diffget<cr>') screen:expect([[ - {1: }{10: 1 }^common line │{1: }{10: 1 }common line | - {1: }{10: 2 }HIL │{1: }{10: 2 }HIL | - {1: }{10: 3 } │{1: }{10: 3 } | - {1: }{10: 4 }aABCabc │{1: }{10: 4 }aABCabc | - {1: }{10: 5 }aABCabc │{1: }{10: 5 }aABCabc | - {1: }{10: 6 }aABCabc │{1: }{10: 6 }aABCabc | - {1: }{10: 7 }aABCabc │{1: }{10: 7 }aABCabc | - {1: }{10: 8 }common line │{1: }{10: 8 }common line | - {1: }{10: 9 }HIL │{1: }{10: 9 }HIL | - {1: }{10: 10 }common line │{1: }{10: 10 }common line | - {1: }{10: 11 }something │{1: }{10: 11 }something | - {1: }{10: 12 } │{1: }{10: 12 } | - {6:~ }│{6:~ }|*6 - {7:Xtest-functional-diff-screen-1.2 [+] }{3:Xtest-functional-diff-screen-1 }| + {7: }{8: 1 }^common line │{7: }{8: 1 }common line | + {7: }{8: 2 }HIL │{7: }{8: 2 }HIL | + {7: }{8: 3 } │{7: }{8: 3 } | + {7: }{8: 4 }aABCabc │{7: }{8: 4 }aABCabc | + {7: }{8: 5 }aABCabc │{7: }{8: 5 }aABCabc | + {7: }{8: 6 }aABCabc │{7: }{8: 6 }aABCabc | + {7: }{8: 7 }aABCabc │{7: }{8: 7 }aABCabc | + {7: }{8: 8 }common line │{7: }{8: 8 }common line | + {7: }{8: 9 }HIL │{7: }{8: 9 }HIL | + {7: }{8: 10 }common line │{7: }{8: 10 }common line | + {7: }{8: 11 }something │{7: }{8: 11 }something | + {7: }{8: 12 } │{7: }{8: 12 } | + {1:~ }│{1:~ }|*6 + {3:Xtest-functional-diff-screen-1.2 [+] }{2:Xtest-functional-diff-screen-1 }| :1,19diffget | ]]) end) @@ -1054,25 +1031,25 @@ something feed('2<c-w>w') feed(':7,8diffget<cr>') screen:expect([[ - {1: }{10: 1 }common line │{1: }{10: 1 }^common line | - {1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }| - {1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 5 } │{1: }{10: 3 } | - {1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }| - {1: }{10: 7 }{8:xyz}{9: }│{1: }{10: 5 }{8:aABCabc}{9: }| - {1: }{10: 8 }{8:xyz}{9: }│{1: }{10: 6 }{8:aABCabc}{9: }| - {1: }{10: 9 }xyz │{1: }{10: 7 }xyz | - {1: }{10: 10 }aDEFabc │{1: }{10: 8 }aDEFabc | - {1: }{10: 11 }aDEFabc │{1: }{10: 9 }aDEFabc | - {1: }{10: 12 }aDEFabc │{1: }{10: 10 }aDEFabc | - {1: }{10: 13 }common line │{1: }{10: 11 }common line | - {1: }{10: 14 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 15 }{8:GHI}{9: }│{1: }{10: 12 }{8:HIL}{9: }| - {1: }{10: 16 }{4:something else }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 17 }common line │{1: }{10: 13 }common line | - {1: }{10: 18 }something │{1: }{10: 14 }something | - {3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }| + {7: }{8: 1 }common line │{7: }{8: 1 }^common line | + {7: }{8: 2 }{22:DEF }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 3 }{27:GHI}{4: }│{7: }{8: 2 }{27:HIL}{4: }| + {7: }{8: 4 }{22:something }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 5 } │{7: }{8: 3 } | + {7: }{8: 6 }{4:a}{27:DEF}{4:abc }│{7: }{8: 4 }{4:a}{27:ABC}{4:abc }| + {7: }{8: 7 }{27:xyz}{4: }│{7: }{8: 5 }{27:aABCabc}{4: }| + {7: }{8: 8 }{27:xyz}{4: }│{7: }{8: 6 }{27:aABCabc}{4: }| + {7: }{8: 9 }xyz │{7: }{8: 7 }xyz | + {7: }{8: 10 }aDEFabc │{7: }{8: 8 }aDEFabc | + {7: }{8: 11 }aDEFabc │{7: }{8: 9 }aDEFabc | + {7: }{8: 12 }aDEFabc │{7: }{8: 10 }aDEFabc | + {7: }{8: 13 }common line │{7: }{8: 11 }common line | + {7: }{8: 14 }{22:DEF }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 15 }{27:GHI}{4: }│{7: }{8: 12 }{27:HIL}{4: }| + {7: }{8: 16 }{22:something else }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 17 }common line │{7: }{8: 13 }common line | + {7: }{8: 18 }something │{7: }{8: 14 }something | + {2:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| :7,8diffget | ]]) end @@ -1083,25 +1060,25 @@ something feed('2<c-w>w') feed(':8,10diffget<cr>') screen:expect([[ - {1: }{10: 1 }common line │{1: }{10: 1 }^common line | - {1: }{10: 2 }{4:DEF }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 3 }{8:GHI}{9: }│{1: }{10: 2 }{8:HIL}{9: }| - {1: }{10: 4 }{4:something }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 5 } │{1: }{10: 3 } | - {1: }{10: 6 }{9:a}{8:DEF}{9:abc }│{1: }{10: 4 }{9:a}{8:ABC}{9:abc }| - {1: }{10: 7 }{8:xyz}{9: }│{1: }{10: 5 }{8:aABCabc}{9: }| - {1: }{10: 8 }{8:xyz}{9: }│{1: }{10: 6 }{8:aABCabc}{9: }| - {1: }{10: 9 }{8:xyz}{9: }│{1: }{10: 7 }{8:aABCabc}{9: }| - {1: }{10: 10 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 11 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 12 }{4:aDEFabc }│{1: }{10: }{2:--------------------------------------------}| - {1: }{10: 13 }common line │{1: }{10: 8 }common line | - {1: }{10: 14 }DEF │{1: }{10: 9 }DEF | - {1: }{10: 15 }GHI │{1: }{10: 10 }GHI | - {1: }{10: 16 }something else │{1: }{10: 11 }something else | - {1: }{10: 17 }common line │{1: }{10: 12 }common line | - {1: }{10: 18 }something │{1: }{10: 13 }something | - {3:Xtest-functional-diff-screen-1.2 }{7:Xtest-functional-diff-screen-1 [+] }| + {7: }{8: 1 }common line │{7: }{8: 1 }^common line | + {7: }{8: 2 }{22:DEF }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 3 }{27:GHI}{4: }│{7: }{8: 2 }{27:HIL}{4: }| + {7: }{8: 4 }{22:something }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 5 } │{7: }{8: 3 } | + {7: }{8: 6 }{4:a}{27:DEF}{4:abc }│{7: }{8: 4 }{4:a}{27:ABC}{4:abc }| + {7: }{8: 7 }{27:xyz}{4: }│{7: }{8: 5 }{27:aABCabc}{4: }| + {7: }{8: 8 }{27:xyz}{4: }│{7: }{8: 6 }{27:aABCabc}{4: }| + {7: }{8: 9 }{27:xyz}{4: }│{7: }{8: 7 }{27:aABCabc}{4: }| + {7: }{8: 10 }{22:aDEFabc }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 11 }{22:aDEFabc }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 12 }{22:aDEFabc }│{7: }{8: }{23:--------------------------------------------}| + {7: }{8: 13 }common line │{7: }{8: 8 }common line | + {7: }{8: 14 }DEF │{7: }{8: 9 }DEF | + {7: }{8: 15 }GHI │{7: }{8: 10 }GHI | + {7: }{8: 16 }something else │{7: }{8: 11 }something else | + {7: }{8: 17 }common line │{7: }{8: 12 }common line | + {7: }{8: 18 }something │{7: }{8: 13 }something | + {2:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 [+] }| :8,10diffget | ]]) end @@ -1118,10 +1095,10 @@ describe('regressions', function() screen = Screen.new(100, 20) screen:attach() -- line must be greater than MATCH_CHAR_MAX_LEN - helpers.api.nvim_buf_set_lines(0, 0, -1, false, { string.rep('a', 1000) .. 'hello' }) - helpers.exec 'vnew' - helpers.api.nvim_buf_set_lines(0, 0, -1, false, { string.rep('a', 1010) .. 'world' }) - helpers.exec 'windo diffthis' + n.api.nvim_buf_set_lines(0, 0, -1, false, { string.rep('a', 1000) .. 'hello' }) + n.exec 'vnew' + n.api.nvim_buf_set_lines(0, 0, -1, false, { string.rep('a', 1010) .. 'world' }) + n.exec 'windo diffthis' end) it('properly computes filler lines for hunks bigger than linematch limit', function() @@ -1133,10 +1110,10 @@ describe('regressions', function() for i = 0, 29 do lines[#lines + 1] = tostring(i) end - helpers.api.nvim_buf_set_lines(0, 0, -1, false, lines) - helpers.exec 'vnew' - helpers.api.nvim_buf_set_lines(0, 0, -1, false, { '00', '29' }) - helpers.exec 'windo diffthis' + n.api.nvim_buf_set_lines(0, 0, -1, false, lines) + n.exec 'vnew' + n.api.nvim_buf_set_lines(0, 0, -1, false, { '00', '29' }) + n.exec 'windo diffthis' feed('<C-e>') screen:expect { grid = [[ diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 31b1464589..ca52a265fa 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1,24 +1,28 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed = helpers.clear, helpers.feed -local eval = helpers.eval -local eq = helpers.eq -local command = helpers.command -local set_method_error = helpers.set_method_error -local api = helpers.api -local async_meths = helpers.async_meths -local test_build_dir = helpers.paths.test_build_dir -local nvim_prog = helpers.nvim_prog -local exec = helpers.exec -local exec_capture = helpers.exec_capture -local exc_exec = helpers.exc_exec -local exec_lua = helpers.exec_lua -local poke_eventloop = helpers.poke_eventloop -local assert_alive = helpers.assert_alive -local is_os = helpers.is_os -local is_ci = helpers.is_ci -local fn = helpers.fn -local skip = helpers.skip + +local clear, feed = n.clear, n.feed +local eval = n.eval +local eq = t.eq +local neq = t.neq +local command = n.command +local set_method_error = n.set_method_error +local api = n.api +local async_meths = n.async_meths +local test_build_dir = t.paths.test_build_dir +local nvim_prog = n.nvim_prog +local testprg = n.testprg +local exec = n.exec +local exec_capture = n.exec_capture +local exc_exec = n.exc_exec +local exec_lua = n.exec_lua +local poke_eventloop = n.poke_eventloop +local assert_alive = n.assert_alive +local retry = t.retry +local is_os = t.is_os +local fn = n.fn +local skip = t.skip describe('ui/ext_messages', function() local screen @@ -28,18 +32,9 @@ describe('ui/ext_messages', function() clear() screen = Screen.new(25, 5) screen:attach({ rgb = true, ext_messages = true, ext_popupmenu = true }) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue1 }, - [2] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, - [3] = { bold = true }, - [4] = { bold = true, foreground = Screen.colors.SeaGreen4 }, - [5] = { foreground = Screen.colors.Blue1 }, - [6] = { bold = true, reverse = true }, - [7] = { background = Screen.colors.Yellow }, - [8] = { foreground = Screen.colors.Red }, - [9] = { special = Screen.colors.Red, undercurl = true }, - [10] = { foreground = Screen.colors.Brown }, - }) + screen:add_extra_attr_ids { + [100] = { undercurl = true, special = Screen.colors.Red }, + } end) after_each(function() os.remove(fname) @@ -55,7 +50,7 @@ describe('ui/ext_messages', function() ]], messages = { { - content = { { '\ntest\n[O]k: ', 4 } }, + content = { { '\ntest\n[O]k: ', 6 } }, kind = 'confirm', }, }, @@ -83,7 +78,7 @@ describe('ui/ext_messages', function() ]], messages = { { - content = { { '\ntest\n[O]k: ', 4 } }, + content = { { '\ntest\n[O]k: ', 6 } }, kind = 'confirm', }, }, @@ -97,7 +92,7 @@ describe('ui/ext_messages', function() ]], messages = { { - content = { { '\ntest\n[O]k: ', 4 } }, + content = { { '\ntest\n[O]k: ', 6 } }, kind = 'confirm', }, { @@ -105,7 +100,7 @@ describe('ui/ext_messages', function() kind = 'echo', }, { - content = { { 'Press ENTER or type command to continue', 4 } }, + content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt', }, }, @@ -116,23 +111,13 @@ describe('ui/ext_messages', function() feed(':%s/i/X/gc<cr>') screen:expect { grid = [[ - l{7:i}ne 1 | - l{8:i}ne ^2 | + l{2:i}ne 1 | + l{10:i}ne ^2 | {1:~ }|*3 ]], - attr_ids = { - [1] = { bold = true, foreground = Screen.colors.Blue1 }, - [2] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, - [3] = { bold = true }, - [4] = { bold = true, foreground = Screen.colors.SeaGreen4 }, - [5] = { foreground = Screen.colors.Blue1 }, - [6] = { bold = true, reverse = true }, - [7] = { reverse = true }, - [8] = { background = Screen.colors.Yellow }, - }, messages = { { - content = { { 'replace with X (y/n/a/q/l/^E/^Y)?', 4 } }, + content = { { 'replace with X (y/n/a/q/l/^E/^Y)?', 6 } }, kind = 'confirm_sub', }, }, @@ -146,16 +131,12 @@ describe('ui/ext_messages', function() screen:expect { grid = [[ line 1 | - {MATCH:.*}| + line ^2 | {1:~ }|*3 ]], - attr_ids = { - [1] = { bold = true, foreground = Screen.colors.Blue1 }, - [7] = { foreground = Screen.colors.Red }, - }, messages = { { - content = { { 'W10: Warning: Changing a readonly file', 7 } }, + content = { { 'W10: Warning: Changing a readonly file', 19 } }, kind = 'wmsg', }, }, @@ -169,18 +150,9 @@ describe('ui/ext_messages', function() line 2 | {1:~ }|*3 ]], - attr_ids = { - [1] = { bold = true, foreground = Screen.colors.Blue1 }, - [2] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, - [3] = { bold = true }, - [4] = { bold = true, foreground = Screen.colors.SeaGreen4 }, - [5] = { foreground = Screen.colors.Blue1 }, - [6] = { bold = true, reverse = true }, - [7] = { foreground = Screen.colors.Red }, - }, messages = { { - content = { { 'search hit BOTTOM, continuing at TOP', 7 } }, + content = { { 'search hit BOTTOM, continuing at TOP', 19 } }, kind = 'wmsg', }, }, @@ -196,15 +168,15 @@ describe('ui/ext_messages', function() ]], messages = { { - content = { { 'Error detected while processing :', 2 } }, + content = { { 'Error detected while processing :', 9 } }, kind = 'emsg', }, { - content = { { 'E605: Exception not caught: foo', 2 } }, + content = { { 'E605: Exception not caught: foo', 9 } }, kind = '', }, { - content = { { 'Press ENTER or type command to continue', 4 } }, + content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt', }, }, @@ -237,7 +209,7 @@ describe('ui/ext_messages', function() {1:~ }|*4 ]], messages = { { - content = { { 'raa', 2 } }, + content = { { 'raa', 9 } }, kind = 'echoerr', } }, } @@ -264,15 +236,15 @@ describe('ui/ext_messages', function() ]], messages = { { - content = { { 'bork', 2 } }, + content = { { 'bork', 9 } }, kind = 'echoerr', }, { - content = { { 'fail', 2 } }, + content = { { 'fail', 9 } }, kind = 'echoerr', }, { - content = { { 'Press ENTER or type command to continue', 4 } }, + content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt', }, }, @@ -286,19 +258,19 @@ describe('ui/ext_messages', function() ]], messages = { { - content = { { 'bork', 2 } }, + content = { { 'bork', 9 } }, kind = 'echoerr', }, { - content = { { 'fail', 2 } }, + content = { { 'fail', 9 } }, kind = 'echoerr', }, { - content = { { 'extrafail', 2 } }, + content = { { 'extrafail', 9 } }, kind = 'echoerr', }, { - content = { { 'Press ENTER or type command to continue', 4 } }, + content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt', }, }, @@ -320,7 +292,7 @@ describe('ui/ext_messages', function() {1:~ }|*4 ]], messages = { { - content = { { 'problem', 2 } }, + content = { { 'problem', 9 } }, kind = 'echoerr', } }, cmdline = { @@ -348,15 +320,15 @@ describe('ui/ext_messages', function() {1:~ }|*4 ]], msg_history = { - { kind = 'echoerr', content = { { 'raa', 2 } } }, - { kind = 'echoerr', content = { { 'bork', 2 } } }, - { kind = 'echoerr', content = { { 'fail', 2 } } }, - { kind = 'echoerr', content = { { 'extrafail', 2 } } }, - { kind = 'echoerr', content = { { 'problem', 2 } } }, + { kind = 'echoerr', content = { { 'raa', 9 } } }, + { kind = 'echoerr', content = { { 'bork', 9 } } }, + { kind = 'echoerr', content = { { 'fail', 9 } } }, + { kind = 'echoerr', content = { { 'extrafail', 9 } } }, + { kind = 'echoerr', content = { { 'problem', 9 } } }, }, messages = { { - content = { { 'Press ENTER or type command to continue', 4 } }, + content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt', }, }, @@ -380,7 +352,7 @@ describe('ui/ext_messages', function() {1:~ }|*4 ]], messages = { { - content = { { 'bork\nfail', 2 } }, + content = { { 'bork\nfail', 9 } }, kind = 'echoerr', } }, } @@ -393,13 +365,13 @@ describe('ui/ext_messages', function() ]], messages = { { - content = { { 'Press ENTER or type command to continue', 4 } }, + content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt', }, }, msg_history = { { - content = { { 'bork\nfail', 2 } }, + content = { { 'bork\nfail', 9 } }, kind = 'echoerr', }, }, @@ -413,8 +385,8 @@ describe('ui/ext_messages', function() feed('/line<cr>') screen:expect { grid = [[ - {7:^line} 1 | - {7:line} 2 | + {10:^line} 1 | + {10:line} 2 | {1:~ }|*3 ]], messages = { @@ -425,8 +397,8 @@ describe('ui/ext_messages', function() feed('n') screen:expect { grid = [[ - {7:line} 1 | - {7:^line} 2 | + {10:line} 1 | + {10:^line} 2 | {1:~ }|*3 ]], messages = { @@ -446,15 +418,15 @@ describe('ui/ext_messages', function() { content = { { '\nErrorMsg ' }, - { 'xxx', 2 }, + { 'xxx', 9 }, { ' ' }, - { 'ctermfg=', 5 }, + { 'ctermfg=', 18 }, { '15 ' }, - { 'ctermbg=', 5 }, + { 'ctermbg=', 18 }, { '1 ' }, - { 'guifg=', 5 }, + { 'guifg=', 18 }, { 'White ' }, - { 'guibg=', 5 }, + { 'guibg=', 18 }, { 'Red' }, }, kind = '', @@ -474,7 +446,7 @@ describe('ui/ext_messages', function() messages = { { content = { { 'x #1' } }, kind = '' }, { content = { { 'y #2' } }, kind = '' }, - { content = { { 'Press ENTER or type command to continue', 4 } }, kind = 'return_prompt' }, + { content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt' }, }, } end) @@ -487,7 +459,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*4 ]], - showmode = { { '-- INSERT --', 3 } }, + showmode = { { '-- INSERT --', 5 } }, } feed('alphpabet<cr>alphanum<cr>') @@ -498,7 +470,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*2 ]], - showmode = { { '-- INSERT --', 3 } }, + showmode = { { '-- INSERT --', 5 } }, } feed('<c-x>') @@ -509,7 +481,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*2 ]], - showmode = { { '-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)', 3 } }, + showmode = { { '-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)', 5 } }, } feed('<c-p>') @@ -525,7 +497,7 @@ describe('ui/ext_messages', function() items = { { 'alphpabet', '', '', '' }, { 'alphanum', '', '', '' } }, pos = 1, }, - showmode = { { '-- Keyword Local completion (^N^P) ', 3 }, { 'match 1 of 2', 4 } }, + showmode = { { '-- Keyword Local completion (^N^P) ', 5 }, { 'match 1 of 2', 6 } }, } -- echomsg and showmode don't overwrite each other, this is the same @@ -547,7 +519,7 @@ describe('ui/ext_messages', function() content = { { 'stuff' } }, kind = 'echomsg', } }, - showmode = { { '-- Keyword Local completion (^N^P) ', 3 }, { 'match 1 of 2', 4 } }, + showmode = { { '-- Keyword Local completion (^N^P) ', 5 }, { 'match 1 of 2', 6 } }, } feed('<c-p>') @@ -567,7 +539,7 @@ describe('ui/ext_messages', function() content = { { 'stuff' } }, kind = 'echomsg', } }, - showmode = { { '-- Keyword Local completion (^N^P) ', 3 }, { 'match 2 of 2', 4 } }, + showmode = { { '-- Keyword Local completion (^N^P) ', 5 }, { 'match 2 of 2', 6 } }, } feed('<esc>:messages<cr>') @@ -584,7 +556,7 @@ describe('ui/ext_messages', function() } }, messages = { { - content = { { 'Press ENTER or type command to continue', 4 } }, + content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt', }, }, @@ -598,7 +570,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*4 ]], - showmode = { { 'recording @q', 3 } }, + showmode = { { 'recording @q', 5 } }, } feed('i') @@ -607,7 +579,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*4 ]], - showmode = { { '-- INSERT --recording @q', 3 } }, + showmode = { { '-- INSERT --recording @q', 5 } }, } feed('<esc>') @@ -616,7 +588,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*4 ]], - showmode = { { 'recording @q', 3 } }, + showmode = { { 'recording @q', 5 } }, } feed('q') @@ -635,7 +607,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*4 ]], - showmode = { { 'recording @q', 3 } }, + showmode = { { 'recording @q', 5 } }, mode = 'normal', } @@ -645,7 +617,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*4 ]], - showmode = { { 'recording @q', 3 } }, + showmode = { { 'recording @q', 5 } }, mode = 'insert', } @@ -655,7 +627,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*4 ]], - showmode = { { 'recording @q', 3 } }, + showmode = { { 'recording @q', 5 } }, mode = 'normal', } @@ -684,7 +656,7 @@ describe('ui/ext_messages', function() ^ | {1:~ }|*4 ]], - showmode = { { '-- INSERT --', 3 } }, + showmode = { { '-- INSERT --', 5 } }, ruler = { { '0,1 All' } }, } feed('abcde<cr>12345<esc>') @@ -715,7 +687,18 @@ describe('ui/ext_messages', function() ]], ruler = { { '2,1 All' } }, } - feed('d') + feed('<c-v>k2l') + screen:expect({ + grid = [[ + {17:ab}^cde | + {17:123}45 | + {1:~ }|*3 + ]], + showmode = { { '-- VISUAL BLOCK --', 5 } }, + showcmd = { { '2x3' } }, + ruler = { { '1,3 All' } }, + }) + feed('o<esc>d') screen:expect { grid = [[ abcde | @@ -752,7 +735,7 @@ describe('ui/ext_messages', function() abcde | ^ | {1:~ }|*2 - {6:<o Name] [+] 2,0-1 All}| + {3:<o Name] [+] 2,0-1 All}| ]]) end) @@ -792,7 +775,7 @@ describe('ui/ext_messages', function() {1:~ }|*4 ]], messages = { { - content = { { 'bork', 2 } }, + content = { { 'bork', 9 } }, kind = 'echoerr', } }, } @@ -817,7 +800,7 @@ describe('ui/ext_messages', function() ]], messages = { { - content = { { 'E117: Unknown function: nosuchfunction', 2 } }, + content = { { 'E117: Unknown function: nosuchfunction', 9 } }, kind = 'emsg', }, }, @@ -832,19 +815,19 @@ describe('ui/ext_messages', function() msg_history = { { kind = 'echomsg', content = { { 'howdy' } } }, { kind = '', content = { { 'Type :qa and press <Enter> to exit Nvim' } } }, - { kind = 'echoerr', content = { { 'bork', 2 } } }, - { kind = 'emsg', content = { { 'E117: Unknown function: nosuchfunction', 2 } } }, + { kind = 'echoerr', content = { { 'bork', 9 } } }, + { kind = 'emsg', content = { { 'E117: Unknown function: nosuchfunction', 9 } } }, }, messages = { { - content = { { 'Press ENTER or type command to continue', 4 } }, + content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt', }, }, } end) - it('implies ext_cmdline and ignores cmdheight', function() + it("implies ext_cmdline but allows changing 'cmdheight'", function() eq(0, eval('&cmdheight')) feed(':set cmdheight=1') screen:expect { @@ -864,15 +847,17 @@ describe('ui/ext_messages', function() feed('<cr>') screen:expect([[ ^ | - {1:~ }|*4 + {1:~ }|*3 + | ]]) - eq(0, eval('&cmdheight')) + eq(1, eval('&cmdheight')) feed(':set cmdheight=0') screen:expect { grid = [[ ^ | - {1:~ }|*4 + {1:~ }|*3 + | ]], cmdline = { { @@ -907,7 +892,7 @@ error stack traceback: [C]: in function 'error' [string ":lua"]:1: in main chunk]], - 2, + 9, }, }, kind = 'lua_error', @@ -927,7 +912,7 @@ stack traceback: messages = { { content = { - { "Error invoking 'test_method' on channel 1:\ncomplete\nerror\n\nmessage", 2 }, + { "Error invoking 'test_method' on channel 1:\ncomplete\nerror\n\nmessage", 9 }, }, kind = 'rpc_error', }, @@ -952,7 +937,7 @@ stack traceback: { content = { { '\nn Q @@\nn Y y$\nn j ' }, - { '*', 5 }, + { '*', 18 }, { ' k' }, }, kind = '', @@ -993,7 +978,7 @@ stack traceback: feed('z=') screen:expect { grid = [[ - {9:helllo} | + {100:helllo} | {1:~ }|*3 {1:^~ }| ]], @@ -1012,7 +997,7 @@ stack traceback: feed('1') screen:expect { grid = [[ - {9:helllo} | + {100:helllo} | {1:~ }|*3 {1:^~ }| ]], @@ -1050,7 +1035,7 @@ stack traceback: {1:~ }|*4 ]], messages = { - { content = { { 'wow, ', 7 }, { 'such\n\nvery ', 2 }, { 'color', 10 } }, kind = 'echomsg' }, + { content = { { 'wow, ', 10 }, { 'such\n\nvery ', 9 }, { 'color', 8 } }, kind = 'echomsg' }, }, } @@ -1072,10 +1057,10 @@ stack traceback: {1:~ }|*4 ]], messages = { - { content = { { 'Press ENTER or type command to continue', 4 } }, kind = 'return_prompt' }, + { content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt' }, }, msg_history = { - { content = { { 'wow, ', 7 }, { 'such\n\nvery ', 2 }, { 'color', 10 } }, kind = 'echomsg' }, + { content = { { 'wow, ', 10 }, { 'such\n\nvery ', 9 }, { 'color', 8 } }, kind = 'echomsg' }, }, } @@ -1104,18 +1089,10 @@ describe('ui/builtin messages', function() clear() screen = Screen.new(60, 7) screen:attach({ rgb = true, ext_popupmenu = true }) - screen:set_default_attr_ids { - [1] = { bold = true, foreground = Screen.colors.Blue1 }, - [2] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, - [3] = { bold = true, reverse = true }, - [4] = { bold = true, foreground = Screen.colors.SeaGreen4 }, - [5] = { foreground = Screen.colors.Blue1 }, - [6] = { bold = true, foreground = Screen.colors.Magenta }, - [7] = { background = Screen.colors.Grey20 }, - [8] = { reverse = true }, - [9] = { background = Screen.colors.LightRed }, - [10] = { background = Screen.colors.Yellow }, - [11] = { foreground = Screen.colors.Brown }, + screen:add_extra_attr_ids { + [100] = { background = Screen.colors.LightRed }, + [101] = { background = Screen.colors.Grey20 }, + [102] = { foreground = Screen.colors.Magenta1, bold = true }, } end) @@ -1125,12 +1102,12 @@ describe('ui/builtin messages', function() screen:expect { grid = [[ {3: }| - {2:Error invoking 'test_method' on channel 1:} | - {2:complete} | - {2:error} | + {9:Error invoking 'test_method' on channel 1:} | + {9:complete} | + {9:error} | | - {2:message} | - {4:Press ENTER or type command to continue}^ | + {9:message} | + {6:Press ENTER or type command to continue}^ | ]], request_cb = function(name) if name == 'test_method' then @@ -1148,8 +1125,8 @@ describe('ui/builtin messages', function() {1:~ }|*2 {3: }| :hi ErrorMsg | - ErrorMsg {2:xxx} {5:ctermfg=}15 {5:ctermbg=}1 {5:guifg=}White {5:guibg=}Red | - {4:Press ENTER or type command to continue}^ | + ErrorMsg {9:xxx} {18:ctermfg=}15 {18:ctermbg=}1 {18:guifg=}White {18:guibg=}Red | + {6:Press ENTER or type command to continue}^ | ]]) feed('<cr>') @@ -1157,12 +1134,12 @@ describe('ui/builtin messages', function() feed(':hi ErrorMsg<cr>') screen:expect([[ :hi ErrorMsg | - ErrorMsg {2:xxx} {5:ctermfg=}15 | - {5:ctermbg=}1 | - {5:guifg=}White| - {5:guibg=}Red | - {4:Press ENTER or type command to}| - {4: continue}^ | + ErrorMsg {9:xxx} {18:ctermfg=}15 | + {18:ctermbg=}1 | + {18:guifg=}White| + {18:guibg=}Red | + {6:Press ENTER or type command to}| + {6: continue}^ | ]]) feed('<cr>') @@ -1176,13 +1153,13 @@ describe('ui/builtin messages', function() screen:try_resize(110, 7) feed(':syntax list vimComment<cr>') screen:expect([[ - {6:--- Syntax items ---} | - vimComment {5:xxx} {5:match} /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 {5:excludenl} {5:contains}=@vimCommentGroup,vimCommentString | + {102:--- Syntax items ---} | + vimComment {18:xxx} {18:match} /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 {18:excludenl} {18:contains}=@vimCommentGroup,vimCommentString | | - {5:match} /\<endif\s\+".*$/ms=s+5,lc=5 {5:contains}=@vimCommentGroup,vimCommentString | - {5:match} /\<else\s\+".*$/ms=s+4,lc=4 {5:contains}=@vimCommentGroup,vimCommentString | - {5:links to} Comment | - {4:Press ENTER or type command to continue}^ | + {18:match} /\<endif\s\+".*$/ms=s+5,lc=5 {18:contains}=@vimCommentGroup,vimCommentString | + {18:match} /\<else\s\+".*$/ms=s+4,lc=4 {18:contains}=@vimCommentGroup,vimCommentString | + {18:links to} Comment | + {6:Press ENTER or type command to continue}^ | ]]) feed('<cr>') @@ -1190,12 +1167,12 @@ describe('ui/builtin messages', function() feed(':syntax list vimComment<cr>') screen:expect([[ | - {5:match} /\<endif\s\+".*$/ms=s+5,lc=5 | - {5:contains}=@vimCommentGroup,vimCommentString | - {5:match} /\<else\s\+".*$/ms=s+4,lc=4 {5:c}| - {5:ontains}=@vimCommentGroup,vimCommentString | - {5:links to} Comment | - {4:Press ENTER or type command to continue}^ | + {18:match} /\<endif\s\+".*$/ms=s+5,lc=5 | + {18:contains}=@vimCommentGroup,vimCommentString | + {18:match} /\<else\s\+".*$/ms=s+4,lc=4 {18:c}| + {18:ontains}=@vimCommentGroup,vimCommentString | + {18:links to} Comment | + {6:Press ENTER or type command to continue}^ | ]]) feed('<cr>') @@ -1268,7 +1245,7 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim grid = [[ ^ | {1:~ }|*5 - {7: 0,0-1 All }| + {101: 0,0-1 All }| ]], } @@ -1277,7 +1254,7 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim grid = [[ ^ | {1:~ }|*5 - {7: 0,0-1 100% }| + {101: 0,0-1 100% }| ]], } end) @@ -1291,7 +1268,7 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim {3: }| line 1 | line 2 | - {4:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]], } @@ -1315,7 +1292,7 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim grid = [[ | {1:~ }| - {8:[No Name] }| + {2:[No Name] }| ^ | {1:~ }| {3:[No Name] }| @@ -1327,12 +1304,12 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim screen:expect { grid = [[ :set colorcolumn=10 | digraphs | - NU {5:^@} 10 SH {5:^A} 1 SX {5:^B} 2 EX {5:^C} 3 | - ET {5:^D} 4 EQ {5:^E} 5 AK {5:^F} 6 BL {5:^G} 7 | - BS {5:^H} 8 HT {5:^I} 9 LF {5:^@} 10 VT {5:^K} 11 | - FF {5:^L} 12 CR {5:^M} 13 SO {5:^N} 14 SI {5:^O} 15 | - DL {5:^P} 16 D1 {5:^Q} 17 D2 {5:^R} 18 D3 {5:^S} 19 | - {4:-- More --}^ | + NU {18:^@} 10 SH {18:^A} 1 SX {18:^B} 2 EX {18:^C} 3 | + ET {18:^D} 4 EQ {18:^E} 5 AK {18:^F} 6 BL {18:^G} 7 | + BS {18:^H} 8 HT {18:^I} 9 LF {18:^@} 10 VT {18:^K} 11 | + FF {18:^L} 12 CR {18:^M} 13 SO {18:^N} 14 SI {18:^O} 15 | + DL {18:^P} 16 D1 {18:^Q} 17 D2 {18:^R} 18 D3 {18:^S} 19 | + {6:-- More --}^ | ]], } @@ -1341,8 +1318,8 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim grid = [[ | {1:~ }| - {8:[No Name] }| - ^ {9: } | + {2:[No Name] }| + ^ {100: } | {1:~ }| {3:[No Name] }| | @@ -1353,13 +1330,13 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim feed(':set colorcolumn=5 | lua error("x\\n\\nx")<cr>') screen:expect { grid = [[ - {2:E5108: Error executing lua [string ":lua"]:1: x} | + {9:E5108: Error executing lua [string ":lua"]:1: x} | | - {2:x} | - {2:stack traceback:} | - {2: [C]: in function 'error'} | - {2: [string ":lua"]:1: in main chunk} | - {4:Press ENTER or type command to continue}^ | + {9:x} | + {9:stack traceback:} | + {9: [C]: in function 'error'} | + {9: [string ":lua"]:1: in main chunk} | + {6:Press ENTER or type command to continue}^ | ]], } @@ -1368,8 +1345,8 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim grid = [[ | {1:~ }| - {8:[No Name] }| - ^ {9: } | + {2:[No Name] }| + ^ {100: } | {1:~ }| {3:[No Name] }| | @@ -1380,12 +1357,12 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim feed(':set colorcolumn=5 | lua error("x\\n\\n\\nx")<cr>') screen:expect { grid = [[ - {2:E5108: Error executing lua [string ":lua"]:1: x} | + {9:E5108: Error executing lua [string ":lua"]:1: x} | |*2 - {2:x} | - {2:stack traceback:} | - {2: [C]: in function 'error'} | - {4:-- More --}^ | + {9:x} | + {9:stack traceback:} | + {9: [C]: in function 'error'} | + {6:-- More --}^ | ]], } @@ -1393,11 +1370,11 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim screen:expect { grid = [[ |*2 - {2:x} | - {2:stack traceback:} | - {2: [C]: in function 'error'} | - {2: [string ":lua"]:1: in main chunk} | - {4:Press ENTER or type command to continue}^ | + {9:x} | + {9:stack traceback:} | + {9: [C]: in function 'error'} | + {9: [string ":lua"]:1: in main chunk} | + {6:Press ENTER or type command to continue}^ | ]], } end) @@ -1413,10 +1390,10 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim | {1:~ }| {3: }| - {10:wow, }{2:such} | + {10:wow, }{9:such} | | - {2:very }{11:color} | - {4:Press ENTER or type command to continue}^ | + {9:very }{8:color} | + {6:Press ENTER or type command to continue}^ | ]], } @@ -1435,10 +1412,10 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim | {1:~ }| {3: }| - {10:wow, }{2:such} | + {10:wow, }{9:such} | | - {2:very }{11:color} | - {4:Press ENTER or type command to continue}^ | + {9:very }{8:color} | + {6:Press ENTER or type command to continue}^ | ]], } end) @@ -1449,19 +1426,19 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim feed('gggQ<CR><CR>1<CR><CR>vi') screen:expect([[ Entering Ex mode. Type "visual" to go to Normal mode. | - {11: 2 }bbb | - {11: 3 }ccc | + {8: 2 }bbb | + {8: 3 }ccc | :1 | - {11: 1 }aaa | - {11: 2 }bbb | + {8: 1 }aaa | + {8: 2 }bbb | :vi^ | ]]) feed('<CR>') screen:expect([[ - {11: 1 }aaa | - {11: 2 }^bbb | - {11: 3 }ccc | - {11: 4 } | + {8: 1 }aaa | + {8: 2 }^bbb | + {8: 3 }ccc | + {8: 4 } | {1:~ }|*2 | ]]) @@ -1502,7 +1479,7 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim aaa | bbb | ccc | - {4:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]], } end @@ -1534,7 +1511,7 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim {1:~ }|*3 {3: }| | - {4:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) feed('<CR>') screen:expect([[ @@ -1572,6 +1549,35 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim |*4 ]]) end) + + it('supports :intro with cmdheight=0 #26505', function() + screen:try_resize(80, 24) + command('set cmdheight=0') + feed(':intro<CR>') + screen:expect([[ + |*5 + {MATCH:.*}| + | + Nvim is open source and freely distributable | + https://neovim.io/#chat | + | + type :help nvim{18:<Enter>} if you are new! | + type :checkhealth{18:<Enter>} to optimize Nvim | + type :q{18:<Enter>} to exit | + type :help{18:<Enter>} for help | + | + {MATCH: +}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+ +}| + | + Help poor children in Uganda! | + type :help iccf{18:<Enter>} for information | + |*2 + {3: }| + | + {6:Press ENTER or type command to continue}^ | + ]]) + feed('<CR>') + assert_alive() + end) end) it('calling screenstring() after redrawing between messages without UI #20999', function() @@ -1592,21 +1598,12 @@ describe('ui/ext_messages', function() clear { args_rm = { '--headless' }, args = { '--cmd', 'set shortmess-=I' } } screen = Screen.new(80, 24) screen:attach({ rgb = true, ext_messages = true, ext_popupmenu = true }) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue1 }, - [2] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, - [3] = { bold = true }, - [4] = { bold = true, foreground = Screen.colors.SeaGreen4 }, - [5] = { foreground = Screen.colors.Blue1 }, - [6] = { reverse = true }, - [7] = { bold = true, reverse = true }, - }) end) it('supports intro screen', function() -- intro message is not externalized. But check that it still works. -- Note parts of it depends on version or is indeterministic. We ignore those parts. - screen:expect([[ + local introscreen = [[ ^ | {1:~ }|*4 {MATCH:.*}| @@ -1614,24 +1611,70 @@ describe('ui/ext_messages', function() {1:~ }Nvim is open source and freely distributable{1: }| {1:~ }https://neovim.io/#chat{1: }| {1:~ }| - {1:~ }type :help nvim{5:<Enter>} if you are new! {1: }| - {1:~ }type :checkhealth{5:<Enter>} to optimize Nvim{1: }| - {1:~ }type :q{5:<Enter>} to exit {1: }| - {1:~ }type :help{5:<Enter>} for help {1: }| + {1:~ }type :help nvim{18:<Enter>} if you are new! {1: }| + {1:~ }type :checkhealth{18:<Enter>} to optimize Nvim{1: }| + {1:~ }type :q{18:<Enter>} to exit {1: }| + {1:~ }type :help{18:<Enter>} for help {1: }| {1:~ }| - {1:~{MATCH: +}}type :help news{5:<Enter>} to see changes in v{MATCH:%d+%.%d+}{1:{MATCH: +}}| + {1:~{MATCH: +}}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+}{1:{MATCH: +}}| {1:~ }| - {MATCH:.*}|*2 + {1:~ }Help poor children in Uganda!{1: }| + {1:~ }type :help iccf{18:<Enter>} for information {1: }| {1:~ }|*5 - ]]) - - feed('<c-l>') - screen:expect([[ + ]] + local showmode = { { '-- INSERT --', 5 } } + screen:expect(introscreen) + + -- <c-l> (same as :mode) does _not_ clear intro message + feed('<c-l>i') + screen:expect { grid = introscreen, showmode = showmode } + + -- opening a float without focus also does not + local win = api.nvim_open_win(api.nvim_create_buf(false, false), false, { + relative = 'editor', + height = 1, + width = 5, + row = 1, + col = 5, + }) + screen:expect { + grid = [[ ^ | + {1:~ }{4: }{1: }| + {1:~ }|*3 + {MATCH:.*}| + {1:~ }| + {1:~ }Nvim is open source and freely distributable{1: }| + {1:~ }https://neovim.io/#chat{1: }| + {1:~ }| + {1:~ }type :help nvim{18:<Enter>} if you are new! {1: }| + {1:~ }type :checkhealth{18:<Enter>} to optimize Nvim{1: }| + {1:~ }type :q{18:<Enter>} to exit {1: }| + {1:~ }type :help{18:<Enter>} for help {1: }| + {1:~ }| + {1:~{MATCH: +}}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+}{1:{MATCH: +}}| + {1:~ }| + {1:~ }Help poor children in Uganda!{1: }| + {1:~ }type :help iccf{18:<Enter>} for information {1: }| + {1:~ }|*5 + ]], + showmode = showmode, + } + + api.nvim_win_close(win, true) + screen:expect { grid = introscreen, showmode = showmode } + + -- but editing text does.. + feed('x') + screen:expect { + grid = [[ + x^ | {1:~ }|*23 - ]]) + ]], + showmode = showmode, + } - feed(':intro<cr>') + feed('<esc>:intro<cr>') screen:expect { grid = [[ ^ | @@ -1641,20 +1684,81 @@ describe('ui/ext_messages', function() Nvim is open source and freely distributable | https://neovim.io/#chat | | - type :help nvim{5:<Enter>} if you are new! | - type :checkhealth{5:<Enter>} to optimize Nvim | - type :q{5:<Enter>} to exit | - type :help{5:<Enter>} for help | + type :help nvim{18:<Enter>} if you are new! | + type :checkhealth{18:<Enter>} to optimize Nvim | + type :q{18:<Enter>} to exit | + type :help{18:<Enter>} for help | | - {MATCH: +}type :help news{5:<Enter>} to see changes in v{MATCH:%d+%.%d+ +}| + {MATCH: +}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+ +}| | - {MATCH:.*}|*2 + Help poor children in Uganda! | + type :help iccf{18:<Enter>} for information | |*5 ]], messages = { - { content = { { 'Press ENTER or type command to continue', 4 } }, kind = 'return_prompt' }, + { content = { { 'Press ENTER or type command to continue', 6 } }, kind = 'return_prompt' }, }, } + + feed('<cr>') + screen:expect { + grid = [[ + ^x | + {1:~ }|*23 + ]], + } + end) + + it('clears intro screen when new buffer is active', function() + api.nvim_set_current_buf(api.nvim_create_buf(true, false)) + screen:expect { + grid = [[ + ^ | + {1:~ }|*23 + ]], + } + end) + + it('clears intro screen when new buffer is active in floating window', function() + local win_opts = { relative = 'editor', height = 1, width = 5, row = 1, col = 5 } + api.nvim_open_win(api.nvim_create_buf(false, false), true, win_opts) + screen:expect { + grid = [[ + | + {1:~ }{4:^ }{1: }| + {1:~ }|*22 + ]], + } + end) + + it('clears intro screen when initial buffer is active in floating window', function() + local win_opts = { relative = 'editor', height = 1, width = 5, row = 1, col = 5 } + api.nvim_open_win(api.nvim_get_current_buf(), true, win_opts) + screen:expect { + grid = [[ + | + {1:~ }{4:^ }{1: }| + {1:~ }|*22 + ]], + } + end) + + it('clears intro screen when initial window is converted to be floating', function() + exec_lua([[ + local init_win_id = vim.api.nvim_get_current_win() + vim.cmd('split') + local win_opts = { relative = 'editor', height = 1, width = 5, row = 1, col = 5 } + vim.api.nvim_win_set_config(init_win_id, win_opts) + vim.api.nvim_set_current_win(init_win_id) + ]]) + screen:expect { + grid = [[ + | + {1:~ }{4:^ }{1: }| + {1:~ }|*21 + {2:[No Name] }| + ]], + } end) it('supports global statusline', function() @@ -1668,7 +1772,7 @@ describe('ui/ext_messages', function() ────────────────────────────────────────────────────────────────────────────────| | {1:~ }|*10 - {7:[No Name] }| + {3:[No Name] }| ]], messages = { { content = { { ' cmdheight=0' } }, kind = '' }, @@ -1684,7 +1788,7 @@ describe('ui/ext_messages', function() ────────────────────────────────────────────────────────────────────────────────| | {1:~ }|*9 - {7:[No Name] }| + {3:[No Name] }| ]], messages = { { content = { { ' laststatus=3' } }, kind = '' }, @@ -1704,7 +1808,7 @@ describe('ui/ext_messages', function() ────────────────────────────────────────────────────────────────────────────────| | {1:~ }|*10 - {7:[No Name] }| + {3:[No Name] }| ]], messages = { { content = { { ' cmdheight=0' } }, kind = '' }, @@ -1713,6 +1817,76 @@ describe('ui/ext_messages', function() end) end) +it('ui/ext_multigrid supports intro screen', function() + clear { args_rm = { '--headless' }, args = { '--cmd', 'set shortmess-=I' } } + local screen = Screen.new(80, 24) + screen:attach({ rgb = true, ext_multigrid = true }) + + screen:expect { + grid = [[ + ## grid 1 + [2:--------------------------------------------------------------------------------]|*23 + [3:--------------------------------------------------------------------------------]| + ## grid 2 + ^ | + {1:~ }|*4 + {MATCH:.*}| + {1:~ }| + {1:~ }Nvim is open source and freely distributable{1: }| + {1:~ }https://neovim.io/#chat{1: }| + {1:~ }| + {1:~ }type :help nvim{18:<Enter>} if you are new! {1: }| + {1:~ }type :checkhealth{18:<Enter>} to optimize Nvim{1: }| + {1:~ }type :q{18:<Enter>} to exit {1: }| + {1:~ }type :help{18:<Enter>} for help {1: }| + {1:~ }| + {1:~{MATCH: +}}type :help news{18:<Enter>} to see changes in v{MATCH:%d+%.%d+}{1:{MATCH: +}}| + {1:~ }| + {1:~ }Help poor children in Uganda!{1: }| + {1:~ }type :help iccf{18:<Enter>} for information {1: }| + {1:~ }|*4 + ## grid 3 + | + ]], + win_viewport = { + [2] = { + win = 1000, + topline = 0, + botline = 2, + curline = 0, + curcol = 0, + linecount = 1, + sum_scroll_delta = 0, + }, + }, + } + + feed 'ix' + screen:expect { + grid = [[ + ## grid 1 + [2:--------------------------------------------------------------------------------]|*23 + [3:--------------------------------------------------------------------------------]| + ## grid 2 + x^ | + {1:~ }|*22 + ## grid 3 + {5:-- INSERT --} | + ]], + win_viewport = { + [2] = { + win = 1000, + topline = 0, + botline = 2, + curline = 0, + curcol = 1, + linecount = 1, + sum_scroll_delta = 0, + }, + }, + } +end) + describe('ui/msg_puts_printf', function() it('output multibyte characters correctly', function() local screen @@ -1728,16 +1902,12 @@ describe('ui/msg_puts_printf', function() pending('missing japanese language features', function() end) return else - cmd = 'chcp 932 > NULL & ' + cmd = 'chcp 932 > NUL & ' end else if exc_exec('lang ja_JP.UTF-8') ~= 0 then pending('Locale ja_JP.UTF-8 not supported', function() end) return - elseif is_ci() then - -- Fails non--Windows CI. Message catalog directory issue? - pending('fails on unix CI', function() end) - return end end @@ -1749,9 +1919,9 @@ describe('ui/msg_puts_printf', function() cmd = cmd .. '"' .. nvim_prog .. '" -u NONE -i NONE -Es -V1' command([[call termopen(']] .. cmd .. [[')]]) screen:expect([[ - ^Exモードに入ります. ノー | - マルモードに戻るには"visu| - al"と入力してください. | + ^Exモードに入ります。ノー | + マルモードに戻るには "vis| + ual" と入力してください。| : | | ]]) @@ -2378,4 +2548,221 @@ aliquip ex ea commodo consequat.]] ]], } end) + + it('g< shows blank line from :echo properly', function() + screen:try_resize(60, 8) + feed([[:echo 1 | echo "\n" | echo 2<CR>]]) + screen:expect([[ + | + {1:~ }|*2 + {12: }| + 1 | + | + 2 | + {4:Press ENTER or type command to continue}^ | + ]]) + + feed('<CR>') + screen:expect([[ + ^ | + {1:~ }|*6 + | + ]]) + + feed('g<lt>') + screen:expect([[ + | + {1:~ }| + {12: }| + :echo 1 | echo "\n" | echo 2 | + 1 | + | + 2 | + {4:Press ENTER or type command to continue}^ | + ]]) + + feed('<CR>') + screen:expect([[ + ^ | + {1:~ }|*6 + | + ]]) + end) + + it('scrolling works properly when :echo output ends with newline', function() + screen:try_resize(60, 6) + feed([[:echo range(100)->join("\n") .. "\n"<CR>]]) + screen:expect([[ + 0 | + 1 | + 2 | + 3 | + 4 | + {4:-- More --}^ | + ]]) + feed('G') + screen:expect([[ + 96 | + 97 | + 98 | + 99 | + | + {4:Press ENTER or type command to continue}^ | + ]]) + for _ = 1, 3 do + feed('k') + screen:expect([[ + 95 | + 96 | + 97 | + 98 | + 99 | + {4:-- More --}^ | + ]]) + feed('k') + screen:expect([[ + 94 | + 95 | + 96 | + 97 | + 98 | + {4:-- More --}^ | + ]]) + feed('j') + screen:expect([[ + 95 | + 96 | + 97 | + 98 | + 99 | + {4:-- More --}^ | + ]]) + feed('j') + screen:expect([[ + 96 | + 97 | + 98 | + 99 | + | + {4:-- More --}^ | + ]]) + feed('j') + screen:expect([[ + 96 | + 97 | + 98 | + 99 | + | + {4:Press ENTER or type command to continue}^ | + ]]) + end + end) + + it('scrolling works properly when :!cmd output ends with newline #27902', function() + screen:try_resize(60, 6) + api.nvim_set_option_value('shell', testprg('shell-test'), {}) + api.nvim_set_option_value('shellcmdflag', 'REP 100', {}) + api.nvim_set_option_value('shellxquote', '', {}) -- win: avoid extra quotes + feed([[:!foo<CR>]]) + screen:expect([[ + 96: foo | + 97: foo | + 98: foo | + 99: foo | + | + {4:Press ENTER or type command to continue}^ | + ]]) + for _ = 1, 3 do + feed('k') + screen:expect([[ + 95: foo | + 96: foo | + 97: foo | + 98: foo | + 99: foo | + {4:-- More --}^ | + ]]) + feed('k') + screen:expect([[ + 94: foo | + 95: foo | + 96: foo | + 97: foo | + 98: foo | + {4:-- More --}^ | + ]]) + feed('j') + screen:expect([[ + 95: foo | + 96: foo | + 97: foo | + 98: foo | + 99: foo | + {4:-- More --}^ | + ]]) + feed('j') + screen:expect([[ + 96: foo | + 97: foo | + 98: foo | + 99: foo | + | + {4:-- More --}^ | + ]]) + feed('j') + screen:expect([[ + 96: foo | + 97: foo | + 98: foo | + 99: foo | + | + {4:Press ENTER or type command to continue}^ | + ]]) + end + end) +end) + +it('pager works in headless mode with UI attached', function() + skip(is_os('win')) + clear() + local child_server = assert(n.new_pipename()) + fn.jobstart({ nvim_prog, '--clean', '--headless', '--listen', child_server }) + retry(nil, nil, function() + neq(nil, vim.uv.fs_stat(child_server)) + end) + + local child_session = n.connect(child_server) + local child_screen = Screen.new(40, 6) + child_screen:attach(nil, child_session) + child_screen._default_attr_ids = nil -- TODO: unskip with new color scheme + + child_session:notify('nvim_command', [[echo range(100)->join("\n")]]) + child_screen:expect([[ + 0 | + 1 | + 2 | + 3 | + 4 | + -- More --^ | + ]]) + + child_session:request('nvim_input', 'G') + child_screen:expect([[ + 95 | + 96 | + 97 | + 98 | + 99 | + Press ENTER or type command to continue^ | + ]]) + + child_session:request('nvim_input', 'g') + child_screen:expect([[ + 0 | + 1 | + 2 | + 3 | + 4 | + -- More --^ | + ]]) end) diff --git a/test/functional/ui/mode_spec.lua b/test/functional/ui/mode_spec.lua index 8d7fae3e91..f623cfda06 100644 --- a/test/functional/ui/mode_spec.lua +++ b/test/functional/ui/mode_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command = helpers.command -local retry = helpers.retry +local clear, feed, insert = n.clear, n.feed, n.insert +local command = n.command +local retry = t.retry describe('ui mode_change event', function() local screen @@ -12,20 +13,13 @@ describe('ui mode_change event', function() clear() screen = Screen.new(25, 4) screen:attach({ rgb = true }) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = 255 }, - [1] = { bold = true, reverse = true }, - [2] = { bold = true }, - [3] = { reverse = true }, - [4] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg - }) end) it('works in normal mode', function() screen:expect { grid = [[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'normal', @@ -35,7 +29,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'operator', @@ -45,7 +39,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'normal', @@ -61,7 +55,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'operator', @@ -71,8 +65,8 @@ describe('ui mode_change event', function() screen:expect { grid = [[ ^ | - {0:~ }|*2 - {4:E21: Cannot make changes, 'modifiable' is off} | + {1:~ }|*2 + {9:E21: Cannot make changes, 'modifiable' is off} | ]], mode = 'normal', } @@ -84,7 +78,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'replace', @@ -94,7 +88,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'normal', @@ -106,8 +100,8 @@ describe('ui mode_change event', function() screen:expect { grid = [[ ^ | - {0:~ }|*2 - {2:-- INSERT --} | + {1:~ }|*2 + {5:-- INSERT --} | ]], mode = 'insert', } @@ -116,7 +110,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ wor^d | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'normal', @@ -133,8 +127,8 @@ describe('ui mode_change event', function() screen:expect { grid = [[ word(stuff^ | - {0:~ }|*2 - {2:-- INSERT --} | + {1:~ }|*2 + {5:-- INSERT --} | ]], mode = 'insert', timeout = screen_timeout, @@ -144,8 +138,8 @@ describe('ui mode_change event', function() screen:expect { grid = [[ word^(stuff) | - {0:~ }|*2 - {2:-- INSERT --} | + {1:~ }|*2 + {5:-- INSERT --} | ]], mode = 'showmatch', timeout = screen_timeout, @@ -154,8 +148,8 @@ describe('ui mode_change event', function() screen:expect { grid = [[ word(stuff)^ | - {0:~ }|*2 - {2:-- INSERT --} | + {1:~ }|*2 + {5:-- INSERT --} | ]], mode = 'insert', timeout = screen_timeout, @@ -168,8 +162,8 @@ describe('ui mode_change event', function() screen:expect { grid = [[ ^ | - {0:~ }|*2 - {2:-- REPLACE --} | + {1:~ }|*2 + {5:-- REPLACE --} | ]], mode = 'replace', } @@ -178,7 +172,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ wor^d | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'normal', @@ -190,7 +184,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ | - {0:~ }|*2 + {1:~ }|*2 :^ | ]], mode = 'cmdline_normal', @@ -200,7 +194,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ | - {0:~ }|*2 + {1:~ }|*2 :^x | ]], mode = 'cmdline_insert', @@ -210,7 +204,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ | - {0:~ }|*2 + {1:~ }|*2 :^x | ]], mode = 'cmdline_replace', @@ -220,7 +214,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ | - {0:~ }|*2 + {1:~ }|*2 :x^ | ]], mode = 'cmdline_normal', @@ -230,7 +224,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'normal', @@ -243,8 +237,8 @@ describe('ui mode_change event', function() screen:expect { grid = [[ tex^t | - {0:~ }|*2 - {2:-- VISUAL --} | + {1:~ }|*2 + {5:-- VISUAL --} | ]], mode = 'visual', } @@ -253,7 +247,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ tex^t | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'normal', @@ -264,8 +258,8 @@ describe('ui mode_change event', function() screen:expect { grid = [[ tex^t | - {0:~ }|*2 - {2:-- VISUAL --} | + {1:~ }|*2 + {5:-- VISUAL --} | ]], mode = 'visual_select', } @@ -274,7 +268,7 @@ describe('ui mode_change event', function() screen:expect { grid = [[ tex^t | - {0:~ }|*2 + {1:~ }|*2 | ]], mode = 'normal', diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 0f30bf4471..42c877fd92 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, api = helpers.clear, helpers.feed, helpers.api -local insert, feed_command = helpers.insert, helpers.feed_command -local eq, fn = helpers.eq, helpers.fn -local poke_eventloop = helpers.poke_eventloop -local command = helpers.command -local exec = helpers.exec + +local clear, feed, api = n.clear, n.feed, n.api +local insert, feed_command = n.insert, n.feed_command +local eq, fn = t.eq, n.fn +local poke_eventloop = n.poke_eventloop +local command = n.command +local exec = n.exec describe('ui/mouse/input', function() local screen @@ -34,6 +36,7 @@ describe('ui/mouse/input', function() [6] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, [7] = { bold = true, foreground = Screen.colors.SeaGreen4 }, [8] = { foreground = Screen.colors.Brown }, + [9] = { background = Screen.colors.DarkGrey, foreground = Screen.colors.LightGrey }, }) command('set mousemodel=extend') feed('itesting<cr>mouse<cr>support and selection<esc>') @@ -574,7 +577,7 @@ describe('ui/mouse/input', function() :tabprevious | ]]) feed('<LeftMouse><10,0><LeftRelease>') -- go to second tab - helpers.poke_eventloop() + n.poke_eventloop() feed('<LeftMouse><0,1>') screen:expect([[ {tab: + foo }{sel: + bar }{fill: }{tab:X}| @@ -1638,6 +1641,59 @@ describe('ui/mouse/input', function() end) end) + it('virtual text does not change cursor placement on concealed line', function() + command('%delete') + insert('aaaaaaaaaa|hidden|bbbbbbbbbb|hidden|cccccccccc') + command('syntax match test /|hidden|/ conceal cchar=X') + command('set conceallevel=2 concealcursor=n virtualedit=all') + screen:expect([[ + aaaaaaaaaa{9:X}bbbbbbb | + bbb{9:X}ccccccccc^c | + {0:~ }|*2 + | + ]]) + api.nvim_input_mouse('left', 'press', '', 0, 0, 22) + screen:expect([[ + aaaaaaaaaa{9:X}bbbbbb^b | + bbb{9:X}cccccccccc | + {0:~ }|*2 + | + ]]) + api.nvim_input_mouse('left', 'press', '', 0, 1, 16) + screen:expect([[ + aaaaaaaaaa{9:X}bbbbbbb | + bbb{9:X}cccccccccc ^ | + {0:~ }|*2 + | + ]]) + + api.nvim_buf_set_extmark(0, api.nvim_create_namespace(''), 0, 0, { + virt_text = { { '?', 'ErrorMsg' } }, + virt_text_pos = 'right_align', + virt_text_repeat_linebreak = true, + }) + screen:expect([[ + aaaaaaaaaa{9:X}bbbbbbb {6:?}| + bbb{9:X}cccccccccc ^ {6:?}| + {0:~ }|*2 + | + ]]) + api.nvim_input_mouse('left', 'press', '', 0, 0, 22) + screen:expect([[ + aaaaaaaaaa{9:X}bbbbbb^b {6:?}| + bbb{9:X}cccccccccc {6:?}| + {0:~ }|*2 + | + ]]) + api.nvim_input_mouse('left', 'press', '', 0, 1, 16) + screen:expect([[ + aaaaaaaaaa{9:X}bbbbbbb {6:?}| + bbb{9:X}cccccccccc ^ {6:?}| + {0:~ }|*2 + | + ]]) + end) + it('getmousepos() works correctly', function() local winwidth = api.nvim_get_option_value('winwidth', {}) -- Set winwidth=1 so that window sizes don't change. @@ -1802,8 +1858,8 @@ describe('ui/mouse/input', function() it('feeding <MouseMove> in Normal mode does not use uninitialized memory #19480', function() feed('<MouseMove>') - helpers.poke_eventloop() - helpers.assert_alive() + n.poke_eventloop() + n.assert_alive() end) it('mousemodel=popup_setpos', function() diff --git a/test/functional/ui/multibyte_spec.lua b/test/functional/ui/multibyte_spec.lua index c2fc763401..dc25a09d0d 100644 --- a/test/functional/ui/multibyte_spec.lua +++ b/test/functional/ui/multibyte_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed -local feed_command = helpers.feed_command -local insert = helpers.insert -local fn = helpers.fn -local api = helpers.api + +local clear = n.clear +local command = n.command +local feed = n.feed +local feed_command = n.feed_command +local insert = n.insert +local fn = n.fn +local api = n.api local split = vim.split -local dedent = helpers.dedent +local dedent = t.dedent describe('multibyte rendering', function() local screen @@ -16,15 +18,6 @@ describe('multibyte rendering', function() clear() screen = Screen.new(60, 6) screen:attach({ rgb = true }) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue }, - [2] = { background = Screen.colors.WebGray }, - [3] = { background = Screen.colors.LightMagenta }, - [4] = { bold = true }, - [5] = { foreground = Screen.colors.Blue }, - [6] = { reverse = true, bold = true }, - [7] = { reverse = true }, - }) end) it('works with composed char at start of line', function() @@ -83,17 +76,17 @@ describe('multibyte rendering', function() ab ^ | -馬 | {1:~ }|*3 - {4:-- INSERT --} | + {5:-- INSERT --} | ]]) -- check double-width char is temporarily hidden when overlapped fn.complete(4, { 'xx', 'yy' }) screen:expect([[ ab xx^ | - - {2: xx } | - {1:~ }{3: yy }{1: }| + - {12: xx } | + {1:~ }{4: yy }{1: }| {1:~ }|*2 - {4:-- INSERT --} | + {5:-- INSERT --} | ]]) -- check it is properly restored @@ -102,7 +95,7 @@ describe('multibyte rendering', function() ab xxz^ | -馬 | {1:~ }|*3 - {4:-- INSERT --} | + {5:-- INSERT --} | ]]) end) @@ -129,7 +122,7 @@ describe('multibyte rendering', function() {1:~ }│a口口口口口口口口口口口口口口口口 | {1:~ }│aa口口口口口口口口口口口口口口口口 | {1:~ }│aaa口口口口口口口口口口口口口口口口 | - {6:[No Name] }{7:[No Name] [+] }| + {3:[No Name] }{2:[No Name] [+] }| | ]]) end) @@ -139,7 +132,7 @@ describe('multibyte rendering', function() feed('$') screen:expect { grid = [[ - {5:<ffff>}!!^! | + {18:<ffff>}!!^! | {1:~ }|*4 | ]], @@ -204,6 +197,32 @@ describe('multibyte rendering', function() } end) + it('works with even huger zalgo chars', function() + screen:try_resize(100, 20) + api.nvim_command 'color default' + api.nvim_set_hl(0, 'MyHighlight', { bg = '#53246b', fg = '#a4d844' }) + screen:add_extra_attr_ids { + [100] = { background = tonumber('0x53246b'), foreground = Screen.colors.NvimLightGrey4 }, + [101] = { background = tonumber('0x53246b'), foreground = tonumber('0xa4d844') }, + } + api.nvim_set_option_value('winhl', 'Normal:MyHighlight', {}) + api.nvim_set_option_value('number', true, {}) + local text = + 'c̯̥̳̮̫̳͔̱̀ͦͩ̄͋̓͘c̯̥̳̮̫̳͔̱̀ͦͩ̄͋̓͘l̶̴̴̨̛͓͚͎̥ͦͤ̑͆͛͢l̶̴̴̨̛͓͚͎̥ͦͤ̑͆͛͢ô̷̤̩ͯͧ͆ͪ̈́́͒̒̐͐̕ô̷̤̩ͯͧ͆ͪ̈́́͒̒̐͐̕s̶̷̢̩̱̠̀ͦ̽ͮ͒ͨ̚͟͠s̶̷̢̩̱̠̀ͦ̽ͮ͒ͨ̚͟͠e͉̯̱̖͕̳̼̽̊̒ͣ̊ͥ̚̚e͉̯̱̖͕̳̼̽̊̒ͣ̊ͥ̚̚ ͇͌̈̄ͬͧͧ͝͠͏̷ͪ̎͟͠ ͇͌̈̄ͬͧͧ͝͠͏̷ͪ̎͟͠t̵̳̅̽͏̵̡̥̲͍͙̹̯ͩ̐t̵̳̅̽͏̵̡̥̲͍͙̹̯ͩ̐o͋͏̬̞̣ͬ́ͬ̎̋̓̽͘͠͡o͋͏̬̞̣ͬ́ͬ̎̋̓̽͘͠͡ ͏̛͙̮̈ͬ̌͐ͤ͒ͧ̎ͤ͜͠ ͏̛͙̮̈ͬ̌͐ͤ͒ͧ̎ͤ͜͠ǫ̛̳͕̦͖̪̀͂͛̅̔ͪ͒͜ǫ̛̳͕̦͖̪̀͂͛̅̔ͪ͒͜v̸̡͖̗̣ͯ̿̔͊̅ͯ̈̓̇ͅv̸̡͖̗̣ͯ̿̔͊̅ͯ̈̓̇ͅe̴̶̢̜̭̠̰̞ͪͨ͂ͤ͆́͗e̴̶̢̜̭̠̰̞ͪͨ͂ͤ͆́͗r̨̖̼̳̳͚̖̒ͯ̊̋̂̑̚͞r̨̖̼̳̳͚̖̒ͯ̊̋̂̑̚͞f̵̮̣̬̾͛̌ͧͦ͘͢͜͜͠ͅf̵̮̣̬̾͛̌ͧͦ͘͢͜͜͠ͅl̡͉̬̳͈̠̏͂ͤ̈ͨ̀ͩ̔͏l̡͉̬̳͈̠̏͂ͤ̈ͨ̀ͩ̔͏ỏ̶̢̻̠͎͇͈̜̈̆ͯ̔ͩ̾ỏ̶̢̻̠͎͇͈̜̈̆ͯ̔ͩ̾ẉ̦̞̼̩̣͙̅̿́̓̉̎̈ͪẉ̦̞̼̩̣͙̅̿́̓̉̎̈ͪi̷̡͍͓͔̝͙͖͖̍ͯͤͬͦ͝i̷̡͍͓͔̝͙͖͖̍ͯͤͬͦ͝n̠̪̰͋ͩ̆͏̶̖̯ͭ̀̿͛͘n̠̪̰͋ͩ̆͏̶̖̯ͭ̀̿͛͘g̴̭͎̞͙̗̯͙͖ͬ́ͧͧ͝ͅg̴̭͎̞͙̗̯͙͖ͬ́ͧͧ͝ͅ ̢͎̬͓̮̹̰̙͍̓̐͋͂͐̚ ̢͎̬͓̮̹̰̙͍̓̐͋͂͐̚ť̴̼̝̗ͩ̍͋̿͏͇ͧ̑̏̚ť̴̼̝̗ͩ̍͋̿͏͇ͧ̑̏̚h̨̨̢͓̲͚͕̦ͨ͛̓ͩ̚͟͠h̨̨̢͓̲͚͕̦ͨ͛̓ͩ̚͟͠ȩ͕̜̥̃̑͋̏͐̎̒͛͊͏͙ȩ͕̜̥̃̑͋̏͐̎̒͛͊͏͙ ̵̨̜̜̠͉̱͎͑ͥ̌͐̽͢͡ ̵̨̜̜̠͉̱͎͑ͥ̌͐̽͢͡r̢̫͓͎̙̭̽ͥͭ͐̂̀̕͟͝r̢̫͓͎̙̭̽ͥͭ͐̂̀̕͟͝e̴̷͓̹̩ͧ́̉̑̈̿ͥ̕͡͝e̴̷͓̹̩ͧ́̉̑̈̿ͥ̕͡͝d̵̡̼̩̠̜͈̯̬͚͛̋̀̆͟d̵̡̼̩̠̜͈̯̬͚͛̋̀̆͟ŕ̻̳̬͏̨̮͚̲ͥ̌͆͗͠ͅŕ̻̳̬͏̨̮͚̲ͥ̌͆͗͠ͅā̸͙̥̤͍͈̣ͪͨ̈͋̈́̈́͜ā̸͙̥̤͍͈̣ͪͨ̈͋̈́̈́͜w̶̦̪͚̤̃ͬ̓͒ͤ̇̇ͮ͢͡w̶̦̪͚̤̃ͬ̓͒ͤ̇̇ͮ͢͡ ̷̡̦̻̻̪͚̳͎ͥ̓ͩͪ͠͝ ̷̡̦̻̻̪͚̳͎ͥ̓ͩͪ͠͝b̳ͮ̒̊̆̒́̈́̏̓͋ͭ̔ͤ̚b̳ͮ̒̊̆̒́̈́̏̓͋ͭ̔ͤ̚u̧̟̫͎͚̭̠͕͂̄̀̒̈̇͜u̧̟̫͎͚̭̠͕͂̄̀̒̈̇͜f̶̸̢͍̑̂̊ͥͫ̈́ͥ͛̈́̃͝f̶̸̢͍̑̂̊ͥͫ̈́ͥ͛̈́̃͝f̵̼̭̮͎ͧ̒̆͊̀ͤ͊̇̕͡f̵̼̭̮͎ͧ̒̆͊̀ͤ͊̇̕͡e̮̪̲̣̞̖͎ͥͤ̐̌̓̐͟͢e̮̪̲̣̞̖͎ͥͤ̐̌̓̐͟͢ŗ̭̘̮̹̖̎̄̆́ͫͭ͢ͅͅŗ̭̘̮̹̖̎̄̆́ͫͭ͢ͅͅ.̪̖͉͚̹̾̉ͮ̔̊ͪ̾̎͟͞.̪̖͉͚̹̾̉ͮ̔̊ͪ̾̎͟͞ ̷̧̺̰͎̣̃͒͗ͮ͑ͪͮ͞ͅ ̷̧̺̰͎̣̃͒͗ͮ͑ͪͮ͞ͅf̛̫͚͚͍̜͎̗̳̂ͬͭ͜͢͝f̛̫͚͚͍̜͎̗̳̂ͬͭ͜͢͝i̴̵̡̛̛͎̤̳̮ͩ̐ͩ͑̇̚i̴̵̡̛̛͎̤̳̮ͩ̐ͩ͑̇̚n̵͇͍͚̖̥̣ͨ̄ͧ̌̂͗͘͝n̵͇͍͚̖̥̣ͨ̄ͧ̌̂͗͘͝ȉ̼̱̫̜̋̓̐͌͆ͨ͘͝͡ͅȉ̼̱̫̜̋̓̐͌͆ͨ͘͝͡ͅs̴̸̝̺̬͚̲̹̘ͪ̆̊ͥ͞͝s̴̸̝̺̬͚̲̹̘ͪ̆̊ͥ͞͝h̴̜̠͇ͦͥ̔̅ͭͭ͋ͩ͟͡͞h̴̜̠͇ͦͥ̔̅ͭͭ͋ͩ͟͡͞ ̶̧̛̻͙̤̘̺̣̻̗͍̓͑͠ ̶̧̛̻͙̤̘̺̣̻̗͍̓͑͠t̠͉̼̬̩͛́ͨ͐̀͛̂ͨ̾͞t̠͉̼̬̩͛́ͨ͐̀͛̂ͨ̾͞h̻̹̝̹̾ͩ̍ͧ͆ͥ̔͘͏̉ͯh̻̹̝̹̾ͩ̍ͧ͆ͥ̔͘͏̉ͯì̷̢̛̺̭͇̟̦̄̓́̓ͪ͟ì̷̢̛̺̭͇̟̦̄̓́̓ͪ͟s̴̡̗͍͕͖̮̟̱̫̎ͣ̀̎̿s̴̡̗͍͕͖̮̟̱̫̎ͣ̀̎̿ ̶͇̟̜̗̗͇͇͐̑̈͋̋̽͟ ̶͇̟̜̗̗͇͇͐̑̈͋̋̽͟e̷̥͙͈̖̤̯̹̯͗̉̈́̽ͨ̕e̷̥͙͈̖̤̯̹̯͗̉̈́̽ͨ̕v̛̝͕̱͙̞̖̒̂̔͆̊̎́ͫv̛̝͕̱͙̞̖̒̂̔͆̊̎́ͫę̴̤̭͖̈̐̿͂ͣ͒̃ͭ̕͟ę̴̤̭͖̈̐̿͂ͣ͒̃ͭ̕͟ṇ̳̭͓̟̠͕̯͑̉ͦ̀ͯ͜͡ṇ̳̭͓̟̠͕̯͑̉ͦ̀ͯ͜͡t̢̞͔̣̄̀͆̂̃ͨͦ͜͜͝͠t̢̞͔̣̄̀͆̂̃ͨͦ͜͜͝͠,̬̳̮͓͉̟͉͓̦͔̑̄ͨ̎͜,̬̳̮͓͉̟͉͓̦͔̑̄ͨ̎͜ ͕͈̠̰̬̬̌͂̏ͥ̀̕̚͢͠ ͕͈̠̰̬̬̌͂̏ͥ̀̕̚͢͠f̺̮̞̈́̏̋͏̺̖̝̓̑̂̚͢f̺̮̞̈́̏̋͏̺̖̝̓̑̂̚͢l̜̪͍̩̩̟͉͓̊̓ͤ̆ͣͫ̌l̜̪͍̩̩̟͉͓̊̓ͤ̆ͣͫ̌u̷̧̞̳̱̜̟̲͑̐͂ͪ͛͘͟u̷̧̞̳̱̜̟̲͑̐͂ͪ͛͘͟s͔͍̽ͣͮ̏̓͛̄ͯ̽͂̐ͯ͜s͔͍̽ͣͮ̏̓͛̄ͯ̽͂̐ͯ͜ḩ̼͕̦͖̼͚̗̃̃ͥ̅̂̈͟ḩ̼͕̦͖̼͚̗̃̃ͥ̅̂̈͟,̵͍̮̮̟͚̝̃ͨ̿ͭ̌ͤ̋̊,̵͍̮̮̟͚̝̃ͨ̿ͭ̌ͤ̋̊ ̷̨̨͈̝̦͂ͦ̒̋̇ͧ̒͟͝ ̷̨̨͈̝̦͂ͦ̒̋̇ͧ̒͟͝a̡̨̲̖̾̂͗̚͢͡͏͈ͤ̉͡a̡̨̲̖̾̂͗̚͢͡͏͈ͤ̉͡nͫͤ̚͜͏̧̛̣̻ͩ̔̍ͦ̕͝nͫͤ̚͜͏̧̛̣̻ͩ̔̍ͦ̕͝d͈̻̗̼̀͡͏̶̵̟̹̻̎̾ͯd͈̻̗̼̀͡͏̶̵̟̹̻̎̾ͯ ̶͙͈̠̜̬̺͛̀̊̂ͪ̔ͮ͑ ̶͙͈̠̜̬̺͛̀̊̂ͪ̔ͮ͑ş̧̡̢͓̠͋ͫ͑͒͊̅̔͜͞ş̧̡̢͓̠͋ͫ͑͒͊̅̔͜͞t̤̭͓̰̣̉̔̎ͫ͛ͦ̑̕͟͞t̤̭͓̰̣̉̔̎ͫ͛ͦ̑̕͟͞a̠͇͇̯̥͍͚̳̒́͐͐̏͋̓a̠͇͇̯̥͍͚̳̒́͐͐̏͋̓r͉͈̻̻͕̩̩̃̅͋͆ͮ͢͢͡r͉͈̻̻͕̩̩̃̅͋͆ͮ͢͢͡t̵̛̝̗̥̙̟̆ͮ̽̏ͧ͜͠ͅt̵̛̝̗̥̙̟̆ͮ̽̏ͧ͜͠ͅ ̷̼͎̦ͫ̈͑̃̽͏̲̪ͣͯ̽ ̷̼͎̦ͫ̈͑̃̽͏̲̪ͣͯ̽á̸̷̴̟̣́̔̌͏̶͕͋ͭͭá̸̷̴̟̣́̔̌͏̶͕͋ͭͭ ̧̧̲͍̘̘͚͔͇͙ͨͬ́̊ͅ ̧̧̲͍̘̘͚͔͇͙ͨͬ́̊ͅn̸̸̩͖͔͚͚̖͗ͦ̓̀̀̈́̈́n̸̸̩͖͔͚͚̖͗ͦ̓̀̀̈́̈́ę̵̧̬̣̦̖̝̹̱͌̃̑ͣ̚ę̵̧̬̣̦̖̝̹̱͌̃̑ͣ̚w͍̥͚̺ͮ̓̈̈́̾̊̆́̚͜͝w͍̥͚̺ͮ̓̈̈́̾̊̆́̚͜͝ ̛̹̲̥̝͙̾ͨ̆̎̃̋͂̓̕ ̛̹̲̥̝͙̾ͨ̆̎̃̋͂̓̕"̴̜̰̰̱̖̙̘̈́͌ͨͪ̐̕͠"̴̜̰̰̱̖̙̘̈́͌ͨͪ̐̕͠g̸̛͇̐͊͂̽͢͏͖̣ͫ͊ͯͅg̸̛͇̐͊͂̽͢͏͖̣ͫ͊ͯͅr̴̨̲͎̹͇̠̐ͤ̇̒ͬ̆ͧ͞r̴̨̲͎̹͇̠̐ͤ̇̒ͬ̆ͧ͞į̝̱̩͔̈ͨ̉͌̋̍̂͜͟͞į̝̱̩͔̈ͨ̉͌̋̍̂͜͟͞d̷̴̷̟̎͌͑͛̈́ͭͨͯ̋ͭ̕d̷̴̷̟̎͌͑͛̈́ͭͨͯ̋ͭ̕_̢̭̙̦̪͇̾̔̆ͬͦ́ͥ͢͡_̢̭̙̦̪͇̾̔̆ͬͦ́ͥ͢͡l̡̢̨̧͔̱̥̹̬̆ͮ̈́̏̅͜l̡̢̨̧͔̱̥̹̬̆ͮ̈́̏̅͜ĩ̢͖̠̩̞͚̰̰̉̋̌͛ͪ͠ĩ̢͖̠̩̞͚̰̰̉̋̌͛ͪ͠n̢̬̜̘̲͉ͮ͆͏̯͕ͭͦ̉̅n̢̬̜̘̲͉ͮ͆͏̯͕ͭͦ̉̅ē̡͈̮̭̰̜͍̗̮͔͌͐͆ͫē̡͈̮̭̰̜͍̗̮͔͌͐͆ͫ"̦̠̟ͣ̽͋͐ͧ̓̂̆̎͒͝ͅ"̦̠̟ͣ̽͋͐ͧ̓̂̆̎͒͝ͅ ̴̡̺̹̖̰̀ͤ͊̊͗̊́͜͠ ̴̡̺̹̖̰̀ͤ͊̊͗̊́͜͠ẻ͎̳̻̯̹͓͊̌̄͑͂̉͜͢ẻ͎̳̻̯̹͓͊̌̄͑͂̉͜͢v̞̬̪̥͖ͤ͐̍́ͤ̇̀̕̕ͅv̞̬̪̥͖ͤ͐̍́ͤ̇̀̕̕ͅȇ̶̱̗̩̠͚͎͊ͤͪͦͫ̂̚ȇ̶̱̗̩̠͚͎͊ͤͪͦͫ̂̚n̢̮̜͉̎ͨͩ̒̓ͬͨ̓ͦ͘͜n̢̮̜͉̎ͨͩ̒̓ͬͨ̓ͦ͘͜t̢͚͉̹͇̺̭ͪ̄̉ͨ̄͐̕ͅt̢͚͉̹͇̺̭ͪ̄̉ͨ̄͐̕ͅ ̵̛̳̱͍̩͓ͣ͋̈́͐ͭͧ̿ͅ ̵̛̳̱͍̩͓ͣ͋̈́͐ͭͧ̿ͅã̰̪̙͉̪͇ͣ͋ͤ̓͋͑̕͘ã̰̪̙͉̪͇ͣ͋ͤ̓͋͑̕͘t̴̴̡̡̛̳̼̻̳̂̽͒̇̚͠t̴̴̡̡̛̳̼̻̳̂̽͒̇̚͠ ̴̙̤̹͔̳̟̽̀̆ͥ̂̕͘͝ ̴̙̤̹͔̳̟̽̀̆ͥ̂̕͘͝t̴͖̼͔̬̦̏ͩ̄́̓̊̔̇͡t̴͖̼͔̬̦̏ͩ̄́̓̊̔̇͡ḫ̷͚͇̫̫̠͒ͮ̍ͣ̃͐͐̍ḫ̷͚͇̫̫̠͒ͮ̍ͣ̃͐͐̍e̳͉̮͖̳̣̎͌̂ͣ͋ͯ̆͜͞e̳͉̮͖̳̣̎͌̂ͣ͋ͯ̆͜͞ ̪̖̉̌̀̽̄̍̓̀̂̋͐̈̚ ̪̖̉̌̀̽̄̍̓̀̂̋͐̈̚c̶̫̜͚̊̿̂̿ͥͭ̓̂̈́͘͡c̶̫̜͚̊̿̂̿ͥͭ̓̂̈́͘͡ṷ̵̷͉̯̜̬̝̑͛ͤ̋ͧͯ̉ṷ̵̷͉̯̜̬̝̑͛ͤ̋ͧͯ̉ŗ̙̬̺̬ͥͤ̓͐̈́ͬ̽̌͡͡ŗ̙̬̺̬ͥͤ̓͐̈́ͬ̽̌͡͡r̛͖̼̣͙̋̇̀̅͒̽ͥ̑ͅͅr̛͖̼̣͙̋̇̀̅͒̽ͥ̑ͅͅě̢̪̦̼͘͏̴̠̞͍̓̐ͭ̇ě̢̪̦̼͘͏̴̠̞͍̓̐ͭ̇ǹ̨̨̛̛̯̍ͭͫ̎̍̃̄̐̍ǹ̨̨̛̛̯̍ͭͫ̎̍̃̄̐̍t̵̳͔̭̮̭̱̰ͤ͐̉̾͗̅͢t̵̳͔̭̮̭̱̰ͤ͐̉̾͗̅͢ ̩͕ͧ̌͂͂́ͫͥ̍͏̬̙͗ͅ ̩͕ͧ̌͂͂́ͫͥ̍͏̬̙͗ͅp̞̞͇͇̯̩̬̜ͥ̃͐̑͋́͗p̞̞͇͇̯̩̬̜ͥ̃͐̑͋́͗o̵͖͈̩̪̥̝̊̒̉̿͋ͩ̆͠o̵͖͈̩̪̥̝̊̒̉̿͋ͩ̆͠s̶͇̺̩̟̺͋̆̒ͫͥ̆͏͔͜s̶͇̺̩̟̺͋̆̒ͫͥ̆͏͔͜i̞̝̭̲͈̝̮̫͚͑̓ͤ̎ͮ̀i̞̝̭̲͈̝̮̫͚͑̓ͤ̎ͮ̀t̷̬͇̗̥̳͔̮͔̎̀ͣ̊̕͢t̷̬͇̗̥̳͔̮͔̎̀ͣ̊̕͢i̶̧̗̠̗̲͑̋ͣͪͦͣ͆̕͢i̶̧̗̠̗̲͑̋ͣͪͦͣ͆̕͢ǫ̰̟̙ͯ͏̬̦͚̝̀̈́ͣ͘͜ǫ̰̟̙ͯ͏̬̦͚̝̀̈́ͣ͘͜n̸̻̩͖͓̠̲̮̐̄̌̇́́͠n̸̻̩͖͓̠̲̮̐̄̌̇́́͠.̧̛͈̙̭̉ͥ̋͛̏͋̂̿͛͞.̧̛͈̙̭̉ͥ̋͛̏͋̂̿͛͞ ̡̲͇̳͔̦̤̹̥ͣ̍ͪͮ̎͡ ̡̲͇̳͔̦̤̹̥ͣ̍ͪͮ̎͡F̼̣͙̳̞͑͒ͧͣ̔̊̇̓̈́͠F̼̣͙̳̞͑͒ͧͣ̔̊̇̓̈́͠ŏ̸̶͎̘̟͙̤̑̒̿͗͛̐̚ŏ̸̶͎̘̟͙̤̑̒̿͗͛̐̚r̵̰͇͚̥̻͚̈́̌̽ͨͥ̕͘͞r̵̰͇͚̥̻͚̈́̌̽ͨͥ̕͘͞ ̵̶̧͖̙̝̻̭̤̒̿͌͗͑͡ ̵̶̧͖̙̝̻̭̤̒̿͌͗͑͡ś̸̬̘̭̼͓̹ͥ͛ͪ̐̈́͆͝ś̸̬̘̭̼͓̹ͥ͛ͪ̐̈́͆͝i͎̺̪̖̻ͫ̀ͤ̾ͦ̽ͭ͒̒͘i͎̺̪̖̻ͫ̀ͤ̾ͦ̽ͭ͒̒͘m̨̩͎̫̫̙̃̈͆ͬ̊͠͏̽͞m̨̩͎̫̫̙̃̈͆ͬ̊͠͏̽͞p̴̢̨͖̼͍̲͍̲ͩ̊̒̌̃ͤp̴̢̨͖̼͍̲͍̲ͩ̊̒̌̃ͤļ͔̱͙̝̟̜͚͎͕̮̹ͣ̀͘ļ͔̱͙̝̟̜͚͎͕̮̹ͣ̀͘ḭ̢̛͈͍̠̟̪̑̎̈́̑̽͜͡ḭ̢̛͈͍̠̟̪̑̎̈́̑̽͜͡c̖̠̥͔̪̼̑̃̆̓ͫ͗ͩͩ̋c̖̠̥͔̪̼̑̃̆̓ͫ͗ͩͩ̋i̴̸̡̘͇͔̹͂̍̐̀ͬͩ̈͘i̴̸̡̘͇͔̹͂̍̐̀ͬͩ̈͘t̵̵̖̫͙͎̒ͭ̔̃̔ͧͫ̽͝t̵̵̖̫͙͎̒ͭ̔̃̔ͧͫ̽͝y̴̘̱͈̳ͯ̈́̍ͮ̔͊͊̚̚͞y̴̘̱͈̳ͯ̈́̍ͮ̔͊͊̚̚͞ ̛̩̤̪͇̬͕̯̞̙̪̮̀̂̕ ̛̩̤̪͇̬͕̯̞̙̪̮̀̂̕ļ̲̰̞̈̌̿͐̉́̋ͬ͟͟͡ļ̲̰̞̈̌̿͐̉́̋ͬ͟͟͡e̡̧̛̬̣̗̣̰͂̐̂͗͛̋̇e̡̧̛̬̣̗̣̰͂̐̂͗͛̋̇a̡̯͉̠̞ͩͮ̉̓́ͮ̔̆̒͘a̡̯͉̠̞ͩͮ̉̓́ͮ̔̆̒͘v͔ͦ͏͎̪̮̝̟̤́̀͊̈̎͟v͔ͦ͏͎̪̮̝̟̤́̀͊̈̎͟ȩ̪͔̤̺͗ͦ̅̓ͭͤͫ̆ͣ͒ȩ̪͔̤̺͗ͦ̅̓ͭͤͫ̆ͣ͒ ̶̢̤͎̰̝̤͂̔͒ͦͦ͂̊̀ ̶̢̤͎̰̝̤͂̔͒ͦͦ͂̊̀p̛̝̪͚͖̫͕̅̍̊́̓͒̂̃p̛̝̪͚͖̫͕̅̍̊́̓͒̂̃l̴̸͉͎̖͕̦̥̾ͨ̌̑ͣ̕͝l̴̸͉͎̖͕̦̥̾ͨ̌̑ͣ̕͝a̰̩͔̼͔̦̩͒ͪͭ̐͡͏̗ͮa̰̩͔̼͔̦̩͒ͪͭ̐͡͏̗ͮč̵̱͇̲̞̉̆̌̄ͧͫ̑̈́͠č̵̱͇̲̞̉̆̌̄ͧͫ̑̈́͠e̶̛̜̬̯̤͔̓ͤͮͦ̂͐͜͜e̶̛̜̬̯̤͔̓ͤͮͦ̂͐͜͜ ̸̧̼̯͕̼̭ͣͣ̿̑̈̎̽͜ ̸̧̼̯͕̼̭ͣͣ̿̑̈̎̽͜f̴̼̞͇̺̊̎̓̑̔̈́͗̊͘͞f̴̼̞͇̺̊̎̓̑̔̈́͗̊͘͞ő̶̶̧̺̤̜̹̫͎̻̏̉̍ͩő̶̶̧̺̤̜̹̫͎̻̏̉̍ͩr̵̙̱̺̮͙̯̼͐̀͗̾͊͟͝r̵̙̱̺̮͙̯̼͐̀͗̾͊͟͝ ̵̪̥͍̩̱ͨ͗̓̎̓͒̚͜͞ ̵̪̥͍̩̱ͨ͗̓̎̓͒̚͜͞t̪̤͙̥̖̹̣̤͎̞̒́̒̽̃t̪̤͙̥̖̹̣̤͎̞̒́̒̽̃ḥ̵̷̷̭̘͙͓ͩ̓ͪͤ̓ͮ̍ḥ̵̷̷̭̘͙͓ͩ̓ͪͤ̓ͮ̍é̡̬̺͉̻̻̀̀͂̽ͬ̕̕͠é̡̬̺͉̻̻̀̀͂̽ͬ̕̕͠ ̵̱̻̣͙̌͊́ͯ́̐̀́͂̕ ̵̱̻̣͙̌͊́ͯ́̐̀́͂̕f̨̪͈̲̬̟̝͎̀̔̋ͨ̀͡ͅf̨̪͈̲̬̟̝͎̀̔̋ͨ̀͡ͅi̵̫̱̝͒̇̏̃ͭ̂̄̄̊̐͠i̵̫̱̝͒̇̏̃ͭ̂̄̄̊̐͠n̵̷͖̻̦͍̻̑̈́̎̓͑ͪͫ͟n̵̷͖̻̦͍̻̑̈́̎̓͑ͪͫ͟a̗̗̠̫̦̻̹͇ͯͦͫ͗̽ͥ̚a̗̗̠̫̦̻̹͇ͯͦͫ͗̽ͥ̚l̵̨̨͙̜͔̘̗̯͌̋͂̑̄͢l̵̨̨͙̜͔̘̗̯͌̋͂̑̄͢ ̵̷̛̺̳͙̲̥̬̊̌̽̓̇͝ ̵̷̛̺̳͙̲̥̬̊̌̽̓̇͝"̸̨̫̟̤̥͉̮̥̀ͤ̐͊̇̑"̸̨̫̟̤̥͉̮̥̀ͤ̐͊̇̑c̵̭̘͍͇̜ͨ̔̍̆͛͌͂͌͛c̵̭̘͍͇̜ͨ̔̍̆͛͌͂͌͛l̸̷̢̡̫̩̃́͐̆͒ͨ̔̄͟l̸̷̢̡̫̩̃́͐̆͒ͨ̔̄͟è̙͎̝̞̠̹͍́́̅͐͌͘ͅè̙͎̝̞̠̹͍́́̅͐͌͘ͅą̶̢̡̘̦͖̰̌̎͌ͩ́̓ͮą̶̢̡̘̦͖̰̌̎͌ͩ́̓ͮȑ̹̻̦͙̠͂̽͆ͫ͛͟͏̺͡ȑ̹̻̦͙̠͂̽͆ͫ͛͟͏̺͡"̣̞͕̇ͯ̓̽ͪ͑́̍̚͘͘ͅ"̣̞͕̇ͯ̓̽ͪ͑́̍̚͘͘ͅ ̛̩̘̗̜̗̥̂͊ͥ̀ͨͫ̾ͧ ̛̩̘̗̜̗̥̂͊ͥ̀ͨͫ̾ͧe̵̡͚͉̤̯̮͛̎̓ͪ̾̉̆͟e̵̡͚͉̤̯̮͛̎̓ͪ̾̉̆͟l̢̩͓̙͓͍̈̊ͫͣ́̅ͧ͛͞l̢̩͓̙͓͍̈̊ͫͣ́̅ͧ͛͞e̠̱͉̯͔̙͔̓ͩ̃͋͌ͬͭ͋e̠̱͉̯͔̙͔̓ͩ̃͋͌ͬͭ͋m̧̡̛̤͕̻̳̽͛̓̈́ͣ̊̚͟m̧̡̛̤͕̻̳̽͛̓̈́ͣ̊̚͟e̯͎̺͔̼̾͂͐̄ͮ͌̍͑̕ͅe̯͎̺͔̼̾͂͐̄ͮ͌̍͑̕ͅṉ̸͔̩̙̙̹͚̭ͩ͛͗̀̾ͅṉ̸͔̩̙̙̹͚̭ͩ͛͗̀̾ͅt̸̢̧̝͉̺͉̓́̇ͯ̕͠͠͝t̸̢̧̝͉̺͉̓́̇ͯ̕͠͠͝ ̳͔̜̹̘̪̅̋̎̉͆̑ͤ͡͞ ̳͔̜̹̘̪̅̋̎̉͆̑ͤ͡͞a̷̳͓̞̱͈̓́̒̔̆ͩͯ̊͠a̷̳͓̞̱͈̓́̒̔̆ͩͯ̊͠s̷̛͙̘̳͙͓̠̞̫̆̓̚̚͡s̷̛͙̘̳͙͓̠̞̫̆̓̚̚͡ ̷̢̦̘̮̰͕̏̀ͩ̓͂ͦ͟ͅ ̷̢̦̘̮̰͕̏̀ͩ̓͂ͦ͟ͅw̮͔͇͕̝͖ͪ͒̏̇ͪ̇̍ͦ͠w̮͔͇͕̝͖ͪ͒̏̇ͪ̇̍ͦ͠ẹ̗͚̮̭̓̍̐̃̍́̐͘̕͘ẹ̗͚̮̭̓̍̐̃̍́̐͘̕͘l̴̡̩̱̞͛ͤ͑͑͂̀ͣ̉̌̕l̴̡̩̱̞͛ͤ͑͑͂̀ͣ̉̌̕l̷̨̘̤̣̯͇̲̝ͨ̍ͦ͝͝ͅl̷̨̘̤̣̯͇̲̝ͨ̍ͦ͝͝ͅ,̷̻̙̖͔͚͋̇̂̑͗̕̕͢ͅ,̷̻̙̖͔͚͋̇̂̑͗̕̕͢ͅ ̴̶̮̣̪̣̟͚̅͊ͧͭ̂͂̓ ̴̶̮̣̪̣̟͚̅͊ͧͭ̂͂̓h̡͈̗̙͚̳͕̤͍̿̄͑ͥ͊̉h̡͈̗̙͚̳͕̤͍̿̄͑ͥ͊̉e̡̞͔̘͐̋͌̅̓̈͆̇̿͟͠e̡̞͔̘͐̋͌̅̓̈͆̇̿͟͠n̤͕͉̣̐͊́̽̍́͂͏̙̾ͅn̤͕͉̣̐͊́̽̍́͂͏̙̾ͅc̨̧̛̜͈͍͓̣̹ͮͧ̊͟͡ͅc̨̧̛̜͈͍͓̣̹ͮͧ̊͟͡ͅȩ̢̛͚̣͓͙ͣͮͩ̌̌́͟͠ȩ̢̛͚̣͓͙ͣͮͩ̌̌́͟͠ ̷̜̀ͪ̅͐͝͏̤̮̄͑̾ͬ͝ ̷̜̀ͪ̅͐͝͏̤̮̄͑̾ͬ͝t̙͔̳͕̝̝͔͐̊̓ͩ̈́ͪ̒͠t̙͔̳͕̝̝͔͐̊̓ͩ̈́ͪ̒͠h̩̖̪̠͂ͪ́͐̿͊ͨ̈̋̃͟h̩̖̪̠͂ͪ́͐̿͊ͨ̈̋̃͟e̋͆ͬ͞͏̵̘̹̙̂ͥ̐̀́ͅe̋͆ͬ͞͏̵̘̹̙̂ͥ̐̀́ͅ ̧̱͌̆̏͘͏̬̠̹̏ͦ̓͋̕ ̧̱͌̆̏͘͏̬̠̹̏ͦ̓͋̕f̸̧͎̣̪̯̻̗̟̎̎͂ͫ̕͝f̸̧͎̣̪̯̻̗̟̎̎͂ͫ̕͝a̟̓̃͒͏̸̧̣̱̎̽̏̐̓ͤa̟̓̃͒͏̸̧̣̱̎̽̏̐̓ͤć̯͚͕͡͏̴̛̜̺̣͙͉̀̍ć̯͚͕͡͏̴̛̜̺̣͙͉̀̍ț̢̹͇͈ͩ̋́̔̇̉ͤ̽͢ͅț̢̹͇͈ͩ̋́̔̇̉ͤ̽͢ͅo͈̥͊ͦ̔̃ͬ̎͛ͧ͌͌͟͟͝o͈̥͊ͦ̔̃ͬ̎͛ͧ͌͌͟͟͝r̸̨͕̙̟̟͈̼͔ͥͮ͋ͫ͒͝r̸̨͕̙̟̟͈̼͔ͥͮ͋ͫ͒͝ ̧̗̝̘͇͉̩̗̅̊̓͂̐͊͡ ̧̗̝̘͇͉̩̗̅̊̓͂̐͊͡o̬̻̪̹̘̥̳̲̫̳̫̳ͬͩ͊o̬̻̪̹̘̥̳̲̫̳̫̳ͬͩ͊f̡̣̜̗͚̙͇̓͑̍ͬ̅̚͞͞f̡̣̜̗͚̙͇̓͑̍ͬ̅̚͞͞ ̵̸͓̱̻̱̌̃ͫ̑̿͐͂ͨ̇ ̵̸͓̱̻̱̌̃ͫ̑̿͐͂ͨ̇2̡̝̥̯͚͔̖̫ͫ̽̔͑̅̚͞2̡̝̥̯͚͔̖̫ͫ̽̔͑̅̚͞ ̨̠̰̯̤͕̗̗̳͆̑͐͌̕͟ ̨̠̰̯̤͕̗̗̳͆̑͐͌̕͟i͇̫̲̲͓̖͙͖̱ͤ͊̎̃ͧ͢i͇̫̲̲͓̖͙͖̱ͤ͊̎̃ͧ͢n̵̢̻̦̭̏̓̂́͏̲̪̙̏͌n̵̢̻̦̭̏̓̂́͏̲̪̙̏͌ ̸̵̛̹͇͚̓̋̎̏̽̚̚͢͜ ̸̵̛̹͇͚̓̋̎̏̽̚̚͢͜t͈͔̤̲̬ͧͩ̔̀̂́͑̂ͭ͘t͈͔̤̲̬ͧͩ̔̀̂́͑̂ͭ͘h̡̻̙̖̪̱̍͗̍́͗́́̅͠h̡̻̙̖̪̱̍͗̍́͗́́̅͠e̶̖̩̜͐ͥͨͪͣ̆͋̋̉͢͡e̶̖̩̜͐ͥͨͪͣ̆͋̋̉͢͡ ̧͈̙̤͉͌ͩ̓͐̌̄ͦ͌ͥ͝ ̧͈̙̤͉͌ͩ̓͐̌̄ͦ͌ͥ͝c̨̨̹̗̬͕̩̈̑̉̃̑́̆͞c̨̨̹̗̬͕̩̈̑̉̃̑́̆͞h̭͚̦̻̘͈͆ͪ̿̌́̏̐͊͠h̭͚̦̻̘͈͆ͪ̿̌́̏̐͊͠ę̙͍͚ͮͦ́ͭͥ̈͑ͧ̕̕͢ę̙͍͚ͮͦ́ͭͥ̈͑ͧ̕̕͢c̝̭͓̹̙̠̄̍ͦ̌̏̉̇͛ͥc̝̭͓̹̙̠̄̍ͦ̌̏̉̇͛ͥk̶̡͚̦̰̣͖̔͌́̋͋̔ͥ̕k̶̡͚̦̰̣͖̔͌́̋͋̔ͥ̕.̛̱̖͓̼͚̲ͪ̆̈́̃̚͜͞ͅ.̛̱̖͓̼͚̲ͪ̆̈́̃̚͜͞ͅ ̶̴̺̹̜̺͇ͮ̉ͯ͋͗͝͝ͅ ̶̴̺̹̜̺͇ͮ̉ͯ͋͗͝͝ͅA͉̺̰̲̟̺͚͙̽́̀̌ͬͩ͠A͉̺̰̲̟̺͚͙̽́̀̌ͬͩ͠l̜͈̟͖̾͑̈́́̇ͮ̐ͦͮ͋̋l̜͈̟͖̾͑̈́́̇ͮ̐ͦͮ͋̋s̡̛̯̜̩̪̤̹̅͛̋̓͂̊͡s̡̛̯̜̩̪̤̹̅͛̋̓͂̊͡o̶͉̱͉̠̫̻ͤͣ̓ͭ͊͑͆ͅo̶͉̱͉̠̫̻ͤͣ̓ͭ͊͑͆ͅ ̢̗͉̝̞͗͛̊́ͦ͛̍̚͜͡ ̢̗͉̝̞͗͛̊́ͦ͛̍̚͜͡i̵̧͍̝̦̬̭̽̎̈̃̓̚͟͝i̵̧͍̝̦̬̭̽̎̈̃̓̚͟͝f̴̛̻̞̬̟͖̙̦̑́̀̍̇͝f̴̛̻̞̬̟͖̙̦̑́̀̍̇͝ ͓͚͇̺ͪ͛ͦͥ̓̎ͨ͒͊̚͟ ͓͚͇̺ͪ͛ͦͥ̓̎ͨ͒͊̚͟t̶̯͙̹̟͖̂̇͒̾ͭ͒͐͛̌t̶̯͙̹̟͖̂̇͒̾ͭ͒͐͛̌ḧ̠̬́͋̕͏̧̙̼͑͂̀̌̉̈ḧ̠̬́͋̕͏̧̙̼͑͂̀̌̉̈e̫̺͔̗̳͋̒͌ͤͬ̔͗̕̕͡e̫̺͔̗̳͋̒͌ͤͬ̔͗̕̕͡r̴̛̭͙͑́ͤ̓̒͊̈ͥ̑ͮ͞r̴̛̭͙͑́ͤ̓̒͊̈ͥ̑ͮ͞ȇ̷̛̝͓̜̮̩͙ͨ̎̎͛̌̽ȇ̷̛̝͓̜̮̩͙ͨ̎̎͛̌̽ ̸̩̤̘̖̳̻̋̈͛͑̈́̌̒͝ ̸̩̤̘̖̳̻̋̈͛͑̈́̌̒͝i̧͖͕̞̩̱̭̎̽̀͏̮ͬͨ͡i̧͖͕̞̩̱̭̎̽̀͏̮ͬͨ͡ś̶͍̞̉ͯ̊ͦͧ̀̆̌͂͞͝ś̶͍̞̉ͯ̊ͦͧ̀̆̌͂͞͝ ̡̛͙͈̜̗̻̮̞͍̝̝̓̅͋ ̡̛͙͈̜̗̻̮̞͍̝̝̓̅͋ȃ̢͍̟̬̳ͣ͏̝̔͑̎ͣ̕ͅȃ̢͍̟̬̳ͣ͏̝̔͑̎ͣ̕ͅ ̖̼͙̯͎̒͒ͩͣ̀̆͗̇͝ͅ ̖̼͙̯͎̒͒ͩͣ̀̆͗̇͝ͅļ̩̪͕͓͖͌̐͛̊ͫͪ̿͊ͤļ̩̪͕͓͖͌̐͛̊ͫͪ̿͊ͤȍ͍̜͓̣͈͍̳̄͊̄ͣ͢͝ͅȍ͍̜͓̣͈͍̳̄͊̄ͣ͢͝ͅt̵̷̛̤͎͕̖̹ͦ̇͒́͂ͮ͘t̵̷̛̤͎͕̖̹ͦ̇͒́͂ͮ͘ ̶̨̳̺̖̼̯͎̯̬̀̈̐ͦ͜ ̶̨̳̺̖̼̯͎̯̬̀̈̐ͦ͜o̷̡̯̼͇̻͙̙̙͒͂ͩ̉̀ͮo̷̡̯̼͇̻͙̙̙͒͂ͩ̉̀ͮf̻̺̖͉̊ͤ̇ͤ̍͌͛̐͟͟͝f̻̺̖͉̊ͤ̇ͤ̍͌͛̐͟͟͝ ̷̴̟̠̹͎̱̈́̈́̆̈́̇̃͘͞ ̷̴̟̠̹͎̱̈́̈́̆̈́̇̃͘͞p̸̸̧̨̦̪͕̮̀͒͒͌͌̑͢p̸̸̧̨̦̪͕̮̀͒͒͌͌̑͢a̯͙ͥͬ̊̓ͯ͌ͮ͊̎̂͒͢͠a̯͙ͥͬ̊̓ͯ͌ͮ͊̎̂͒͢͠ċ̛̫̱̥͕̳̲ͫͤ̎͐̄̕͠ċ̛̫̱̥͕̳̲ͫͤ̎͐̄̕͠k̵̡̹̲͓̾̉͏̶̻́ͩ̎̓̊k̵̡̹̲͓̾̉͏̶̻́ͩ̎̓̊e̵̡̢̢̪͍̲̣͒̒̍ͯͤ͊͑e̵̡̢̢̪͍̲̣͒̒̍ͯͤ͊͑ḑ̘͖̼̌ͭ̐͑ͯ͋ͬ̈́͆̓̚ḑ̘͖̼̌ͭ̐͑ͯ͋ͬ̈́͆̓̚ ̶̶̧̺̰͖̯͇̏̄̿ͤ̔͘͡ ̶̶̧̺̰͖̯͇̏̄̿ͤ̔͘͡c̩̝̯͂͆ͤ̈́͆̏̑̄ͦ͗̿͞c̩̝̯͂͆ͤ̈́͆̏̑̄ͦ͗̿͞e̥̫͌̓̚͏̵̧͙̣̻̞́̌̉e̥̫͌̓̚͏̵̧͙̣̻̞́̌̉l̵̪̣̪̘̙̟̘͊͒̿̏͗͝͠l̵̪̣̪̘̙̟̘͊͒̿̏͗͝͠l̨̮͚̟̺̹ͮ͊ͭ̊ͮ̿͘͘͟l̨̮͚̟̺̹ͮ͊ͭ̊ͮ̿͘͘͟s̩̯͌̊͒͝͏̥͑̈̎ͥ̀͟͞s̩̯͌̊͒͝͏̥͑̈̎ͥ̀͟͞,̡̙̻̭̪̭̖̀̇̒ͮ̈̒̇̕,̡̙̻̭̪̭̖̀̇̒ͮ̈̒̇̕ ̷̢̫̳̺͉̯̳̂̓ͨ͋͛͂͞ ̷̢̫̳̺͉̯̳̂̓ͨ͋͛͂͞p̟̰͖̹̦̲͙̉̑͐͑͗̀͟͢p̟̰͖̹̦̲͙̉̑͐͑͗̀͟͢á̳̜̈̓͛͠͏̢̄ͧ̉ͧͨͅá̳̜̈̓͛͠͏̢̄ͧ̉ͧͨͅs̵̢̥̖̰̥̼̯ͪͭ̿͗͌ͪ͊s̵̢̥̖̰̥̼̯ͪͭ̿͗͌ͪ͊s̘̱͕̓̅͛̆̅ͮ́̅ͧͣ̈̚s̘̱͕̓̅͛̆̅ͮ́̅ͧͣ̈̚ ̡̮̹̹̗͎̝ͣ̇́ͤ̾ͤ͞ͅ ̡̮̹̹̗͎̝ͣ̇́ͤ̾ͤ͞ͅt̡̛̝̯̱̖̠̱ͤ͒͆̍̚͘ͅt̡̛̝̯̱̖̠̱ͤ͒͆̍̚͘ͅh̛̯̻̩̘̤̳̿̓͂̐͐͡͡͠h̛̯̻̩̘̤̳̿̓͂̐͐͡͡͠ë́͏̩̣̗̺̟ͭ̃̾͂̀͘͢͝ë́͏̩̣̗̺̟ͭ̃̾͂̀͘͢͝m̸̜͔̮̥̹̲̖̲̄̈̊̎ͬ͠m̸̜͔̮̥̹̲̖̲̄̈̊̎ͬ͠ ̝͇̏͏̡̟̥͍ͦ̋̎ͤ̕͢͜ ̝͇̏͏̡̟̥͍ͦ̋̎ͤ̕͢͜ó͖̬̩̤̜̫͔͑̌ͫ̈́͡͏̇ó͖̬̩̤̜̫͔͑̌ͫ̈́͡͏̇f̴̶̴̥͔̗͖̬̆͒ͨ͊ͬͅͅf̴̶̴̥͔̗͖̬̆͒ͨ͊ͬͅͅ ̮̦̲̼̞̑ͨ͒̌ͤ̿́̌͐͒ ̮̦̲̼̞̑ͨ͒̌ͤ̿́̌͐͒t̛̮̰͔̋͂͗̓͋̇̅̆̒͊ͅt̛̮̰͔̋͂͗̓͋̇̅̆̒͊ͅo̴̢͚̮̳͙̟̳̐ͬ͑̃̓͡ͅo̴̢͚̮̳͙̟̳̐ͬ͑̃̓͡ͅ ̝̺͖͒ͯͭ̑ͤ͊ͮ͂̚̚͡ͅ ̝̺͖͒ͯͭ̑ͤ͊ͮ͂̚̚͡ͅt̶̨̲̦͉͖̲̤̗ͬ̋̋ͦ̂ͯt̶̨̲̦͉͖̲̤̗ͬ̋̋ͦ̂ͯh̶̨̤͍̜̯͈͇͛ͩ́̀͊ͮͩh̶̨̤͍̜̯͈͇͛ͩ́̀͊ͮͩe̡̢̤̰̰͙̭͈̓ͯͬ͑ͨ̃͢e̡̢̤̰̰͙̭͈̓ͯͬ͑ͨ̃͢ ̶̛̺̦̯̹̞͎̟̥̞̝̐̕ͅ ̶̛̺̦̯̹̞͎̟̥̞̝̐̕ͅU͙̓ͤ͏̟̗̟͕̥̰́͑̆ͧ͟U͙̓ͤ͏̟̗̟͕̥̰́͑̆ͧ͟I̪̩͕͕̼̎͐ͭͦ͐̋̐̅ͯ͢I̪̩͕͕̼̎͐ͭͦ͐̋̐̅ͯ͢ ̢̦͍̰̬͚ͯ́ͫ̿ͨ͘͘͝͡ ̢̦͍̰̬͚ͯ́ͫ̿ͨ͘͘͝͡t̴̨͉̟̥͛͐̇̀̎̒̄ͧ̀̇t̴̨͉̟̥͛͐̇̀̎̒̄ͧ̀̇o̸͇͏̷͍͉̠ͤ̈̾ͤ͛ͥ̚͝o̸͇͏̷͍͉̠ͤ̈̾ͤ͛ͥ̚͝ ̸̣͕̝̯̼́ͨͯ̏̋̂̑́͘ ̸̣͕̝̯̼́ͨͯ̏̋̂̑́͘l̵̨̛̬̱̣̬͚̺̫̳ͨ̽ͫͯl̵̨̛̬̱̣̬͚̺̫̳ͨ̽ͫͯḙ̳͕̝̥̄̆ͮ̄ͦͨ̓̚͏͢ḙ̳͕̝̥̄̆ͮ̄ͦͨ̓̚͏͢t̵̼̲̣̉̀̌ͭ̒̓͌̀͒̀ͅt̵̼̲̣̉̀̌ͭ̒̓͌̀͒̀ͅ ̺͈͙̘̣̩̙͕̋̇͆̀͊̇̂ ̺͈͙̘̣̩̙͕̋̇͆̀͊̇̂i̧̭̲̼̗̥̪̣̭ͬ͑ͨ̓̈̐i̧̭̲̼̗̥̪̣̭ͬ͑ͨ̓̈̐ṱ̴̯̲̱̫̲̥ͭ̀͋̂̚̕͟ṱ̴̯̲̱̫̲̥ͭ̀͋̂̚̕͟ ̖̗͔̦̲̻̌̀̏͆͊ͤ̑ͨ̉ ̖̗͔̦̲̻̌̀̏͆͊ͤ̑ͨ̉s̨̢͔̄̃ͯ̈́̑͐̚̕͢͝͝s̨̢͔̄̃ͯ̈́̑͐̚̕͢͝͝t͚͉͇̳̘̅̇̋͌̀̋́ͨ́͏t͚͉͇̳̘̅̇̋͌̀̋́ͨ́͏â͇͎̺ͨͭ͡͏͚̯ͤͩ͋ͧ͘â͇͎̺ͨͭ͡͏͚̯ͤͩ͋ͧ͘r̷̘̜͍̫̯ͭͪ͑͗̓͆͟͝ͅr̷̘̜͍̫̯ͭͪ͑͗̓͆͟͝ͅṭ̷̢̱͕̎ͬ̿́́͊̐͟͢͜ṭ̷̢̱͕̎ͬ̿́́͊̐͟͢͜ ̟̥̖͕̭͍̲ͩ̆̒̑͊̕̚͡ ̟̥̖͕̭͍̲ͩ̆̒̑͊̕̚͡p̶̴̨̧̜̰̜̘̉̽ͤ́͘͢͜p̶̴̨̧̜̰̜̘̉̽ͤ́͘͢͜ṙ̠̘̟̭͖̜̜͍ͦ̾͒͜͠͞ṙ̠̘̟̭͖̜̜͍ͦ̾͒͜͠͞ǒ̷̶̫͈̫̹ͬ͂ͧ̿̿̑͂͂ǒ̷̶̫͈̫̹ͬ͂ͧ̿̿̑͂͂c̵̸̮͕̮͈̞̼͎͉͍̑́ͭ̀c̵̸̮͕̮͈̞̼͎͉͍̑́ͭ̀e̸̡͔͓̳̞̟̘ͩ̍̽ͪͤͣ͢e̸̡͔͓̳̞̟̘ͩ̍̽ͪͤͣ͢s̨͍̬̖͂̏ͦ̀ͫ̐ͨͪ͡ͅͅs̨͍̬̖͂̏ͦ̀ͫ̐ͨͪ͡ͅͅs̡̻̹̜͍̺͌͛ͮ̓̀̈́̉̿͑s̡̻̹̜͍̺͌͛ͮ̓̀̈́̉̿͑í̤̜ͭ͢͏̶̶̹͍ͤͪ̉ͫ̋í̤̜ͭ͢͏̶̶̹͍ͤͪ̉ͫ̋ņ̵̼̪̭̲̤͙͖̒̑ͨ͂̌͘ņ̵̼̪̭̲̤͙͖̒̑ͨ͂̌͘g̷̢̢̭̘̙̥̖̲̭͛ͣ́̀̍g̷̢̢̭̘̙̥̖̲̭͛ͣ́̀̍ ̷̺̱̙̹͔̗͆ͪ̾ͬͬ̓͘͠ ̷̺̱̙̹͔̗͆ͪ̾ͬͬ̓͘͠t̛̤̻̲̓̓ͩ̇ͫͫ͆ͦ͛̕͞t̛̤̻̲̓̓ͩ̇ͫͫ͆ͦ͛̕͞ḣ̜̘̮̫͍̗͇ͥͯ̽̕͢͢͝ḣ̜̘̮̫͍̗͇ͥͯ̽̕͢͢͝ẹ̵̱̹͚͖̜̞̆̾͌ͬ̀̿͞ẹ̵̱̹͚͖̜̞̆̾͌ͬ̀̿͞m̶̢̧͉̝̜̝̍͑͂̽̌̉̈́ͩm̶̢̧͉̝̜̝̍͑͂̽̌̉̈́ͩ' + api.nvim_buf_set_lines(0, 0, -1, true, { text }) + + screen:try_resize(250, 10) + screen:expect({ + grid = [[ + {100: 1 }{101:^c̯̥̳̮̫̳͔̱̀ͦͩ̄͋̓͘c̯̥̳̮̫̳͔̱̀ͦͩ̄͋̓͘l̶̴̴̨̛͓͚͎̥ͦͤ̑͆͛͢l̶̴̴̨̛͓͚͎̥ͦͤ̑͆͛͢ô̷̤̩ͯͧ͆ͪ̈́́͒̒̐͐̕ô̷̤̩ͯͧ͆ͪ̈́́͒̒̐͐̕s̶̷̢̩̱̠̀ͦ̽ͮ͒ͨ̚͟͠s̶̷̢̩̱̠̀ͦ̽ͮ͒ͨ̚͟͠e͉̯̱̖͕̳̼̽̊̒ͣ̊ͥ̚̚e͉̯̱̖͕̳̼̽̊̒ͣ̊ͥ̚̚ ͇͌̈̄ͬͧͧ͝͠͏̷ͪ̎͟͠ ͇͌̈̄ͬͧͧ͝͠͏̷ͪ̎͟͠t̵̳̅̽͏̵̡̥̲͍͙̹̯ͩ̐t̵̳̅̽͏̵̡̥̲͍͙̹̯ͩ̐o͋͏̬̞̣ͬ́ͬ̎̋̓̽͘͠͡o͋͏̬̞̣ͬ́ͬ̎̋̓̽͘͠͡ ͏̛͙̮̈ͬ̌͐ͤ͒ͧ̎ͤ͜͠ ͏̛͙̮̈ͬ̌͐ͤ͒ͧ̎ͤ͜͠ǫ̛̳͕̦͖̪̀͂͛̅̔ͪ͒͜ǫ̛̳͕̦͖̪̀͂͛̅̔ͪ͒͜v̸̡͖̗̣ͯ̿̔͊̅ͯ̈̓̇ͅv̸̡͖̗̣ͯ̿̔͊̅ͯ̈̓̇ͅe̴̶̢̜̭̠̰̞ͪͨ͂ͤ͆́͗e̴̶̢̜̭̠̰̞ͪͨ͂ͤ͆́͗r̨̖̼̳̳͚̖̒ͯ̊̋̂̑̚͞r̨̖̼̳̳͚̖̒ͯ̊̋̂̑̚͞f̵̮̣̬̾͛̌ͧͦ͘͢͜͜͠ͅf̵̮̣̬̾͛̌ͧͦ͘͢͜͜͠ͅl̡͉̬̳͈̠̏͂ͤ̈ͨ̀ͩ̔͏l̡͉̬̳͈̠̏͂ͤ̈ͨ̀ͩ̔͏ỏ̶̢̻̠͎͇͈̜̈̆ͯ̔ͩ̾ỏ̶̢̻̠͎͇͈̜̈̆ͯ̔ͩ̾ẉ̦̞̼̩̣͙̅̿́̓̉̎̈ͪẉ̦̞̼̩̣͙̅̿́̓̉̎̈ͪi̷̡͍͓͔̝͙͖͖̍ͯͤͬͦ͝i̷̡͍͓͔̝͙͖͖̍ͯͤͬͦ͝n̠̪̰͋ͩ̆͏̶̖̯ͭ̀̿͛͘n̠̪̰͋ͩ̆͏̶̖̯ͭ̀̿͛͘g̴̭͎̞͙̗̯͙͖ͬ́ͧͧ͝ͅg̴̭͎̞͙̗̯͙͖ͬ́ͧͧ͝ͅ ̢͎̬͓̮̹̰̙͍̓̐͋͂͐̚ ̢͎̬͓̮̹̰̙͍̓̐͋͂͐̚ť̴̼̝̗ͩ̍͋̿͏͇ͧ̑̏̚ť̴̼̝̗ͩ̍͋̿͏͇ͧ̑̏̚h̨̨̢͓̲͚͕̦ͨ͛̓ͩ̚͟͠h̨̨̢͓̲͚͕̦ͨ͛̓ͩ̚͟͠ȩ͕̜̥̃̑͋̏͐̎̒͛͊͏͙ȩ͕̜̥̃̑͋̏͐̎̒͛͊͏͙ ̵̨̜̜̠͉̱͎͑ͥ̌͐̽͢͡ ̵̨̜̜̠͉̱͎͑ͥ̌͐̽͢͡r̢̫͓͎̙̭̽ͥͭ͐̂̀̕͟͝r̢̫͓͎̙̭̽ͥͭ͐̂̀̕͟͝e̴̷͓̹̩ͧ́̉̑̈̿ͥ̕͡͝e̴̷͓̹̩ͧ́̉̑̈̿ͥ̕͡͝d̵̡̼̩̠̜͈̯̬͚͛̋̀̆͟d̵̡̼̩̠̜͈̯̬͚͛̋̀̆͟ŕ̻̳̬͏̨̮͚̲ͥ̌͆͗͠ͅŕ̻̳̬͏̨̮͚̲ͥ̌͆͗͠ͅā̸͙̥̤͍͈̣ͪͨ̈͋̈́̈́͜ā̸͙̥̤͍͈̣ͪͨ̈͋̈́̈́͜w̶̦̪͚̤̃ͬ̓͒ͤ̇̇ͮ͢͡w̶̦̪͚̤̃ͬ̓͒ͤ̇̇ͮ͢͡ ̷̡̦̻̻̪͚̳͎ͥ̓ͩͪ͠͝ ̷̡̦̻̻̪͚̳͎ͥ̓ͩͪ͠͝b̳ͮ̒̊̆̒́̈́̏̓͋ͭ̔ͤ̚b̳ͮ̒̊̆̒́̈́̏̓͋ͭ̔ͤ̚u̧̟̫͎͚̭̠͕͂̄̀̒̈̇͜u̧̟̫͎͚̭̠͕͂̄̀̒̈̇͜f̶̸̢͍̑̂̊ͥͫ̈́ͥ͛̈́̃͝f̶̸̢͍̑̂̊ͥͫ̈́ͥ͛̈́̃͝f̵̼̭̮͎ͧ̒̆͊̀ͤ͊̇̕͡f̵̼̭̮͎ͧ̒̆͊̀ͤ͊̇̕͡e̮̪̲̣̞̖͎ͥͤ̐̌̓̐͟͢e̮̪̲̣̞̖͎ͥͤ̐̌̓̐͟͢ŗ̭̘̮̹̖̎̄̆́ͫͭ͢ͅͅŗ̭̘̮̹̖̎̄̆́ͫͭ͢ͅͅ.̪̖͉͚̹̾̉ͮ̔̊ͪ̾̎͟͞.̪̖͉͚̹̾̉ͮ̔̊ͪ̾̎͟͞ ̷̧̺̰͎̣̃͒͗ͮ͑ͪͮ͞ͅ ̷̧̺̰͎̣̃͒͗ͮ͑ͪͮ͞ͅf̛̫͚͚͍̜͎̗̳̂ͬͭ͜͢͝f̛̫͚͚͍̜͎̗̳̂ͬͭ͜͢͝i̴̵̡̛̛͎̤̳̮ͩ̐ͩ͑̇̚i̴̵̡̛̛͎̤̳̮ͩ̐ͩ͑̇̚n̵͇͍͚̖̥̣ͨ̄ͧ̌̂͗͘͝n̵͇͍͚̖̥̣ͨ̄ͧ̌̂͗͘͝ȉ̼̱̫̜̋̓̐͌͆ͨ͘͝͡ͅȉ̼̱̫̜̋̓̐͌͆ͨ͘͝͡ͅs̴̸̝̺̬͚̲̹̘ͪ̆̊ͥ͞͝s̴̸̝̺̬͚̲̹̘ͪ̆̊ͥ͞͝h̴̜̠͇ͦͥ̔̅ͭͭ͋ͩ͟͡͞h̴̜̠͇ͦͥ̔̅ͭͭ͋ͩ͟͡͞ ̶̧̛̻͙̤̘̺̣̻̗͍̓͑͠ ̶̧̛̻͙̤̘̺̣̻̗͍̓͑͠t̠͉̼̬̩͛́ͨ͐̀͛̂ͨ̾͞t̠͉̼̬̩͛́ͨ͐̀͛̂ͨ̾͞h̻̹̝̹̾ͩ̍ͧ͆ͥ̔͘͏̉ͯh̻̹̝̹̾ͩ̍ͧ͆ͥ̔͘͏̉ͯì̷̢̛̺̭͇̟̦̄̓́̓ͪ͟ì̷̢̛̺̭͇̟̦̄̓́̓ͪ͟s̴̡̗͍͕͖̮̟̱̫̎ͣ̀̎̿s̴̡̗͍͕͖̮̟̱̫̎ͣ̀̎̿ ̶͇̟̜̗̗͇͇͐̑̈͋̋̽͟ ̶͇̟̜̗̗͇͇͐̑̈͋̋̽͟e̷̥͙͈̖̤̯̹̯͗̉̈́̽ͨ̕e̷̥͙͈̖̤̯̹̯͗̉̈́̽ͨ̕v̛̝͕̱͙̞̖̒̂̔͆̊̎́ͫv̛̝͕̱͙̞̖̒̂̔͆̊̎́ͫę̴̤̭͖̈̐̿͂ͣ͒̃ͭ̕͟ę̴̤̭͖̈̐̿͂ͣ͒̃ͭ̕͟ṇ̳̭͓̟̠͕̯͑̉ͦ̀ͯ͜͡ṇ̳̭͓̟̠͕̯͑̉ͦ̀ͯ͜͡t̢̞͔̣̄̀͆̂̃ͨͦ͜͜͝͠t̢̞͔̣̄̀͆̂̃ͨͦ͜͜͝͠,̬̳̮͓͉̟͉͓̦͔̑̄ͨ̎͜,̬̳̮͓͉̟͉͓̦͔̑̄ͨ̎͜ ͕͈̠̰̬̬̌͂̏ͥ̀̕̚͢͠ ͕͈̠̰̬̬̌͂̏ͥ̀̕̚͢͠f̺̮̞̈́̏̋͏̺̖̝̓̑̂̚͢f̺̮̞̈́̏̋͏̺̖̝̓̑̂̚͢l̜̪͍̩̩̟͉͓̊̓ͤ̆ͣͫ̌l̜̪͍̩̩̟͉͓̊̓ͤ̆ͣͫ̌u̷̧̞̳̱̜̟̲͑̐͂ͪ͛͘͟u̷̧̞̳̱̜̟̲͑̐͂ͪ͛͘͟s͔͍̽ͣͮ̏̓͛̄ͯ̽͂̐ͯ͜s͔͍̽ͣͮ̏̓͛̄ͯ̽͂̐ͯ͜ḩ̼͕̦͖̼͚̗̃̃ͥ̅̂̈͟ḩ̼͕̦͖̼͚̗̃̃ͥ̅̂̈͟,̵͍̮̮̟͚̝̃ͨ̿ͭ̌ͤ̋̊,̵͍̮̮̟͚̝̃ͨ̿ͭ̌ͤ̋̊ ̷̨̨͈̝̦͂ͦ̒̋̇ͧ̒͟͝ ̷̨̨͈̝̦͂ͦ̒̋̇ͧ̒͟͝a̡̨̲̖̾̂͗̚͢͡͏͈ͤ̉͡a̡̨̲̖̾̂͗̚͢͡͏͈ͤ̉͡nͫͤ̚͜͏̧̛̣̻ͩ̔̍ͦ̕͝nͫͤ̚͜͏̧̛̣̻ͩ̔̍ͦ̕͝d͈̻̗̼̀͡͏̶̵̟̹̻̎̾ͯd͈̻̗̼̀͡͏̶̵̟̹̻̎̾ͯ ̶͙͈̠̜̬̺͛̀̊̂ͪ̔ͮ͑ ̶͙͈̠̜̬̺͛̀̊̂ͪ̔ͮ͑ş̧̡̢͓̠͋ͫ͑͒͊̅̔͜͞ş̧̡̢͓̠͋ͫ͑͒͊̅̔͜͞t̤̭͓̰̣̉̔̎ͫ͛ͦ̑̕͟͞t̤̭͓̰̣̉̔̎ͫ͛ͦ̑̕͟͞a̠͇͇̯̥͍͚̳̒́͐͐̏͋̓a̠͇͇̯̥͍͚̳̒́͐͐̏͋̓r͉͈̻̻͕̩̩̃̅͋͆ͮ͢͢͡r͉͈̻̻͕̩̩̃̅͋͆ͮ͢͢͡t̵̛̝̗̥̙̟̆ͮ̽̏ͧ͜͠ͅt̵̛̝̗̥̙̟̆ͮ̽̏ͧ͜͠ͅ ̷̼͎̦ͫ̈͑̃̽͏̲̪ͣͯ̽ ̷̼͎̦ͫ̈͑̃̽͏̲̪ͣͯ̽á̸̷̴̟̣́̔̌͏̶͕͋ͭͭá̸̷̴̟̣́̔̌͏̶͕͋ͭͭ ̧̧̲͍̘̘͚͔͇͙ͨͬ́̊ͅ ̧̧̲͍̘̘͚͔͇͙ͨͬ́̊ͅn̸̸̩͖͔͚͚̖͗ͦ̓̀̀̈́̈́n̸̸̩͖͔͚͚̖͗ͦ̓̀̀̈́̈́ę̵̧̬̣̦̖̝̹̱͌̃̑ͣ̚ę̵̧̬̣̦̖̝̹̱͌̃̑ͣ̚w͍̥͚̺ͮ̓̈̈́̾̊̆́̚͜͝w͍̥͚̺ͮ̓̈̈́̾̊̆́̚͜͝ ̛̹̲̥̝͙̾ͨ̆̎̃̋͂̓̕ ̛̹̲̥̝͙̾ͨ̆̎̃̋͂̓̕"̴̜̰̰̱̖̙̘̈́͌ͨͪ̐̕͠"̴̜̰̰̱̖̙̘̈́͌ͨͪ̐̕͠g̸̛͇̐͊͂̽͢͏͖̣ͫ͊ͯͅg̸̛͇̐͊͂̽͢͏͖̣ͫ͊ͯͅr̴̨̲͎̹͇̠̐ͤ̇̒ͬ̆ͧ͞r̴̨̲͎̹͇̠̐ͤ̇̒ͬ̆ͧ͞į̝̱̩͔̈ͨ̉͌̋̍̂͜͟͞į̝̱̩͔̈ͨ̉͌̋̍̂͜͟͞d̷̴̷̟̎͌͑͛̈́ͭͨͯ̋ͭ̕d̷̴̷̟̎͌͑͛̈́ͭͨͯ̋ͭ̕_̢̭̙̦̪͇̾̔̆ͬͦ́ͥ͢͡_̢̭̙̦̪͇̾̔̆ͬͦ́ͥ͢͡l̡̢̨̧͔̱̥̹̬̆ͮ̈́̏̅͜l̡̢̨̧͔̱̥̹̬̆ͮ̈́̏̅͜ĩ̢͖̠̩̞͚̰̰̉̋̌͛ͪ͠ĩ̢͖̠̩̞͚̰̰̉̋̌͛ͪ͠n̢̬̜̘̲͉ͮ͆͏̯͕ͭͦ̉̅n̢̬̜̘̲͉ͮ͆͏̯͕ͭͦ̉̅ē̡͈̮̭̰̜͍̗̮͔͌͐͆ͫē̡͈̮̭̰̜͍̗̮͔͌͐͆ͫ"̦̠̟ͣ̽͋͐ͧ̓̂̆̎͒͝ͅ"̦̠̟ͣ̽͋͐ͧ̓̂̆̎͒͝ͅ ̴̡̺̹̖̰̀ͤ͊̊͗̊́͜͠ ̴̡̺̹̖̰̀ͤ͊̊͗̊́͜͠ẻ͎̳̻̯̹͓͊̌̄͑͂̉͜͢ẻ͎̳̻̯̹͓͊̌̄͑͂̉͜͢v̞̬̪̥͖ͤ͐̍́ͤ̇̀̕̕ͅv̞̬̪̥͖ͤ͐̍́ͤ̇̀̕̕ͅȇ̶̱̗̩̠͚͎͊ͤͪͦͫ̂̚ȇ̶̱̗̩̠͚͎͊ͤͪͦͫ̂̚n̢̮̜͉̎ͨͩ̒̓ͬͨ̓ͦ͘͜n̢̮̜͉̎ͨͩ̒̓ͬͨ̓ͦ͘͜t̢͚͉̹͇̺̭ͪ̄̉ͨ̄͐̕ͅt̢͚͉̹͇̺̭ͪ̄̉ͨ̄͐̕ͅ ̵̛̳̱͍̩͓ͣ͋̈́͐ͭͧ̿ͅ ̵̛̳̱͍̩͓ͣ͋̈́͐ͭͧ̿ͅã̰̪̙͉̪͇ͣ͋ͤ̓͋͑̕͘ã̰̪̙͉̪͇ͣ͋ͤ̓͋͑̕͘t̴̴̡̡̛̳̼̻̳̂̽͒̇̚͠t̴̴̡̡̛̳̼̻̳̂̽͒̇̚͠ ̴̙̤̹͔̳̟̽̀̆ͥ̂̕͘͝ ̴̙̤̹͔̳̟̽̀̆ͥ̂̕͘͝t̴͖̼͔̬̦̏ͩ̄́̓̊̔̇͡t̴͖̼͔̬̦̏ͩ̄́̓̊̔̇͡ḫ̷͚͇̫̫̠͒ͮ̍ͣ̃͐͐̍ḫ̷͚͇̫̫̠͒ͮ̍ͣ̃͐͐̍e̳͉̮͖̳̣̎͌̂ͣ͋ͯ̆͜͞e̳͉̮͖̳̣̎͌̂ͣ͋ͯ̆͜͞ ̪̖̉̌̀̽̄̍̓̀̂̋͐̈̚ ̪̖̉̌̀̽̄̍̓̀̂̋͐̈̚c̶̫̜͚̊̿̂̿ͥͭ̓̂̈́͘͡c̶̫̜͚̊̿̂̿ͥͭ̓̂̈́͘͡ṷ̵̷͉̯̜̬̝̑͛ͤ̋ͧͯ̉ṷ̵̷͉̯̜̬̝̑͛ͤ̋ͧͯ̉ŗ̙̬̺̬ͥͤ̓͐̈́ͬ̽̌͡͡ŗ̙̬̺̬ͥͤ̓͐̈́ͬ̽̌͡͡r̛͖̼̣͙̋̇̀̅͒̽ͥ̑ͅͅr̛͖̼̣͙̋̇̀̅͒̽ͥ̑ͅͅě̢̪̦̼͘͏̴̠̞͍̓̐ͭ̇ě̢̪̦̼͘͏̴̠̞͍̓̐ͭ̇ǹ̨̨̛̛̯̍ͭͫ̎̍̃̄̐̍ǹ̨̨̛̛̯̍ͭͫ̎̍̃̄̐̍t̵̳͔̭̮̭̱̰ͤ͐̉̾͗̅͢t̵̳͔̭̮̭̱̰ͤ͐̉̾͗̅͢ ̩͕ͧ̌͂͂́ͫͥ̍͏̬̙͗ͅ ̩͕ͧ̌͂͂́ͫͥ̍͏̬̙͗ͅp̞̞͇͇̯̩̬̜ͥ̃͐̑͋́͗p̞̞͇͇̯̩̬̜ͥ̃͐̑͋́͗o̵͖͈̩̪̥̝̊̒̉̿͋ͩ̆͠o̵͖͈̩̪̥̝̊̒̉̿͋ͩ̆͠s̶͇̺̩̟̺͋̆̒ͫͥ̆͏͔͜s̶͇̺̩̟̺͋̆̒ͫͥ̆͏͔͜i̞̝̭̲͈̝̮̫͚͑̓ͤ̎ͮ̀i̞̝̭̲͈̝̮̫͚͑̓ͤ̎ͮ̀t̷̬͇̗̥̳͔̮͔̎̀ͣ̊̕͢t̷̬͇̗̥̳͔̮͔̎̀ͣ̊̕͢i̶̧̗̠̗̲͑̋ͣͪͦͣ͆̕͢i̶̧̗̠̗̲͑̋ͣͪͦͣ͆̕͢ǫ̰̟̙ͯ͏̬̦͚̝̀̈́ͣ͘͜ǫ̰̟̙ͯ͏̬̦͚̝̀̈́ͣ͘͜n̸̻̩͖͓̠̲̮̐̄̌̇́́͠n̸̻̩͖͓̠̲̮̐̄̌̇́́͠}| + {100: }{101:.̧̛͈̙̭̉ͥ̋͛̏͋̂̿͛͞.̧̛͈̙̭̉ͥ̋͛̏͋̂̿͛͞ ̡̲͇̳͔̦̤̹̥ͣ̍ͪͮ̎͡ ̡̲͇̳͔̦̤̹̥ͣ̍ͪͮ̎͡F̼̣͙̳̞͑͒ͧͣ̔̊̇̓̈́͠F̼̣͙̳̞͑͒ͧͣ̔̊̇̓̈́͠ŏ̸̶͎̘̟͙̤̑̒̿͗͛̐̚ŏ̸̶͎̘̟͙̤̑̒̿͗͛̐̚r̵̰͇͚̥̻͚̈́̌̽ͨͥ̕͘͞r̵̰͇͚̥̻͚̈́̌̽ͨͥ̕͘͞ ̵̶̧͖̙̝̻̭̤̒̿͌͗͑͡ ̵̶̧͖̙̝̻̭̤̒̿͌͗͑͡ś̸̬̘̭̼͓̹ͥ͛ͪ̐̈́͆͝ś̸̬̘̭̼͓̹ͥ͛ͪ̐̈́͆͝i͎̺̪̖̻ͫ̀ͤ̾ͦ̽ͭ͒̒͘i͎̺̪̖̻ͫ̀ͤ̾ͦ̽ͭ͒̒͘m̨̩͎̫̫̙̃̈͆ͬ̊͠͏̽͞m̨̩͎̫̫̙̃̈͆ͬ̊͠͏̽͞p̴̢̨͖̼͍̲͍̲ͩ̊̒̌̃ͤp̴̢̨͖̼͍̲͍̲ͩ̊̒̌̃ͤļ͔̱͙̝̟̜͚͎͕̮̹ͣ̀͘ļ͔̱͙̝̟̜͚͎͕̮̹ͣ̀͘ḭ̢̛͈͍̠̟̪̑̎̈́̑̽͜͡ḭ̢̛͈͍̠̟̪̑̎̈́̑̽͜͡c̖̠̥͔̪̼̑̃̆̓ͫ͗ͩͩ̋c̖̠̥͔̪̼̑̃̆̓ͫ͗ͩͩ̋i̴̸̡̘͇͔̹͂̍̐̀ͬͩ̈͘i̴̸̡̘͇͔̹͂̍̐̀ͬͩ̈͘t̵̵̖̫͙͎̒ͭ̔̃̔ͧͫ̽͝t̵̵̖̫͙͎̒ͭ̔̃̔ͧͫ̽͝y̴̘̱͈̳ͯ̈́̍ͮ̔͊͊̚̚͞y̴̘̱͈̳ͯ̈́̍ͮ̔͊͊̚̚͞ ̛̩̤̪͇̬͕̯̞̙̪̮̀̂̕ ̛̩̤̪͇̬͕̯̞̙̪̮̀̂̕ļ̲̰̞̈̌̿͐̉́̋ͬ͟͟͡ļ̲̰̞̈̌̿͐̉́̋ͬ͟͟͡e̡̧̛̬̣̗̣̰͂̐̂͗͛̋̇e̡̧̛̬̣̗̣̰͂̐̂͗͛̋̇a̡̯͉̠̞ͩͮ̉̓́ͮ̔̆̒͘a̡̯͉̠̞ͩͮ̉̓́ͮ̔̆̒͘v͔ͦ͏͎̪̮̝̟̤́̀͊̈̎͟v͔ͦ͏͎̪̮̝̟̤́̀͊̈̎͟ȩ̪͔̤̺͗ͦ̅̓ͭͤͫ̆ͣ͒ȩ̪͔̤̺͗ͦ̅̓ͭͤͫ̆ͣ͒ ̶̢̤͎̰̝̤͂̔͒ͦͦ͂̊̀ ̶̢̤͎̰̝̤͂̔͒ͦͦ͂̊̀p̛̝̪͚͖̫͕̅̍̊́̓͒̂̃p̛̝̪͚͖̫͕̅̍̊́̓͒̂̃l̴̸͉͎̖͕̦̥̾ͨ̌̑ͣ̕͝l̴̸͉͎̖͕̦̥̾ͨ̌̑ͣ̕͝a̰̩͔̼͔̦̩͒ͪͭ̐͡͏̗ͮa̰̩͔̼͔̦̩͒ͪͭ̐͡͏̗ͮč̵̱͇̲̞̉̆̌̄ͧͫ̑̈́͠č̵̱͇̲̞̉̆̌̄ͧͫ̑̈́͠e̶̛̜̬̯̤͔̓ͤͮͦ̂͐͜͜e̶̛̜̬̯̤͔̓ͤͮͦ̂͐͜͜ ̸̧̼̯͕̼̭ͣͣ̿̑̈̎̽͜ ̸̧̼̯͕̼̭ͣͣ̿̑̈̎̽͜f̴̼̞͇̺̊̎̓̑̔̈́͗̊͘͞f̴̼̞͇̺̊̎̓̑̔̈́͗̊͘͞ő̶̶̧̺̤̜̹̫͎̻̏̉̍ͩő̶̶̧̺̤̜̹̫͎̻̏̉̍ͩr̵̙̱̺̮͙̯̼͐̀͗̾͊͟͝r̵̙̱̺̮͙̯̼͐̀͗̾͊͟͝ ̵̪̥͍̩̱ͨ͗̓̎̓͒̚͜͞ ̵̪̥͍̩̱ͨ͗̓̎̓͒̚͜͞t̪̤͙̥̖̹̣̤͎̞̒́̒̽̃t̪̤͙̥̖̹̣̤͎̞̒́̒̽̃ḥ̵̷̷̭̘͙͓ͩ̓ͪͤ̓ͮ̍ḥ̵̷̷̭̘͙͓ͩ̓ͪͤ̓ͮ̍é̡̬̺͉̻̻̀̀͂̽ͬ̕̕͠é̡̬̺͉̻̻̀̀͂̽ͬ̕̕͠ ̵̱̻̣͙̌͊́ͯ́̐̀́͂̕ ̵̱̻̣͙̌͊́ͯ́̐̀́͂̕f̨̪͈̲̬̟̝͎̀̔̋ͨ̀͡ͅf̨̪͈̲̬̟̝͎̀̔̋ͨ̀͡ͅi̵̫̱̝͒̇̏̃ͭ̂̄̄̊̐͠i̵̫̱̝͒̇̏̃ͭ̂̄̄̊̐͠n̵̷͖̻̦͍̻̑̈́̎̓͑ͪͫ͟n̵̷͖̻̦͍̻̑̈́̎̓͑ͪͫ͟a̗̗̠̫̦̻̹͇ͯͦͫ͗̽ͥ̚a̗̗̠̫̦̻̹͇ͯͦͫ͗̽ͥ̚l̵̨̨͙̜͔̘̗̯͌̋͂̑̄͢l̵̨̨͙̜͔̘̗̯͌̋͂̑̄͢ ̵̷̛̺̳͙̲̥̬̊̌̽̓̇͝ ̵̷̛̺̳͙̲̥̬̊̌̽̓̇͝"̸̨̫̟̤̥͉̮̥̀ͤ̐͊̇̑"̸̨̫̟̤̥͉̮̥̀ͤ̐͊̇̑c̵̭̘͍͇̜ͨ̔̍̆͛͌͂͌͛c̵̭̘͍͇̜ͨ̔̍̆͛͌͂͌͛l̸̷̢̡̫̩̃́͐̆͒ͨ̔̄͟l̸̷̢̡̫̩̃́͐̆͒ͨ̔̄͟è̙͎̝̞̠̹͍́́̅͐͌͘ͅè̙͎̝̞̠̹͍́́̅͐͌͘ͅą̶̢̡̘̦͖̰̌̎͌ͩ́̓ͮą̶̢̡̘̦͖̰̌̎͌ͩ́̓ͮȑ̹̻̦͙̠͂̽͆ͫ͛͟͏̺͡ȑ̹̻̦͙̠͂̽͆ͫ͛͟͏̺͡"̣̞͕̇ͯ̓̽ͪ͑́̍̚͘͘ͅ"̣̞͕̇ͯ̓̽ͪ͑́̍̚͘͘ͅ ̛̩̘̗̜̗̥̂͊ͥ̀ͨͫ̾ͧ ̛̩̘̗̜̗̥̂͊ͥ̀ͨͫ̾ͧe̵̡͚͉̤̯̮͛̎̓ͪ̾̉̆͟e̵̡͚͉̤̯̮͛̎̓ͪ̾̉̆͟l̢̩͓̙͓͍̈̊ͫͣ́̅ͧ͛͞l̢̩͓̙͓͍̈̊ͫͣ́̅ͧ͛͞e̠̱͉̯͔̙͔̓ͩ̃͋͌ͬͭ͋e̠̱͉̯͔̙͔̓ͩ̃͋͌ͬͭ͋m̧̡̛̤͕̻̳̽͛̓̈́ͣ̊̚͟m̧̡̛̤͕̻̳̽͛̓̈́ͣ̊̚͟e̯͎̺͔̼̾͂͐̄ͮ͌̍͑̕ͅe̯͎̺͔̼̾͂͐̄ͮ͌̍͑̕ͅṉ̸͔̩̙̙̹͚̭ͩ͛͗̀̾ͅṉ̸͔̩̙̙̹͚̭ͩ͛͗̀̾ͅt̸̢̧̝͉̺͉̓́̇ͯ̕͠͠͝t̸̢̧̝͉̺͉̓́̇ͯ̕͠͠͝ ̳͔̜̹̘̪̅̋̎̉͆̑ͤ͡͞ ̳͔̜̹̘̪̅̋̎̉͆̑ͤ͡͞a̷̳͓̞̱͈̓́̒̔̆ͩͯ̊͠a̷̳͓̞̱͈̓́̒̔̆ͩͯ̊͠s̷̛͙̘̳͙͓̠̞̫̆̓̚̚͡s̷̛͙̘̳͙͓̠̞̫̆̓̚̚͡ ̷̢̦̘̮̰͕̏̀ͩ̓͂ͦ͟ͅ ̷̢̦̘̮̰͕̏̀ͩ̓͂ͦ͟ͅw̮͔͇͕̝͖ͪ͒̏̇ͪ̇̍ͦ͠w̮͔͇͕̝͖ͪ͒̏̇ͪ̇̍ͦ͠ẹ̗͚̮̭̓̍̐̃̍́̐͘̕͘ẹ̗͚̮̭̓̍̐̃̍́̐͘̕͘l̴̡̩̱̞͛ͤ͑͑͂̀ͣ̉̌̕l̴̡̩̱̞͛ͤ͑͑͂̀ͣ̉̌̕l̷̨̘̤̣̯͇̲̝ͨ̍ͦ͝͝ͅl̷̨̘̤̣̯͇̲̝ͨ̍ͦ͝͝ͅ,̷̻̙̖͔͚͋̇̂̑͗̕̕͢ͅ,̷̻̙̖͔͚͋̇̂̑͗̕̕͢ͅ ̴̶̮̣̪̣̟͚̅͊ͧͭ̂͂̓ ̴̶̮̣̪̣̟͚̅͊ͧͭ̂͂̓h̡͈̗̙͚̳͕̤͍̿̄͑ͥ͊̉h̡͈̗̙͚̳͕̤͍̿̄͑ͥ͊̉e̡̞͔̘͐̋͌̅̓̈͆̇̿͟͠e̡̞͔̘͐̋͌̅̓̈͆̇̿͟͠n̤͕͉̣̐͊́̽̍́͂͏̙̾ͅn̤͕͉̣̐͊́̽̍́͂͏̙̾ͅc̨̧̛̜͈͍͓̣̹ͮͧ̊͟͡ͅc̨̧̛̜͈͍͓̣̹ͮͧ̊͟͡ͅȩ̢̛͚̣͓͙ͣͮͩ̌̌́͟͠ȩ̢̛͚̣͓͙ͣͮͩ̌̌́͟͠ ̷̜̀ͪ̅͐͝͏̤̮̄͑̾ͬ͝ ̷̜̀ͪ̅͐͝͏̤̮̄͑̾ͬ͝t̙͔̳͕̝̝͔͐̊̓ͩ̈́ͪ̒͠t̙͔̳͕̝̝͔͐̊̓ͩ̈́ͪ̒͠h̩̖̪̠͂ͪ́͐̿͊ͨ̈̋̃͟h̩̖̪̠͂ͪ́͐̿͊ͨ̈̋̃͟e̋͆ͬ͞͏̵̘̹̙̂ͥ̐̀́ͅe̋͆ͬ͞͏̵̘̹̙̂ͥ̐̀́ͅ ̧̱͌̆̏͘͏̬̠̹̏ͦ̓͋̕ ̧̱͌̆̏͘͏̬̠̹̏ͦ̓͋̕f̸̧͎̣̪̯̻̗̟̎̎͂ͫ̕͝f̸̧͎̣̪̯̻̗̟̎̎͂ͫ̕͝a̟̓̃͒͏̸̧̣̱̎̽̏̐̓ͤa̟̓̃͒͏̸̧̣̱̎̽̏̐̓ͤć̯͚͕͡͏̴̛̜̺̣͙͉̀̍ć̯͚͕͡͏̴̛̜̺̣͙͉̀̍ț̢̹͇͈ͩ̋́̔̇̉ͤ̽͢ͅț̢̹͇͈ͩ̋́̔̇̉ͤ̽͢ͅo͈̥͊ͦ̔̃ͬ̎͛ͧ͌͌͟͟͝o͈̥͊ͦ̔̃ͬ̎͛ͧ͌͌͟͟͝r̸̨͕̙̟̟͈̼͔ͥͮ͋ͫ͒͝r̸̨͕̙̟̟͈̼͔ͥͮ͋ͫ͒͝ ̧̗̝̘͇͉̩̗̅̊̓͂̐͊͡ ̧̗̝̘͇͉̩̗̅̊̓͂̐͊͡o̬̻̪̹̘̥̳̲̫̳̫̳ͬͩ͊o̬̻̪̹̘̥̳̲̫̳̫̳ͬͩ͊f̡̣̜̗͚̙͇̓͑̍ͬ̅̚͞͞f̡̣̜̗͚̙͇̓͑̍ͬ̅̚͞͞ ̵̸͓̱̻̱̌̃ͫ̑̿͐͂ͨ̇ ̵̸͓̱̻̱̌̃ͫ̑̿͐͂ͨ̇2̡̝̥̯͚͔̖̫ͫ̽̔͑̅̚͞2̡̝̥̯͚͔̖̫ͫ̽̔͑̅̚͞ ̨̠̰̯̤͕̗̗̳͆̑͐͌̕͟ ̨̠̰̯̤͕̗̗̳͆̑͐͌̕͟i͇̫̲̲͓̖͙͖̱ͤ͊̎̃ͧ͢i͇̫̲̲͓̖͙͖̱ͤ͊̎̃ͧ͢n̵̢̻̦̭̏̓̂́͏̲̪̙̏͌n̵̢̻̦̭̏̓̂́͏̲̪̙̏͌ ̸̵̛̹͇͚̓̋̎̏̽̚̚͢͜ ̸̵̛̹͇͚̓̋̎̏̽̚̚͢͜t͈͔̤̲̬ͧͩ̔̀̂́͑̂ͭ͘t͈͔̤̲̬ͧͩ̔̀̂́͑̂ͭ͘h̡̻̙̖̪̱̍͗̍́͗́́̅͠h̡̻̙̖̪̱̍͗̍́͗́́̅͠e̶̖̩̜͐ͥͨͪͣ̆͋̋̉͢͡e̶̖̩̜͐ͥͨͪͣ̆͋̋̉͢͡ ̧͈̙̤͉͌ͩ̓͐̌̄ͦ͌ͥ͝ ̧͈̙̤͉͌ͩ̓͐̌̄ͦ͌ͥ͝c̨̨̹̗̬͕̩̈̑̉̃̑́̆͞c̨̨̹̗̬͕̩̈̑̉̃̑́̆͞h̭͚̦̻̘͈͆ͪ̿̌́̏̐͊͠h̭͚̦̻̘͈͆ͪ̿̌́̏̐͊͠ę̙͍͚ͮͦ́ͭͥ̈͑ͧ̕̕͢ę̙͍͚ͮͦ́ͭͥ̈͑ͧ̕̕͢c̝̭͓̹̙̠̄̍ͦ̌̏̉̇͛ͥc̝̭͓̹̙̠̄̍ͦ̌̏̉̇͛ͥk̶̡͚̦̰̣͖̔͌́̋͋̔ͥ̕k̶̡͚̦̰̣͖̔͌́̋͋̔ͥ̕.̛̱̖͓̼͚̲ͪ̆̈́̃̚͜͞ͅ.̛̱̖͓̼͚̲ͪ̆̈́̃̚͜͞ͅ ̶̴̺̹̜̺͇ͮ̉ͯ͋͗͝͝ͅ ̶̴̺̹̜̺͇ͮ̉ͯ͋͗͝͝ͅA͉̺̰̲̟̺͚͙̽́̀̌ͬͩ͠A͉̺̰̲̟̺͚͙̽́̀̌ͬͩ͠l̜͈̟͖̾͑̈́́̇ͮ̐ͦͮ͋̋l̜͈̟͖̾͑̈́́̇ͮ̐ͦͮ͋̋s̡̛̯̜̩̪̤̹̅͛̋̓͂̊͡s̡̛̯̜̩̪̤̹̅͛̋̓͂̊͡o̶͉̱͉̠̫̻ͤͣ̓ͭ͊͑͆ͅo̶͉̱͉̠̫̻ͤͣ̓ͭ͊͑͆ͅ ̢̗͉̝̞͗͛̊́ͦ͛̍̚͜͡ ̢̗͉̝̞͗͛̊́ͦ͛̍̚͜͡i̵̧͍̝̦̬̭̽̎̈̃̓̚͟͝i̵̧͍̝̦̬̭̽̎̈̃̓̚͟͝f̴̛̻̞̬̟͖̙̦̑́̀̍̇͝f̴̛̻̞̬̟͖̙̦̑́̀̍̇͝ ͓͚͇̺ͪ͛ͦͥ̓̎ͨ͒͊̚͟ ͓͚͇̺ͪ͛ͦͥ̓̎ͨ͒͊̚͟t̶̯͙̹̟͖̂̇͒̾ͭ͒͐͛̌t̶̯͙̹̟͖̂̇͒̾ͭ͒͐͛̌ḧ̠̬́͋̕͏̧̙̼͑͂̀̌̉̈ḧ̠̬́͋̕͏̧̙̼͑͂̀̌̉̈e̫̺͔̗̳͋̒͌ͤͬ̔͗̕̕͡e̫̺͔̗̳͋̒͌ͤͬ̔͗̕̕͡r̴̛̭͙͑́ͤ̓̒͊̈ͥ̑ͮ͞r̴̛̭͙͑́ͤ̓̒͊̈ͥ̑ͮ͞ȇ̷̛̝͓̜̮̩͙ͨ̎̎͛̌̽ȇ̷̛̝͓̜̮̩͙ͨ̎̎͛̌̽ ̸̩̤̘̖̳̻̋̈͛͑̈́̌̒͝ ̸̩̤̘̖̳̻̋̈͛͑̈́̌̒͝i̧͖͕̞̩̱̭̎̽̀͏̮ͬͨ͡i̧͖͕̞̩̱̭̎̽̀͏̮ͬͨ͡ś̶͍̞̉ͯ̊ͦͧ̀̆̌͂͞͝ś̶͍̞̉ͯ̊ͦͧ̀̆̌͂͞͝ ̡̛͙͈̜̗̻̮̞͍̝̝̓̅͋ ̡̛͙͈̜̗̻̮̞͍̝̝̓̅͋ȃ̢͍̟̬̳ͣ͏̝̔͑̎ͣ̕ͅȃ̢͍̟̬̳ͣ͏̝̔͑̎ͣ̕ͅ ̖̼͙̯͎̒͒ͩͣ̀̆͗̇͝ͅ ̖̼͙̯͎̒͒ͩͣ̀̆͗̇͝ͅ}| + {100: }{101:ļ̩̪͕͓͖͌̐͛̊ͫͪ̿͊ͤļ̩̪͕͓͖͌̐͛̊ͫͪ̿͊ͤȍ͍̜͓̣͈͍̳̄͊̄ͣ͢͝ͅȍ͍̜͓̣͈͍̳̄͊̄ͣ͢͝ͅt̵̷̛̤͎͕̖̹ͦ̇͒́͂ͮ͘t̵̷̛̤͎͕̖̹ͦ̇͒́͂ͮ͘ ̶̨̳̺̖̼̯͎̯̬̀̈̐ͦ͜ ̶̨̳̺̖̼̯͎̯̬̀̈̐ͦ͜o̷̡̯̼͇̻͙̙̙͒͂ͩ̉̀ͮo̷̡̯̼͇̻͙̙̙͒͂ͩ̉̀ͮf̻̺̖͉̊ͤ̇ͤ̍͌͛̐͟͟͝f̻̺̖͉̊ͤ̇ͤ̍͌͛̐͟͟͝ ̷̴̟̠̹͎̱̈́̈́̆̈́̇̃͘͞ ̷̴̟̠̹͎̱̈́̈́̆̈́̇̃͘͞p̸̸̧̨̦̪͕̮̀͒͒͌͌̑͢p̸̸̧̨̦̪͕̮̀͒͒͌͌̑͢a̯͙ͥͬ̊̓ͯ͌ͮ͊̎̂͒͢͠a̯͙ͥͬ̊̓ͯ͌ͮ͊̎̂͒͢͠ċ̛̫̱̥͕̳̲ͫͤ̎͐̄̕͠ċ̛̫̱̥͕̳̲ͫͤ̎͐̄̕͠k̵̡̹̲͓̾̉͏̶̻́ͩ̎̓̊k̵̡̹̲͓̾̉͏̶̻́ͩ̎̓̊e̵̡̢̢̪͍̲̣͒̒̍ͯͤ͊͑e̵̡̢̢̪͍̲̣͒̒̍ͯͤ͊͑ḑ̘͖̼̌ͭ̐͑ͯ͋ͬ̈́͆̓̚ḑ̘͖̼̌ͭ̐͑ͯ͋ͬ̈́͆̓̚ ̶̶̧̺̰͖̯͇̏̄̿ͤ̔͘͡ ̶̶̧̺̰͖̯͇̏̄̿ͤ̔͘͡c̩̝̯͂͆ͤ̈́͆̏̑̄ͦ͗̿͞c̩̝̯͂͆ͤ̈́͆̏̑̄ͦ͗̿͞e̥̫͌̓̚͏̵̧͙̣̻̞́̌̉e̥̫͌̓̚͏̵̧͙̣̻̞́̌̉l̵̪̣̪̘̙̟̘͊͒̿̏͗͝͠l̵̪̣̪̘̙̟̘͊͒̿̏͗͝͠l̨̮͚̟̺̹ͮ͊ͭ̊ͮ̿͘͘͟l̨̮͚̟̺̹ͮ͊ͭ̊ͮ̿͘͘͟s̩̯͌̊͒͝͏̥͑̈̎ͥ̀͟͞s̩̯͌̊͒͝͏̥͑̈̎ͥ̀͟͞,̡̙̻̭̪̭̖̀̇̒ͮ̈̒̇̕,̡̙̻̭̪̭̖̀̇̒ͮ̈̒̇̕ ̷̢̫̳̺͉̯̳̂̓ͨ͋͛͂͞ ̷̢̫̳̺͉̯̳̂̓ͨ͋͛͂͞p̟̰͖̹̦̲͙̉̑͐͑͗̀͟͢p̟̰͖̹̦̲͙̉̑͐͑͗̀͟͢á̳̜̈̓͛͠͏̢̄ͧ̉ͧͨͅá̳̜̈̓͛͠͏̢̄ͧ̉ͧͨͅs̵̢̥̖̰̥̼̯ͪͭ̿͗͌ͪ͊s̵̢̥̖̰̥̼̯ͪͭ̿͗͌ͪ͊s̘̱͕̓̅͛̆̅ͮ́̅ͧͣ̈̚s̘̱͕̓̅͛̆̅ͮ́̅ͧͣ̈̚ ̡̮̹̹̗͎̝ͣ̇́ͤ̾ͤ͞ͅ ̡̮̹̹̗͎̝ͣ̇́ͤ̾ͤ͞ͅt̡̛̝̯̱̖̠̱ͤ͒͆̍̚͘ͅt̡̛̝̯̱̖̠̱ͤ͒͆̍̚͘ͅh̛̯̻̩̘̤̳̿̓͂̐͐͡͡͠h̛̯̻̩̘̤̳̿̓͂̐͐͡͡͠ë́͏̩̣̗̺̟ͭ̃̾͂̀͘͢͝ë́͏̩̣̗̺̟ͭ̃̾͂̀͘͢͝m̸̜͔̮̥̹̲̖̲̄̈̊̎ͬ͠m̸̜͔̮̥̹̲̖̲̄̈̊̎ͬ͠ ̝͇̏͏̡̟̥͍ͦ̋̎ͤ̕͢͜ ̝͇̏͏̡̟̥͍ͦ̋̎ͤ̕͢͜ó͖̬̩̤̜̫͔͑̌ͫ̈́͡͏̇ó͖̬̩̤̜̫͔͑̌ͫ̈́͡͏̇f̴̶̴̥͔̗͖̬̆͒ͨ͊ͬͅͅf̴̶̴̥͔̗͖̬̆͒ͨ͊ͬͅͅ ̮̦̲̼̞̑ͨ͒̌ͤ̿́̌͐͒ ̮̦̲̼̞̑ͨ͒̌ͤ̿́̌͐͒t̛̮̰͔̋͂͗̓͋̇̅̆̒͊ͅt̛̮̰͔̋͂͗̓͋̇̅̆̒͊ͅo̴̢͚̮̳͙̟̳̐ͬ͑̃̓͡ͅo̴̢͚̮̳͙̟̳̐ͬ͑̃̓͡ͅ ̝̺͖͒ͯͭ̑ͤ͊ͮ͂̚̚͡ͅ ̝̺͖͒ͯͭ̑ͤ͊ͮ͂̚̚͡ͅt̶̨̲̦͉͖̲̤̗ͬ̋̋ͦ̂ͯt̶̨̲̦͉͖̲̤̗ͬ̋̋ͦ̂ͯh̶̨̤͍̜̯͈͇͛ͩ́̀͊ͮͩh̶̨̤͍̜̯͈͇͛ͩ́̀͊ͮͩe̡̢̤̰̰͙̭͈̓ͯͬ͑ͨ̃͢e̡̢̤̰̰͙̭͈̓ͯͬ͑ͨ̃͢ ̶̛̺̦̯̹̞͎̟̥̞̝̐̕ͅ ̶̛̺̦̯̹̞͎̟̥̞̝̐̕ͅU͙̓ͤ͏̟̗̟͕̥̰́͑̆ͧ͟U͙̓ͤ͏̟̗̟͕̥̰́͑̆ͧ͟I̪̩͕͕̼̎͐ͭͦ͐̋̐̅ͯ͢I̪̩͕͕̼̎͐ͭͦ͐̋̐̅ͯ͢ ̢̦͍̰̬͚ͯ́ͫ̿ͨ͘͘͝͡ ̢̦͍̰̬͚ͯ́ͫ̿ͨ͘͘͝͡t̴̨͉̟̥͛͐̇̀̎̒̄ͧ̀̇t̴̨͉̟̥͛͐̇̀̎̒̄ͧ̀̇o̸͇͏̷͍͉̠ͤ̈̾ͤ͛ͥ̚͝o̸͇͏̷͍͉̠ͤ̈̾ͤ͛ͥ̚͝ ̸̣͕̝̯̼́ͨͯ̏̋̂̑́͘ ̸̣͕̝̯̼́ͨͯ̏̋̂̑́͘l̵̨̛̬̱̣̬͚̺̫̳ͨ̽ͫͯl̵̨̛̬̱̣̬͚̺̫̳ͨ̽ͫͯḙ̳͕̝̥̄̆ͮ̄ͦͨ̓̚͏͢ḙ̳͕̝̥̄̆ͮ̄ͦͨ̓̚͏͢t̵̼̲̣̉̀̌ͭ̒̓͌̀͒̀ͅt̵̼̲̣̉̀̌ͭ̒̓͌̀͒̀ͅ ̺͈͙̘̣̩̙͕̋̇͆̀͊̇̂ ̺͈͙̘̣̩̙͕̋̇͆̀͊̇̂i̧̭̲̼̗̥̪̣̭ͬ͑ͨ̓̈̐i̧̭̲̼̗̥̪̣̭ͬ͑ͨ̓̈̐ṱ̴̯̲̱̫̲̥ͭ̀͋̂̚̕͟ṱ̴̯̲̱̫̲̥ͭ̀͋̂̚̕͟ ̖̗͔̦̲̻̌̀̏͆͊ͤ̑ͨ̉ ̖̗͔̦̲̻̌̀̏͆͊ͤ̑ͨ̉s̨̢͔̄̃ͯ̈́̑͐̚̕͢͝͝s̨̢͔̄̃ͯ̈́̑͐̚̕͢͝͝t͚͉͇̳̘̅̇̋͌̀̋́ͨ́͏t͚͉͇̳̘̅̇̋͌̀̋́ͨ́͏â͇͎̺ͨͭ͡͏͚̯ͤͩ͋ͧ͘â͇͎̺ͨͭ͡͏͚̯ͤͩ͋ͧ͘r̷̘̜͍̫̯ͭͪ͑͗̓͆͟͝ͅr̷̘̜͍̫̯ͭͪ͑͗̓͆͟͝ͅṭ̷̢̱͕̎ͬ̿́́͊̐͟͢͜ṭ̷̢̱͕̎ͬ̿́́͊̐͟͢͜ ̟̥̖͕̭͍̲ͩ̆̒̑͊̕̚͡ ̟̥̖͕̭͍̲ͩ̆̒̑͊̕̚͡p̶̴̨̧̜̰̜̘̉̽ͤ́͘͢͜p̶̴̨̧̜̰̜̘̉̽ͤ́͘͢͜ṙ̠̘̟̭͖̜̜͍ͦ̾͒͜͠͞ṙ̠̘̟̭͖̜̜͍ͦ̾͒͜͠͞ǒ̷̶̫͈̫̹ͬ͂ͧ̿̿̑͂͂ǒ̷̶̫͈̫̹ͬ͂ͧ̿̿̑͂͂c̵̸̮͕̮͈̞̼͎͉͍̑́ͭ̀c̵̸̮͕̮͈̞̼͎͉͍̑́ͭ̀e̸̡͔͓̳̞̟̘ͩ̍̽ͪͤͣ͢e̸̡͔͓̳̞̟̘ͩ̍̽ͪͤͣ͢s̨͍̬̖͂̏ͦ̀ͫ̐ͨͪ͡ͅͅs̨͍̬̖͂̏ͦ̀ͫ̐ͨͪ͡ͅͅs̡̻̹̜͍̺͌͛ͮ̓̀̈́̉̿͑s̡̻̹̜͍̺͌͛ͮ̓̀̈́̉̿͑í̤̜ͭ͢͏̶̶̹͍ͤͪ̉ͫ̋í̤̜ͭ͢͏̶̶̹͍ͤͪ̉ͫ̋ņ̵̼̪̭̲̤͙͖̒̑ͨ͂̌͘ņ̵̼̪̭̲̤͙͖̒̑ͨ͂̌͘g̷̢̢̭̘̙̥̖̲̭͛ͣ́̀̍g̷̢̢̭̘̙̥̖̲̭͛ͣ́̀̍ ̷̺̱̙̹͔̗͆ͪ̾ͬͬ̓͘͠ ̷̺̱̙̹͔̗͆ͪ̾ͬͬ̓͘͠t̛̤̻̲̓̓ͩ̇ͫͫ͆ͦ͛̕͞t̛̤̻̲̓̓ͩ̇ͫͫ͆ͦ͛̕͞ḣ̜̘̮̫͍̗͇ͥͯ̽̕͢͢͝ḣ̜̘̮̫͍̗͇ͥͯ̽̕͢͢͝ẹ̵̱̹͚͖̜̞̆̾͌ͬ̀̿͞ẹ̵̱̹͚͖̜̞̆̾͌ͬ̀̿͞m̶̢̧͉̝̜̝̍͑͂̽̌̉̈́ͩm̶̢̧͉̝̜̝̍͑͂̽̌̉̈́ͩ }| + {100:~ }|*6 + | + ]], + }) + end) + it('works with arabic input and arabicshape', function() command('set arabic') @@ -285,11 +304,6 @@ describe('multibyte rendering: statusline', function() before_each(function() clear() screen = Screen.new(40, 4) - screen:set_default_attr_ids({ - [1] = { bold = true, foreground = Screen.colors.Blue1 }, - [2] = { bold = true, reverse = true }, - [3] = { background = Screen.colors.Red, foreground = Screen.colors.Gray100 }, - }) screen:attach() command('set laststatus=2') end) @@ -299,7 +313,7 @@ describe('multibyte rendering: statusline', function() screen:expect([[ ^ | {1:~ }| - {2:你好 }| + {3:你好 }| | ]]) end) @@ -308,7 +322,7 @@ describe('multibyte rendering: statusline', function() screen:expect([[ ^ | {1:~ }| - {2:abc }| + {3:abc }| | ]]) end) @@ -317,7 +331,7 @@ describe('multibyte rendering: statusline', function() screen:expect([[ ^ | {1:~ }| - {2:<9f> }| + {3:<9f> }| | ]]) end) @@ -327,7 +341,7 @@ describe('multibyte rendering: statusline', function() screen:expect([[ ^ | {1:~ }| - {2:o̸⃯ᷰ⃐⃧⃝ }| + {3:o̸⃯ᷰ⃐⃧⃝ }| | ]]) end) @@ -337,7 +351,7 @@ describe('multibyte rendering: statusline', function() screen:expect([[ ^ | {1:~ }| - {2:<9f><1df0><20ef><0338><20d0><20e7><20dd>}| + {3:<9f><1df0><20ef><0338><20d0><20e7><20dd>}| | ]]) end) @@ -348,7 +362,7 @@ describe('multibyte rendering: statusline', function() grid = [[ ^ | {1:~ }| - {2: Q≡ }| + {3: Q≡ }| | ]], } @@ -361,7 +375,7 @@ describe('multibyte rendering: statusline', function() grid = [[ ^ | {1:~ }| - {2:🧑�💻 }| + {3:🧑�💻 }| | ]], } @@ -375,7 +389,7 @@ describe('multibyte rendering: statusline', function() grid = [[ ^ | {1:~ }| - {2:xx}{3:🧑<200d>💻}{2:yy }| + {3:xx}{9:🧑<200d>💻}{3:yy }| | ]], } diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index c1d3af085f..dc48061a6c 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local feed, command, insert = helpers.feed, helpers.command, helpers.insert -local eq = helpers.eq -local fn = helpers.fn -local api = helpers.api -local curwin = helpers.api.nvim_get_current_win -local poke_eventloop = helpers.poke_eventloop + +local clear = n.clear +local feed, command, insert = n.feed, n.command, n.insert +local eq = t.eq +local fn = n.fn +local api = n.api +local curwin = n.api.nvim_get_current_win +local poke_eventloop = n.poke_eventloop describe('ext_multigrid', function() @@ -412,9 +414,23 @@ describe('ext_multigrid', function() end) describe('grid of smaller inner size', function() + before_each(function() + screen:try_resize_grid(2, 20, 5) + end) + it('is rendered correctly', function() + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]|*12 + {11:[No Name] }| + [3:-----------------------------------------------------]| + ## grid 2 + ^ | + {1:~ }|*4 + ## grid 3 + | + ]]} screen:try_resize_grid(2, 8, 5) - screen:expect{grid=[[ ## grid 1 [2:-----------------------------------------------------]|*12 @@ -427,12 +443,43 @@ describe('ext_multigrid', function() | ]]} end) + + it("cursor draws correctly with double-width char and 'showbreak'", function() + insert(('a'):rep(19) .. '哦bbbb') + command('setlocal showbreak=++') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]|*12 + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + aaaaaaaaaaaaaaaaaaa{1:>}| + {1:++}哦bbb^b | + {1:~ }|*3 + ## grid 3 + | + ]]} + end) end) describe('grid of bigger inner size', function() + before_each(function() + screen:try_resize_grid(2, 60, 20) + end) + it('is rendered correctly', function() + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]|*12 + {11:[No Name] }| + [3:-----------------------------------------------------]| + ## grid 2 + ^ | + {1:~ }|*19 + ## grid 3 + | + ]]} screen:try_resize_grid(2, 80, 20) - screen:expect{grid=[[ ## grid 1 [2:-----------------------------------------------------]|*12 @@ -445,13 +492,6 @@ describe('ext_multigrid', function() | ]]} end) - end) - - - describe('with resized grid', function() - before_each(function() - screen:try_resize_grid(2, 60, 20) - end) it('winwidth() winheight() getwininfo() return inner width and height #19743', function() eq(60, fn.winwidth(0)) @@ -483,7 +523,7 @@ describe('ext_multigrid', function() ]]} end) - it('"g$" works correctly with double-width characters and no wrapping', function() + it('g$ works correctly with double-width chars and no wrapping', function() command('set nowrap') insert(('a'):rep(58) .. ('哦'):rep(3)) feed('0') @@ -543,6 +583,22 @@ describe('ext_multigrid', function() ## grid 3 | ]]} + command('setlocal breakindent breakindentopt=shift:8') + feed('g$') + screen:expect{grid=[[ + ## grid 1 + [2:-----------------------------------------------------]|*12 + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb| + {23:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb^b}| + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | + | + {1:~ }|*16 + ## grid 3 + | + ]]} end) it('displays messages with default grid width', function() @@ -685,6 +741,75 @@ describe('ext_multigrid', function() [4] = {-1, "SW", 1, 13, 5, false, 250}; }} end) + + it('half-page scrolling stops at end of buffer', function() + command('set number') + insert(('foobar\n'):rep(100)) + feed('7<C-Y>') + screen:expect({ + grid = [[ + ## grid 1 + [2:-----------------------------------------------------]|*12 + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + {19: 75 }foobar | + {19: 76 }foobar | + {19: 77 }foobar | + {19: 78 }foobar | + {19: 79 }foobar | + {19: 80 }foobar | + {19: 81 }foobar | + {19: 82 }foobar | + {19: 83 }foobar | + {19: 84 }foobar | + {19: 85 }foobar | + {19: 86 }foobar | + {19: 87 }foobar | + {19: 88 }foobar | + {19: 89 }foobar | + {19: 90 }foobar | + {19: 91 }foobar | + {19: 92 }foobar | + {19: 93 }foobar | + {19: 94 }^foobar | + ## grid 3 + | + ]], + }) + feed('<C-D>') + screen:expect({ + grid = [[ + ## grid 1 + [2:-----------------------------------------------------]|*12 + {11:[No Name] [+] }| + [3:-----------------------------------------------------]| + ## grid 2 + {19: 82 }foobar | + {19: 83 }foobar | + {19: 84 }foobar | + {19: 85 }foobar | + {19: 86 }foobar | + {19: 87 }foobar | + {19: 88 }foobar | + {19: 89 }foobar | + {19: 90 }foobar | + {19: 91 }foobar | + {19: 92 }foobar | + {19: 93 }foobar | + {19: 94 }foobar | + {19: 95 }foobar | + {19: 96 }foobar | + {19: 97 }foobar | + {19: 98 }foobar | + {19: 99 }foobar | + {19:100 }foobar | + {19:101 }^ | + ## grid 3 + | + ]], + }) + end) end) it('multiline messages scroll over windows', function() @@ -2300,6 +2425,9 @@ describe('ext_multigrid', function() ]], win_viewport={ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + }, win_viewport_margins={ + [2] = {win = 1000, top = 0, bottom = 0, left = 0, right = 0}; + [4] = {win = 1001, top = 0, bottom = 0, left = 0, right = 0}; }} -- XXX: hack to get notifications. Could use next_msg() also. @@ -2330,6 +2458,9 @@ describe('ext_multigrid', function() ]], win_viewport={ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + }, win_viewport_margins={ + [2] = {win = 1000, top = 0, bottom = 0, left = 0, right = 0}; + [4] = {win = 1001, top = 1, bottom = 0, left = 0, right = 0}; }} eq({}, win_pos) @@ -2352,6 +2483,9 @@ describe('ext_multigrid', function() ]], win_viewport={ [2] = {win = 1000, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; [4] = {win = 1001, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0}; + }, win_viewport_margins={ + [2] = {win = 1000, top = 0, bottom = 0, left = 0, right = 0}; + [4] = {win = 1001, top = 0, bottom = 0, left = 0, right = 0}; }} eq({}, win_pos) end) diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua index 98af82a7c5..b40ff29dec 100644 --- a/test/functional/ui/options_spec.lua +++ b/test/functional/ui/options_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local shallowcopy = helpers.shallowcopy -local eval = helpers.eval + +local clear = n.clear +local command = n.command +local eq = t.eq +local shallowcopy = t.shallowcopy +local eval = n.eval describe('UI receives option updates', function() local screen diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 5e340af89c..4f6454a0fb 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -1,19 +1,21 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local helpers = require('test.functional.helpers')(after_each) -local child_session = require('test.functional.terminal.helpers') -local assert_alive = helpers.assert_alive -local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir -local eq = helpers.eq -local feed = helpers.feed -local feed_command = helpers.feed_command -local clear = helpers.clear -local command = helpers.command -local testprg = helpers.testprg -local nvim_dir = helpers.nvim_dir -local has_powershell = helpers.has_powershell -local set_shell_powershell = helpers.set_shell_powershell -local skip = helpers.skip -local is_os = helpers.is_os +local tt = require('test.functional.terminal.testutil') + +local assert_alive = n.assert_alive +local mkdir, write_file, rmdir = t.mkdir, t.write_file, n.rmdir +local eq = t.eq +local feed = n.feed +local feed_command = n.feed_command +local clear = n.clear +local command = n.command +local testprg = n.testprg +local nvim_dir = n.nvim_dir +local has_powershell = n.has_powershell +local set_shell_powershell = n.set_shell_powershell +local skip = t.skip +local is_os = t.is_os clear() -- for has_powershell() @@ -21,7 +23,7 @@ describe('shell command :!', function() local screen before_each(function() clear() - screen = child_session.setup_child_nvim({ + screen = tt.setup_child_nvim({ '-u', 'NONE', '-i', @@ -29,7 +31,7 @@ describe('shell command :!', function() '--cmd', 'colorscheme vim', '--cmd', - helpers.nvim_set .. ' notermguicolors', + n.nvim_set .. ' notermguicolors', }) screen:expect([[ {1: } | @@ -40,14 +42,14 @@ describe('shell command :!', function() end) after_each(function() - child_session.feed_data('\3') -- Ctrl-C + tt.feed_data('\3') -- Ctrl-C end) it('displays output without LF/EOF. #4646 #4569 #3772', function() skip(is_os('win')) -- NOTE: We use a child nvim (within a :term buffer) -- to avoid triggering a UI flush. - child_session.feed_data(':!printf foo; sleep 200\n') + tt.feed_data(':!printf foo; sleep 200\n') screen:expect([[ | {4:~ }|*2 @@ -61,7 +63,7 @@ describe('shell command :!', function() it('throttles shell-command output greater than ~10KB', function() skip(is_os('openbsd'), 'FIXME #10804') skip(is_os('win')) - child_session.feed_data((':!%s REP 30001 foo\n'):format(testprg('shell-test'))) + tt.feed_data((':!%s REP 30001 foo\n'):format(testprg('shell-test'))) -- If we observe any line starting with a dot, then throttling occurred. -- Avoid false failure on slow systems. @@ -80,7 +82,7 @@ describe('shell command :!', function() {3:-- TERMINAL --} | ]], { - -- test/functional/helpers.lua defaults to background=light. + -- test/functional/testnvim.lua defaults to background=light. [1] = { reverse = true }, [3] = { bold = true }, [10] = { foreground = 2 }, @@ -107,20 +109,15 @@ describe('shell command :!', function() it('handles control codes', function() skip(is_os('win'), 'missing printf') local screen = Screen.new(50, 4) - screen:set_default_attr_ids { - [1] = { bold = true, reverse = true }, - [2] = { bold = true, foreground = Screen.colors.SeaGreen }, - [3] = { foreground = Screen.colors.Blue }, - } screen:attach() -- Print TAB chars. #2958 feed([[:!printf '1\t2\t3'<CR>]]) screen:expect { grid = [[ - {1: }| + {3: }| :!printf '1\t2\t3' | 1 2 3 | - {2:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]], } feed([[<CR>]]) @@ -130,10 +127,10 @@ describe('shell command :!', function() feed([[:!printf '\007\007\007\007text'<CR>]]) screen:expect { grid = [[ - {1: }| + {3: }| :!printf '\007\007\007\007text' | text | - {2:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]], condition = function() eq(true, screen.bell) @@ -144,10 +141,10 @@ describe('shell command :!', function() -- Print BS control code. feed([[:echo system('printf ''\010\n''')<CR>]]) screen:expect([[ - {1: }| - {3:^H} | + {3: }| + {18:^H} | | - {2:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) feed([[<CR>]]) @@ -156,7 +153,7 @@ describe('shell command :!', function() screen:expect([[ :!printf '\n' | |*2 - {2:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) feed([[<CR>]]) end) @@ -170,12 +167,6 @@ describe('shell command :!', function() write_file('bang_filter_spec/f2', 'f2') write_file('bang_filter_spec/f3', 'f3') screen = Screen.new(53, 10) - 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, reverse = true }, - }) screen:attach() end) @@ -195,13 +186,13 @@ describe('shell command :!', function() screen:expect([[ | {1:~ }|*2 - {4: }| + {3: }| ]] .. result .. [[ | f1 | f2 | f3 | | - {3:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) end) @@ -212,14 +203,14 @@ describe('shell command :!', function() grid = [[ | {1:~ }| - {4: }| + {3: }| :!cat test/functional/fixtures/shell_data.txt | - {2:^@^A^B^C^D^E^F^H} | - {2:^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_} | - ö 한글 {2:<a5><c3>} | - t {2:<ff>} | + {18:^@^A^B^C^D^E^F^H} | + {18:^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_} | + ö 한글 {18:<a5><c3>} | + t {18:<ff>} | | - {3:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]], condition = function() eq(true, screen.bell) @@ -234,7 +225,7 @@ describe('shell command :!', function() -- Note: only the first example of split composed char works screen:expect([[ | - {4: }| + {3: }| :]] .. cmd .. [[ | å | ref: å̲ | @@ -242,7 +233,7 @@ describe('shell command :!', function() 2: å ̲ | 3: å ̲ | | - {3:Press ENTER or type command to continue}^ | + {6:Press ENTER or type command to continue}^ | ]]) end) end) @@ -256,28 +247,28 @@ describe('shell command :!', function() :!'Write-Output $a' | Write-Output $a | | - Press ENTER or type command to continue^ | + {6:Press ENTER or type command to continue}^ | ]]) feed_command([[!$a = 1; Write-Output '$a']]) screen:expect([[ :!$a = 1; Write-Output '$a' | $a | | - Press ENTER or type command to continue^ | + {6:Press ENTER or type command to continue}^ | ]]) feed_command([[!"Write-Output $a"]]) screen:expect([[ :!"Write-Output $a" | Write-Output | | - Press ENTER or type command to continue^ | + {6:Press ENTER or type command to continue}^ | ]]) feed_command([[!$a = 1; Write-Output "$a"]]) screen:expect([[ :!$a = 1; Write-Output "$a" | 1 | | - Press ENTER or type command to continue^ | + {6:Press ENTER or type command to continue}^ | ]]) if is_os('win') then feed_command([[!& 'cmd.exe' /c 'echo $a']]) @@ -285,7 +276,7 @@ describe('shell command :!', function() :!& 'cmd.exe' /c 'echo $a' | $a | | - Press ENTER or type command to continue^ | + {6:Press ENTER or type command to continue}^ | ]]) else feed_command([[!& '/bin/sh' -c 'echo ''$a''']]) @@ -293,7 +284,7 @@ describe('shell command :!', function() :!& '/bin/sh' -c 'echo ''$a''' | $a | | - Press ENTER or type command to continue^ | + {6:Press ENTER or type command to continue}^ | ]]) end end) diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 1f0d20f66d..8f8604eecb 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1,17 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local assert_alive = helpers.assert_alive -local clear, feed = helpers.clear, helpers.feed -local source = helpers.source -local insert = helpers.insert -local api = helpers.api -local async_meths = helpers.async_meths -local command = helpers.command -local fn = helpers.fn -local eq = helpers.eq -local pcall_err = helpers.pcall_err -local exec_lua = helpers.exec_lua -local exec = helpers.exec + +local assert_alive = n.assert_alive +local clear, feed = n.clear, n.feed +local source = n.source +local insert = n.insert +local api = n.api +local async_meths = n.async_meths +local command = n.command +local fn = n.fn +local eq = t.eq +local pcall_err = t.pcall_err +local exec_lua = n.exec_lua +local exec = n.exec describe('ui/ext_popupmenu', function() local screen @@ -1171,6 +1173,10 @@ describe('builtin popupmenu', function() [6] = { foreground = Screen.colors.Grey100, background = Screen.colors.Red }, [7] = { background = Screen.colors.Yellow }, -- Search [8] = { foreground = Screen.colors.Red }, + kn = { foreground = Screen.colors.Red, background = Screen.colors.Magenta }, + ks = { foreground = Screen.colors.Red, background = Screen.colors.Grey }, + xn = { foreground = Screen.colors.White, background = Screen.colors.Magenta }, + xs = { foreground = Screen.colors.Black, background = Screen.colors.Grey }, }) screen:attach({ ext_multigrid = multigrid }) end) @@ -1588,13 +1594,13 @@ describe('builtin popupmenu', function() describe('floating window preview #popup', function() it('pum popup preview', function() --row must > 10 - screen:try_resize(30, 11) + screen:try_resize(40, 11) exec([[ funct Omni_test(findstart, base) if a:findstart return col(".") - 1 endif - return [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "three"}] + return [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "looooooooooooooong"}] endfunc set omnifunc=Omni_test set completeopt=menu,popup @@ -1602,94 +1608,185 @@ describe('builtin popupmenu', function() funct Set_info() let comp_info = complete_info() if comp_info['selected'] == 2 - call nvim_complete_set(comp_info['selected'], {"info": "3info"}) + call nvim__complete_set(comp_info['selected'], {"info": "3info"}) endif endfunc autocmd CompleteChanged * call Set_info() ]]) feed('Gi<C-x><C-o>') - --floating preview in right if multigrid then screen:expect { grid = [[ ## grid 1 - [2:------------------------------]|*10 - [3:------------------------------]| + [2:----------------------------------------]|*10 + [3:----------------------------------------]| ## grid 2 - one^ | - {1:~ }|*9 + one^ | + {1:~ }|*9 ## grid 3 - {2:-- }{5:match 1 of 3} | + {2:-- }{5:match 1 of 3} | ## grid 4 {n:1info}| {n: }| ## grid 5 - {s:one }| - {n:two }| - {n:three }| + {s:one }| + {n:two }| + {n:looooooooooooooong }| ]], float_pos = { [5] = { -1, 'NW', 2, 1, 0, false, 100 }, - [4] = { 1001, 'NW', 1, 1, 15, true, 50 }, + [4] = { 1001, 'NW', 1, 1, 19, false, 50 }, + }, + win_viewport = { + [2] = { + win = 1000, + topline = 0, + botline = 2, + curline = 0, + curcol = 3, + linecount = 1, + sum_scroll_delta = 0, + }, + [4] = { + win = 1001, + topline = 0, + botline = 2, + curline = 0, + curcol = 0, + linecount = 1, + sum_scroll_delta = 0, + }, }, } else screen:expect { grid = [[ - one^ | - {s:one }{n:1info}{1: }| - {n:two }{1: }| - {n:three }{1: }| - {1:~ }|*6 - {2:-- }{5:match 1 of 3} | + one^ | + {s:one }{n:1info}{1: }| + {n:two }{1: }| + {n:looooooooooooooong }{1: }| + {1:~ }|*6 + {2:-- }{5:match 1 of 3} | ]], - unchanged = true, } end - -- test nvim_complete_set_info - feed('<C-N><C-N>') - vim.uv.sleep(10) + -- info window position should be adjusted when new leader add + feed('<C-P>o') if multigrid then screen:expect { grid = [[ ## grid 1 - [2:------------------------------]|*10 - [3:------------------------------]| + [2:----------------------------------------]|*10 + [3:----------------------------------------]| ## grid 2 - three^ | - {1:~ }|*9 + o^ | + {1:~ }|*9 ## grid 3 - {2:-- }{5:match 3 of 3} | + {2:-- }{8:Back at original} | ## grid 4 - {n:3info}| + {n:1info}| {n: }| ## grid 5 {n:one }| - {n:two }| - {s:three }| ]], float_pos = { [5] = { -1, 'NW', 2, 1, 0, false, 100 }, - [4] = { 1001, 'NW', 1, 1, 15, true, 50 }, + [4] = { 1001, 'NW', 1, 1, 15, false, 50 }, + }, + win_viewport = { + [2] = { + win = 1000, + topline = 0, + botline = 2, + curline = 0, + curcol = 1, + linecount = 1, + sum_scroll_delta = 0, + }, + [4] = { + win = 1001, + topline = 0, + botline = 2, + curline = 0, + curcol = 0, + linecount = 1, + sum_scroll_delta = 0, + }, }, } else screen:expect { grid = [[ - three^ | - {n:one 3info}{1: }| - {n:two }{1: }| - {s:three }{1: }| - {1:~ }|*6 - {2:-- }{5:match 3 of 3} | + o^ | + {n:one 1info}{1: }| + {1:~ }{n: }{1: }| + {1:~ }|*7 + {2:-- }{8:Back at original} | + ]], + } + end + + -- test nvim__complete_set_info + feed('<ESC>cc<C-X><C-O><C-N><C-N>') + vim.uv.sleep(10) + if multigrid then + screen:expect { + grid = [[ + ## grid 1 + [2:----------------------------------------]|*10 + [3:----------------------------------------]| + ## grid 2 + looooooooooooooong^ | + {1:~ }|*9 + ## grid 3 + {2:-- }{5:match 3 of 3} | + ## grid 5 + {n:one }| + {n:two }| + {s:looooooooooooooong }| + ## grid 6 + {n:3info}| + {n: }| + ]], + float_pos = { + [5] = { -1, 'NW', 2, 1, 0, false, 100 }, + [6] = { 1002, 'NW', 1, 1, 19, false, 50 }, + }, + win_viewport = { + [2] = { + win = 1000, + topline = 0, + botline = 2, + curline = 0, + curcol = 18, + linecount = 1, + sum_scroll_delta = 0, + }, + [6] = { + win = 1002, + topline = 0, + botline = 2, + curline = 0, + curcol = 0, + linecount = 1, + sum_scroll_delta = 0, + }, + }, + } + else + screen:expect { + grid = [[ + looooooooooooooong^ | + {n:one 3info}{1: }| + {n:two }{1: }| + {s:looooooooooooooong }{1: }| + {1:~ }|*6 + {2:-- }{5:match 3 of 3} | ]], } end - -- make sure info has set - feed('<C-y>') - eq('3info', exec_lua('return vim.v.completed_item.info')) -- preview in left feed('<ESC>cc') @@ -1699,24 +1796,24 @@ describe('builtin popupmenu', function() screen:expect { grid = [[ ## grid 1 - [2:------------------------------]|*10 - [3:------------------------------]| + [2:----------------------------------------]|*10 + [3:----------------------------------------]| ## grid 2 - itesttesttesttesttesone^t | - {1:~ }|*9 + itesttesttesttesttesone^t | + {1:~ }|*9 ## grid 3 - {2:-- }{5:match 1 of 3} | + {2:-- }{5:match 1 of 3} | ## grid 5 - {s: one }| - {n: two }| - {n: three }| - ## grid 6 + {s: one }| + {n: two }| + {n: looooooooooooooong }| + ## grid 7 {n:1info}| {n: }| ]], float_pos = { + [7] = { 1003, 'NW', 1, 1, 14, false, 50 }, [5] = { -1, 'NW', 2, 1, 19, false, 100 }, - [6] = { 1002, 'NW', 1, 1, 1, true, 50 }, }, win_viewport = { [2] = { @@ -1728,8 +1825,8 @@ describe('builtin popupmenu', function() linecount = 1, sum_scroll_delta = 0, }, - [6] = { - win = 1002, + [7] = { + win = 1003, topline = 0, botline = 2, curline = 0, @@ -1742,12 +1839,12 @@ describe('builtin popupmenu', function() else screen:expect { grid = [[ - itesttesttesttesttesone^t | - {1:~}{n:1info}{1: }{s: one }{1: }| - {1:~}{n: }{1: }{n: two }{1: }| - {1:~ }{n: three }{1: }| - {1:~ }|*6 - {2:-- }{5:match 1 of 3} | + itesttesttesttesttesone^t | + {1:~ }{n:1info}{s: one }{1: }| + {1:~ }{n: two }{1: }| + {1:~ }{n: looooooooooooooong }{1: }| + {1:~ }|*6 + {2:-- }{5:match 1 of 3} | ]], } end @@ -2942,7 +3039,7 @@ describe('builtin popupmenu', function() | {3:[No Name] }| {1::}sign define | - {1::}sign defin^e | + {1::}sign define^ | {1:~ }|*4 {4:[Command Line] }| :sign define | @@ -4477,23 +4574,15 @@ describe('builtin popupmenu', function() hi PmenuExtra guifg=White guibg=Magenta hi PmenuExtraSel guifg=Black guibg=Grey ]]) - local attrs = screen:get_default_attr_ids() - attrs.kn = { foreground = Screen.colors.Red, background = Screen.colors.Magenta } - attrs.ks = { foreground = Screen.colors.Red, background = Screen.colors.Grey } - attrs.xn = { foreground = Screen.colors.White, background = Screen.colors.Magenta } - attrs.xs = { foreground = Screen.colors.Black, background = Screen.colors.Grey } feed('iaw<C-X><C-u>') - screen:expect( - [[ + screen:expect([[ aword1^ | {s:aword1 }{ks:W }{xs:extra text 1 }{1: }| {n:aword2 }{kn:W }{xn:extra text 2 }{1: }| {n:aword3 }{kn:W }{xn:extra text 3 }{1: }| {1:~ }|*3 {2:-- }{5:match 1 of 3} | - ]], - attrs - ) + ]]) end) end) end diff --git a/test/functional/ui/quickfix_spec.lua b/test/functional/ui/quickfix_spec.lua index 40f8ef353a..73923a153a 100644 --- a/test/functional/ui/quickfix_spec.lua +++ b/test/functional/ui/quickfix_spec.lua @@ -1,7 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, api = helpers.clear, helpers.feed, helpers.api -local insert, command = helpers.insert, helpers.command + +local clear, feed, api = n.clear, n.feed, n.api +local insert, command = n.insert, n.command describe('quickfix selection highlight', function() local screen diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index e8d7d5c72d..4625ce8553 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -70,17 +70,19 @@ -- To help write screen tests, see Screen:snapshot_util(). -- To debug screen tests, see Screen:redraw_debug(). -local helpers = require('test.functional.helpers')(nil) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local busted = require('busted') + local deepcopy = vim.deepcopy -local shallowcopy = helpers.shallowcopy -local concat_tables = helpers.concat_tables +local shallowcopy = t.shallowcopy +local concat_tables = t.concat_tables local pesc = vim.pesc -local run_session = helpers.run_session -local eq = helpers.eq -local dedent = helpers.dedent -local get_session = helpers.get_session -local create_callindex = helpers.create_callindex +local run_session = n.run_session +local eq = t.eq +local dedent = t.dedent +local get_session = n.get_session +local create_callindex = n.create_callindex local inspect = vim.inspect @@ -139,6 +141,43 @@ local function _init_colors() end Screen.colors = colors Screen.colornames = colornames + + Screen._global_default_attr_ids = { + [1] = { foreground = Screen.colors.Blue1, bold = true }, + [2] = { reverse = true }, + [3] = { bold = true, reverse = true }, + [4] = { background = Screen.colors.LightMagenta }, + [5] = { bold = true }, + [6] = { foreground = Screen.colors.SeaGreen, bold = true }, + [7] = { background = Screen.colors.Gray, foreground = Screen.colors.DarkBlue }, + [8] = { foreground = Screen.colors.Brown }, + [9] = { background = Screen.colors.Red, foreground = Screen.colors.Grey100 }, + [10] = { background = Screen.colors.Yellow }, + [11] = { + foreground = Screen.colors.Blue1, + background = Screen.colors.LightMagenta, + bold = true, + }, + [12] = { background = Screen.colors.Gray }, + [13] = { background = Screen.colors.LightGrey, foreground = Screen.colors.DarkBlue }, + [14] = { background = Screen.colors.DarkGray, foreground = Screen.colors.LightGrey }, + [15] = { foreground = Screen.colors.Brown, bold = true }, + [16] = { foreground = Screen.colors.SlateBlue }, + [17] = { background = Screen.colors.LightGrey, foreground = Screen.colors.Black }, + [18] = { foreground = Screen.colors.Blue1 }, + [19] = { foreground = Screen.colors.Red }, + [20] = { background = Screen.colors.Yellow, foreground = Screen.colors.Red }, + [21] = { background = Screen.colors.Grey90 }, + [22] = { background = Screen.colors.LightBlue }, + [23] = { foreground = Screen.colors.Blue1, background = Screen.colors.LightCyan, bold = true }, + [24] = { background = Screen.colors.LightGrey, underline = true }, + [25] = { foreground = Screen.colors.Cyan4 }, + [26] = { foreground = Screen.colors.Fuchsia }, + [27] = { background = Screen.colors.Red, bold = true }, + [28] = { foreground = Screen.colors.SlateBlue, underline = true }, + [29] = { foreground = Screen.colors.SlateBlue, bold = true }, + [30] = { background = Screen.colors.Red }, + } end --- @param width? integer @@ -167,6 +206,7 @@ function Screen.new(width, height) wildmenu_selected = nil, win_position = {}, win_viewport = {}, + win_viewport_margins = {}, float_pos = {}, msg_grid = nil, msg_grid_pos = nil, @@ -217,6 +257,17 @@ function Screen:set_default_attr_ids(attr_ids) self._default_attr_ids = attr_ids end +function Screen:add_extra_attr_ids(extra_attr_ids) + local attr_ids = vim.deepcopy(Screen._global_default_attr_ids) + for id, attr in pairs(extra_attr_ids) do + if type(id) == 'number' and id < 100 then + error('extra attr ids should be at least 100 or be strings') + end + attr_ids[id] = attr + end + self._default_attr_ids = attr_ids +end + function Screen:get_default_attr_ids() return deepcopy(self._default_attr_ids) end @@ -234,7 +285,7 @@ end --- @field rgb? boolean --- @field _debug_float? boolean ---- @param options test.functional.ui.screen.Opts +--- @param options? test.functional.ui.screen.Opts --- @param session? test.Session function Screen:attach(options, session) session = session or get_session() @@ -257,6 +308,10 @@ function Screen:attach(options, session) if self._options.ext_multigrid then self._options.ext_linegrid = true end + + if self._default_attr_ids == nil then + self._default_attr_ids = Screen._global_default_attr_ids + end end function Screen:detach() @@ -296,6 +351,7 @@ local ext_keys = { 'ruler', 'float_pos', 'win_viewport', + 'win_viewport_margins', } local expect_keys = { @@ -480,7 +536,10 @@ function Screen:expect(expected, attr_ids, ...) attr_state.id_to_index = self:linegrid_check_attrs(attr_state.ids or {}) end - local actual_rows = self:render(not expected.any, attr_state) + local actual_rows + if expected.any or grid then + actual_rows = self:render(not expected.any, attr_state) + end if expected.any then -- Search for `any` anywhere in the screen lines. @@ -577,6 +636,9 @@ screen:redraw_debug() to show all intermediate screen states.]] if expected.win_viewport == nil then extstate.win_viewport = nil end + if expected.win_viewport_margins == nil then + extstate.win_viewport_margins = nil + end if expected.float_pos then expected.float_pos = deepcopy(expected.float_pos) @@ -949,6 +1011,7 @@ function Screen:_handle_grid_destroy(grid) if self._options.ext_multigrid then self.win_position[grid] = nil self.win_viewport[grid] = nil + self.win_viewport_margins[grid] = nil end end @@ -1004,6 +1067,16 @@ function Screen:_handle_win_viewport( } end +function Screen:_handle_win_viewport_margins(grid, win, top, bottom, left, right) + self.win_viewport_margins[grid] = { + win = win, + top = top, + bottom = bottom, + left = left, + right = right, + } +end + function Screen:_handle_win_float_pos(grid, ...) self.win_position[grid] = nil self.float_pos[grid] = { ... } @@ -1422,6 +1495,8 @@ function Screen:_extstate_repr(attr_state) end local win_viewport = (next(self.win_viewport) and self.win_viewport) or nil + local win_viewport_margins = (next(self.win_viewport_margins) and self.win_viewport_margins) + or nil return { popupmenu = self.popupmenu, @@ -1436,6 +1511,7 @@ function Screen:_extstate_repr(attr_state) msg_history = msg_history, float_pos = self.float_pos, win_viewport = win_viewport, + win_viewport_margins = win_viewport_margins, } end @@ -1642,23 +1718,26 @@ function Screen:_print_snapshot(attrs, ignore) if self._options.ext_linegrid then dict = self:_pprint_hlitem(a) else - dict = '{' .. self:_pprint_attrs(a) .. '}' + dict = '{ ' .. self:_pprint_attrs(a) .. ' }' end local keyval = (type(i) == 'number') and '[' .. tostring(i) .. ']' or i - table.insert(attrstrs, ' ' .. keyval .. ' = ' .. dict .. ';') + table.insert(attrstrs, ' ' .. keyval .. ' = ' .. dict .. ',') end - attrstr = (', attr_ids={\n' .. table.concat(attrstrs, '\n') .. '\n}') + attrstr = (',\n attr_ids = {\n ' .. table.concat(attrstrs, '\n ') .. '\n },') elseif isempty(attrs) then - attrstr = ', attr_ids={}' + attrstr = ',\n attr_ids = {},' end - local result = 'screen:expect{grid=[[\n' .. kwargs.grid .. '\n]]' .. attrstr + local result = ('screen:expect({\n grid = [[\n %s\n ]]%s'):format( + kwargs.grid:gsub('\n', '\n '), + attrstr + ) for _, k in ipairs(ext_keys) do if ext_state[k] ~= nil and not (k == 'win_viewport' and not self.options.ext_multigrid) then result = result .. ', ' .. k .. '=' .. fmt_ext_state(k, ext_state[k]) end end - result = result .. '}' + result = result .. '\n})' return result end @@ -1764,20 +1843,20 @@ function Screen:_pprint_hlitem(item) -- print(inspect(item)) local multi = self._rgb_cterm or self._options.ext_hlstate local cterm = (not self._rgb_cterm and not self._options.rgb) - local attrdict = '{' .. self:_pprint_attrs(multi and item[1] or item, cterm) .. '}' + local attrdict = '{ ' .. self:_pprint_attrs(multi and item[1] or item, cterm) .. ' }' local attrdict2, hlinfo local descdict = '' if self._rgb_cterm then - attrdict2 = ', {' .. self:_pprint_attrs(item[2], true) .. '}' + attrdict2 = ', { ' .. self:_pprint_attrs(item[2], true) .. ' }' hlinfo = item[3] else attrdict2 = '' hlinfo = item[2] end if self._options.ext_hlstate then - descdict = ', {' .. self:_pprint_hlinfo(hlinfo) .. '}' + descdict = ', { ' .. self:_pprint_hlinfo(hlinfo) .. ' }' end - return (multi and '{' or '') .. attrdict .. attrdict2 .. descdict .. (multi and '}' or '') + return (multi and '{ ' or '') .. attrdict .. attrdict2 .. descdict .. (multi and ' }' or '') end function Screen:_pprint_hlinfo(states) diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index 42e2b4d4b5..54580bf47c 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -1,15 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local spawn, set_session, clear = helpers.spawn, helpers.set_session, helpers.clear -local feed, command = helpers.feed, helpers.command -local insert = helpers.insert -local eq = helpers.eq -local fn, api = helpers.fn, helpers.api + +local spawn, set_session, clear = n.spawn, n.set_session, n.clear +local feed, command = n.feed, n.command +local insert = n.insert +local eq = t.eq +local fn, api = n.fn, n.api describe('screen', function() local screen local nvim_argv = { - helpers.nvim_prog, + n.nvim_prog, '-u', 'NONE', '-i', @@ -27,17 +29,13 @@ describe('screen', function() set_session(screen_nvim) screen = Screen.new() screen:attach() - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = 255 }, - [1] = { bold = true, reverse = true }, - }) end) it('default initial screen', function() screen:expect([[ ^ | - {0:~ }|*11 - {1:[No Name] }| + {1:~ }|*11 + {3:[No Name] }| | ]]) end) @@ -704,7 +702,7 @@ describe('Screen default colors', function() local extra = (light and ' background=light') or '' local nvim_argv = { - helpers.nvim_prog, + n.nvim_prog, '-u', 'NONE', '-i', @@ -811,9 +809,6 @@ end) it("showcmd doesn't cause empty grid_line with redrawdebug=compositor #22593", function() clear() local screen = Screen.new(30, 2) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, - }) screen:attach() command('set showcmd redrawdebug=compositor') feed('d') diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index a05436cf55..8bdf528412 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert -local command = helpers.command -local feed_command = helpers.feed_command -local eq = helpers.eq -local eval = helpers.eval -local fn = helpers.fn -local testprg = helpers.testprg + +local clear, feed, insert = n.clear, n.feed, n.insert +local command = n.command +local feed_command = n.feed_command +local eq = t.eq +local eval = n.eval +local fn = n.fn +local testprg = n.testprg describe('search highlighting', function() local screen @@ -53,7 +55,7 @@ describe('search highlighting', function() topline = 0, botline = 3, curline = 0, - curcol = 8, + curcol = 9, linecount = 2, sum_scroll_delta = 0, }, @@ -674,4 +676,18 @@ describe('search highlighting', function() :%g@a/b^ | ]]) end) + + it('incsearch is still visible after :redraw from K_EVENT', function() + fn.setline(1, { 'foo', 'bar' }) + feed('/foo<CR>/bar') + screen:expect([[ + foo | + {3:bar} | + {1:~ }|*4 + /bar^ | + ]]) + command('redraw!') + -- There is an intermediate state where :redraw! removes 'incsearch' highlight. + screen:expect_unchanged(true) + end) end) diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index 847a918dc9..b353b3738a 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local api, clear, eq = helpers.api, helpers.clear, helpers.eq -local eval, exec, feed = helpers.eval, helpers.exec, helpers.feed + +local api, clear, eq = n.api, n.clear, t.eq +local eval, exec, feed = n.eval, n.exec, n.feed describe('Signs', function() local screen @@ -205,7 +207,7 @@ describe('Signs', function() screen:expect([[ {2: }{6: 1 }a | {2: }{6: 2 }b | - {1:>>}WW{6: 3 }c | + WW{1:>>}{6: 3 }c | {2: }{6: 4 }^ | {0:~ }|*9 | @@ -218,9 +220,9 @@ describe('Signs', function() sign place 3 line=2 name=pietError buffer=1 ]]) screen:expect([[ - {1:>>}{8:XX}{6: 1 }a | - {8:XX}{1:>>}{6: 2 }b | - {1:>>}WW{6: 3 }c | + {8:XX}{1:>>}{6: 1 }a | + {1:>>}{8:XX}{6: 2 }b | + WW{1:>>}{6: 3 }c | {2: }{6: 4 }^ | {0:~ }|*9 | @@ -238,9 +240,9 @@ describe('Signs', function() -- "auto:3" accommodates all the signs we defined so far. exec('set signcolumn=auto:3') local s3 = [[ - {1:>>}{8:XX}{2: }{6: 1 }a | - {8:XX}{1:>>}{2: }{6: 2 }b | - {8:XX}{1:>>}WW{6: 3 }c | + {8:XX}{1:>>}{2: }{6: 1 }a | + {1:>>}{8:XX}{2: }{6: 2 }b | + WW{1:>>}{8:XX}{6: 3 }c | {2: }{6: 4 }^ | {0:~ }|*9 | @@ -249,9 +251,9 @@ describe('Signs', function() -- Check "yes:9". exec('set signcolumn=yes:9') screen:expect([[ - {1:>>}{8:XX}{2: }{6: 1 }a | - {8:XX}{1:>>}{2: }{6: 2 }b | - {8:XX}{1:>>}WW{2: }{6: 3 }c | + {8:XX}{1:>>}{2: }{6: 1 }a | + {1:>>}{8:XX}{2: }{6: 2 }b | + WW{1:>>}{8:XX}{2: }{6: 3 }c | {2: }{6: 4 }^ | {0:~ }|*9 | @@ -264,8 +266,8 @@ describe('Signs', function() exec('3move1') exec('2d') screen:expect([[ - {1:>>}{8:XX}{6: 1 }a | - {8:XX}{1:>>}{6: 2 }^b | + {8:XX}{1:>>}{6: 1 }a | + {1:>>}{8:XX}{6: 2 }^b | {2: }{6: 3 } | {0:~ }|*10 | @@ -273,8 +275,8 @@ describe('Signs', function() -- character deletion does not delete signs. feed('x') screen:expect([[ - {1:>>}{8:XX}{6: 1 }a | - {8:XX}{1:>>}{6: 2 }^ | + {8:XX}{1:>>}{6: 1 }a | + {1:>>}{8:XX}{6: 2 }^ | {2: }{6: 3 } | {0:~ }|*10 | diff --git a/test/functional/ui/spell_spec.lua b/test/functional/ui/spell_spec.lua index 8b5644ee42..da112148cd 100644 --- a/test/functional/ui/spell_spec.lua +++ b/test/functional/ui/spell_spec.lua @@ -1,13 +1,15 @@ -- Test for scenarios involving 'spell' -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local exec = helpers.exec -local feed = helpers.feed -local insert = helpers.insert -local api = helpers.api -local is_os = helpers.is_os + +local clear = n.clear +local exec = n.exec +local feed = n.feed +local insert = n.insert +local api = n.api +local is_os = t.is_os describe("'spell'", function() local screen diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index 41406a5860..faf94bccbe 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -1,15 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local exec = helpers.exec -local eval = helpers.eval -local exec_lua = helpers.exec_lua -local feed = helpers.feed -local api = helpers.api -local pcall_err = helpers.pcall_err -local assert_alive = helpers.assert_alive + +local clear = n.clear +local command = n.command +local eq = t.eq +local exec = n.exec +local eval = n.eval +local exec_lua = n.exec_lua +local feed = n.feed +local api = n.api +local pcall_err = t.pcall_err +local assert_alive = n.assert_alive local mousemodels = { 'extend', 'popup', 'popup_setpos' } @@ -27,38 +29,38 @@ describe('statuscolumn', function() [[set stc=%{v:relnum?v:relnum:(v:lnum==5?'truncate':v:lnum)}%{!v:relnum&&v:lnum==5?invalid:''}\ ]] ) screen:expect([[ - 4 aaaaa | - 3 aaaaa | - 2 aaaaa | - 1 aaaaa | - 8 ^aaaaa | - 1 aaaaa | - 2 aaaaa | - 3 aaaaa | - 4 aaaaa | - 5 aaaaa | - 6 aaaaa | - 7 aaaaa | - 8 aaaaa | + {8:4 }aaaaa | + {8:3 }aaaaa | + {8:2 }aaaaa | + {8:1 }aaaaa | + {8:8 }^aaaaa | + {8:1 }aaaaa | + {8:2 }aaaaa | + {8:3 }aaaaa | + {8:4 }aaaaa | + {8:5 }aaaaa | + {8:6 }aaaaa | + {8:7 }aaaaa | + {8:8 }aaaaa | | ]]) command('norm 5G') eq('Vim(redraw):E121: Undefined variable: invalid', pcall_err(command, 'redraw!')) eq('', eval('&statuscolumn')) screen:expect([[ - 4 aaaaa | - 5 ^aaaaa | - 6 aaaaa | - 7 aaaaa | - 8 aaaaa | - 9 aaaaa | - 10 aaaaa | - 11 aaaaa | - 12 aaaaa | - 13 aaaaa | - 14 aaaaa | - 15 aaaaa | - 16 aaaaa | + {8: 4 }aaaaa | + {8: 5 }^aaaaa | + {8: 6 }aaaaa | + {8: 7 }aaaaa | + {8: 8 }aaaaa | + {8: 9 }aaaaa | + {8:10 }aaaaa | + {8:11 }aaaaa | + {8:12 }aaaaa | + {8:13 }aaaaa | + {8:14 }aaaaa | + {8:15 }aaaaa | + {8:16 }aaaaa | | ]]) end) @@ -72,9 +74,9 @@ describe('statuscolumn', function() norm 5G | redraw! ]=]) screen:expect([[ - 1 aaaaa virt_text | - bbbbba^eaaa | - 1 aaaaa | + {8:1 }aaaaa virt_text | + {8:bbbbb}a^eaaa | + {8:1 }aaaaa | | ]]) -- Doesn't crash when trying to fill click defs that do not fit (#26845) @@ -84,9 +86,9 @@ describe('statuscolumn', function() norm 5Gzt | redraw! ]=]) screen:expect([[ - bbbbba^eaaa | - 1 aaaaa | - 2 aaaaa | + {8:bbbbb}a^eaaa | + {8:1 }aaaaa | + {8:2 }aaaaa | | ]]) end) @@ -94,19 +96,19 @@ describe('statuscolumn', function() it("works with 'number' and 'relativenumber'", function() command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]]) screen:expect([[ - 4 │aaaaa | - 5 │aaaaa | - 6 │aaaaa | - 7 │aaaaa | - 8 │^aaaaa | - 9 │aaaaa | - 10│aaaaa | - 11│aaaaa | - 12│aaaaa | - 13│aaaaa | - 14│aaaaa | - 15│aaaaa | - 16│aaaaa | + {8:4 │}aaaaa | + {8:5 │}aaaaa | + {8:6 │}aaaaa | + {8:7 │}aaaaa | + {8:8 │}^aaaaa | + {8:9 │}aaaaa | + {8:10│}aaaaa | + {8:11│}aaaaa | + {8:12│}aaaaa | + {8:13│}aaaaa | + {8:14│}aaaaa | + {8:15│}aaaaa | + {8:16│}aaaaa | | ]]) command([[set stc=%l%=%{&rnu?'\ ':''}%r│]]) @@ -114,39 +116,19 @@ describe('statuscolumn', function() command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]]) command('set relativenumber') screen:expect([[ - 4 4│aaaaa | - 5 3│aaaaa | - 6 2│aaaaa | - 7 1│aaaaa | - 8 0│^aaaaa | - 9 1│aaaaa | - 10 2│aaaaa | - 11 3│aaaaa | - 12 4│aaaaa | - 13 5│aaaaa | - 14 6│aaaaa | - 15 7│aaaaa | - 16 8│aaaaa | - | - ]]) - command([[set stc=%l%=%{&rnu?'\ ':''}%r│]]) - screen:expect_unchanged() - command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]]) - command('norm 12GH') - screen:expect([[ - 4 0│^aaaaa | - 5 1│aaaaa | - 6 2│aaaaa | - 7 3│aaaaa | - 8 4│aaaaa | - 9 5│aaaaa | - 10 6│aaaaa | - 11 7│aaaaa | - 12 8│aaaaa | - 13 9│aaaaa | - 14 10│aaaaa | - 15 11│aaaaa | - 16 12│aaaaa | + {8:4 4│}aaaaa | + {8:5 3│}aaaaa | + {8:6 2│}aaaaa | + {8:7 1│}aaaaa | + {8:8 0│}^aaaaa | + {8:9 1│}aaaaa | + {8:10 2│}aaaaa | + {8:11 3│}aaaaa | + {8:12 4│}aaaaa | + {8:13 5│}aaaaa | + {8:14 6│}aaaaa | + {8:15 7│}aaaaa | + {8:16 8│}aaaaa | | ]]) command([[set stc=%l%=%{&rnu?'\ ':''}%r│]]) @@ -160,58 +142,54 @@ describe('statuscolumn', function() .. [[%=%{&rnu&&(v:lnum%2)?'\ '.v:relnum:''}]] .. [[%#LineNr#%{&rnu&&!(v:lnum%2)?'\ '.v:relnum:''}│]] ) - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, - [1] = { foreground = Screen.colors.Brown }, - }) screen:expect([[ - {0:4 }{1:│}aaaaa | - {0:5 }{1:│}aaaaa | - {0:6 }{1:│}aaaaa | - {0:7 }{1:│}aaaaa | - {0:8 }{1:│}^aaaaa | - {0:9 }{1:│}aaaaa | - {0:10}{1:│}aaaaa | - {0:11}{1:│}aaaaa | - {0:12}{1:│}aaaaa | - {0:13}{1:│}aaaaa | - {0:14}{1:│}aaaaa | - {0:15}{1:│}aaaaa | - {0:16}{1:│}aaaaa | + {1:4 }{8:│}aaaaa | + {1:5 }{8:│}aaaaa | + {1:6 }{8:│}aaaaa | + {1:7 }{8:│}aaaaa | + {1:8 }{8:│}^aaaaa | + {1:9 }{8:│}aaaaa | + {1:10}{8:│}aaaaa | + {1:11}{8:│}aaaaa | + {1:12}{8:│}aaaaa | + {1:13}{8:│}aaaaa | + {1:14}{8:│}aaaaa | + {1:15}{8:│}aaaaa | + {1:16}{8:│}aaaaa | | ]]) command('set relativenumber') screen:expect([[ - {0:4 }{1: 4│}aaaaa | - {0:5 3}{1:│}aaaaa | - {0:6 }{1: 2│}aaaaa | - {0:7 1}{1:│}aaaaa | - {0:8 }{1: 0│}^aaaaa | - {0:9 1}{1:│}aaaaa | - {0:10}{1: 2│}aaaaa | - {0:11 3}{1:│}aaaaa | - {0:12}{1: 4│}aaaaa | - {0:13 5}{1:│}aaaaa | - {0:14}{1: 6│}aaaaa | - {0:15 7}{1:│}aaaaa | - {0:16}{1: 8│}aaaaa | + {1:4 }{8: 4│}aaaaa | + {1:5 3}{8:│}aaaaa | + {1:6 }{8: 2│}aaaaa | + {1:7 1}{8:│}aaaaa | + {1:8 }{8: 0│}^aaaaa | + {1:9 1}{8:│}aaaaa | + {1:10}{8: 2│}aaaaa | + {1:11 3}{8:│}aaaaa | + {1:12}{8: 4│}aaaaa | + {1:13 5}{8:│}aaaaa | + {1:14}{8: 6│}aaaaa | + {1:15 7}{8:│}aaaaa | + {1:16}{8: 8│}aaaaa | | ]]) command('set nonumber') screen:expect([[ - {1:4│}aaaaa | - {0:3}{1:│}aaaaa | - {1:2│}aaaaa | - {0:1}{1:│}aaaaa | - {1:0│}^aaaaa | - {0:1}{1:│}aaaaa | - {1:2│}aaaaa | - {0:3}{1:│}aaaaa | - {1:4│}aaaaa | - {0:5}{1:│}aaaaa | - {1:6│}aaaaa | - {0:7}{1:│}aaaaa | - {1:8│}aaaaa | + {8:4│}aaaaa | + {1:3}{8:│}aaaaa | + {8:2│}aaaaa | + {1:1}{8:│}aaaaa | + {8:0│}^aaaaa | + {1:1}{8:│}aaaaa | + {8:2│}aaaaa | + {1:3}{8:│}aaaaa | + {8:4│}aaaaa | + {1:5}{8:│}aaaaa | + {8:6│}aaaaa | + {1:7}{8:│}aaaaa | + {8:8│}aaaaa | | ]]) end) @@ -259,7 +237,7 @@ describe('statuscolumn', function() {2: }{1: │ }aaaaa | {0:>!}{2: }{1: 5│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │ }aaaaa | - {1:>>}{0:>!}{1: 6│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {0:>!}{1:>> 6│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │ }aaaaa | {2: }{1: 7│ }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │ }aaaaa | @@ -278,7 +256,7 @@ describe('statuscolumn', function() {2: }{1: │}{2: }{1: }aaaaaa | {2: }{1: 5│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | - {2: }{1: 6│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 6│}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | {2: }{1: 7│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | @@ -295,7 +273,7 @@ describe('statuscolumn', function() {2: }{1: │}{2: }{1: }aaaaaa | {2: }{1: 5│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | - {2: }{1: 6│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 6│}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | {2: }{1: 7│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | @@ -313,7 +291,7 @@ describe('statuscolumn', function() {2: }{1: 4│}{2: }{1: }aaaaaa | {2: }{1: 5│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: 5│}{2: }{1: }aaaaaa | - {2: }{1: 6│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 6│}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: 6│}{2: }{1: }aaaaaa | {2: }{1: 7│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: 7│}{2: }{1: }aaaaaa | @@ -331,7 +309,7 @@ describe('statuscolumn', function() {2: }{1: 4│}{2: }{1: }aaaaaa | {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: 3│}{2: }{1: }aaaaaa | - {2: }{1: 2│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 2│}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: 2│}{2: }{1: }aaaaaa | {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: 1│}{2: }{1: }aaaaaa | @@ -348,7 +326,7 @@ describe('statuscolumn', function() {2: }{1: │}{2: }{1: }aaaaaa | {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | - {2: }{1: 2│>>}{0:>!}{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 2│}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {2: }{1: │}{2: }{1: }aaaaaa | @@ -552,15 +530,11 @@ describe('statuscolumn', function() it('does not corrupt the screen with minwid sign item', function() screen:try_resize(screen._width, 3) - screen:set_default_attr_ids({ - [0] = { foreground = Screen.colors.Brown }, - [1] = { foreground = Screen.colors.Blue4, background = Screen.colors.Gray }, - }) command([[set stc=%6s\ %l]]) exec_lua('vim.api.nvim_buf_set_extmark(0, ns, 7, 0, {sign_text = "𒀀"})') screen:expect([[ - {0: 𒀀 8 }^aaaaa | - {0: }{1: }{0: 9 }aaaaa | + {8: 𒀀 8 }^aaaaa | + {8: }{7: }{8: 9 }aaaaa | | ]]) end) @@ -735,19 +709,19 @@ describe('statuscolumn', function() virt_lines_leftcol = true, virt_lines = {{{"virt", ""}}} }) ]]) screen:expect([[ - 4 aaaaa | - 5 aaaaa | - 6 aaaaa | - 7 aaaaa | + {7: }{8: 4 }aaaaa | + {7: }{8: 5 }aaaaa | + {7: }{8: 6 }aaaaa | + {7: }{8: 7 }aaaaa | virt | - --------- 8 ^aaaaa | + {7:---------}{8: 8 }^aaaaa | virt | - 𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀 9 aaaaa | - 10 aaaaa | - 11 aaaaa | - 12 aaaaa | - 13 aaaaa | - 14 aaaaa | + {7:𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀𒀀}{8: 9 }aaaaa | + {7: }{8:10 }aaaaa | + {7: }{8:11 }aaaaa | + {7: }{8:12 }aaaaa | + {7: }{8:13 }aaaaa | + {7: }{8:14 }aaaaa | | ]]) command('set stc=') -- also for the default fold column @@ -756,18 +730,18 @@ describe('statuscolumn', function() command([[set stc=%{foldlevel(v:lnum)>0?repeat('-',foldlevel(v:lnum)):''}%=%l\ ]]) feed('Gd10Ggg<C-l>') screen:expect([[ - 1 ^aaaaa | - 2 aaaaa | - 3 aaaaa | - 4 aaaaa | - 5 aaaaa | - 6 aaaaa | - 7 aaaaa | + {8: 1 }^aaaaa | + {8: 2 }aaaaa | + {8: 3 }aaaaa | + {8: 4 }aaaaa | + {8: 5 }aaaaa | + {8: 6 }aaaaa | + {8: 7 }aaaaa | virt | - ---------8 aaaaa | + {8:---------8 }aaaaa | virt | - ---------9 aaaaa | - ~ |*2 + {8:---------9 }aaaaa | + {1:~ }|*2 | ]]) end) @@ -775,15 +749,15 @@ describe('statuscolumn', function() it('works with cmdwin', function() feed(':set stc=%l<CR>q:k$') screen:expect([[ - 7 aaaaa | - 8 aaaaa | - 9 aaaaa | - 10aaaaa | - [No Name] [+] | - :1set stc=%^l | - :2 | - ~ |*5 - [Command Line] | + {8:7 }aaaaa | + {8:8 }aaaaa | + {8:9 }aaaaa | + {8:10}aaaaa | + {2:[No Name] [+] }| + {1::}{8:1}set stc=%^l | + {1::}{8:2} | + {1:~ }|*5 + {3:[Command Line] }| : | ]]) end) @@ -794,11 +768,11 @@ describe('statuscolumn', function() command('set relativenumber') command([[set stc=%{!&nu&&!&rnu?'':&rnu?v:relnum?v:relnum:&nu?v:lnum:'0':v:lnum}]]) screen:expect([[ - 1 aaaaa | - 8 ^aaaaa | - 1 aaaaa | - 2 aaaaa | - 3 aaaaa | + {8:1 }aaaaa | + {8:8 }^aaaaa | + {8:1 }aaaaa | + {8:2 }aaaaa | + {8:3 }aaaaa | | ]]) -- width correctly estimated with "w_nrwidth_line_count" when setting 'stc' @@ -815,11 +789,11 @@ describe('statuscolumn', function() -- width correctly estimated with "w_nrwidth_line_count" when setting 'nu' command('set number') screen:expect([[ - 7 aaaaa | - 8 ^aaaaa | - 9 aaaaa | - 10 aaaaa | - 11 aaaaa | + {8:7 }aaaaa | + {8:8 }^aaaaa | + {8:9 }aaaaa | + {8:10 }aaaaa | + {8:11 }aaaaa | | ]]) end) @@ -846,59 +820,59 @@ describe('statuscolumn', function() ]]) command('sign place 1 line=2 name=sign') screen:expect([[ - 1 ^aaaaa | - 2 ssaaaaa | + {8:1 }^aaaaa | + {8:2 ss}aaaaa | | ]]) command('sign place 2 line=2 name=sign') screen:expect([[ - 1 ^aaaaa | - 2 ssssaaaaa | + {8:1 }^aaaaa | + {8:2 ssss}aaaaa | | ]]) command('sign unplace 2') screen:expect([[ - 1 ^aaaaa | - 2 ssaaaaa | + {8:1 }^aaaaa | + {8:2 ss}aaaaa | | ]]) command('sign unplace 1') screen:expect([[ - 1 ^aaaaa | - 2 aaaaa | + {8:1 }^aaaaa | + {8:2 }aaaaa | | ]]) -- Also for extmark signs exec_lua('id1 = vim.api.nvim_buf_set_extmark(0, ns, 1, 0, {sign_text = "ss"})') screen:expect([[ - 1 ^aaaaa | - 2 ssaaaaa | + {8:1 }^aaaaa | + {8:2 ss}aaaaa | | ]]) exec_lua('id2 = vim.api.nvim_buf_set_extmark(0, ns, 1, 0, {sign_text = "ss"})') screen:expect([[ - 1 ^aaaaa | - 2 ssssaaaaa | + {8:1 }^aaaaa | + {8:2 ssss}aaaaa | | ]]) exec_lua('vim.api.nvim_buf_del_extmark(0, ns, id1)') screen:expect([[ - 1 ^aaaaa | - 2 ssaaaaa | + {8:1 }^aaaaa | + {8:2 ss}aaaaa | | ]]) exec_lua('vim.api.nvim_buf_del_extmark(0, ns, id2)') screen:expect([[ - 1 ^aaaaa | - 2 aaaaa | + {8:1 }^aaaaa | + {8:2 }aaaaa | | ]]) -- In all windows command('wincmd v | set ls=0') command('sign place 1 line=2 name=sign') screen:expect([[ - 1 ^aaaaa │1 aaaaa | - 2 ssaaaaa │2 ssaaaaa | + {8:1 }^aaaaa │{8:1 }aaaaa | + {8:2 ss}aaaaa │{8:2 ss}aaaaa | | ]]) end) @@ -918,12 +892,16 @@ describe('statuscolumn', function() it('does not wrap multibyte characters at the end of a line', function() screen:try_resize(33, 4) + screen:set_default_attr_ids { + [8] = { foreground = Screen.colors.Brown }, + [31] = { undercurl = true, special = Screen.colors.Red }, + } command([[set spell stc=%l\ ]]) command('call setline(8, "This is a line that contains ᶏ multibyte character.")') screen:expect([[ - 8 ^This is a line that contains ᶏ| - multibyte character. | - 9 aaaaa | + {8:8 }^This is a line that contains {31:ᶏ}| + {8: } {31:multibyte} character. | + {8:9 }{31:aaaaa} | | ]]) end) @@ -934,10 +912,63 @@ describe('statuscolumn', function() command('call setline(1, range(1, 99))') feed('Gyyp') screen:expect([[ - 98 98 | - 99 99 | - 100 ^99 | + {8:98 }98 | + {8:99 }99 | + {8:100 }^99 | | ]]) end) + + it('forces a rebuild with nvim__redraw', function() + screen:try_resize(40, 4) + -- Current window + command([[ + let g:insert = v:false + set nonu stc=%{g:insert?'insert':''} + vsplit + au InsertEnter * let g:insert = v:true | call nvim__redraw(#{statuscolumn:1, win:0}) + au InsertLeave * let g:insert = v:false | call nvim__redraw(#{statuscolumn:1, win:0}) + ]]) + feed('i') + screen:expect({ + grid = [[ + {8:insert}^aaaaa │aaaaa | + {8:insert}aaaaa │aaaaa | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- INSERT --} | + ]], + }) + feed('<esc>') + screen:expect({ + grid = [[ + ^aaaaa │aaaaa | + aaaaa │aaaaa | + {3:[No Name] [+] }{2:[No Name] [+] }| + | + ]], + }) + -- All windows + command([[ + au! InsertEnter * let g:insert = v:true | call nvim__redraw(#{statuscolumn:1}) + au! InsertLeave * let g:insert = v:false | call nvim__redraw(#{statuscolumn:1}) + ]]) + feed('i') + screen:expect({ + grid = [[ + {8:insert}^aaaaa │{8:insert}aaaaa | + {8:insert}aaaaa │{8:insert}aaaaa | + {3:[No Name] [+] }{2:[No Name] [+] }| + {5:-- INSERT --} | + ]], + }) + feed('<esc>') + screen:expect({ + grid = [[ + ^aaaaa │aaaaa | + aaaaa │aaaaa | + {3:[No Name] [+] }{2:[No Name] [+] }| + | + ]], + }) + end) end) diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index fee4b64d44..3087a0cde1 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -1,16 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local assert_alive = helpers.assert_alive -local clear = helpers.clear -local command = helpers.command -local feed = helpers.feed -local eq = helpers.eq -local fn = helpers.fn -local api = helpers.api -local exec = helpers.exec -local exec_lua = helpers.exec_lua -local eval = helpers.eval + +local assert_alive = n.assert_alive +local clear = n.clear +local command = n.command +local feed = n.feed +local eq = t.eq +local fn = n.fn +local api = n.api +local exec = n.exec +local exec_lua = n.exec_lua +local eval = n.eval local sleep = vim.uv.sleep +local pcall_err = t.pcall_err local mousemodels = { 'extend', 'popup', 'popup_setpos' } @@ -474,6 +477,25 @@ describe('global statusline', function() | ]]) end) + + it('horizontal separators unchanged when failing to split-move window', function() + exec([[ + botright split + let &winwidth = &columns + let &winminwidth = &columns + ]]) + eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd L')) + command('mode') + screen:expect([[ + | + {1:~ }|*5 + ────────────────────────────────────────────────────────────| + ^ | + {1:~ }|*6 + {2:[No Name] 0,0-1 All}| + | + ]]) + end) end) it('statusline does not crash if it has Arabic characters #19447', function() @@ -576,57 +598,59 @@ it('statusline is redrawn on various state changes', function() command('set ls=2 stl=%{repeat(reg_recording(),5)}') screen:expect([[ ^ | - ~ | - |*2 + {1:~ }| + {3: }| + | ]]) feed('qQ') screen:expect([[ ^ | - ~ | - QQQQQ | - recording @Q | + {1:~ }| + {3:QQQQQ }| + {5:recording @Q} | ]]) feed('q') screen:expect([[ ^ | - ~ | - |*2 + {1:~ }| + {3: }| + | ]]) -- Visual mode change #23932 command('set ls=2 stl=%{mode(1)}') screen:expect([[ ^ | - ~ | - n | + {1:~ }| + {3:n }| | ]]) feed('v') screen:expect([[ ^ | - ~ | - v | - -- VISUAL -- | + {1:~ }| + {3:v }| + {5:-- VISUAL --} | ]]) feed('V') screen:expect([[ ^ | - ~ | - V | - -- VISUAL LINE -- | + {1:~ }| + {3:V }| + {5:-- VISUAL LINE --} | ]]) feed('<C-V>') screen:expect([[ ^ | - ~ | - ^V | - -- VISUAL BLOCK -- | + {1:~ }| + {3:^V }| + {5:-- VISUAL BLOCK --} | ]]) feed('<Esc>') screen:expect([[ ^ | - ~ | - n | + {1:~ }| + {3:n }| | ]]) end) diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua index 5afc7dfe6c..be35e9bf4f 100644 --- a/test/functional/ui/syntax_conceal_spec.lua +++ b/test/functional/ui/syntax_conceal_spec.lua @@ -1,10 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, command = helpers.clear, helpers.feed, helpers.command -local eq = helpers.eq -local insert = helpers.insert -local poke_eventloop = helpers.poke_eventloop -local exec = helpers.exec + +local clear, feed, command = n.clear, n.feed, n.command +local eq = t.eq +local insert = n.insert +local poke_eventloop = n.poke_eventloop +local exec = n.exec describe('Screen', function() local screen @@ -435,6 +437,18 @@ describe('Screen', function() {0:~ }|*3 | ]]) + + feed('r') + screen:expect_unchanged() + + feed('m') + screen:expect([[ + ^moo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs |*4 + | + {0:~ }|*3 + | + ]]) end) it('and open line', function() diff --git a/test/functional/ui/tabline_spec.lua b/test/functional/ui/tabline_spec.lua index 1c90b17e57..5cda70df21 100644 --- a/test/functional/ui/tabline_spec.lua +++ b/test/functional/ui/tabline_spec.lua @@ -1,9 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, command, eq = helpers.clear, helpers.command, helpers.eq -local insert = helpers.insert -local api = helpers.api -local assert_alive = helpers.assert_alive + +local clear, command, eq = n.clear, n.command, t.eq +local insert = n.insert +local api = n.api +local assert_alive = n.assert_alive describe('ui/ext_tabline', function() local screen @@ -31,7 +33,7 @@ describe('ui/ext_tabline', function() screen:expect { grid = [[ ^ | - ~ |*3 + {1:~ }|*3 | ]], condition = function() @@ -44,7 +46,7 @@ describe('ui/ext_tabline', function() screen:expect { grid = [[ ^ | - ~ |*3 + {1:~ }|*3 | ]], condition = function() @@ -62,7 +64,7 @@ describe('ui/ext_tabline', function() screen:expect { grid = [[ ^ | - ~ |*3 + {1:~ }|*3 | ]], condition = function() @@ -81,7 +83,7 @@ describe('ui/ext_tabline', function() screen:expect { grid = [[ ^ | - ~ |*3 + {1:~ }|*3 | ]], condition = function() @@ -99,10 +101,6 @@ describe('tabline', function() clear() screen = Screen.new(42, 5) screen:attach() - screen:set_default_attr_ids({ - [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText - [1] = { reverse = true }, -- TabLineFill - }) end) it('redraws when tabline option is set', function() @@ -110,18 +108,18 @@ describe('tabline', function() command('set showtabline=2') screen:expect { grid = [[ - {1:asdf }| + {2:asdf }| ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], } command('set tabline=jkl') screen:expect { grid = [[ - {1:jkl }| + {2:jkl }| ^ | - {0:~ }|*2 + {1:~ }|*2 | ]], } @@ -141,9 +139,9 @@ describe('tabline', function() api.nvim_set_option_value('tabline', '%1T口口%2Ta' .. ('b'):rep(38) .. '%999Xc', {}) screen:expect { grid = [[ - {1:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| + {2:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| tab^1 | - {0:~ }|*2 + {1:~ }|*2 | ]], } @@ -151,27 +149,27 @@ describe('tabline', function() api.nvim_input_mouse('left', 'press', '', 0, 0, 1) screen:expect { grid = [[ - {1:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| + {2:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| tab^2 | - {0:~ }|*2 + {1:~ }|*2 | ]], } api.nvim_input_mouse('left', 'press', '', 0, 0, 0) screen:expect { grid = [[ - {1:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| + {2:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| tab^1 | - {0:~ }|*2 + {1:~ }|*2 | ]], } api.nvim_input_mouse('left', 'press', '', 0, 0, 39) screen:expect { grid = [[ - {1:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| + {2:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| tab^2 | - {0:~ }|*2 + {1:~ }|*2 | ]], } @@ -179,7 +177,7 @@ describe('tabline', function() screen:expect { grid = [[ tab^1 | - {0:~ }|*3 + {1:~ }|*3 | ]], } diff --git a/test/functional/ui/title_spec.lua b/test/functional/ui/title_spec.lua index 8060d3a460..e86fdbe5a3 100644 --- a/test/functional/ui/title_spec.lua +++ b/test/functional/ui/title_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local curwin = helpers.api.nvim_get_current_win -local eq = helpers.eq -local exec_lua = helpers.exec_lua -local feed = helpers.feed -local fn = helpers.fn -local api = helpers.api -local is_os = helpers.is_os + +local clear = n.clear +local command = n.command +local curwin = n.api.nvim_get_current_win +local eq = t.eq +local exec_lua = n.exec_lua +local feed = n.feed +local fn = n.fn +local api = n.api +local is_os = t.is_os describe('title', function() local screen diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 667dd64d62..0feec6bd03 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -1,19 +1,28 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear, feed, command = helpers.clear, helpers.feed, helpers.command -local fn = helpers.fn -local api = helpers.api -local eq = helpers.eq -local eval = helpers.eval -local retry = helpers.retry -local testprg = helpers.testprg -local is_os = helpers.is_os + +local clear, feed, command = n.clear, n.feed, n.command +local fn = n.fn +local api = n.api +local eq = t.eq +local eval = n.eval +local retry = t.retry +local testprg = n.testprg +local is_os = t.is_os describe("'wildmenu'", function() local screen before_each(function() clear() screen = Screen.new(25, 5) + screen:set_default_attr_ids { + [1] = { foreground = Screen.colors.Blue, bold = true }, + [2] = { reverse = true }, + [3] = { bold = true, reverse = true }, + [5] = { bold = true }, + [31] = { foreground = Screen.colors.Grey0, background = Screen.colors.Yellow }, + } screen:attach() end) @@ -105,14 +114,14 @@ describe("'wildmenu'", function() feed(':sign <tab>') screen:expect([[ | - ~ |*2 - define jump list > | + {1:~ }|*2 + {31:define}{3: jump list > }| :sign define^ | ]]) feed('<C-E>') screen:expect([[ | - ~ |*3 + {1:~ }|*3 :sign ^ | ]]) end) @@ -121,14 +130,14 @@ describe("'wildmenu'", function() feed(':sign <tab>') screen:expect([[ | - ~ |*2 - define jump list > | + {1:~ }|*2 + {31:define}{3: jump list > }| :sign define^ | ]]) feed('<tab><C-Y>') screen:expect([[ | - ~ |*3 + {1:~ }|*3 :sign jump^ | ]]) end) @@ -138,8 +147,8 @@ describe("'wildmenu'", function() feed(':sign <tab>') screen:expect([[ | - ~ |*2 - define jump list > | + {1:~ }|*2 + {31:define}{3: jump list > }| :sign define^ | ]]) end) @@ -152,15 +161,15 @@ describe("'wildmenu'", function() feed(':sign <tab>') screen:expect([[ | - ~ |*2 - define jump list > | + {1:~ }|*2 + {31:define}{3: jump list > }| :sign define^ | ]]) feed('<space>') screen:expect([[ | - ~ |*2 - [No Name] | + {1:~ }|*2 + {3:[No Name] }| :sign define ^ | ]]) end) @@ -170,16 +179,16 @@ describe("'wildmenu'", function() feed(':j<Tab><Tab><Tab>') screen:expect([[ | - ~ |*2 - join jumps | + {1:~ }|*2 + {3:join jumps }| :j^ | ]]) -- This would cause nvim to crash before #6650 feed('<BS><Tab>') screen:expect([[ | - ~ |*2 - ! # & < = > @ > | + {1:~ }|*2 + {31:!}{3: # & < = > @ > }| :!^ | ]]) end) @@ -192,7 +201,7 @@ describe("'wildmenu'", function() feed([[:sign <Tab>]]) -- Invoke wildmenu. -- NB: in earlier versions terminal output was redrawn during cmdline mode. -- For now just assert that the screen remains unchanged. - screen:expect { any = 'define jump list > |\n:sign define^ |' } + screen:expect { any = '{31:define}{3: jump list > }|\n:sign define^ |' } screen:expect_unchanged() -- cmdline CTRL-D display should also be preserved. @@ -222,8 +231,8 @@ describe("'wildmenu'", function() screen:expect { grid = [[ | - ~ |*2 - define jump list > | + {1:~ }|*2 + {31:define}{3: jump list > }| :sign define^ | ]], } @@ -252,7 +261,7 @@ describe("'wildmenu'", function() feed([[:<Tab>]]) -- Invoke wildmenu. -- Check only the last 2 lines, because the shell output is -- system-dependent. - screen:expect { any = '! # & < = > @ > |\n:!^' } + screen:expect { any = '{31:!}{3: # & < = > @ > }|\n:!^' } -- Because this test verifies a _lack_ of activity, we must wait the full timeout. -- So make it reasonable. screen:expect_unchanged(false, 1000) @@ -266,27 +275,29 @@ describe("'wildmenu'", function() command('set showtabline=2') feed(':set wildm<tab>') screen:expect([[ - [No Name] | - | - ~ | + {5: [No Name] }{2: }| | + {1:~ }| + {3: }| :set wildm | wildmenu wildmode | :set wildm^ | ]]) feed('<tab>') -- trigger wildmode full screen:expect([[ - [No Name] | - |*2 + {5: [No Name] }{2: }| + | + {3: }| :set wildm | - wildmenu wildmode |*2 + wildmenu wildmode | + {31:wildmenu}{3: wildmode }| :set wildmenu^ | ]]) feed('<Esc>') screen:expect([[ - [No Name] | + {5: [No Name] }{2: }| ^ | - ~ |*4 + {1:~ }|*4 | ]]) end) @@ -301,14 +312,14 @@ describe("'wildmenu'", function() feed(':sign u<tab>') screen:expect([[ | - ~ |*5 + {1:~ }|*5 :sign un^ | ]]) feed('<tab>') -- trigger wildmode list screen:expect([[ | - ~ |*2 - | + {1:~ }|*2 + {3: }| :sign un | undefine unplace | :sign un^ | @@ -316,7 +327,7 @@ describe("'wildmenu'", function() feed('<Esc>') screen:expect([[ ^ | - ~ |*5 + {1:~ }|*5 | ]]) @@ -324,8 +335,8 @@ describe("'wildmenu'", function() feed(':sign un<tab>') screen:expect([[ | - ~ |*2 - | + {1:~ }|*2 + {3: }| :sign un | undefine unplace | :sign un^ | @@ -335,7 +346,7 @@ describe("'wildmenu'", function() feed('<Esc>') screen:expect([[ ^ | - ~ |*5 + {1:~ }|*5 | ]]) end) @@ -348,8 +359,8 @@ describe("'wildmenu'", function() feed(':sign u<tab>') screen:expect([[ | - ~ |*2 - | + {1:~ }|*2 + {3: }| :sign u | undefine unplace | :sign u^ | @@ -357,8 +368,8 @@ describe("'wildmenu'", function() feed('<tab>') -- trigger wildmode longest screen:expect([[ | - ~ |*2 - | + {1:~ }|*2 + {3: }| :sign u | undefine unplace | :sign un^ | @@ -366,7 +377,7 @@ describe("'wildmenu'", function() feed('<Esc>') screen:expect([[ ^ | - ~ |*5 + {1:~ }|*5 | ]]) end) @@ -379,15 +390,16 @@ describe("'wildmenu'", function() feed('<c-d>') screen:expect([[ | - ~ |*2 - | + {1:~ }|*2 + {3: }| :set wildm | wildmenu wildmode | :set wildm^ | ]]) feed('<c-d>') screen:expect([[ - |*2 + | + {3: }| :set wildm | wildmenu wildmode | :set wildm | @@ -397,8 +409,8 @@ describe("'wildmenu'", function() feed('<Esc>') screen:expect([[ ^ | - ~ |*4 - [No Name] | + {1:~ }|*4 + {3:[No Name] }| | ]]) end) @@ -594,7 +606,7 @@ describe('ui/ext_wildmenu', function() screen:expect { grid = [[ | - ~ |*3 + {1:~ }|*3 :sign define^ | ]], wildmenu_items = expected, @@ -605,7 +617,7 @@ describe('ui/ext_wildmenu', function() screen:expect { grid = [[ | - ~ |*3 + {1:~ }|*3 :sign jump^ | ]], wildmenu_items = expected, @@ -616,7 +628,7 @@ describe('ui/ext_wildmenu', function() screen:expect { grid = [[ | - ~ |*3 + {1:~ }|*3 :sign ^ | ]], wildmenu_items = expected, @@ -627,7 +639,7 @@ describe('ui/ext_wildmenu', function() screen:expect { grid = [[ | - ~ |*3 + {1:~ }|*3 :sign define^ | ]], wildmenu_items = expected, @@ -638,7 +650,7 @@ describe('ui/ext_wildmenu', function() screen:expect { grid = [[ | - ~ |*3 + {1:~ }|*3 :sign definea^ | ]], } diff --git a/test/functional/ui/winbar_spec.lua b/test/functional/ui/winbar_spec.lua index c2a52c0f21..fb907026a5 100644 --- a/test/functional/ui/winbar_spec.lua +++ b/test/functional/ui/winbar_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local command = helpers.command -local insert = helpers.insert -local api = helpers.api -local eq = helpers.eq -local poke_eventloop = helpers.poke_eventloop -local feed = helpers.feed -local fn = helpers.fn -local pcall_err = helpers.pcall_err + +local clear = n.clear +local command = n.command +local insert = n.insert +local api = n.api +local eq = t.eq +local poke_eventloop = n.poke_eventloop +local feed = n.feed +local fn = n.fn +local pcall_err = t.pcall_err describe('winbar', function() local screen @@ -525,30 +527,24 @@ describe('local winbar with tabs', function() clear() screen = Screen.new(60, 10) screen:attach() - screen:set_default_attr_ids({ - [1] = { bold = true }, - [2] = { reverse = true }, - [3] = { bold = true, foreground = Screen.colors.Blue }, - [4] = { underline = true, background = Screen.colors.LightGray }, - }) api.nvim_set_option_value('winbar', 'foo', { scope = 'local', win = 0 }) end) it('works', function() command('tabnew') screen:expect([[ - {4: [No Name] }{1: [No Name] }{2: }{4:X}| + {24: [No Name] }{5: [No Name] }{2: }{24:X}| ^ | - {3:~ }|*7 + {1:~ }|*7 | ]]) command('tabnext') screen:expect { grid = [[ - {1: [No Name] }{4: [No Name] }{2: }{4:X}| - {1:foo }| + {5: [No Name] }{24: [No Name] }{2: }{24:X}| + {5:foo }| ^ | - {3:~ }|*6 + {1:~ }|*6 | ]], } @@ -561,11 +557,11 @@ describe('local winbar with tabs', function() text]] screen:expect { grid = [[ - {1:foo }| + {5:foo }| some | goofy | tex^t | - {3:~ }|*5 + {1:~ }|*5 | ]], } @@ -574,9 +570,9 @@ describe('local winbar with tabs', function() command 'tabedit' screen:expect { grid = [[ - {4: + [No Name] }{1: [No Name] }{2: }{4:X}| + {24: + [No Name] }{5: [No Name] }{2: }{24:X}| ^ | - {3:~ }|*7 + {1:~ }|*7 | ]], } @@ -584,12 +580,12 @@ describe('local winbar with tabs', function() command 'tabprev' screen:expect { grid = [[ - {1: + [No Name] }{4: [No Name] }{2: }{4:X}| - {1:foo }| + {5: + [No Name] }{24: [No Name] }{2: }{24:X}| + {5:foo }| some | goofy | tex^t | - {3:~ }|*4 + {1:~ }|*4 | ]], } @@ -609,16 +605,11 @@ it('winbar works properly when redrawing is postponed #23534', function() }) local screen = Screen.new(60, 6) screen:attach() - screen:set_default_attr_ids({ - [0] = { foreground = Screen.colors.Blue, bold = true }, - [1] = { bold = true }, - [2] = { bold = true, reverse = true }, - }) screen:expect([[ - {1:(winbar) }| + {5:(winbar) }| ^ | - {0:~ }|*2 - {2:(statusline) }| + {1:~ }|*2 + {3:(statusline) }| | ]]) end) diff --git a/test/functional/vimscript/api_functions_spec.lua b/test/functional/vimscript/api_functions_spec.lua index 4985768bb0..b2865d2b4c 100644 --- a/test/functional/vimscript/api_functions_spec.lua +++ b/test/functional/vimscript/api_functions_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local neq, eq, command = helpers.neq, helpers.eq, helpers.command -local clear = helpers.clear -local exc_exec, expect, eval = helpers.exc_exec, helpers.expect, helpers.eval -local exec_lua = helpers.exec_lua -local insert, pcall_err = helpers.insert, helpers.pcall_err -local matches = helpers.matches -local api = helpers.api -local feed = helpers.feed + +local neq, eq, command = t.neq, t.eq, n.command +local clear = n.clear +local exc_exec, expect, eval = n.exc_exec, n.expect, n.eval +local exec_lua = n.exec_lua +local insert, pcall_err = n.insert, t.pcall_err +local matches = t.matches +local api = n.api +local feed = n.feed describe('eval-API', function() before_each(clear) @@ -179,8 +181,8 @@ describe('eval-API', function() eq('Vim(call):E117: Unknown function: buffer_get_line', err) -- some api functions are only useful from a msgpack-rpc channel - err = exc_exec('call nvim_subscribe("fancyevent")') - eq('Vim(call):E117: Unknown function: nvim_subscribe', err) + err = exc_exec('call nvim_set_client_info()') + eq('Vim(call):E117: Unknown function: nvim_set_client_info', err) end) it('have metadata accessible with api_info()', function() diff --git a/test/functional/vimscript/buf_functions_spec.lua b/test/functional/vimscript/buf_functions_spec.lua index 931fe640a9..625fbe7e03 100644 --- a/test/functional/vimscript/buf_functions_spec.lua +++ b/test/functional/vimscript/buf_functions_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local clear = helpers.clear -local fn = helpers.fn -local api = helpers.api -local command = helpers.command -local exc_exec = helpers.exc_exec -local get_pathsep = helpers.get_pathsep -local rmdir = helpers.rmdir -local pcall_err = helpers.pcall_err -local mkdir = helpers.mkdir +local eq = t.eq +local clear = n.clear +local fn = n.fn +local api = n.api +local command = n.command +local exc_exec = n.exc_exec +local get_pathsep = n.get_pathsep +local rmdir = n.rmdir +local pcall_err = t.pcall_err +local mkdir = t.mkdir local fname = 'Xtest-functional-eval-buf_functions' local fname2 = fname .. '.2' diff --git a/test/functional/vimscript/changedtick_spec.lua b/test/functional/vimscript/changedtick_spec.lua index 85928921c5..baea53a700 100644 --- a/test/functional/vimscript/changedtick_spec.lua +++ b/test/functional/vimscript/changedtick_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed -local clear = helpers.clear -local fn = helpers.fn -local api = helpers.api -local command = helpers.command -local exc_exec = helpers.exc_exec -local pcall_err = helpers.pcall_err -local exec_capture = helpers.exec_capture +local eq = t.eq +local eval = n.eval +local feed = n.feed +local clear = n.clear +local fn = n.fn +local api = n.api +local command = n.command +local exc_exec = n.exc_exec +local pcall_err = t.pcall_err +local exec_capture = n.exec_capture before_each(clear) diff --git a/test/functional/vimscript/container_functions_spec.lua b/test/functional/vimscript/container_functions_spec.lua index 1b34ea0165..1d95bf6470 100644 --- a/test/functional/vimscript/container_functions_spec.lua +++ b/test/functional/vimscript/container_functions_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local eval = helpers.eval -local api = helpers.api -local clear = helpers.clear +local eq = t.eq +local eval = n.eval +local api = n.api +local clear = n.clear before_each(clear) diff --git a/test/functional/vimscript/ctx_functions_spec.lua b/test/functional/vimscript/ctx_functions_spec.lua index dc60a474f3..5e9a803b5d 100644 --- a/test/functional/vimscript/ctx_functions_spec.lua +++ b/test/functional/vimscript/ctx_functions_spec.lua @@ -1,19 +1,20 @@ -local helpers = require('test.functional.helpers')(after_each) - -local call = helpers.call -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local eval = helpers.eval -local feed = helpers.feed +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local call = n.call +local clear = n.clear +local command = n.command +local eq = t.eq +local eval = n.eval +local feed = n.feed local map = vim.tbl_map -local api = helpers.api -local parse_context = helpers.parse_context -local exec_capture = helpers.exec_capture -local source = helpers.source +local api = n.api +local parse_context = n.parse_context +local exec_capture = n.exec_capture +local source = n.source local trim = vim.trim -local write_file = helpers.write_file -local pcall_err = helpers.pcall_err +local write_file = t.write_file +local pcall_err = t.pcall_err describe('context functions', function() local fname1 = 'Xtest-functional-eval-ctx1' diff --git a/test/functional/vimscript/environ_spec.lua b/test/functional/vimscript/environ_spec.lua index 0763def84e..abb093a6c8 100644 --- a/test/functional/vimscript/environ_spec.lua +++ b/test/functional/vimscript/environ_spec.lua @@ -1,13 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local environ = helpers.fn.environ -local exists = helpers.fn.exists -local system = helpers.fn.system -local nvim_prog = helpers.nvim_prog -local command = helpers.command -local eval = helpers.eval -local setenv = helpers.fn.setenv +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local environ = n.fn.environ +local exists = n.fn.exists +local system = n.fn.system +local nvim_prog = n.nvim_prog +local command = n.command +local eval = n.eval +local setenv = n.fn.setenv describe('environment variables', function() it('environ() handles empty env variable', function() diff --git a/test/functional/vimscript/errorlist_spec.lua b/test/functional/vimscript/errorlist_spec.lua index 1e405e7e64..ef9111f788 100644 --- a/test/functional/vimscript/errorlist_spec.lua +++ b/test/functional/vimscript/errorlist_spec.lua @@ -1,13 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local command = helpers.command -local eq = helpers.eq -local exc_exec = helpers.exc_exec -local get_win_var = helpers.api.nvim_win_get_var +local clear = n.clear +local command = n.command +local eq = t.eq +local exc_exec = n.exc_exec +local get_win_var = n.api.nvim_win_get_var describe('setqflist()', function() - local setqflist = helpers.fn.setqflist + local setqflist = n.fn.setqflist before_each(clear) @@ -46,7 +47,7 @@ describe('setqflist()', function() end) describe('setloclist()', function() - local setloclist = helpers.fn.setloclist + local setloclist = n.fn.setloclist before_each(clear) @@ -73,7 +74,7 @@ describe('setloclist()', function() end) it("doesn't crash when when window is closed in the middle #13721", function() - helpers.insert([[ + n.insert([[ hello world]]) command('vsplit') @@ -82,6 +83,6 @@ describe('setloclist()', function() command('call setloclist(0, [])') command('lopen') - helpers.assert_alive() + n.assert_alive() end) end) diff --git a/test/functional/vimscript/eval_spec.lua b/test/functional/vimscript/eval_spec.lua index e337959810..0c812d968e 100644 --- a/test/functional/vimscript/eval_spec.lua +++ b/test/functional/vimscript/eval_spec.lua @@ -9,37 +9,38 @@ -- test/functional/vimscript/<funcname>_spec.lua -- test/functional/vimscript/functions_spec.lua -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local mkdir = helpers.mkdir -local clear = helpers.clear -local eq = helpers.eq -local exec = helpers.exec -local exc_exec = helpers.exc_exec -local exec_lua = helpers.exec_lua -local exec_capture = helpers.exec_capture -local eval = helpers.eval -local command = helpers.command -local write_file = helpers.write_file -local api = helpers.api +local mkdir = t.mkdir +local clear = n.clear +local eq = t.eq +local exec = n.exec +local exc_exec = n.exc_exec +local exec_lua = n.exec_lua +local exec_capture = n.exec_capture +local eval = n.eval +local command = n.command +local write_file = t.write_file +local api = n.api local sleep = vim.uv.sleep -local matches = helpers.matches -local pcall_err = helpers.pcall_err -local assert_alive = helpers.assert_alive -local poke_eventloop = helpers.poke_eventloop -local feed = helpers.feed -local expect_exit = helpers.expect_exit +local matches = t.matches +local pcall_err = t.pcall_err +local assert_alive = n.assert_alive +local poke_eventloop = n.poke_eventloop +local feed = n.feed +local expect_exit = n.expect_exit describe('Up to MAX_FUNC_ARGS arguments are handled by', function() local max_func_args = 20 -- from eval.h - local range = helpers.fn.range + local range = n.fn.range before_each(clear) it('printf()', function() - local printf = helpers.fn.printf - local rep = helpers.fn['repeat'] + local printf = n.fn.printf + local rep = n.fn['repeat'] local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,' eq(expected, printf(rep('%d,', max_func_args - 1), unpack(range(2, max_func_args)))) local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') @@ -47,7 +48,7 @@ describe('Up to MAX_FUNC_ARGS arguments are handled by', function() end) it('rpcnotify()', function() - local rpcnotify = helpers.fn.rpcnotify + local rpcnotify = n.fn.rpcnotify local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args))) eq(1, ret) ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)') @@ -69,17 +70,17 @@ describe('backtick expansion', function() end) teardown(function() - helpers.rmdir('test-backticks') + n.rmdir('test-backticks') end) it("with default 'shell'", function() - if helpers.is_os('win') then + if t.is_os('win') then command(':silent args `dir /b *2`') else command(':silent args `echo ***2`') end eq({ 'file2' }, eval('argv()')) - if helpers.is_os('win') then + if t.is_os('win') then command(':silent args `dir /s/b *4`') eq({ 'subdir\\file4' }, eval('map(argv(), \'fnamemodify(v:val, ":.")\')')) else diff --git a/test/functional/vimscript/executable_spec.lua b/test/functional/vimscript/executable_spec.lua index 1d95f6088e..e5530926c4 100644 --- a/test/functional/vimscript/executable_spec.lua +++ b/test/functional/vimscript/executable_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq, clear, call, write_file, command = - helpers.eq, helpers.clear, helpers.call, helpers.write_file, helpers.command -local exc_exec = helpers.exc_exec -local eval = helpers.eval -local is_os = helpers.is_os +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq, clear, call, write_file, command = t.eq, n.clear, n.call, t.write_file, n.command +local exc_exec = n.exc_exec +local eval = n.eval +local is_os = t.is_os describe('executable()', function() before_each(clear) diff --git a/test/functional/vimscript/execute_spec.lua b/test/functional/vimscript/execute_spec.lua index 29488ed31c..8caaea39a7 100644 --- a/test/functional/vimscript/execute_spec.lua +++ b/test/functional/vimscript/execute_spec.lua @@ -1,15 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local eval = helpers.eval -local clear = helpers.clear -local source = helpers.source -local exc_exec = helpers.exc_exec -local pcall_err = helpers.pcall_err -local fn = helpers.fn +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local command = helpers.command -local feed = helpers.feed -local is_os = helpers.is_os + +local eq = t.eq +local eval = n.eval +local clear = n.clear +local source = n.source +local exc_exec = n.exc_exec +local pcall_err = t.pcall_err +local fn = n.fn +local command = n.command +local feed = n.feed +local is_os = t.is_os describe('execute()', function() before_each(clear) @@ -191,21 +193,21 @@ describe('execute()', function() feed([[:call Test1()<cr>]]) screen:expect([[ ^ | - ~ |*4 + {1:~ }|*4 ABCD | ]]) feed([[:call Test2()<cr>]]) screen:expect([[ ^ | - ~ |*4 + {1:~ }|*4 1234ABCD | ]]) feed([[:call Test3()<cr>]]) screen:expect([[ ^ | - ~ |*4 + {1:~ }|*4 1234ABCDXZYZ | ]]) @@ -215,39 +217,39 @@ describe('execute()', function() -- "ef" was overwritten since msg_col was recovered wrongly screen:expect([[ 1234 | - Error detected while processing function| - Test4: | - line 2: | - abcdABCD | - Press ENTER or type command to continue^ | + {9:Error detected while processing function}| + {9: Test4:} | + {8:line 2:} | + {9:abcd}ABCD | + {6:Press ENTER or type command to continue}^ | ]]) feed([[<cr>]]) -- to clear screen feed([[:call Test5()<cr>]]) screen:expect([[ ^ | - ~ |*4 + {1:~ }|*4 1234ABCD | ]]) feed([[:call Test6()<cr>]]) screen:expect([[ | - Error detected while processing function| - Test6: | - line 2: | - E121ABCD | - Press ENTER or type command to continue^ | + {9:Error detected while processing function}| + {9: Test6:} | + {8:line 2:} | + {9:E121}ABCD | + {6:Press ENTER or type command to continue}^ | ]]) feed([[:call Test7()<cr>]]) screen:expect([[ - Error detected while processing function| - Test6: | - line 2: | - E121ABCD | + {9:Error detected while processing function}| + {9: Test6:} | + {8:line 2:} | + {9:E121}ABCD | ABCD | - Press ENTER or type command to continue^ | + {6:Press ENTER or type command to continue}^ | ]]) end) @@ -265,7 +267,7 @@ describe('execute()', function() command('let g:mes = execute("echon 42", "")') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 42 | ]]) eq('42', eval('g:mes')) @@ -289,7 +291,7 @@ describe('execute()', function() command('let g:mes = execute("echon 42")') screen:expect([[ ^ | - ~ |*3 + {1:~ }|*3 | ]]) eq('42', eval('g:mes')) @@ -298,7 +300,7 @@ describe('execute()', function() screen:expect { grid = [[ ^ | - ~ |*3 + {1:~ }|*3 | ]], unchanged = true, diff --git a/test/functional/vimscript/exepath_spec.lua b/test/functional/vimscript/exepath_spec.lua index 01033a2140..97747619ad 100644 --- a/test/functional/vimscript/exepath_spec.lua +++ b/test/functional/vimscript/exepath_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq, clear, call = helpers.eq, helpers.clear, helpers.call -local command = helpers.command -local exc_exec = helpers.exc_exec -local matches = helpers.matches -local is_os = helpers.is_os -local set_shell_powershell = helpers.set_shell_powershell -local eval = helpers.eval +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq, clear, call = t.eq, n.clear, n.call +local command = n.command +local exc_exec = n.exc_exec +local matches = t.matches +local is_os = t.is_os +local set_shell_powershell = n.set_shell_powershell +local eval = n.eval local find_dummies = function(ext_pat) local tmp_path = eval('$PATH') diff --git a/test/functional/vimscript/fnamemodify_spec.lua b/test/functional/vimscript/fnamemodify_spec.lua index 4a134fe23c..51b1e8489a 100644 --- a/test/functional/vimscript/fnamemodify_spec.lua +++ b/test/functional/vimscript/fnamemodify_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local fnamemodify = helpers.fn.fnamemodify -local getcwd = helpers.fn.getcwd -local command = helpers.command -local write_file = helpers.write_file -local alter_slashes = helpers.alter_slashes -local is_os = helpers.is_os +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local fnamemodify = n.fn.fnamemodify +local getcwd = n.fn.getcwd +local command = n.command +local write_file = t.write_file +local alter_slashes = n.alter_slashes +local is_os = t.is_os local function eq_slashconvert(expected, got) eq(alter_slashes(expected), alter_slashes(got)) @@ -24,7 +26,7 @@ describe('fnamemodify()', function() end) it('handles the root path', function() - local root = helpers.pathroot() + local root = n.pathroot() eq(root, fnamemodify([[/]], ':p:h')) eq(root, fnamemodify([[/]], ':p')) if is_os('win') then diff --git a/test/functional/vimscript/functions_spec.lua b/test/functional/vimscript/functions_spec.lua index 09b3334989..7d9e80a32b 100644 --- a/test/functional/vimscript/functions_spec.lua +++ b/test/functional/vimscript/functions_spec.lua @@ -5,12 +5,13 @@ -- -- Core "eval" tests live in eval_spec.lua. -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eval = helpers.eval -local matches = helpers.matches -local is_os = helpers.is_os +local clear = n.clear +local eval = n.eval +local matches = t.matches +local is_os = t.is_os before_each(clear) diff --git a/test/functional/vimscript/getline_spec.lua b/test/functional/vimscript/getline_spec.lua index 08e7711b8c..32be6b5702 100644 --- a/test/functional/vimscript/getline_spec.lua +++ b/test/functional/vimscript/getline_spec.lua @@ -1,9 +1,10 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local call = helpers.call -local clear = helpers.clear -local eq = helpers.eq -local expect = helpers.expect +local call = n.call +local clear = n.clear +local eq = t.eq +local expect = n.expect describe('getline()', function() before_each(function() diff --git a/test/functional/vimscript/glob_spec.lua b/test/functional/vimscript/glob_spec.lua index 77351f95fa..754381231e 100644 --- a/test/functional/vimscript/glob_spec.lua +++ b/test/functional/vimscript/glob_spec.lua @@ -1,6 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, command, eval, eq = helpers.clear, helpers.command, helpers.eval, helpers.eq -local mkdir = helpers.mkdir +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, command, eval, eq = n.clear, n.command, n.eval, t.eq +local mkdir = t.mkdir before_each(function() clear() diff --git a/test/functional/vimscript/has_spec.lua b/test/functional/vimscript/has_spec.lua index 82b3db5b67..1d2187be6b 100644 --- a/test/functional/vimscript/has_spec.lua +++ b/test/functional/vimscript/has_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local clear = helpers.clear -local connect = helpers.connect -local eq = helpers.eq -local fn = helpers.fn -local is_os = helpers.is_os -local nvim_prog = helpers.nvim_prog + +local clear = n.clear +local connect = n.connect +local eq = t.eq +local fn = n.fn +local is_os = t.is_os +local nvim_prog = n.nvim_prog describe('has()', function() before_each(clear) diff --git a/test/functional/vimscript/hostname_spec.lua b/test/functional/vimscript/hostname_spec.lua index 62520e8222..e6b2499775 100644 --- a/test/functional/vimscript/hostname_spec.lua +++ b/test/functional/vimscript/hostname_spec.lua @@ -1,9 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local ok = helpers.ok -local call = helpers.call -local clear = helpers.clear -local is_os = helpers.is_os +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local ok = t.ok +local call = n.call +local clear = n.clear +local is_os = t.is_os describe('hostname()', function() before_each(clear) diff --git a/test/functional/vimscript/input_spec.lua b/test/functional/vimscript/input_spec.lua index b749d5a7f0..552ae6d5cc 100644 --- a/test/functional/vimscript/input_spec.lua +++ b/test/functional/vimscript/input_spec.lua @@ -1,15 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local eq = helpers.eq -local feed = helpers.feed -local api = helpers.api -local clear = helpers.clear -local source = helpers.source -local command = helpers.command -local exc_exec = helpers.exc_exec -local pcall_err = helpers.pcall_err -local async_meths = helpers.async_meths +local eq = t.eq +local feed = n.feed +local api = n.api +local clear = n.clear +local source = n.source +local command = n.command +local exc_exec = n.exc_exec +local async_meths = n.async_meths local NIL = vim.NIL local screen @@ -407,7 +407,6 @@ describe('inputdialog()', function() end) describe('confirm()', function() - -- oldtest: Test_confirm() it('works', function() api.nvim_set_option_value('more', false, {}) -- Avoid hit-enter prompt api.nvim_set_option_value('laststatus', 2, {}) @@ -470,20 +469,6 @@ describe('confirm()', function() screen:expect({ any = '%[No Name%]' }) eq(1, api.nvim_get_var('a')) end - - eq('Vim(call):E730: Using a List as a String', pcall_err(command, 'call confirm([])')) - eq( - 'Vim(call):E730: Using a List as a String', - pcall_err(command, 'call confirm("Are you sure?", [])') - ) - eq( - 'Vim(call):E745: Using a List as a Number', - pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", [])') - ) - eq( - 'Vim(call):E730: Using a List as a String', - pcall_err(command, 'call confirm("Are you sure?", "&Yes\n&No\n", 0, [])') - ) end) it('shows dialog even if :silent #8788', function() diff --git a/test/functional/vimscript/json_functions_spec.lua b/test/functional/vimscript/json_functions_spec.lua index ef0359263e..ae56e8873d 100644 --- a/test/functional/vimscript/json_functions_spec.lua +++ b/test/functional/vimscript/json_functions_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local fn = helpers.fn -local api = helpers.api -local eq = helpers.eq -local eval = helpers.eval -local command = helpers.command -local exc_exec = helpers.exc_exec -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local fn = n.fn +local api = n.api +local eq = t.eq +local eval = n.eval +local command = n.command +local exc_exec = n.exc_exec +local pcall_err = t.pcall_err local NIL = vim.NIL -local source = helpers.source +local source = n.source describe('json_decode() function', function() local restart = function(...) diff --git a/test/functional/vimscript/lang_spec.lua b/test/functional/vimscript/lang_spec.lua index 2dde90e334..923c8c215d 100644 --- a/test/functional/vimscript/lang_spec.lua +++ b/test/functional/vimscript/lang_spec.lua @@ -1,6 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq -local exc_exec, source = helpers.exc_exec, helpers.source +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eval, eq = n.clear, n.eval, t.eq +local exc_exec, source = n.exc_exec, n.source describe('vimscript', function() before_each(clear) diff --git a/test/functional/vimscript/let_spec.lua b/test/functional/vimscript/let_spec.lua index 15d4b189b8..412e06df20 100644 --- a/test/functional/vimscript/let_spec.lua +++ b/test/functional/vimscript/let_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local clear = helpers.clear -local command = helpers.command -local eval = helpers.eval -local api = helpers.api -local exec = helpers.exec -local exec_capture = helpers.exec_capture -local expect_exit = helpers.expect_exit -local source = helpers.source -local testprg = helpers.testprg +local eq = t.eq +local clear = n.clear +local command = n.command +local eval = n.eval +local api = n.api +local exec = n.exec +local exec_capture = n.exec_capture +local expect_exit = n.expect_exit +local source = n.source +local testprg = n.testprg before_each(clear) diff --git a/test/functional/vimscript/map_functions_spec.lua b/test/functional/vimscript/map_functions_spec.lua index 59d427ca90..44be5b3185 100644 --- a/test/functional/vimscript/map_functions_spec.lua +++ b/test/functional/vimscript/map_functions_spec.lua @@ -1,18 +1,19 @@ -local helpers = require('test.functional.helpers')(after_each) - -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local exec = helpers.exec -local exec_lua = helpers.exec_lua -local expect = helpers.expect -local feed = helpers.feed -local fn = helpers.fn -local api = helpers.api -local source = helpers.source -local command = helpers.command -local exec_capture = helpers.exec_capture -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local eval = n.eval +local exec = n.exec +local exec_lua = n.exec_lua +local expect = n.expect +local feed = n.feed +local fn = n.fn +local api = n.api +local source = n.source +local command = n.command +local exec_capture = n.exec_capture +local pcall_err = t.pcall_err describe('maparg()', function() before_each(clear) @@ -233,7 +234,9 @@ describe('mapset()', function() 0, exec_lua([[ GlobalCount = 0 - vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 1 end, + }) return GlobalCount ]]) ) @@ -242,7 +245,9 @@ describe('mapset()', function() exec_lua([[ _G.saved_asdf_map = vim.fn.maparg('asdf', 'n', false, true) - vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 10 end }) + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 10 end, + }) ]]) feed('asdf') eq(11, exec_lua([[return GlobalCount]])) @@ -253,7 +258,10 @@ describe('mapset()', function() exec([[ let g:saved_asdf_map = maparg('asdf', 'n', v:false, v:true) - lua vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 10 end }) + lua << + vim.api.nvim_set_keymap('n', 'asdf', '', { + callback = function() GlobalCount = GlobalCount + 10 end, + }) ]]) feed('asdf') eq(22, exec_lua([[return GlobalCount]])) diff --git a/test/functional/vimscript/match_functions_spec.lua b/test/functional/vimscript/match_functions_spec.lua index 3db612e472..87c57f1c15 100644 --- a/test/functional/vimscript/match_functions_spec.lua +++ b/test/functional/vimscript/match_functions_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local eq = helpers.eq -local clear = helpers.clear -local fn = helpers.fn -local command = helpers.command -local exc_exec = helpers.exc_exec +local eq = t.eq +local clear = n.clear +local fn = n.fn +local command = n.command +local exc_exec = n.exc_exec before_each(clear) diff --git a/test/functional/vimscript/minmax_functions_spec.lua b/test/functional/vimscript/minmax_functions_spec.lua index c4a986bc8c..bf223c436e 100644 --- a/test/functional/vimscript/minmax_functions_spec.lua +++ b/test/functional/vimscript/minmax_functions_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local eval = helpers.eval -local command = helpers.command -local clear = helpers.clear -local fn = helpers.fn -local pcall_err = helpers.pcall_err +local eq = t.eq +local eval = n.eval +local command = n.command +local clear = n.clear +local fn = n.fn +local pcall_err = t.pcall_err before_each(clear) for _, func in ipairs({ 'min', 'max' }) do diff --git a/test/functional/vimscript/modeline_spec.lua b/test/functional/vimscript/modeline_spec.lua index ae63a66f43..53a5e9b692 100644 --- a/test/functional/vimscript/modeline_spec.lua +++ b/test/functional/vimscript/modeline_spec.lua @@ -1,9 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) -local assert_alive = helpers.assert_alive -local clear, command, write_file = helpers.clear, helpers.command, helpers.write_file +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local assert_alive = n.assert_alive +local clear, command, write_file = n.clear, n.command, t.write_file describe('modeline', function() - local tempfile = helpers.tmpname() + local tempfile = t.tmpname() before_each(clear) after_each(function() diff --git a/test/functional/vimscript/msgpack_functions_spec.lua b/test/functional/vimscript/msgpack_functions_spec.lua index 609a706155..d59dceef31 100644 --- a/test/functional/vimscript/msgpack_functions_spec.lua +++ b/test/functional/vimscript/msgpack_functions_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local fn = helpers.fn -local eval, eq = helpers.eval, helpers.eq -local command = helpers.command -local api = helpers.api -local exc_exec = helpers.exc_exec -local is_os = helpers.is_os +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local fn = n.fn +local eval, eq = n.eval, t.eq +local command = n.command +local api = n.api +local exc_exec = n.exc_exec +local is_os = t.is_os describe('msgpack*() functions', function() before_each(clear) @@ -469,12 +471,12 @@ describe('msgpackparse() function', function() end) it('msgpackparse(systemlist(...)) does not segfault. #3135', function() - local cmd = "sort(keys(msgpackparse(systemlist('" .. helpers.nvim_prog .. " --api-info'))[0]))" + local cmd = "sort(keys(msgpackparse(systemlist('" .. n.nvim_prog .. " --api-info'))[0]))" eval(cmd) eval(cmd) -- do it again (try to force segfault) local api_info = eval(cmd) -- do it again if is_os('win') then - helpers.assert_alive() + n.assert_alive() pending('msgpackparse() has a bug on windows') return end diff --git a/test/functional/vimscript/null_spec.lua b/test/functional/vimscript/null_spec.lua index 805cd13844..9a27239a6d 100644 --- a/test/functional/vimscript/null_spec.lua +++ b/test/functional/vimscript/null_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local exc_exec = helpers.exc_exec -local command = helpers.command -local clear = helpers.clear -local api = helpers.api -local fn = helpers.fn -local eq = helpers.eq +local exc_exec = n.exc_exec +local command = n.command +local clear = n.clear +local api = n.api +local fn = n.fn +local eq = t.eq local function redir_exec(cmd) api.nvim_set_var('__redir_exec_cmd', cmd) diff --git a/test/functional/vimscript/operators_spec.lua b/test/functional/vimscript/operators_spec.lua index 64f6b60238..9c4a91d655 100644 --- a/test/functional/vimscript/operators_spec.lua +++ b/test/functional/vimscript/operators_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local eq = helpers.eq -local eval = helpers.eval -local clear = helpers.clear +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local eval = n.eval +local clear = n.clear describe('Division operator', function() before_each(clear) diff --git a/test/functional/vimscript/printf_spec.lua b/test/functional/vimscript/printf_spec.lua index 4fa4ea7f4c..3c66e07618 100644 --- a/test/functional/vimscript/printf_spec.lua +++ b/test/functional/vimscript/printf_spec.lua @@ -1,11 +1,12 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local clear = helpers.clear -local eq = helpers.eq -local eval = helpers.eval -local fn = helpers.fn -local api = helpers.api -local exc_exec = helpers.exc_exec +local clear = n.clear +local eq = t.eq +local eval = n.eval +local fn = n.fn +local api = n.api +local exc_exec = n.exc_exec describe('printf()', function() before_each(clear) diff --git a/test/functional/vimscript/reltime_spec.lua b/test/functional/vimscript/reltime_spec.lua index 7cdb78e4ce..fa110d602a 100644 --- a/test/functional/vimscript/reltime_spec.lua +++ b/test/functional/vimscript/reltime_spec.lua @@ -1,6 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eq, ok = helpers.clear, helpers.eq, helpers.ok -local neq, command, fn = helpers.neq, helpers.command, helpers.fn +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq, ok = n.clear, t.eq, t.ok +local neq, command, fn = t.neq, n.command, n.fn +local matches = t.matches local reltime, reltimestr, reltimefloat = fn.reltime, fn.reltimestr, fn.reltimefloat describe('reltimestr(), reltimefloat()', function() @@ -15,7 +18,7 @@ describe('reltimestr(), reltimefloat()', function() neq('0.0', reltimestr(elapsed)) ok(reltimefloat(elapsed) > 0.0) -- original vim test for < 0.1, but easily fails on travis - ok(nil ~= string.match(reltimestr(elapsed), '0%.')) + matches('0%.', reltimestr(elapsed)) ok(reltimefloat(elapsed) < 1.0) local same = reltime(now, now) @@ -29,7 +32,7 @@ describe('reltimestr(), reltimefloat()', function() neq('0.0', reltimestr(differs)) ok(reltimefloat(differs) > 0.0) -- original vim test for < 0.1, but easily fails on travis - ok(nil ~= string.match(reltimestr(differs), '0%.')) + matches('0%.', reltimestr(differs)) ok(reltimefloat(differs) < 1.0) end) diff --git a/test/functional/vimscript/screenchar_spec.lua b/test/functional/vimscript/screenchar_spec.lua index 48b6893865..b6137d7741 100644 --- a/test/functional/vimscript/screenchar_spec.lua +++ b/test/functional/vimscript/screenchar_spec.lua @@ -1,6 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq -local command, api, fn = helpers.command, helpers.api, helpers.fn +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq, neq = n.clear, t.eq, t.neq +local command, api, fn = n.command, n.api, n.fn local tbl_deep_extend = vim.tbl_deep_extend -- Set up two overlapping floating windows diff --git a/test/functional/vimscript/screenpos_spec.lua b/test/functional/vimscript/screenpos_spec.lua index b951d830a6..630e19b841 100644 --- a/test/functional/vimscript/screenpos_spec.lua +++ b/test/functional/vimscript/screenpos_spec.lua @@ -1,7 +1,9 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eq, api = helpers.clear, helpers.eq, helpers.api -local command, fn = helpers.command, helpers.fn -local feed = helpers.feed +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eq, api = n.clear, t.eq, n.api +local command, fn = n.command, n.fn +local feed = n.feed before_each(clear) diff --git a/test/functional/vimscript/server_spec.lua b/test/functional/vimscript/server_spec.lua index 360fcf0dfe..4b0dc087f6 100644 --- a/test/functional/vimscript/server_spec.lua +++ b/test/functional/vimscript/server_spec.lua @@ -1,12 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) -local assert_log = helpers.assert_log -local eq, neq, eval = helpers.eq, helpers.neq, helpers.eval -local clear, fn, api = helpers.clear, helpers.fn, helpers.api -local ok = helpers.ok -local matches = helpers.matches -local pcall_err = helpers.pcall_err -local mkdir = helpers.mkdir -local is_os = helpers.is_os +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local assert_log = t.assert_log +local eq, neq, eval = t.eq, t.neq, n.eval +local clear, fn, api = n.clear, n.fn, n.api +local ok = t.ok +local matches = t.matches +local pcall_err = t.pcall_err +local check_close = n.check_close +local mkdir = t.mkdir +local rmdir = n.rmdir +local is_os = t.is_os local testlog = 'Xtest-server-log' @@ -18,12 +22,16 @@ end describe('server', function() after_each(function() + check_close() os.remove(testlog) end) it('serverstart() stores sockets in $XDG_RUNTIME_DIR', function() local dir = 'Xtest_xdg_run' mkdir(dir) + finally(function() + rmdir(dir) + end) clear({ env = { XDG_RUNTIME_DIR = dir } }) matches(dir, fn.stdpath('run')) if not is_os('win') then @@ -100,14 +108,14 @@ describe('server', function() local s = fn.serverstart('127.0.0.1:0') -- assign random port if #s > 0 then - assert(string.match(s, '127.0.0.1:%d+')) + matches('127.0.0.1:%d+', s) eq(s, fn.serverlist()[1]) clear_serverlist() end s = fn.serverstart('127.0.0.1:') -- assign random port if #s > 0 then - assert(string.match(s, '127.0.0.1:%d+')) + matches('127.0.0.1:%d+', s) eq(s, fn.serverlist()[1]) clear_serverlist() end @@ -142,7 +150,7 @@ describe('server', function() it('serverlist() returns the list of servers', function() clear() -- There should already be at least one server. - local n = eval('len(serverlist())') + local _n = eval('len(serverlist())') -- Add some servers. local servs = ( @@ -156,25 +164,25 @@ describe('server', function() local new_servs = eval('serverlist()') -- Exactly #servs servers should be added. - eq(n + #servs, #new_servs) + eq(_n + #servs, #new_servs) -- The new servers should be at the end of the list. for i = 1, #servs do - eq(servs[i], new_servs[i + n]) + eq(servs[i], new_servs[i + _n]) eq(1, eval("serverstop('" .. servs[i] .. "')")) end -- After serverstop() the servers should NOT be in the list. - eq(n, eval('len(serverlist())')) + eq(_n, eval('len(serverlist())')) end) end) describe('startup --listen', function() it('validates', function() clear() - local cmd = { unpack(helpers.nvim_argv) } + local cmd = { unpack(n.nvim_argv) } table.insert(cmd, '--listen') matches('nvim.*: Argument missing after: "%-%-listen"', fn.system(cmd)) - cmd = { unpack(helpers.nvim_argv) } + cmd = { unpack(n.nvim_argv) } table.insert(cmd, '--listen2') matches('nvim.*: Garbage after option argument: "%-%-listen2"', fn.system(cmd)) end) diff --git a/test/functional/vimscript/setpos_spec.lua b/test/functional/vimscript/setpos_spec.lua index a26e48f469..968da4d8f4 100644 --- a/test/functional/vimscript/setpos_spec.lua +++ b/test/functional/vimscript/setpos_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) -local setpos = helpers.fn.setpos -local getpos = helpers.fn.getpos -local insert = helpers.insert -local clear = helpers.clear -local command = helpers.command -local eval = helpers.eval -local eq = helpers.eq -local exc_exec = helpers.exc_exec +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local setpos = n.fn.setpos +local getpos = n.fn.getpos +local insert = n.insert +local clear = n.clear +local command = n.command +local eval = n.eval +local eq = t.eq +local exc_exec = n.exc_exec describe('setpos() function', function() before_each(function() diff --git a/test/functional/vimscript/sort_spec.lua b/test/functional/vimscript/sort_spec.lua index bd3d0da146..c8c1651ed8 100644 --- a/test/functional/vimscript/sort_spec.lua +++ b/test/functional/vimscript/sort_spec.lua @@ -1,14 +1,15 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq +local eq = t.eq local NIL = vim.NIL -local eval = helpers.eval -local clear = helpers.clear -local api = helpers.api -local fn = helpers.fn -local command = helpers.command -local exc_exec = helpers.exc_exec -local pcall_err = helpers.pcall_err +local eval = n.eval +local clear = n.clear +local api = n.api +local fn = n.fn +local command = n.command +local exc_exec = n.exc_exec +local pcall_err = t.pcall_err before_each(clear) diff --git a/test/functional/vimscript/special_vars_spec.lua b/test/functional/vimscript/special_vars_spec.lua index 590d409141..70e195ba0b 100644 --- a/test/functional/vimscript/special_vars_spec.lua +++ b/test/functional/vimscript/special_vars_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local exc_exec = helpers.exc_exec -local command = helpers.command -local fn = helpers.fn -local clear = helpers.clear -local eval = helpers.eval -local eq = helpers.eq -local api = helpers.api +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local exc_exec = n.exc_exec +local command = n.command +local fn = n.fn +local clear = n.clear +local eval = n.eval +local eq = t.eq +local api = n.api local NIL = vim.NIL describe('Special values', function() diff --git a/test/functional/vimscript/state_spec.lua b/test/functional/vimscript/state_spec.lua index 7179806e36..211d79928a 100644 --- a/test/functional/vimscript/state_spec.lua +++ b/test/functional/vimscript/state_spec.lua @@ -1,11 +1,13 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local exec = helpers.exec -local exec_lua = helpers.exec_lua -local feed = helpers.feed -local api = helpers.api -local poke_eventloop = helpers.poke_eventloop +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local exec = n.exec +local exec_lua = n.exec_lua +local feed = n.feed +local api = n.api +local poke_eventloop = n.poke_eventloop before_each(clear) diff --git a/test/functional/vimscript/string_spec.lua b/test/functional/vimscript/string_spec.lua index 6a7fe1bad9..32aa04c0d0 100644 --- a/test/functional/vimscript/string_spec.lua +++ b/test/functional/vimscript/string_spec.lua @@ -1,14 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear = helpers.clear -local eq = helpers.eq -local command = helpers.command -local api = helpers.api -local eval = helpers.eval -local exc_exec = helpers.exc_exec -local pcall_err = helpers.pcall_err -local fn = helpers.fn +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear = n.clear +local eq = t.eq +local command = n.command +local api = n.api +local eval = n.eval +local exc_exec = n.exc_exec +local pcall_err = t.pcall_err +local fn = n.fn local NIL = vim.NIL -local source = helpers.source +local source = n.source describe('string() function', function() before_each(clear) @@ -190,7 +192,7 @@ describe('string() function', function() eval('add(l, l)') -- Regression: the below line used to crash (add returns original list and -- there was error in dumping partials). Tested explicitly in - -- test/unit/api/private_helpers_spec.lua. + -- test/unit/api/private_t_spec.lua. eval('add(l, function("Test1", l))') eq( [=[Vim(echo):E724: unable to correctly dump variable with self-referencing container]=], diff --git a/test/functional/vimscript/system_spec.lua b/test/functional/vimscript/system_spec.lua index d44f68e152..792e4c46c3 100644 --- a/test/functional/vimscript/system_spec.lua +++ b/test/functional/vimscript/system_spec.lua @@ -1,26 +1,20 @@ -- Tests for system() and :! shell. -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() +local Screen = require('test.functional.ui.screen') -local assert_alive = helpers.assert_alive -local testprg = helpers.testprg +local assert_alive = n.assert_alive +local testprg = n.testprg local eq, call, clear, eval, feed_command, feed, api = - helpers.eq, - helpers.call, - helpers.clear, - helpers.eval, - helpers.feed_command, - helpers.feed, - helpers.api -local command = helpers.command -local insert = helpers.insert -local expect = helpers.expect -local exc_exec = helpers.exc_exec -local os_kill = helpers.os_kill -local pcall_err = helpers.pcall_err -local is_os = helpers.is_os - -local Screen = require('test.functional.ui.screen') + t.eq, n.call, n.clear, n.eval, n.feed_command, n.feed, n.api +local command = n.command +local insert = n.insert +local expect = n.expect +local exc_exec = n.exc_exec +local os_kill = n.os_kill +local pcall_err = t.pcall_err +local is_os = t.is_os local function create_file_with_nuls(name) return function() @@ -189,7 +183,7 @@ describe('system()', function() end) it('with powershell', function() - helpers.set_shell_powershell() + n.set_shell_powershell() eq('a\nb\n', eval([[system('Write-Output a b')]])) eq('C:\\\n', eval([[system('cd c:\; (Get-Location).Path')]])) eq('a b\n', eval([[system('Write-Output "a b"')]])) @@ -197,11 +191,11 @@ describe('system()', function() end it('powershell w/ UTF-8 text #13713', function() - if not helpers.has_powershell() then + if not n.has_powershell() then pending('powershell not found', function() end) return end - helpers.set_shell_powershell() + n.set_shell_powershell() eq('ああ\n', eval([[system('Write-Output "ああ"')]])) -- Sanity test w/ default encoding -- * on Windows, expected to default to Western European enc @@ -214,7 +208,7 @@ describe('system()', function() feed(':call system("echo")<cr>') screen:expect([[ ^ | - ~ |*12 + {1:~ }|*12 :call system("echo") | ]]) end) @@ -234,7 +228,7 @@ describe('system()', function() end) it('self and total time recorded separately', function() - local tempfile = helpers.tmpname() + local tempfile = t.tmpname() feed(':function! AlmostNoSelfTime()<cr>') feed('echo system("echo hi")<cr>') @@ -247,11 +241,11 @@ describe('system()', function() feed(':edit ' .. tempfile .. '<cr>') - local command_total_time = tonumber(helpers.fn.split(helpers.fn.getline(7))[2]) - local command_self_time = tonumber(helpers.fn.split(helpers.fn.getline(7))[3]) + local command_total_time = tonumber(n.fn.split(n.fn.getline(7))[2]) + local command_self_time = tonumber(n.fn.split(n.fn.getline(7))[3]) - helpers.neq(nil, command_total_time) - helpers.neq(nil, command_self_time) + t.neq(nil, command_total_time) + t.neq(nil, command_self_time) end) it('`yes` interrupted with CTRL-C', function() @@ -262,14 +256,14 @@ describe('system()', function() ) screen:expect([[ | - ~ |*12 + {1:~ }|*12 ]] .. (is_os('win') and [[ :call system("for /L %I in (1,0,2) do @echo y") |]] or [[ :call system("yes") |]])) feed('foo<c-c>') screen:expect([[ ^ | - ~ |*12 + {1:~ }|*12 Type :qa and press <Enter> to exit Nvim | ]]) end) @@ -283,15 +277,15 @@ describe('system()', function() ) screen:expect([[ | - ~ |*12 + {1:~ }|*12 ]] .. (is_os('win') and [[ :call system("for /L %I in (1,0,2) do @echo y") |]] or [[ :call system("yes") |]])) feed('foo<c-c>') screen:expect([[ ^ | - ~ |*12 - -- INSERT -- | + {1:~ }|*12 + {5:-- INSERT --} | ]]) end) end) @@ -447,7 +441,7 @@ describe('systemlist()', function() feed(':call systemlist("echo")<cr>') screen:expect([[ ^ | - ~ |*12 + {1:~ }|*12 :call systemlist("echo") | ]]) end) @@ -456,13 +450,13 @@ describe('systemlist()', function() feed(':call systemlist("yes | xargs")<cr>') screen:expect([[ | - ~ |*12 + {1:~ }|*12 :call systemlist("yes | xargs") | ]]) feed('<c-c>') screen:expect([[ ^ | - ~ |*12 + {1:~ }|*12 Type :qa and press <Enter> to exit Nvim | ]]) end) @@ -554,11 +548,11 @@ describe('systemlist()', function() end) it('powershell w/ UTF-8 text #13713', function() - if not helpers.has_powershell() then + if not n.has_powershell() then pending('powershell not found', function() end) return end - helpers.set_shell_powershell() + n.set_shell_powershell() eq({ is_os('win') and 'あ\r' or 'あ' }, eval([[systemlist('Write-Output あ')]])) -- Sanity test w/ default encoding -- * on Windows, expected to default to Western European enc @@ -574,7 +568,7 @@ describe('shell :!', function() it(':{range}! with powershell filter/redirect #16271 #19250', function() local screen = Screen.new(500, 8) screen:attach() - local found = helpers.set_shell_powershell(true) + local found = n.set_shell_powershell(true) insert([[ 3 1 @@ -621,12 +615,12 @@ describe('shell :!', function() } end feed('<CR>') - helpers.set_shell_powershell(true) + n.set_shell_powershell(true) feed(':4verbose %w !sort<cr>') screen:expect { any = [[Executing command: .?& { Get%-Content .* | & sort }]], } feed('<CR>') - helpers.expect_exit(command, 'qall!') + n.expect_exit(command, 'qall!') end) end) diff --git a/test/functional/vimscript/timer_spec.lua b/test/functional/vimscript/timer_spec.lua index 046d451888..f075e382bc 100644 --- a/test/functional/vimscript/timer_spec.lua +++ b/test/functional/vimscript/timer_spec.lua @@ -1,12 +1,14 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() local Screen = require('test.functional.ui.screen') -local feed, eq, eval, ok = helpers.feed, helpers.eq, helpers.eval, helpers.ok -local source, async_meths, run = helpers.source, helpers.async_meths, helpers.run -local clear, command, fn = helpers.clear, helpers.command, helpers.fn -local exc_exec = helpers.exc_exec -local api = helpers.api -local load_adjust = helpers.load_adjust -local retry = helpers.retry + +local feed, eq, eval, ok = n.feed, t.eq, n.eval, t.ok +local source, async_meths, run = n.source, n.async_meths, n.run +local clear, command, fn = n.clear, n.command, n.fn +local exc_exec = n.exc_exec +local api = n.api +local load_adjust = n.load_adjust +local retry = t.retry describe('timers', function() before_each(function() diff --git a/test/functional/vimscript/uniq_spec.lua b/test/functional/vimscript/uniq_spec.lua index 8fd4004be4..4e1ef699a4 100644 --- a/test/functional/vimscript/uniq_spec.lua +++ b/test/functional/vimscript/uniq_spec.lua @@ -1,10 +1,11 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local eq = helpers.eq -local clear = helpers.clear -local command = helpers.command -local exc_exec = helpers.exc_exec -local pcall_err = helpers.pcall_err +local eq = t.eq +local clear = n.clear +local command = n.command +local exc_exec = n.exc_exec +local pcall_err = t.pcall_err before_each(clear) diff --git a/test/functional/vimscript/vvar_event_spec.lua b/test/functional/vimscript/vvar_event_spec.lua index 68eda05363..16fa2c9f9f 100644 --- a/test/functional/vimscript/vvar_event_spec.lua +++ b/test/functional/vimscript/vvar_event_spec.lua @@ -1,6 +1,8 @@ -local helpers = require('test.functional.helpers')(after_each) -local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq -local command = helpers.command +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local clear, eval, eq = n.clear, n.eval, t.eq +local command = n.command describe('v:event', function() before_each(clear) it('is empty before any autocommand', function() diff --git a/test/functional/vimscript/wait_spec.lua b/test/functional/vimscript/wait_spec.lua index 50cdb2cfb4..0932bd3593 100644 --- a/test/functional/vimscript/wait_spec.lua +++ b/test/functional/vimscript/wait_spec.lua @@ -1,15 +1,17 @@ -local helpers = require('test.functional.helpers')(after_each) -local call = helpers.call -local clear = helpers.clear -local command = helpers.command -local eval = helpers.eval -local eq = helpers.eq -local feed = helpers.feed -local feed_command = helpers.feed_command -local next_msg = helpers.next_msg -local api = helpers.api -local source = helpers.source -local pcall_err = helpers.pcall_err +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local call = n.call +local clear = n.clear +local command = n.command +local eval = n.eval +local eq = t.eq +local feed = n.feed +local feed_command = n.feed_command +local next_msg = n.next_msg +local api = n.api +local source = n.source +local pcall_err = t.pcall_err before_each(function() clear() @@ -60,7 +62,7 @@ describe('wait()', function() ]]) -- XXX: flaky (#11137) - helpers.retry(nil, nil, function() + t.retry(nil, nil, function() api.nvim_set_var('counter', 0) eq(-1, call('wait', 20, 'Count() >= 5', 99999)) end) diff --git a/test/functional/vimscript/writefile_spec.lua b/test/functional/vimscript/writefile_spec.lua index 051e3794a3..404aceb92b 100644 --- a/test/functional/vimscript/writefile_spec.lua +++ b/test/functional/vimscript/writefile_spec.lua @@ -1,15 +1,16 @@ -local helpers = require('test.functional.helpers')(after_each) +local t = require('test.testutil') +local n = require('test.functional.testnvim')() -local mkdir = helpers.mkdir -local clear = helpers.clear -local eq = helpers.eq -local fn = helpers.fn -local api = helpers.api -local exc_exec = helpers.exc_exec -local read_file = helpers.read_file -local write_file = helpers.write_file -local pcall_err = helpers.pcall_err -local command = helpers.command +local mkdir = t.mkdir +local clear = n.clear +local eq = t.eq +local fn = n.fn +local api = n.api +local exc_exec = n.exc_exec +local read_file = t.read_file +local write_file = t.write_file +local pcall_err = t.pcall_err +local command = n.command local fname = 'Xtest-functional-eval-writefile' local dname = fname .. '.d' diff --git a/test/old/testdir/load.vim b/test/old/testdir/load.vim index 5697ee7304..58ec0b1356 100644 --- a/test/old/testdir/load.vim +++ b/test/old/testdir/load.vim @@ -1,4 +1,4 @@ -" Also used by: test/functional/helpers.lua +" Also used by: test/functional/testnvim.lua function! s:load_factor() abort let timeout = 200 diff --git a/test/old/testdir/setup.vim b/test/old/testdir/setup.vim index 091fb95806..7313a0a162 100644 --- a/test/old/testdir/setup.vim +++ b/test/old/testdir/setup.vim @@ -44,12 +44,16 @@ if exists('s:did_load') endif let s:did_load = 1 -" Clear Nvim default mappings and menus. +" Clear Nvim default user commands, mappings and menus. +comclear mapclear mapclear! aunmenu * tlunmenu * +" Undo the 'grepprg' and 'grepformat' setting in _defaults.lua. +set grepprg& grepformat& + " roughly equivalent to test_setmouse() in Vim func Ntest_setmouse(row, col) call nvim_input_mouse('move', '', '', 0, a:row - 1, a:col - 1) diff --git a/test/old/testdir/test.sh b/test/old/testdir/test.sh index 6e7fa9db18..cb6feb4e68 100644 --- a/test/old/testdir/test.sh +++ b/test/old/testdir/test.sh @@ -58,7 +58,7 @@ check_core_dumps() { } check_logs() { - # Iterate through each log to remove an useless warning. + # Iterate through each log to remove a useless warning. # shellcheck disable=SC2044 for log in $(find "${1}" -type f -name "${2}"); do sed -i "${log}" \ diff --git a/test/old/testdir/test_autocmd.vim b/test/old/testdir/test_autocmd.vim index 4d88573a1f..cdcd68f3d6 100644 --- a/test/old/testdir/test_autocmd.vim +++ b/test/old/testdir/test_autocmd.vim @@ -740,6 +740,27 @@ func Test_WinClosed_switch_tab() %bwipe! endfunc +" This used to trigger WinClosed twice for the same window, and the window's +" buffer was NULL in the second autocommand. +func Test_WinClosed_BufUnload_close_other() + tabnew + let g:tab = tabpagenr() + let g:buf = bufnr() + new + setlocal bufhidden=wipe + augroup test-WinClosed + autocmd BufUnload * ++once exe g:buf .. 'bwipe!' + autocmd WinClosed * call tabpagebuflist(g:tab) + augroup END + close + + unlet g:tab + unlet g:buf + autocmd! test-WinClosed + augroup! test-WinClosed + %bwipe! +endfunc + func s:AddAnAutocmd() augroup vimBarTest au BufReadCmd * echo 'hello' @@ -3476,74 +3497,6 @@ func Test_autocmd_vimgrep() augroup END endfunc -" Test TextChangedI and TextChanged -func Test_Changed_ChangedI() - throw 'Skipped: use test/functional/autocmd/textchanged_spec.lua' - new - call test_override("char_avail", 1) - let [g:autocmd_i, g:autocmd_n] = ['',''] - - func! TextChangedAutocmdI(char) - let g:autocmd_{tolower(a:char)} = a:char .. b:changedtick - endfunc - - augroup Test_TextChanged - au! - au TextChanged <buffer> :call TextChangedAutocmdI('N') - au TextChangedI <buffer> :call TextChangedAutocmdI('I') - augroup END - - call feedkeys("ifoo\<esc>", 'tnix') - " TODO: Test test does not seem to trigger TextChanged autocommand, this - " requires running Vim in a terminal window. - " call assert_equal('N3', g:autocmd_n) - call assert_equal('I3', g:autocmd_i) - - call feedkeys("yyp", 'tnix') - " TODO: Test test does not seem to trigger TextChanged autocommand. - " call assert_equal('N4', g:autocmd_n) - call assert_equal('I3', g:autocmd_i) - - " TextChangedI should only trigger if change was done in Insert mode - let g:autocmd_i = '' - call feedkeys("yypi\<esc>", 'tnix') - call assert_equal('', g:autocmd_i) - - " TextChanged should only trigger if change was done in Normal mode - let g:autocmd_n = '' - call feedkeys("ibar\<esc>", 'tnix') - call assert_equal('', g:autocmd_n) - - " If change is a mix of Normal and Insert modes, TextChangedI should trigger - func s:validate_mixed_textchangedi(keys) - call feedkeys("ifoo\<esc>", 'tnix') - let g:autocmd_i = '' - let g:autocmd_n = '' - call feedkeys(a:keys, 'tnix') - call assert_notequal('', g:autocmd_i) - call assert_equal('', g:autocmd_n) - endfunc - - call s:validate_mixed_textchangedi("o\<esc>") - call s:validate_mixed_textchangedi("O\<esc>") - call s:validate_mixed_textchangedi("ciw\<esc>") - call s:validate_mixed_textchangedi("cc\<esc>") - call s:validate_mixed_textchangedi("C\<esc>") - call s:validate_mixed_textchangedi("s\<esc>") - call s:validate_mixed_textchangedi("S\<esc>") - - - " CleanUp - call test_override("char_avail", 0) - au! TextChanged <buffer> - au! TextChangedI <buffer> - augroup! Test_TextChanged - delfu TextChangedAutocmdI - unlet! g:autocmd_i g:autocmd_n - - bw! -endfunc - func Test_closing_autocmd_window() let lines =<< trim END edit Xa.txt @@ -3569,6 +3522,49 @@ func Test_switch_window_in_autocmd_window() call assert_false(bufexists('Xb.txt')) endfunc +" Test that using the autocommand window doesn't change current directory. +func Test_autocmd_window_cwd() + let saveddir = getcwd() + call mkdir('Xcwd/a/b/c/d', 'pR') + + new Xa.txt + tabnew + new Xb.txt + + tabprev + cd Xcwd + call assert_match('/Xcwd$', getcwd()) + call assert_match('\[global\] .*/Xcwd$', trim(execute('verbose pwd'))) + + autocmd BufEnter Xb.txt lcd ./a/b/c/d + doautoall BufEnter + au! BufEnter + call assert_match('/Xcwd$', getcwd()) + call assert_match('\[global\] .*/Xcwd$', trim(execute('verbose pwd'))) + + tabnext + cd ./a + tcd ./b + lcd ./c + call assert_match('/Xcwd/a/b/c$', getcwd()) + call assert_match('\[window\] .*/Xcwd/a/b/c$', trim(execute('verbose pwd'))) + + autocmd BufEnter Xa.txt call assert_match('Xcwd/a/b/c$', getcwd()) + doautoall BufEnter + au! BufEnter + call assert_match('/Xcwd/a/b/c$', getcwd()) + call assert_match('\[window\] .*/Xcwd/a/b/c$', trim(execute('verbose pwd'))) + bwipe! + call assert_match('/Xcwd/a/b$', getcwd()) + call assert_match('\[tabpage\] .*/Xcwd/a/b$', trim(execute('verbose pwd'))) + bwipe! + call assert_match('/Xcwd/a$', getcwd()) + call assert_match('\[global\] .*/Xcwd/a$', trim(execute('verbose pwd'))) + bwipe! + + call chdir(saveddir) +endfunc + func Test_bufwipeout_changes_window() " This should not crash, but we don't have any expectations about what " happens, changing window in BufWipeout has unpredictable results. @@ -3880,4 +3876,248 @@ func Test_autocmd_creates_new_buffer_on_bufleave() bw c.txt endfunc +" Ensure `expected` was just recently written as a Vim session +func s:assert_session_path(expected) + call assert_equal(a:expected, v:this_session) +endfunc + +" Check for `expected` after a session is written to-disk. +func s:watch_for_session_path(expected) + execute 'autocmd SessionWritePost * ++once execute "call s:assert_session_path(\"' + \ . a:expected + \ . '\")"' +endfunc + +" Ensure v:this_session gets the full session path, if explicitly stated +func Test_explicit_session_absolute_path() + %bwipeout! + + let directory = getcwd() + + let v:this_session = "" + let name = "some_file.vim" + let expected = fnamemodify(name, ":p") + call s:watch_for_session_path(expected) + execute "mksession! " .. expected + + call delete(expected) +endfunc + +" Ensure v:this_session gets the full session path, if explicitly stated +func Test_explicit_session_relative_path() + %bwipeout! + + let directory = getcwd() + + let v:this_session = "" + let name = "some_file.vim" + let expected = fnamemodify(name, ":p") + call s:watch_for_session_path(expected) + execute "mksession! " .. name + + call delete(expected) +endfunc + +" Ensure v:this_session gets the full session path, if not specified +func Test_implicit_session() + %bwipeout! + + let directory = getcwd() + + let v:this_session = "" + let expected = fnamemodify("Session.vim", ":p") + call s:watch_for_session_path(expected) + mksession! + + call delete(expected) +endfunc + +" Test TextChangedI and TextChanged +func Test_Changed_ChangedI() + " Run this test in a terminal because it requires running the main loop. + " Don't use CheckRunVimInTerminal as that will skip the test on Windows. + CheckFeature terminal + CheckNotGui + " Starting a terminal to run Vim is always considered flaky. + let g:test_is_flaky = 1 + + call writefile(['one', 'two', 'three'], 'XTextChangedI2', 'D') + let before =<< trim END + set ttimeout ttimeoutlen=10 + let [g:autocmd_n, g:autocmd_i] = ['',''] + + func TextChangedAutocmd(char) + let g:autocmd_{tolower(a:char)} = a:char .. b:changedtick + call writefile([$'{g:autocmd_n},{g:autocmd_i}'], 'XTextChangedI3') + endfunc + + au TextChanged <buffer> :call TextChangedAutocmd('N') + au TextChangedI <buffer> :call TextChangedAutocmd('I') + + nnoremap <CR> o<Esc> + autocmd SafeState * ++once call writefile([''], 'XTextChangedI3') + END + + call writefile(before, 'Xinit', 'D') + let buf = term_start( + \ GetVimCommandCleanTerm() .. '-n -S Xinit XTextChangedI2', + \ {'term_rows': 10}) + call assert_equal('running', term_getstatus(buf)) + call WaitForAssert({-> assert_true(filereadable('XTextChangedI3'))}) + defer delete('XTextChangedI3') + call WaitForAssert({-> assert_equal([''], readfile('XTextChangedI3'))}) + + " TextChanged should trigger if a mapping enters and leaves Insert mode. + call term_sendkeys(buf, "\<CR>") + call WaitForAssert({-> assert_equal('N4,', readfile('XTextChangedI3')->join("\n"))}) + + call term_sendkeys(buf, "i") + call WaitForAssert({-> assert_match('^-- INSERT --', term_getline(buf, 10))}) + call WaitForAssert({-> assert_equal('N4,', readfile('XTextChangedI3')->join("\n"))}) + " TextChangedI should trigger if change is done in Insert mode. + call term_sendkeys(buf, "f") + call WaitForAssert({-> assert_equal('N4,I5', readfile('XTextChangedI3')->join("\n"))}) + call term_sendkeys(buf, "o") + call WaitForAssert({-> assert_equal('N4,I6', readfile('XTextChangedI3')->join("\n"))}) + call term_sendkeys(buf, "o") + call WaitForAssert({-> assert_equal('N4,I7', readfile('XTextChangedI3')->join("\n"))}) + " TextChanged shouldn't trigger when leaving Insert mode and TextChangedI + " has been triggered. + call term_sendkeys(buf, "\<Esc>") + call WaitForAssert({-> assert_notmatch('^-- INSERT --', term_getline(buf, 10))}) + call WaitForAssert({-> assert_equal('N4,I7', readfile('XTextChangedI3')->join("\n"))}) + + " TextChanged should trigger if change is done in Normal mode. + call term_sendkeys(buf, "yyp") + call WaitForAssert({-> assert_equal('N8,I7', readfile('XTextChangedI3')->join("\n"))}) + + " TextChangedI shouldn't trigger if change isn't done in Insert mode. + call term_sendkeys(buf, "i") + call WaitForAssert({-> assert_match('^-- INSERT --', term_getline(buf, 10))}) + call WaitForAssert({-> assert_equal('N8,I7', readfile('XTextChangedI3')->join("\n"))}) + call term_sendkeys(buf, "\<Esc>") + call WaitForAssert({-> assert_notmatch('^-- INSERT --', term_getline(buf, 10))}) + call WaitForAssert({-> assert_equal('N8,I7', readfile('XTextChangedI3')->join("\n"))}) + + " TextChangedI should trigger if change is a mix of Normal and Insert modes. + func! s:validate_mixed_textchangedi(buf, keys) + let buf = a:buf + call term_sendkeys(buf, "ifoo") + call WaitForAssert({-> assert_match('^-- INSERT --', term_getline(buf, 10))}) + call term_sendkeys(buf, "\<Esc>") + call WaitForAssert({-> assert_notmatch('^-- INSERT --', term_getline(buf, 10))}) + call term_sendkeys(buf, ":let [g:autocmd_n, g:autocmd_i] = ['', '']\<CR>") + call writefile([], 'XTextChangedI3') + call term_sendkeys(buf, a:keys) + call WaitForAssert({-> assert_match('^-- INSERT --', term_getline(buf, 10))}) + call WaitForAssert({-> assert_match('^,I\d\+', readfile('XTextChangedI3')->join("\n"))}) + call term_sendkeys(buf, "\<Esc>") + call WaitForAssert({-> assert_notmatch('^-- INSERT --', term_getline(buf, 10))}) + call WaitForAssert({-> assert_match('^,I\d\+', readfile('XTextChangedI3')->join("\n"))}) + endfunc + + call s:validate_mixed_textchangedi(buf, "o") + call s:validate_mixed_textchangedi(buf, "O") + call s:validate_mixed_textchangedi(buf, "ciw") + call s:validate_mixed_textchangedi(buf, "cc") + call s:validate_mixed_textchangedi(buf, "C") + call s:validate_mixed_textchangedi(buf, "s") + call s:validate_mixed_textchangedi(buf, "S") + + " clean up + bwipe! +endfunc + +" Test that filetype detection still works when SwapExists autocommand sets +" filetype in another buffer. +func Test_SwapExists_set_other_buf_filetype() + let lines =<< trim END + set nocompatible directory=. + filetype on + + let g:buf = bufnr() + new + + func SwapExists() + let v:swapchoice = 'o' + call setbufvar(g:buf, '&filetype', 'text') + endfunc + + func SafeState() + edit <script> + redir! > XftSwapExists.out + set readonly? filetype? + redir END + qall! + endfunc + + autocmd SwapExists * ++nested call SwapExists() + autocmd SafeState * ++nested ++once call SafeState() + END + call writefile(lines, 'XftSwapExists.vim', 'D') + + new XftSwapExists.vim + if RunVim('', '', ' -S XftSwapExists.vim') + call assert_equal( + \ ['', ' readonly', ' filetype=vim'], + \ readfile('XftSwapExists.out')) + call delete('XftSwapExists.out') + endif + + bwipe! +endfunc + +" Test that file is not marked as modified when SwapExists autocommand sets +" 'modified' in another buffer. +func Test_SwapExists_set_other_buf_modified() + let lines =<< trim END + set nocompatible directory=. + + let g:buf = bufnr() + new + + func SwapExists() + let v:swapchoice = 'o' + call setbufvar(g:buf, '&modified', 1) + endfunc + + func SafeState() + edit <script> + redir! > XmodSwapExists.out + set readonly? modified? + redir END + qall! + endfunc + + autocmd SwapExists * ++nested call SwapExists() + autocmd SafeState * ++nested ++once call SafeState() + END + call writefile(lines, 'XmodSwapExists.vim', 'D') + + new XmodSwapExists.vim + if RunVim('', '', ' -S XmodSwapExists.vim') + call assert_equal( + \ ['', ' readonly', 'nomodified'], + \ readfile('XmodSwapExists.out')) + call delete('XmodSwapExists.out') + endif + + bwipe! +endfunc + +func Test_BufEnter_botline() + set hidden + call writefile(range(10), 'Xxx1', 'D') + call writefile(range(20), 'Xxx2', 'D') + edit Xxx1 + edit Xxx2 + au BufEnter Xxx1 call assert_true(line('w$') > 1) + edit Xxx1 + + bwipe! Xxx1 + bwipe! Xxx2 + au! BufEnter Xxx1 + set hidden&vim +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_buffer.vim b/test/old/testdir/test_buffer.vim index b0b9f4f24b..bc8e5eaf7b 100644 --- a/test/old/testdir/test_buffer.vim +++ b/test/old/testdir/test_buffer.vim @@ -133,7 +133,7 @@ func Test_bdelete_cmd() call assert_fails('1,1bdelete 1 2', 'E488:') call assert_fails('bdelete \)', 'E55:') - " Deleting a unlisted and unloaded buffer + " Deleting an unlisted and unloaded buffer edit Xbdelfile1 let bnr = bufnr() set nobuflisted @@ -232,8 +232,6 @@ endfunc " Test for deleting a modified buffer with :confirm func Test_bdel_with_confirm() - " requires a UI to be active - throw 'Skipped: use test/functional/legacy/buffer_spec.lua' CheckUnix CheckNotGui CheckFeature dialog_con @@ -251,26 +249,33 @@ endfunc " Test for editing another buffer from a modified buffer with :confirm func Test_goto_buf_with_confirm() - " requires a UI to be active - throw 'Skipped: use test/functional/legacy/buffer_spec.lua' CheckUnix CheckNotGui CheckFeature dialog_con + " When dialog_con_gui is defined, Vim is compiled with GUI support + " and FEAT_BROWSE will be defined, which causes :confirm :b to + " call do_browse(), which will try to use a GUI file browser, + " which aborts if a GUI is not available. + CheckNotFeature dialog_con_gui new XgotoConf enew call setline(1, 'test') call assert_fails('b XgotoConf', 'E37:') call feedkeys('c', 'L') call assert_fails('confirm b XgotoConf', 'E37:') - call assert_equal(1, &modified) - call assert_equal('', @%) + call assert_true(&modified) + call assert_true(empty(bufname('%'))) call feedkeys('y', 'L') - call assert_fails('confirm b XgotoConf', ['', 'E37:']) - call assert_equal(1, &modified) - call assert_equal('', @%) + confirm b XgotoConf + call assert_equal('XgotoConf', bufname('%')) + call assert_equal(['test'], readfile('Untitled')) + e Untitled + call setline(2, 'test2') call feedkeys('n', 'L') confirm b XgotoConf - call assert_equal('XgotoConf', @%) + call assert_equal('XgotoConf', bufname('%')) + call assert_equal(['test'], readfile('Untitled')) + call delete('Untitled') close! endfunc diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim index b8eb9a2b8b..443539fbfd 100644 --- a/test/old/testdir/test_cmdline.vim +++ b/test/old/testdir/test_cmdline.vim @@ -3907,9 +3907,9 @@ func Test_custom_completion() call assert_fails("call getcompletion('', 'custom')", 'E475:') call assert_fails("call getcompletion('', 'customlist')", 'E475:') - call assert_equal(getcompletion('', 'custom,CustomComplete1'), ['a', 'b', 'c']) - call assert_equal(getcompletion('', 'customlist,CustomComplete2'), ['a', 'b']) - call assert_equal(getcompletion('b', 'customlist,CustomComplete2'), ['b']) + call assert_equal(['a', 'b', 'c'], getcompletion('', 'custom,CustomComplete1')) + call assert_equal(['a', 'b'], getcompletion('', 'customlist,CustomComplete2')) + call assert_equal(['b'], getcompletion('b', 'customlist,CustomComplete2')) delcom Test1 delcom Test2 @@ -3966,4 +3966,22 @@ func Test_buffer_completion() call assert_equal("\"b Xbuf_complete/Foobar.c Xbuf_complete/MyFoobar.c AFoobar.h", @:) endfunc +" :set t_?? +func Test_term_option() + throw 'Skipped: Nvim does not support termcap options' + set wildoptions& + let _cpo = &cpo + set cpo-=C + " There may be more, test only until t_xo + let expected='"set t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm' + \ .. ' t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe' + \ .. ' t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC' + \ .. ' t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr' + \ .. ' t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi' + \ .. ' t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo .*' + call feedkeys(":set t_\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match(expected, @:) + let &cpo = _cpo +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_comments.vim b/test/old/testdir/test_comments.vim index c34b85c42d..67454f477e 100644 --- a/test/old/testdir/test_comments.vim +++ b/test/old/testdir/test_comments.vim @@ -237,6 +237,12 @@ func Test_comment_autoformat() call feedkeys("aone\ntwo\n", 'xt') call assert_equal(['one', 'two', ''], getline(1, '$')) + set backspace=indent,eol,start + %d + call feedkeys("aone \n\<BS>", 'xt') + call assert_equal(['one'], getline(1, '$')) + set backspace& + close! endfunc diff --git a/test/old/testdir/test_compiler.vim b/test/old/testdir/test_compiler.vim index 0b22bafabb..69420b4b7f 100644 --- a/test/old/testdir/test_compiler.vim +++ b/test/old/testdir/test_compiler.vim @@ -62,10 +62,10 @@ func Test_compiler_completion() call assert_match('^"compiler ' .. clist .. '$', @:) call feedkeys(":compiler p\<C-A>\<C-B>\"\<CR>", 'tx') - call assert_match('"compiler pbx perl\( p[a-z]\+\)\+ pylint pyunit', @:) + call assert_match('"compiler pandoc pbx perl\( p[a-z_]\+\)\+ pylint pyunit', @:) call feedkeys(":compiler! p\<C-A>\<C-B>\"\<CR>", 'tx') - call assert_match('"compiler! pbx perl\( p[a-z]\+\)\+ pylint pyunit', @:) + call assert_match('"compiler! pandoc pbx perl\( p[a-z_]\+\)\+ pylint pyunit', @:) endfunc func Test_compiler_error() diff --git a/test/old/testdir/test_conceal.vim b/test/old/testdir/test_conceal.vim index a679061544..df20654f12 100644 --- a/test/old/testdir/test_conceal.vim +++ b/test/old/testdir/test_conceal.vim @@ -169,6 +169,58 @@ func Test_conceal_with_cursorcolumn() call StopVimInTerminal(buf) endfunc +" Check that 'cursorline' and 'wincolor' apply to the whole line in presence +" of wrapped lines containing concealed text. +func Test_conceal_wrapped_cursorline_wincolor() + CheckScreendump + + let code =<< trim [CODE] + call setline(1, 'one one one |hidden| one one one one one one one one') + syntax match test /|hidden|/ conceal + set conceallevel=2 concealcursor=n cursorline + normal! g$ + [CODE] + + call writefile(code, 'XTest_conceal_cul_wcr', 'D') + let buf = RunVimInTerminal('-S XTest_conceal_cul_wcr', {'rows': 4, 'cols': 40}) + call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_01', {}) + + call term_sendkeys(buf, ":set wincolor=ErrorMsg\n") + call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_02', {}) + + call term_sendkeys(buf, ":set nocursorline\n") + call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_03', {}) + + " clean up + call StopVimInTerminal(buf) +endfunc + +" Same as Test_conceal_wrapped_cursorline_wincolor(), but with 'rightleft'. +func Test_conceal_wrapped_cursorline_wincolor_rightleft() + CheckFeature rightleft + CheckScreendump + + let code =<< trim [CODE] + call setline(1, 'one one one |hidden| one one one one one one one one') + syntax match test /|hidden|/ conceal + set conceallevel=2 concealcursor=n cursorline rightleft + normal! g$ + [CODE] + + call writefile(code, 'XTest_conceal_cul_wcr_rl', 'D') + let buf = RunVimInTerminal('-S XTest_conceal_cul_wcr_rl', {'rows': 4, 'cols': 40}) + call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_rl_01', {}) + + call term_sendkeys(buf, ":set wincolor=ErrorMsg\n") + call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_rl_02', {}) + + call term_sendkeys(buf, ":set nocursorline\n") + call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_rl_03', {}) + + " clean up + call StopVimInTerminal(buf) +endfunc + func Test_conceal_resize_term() CheckScreendump @@ -336,77 +388,290 @@ func Test_conceal_eol() endfunc func Test_conceal_mouse_click() - enew! + call NewWindow(10, 40) set mouse=a setlocal conceallevel=2 concealcursor=nc syn match Concealed "this" conceal hi link Concealed Search - call setline(1, 'conceal this click here') - redraw - call assert_equal(['conceal click here '], ScreenLines(1, 20)) - - " click on the space between "this" and "click" puts cursor there - call Ntest_setmouse(1, 9) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 13, 0, 13], getcurpos()) - " click on 'h' of "here" puts cursor there - call Ntest_setmouse(1, 16) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 20, 0, 20], getcurpos()) - " click on 'e' of "here" puts cursor there - call Ntest_setmouse(1, 19) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 23, 0, 23], getcurpos()) - " click after end of line puts cursor on 'e' without 'virtualedit' - call Ntest_setmouse(1, 20) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 23, 0, 24], getcurpos()) - call Ntest_setmouse(1, 21) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 23, 0, 25], getcurpos()) - call Ntest_setmouse(1, 22) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 23, 0, 26], getcurpos()) - call Ntest_setmouse(1, 31) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 23, 0, 35], getcurpos()) - call Ntest_setmouse(1, 32) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 23, 0, 36], getcurpos()) - - set virtualedit=all - redraw - " click on the space between "this" and "click" puts cursor there - call Ntest_setmouse(1, 9) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 13, 0, 13], getcurpos()) - " click on 'h' of "here" puts cursor there - call Ntest_setmouse(1, 16) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 20, 0, 20], getcurpos()) - " click on 'e' of "here" puts cursor there - call Ntest_setmouse(1, 19) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 23, 0, 23], getcurpos()) - " click after end of line puts cursor there without 'virtualedit' - call Ntest_setmouse(1, 20) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 24, 0, 24], getcurpos()) - call Ntest_setmouse(1, 21) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 24, 1, 25], getcurpos()) - call Ntest_setmouse(1, 22) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 24, 2, 26], getcurpos()) - call Ntest_setmouse(1, 31) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 24, 11, 35], getcurpos()) - call Ntest_setmouse(1, 32) - call feedkeys("\<LeftMouse>", "tx") - call assert_equal([0, 1, 24, 12, 36], getcurpos()) - - bwipe! - set mouse& virtualedit& + + " Test with both 'nocursorline' and 'cursorline', as they use two different + " code paths to set virtual columns for the cells to clear. + for cul in [v:false, v:true] + let &l:cursorline = cul + + call setline(1, 'conceal this click here') + call assert_equal([ + \ 'conceal click here ', + \ ], ScreenLines(1, 40)) + + " Click on the space between "this" and "click" puts cursor there. + call Ntest_setmouse(1, 9) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 13, 0, 13], getcurpos()) + " Click on 'h' of "here" puts cursor there. + call Ntest_setmouse(1, 16) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 20, 0, 20], getcurpos()) + " Click on 'e' of "here" puts cursor there. + call Ntest_setmouse(1, 19) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 23, 0, 23], getcurpos()) + " Click after end of line puts cursor on 'e' without 'virtualedit'. + call Ntest_setmouse(1, 20) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 23, 0, 24], getcurpos()) + call Ntest_setmouse(1, 21) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 23, 0, 25], getcurpos()) + call Ntest_setmouse(1, 22) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 23, 0, 26], getcurpos()) + call Ntest_setmouse(1, 31) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 23, 0, 35], getcurpos()) + call Ntest_setmouse(1, 32) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 23, 0, 36], getcurpos()) + + set virtualedit=all + redraw + " Click on the space between "this" and "click" puts cursor there. + call Ntest_setmouse(1, 9) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 13, 0, 13], getcurpos()) + " Click on 'h' of "here" puts cursor there. + call Ntest_setmouse(1, 16) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 20, 0, 20], getcurpos()) + " Click on 'e' of "here" puts cursor there. + call Ntest_setmouse(1, 19) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 23, 0, 23], getcurpos()) + " Click after end of line puts cursor there with 'virtualedit'. + call Ntest_setmouse(1, 20) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 0, 24], getcurpos()) + call Ntest_setmouse(1, 21) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 1, 25], getcurpos()) + call Ntest_setmouse(1, 22) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 2, 26], getcurpos()) + call Ntest_setmouse(1, 31) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 11, 35], getcurpos()) + call Ntest_setmouse(1, 32) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 12, 36], getcurpos()) + " Behavior should also be the same with 'colorcolumn'. + setlocal colorcolumn=30 + redraw + call Ntest_setmouse(1, 31) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 11, 35], getcurpos()) + call Ntest_setmouse(1, 32) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 12, 36], getcurpos()) + setlocal colorcolumn& + + if has('rightleft') + setlocal rightleft + call assert_equal([ + \ ' ereh kcilc laecnoc', + \ ], ScreenLines(1, 40)) + " Click on the space between "this" and "click" puts cursor there. + call Ntest_setmouse(1, 41 - 9) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 13, 0, 13], getcurpos()) + " Click on 'h' of "here" puts cursor there. + call Ntest_setmouse(1, 41 - 16) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 20, 0, 20], getcurpos()) + " Click on 'e' of "here" puts cursor there. + call Ntest_setmouse(1, 41 - 19) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 23, 0, 23], getcurpos()) + " Click after end of line puts cursor there with 'virtualedit'. + call Ntest_setmouse(1, 41 - 20) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 0, 24], getcurpos()) + call Ntest_setmouse(1, 41 - 21) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 1, 25], getcurpos()) + call Ntest_setmouse(1, 41 - 22) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 2, 26], getcurpos()) + call Ntest_setmouse(1, 41 - 31) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 11, 35], getcurpos()) + call Ntest_setmouse(1, 41 - 32) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 24, 12, 36], getcurpos()) + setlocal rightleft& + endif + + set virtualedit& + + " Test with a wrapped line. + call setline(1, ['conceal this click here']->repeat(3)->join()) + call assert_equal([ + \ 'conceal click here conceal cli ', + \ 'ck here conceal click here ', + \ ], ScreenLines([1, 2], 40)) + " Click on boguscols puts cursor on the last char of a screen line. + for col in range(33, 40) + call Ntest_setmouse(1, col) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 40, 0, 40], getcurpos()) + endfor + + " Also test with the last char of a screen line concealed. + setlocal number signcolumn=yes + call assert_equal([ + \ ' 1 conceal click here conceal ', + \ ' click here conceal click h ', + \ ' ere ', + \ ], ScreenLines([1, 3], 40)) + call Ntest_setmouse(1, 34) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 32, 0, 32], getcurpos()) + call Ntest_setmouse(2, 7) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 37, 0, 37], getcurpos()) + " Click on boguscols puts cursor on the last char of a screen line. + for col in range(35, 40) + call Ntest_setmouse(1, col) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 34, 0, 34], getcurpos()) + call Ntest_setmouse(2, col) + call feedkeys("\<LeftMouse>", "tx") + call assert_equal([0, 1, 68, 0, 68], getcurpos()) + endfor + setlocal number& signcolumn& + endfor + + call CloseWindow() + set mouse& +endfunc + +" Test that cursor is drawn at the correct column when it is after end of the +" line with 'virtualedit' and concealing. +func Run_test_conceal_virtualedit_after_eol(wrap) + let code =<< trim eval [CODE] + let &wrap = {a:wrap} + call setline(1, 'abcdefgh|hidden|ijklmnpop') + syntax match test /|hidden|/ conceal + set conceallevel=2 concealcursor=n virtualedit=all + normal! $ + [CODE] + call writefile(code, 'XTest_conceal_ve_after_eol', 'D') + let buf = RunVimInTerminal('-S XTest_conceal_ve_after_eol', {'rows': 3}) + call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_1', {}) + call term_sendkeys(buf, "l") + call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_2', {}) + call term_sendkeys(buf, "l") + call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_3', {}) + call term_sendkeys(buf, "l") + call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_4', {}) + call term_sendkeys(buf, "rr") + call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_5', {}) + + " clean up + call StopVimInTerminal(buf) +endfunc + +func Test_conceal_virtualedit_after_eol() + CheckScreendump + + call Run_test_conceal_virtualedit_after_eol(1) + call Run_test_conceal_virtualedit_after_eol(0) +endfunc + +" Same as Run_test_conceal_virtualedit_after_eol(), but with 'rightleft'. +func Run_test_conceal_virtualedit_after_eol_rightleft(wrap) + let code =<< trim eval [CODE] + let &wrap = {a:wrap} + call setline(1, 'abcdefgh|hidden|ijklmnpop') + syntax match test /|hidden|/ conceal + set conceallevel=2 concealcursor=n virtualedit=all rightleft + normal! $ + [CODE] + call writefile(code, 'XTest_conceal_ve_after_eol_rl', 'D') + let buf = RunVimInTerminal('-S XTest_conceal_ve_after_eol_rl', {'rows': 3}) + call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_rl_1', {}) + call term_sendkeys(buf, "h") + call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_rl_2', {}) + call term_sendkeys(buf, "h") + call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_rl_3', {}) + call term_sendkeys(buf, "h") + call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_rl_4', {}) + call term_sendkeys(buf, "rr") + call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_rl_5', {}) + + " clean up + call StopVimInTerminal(buf) +endfunc + +func Test_conceal_virtualedit_after_eol_rightleft() + CheckFeature rightleft + CheckScreendump + + call Run_test_conceal_virtualedit_after_eol_rightleft(1) + call Run_test_conceal_virtualedit_after_eol_rightleft(0) +endfunc + +" Test that cursor position is correct when double-width chars are concealed. +func Run_test_conceal_double_width(wrap) + let code =<< trim eval [CODE] + let &wrap = {a:wrap} + call setline(1, ['aaaaa口=口bbbbb口=口ccccc', 'foobar']) + syntax match test /口=口/ conceal cchar=β + set conceallevel=2 concealcursor=n colorcolumn=30 + normal! $ + [CODE] + call writefile(code, 'XTest_conceal_double_width', 'D') + let buf = RunVimInTerminal('-S XTest_conceal_double_width', {'rows': 4}) + call VerifyScreenDump(buf, 'Test_conceal_double_width_1', {}) + call term_sendkeys(buf, "gM") + call VerifyScreenDump(buf, 'Test_conceal_double_width_2', {}) + call term_sendkeys(buf, ":set conceallevel=3\<CR>") + call VerifyScreenDump(buf, 'Test_conceal_double_width_3', {}) + call term_sendkeys(buf, "$") + call VerifyScreenDump(buf, 'Test_conceal_double_width_4', {}) + + " clean up + call StopVimInTerminal(buf) +endfunc + +func Test_conceal_double_width() + CheckScreendump + + call Run_test_conceal_double_width(1) + call Run_test_conceal_double_width(0) +endfunc + +" Test that line wrapping is correct when double-width chars are concealed. +func Test_conceal_double_width_wrap() + CheckScreendump + + let code =<< trim [CODE] + call setline(1, 'aaaaaaaaaa口=口bbbbbbbbbb口=口cccccccccc') + syntax match test /口=口/ conceal cchar=β + set conceallevel=2 concealcursor=n + normal! $ + [CODE] + call writefile(code, 'XTest_conceal_double_width_wrap', 'D') + let buf = RunVimInTerminal('-S XTest_conceal_double_width_wrap', {'rows': 4, 'cols': 20}) + call VerifyScreenDump(buf, 'Test_conceal_double_width_wrap_1', {}) + call term_sendkeys(buf, "gM") + call VerifyScreenDump(buf, 'Test_conceal_double_width_wrap_2', {}) + call term_sendkeys(buf, ":set conceallevel=3\<CR>") + call VerifyScreenDump(buf, 'Test_conceal_double_width_wrap_3', {}) + call term_sendkeys(buf, "$") + call VerifyScreenDump(buf, 'Test_conceal_double_width_wrap_4', {}) + + " clean up + call StopVimInTerminal(buf) endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_diffmode.vim b/test/old/testdir/test_diffmode.vim index 9a3e006430..71483b7469 100644 --- a/test/old/testdir/test_diffmode.vim +++ b/test/old/testdir/test_diffmode.vim @@ -3,6 +3,7 @@ source shared.vim source screendump.vim source check.vim +source view_util.vim func Test_diff_fold_sync() enew! @@ -1631,36 +1632,39 @@ endfunc func Test_diff_scroll_many_filler() 20new vnew - call setline(1, ['^^^', '^^^', '$$$', '$$$']) + call setline(1, range(1, 40)) diffthis setlocal scrolloff=0 wincmd p - call setline(1, ['^^^', '^^^'] + repeat(['###'], 41) + ['$$$', '$$$']) + call setline(1, range(1, 20)->reverse() + ['###']->repeat(41) + range(21, 40)->reverse()) diffthis setlocal scrolloff=0 wincmd p redraw " Note: need a redraw after each scroll, otherwise the test always passes. - normal! G - redraw - call assert_equal(3, winsaveview().topline) - call assert_equal(18, winsaveview().topfill) - exe "normal! \<C-B>" - redraw - call assert_equal(3, winsaveview().topline) - call assert_equal(19, winsaveview().topfill) - exe "normal! \<C-B>" - redraw - call assert_equal(2, winsaveview().topline) - call assert_equal(0, winsaveview().topfill) - exe "normal! \<C-B>" - redraw - call assert_equal(1, winsaveview().topline) - call assert_equal(0, winsaveview().topfill) + for _ in range(2) + normal! G + redraw + call assert_equal(40, winsaveview().topline) + call assert_equal(19, winsaveview().topfill) + exe "normal! \<C-B>" + redraw + call assert_equal(22, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + exe "normal! \<C-B>" + redraw + call assert_equal(4, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + exe "normal! \<C-B>" + redraw + call assert_equal(1, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + set smoothscroll + endfor - bwipe! - bwipe! + set smoothscroll& + %bwipe! endfunc " This was trying to update diffs for a buffer being closed @@ -1702,4 +1706,89 @@ func Test_diff_put_and_undo() endfunc +func Test_diff_toggle_wrap_skipcol_leftcol() + 61vnew + call setline(1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.') + 30vnew + call setline(1, 'ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.') + let win1 = win_getid() + setlocal smoothscroll + exe "normal! $\<C-E>" + wincmd l + let win2 = win_getid() + setlocal smoothscroll + exe "normal! $\<C-E>" + call assert_equal([ + \ '<<<sadipscing elitr, sed diam |<<<tetur sadipscing elitr, sed|', + \ 'nonumy eirmod tempor invidunt | diam nonumy eirmod tempor inv|', + \ 'ut labore et dolore magna aliq|idunt ut labore et dolore magn|', + \ 'uyam erat, sed diam voluptua. |a aliquyam erat, sed diam volu|', + \ '~ |ptua. |', + \ ], ScreenLines([1, 5], 62)) + call assert_equal({'col': 29, 'row': 4, 'endcol': 29, 'curscol': 29}, + \ screenpos(win1, line('.', win1), col('.', win1))) + call assert_equal({'col': 36, 'row': 5, 'endcol': 36, 'curscol': 36}, + \ screenpos(win2, line('.', win2), col('.', win2))) + + wincmd h + diffthis + wincmd l + diffthis + normal! 0 + call assert_equal([ + \ ' ipsum dolor sit amet, conset| Lorem ipsum dolor sit amet, |', + \ '~ |~ |', + \ ], ScreenLines([1, 2], 62)) + call assert_equal({'col': 3, 'row': 1, 'endcol': 3, 'curscol': 3}, + \ screenpos(win1, line('.', win1), col('.', win1))) + call assert_equal({'col': 34, 'row': 1, 'endcol': 34, 'curscol': 34}, + \ screenpos(win2, line('.', win2), col('.', win2))) + + normal! $ + call assert_equal([ + \ ' voluptua. | diam voluptua. |', + \ '~ |~ |', + \ ], ScreenLines([1, 2], 62)) + call assert_equal({'col': 11, 'row': 1, 'endcol': 11, 'curscol': 11}, + \ screenpos(win1, line('.', win1), col('.', win1))) + call assert_equal({'col': 48, 'row': 1, 'endcol': 48, 'curscol': 48}, + \ screenpos(win2, line('.', win2), col('.', win2))) + + diffoff! + call assert_equal([ + \ 'ipsum dolor sit amet, consetet|Lorem ipsum dolor sit amet, co|', + \ 'ur sadipscing elitr, sed diam |nsetetur sadipscing elitr, sed|', + \ 'nonumy eirmod tempor invidunt | diam nonumy eirmod tempor inv|', + \ 'ut labore et dolore magna aliq|idunt ut labore et dolore magn|', + \ 'uyam erat, sed diam voluptua. |a aliquyam erat, sed diam volu|', + \ '~ |ptua. |', + \ ], ScreenLines([1, 6], 62)) + call assert_equal({'col': 29, 'row': 5, 'endcol': 29, 'curscol': 29}, + \ screenpos(win1, line('.', win1), col('.', win1))) + call assert_equal({'col': 36, 'row': 6, 'endcol': 36, 'curscol': 36}, + \ screenpos(win2, line('.', win2), col('.', win2))) + + bwipe! + bwipe! +endfunc + +" Ctrl-D reveals filler lines below the last line in the buffer. +func Test_diff_eob_halfpage() + new + call setline(1, ['']->repeat(10) + ['a']) + diffthis + new + call setline(1, ['']->repeat(3) + ['a', 'b']) + diffthis + resize 5 + wincmd j + resize 5 + norm G + call assert_equal(7, line('w0')) + exe "norm! \<C-D>" + call assert_equal(8, line('w0')) + + %bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_digraph.vim b/test/old/testdir/test_digraph.vim index 8229248f7f..8fbcd4d8ca 100644 --- a/test/old/testdir/test_digraph.vim +++ b/test/old/testdir/test_digraph.vim @@ -539,6 +539,8 @@ func Test_digraph_set_function() call assert_fails('call digraph_set("あ", "あ")', 'E1214: Digraph must be just two characters: あ') call assert_fails('call digraph_set("aa", "ああ")', 'E1215: Digraph must be one character: ああ') call assert_fails('call digraph_set("aa", "か" .. nr2char(0x3099))', 'E1215: Digraph must be one character: か' .. nr2char(0x3099)) + call assert_fails('call digraph_set(v:_null_string, "い")', 'E1214: Digraph must be just two characters') + call assert_fails('call digraph_set("aa", 0z10)', 'E976: Using a Blob as a String') bwipe! endfunc @@ -556,6 +558,8 @@ func Test_digraph_get_function() call assert_equal('う', digraph_get(' ')) call assert_fails('call digraph_get("aaa")', 'E1214: Digraph must be just two characters: aaa') call assert_fails('call digraph_get("b")', 'E1214: Digraph must be just two characters: b') + call assert_fails('call digraph_get(v:_null_string)', 'E1214: Digraph must be just two characters:') + call assert_fails('call digraph_get(0z10)', 'E976: Using a Blob as a String') endfunc func Test_digraph_get_function_encode() @@ -580,8 +584,12 @@ func Test_digraph_setlist_function() call assert_equal('く', digraph_get('bb')) call assert_fails('call digraph_setlist([[]])', 'E1216:') - call assert_fails('call digraph_setlist([["aa", "b", "cc"]])', '1216:') + call assert_fails('call digraph_setlist([["aa", "b", "cc"]])', 'E1216:') call assert_fails('call digraph_setlist([["あ", "あ"]])', 'E1214: Digraph must be just two characters: あ') + call assert_fails('call digraph_setlist([v:_null_list])', 'E1216:') + call assert_fails('call digraph_setlist({})', 'E1216:') + call assert_fails('call digraph_setlist([{}])', 'E1216:') + call assert_true(digraph_setlist(v:_null_list)) endfunc func Test_digraph_getlist_function() @@ -589,13 +597,15 @@ func Test_digraph_getlist_function() call digraph_setlist([['aa', 'き'], ['bb', 'く']]) for pair in digraph_getlist(1) - call assert_equal(digraph_get(pair[0]), pair[1]) + call assert_equal(pair[1], digraph_get(pair[0])) endfor " We don't know how many digraphs are registered before, so check the number " of digraphs returned. call assert_equal(digraph_getlist()->len(), digraph_getlist(0)->len()) - call assert_notequal((digraph_getlist()->len()), digraph_getlist(1)->len()) + call assert_notequal(digraph_getlist()->len(), digraph_getlist(1)->len()) + + call assert_fails('call digraph_getlist(0z12)', 'E974: Using a Blob as a Number') endfunc diff --git a/test/old/testdir/test_edit.vim b/test/old/testdir/test_edit.vim index 67143ab524..57ff63f26d 100644 --- a/test/old/testdir/test_edit.vim +++ b/test/old/testdir/test_edit.vim @@ -6,8 +6,6 @@ endif source check.vim source screendump.vim - -" Needed for testing basic rightleft: Test_edit_rightleft source view_util.vim " Needs to come first until the bug in getchar() is @@ -1320,9 +1318,9 @@ func Test_edit_PAGEUP_PAGEDOWN() call feedkeys("A\<PageUp>\<esc>", 'tnix') call assert_equal([0, 13, 1, 0], getpos('.')) call feedkeys("A\<PageUp>\<esc>", 'tnix') - call assert_equal([0, 5, 1, 0], getpos('.')) + call assert_equal([0, 10, 1, 0], getpos('.')) call feedkeys("A\<PageUp>\<esc>", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) " <S-Up> is the same as <PageUp> " <S-Down> is the same as <PageDown> call cursor(1, 1) @@ -1343,9 +1341,9 @@ func Test_edit_PAGEUP_PAGEDOWN() call feedkeys("A\<S-Up>\<esc>", 'tnix') call assert_equal([0, 13, 1, 0], getpos('.')) call feedkeys("A\<S-Up>\<esc>", 'tnix') - call assert_equal([0, 5, 1, 0], getpos('.')) + call assert_equal([0, 10, 1, 0], getpos('.')) call feedkeys("A\<S-Up>\<esc>", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) set nostartofline call cursor(30, 11) norm! zt @@ -1356,9 +1354,9 @@ func Test_edit_PAGEUP_PAGEDOWN() call feedkeys("A\<PageUp>\<esc>", 'tnix') call assert_equal([0, 13, 11, 0], getpos('.')) call feedkeys("A\<PageUp>\<esc>", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) call feedkeys("A\<PageUp>\<esc>", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) call cursor(1, 1) call feedkeys("A\<PageDown>\<esc>", 'tnix') call assert_equal([0, 9, 11, 0], getpos('.')) @@ -1381,9 +1379,9 @@ func Test_edit_PAGEUP_PAGEDOWN() call feedkeys("A\<S-Up>\<esc>", 'tnix') call assert_equal([0, 13, 11, 0], getpos('.')) call feedkeys("A\<S-Up>\<esc>", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) call feedkeys("A\<S-Up>\<esc>", 'tnix') - call assert_equal([0, 5, 11, 0], getpos('.')) + call assert_equal([0, 10, 11, 0], getpos('.')) call cursor(1, 1) call feedkeys("A\<S-Down>\<esc>", 'tnix') call assert_equal([0, 9, 11, 0], getpos('.')) @@ -1985,8 +1983,8 @@ func Test_edit_ctrl_r_failed() let buf = RunVimInTerminal('', #{rows: 6, cols: 60}) - " trying to insert a dictionary produces an error - call term_sendkeys(buf, "i\<C-R>={}\<CR>") + " trying to insert a blob produces an error + call term_sendkeys(buf, "i\<C-R>=0z\<CR>") " ending Insert mode should put the cursor back on the ':' call term_sendkeys(buf, ":\<Esc>") @@ -2067,7 +2065,10 @@ func Test_edit_revins() call setline(1, 'one two three') exe "normal! wi\nfour" call assert_equal(['one two three', 'ruof'], getline(1, '$')) - set revins& + set backspace=indent,eol,start + exe "normal! ggA\<BS>:" + call assert_equal(['one two three:ruof'], getline(1, '$')) + set revins& backspace& bw! endfunc @@ -2153,4 +2154,133 @@ func Test_edit_Ctrl_RSB() bwipe! endfunc +func s:check_backspace(expected) + let g:actual = [] + inoremap <buffer> <F2> <Cmd>let g:actual += [getline('.')]<CR> + set backspace=indent,eol,start + + exe "normal i" .. repeat("\<BS>\<F2>", len(a:expected)) + call assert_equal(a:expected, g:actual) + + set backspace& + iunmap <buffer> <F2> + unlet g:actual +endfunc + +" Test that backspace works with 'smarttab' and mixed Tabs and spaces. +func Test_edit_backspace_smarttab_mixed() + set smarttab + call NewWindow(1, 30) + setlocal tabstop=4 shiftwidth=4 + + call setline(1, "\t \t \t a") + normal! $ + call s:check_backspace([ + \ "\t \t \ta", + \ "\t \t a", + \ "\t \t a", + \ "\t \ta", + \ "\t a", + \ "\ta", + \ "a", + \ ]) + + call CloseWindow() + set smarttab& +endfunc + +" Test that backspace works with 'smarttab' and 'varsofttabstop'. +func Test_edit_backspace_smarttab_varsofttabstop() + CheckFeature vartabs + + set smarttab + call NewWindow(1, 30) + setlocal tabstop=8 varsofttabstop=6,2,5,3 + + call setline(1, "a\t \t a") + normal! $ + call s:check_backspace([ + \ "a\t \ta", + \ "a\t a", + \ "a\ta", + \ "a a", + \ "aa", + \ "a", + \ ]) + + call CloseWindow() + set smarttab& +endfunc + +" Test that backspace works with 'smarttab' when a Tab is shown as "^I". +func Test_edit_backspace_smarttab_list() + set smarttab + call NewWindow(1, 30) + setlocal tabstop=4 shiftwidth=4 list listchars= + + call setline(1, "\t \t \t a") + normal! $ + call s:check_backspace([ + \ "\t \t a", + \ "\t \t a", + \ "\t \ta", + \ "\t a", + \ "a", + \ ]) + + call CloseWindow() + set smarttab& +endfunc + +" Test that backspace works with 'smarttab' and 'breakindent'. +func Test_edit_backspace_smarttab_breakindent() + CheckFeature linebreak + + set smarttab + call NewWindow(3, 17) + setlocal tabstop=4 shiftwidth=4 breakindent breakindentopt=min:5 + + call setline(1, "\t \t \t a") + normal! $ + call s:check_backspace([ + \ "\t \t \ta", + \ "\t \t a", + \ "\t \t a", + \ "\t \ta", + \ "\t a", + \ "\ta", + \ "a", + \ ]) + + call CloseWindow() + set smarttab& +endfunc + +" Test that backspace works with 'smarttab' and virtual text. +func Test_edit_backspace_smarttab_virtual_text() + CheckFeature textprop + + set smarttab + call NewWindow(1, 50) + setlocal tabstop=4 shiftwidth=4 + + call setline(1, "\t \t \t a") + call prop_type_add('theprop', {}) + call prop_add(1, 3, {'type': 'theprop', 'text': 'text'}) + normal! $ + call s:check_backspace([ + \ "\t \t \ta", + \ "\t \t a", + \ "\t \t a", + \ "\t \ta", + \ "\t a", + \ "\ta", + \ "a", + \ ]) + + call CloseWindow() + call prop_type_delete('theprop') + set smarttab& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_excmd.vim b/test/old/testdir/test_excmd.vim index b7356c22fa..4a780078fd 100644 --- a/test/old/testdir/test_excmd.vim +++ b/test/old/testdir/test_excmd.vim @@ -3,6 +3,7 @@ source check.vim source shared.vim source term_util.vim +source screendump.vim func Test_ex_delete() new @@ -758,4 +759,19 @@ func Test_ex_address_range_overflow() call assert_fails(':--+foobar', 'E492:') endfunc +func Test_drop_modified_file() + CheckScreendump + let lines =<< trim END + call setline(1, 'The quick brown fox jumped over the lazy dogs') + END + call writefile([''], 'Xdrop_modified.txt', 'D') + call writefile(lines, 'Xtest_drop_modified', 'D') + let buf = RunVimInTerminal('-S Xtest_drop_modified Xdrop_modified.txt', {'rows': 10,'columns': 40}) + call term_sendkeys(buf, ":drop Xdrop_modified.txt\<CR>") + call VerifyScreenDump(buf, 'Test_drop_modified_1', {}) + + " clean up + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_execute_func.vim b/test/old/testdir/test_execute_func.vim index 2edae39b8f..ec8ed160c3 100644 --- a/test/old/testdir/test_execute_func.vim +++ b/test/old/testdir/test_execute_func.vim @@ -3,6 +3,7 @@ source view_util.vim source check.vim source vim9.vim +source term_util.vim func NestedEval() let nested = execute('echo "nested\nlines"') @@ -177,6 +178,27 @@ func Test_win_execute_visual_redraw() bwipe! endfunc +func Test_win_execute_on_startup() + CheckRunVimInTerminal + + let lines =<< trim END + vim9script + [repeat('x', &columns)]->writefile('Xfile1') + silent tabedit Xfile2 + var id = win_getid() + silent tabedit Xfile3 + autocmd VimEnter * win_execute(id, 'close') + END + call writefile(lines, 'XwinExecute') + let buf = RunVimInTerminal('-p Xfile1 -Nu XwinExecute', {}) + + " this was crashing on exit with EXITFREE defined + call StopVimInTerminal(buf) + + call delete('XwinExecute') + call delete('Xfile1') +endfunc + func Test_execute_cmd_with_null() call assert_equal("", execute(v:_null_string)) call assert_equal("", execute(v:_null_list)) @@ -190,4 +212,28 @@ func Test_execute_cmd_with_null() endif endfunc +func Test_win_execute_tabpagewinnr() + belowright split + tab split + belowright split + call assert_equal(2, tabpagewinnr(1)) + + tabprevious + wincmd p + call assert_equal(1, tabpagenr()) + call assert_equal(1, tabpagewinnr(1)) + call assert_equal(2, tabpagewinnr(2)) + + call win_execute(win_getid(1, 2), + \ 'call assert_equal(2, tabpagenr())' + \ .. '| call assert_equal(1, tabpagewinnr(1))' + \ .. '| call assert_equal(1, tabpagewinnr(2))') + + call assert_equal(1, tabpagenr()) + call assert_equal(1, tabpagewinnr(1)) + call assert_equal(2, tabpagewinnr(2)) + + %bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_expand.vim b/test/old/testdir/test_expand.vim index cd537f4ea1..24df156386 100644 --- a/test/old/testdir/test_expand.vim +++ b/test/old/testdir/test_expand.vim @@ -45,12 +45,25 @@ endfunc func Test_expand_tilde_filename() split ~ - call assert_equal('~', expand('%')) + call assert_equal('~', expand('%')) call assert_notequal(expand('%:p'), expand('~/')) - call assert_match('\~', expand('%:p')) + call assert_match('\~', expand('%:p')) bwipe! endfunc +func Test_expand_env_pathsep() + let $FOO = './foo' + call assert_equal('./foo/bar', expand('$FOO/bar')) + let $FOO = './foo/' + call assert_equal('./foo/bar', expand('$FOO/bar')) + let $FOO = 'C:' + call assert_equal('C:/bar', expand('$FOO/bar')) + let $FOO = 'C:/' + call assert_equal('C:/bar', expand('$FOO/bar')) + + unlet $FOO +endfunc + func Test_expandcmd() let $FOO = 'Test' call assert_equal('e x/Test/y', expandcmd('e x/$FOO/y')) diff --git a/test/old/testdir/test_expr.vim b/test/old/testdir/test_expr.vim index d316e63818..8871e7e7d7 100644 --- a/test/old/testdir/test_expr.vim +++ b/test/old/testdir/test_expr.vim @@ -904,6 +904,22 @@ func Test_string_interp() endif call assert_equal(0, tmp) + #" Dict interpolation + VAR d = {'a': 10, 'b': [1, 2]} + call assert_equal("{'a': 10, 'b': [1, 2]}", $'{d}') + VAR emptydict = {} + call assert_equal("a{}b", $'a{emptydict}b') + VAR nulldict = v:_null_dict + call assert_equal("a{}b", $'a{nulldict}b') + + #" List interpolation + VAR l = ['a', 'b', 'c'] + call assert_equal("['a', 'b', 'c']", $'{l}') + VAR emptylist = [] + call assert_equal("a[]b", $'a{emptylist}b') + VAR nulllist = v:_null_list + call assert_equal("a[]b", $'a{nulllist}b') + #" Stray closing brace. call assert_fails('echo $"moo}"', 'E1278:') #" Undefined variable in expansion. diff --git a/test/old/testdir/test_filechanged.vim b/test/old/testdir/test_filechanged.vim index fef0eb732f..a8fc78ff90 100644 --- a/test/old/testdir/test_filechanged.vim +++ b/test/old/testdir/test_filechanged.vim @@ -11,8 +11,12 @@ func Test_FileChangedShell_reload() new Xchanged_r call setline(1, 'reload this') write - " Need to wait until the timestamp would change by at least a second. - sleep 2 + " Need to wait until the timestamp would change. + if has('nanotime') + sleep 10m + else + sleep 2 + endif silent !echo 'extra line' >>Xchanged_r checktime call assert_equal('changed', g:reason) @@ -50,7 +54,11 @@ func Test_FileChangedShell_reload() call assert_equal('new line', getline(1)) " Only time changed - sleep 2 + if has('nanotime') + sleep 10m + else + sleep 2 + endif silent !touch Xchanged_r let g:reason = '' checktime @@ -65,7 +73,11 @@ func Test_FileChangedShell_reload() call setline(2, 'before write') write call setline(2, 'after write') - sleep 2 + if has('nanotime') + sleep 10m + else + sleep 2 + endif silent !echo 'different line' >>Xchanged_r let g:reason = '' checktime @@ -140,8 +152,6 @@ func Test_FileChangedShell_edit() endfunc func Test_FileChangedShell_edit_dialog() - " requires a UI to be active - throw 'Skipped: use test/functional/legacy/filechanged_spec.lua' CheckNotGui CheckUnix " Using low level feedkeys() does not work on MS-Windows. @@ -156,6 +166,7 @@ func Test_FileChangedShell_edit_dialog() au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'ask' augroup END call assert_equal(&fileformat, 'unix') + sleep 10m " make the test less flaky in Nvim call writefile(["line1\r", "line2\r"], 'Xchanged_r') let g:reason = '' call feedkeys('L', 'L') " load file content only @@ -173,6 +184,7 @@ func Test_FileChangedShell_edit_dialog() au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'ask' augroup END call assert_equal(&fileformat, 'unix') + sleep 10m " make the test less flaky in Nvim call writefile(["line1\r", "line2\r"], 'Xchanged_r') let g:reason = '' call feedkeys('a', 'L') " load file content and options @@ -191,8 +203,6 @@ func Test_FileChangedShell_edit_dialog() endfunc func Test_file_changed_dialog() - " requires a UI to be active - throw 'Skipped: use test/functional/legacy/filechanged_spec.lua' CheckUnix CheckNotGui au! FileChangedShell @@ -200,8 +210,12 @@ func Test_file_changed_dialog() new Xchanged_d call setline(1, 'reload this') write - " Need to wait until the timestamp would change by at least a second. - sleep 2 + " Need to wait until the timestamp would change. + if has('nanotime') + sleep 10m + else + sleep 2 + endif silent !echo 'extra line' >>Xchanged_d call feedkeys('L', 'L') checktime @@ -236,7 +250,11 @@ func Test_file_changed_dialog() call assert_equal('new line', getline(1)) " Only time changed, no prompt - sleep 2 + if has('nanotime') + sleep 10m + else + sleep 2 + endif silent !touch Xchanged_d let v:warningmsg = '' checktime Xchanged_d diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index 303b1585e2..34fe93fd55 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -1,5 +1,16 @@ " Test :setfiletype +func Test_backup_strip() + filetype on + let fname = 'Xdetect.js~~~~~~~~~~~' + call writefile(['one', 'two', 'three'], fname, 'D') + exe 'edit ' .. fname + call assert_equal('javascript', &filetype) + + bwipe! + filetype off +endfunc + func Test_detection() filetype on augroup filetypedetect @@ -97,7 +108,7 @@ func s:GetFilenameChecks() abort \ 'asterisk': ['asterisk/file.conf', 'asterisk/file.conf-file', 'some-asterisk/file.conf', 'some-asterisk/file.conf-file'], \ 'astro': ['file.astro'], \ 'atlas': ['file.atl', 'file.as'], - \ 'authzed': ['file.zed'], + \ 'authzed': ['schema.zed'], \ 'autohotkey': ['file.ahk'], \ 'autoit': ['file.au3'], \ 'automake': ['GNUmakefile.am', 'makefile.am', 'Makefile.am'], @@ -116,11 +127,12 @@ func s:GetFilenameChecks() abort \ 'blade': ['file.blade.php'], \ 'blank': ['file.bl'], \ 'blueprint': ['file.blp'], + \ 'bp': ['Android.bp'], \ 'bsdl': ['file.bsd', 'file.bsdl'], \ 'bst': ['file.bst'], \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE', 'WORKSPACE.bzlmod'], \ 'bzr': ['bzr_log.any', 'bzr_log.file'], - \ 'c': ['enlightenment/file.cfg', 'file.qc', 'file.c', 'some-enlightenment/file.cfg'], + \ 'c': ['enlightenment/file.cfg', 'file.qc', 'file.c', 'some-enlightenment/file.cfg', 'file.mdh', 'file.epro'], \ 'cabal': ['file.cabal'], \ 'cabalconfig': ['cabal.config', expand("$HOME/.config/cabal/config")] + s:WhenConfigHome('$XDG_CONFIG_HOME/cabal/config'), \ 'cabalproject': ['cabal.project', 'cabal.project.local'], @@ -134,6 +146,7 @@ func s:GetFilenameChecks() abort \ 'cf': ['file.cfm', 'file.cfi', 'file.cfc'], \ 'cfengine': ['cfengine.conf'], \ 'cfg': ['file.hgrc', 'filehgrc', 'hgrc', 'some-hgrc'], + \ 'cgdbrc': ['cgdbrc'], \ 'ch': ['file.chf'], \ 'chaiscript': ['file.chai'], \ 'chaskell': ['file.chs'], @@ -143,16 +156,17 @@ func s:GetFilenameChecks() abort \ 'chuck': ['file.ck'], \ 'cl': ['file.eni'], \ 'clean': ['file.dcl', 'file.icl'], - \ 'clojure': ['file.clj', 'file.cljs', 'file.cljx', 'file.cljc'], + \ 'clojure': ['file.clj', 'file.cljs', 'file.cljx', 'file.cljc', 'init.trans', 'any/etc/translate-shell', '.trans'], \ 'cmake': ['CMakeLists.txt', 'file.cmake', 'file.cmake.in'], + \ 'cmakecache': ['CMakeCache.txt'], \ 'cmod': ['file.cmod'], \ 'cmusrc': ['any/.cmus/autosave', 'any/.cmus/rc', 'any/.cmus/command-history', 'any/.cmus/file.theme', 'any/cmus/rc', 'any/cmus/file.theme', '/.cmus/autosave', '/.cmus/command-history', '/.cmus/file.theme', '/.cmus/rc', '/cmus/file.theme', '/cmus/rc'], \ 'cobol': ['file.cbl', 'file.cob', 'file.lib'], \ 'coco': ['file.atg'], \ 'conaryrecipe': ['file.recipe'], - \ 'conf': ['auto.master', 'file.conf'], + \ 'conf': ['auto.master', 'file.conf', 'texdoc.cnf', '.x11vncrc', '.chktexrc', '.ripgreprc', 'ripgreprc', 'file.ctags', '.mbsyncrc'], \ 'config': ['configure.in', 'configure.ac', '/etc/hostname.file', 'any/etc/hostname.file'], - \ 'confini': ['/etc/pacman.conf', 'any/etc/pacman.conf', 'mpv.conf', 'any/.aws/config', 'any/.aws/credentials', 'file.nmconnection'], + \ 'confini': ['pacman.conf', 'paru.conf', 'mpv.conf', 'any/.aws/config', 'any/.aws/credentials', 'file.nmconnection'], \ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'], \ 'cook': ['file.cook'], \ 'corn': ['file.corn'], @@ -179,6 +193,7 @@ func s:GetFilenameChecks() abort \ 'cynpp': ['file.cyn'], \ 'cypher': ['file.cypher'], \ 'd': ['file.d'], + \ 'dafny': ['file.dfy'], \ 'dart': ['file.dart', 'file.drt'], \ 'datascript': ['file.ds'], \ 'dcd': ['file.dcd'], @@ -199,16 +214,24 @@ func s:GetFilenameChecks() abort \ 'dnsmasq': ['/etc/dnsmasq.conf', '/etc/dnsmasq.d/file', 'any/etc/dnsmasq.conf', 'any/etc/dnsmasq.d/file'], \ 'dockerfile': ['Containerfile', 'Dockerfile', 'dockerfile', 'file.Dockerfile', 'file.dockerfile', 'Dockerfile.debian', 'Containerfile.something'], \ 'dosbatch': ['file.bat'], - \ 'dosini': ['/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap', 'file.vbp', 'ja2.ini', 'JA2.INI'], + \ 'dosini': ['/etc/yum.conf', '/etc/nfs.conf', '/etc/nfsmount.conf', 'file.ini', + \ 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', + \ '/etc/yum.repos.d/file', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap', + \ 'file.vbp', 'ja2.ini', 'JA2.INI', 'mimeapps.list', 'pip.conf', 'setup.cfg', 'pudb.cfg', + \ '.coveragerc', '.pypirc', '.gitlint', '.oelint.cfg', 'pylintrc', '.pylintrc', + \ '/home/user/.config/bpython/config', '/home/user/.config/mypy/config', '.wakatime.cfg', '.replyrc', + \ 'psprint.conf', 'sofficerc', 'any/.config/lxqt/globalkeyshortcuts.conf', 'any/.config/screengrab/screengrab.conf', + \ 'any/.local/share/flatpak/repo/config', '.notmuch-config'], \ 'dot': ['file.dot', 'file.gv'], \ 'dracula': ['file.drac', 'file.drc', 'filelvs', 'filelpe', 'drac.file', 'lpe', 'lvs', 'some-lpe', 'some-lvs'], \ 'dtd': ['file.dtd'], \ 'dtrace': ['/usr/lib/dtrace/io.d'], - \ 'dts': ['file.dts', 'file.dtsi', 'file.dtso', 'file.its'], + \ 'dts': ['file.dts', 'file.dtsi', 'file.dtso', 'file.its', 'file.keymap'], \ 'dune': ['jbuild', 'dune', 'dune-project', 'dune-workspace'], \ 'dylan': ['file.dylan'], \ 'dylanintr': ['file.intr'], \ 'dylanlid': ['file.lid'], + \ 'earthfile': ['Earthfile'], \ 'ecd': ['file.ecd'], \ 'edif': ['file.edf', 'file.edif', 'file.edo'], \ 'editorconfig': ['.editorconfig'], @@ -272,7 +295,7 @@ func s:GetFilenameChecks() abort \ 'glsl': ['file.glsl'], \ 'gn': ['file.gn', 'file.gni'], \ 'gnash': ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'], - \ 'gnuplot': ['file.gpi', '.gnuplot'], + \ 'gnuplot': ['file.gpi', '.gnuplot', 'file.gnuplot', '.gnuplot_history'], \ 'go': ['file.go'], \ 'gomod': ['go.mod'], \ 'gosum': ['go.sum', 'go.work.sum'], @@ -301,7 +324,7 @@ func s:GetFilenameChecks() abort \ 'hcl': ['file.hcl'], \ 'heex': ['file.heex'], \ 'hercules': ['file.vc', 'file.ev', 'file.sum', 'file.errsum'], - \ 'hex': ['file.hex', 'file.h32'], + \ 'hex': ['file.hex', 'file.ihex', 'file.ihe', 'file.ihx', 'file.int', 'file.mcs', 'file.h32', 'file.h80', 'file.h86', 'file.a43', 'file.a90'], \ 'hgcommit': ['hg-editor-file.txt'], \ 'hjson': ['file.hjson'], \ 'hlsplaylist': ['file.m3u', 'file.m3u8'], @@ -314,6 +337,7 @@ func s:GetFilenameChecks() abort \ 'htmlm4': ['file.html.m4'], \ 'httest': ['file.htt', 'file.htb'], \ 'hurl': ['file.hurl'], + \ 'hyprlang': ['hyprlock.conf', 'hyprland.conf', 'hypridle.conf', 'hyprpaper.conf'], \ 'i3config': ['/home/user/.i3/config', '/home/user/.config/i3/config', '/etc/i3/config', '/etc/xdg/i3/config'], \ 'ibasic': ['file.iba', 'file.ibi'], \ 'icemenu': ['/.icewm/menu', 'any/.icewm/menu'], @@ -322,6 +346,7 @@ func s:GetFilenameChecks() abort \ 'inform': ['file.inf', 'file.INF'], \ 'initng': ['/etc/initng/any/file.i', 'file.ii', 'any/etc/initng/any/file.i'], \ 'inittab': ['inittab'], + \ 'inko': ['file.inko'], \ 'ipfilter': ['ipf.conf', 'ipf6.conf', 'ipf.rules'], \ 'iss': ['file.iss'], \ 'ist': ['file.ist', 'file.mst'], @@ -331,17 +356,18 @@ func s:GetFilenameChecks() abort \ 'janet': ['file.janet'], \ 'java': ['file.java', 'file.jav'], \ 'javacc': ['file.jj', 'file.jjt'], - \ 'javascript': ['file.js', 'file.jsm', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs'], + \ 'javascript': ['file.js', 'file.jsm', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs', '.node_repl_history'], \ 'javascript.glimmer': ['file.gjs'], \ 'javascriptreact': ['file.jsx'], \ 'jess': ['file.clp'], \ 'jgraph': ['file.jgr'], + \ 'jj': ['file.jjdescription'], \ 'jq': ['file.jq'], \ 'jovial': ['file.jov', 'file.j73', 'file.jovial'], \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file', 'org.eclipse.xyz.prefs'], - \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.geojson', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.prettierrc', '.firebaserc', '.stylelintrc', 'file.slnf'], + \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.geojson', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', 'file.jupyterlab-settings', '.prettierrc', '.firebaserc', '.stylelintrc', 'file.slnf', 'file.sublime-project', 'file.sublime-settings', 'file.sublime-workspace', 'file.bd', 'file.bda', 'file.xci', 'flake.lock'], \ 'json5': ['file.json5'], - \ 'jsonc': ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintrc', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json', '.luaurc'], + \ 'jsonc': ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintrc', '.jscsrc', '.vsconfig', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json', '.luaurc'], \ 'jsonl': ['file.jsonl'], \ 'jsonnet': ['file.jsonnet', 'file.libsonnet'], \ 'jsp': ['file.jsp'], @@ -357,7 +383,7 @@ func s:GetFilenameChecks() abort \ 'kwt': ['file.k'], \ 'lace': ['file.ace', 'file.ACE'], \ 'latte': ['file.latte', 'file.lte'], - \ 'ld': ['file.ld'], + \ 'ld': ['file.ld', 'any/usr/lib/aarch64-xilinx-linux/ldscripts/aarch64elf32b.x'], \ 'ldif': ['file.ldif'], \ 'lean': ['file.lean'], \ 'ledger': ['file.ldg', 'file.ledger', 'file.journal'], @@ -372,7 +398,7 @@ func s:GetFilenameChecks() abort \ 'limits': ['/etc/limits', '/etc/anylimits.conf', '/etc/anylimits.d/file.conf', '/etc/limits.conf', '/etc/limits.d/file.conf', '/etc/some-limits.conf', '/etc/some-limits.d/file.conf', 'any/etc/limits', 'any/etc/limits.conf', 'any/etc/limits.d/file.conf', 'any/etc/some-limits.conf', 'any/etc/some-limits.d/file.conf'], \ 'liquidsoap': ['file.liq'], \ 'liquid': ['file.liquid'], - \ 'lisp': ['file.lsp', 'file.lisp', 'file.asd', 'file.el', 'file.cl', '.emacs', '.sawfishrc', 'sbclrc', '.sbclrc'], + \ 'lisp': ['file.lsp', 'file.lisp', 'file.asd', 'file.el', 'file.cl', '.emacs', '.sawfishrc', 'sbclrc', '.sbclrc', 'file.stsg', 'any/local/share/supertux2/config'], \ 'lite': ['file.lite', 'file.lt'], \ 'litestep': ['/LiteStep/any/file.rc', 'any/LiteStep/any/file.rc'], \ 'logcheck': ['/etc/logcheck/file.d-some/file', '/etc/logcheck/file.d/file', 'any/etc/logcheck/file.d-some/file', 'any/etc/logcheck/file.d/file'], @@ -385,17 +411,17 @@ func s:GetFilenameChecks() abort \ 'lpc': ['file.lpc', 'file.ulpc'], \ 'lsl': ['file.lsl'], \ 'lss': ['file.lss'], - \ 'lua': ['file.lua', 'file.rockspec', 'file.nse', '.luacheckrc', '.busted'], + \ 'lua': ['file.lua', 'file.tlu', 'file.rockspec', 'file.nse', '.lua_history', '.luacheckrc', '.busted', 'rock_manifest', 'config.ld'], \ 'luau': ['file.luau'], \ 'lynx': ['lynx.cfg'], \ 'lyrics': ['file.lrc'], \ 'm3build': ['m3makefile', 'm3overrides'], \ 'm3quake': ['file.quake', 'cm3.cfg'], - \ 'm4': ['file.at'], + \ 'm4': ['file.at', '.m4_history'], \ 'mail': ['snd.123', '.letter', '.letter.123', '.followup', '.article', '.article.123', 'pico.123', 'mutt-xx-xxx', 'muttng-xx-xxx', 'ae123.txt', 'file.eml', 'reportbug-file'], \ 'mailaliases': ['/etc/mail/aliases', '/etc/aliases', 'any/etc/aliases', 'any/etc/mail/aliases'], \ 'mailcap': ['.mailcap', 'mailcap'], - \ 'make': ['file.mk', 'file.mak', 'file.dsp', 'makefile', 'Makefile', 'makefile-file', 'Makefile-file', 'some-makefile', 'some-Makefile'], + \ 'make': ['file.mk', 'file.mak', 'file.dsp', 'makefile', 'Makefile', 'makefile-file', 'Makefile-file', 'some-makefile', 'some-Makefile', 'Kbuild'], \ 'mallard': ['file.page'], "\ 'man': ['file.man'], \ 'manconf': ['/etc/man.conf', 'man.config', 'any/etc/man.conf'], @@ -410,13 +436,32 @@ func s:GetFilenameChecks() abort \ 'mel': ['file.mel'], \ 'mermaid': ['file.mmd', 'file.mmdc', 'file.mermaid'], \ 'meson': ['meson.build', 'meson.options', 'meson_options.txt'], - \ 'messages': ['/log/auth', '/log/cron', '/log/daemon', '/log/debug', '/log/kern', '/log/lpr', '/log/mail', '/log/messages', '/log/news/news', '/log/syslog', '/log/user', - \ '/log/auth.log', '/log/cron.log', '/log/daemon.log', '/log/debug.log', '/log/kern.log', '/log/lpr.log', '/log/mail.log', '/log/messages.log', '/log/news/news.log', '/log/syslog.log', '/log/user.log', - \ '/log/auth.err', '/log/cron.err', '/log/daemon.err', '/log/debug.err', '/log/kern.err', '/log/lpr.err', '/log/mail.err', '/log/messages.err', '/log/news/news.err', '/log/syslog.err', '/log/user.err', - \ '/log/auth.info', '/log/cron.info', '/log/daemon.info', '/log/debug.info', '/log/kern.info', '/log/lpr.info', '/log/mail.info', '/log/messages.info', '/log/news/news.info', '/log/syslog.info', '/log/user.info', - \ '/log/auth.warn', '/log/cron.warn', '/log/daemon.warn', '/log/debug.warn', '/log/kern.warn', '/log/lpr.warn', '/log/mail.warn', '/log/messages.warn', '/log/news/news.warn', '/log/syslog.warn', '/log/user.warn', - \ '/log/auth.crit', '/log/cron.crit', '/log/daemon.crit', '/log/debug.crit', '/log/kern.crit', '/log/lpr.crit', '/log/mail.crit', '/log/messages.crit', '/log/news/news.crit', '/log/syslog.crit', '/log/user.crit', - \ '/log/auth.notice', '/log/cron.notice', '/log/daemon.notice', '/log/debug.notice', '/log/kern.notice', '/log/lpr.notice', '/log/mail.notice', '/log/messages.notice', '/log/news/news.notice', '/log/syslog.notice', '/log/user.notice'], + \ 'messages': ['/log/auth', '/log/cron', '/log/daemon', '/log/debug', + \ '/log/kern', '/log/lpr', '/log/mail', '/log/messages', + \ '/log/news/news', '/log/syslog', '/log/user', '/log/auth.log', + \ '/log/cron.log', '/log/daemon.log', '/log/debug.log', + \ '/log/kern.log', '/log/lpr.log', '/log/mail.log', + \ '/log/messages.log', '/log/news/news.log', '/log/syslog.log', + \ '/log/user.log', '/log/auth.err', '/log/cron.err', + \ '/log/daemon.err', '/log/debug.err', '/log/kern.err', + \ '/log/lpr.err', '/log/mail.err', '/log/messages.err', + \ '/log/news/news.err', '/log/syslog.err', '/log/user.err', + \ '/log/auth.info', '/log/cron.info', '/log/daemon.info', + \ '/log/debug.info', '/log/kern.info', '/log/lpr.info', + \ '/log/mail.info', '/log/messages.info', '/log/news/news.info', + \ '/log/syslog.info', '/log/user.info', '/log/auth.warn', + \ '/log/cron.warn', '/log/daemon.warn', '/log/debug.warn', + \ '/log/kern.warn', '/log/lpr.warn', '/log/mail.warn', + \ '/log/messages.warn', '/log/news/news.warn', + \ '/log/syslog.warn', '/log/user.warn', '/log/auth.crit', + \ '/log/cron.crit', '/log/daemon.crit', '/log/debug.crit', + \ '/log/kern.crit', '/log/lpr.crit', '/log/mail.crit', + \ '/log/messages.crit', '/log/news/news.crit', + \ '/log/syslog.crit', '/log/user.crit', '/log/auth.notice', + \ '/log/cron.notice', '/log/daemon.notice', '/log/debug.notice', + \ '/log/kern.notice', '/log/lpr.notice', '/log/mail.notice', + \ '/log/messages.notice', '/log/news/news.notice', + \ '/log/syslog.notice', '/log/user.notice'], \ 'mf': ['file.mf'], \ 'mgl': ['file.mgl'], \ 'mgp': ['file.mgp'], @@ -436,11 +481,23 @@ func s:GetFilenameChecks() abort \ 'msidl': ['file.odl', 'file.mof'], \ 'msql': ['file.msql'], \ 'mojo': ['file.mojo', 'file.🔥'], + \ 'msmtp': ['.msmtprc'], \ 'mupad': ['file.mu'], \ 'mush': ['file.mush'], \ 'mustache': ['file.mustache'], - \ 'muttrc': ['Muttngrc', 'Muttrc', '.muttngrc', '.muttngrc-file', '.muttrc', '.muttrc-file', '/.mutt/muttngrc', '/.mutt/muttngrc-file', '/.mutt/muttrc', '/.mutt/muttrc-file', '/.muttng/muttngrc', '/.muttng/muttngrc-file', '/.muttng/muttrc', '/.muttng/muttrc-file', '/etc/Muttrc.d/file', '/etc/Muttrc.d/file.rc', 'Muttngrc-file', 'Muttrc-file', 'any/.mutt/muttngrc', 'any/.mutt/muttngrc-file', 'any/.mutt/muttrc', 'any/.mutt/muttrc-file', 'any/.muttng/muttngrc', 'any/.muttng/muttngrc-file', 'any/.muttng/muttrc', 'any/.muttng/muttrc-file', 'any/etc/Muttrc.d/file', 'muttngrc', 'muttngrc-file', 'muttrc', 'muttrc-file'], - \ 'mysql': ['file.mysql'], + \ 'muttrc': ['Muttngrc', 'Muttrc', '.muttngrc', '.muttngrc-file', '.muttrc', + \ '.muttrc-file', '/.mutt/muttngrc', '/.mutt/muttngrc-file', + \ '/.mutt/muttrc', '/.mutt/muttrc-file', '/.muttng/muttngrc', + \ '/.muttng/muttngrc-file', '/.muttng/muttrc', + \ '/.muttng/muttrc-file', '/etc/Muttrc.d/file', + \ '/etc/Muttrc.d/file.rc', 'Muttngrc-file', 'Muttrc-file', + \ 'any/.mutt/muttngrc', 'any/.mutt/muttngrc-file', + \ 'any/.mutt/muttrc', 'any/.mutt/muttrc-file', + \ 'any/.muttng/muttngrc', 'any/.muttng/muttngrc-file', + \ 'any/.muttng/muttrc', 'any/.muttng/muttrc-file', + \ 'any/etc/Muttrc.d/file', 'muttngrc', 'muttngrc-file', 'muttrc', + \ 'muttrc-file'], + \ 'mysql': ['file.mysql', '.mysql_history'], \ 'n1ql': ['file.n1ql', 'file.nql'], \ 'named': ['namedfile.conf', 'rndcfile.conf', 'named-file.conf', 'named.conf', 'rndc-file.conf', 'rndc-file.key', 'rndc.conf', 'rndc.key'], \ 'nanorc': ['/etc/nanorc', 'file.nanorc', 'any/etc/nanorc'], @@ -462,9 +519,10 @@ func s:GetFilenameChecks() abort \ 'obse': ['file.obl', 'file.obse', 'file.oblivion', 'file.obscript'], \ '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'], - \ 'octave': ['octaverc', '.octaverc', 'octave.conf'], + \ 'octave': ['octaverc', '.octaverc', 'octave.conf', 'any/.local/share/octave/history'], \ 'odin': ['file.odin'], \ 'omnimark': ['file.xom', 'file.xin'], + \ 'ondir': ['.ondirrc'], \ 'opam': ['opam', 'file.opam', 'file.opam.template'], \ 'openroad': ['file.or'], \ 'openscad': ['file.scad'], @@ -475,10 +533,11 @@ func s:GetFilenameChecks() abort \ 'pacmanlog': ['pacman.log'], \ 'pamconf': ['/etc/pam.conf', '/etc/pam.d/file', 'any/etc/pam.conf', 'any/etc/pam.d/file'], \ 'pamenv': ['/etc/security/pam_env.conf', '/home/user/.pam_environment', '.pam_environment', 'pam_env.conf'], + \ 'pandoc': ['file.pandoc', 'file.pdk', 'file.pd', 'file.pdc'], \ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'], \ 'pascal': ['file.pas', 'file.dpr', 'file.lpr'], \ 'passwd': ['any/etc/passwd', 'any/etc/passwd-', 'any/etc/passwd.edit', 'any/etc/shadow', 'any/etc/shadow-', 'any/etc/shadow.edit', 'any/var/backups/passwd.bak', 'any/var/backups/shadow.bak', '/etc/passwd', '/etc/passwd-', '/etc/passwd.edit', '/etc/shadow', '/etc/shadow-', '/etc/shadow.edit', '/var/backups/passwd.bak', '/var/backups/shadow.bak'], - \ 'pbtxt': ['file.pbtxt'], + \ 'pbtxt': ['file.txtpb', 'file.textproto', 'file.textpb', 'file.pbtxt'], \ 'pccts': ['file.g'], \ 'pcmk': ['file.pcmk'], \ 'pdf': ['file.pdf'], @@ -519,16 +578,17 @@ func s:GetFilenameChecks() abort \ 'psl': ['file.psl'], \ 'pug': ['file.pug'], \ 'puppet': ['file.pp'], + \ 'purescript': ['file.purs'], \ 'pymanifest': ['MANIFEST.in'], \ 'pyret': ['file.arr'], \ 'pyrex': ['file.pyx', 'file.pxd'], - \ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi', 'SConstruct'], + \ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', '.python_history', '.jline-jython.history', 'file.ptl', 'file.pyi', 'SConstruct'], \ 'ql': ['file.ql', 'file.qll'], \ 'qml': ['file.qml', 'file.qbs'], \ 'qmldir': ['qmldir'], \ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg', 'baseq2/file.cfg', 'id1/file.cfg', 'quake1/file.cfg', 'some-baseq2/file.cfg', 'some-id1/file.cfg', 'some-quake1/file.cfg'], \ 'quarto': ['file.qmd'], - \ 'r': ['file.r', '.Rprofile', 'Rprofile', 'Rprofile.site'], + \ 'r': ['file.r', '.Rhistory', '.Rprofile', 'Rprofile', 'Rprofile.site'], \ 'racket': ['file.rkt', 'file.rktd', 'file.rktl'], \ 'radiance': ['file.rad', 'file.mat'], \ 'raku': ['file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'], @@ -540,7 +600,7 @@ func s:GetFilenameChecks() abort \ 'readline': ['.inputrc', 'inputrc'], \ 'rego': ['file.rego'], \ 'remind': ['.reminders', 'file.remind', 'file.rem', '.reminders-file'], - \ 'requirements': ['file.pip', 'requirements.txt'], + \ 'requirements': ['file.pip', 'requirements.txt', 'dev-requirements.txt', 'constraints.txt', 'requirements.in', 'requirements/dev.txt', 'requires/dev.txt'], \ 'rescript': ['file.res', 'file.resi'], \ 'resolv': ['resolv.conf'], \ 'reva': ['file.frt'], @@ -554,6 +614,7 @@ func s:GetFilenameChecks() abort \ 'rpgle': ['file.rpgle', 'file.rpgleinc'], \ 'robot': ['file.robot', 'file.resource'], \ 'robots': ['robots.txt'], + \ 'roc': ['file.roc'], \ 'ron': ['file.ron'], \ 'routeros': ['file.rsc'], \ 'rpcgen': ['file.x'], @@ -561,7 +622,7 @@ func s:GetFilenameChecks() abort \ 'rrst': ['file.rrst', 'file.srst'], \ 'rst': ['file.rst'], \ 'rtf': ['file.rtf'], - \ 'ruby': ['.irbrc', 'irbrc', 'file.rb', 'file.rbw', 'file.gemspec', 'file.ru', 'Gemfile', 'file.builder', 'file.rxml', 'file.rjs', 'file.rant', 'file.rake', 'rakefile', 'Rakefile', 'rantfile', 'Rantfile', 'rakefile-file', 'Rakefile-file', 'Puppetfile', 'Vagrantfile'], + \ 'ruby': ['.irbrc', 'irbrc', '.irb_history', 'irb_history', 'file.rb', 'file.rbw', 'file.gemspec', 'file.ru', 'Gemfile', 'file.builder', 'file.rxml', 'file.rjs', 'file.rant', 'file.rake', 'rakefile', 'Rakefile', 'rantfile', 'Rantfile', 'rakefile-file', 'Rakefile-file', 'Puppetfile', 'Vagrantfile'], \ 'rust': ['file.rs'], \ 'samba': ['smb.conf'], \ 'sas': ['file.sas'], @@ -581,7 +642,10 @@ func s:GetFilenameChecks() abort \ 'services': ['/etc/services', 'any/etc/services'], \ 'setserial': ['/etc/serial.conf', 'any/etc/serial.conf'], \ 'sexplib': ['file.sexp'], - \ 'sh': ['.bashrc', '.bash_profile', '.bash-profile', '.bash_logout', '.bash-logout', '.bash_aliases', '.bash-aliases', '/tmp/bash-fc-3Ozjlw', '/tmp/bash-fc.3Ozjlw', 'PKGBUILD', 'APKBUILD', 'file.bash', '/usr/share/doc/bash-completion/filter.sh', '/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf', 'file.bats'], + \ 'sh': ['.bashrc', '.bash_profile', '.bash-profile', '.bash_logout', '.bash-logout', '.bash_aliases', '.bash-aliases', '.bash_history', '.bash-history', + \ '/tmp/bash-fc-3Ozjlw', '/tmp/bash-fc.3Ozjlw', 'PKGBUILD', 'APKBUILD', 'file.bash', '/usr/share/doc/bash-completion/filter.sh', + \ '/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf', 'file.bats', '.ash_history', 'any/etc/neofetch/config.conf', '.xprofile', + \ 'user-dirs.defaults', 'user-dirs.dirs', 'makepkg.conf', '.makepkg.conf', 'file.mdd', 'file.cygport'], \ 'sieve': ['file.siv', 'file.sieve'], \ 'sil': ['file.sil'], \ 'simula': ['file.sim'], @@ -592,6 +656,7 @@ func s:GetFilenameChecks() abort \ 'slang': ['file.sl'], \ 'sage': ['file.sage'], \ 'slice': ['file.ice'], + \ 'slint': ['file.slint'], \ 'slpconf': ['/etc/slp.conf', 'any/etc/slp.conf'], \ 'slpreg': ['/etc/slp.reg', 'any/etc/slp.reg'], \ 'slpspi': ['/etc/slp.spi', 'any/etc/slp.spi'], @@ -612,7 +677,7 @@ func s:GetFilenameChecks() abort \ 'spice': ['file.sp', 'file.spice'], \ 'spup': ['file.speedup', 'file.spdata', 'file.spd'], \ 'spyce': ['file.spy', 'file.spi'], - \ 'sql': ['file.tyb', 'file.tyc', 'file.pkb', 'file.pks'], + \ 'sql': ['file.tyb', 'file.tyc', 'file.pkb', 'file.pks', '.sqlite_history'], \ 'sqlj': ['file.sqlj'], \ 'prql': ['file.prql'], \ 'sqr': ['file.sqr', 'file.sqi'], @@ -627,6 +692,7 @@ func s:GetFilenameChecks() abort \ 'starlark': ['file.ipd', 'file.star', 'file.starlark'], \ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'], \ 'stp': ['file.stp'], + \ 'stylus': ['a.styl', 'file.stylus'], \ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'], \ 'supercollider': ['file.quark'], \ 'surface': ['file.sface'], @@ -638,7 +704,45 @@ func s:GetFilenameChecks() abort \ 'swiftgyb': ['file.swift.gyb'], \ 'swig': ['file.swg', 'file.swig'], \ 'sysctl': ['/etc/sysctl.conf', '/etc/sysctl.d/file.conf', 'any/etc/sysctl.conf', 'any/etc/sysctl.d/file.conf'], - \ 'systemd': ['any/systemd/file.automount', 'any/systemd/file.dnssd', 'any/systemd/file.link', 'any/systemd/file.mount', 'any/systemd/file.netdev', 'any/systemd/file.network', 'any/systemd/file.nspawn', 'any/systemd/file.path', 'any/systemd/file.service', 'any/systemd/file.slice', 'any/systemd/file.socket', 'any/systemd/file.swap', 'any/systemd/file.target', 'any/systemd/file.timer', '/etc/systemd/some.conf.d/file.conf', '/etc/systemd/system/some.d/file.conf', '/etc/systemd/system/some.d/.#file', '/etc/systemd/system/.#otherfile', '/home/user/.config/systemd/user/some.d/mine.conf', '/home/user/.config/systemd/user/some.d/.#file', '/home/user/.config/systemd/user/.#otherfile', '/.config/systemd/user/.#', '/.config/systemd/user/.#-file', '/.config/systemd/user/file.d/.#', '/.config/systemd/user/file.d/.#-file', '/.config/systemd/user/file.d/file.conf', '/etc/systemd/file.conf.d/file.conf', '/etc/systemd/system/.#', '/etc/systemd/system/.#-file', '/etc/systemd/system/file.d/.#', '/etc/systemd/system/file.d/.#-file', '/etc/systemd/system/file.d/file.conf', '/systemd/file.automount', '/systemd/file.dnssd', '/systemd/file.link', '/systemd/file.mount', '/systemd/file.netdev', '/systemd/file.network', '/systemd/file.nspawn', '/systemd/file.path', '/systemd/file.service', '/systemd/file.slice', '/systemd/file.socket', '/systemd/file.swap', '/systemd/file.target', '/systemd/file.timer', 'any/.config/systemd/user/.#', 'any/.config/systemd/user/.#-file', 'any/.config/systemd/user/file.d/.#', 'any/.config/systemd/user/file.d/.#-file', 'any/.config/systemd/user/file.d/file.conf', 'any/etc/systemd/file.conf.d/file.conf', 'any/etc/systemd/system/.#', 'any/etc/systemd/system/.#-file', 'any/etc/systemd/system/file.d/.#', 'any/etc/systemd/system/file.d/.#-file', 'any/etc/systemd/system/file.d/file.conf'], + \ 'systemd': ['any/systemd/file.automount', 'any/systemd/file.dnssd', + \ 'any/systemd/file.link', 'any/systemd/file.mount', + \ 'any/systemd/file.netdev', 'any/systemd/file.network', + \ 'any/systemd/file.nspawn', 'any/systemd/file.path', + \ 'any/systemd/file.service', 'any/systemd/file.slice', + \ 'any/systemd/file.socket', 'any/systemd/file.swap', + \ 'any/systemd/file.target', 'any/systemd/file.timer', + \ '/etc/systemd/some.conf.d/file.conf', + \ '/etc/systemd/system/some.d/file.conf', + \ '/etc/systemd/system/some.d/.#file', + \ '/etc/systemd/system/.#otherfile', + \ '/home/user/.config/systemd/user/some.d/mine.conf', + \ '/home/user/.config/systemd/user/some.d/.#file', + \ '/home/user/.config/systemd/user/.#otherfile', + \ '/.config/systemd/user/.#', '/.config/systemd/user/.#-file', + \ '/.config/systemd/user/file.d/.#', + \ '/.config/systemd/user/file.d/.#-file', + \ '/.config/systemd/user/file.d/file.conf', + \ '/etc/systemd/file.conf.d/file.conf', '/etc/systemd/system/.#', + \ '/etc/systemd/system/.#-file', '/etc/systemd/system/file.d/.#', + \ '/etc/systemd/system/file.d/.#-file', + \ '/etc/systemd/system/file.d/file.conf', + \ '/systemd/file.automount', '/systemd/file.dnssd', + \ '/systemd/file.link', '/systemd/file.mount', + \ '/systemd/file.netdev', '/systemd/file.network', + \ '/systemd/file.nspawn', '/systemd/file.path', + \ '/systemd/file.service', '/systemd/file.slice', + \ '/systemd/file.socket', '/systemd/file.swap', + \ '/systemd/file.target', '/systemd/file.timer', + \ 'any/.config/systemd/user/.#', + \ 'any/.config/systemd/user/.#-file', + \ 'any/.config/systemd/user/file.d/.#', + \ 'any/.config/systemd/user/file.d/.#-file', + \ 'any/.config/systemd/user/file.d/file.conf', + \ 'any/etc/systemd/file.conf.d/file.conf', + \ 'any/etc/systemd/system/.#', 'any/etc/systemd/system/.#-file', + \ 'any/etc/systemd/system/file.d/.#', + \ 'any/etc/systemd/system/file.d/.#-file', + \ 'any/etc/systemd/system/file.d/file.conf'], \ 'systemverilog': ['file.sv', 'file.svh'], \ 'trace32': ['file.cmm', 'file.t32'], \ 'tags': ['tags'], @@ -646,14 +750,15 @@ func s:GetFilenameChecks() abort \ 'tal': ['file.tal'], \ 'taskdata': ['pending.data', 'completed.data', 'undo.data'], \ 'taskedit': ['file.task'], - \ 'tcl': ['file.tcl', 'file.tm', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl', '.tclshrc', 'tclsh.rc', '.wishrc'], + \ 'tcl': ['file.tcl', 'file.tm', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl', '.tclshrc', 'tclsh.rc', '.wishrc', '.tclsh-history', '.xsctcmdhistory', '.xsdbcmdhistory'], \ 'tablegen': ['file.td'], \ 'teal': ['file.tl'], + \ 'templ': ['file.templ'], \ 'template': ['file.tmpl'], \ 'teraterm': ['file.ttl'], \ 'terminfo': ['file.ti'], \ 'terraform-vars': ['file.tfvars'], - \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'], + \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl', 'any/.texlive/texmf-config/tex/latex/file/file.cfg', 'file.pgf', 'file.nlo', 'file.nls', 'file.thm', 'file.eps_tex', 'file.pygtex', 'file.pygstyle', 'file.clo', 'file.aux', 'file.brf', 'file.ind', 'file.lof', 'file.loe', 'file.nav', 'file.vrb', 'file.ins', 'file.tikz', 'file.bbx', 'file.cbx', 'file.beamer'], \ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'], \ 'texmf': ['texmf.cnf'], \ 'text': ['file.text', 'file.txt', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'], @@ -664,7 +769,7 @@ func s:GetFilenameChecks() abort \ 'tla': ['file.tla'], \ 'tli': ['file.tli'], \ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf', '.tmux-file.conf', '.tmux.conf', 'tmux-file.conf', 'tmux.conf', 'tmux.conf.local'], - \ 'toml': ['file.toml', 'Gopkg.lock', 'Pipfile', '/home/user/.cargo/config'], + \ 'toml': ['file.toml', 'Gopkg.lock', 'Pipfile', '/home/user/.cargo/config', '.black'], \ 'tpp': ['file.tpp'], \ 'treetop': ['file.treetop'], \ 'trustees': ['trustees.conf'], @@ -674,9 +779,10 @@ func s:GetFilenameChecks() abort \ 'tssop': ['file.tssop'], \ 'tsv': ['file.tsv'], \ 'twig': ['file.twig'], - \ 'typescript': ['file.mts', 'file.cts'], + \ 'typescript': ['file.mts', 'file.cts', '.ts_node_repl_history'], \ 'typescript.glimmer': ['file.gts'], \ 'typescriptreact': ['file.tsx'], + \ 'typespec': ['file.tsp'], \ 'ungrammar': ['file.ungram'], \ 'uc': ['file.uc'], \ 'udevconf': ['/etc/udev/udev.conf', 'any/etc/udev/udev.conf'], @@ -700,12 +806,13 @@ func s:GetFilenameChecks() abort \ 'vdmpp': ['file.vpp', 'file.vdmpp'], \ 'vdmrt': ['file.vdmrt'], \ 'vdmsl': ['file.vdm', 'file.vdmsl'], + \ 'vento': ['file.vto'], \ 'vera': ['file.vr', 'file.vri', 'file.vrh'], \ 'verilogams': ['file.va', 'file.vams'], \ 'vgrindefs': ['vgrindefs'], \ 'vhdl': ['file.hdl', 'file.vhd', 'file.vhdl', 'file.vbe', 'file.vst', 'file.vhdl_123', 'file.vho', 'some.vhdl_1', 'some.vhdl_1-file'], \ 'vhs': ['file.tape'], - \ 'vim': ['file.vim', '.exrc', '_exrc', 'some-vimrc', 'some-vimrc-file', 'vimrc', 'vimrc-file'], + \ 'vim': ['file.vim', '.exrc', '_exrc', 'some-vimrc', 'some-vimrc-file', 'vimrc', 'vimrc-file', '.netrwhist'], \ 'viminfo': ['.viminfo', '_viminfo'], \ 'vmasm': ['file.mar'], \ 'voscm': ['file.cm'], @@ -731,7 +838,7 @@ func s:GetFilenameChecks() abort \ 'xinetd': ['/etc/xinetd.conf', '/etc/xinetd.d/file', 'any/etc/xinetd.conf', 'any/etc/xinetd.d/file'], \ 'xkb': ['/usr/share/X11/xkb/compat/pc', '/usr/share/X11/xkb/geometry/pc', '/usr/share/X11/xkb/keycodes/evdev', '/usr/share/X11/xkb/symbols/pc', '/usr/share/X11/xkb/types/pc'], \ 'xmath': ['file.msc', 'file.msf'], - \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.fsproj', 'file.fsproj.user', 'file.vbproj', 'file.vbproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old', 'any/etc/xdg/menus/file.menu', 'file.atom', 'file.rss', 'file.cdxml', 'file.psc1', 'file.mpd'], + \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.fsproj', 'file.fsproj.user', 'file.vbproj', 'file.vbproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old', 'any/etc/xdg/menus/file.menu', 'file.atom', 'file.rss', 'file.cdxml', 'file.psc1', 'file.mpd', 'fonts.conf', 'file.xcu', 'file.xlb', 'file.xlc', 'file.xba', 'file.xpr', 'file.xpfm', 'file.spfm', 'file.bxml'], \ 'xmodmap': ['anyXmodmap', 'Xmodmap', 'some-Xmodmap', 'some-xmodmap', 'some-xmodmap-file', 'xmodmap', 'xmodmap-file'], \ 'xpm': ['file.xpm'], \ 'xpm2': ['file.xpm2'], @@ -740,24 +847,30 @@ func s:GetFilenameChecks() abort \ 'xsd': ['file.xsd'], \ 'xslt': ['file.xsl', 'file.xslt'], \ 'yacc': ['file.yy', 'file.yxx', 'file.y++'], - \ 'yaml': ['file.yaml', 'file.yml', 'file.eyaml', '.clangd', '.clang-format', '.clang-tidy'], + \ 'yaml': ['file.yaml', 'file.yml', 'file.eyaml', 'any/.bundle/config', '.clangd', '.clang-format', '.clang-tidy', 'file.mplstyle', 'matplotlibrc', 'yarn.lock'], \ 'yang': ['file.yang'], \ 'yuck': ['file.yuck'], \ 'z8a': ['file.z8a'], + \ 'zathurarc': ['zathurarc'], \ 'zig': ['file.zig', 'build.zig.zon'], \ 'zimbu': ['file.zu'], \ 'zimbutempl': ['file.zut'], \ 'zserio': ['file.zs'], - \ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh', '.zcompdump', '.zlogin', '.zlogout', '.zshenv', '.zshrc', '.zcompdump-file', '.zlog', '.zlog-file', '.zsh', '.zsh-file', 'any/etc/zprofile', 'zlog', 'zlog-file', 'zsh', 'zsh-file'], + \ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh', 'file.zsh-theme', 'file.zunit', + \ '.zcompdump', '.zlogin', '.zlogout', '.zshenv', '.zshrc', '.zsh_history', + \ '.zcompdump-file', '.zlog', '.zlog-file', '.zsh', '.zsh-file', + \ 'any/etc/zprofile', 'zlog', 'zlog-file', 'zsh', 'zsh-file'], \ - \ 'help': [$VIMRUNTIME . '/doc/help.txt'], + \ 'help': [$VIMRUNTIME .. '/doc/help.txt'], \ } endfunc -let s:filename_case_checks = { +func s:GetFilenameCaseChecks() abort + return { \ 'modula2': ['file.DEF'], \ 'bzl': ['file.BUILD', 'BUILD', 'BUCK'], \ } +endfunc func CheckItems(checks) set noswapfile @@ -792,26 +905,29 @@ func Test_filetype_detection() filetype on call CheckItems(s:GetFilenameChecks()) if has('fname_case') - call CheckItems(s:filename_case_checks) + call CheckItems(s:GetFilenameCaseChecks()) endif filetype off endfunc " Content lines that should not result in filetype detection -let s:false_positive_checks = { +func s:GetFalsePositiveChecks() abort + return { \ '': [['test execve("/usr/bin/pstree", ["pstree"], 0x7ff0 /* 63 vars */) = 0']], \ } +endfunc " Filetypes detected from the file contents by scripts.vim -let s:script_checks = { +func s:GetScriptChecks() abort + return { \ 'virata': [['% Virata'], - \ ['', '% Virata'], - \ ['', '', '% Virata'], - \ ['', '', '', '% Virata'], - \ ['', '', '', '', '% Virata']], + \ ['', '% Virata'], + \ ['', '', '% Virata'], + \ ['', '', '', '% Virata'], + \ ['', '', '', '', '% Virata']], \ 'strace': [['execve("/usr/bin/pstree", ["pstree"], 0x7ff0 /* 63 vars */) = 0'], - \ ['15:17:47 execve("/usr/bin/pstree", ["pstree"], ... "_=/usr/bin/strace"]) = 0'], - \ ['__libc_start_main and something']], + \ ['15:17:47 execve("/usr/bin/pstree", ["pstree"], ... "_=/usr/bin/strace"]) = 0'], + \ ['__libc_start_main and something']], \ 'clojure': [['#!/path/clojure']], \ 'scala': [['#!/path/scala']], \ 'sh': [['#!/path/sh'], @@ -875,9 +991,11 @@ let s:script_checks = { \ 'janet': [['#!/path/janet']], \ 'dart': [['#!/path/dart']], \ } +endfunc " Various forms of "env" optional arguments. -let s:script_env_checks = { +func s:GetScriptEnvChecks() abort + return { \ 'perl': [['#!/usr/bin/env VAR=val perl']], \ 'scala': [['#!/usr/bin/env VAR=val VVAR=vval scala']], \ 'awk': [['#!/usr/bin/env VAR=val -i awk']], @@ -887,6 +1005,7 @@ let s:script_env_checks = { \ 'wml': [['#!/usr/bin/env VAR=val --split-string wml']], \ 'nix': [['#!/usr/bin/env nix-shell']], \ } +endfunc func Run_script_detection(test_dict) filetype on @@ -902,9 +1021,9 @@ func Run_script_detection(test_dict) endfunc func Test_script_detection() - call Run_script_detection(s:false_positive_checks) - call Run_script_detection(s:script_checks) - call Run_script_detection(s:script_env_checks) + call Run_script_detection(s:GetFalsePositiveChecks()) + call Run_script_detection(s:GetScriptChecks()) + call Run_script_detection(s:GetScriptEnvChecks()) endfunc func Test_setfiletype_completion() @@ -1570,14 +1689,14 @@ func Test_mod_file() call assert_equal('pim', b:modula2.dialect) bwipe! - " Modula-2 program MODULE with priorty (and uppercase extension) + " Modula-2 program MODULE with priority (and uppercase extension) call writefile(['MODULE Module2Mod [42];'], 'Xfile.MOD') split Xfile.MOD call assert_equal('modula2', &filetype) call assert_equal('pim', b:modula2.dialect) bwipe! - " Modula-2 implementation MODULE with priorty (and uppercase extension) + " Modula-2 implementation MODULE with priority (and uppercase extension) call writefile(['IMPLEMENTATION MODULE Module2Mod [42];'], 'Xfile.MOD') split Xfile.MOD call assert_equal('modula2', &filetype) @@ -2328,4 +2447,26 @@ func Test_def_file() filetype off endfunc +func Test_uci_file() + filetype on + + call mkdir('any/etc/config', 'pR') + call writefile(['config firewall'], 'any/etc/config/firewall', 'D') + split any/etc/config/firewall + call assert_equal('uci', &filetype) + bwipe! + + call writefile(['# config for nginx here'], 'any/etc/config/firewall', 'D') + split any/etc/config/firewall + call assert_notequal('uci', &filetype) + bwipe! + + call writefile(['# Copyright Cool Cats 1997', 'config firewall'], 'any/etc/config/firewall', 'D') + split any/etc/config/firewall + call assert_equal('uci', &filetype) + bwipe! + + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_format.vim b/test/old/testdir/test_format.vim index d3578e7165..36795c87ea 100644 --- a/test/old/testdir/test_format.vim +++ b/test/old/testdir/test_format.vim @@ -105,67 +105,6 @@ func Test_printf_pos_misc() END call CheckLegacyAndVim9Success(lines) - call CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 1, 3, 4)"], "E767:") - - call CheckLegacyAndVim9Failure(["call printf('%2$d%d', 1, 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%d%2$d', 1, 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%2$*1$d%d', 1, 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%d%2$*1$d', 1, 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%2$.*1$d%d', 1, 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%d%2$.*1$d', 1, 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%1$%')"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%1$')"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%1$_')"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%1$*3$.*d', 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%1$*.*2$d', 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%1$*.*d', 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%*.*1$d', 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%*1$.*d', 3)"], "E1500:") - call CheckLegacyAndVim9Failure(["call printf('%*1$.*1$d', 3)"], "E1500:") - - call CheckLegacyAndVim9Failure(["call printf('%2$d', 3, 3)"], "E1501:") - - call CheckLegacyAndVim9Failure(["call printf('%2$*1$d %1$ld', 3, 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$p %1$*1$d', 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$f %1$*1$d', 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$lud %1$*1$d', 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$llud %1$*1$d', 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$lld %1$*1$d', 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$c %1$*1$d', 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$ld %1$*1$d', 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$ld %2$*1$d', 3, 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$*1$ld', 3)"], "E1502:") - call CheckLegacyAndVim9Failure(["call printf('%1$*1$.*1$ld', 3)"], "E1502:") - - call CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 3)"], "E1503:") - - call CheckLegacyAndVim9Failure(["call printf('%1$d %1$s', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$ld %1$s', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$ud %1$d', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$s %1$f', 3.0)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$*1$d %1$ld', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$p %1$d', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$f %1$d', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$lud %1$d', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$llud %1$d', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$lld %1$d', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$c %1$d', 3)"], "E1504:") - call CheckLegacyAndVim9Failure(["call printf('%1$ld %1$d', 3)"], "E1504:") - - call CheckLegacyAndVim9Failure(["call printf('%1$.2$d', 3)"], "E1505:") - call CheckLegacyAndVim9Failure(["call printf('%01$d', 3)"], "E1505:") - call CheckLegacyAndVim9Failure(["call printf('%01$0d', 3)"], "E1505:") - call CheckLegacyAndVim9Failure(["call printf('%1$*2d', 3)"], "E1505:") - call CheckLegacyAndVim9Failure(["call printf('%1$*3.*2$d', 3)"], "E1505:") - call CheckLegacyAndVim9Failure(["call printf('%1$*3$.2$d', 3)"], "E1505:") - call CheckLegacyAndVim9Failure(["call printf('%1$*3$.*2d', 3)"], "E1505:") - call CheckLegacyAndVim9Failure(["call printf('%1$1$.5d', 5)"], "E1505:") - call CheckLegacyAndVim9Failure(["call printf('%1$5.1$d', 5)"], "E1505:") - call CheckLegacyAndVim9Failure(["call printf('%1$1$.1$d', 5)"], "E1505:") endfunc func Test_printf_pos_float() @@ -287,8 +226,6 @@ func Test_printf_pos_float() call assert_equal("str2float('nan')", printf('%1$S', -0.0 / 0.0)) END call CheckLegacyAndVim9Success(lines) - - call CheckLegacyAndVim9Failure(['echo printf("%f", "a")'], 'E807:') endfunc func Test_printf_pos_errors() @@ -299,6 +236,111 @@ func Test_printf_pos_errors() call CheckLegacyAndVim9Failure(['echo printf("%1$s")'], 'E1503:') call CheckLegacyAndVim9Failure(['echo printf("%1$d", 1.2)'], 'E805:') call CheckLegacyAndVim9Failure(['echo printf("%1$f")'], 'E1503:') + + call CheckLegacyAndVim9Failure(['echo printf("%f", "a")'], 'E807:') + + call CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 1, 3, 4)"], "E767:") + + call CheckLegacyAndVim9Failure(["call printf('%2$d%d', 1, 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%d%2$d', 1, 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%2$*1$d%d', 1, 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%d%2$*1$d', 1, 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%2$.*1$d%d', 1, 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%d%2$.*1$d', 1, 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%1$%')"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%1$')"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%1$_')"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%1$*3$.*d', 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%1$*.*2$d', 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%1$*.*d', 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%*.*1$d', 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%*1$.*d', 3)"], "E1500:") + call CheckLegacyAndVim9Failure(["call printf('%*1$.*1$d', 3)"], "E1500:") + + call CheckLegacyAndVim9Failure(["call printf('%2$d', 3, 3)"], "E1501:") + + call CheckLegacyAndVim9Failure(["call printf('%2$*1$d %1$ld', 3, 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$p %1$*1$d', 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$f %1$*1$d', 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$lud %1$*1$d', 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$llud %1$*1$d', 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$lld %1$*1$d', 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$c %1$*1$d', 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$ld %1$*1$d', 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$ld %2$*1$d', 3, 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$*1$ld', 3)"], "E1502:") + call CheckLegacyAndVim9Failure(["call printf('%1$*1$.*1$ld', 3)"], "E1502:") + + call CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 3)"], "E1503:") + + call CheckLegacyAndVim9Failure(["call printf('%1$d %1$s', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$ld %1$s', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$ud %1$d', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$s %1$f', 3.0)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$*1$d %1$ld', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$p %1$d', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$f %1$d', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$lud %1$d', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$llud %1$d', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$lld %1$d', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$c %1$d', 3)"], "E1504:") + call CheckLegacyAndVim9Failure(["call printf('%1$ld %1$d', 3)"], "E1504:") + + call CheckLegacyAndVim9Failure(["call printf('%1$.2$d', 3)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%01$d', 3)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%01$0d', 3)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%1$*2d', 3)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%1$*3.*2$d', 3)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%1$*3$.2$d', 3)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%1$*3$.*2d', 3)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%1$1$.5d', 5)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%1$5.1$d', 5)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%1$1$.1$d', 5)"], "E1505:") + + call CheckLegacyAndVim9Failure(["call printf('%.123456789$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%.123456789d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%123456789$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%123456789d', 5)"], "E1510:") + + call CheckLegacyAndVim9Failure(["call printf('%123456789$5.5d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$123456789.5d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$5.123456789d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%123456789$987654321.5d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$123456789.987654321d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%123456789$5.987654321d', 5)"], "E1510:") + + call CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.5d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.5d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$*1$.123456789d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%123456789$*987654321$.5d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.987654321d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.987654321d', 5)"], "E1510:") + + call CheckLegacyAndVim9Failure(["call printf('%123456789$5.*1$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$123456789.*1$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$5.*123456789$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%123456789$987654321.*1$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$123456789.*987654321$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%123456789$5.*987654321$d', 5)"], "E1510:") + + call CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.*1$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.*1$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$*1$.*123456789d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%123456789$*987654321$.*1$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.*987654321$d', 5)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.*987654321$d', 5)"], "E1510:") + + call CheckLegacyAndVim9Failure(["call printf('%1$*2$.*1$d', 5, 9999)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$*1$.*2$d', 5, 9999)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%2$*3$.*1$d', 5, 9123, 9321)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%1$*2$.*3$d', 5, 9123, 9321)"], "E1510:") + call CheckLegacyAndVim9Failure(["call printf('%2$*1$.*3$d', 5, 9123, 9312)"], "E1510:") + + call CheckLegacyAndVim9Failure(["call printf('%1$*2$d', 5, 9999)"], "E1510:") endfunc func Test_printf_pos_64bit() diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim index 3f9ac94782..18d0d9aa5d 100644 --- a/test/old/testdir/test_functions.vim +++ b/test/old/testdir/test_functions.vim @@ -98,6 +98,11 @@ func Test_err_teapot() call assert_fails('call err_teapot(expr)', "E503: Coffee is currently not available") endfunc +func Test_islocked() + call assert_fails('call islocked(99)', 'E475:') + call assert_fails('call islocked("s: x")', 'E488:') +endfunc + func Test_len() call assert_equal(1, len(0)) call assert_equal(2, len(12)) @@ -2590,8 +2595,6 @@ endfunc " Test confirm({msg} [, {choices} [, {default} [, {type}]]]) func Test_confirm() - " requires a UI to be active - throw 'Skipped: use test/functional/vimscript/input_spec.lua' CheckUnix CheckNotGui @@ -3401,6 +3404,73 @@ func Test_getmousepos() \ column: 8, \ coladd: 21, \ }, getmousepos()) + + 30vnew + setlocal smoothscroll number + call setline(1, join(range(100))) + exe "normal! \<C-E>" + call Ntest_setmouse(1, 5) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 5, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 5, + \ line: 1, + \ column: 27, + \ coladd: 0, + \ }, getmousepos()) + call Ntest_setmouse(2, 5) + call assert_equal(#{ + \ screenrow: 2, + \ screencol: 5, + \ winid: win_getid(), + \ winrow: 2, + \ wincol: 5, + \ line: 1, + \ column: 53, + \ coladd: 0, + \ }, getmousepos()) + + exe "normal! \<C-E>" + call Ntest_setmouse(1, 5) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 5, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 5, + \ line: 1, + \ column: 53, + \ coladd: 0, + \ }, getmousepos()) + call Ntest_setmouse(2, 5) + call assert_equal(#{ + \ screenrow: 2, + \ screencol: 5, + \ winid: win_getid(), + \ winrow: 2, + \ wincol: 5, + \ line: 1, + \ column: 79, + \ coladd: 0, + \ }, getmousepos()) + + vert resize 4 + call Ntest_setmouse(2, 2) + " This used to crash Vim + call assert_equal(#{ + \ screenrow: 2, + \ screencol: 2, + \ winid: win_getid(), + \ winrow: 2, + \ wincol: 2, + \ line: 1, + \ column: 53, + \ coladd: 0, + \ }, getmousepos()) + + bwipe! bwipe! endfunc @@ -3610,4 +3680,55 @@ func Test_glob_extended_mswin() call delete('Xtestglob', 'rf') endfunc +" Tests for the slice() function. +func Test_slice() + let lines =<< trim END + call assert_equal([1, 2, 3, 4, 5], slice(range(6), 1)) + call assert_equal([2, 3, 4, 5], slice(range(6), 2)) + call assert_equal([2, 3], slice(range(6), 2, 4)) + call assert_equal([0, 1, 2, 3], slice(range(6), 0, 4)) + call assert_equal([1, 2, 3], slice(range(6), 1, 4)) + call assert_equal([1, 2, 3, 4], slice(range(6), 1, -1)) + call assert_equal([1, 2], slice(range(6), 1, -3)) + call assert_equal([1], slice(range(6), 1, -4)) + call assert_equal([], slice(range(6), 1, -5)) + call assert_equal([], slice(range(6), 1, -6)) + + call assert_equal(0z1122334455, slice(0z001122334455, 1)) + call assert_equal(0z22334455, slice(0z001122334455, 2)) + call assert_equal(0z2233, slice(0z001122334455, 2, 4)) + call assert_equal(0z00112233, slice(0z001122334455, 0, 4)) + call assert_equal(0z112233, slice(0z001122334455, 1, 4)) + call assert_equal(0z11223344, slice(0z001122334455, 1, -1)) + call assert_equal(0z1122, slice(0z001122334455, 1, -3)) + call assert_equal(0z11, slice(0z001122334455, 1, -4)) + call assert_equal(0z, slice(0z001122334455, 1, -5)) + call assert_equal(0z, slice(0z001122334455, 1, -6)) + + call assert_equal('12345', slice('012345', 1)) + call assert_equal('2345', slice('012345', 2)) + call assert_equal('23', slice('012345', 2, 4)) + call assert_equal('0123', slice('012345', 0, 4)) + call assert_equal('123', slice('012345', 1, 4)) + call assert_equal('1234', slice('012345', 1, -1)) + call assert_equal('12', slice('012345', 1, -3)) + call assert_equal('1', slice('012345', 1, -4)) + call assert_equal('', slice('012345', 1, -5)) + call assert_equal('', slice('012345', 1, -6)) + + #" Composing chars are treated as a part of the preceding base char. + call assert_equal('β̳́γ̳̂δ̳̃ε̳̄ζ̳̅', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1)) + call assert_equal('γ̳̂δ̳̃ε̳̄ζ̳̅', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(2)) + call assert_equal('γ̳̂δ̳̃', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(2, 4)) + call assert_equal('ὰ̳β̳́γ̳̂δ̳̃', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(0, 4)) + call assert_equal('β̳́γ̳̂δ̳̃', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, 4)) + call assert_equal('β̳́γ̳̂δ̳̃ε̳̄', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -1)) + call assert_equal('β̳́γ̳̂', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -3)) + call assert_equal('β̳́', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -4)) + call assert_equal('', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -5)) + call assert_equal('', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -6)) + END + call CheckLegacyAndVim9Success(lines) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_global.vim b/test/old/testdir/test_global.vim index 44a8784348..8c7e7cb26b 100644 --- a/test/old/testdir/test_global.vim +++ b/test/old/testdir/test_global.vim @@ -96,7 +96,16 @@ func Test_global_newline() close! endfunc -func Test_wrong_delimiter() +" Test :g with ? as delimiter. +func Test_global_question_delimiter() + new + call setline(1, ['aaaaa', 'b?bbb', 'ccccc', 'ddd?d', 'eeeee']) + g?\??delete + call assert_equal(['aaaaa', 'ccccc', 'eeeee'], getline(1, '$')) + bwipe! +endfunc + +func Test_global_wrong_delimiter() call assert_fails('g x^bxd', 'E146:') endfunc diff --git a/test/old/testdir/test_highlight.vim b/test/old/testdir/test_highlight.vim index bcaa1dac5d..0a64c63d30 100644 --- a/test/old/testdir/test_highlight.vim +++ b/test/old/testdir/test_highlight.vim @@ -825,6 +825,24 @@ func Test_highlight_cmd_errors() let &t_fo = "" endfunc +" Test for User group highlighting used in the statusline +func Test_highlight_User() + CheckNotGui + hi User1 ctermfg=12 + redraw! + call assert_equal('12', synIDattr(synIDtrans(hlID('User1')), 'fg')) + hi clear +endfunc + +" Test for MsgArea highlighting +func Test_highlight_MsgArea() + CheckNotGui + hi MsgArea ctermfg=20 + redraw! + call assert_equal('20', synIDattr(synIDtrans(hlID('MsgArea')), 'fg')) + hi clear +endfunc + " Test for using RGB color values in a highlight group func Test_xxlast_highlight_RGB_color() CheckCanRunGui diff --git a/test/old/testdir/test_history.vim b/test/old/testdir/test_history.vim index 482328ab4a..b288abc3f6 100644 --- a/test/old/testdir/test_history.vim +++ b/test/old/testdir/test_history.vim @@ -96,6 +96,60 @@ function Test_History() call assert_fails('history xyz', 'E488:') call assert_fails('history ,abc', 'E488:') call assert_fails('call histdel(":", "\\%(")', 'E53:') + + " Test for filtering the history list + let hist_filter = execute(':filter /_\d/ :history all')->split('\n') + call assert_equal(20, len(hist_filter)) + let expected = [' # cmd history', + \ ' 2 text_2', + \ ' 3 text_3', + \ '> 4 text_4', + \ ' # search history', + \ ' 2 text_2', + \ ' 3 text_3', + \ '> 4 text_4', + \ ' # expr history', + \ ' 2 text_2', + \ ' 3 text_3', + \ '> 4 text_4', + \ ' # input history', + \ ' 2 text_2', + \ ' 3 text_3', + \ '> 4 text_4', + \ ' # debug history', + \ ' 2 text_2', + \ ' 3 text_3', + \ '> 4 text_4'] + call assert_equal(expected, hist_filter) + + let cmds = {'c': 'cmd', 's': 'search', 'e': 'expr', 'i': 'input', 'd': 'debug'} + for h in sort(keys(cmds)) + " find some items + let hist_filter = execute(':filter /_\d/ :history ' .. h)->split('\n') + call assert_equal(4, len(hist_filter)) + + let expected = [' # ' .. cmds[h] .. ' history', + \ ' 2 text_2', + \ ' 3 text_3', + \ '> 4 text_4'] + call assert_equal(expected, hist_filter) + + " Search for an item that is not there + let hist_filter = execute(':filter /XXXX/ :history ' .. h)->split('\n') + call assert_equal(1, len(hist_filter)) + + let expected = [' # ' .. cmds[h] .. ' history'] + call assert_equal(expected, hist_filter) + + " Invert the filter condition, find non-matches + let hist_filter = execute(':filter! /_3$/ :history ' .. h)->split('\n') + call assert_equal(3, len(hist_filter)) + + let expected = [' # ' .. cmds[h] .. ' history', + \ ' 2 text_2', + \ '> 4 text_4'] + call assert_equal(expected, hist_filter) + endfor endfunction function Test_history_truncates_long_entry() @@ -254,7 +308,7 @@ func Test_history_crypt_key() set key& bs& ts& endfunc -" The following used to overflow and causing an use-after-free +" The following used to overflow and causing a use-after-free func Test_history_max_val() set history=10 diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim index 917c37c324..3f67a06999 100644 --- a/test/old/testdir/test_ins_complete.vim +++ b/test/old/testdir/test_ins_complete.vim @@ -388,6 +388,62 @@ func Test_completefunc_info() set completefunc& endfunc +func CompleteInfoUserDefinedFn(findstart, query) + " User defined function (i_CTRL-X_CTRL-U) + if a:findstart + return col('.') + endif + return [{'word': 'foo'}, {'word': 'bar'}, {'word': 'baz'}, {'word': 'qux'}] +endfunc + +func CompleteInfoTestUserDefinedFn(mvmt, idx, noselect) + new + if a:noselect + set completeopt=menuone,popup,noinsert,noselect + else + set completeopt=menu,preview + endif + set completefunc=CompleteInfoUserDefinedFn + call feedkeys("i\<C-X>\<C-U>" . a:mvmt . "\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx") + let completed = a:idx != -1 ? ['foo', 'bar', 'baz', 'qux']->get(a:idx) : '' + call assert_equal(completed. "{'pum_visible': 1, 'mode': 'function', 'selected': " . a:idx . ", 'items': [" . + \ "{'word': 'foo', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " . + \ "{'word': 'bar', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " . + \ "{'word': 'baz', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " . + \ "{'word': 'qux', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}" . + \ "]}", getline(1)) + bwipe! + set completeopt& + set completefunc& +endfunc + +func Test_complete_info_user_defined_fn() + " forward + call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>", 1, v:true) + call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>", 2, v:true) + call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>", 2, v:false) + call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>", 3, v:false) + call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>\<C-N>", -1, v:false) + " backward + call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>", 2, v:true) + call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>", 1, v:true) + call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>\<C-P>\<C-P>", -1, v:true) + call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>", 3, v:false) + call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>", 2, v:false) + " forward backward + call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>\<C-P>", 1, v:true) + call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-P>", 0, v:true) + call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>\<C-P>", 2, v:false) + call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>\<C-N>\<C-P>", 3, v:false) + call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-P>", 1, v:false) + " backward forward + call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>\<C-P>\<C-P>\<C-N>", 0, v:true) + call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>\<C-N>", 2, v:true) + call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>\<C-P>\<C-P>\<C-N>", 1, v:false) + call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>\<C-N>", 3, v:false) + call CompleteInfoTestUserDefinedFn("\<C-P>\<C-N>\<C-N>", 1, v:false) +endfunc + " Test that mouse scrolling/movement should not interrupt completion. func Test_mouse_scroll_move_during_completion() new @@ -1152,7 +1208,7 @@ func Test_complete_wholeline_unlistedbuf() edit Xfile1 enew set complete=U - " completing from a unloaded buffer should fail + " completing from an unloaded buffer should fail exe "normal! ia\<C-X>\<C-L>\<C-P>" call assert_equal('a', getline(1)) %d @@ -2406,7 +2462,7 @@ func Test_complete_info_index() call assert_equal(-1, g:compl_info['selected']) call feedkeys("Go\<C-X>\<C-N>\<C-P>\<F5>\<Esc>_dd", 'tx') - call assert_equal(0, g:compl_info['selected']) + call assert_equal(5, g:compl_info['selected']) call assert_equal(6 , len(g:compl_info['items'])) call assert_equal("fff", g:compl_info['items'][g:compl_info['selected']]['word']) call feedkeys("Go\<C-X>\<C-N>\<C-N>\<C-N>\<C-N>\<C-N>\<C-N>\<C-N>\<C-N>\<C-N>\<F5>\<Esc>_dd", 'tx') @@ -2434,4 +2490,26 @@ func Test_complete_changed_complete_info() call StopVimInTerminal(buf) endfunc +func Test_completefunc_first_call_complete_add() + new + + func Complete(findstart, base) abort + if a:findstart + let col = col('.') + call complete_add('#') + return col - 1 + else + return [] + endif + endfunc + + set completeopt=longest completefunc=Complete + " This used to cause heap-buffer-overflow + call assert_fails('call feedkeys("ifoo#\<C-X>\<C-U>", "xt")', 'E840:') + + delfunc Complete + set completeopt& completefunc& + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab nofoldenable diff --git a/test/old/testdir/test_let.vim b/test/old/testdir/test_let.vim index a9cc8a14a4..d37af45aaa 100644 --- a/test/old/testdir/test_let.vim +++ b/test/old/testdir/test_let.vim @@ -397,6 +397,45 @@ func Test_let_heredoc_fails() call assert_report('Caught exception: ' .. v:exception) endtry + try + let @- =<< trim TEXT + change + insert + append + TEXT + call assert_report('No exception thrown') + catch /E730:/ + catch + call assert_report('Caught exception: ' .. v:exception) + endtry + + try + let [] =<< trim TEXT + TEXT + call assert_report('No exception thrown') + catch /E475:/ + catch + call assert_report('Caught exception: ' .. v:exception) + endtry + + try + let [a b c] =<< trim TEXT + TEXT + call assert_report('No exception thrown') + catch /E475:/ + catch + call assert_report('Caught exception: ' .. v:exception) + endtry + + try + let [a; b; c] =<< trim TEXT + TEXT + call assert_report('No exception thrown') + catch /E452:/ + catch + call assert_report('Caught exception: ' .. v:exception) + endtry + let text =<< trim END func WrongSyntax() let v =<< that there @@ -503,6 +542,13 @@ END XX call assert_equal(['Line1'], var1) + let var1 =<< trim XX " comment + Line1 + Line2 + Line3 + XX + call assert_equal(['Line1', ' Line2', 'Line3'], var1) + " ignore "endfunc" let var1 =<< END something @@ -569,6 +615,22 @@ append END call assert_equal(['change', 'insert', 'append'], [a, b, c]) + " unpack assignment with semicolon + let [a; b] =<< END +change +insert +append +END + call assert_equal(['change', ['insert', 'append']], [a, b]) + + " unpack assignment with registers + let [@/, @", @-] =<< END +change +insert +append +END + call assert_equal(['change', 'insert', 'append'], [@/, @", @-]) + " curly braces name and list slice assignment let foo_3_bar = ['', '', ''] let foo_{1 + 2}_bar[ : ] =<< END @@ -625,6 +687,48 @@ END END call assert_equal(['let a = {abc}', 'let b = X', 'let c = {'], code) + " Evaluate a dictionary + let d1 = #{a: 10, b: 'ss', c: {}} + let code =<< eval trim END + let d2 = {d1} + END + call assert_equal(["let d2 = {'a': 10, 'b': 'ss', 'c': {}}"], code) + + " Empty dictionary + let d1 = {} + let code =<< eval trim END + let d2 = {d1} + END + call assert_equal(["let d2 = {}"], code) + + " null dictionary + let d1 = v:_null_dict + let code =<< eval trim END + let d2 = {d1} + END + call assert_equal(["let d2 = {}"], code) + + " Evaluate a List + let l1 = ['a', 'b', 'c'] + let code =<< eval trim END + let l2 = {l1} + END + call assert_equal(["let l2 = ['a', 'b', 'c']"], code) + + " Empty List + let l1 = [] + let code =<< eval trim END + let l2 = {l1} + END + call assert_equal(["let l2 = []"], code) + + " Null List + let l1 = v:_null_list + let code =<< eval trim END + let l2 = {l1} + END + call assert_equal(["let l2 = []"], code) + let code = 'xxx' let code =<< eval trim END let n = {5 + @@ -658,6 +762,34 @@ END LINES call CheckScriptFailure(lines, 'E15:') + " Test for using heredoc in a single string using :execute or execute() + for [cmd, res] in items({ + \ "let x =<< trim END\n one\n two\nEND": ['one', 'two'], + \ "let x =<< trim END\n one\n two\nEND": ['one', ' two'], + \ " let x =<< trim END\n one\n two\n END": ['one', 'two'], + \ " let x =<< trim END\n one\n two\n END": ['one', ' two'], + \ "let x =<< END\n one\n two\nEND": [' one', ' two'], + \ "let x =<< END\none\ntwo\nEND": ['one', 'two'], + \ "let x =<< END \" comment\none\ntwo\nEND": ['one', 'two'], + \ }) + execute cmd + call assert_equal(res, x) + unlet x + call assert_equal($"\n{string(res)}", execute($"{cmd}\necho x")) + unlet x + endfor + for [cmd, err] in items({ + \ "let x =<<\none\ntwo": "E172:", + \ "let x =<< trim\n one\n two": "E172:", + \ "let x =<< end\none\ntwo\nend": "E221:", + \ "let x =<< END\none\ntwo": "E990: Missing end marker 'END'", + \ "let x =<< END !\none\ntwo\nEND": "E488: Trailing characters: !", + \ "let x =<< eval END\none\ntwo{y}\nEND": "E121: Undefined variable: y", + \ }) + call assert_fails('execute cmd', err) + call assert_fails('call execute(cmd)', err) + endfor + " skipped heredoc if 0 let msg =<< trim eval END diff --git a/test/old/testdir/test_listdict.vim b/test/old/testdir/test_listdict.vim index 649d5f5c6c..0adc3286f9 100644 --- a/test/old/testdir/test_listdict.vim +++ b/test/old/testdir/test_listdict.vim @@ -1441,4 +1441,10 @@ func Test_indexof() delfunc TestIdx endfunc +func Test_extendnew_leak() + " This used to leak memory + for i in range(100) | silent! call extendnew([], [], []) | endfor + for i in range(100) | silent! call extendnew({}, {}, {}) | endfor +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_mapping.vim b/test/old/testdir/test_mapping.vim index 623228b347..2a4d068dea 100644 --- a/test/old/testdir/test_mapping.vim +++ b/test/old/testdir/test_mapping.vim @@ -236,21 +236,25 @@ func Test_map_meta_multibyte() endfunc func Test_map_super_quotes() - if has('gui_gtk') || has('gui_gtk3') || has("macos") - imap <D-"> foo - call feedkeys("Go-\<*D-\">-\<Esc>", "xt") - call assert_equal("-foo-", getline('$')) - set nomodified - iunmap <D-"> + if "\<D-j>"[-1:] == '>' + throw 'Skipped: <D- modifier not supported' endif + + imap <D-"> foo + call feedkeys("Go-\<*D-\">-\<Esc>", "xt") + call assert_equal("-foo-", getline('$')) + set nomodified + iunmap <D-"> endfunc func Test_map_super_multibyte() - if has('gui_gtk') || has('gui_gtk3') || has("macos") - imap <D-á> foo - call assert_match('i <D-á>\s*foo', execute('imap')) - iunmap <D-á> + if "\<D-j>"[-1:] == '>' + throw 'Skipped: <D- modifier not supported' endif + + imap <D-á> foo + call assert_match('i <D-á>\s*foo', execute('imap')) + iunmap <D-á> endfunc func Test_abbr_after_line_join() @@ -1693,7 +1697,7 @@ func Test_map_after_timed_out_nop() inoremap ab TEST inoremap a <Nop> END - call writefile(lines, 'Xtest_map_after_timed_out_nop') + call writefile(lines, 'Xtest_map_after_timed_out_nop', 'D') let buf = RunVimInTerminal('-S Xtest_map_after_timed_out_nop', #{rows: 6}) " Enter Insert mode @@ -1710,7 +1714,49 @@ func Test_map_after_timed_out_nop() " clean up call StopVimInTerminal(buf) - call delete('Xtest_map_after_timed_out_nop') +endfunc + +" Test 'showcmd' behavior with a partial mapping +func Test_showcmd_part_map() + CheckRunVimInTerminal + + let lines =<< trim END + set notimeout showcmd + nnoremap ,a <Ignore> + nnoremap ;a <Ignore> + nnoremap Àa <Ignore> + nnoremap Ëa <Ignore> + nnoremap βa <Ignore> + nnoremap ωa <Ignore> + nnoremap …a <Ignore> + nnoremap <C-W>a <Ignore> + END + call writefile(lines, 'Xtest_showcmd_part_map', 'D') + let buf = RunVimInTerminal('-S Xtest_showcmd_part_map', #{rows: 6}) + + call term_sendkeys(buf, ":set noruler | echo\<CR>") + call WaitForAssert({-> assert_equal('', term_getline(buf, 6))}) + + for c in [',', ';', 'À', 'Ë', 'β', 'ω', '…'] + call term_sendkeys(buf, c) + call WaitForAssert({-> assert_equal(c, trim(term_getline(buf, 6)))}) + call term_sendkeys(buf, 'a') + call WaitForAssert({-> assert_equal('', trim(term_getline(buf, 6)))}) + endfor + + call term_sendkeys(buf, "\<C-W>") + call WaitForAssert({-> assert_equal('^W', trim(term_getline(buf, 6)))}) + call term_sendkeys(buf, 'a') + call WaitForAssert({-> assert_equal('', trim(term_getline(buf, 6)))}) + + " Use feedkeys() as terminal buffer cannot forward unsimplified Ctrl-W. + " This is like typing Ctrl-W with modifyOtherKeys enabled. + call term_sendkeys(buf, ':call feedkeys("\<*C-W>", "m")' .. " | echo\<CR>") + call WaitForAssert({-> assert_equal('^W', trim(term_getline(buf, 6)))}) + call term_sendkeys(buf, 'a') + call WaitForAssert({-> assert_equal('', trim(term_getline(buf, 6)))}) + + call StopVimInTerminal(buf) endfunc func Test_using_past_typeahead() diff --git a/test/old/testdir/test_match.vim b/test/old/testdir/test_match.vim index 1cb52b8b2a..ddf032d593 100644 --- a/test/old/testdir/test_match.vim +++ b/test/old/testdir/test_match.vim @@ -310,6 +310,7 @@ func Test_matchaddpos_error() " call assert_fails("call matchaddpos('Error', [{}])", 'E290:') call assert_fails("call matchaddpos('Error', [{}])", 'E5031:') call assert_equal(-1, matchaddpos('Error', v:_null_list)) + call assert_equal(-1, matchaddpos('Error', [])) call assert_fails("call matchaddpos('Error', [1], [], 1)", 'E745:') endfunc @@ -427,13 +428,11 @@ func Test_match_tab_with_linebreak() call setline(1, "\tix") call matchadd('ErrorMsg', '\t') END - call writefile(lines, 'XscriptMatchTabLinebreak') + call writefile(lines, 'XscriptMatchTabLinebreak', 'D') let buf = RunVimInTerminal('-S XscriptMatchTabLinebreak', #{rows: 10}) call VerifyScreenDump(buf, 'Test_match_tab_linebreak', {}) call StopVimInTerminal(buf) - call delete('XscriptMatchTabLinebreak') endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_matchparen.vim b/test/old/testdir/test_matchparen.vim index 3138180c66..ab425b046a 100644 --- a/test/old/testdir/test_matchparen.vim +++ b/test/old/testdir/test_matchparen.vim @@ -61,6 +61,31 @@ func Test_matchparen_clear_highlight() call StopVimInTerminal(buf) endfunc +" Test for matchparen highlight when switching buffer in win_execute() +func Test_matchparen_win_execute() + CheckScreendump + + let lines =<< trim END + source $VIMRUNTIME/plugin/matchparen.vim + let s:win = win_getid() + call setline(1, '{}') + split + + func SwitchBuf() + call win_execute(s:win, 'enew | buffer #') + endfunc + END + call writefile(lines, 'XMatchparenWinExecute', 'D') + let buf = RunVimInTerminal('-S XMatchparenWinExecute', #{rows: 5}) + call VerifyScreenDump(buf, 'Test_matchparen_win_execute_1', {}) + + " Switching buffer away and back shouldn't change matchparen highlight. + call term_sendkeys(buf, ":call SwitchBuf()\<CR>:\<Esc>") + call VerifyScreenDump(buf, 'Test_matchparen_win_execute_1', {}) + + call StopVimInTerminal(buf) +endfunc + " Test for scrolling that modifies buffer during visual block func Test_matchparen_pum_clear() CheckScreendump diff --git a/test/old/testdir/test_mksession.vim b/test/old/testdir/test_mksession.vim index d63d10b44c..7e9cd6c8e8 100644 --- a/test/old/testdir/test_mksession.vim +++ b/test/old/testdir/test_mksession.vim @@ -631,11 +631,11 @@ endfunc func Test_mkview_no_file_name() new - " :mkview or :mkview {nr} should fail in a unnamed buffer. + " :mkview or :mkview {nr} should fail in an unnamed buffer. call assert_fails('mkview', 'E32:') call assert_fails('mkview 1', 'E32:') - " :mkview {file} should succeed in a unnamed buffer. + " :mkview {file} should succeed in an unnamed buffer. mkview Xview help source Xview diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim index 91c058df9e..a2ef07193d 100644 --- a/test/old/testdir/test_normal.vim +++ b/test/old/testdir/test_normal.vim @@ -131,7 +131,7 @@ func Test_normal01_keymodel() call assert_equal([0, 1, 1, 0], getpos("'<")) call assert_equal([0, 3, 1, 0], getpos("'>")) call feedkeys("Gz\<CR>8|\<S-PageUp>y", 'xt') - call assert_equal([0, 2, 1, 0], getpos("'<")) + call assert_equal([0, 3, 1, 0], getpos("'<")) call assert_equal([0, 3, 8, 0], getpos("'>")) " Test for <S-C-Home> and <S-C-End> call cursor(2, 12) @@ -915,12 +915,10 @@ func Test_normal14_page() set scrolloff=0 100 exe "norm! $\<c-b>" - call assert_equal('92', getline('.')) call assert_equal([0, 92, 1, 0, 1], getcurpos()) 100 set nostartofline exe "norm! $\<c-b>" - call assert_equal('92', getline('.')) call assert_equal([0, 92, 2, 0, v:maxcol], getcurpos()) " cleanup set startofline @@ -1287,7 +1285,7 @@ func Test_vert_scroll_cmds() exe "normal \<C-U>" call assert_equal(36, line('.')) exe "normal \<C-U>" - call assert_equal(10, line('.')) + call assert_equal(1, line('.')) exe "normal \<C-U>" call assert_equal(1, line('.')) set scroll& @@ -2370,6 +2368,14 @@ func Test_normal30_changecase() %d call assert_beeps('norm! ~') + " Test with multiple lines + call setline(1, ['AA', 'BBBB', 'CCCCCC', 'DDDDDDDD']) + norm! ggguG + call assert_equal(['aa', 'bbbb', 'cccccc', 'dddddddd'], getline(1, '$')) + norm! GgUgg + call assert_equal(['AA', 'BBBB', 'CCCCCC', 'DDDDDDDD'], getline(1, '$')) + %d + " Test for changing case across lines using 'whichwrap' call setline(1, ['aaaaaa', 'aaaaaa']) normal! gg10~ @@ -3817,8 +3823,8 @@ func Test_normal_vert_scroll_longline() call assert_equal(11, line('.')) call assert_equal(1, winline()) exe "normal \<C-B>" - call assert_equal(10, line('.')) - call assert_equal(3, winline()) + call assert_equal(11, line('.')) + call assert_equal(5, winline()) exe "normal \<C-B>\<C-B>" call assert_equal(5, line('.')) call assert_equal(5, winline()) @@ -4176,12 +4182,8 @@ endfunc " Test for { and } paragraph movements in a single line func Test_brace_single_line() - let text =<< trim [DATA] - foobar one two three - [DATA] - new - call setline(1, text) + call setline(1, ['foobar one two three']) 1 norm! 0} @@ -4191,4 +4193,99 @@ func Test_brace_single_line() bw! endfunc +" Test for Ctrl-B/Ctrl-U in buffer with a single line +func Test_single_line_scroll() + CheckFeature textprop + + new + call setline(1, ['foobar one two three']) + let vt = 'virt_above' + call prop_type_add(vt, {'highlight': 'IncSearch'}) + call prop_add(1, 0, {'type': vt, 'text': '---', 'text_align': 'above'}) + call cursor(1, 1) + + " Ctrl-B/Ctrl-U scroll up with hidden "above" virtual text. + set smoothscroll + exe "normal \<C-E>" + call assert_notequal(0, winsaveview().skipcol) + exe "normal \<C-B>" + call assert_equal(0, winsaveview().skipcol) + exe "normal \<C-E>" + call assert_notequal(0, winsaveview().skipcol) + exe "normal \<C-U>" + call assert_equal(0, winsaveview().skipcol) + + set smoothscroll& + bw! + call prop_type_delete(vt) +endfunc + +" Test for zb in buffer with a single line and filler lines +func Test_single_line_filler_zb() + call setline(1, ['', 'foobar one two three']) + diffthis + new + call setline(1, ['foobar one two three']) + diffthis + + " zb scrolls to reveal filler lines at the start of the buffer. + exe "normal \<C-E>zb" + call assert_equal(1, winsaveview().topfill) + + bw! +endfunc + +" Test for Ctrl-U not getting stuck at end of buffer with 'scrolloff'. +func Test_halfpage_scrolloff_eob() + set scrolloff=5 + + call setline(1, range(1, 100)) + exe "norm! Gzz\<C-U>zz" + call assert_notequal(100, line('.')) + + set scrolloff& + bwipe! +endfunc + +" Test for Ctrl-U/D moving the cursor at the buffer boundaries. +func Test_halfpage_cursor_startend() + call setline(1, range(1, 100)) + exe "norm! jztj\<C-U>" + call assert_equal(1, line('.')) + exe "norm! G\<C-Y>k\<C-D>" + call assert_equal(100, line('.')) + bwipe! +endfunc + +" Test for Ctrl-F/B moving the cursor to the window boundaries. +func Test_page_cursor_topbot() + 10new + call setline(1, range(1, 100)) + exe "norm! gg2\<C-F>" + call assert_equal(17, line('.')) + exe "norm! \<C-B>" + call assert_equal(18, line('.')) + exe "norm! \<C-B>\<C-F>" + call assert_equal(9, line('.')) + bwipe! +endfunc + +" Test for Ctrl-D with long line +func Test_halfpage_longline() + 10new + call setline(1, ['long'->repeat(1000), 'short']) + exe "norm! \<C-D>" + call assert_equal(2, line('.')) + bwipe! +endfunc + +" Test for Ctrl-E with long line and very narrow window, +" used to cause an inifite loop +func Test_scroll_longline_no_loop() + 4vnew + setl smoothscroll number showbreak=> scrolloff=2 + call setline(1, repeat(['Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'], 3)) + exe "normal! \<C-E>" + bwipe! +endfunc " vim: shiftwidth=2 sts=2 expandtab nofoldenable diff --git a/test/old/testdir/test_perl.vim b/test/old/testdir/test_perl.vim index ce2a566f62..c08a042dae 100644 --- a/test/old/testdir/test_perl.vim +++ b/test/old/testdir/test_perl.vim @@ -94,6 +94,7 @@ func Test_buffer_Number() endfunc func Test_window_Cursor() + throw 'skipped: flaky ' new call setline(1, ['line1', 'line2']) perl $curwin->Cursor(2, 3) @@ -104,6 +105,7 @@ func Test_window_Cursor() endfunc func Test_window_SetHeight() + throw 'skipped: flaky ' new perl $curwin->SetHeight(2) call assert_equal(2, winheight(0)) diff --git a/test/old/testdir/test_put.vim b/test/old/testdir/test_put.vim index b1cf268a58..73b58dbe33 100644 --- a/test/old/testdir/test_put.vim +++ b/test/old/testdir/test_put.vim @@ -323,4 +323,21 @@ func Test_put_visual_replace_fold_marker() bwipe! endfunc +func Test_put_dict() + new + let d = #{a: #{b: 'abc'}, c: [1, 2], d: 0z10} + put! =d + call assert_equal(["{'a': {'b': 'abc'}, 'c': [1, 2], 'd': 0z10}", ''], + \ getline(1, '$')) + bw! +endfunc + +func Test_put_list() + new + let l = ['a', 'b', 'c'] + put! =l + call assert_equal(['a', 'b', 'c', ''], getline(1, '$')) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_quickfix.vim b/test/old/testdir/test_quickfix.vim index b5abb08ed8..a708cabc26 100644 --- a/test/old/testdir/test_quickfix.vim +++ b/test/old/testdir/test_quickfix.vim @@ -6369,4 +6369,74 @@ func Test_efm_format_b() call setqflist([], 'f') endfunc +func XbufferTests_range(cchar) + call s:setup_commands(a:cchar) + + enew! + let lines =<< trim END + Xtestfile7:700:10:Line 700 + Xtestfile8:800:15:Line 800 + END + silent! call setline(1, lines) + norm! Vy + " Note: We cannot use :Xbuffer here, + " it doesn't properly fail, so we need to + " test using the raw c/l commands. + " (also further down) + if (a:cchar == 'c') + exe "'<,'>cbuffer!" + else + exe "'<,'>lbuffer!" + endif + let l = g:Xgetlist() + call assert_true(len(l) == 1 && + \ l[0].lnum == 700 && l[0].col == 10 && l[0].text ==# 'Line 700') + + enew! + let lines =<< trim END + Xtestfile9:900:55:Line 900 + Xtestfile10:950:66:Line 950 + END + silent! call setline(1, lines) + if (a:cchar == 'c') + 1cgetbuffer + else + 1lgetbuffer + endif + let l = g:Xgetlist() + call assert_true(len(l) == 1 && + \ l[0].lnum == 900 && l[0].col == 55 && l[0].text ==# 'Line 900') + + enew! + let lines =<< trim END + Xtestfile11:700:20:Line 700 + Xtestfile12:750:25:Line 750 + END + silent! call setline(1, lines) + if (a:cchar == 'c') + 1,1caddbuffer + else + 1,1laddbuffer + endif + let l = g:Xgetlist() + call assert_true(len(l) == 2 && + \ l[0].lnum == 900 && l[0].col == 55 && l[0].text ==# 'Line 900' && + \ l[1].lnum == 700 && l[1].col == 20 && l[1].text ==# 'Line 700') + enew! + + " Check for invalid range + " Using Xbuffer will not run the range check in the cbuffer/lbuffer + " commands. So directly call the commands. + if (a:cchar == 'c') + call assert_fails('900,999caddbuffer', 'E16:') + else + call assert_fails('900,999laddbuffer', 'E16:') + endif +endfunc + +func Test_cbuffer_range() + call XbufferTests_range('c') + call XbufferTests_range('l') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_recover.vim b/test/old/testdir/test_recover.vim index fa8cc1abaf..ca1ee11b44 100644 --- a/test/old/testdir/test_recover.vim +++ b/test/old/testdir/test_recover.vim @@ -385,7 +385,7 @@ func Test_recover_encrypted_swap_file() call delete('Xfile1') endfunc -" Test for :recover using a unreadable swap file +" Test for :recover using an unreadable swap file func Test_recover_unreadable_swap_file() CheckUnix CheckNotRoot diff --git a/test/old/testdir/test_regexp_latin.vim b/test/old/testdir/test_regexp_latin.vim index ece6ae518e..2cfa81e078 100644 --- a/test/old/testdir/test_regexp_latin.vim +++ b/test/old/testdir/test_regexp_latin.vim @@ -842,12 +842,26 @@ func Regex_Mark() %d endfunc +" Same test as above, but use verymagic +func Regex_Mark_Verymagic() + call append(0, ['', '', '', 'Marks:', 'asdfSasdfsadfEasdf', 'asdfSas', + \ 'dfsadfEasdf', '', '', '', '', '']) + call cursor(4, 1) + exe "normal jfSmsfEme:.-4,.+6s/\\v.%>'s.*%<'e../here/\<CR>" + exe "normal jfSmsj0fEme:.-4,.+6s/\\v.%>'s\\_.*%<'e../again/\<CR>" + call assert_equal(['', '', '', 'Marks:', 'asdfhereasdf', 'asdfagainasdf', + \ '', '', '', '', '', ''], getline(1, '$')) + %d +endfunc + func Test_matching_marks() new set regexpengine=1 call Regex_Mark() + call Regex_Mark_Verymagic() set regexpengine=2 call Regex_Mark() + call Regex_Mark_Verymagic() bwipe! endfunc diff --git a/test/old/testdir/test_regexp_utf8.vim b/test/old/testdir/test_regexp_utf8.vim index 97f48a0c09..728a88fa0f 100644 --- a/test/old/testdir/test_regexp_utf8.vim +++ b/test/old/testdir/test_regexp_utf8.vim @@ -372,7 +372,7 @@ func Test_multibyte_chars() endfunc " check that 'ambiwidth' does not change the meaning of \p -func Test_ambiwidth() +func Test_regexp_ambiwidth() set regexpengine=1 ambiwidth=single call assert_equal(0, match("\u00EC", '\p')) set regexpengine=1 ambiwidth=double diff --git a/test/old/testdir/test_registers.vim b/test/old/testdir/test_registers.vim index e113bd9e75..e5add9414f 100644 --- a/test/old/testdir/test_registers.vim +++ b/test/old/testdir/test_registers.vim @@ -214,6 +214,71 @@ func Test_recording_with_select_mode() bwipe! endfunc +func Run_test_recording_with_select_mode_utf8() + new + + " Test with different text lengths: 5, 7, 9, 11, 13, 15, to check that + " a character isn't split between two buffer blocks. + for s in ['12345', '口=口', '口口口', '口=口=口', '口口=口口', '口口口口口'] + " 0x80 is K_SPECIAL + " 0x9B is CSI + " 哦: 0xE5 0x93 0xA6 + " 洛: 0xE6 0xB4 0x9B + " 固: 0xE5 0x9B 0xBA + " 四: 0xE5 0x9B 0x9B + " 最: 0xE6 0x9C 0x80 + " 倒: 0xE5 0x80 0x92 + " 倀: 0xE5 0x80 0x80 + for c in ['哦', '洛', '固', '四', '最', '倒', '倀'] + call setline(1, 'asdf') + call feedkeys($"qacc{s}\<Esc>gH{c}\<Esc>q", "tx") + call assert_equal(c, getline(1)) + call assert_equal($"cc{s}\<Esc>gH{c}\<Esc>", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal(c, getline(1)) + + " Test with Shift modifier. + let shift_c = eval($'"\<S-{c}>"') + call setline(1, 'asdf') + call feedkeys($"qacc{s}\<Esc>gH{shift_c}\<Esc>q", "tx") + call assert_equal(c, getline(1)) + call assert_equal($"cc{s}\<Esc>gH{shift_c}\<Esc>", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal(c, getline(1)) + endfor + endfor + + bwipe! +endfunc + +func Test_recording_with_select_mode_utf8() + call Run_test_recording_with_select_mode_utf8() +endfunc + +" This must be done as one of the last tests, because it starts the GUI, which +" cannot be undone. +func Test_zz_recording_with_select_mode_utf8_gui() + CheckCanRunGui + + gui -f + call Run_test_recording_with_select_mode_utf8() +endfunc + +func Test_recording_with_super_mod() + if "\<D-j>"[-1:] == '>' + throw 'Skipped: <D- modifier not supported' + endif + + nnoremap <D-j> <Ignore> + let s = repeat("\<D-j>", 1000) + " This used to crash Vim + call feedkeys($'qr{s}q', 'tx') + call assert_equal(s, @r) + nunmap <D-j> +endfunc + " Test for executing the last used register (@) func Test_last_used_exec_reg() " Test for the @: command @@ -706,13 +771,13 @@ func Test_ve_blockpaste() call cursor(1,1) exe ":norm! \<C-V>3ljdP" call assert_equal(1, col('.')) - call assert_equal(getline(1, 2), ['QWERTZ', 'ASDFGH']) + call assert_equal(['QWERTZ', 'ASDFGH'], getline(1, 2)) call cursor(1,1) exe ":norm! \<C-V>3ljd" call cursor(1,1) norm! $3lP call assert_equal(5, col('.')) - call assert_equal(getline(1, 2), ['TZ QWER', 'GH ASDF']) + call assert_equal(['TZ QWER', 'GH ASDF'], getline(1, 2)) set ve&vim bwipe! endfunc diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index a1987ed3c9..76b4089bd1 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -552,14 +552,14 @@ func Test_smoothscroll_cursor_position() exe "normal \<C-Y>" call s:check_col_calc(1, 3, 41) - " Test "g0/g<Home>" + " Test "g0/g<Home>" exe "normal gg\<C-E>" norm $gkg0 - call s:check_col_calc(1, 2, 21) + call s:check_col_calc(4, 1, 24) " Test moving the cursor behind the <<< display with 'virtualedit' set virtualedit=all - exe "normal \<C-E>3lgkh" + exe "normal \<C-E>gkh" call s:check_col_calc(3, 2, 23) set virtualedit& @@ -741,6 +741,7 @@ func Test_smoothscroll_mouse_pos() let &mouse = save_mouse "let &term = save_term "let &ttymouse = save_ttymouse + bwipe! endfunc " this was dividing by zero @@ -832,7 +833,7 @@ func Test_smoothscroll_eob() call VerifyScreenDump(buf, 'Test_smooth_eob_1', {}) " cursor is not placed below window - call term_sendkeys(buf, ":call setline(92, 'a'->repeat(100))\<CR>\<C-B>G") + call term_sendkeys(buf, ":call setline(92, 'a'->repeat(100))\<CR>\<C-L>\<C-B>G") call VerifyScreenDump(buf, 'Test_smooth_eob_2', {}) call StopVimInTerminal(buf) @@ -963,4 +964,231 @@ func Test_smoothscroll_insert_bottom() call StopVimInTerminal(buf) endfunc +func Test_smoothscroll_in_qf_window() + CheckFeature quickfix + CheckScreendump + + let lines =<< trim END + set nocompatible display=lastline + copen 5 + setlocal number smoothscroll + let g:l = [{'text': 'foo'}] + repeat([{'text': join(range(30))}], 10) + call setqflist(g:l, 'r') + normal! G + wincmd t + let g:l1 = [{'text': join(range(1000))}] + END + call writefile(lines, 'XSmoothScrollInQfWindow', 'D') + let buf = RunVimInTerminal('-u NONE -S XSmoothScrollInQfWindow', #{rows: 20, cols: 60}) + call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_1', {}) + + call term_sendkeys(buf, ":call setqflist([], 'r')\<CR>") + call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_2', {}) + + call term_sendkeys(buf, ":call setqflist(g:l, 'r')\<CR>") + call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_3', {}) + + call term_sendkeys(buf, ":call setqflist(g:l1, 'r')\<CR>") + call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_4', {}) + + call term_sendkeys(buf, "\<C-W>b$\<C-W>t") + call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_5', {}) + + call term_sendkeys(buf, ":call setqflist([], 'r')\<CR>") + call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_2', {}) + + call term_sendkeys(buf, ":call setqflist(g:l1, 'r')\<CR>") + call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_4', {}) + + call term_sendkeys(buf, "\<C-W>b$\<C-W>t") + call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_5', {}) + + call term_sendkeys(buf, ":call setqflist(g:l, 'r')\<CR>") + call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_3', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_smoothscroll_in_zero_width_window() + set cpo+=n number smoothscroll + set winwidth=99999 winminwidth=0 + + vsplit + call assert_equal(0, winwidth(winnr('#'))) + call win_execute(win_getid(winnr('#')), "norm! \<C-Y>") + + only! + set winwidth& winminwidth& + set cpo-=n nonumber nosmoothscroll +endfunc + +func Test_smoothscroll_textoff_small_winwidth() + set smoothscroll number + call setline(1, 'llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch') + vsplit + + let textoff = getwininfo(win_getid())[0].textoff + execute 'vertical resize' textoff + 1 + redraw + call assert_equal(0, winsaveview().skipcol) + execute "normal! 0\<C-E>" + redraw + call assert_equal(1, winsaveview().skipcol) + execute 'vertical resize' textoff - 1 + " This caused a signed integer overflow. + redraw + call assert_equal(1, winsaveview().skipcol) + execute 'vertical resize' textoff + " This caused an infinite loop. + redraw + call assert_equal(1, winsaveview().skipcol) + + %bw! + set smoothscroll& number& +endfunc + +func Test_smoothscroll_page() + call NewWindow(10, 40) + setlocal smoothscroll + call setline(1, 'abcde '->repeat(150)) + + exe "norm! \<C-F>" + call assert_equal(400, winsaveview().skipcol) + exe "norm! \<C-F>" + call assert_equal(800, winsaveview().skipcol) + exe "norm! \<C-F>" + call assert_equal(880, winsaveview().skipcol) + exe "norm! \<C-B>" + call assert_equal(480, winsaveview().skipcol) + exe "norm! \<C-B>" + call assert_equal(80, winsaveview().skipcol) + exe "norm! \<C-B>" + call assert_equal(0, winsaveview().skipcol) + + " Half-page scrolling does not go beyond end of buffer and moves the cursor. + " Even with 'nostartofline', the correct amount of lines is scrolled. + setl nostartofline + exe "norm! 15|\<C-D>" + call assert_equal(200, winsaveview().skipcol) + call assert_equal(215, col('.')) + exe "norm! \<C-D>" + call assert_equal(400, winsaveview().skipcol) + call assert_equal(415, col('.')) + exe "norm! \<C-D>" + call assert_equal(520, winsaveview().skipcol) + call assert_equal(615, col('.')) + exe "norm! \<C-D>" + call assert_equal(520, winsaveview().skipcol) + call assert_equal(815, col('.')) + exe "norm! \<C-D>" + call assert_equal(520, winsaveview().skipcol) + call assert_equal(895, col('.')) + exe "norm! \<C-U>" + call assert_equal(320, winsaveview().skipcol) + call assert_equal(695, col('.')) + exe "norm! \<C-U>" + call assert_equal(120, winsaveview().skipcol) + call assert_equal(495, col('.')) + exe "norm! \<C-U>" + call assert_equal(0, winsaveview().skipcol) + call assert_equal(295, col('.')) + exe "norm! \<C-U>" + call assert_equal(0, winsaveview().skipcol) + call assert_equal(95, col('.')) + exe "norm! \<C-U>" + call assert_equal(0, winsaveview().skipcol) + call assert_equal(15, col('.')) + + bwipe! +endfunc + +func Test_smoothscroll_next_topline() + call NewWindow(10, 40) + setlocal smoothscroll + call setline(1, ['abcde '->repeat(150)]->repeat(2)) + + " Scrolling a screenline that causes the cursor to move to the next buffer + " line should not skip part of that line to bring the cursor into view. + exe "norm! 22\<C-E>" + call assert_equal(880, winsaveview().skipcol) + exe "norm! \<C-E>" + redraw + call assert_equal(0, winsaveview().skipcol) + + " Also when scrolling back. + exe "norm! G\<C-Y>" + redraw + call assert_equal(880, winsaveview().skipcol) + + " Cursor in correct place when not in the first screenline of a buffer line. + exe "norm! gg4gj20\<C-D>\<C-D>" + redraw + call assert_equal(2, line('w0')) + + " Cursor does not end up above topline, adjusting topline later. + setlocal nu cpo+=n + exe "norm! G$g013\<C-Y>" + redraw + call assert_equal(2, line('.')) + call assert_equal(0, winsaveview().skipcol) + + set cpo-=n + bwipe! +endfunc + +func Test_smoothscroll_long_line_zb() + call NewWindow(10, 40) + call setline(1, 'abcde '->repeat(150)) + + " Also works without 'smoothscroll' when last line of buffer doesn't fit. + " Used to set topline to buffer line count plus one, causing an empty screen. + norm zb + redraw + call assert_equal(1, winsaveview().topline) + + " Moving cursor to bottom works on line that doesn't fit with 'smoothscroll'. + " Skipcol was adjusted later for cursor being on not visible part of line. + setlocal smoothscroll + norm zb + redraw + call assert_equal(520, winsaveview().skipcol) + + bwipe! +endfunc + +func Test_smooth_long_scrolloff() + CheckScreendump + + let lines =<< trim END + set smoothscroll scrolloff=3 + call setline(1, ['one', 'two long '->repeat(100), 'three', 'four', 'five', 'six']) + END + call writefile(lines, 'XSmoothLongScrolloff', 'D') + let buf = RunVimInTerminal('-u NONE -S XSmoothLongScrolloff', #{rows: 8, cols: 40}) + "FIXME: empty screen due to reset_skipcol()/curs_columns() shenanigans + call term_sendkeys(buf, ":norm j721|\<CR>") + call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_1', {}) + + call term_sendkeys(buf, "gj") + call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_2', {}) + + call term_sendkeys(buf, "gj") + call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_3', {}) + + call term_sendkeys(buf, "gj") + call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_4', {}) + + call term_sendkeys(buf, "gj") + call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_5', {}) + + call term_sendkeys(buf, "gj") + call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_6', {}) + + call term_sendkeys(buf, "gk") + "FIXME: empty screen due to reset_skipcol()/curs_columns() shenanigans + call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_7', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_spellrare.vim b/test/old/testdir/test_spellrare.vim new file mode 100644 index 0000000000..bbb13c27c2 --- /dev/null +++ b/test/old/testdir/test_spellrare.vim @@ -0,0 +1,61 @@ +" Test spell checking + +source check.vim +CheckFeature spell + +" Test spellbadword() with argument, specifically to move to "rare" words +" in normal mode. +func Test_spellrareword() + set spell + + " Create a small word list to test that spellbadword('...') + " can return ['...', 'rare']. + let lines =<< trim END + foo + foobar/? + foobara/? +END + call writefile(lines, 'Xwords', 'D') + + mkspell! Xwords.spl Xwords + set spelllang=Xwords.spl + call assert_equal(['foobar', 'rare'], spellbadword('foo foobar')) + + new + call setline(1, ['foo', '', 'foo bar foo bar foobara foo foo foo foobar', '', 'End']) + set spell wrapscan + normal ]s + call assert_equal('foo', expand('<cword>')) + normal ]s + call assert_equal('bar', expand('<cword>')) + + normal ]r + call assert_equal('foobara', expand('<cword>')) + normal ]r + call assert_equal('foobar', expand('<cword>')) + normal ]r + call assert_equal('foobara', expand('<cword>')) + normal 2]r + call assert_equal('foobara', expand('<cword>')) + + normal [r + call assert_equal('foobar', expand('<cword>')) + normal [r + call assert_equal('foobara', expand('<cword>')) + normal [r + call assert_equal('foobar', expand('<cword>')) + normal 2[r + call assert_equal('foobar', expand('<cword>')) + + bwipe! + set nospell + + call delete('Xwords.spl') + set spelllang& + set spell& + + " set 'encoding' to clear the word list + set encoding=utf-8 +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_stat.vim b/test/old/testdir/test_stat.vim index d3059664e9..13ade5bee1 100644 --- a/test/old/testdir/test_stat.vim +++ b/test/old/testdir/test_stat.vim @@ -55,7 +55,7 @@ func SleepForTimestamp() if has('win32') sleep 2 else - sleep 2 + sleep 1 endif endfunc diff --git a/test/old/testdir/test_substitute.vim b/test/old/testdir/test_substitute.vim index a9e317da02..fdb0f6fc37 100644 --- a/test/old/testdir/test_substitute.vim +++ b/test/old/testdir/test_substitute.vim @@ -173,6 +173,16 @@ func Test_substitute_repeat() call feedkeys("Qsc\<CR>y", 'tx') bwipe! endfunc + +" Test :s with ? as delimiter. +func Test_substitute_question_delimiter() + new + call setline(1, '??:??') + %s?\?\??!!?g + call assert_equal('!!:!!', getline(1)) + bwipe! +endfunc + " Test %s/\n// which is implemented as a special case to use a " more efficient join rather than doing a regular substitution. func Test_substitute_join() diff --git a/test/old/testdir/test_syntax.vim b/test/old/testdir/test_syntax.vim index 711b2adf7c..35523df17d 100644 --- a/test/old/testdir/test_syntax.vim +++ b/test/old/testdir/test_syntax.vim @@ -197,14 +197,14 @@ func Test_syntax_completion() " Check that clearing "Aap" avoids it showing up before Boolean. hi @Aap ctermfg=blue call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_match('^"syn list @Aap @attribute @boolean @character ', @:) + call assert_match('^"syn list @Aap @attribute @attribute.builtin @boolean @character ', @:) hi clear @Aap call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_match('^"syn list @attribute @boolean @character ', @:) + call assert_match('^"syn list @attribute @attribute.builtin @boolean @character ', @:) call feedkeys(":syn match \<C-A>\<C-B>\"\<CR>", 'tx') - call assert_match('^"syn match @attribute @boolean @character ', @:) + call assert_match('^"syn match @attribute @attribute.builtin @boolean @character ', @:) syn cluster Aax contains=Aap call feedkeys(":syn list @A\<C-A>\<C-B>\"\<CR>", 'tx') diff --git a/test/old/testdir/test_tabpage.vim b/test/old/testdir/test_tabpage.vim index 0f038c8bee..2bd2907a55 100644 --- a/test/old/testdir/test_tabpage.vim +++ b/test/old/testdir/test_tabpage.vim @@ -117,10 +117,16 @@ function Test_tabpage() call assert_equal(3, tabpagenr()) +3tabmove call assert_equal(6, tabpagenr()) + silent -tabmove + call assert_equal(5, tabpagenr()) + silent -2 tabmove + call assert_equal(3, tabpagenr()) + silent -2 tabmove + call assert_equal(1, tabpagenr()) - " The following are a no-op norm! 2gt call assert_equal(2, tabpagenr()) + " The following are a no-op tabmove 2 call assert_equal(2, tabpagenr()) 2tabmove diff --git a/test/old/testdir/test_tagjump.vim b/test/old/testdir/test_tagjump.vim index ff1110e070..a614c19ce2 100644 --- a/test/old/testdir/test_tagjump.vim +++ b/test/old/testdir/test_tagjump.vim @@ -1551,14 +1551,14 @@ func Test_tagbsearch() \ "third\tXfoo\t3", \ "second\tXfoo\t2", \ "first\tXfoo\t1"], - \ 'Xtags') + \ 'Xtags', 'D') set tags=Xtags let code =<< trim [CODE] int first() {} int second() {} int third() {} [CODE] - call writefile(code, 'Xfoo') + call writefile(code, 'Xfoo', 'D') enew set tagbsearch @@ -1618,9 +1618,25 @@ func Test_tagbsearch() \ 'Xtags') call assert_fails('tag bbb', 'E426:') - call delete('Xtags') - call delete('Xfoo') set tags& tagbsearch& endfunc +" Test tag guessing with very short names +func Test_tag_guess_short() + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "y\tXf\t/^y()/"], + \ 'Xt', 'D') + set tags=Xt cpoptions+=t + call writefile(['', 'int * y () {}', ''], 'Xf', 'D') + + let v:statusmsg = '' + let @/ = '' + ta y + call assert_match('E435:', v:statusmsg) + call assert_equal(2, line('.')) + call assert_match('<y', @/) + + set tags& cpoptions-=t +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_termdebug.vim b/test/old/testdir/test_termdebug.vim index 33cdaf1611..fd0c850577 100644 --- a/test/old/testdir/test_termdebug.vim +++ b/test/old/testdir/test_termdebug.vim @@ -232,26 +232,26 @@ endfunc func Test_termdebug_mapping() %bw! - call assert_equal(maparg('K', 'n', 0, 1)->empty(), 1) - call assert_equal(maparg('-', 'n', 0, 1)->empty(), 1) - call assert_equal(maparg('+', 'n', 0, 1)->empty(), 1) + call assert_true(maparg('K', 'n', 0, 1)->empty()) + call assert_true(maparg('-', 'n', 0, 1)->empty()) + call assert_true(maparg('+', 'n', 0, 1)->empty()) Termdebug call WaitForAssert({-> assert_equal(3, winnr('$'))}) wincmd b - call assert_equal(maparg('K', 'n', 0, 1)->empty(), 0) - call assert_equal(maparg('-', 'n', 0, 1)->empty(), 0) - call assert_equal(maparg('+', 'n', 0, 1)->empty(), 0) - call assert_equal(maparg('K', 'n', 0, 1).buffer, 0) - call assert_equal(maparg('-', 'n', 0, 1).buffer, 0) - call assert_equal(maparg('+', 'n', 0, 1).buffer, 0) - call assert_equal(maparg('K', 'n', 0, 1).rhs, ':Evaluate<CR>') + call assert_false(maparg('K', 'n', 0, 1)->empty()) + call assert_false(maparg('-', 'n', 0, 1)->empty()) + call assert_false(maparg('+', 'n', 0, 1)->empty()) + call assert_false(maparg('K', 'n', 0, 1).buffer) + call assert_false(maparg('-', 'n', 0, 1).buffer) + call assert_false(maparg('+', 'n', 0, 1).buffer) + call assert_equal(':Evaluate<CR>', maparg('K', 'n', 0, 1).rhs) wincmd t quit! redraw! call WaitForAssert({-> assert_equal(1, winnr('$'))}) - call assert_equal(maparg('K', 'n', 0, 1)->empty(), 1) - call assert_equal(maparg('-', 'n', 0, 1)->empty(), 1) - call assert_equal(maparg('+', 'n', 0, 1)->empty(), 1) + call assert_true(maparg('K', 'n', 0, 1)->empty()) + call assert_true(maparg('-', 'n', 0, 1)->empty()) + call assert_true(maparg('+', 'n', 0, 1)->empty()) %bw! nnoremap K :echom "K"<cr> @@ -260,24 +260,24 @@ func Test_termdebug_mapping() Termdebug call WaitForAssert({-> assert_equal(3, winnr('$'))}) wincmd b - call assert_equal(maparg('K', 'n', 0, 1)->empty(), 0) - call assert_equal(maparg('-', 'n', 0, 1)->empty(), 0) - call assert_equal(maparg('+', 'n', 0, 1)->empty(), 0) - call assert_equal(maparg('K', 'n', 0, 1).buffer, 0) - call assert_equal(maparg('-', 'n', 0, 1).buffer, 0) - call assert_equal(maparg('+', 'n', 0, 1).buffer, 0) - call assert_equal(maparg('K', 'n', 0, 1).rhs, ':Evaluate<CR>') + call assert_false(maparg('K', 'n', 0, 1)->empty()) + call assert_false(maparg('-', 'n', 0, 1)->empty()) + call assert_false(maparg('+', 'n', 0, 1)->empty()) + call assert_false(maparg('K', 'n', 0, 1).buffer) + call assert_false(maparg('-', 'n', 0, 1).buffer) + call assert_false(maparg('+', 'n', 0, 1).buffer) + call assert_equal(':Evaluate<CR>', maparg('K', 'n', 0, 1).rhs) wincmd t quit! redraw! call WaitForAssert({-> assert_equal(1, winnr('$'))}) - call assert_equal(maparg('K', 'n', 0, 1)->empty(), 0) - call assert_equal(maparg('-', 'n', 0, 1)->empty(), 0) - call assert_equal(maparg('+', 'n', 0, 1)->empty(), 0) - call assert_equal(maparg('K', 'n', 0, 1).buffer, 0) - call assert_equal(maparg('-', 'n', 0, 1).buffer, 0) - call assert_equal(maparg('+', 'n', 0, 1).buffer, 0) - call assert_equal(maparg('K', 'n', 0, 1).rhs, ':echom "K"<cr>') + call assert_false(maparg('K', 'n', 0, 1)->empty()) + call assert_false(maparg('-', 'n', 0, 1)->empty()) + call assert_false(maparg('+', 'n', 0, 1)->empty()) + call assert_false(maparg('K', 'n', 0, 1).buffer) + call assert_false(maparg('-', 'n', 0, 1).buffer) + call assert_false(maparg('+', 'n', 0, 1).buffer) + call assert_equal(':echom "K"<cr>', maparg('K', 'n', 0, 1).rhs) %bw! nnoremap <buffer> K :echom "bK"<cr> @@ -286,20 +286,59 @@ func Test_termdebug_mapping() Termdebug call WaitForAssert({-> assert_equal(3, winnr('$'))}) wincmd b - call assert_equal(maparg('K', 'n', 0, 1).buffer, 1) - call assert_equal(maparg('-', 'n', 0, 1).buffer, 1) - call assert_equal(maparg('+', 'n', 0, 1).buffer, 1) + call assert_true(maparg('K', 'n', 0, 1).buffer) + call assert_true(maparg('-', 'n', 0, 1).buffer) + call assert_true(maparg('+', 'n', 0, 1).buffer) call assert_equal(maparg('K', 'n', 0, 1).rhs, ':echom "bK"<cr>') wincmd t quit! redraw! call WaitForAssert({-> assert_equal(1, winnr('$'))}) - call assert_equal(maparg('K', 'n', 0, 1).buffer, 1) - call assert_equal(maparg('-', 'n', 0, 1).buffer, 1) - call assert_equal(maparg('+', 'n', 0, 1).buffer, 1) - call assert_equal(maparg('K', 'n', 0, 1).rhs, ':echom "bK"<cr>') + call assert_true(maparg('K', 'n', 0, 1).buffer) + call assert_true(maparg('-', 'n', 0, 1).buffer) + call assert_true(maparg('+', 'n', 0, 1).buffer) + call assert_equal(':echom "bK"<cr>', maparg('K', 'n', 0, 1).rhs) %bw! endfunc +func Test_termdebug_bufnames() + " Test if user has filename/folders named gdb, Termdebug-gdb-console, + " etc. in the current directory + let g:termdebug_config = {} + let g:termdebug_config['use_prompt'] = 1 + let filename = 'gdb' + let replacement_filename = 'Termdebug-gdb-console' + + call writefile(['This', 'is', 'a', 'test'], filename, 'D') + " Throw away the file once the test has done. + Termdebug + " Once termdebug has completed the startup you should have 3 windows on screen + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + " A file named filename already exists in the working directory, + " hence you must call the newly created buffer differently + call WaitForAssert({-> assert_false(bufexists(filename))}) + call WaitForAssert({-> assert_true(bufexists(replacement_filename))}) + quit! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + + " Check if error message is in :message + let g:termdebug_config['disasm_window'] = 1 + let filename = 'Termdebug-asm-listing' + call writefile(['This', 'is', 'a', 'test'], filename, 'D') + " Check only the head of the error message + let error_message = "You have a file/folder named '" .. filename .. "'" + Termdebug + " Once termdebug has completed the startup you should have 4 windows on screen + call WaitForAssert({-> assert_equal(4, winnr('$'))}) + call WaitForAssert({-> assert_notequal(-1, stridx(execute('messages'), error_message))}) + quit! + wincmd b + wincmd q + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + + unlet g:termdebug_config +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_user_func.vim b/test/old/testdir/test_user_func.vim index fc7dcd62b8..3c24412eb7 100644 --- a/test/old/testdir/test_user_func.vim +++ b/test/old/testdir/test_user_func.vim @@ -891,4 +891,23 @@ func Test_multidefer_with_exception() delfunc Foo endfunc +func Test_func_curly_brace_invalid_name() + func Fail() + func Foo{'()'}bar() + endfunc + endfunc + + call assert_fails('call Fail()', 'E475: Invalid argument: Foo()bar') + + silent! call Fail() + call assert_equal([], getcompletion('Foo', 'function')) + + set formatexpr=Fail() + normal! gqq + call assert_equal([], getcompletion('Foo', 'function')) + + set formatexpr& + delfunc Fail +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_utf8.vim b/test/old/testdir/test_utf8.vim index a5a9624ec3..51ac47f082 100644 --- a/test/old/testdir/test_utf8.vim +++ b/test/old/testdir/test_utf8.vim @@ -62,6 +62,9 @@ func Test_customlist_completion() call assert_equal('"Test3 N', getreg(':')) call garbagecollect(1) + delcommand Test1 + delcommand Test2 + delcommand Test3 endfunc " Yank one 3 byte character and check the mark columns. @@ -75,53 +78,6 @@ func Test_getvcol() call assert_equal(2, virtcol("']")) endfunc -func Test_screenchar_utf8() - new - - " 1-cell, with composing characters - call setline(1, ["ABC\u0308"]) - redraw - call assert_equal([0x0041], screenchars(1, 1)) - call assert_equal([0x0042], 1->screenchars(2)) - call assert_equal([0x0043, 0x0308], screenchars(1, 3)) - call assert_equal("A", screenstring(1, 1)) - call assert_equal("B", screenstring(1, 2)) - call assert_equal("C\u0308", screenstring(1, 3)) - - " 1-cell, with 6 composing characters - set maxcombine=6 - call setline(1, ["ABC" .. repeat("\u0308", 6)]) - redraw - call assert_equal([0x0041], screenchars(1, 1)) - call assert_equal([0x0042], 1->screenchars(2)) - " This should not use uninitialized memory - call assert_equal([0x0043] + repeat([0x0308], 6), screenchars(1, 3)) - call assert_equal("A", screenstring(1, 1)) - call assert_equal("B", screenstring(1, 2)) - call assert_equal("C" .. repeat("\u0308", 6), screenstring(1, 3)) - set maxcombine& - - " 2-cells, with composing characters - let text = "\u3042\u3044\u3046\u3099" - call setline(1, text) - redraw - call assert_equal([0x3042], screenchars(1, 1)) - call assert_equal([0], screenchars(1, 2)) - call assert_equal([0x3044], screenchars(1, 3)) - call assert_equal([0], screenchars(1, 4)) - call assert_equal([0x3046, 0x3099], screenchars(1, 5)) - - call assert_equal("\u3042", screenstring(1, 1)) - call assert_equal("", screenstring(1, 2)) - call assert_equal("\u3044", screenstring(1, 3)) - call assert_equal("", screenstring(1, 4)) - call assert_equal("\u3046\u3099", screenstring(1, 5)) - - call assert_equal([text . ' '], ScreenLines(1, 8)) - - bwipe! -endfunc - func Test_list2str_str2list_utf8() " One Unicode codepoint let s = "\u3042\u3044" @@ -169,7 +125,55 @@ func Test_list2str_str2list_latin1() call assert_equal(s, sres) endfunc +func Test_screenchar_utf8() + new + + " 1-cell, with composing characters + call setline(1, ["ABC\u0308"]) + redraw + call assert_equal([0x0041], screenchars(1, 1)) + call assert_equal([0x0042], 1->screenchars(2)) + call assert_equal([0x0043, 0x0308], screenchars(1, 3)) + call assert_equal("A", screenstring(1, 1)) + call assert_equal("B", screenstring(1, 2)) + call assert_equal("C\u0308", screenstring(1, 3)) + + " 1-cell, with 6 composing characters + set maxcombine=6 + call setline(1, ["ABC" .. repeat("\u0308", 6)]) + redraw + call assert_equal([0x0041], screenchars(1, 1)) + call assert_equal([0x0042], 1->screenchars(2)) + " This should not use uninitialized memory + call assert_equal([0x0043] + repeat([0x0308], 6), screenchars(1, 3)) + call assert_equal("A", screenstring(1, 1)) + call assert_equal("B", screenstring(1, 2)) + call assert_equal("C" .. repeat("\u0308", 6), screenstring(1, 3)) + set maxcombine& + + " 2-cells, with composing characters + let text = "\u3042\u3044\u3046\u3099" + call setline(1, text) + redraw + call assert_equal([0x3042], screenchars(1, 1)) + call assert_equal([0], screenchars(1, 2)) + call assert_equal([0x3044], screenchars(1, 3)) + call assert_equal([0], screenchars(1, 4)) + call assert_equal([0x3046, 0x3099], screenchars(1, 5)) + + call assert_equal("\u3042", screenstring(1, 1)) + call assert_equal("", screenstring(1, 2)) + call assert_equal("\u3044", screenstring(1, 3)) + call assert_equal("", screenstring(1, 4)) + call assert_equal("\u3046\u3099", screenstring(1, 5)) + + call assert_equal([text . ' '], ScreenLines(1, 8)) + + bwipe! +endfunc + func Test_setcellwidths() + new call setcellwidths([ \ [0x1330, 0x1330, 2], \ [9999, 10000, 1], @@ -212,6 +216,18 @@ func Test_setcellwidths() " Ambiguous width chars call assert_equal(2, strwidth("\u00A1")) call assert_equal(2, strwidth("\u2010")) + + call setcellwidths([]) + call setline(1, repeat("\u2103", 10)) + normal! $ + redraw + call assert_equal((aw == 'single') ? 10 : 19, wincol()) + call setcellwidths([[0x2103, 0x2103, 1]]) + redraw + call assert_equal(10, wincol()) + call setcellwidths([[0x2103, 0x2103, 2]]) + redraw + call assert_equal(19, wincol()) endfor set ambiwidth& isprint& @@ -245,6 +261,7 @@ func Test_setcellwidths() set listchars& set fillchars& call setcellwidths([]) + bwipe! endfunc func Test_getcellwidths() @@ -283,64 +300,66 @@ func Test_setcellwidths_dump() call StopVimInTerminal(buf) 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 +" Test setcellwidths() on characters that are not targets of 'ambiwidth'. +func Test_setcellwidths_with_non_ambiwidth_character_dump() + CheckRunVimInTerminal + + let lines =<< trim END + call setline(1, [repeat("\u279c", 60), repeat("\u279c", 60)]) + set ambiwidth=single + END + call writefile(lines, 'XCellwidthsWithNonAmbiwidthCharacter', 'D') + let buf = RunVimInTerminal('-S XCellwidthsWithNonAmbiwidthCharacter', {'rows': 6, 'cols': 50}) + call term_sendkeys(buf, ":call setcellwidths([[0x279c, 0x279c, 1]])\<CR>") + call term_sendkeys(buf, ":echo\<CR>") + call VerifyScreenDump(buf, 'Test_setcellwidths_with_non_ambiwidth_character_dump_1', {}) -func Test_recording_with_select_mode_utf8() - call Run_test_recording_with_select_mode_utf8() + call term_sendkeys(buf, ":call setcellwidths([[0x279c, 0x279c, 2]])\<CR>") + call term_sendkeys(buf, ":echo\<CR>") + call VerifyScreenDump(buf, 'Test_setcellwidths_with_non_ambiwidth_character_dump_2', {}) + + call StopVimInTerminal(buf) endfunc -func Run_test_recording_with_select_mode_utf8() - new +" For some reason this test causes Test_customlist_completion() to fail on CI, +" so run it as the last test. +func Test_zz_ambiwidth_hl_dump() + CheckRunVimInTerminal - " No escaping - call feedkeys("qacc12345\<Esc>gH哦\<Esc>q", "tx") - call assert_equal("哦", getline(1)) - call assert_equal("cc12345\<Esc>gH哦\<Esc>", @a) - call setline(1, 'asdf') - normal! @a - call assert_equal("哦", getline(1)) - - " 固 is 0xE5 0x9B 0xBA where 0x9B is CSI - call feedkeys("qacc12345\<Esc>gH固\<Esc>q", "tx") - call assert_equal("固", getline(1)) - call assert_equal("cc12345\<Esc>gH固\<Esc>", @a) - call setline(1, 'asdf') - normal! @a - call assert_equal("固", getline(1)) - - " 四 is 0xE5 0x9B 0x9B where 0x9B is CSI - call feedkeys("qacc12345\<Esc>gH四\<Esc>q", "tx") - call assert_equal("四", getline(1)) - call assert_equal("cc12345\<Esc>gH四\<Esc>", @a) - call setline(1, 'asdf') - normal! @a - call assert_equal("四", getline(1)) - - " 倒 is 0xE5 0x80 0x92 where 0x80 is K_SPECIAL - call feedkeys("qacc12345\<Esc>gH倒\<Esc>q", "tx") - call assert_equal("倒", getline(1)) - call assert_equal("cc12345\<Esc>gH倒\<Esc>", @a) - call setline(1, 'asdf') - normal! @a - call assert_equal("倒", getline(1)) + let lines =<< trim END + call setline(1, [repeat("\u2103", 60), repeat("\u2103", 60)]) + set ambiwidth=single cursorline list display=lastline + END + call writefile(lines, 'XAmbiwidthHl', 'D') + let buf = RunVimInTerminal('-S XAmbiwidthHl', {'rows': 6, 'cols': 50}) + call VerifyScreenDump(buf, 'Test_ambiwidth_hl_dump_1', {}) - bwipe! -endfunc + call term_sendkeys(buf, ":set ambiwidth=double\<CR>") + call term_sendkeys(buf, ":echo\<CR>") + call VerifyScreenDump(buf, 'Test_ambiwidth_hl_dump_2', {}) -" This must be done as one of the last tests, because it starts the GUI, which -" cannot be undone. -func Test_zz_recording_with_select_mode_utf8_gui() - CheckCanRunGui + call term_sendkeys(buf, ":set ambiwidth=single\<CR>") + call term_sendkeys(buf, ":echo\<CR>") + call VerifyScreenDump(buf, 'Test_ambiwidth_hl_dump_1', {}) + + call term_sendkeys(buf, ":call setcellwidths([[0x2103, 0x2103, 2]])\<CR>") + call term_sendkeys(buf, ":echo\<CR>") + call VerifyScreenDump(buf, 'Test_ambiwidth_hl_dump_2', {}) + + call term_sendkeys(buf, ":call setcellwidths([[0x2103, 0x2103, 1]])\<CR>") + call term_sendkeys(buf, ":echo\<CR>") + call VerifyScreenDump(buf, 'Test_ambiwidth_hl_dump_1', {}) + + call StopVimInTerminal(buf) +endfunc - gui -f - call Run_test_recording_with_select_mode_utf8() +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/test/old/testdir/test_virtualedit.vim b/test/old/testdir/test_virtualedit.vim index 6448c7a3e3..8d9656e058 100644 --- a/test/old/testdir/test_virtualedit.vim +++ b/test/old/testdir/test_virtualedit.vim @@ -709,5 +709,27 @@ func Test_virtualedit_replace_after_tab() bwipe! endfunc +" Test that setpos('.') and cursor() behave the same for v:maxcol +func Test_virtualedit_set_cursor_pos_maxcol() + new + set virtualedit=all + + call setline(1, 'foobar') + exe "normal! V\<Esc>" + call assert_equal([0, 1, 1, 0], getpos("'<")) + call assert_equal([0, 1, v:maxcol, 0], getpos("'>")) + let pos = getpos("'>") + + call cursor(1, 1) + call setpos('.', pos) + call assert_equal([0, 1, 7, 0], getpos('.')) + + call cursor(1, 1) + call cursor(pos[1:]) + call assert_equal([0, 1, 7, 0], getpos('.')) + + set virtualedit& + bwipe! +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_visual.vim b/test/old/testdir/test_visual.vim index 4c5ab9f61f..b7b5f611c4 100644 --- a/test/old/testdir/test_visual.vim +++ b/test/old/testdir/test_visual.vim @@ -1151,7 +1151,7 @@ func Test_visual_inner_block() " try to select non-existing inner block call cursor(5, 1) call assert_beeps('normal ViBiBiB') - " try to select a unclosed inner block + " try to select an unclosed inner block 8,9d call cursor(5, 1) call assert_beeps('normal ViBiB') @@ -1635,6 +1635,22 @@ func Test_visual_substitute_visual() bwipe! endfunc +func Test_virtualedit_exclusive_selection() + new + set virtualedit=all selection=exclusive + + call setline(1, "a\tb") + normal! 0v8ly + call assert_equal("a\t", getreg('"')) + normal! 0v6ly + call assert_equal('a ', getreg('"')) + normal! 06lv2ly + call assert_equal(' ', getreg('"')) + + set virtualedit& selection& + bwipe! +endfunc + func Test_visual_getregion() let lines =<< trim END new @@ -1644,18 +1660,52 @@ func Test_visual_getregion() #" Visual mode call cursor(1, 1) call feedkeys("\<ESC>vjl", 'tx') + call assert_equal(['one', 'tw'], \ 'v'->getpos()->getregion(getpos('.'))) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 0]] + \ ], + \ 'v'->getpos()->getregionpos(getpos('.'))) + call assert_equal(['one', 'tw'], \ '.'->getpos()->getregion(getpos('v'))) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 0]] + \ ], + \ '.'->getpos()->getregionpos(getpos('v'))) + call assert_equal(['o'], \ 'v'->getpos()->getregion(getpos('v'))) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 1, 0]], + \ ], + \ 'v'->getpos()->getregionpos(getpos('v'))) + call assert_equal(['w'], \ '.'->getpos()->getregion(getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 0]], + \ ], + \ '.'->getpos()->getregionpos(getpos('.'), {'type': 'v' })) + call assert_equal(['one', 'two'], \ getpos('.')->getregion(getpos('v'), {'type': 'V' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ ], + \ getpos('.')->getregionpos(getpos('v'), {'type': 'V' })) + call assert_equal(['on', 'tw'], \ getpos('.')->getregion(getpos('v'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 0]], + \ ], + \ getpos('.')->getregionpos(getpos('v'), {'type': "\<C-v>" })) #" Line visual mode call cursor(1, 1) @@ -1702,6 +1752,9 @@ func Test_visual_getregion() \ "'a"->getpos()->getregion(getpos("'a"), {'type': 'V' })) call assert_equal(['one', 'two'], \ "."->getpos()->getregion(getpos("'a"), {'type': "\<c-v>" })) + call feedkeys("\<ESC>jVj\<ESC>", 'tx') + call assert_equal(['two', 'three'], getregion(getpos("'<"), getpos("'>"))) + call assert_equal(['two', 'three'], getregion(getpos("'>"), getpos("'<"))) #" Using List call cursor(1, 1) @@ -1721,65 +1774,264 @@ func Test_visual_getregion() call feedkeys("\<ESC>Vjj", 'tx') call assert_equal(['one', 'two', 'three'], \ getregion(getpos('v'), getpos('.'), {'type': 'V' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'V' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 6, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': 'V', 'eol': v:true })) #" Multiline with block visual mode call cursor(1, 1) call feedkeys("\<ESC>\<C-v>jj", 'tx') call assert_equal(['o', 't', 't'], \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 1, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 1, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 1, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) call cursor(1, 1) call feedkeys("\<ESC>\<C-v>jj$", 'tx') call assert_equal(['one', 'two', 'three'], \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': "\<C-v>", 'eol': v:true })) #" 'virtualedit' set virtualedit=all + call cursor(1, 1) call feedkeys("\<ESC>\<C-v>10ljj$", 'tx') call assert_equal(['one ', 'two ', 'three '], \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 3]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 3]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 6, 1]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': "\<C-v>", 'eol': v:true })) + + call cursor(3, 5) + call feedkeys("\<ESC>\<C-v>hkk", 'tx') + call assert_equal([' ', ' ', 'ee'], + \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 0, 0], [bufnr('%'), 1, 0, 0]], + \ [[bufnr('%'), 2, 0, 0], [bufnr('%'), 2, 0, 0]], + \ [[bufnr('%'), 3, 4, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 4, 0], [bufnr('%'), 1, 4, 2]], + \ [[bufnr('%'), 2, 4, 0], [bufnr('%'), 2, 4, 2]], + \ [[bufnr('%'), 3, 4, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': "\<C-v>", 'eol': v:true })) + + call cursor(3, 5) + call feedkeys("\<ESC>\<C-v>kk", 'tx') + call assert_equal([' ', ' ', 'e'], + \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 0, 0], [bufnr('%'), 1, 0, 0]], + \ [[bufnr('%'), 2, 0, 0], [bufnr('%'), 2, 0, 0]], + \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 4, 1], [bufnr('%'), 1, 4, 2]], + \ [[bufnr('%'), 2, 4, 1], [bufnr('%'), 2, 4, 2]], + \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': "\<C-v>", 'eol': v:true })) + + call cursor(1, 3) + call feedkeys("\<ESC>vjj4l", 'tx') + call assert_equal(['e', 'two', 'three '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 4, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 6, 2]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': 'v', 'eol': v:true })) + + call cursor(1, 3) + call feedkeys("\<ESC>lvjj3l", 'tx') + call assert_equal(['', 'two', 'three '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 0, 0], [bufnr('%'), 1, 0, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 4, 0], [bufnr('%'), 1, 4, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 6, 2]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': 'v', 'eol': v:true })) + + call cursor(3, 5) + call feedkeys("\<ESC>v3l", 'tx') + call assert_equal(['e '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 6, 3]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': 'v', 'eol': v:true })) + + call cursor(3, 5) + call feedkeys("\<ESC>lv3l", 'tx') + call assert_equal([' '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 3, 6, 0], [bufnr('%'), 3, 6, 4]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': 'v', 'eol': v:true })) + + call cursor(3, 5) + call feedkeys("\<ESC>3lv3l", 'tx') + call assert_equal([' '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 3, 6, 2], [bufnr('%'), 3, 6, 6]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': 'v', 'eol': v:true })) + set virtualedit& - #" Invalid position + #" using wrong types for positions call cursor(1, 1) call feedkeys("\<ESC>vjj$", 'tx') call assert_fails("call getregion(1, 2)", 'E1211:') call assert_fails("call getregion(getpos('.'), {})", 'E1211:') - call assert_equal([], getregion(getpos('.'), getpos('.'), {'type': '' })) - - #" using the wrong type call assert_fails(':echo "."->getpos()->getregion("$", [])', 'E1211:') + call assert_fails("call getregionpos(1, 2)", 'E1211:') + call assert_fails("call getregionpos(getpos('.'), {})", 'E1211:') + call assert_fails(':echo "."->getpos()->getregionpos("$", [])', 'E1211:') + + #" using invalid value for "type" + call assert_fails("call getregion(getpos('.'), getpos('.'), {'type': '' })", 'E475:') + call assert_fails("call getregionpos(getpos('.'), getpos('.'), {'type': '' })", 'E475:') #" using a mark from another buffer to current buffer new - VAR newbuf = bufnr() + LET g:buf = bufnr() call setline(1, range(10)) normal! GmA wincmd p - call assert_equal([newbuf, 10, 1, 0], getpos("'A")) + call assert_equal([g:buf, 10, 1, 0], getpos("'A")) call assert_equal([], getregion(getpos('.'), getpos("'A"), {'type': 'v' })) call assert_equal([], getregion(getpos("'A"), getpos('.'), {'type': 'v' })) - exe $':{newbuf}bwipe!' + call assert_equal([], getregionpos(getpos('.'), getpos("'A"), {'type': 'v' })) + call assert_equal([], getregionpos(getpos("'A"), getpos('.'), {'type': 'v' })) - #" using a mark from another buffer to another buffer - new - VAR anotherbuf = bufnr() - call setline(1, range(10)) - normal! GmA + #" using two marks from another buffer + wincmd p normal! GmB wincmd p - call assert_equal([anotherbuf, 10, 1, 0], getpos("'A")) - call assert_equal(['9'], getregion(getpos("'B"), getpos("'A"), {'type': 'v' })) - exe $':{anotherbuf}bwipe!' + call assert_equal([g:buf, 10, 1, 0], getpos("'B")) + call assert_equal(['9'], + \ getregion(getpos("'B"), getpos("'A"), {'type': 'v' })) + call assert_equal([ + \ [[g:buf, 10, 1, 0], [g:buf, 10, 1, 0]], + \ ], + \ getregionpos(getpos("'B"), getpos("'A"), {'type': 'v' })) + + #" using two positions from another buffer + for type in ['v', 'V', "\<C-V>"] + for exclusive in [v:false, v:true] + call assert_equal(range(10)->mapnew('string(v:val)'), + \ getregion([g:buf, 1, 1, 0], [g:buf, 10, 2, 0], + \ {'type': type, 'exclusive': exclusive })) + call assert_equal(range(10)->mapnew('string(v:val)'), + \ getregion([g:buf, 10, 2, 0], [g:buf, 1, 1, 0], + \ {'type': type, 'exclusive': exclusive })) + call assert_equal(range(1, 10)->mapnew('repeat([[g:buf, v:val, 1, 0]], 2)'), + \ getregionpos([g:buf, 1, 1, 0], [g:buf, 10, 2, 0], + \ {'type': type, 'exclusive': exclusive })) + call assert_equal(range(1, 10)->mapnew('repeat([[g:buf, v:val, 1, 0]], 2)'), + \ getregionpos([g:buf, 10, 2, 0], [g:buf, 1, 1, 0], + \ {'type': type, 'exclusive': exclusive })) + endfor + endfor + + #" using invalid positions in buffer + call assert_fails('call getregion([g:buf, 0, 1, 0], [g:buf, 10, 2, 0])', 'E966:') + call assert_fails('call getregion([g:buf, 10, 2, 0], [g:buf, 0, 1, 0])', 'E966:') + call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 11, 2, 0])', 'E966:') + call assert_fails('call getregion([g:buf, 11, 2, 0], [g:buf, 1, 1, 0])', 'E966:') + call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 10, 0, 0])', 'E964:') + call assert_fails('call getregion([g:buf, 10, 0, 0], [g:buf, 1, 1, 0])', 'E964:') + call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 10, 3, 0])', 'E964:') + call assert_fails('call getregion([g:buf, 10, 3, 0], [g:buf, 1, 1, 0])', 'E964:') + call assert_fails('call getregion([g:buf, 1, 0, 0], [g:buf, 1, 1, 0])', 'E964:') + call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 1, 0, 0])', 'E964:') #" using invalid buffer - call assert_equal([], getregion([10000, 10, 1, 0], [10000, 10, 1, 0])) + call assert_fails('call getregion([10000, 10, 1, 0], [10000, 10, 1, 0])', 'E681:') + + exe $':{g:buf}bwipe!' + unlet g:buf + bwipe! END call CheckLegacyAndVim9Success(lines) - bwipe! - let lines =<< trim END #" Selection in starts or ends in the middle of a multibyte character new @@ -1788,35 +2040,95 @@ func Test_visual_getregion() \ "\U0001f1e6\u00ab\U0001f1e7\u00ab\U0001f1e8\u00ab\U0001f1e9", \ "1234567890" \ ]) + call cursor(1, 3) call feedkeys("\<Esc>\<C-v>ljj", 'xt') call assert_equal(['cd', "\u00ab ", '34'], \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 4, 0]], + \ [[bufnr('%'), 2, 5, 0], [bufnr('%'), 2, 7, 1]], + \ [[bufnr('%'), 3, 3, 0], [bufnr('%'), 3, 4, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call cursor(1, 4) call feedkeys("\<Esc>\<C-v>ljj", 'xt') call assert_equal(['de', "\U0001f1e7", '45'], \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 4, 0], [bufnr('%'), 1, 5, 0]], + \ [[bufnr('%'), 2, 7, 0], [bufnr('%'), 2, 10, 0]], + \ [[bufnr('%'), 3, 4, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call cursor(1, 5) call feedkeys("\<Esc>\<C-v>jj", 'xt') call assert_equal(['e', ' ', '5'], \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 5, 0], [bufnr('%'), 1, 5, 0]], + \ [[bufnr('%'), 2, 10, 1], [bufnr('%'), 2, 10, 2]], + \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 5, 0], [bufnr('%'), 1, 13, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 22, 0]], + \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + #" characterwise selection with multibyte chars call cursor(1, 1) call feedkeys("\<Esc>vj", 'xt') call assert_equal(['abcdefghijk«', "\U0001f1e6"], \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 13, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + set selection=exclusive + call feedkeys('l', 'xt') + call assert_equal(['abcdefghijk«', "\U0001f1e6"], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 13, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) #" marks on multibyte chars - :set selection=exclusive call setpos("'a", [0, 1, 11, 0]) call setpos("'b", [0, 2, 16, 0]) call setpos("'c", [0, 2, 0, 0]) call cursor(1, 1) + call assert_equal(['ghijk', '🇨«🇩'], - \ getregion(getpos("'a"), getpos("'b"), {'type': "\<c-v>" })) + \ getregion(getpos("'a"), getpos("'b"), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 7, 0], [bufnr('%'), 1, 11, 0]], + \ [[bufnr('%'), 2, 13, 0], [bufnr('%'), 2, 22, 0]], + \ ], + \ getregionpos(getpos("'a"), getpos("'b"), {'type': "\<C-v>" })) + call assert_equal(['k«', '🇦«🇧«🇨'], \ getregion(getpos("'a"), getpos("'b"), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 11, 0], [bufnr('%'), 1, 13, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 16, 0]], + \ ], + \ getregionpos(getpos("'a"), getpos("'b"), {'type': 'v' })) + call assert_equal(['k«'], \ getregion(getpos("'a"), getpos("'c"), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 11, 0], [bufnr('%'), 1, 13, 0]], + \ ], + \ getregionpos(getpos("'a"), getpos("'c"), {'type': 'v' })) #" use inclusive selection, although 'selection' is exclusive call setpos("'a", [0, 1, 11, 0]) @@ -1839,12 +2151,12 @@ func Test_visual_getregion() call assert_equal(['abcdefghijk«'], \ getregion(getpos("'a"), getpos("'b"), \ {'type': 'V', 'exclusive': 1 })) - :set selection& + + set selection& + bwipe! END call CheckLegacyAndVim9Success(lines) - bwipe! - let lines =<< trim END #" Exclusive selection new @@ -1875,51 +2187,346 @@ func Test_visual_getregion() call assert_equal(["c", "x\tz"], \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) set selection& + bwipe! #" Exclusive selection 2 new call setline(1, ["a\tc", "x\tz", '', '']) + call cursor(1, 1) call feedkeys("\<Esc>v2l", 'xt') call assert_equal(["a\t"], \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true })) + call cursor(1, 1) call feedkeys("\<Esc>v$G", 'xt') call assert_equal(["a\tc", "x\tz", ''], \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true })) + call cursor(1, 1) call feedkeys("\<Esc>v$j", 'xt') call assert_equal(["a\tc", "x\tz"], \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true })) + call cursor(1, 1) call feedkeys("\<Esc>\<C-v>$j", 'xt') call assert_equal(["a\tc", "x\tz"], \ getregion(getpos('v'), getpos('.'), \ {'exclusive': v:true, 'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'exclusive': v:true, 'type': "\<C-v>" })) + call cursor(1, 1) call feedkeys("\<Esc>\<C-v>$G", 'xt') call assert_equal(["a", "x", '', ''], \ getregion(getpos('v'), getpos('.'), \ {'exclusive': v:true, 'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 1, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 1, 0]], + \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]], + \ [[bufnr('%'), 4, 0, 0], [bufnr('%'), 4, 0, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'exclusive': v:true, 'type': "\<C-v>" })) + call cursor(1, 1) call feedkeys("\<Esc>wv2j", 'xt') call assert_equal(["c", "x\tz"], \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true })) + call assert_equal([ + \ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true })) - #" virtualedit + #" 'virtualedit' with exclusive selection set selection=exclusive set virtualedit=all + + call cursor(1, 1) + call feedkeys("\<Esc>vj", 'xt') + call assert_equal(["a\tc"], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>v8l", 'xt') + call assert_equal(["a\t"], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>v6l", 'xt') + call assert_equal(['a '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 5]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + call cursor(1, 1) - call feedkeys("\<Esc>2lv2lj", 'xt') + call feedkeys("\<Esc>6lv2l", 'xt') + call assert_equal([' '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 2, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>lv2l", 'xt') + call assert_equal([' '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 2]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>2lv2l", 'xt') + call assert_equal([' '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 3]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call feedkeys('j', 'xt') call assert_equal([' c', 'x '], \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 3]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>6l\<C-v>2lj", 'xt') + call assert_equal([' ', ' '], + \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 2, 7]], + \ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 2, 7]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + + call cursor(1, 1) + call feedkeys("\<Esc>l\<C-v>2l2j", 'xt') + call assert_equal([' ', ' ', ' '], + \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 2]], + \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 2]], + \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 2]], + \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 2]], + \ [[bufnr('%'), 3, 1, 1], [bufnr('%'), 3, 1, 3]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': "\<C-v>", "eol": v:true })) + call cursor(1, 1) call feedkeys("\<Esc>2l\<C-v>2l2j", 'xt') call assert_equal([' ', ' ', ' '], \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) - set virtualedit& + call assert_equal([ + \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 3]], + \ [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 3]], + \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 3]], + \ [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 3]], + \ [[bufnr('%'), 3, 1, 2], [bufnr('%'), 3, 1, 4]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': "\<C-v>", "eol": v:true })) + + #" 'virtualedit' with inclusive selection set selection& + call cursor(1, 1) + call feedkeys("\<Esc>vj", 'xt') + call assert_equal(["a\tc", 'x'], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 1, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>v8l", 'xt') + call assert_equal(["a\tc"], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>v6l", 'xt') + call assert_equal(['a '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 6]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>6lv2l", 'xt') + call assert_equal([' c'], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 3, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>lv2l", 'xt') + call assert_equal([' '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 3]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>2lv2l", 'xt') + call assert_equal([' '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 4]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call feedkeys('j', 'xt') + call assert_equal([' c', 'x '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 4]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + + call cursor(1, 1) + call feedkeys("\<Esc>6l\<C-v>2lj", 'xt') + call assert_equal([' c', ' z'], + \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 3, 0]], + \ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 3, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + + call cursor(1, 1) + call feedkeys("\<Esc>l\<C-v>2l2j", 'xt') + call assert_equal([' ', ' ', ' '], + \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 3]], + \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 3]], + \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 3]], + \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 3]], + \ [[bufnr('%'), 3, 1, 1], [bufnr('%'), 3, 1, 4]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': "\<C-v>", "eol": v:true })) + + call cursor(1, 1) + call feedkeys("\<Esc>2l\<C-v>2l2j", 'xt') + call assert_equal([' ', ' ', ' '], + \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 4]], + \ [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 4]], + \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 4]], + \ [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 4]], + \ [[bufnr('%'), 3, 1, 2], [bufnr('%'), 3, 1, 5]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': "\<C-v>", "eol": v:true })) + + set virtualedit& + bwipe! + END + call CheckLegacyAndVim9Success(lines) + + let lines =<< trim END + #" 'virtualedit' with TABs at end of line + new + set virtualedit=all + call setline(1, ["\t", "a\t", "aa\t"]) + + call feedkeys("gg06l\<C-v>3l2j", 'xt') + call assert_equal([' ', ' ', ' '], + \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 6], [bufnr('%'), 1, 1, 0]], + \ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 2, 0]], + \ [[bufnr('%'), 3, 3, 4], [bufnr('%'), 3, 3, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 6], [bufnr('%'), 1, 2, 2]], + \ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 3, 2]], + \ [[bufnr('%'), 3, 3, 4], [bufnr('%'), 3, 4, 2]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': "\<C-v>", "eol": v:true })) + + call feedkeys("gg06lv3l", 'xt') + call assert_equal([' '], + \ getregion(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 6], [bufnr('%'), 1, 1, 0]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' })) + call assert_equal([ + \ [[bufnr('%'), 1, 1, 6], [bufnr('%'), 1, 2, 2]], + \ ], + \ getregionpos(getpos('v'), getpos('.'), + \ {'type': 'v', "eol": v:true })) + + set virtualedit& bwipe! END call CheckLegacyAndVim9Success(lines) @@ -1935,7 +2542,41 @@ func Test_getregion_invalid_buf() call assert_equal(['Move around:'], getregion(getpos("'A"), getpos("'B"))) " close the help window q - call assert_equal([], getregion(getpos("'A"), getpos("'B"))) + call assert_fails("call getregion(getpos(\"'A\"), getpos(\"'B\"))", 'E681:') + bwipe! +endfunc + +func Test_getregion_maxcol() + new + autocmd TextYankPost * + \ : if v:event.operator ==? 'y' + \ | call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 0]], + \ ], + \ getregionpos(getpos("'["), getpos("']"), + \ #{ mode: visualmode() })) + \ | call assert_equal(['abcd'], + \ getregion(getpos("'["), getpos("']"), + \ #{ mode: visualmode() })) + \ | call assert_equal([ + \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 0]], + \ ], + \ getregionpos(getpos("']"), getpos("'["), + \ #{ mode: visualmode() })) + \ | call assert_equal(['abcd'], + \ getregion(getpos("']"), getpos("'["), + \ #{ mode: visualmode() })) + \ | endif + call setline(1, ['abcd', 'efghij']) + normal yy + bwipe! +endfunc + +func Test_visual_block_cursor_delete() + new + call setline(1, 'ab') + exe ":norm! $\<c-v>hI\<Del>\<ESC>" + call assert_equal(['b'], getline(1, 1)) bwipe! endfunc diff --git a/test/old/testdir/test_window_cmd.vim b/test/old/testdir/test_window_cmd.vim index da1711a0a1..50da2beb40 100644 --- a/test/old/testdir/test_window_cmd.vim +++ b/test/old/testdir/test_window_cmd.vim @@ -113,64 +113,6 @@ func Test_window_quit() bw Xa Xb endfunc -func Test_window_curwin_not_prevwin() - botright split - call assert_equal(2, winnr()) - call assert_equal(1, winnr('#')) - quit - call assert_equal(1, winnr()) - call assert_equal(0, winnr('#')) - - botright split - botright split - call assert_equal(3, winnr()) - call assert_equal(2, winnr('#')) - 1quit - call assert_equal(2, winnr()) - call assert_equal(1, winnr('#')) - - botright split - call assert_equal(1, tabpagenr()) - call assert_equal(3, winnr()) - call assert_equal(2, winnr('#')) - wincmd T - call assert_equal(2, tabpagenr()) - call assert_equal(1, winnr()) - call assert_equal(0, winnr('#')) - tabfirst - call assert_equal(1, tabpagenr()) - call assert_equal(2, winnr()) - call assert_equal(0, winnr('#')) - - tabonly - botright split - wincmd t - wincmd p - call assert_equal(3, winnr()) - call assert_equal(1, winnr('#')) - quit - call assert_equal(2, winnr()) - call assert_equal(1, winnr('#')) - - botright split - wincmd t - wincmd p - call assert_equal(1, tabpagenr()) - call assert_equal(3, winnr()) - call assert_equal(1, winnr('#')) - wincmd T - call assert_equal(2, tabpagenr()) - call assert_equal(1, winnr()) - call assert_equal(0, winnr('#')) - tabfirst - call assert_equal(1, tabpagenr()) - call assert_equal(2, winnr()) - call assert_equal(1, winnr('#')) - - tabonly - only -endfunc - func Test_window_horizontal_split() call assert_equal(1, winnr('$')) 3wincmd s @@ -258,6 +200,20 @@ func Test_window_split_edit_bufnr() %bw! endfunc +func s:win_layout_info(tp = tabpagenr()) abort + return #{ + \ layout: winlayout(a:tp), + \ pos_sizes: range(1, tabpagewinnr(a:tp, '$')) + \ ->map({_, nr -> win_getid(nr, a:tp)->getwininfo()[0]}) + \ ->map({_, wininfo -> #{id: wininfo.winid, + \ row: wininfo.winrow, + \ col: wininfo.wincol, + \ width: wininfo.width, + \ height: wininfo.height}}) + \ ->sort({a, b -> a.id - b.id}) + \ } +endfunc + func Test_window_split_no_room() " N horizontal windows need >= 2*N + 1 lines: " - 1 line + 1 status line in each window @@ -272,6 +228,14 @@ func Test_window_split_no_room() for s in range(1, hor_split_count) | split | endfor call assert_fails('split', 'E36:') + botright vsplit + wincmd | + let info = s:win_layout_info() + call assert_fails('wincmd J', 'E36:') + call assert_fails('wincmd K', 'E36:') + call assert_equal(info, s:win_layout_info()) + only + " N vertical windows need >= 2*(N - 1) + 1 columns: " - 1 column + 1 separator for each window (except last window) " - 1 column for the last window which does not have separator @@ -284,7 +248,37 @@ func Test_window_split_no_room() for s in range(1, ver_split_count) | vsplit | endfor call assert_fails('vsplit', 'E36:') + split + wincmd | + let info = s:win_layout_info() + call assert_fails('wincmd H', 'E36:') + call assert_fails('wincmd L', 'E36:') + call assert_equal(info, s:win_layout_info()) + + " Check that the last statusline isn't lost. + " Set its window's width to 2 for the test. + wincmd j + set laststatus=0 winminwidth=0 + vertical resize 2 + " Update expected positions/sizes after the resize. Layout is unchanged. + let info.pos_sizes = s:win_layout_info().pos_sizes + set winminwidth& + call setwinvar(winnr('k'), '&statusline', '@#') + let last_stl_row = win_screenpos(0)[0] - 1 + redraw + call assert_equal('@#|', GetScreenStr(last_stl_row)) + call assert_equal('~ |', GetScreenStr(&lines - &cmdheight)) + + call assert_fails('wincmd H', 'E36:') + call assert_fails('wincmd L', 'E36:') + call assert_equal(info, s:win_layout_info()) + call setwinvar(winnr('k'), '&statusline', '=-') + redraw + call assert_equal('=-|', GetScreenStr(last_stl_row)) + call assert_equal('~ |', GetScreenStr(&lines - &cmdheight)) + %bw! + set laststatus& endfunc func Test_window_exchange() @@ -1024,6 +1018,19 @@ func Test_win_splitmove() leftabove split b leftabove vsplit c leftabove split d + + " win_splitmove doesn't actually create or close any windows, so expect an + " unchanged winid and no WinNew/WinClosed events, like :wincmd H/J/K/L. + let s:triggered = [] + augroup WinSplitMove + au! + " Nvim: WinNewPre not ported yet. Also needs full port of v9.1.0117 to pass. + " au WinNewPre * let s:triggered += ['WinNewPre'] + au WinNew * let s:triggered += ['WinNew', win_getid()] + au WinClosed * let s:triggered += ['WinClosed', str2nr(expand('<afile>'))] + augroup END + let winid = win_getid() + call assert_equal(0, win_splitmove(winnr(), winnr('l'))) call assert_equal(bufname(winbufnr(1)), 'c') call assert_equal(bufname(winbufnr(2)), 'd') @@ -1046,6 +1053,11 @@ func Test_win_splitmove() call assert_equal(bufname(winbufnr(3)), 'a') call assert_equal(bufname(winbufnr(4)), 'd') call assert_fails('call win_splitmove(winnr(), winnr("k"), v:_null_dict)', 'E1297:') + call assert_equal([], s:triggered) + call assert_equal(winid, win_getid()) + + unlet! s:triggered + au! WinSplitMove only | bd call assert_fails('call win_splitmove(winnr(), 123)', 'E957:') @@ -1055,18 +1067,54 @@ func Test_win_splitmove() tabnew call assert_fails('call win_splitmove(1, win_getid(1, 1))', 'E957:') tabclose -endfunc -func Test_floatwin_splitmove() - vsplit - let win2 = win_getid() - let popup_winid = nvim_open_win(0, 0, {'relative': 'win', - \ 'row': 3, 'col': 3, 'width': 12, 'height': 3}) - call assert_fails('call win_splitmove(popup_winid, win2)', 'E957:') - call assert_fails('call win_splitmove(win2, popup_winid)', 'E957:') + split + augroup WinSplitMove + au! + au WinEnter * ++once call win_gotoid(win_getid(winnr('#'))) + augroup END + call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E855:') + + augroup WinSplitMove + au! + au WinLeave * ++once quit + augroup END + call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E855:') + + split + split + augroup WinSplitMove + au! + au WinEnter * ++once let s:triggered = v:true + \| call assert_fails('call win_splitmove(winnr(), winnr("$"))', 'E242:') + \| call assert_fails('call win_splitmove(winnr("$"), winnr())', 'E242:') + augroup END + quit + call assert_equal(v:true, s:triggered) + unlet! s:triggered + + new + augroup WinSplitMove + au! + au BufHidden * ++once let s:triggered = v:true + \| call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E1159:') + augroup END + hide + call assert_equal(v:true, s:triggered) + unlet! s:triggered - call nvim_win_close(popup_winid, 1) - bwipe + split + let close_win = winnr('#') + augroup WinSplitMove + au! + au WinEnter * ++once quit! + augroup END + call win_splitmove(close_win, winnr()) + call assert_equal(0, win_id2win(close_win)) + + au! WinSplitMove + augroup! WinSplitMove + %bw! endfunc " Test for the :only command @@ -2006,24 +2054,160 @@ func Test_new_help_window_on_error() call assert_equal(expand("<cword>"), "'mod'") endfunc -func Test_smoothscroll_in_zero_width_window() - let save_lines = &lines - let save_columns = &columns +func Test_splitmove_flatten_frame() + split + vsplit - winsize 0 24 - set cpo+=n - exe "noremap 0 \<C-W>n\<C-W>L" - norm 000000 - set number smoothscroll - exe "norm \<C-Y>" + wincmd L + let layout = winlayout() + wincmd K + wincmd L + call assert_equal(winlayout(), layout) only! - let &lines = save_lines - let &columns = save_columns - set cpo-=n - unmap 0 - set nonumber nosmoothscroll endfunc +func Test_autocmd_window_force_room() + " Open as many windows as possible + while v:true + try + split + catch /E36:/ + break + endtry + endwhile + while v:true + try + vsplit + catch /E36:/ + break + endtry + endwhile + + wincmd j + vsplit + call assert_fails('wincmd H', 'E36:') + call assert_fails('wincmd J', 'E36:') + call assert_fails('wincmd K', 'E36:') + call assert_fails('wincmd L', 'E36:') + + edit unload me + enew + bunload! unload\ me + augroup AucmdWinForceRoom + au! + au BufEnter * ++once let s:triggered = v:true + \| call assert_equal('autocmd', win_gettype()) + augroup END + let info = s:win_layout_info() + " bufload opening the autocommand window shouldn't give E36. + call bufload('unload me') + call assert_equal(v:true, s:triggered) + call assert_equal(info, s:win_layout_info()) + + unlet! s:triggered + au! AucmdWinForceRoom + augroup! AucmdWinForceRoom + %bw! +endfunc + +func Test_win_gotoid_splitmove_textlock_cmdwin() + call setline(1, 'foo') + new + let curwin = win_getid() + call setline(1, 'bar') + + set debug+=throw indentexpr=win_gotoid(win_getid(winnr('#'))) + call assert_fails('normal! ==', 'E565:') + call assert_equal(curwin, win_getid()) + " No error if attempting to switch to curwin; nothing happens. + set indentexpr=assert_equal(1,win_gotoid(win_getid())) + normal! == + call assert_equal(curwin, win_getid()) + + set indentexpr=win_splitmove(winnr('#'),winnr()) + call assert_fails('normal! ==', 'E565:') + call assert_equal(curwin, win_getid()) + + %bw! + set debug-=throw indentexpr& + + call feedkeys('q:' + \ .. ":call assert_fails('call win_splitmove(winnr(''#''), winnr())', 'E11:')\<CR>" + \ .. ":call assert_equal('command', win_gettype())\<CR>" + \ .. ":call assert_equal('', win_gettype(winnr('#')))\<CR>", 'ntx') + + call feedkeys('q:' + \ .. ":call assert_fails('call win_gotoid(win_getid(winnr(''#'')))', 'E11:')\<CR>" + "\ No error if attempting to switch to curwin; nothing happens. + \ .. ":call assert_equal(1, win_gotoid(win_getid()))\<CR>" + \ .. ":call assert_equal('command', win_gettype())\<CR>" + \ .. ":call assert_equal('', win_gettype(winnr('#')))\<CR>", 'ntx') +endfunc + +func Test_winfixsize_positions() + " Check positions are correct when closing a window in a non-current tabpage + " causes non-adjacent window to fill the space due to 'winfix{width,height}'. + tabnew + vsplit + wincmd | + split + set winfixheight + split foo + tabfirst + + bwipe! foo + " Save actual values before entering the tabpage. + let info = s:win_layout_info(2) + tabnext + " Compare it with the expected value (after win_comp_pos) from entering. + call assert_equal(s:win_layout_info(), info) + + $tabnew + split + split + wincmd k + belowright vsplit + set winfixwidth + belowright vsplit foo + tabprevious + + bwipe! foo + " Save actual values before entering the tabpage. + let info = s:win_layout_info(3) + tabnext + " Compare it with the expected value (after win_comp_pos) from entering. + call assert_equal(s:win_layout_info(), info) + + " Check positions unchanged when failing to move a window, if 'winfix{width, + " height}' would otherwise cause a non-adjacent window to fill the space. + %bwipe + call assert_fails('execute "split|"->repeat(&lines)', 'E36:') + wincmd p + vsplit + set winfixwidth + vsplit + set winfixwidth + vsplit + vsplit + set winfixwidth + wincmd p + + let info = s:win_layout_info() + call assert_fails('wincmd J', 'E36:') + call assert_equal(info, s:win_layout_info()) + + only + call assert_fails('execute "vsplit|"->repeat(&columns)', 'E36:') + belowright split + set winfixheight + belowright split + + let info = s:win_layout_info() + call assert_fails('wincmd H', 'E36:') + call assert_equal(info, s:win_layout_info()) + + %bwipe +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_winfixbuf.vim b/test/old/testdir/test_winfixbuf.vim new file mode 100644 index 0000000000..b41eaf3c9b --- /dev/null +++ b/test/old/testdir/test_winfixbuf.vim @@ -0,0 +1,3358 @@ +" Test 'winfixbuf' + +source check.vim +source shared.vim + +" Find the number of open windows in the current tab +func s:get_windows_count() + return tabpagewinnr(tabpagenr(), '$') +endfunc + +" Create some unnamed buffers. +func s:make_buffers_list() + enew + file first + let l:first = bufnr() + + enew + file middle + let l:middle = bufnr() + + enew + file last + let l:last = bufnr() + + set winfixbuf + + return [l:first, l:last] +endfunc + +" Create some unnamed buffers and add them to an args list +func s:make_args_list() + let [l:first, l:last] = s:make_buffers_list() + + args! first middle last + + return [l:first, l:last] +endfunc + +" Create two buffers and then set the window to 'winfixbuf' +func s:make_buffer_pairs(...) + let l:reversed = get(a:, 1, 0) + + if l:reversed == 1 + enew + file original + + set winfixbuf + + enew! + file other + let l:other = bufnr() + + return l:other + endif + + enew + file other + let l:other = bufnr() + + enew + file current + + set winfixbuf + + return l:other +endfunc + +" Create 3 quick buffers and set the window to 'winfixbuf' +func s:make_buffer_trio() + edit first + let l:first = bufnr() + edit second + let l:second = bufnr() + + set winfixbuf + + edit! third + let l:third = bufnr() + + execute ":buffer! " . l:second + + return [l:first, l:second, l:third] +endfunc + +" Create a location list with at least 2 entries + a 'winfixbuf' window. +func s:make_simple_location_list() + enew + file middle + let l:middle = bufnr() + call append(0, ["winfix search-term", "another line"]) + + enew! + file first + let l:first = bufnr() + call append(0, "first search-term") + + enew! + file last + let l:last = bufnr() + call append(0, "last search-term") + + call setloclist( + \ 0, + \ [ + \ { + \ "filename": "first", + \ "bufnr": l:first, + \ "lnum": 1, + \ }, + \ { + \ "filename": "middle", + \ "bufnr": l:middle, + \ "lnum": 1, + \ }, + \ { + \ "filename": "middle", + \ "bufnr": l:middle, + \ "lnum": 2, + \ }, + \ { + \ "filename": "last", + \ "bufnr": l:last, + \ "lnum": 1, + \ }, + \ ] + \) + + set winfixbuf + + return [l:first, l:middle, l:last] +endfunc + +" Create a quickfix with at least 2 entries that are in the current 'winfixbuf' window. +func s:make_simple_quickfix() + enew + file current + let l:current = bufnr() + call append(0, ["winfix search-term", "another line"]) + + enew! + file first + let l:first = bufnr() + call append(0, "first search-term") + + enew! + file last + let l:last = bufnr() + call append(0, "last search-term") + + call setqflist( + \ [ + \ { + \ "filename": "first", + \ "bufnr": l:first, + \ "lnum": 1, + \ }, + \ { + \ "filename": "current", + \ "bufnr": l:current, + \ "lnum": 1, + \ }, + \ { + \ "filename": "current", + \ "bufnr": l:current, + \ "lnum": 2, + \ }, + \ { + \ "filename": "last", + \ "bufnr": l:last, + \ "lnum": 1, + \ }, + \ ] + \) + + set winfixbuf + + return [l:current, l:last] +endfunc + +" Create a quickfix with at least 2 entries that are in the current 'winfixbuf' window. +func s:make_quickfix_windows() + let [l:current, _] = s:make_simple_quickfix() + execute "buffer! " . l:current + + split + let l:first_window = win_getid() + execute "normal \<C-w>j" + let l:winfix_window = win_getid() + + " Open the quickfix in a separate split and go to it + copen + let l:quickfix_window = win_getid() + + return [l:first_window, l:winfix_window, l:quickfix_window] +endfunc + +" Revert all changes that occurred in any past test +func s:reset_all_buffers() + %bwipeout! + set nowinfixbuf + + call setqflist([]) + call setloclist(0, [], 'f') + + delmarks A-Z0-9 +endfunc + +" Find and set the first quickfix entry that points to `buffer` +func s:set_quickfix_by_buffer(buffer) + let l:index = 1 " quickfix indices start at 1 + for l:entry in getqflist() + if l:entry["bufnr"] == a:buffer + execute l:index . "cc" + + return + endif + + let l:index += 1 + endfor + + echoerr 'No quickfix entry matching "' . a:buffer . '" could be found.' +endfunc + +" Fail to call :Next on a 'winfixbuf' window unless :Next! is used. +func Test_Next() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + next! + + call assert_fails("Next", "E1513:") + call assert_notequal(l:first, bufnr()) + + Next! + call assert_equal(l:first, bufnr()) +endfunc + +" Call :argdo and choose the next available 'nowinfixbuf' window. +func Test_argdo_choose_available_window() + call s:reset_all_buffers() + + let [_, l:last] = s:make_args_list() + + " Make a split window that is 'nowinfixbuf' but make it the second-to-last + " window so that :argdo will first try the 'winfixbuf' window, pass over it, + " and prefer the other 'nowinfixbuf' window, instead. + " + " +-------------------+ + " | 'nowinfixbuf' | + " +-------------------+ + " | 'winfixbuf' | <-- Cursor is here + " +-------------------+ + split + let l:nowinfixbuf_window = win_getid() + " Move to the 'winfixbuf' window now + execute "normal \<C-w>j" + let l:winfixbuf_window = win_getid() + let l:expected_windows = s:get_windows_count() + + argdo echo '' + call assert_equal(l:nowinfixbuf_window, win_getid()) + call assert_equal(l:last, bufnr()) + call assert_equal(l:expected_windows, s:get_windows_count()) +endfunc + +" Call :argdo and create a new split window if all available windows are 'winfixbuf'. +func Test_argdo_make_new_window() + call s:reset_all_buffers() + + let [l:first, l:last] = s:make_args_list() + let l:current = win_getid() + let l:current_windows = s:get_windows_count() + + argdo echo '' + call assert_notequal(l:current, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:first, bufnr()) + call assert_equal(l:current_windows + 1, s:get_windows_count()) +endfunc + +" Fail :argedit but :argedit! is allowed +func Test_argedit() + call s:reset_all_buffers() + + args! first middle last + enew + file first + let l:first = bufnr() + + enew + file middle + let l:middle = bufnr() + + enew + file last + let l:last = bufnr() + + set winfixbuf + + let l:current = bufnr() + call assert_fails("argedit first middle last", "E1513:") + call assert_equal(l:current, bufnr()) + + argedit! first middle last + call assert_equal(l:first, bufnr()) +endfunc + +" Fail :arglocal but :arglocal! is allowed +func Test_arglocal() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + argglobal! other + execute "buffer! " . l:current + + call assert_fails("arglocal other", "E1513:") + call assert_equal(l:current, bufnr()) + + arglocal! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :argglobal but :argglobal! is allowed +func Test_argglobal() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("argglobal other", "E1513:") + call assert_equal(l:current, bufnr()) + + argglobal! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :args but :args! is allowed +func Test_args() + call s:reset_all_buffers() + + let [l:first, _] = s:make_buffers_list() + let l:current = bufnr() + + call assert_fails("args first middle last", "E1513:") + call assert_equal(l:current, bufnr()) + + args! first middle last + call assert_equal(l:first, bufnr()) +endfunc + +" Fail :bNext but :bNext! is allowed +func Test_bNext() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + call assert_fails("bNext", "E1513:") + let l:current = bufnr() + + call assert_equal(l:current, bufnr()) + + bNext! + call assert_equal(l:other, bufnr()) +endfunc + +" Allow :badd because it doesn't actually change the current window's buffer +func Test_badd() + call s:reset_all_buffers() + + call s:make_buffer_pairs() + let l:current = bufnr() + + badd other + call assert_equal(l:current, bufnr()) +endfunc + +" Allow :balt because it doesn't actually change the current window's buffer +func Test_balt() + call s:reset_all_buffers() + + call s:make_buffer_pairs() + let l:current = bufnr() + + balt other + call assert_equal(l:current, bufnr()) +endfunc + +" Fail :bfirst but :bfirst! is allowed +func Test_bfirst() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("bfirst", "E1513:") + call assert_equal(l:current, bufnr()) + + bfirst! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :blast but :blast! is allowed +func Test_blast() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs(1) + bfirst! + let l:current = bufnr() + + call assert_fails("blast", "E1513:") + call assert_equal(l:current, bufnr()) + + blast! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :bmodified but :bmodified! is allowed +func Test_bmodified() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + execute "buffer! " . l:other + set modified + execute "buffer! " . l:current + + call assert_fails("bmodified", "E1513:") + call assert_equal(l:current, bufnr()) + + bmodified! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :bnext but :bnext! is allowed +func Test_bnext() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("bnext", "E1513:") + call assert_equal(l:current, bufnr()) + + bnext! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :bprevious but :bprevious! is allowed +func Test_bprevious() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("bprevious", "E1513:") + call assert_equal(l:current, bufnr()) + + bprevious! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :brewind but :brewind! is allowed +func Test_brewind() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("brewind", "E1513:") + call assert_equal(l:current, bufnr()) + + brewind! + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :browse edit but :browse edit! is allowed +func Test_browse_edit_fail() + " A GUI dialog may stall the test. + CheckNotGui + + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("browse edit other", "E1513:") + call assert_equal(l:current, bufnr()) + + try + browse edit! other + call assert_equal(l:other, bufnr()) + catch /^Vim\%((\a\+)\)\=:E338:/ + " Ignore E338, which occurs if console Vim is built with +browse. + " Console Vim without +browse will treat this as a regular :edit. + endtry +endfunc + +" Allow :browse w because it doesn't change the buffer in the current file +func Test_browse_edit_pass() + " A GUI dialog may stall the test. + CheckNotGui + + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + try + browse write other + catch /^Vim\%((\a\+)\)\=:E338:/ + " Ignore E338, which occurs if console Vim is built with +browse. + " Console Vim without +browse will treat this as a regular :write. + endtry + + call delete("other") +endfunc + +" Call :bufdo and choose the next available 'nowinfixbuf' window. +func Test_bufdo_choose_available_window() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + + " Make a split window that is 'nowinfixbuf' but make it the second-to-last + " window so that :bufdo will first try the 'winfixbuf' window, pass over it, + " and prefer the other 'nowinfixbuf' window, instead. + " + " +-------------------+ + " | 'nowinfixbuf' | + " +-------------------+ + " | 'winfixbuf' | <-- Cursor is here + " +-------------------+ + split + let l:nowinfixbuf_window = win_getid() + " Move to the 'winfixbuf' window now + execute "normal \<C-w>j" + let l:winfixbuf_window = win_getid() + + let l:current = bufnr() + let l:expected_windows = s:get_windows_count() + + call assert_notequal(l:current, l:other) + + bufdo echo '' + call assert_equal(l:nowinfixbuf_window, win_getid()) + call assert_notequal(l:other, bufnr()) + call assert_equal(l:expected_windows, s:get_windows_count()) +endfunc + +" Call :bufdo and create a new split window if all available windows are 'winfixbuf'. +func Test_bufdo_make_new_window() + call s:reset_all_buffers() + + let [l:first, l:last] = s:make_buffers_list() + execute "buffer! " . l:first + let l:current = win_getid() + let l:current_windows = s:get_windows_count() + + bufdo echo '' + call assert_notequal(l:current, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:first, bufnr()) + call assert_equal(l:current_windows + 1, s:get_windows_count()) +endfunc + +" Fail :buffer but :buffer! is allowed +func Test_buffer() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("buffer " . l:other, "E1513:") + call assert_equal(l:current, bufnr()) + + execute "buffer! " . l:other + call assert_equal(l:other, bufnr()) +endfunc + +" Allow :buffer on a 'winfixbuf' window if there is no change in buffer +func Test_buffer_same_buffer() + call s:reset_all_buffers() + + call s:make_buffer_pairs() + let l:current = bufnr() + + execute "buffer " . l:current + call assert_equal(l:current, bufnr()) + + execute "buffer! " . l:current + call assert_equal(l:current, bufnr()) +endfunc + +" Allow :cNext but the 'nowinfixbuf' window is selected, instead +func Test_cNext() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cNext` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cNext + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :cNfile but the 'nowinfixbuf' window is selected, instead +func Test_cNfile() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cNfile` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + cnext! + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cNfile + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :caddexpr because it doesn't change the current buffer +func Test_caddexpr() + CheckFeature quickfix + call s:reset_all_buffers() + + let l:file_path = tempname() + call writefile(["Error - bad-thing-found"], l:file_path, 'D') + execute "edit " . l:file_path + let l:file_buffer = bufnr() + let l:current = bufnr() + + edit first.unittest + call append(0, ["some-search-term bad-thing-found"]) + + edit! other.unittest + + set winfixbuf + + execute "buffer! " . l:file_buffer + + execute 'caddexpr expand("%") .. ":" .. line(".") .. ":" .. getline(".")' + call assert_equal(l:current, bufnr()) +endfunc + +" Fail :cbuffer but :cbuffer! is allowed +func Test_cbuffer() + CheckFeature quickfix + call s:reset_all_buffers() + + let l:file_path = tempname() + call writefile(["first.unittest:1:Error - bad-thing-found"], l:file_path, 'D') + execute "edit " . l:file_path + let l:file_buffer = bufnr() + let l:current = bufnr() + + edit first.unittest + call append(0, ["some-search-term bad-thing-found"]) + + edit! other.unittest + + set winfixbuf + + execute "buffer! " . l:file_buffer + + call assert_fails("cbuffer " . l:file_buffer) + call assert_equal(l:current, bufnr()) + + execute "cbuffer! " . l:file_buffer + call assert_equal("first.unittest", expand("%:t")) +endfunc + +" Allow :cc but the 'nowinfixbuf' window is selected, instead +func Test_cc() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cnext` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + " Go up one line in the quickfix window to an quickfix entry that doesn't + " point to a winfixbuf buffer + normal k + " Attempt to make the previous window, winfixbuf buffer, to go to the + " non-winfixbuf quickfix entry + .cc + + " Confirm that :.cc did not change the winfixbuf-enabled window + call assert_equal(l:first_window, win_getid()) +endfunc + +" Call :cdo and choose the next available 'nowinfixbuf' window. +func Test_cdo_choose_available_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current, l:last] = s:make_simple_quickfix() + execute "buffer! " . l:current + + " Make a split window that is 'nowinfixbuf' but make it the second-to-last + " window so that :cdo will first try the 'winfixbuf' window, pass over it, + " and prefer the other 'nowinfixbuf' window, instead. + " + " +-------------------+ + " | 'nowinfixbuf' | + " +-------------------+ + " | 'winfixbuf' | <-- Cursor is here + " +-------------------+ + split + let l:nowinfixbuf_window = win_getid() + " Move to the 'winfixbuf' window now + execute "normal \<C-w>j" + let l:winfixbuf_window = win_getid() + let l:expected_windows = s:get_windows_count() + + cdo echo '' + + call assert_equal(l:nowinfixbuf_window, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:current, bufnr()) + call assert_equal(l:expected_windows, s:get_windows_count()) +endfunc + +" Call :cdo and create a new split window if all available windows are 'winfixbuf'. +func Test_cdo_make_new_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current_buffer, l:last] = s:make_simple_quickfix() + execute "buffer! " . l:current_buffer + + let l:current_window = win_getid() + let l:current_windows = s:get_windows_count() + + cdo echo '' + call assert_notequal(l:current_window, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:current_buffer, bufnr()) + call assert_equal(l:current_windows + 1, s:get_windows_count()) +endfunc + +" Fail :cexpr but :cexpr! is allowed +func Test_cexpr() + CheckFeature quickfix + call s:reset_all_buffers() + + let l:file = tempname() + let l:entry = '["' . l:file . ':1:bar"]' + let l:current = bufnr() + + set winfixbuf + + call assert_fails("cexpr " . l:entry) + call assert_equal(l:current, bufnr()) + + execute "cexpr! " . l:entry + call assert_equal(fnamemodify(l:file, ":t"), expand("%:t")) +endfunc + +" Call :cfdo and choose the next available 'nowinfixbuf' window. +func Test_cfdo_choose_available_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current, l:last] = s:make_simple_quickfix() + execute "buffer! " . l:current + + " Make a split window that is 'nowinfixbuf' but make it the second-to-last + " window so that :cfdo will first try the 'winfixbuf' window, pass over it, + " and prefer the other 'nowinfixbuf' window, instead. + " + " +-------------------+ + " | 'nowinfixbuf' | + " +-------------------+ + " | 'winfixbuf' | <-- Cursor is here + " +-------------------+ + split + let l:nowinfixbuf_window = win_getid() + " Move to the 'winfixbuf' window now + execute "normal \<C-w>j" + let l:winfixbuf_window = win_getid() + let l:expected_windows = s:get_windows_count() + + cfdo echo '' + + call assert_equal(l:nowinfixbuf_window, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:current, bufnr()) + call assert_equal(l:expected_windows, s:get_windows_count()) +endfunc + +" Call :cfdo and create a new split window if all available windows are 'winfixbuf'. +func Test_cfdo_make_new_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current_buffer, l:last] = s:make_simple_quickfix() + execute "buffer! " . l:current_buffer + + let l:current_window = win_getid() + let l:current_windows = s:get_windows_count() + + cfdo echo '' + call assert_notequal(l:current_window, win_getid()) + call assert_equal(l:last, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:current_buffer, bufnr()) + call assert_equal(l:current_windows + 1, s:get_windows_count()) +endfunc + +" Fail :cfile but :cfile! is allowed +func Test_cfile() + CheckFeature quickfix + call s:reset_all_buffers() + + edit first.unittest + call append(0, ["some-search-term bad-thing-found"]) + write + let l:first = bufnr() + + edit! second.unittest + call append(0, ["some-search-term"]) + write + + let l:file = tempname() + call writefile(["first.unittest:1:Error - bad-thing-found was detected"], l:file) + + let l:current = bufnr() + + set winfixbuf + + call assert_fails(":cfile " . l:file) + call assert_equal(l:current, bufnr()) + + execute ":cfile! " . l:file + call assert_equal(l:first, bufnr()) + + call delete(l:file) + call delete("first.unittest") + call delete("second.unittest") +endfunc + +" Allow :cfirst but the 'nowinfixbuf' window is selected, instead +func Test_cfirst() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cfirst` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cfirst + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :clast but the 'nowinfixbuf' window is selected, instead +func Test_clast() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:clast` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + clast + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :cnext but the 'nowinfixbuf' window is selected, instead +" Make sure no new windows are created and previous windows are reused +func Test_cnext() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + let l:expected = s:get_windows_count() + + " The call to `:cnext` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + cnext! + call assert_equal(l:expected, s:get_windows_count()) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cnext + call assert_equal(l:first_window, win_getid()) + call assert_equal(l:expected, s:get_windows_count()) +endfunc + +" Make sure :cnext creates a split window if no previous window exists +func Test_cnext_no_previous_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current, _] = s:make_simple_quickfix() + execute "buffer! " . l:current + + let l:expected = s:get_windows_count() + + " Open the quickfix in a separate split and go to it + copen + + call assert_equal(l:expected + 1, s:get_windows_count()) +endfunc + +" Allow :cnext and create a 'nowinfixbuf' window if none exists +func Test_cnext_make_new_window() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:current, _] = s:make_simple_quickfix() + let l:current = win_getid() + + cfirst! + + let l:windows = s:get_windows_count() + let l:expected = l:windows + 1 " We're about to create a new split window + + cnext + call assert_equal(l:expected, s:get_windows_count()) + + cnext! + call assert_equal(l:expected, s:get_windows_count()) +endfunc + +" Allow :cprevious but the 'nowinfixbuf' window is selected, instead +func Test_cprevious() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cprevious` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cprevious + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :cnfile but the 'nowinfixbuf' window is selected, instead +func Test_cnfile() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cnfile` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + cnext! + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cnfile + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :cpfile but the 'nowinfixbuf' window is selected, instead +func Test_cpfile() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:cpfile` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + cnext! + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + cpfile + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow :crewind but the 'nowinfixbuf' window is selected, instead +func Test_crewind() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows() + + " The call to `:crewind` succeeds but it selects the window with 'nowinfixbuf' instead + call s:set_quickfix_by_buffer(winbufnr(l:winfix_window)) + cnext! + + " Make sure the previous window has 'winfixbuf' so we can test that our + " "skip 'winfixbuf' window" logic works. + call win_gotoid(l:winfix_window) + call win_gotoid(l:quickfix_window) + + crewind + call assert_equal(l:first_window, win_getid()) +endfunc + +" Allow <C-w>f because it opens in a new split +func Test_ctrl_w_f() + call s:reset_all_buffers() + + enew + let l:file_name = tempname() + call writefile([], l:file_name) + let l:file_buffer = bufnr() + + enew + file other + let l:other_buffer = bufnr() + + set winfixbuf + + call setline(1, l:file_name) + let l:current_windows = s:get_windows_count() + execute "normal \<C-w>f" + + call assert_equal(l:current_windows + 1, s:get_windows_count()) + + call delete(l:file_name) +endfunc + +" Fail :djump but :djump! is allowed +func Test_djump() + call s:reset_all_buffers() + + let l:include_file = tempname() . ".h" + call writefile(["min(1, 12);", + \ '#include "' . l:include_file . '"' + \ ], + \ "main.c") + call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file) + edit main.c + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("djump 1 /min/", "E1513:") + call assert_equal(l:current, bufnr()) + + djump! 1 /min/ + call assert_notequal(l:current, bufnr()) + + call delete("main.c") + call delete(l:include_file) +endfunc + +" Fail :drop but :drop! is allowed +func Test_drop() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("drop other", "E1513:") + call assert_equal(l:current, bufnr()) + + drop! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :edit but :edit! is allowed +func Test_edit() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("edit other", "E1513:") + call assert_equal(l:current, bufnr()) + + edit! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :e when selecting a buffer from a relative path if in a different folder +" +" In this tests there's 2 buffers +" +" foo - lives on disk, in some folder. e.g. /tmp/foo +" foo - an in-memory buffer that has not been saved to disk. If saved, it +" would live in a different folder, /other/foo. +" +" The 'winfixbuf' is looking at the in-memory buffer and trying to switch to +" the buffer on-disk (and fails, because it's a different buffer) +func Test_edit_different_buffer_on_disk_and_relative_path_to_disk() + call s:reset_all_buffers() + + let l:file_on_disk = tempname() + let l:directory_on_disk1 = fnamemodify(l:file_on_disk, ":p:h") + let l:name = fnamemodify(l:file_on_disk, ":t") + execute "edit " . l:file_on_disk + write! + + let l:directory_on_disk2 = l:directory_on_disk1 . "_something_else" + + if !isdirectory(l:directory_on_disk2) + call mkdir(l:directory_on_disk2) + endif + + execute "cd " . l:directory_on_disk2 + execute "edit " l:name + + let l:current = bufnr() + + call assert_equal(l:current, bufnr()) + set winfixbuf + call assert_fails("edit " . l:file_on_disk, "E1513:") + call assert_equal(l:current, bufnr()) + + call delete(l:directory_on_disk1) + call delete(l:directory_on_disk2) +endfunc + +" Fail :e when selecting a buffer from a relative path if in a different folder +" +" In this tests there's 2 buffers +" +" foo - lives on disk, in some folder. e.g. /tmp/foo +" foo - an in-memory buffer that has not been saved to disk. If saved, it +" would live in a different folder, /other/foo. +" +" The 'winfixbuf' is looking at the on-disk buffer and trying to switch to +" the in-memory buffer (and fails, because it's a different buffer) +func Test_edit_different_buffer_on_disk_and_relative_path_to_memory() + call s:reset_all_buffers() + + let l:file_on_disk = tempname() + let l:directory_on_disk1 = fnamemodify(l:file_on_disk, ":p:h") + let l:name = fnamemodify(l:file_on_disk, ":t") + execute "edit " . l:file_on_disk + write! + + let l:directory_on_disk2 = l:directory_on_disk1 . "_something_else" + + if !isdirectory(l:directory_on_disk2) + call mkdir(l:directory_on_disk2) + endif + + execute "cd " . l:directory_on_disk2 + execute "edit " l:name + execute "cd " . l:directory_on_disk1 + execute "edit " l:file_on_disk + execute "cd " . l:directory_on_disk2 + + let l:current = bufnr() + + call assert_equal(l:current, bufnr()) + set winfixbuf + call assert_fails("edit " . l:name, "E1513:") + call assert_equal(l:current, bufnr()) + + call delete(l:directory_on_disk1) + call delete(l:directory_on_disk2) +endfunc + +" Fail to call `:e first` if called from a starting, in-memory buffer +func Test_edit_first_buffer() + call s:reset_all_buffers() + + set winfixbuf + let l:current = bufnr() + + call assert_fails("edit first", "E1513:") + call assert_equal(l:current, bufnr()) + + edit! first + call assert_equal(l:current, bufnr()) + edit! somewhere_else + call assert_notequal(l:current, bufnr()) +endfunc + +" Allow reloading a buffer using :e +func Test_edit_no_arguments() + call s:reset_all_buffers() + + let l:current = bufnr() + file some_buffer + + call assert_equal(l:current, bufnr()) + set winfixbuf + edit + call assert_equal(l:current, bufnr()) +endfunc + +" Allow :e selecting the current buffer +func Test_edit_same_buffer_in_memory() + call s:reset_all_buffers() + + let current = bufnr() + file same_buffer + + call assert_equal(current, bufnr()) + set winfixbuf + edit same_buffer + call assert_equal(current, bufnr()) + set nowinfixbuf +endfunc + +" Allow :e selecting the current buffer as a full path +func Test_edit_same_buffer_on_disk_absolute_path() + call s:reset_all_buffers() + + let file = tempname() + " file must exist for expansion of 8.3 paths to succeed + call writefile([], file, 'D') + let file = fnamemodify(file, ':p') + let current = bufnr() + execute "edit " . file + write! + + call assert_equal(current, bufnr()) + set winfixbuf + execute "edit " file + call assert_equal(current, bufnr()) + + set nowinfixbuf +endfunc + +" Fail :enew but :enew! is allowed +func Test_enew() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("enew", "E1513:") + call assert_equal(l:current, bufnr()) + + enew! + call assert_notequal(l:other, bufnr()) + call assert_notequal(3, bufnr()) +endfunc + +" Fail :ex but :ex! is allowed +func Test_ex() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("ex other", "E1513:") + call assert_equal(l:current, bufnr()) + + ex! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :find but :find! is allowed +func Test_find() + call s:reset_all_buffers() + + let l:current = bufnr() + let l:file = tempname() + call writefile([], l:file, 'D') + let l:file = fnamemodify(l:file, ':p') " In case it's Windows 8.3-style. + let l:directory = fnamemodify(l:file, ":p:h") + let l:name = fnamemodify(l:file, ":p:t") + + let l:original_path = &path + execute "set path=" . l:directory + + set winfixbuf + + call assert_fails("execute 'find " . l:name . "'", "E1513:") + call assert_equal(l:current, bufnr()) + + execute "find! " . l:name + call assert_equal(l:file, expand("%:p")) + + execute "set path=" . l:original_path +endfunc + +" Fail :first but :first! is allowed +func Test_first() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + next! + + call assert_fails("first", "E1513:") + call assert_notequal(l:first, bufnr()) + + first! + call assert_equal(l:first, bufnr()) +endfunc + +" Fail :grep but :grep! is allowed +func Test_grep() + CheckFeature quickfix + call s:reset_all_buffers() + + edit first.unittest + call append(0, ["some-search-term"]) + write + let l:first = bufnr() + + edit current.unittest + call append(0, ["some-search-term"]) + write + let l:current = bufnr() + + edit! last.unittest + call append(0, ["some-search-term"]) + write + let l:last = bufnr() + + set winfixbuf + + buffer! current.unittest + + call assert_fails("silent! grep some-search-term *.unittest", "E1513:") + call assert_equal(l:current, bufnr()) + execute "edit! " . l:first + + silent! grep! some-search-term *.unittest + call assert_notequal(l:first, bufnr()) + + call delete("first.unittest") + call delete("current.unittest") + call delete("last.unittest") +endfunc + +" Fail :ijump but :ijump! is allowed +func Test_ijump() + call s:reset_all_buffers() + + let l:include_file = tempname() . ".h" + call writefile([ + \ '#include "' . l:include_file . '"' + \ ], + \ "main.c", 'D') + call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file, 'D') + edit main.c + + set winfixbuf + + let l:current = bufnr() + + set define=^\\s*#\\s*define + set include=^\\s*#\\s*include + set path=.,/usr/include,, + + call assert_fails("ijump /min/", "E1513:") + call assert_equal(l:current, bufnr()) + + set nowinfixbuf + + ijump! /min/ + call assert_notequal(l:current, bufnr()) + + set define& + set include& + set path& +endfunc + +" Fail :lNext but :lNext! is allowed +func Test_lNext() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first, l:middle, _] = s:make_simple_location_list() + call assert_equal(1, getloclist(0, #{idx: 0}).idx) + + lnext! + call assert_equal(2, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:middle, bufnr()) + + call assert_fails("lNext", "E1513:") + " Ensure the entry didn't change. + call assert_equal(2, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:middle, bufnr()) + + lnext! + call assert_equal(3, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:middle, bufnr()) + + lNext! + call assert_equal(2, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:middle, bufnr()) + + lNext! + call assert_equal(1, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:first, bufnr()) +endfunc + +" Fail :lNfile but :lNfile! is allowed +func Test_lNfile() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first, l:current, _] = s:make_simple_location_list() + call assert_equal(1, getloclist(0, #{idx: 0}).idx) + + lnext! + call assert_equal(2, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:current, bufnr()) + + call assert_fails("lNfile", "E1513:") + " Ensure the entry didn't change. + call assert_equal(2, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:current, bufnr()) + + lnext! + call assert_equal(3, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:current, bufnr()) + + lNfile! + call assert_equal(1, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:first, bufnr()) +endfunc + +" Allow :laddexpr because it doesn't change the current buffer +func Test_laddexpr() + CheckFeature quickfix + call s:reset_all_buffers() + + let l:file_path = tempname() + call writefile(["Error - bad-thing-found"], l:file_path, 'D') + execute "edit " . l:file_path + let l:file_buffer = bufnr() + let l:current = bufnr() + + edit first.unittest + call append(0, ["some-search-term bad-thing-found"]) + + edit! other.unittest + + set winfixbuf + + execute "buffer! " . l:file_buffer + + execute 'laddexpr expand("%") .. ":" .. line(".") .. ":" .. getline(".")' + call assert_equal(l:current, bufnr()) +endfunc + +" Fail :last but :last! is allowed +func Test_last() + call s:reset_all_buffers() + + let [_, l:last] = s:make_args_list() + next! + + call assert_fails("last", "E1513:") + call assert_notequal(l:last, bufnr()) + + last! + call assert_equal(l:last, bufnr()) +endfunc + +" Fail :lbuffer but :lbuffer! is allowed +func Test_lbuffer() + CheckFeature quickfix + call s:reset_all_buffers() + + let l:file_path = tempname() + call writefile(["first.unittest:1:Error - bad-thing-found"], l:file_path, 'D') + execute "edit " . l:file_path + let l:file_buffer = bufnr() + let l:current = bufnr() + + edit first.unittest + call append(0, ["some-search-term bad-thing-found"]) + + edit! other.unittest + + set winfixbuf + + execute "buffer! " . l:file_buffer + + call assert_fails("lbuffer " . l:file_buffer) + call assert_equal(l:current, bufnr()) + + execute "lbuffer! " . l:file_buffer + call assert_equal("first.unittest", expand("%:t")) +endfunc + +" Fail :ldo but :ldo! is allowed +func Test_ldo() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first, l:middle, l:last] = s:make_simple_location_list() + lnext! + + call assert_fails('execute "ldo buffer ' . l:first . '"', "E1513:") + call assert_equal(l:middle, bufnr()) + execute "ldo! buffer " . l:first + call assert_notequal(l:last, bufnr()) +endfunc + +" Fail :lfdo but :lfdo! is allowed +func Test_lexpr() + CheckFeature quickfix + call s:reset_all_buffers() + + let l:file = tempname() + let l:entry = '["' . l:file . ':1:bar"]' + let l:current = bufnr() + + set winfixbuf + + call assert_fails("lexpr " . l:entry) + call assert_equal(l:current, bufnr()) + + execute "lexpr! " . l:entry + call assert_equal(fnamemodify(l:file, ":t"), expand("%:t")) +endfunc + +" Fail :lfdo but :lfdo! is allowed +func Test_lfdo() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first, l:middle, l:last] = s:make_simple_location_list() + lnext! + + call assert_fails('execute "lfdo buffer ' . l:first . '"', "E1513:") + call assert_equal(l:middle, bufnr()) + execute "lfdo! buffer " . l:first + call assert_notequal(l:last, bufnr()) +endfunc + +" Fail :lfile but :lfile! is allowed +func Test_lfile() + CheckFeature quickfix + call s:reset_all_buffers() + + edit first.unittest + call append(0, ["some-search-term bad-thing-found"]) + write + let l:first = bufnr() + + edit! second.unittest + call append(0, ["some-search-term"]) + write + + let l:file = tempname() + call writefile(["first.unittest:1:Error - bad-thing-found was detected"], l:file, 'D') + + let l:current = bufnr() + + set winfixbuf + + call assert_fails(":lfile " . l:file) + call assert_equal(l:current, bufnr()) + + execute ":lfile! " . l:file + call assert_equal(l:first, bufnr()) + + call delete("first.unittest") + call delete("second.unittest") +endfunc + +" Fail :ll but :ll! is allowed +func Test_ll() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first, l:middle, l:last] = s:make_simple_location_list() + lopen + lfirst! + execute "normal \<C-w>j" + normal j + + call assert_fails(".ll", "E1513:") + execute "normal \<C-w>k" + call assert_equal(l:first, bufnr()) + execute "normal \<C-w>j" + .ll! + execute "normal \<C-w>k" + call assert_equal(l:middle, bufnr()) +endfunc + +" Fail :llast but :llast! is allowed +func Test_llast() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first, _, l:last] = s:make_simple_location_list() + lfirst! + + call assert_fails("llast", "E1513:") + call assert_equal(l:first, bufnr()) + + llast! + call assert_equal(l:last, bufnr()) +endfunc + +" Fail :lnext but :lnext! is allowed +func Test_lnext() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first, l:middle, l:last] = s:make_simple_location_list() + ll! + + call assert_fails("lnext", "E1513:") + call assert_equal(l:first, bufnr()) + + lnext! + call assert_equal(l:middle, bufnr()) +endfunc + +" Fail :lnfile but :lnfile! is allowed +func Test_lnfile() + CheckFeature quickfix + call s:reset_all_buffers() + + let [_, l:current, l:last] = s:make_simple_location_list() + call assert_equal(1, getloclist(0, #{idx: 0}).idx) + + lnext! + call assert_equal(2, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:current, bufnr()) + + call assert_fails("lnfile", "E1513:") + " Ensure the entry didn't change. + call assert_equal(2, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:current, bufnr()) + + lnfile! + call assert_equal(4, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:last, bufnr()) +endfunc + +" Fail :lpfile but :lpfile! is allowed +func Test_lpfile() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first, l:current, _] = s:make_simple_location_list() + lnext! + + call assert_fails("lpfile", "E1513:") + call assert_equal(l:current, bufnr()) + + lnext! " Reset for the next test call + + lpfile! + call assert_equal(l:first, bufnr()) +endfunc + +" Fail :lprevious but :lprevious! is allowed +func Test_lprevious() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first, l:middle, _] = s:make_simple_location_list() + call assert_equal(1, getloclist(0, #{idx: 0}).idx) + + lnext! + call assert_equal(2, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:middle, bufnr()) + + call assert_fails("lprevious", "E1513:") + " Ensure the entry didn't change. + call assert_equal(2, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:middle, bufnr()) + + lprevious! + call assert_equal(1, getloclist(0, #{idx: 0}).idx) + call assert_equal(l:first, bufnr()) +endfunc + +" Fail :lrewind but :lrewind! is allowed +func Test_lrewind() + CheckFeature quickfix + call s:reset_all_buffers() + + let [l:first, l:middle, _] = s:make_simple_location_list() + lnext! + + call assert_fails("lrewind", "E1513:") + call assert_equal(l:middle, bufnr()) + + lrewind! + call assert_equal(l:first, bufnr()) +endfunc + +" Fail :ltag but :ltag! is allowed +func Test_ltag() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + execute "normal \<C-]>" + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("ltag one", "E1513:") + + ltag! one + + set tags& +endfunc + +" Fail vim.cmd if we try to change buffers while 'winfixbuf' is set +func Test_lua_command() + " CheckFeature lua + call s:reset_all_buffers() + + enew + file first + let l:previous = bufnr() + + enew + file second + let l:current = bufnr() + + set winfixbuf + + call assert_fails('lua vim.cmd("buffer " .. ' . l:previous . ')') + call assert_equal(l:current, bufnr()) + + execute 'lua vim.cmd("buffer! " .. ' . l:previous . ')' + call assert_equal(l:previous, bufnr()) +endfunc + +" Fail :lvimgrep but :lvimgrep! is allowed +func Test_lvimgrep() + CheckFeature quickfix + call s:reset_all_buffers() + + edit first.unittest + call append(0, ["some-search-term"]) + write + + edit winfix.unittest + call append(0, ["some-search-term"]) + write + let l:current = bufnr() + + set winfixbuf + + edit! last.unittest + call append(0, ["some-search-term"]) + write + let l:last = bufnr() + + buffer! winfix.unittest + + call assert_fails("lvimgrep /some-search-term/ *.unittest", "E1513:") + call assert_equal(l:current, bufnr()) + + lvimgrep! /some-search-term/ *.unittest + call assert_notequal(l:current, bufnr()) + + call delete("first.unittest") + call delete("winfix.unittest") + call delete("last.unittest") +endfunc + +" Fail :lvimgrepadd but :lvimgrepadd! is allowed +func Test_lvimgrepadd() + CheckFeature quickfix + call s:reset_all_buffers() + + edit first.unittest + call append(0, ["some-search-term"]) + write + + edit winfix.unittest + call append(0, ["some-search-term"]) + write + let l:current = bufnr() + + set winfixbuf + + edit! last.unittest + call append(0, ["some-search-term"]) + write + let l:last = bufnr() + + buffer! winfix.unittest + + call assert_fails("lvimgrepadd /some-search-term/ *.unittest") + call assert_equal(l:current, bufnr()) + + lvimgrepadd! /some-search-term/ *.unittest + call assert_notequal(l:current, bufnr()) + + call delete("first.unittest") + call delete("winfix.unittest") + call delete("last.unittest") +endfunc + +" Don't allow global marks to change the current 'winfixbuf' window +func Test_marks_mappings_fail() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + execute "buffer! " . l:other + normal mA + execute "buffer! " . l:current + normal mB + + call assert_fails("normal `A", "E1513:") + call assert_equal(l:current, bufnr()) + + call assert_fails("normal 'A", "E1513:") + call assert_equal(l:current, bufnr()) + + set nowinfixbuf + + normal `A + call assert_equal(l:other, bufnr()) +endfunc + +" Allow global marks in a 'winfixbuf' window if the jump is the same buffer +func Test_marks_mappings_pass_intra_move() + call s:reset_all_buffers() + + let l:current = bufnr() + call append(0, ["some line", "another line"]) + normal mA + normal j + normal mB + + set winfixbuf + + normal `A + call assert_equal(l:current, bufnr()) +endfunc + +" Fail :next but :next! is allowed +func Test_next() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + first! + + call assert_fails("next", "E1513:") + call assert_equal(l:first, bufnr()) + + next! + call assert_notequal(l:first, bufnr()) +endfunc + +" Ensure :mksession saves 'winfixbuf' details +func Test_mksession() + CheckFeature mksession + call s:reset_all_buffers() + + set sessionoptions+=options + set winfixbuf + + mksession test_winfixbuf_Test_mksession.vim + + call s:reset_all_buffers() + let l:winfixbuf = &winfixbuf + call assert_equal(0, l:winfixbuf) + + source test_winfixbuf_Test_mksession.vim + + let l:winfixbuf = &winfixbuf + call assert_equal(1, l:winfixbuf) + + set sessionoptions& + call delete("test_winfixbuf_Test_mksession.vim") +endfunc + +" Allow :next if the next index is the same as the current buffer +func Test_next_same_buffer() + call s:reset_all_buffers() + + enew + file foo + enew + file bar + enew + file fizz + enew + file buzz + args foo foo bar fizz buzz + + edit foo + set winfixbuf + let l:current = bufnr() + + " Allow :next because the args list is `[foo] foo bar fizz buzz + next + call assert_equal(l:current, bufnr()) + + " Fail :next because the args list is `foo [foo] bar fizz buzz + " and the next buffer would be bar, which is a different buffer + call assert_fails("next", "E1513:") + call assert_equal(l:current, bufnr()) +endfunc + +" Fail to jump to a tag with g<C-]> if 'winfixbuf' is enabled +func Test_normal_g_ctrl_square_bracket_right() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal g\<C-]>", "E1513:") + call assert_equal(l:current, bufnr()) + + set tags& +endfunc + +" Fail to jump to a tag with g<RightMouse> if 'winfixbuf' is enabled +func Test_normal_g_rightmouse() + call s:reset_all_buffers() + set mouse=n + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + execute "normal \<C-]>" + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal g\<RightMouse>", "E1513:") + call assert_equal(l:current, bufnr()) + + set tags& + set mouse& +endfunc + +" Fail to jump to a tag with g] if 'winfixbuf' is enabled +func Test_normal_g_square_bracket_right() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal g]", "E1513:") + call assert_equal(l:current, bufnr()) + + set tags& +endfunc + +" Fail to jump to a tag with <C-RightMouse> if 'winfixbuf' is enabled +func Test_normal_ctrl_rightmouse() + call s:reset_all_buffers() + set mouse=n + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + execute "normal \<C-]>" + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal \<C-RightMouse>", "E1513:") + call assert_equal(l:current, bufnr()) + + set tags& + set mouse& +endfunc + +" Fail to jump to a tag with <C-t> if 'winfixbuf' is enabled +func Test_normal_ctrl_t() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + execute "normal \<C-]>" + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal \<C-t>", "E1513:") + call assert_equal(l:current, bufnr()) + + set tags& +endfunc + +" Disallow <C-^> in 'winfixbuf' windows +func Test_normal_ctrl_hat() + call s:reset_all_buffers() + clearjumps + + enew + file first + let l:first = bufnr() + + enew + file current + let l:current = bufnr() + + set winfixbuf + + call assert_fails("normal \<C-^>", "E1513:") + call assert_equal(l:current, bufnr()) +endfunc + +" Allow <C-i> in 'winfixbuf' windows if the movement stays within the buffer +func Test_normal_ctrl_i_pass() + call s:reset_all_buffers() + clearjumps + + enew + file first + let l:first = bufnr() + + enew! + file current + let l:current = bufnr() + " Add some lines so we can populate a jumplist" + call append(0, ["some line", "another line"]) + " Add an entry to the jump list + " Go up another line + normal m` + normal k + execute "normal \<C-o>" + + set winfixbuf + + let l:line = getcurpos()[1] + execute "normal 1\<C-i>" + call assert_notequal(l:line, getcurpos()[1]) +endfunc + +" Disallow <C-o> in 'winfixbuf' windows if it would cause the buffer to switch +func Test_normal_ctrl_o_fail() + call s:reset_all_buffers() + clearjumps + + enew + file first + let l:first = bufnr() + + enew + file current + let l:current = bufnr() + + set winfixbuf + + call assert_fails("normal \<C-o>", "E1513:") + call assert_equal(l:current, bufnr()) +endfunc + +" Allow <C-o> in 'winfixbuf' windows if the movement stays within the buffer +func Test_normal_ctrl_o_pass() + call s:reset_all_buffers() + clearjumps + + enew + file first + let l:first = bufnr() + + enew! + file current + let l:current = bufnr() + " Add some lines so we can populate a jumplist + call append(0, ["some line", "another line"]) + " Add an entry to the jump list + " Go up another line + normal m` + normal k + + set winfixbuf + + execute "normal \<C-o>" + call assert_equal(l:current, bufnr()) +endfunc + +" Fail to jump to a tag with <C-]> if 'winfixbuf' is enabled +func Test_normal_ctrl_square_bracket_right() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal \<C-]>", "E1513:") + call assert_equal(l:current, bufnr()) + + set tags& +endfunc + +" Allow <C-w><C-]> with 'winfixbuf' enabled because it runs in a new, split window +func Test_normal_ctrl_w_ctrl_square_bracket_right() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current_windows = s:get_windows_count() + execute "normal \<C-w>\<C-]>" + call assert_equal(l:current_windows + 1, s:get_windows_count()) + + set tags& +endfunc + +" Allow <C-w>g<C-]> with 'winfixbuf' enabled because it runs in a new, split window +func Test_normal_ctrl_w_g_ctrl_square_bracket_right() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current_windows = s:get_windows_count() + execute "normal \<C-w>g\<C-]>" + call assert_equal(l:current_windows + 1, s:get_windows_count()) + + set tags& +endfunc + +" Fail to jump to a tag with <C-]> if 'winfixbuf' is enabled +func Test_normal_gt() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one", "two", "three"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal \<C-]>", "E1513:") + call assert_equal(l:current, bufnr()) + + set tags& +endfunc + +" Prevent gF from switching a 'winfixbuf' window's buffer +func Test_normal_gF() + call s:reset_all_buffers() + + let l:file = tempname() + call append(0, [l:file]) + call writefile([], l:file, 'D') + " Place the cursor onto the line that has `l:file` + normal gg + " Prevent Vim from erroring with "No write since last change @ command + " line" when we try to call gF, later. + set hidden + + set winfixbuf + + let l:buffer = bufnr() + + call assert_fails("normal gF", "E1513:") + call assert_equal(l:buffer, bufnr()) + + set nowinfixbuf + + normal gF + call assert_notequal(l:buffer, bufnr()) + + set nohidden +endfunc + +" Prevent gf from switching a 'winfixbuf' window's buffer +func Test_normal_gf() + call s:reset_all_buffers() + + let l:file = tempname() + call append(0, [l:file]) + call writefile([], l:file, 'D') + " Place the cursor onto the line that has `l:file` + normal gg + " Prevent Vim from erroring with "No write since last change @ command + " line" when we try to call gf, later. + set hidden + + set winfixbuf + + let l:buffer = bufnr() + + call assert_fails("normal gf", "E1513:") + call assert_equal(l:buffer, bufnr()) + + set nowinfixbuf + + normal gf + call assert_notequal(l:buffer, bufnr()) + + set nohidden +endfunc + +" Fail "goto file under the cursor" (using [f, which is the same as `:normal gf`) +func Test_normal_square_bracket_left_f() + call s:reset_all_buffers() + + let l:file = tempname() + call append(0, [l:file]) + call writefile([], l:file, 'D') + " Place the cursor onto the line that has `l:file` + normal gg + " Prevent Vim from erroring with "No write since last change @ command + " line" when we try to call gf, later. + set hidden + + set winfixbuf + + let l:buffer = bufnr() + + call assert_fails("normal [f", "E1513:") + call assert_equal(l:buffer, bufnr()) + + set nowinfixbuf + + normal [f + call assert_notequal(l:buffer, bufnr()) + + set nohidden +endfunc + +" Fail to go to a C macro with [<C-d> if 'winfixbuf' is enabled +func Test_normal_square_bracket_left_ctrl_d() + call s:reset_all_buffers() + + let l:include_file = tempname() . ".h" + call writefile(["min(1, 12);", + \ '#include "' . l:include_file . '"' + \ ], + \ "main.c", 'D') + call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file, 'D') + edit main.c + normal ]\<C-d> + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal [\<C-d>", "E1513:") + call assert_equal(l:current, bufnr()) + + set nowinfixbuf + + execute "normal [\<C-d>" + call assert_notequal(l:current, bufnr()) +endfunc + +" Fail to go to a C macro with ]<C-d> if 'winfixbuf' is enabled +func Test_normal_square_bracket_right_ctrl_d() + call s:reset_all_buffers() + + let l:include_file = tempname() . ".h" + call writefile(["min(1, 12);", + \ '#include "' . l:include_file . '"' + \ ], + \ "main.c", 'D') + call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file, 'D') + edit main.c + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal ]\<C-d>", "E1513:") + call assert_equal(l:current, bufnr()) + + set nowinfixbuf + + execute "normal ]\<C-d>" + call assert_notequal(l:current, bufnr()) +endfunc + +" Fail to go to a C macro with [<C-i> if 'winfixbuf' is enabled +func Test_normal_square_bracket_left_ctrl_i() + call s:reset_all_buffers() + + let l:include_file = tempname() . ".h" + call writefile(['#include "' . l:include_file . '"', + \ "min(1, 12);", + \ ], + \ "main.c", 'D') + call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file, 'D') + edit main.c + " Move to the line with `min(1, 12);` on it" + normal j + + set define=^\\s*#\\s*define + set include=^\\s*#\\s*include + set path=.,/usr/include,, + + let l:current = bufnr() + + set winfixbuf + + call assert_fails("normal [\<C-i>", "E1513:") + + set nowinfixbuf + + execute "normal [\<C-i>" + call assert_notequal(l:current, bufnr()) + + set define& + set include& + set path& +endfunc + +" Fail to go to a C macro with ]<C-i> if 'winfixbuf' is enabled +func Test_normal_square_bracket_right_ctrl_i() + call s:reset_all_buffers() + + let l:include_file = tempname() . ".h" + call writefile(["min(1, 12);", + \ '#include "' . l:include_file . '"' + \ ], + \ "main.c", 'D') + call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file, 'D') + edit main.c + + set winfixbuf + + set define=^\\s*#\\s*define + set include=^\\s*#\\s*include + set path=.,/usr/include,, + + let l:current = bufnr() + + call assert_fails("normal ]\<C-i>", "E1513:") + call assert_equal(l:current, bufnr()) + + set nowinfixbuf + + execute "normal ]\<C-i>" + call assert_notequal(l:current, bufnr()) + + set define& + set include& + set path& +endfunc + +" Fail "goto file under the cursor" (using ]f, which is the same as `:normal gf`) +func Test_normal_square_bracket_right_f() + call s:reset_all_buffers() + + let l:file = tempname() + call append(0, [l:file]) + call writefile([], l:file, 'D') + " Place the cursor onto the line that has `l:file` + normal gg + " Prevent Vim from erroring with "No write since last change @ command + " line" when we try to call gf, later. + set hidden + + set winfixbuf + + let l:buffer = bufnr() + + call assert_fails("normal ]f", "E1513:") + call assert_equal(l:buffer, bufnr()) + + set nowinfixbuf + + normal ]f + call assert_notequal(l:buffer, bufnr()) + + set nohidden +endfunc + +" Fail to jump to a tag with v<C-]> if 'winfixbuf' is enabled +func Test_normal_v_ctrl_square_bracket_right() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal v\<C-]>", "E1513:") + call assert_equal(l:current, bufnr()) + + set tags& +endfunc + +" Fail to jump to a tag with vg<C-]> if 'winfixbuf' is enabled +func Test_normal_v_g_ctrl_square_bracket_right() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("normal vg\<C-]>", "E1513:") + call assert_equal(l:current, bufnr()) + + set tags& +endfunc + +" Allow :pedit because, unlike :edit, it uses a separate window +func Test_pedit() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + + pedit other + + execute "normal \<C-w>w" + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :pop but :pop! is allowed +func Test_pop() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "thesame\tXfile\t1;\"\td\tfile:", + \ "thesame\tXfile\t2;\"\td\tfile:", + \ "thesame\tXfile\t3;\"\td\tfile:", + \ ], + \ "Xtags", 'D') + call writefile(["thesame one", "thesame two", "thesame three"], "Xfile", 'D') + call writefile(["thesame one"], "Xother", 'D') + edit Xother + + tag thesame + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("pop", "E1513:") + call assert_equal(l:current, bufnr()) + + pop! + call assert_notequal(l:current, bufnr()) + + set tags& +endfunc + +" Fail :previous but :previous! is allowed +func Test_previous() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + next! + + call assert_fails("previous", "E1513:") + call assert_notequal(l:first, bufnr()) + + previous! + call assert_equal(l:first, bufnr()) +endfunc + +" Fail pyxdo if it changes a window with 'winfixbuf' is set +func Test_pythonx_pyxdo() + CheckFeature pythonx + call s:reset_all_buffers() + + enew + file first + let g:_previous_buffer = bufnr() + + enew + file second + + set winfixbuf + + pythonx << EOF +import vim + +def test_winfixbuf_Test_pythonx_pyxdo_set_buffer(): + buffer = vim.vars['_previous_buffer'] + vim.current.buffer = vim.buffers[buffer] +EOF + + try + pyxdo test_winfixbuf_Test_pythonx_pyxdo_set_buffer() + catch /pynvim\.api\.common\.NvimError: E1513:/ + let l:caught = 1 + endtry + + call assert_equal(1, l:caught) + + unlet g:_previous_buffer +endfunc + +" Fail pyxfile if it changes a window with 'winfixbuf' is set +func Test_pythonx_pyxfile() + CheckFeature pythonx + call s:reset_all_buffers() + + enew + file first + let g:_previous_buffer = bufnr() + + enew + file second + + set winfixbuf + + call writefile(["import vim", + \ "buffer = vim.vars['_previous_buffer']", + \ "vim.current.buffer = vim.buffers[buffer]", + \ ], + \ "file.py", 'D') + + try + pyxfile file.py + catch /pynvim\.api\.common\.NvimError: E1513:/ + let l:caught = 1 + endtry + + call assert_equal(1, l:caught) + + unlet g:_previous_buffer +endfunc + +" Fail vim.current.buffer if 'winfixbuf' is set +func Test_pythonx_vim_current_buffer() + CheckFeature pythonx + call s:reset_all_buffers() + + enew + file first + let g:_previous_buffer = bufnr() + + enew + file second + + let l:caught = 0 + + set winfixbuf + + try + pythonx << EOF +import vim + +buffer = vim.vars["_previous_buffer"] +vim.current.buffer = vim.buffers[buffer] +EOF + catch /pynvim\.api\.common\.NvimError: E1513:/ + let l:caught = 1 + endtry + + call assert_equal(1, l:caught) + unlet g:_previous_buffer +endfunc + +" Ensure remapping to a disabled action still triggers failures +func Test_remap_key_fail() + call s:reset_all_buffers() + + enew + file first + let l:first = bufnr() + + enew + file current + let l:current = bufnr() + + set winfixbuf + + nnoremap g <C-^> + + call assert_fails("normal g", "E1513:") + call assert_equal(l:current, bufnr()) + + nunmap g +endfunc + +" Ensure remapping a disabled key to something valid does trigger any failures +func Test_remap_key_pass() + call s:reset_all_buffers() + + enew + file first + let l:first = bufnr() + + enew + file current + let l:current = bufnr() + + set winfixbuf + + call assert_fails("normal \<C-^>", "E1513:") + call assert_equal(l:current, bufnr()) + + " Disallow <C-^> by default but allow it if the command does something else + nnoremap <C-^> :echo "hello!" + + execute "normal \<C-^>" + call assert_equal(l:current, bufnr()) + + nunmap <C-^> +endfunc + +" Fail :rewind but :rewind! is allowed +func Test_rewind() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + next! + + call assert_fails("rewind", "E1513:") + call assert_notequal(l:first, bufnr()) + + rewind! + call assert_equal(l:first, bufnr()) +endfunc + +" Allow :sblast because it opens the buffer in a new, split window +func Test_sblast() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs(1) + bfirst! + let l:current = bufnr() + + sblast + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :sbprevious but :sbprevious! is allowed +func Test_sbprevious() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + sbprevious + call assert_equal(l:other, bufnr()) +endfunc + +" Make sure 'winfixbuf' can be set using 'winfixbuf' or 'wfb' +func Test_short_option() + call s:reset_all_buffers() + + call s:make_buffer_pairs() + + set winfixbuf + call assert_fails("edit something_else", "E1513:") + + set nowinfixbuf + set wfb + call assert_fails("edit another_place", "E1513:") + + set nowfb + edit last_place +endfunc + +" Allow :snext because it makes a new window +func Test_snext() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + first! + + let l:current_window = win_getid() + + snext + call assert_notequal(l:current_window, win_getid()) + call assert_notequal(l:first, bufnr()) +endfunc + +" Ensure the first has 'winfixbuf' and a new split window is 'nowinfixbuf' +func Test_split_window() + call s:reset_all_buffers() + + split + execute "normal \<C-w>j" + + set winfixbuf + + let l:winfix_window_1 = win_getid() + vsplit + let l:winfix_window_2 = win_getid() + + call assert_equal(1, getwinvar(l:winfix_window_1, "&winfixbuf")) + call assert_equal(0, getwinvar(l:winfix_window_2, "&winfixbuf")) +endfunc + +" Fail :tNext but :tNext! is allowed +func Test_tNext() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "thesame\tXfile\t1;\"\td\tfile:", + \ "thesame\tXfile\t2;\"\td\tfile:", + \ "thesame\tXfile\t3;\"\td\tfile:", + \ ], + \ "Xtags", 'D') + call writefile(["thesame one", "thesame two", "thesame three"], "Xfile", 'D') + call writefile(["thesame one"], "Xother", 'D') + edit Xother + + tag thesame + execute "normal \<C-^>" + tnext! + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("tNext", "E1513:") + call assert_equal(l:current, bufnr()) + + tNext! + + set tags& +endfunc + +" Call :tabdo and choose the next available 'nowinfixbuf' window. +func Test_tabdo_choose_available_window() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + + " Make a split window that is 'nowinfixbuf' but make it the second-to-last + " window so that :tabdo will first try the 'winfixbuf' window, pass over it, + " and prefer the other 'nowinfixbuf' window, instead. + " + " +-------------------+ + " | 'nowinfixbuf' | + " +-------------------+ + " | 'winfixbuf' | <-- Cursor is here + " +-------------------+ + split + let l:nowinfixbuf_window = win_getid() + " Move to the 'winfixbuf' window now + execute "normal \<C-w>j" + let l:winfixbuf_window = win_getid() + + let l:expected_windows = s:get_windows_count() + tabdo echo '' + call assert_equal(l:nowinfixbuf_window, win_getid()) + call assert_equal(l:first, bufnr()) + call assert_equal(l:expected_windows, s:get_windows_count()) +endfunc + +" Call :tabdo and create a new split window if all available windows are 'winfixbuf'. +func Test_tabdo_make_new_window() + call s:reset_all_buffers() + + let [l:first, _] = s:make_buffers_list() + execute "buffer! " . l:first + + let l:current = win_getid() + let l:current_windows = s:get_windows_count() + + tabdo echo '' + call assert_notequal(l:current, win_getid()) + call assert_equal(l:first, bufnr()) + execute "normal \<C-w>j" + call assert_equal(l:first, bufnr()) + call assert_equal(l:current_windows + 1, s:get_windows_count()) +endfunc + +" Fail :tag but :tag! is allowed +func Test_tag() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("tag one", "E1513:") + call assert_equal(l:current, bufnr()) + + tag! one + call assert_notequal(l:current, bufnr()) + + set tags& +endfunc + + +" Fail :tfirst but :tfirst! is allowed +func Test_tfirst() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("tfirst", "E1513:") + call assert_equal(l:current, bufnr()) + + tfirst! + call assert_notequal(l:current, bufnr()) + + set tags& +endfunc + +" Fail :tjump but :tjump! is allowed +func Test_tjump() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + call writefile(["one"], "Xother", 'D') + edit Xother + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("tjump one", "E1513:") + call assert_equal(l:current, bufnr()) + + tjump! one + call assert_notequal(l:current, bufnr()) + + set tags& +endfunc + +" Fail :tlast but :tlast! is allowed +func Test_tlast() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "one\tXfile\t1", + \ "three\tXfile\t3", + \ "two\tXfile\t2"], + \ "Xtags", 'D') + call writefile(["one", "two", "three"], "Xfile", 'D') + edit Xfile + tjump one + edit Xfile + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("tlast", "E1513:") + call assert_equal(l:current, bufnr()) + + tlast! + call assert_equal(l:current, bufnr()) + + set tags& +endfunc + +" Fail :tnext but :tnext! is allowed +func Test_tnext() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "thesame\tXfile\t1;\"\td\tfile:", + \ "thesame\tXfile\t2;\"\td\tfile:", + \ "thesame\tXfile\t3;\"\td\tfile:", + \ ], + \ "Xtags", 'D') + call writefile(["thesame one", "thesame two", "thesame three"], "Xfile", 'D') + call writefile(["thesame one"], "Xother", 'D') + edit Xother + + tag thesame + execute "normal \<C-^>" + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("tnext", "E1513:") + call assert_equal(l:current, bufnr()) + + tnext! + call assert_notequal(l:current, bufnr()) + + set tags& +endfunc + +" Fail :tprevious but :tprevious! is allowed +func Test_tprevious() + call s:reset_all_buffers() + + set tags=Xtags + call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", + \ "thesame\tXfile\t1;\"\td\tfile:", + \ "thesame\tXfile\t2;\"\td\tfile:", + \ "thesame\tXfile\t3;\"\td\tfile:", + \ ], + \ "Xtags", 'D') + call writefile(["thesame one", "thesame two", "thesame three"], "Xfile", 'D') + call writefile(["thesame one"], "Xother", 'D') + edit Xother + + tag thesame + execute "normal \<C-^>" + tnext! + + set winfixbuf + + let l:current = bufnr() + + call assert_fails("tprevious", "E1513:") + call assert_equal(l:current, bufnr()) + + tprevious! + + set tags& +endfunc + +" Fail :view but :view! is allowed +func Test_view() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("view other", "E1513:") + call assert_equal(l:current, bufnr()) + + view! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :visual but :visual! is allowed +func Test_visual() + call s:reset_all_buffers() + + let l:other = s:make_buffer_pairs() + let l:current = bufnr() + + call assert_fails("visual other", "E1513:") + call assert_equal(l:current, bufnr()) + + visual! other + call assert_equal(l:other, bufnr()) +endfunc + +" Fail :vimgrep but :vimgrep! is allowed +func Test_vimgrep() + CheckFeature quickfix + call s:reset_all_buffers() + + edit first.unittest + call append(0, ["some-search-term"]) + write + + edit winfix.unittest + call append(0, ["some-search-term"]) + write + let l:current = bufnr() + + set winfixbuf + + edit! last.unittest + call append(0, ["some-search-term"]) + write + let l:last = bufnr() + + buffer! winfix.unittest + + call assert_fails("vimgrep /some-search-term/ *.unittest") + call assert_equal(l:current, bufnr()) + + " Don't error and also do swap to the first match because ! was included + vimgrep! /some-search-term/ *.unittest + call assert_notequal(l:current, bufnr()) + + call delete("first.unittest") + call delete("winfix.unittest") + call delete("last.unittest") +endfunc + +" Fail :vimgrepadd but ::vimgrepadd! is allowed +func Test_vimgrepadd() + CheckFeature quickfix + call s:reset_all_buffers() + + edit first.unittest + call append(0, ["some-search-term"]) + write + + edit winfix.unittest + call append(0, ["some-search-term"]) + write + let l:current = bufnr() + + set winfixbuf + + edit! last.unittest + call append(0, ["some-search-term"]) + write + let l:last = bufnr() + + buffer! winfix.unittest + + call assert_fails("vimgrepadd /some-search-term/ *.unittest") + call assert_equal(l:current, bufnr()) + + vimgrepadd! /some-search-term/ *.unittest + call assert_notequal(l:current, bufnr()) + call delete("first.unittest") + call delete("winfix.unittest") + call delete("last.unittest") +endfunc + +" Fail :wNext but :wNext! is allowed +func Test_wNext() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + next! + + call assert_fails("wNext", "E1513:") + call assert_notequal(l:first, bufnr()) + + wNext! + call assert_equal(l:first, bufnr()) + + call delete("first") + call delete("middle") + call delete("last") +endfunc + +" Allow :windo unless `:windo foo` would change a 'winfixbuf' window's buffer +func Test_windo() + call s:reset_all_buffers() + + let l:current_window = win_getid() + let l:current_buffer = bufnr() + split + enew + file some_other_buffer + + set winfixbuf + + let l:current = win_getid() + + windo echo '' + call assert_equal(l:current_window, win_getid()) + + call assert_fails('execute "windo buffer ' . l:current_buffer . '"', "E1513:") + call assert_equal(l:current_window, win_getid()) + + execute "windo buffer! " . l:current_buffer + call assert_equal(l:current_window, win_getid()) +endfunc + +" Fail :wnext but :wnext! is allowed +func Test_wnext() + call s:reset_all_buffers() + + let [_, l:last] = s:make_args_list() + next! + + call assert_fails("wnext", "E1513:") + call assert_notequal(l:last, bufnr()) + + wnext! + call assert_equal(l:last, bufnr()) + + call delete("first") + call delete("middle") + call delete("last") +endfunc + +" Fail :wprevious but :wprevious! is allowed +func Test_wprevious() + call s:reset_all_buffers() + + let [l:first, _] = s:make_args_list() + next! + + call assert_fails("wprevious", "E1513:") + call assert_notequal(l:first, bufnr()) + + wprevious! + call assert_equal(l:first, bufnr()) + + call delete("first") + call delete("middle") + call delete("last") +endfunc + +func Test_quickfix_switchbuf_invalid_prevwin() + call s:reset_all_buffers() + + call s:make_simple_quickfix() + call assert_equal(1, getqflist(#{idx: 0}).idx) + + set switchbuf=uselast + split + copen + execute winnr('#') 'quit' + call assert_equal(2, winnr('$')) + + cnext " Would've triggered a null pointer member access + call assert_equal(2, getqflist(#{idx: 0}).idx) + + set switchbuf& +endfunc + +func Test_listdo_goto_prevwin() + call s:reset_all_buffers() + call s:make_buffers_list() + + new + call assert_equal(0, &winfixbuf) + wincmd p + call assert_equal(1, &winfixbuf) + call assert_notequal(bufnr(), bufnr('#')) + + augroup ListDoGotoPrevwin + au! + au BufLeave * let s:triggered = 1 + \| call assert_equal(bufnr(), winbufnr(winnr())) + augroup END + " Should correctly switch to the window without 'winfixbuf', and curbuf should + " be consistent with curwin->w_buffer for autocommands. + bufdo " + call assert_equal(0, &winfixbuf) + call assert_equal(1, s:triggered) + unlet! s:triggered + au! ListDoGotoPrevwin + + set winfixbuf + wincmd p + call assert_equal(2, winnr('$')) + " Both curwin and prevwin have 'winfixbuf' set, so should split a new window + " without it set. + bufdo " + call assert_equal(0, &winfixbuf) + call assert_equal(3, winnr('$')) + + quit + call assert_equal(2, winnr('$')) + call assert_equal(1, &winfixbuf) + augroup ListDoGotoPrevwin + au! + au WinEnter * ++once set winfixbuf + augroup END + " Same as before, but naughty autocommands set 'winfixbuf' for the new window. + " :bufdo should give up in this case. + call assert_fails('bufdo "', 'E1513:') + + au! ListDoGotoPrevwin + augroup! ListDoGotoPrevwin +endfunc + +func Test_quickfix_changed_split_failed() + call s:reset_all_buffers() + + call s:make_simple_quickfix() + call assert_equal(1, winnr('$')) + + " Quickfix code will open a split in an attempt to get a 'nowinfixbuf' window + " to switch buffers in. Interfere with things by setting 'winfixbuf' in it. + augroup QfChanged + au! + au WinEnter * ++once call assert_equal(2, winnr('$')) + \| set winfixbuf | call setqflist([], 'f') + augroup END + call assert_fails('cnext', ['E1513:', 'E925:']) + " Check that the split was automatically closed. + call assert_equal(1, winnr('$')) + + au! QfChanged + augroup! QfChanged +endfunc + +func Test_bufdo_cnext_splitwin_fails() + call s:reset_all_buffers() + call s:make_simple_quickfix() + call assert_equal(1, getqflist(#{idx: 0}).idx) + " Make sure there is not enough room to + " split the winfixedbuf window + let &winheight=&lines + let &winminheight=&lines-2 + " Still want E1513, or it may not be clear why a split was attempted and why + " it failing caused the commands to abort. + call assert_fails(':bufdo echo 1', ['E36:', 'E1513:']) + call assert_fails(':cnext', ['E36:', 'E1513:']) + " Ensure the entry didn't change. + call assert_equal(1, getqflist(#{idx: 0}).idx) + set winminheight&vim winheight&vim +endfunc + +" Test that exiting with 'winfixbuf' and EXITFREE doesn't cause an error. +func Test_exitfree_no_error() + let lines =<< trim END + set winfixbuf + qall! + END + call writefile(lines, 'Xwfb_exitfree', 'D') + call assert_notmatch('E1513:', + "\ system(GetVimCommandClean() .. ' --not-a-term -X -S Xwfb_exitfree')) + \ system(GetVimCommandClean() .. ' -X -S Xwfb_exitfree')) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/vim9.vim b/test/old/testdir/vim9.vim index 218fab6c5e..7d1eec7d4f 100644 --- a/test/old/testdir/vim9.vim +++ b/test/old/testdir/vim9.vim @@ -46,49 +46,6 @@ func CheckScriptSuccess(lines) endtry endfunc -" :source a list of "lines" and check whether it fails with "error" -func CheckSourceFailure(lines, error, lnum = -3) - if get(a:lines, 0, '') ==# 'vim9script' - return - endif - new - call setline(1, a:lines) - try - call assert_fails('source', a:error, a:lines, a:lnum) - finally - bw! - endtry -endfunc - -" :source a list of "lines" and check whether it fails with the list of -" "errors" -func CheckSourceFailureList(lines, errors, lnum = -3) - if get(a:lines, 0, '') ==# 'vim9script' - return - endif - new - call setline(1, a:lines) - try - call assert_fails('source', a:errors, a:lines, a:lnum) - finally - bw! - endtry -endfunc - -" :source a list of "lines" and check whether it succeeds -func CheckSourceSuccess(lines) - if get(a:lines, 0, '') ==# 'vim9script' - return - endif - new - call setline(1, a:lines) - try - :source - finally - bw! - endtry -endfunc - func CheckDefAndScriptSuccess(lines) return endfunc @@ -185,3 +142,96 @@ func CheckLegacyAndVim9Failure(lines, error) \ }) call CheckLegacyFailure(legacylines, legacyError) endfunc + +" :source a list of "lines" and check whether it fails with "error" +func CheckSourceScriptFailure(lines, error, lnum = -3) + if get(a:lines, 0, '') ==# 'vim9script' + return + endif + let cwd = getcwd() + new + call setline(1, a:lines) + let bnr = bufnr() + try + call assert_fails('source', a:error, a:lines, a:lnum) + finally + call chdir(cwd) + exe $':bw! {bnr}' + endtry +endfunc + +" :source a list of "lines" and check whether it fails with the list of +" "errors" +func CheckSourceScriptFailureList(lines, errors, lnum = -3) + if get(a:lines, 0, '') ==# 'vim9script' + return + endif + let cwd = getcwd() + new + let bnr = bufnr() + call setline(1, a:lines) + try + call assert_fails('source', a:errors, a:lines, a:lnum) + finally + call chdir(cwd) + exe $':bw! {bnr}' + endtry +endfunc + +" :source a list of "lines" and check whether it succeeds +func CheckSourceScriptSuccess(lines) + if get(a:lines, 0, '') ==# 'vim9script' + return + endif + let cwd = getcwd() + new + let bnr = bufnr() + call setline(1, a:lines) + try + :source + finally + call chdir(cwd) + exe $':bw! {bnr}' + endtry +endfunc + +func CheckSourceSuccess(lines) + call CheckSourceScriptSuccess(a:lines) +endfunc + +func CheckSourceFailure(lines, error, lnum = -3) + call CheckSourceScriptFailure(a:lines, a:error, a:lnum) +endfunc + +func CheckSourceFailureList(lines, errors, lnum = -3) + call CheckSourceScriptFailureList(a:lines, a:errors, a:lnum) +endfunc + +func CheckSourceDefSuccess(lines) + return +endfunc + +func CheckSourceDefAndScriptSuccess(lines) + return +endfunc + +func CheckSourceDefCompileSuccess(lines) + return +endfunc + +func CheckSourceDefFailure(lines, error, lnum = -3) + return +endfunc + +func CheckSourceDefExecFailure(lines, error, lnum = -3) + return +endfunc + +func CheckSourceDefAndScriptFailure(lines, error, lnum = -3) + return +endfunc + +func CheckSourceDefExecAndScriptFailure(lines, error, lnum = -3) + return +endfunc + diff --git a/test/helpers.lua b/test/testutil.lua index 3d53aa3be9..439f13cf49 100644 --- a/test/helpers.lua +++ b/test/testutil.lua @@ -16,8 +16,9 @@ local function shell_quote(str) return str end ---- @class test.helpers -local module = { +--- This module uses functions from the context of the test runner. +--- @class test.testutil +local M = { paths = Paths, } @@ -30,7 +31,7 @@ end --- @param path string --- @return boolean -function module.isdir(path) +function M.isdir(path) if not path then return false end @@ -43,7 +44,7 @@ end --- @param ... string|string[] --- @return string -function module.argss_to_cmd(...) +function M.argss_to_cmd(...) local cmd = {} --- @type string[] for i = 1, select('#', ...) do local arg = select(i, ...) @@ -59,8 +60,8 @@ function module.argss_to_cmd(...) return table.concat(cmd, ' ') end -function module.popen_r(...) - return io.popen(module.argss_to_cmd(...), 'r') +function M.popen_r(...) + return io.popen(M.argss_to_cmd(...), 'r') end --- Calls fn() until it succeeds, up to `max` times or until `max_ms` @@ -69,7 +70,7 @@ end --- @param max_ms integer? --- @param fn function --- @return any -function module.retry(max, max_ms, fn) +function M.retry(max, max_ms, fn) luaassert(max == nil or max > 0) luaassert(max_ms == nil or max_ms > 0) local tries = 1 @@ -96,10 +97,10 @@ local check_logs_useless_lines = { ['See README_MISSING_SYSCALL_OR_IOCTL for guidance'] = 3, } -function module.eq(expected, actual, context) +function M.eq(expected, actual, context) return luaassert.are.same(expected, actual, context) end -function module.neq(expected, actual, context) +function M.neq(expected, actual, context) return luaassert.are_not.same(expected, actual, context) end @@ -108,7 +109,7 @@ end --- @param cond (boolean) expression to assert --- @param expected (any) description of expected result --- @param actual (any) description of actual result -function module.ok(cond, expected, actual) +function M.ok(cond, expected, actual) luaassert( (not expected and not actual) or (expected and actual), 'if "expected" is given, "actual" is also required' @@ -122,14 +123,14 @@ local function epicfail(state, arguments, _) return false end luaassert:register('assertion', 'epicfail', epicfail) -function module.fail(msg) +function M.fail(msg) return luaassert.epicfail(msg) end --- @param pat string --- @param actual string --- @return boolean -function module.matches(pat, actual) +function M.matches(pat, actual) if nil ~= string.match(actual, pat) then return true end @@ -144,14 +145,14 @@ end ---@param logfile? (string) Full path to log file (default=$NVIM_LOG_FILE) ---@param nrlines? (number) Search up to this many log lines ---@param inverse? (boolean) Assert that the pattern does NOT match. -function module.assert_log(pat, logfile, nrlines, inverse) +function M.assert_log(pat, logfile, nrlines, inverse) logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog' luaassert(logfile ~= nil, 'no logfile') nrlines = nrlines or 10 inverse = inverse or false - module.retry(nil, 1000, function() - local lines = module.read_file_list(logfile, -nrlines) or {} + M.retry(nil, 1000, function() + local lines = M.read_file_list(logfile, -nrlines) or {} local msg = string.format( 'Pattern %q %sfound in log (last %d lines): %s:\n%s', pat, @@ -181,14 +182,14 @@ end --- @param pat (string) Lua pattern to match lines in the log file --- @param logfile? (string) Full path to log file (default=$NVIM_LOG_FILE) --- @param nrlines? (number) Search up to this many log lines -function module.assert_nolog(pat, logfile, nrlines) - return module.assert_log(pat, logfile, nrlines, true) +function M.assert_nolog(pat, logfile, nrlines) + return M.assert_log(pat, logfile, nrlines, true) end --- @param fn fun(...): any --- @param ... any --- @return boolean, any -function module.pcall(fn, ...) +function M.pcall(fn, ...) luaassert(type(fn) == 'function') local status, rv = pcall(fn, ...) if status then @@ -237,9 +238,9 @@ end -- --- @param fn function --- @return string -function module.pcall_err_withfile(fn, ...) +function M.pcall_err_withfile(fn, ...) luaassert(type(fn) == 'function') - local status, rv = module.pcall(fn, ...) + local status, rv = M.pcall(fn, ...) if status == true then error('expected failure, but got success') end @@ -249,12 +250,12 @@ end --- @param fn function --- @param ... any --- @return string -function module.pcall_err_withtrace(fn, ...) - local errmsg = module.pcall_err_withfile(fn, ...) +function M.pcall_err_withtrace(fn, ...) + local errmsg = M.pcall_err_withfile(fn, ...) return ( errmsg - :gsub('^%.%.%./helpers%.lua:0: ', '') + :gsub('^%.%.%./testnvim%.lua:0: ', '') :gsub('^Error executing lua:- ', '') :gsub('^%[string "<nvim>"%]:0: ', '') ) @@ -263,20 +264,20 @@ end --- @param fn function --- @param ... any --- @return string -function module.pcall_err(fn, ...) - return module.remove_trace(module.pcall_err_withtrace(fn, ...)) +function M.pcall_err(fn, ...) + return M.remove_trace(M.pcall_err_withtrace(fn, ...)) end --- @param s string --- @return string -function module.remove_trace(s) +function M.remove_trace(s) return (s:gsub('\n%s*stack traceback:.*', '')) end -- initial_path: directory to recurse into -- re: include pattern (string) -- exc_re: exclude pattern(s) (string or table) -function module.glob(initial_path, re, exc_re) +function M.glob(initial_path, re, exc_re) exc_re = type(exc_re) == 'table' and exc_re or { exc_re } local paths_to_check = { initial_path } --- @type string[] local ret = {} --- @type string[] @@ -318,10 +319,10 @@ function module.glob(initial_path, re, exc_re) return ret end -function module.check_logs() +function M.check_logs() local log_dir = os.getenv('LOG_DIR') local runtime_errors = {} - if log_dir and module.isdir(log_dir) then + if log_dir and M.isdir(log_dir) then for tail in vim.fs.dir(log_dir) do if tail:sub(1, 30) == 'valgrind-' or tail:find('san%.') then local file = log_dir .. '/' .. tail @@ -343,7 +344,7 @@ function module.check_logs() local status, f local out = io.stdout if os.getenv('SYMBOLIZER') then - status, f = pcall(module.popen_r, os.getenv('SYMBOLIZER'), '-l', file) + status, f = pcall(M.popen_r, os.getenv('SYMBOLIZER'), '-l', file) end out:write(start_msg .. '\n') if status then @@ -368,22 +369,22 @@ function module.check_logs() ) end -function module.sysname() +function M.sysname() return uv.os_uname().sysname:lower() end --- @param s 'win'|'mac'|'freebsd'|'openbsd'|'bsd' --- @return boolean -function module.is_os(s) +function M.is_os(s) if not (s == 'win' or s == 'mac' or s == 'freebsd' or s == 'openbsd' or s == 'bsd') then error('unknown platform: ' .. tostring(s)) end return not not ( - (s == 'win' and (module.sysname():find('windows') or module.sysname():find('mingw'))) - or (s == 'mac' and module.sysname() == 'darwin') - or (s == 'freebsd' and module.sysname() == 'freebsd') - or (s == 'openbsd' and module.sysname() == 'openbsd') - or (s == 'bsd' and module.sysname():find('bsd')) + (s == 'win' and (M.sysname():find('windows') or M.sysname():find('mingw'))) + or (s == 'mac' and M.sysname() == 'darwin') + or (s == 'freebsd' and M.sysname() == 'freebsd') + or (s == 'openbsd' and M.sysname() == 'openbsd') + or (s == 'bsd' and M.sysname():find('bsd')) ) end @@ -402,7 +403,7 @@ local tmpname_id = 0 local tmpdir = tmpdir_get() --- Creates a new temporary file for use by tests. -function module.tmpname() +function M.tmpname() if tmpdir_is_local(tmpdir) then -- Cannot control os.tmpname() dir, so hack our own tmpname() impl. tmpname_id = tmpname_id + 1 @@ -413,11 +414,11 @@ function module.tmpname() end local fname = os.tmpname() - if module.is_os('win') and fname:sub(1, 2) == '\\s' then + if M.is_os('win') and fname:sub(1, 2) == '\\s' then -- In Windows tmpname() returns a filename starting with -- special sequence \s, prepend $TEMP path return tmpdir .. fname - elseif module.is_os('mac') and fname:match('^/tmp') then + elseif M.is_os('mac') and fname:match('^/tmp') then -- In OS X /tmp links to /private/tmp return '/private' .. fname end @@ -432,7 +433,7 @@ end local tests_skipped = 0 -function module.check_cores(app, force) -- luacheck: ignore +function M.check_cores(app, force) -- luacheck: ignore -- Temporary workaround: skip core check as it interferes with CI. if true then return @@ -459,14 +460,14 @@ function module.check_cores(app, force) -- luacheck: ignore exc_re = { os.getenv('NVIM_TEST_CORE_EXC_RE'), local_tmpdir } db_cmd = os.getenv('NVIM_TEST_CORE_DB_CMD') or gdb_db_cmd random_skip = os.getenv('NVIM_TEST_CORE_RANDOM_SKIP') ~= '' - elseif module.is_os('mac') then + elseif M.is_os('mac') then initial_path = '/cores' re = nil exc_re = { local_tmpdir } db_cmd = lldb_db_cmd else initial_path = '.' - if module.is_os('freebsd') then + if M.is_os('freebsd') then re = '/nvim.core$' else re = '/core[^/]*$' @@ -480,7 +481,7 @@ function module.check_cores(app, force) -- luacheck: ignore tests_skipped = tests_skipped + 1 return end - local cores = module.glob(initial_path, re, exc_re) + local cores = M.glob(initial_path, re, exc_re) local found_cores = 0 local out = io.stdout for _, core in ipairs(cores) do @@ -503,23 +504,23 @@ function module.check_cores(app, force) -- luacheck: ignore end --- @return string? -function module.repeated_read_cmd(...) +function M.repeated_read_cmd(...) for _ = 1, 10 do - local stream = module.popen_r(...) + local stream = M.popen_r(...) local ret = stream:read('*a') stream:close() if ret then return ret end end - print('ERROR: Failed to execute ' .. module.argss_to_cmd(...) .. ': nil return after 10 attempts') + print('ERROR: Failed to execute ' .. M.argss_to_cmd(...) .. ': nil return after 10 attempts') return nil end --- @generic T --- @param orig T --- @return T -function module.shallowcopy(orig) +function M.shallowcopy(orig) if type(orig) ~= 'table' then return orig end @@ -534,13 +535,13 @@ end --- @param d1 table<any,any> --- @param d2 table<any,any> --- @return table<any,any> -function module.mergedicts_copy(d1, d2) - local ret = module.shallowcopy(d1) +function M.mergedicts_copy(d1, d2) + local ret = M.shallowcopy(d1) for k, v in pairs(d2) do if d2[k] == vim.NIL then ret[k] = nil elseif type(d1[k]) == 'table' and type(v) == 'table' then - ret[k] = module.mergedicts_copy(d1[k], v) + ret[k] = M.mergedicts_copy(d1[k], v) else ret[k] = v end @@ -553,7 +554,7 @@ end --- Note: does not do copies of d2 values used. --- @param d1 table<any,any> --- @param d2 table<any,any> -function module.dictdiff(d1, d2) +function M.dictdiff(d1, d2) local ret = {} --- @type table<any,any> local hasdiff = false for k, v in pairs(d1) do @@ -562,7 +563,7 @@ function module.dictdiff(d1, d2) ret[k] = vim.NIL elseif type(v) == type(d2[k]) then if type(v) == 'table' then - local subdiff = module.dictdiff(v, d2[k]) + local subdiff = M.dictdiff(v, d2[k]) if subdiff ~= nil then hasdiff = true ret[k] = subdiff @@ -576,7 +577,7 @@ function module.dictdiff(d1, d2) hasdiff = true end end - local shallowcopy = module.shallowcopy + local shallowcopy = M.shallowcopy for k, v in pairs(d2) do if d1[k] == nil then ret[k] = shallowcopy(v) @@ -591,7 +592,7 @@ function module.dictdiff(d1, d2) end -- Concat list-like tables. -function module.concat_tables(...) +function M.concat_tables(...) local ret = {} --- @type table<any,any> for i = 1, select('#', ...) do --- @type table<any,any> @@ -608,7 +609,7 @@ end --- @param str string --- @param leave_indent? integer --- @return string -function module.dedent(str, leave_indent) +function M.dedent(str, leave_indent) -- find minimum common indent across lines local indent --- @type string? for line in str:gmatch('[^\n]+') do @@ -633,14 +634,14 @@ function module.dedent(str, leave_indent) return str end -function module.intchar2lua(ch) +function M.intchar2lua(ch) ch = tonumber(ch) return (20 <= ch and ch < 127) and ('%c'):format(ch) or ch end --- @param str string --- @return string -function module.hexdump(str) +function M.hexdump(str) local len = string.len(str) local dump = '' local hex = '' @@ -669,7 +670,7 @@ end --- @param filename string path to file --- @param start? integer start line (1-indexed), negative means "lines before end" (tail) --- @return string[]? -function module.read_file_list(filename, start) +function M.read_file_list(filename, start) local lnum = (start ~= nil and type(start) == 'number') and start or 1 local tail = (lnum < 0) local maxlines = tail and math.abs(lnum) or nil @@ -707,7 +708,7 @@ end --- Reads the entire contents of `filename` into a string. --- @param filename string --- @return string? -function module.read_file(filename) +function M.read_file(filename) local file = io.open(filename, 'r') if not file then return nil @@ -718,7 +719,7 @@ function module.read_file(filename) end -- Dedent the given text and write it to the file name. -function module.write_file(name, text, no_dedent, append) +function M.write_file(name, text, no_dedent, append) local file = assert(io.open(name, (append and 'a' or 'w'))) if type(text) == 'table' then -- Byte blob @@ -729,7 +730,7 @@ function module.write_file(name, text, no_dedent, append) text = ('%s%c'):format(text, char) end elseif not no_dedent then - text = module.dedent(text) + text = M.dedent(text) end file:write(text) file:flush() @@ -738,7 +739,7 @@ end --- @param name? 'cirrus'|'github' --- @return boolean -function module.is_ci(name) +function M.is_ci(name) local any = (name == nil) luaassert(any or name == 'github' or name == 'cirrus') local gh = ((any or name == 'github') and nil ~= os.getenv('GITHUB_ACTIONS')) @@ -748,11 +749,11 @@ end -- Gets the (tail) contents of `logfile`. -- Also moves the file to "${NVIM_LOG_FILE}.displayed" on CI environments. -function module.read_nvim_log(logfile, ci_rename) +function M.read_nvim_log(logfile, ci_rename) logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog' - local is_ci = module.is_ci() + local is_ci = M.is_ci() local keep = is_ci and 100 or 10 - local lines = module.read_file_list(logfile, -keep) or {} + local lines = M.read_file_list(logfile, -keep) or {} local log = ( ('-'):rep(78) .. '\n' @@ -771,9 +772,59 @@ end --- @param path string --- @return boolean? -function module.mkdir(path) +function M.mkdir(path) -- 493 is 0755 in decimal return (uv.fs_mkdir(path, 493)) end -return module +--- @param expected any[] +--- @param received any[] +--- @param kind string +--- @return any +function M.expect_events(expected, received, kind) + if not pcall(M.eq, expected, received) then + local msg = 'unexpected ' .. kind .. ' received.\n\n' + + msg = msg .. 'received events:\n' + for _, e in ipairs(received) do + msg = msg .. ' ' .. vim.inspect(e) .. ';\n' + end + msg = msg .. '\nexpected events:\n' + for _, e in ipairs(expected) do + msg = msg .. ' ' .. vim.inspect(e) .. ';\n' + end + M.fail(msg) + end + return received +end + +--- @param cond boolean +--- @param reason? string +--- @return boolean +function M.skip(cond, reason) + if cond then + --- @type fun(reason: string) + local pending = getfenv(2).pending + pending(reason or 'FIXME') + return true + 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 M.skip_fragile(pending_fn, cond) + if pending_fn == nil or type(pending_fn) ~= type(function() end) then + error('invalid pending_fn') + end + if cond then + pending_fn('skipped (test is fragile on this system)', function() end) + return true + elseif os.getenv('TEST_SKIP_FRAGILE') then + pending_fn('skipped (TEST_SKIP_FRAGILE)', function() end) + return true + end + return false +end + +return M diff --git a/test/unit/api/private_helpers_spec.lua b/test/unit/api/private_helpers_spec.lua index 9843bd5c9e..a31374bd70 100644 --- a/test/unit/api/private_helpers_spec.lua +++ b/test/unit/api/private_helpers_spec.lua @@ -1,24 +1,24 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) -local eval_helpers = require('test.unit.eval.helpers') -local api_helpers = require('test.unit.api.helpers') - -local cimport = helpers.cimport -local NULL = helpers.NULL -local eq = helpers.eq - -local lua2typvalt = eval_helpers.lua2typvalt -local typvalt2lua = eval_helpers.typvalt2lua -local typvalt = eval_helpers.typvalt - -local nil_value = api_helpers.nil_value -local list_type = api_helpers.list_type -local int_type = api_helpers.int_type -local type_key = api_helpers.type_key -local obj2lua = api_helpers.obj2lua -local func_type = api_helpers.func_type - -local api = cimport('./src/nvim/api/private/helpers.h', './src/nvim/api/private/converter.h') +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) +local t_eval = require('test.unit.eval.testutil') +local api_t = require('test.unit.api.testutil') + +local cimport = t.cimport +local NULL = t.NULL +local eq = t.eq + +local lua2typvalt = t_eval.lua2typvalt +local typvalt2lua = t_eval.typvalt2lua +local typvalt = t_eval.typvalt + +local nil_value = api_t.nil_value +local list_type = api_t.list_type +local int_type = api_t.int_type +local type_key = api_t.type_key +local obj2lua = api_t.obj2lua +local func_type = api_t.func_type + +local api = cimport('./src/nvim/api/private/t.h', './src/nvim/api/private/converter.h') describe('vim_to_object', function() local vim_to_object = function(l) diff --git a/test/unit/api/helpers.lua b/test/unit/api/testutil.lua index 23c5db43f7..0946ef194c 100644 --- a/test/unit/api/helpers.lua +++ b/test/unit/api/testutil.lua @@ -1,23 +1,20 @@ -local helpers = require('test.unit.helpers')(nil) -local eval_helpers = require('test.unit.eval.helpers') +local t = require('test.unit.testutil') +local t_eval = require('test.unit.eval.testutil') -local cimport = helpers.cimport -local to_cstr = helpers.to_cstr -local ffi = helpers.ffi +local cimport = t.cimport +local to_cstr = t.to_cstr +local ffi = t.ffi -local list_type = eval_helpers.list_type -local dict_type = eval_helpers.dict_type -local func_type = eval_helpers.func_type -local nil_value = eval_helpers.nil_value -local int_type = eval_helpers.int_type -local flt_type = eval_helpers.flt_type -local type_key = eval_helpers.type_key +local list_type = t_eval.list_type +local dict_type = t_eval.dict_type +local func_type = t_eval.func_type +local nil_value = t_eval.nil_value +local int_type = t_eval.int_type +local flt_type = t_eval.flt_type +local type_key = t_eval.type_key -local api = cimport( - './src/nvim/api/private/defs.h', - './src/nvim/api/private/helpers.h', - './src/nvim/memory.h' -) +local api = + cimport('./src/nvim/api/private/defs.h', './src/nvim/api/private/t.h', './src/nvim/memory.h') local obj2lua diff --git a/test/unit/buffer_spec.lua b/test/unit/buffer_spec.lua index 1ef3e97165..a7c672322b 100644 --- a/test/unit/buffer_spec.lua +++ b/test/unit/buffer_spec.lua @@ -1,11 +1,11 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local to_cstr = helpers.to_cstr -local eq = helpers.eq -local NULL = helpers.NULL +local to_cstr = t.to_cstr +local eq = t.eq +local NULL = t.NULL -local buffer = helpers.cimport('./src/nvim/buffer.h') +local buffer = t.cimport('./src/nvim/buffer.h') describe('buffer functions', function() local buflist_new = function(file, flags) diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index ad87d026e5..a70ee77716 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -1,11 +1,11 @@ -local helpers = require('test.unit.helpers')(after_each) +local t = require('test.unit.testutil') local bit = require('bit') -local itp = helpers.gen_itp(it) +local itp = t.gen_itp(it) -local child_call_once = helpers.child_call_once -local cimport = helpers.cimport -local ffi = helpers.ffi +local child_call_once = t.child_call_once +local cimport = t.cimport +local ffi = t.ffi local lib = cimport('./src/nvim/charset.h') diff --git a/test/unit/eval/decode_spec.lua b/test/unit/eval/decode_spec.lua index 5a8374a2a6..7e037500b9 100644 --- a/test/unit/eval/decode_spec.lua +++ b/test/unit/eval/decode_spec.lua @@ -1,10 +1,10 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local cimport = helpers.cimport -local eq = helpers.eq -local neq = helpers.neq -local ffi = helpers.ffi +local cimport = t.cimport +local eq = t.eq +local neq = t.neq +local ffi = t.ffi local decode = cimport( './src/nvim/eval/decode.h', diff --git a/test/unit/eval/encode_spec.lua b/test/unit/eval/encode_spec.lua index 498346d7cc..5b9188163e 100644 --- a/test/unit/eval/encode_spec.lua +++ b/test/unit/eval/encode_spec.lua @@ -1,16 +1,16 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) -local eval_helpers = require('test.unit.eval.helpers') +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) +local t_eval = require('test.unit.eval.testutil') -local cimport = helpers.cimport -local to_cstr = helpers.to_cstr -local eq = helpers.eq +local cimport = t.cimport +local to_cstr = t.to_cstr +local eq = t.eq -local list = eval_helpers.list -local lst2tbl = eval_helpers.lst2tbl -local type_key = eval_helpers.type_key -local list_type = eval_helpers.list_type -local null_string = eval_helpers.null_string +local list = t_eval.list +local lst2tbl = t_eval.lst2tbl +local type_key = t_eval.type_key +local list_type = t_eval.list_type +local null_string = t_eval.null_string local encode = cimport('./src/nvim/eval/encode.h') diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/testutil.lua index 6402e1f8c9..78788e5837 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/testutil.lua @@ -1,10 +1,10 @@ -local helpers = require('test.unit.helpers')(nil) +local t = require('test.unit.testutil') -local ptr2key = helpers.ptr2key -local cimport = helpers.cimport -local to_cstr = helpers.to_cstr -local ffi = helpers.ffi -local eq = helpers.eq +local ptr2key = t.ptr2key +local cimport = t.cimport +local to_cstr = t.to_cstr +local ffi = t.ffi +local eq = t.eq local eval = cimport( './src/nvim/eval.h', @@ -142,56 +142,56 @@ local function typvalt2lua_tab_init() return end typvalt2lua_tab = { - [tonumber(eval.VAR_BOOL)] = function(t) + [tonumber(eval.VAR_BOOL)] = function(q) return ({ [tonumber(eval.kBoolVarFalse)] = false, [tonumber(eval.kBoolVarTrue)] = true, - })[tonumber(t.vval.v_bool)] + })[tonumber(q.vval.v_bool)] end, - [tonumber(eval.VAR_SPECIAL)] = function(t) + [tonumber(eval.VAR_SPECIAL)] = function(q) return ({ [tonumber(eval.kSpecialVarNull)] = nil_value, - })[tonumber(t.vval.v_special)] + })[tonumber(q.vval.v_special)] end, - [tonumber(eval.VAR_NUMBER)] = function(t) - return { [type_key] = int_type, value = tonumber(t.vval.v_number) } + [tonumber(eval.VAR_NUMBER)] = function(q) + return { [type_key] = int_type, value = tonumber(q.vval.v_number) } end, - [tonumber(eval.VAR_FLOAT)] = function(t) - return tonumber(t.vval.v_float) + [tonumber(eval.VAR_FLOAT)] = function(q) + return tonumber(q.vval.v_float) end, - [tonumber(eval.VAR_STRING)] = function(t) - local str = t.vval.v_string + [tonumber(eval.VAR_STRING)] = function(q) + local str = q.vval.v_string if str == nil then return null_string else return ffi.string(str) end end, - [tonumber(eval.VAR_LIST)] = function(t, processed) - return lst2tbl(t.vval.v_list, processed) + [tonumber(eval.VAR_LIST)] = function(q, processed) + return lst2tbl(q.vval.v_list, processed) end, - [tonumber(eval.VAR_DICT)] = function(t, processed) - return dct2tbl(t.vval.v_dict, processed) + [tonumber(eval.VAR_DICT)] = function(q, processed) + return dct2tbl(q.vval.v_dict, processed) end, - [tonumber(eval.VAR_FUNC)] = function(t, processed) - return { [type_key] = func_type, value = typvalt2lua_tab[eval.VAR_STRING](t, processed or {}) } + [tonumber(eval.VAR_FUNC)] = function(q, processed) + return { [type_key] = func_type, value = typvalt2lua_tab[eval.VAR_STRING](q, processed or {}) } end, - [tonumber(eval.VAR_PARTIAL)] = function(t, processed) - local p_key = ptr2key(t) + [tonumber(eval.VAR_PARTIAL)] = function(q, processed) + local p_key = ptr2key(q) if processed[p_key] then return processed[p_key] end - return partial2lua(t.vval.v_partial, processed) + return partial2lua(q.vval.v_partial, processed) end, } end -typvalt2lua = function(t, processed) +typvalt2lua = function(q, processed) typvalt2lua_tab_init() return ( - (typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner) + (typvalt2lua_tab[tonumber(q.v_type)] or function(t_inner) assert(false, 'Converting ' .. tonumber(t_inner.v_type) .. ' was not implemented yet') - end)(t, processed or {}) + end)(q, processed or {}) ) end @@ -419,7 +419,7 @@ local function alloc_len(len, get_ptr) end end -local alloc_logging_helpers = { +local alloc_logging_t = { list = function(l) return { func = 'calloc', args = { 1, ffi.sizeof('list_T') }, ret = void(l) } end, @@ -523,7 +523,7 @@ local function tbl2callback(tbl) else assert(false) end - return ffi.gc(ffi.cast('Callback*', ret), helpers.callback_free) + return ffi.gc(ffi.cast('Callback*', ret), t.callback_free) end local function dict_watchers(d) @@ -591,7 +591,7 @@ return { list_iter = list_iter, first_di = first_di, - alloc_logging_helpers = alloc_logging_helpers, + alloc_logging_t = alloc_logging_t, list_items = list_items, dict_items = dict_items, diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua index ed26fd44e4..20ecf0d920 100644 --- a/test/unit/eval/tricks_spec.lua +++ b/test/unit/eval/tricks_spec.lua @@ -1,12 +1,12 @@ -local helpers = require('test.unit.helpers')(after_each) -local eval_helpers = require('test.unit.eval.helpers') +local t = require('test.unit.testutil') +local t_eval = require('test.unit.eval.testutil') -local itp = helpers.gen_itp(it) +local itp = t.gen_itp(it) -local cimport = helpers.cimport -local eq = helpers.eq +local cimport = t.cimport +local eq = t.eq -local eval0 = eval_helpers.eval0 +local eval0 = t_eval.eval0 local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', './src/nvim/memory.h') diff --git a/test/unit/eval/tv_clear_spec.lua b/test/unit/eval/tv_clear_spec.lua index a34cf6082a..1ad537d8bf 100644 --- a/test/unit/eval/tv_clear_spec.lua +++ b/test/unit/eval/tv_clear_spec.lua @@ -1,18 +1,18 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) -local eval_helpers = require('test.unit.eval.helpers') +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) +local t_eval = require('test.unit.eval.testutil') -local alloc_log_new = helpers.alloc_log_new -local cimport = helpers.cimport -local ffi = helpers.ffi -local eq = helpers.eq +local alloc_log_new = t.alloc_log_new +local cimport = t.cimport +local ffi = t.ffi +local eq = t.eq -local a = eval_helpers.alloc_logging_helpers -local type_key = eval_helpers.type_key -local list_type = eval_helpers.list_type -local list_items = eval_helpers.list_items -local dict_items = eval_helpers.dict_items -local lua2typvalt = eval_helpers.lua2typvalt +local a = t_eval.alloc_logging_t +local type_key = t_eval.type_key +local list_type = t_eval.list_type +local list_items = t_eval.list_items +local dict_items = t_eval.dict_items +local lua2typvalt = t_eval.lua2typvalt local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/eval.h') diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 6edd164438..c69c9b0fae 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1,45 +1,45 @@ local bit = require('bit') -local helpers = require('test.unit.helpers')(after_each) -local eval_helpers = require('test.unit.eval.helpers') - -local itp = helpers.gen_itp(it) - -local OK = helpers.OK -local eq = helpers.eq -local neq = helpers.neq -local ffi = helpers.ffi -local FAIL = helpers.FAIL -local NULL = helpers.NULL -local cimport = helpers.cimport -local to_cstr = helpers.to_cstr -local alloc_log_new = helpers.alloc_log_new -local concat_tables = helpers.concat_tables +local t = require('test.unit.testutil') +local t_eval = require('test.unit.eval.testutil') + +local itp = t.gen_itp(it) + +local OK = t.OK +local eq = t.eq +local neq = t.neq +local ffi = t.ffi +local FAIL = t.FAIL +local NULL = t.NULL +local cimport = t.cimport +local to_cstr = t.to_cstr +local alloc_log_new = t.alloc_log_new +local concat_tables = t.concat_tables local map = vim.tbl_map -local a = eval_helpers.alloc_logging_helpers -local int = eval_helpers.int -local list = eval_helpers.list -local dict = eval_helpers.dict -local eval0 = eval_helpers.eval0 -local lst2tbl = eval_helpers.lst2tbl -local dct2tbl = eval_helpers.dct2tbl -local typvalt = eval_helpers.typvalt -local type_key = eval_helpers.type_key -local li_alloc = eval_helpers.li_alloc -local first_di = eval_helpers.first_di -local nil_value = eval_helpers.nil_value -local func_type = eval_helpers.func_type -local null_list = eval_helpers.null_list -local null_dict = eval_helpers.null_dict -local dict_items = eval_helpers.dict_items -local list_items = eval_helpers.list_items -local empty_list = eval_helpers.empty_list -local lua2typvalt = eval_helpers.lua2typvalt -local typvalt2lua = eval_helpers.typvalt2lua -local null_string = eval_helpers.null_string -local callback2tbl = eval_helpers.callback2tbl -local tbl2callback = eval_helpers.tbl2callback -local dict_watchers = eval_helpers.dict_watchers +local a = t_eval.alloc_logging_t +local int = t_eval.int +local list = t_eval.list +local dict = t_eval.dict +local eval0 = t_eval.eval0 +local lst2tbl = t_eval.lst2tbl +local dct2tbl = t_eval.dct2tbl +local typvalt = t_eval.typvalt +local type_key = t_eval.type_key +local li_alloc = t_eval.li_alloc +local first_di = t_eval.first_di +local nil_value = t_eval.nil_value +local func_type = t_eval.func_type +local null_list = t_eval.null_list +local null_dict = t_eval.null_dict +local dict_items = t_eval.dict_items +local list_items = t_eval.list_items +local empty_list = t_eval.empty_list +local lua2typvalt = t_eval.lua2typvalt +local typvalt2lua = t_eval.typvalt2lua +local null_string = t_eval.null_string +local callback2tbl = t_eval.callback2tbl +local tbl2callback = t_eval.tbl2callback +local dict_watchers = t_eval.dict_watchers local lib = cimport( './src/nvim/eval/typval.h', @@ -3223,7 +3223,7 @@ describe('typval.c', function() end) end) describe('lnum()', function() - itp('works', function() + pending('works (skip due to flakiness)', function() for _, v in ipairs({ { lib.VAR_NUMBER, { v_number = 42 }, nil, 42 }, { lib.VAR_STRING, { v_string = to_cstr('100500') }, nil, 100500 }, @@ -3352,7 +3352,7 @@ describe('typval.c', function() end end describe('string()', function() - itp('works', function() + pending('works (skip due to flakiness)', function() local buf = lib.tv_get_string(lua2typvalt(int(1))) local buf_chk = lib.tv_get_string_chk(lua2typvalt(int(1))) neq(buf, buf_chk) diff --git a/test/unit/fileio_spec.lua b/test/unit/fileio_spec.lua index 1284f84222..8f915372fe 100644 --- a/test/unit/fileio_spec.lua +++ b/test/unit/fileio_spec.lua @@ -1,13 +1,13 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) ---{:cimport, :internalize, :eq, :neq, :ffi, :lib, :cstr, :to_cstr} = require 'test.unit.helpers' +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) +--{:cimport, :internalize, :eq, :neq, :ffi, :lib, :cstr, :to_cstr} = require 'test.unit.testutil' -local eq = helpers.eq -local ffi = helpers.ffi -local to_cstr = helpers.to_cstr -local NULL = helpers.NULL +local eq = t.eq +local ffi = t.ffi +local to_cstr = t.to_cstr +local NULL = t.NULL -local fileio = helpers.cimport('./src/nvim/fileio.h') +local fileio = t.cimport('./src/nvim/fileio.h') describe('file_pat functions', function() describe('file_pat_to_reg_pat', function() diff --git a/test/unit/garray_spec.lua b/test/unit/garray_spec.lua index 0f947c42b8..30d1d3f21e 100644 --- a/test/unit/garray_spec.lua +++ b/test/unit/garray_spec.lua @@ -1,13 +1,13 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) - -local cimport = helpers.cimport -local internalize = helpers.internalize -local eq = helpers.eq -local neq = helpers.neq -local ffi = helpers.ffi -local to_cstr = helpers.to_cstr -local NULL = helpers.NULL +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) + +local cimport = t.cimport +local internalize = t.internalize +local eq = t.eq +local neq = t.neq +local ffi = t.ffi +local to_cstr = t.to_cstr +local NULL = t.NULL local garray = cimport('./src/nvim/garray.h') diff --git a/test/unit/indent_spec.lua b/test/unit/indent_spec.lua index 7902918c54..a1c3dc4a75 100644 --- a/test/unit/indent_spec.lua +++ b/test/unit/indent_spec.lua @@ -1,12 +1,12 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local to_cstr = helpers.to_cstr -local ffi = helpers.ffi -local eq = helpers.eq +local to_cstr = t.to_cstr +local ffi = t.ffi +local eq = t.eq -local indent = helpers.cimport('./src/nvim/indent.h') -local globals = helpers.cimport('./src/nvim/globals.h') +local indent = t.cimport('./src/nvim/indent.h') +local globals = t.cimport('./src/nvim/globals.h') describe('get_sts_value', function() itp([[returns 'softtabstop' when it is non-negative]], function() diff --git a/test/unit/keycodes_spec.lua b/test/unit/keycodes_spec.lua index 4a81c62ac1..948f0a3ead 100644 --- a/test/unit/keycodes_spec.lua +++ b/test/unit/keycodes_spec.lua @@ -1,12 +1,12 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local ffi = helpers.ffi -local eq = helpers.eq -local neq = helpers.neq +local ffi = t.ffi +local eq = t.eq +local neq = t.neq -local keycodes = helpers.cimport('./src/nvim/keycodes.h') -local NULL = helpers.NULL +local keycodes = t.cimport('./src/nvim/keycodes.h') +local NULL = t.NULL describe('keycodes.c', function() describe('find_special_key()', function() diff --git a/test/unit/marktree_spec.lua b/test/unit/marktree_spec.lua index b0a861727d..cccce5ca8f 100644 --- a/test/unit/marktree_spec.lua +++ b/test/unit/marktree_spec.lua @@ -1,15 +1,15 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local ffi = helpers.ffi -local eq = helpers.eq -local ok = helpers.ok +local ffi = t.ffi +local eq = t.eq +local ok = t.ok -local lib = helpers.cimport('./src/nvim/marktree.h') +local lib = t.cimport('./src/nvim/marktree.h') -local function tablelength(t) +local function tablelength(tbl) local count = 0 - for _ in pairs(t) do + for _ in pairs(tbl) do count = count + 1 end return count @@ -460,7 +460,7 @@ describe('marktree', function() local ids = {} -- too much overhead on ASAN - local size_factor = helpers.is_asan() and 3 or 10 + local size_factor = t.is_asan() and 3 or 10 local at_row = {} for i = 1, 10 do @@ -528,7 +528,7 @@ describe('marktree', function() local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit -- too much overhead on ASAN - local size_factor = helpers.is_asan() and 3 or 10 + local size_factor = t.is_asan() and 3 or 10 local at_row = {} for i = 1, 10 do diff --git a/test/unit/mbyte_spec.lua b/test/unit/mbyte_spec.lua index 00a8c06ceb..8fcc67d20b 100644 --- a/test/unit/mbyte_spec.lua +++ b/test/unit/mbyte_spec.lua @@ -1,10 +1,10 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local ffi = helpers.ffi -local eq = helpers.eq +local ffi = t.ffi +local eq = t.eq -local lib = helpers.cimport('./src/nvim/mbyte.h', './src/nvim/charset.h', './src/nvim/grid.h') +local lib = t.cimport('./src/nvim/mbyte.h', './src/nvim/charset.h', './src/nvim/grid.h') describe('mbyte', function() -- Convert from bytes to string @@ -205,7 +205,7 @@ describe('mbyte', function() end) describe('utf_cp_bounds_len', function() - local to_cstr = helpers.to_cstr + local to_cstr = t.to_cstr local tests = { { diff --git a/test/unit/memory_spec.lua b/test/unit/memory_spec.lua index 8be55fdbf3..169d6b9cb0 100644 --- a/test/unit/memory_spec.lua +++ b/test/unit/memory_spec.lua @@ -1,11 +1,11 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local cimport = helpers.cimport -local cstr = helpers.cstr -local eq = helpers.eq -local ffi = helpers.ffi -local to_cstr = helpers.to_cstr +local cimport = t.cimport +local cstr = t.cstr +local eq = t.eq +local ffi = t.ffi +local to_cstr = t.to_cstr local cimp = cimport('stdlib.h', './src/nvim/memory.h') diff --git a/test/unit/message_spec.lua b/test/unit/message_spec.lua index 71aa74d90d..fee8f5124d 100644 --- a/test/unit/message_spec.lua +++ b/test/unit/message_spec.lua @@ -1,11 +1,11 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local ffi = helpers.ffi -local eq = helpers.eq -local to_cstr = helpers.to_cstr +local ffi = t.ffi +local eq = t.eq +local to_cstr = t.to_cstr -local cimp = helpers.cimport('./src/nvim/message.h', './src/nvim/memory.h', './src/nvim/strings.h') +local cimp = t.cimport('./src/nvim/message.h', './src/nvim/memory.h', './src/nvim/strings.h') describe('trunc_string', function() local buflen = 40 @@ -33,26 +33,26 @@ describe('trunc_string', function() { ['desc'] = 'by copy', ['func'] = test_copy }, } - for _, t in ipairs(permutations) do - describe('populates buf ' .. t.desc, function() + for _, q in ipairs(permutations) do + describe('populates buf ' .. q.desc, function() itp('with a small string', function() - t.func('text', 'text') + q.func('text', 'text') end) itp('with a medium string', function() - t.func('a short text', 'a short text') + q.func('a short text', 'a short text') end) itp('with a string of length == 1/2 room', function() - t.func('a text that fits', 'a text that fits', 34) + q.func('a text that fits', 'a text that fits', 34) end) itp('with a string exactly the truncate size', function() - t.func('a text tha just fits', 'a text tha just fits') + q.func('a text tha just fits', 'a text tha just fits') end) itp('with a string that must be truncated', function() - t.func('a text that nott fits', 'a text t...nott fits') + q.func('a text that nott fits', 'a text t...nott fits') end) end) end diff --git a/test/unit/msgpack_spec.lua b/test/unit/msgpack_spec.lua index bd663a3c75..730c4f9e36 100644 --- a/test/unit/msgpack_spec.lua +++ b/test/unit/msgpack_spec.lua @@ -1,10 +1,10 @@ -local helpers = require('test.unit.helpers')(after_each) -local cimport = helpers.cimport -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local cimport = t.cimport +local itp = t.gen_itp(it) local lib = cimport('./src/nvim/msgpack_rpc/unpacker.h', './src/nvim/memory.h') -local ffi = helpers.ffi -local eq = helpers.eq -local to_cstr = helpers.to_cstr +local ffi = t.ffi +local eq = t.eq +local to_cstr = t.to_cstr --- @class Unpacker --- @field read_ptr ffi.cdata* @@ -51,11 +51,11 @@ describe('msgpack', function() unpacker_goto(unpacker, payload, payload:len() - 1) local finished = unpacker_advance(unpacker) - eq(finished, false) + eq(false, finished) unpacker[0].read_size = unpacker[0].read_size + 1 finished = unpacker_advance(unpacker) - eq(finished, true) + eq(true, finished) end ) @@ -73,7 +73,7 @@ describe('msgpack', function() '\x93\x02\xa6\x72\x65\x64\x72\x61\x77\x91\x92\xa9\x67\x72\x69\x64\x5f\x6c\x69\x6e\x65\x95\x02\x00\x00\x90\xc2' ) local finished = unpacker_advance(unpacker) - eq(finished, true) + eq(true, finished) end) end) end) diff --git a/test/unit/multiqueue_spec.lua b/test/unit/multiqueue_spec.lua index f6d11ebed0..931a5efa94 100644 --- a/test/unit/multiqueue_spec.lua +++ b/test/unit/multiqueue_spec.lua @@ -1,10 +1,10 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local child_call_once = helpers.child_call_once -local cimport = helpers.cimport -local ffi = helpers.ffi -local eq = helpers.eq +local child_call_once = t.child_call_once +local cimport = t.cimport +local ffi = t.ffi +local eq = t.eq local multiqueue = cimport('./test/unit/fixtures/multiqueue.h') diff --git a/test/unit/optionstr_spec.lua b/test/unit/optionstr_spec.lua index 7666db910e..b9c9ceaa85 100644 --- a/test/unit/optionstr_spec.lua +++ b/test/unit/optionstr_spec.lua @@ -1,10 +1,10 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local to_cstr = helpers.to_cstr -local eq = helpers.eq +local to_cstr = t.to_cstr +local eq = t.eq -local optionstr = helpers.cimport('./src/nvim/optionstr.h') +local optionstr = t.cimport('./src/nvim/optionstr.h') local check_ff_value = function(ff) return optionstr.check_ff_value(to_cstr(ff)) diff --git a/test/unit/os/env_spec.lua b/test/unit/os/env_spec.lua index 2c638fcb37..d8231545f3 100644 --- a/test/unit/os/env_spec.lua +++ b/test/unit/os/env_spec.lua @@ -1,13 +1,13 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) - -local cimport = helpers.cimport -local eq = helpers.eq -local neq = helpers.neq -local ffi = helpers.ffi -local cstr = helpers.cstr -local to_cstr = helpers.to_cstr -local NULL = helpers.NULL +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) + +local cimport = t.cimport +local eq = t.eq +local neq = t.neq +local ffi = t.ffi +local cstr = t.cstr +local to_cstr = t.to_cstr +local NULL = t.NULL local OK = 0 local cimp = cimport('./src/nvim/os/os.h') @@ -306,9 +306,9 @@ describe('env.c', function() -- expand_env_esc SHOULD NOT expand the variable if there is not enough space to -- contain the result for i = 0, 3 do - eq(output[i], input[i]) + eq(input[i], output[i]) end - eq(output[4], 0) + eq(0, output[4]) end) end) end) diff --git a/test/unit/os/fileio_spec.lua b/test/unit/os/fileio_spec.lua index 617141fd3a..fd9eb2bafb 100644 --- a/test/unit/os/fileio_spec.lua +++ b/test/unit/os/fileio_spec.lua @@ -1,13 +1,13 @@ local uv = vim.uv -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) - -local eq = helpers.eq -local ffi = helpers.ffi -local cimport = helpers.cimport -local cppimport = helpers.cppimport -local mkdir = helpers.mkdir +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) + +local eq = t.eq +local ffi = t.ffi +local cimport = t.cimport +local cppimport = t.cppimport +local mkdir = t.mkdir local m = cimport('./src/nvim/os/os.h', './src/nvim/os/fileio.h') cppimport('fcntl.h') diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index c15cd12fef..a829962305 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -1,22 +1,22 @@ local uv = vim.uv local bit = require('bit') -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) - -local cimport = helpers.cimport -local cppimport = helpers.cppimport -local internalize = helpers.internalize -local ok = helpers.ok -local eq = helpers.eq -local neq = helpers.neq -local ffi = helpers.ffi -local cstr = helpers.cstr -local to_cstr = helpers.to_cstr -local OK = helpers.OK -local FAIL = helpers.FAIL -local NULL = helpers.NULL -local mkdir = helpers.mkdir +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) + +local cimport = t.cimport +local cppimport = t.cppimport +local internalize = t.internalize +local ok = t.ok +local eq = t.eq +local neq = t.neq +local ffi = t.ffi +local cstr = t.cstr +local to_cstr = t.to_cstr +local OK = t.OK +local FAIL = t.FAIL +local NULL = t.NULL +local mkdir = t.mkdir local endswith = vim.endswith local NODE_NORMAL = 0 diff --git a/test/unit/os/shell_spec.lua b/test/unit/os/shell_spec.lua index ae162f2317..94ad6f4a10 100644 --- a/test/unit/os/shell_spec.lua +++ b/test/unit/os/shell_spec.lua @@ -1,14 +1,14 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) -local cimported = helpers.cimport( +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) +local cimported = t.cimport( './src/nvim/os/shell.h', './src/nvim/option_vars.h', './src/nvim/main.h', './src/nvim/memory.h' ) -local ffi, eq = helpers.ffi, helpers.eq -local intern = helpers.internalize -local to_cstr = helpers.to_cstr +local ffi, eq = t.ffi, t.eq +local intern = t.internalize +local to_cstr = t.to_cstr local NULL = ffi.cast('void *', 0) describe('shell functions', function() @@ -125,9 +125,9 @@ describe('shell functions', function() cimported.p_sxe = to_cstr('"&|<>()@^') local argv = ffi.cast('char**', cimported.shell_build_argv(to_cstr('echo &|<>()@^'), nil)) - eq(ffi.string(argv[0]), '/bin/sh') - eq(ffi.string(argv[1]), '-c') - eq(ffi.string(argv[2]), '(echo ^&^|^<^>^(^)^@^^)') + eq('/bin/sh', ffi.string(argv[0])) + eq('-c', ffi.string(argv[1])) + eq('(echo ^&^|^<^>^(^)^@^^)', ffi.string(argv[2])) eq(nil, argv[3]) end) @@ -136,9 +136,9 @@ describe('shell functions', function() cimported.p_sxe = to_cstr('"&|<>()@^') local argv = ffi.cast('char**', cimported.shell_build_argv(to_cstr('echo -n some text'), nil)) - eq(ffi.string(argv[0]), '/bin/sh') - eq(ffi.string(argv[1]), '-c') - eq(ffi.string(argv[2]), '"(echo -n some text)"') + eq('/bin/sh', ffi.string(argv[0])) + eq('-c', ffi.string(argv[1])) + eq('"(echo -n some text)"', ffi.string(argv[2])) eq(nil, argv[3]) end) @@ -147,17 +147,17 @@ describe('shell functions', function() cimported.p_sxe = to_cstr('') local argv = ffi.cast('char**', cimported.shell_build_argv(to_cstr('echo -n some text'), nil)) - eq(ffi.string(argv[0]), '/bin/sh') - eq(ffi.string(argv[1]), '-c') - eq(ffi.string(argv[2]), '"echo -n some text"') + eq('/bin/sh', ffi.string(argv[0])) + eq('-c', ffi.string(argv[1])) + eq('"echo -n some text"', ffi.string(argv[2])) eq(nil, argv[3]) end) itp('with empty shellxquote/shellxescape', function() local argv = ffi.cast('char**', cimported.shell_build_argv(to_cstr('echo -n some text'), nil)) - eq(ffi.string(argv[0]), '/bin/sh') - eq(ffi.string(argv[1]), '-c') - eq(ffi.string(argv[2]), 'echo -n some text') + eq('/bin/sh', ffi.string(argv[0])) + eq('-c', ffi.string(argv[1])) + eq('echo -n some text', ffi.string(argv[2])) eq(nil, argv[3]) end) end) diff --git a/test/unit/os/users_spec.lua b/test/unit/os/users_spec.lua index 679e76fae1..21d47fdb6e 100644 --- a/test/unit/os/users_spec.lua +++ b/test/unit/os/users_spec.lua @@ -1,13 +1,13 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local cimport = helpers.cimport -local eq = helpers.eq -local ffi = helpers.ffi -local lib = helpers.lib -local NULL = helpers.NULL -local OK = helpers.OK -local FAIL = helpers.FAIL +local cimport = t.cimport +local eq = t.eq +local ffi = t.ffi +local lib = t.lib +local NULL = t.NULL +local OK = t.OK +local FAIL = t.FAIL local users = cimport('./src/nvim/os/os.h', 'unistd.h') diff --git a/test/unit/path_spec.lua b/test/unit/path_spec.lua index c564ec119e..6f6a80f44e 100644 --- a/test/unit/path_spec.lua +++ b/test/unit/path_spec.lua @@ -1,17 +1,17 @@ local uv = vim.uv -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) - -local cimport = helpers.cimport -local eq = helpers.eq -local neq = helpers.neq -local ffi = helpers.ffi -local cstr = helpers.cstr -local to_cstr = helpers.to_cstr -local NULL = helpers.NULL -local OK = helpers.OK -local FAIL = helpers.FAIL -local mkdir = helpers.mkdir +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) + +local cimport = t.cimport +local eq = t.eq +local neq = t.neq +local ffi = t.ffi +local cstr = t.cstr +local to_cstr = t.to_cstr +local NULL = t.NULL +local OK = t.OK +local FAIL = t.FAIL +local mkdir = t.mkdir cimport('string.h') local cimp = cimport('./src/nvim/os/os.h', './src/nvim/path.h') @@ -22,11 +22,16 @@ local buffer = nil describe('path.c', function() describe('path_full_dir_name', function() + local old_dir + setup(function() + old_dir = uv.cwd() mkdir('unit-test-directory') + uv.fs_symlink(old_dir .. '/unit-test-directory', 'unit-test-symlink') end) teardown(function() + uv.fs_unlink('unit-test-symlink') uv.fs_rmdir('unit-test-directory') end) @@ -37,35 +42,64 @@ describe('path.c', function() before_each(function() -- Create empty string buffer which will contain the resulting path. - length = string.len(uv.cwd()) + 22 + length = string.len(old_dir) + 22 buffer = cstr(length, '') end) + after_each(function() + uv.chdir(old_dir) + end) + itp('returns the absolute directory name of a given relative one', function() - local result = path_full_dir_name('..', buffer, length) - eq(OK, result) - local old_dir = uv.cwd() + eq(OK, path_full_dir_name('..', buffer, length)) uv.chdir('..') local expected = uv.cwd() uv.chdir(old_dir) - eq(expected, (ffi.string(buffer))) + eq(expected, ffi.string(buffer)) end) itp('returns the current directory name if the given string is empty', function() - eq(OK, (path_full_dir_name('', buffer, length))) - eq(uv.cwd(), (ffi.string(buffer))) + eq(OK, path_full_dir_name('', buffer, length)) + eq(old_dir, ffi.string(buffer)) + end) + + local function test_full_dir_absolute() + itp('works with a normal absolute dir', function() + eq(OK, path_full_dir_name(old_dir .. '/unit-test-directory', buffer, length)) + eq(old_dir .. '/unit-test-directory', ffi.string(buffer)) + end) + + itp('works with a symlinked absolute dir', function() + eq(OK, path_full_dir_name(old_dir .. '/unit-test-symlink', buffer, length)) + eq(old_dir .. '/unit-test-directory', ffi.string(buffer)) + end) + end + + test_full_dir_absolute() + + describe('when cwd does not exist #28786', function() + before_each(function() + mkdir('dir-to-remove') + uv.chdir('dir-to-remove') + uv.fs_rmdir(old_dir .. '/dir-to-remove') + end) + + test_full_dir_absolute() end) itp('works with a normal relative dir', function() - local result = path_full_dir_name('unit-test-directory', buffer, length) - eq(uv.cwd() .. '/unit-test-directory', (ffi.string(buffer))) - eq(OK, result) + eq(OK, path_full_dir_name('unit-test-directory', buffer, length)) + eq(old_dir .. '/unit-test-directory', ffi.string(buffer)) + end) + + itp('works with a symlinked relative dir', function() + eq(OK, path_full_dir_name('unit-test-symlink', buffer, length)) + eq(old_dir .. '/unit-test-directory', ffi.string(buffer)) end) itp('works with a non-existing relative dir', function() - local result = path_full_dir_name('does-not-exist', buffer, length) - eq(uv.cwd() .. '/does-not-exist', (ffi.string(buffer))) - eq(OK, result) + eq(OK, path_full_dir_name('does-not-exist', buffer, length)) + eq(old_dir .. '/does-not-exist', ffi.string(buffer)) end) itp('fails with a non-existing absolute dir', function() @@ -380,8 +414,8 @@ describe('path.c', function() return buf, result end - local function get_buf_len(s, t) - return math.max(string.len(s), string.len(t)) + 1 + local function get_buf_len(s, q) + return math.max(string.len(s), string.len(q)) + 1 end itp('fails if given filename is NULL', function() @@ -413,12 +447,15 @@ describe('path.c', function() end) itp('fails and uses filename if given filename contains non-existing directory', function() - local filename = 'non_existing_dir/test.file' - local buflen = string.len(filename) + 1 - local do_expand = 1 - local buf, result = vim_FullName(filename, buflen, do_expand) - eq(filename, ffi.string(buf)) - eq(FAIL, result) + -- test with different filename lengths + for rep = 1, 10 do + local filename = ('non_existing_'):rep(rep) .. 'dir/test.file' + local buflen = string.len(filename) + 1 + local do_expand = 1 + local buf, result = vim_FullName(filename, buflen, do_expand) + eq(filename, ffi.string(buf)) + eq(FAIL, result) + end end) itp('concatenates filename if it does not contain a slash', function() diff --git a/test/unit/preload.lua b/test/unit/preload.lua index c2d051d98a..1865bba719 100644 --- a/test/unit/preload.lua +++ b/test/unit/preload.lua @@ -2,5 +2,5 @@ -- Busted started doing this to help provide more isolation. See issue #62 -- for more information about this. local ffi = require('ffi') -local helpers = require('test.unit.helpers')(nil) +local t = require('test.unit.testutil') local preprocess = require('test.unit.preprocess') diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index 6294114e1e..8d481df0d0 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -2,10 +2,10 @@ -- windows, will probably need quite a bit of adjustment to run there. local ffi = require('ffi') -local global_helpers = require('test.helpers') +local global_t = require('test.testutil') -local argss_to_cmd = global_helpers.argss_to_cmd -local repeated_read_cmd = global_helpers.repeated_read_cmd +local argss_to_cmd = global_t.argss_to_cmd +local repeated_read_cmd = global_t.repeated_read_cmd --- @alias Compiler {path: string[], type: string} diff --git a/test/unit/profile_spec.lua b/test/unit/profile_spec.lua index 011d3632d5..27b817cf42 100644 --- a/test/unit/profile_spec.lua +++ b/test/unit/profile_spec.lua @@ -1,10 +1,10 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local cimport = helpers.cimport -local ffi = helpers.ffi -local eq = helpers.eq -local neq = helpers.neq +local cimport = t.cimport +local ffi = t.ffi +local eq = t.eq +local neq = t.neq local prof = cimport('./src/nvim/profile.h') @@ -13,13 +13,13 @@ local function split(inputstr, sep) sep = '%s' end - local t, i = {}, 1 + local q, i = {}, 1 for str in string.gmatch(inputstr, '([^' .. sep .. ']+)') do - t[i] = str + q[i] = str i = i + 1 end - return t + return q end local function trim(s) @@ -72,8 +72,8 @@ describe('profiling related functions', function() local function profile_start() return prof.profile_start() end - local function profile_end(t) - return prof.profile_end(t) + local function profile_end(q) + return prof.profile_end(q) end local function profile_zero() return prof.profile_zero() @@ -81,8 +81,8 @@ describe('profiling related functions', function() local function profile_setlimit(ms) return prof.profile_setlimit(ms) end - local function profile_passed_limit(t) - return prof.profile_passed_limit(t) + local function profile_passed_limit(q) + return prof.profile_passed_limit(q) end local function profile_add(t1, t2) return prof.profile_add(t1, t2) @@ -90,8 +90,8 @@ describe('profiling related functions', function() local function profile_sub(t1, t2) return prof.profile_sub(t1, t2) end - local function profile_divide(t, cnt) - return prof.profile_divide(t, cnt) + local function profile_divide(q, cnt) + return prof.profile_divide(q, cnt) end local function profile_cmp(t1, t2) return prof.profile_cmp(t1, t2) @@ -99,12 +99,12 @@ describe('profiling related functions', function() local function profile_equal(t1, t2) return prof.profile_equal(t1, t2) end - local function profile_msg(t) - return ffi.string(prof.profile_msg(t)) + local function profile_msg(q) + return ffi.string(prof.profile_msg(q)) end - local function toseconds(t) -- luacheck: ignore - local str = trim(profile_msg(t)) + local function toseconds(q) -- luacheck: ignore + local str = trim(profile_msg(q)) local spl = split(str, '.') local s, us = spl[1], spl[2] return tonumber(s) + tonumber(us) / 1000000 @@ -229,7 +229,7 @@ describe('profiling related functions', function() describe('profile_msg', function() itp('prints the zero time as 0.00000', function() local str = trim(profile_msg(profile_zero())) - eq(str, '0.000000') + eq('0.000000', str) end) itp('prints the time passed, in seconds.microsends', function() @@ -245,7 +245,7 @@ describe('profiling related functions', function() -- zero seconds have passed (if this is not true, either LuaJIT is too -- slow or the profiling functions are too slow and need to be fixed) - eq(s, '0') + eq('0', s) -- more or less the same goes for the microsecond part, if it doesn't -- start with 0, it's too slow. diff --git a/test/unit/rbuffer_spec.lua b/test/unit/rbuffer_spec.lua index 328e5b93da..ad18ea2ddc 100644 --- a/test/unit/rbuffer_spec.lua +++ b/test/unit/rbuffer_spec.lua @@ -1,13 +1,13 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local eq = helpers.eq -local ffi = helpers.ffi -local cstr = helpers.cstr -local to_cstr = helpers.to_cstr -local child_call_once = helpers.child_call_once +local eq = t.eq +local ffi = t.ffi +local cstr = t.cstr +local to_cstr = t.to_cstr +local child_call_once = t.child_call_once -local rbuffer = helpers.cimport('./test/unit/fixtures/rbuffer.h') +local rbuffer = t.cimport('./test/unit/fixtures/rbuffer.h') describe('rbuffer functions', function() local capacity = 16 diff --git a/test/unit/search_spec.lua b/test/unit/search_spec.lua index efe49f974a..9171cf9f06 100644 --- a/test/unit/search_spec.lua +++ b/test/unit/search_spec.lua @@ -1,12 +1,12 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local to_cstr = helpers.to_cstr -local eq = helpers.eq +local to_cstr = t.to_cstr +local eq = t.eq -local search = helpers.cimport('./src/nvim/search.h') -local globals = helpers.cimport('./src/nvim/globals.h') -local ffi = helpers.ffi +local search = t.cimport('./src/nvim/search.h') +local globals = t.cimport('./src/nvim/globals.h') +local ffi = t.ffi itp('pat_has_uppercase', function() -- works on empty string @@ -35,14 +35,15 @@ itp('pat_has_uppercase', function() end) describe('search_regcomp', function() - local search_regcomp = function(pat, pat_save, pat_use, options) + local search_regcomp = function(pat, patlen, pat_save, pat_use, options) local regmatch = ffi.new('regmmatch_T') - local fail = search.search_regcomp(to_cstr(pat), nil, pat_save, pat_use, options, regmatch) + local fail = + search.search_regcomp(to_cstr(pat), patlen, nil, pat_save, pat_use, options, regmatch) return fail, regmatch end local get_search_pat = function() - return helpers.internalize(search.get_search_pat()) + return t.internalize(search.get_search_pat()) end itp('accepts regexp pattern with invalid utf', function() @@ -50,7 +51,7 @@ describe('search_regcomp', function() globals.curwin.w_onebuf_opt.wo_rl = 1 globals.curwin.w_onebuf_opt.wo_rlc = to_cstr('s') globals.cmdmod.cmod_flags = globals.CMOD_KEEPPATTERNS - local fail = search_regcomp('a\192', 0, 0, 0) + local fail = search_regcomp('a\192', 2, 0, 0, 0) eq(1, fail) eq('\192a', get_search_pat()) end) diff --git a/test/unit/statusline_spec.lua b/test/unit/statusline_spec.lua index 83ba4176c5..973d9ec992 100644 --- a/test/unit/statusline_spec.lua +++ b/test/unit/statusline_spec.lua @@ -1,15 +1,15 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local to_cstr = helpers.to_cstr -local get_str = helpers.ffi.string -local eq = helpers.eq -local NULL = helpers.NULL +local to_cstr = t.to_cstr +local get_str = t.ffi.string +local eq = t.eq +local NULL = t.NULL -local buffer = helpers.cimport('./src/nvim/buffer.h') -local globals = helpers.cimport('./src/nvim/globals.h') -local stl = helpers.cimport('./src/nvim/statusline.h') -local grid = helpers.cimport('./src/nvim/grid.h') +local buffer = t.cimport('./src/nvim/buffer.h') +local globals = t.cimport('./src/nvim/globals.h') +local stl = t.cimport('./src/nvim/statusline.h') +local grid = t.cimport('./src/nvim/grid.h') describe('build_stl_str_hl', function() local buffer_byte_size = 100 diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua index eea669c964..25cdc27b28 100644 --- a/test/unit/strings_spec.lua +++ b/test/unit/strings_spec.lua @@ -1,10 +1,10 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local cimport = helpers.cimport -local eq = helpers.eq -local ffi = helpers.ffi -local to_cstr = helpers.to_cstr +local cimport = t.cimport +local eq = t.eq +local ffi = t.ffi +local to_cstr = t.to_cstr local strings = cimport('stdlib.h', './src/nvim/strings.h', './src/nvim/memory.h') @@ -188,7 +188,7 @@ describe('vim_snprintf()', function() a('nan', buf, bsize, '%f', 0.0 / 0.0) a('inf', buf, bsize, '%f', 1.0 / 0.0) a('-inf', buf, bsize, '%f', -1.0 / 0.0) - a('-0.000000', buf, bsize, '%f', -0.0) + a('-0.000000', buf, bsize, '%f', tonumber('-0.0')) a('漢語', buf, bsize, '%s', '漢語') a(' 漢語', buf, bsize, '%8s', '漢語') a('漢語 ', buf, bsize, '%-8s', '漢語') @@ -233,7 +233,7 @@ describe('vim_snprintf()', function() a('nan', buf, bsize, '%1$f', 0.0 / 0.0) a('inf', buf, bsize, '%1$f', 1.0 / 0.0) a('-inf', buf, bsize, '%1$f', -1.0 / 0.0) - a('-0.000000', buf, bsize, '%1$f', -0.0) + a('-0.000000', buf, bsize, '%1$f', tonumber('-0.0')) end end) @@ -261,7 +261,7 @@ end) describe('reverse_text', function() local reverse_text = function(str) - return helpers.internalize(strings.reverse_text(to_cstr(str))) + return t.internalize(strings.reverse_text(to_cstr(str))) end itp('handles empty string', function() diff --git a/test/unit/tempfile_spec.lua b/test/unit/tempfile_spec.lua index e35490a561..1a42eed17e 100644 --- a/test/unit/tempfile_spec.lua +++ b/test/unit/tempfile_spec.lua @@ -1,11 +1,11 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) -local eq = helpers.eq -local neq = helpers.neq -local cimport = helpers.cimport -local child_call_once = helpers.child_call_once -local child_cleanup_once = helpers.child_cleanup_once +local eq = t.eq +local neq = t.neq +local cimport = t.cimport +local child_call_once = t.child_call_once +local child_cleanup_once = t.child_cleanup_once local lib = cimport('./src/nvim/os/os.h', './src/nvim/fileio.h') @@ -19,7 +19,7 @@ describe('tempfile related functions', function() end) local vim_gettempdir = function() - return helpers.ffi.string(lib.vim_gettempdir()) + return t.ffi.string(lib.vim_gettempdir()) end describe('vim_gettempdir', function() @@ -28,7 +28,7 @@ describe('tempfile related functions', function() assert.True(dir ~= nil and dir:len() > 0) -- os_file_is_writable returns 2 for a directory which we have rights -- to write into. - eq(lib.os_file_is_writable(helpers.to_cstr(dir)), 2) + eq(2, lib.os_file_is_writable(t.to_cstr(dir))) for entry in vim.fs.dir(dir) do assert.True(entry == '.' or entry == '..') end @@ -41,7 +41,7 @@ describe('tempfile related functions', function() describe('vim_tempname', function() local vim_tempname = function() - return helpers.ffi.string(lib.vim_tempname()) + return t.ffi.string(lib.vim_tempname()) end itp('generate name of non-existing file', function() @@ -57,7 +57,7 @@ describe('tempfile related functions', function() itp('generate file name in Nvim own temp directory', function() local dir = vim_gettempdir() local file = vim_tempname() - eq(string.sub(file, 1, string.len(dir)), dir) + eq(dir, string.sub(file, 1, string.len(dir))) end) end) end) diff --git a/test/unit/testtest_spec.lua b/test/unit/testtest_spec.lua index d2f3632b6f..0750cac5cc 100644 --- a/test/unit/testtest_spec.lua +++ b/test/unit/testtest_spec.lua @@ -1,9 +1,9 @@ -local helpers = require('test.unit.helpers')(after_each) +local t = require('test.unit.testutil') local assert = require('luassert') -local itp = helpers.gen_itp(it) +local itp = t.gen_itp(it) -local sc = helpers.sc +local sc = t.sc -- All of the below tests must fail. Check how exactly they fail. if os.getenv('NVIM_TEST_RUN_TESTTEST') ~= '1' then diff --git a/test/unit/helpers.lua b/test/unit/testutil.lua index ab4a59cfdb..a6db7beab1 100644 --- a/test/unit/helpers.lua +++ b/test/unit/testutil.lua @@ -2,16 +2,16 @@ local ffi = require('ffi') local formatc = require('test.unit.formatc') local Set = require('test.unit.set') local Preprocess = require('test.unit.preprocess') -local global_helpers = require('test.helpers') -local paths = global_helpers.paths +local t_global = require('test.testutil') +local paths = t_global.paths local assert = require('luassert') local say = require('say') -local check_cores = global_helpers.check_cores -local dedent = global_helpers.dedent -local neq = global_helpers.neq +local check_cores = t_global.check_cores +local dedent = t_global.dedent +local neq = t_global.neq local map = vim.tbl_map -local eq = global_helpers.eq +local eq = t_global.eq local trim = vim.trim -- add some standard header locations @@ -146,6 +146,9 @@ local function filter_complex_blocks(body) or string.find(line, 'value_init_') or string.find(line, 'UUID_NULL') -- static const uuid_t UUID_NULL = {...} or string.find(line, 'inline _Bool') + -- used by macOS headers + or string.find(line, 'typedef enum : ') + or string.find(line, 'mach_vm_range_recipe') ) then result[#result + 1] = line @@ -209,7 +212,7 @@ local function cimport(...) local new_cdefs = Set:new() for line in body:gmatch('[^\r\n]+') do line = trim(line) - -- give each #pragma pack an unique id, so that they don't get removed + -- give each #pragma pack a unique id, so that they don't get removed -- if they are inserted into the set -- (they are needed in the right order with the struct definitions, -- otherwise luajit has wrong memory layouts for the structs) @@ -873,8 +876,8 @@ local function is_asan() end end ---- @class test.unit.helpers.module -local module = { +--- @class test.unit.testutil.module +local M = { cimport = cimport, cppimport = cppimport, internalize = internalize, @@ -903,9 +906,7 @@ local module = { debug_log = debug_log, is_asan = is_asan, } ---- @class test.unit.helpers: test.unit.helpers.module, test.helpers -module = vim.tbl_extend('error', module, global_helpers) +--- @class test.unit.testutil: test.unit.testutil.module, test.testutil +M = vim.tbl_extend('error', M, t_global) -return function() - return module -end +return M diff --git a/test/unit/undo_spec.lua b/test/unit/undo_spec.lua index 0e2f38a8c8..5a96280caf 100644 --- a/test/unit/undo_spec.lua +++ b/test/unit/undo_spec.lua @@ -1,15 +1,15 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) local uv = vim.uv -local child_call_once = helpers.child_call_once +local child_call_once = t.child_call_once local sleep = uv.sleep -local ffi = helpers.ffi -local cimport = helpers.cimport -local to_cstr = helpers.to_cstr -local neq = helpers.neq -local eq = helpers.eq -local mkdir = helpers.mkdir +local ffi = t.ffi +local cimport = t.cimport +local to_cstr = t.to_cstr +local neq = t.neq +local eq = t.eq +local mkdir = t.mkdir local options = cimport('./src/nvim/option_vars.h') local undo = cimport('./src/nvim/undo.h') @@ -151,12 +151,12 @@ describe('u_write_undo', function() local file_contents = 'testing permissions' -- Write a text file where the undofile should go local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false)) - helpers.write_file(correct_name, file_contents, true, false) + t.write_file(correct_name, file_contents, true, false) -- Call with `forceit`. u_write_undo(correct_name, true, file_buffer, buffer_hash) - local undo_file_contents = helpers.read_file(correct_name) + local undo_file_contents = t.read_file(correct_name) neq(file_contents, undo_file_contents) local success, deletion_err = os.remove(correct_name) -- delete the file now that we're done with it. diff --git a/test/unit/viml/expressions/lexer_spec.lua b/test/unit/viml/expressions/lexer_spec.lua index 96052a5ce1..30483f4c1d 100644 --- a/test/unit/viml/expressions/lexer_spec.lua +++ b/test/unit/viml/expressions/lexer_spec.lua @@ -1,20 +1,20 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) -local viml_helpers = require('test.unit.viml.helpers') - -local child_call_once = helpers.child_call_once -local conv_enum = helpers.conv_enum -local cimport = helpers.cimport -local ffi = helpers.ffi -local eq = helpers.eq -local shallowcopy = helpers.shallowcopy -local intchar2lua = helpers.intchar2lua - -local conv_ccs = viml_helpers.conv_ccs -local new_pstate = viml_helpers.new_pstate -local conv_cmp_type = viml_helpers.conv_cmp_type -local pstate_set_str = viml_helpers.pstate_set_str -local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) +local t_viml = require('test.unit.viml.testutil') + +local child_call_once = t.child_call_once +local conv_enum = t.conv_enum +local cimport = t.cimport +local ffi = t.ffi +local eq = t.eq +local shallowcopy = t.shallowcopy +local intchar2lua = t.intchar2lua + +local conv_ccs = t_viml.conv_ccs +local new_pstate = t_viml.new_pstate +local conv_cmp_type = t_viml.conv_cmp_type +local pstate_set_str = t_viml.pstate_set_str +local conv_expr_asgn_type = t_viml.conv_expr_asgn_type local lib = cimport('./src/nvim/viml/parser/expressions.h') diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index c7d3f8532f..6f18448bd2 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -1,29 +1,29 @@ -local helpers = require('test.unit.helpers')(after_each) -local itp = helpers.gen_itp(it) -local viml_helpers = require('test.unit.viml.helpers') +local t = require('test.unit.testutil') +local itp = t.gen_itp(it) +local t_viml = require('test.unit.viml.testutil') -local make_enum_conv_tab = helpers.make_enum_conv_tab -local child_call_once = helpers.child_call_once -local alloc_log_new = helpers.alloc_log_new -local kvi_destroy = helpers.kvi_destroy -local conv_enum = helpers.conv_enum -local debug_log = helpers.debug_log -local ptr2key = helpers.ptr2key -local cimport = helpers.cimport -local ffi = helpers.ffi -local neq = helpers.neq -local eq = helpers.eq -local mergedicts_copy = helpers.mergedicts_copy +local make_enum_conv_tab = t.make_enum_conv_tab +local child_call_once = t.child_call_once +local alloc_log_new = t.alloc_log_new +local kvi_destroy = t.kvi_destroy +local conv_enum = t.conv_enum +local debug_log = t.debug_log +local ptr2key = t.ptr2key +local cimport = t.cimport +local ffi = t.ffi +local neq = t.neq +local eq = t.eq +local mergedicts_copy = t.mergedicts_copy local format_string = require('test.format_string').format_string local format_luav = require('test.format_string').format_luav -local intchar2lua = helpers.intchar2lua -local dictdiff = helpers.dictdiff +local intchar2lua = t.intchar2lua +local dictdiff = t.dictdiff -local conv_ccs = viml_helpers.conv_ccs -local new_pstate = viml_helpers.new_pstate -local conv_cmp_type = viml_helpers.conv_cmp_type -local pstate_set_str = viml_helpers.pstate_set_str -local conv_expr_asgn_type = viml_helpers.conv_expr_asgn_type +local conv_ccs = t_viml.conv_ccs +local new_pstate = t_viml.new_pstate +local conv_cmp_type = t_viml.conv_cmp_type +local pstate_set_str = t_viml.pstate_set_str +local conv_expr_asgn_type = t_viml.conv_expr_asgn_type local lib = cimport('./src/nvim/viml/parser/expressions.h', './src/nvim/syntax.h') diff --git a/test/unit/viml/helpers.lua b/test/unit/viml/testutil.lua index 92661e3027..376ef538d4 100644 --- a/test/unit/viml/helpers.lua +++ b/test/unit/viml/testutil.lua @@ -1,11 +1,11 @@ -local helpers = require('test.unit.helpers')(nil) +local t = require('test.unit.testutil') -local ffi = helpers.ffi -local cimport = helpers.cimport -local kvi_new = helpers.kvi_new -local kvi_init = helpers.kvi_init -local conv_enum = helpers.conv_enum -local make_enum_conv_tab = helpers.make_enum_conv_tab +local ffi = t.ffi +local cimport = t.cimport +local kvi_new = t.kvi_new +local kvi_init = t.kvi_init +local conv_enum = t.conv_enum +local make_enum_conv_tab = t.make_enum_conv_tab local lib = cimport('./src/nvim/viml/parser/expressions.h') |