diff options
487 files changed, 36878 insertions, 19609 deletions
diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml index 0ffc8aa786..422fa366b6 100644 --- a/.builds/openbsd.yml +++ b/.builds/openbsd.yml @@ -1,17 +1,17 @@ # sourcehut CI: https://builds.sr.ht/~jmk/neovim -image: openbsd/6.7 +image: openbsd/6.9 packages: -- autoconf-2.69p2 -- automake-1.15.1 +- autoconf-2.71 +- automake-1.16.3 - cmake -- gettext-runtime-0.20.1p1 -- gettext-tools-0.20.1p3 +- gettext-runtime-0.21p1 +- gettext-tools-0.21p1 - gmake - libtool -- ninja-1.10.0 -- unzip-6.0p13 +- ninja-1.10.2p0 +- unzip-6.0p14 sources: - https://github.com/neovim/neovim @@ -23,8 +23,8 @@ environment: tasks: - build-deps: | - export AUTOCONF_VERSION=2.69 - export AUTOMAKE_VERSION=1.15 + export AUTOCONF_VERSION=2.71 + export AUTOMAKE_VERSION=1.16 mkdir neovim/.deps cd neovim/.deps cmake -G Ninja ../third-party/ diff --git a/.clang-format b/.clang-format index ff61915aac..c86f5a3ddf 100644 --- a/.clang-format +++ b/.clang-format @@ -1,6 +1,6 @@ BasedOnStyle: Google Language: Cpp -ColumnLimit: 80 +ColumnLimit: 100 IndentWidth: 2 TabWidth: 2 UseTab: Never diff --git a/.clangd b/.clangd new file mode 100644 index 0000000000..d7911aaf64 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + CompilationDatabase: build/ # Search build/ directory for compile_commands.json diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..d5e725e5c8 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,20 @@ +# To use this file (requires git 2.23): +# git config blame.ignoreRevsFile .git-blame-ignore-revs + +# eval.c: factor out eval/funcs.c #11828 +# - This is a move/rename. git 2.33 doesn't know how to ignore it. +# It is here anyway, (1) in case git improves later, and (2) to +# save you the trouble of attempting this. +6c5bbf07d988ef55e5e8ba8d70b62c1f0885261b + +# symbol renames +6186df3562e33e92f04ed8c850204ceabc4746e1 + +# style / uncrustify +2d240024acbd68c2d3f82bc72cb12b1a4928c6bf +61178778230e609d68b271ffd53ffd993cd23c42 +15af08ad176339d1f269ce264bb0efea283c9536 +47f99d66440ae8be26b34531989ac61edc1ad9fe +1e49a1c888a3d9a581f4aa409a26ada3ac2417cb +3b3dbcf7b7ba5466e6ab643e256f2374b520a6b2 +e8067d1490a31ff76143d576dc9948b4f09c6c55 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index ae30f00f31..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: Bug report -about: Report a problem in Nvim -title: '' -labels: bug - ---- - -<!-- Before reporting: search existing issues and check the FAQ. --> - -- `nvim --version`: -- Operating system/version: -- Terminal name/version: -- `$TERM`: - -<!-- -If this report is about different behaviour between Nvim and Vim, make sure to -read `:h vim-differences` first. Otherwise remove the next line. ---> -[ ] `vim -u DEFAULTS` (version: ) behaves differently - -### Steps to reproduce using `nvim -u NORC` - -``` -nvim -u NORC -# Alternative for shell-related problems: -# env -i TERM=ansi-256color "$(which nvim)" - -``` - -### Actual behaviour - -### Expected behaviour - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..fa8d05f6b5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,75 @@ +name: Bug Report +description: Report a problem in Neovim +labels: [bug] +body: + + - type: markdown + attributes: + value: | + _Before reporting:_ search [existing issues](https://github.com/neovim/neovim/issues?q=is%3Aissue+is%3Aopen+label%3Abug) and check the [FAQ](https://github.com/neovim/neovim/wiki/FAQ). Usage questions such as "How do I...?" belong on the [Neovim Discourse](https://neovim.discourse.group/c/7-category/7) and will be closed. + + - type: input + attributes: + label: "Neovim version (nvim -v)" + placeholder: "0.6.0 commit db1b0ee3b30f" + validations: + required: true + - type: input + attributes: + label: "Vim (not Nvim) behaves the same?" + description: "Does `vim -u DEFAULTS` have the same issue? Note the exact Vim version (`8.x.yyyy`)." + placeholder: "no, vim 7.3.432" + validations: + required: true + - type: input + attributes: + label: "Operating system/version" + placeholder: "macOS 11.5" + validations: + required: true + - type: input + attributes: + label: "Terminal name/version" + placeholder: "xterm 3.1" + validations: + required: true + - type: input + attributes: + label: "$TERM environment variable" + placeholder: "echo $TERM" + validations: + required: true + + - type: input + attributes: + label: "Installation" + description: "How did you install neovim: build from repo / system package manager / appimage / homebrew / snap / chocolatey / other (describe)?" + placeholder: "Arch User Repository (AUR)" + validations: + required: true + + - type: textarea + attributes: + label: "How to reproduce the issue" + description: | + - Steps to reproduce using `nvim -u NORC` or `nvim -u NONE` (try both). + - For build failures: list the exact steps including CMake flags (if any). + - For shell-related problems: try `env -i TERM=ansi-256color "$(which nvim)"`. + placeholder: | + nvim -u NONE + :edit foo + yiwp + validations: + required: true + + - type: textarea + attributes: + label: "Expected behavior" + description: "Describe the behavior you expect. May include logs, images, or videos." + validations: + required: true + - type: textarea + attributes: + label: "Actual behavior" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..796707be03 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Question + url: https://neovim.discourse.group/ + about: Ask questions about configuration and usage of Neovim diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 928cde894c..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Feature request -about: Request an enhancement for Nvim -title: '' -labels: enhancement - ---- - -<!-- Before reporting: search existing issues and check the FAQ. --> - -- `nvim --version`: -- `vim -u DEFAULTS` (version: ) behaves differently? -- Operating system/version: -- Terminal name/version: -- `$TERM`: - -### Steps to reproduce using `nvim -u NORC` - -``` -nvim -u NORC - -``` - -### Actual behaviour - -### Expected behaviour - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..2b6fa3daf4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,21 @@ +name: Feature request +description: Request an enhancement for Neovim +labels: [enhancement] +body: + + - type: markdown + attributes: + value: | + Before requesting: search [existing issues](https://github.com/neovim/neovim/labels/enhancement) and check the [FAQ](https://github.com/neovim/neovim/wiki/FAQ). + + - type: input + attributes: + label: "Feature already in Vim?" + description: "Does the feature already exist in Vim? If possible, specify which version (or commit) that introduced it." + placeholder: "Yes, Vim 7.3.432" + + - type: textarea + attributes: + label: "Feature description" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/lsp_bug_report.md b/.github/ISSUE_TEMPLATE/lsp_bug_report.md deleted file mode 100644 index d2488a14e8..0000000000 --- a/.github/ISSUE_TEMPLATE/lsp_bug_report.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -name: Language server client bug report -about: Report a built-in lsp problem in Nvim -title: '' -labels: bug, lsp - ---- - -<!-- -Before reporting: search existing issues and check the FAQ. Usage questions -such as "How do I...?" or "Why isn't X language server/feature working?" belong -on the [Neovim Discourse](https://neovim.discourse.group/c/7-category/7) and will -be closed. ---> - -- `nvim --version`: -- language server name/version: -- Operating system/version: - -<details> -<summary>nvim -c ":checkhealth nvim lspconfig"</summary> - -<!-- Paste the results from `nvim -c ":checkhealth nvim lspconfig"` here. --> - -</details> - -<details> -<summary>lsp.log</summary> - -<!-- -Please paste the lsp log before and after the problem. - -You can set log level like this. -`:lua vim.lsp.set_log_level("debug")` - -You can find the location of the log with the following command. -`:lua print(vim.lsp.get_log_path())` ---> - -</details> - -### Steps to reproduce using nvim -u minimal_init.lua -<!-- - Note, if the issue is with an autocompletion or other LSP plugin, please - report to the upstream tracker. Download the minmal config with - wget https://raw.githubusercontent.com/neovim/nvim-lspconfig/master/test/minimal_init.lua - and modify it to include any specific commands or servers pertaining to your issues. ---> - - -``` -nvim -u minimal_init.lua -``` - -### Actual behaviour - -### Expected behaviour - diff --git a/.github/ISSUE_TEMPLATE/lsp_bug_report.yml b/.github/ISSUE_TEMPLATE/lsp_bug_report.yml new file mode 100644 index 0000000000..b5b7687bf8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/lsp_bug_report.yml @@ -0,0 +1,61 @@ +name: Language server (LSP) client bug +description: Report an issue with Neovim LSP +labels: [bug, lsp] +body: + + - type: markdown + attributes: + value: | + _Before reporting:_ search [existing issues](https://github.com/neovim/neovim/issues?q=is%3Aissue+is%3Aopen+label%3Abug) and check the [FAQ](https://github.com/neovim/neovim/wiki/FAQ). Usage questions such as "How do I...?" or "Why isn't X language server/feature working?" belong on the [Neovim Discourse](https://neovim.discourse.group/c/7-category/7) and will be closed. + + - type: input + attributes: + label: "Neovim version (nvim -v)" + placeholder: "0.6.0 commit db1b0ee3b30f" + validations: + required: true + - type: input + attributes: + label: "Language server name/version" + placeholder: "rls 0.5.2" + validations: + required: true + - type: input + attributes: + label: "Operating system/version" + placeholder: "emacs 23" + validations: + required: true + + - type: textarea + attributes: + label: ':checkhealth' + description: | + Paste the results from `nvim -c ":checkhealth nvim lspconfig"` + render: markdown + + - type: textarea + attributes: + label: 'Steps to reproduce using "nvim -u minimal_init.lua"' + description: | + - Download the minimal config with `curl -LO https://raw.githubusercontent.com/neovim/nvim-lspconfig/master/test/minimal_init.lua` and modify it to include any specific commands or servers pertaining to your issues. + - _Note_: if the issue is with an autocompletion or other LSP plugin, report to that plugin's issue tracker. + validations: + required: true + + - type: textarea + attributes: + label: "Expected behavior" + description: "Describe the behavior you expect. May include logs, images, or videos." + - type: textarea + attributes: + label: "Actual behavior" + + - type: input + attributes: + label: "Log file" + placeholder: "https://gist.github.com/prakhar1989/1b0a2c9849b2e1e912fb" + description: | + - Upload `lsp.log` before and after the problem in a [secret gist](https://gist.github.com/). Paste the URL to the gist. + - You can set the log level by adding `vim.lsp.set_log_level("debug")` after setting up LSP in your config. + - You can find the location of the log with `:lua print(vim.lsp.get_log_path())` diff --git a/.github/labeler.yml b/.github/labeler.yml index 100f97c5f2..d670c2a8f4 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -4,38 +4,55 @@ "lua": - runtime/lua/**/* - - src/nvim/lua + - src/nvim/lua/* "tui": - src/nvim/tui/tui.* -"tree-sitter": +"treesitter": - src/nvim/lua/treesitter.* - runtime/lua/vim/treesitter.lua - runtime/lua/vim/treesitter/* -"topic: spell": +"dependencies": + - third-party/**/* + +"spell": - src/nvim/spell* -"topic: :terminal": +"terminal": - src/nvim/terminal.* -"topic: column": +"column": - src/nvim/mark.h - src/nvim/mark.c - src/nvim/sign* -"topic: folds": +"folds": - src/nvim/fold* -"topic: mouse": +"mouse": - src/nvim/mouse* -"topic: documentation": - - runtime/doc/* +"documentation": + - all: ["runtime/doc/*"] -"topic: clipboard": +"clipboard": - runtime/autoload/provider/clipboard.vim -"topic: diff": +"diff": - src/nvim/diff.* + +"build": + - CMakeLists.txt + - "**/CMakeLists.txt" + - "**/*.cmake" + +"tests": + - all: ["test/**/*"] + +"ci": + - .github/labeler.yml + - .github/workflows/**/* + - .builds/* + - ci/**/* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72a6be304c..f75320048b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ on: push: branches: '**' pull_request: + types: [opened, synchronize, reopened, ready_for_review] branches: - 'master' - 'release-[0-9]+.[0-9]+' @@ -29,7 +30,12 @@ jobs: - cc: clang runner: macos-10.15 os: osx + - flavor: functionaltest-lua + cc: gcc + runner: ubuntu-20.04 + os: linux runs-on: ${{ matrix.runner }} + timeout-minutes: 45 if: github.event.pull_request.draft == false env: CC: ${{ matrix.cc }} diff --git a/.github/workflows/commitlint.config.js b/.github/workflows/commitlint.config.js new file mode 100644 index 0000000000..5f10ffc6f4 --- /dev/null +++ b/.github/workflows/commitlint.config.js @@ -0,0 +1,35 @@ +module.exports = { + rules: { + 'body-leading-blank': [1, 'always'], + 'body-max-line-length': [2, 'always', 100], + 'footer-leading-blank': [1, 'always'], + 'footer-max-line-length': [2, 'always', 100], + 'header-max-length': [2, 'always', 100], + 'scope-case': [2, 'always', 'lower-case'], + 'subject-case': [ + 2, + 'never', + ['sentence-case', 'start-case', 'pascal-case', 'upper-case'], + ], + 'subject-empty': [2, 'never'], + 'subject-full-stop': [2, 'never', '.'], + 'type-case': [2, 'always', 'lower-case'], + 'type-empty': [2, 'never'], + 'type-enum': [ + 2, + 'always', + [ + 'build', + 'chore', + 'ci', + 'docs', + 'feat', + 'fix', + 'perf', + 'refactor', + 'revert', + 'test', + ], + ], + }, +}; diff --git a/.github/workflows/commitlint.config_patch.js b/.github/workflows/commitlint.config_patch.js new file mode 100644 index 0000000000..ca398c45dc --- /dev/null +++ b/.github/workflows/commitlint.config_patch.js @@ -0,0 +1,27 @@ +module.exports = { + parserPreset: { + parserOpts: { headerPattern: /^([^\(\):]*)(?:\((.*)\))?!?:(.*)$/ } + }, + rules: { + 'body-leading-blank': [1, 'always'], + 'body-max-line-length': [2, 'always', 100], + 'footer-max-line-length': [2, 'always', 100], + 'scope-case': [2, 'always', 'lower-case'], + 'subject-case': [ + 2, + 'never', + ['sentence-case', 'start-case', 'pascal-case', 'upper-case'], + ], + 'subject-empty': [2, 'never'], + 'subject-full-stop': [2, 'never', '.'], + 'type-case': [2, 'always', 'lower-case'], + 'type-empty': [2, 'never'], + 'type-enum': [ + 2, + 'always', + [ + 'vim-patch', + ], + ], + }, +}; diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml new file mode 100644 index 0000000000..9ae138fbd7 --- /dev/null +++ b/.github/workflows/commitlint.yml @@ -0,0 +1,18 @@ +name: "Commit Linter" +on: pull_request +jobs: + lint-commits: + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v2.3.1 + with: + fetch-depth: 0 + - run: npm install --save-dev @commitlint/cli + - run: | + if [[ "$(gh pr view ${{ github.event.pull_request.number }} --json commits --jq '.[][0].messageHeadline')" == vim-patch* ]];then + npx commitlint --from HEAD~1 --to HEAD --verbose --help-url https://github.com/neovim/neovim/blob/master/CONTRIBUTING.md#commit-messages --config .github/workflows/commitlint.config_patch.js + else + npx commitlint --from HEAD~1 --to HEAD --verbose --help-url https://github.com/neovim/neovim/blob/master/CONTRIBUTING.md#commit-messages --config .github/workflows/commitlint.config.js + fi diff --git a/.github/workflows/env.sh b/.github/workflows/env.sh index 459ed669eb..a30e06ae26 100755 --- a/.github/workflows/env.sh +++ b/.github/workflows/env.sh @@ -20,13 +20,13 @@ VALGRIND_LOG=$GITHUB_WORKSPACE/build/log/valgrind-%p.log CACHE_NVIM_DEPS_DIR=$HOME/.cache/nvim-deps CACHE_MARKER=$HOME/.cache/nvim-deps/.ci_cache_marker CCACHE_BASEDIR=$GITHUB_WORKSPACE -DEPS_CMAKE_FLAGS=-DUSE_BUNDLED_GPERF=OFF -FUNCTIONALTEST=functionaltest CCACHE_COMPRESS=1 CCACHE_SLOPPINESS=time_macros,file_macro CCACHE_DIR=$HOME/.ccache EOF +DEPS_CMAKE_FLAGS=-DUSE_BUNDLED_GPERF=OFF +FUNCTIONALTEST=functionaltest BUILD_FLAGS="CMAKE_FLAGS=-DCI_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=$HOME/nvim-install -DBUSTED_OUTPUT_TYPE=nvim -DDEPS_PREFIX=$HOME/nvim-deps/usr -DMIN_LOG_LEVEL=3" case "$FLAVOR" in @@ -49,10 +49,17 @@ EOF CI_TARGET=lint EOF ;; + functionaltest-lua) + BUILD_FLAGS="$BUILD_FLAGS -DPREFER_LUA=ON" + FUNCTIONALTEST=functionaltest-lua + DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED_LUAJIT=OFF" + ;; *) ;; esac cat <<EOF >> "$GITHUB_ENV" $BUILD_FLAGS +DEPS_CMAKE_FLAGS=$DEPS_CMAKE_FLAGS +FUNCTIONALTEST=$FUNCTIONALTEST EOF diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 909e197b57..67ad4c0552 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,6 +1,7 @@ name: "Pull Request Labeler" on: -- pull_request_target + pull_request_target: + types: opened jobs: triage: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8c2333e68d..c6d3eaf42b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,7 @@ on: - v[0-9]+.[0-9]+.[0-9]+ # Build on the oldest supported images, so we have broader compatibility +# Upgrade to gcc-11 to prevent it from using its builtins (#14150) jobs: linux: runs-on: ubuntu-18.04 @@ -26,11 +27,15 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y autoconf automake build-essential cmake gettext gperf libtool-bin locales ninja-build pkg-config unzip + sudo apt-get install -y autoconf automake build-essential cmake gcc-11 gettext gperf libtool-bin locales ninja-build pkg-config unzip + - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly') + run: printf 'NVIM_BUILD_TYPE=Release\n' >> $GITHUB_ENV + - if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly') + run: printf 'NVIM_BUILD_TYPE=RelWithDebInfo\n' >> $GITHUB_ENV - name: Build release id: build run: | - make CMAKE_BUILD_TYPE=RelWithDebInfo CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX:PATH=" + CC=gcc-11 make CMAKE_BUILD_TYPE=${NVIM_BUILD_TYPE} CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX:PATH=" printf '::set-output name=version::%s\n' "$(./build/bin/nvim --version | head -n 3 | sed -z 's/\n/%0A/g')" printf '::set-output name=release::%s\n' "$(./build/bin/nvim --version | head -n 1)" make DESTDIR="$GITHUB_WORKSPACE/build/release/nvim-linux64" install @@ -51,11 +56,11 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y autoconf automake build-essential cmake gettext gperf libtool-bin locales ninja-build pkg-config unzip + sudo apt-get install -y autoconf automake build-essential cmake gcc-11 gettext gperf libtool-bin locales ninja-build pkg-config unzip - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly') - run: make appimage-latest + run: CC=gcc-11 make appimage-latest - if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly') - run: make appimage-nightly + run: CC=gcc-11 make appimage-nightly - uses: actions/upload-artifact@v2 with: name: appimage @@ -79,9 +84,13 @@ jobs: brew update >/dev/null brew upgrade brew install automake ninja + - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly') + run: printf 'NVIM_BUILD_TYPE=Release\n' >> $GITHUB_ENV + - if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name == 'nightly') + run: printf 'NVIM_BUILD_TYPE=RelWithDebInfo\n' >> $GITHUB_ENV - name: Build release run: | - make CMAKE_BUILD_TYPE=Release CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11" + make CMAKE_BUILD_TYPE=${NVIM_BUILD_TYPE} CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11" make DESTDIR="$GITHUB_WORKSPACE/build/release/nvim-osx64" install - name: Create package run: | @@ -159,6 +168,38 @@ jobs: with: delete_release: true tag_name: nightly + # `sha256sum` outputs <sha> <path>, so we cd into each dir to drop the + # containing folder from the output. + - name: Generate Linux64 SHA256 checksums + run: | + cd ./nvim-linux64 + sha256sum nvim-linux64.tar.gz > nvim-linux64.tar.gz.sha256sum + echo "SHA_LINUX_64=$(cat nvim-linux64.tar.gz.sha256sum)" >> $GITHUB_ENV + - name: Generate App Image SHA256 checksums + run: | + cd ./appimage + sha256sum nvim.appimage > nvim.appimage.sha256sum + echo "SHA_APP_IMAGE=$(cat nvim.appimage.sha256sum)" >> $GITHUB_ENV + - name: Generate App Image Zsync SHA256 checksums + run: | + 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 + 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 + - name: Generate Win32 SHA256 checksums + run: | + cd ./nvim-win32 + sha256sum nvim-win32.zip > nvim-win32.zip.sha256sum + echo "SHA_WIN_32=$(cat nvim-win32.zip.sha256sum)" >> $GITHUB_ENV + - name: Generate Win64 SHA256 checksums + run: | + cd ./nvim-win64 + sha256sum nvim-win64.zip > nvim-win64.zip.sha256sum + echo "SHA_WIN_64=$(cat nvim-win64.zip.sha256sum)" >> $GITHUB_ENV - uses: meeDamian/github-release@2.0 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -170,11 +211,17 @@ jobs: allow_override: ${{ env.TAG_NAME == 'nightly' }} files: | nvim-macos.tar.gz:./nvim-macos/nvim-macos.tar.gz + nvim-macos.tar.gz.sha256sum:./nvim-macos/nvim-macos.tar.gz.sha256sum nvim-linux64.tar.gz:./nvim-linux64/nvim-linux64.tar.gz + nvim-linux64.tar.gz.sha256sum:./nvim-linux64/nvim-linux64.tar.gz.sha256sum nvim.appimage:./appimage/nvim.appimage + nvim.appimage.sha256sum:./appimage/nvim.appimage.sha256sum nvim.appimage.zsync:./appimage/nvim.appimage.zsync + nvim.appimage.zsync.sha256sum:./appimage/nvim.appimage.zsync.sha256sum nvim-win32.zip:./nvim-win32/nvim-win32.zip + nvim-win32.zip.sha256sum:./nvim-win32/nvim-win32.zip.sha256sum nvim-win64.zip:./nvim-win64/nvim-win64.zip + nvim-win64.zip.sha256sum:./nvim-win64/nvim-win64.zip.sha256sum body: | ${{ env.SUBJECT }} ``` @@ -206,3 +253,14 @@ jobs: ### Other - Install by [package manager](https://github.com/neovim/neovim/wiki/Installing-Neovim) + + ## SHA256 Checksums + + ``` + ${{ env.SHA_LINUX_64 }} + ${{ env.SHA_APP_IMAGE }} + ${{ env.SHA_APP_IMAGE_ZSYNC }} + ${{ env.SHA_MACOS }} + ${{ env.SHA_WIN_64 }} + ${{ env.SHA_WIN_32 }} + ``` diff --git a/.github/workflows/squash-typos.yml b/.github/workflows/squash-typos.yml new file mode 100644 index 0000000000..6779589dc6 --- /dev/null +++ b/.github/workflows/squash-typos.yml @@ -0,0 +1,33 @@ +name: Squash Typo Pull Requests + +on: + pull_request_target: + types: labeled +concurrency: + group: ${{ github.workflow }} +jobs: + build: + if: github.event.label.name == 'typo' + runs-on: ubuntu-latest + + permissions: + contents: write + pull-requests: write + + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-python@v2 + + - name: Setup git config + run: | + git config --global user.name 'marvim' + git config --global user.email 'marvim@users.noreply.github.com' + + - run: python scripts/squash_typos.py + env: + PR_NUMBER: ${{ github.event.number }} diff --git a/.gitignore b/.gitignore index 163140c6e1..e07ce4906e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ compile_commands.json /tmp/ /.clangd/ /.cache/clangd/ +/.ccls-cache/ .DS_Store *.mo diff --git a/BSDmakefile b/BSDmakefile index 93b7dc7f3d..f81223bafa 100644 --- a/BSDmakefile +++ b/BSDmakefile @@ -1,4 +1,4 @@ .DONE: - @echo "Please use GNU Make (gmake) to build neovim" + @echo "Use GNU Make (gmake) to build neovim" .DEFAULT: - @echo "Please use GNU Make (gmake) to build neovim" + @echo "Use GNU Make (gmake) to build neovim" diff --git a/CMakeLists.txt b/CMakeLists.txt index c22ab8dbae..a3c3fec279 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,9 +23,23 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) # Prefer our bundled versions of dependencies. if(DEFINED ENV{DEPS_BUILD_DIR}) - set(DEPS_PREFIX "$ENV{DEPS_BUILD_DIR}/usr" CACHE PATH "Path prefix for finding dependencies") + if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + # pkg-config 29.2 has a bug on OpenBSD which causes it to drop any paths that + # *contain* system include paths. To avoid this, we prefix what would be + # "/usr/include" as "/_usr/include". + # This check is also performed in the third-party/CMakeLists.txt and in the + # else clause following here. + # https://github.com/neovim/neovim/pull/14745#issuecomment-860201794 + set(DEPS_PREFIX "$ENV{DEPS_BUILD_DIR}/_usr" CACHE PATH "Path prefix for finding dependencies") + else() + set(DEPS_PREFIX "$ENV{DEPS_BUILD_DIR}/usr" CACHE PATH "Path prefix for finding dependencies") + endif() else() - set(DEPS_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/.deps/usr" CACHE PATH "Path prefix for finding dependencies") + if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + set(DEPS_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/.deps/_usr" CACHE PATH "Path prefix for finding dependencies") + else() + set(DEPS_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/.deps/usr" CACHE PATH "Path prefix for finding dependencies") + endif() # When running from within CLion or Visual Studio, # build bundled dependencies automatically. if(NOT EXISTS ${DEPS_PREFIX} @@ -122,12 +136,12 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY # 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 5) +set(NVIM_VERSION_MINOR 6) set(NVIM_VERSION_PATCH 0) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers # API level -set(NVIM_API_LEVEL 7) # Bump this after any API change. +set(NVIM_API_LEVEL 8) # Bump this after any API change. set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change. set(NVIM_API_PRERELEASE true) @@ -379,6 +393,22 @@ include_directories(SYSTEM ${LIBLUV_INCLUDE_DIRS}) find_package(TreeSitter REQUIRED) include_directories(SYSTEM ${TreeSitter_INCLUDE_DIRS}) +list(APPEND CMAKE_REQUIRED_INCLUDES "${TreeSitter_INCLUDE_DIRS}") +list(APPEND CMAKE_REQUIRED_LIBRARIES "${TreeSitter_LIBRARIES}") +check_c_source_compiles(" +#include <tree_sitter/api.h> +int +main(void) +{ + TSQueryCursor *cursor = ts_query_cursor_new(); + ts_query_cursor_set_match_limit(cursor, 32); + return 0; +} +" TS_HAS_SET_MATCH_LIMIT) +if(TS_HAS_SET_MATCH_LIMIT) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVIM_TS_HAS_SET_MATCH_LIMIT") +endif() + # Note: The test lib requires LuaJIT; it will be skipped if LuaJIT is missing. option(PREFER_LUA "Prefer Lua over LuaJIT in the nvim executable." OFF) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 94c371b62d..2a565574fa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,9 +8,10 @@ If you want to help but don't know where to start, here are some low-risk/isolated tasks: - [Merge a Vim patch]. -- Try a [good first issue](../../labels/good%20first%20issue) or [complexity:low] issue. +- Try a [good first issue](../../labels/good-first-issue) or [complexity:low] issue. - Fix bugs found by [Clang](#clang-scan-build), [PVS](#pvs-studio) or [Coverity](#coverity). +- [Improve documentation][wiki-contribute-help] Reporting problems ------------------ @@ -18,31 +19,31 @@ Reporting problems - [Check the FAQ][wiki-faq]. - [Search existing issues][github-issues] (including closed!) - Update Neovim to the latest version to see if your problem persists. -- Disable plugins incrementally, to narrow down the cause of the issue. +- [Bisect](https://neovim.io/doc/user/starting.html#bisect) your config: disable plugins incrementally, to narrow down the cause of the issue. +- [Bisect][git-bisect] Neovim's source code to find the cause of a regression, if you can. This is _extremely_ helpful. - When reporting a crash, [include a stacktrace](https://github.com/neovim/neovim/wiki/FAQ#backtrace-linux). -- [Bisect][git-bisect] to the cause of a regression, if you are able. This is _extremely_ helpful. -- Check `$NVIM_LOG_FILE`, if it exists. +- Use [ASAN/UBSAN](#clang-sanitizers-asan-and-ubsan) to get detailed errors for segfaults and undefined behavior. +- Check the logs. `:edit $NVIM_LOG_FILE` - Include `cmake --system-information` for build-related issues. Developer guidelines -------------------- -- Nvim contributors should read `:help dev`. -- External UI developers should read `:help dev-ui`. -- API client developers should read `:help dev-api-client`. -- Nvim developers are _strongly encouraged_ to install `ninja` for faster builds. +- Read `:help dev` if you are working on Nvim core. +- Read `:help dev-ui` if you are developing a UI. +- Read `:help dev-api-client` if you are developing an API client. +- Install `ninja` for faster builds of Nvim. ``` sudo apt-get install ninja-build make distclean make # Nvim build system uses ninja automatically, if available. ``` -- [Improve documentation][wiki-contribute-help] Pull requests (PRs) --------------------- -- To avoid duplicate work, create a `[WIP]` pull request as soon as possible. -- Your PR must include **test coverage.** See [test/README.md][run-tests]. +- To avoid duplicate work, create a draft pull request. +- Your PR must include [test coverage][run-tests]. - Avoid cosmetic changes to unrelated files in the same commit. - Use a [feature branch][git-feature-branch] instead of the master branch. - Use a **rebase workflow** for small PRs. @@ -61,21 +62,25 @@ Pull requests (PRs) - During a squash/fixup, use `exec make -C build unittest` between each pick/edit/reword. -### Stages: WIP, RFC, RDY +### Stages: Draft and Ready for review -Pull requests have three stages: `[WIP]` (Work In Progress), `[RFC]` (Request -For Comment) and `[RDY]` (Ready). +Pull requests have two stages: Draft and Ready for review. -1. `[RFC]` is assumed by default, **do not** put "RFC" in the PR title (it adds - noise to merge commit messages). -2. Add `[WIP]` to the PR title if you are _not_ requesting feedback and the work - is still in flux. -3. Add `[RDY]` to the PR title if you are _done_ and only waiting on merge. +1. [Create a Draft PR][pr-draft] while you are _not_ requesting feedback as + you are still working on the PR. + - You can skip this if your PR is ready for review. +2. [Change your PR to ready][pr-ready] when the PR is ready for review. + - You can convert back to Draft at any time. + +Do __not__ add labels like `[RFC]` or `[WIP]` in the title to indicate the +state of your PR: this just adds noise. Non-Draft PRs are assumed to be open +for comments; if you want feedback from specific people, `@`-mention them in +a comment. ### Commit messages -Follow the [convential 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 as follows: +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> @@ -85,21 +90,27 @@ the VCS/git logs more valuable. The general structure of a commit message is as [optional footer(s)] ``` -- **Prefix the commit subject with a _type_:** `doc:`, `test:` - `runtime:`, ... - - Subject line for commits with only style/lint changes can be a single - word: `style` or `lint`. -- **Add the optional scope following <type> if possible:** `(lsp)`, `(treesitter)`, `(multigrid)`, ... -- Try to keep the first line under 72 characters. -- A blank line must separate the subject from the description. -- Breaking changes must be indicated at the very beginning of the footer or body section of a commit. A breaking change must consist of the uppercase text BREAKING CHANGE, followed by a colon, a space, and a description of what has changed about the API. -- Check your commit message for spelling and grammatical mistakes. +- 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`, `chore` + - 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. + ``` ### Automated builds (CI) -Each pull request must pass the automated builds on [Travis CI], [sourcehut] -and [AppVeyor]. +Each pull request must pass the automated builds on [sourcehut] and [GitHub Actions]. - CI builds are compiled with [`-Werror`][gcc-warnings], so compiler warnings will fail the build. @@ -173,6 +184,20 @@ master build. To view the defects, just request access; you will be approved. git log --oneline --no-merges --grep coverity ``` +### Clang sanitizers (ASAN and UBSAN) + + ASAN/UBSAN can be used to detect memory errors and other common forms of undefined behavior at runtime in debug builds. + +- To build Neovim with sanitizers enabled, use + ``` + rm -rf build && CMAKE_EXTRA_FLAGS="-DCMAKE_C_COMPILER=clang -DCLANG_ASAN_UBSAN=1" make + ``` +- When running Neovim, use + ``` + UBSAN_OPTIONS=print_stacktrace=1 ASAN_OPTIONS=log_path=/tmp/nvim_asan nvim args... + ``` +- If Neovim exits unexpectedly, check `/tmp/nvim_asan.{PID}` (or your preferred `log_path`) for log files with error messages. + Coding ------ @@ -202,6 +227,10 @@ operator in Nvim: ### Navigate +- Set `blame.ignoreRevsFile` to ignore [noise commits](https://github.com/neovim/neovim/commit/2d240024acbd68c2d3f82bc72cb12b1a4928c6bf) in git blame: + ``` + git config blame.ignoreRevsFile .git-blame-ignore-revs + ``` - Use **[universal-ctags](https://github.com/universal-ctags/ctags).** ("Exuberant ctags", the typical `ctags` binary provided by your distro, is unmaintained and won't recognize many function signatures in Neovim source.) @@ -240,11 +269,12 @@ as context, use the `-W` argument as well. [wiki-faq]: https://github.com/neovim/neovim/wiki/FAQ [review-checklist]: https://github.com/neovim/neovim/wiki/Code-review-checklist [3174]: https://github.com/neovim/neovim/issues/3174 -[Travis CI]: https://travis-ci.org/neovim/neovim [sourcehut]: https://builds.sr.ht/~jmk -[AppVeyor]: https://ci.appveyor.com/project/neovim/neovim +[GitHub Actions]: https://github.com/neovim/neovim/actions [Merge a Vim patch]: https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-Vim [Clang report]: https://neovim.io/doc/reports/clang/ [complexity:low]: https://github.com/neovim/neovim/issues?q=is%3Aopen+is%3Aissue+label%3Acomplexity%3Alow [master error list]: https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint/errors.json [wiki-contribute-help]: https://github.com/neovim/neovim/wiki/contribute-%3Ahelp +[pr-draft]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request +[pr-ready]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request @@ -1,10 +1,10 @@ [](https://neovim.io) [Documentation](https://neovim.io/doc/general/) | -[Chat](https://gitter.im/neovim/neovim) | +[Chat](https://app.element.io/#/room/#neovim:matrix.org) | [Twitter](https://twitter.com/Neovim) -[](https://github.com/neovim/neovim/actions?query=workflow%3A%22CI%22) +[](https://github.com/neovim/neovim/actions?query=workflow%3ACI+branch%3Amaster+event%3Apush) [](https://codecov.io/gh/neovim/neovim) [](https://scan.coverity.com/projects/2227) [](https://neovim.io/doc/reports/clang) @@ -46,8 +46,7 @@ Install from package Pre-built packages for Windows, macOS, and Linux are found on the [Releases](https://github.com/neovim/neovim/releases/) page. -[Managed packages] are in [Homebrew], [Debian], [Ubuntu], [Fedora], [Arch Linux], -[Gentoo], and more! +[Managed packages] are in [Homebrew], [Debian], [Ubuntu], [Fedora], [Arch Linux], [Void Linux], [Gentoo], and more! Install from source ------------------- @@ -65,7 +64,7 @@ To install to a non-default location: make CMAKE_INSTALL_PREFIX=/full/path/ make install -To inspect the build, these CMake features are useful: +CMake hints for inspecting the build: - `cmake --build build --target help` lists all build targets. - `build/CMakeCache.txt` (or `cmake -LAH build/`) contains the resolved values of all CMake variables. @@ -122,6 +121,7 @@ Apache 2.0 license, except for contributions copied from Vim (identified by the [Ubuntu]: http://packages.ubuntu.com/search?keywords=neovim [Fedora]: https://apps.fedoraproject.org/packages/neovim [Arch Linux]: https://www.archlinux.org/packages/?q=neovim +[Void Linux]: https://voidlinux.org/packages/?arch=x86_64&q=neovim [Gentoo]: https://packages.gentoo.org/packages/app-editors/neovim [Homebrew]: https://formulae.brew.sh/formula/neovim diff --git a/cmake/FindLibLUV.cmake b/cmake/FindLibLUV.cmake index bc53d00f24..23b62b66d3 100644 --- a/cmake/FindLibLUV.cmake +++ b/cmake/FindLibLUV.cmake @@ -15,7 +15,7 @@ find_path(LIBLUV_INCLUDE_DIR luv/luv.h PATHS ${PC_LIBLUV_INCLUDEDIR} ${PC_LIBLUV_INCLUDE_DIRS}) # Explicitly look for luv.so. #10407 -list(APPEND LIBLUV_NAMES luv luv${CMAKE_SHARED_LIBRARY_SUFFIX}) +list(APPEND LIBLUV_NAMES luv_a luv libluv_a luv${CMAKE_SHARED_LIBRARY_SUFFIX}) find_library(LIBLUV_LIBRARY NAMES ${LIBLUV_NAMES} HINTS ${PC_LIBLUV_LIBDIR} ${PC_LIBLUV_LIBRARY_DIRS}) diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake index f000004aec..0c48a77b9a 100644 --- a/cmake/RunTests.cmake +++ b/cmake/RunTests.cmake @@ -49,6 +49,13 @@ endif() set(ENV{TMPDIR} "${BUILD_DIR}/Xtest_tmpdir/${TEST_PATH}") execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory $ENV{TMPDIR}) +# HISTFILE: do not write into user's ~/.bash_history +set(ENV{HISTFILE} "/dev/null") + +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. execute_process( COMMAND ${BUSTED_PRG} -v -o test.busted.outputHandlers.${BUSTED_OUTPUT_TYPE} @@ -58,6 +65,7 @@ execute_process( --lpath=?.lua ${BUSTED_ARGS} ${TEST_PATH} + TIMEOUT $ENV{TEST_TIMEOUT} WORKING_DIRECTORY ${WORKING_DIR} ERROR_VARIABLE err RESULT_VARIABLE res diff --git a/contrib/flake.lock b/contrib/flake.lock index 521b7629d9..c4d7f120ba 100644 --- a/contrib/flake.lock +++ b/contrib/flake.lock @@ -2,11 +2,11 @@ "nodes": { "flake-utils": { "locked": { - "lastModified": 1610051610, - "narHash": "sha256-U9rPz/usA1/Aohhk7Cmc2gBrEEKRzcW4nwPWMPwja4Y=", + "lastModified": 1629481132, + "narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=", "owner": "numtide", "repo": "flake-utils", - "rev": "3982c9903e93927c2164caa727cd3f6a0e6d14cc", + "rev": "997f7efcb746a9c140ce1f13c72263189225f482", "type": "github" }, "original": { @@ -17,11 +17,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1613226215, - "narHash": "sha256-3rA5cGIrBHD6yeKhNhsF7/t461ww25oJY8KyBb0IhjU=", + "lastModified": 1630074300, + "narHash": "sha256-BFM7OiXRs0RvSUZd6NCGAKWVPn3VodgYQ4TUQXxbMBU=", "owner": "nixos", "repo": "nixpkgs", - "rev": "ff96a0fa5635770390b184ae74debea75c3fd534", + "rev": "21c937f8cb1e6adcfeb36dfd6c90d9d9bfab1d28", "type": "github" }, "original": { diff --git a/contrib/flake.nix b/contrib/flake.nix index e75ff0356b..848c52d208 100644 --- a/contrib/flake.nix +++ b/contrib/flake.nix @@ -16,24 +16,20 @@ neovim = pkgs.neovim-unwrapped.overrideAttrs (oa: { version = "master"; src = ../.; - - buildInputs = oa.buildInputs ++ ([ - pkgs.tree-sitter - ]); - - cmakeFlags = oa.cmakeFlags ++ [ - "-DUSE_BUNDLED=OFF" - ]; }); # a development binary to help debug issues neovim-debug = let - stdenv = pkgs.stdenvAdapters.keepDebugInfo (if pkgs.stdenv.isLinux then pkgs.llvmPackages_latest.stdenv else pkgs.stdenv); + stdenv = if pkgs.stdenv.isLinux then pkgs.llvmPackages_latest.stdenv else pkgs.stdenv; in - pkgs.enableDebugging ((neovim.override { - lua = pkgs.enableDebugging pkgs.luajit; + ((neovim.override { + lua = pkgs.luajit; inherit stdenv; }).overrideAttrs (oa: { + + dontStrip = true; + NIX_CFLAGS_COMPILE = " -ggdb -Og"; + cmakeBuildType = "Debug"; cmakeFlags = oa.cmakeFlags ++ [ "-DMIN_LOG_LEVEL=0" @@ -42,16 +38,10 @@ disallowedReferences = []; })); - # for neovim developers, builds a slow binary - # huge closure size but aims at covering all scripts - # brings development tools as well + # for neovim developers, beware of the slow binary neovim-developer = let lib = nixpkgs.lib; - pythonEnv = pkgs.python3.withPackages(ps: [ - ps.msgpack - ps.flake8 # for 'make pylint' - ]); luacheck = pkgs.luaPackages.luacheck; in (neovim-debug.override ({ doCheck = pkgs.stdenv.isLinux; })).overrideAttrs (oa: { @@ -59,29 +49,11 @@ "-DLUACHECK_PRG=${luacheck}/bin/luacheck" "-DMIN_LOG_LEVEL=0" "-DENABLE_LTO=OFF" - "-DUSE_BUNDLED=OFF" ] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [ # https://github.com/google/sanitizers/wiki/AddressSanitizerFlags # https://clang.llvm.org/docs/AddressSanitizer.html#symbolizing-the-reports "-DCLANG_ASAN_UBSAN=ON" ]; - - nativeBuildInputs = oa.nativeBuildInputs ++ (with pkgs; [ - pythonEnv - include-what-you-use # for scripts/check-includes.py - jq # jq for scripts/vim-patch.sh -r - shellcheck # for `make shlint` - doxygen # for script/gen_vimdoc.py - 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_OPTIONS="log_path=./test.log:abort_on_error=1" - export UBSAN_OPTIONS=print_stacktrace=1 - ''; }); }; } // @@ -91,6 +63,11 @@ overlays = [ self.overlay ]; inherit system; }; + + pythonEnv = pkgs.python3.withPackages(ps: [ + ps.msgpack + ps.flake8 # for 'make pylint' + ]); in rec { @@ -98,6 +75,18 @@ inherit neovim neovim-debug neovim-developer; }; + checks = { + pylint = pkgs.runCommandNoCC "pylint" { + nativeBuildInputs = [ pythonEnv ]; + preferLocalBuild = true; + } "make -C ${./..} pylint > $out"; + + shlint = pkgs.runCommandNoCC "shlint" { + nativeBuildInputs = [ pkgs.shellcheck ]; + preferLocalBuild = true; + } "make -C ${./..} shlint > $out"; + }; + defaultPackage = pkgs.neovim; apps = { @@ -107,6 +96,38 @@ defaultApp = apps.nvim; - devShell = pkgs.neovim-developer; + devShell = let + in + pkgs.neovim-developer.overrideAttrs(oa: { + + buildInputs = with pkgs; oa.buildInputs ++ [ + cmake + pythonEnv + include-what-you-use # for scripts/check-includes.py + jq # jq for scripts/vim-patch.sh -r + shellcheck # for `make shlint` + doxygen # for script/gen_vimdoc.py + 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" + export UBSAN_OPTIONS=print_stacktrace=1 + mkdir -p build/runtime/parser + # nvim looks into CMAKE_INSTALL_DIR. Hack to avoid errors + # when running the functionaltests + mkdir -p outputs/out/share/nvim/syntax + touch outputs/out/share/nvim/syntax/syntax.vim + + # for treesitter functionaltests + mkdir -p runtime/parser + cp -f ${pkgs.tree-sitter.builtGrammars.tree-sitter-c}/parser runtime/parser/c.so + ''; + }); }); } diff --git a/contrib/uncrustify.cfg b/contrib/uncrustify.cfg index 11da34d59a..567354600c 100644 --- a/contrib/uncrustify.cfg +++ b/contrib/uncrustify.cfg @@ -1,1578 +1,3283 @@ -# Uncrustify 0.60 +# Uncrustify-0.73.0-159-81b1bc77 # # General options # -# The type of line endings -newlines = auto # auto/lf/crlf/cr +# The type of line endings. +# +# Default: auto +newlines = auto # lf/crlf/cr/auto -# The original size of tabs in the input -input_tab_size = 8 # number +# The original size of tabs in the input. +# +# Default: 8 +input_tab_size = 8 # unsigned number -# The size of tabs in the output (only used if align_with_tabs=true) -output_tab_size = 8 # number +# The size of tabs in the output (only used if align_with_tabs=true). +# +# Default: 8 +output_tab_size = 8 # unsigned number + +# The ASCII value of the string escape char, usually 92 (\) or (Pawn) 94 (^). +# +# Default: 92 +string_escape_char = 92 # unsigned number -# The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn) -string_escape_char = 92 # number +# Alternate string escape char (usually only used for Pawn). +# Only works right before the quote char. +string_escape_char2 = 0 # unsigned number -# Alternate string escape char for Pawn. Only works right before the quote char. -string_escape_char2 = 0 # number +# Replace tab characters found in string literals with the escape sequence \t +# instead. +string_replace_tab_chars = false # true/false -# Allow interpreting '>=' and '>>=' as part of a template in 'void f(list<list<B>>=val);'. -# If true (default), 'assert(x<0 && y>=3)' will be broken. +# Allow interpreting '>=' and '>>=' as part of a template in code like +# 'void f(list<list<B>>=val);'. If true, 'assert(x<0 && y>=3)' will be broken. # Improvements to template detection may make this option obsolete. -tok_split_gte = false # false/true +tok_split_gte = false # true/false -# Control what to do with the UTF-8 BOM (recommend 'remove') -utf8_bom = ignore # ignore/add/remove/force +# Disable formatting of NL_CONT ('\\n') ended lines (e.g. multi-line macros). +disable_processing_nl_cont = false # true/false + +# Specify the marker used in comments to disable processing of part of the +# file. +# +# Default: *INDENT-OFF* +disable_processing_cmt = " *INDENT-OFF*" # string + +# Specify the marker used in comments to (re)enable processing in a file. +# +# Default: *INDENT-ON* +enable_processing_cmt = " *INDENT-ON*" # string -# If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8 -utf8_byte = false # false/true +# Enable parsing of digraphs. +enable_digraphs = false # true/false -# Force the output encoding to UTF-8 -utf8_force = false # false/true +# Option to allow both disable_processing_cmt and enable_processing_cmt +# strings, if specified, to be interpreted as ECMAScript regular expressions. +# If true, a regex search will be performed within comments according to the +# specified patterns in order to disable/enable processing. +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 + +# If the file contains bytes with values between 128 and 255, but is not +# UTF-8, then output as UTF-8. +utf8_byte = false # true/false + +# Force the output encoding to UTF-8. +utf8_force = false # true/false # -# Indenting +# Spacing options # -# The number of columns to indent per level. -# Usually 2, 3, 4, or 8. -indent_columns = 2 # number +# Add or remove space around non-assignment symbolic operators ('+', '/', '%', +# '<<', and so forth). +sp_arith = ignore # ignore/add/remove/force/not_defined -# The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents. -# For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level -indent_continue = 0 # number +# Add or remove space around arithmetic operators '+' and '-'. +# +# Overrides sp_arith. +sp_arith_additive = ignore # ignore/add/remove/force/not_defined -# How to use tabs when indenting code -# 0=spaces only -# 1=indent with tabs to brace level, align with spaces -# 2=indent and align with tabs, using spaces when not on a tabstop -indent_with_tabs = 0 # number +# Add or remove space around assignment operator '=', '+=', etc. +sp_assign = ignore # ignore/add/remove/force/not_defined -# Comments that are not a brace level are indented with tabs on a tabstop. -# Requires indent_with_tabs=2. If false, will use spaces. -indent_cmt_with_tabs = false # false/true +# 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 -# Whether to indent strings broken by '\' so that they line up -indent_align_string = true # false/true +# 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 -# The number of spaces to indent multi-line XML strings. -# Requires indent_align_string=True -indent_xml_string = 0 # number +# 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 -# Spaces to indent '{' from level -indent_brace = 0 # number +# 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 -# Whether braces are indented to the body level -indent_braces = false # false/true +# 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 -# Disabled indenting function braces if indent_braces is true -indent_braces_no_func = false # false/true +# 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 -# Disabled indenting class braces if indent_braces is true -indent_braces_no_class = false # false/true +# 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 -# Disabled indenting struct braces if indent_braces is true -indent_braces_no_struct = false # false/true +# Add or remove space before assignment operator '=', '+=', etc. +# +# Overrides sp_assign. +sp_before_assign = ignore # ignore/add/remove/force/not_defined -# Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. -indent_brace_parent = false # false/true +# Add or remove space after assignment operator '=', '+=', etc. +# +# Overrides sp_assign. +sp_after_assign = ignore # ignore/add/remove/force/not_defined -# Whether the 'namespace' body is indented -indent_namespace = false # false/true +# Add or remove space in 'NS_ENUM ('. +sp_enum_paren = ignore # ignore/add/remove/force/not_defined -# The number of spaces to indent a namespace block -indent_namespace_level = 0 # number +# Add or remove space around assignment '=' in enum. +sp_enum_assign = ignore # ignore/add/remove/force/not_defined -# If the body of the namespace is longer than this number, it won't be indented. -# Requires indent_namespace=true. Default=0 (no limit) -indent_namespace_limit = 0 # number +# Add or remove space before assignment '=' in enum. +# +# Overrides sp_enum_assign. +sp_enum_before_assign = ignore # ignore/add/remove/force/not_defined -# Whether the 'extern "C"' body is indented -indent_extern = false # false/true +# Add or remove space after assignment '=' in enum. +# +# Overrides sp_enum_assign. +sp_enum_after_assign = ignore # ignore/add/remove/force/not_defined -# Whether the 'class' body is indented -indent_class = true # false/true +# Add or remove space around assignment ':' in enum. +sp_enum_colon = ignore # ignore/add/remove/force/not_defined -# Whether to indent the stuff after a leading class colon -indent_class_colon = true # false/true +# Add or remove space around preprocessor '##' concatenation operator. +# +# Default: add +sp_pp_concat = ignore # ignore/add/remove/force/not_defined -# Virtual indent from the ':' for member initializers. Default is 2 -indent_ctor_init_leading = 2 # number +# Add or remove space after preprocessor '#' stringify operator. +# Also affects the '#@' charizing operator. +sp_pp_stringify = ignore # ignore/add/remove/force/not_defined -# Additional indenting for constructor initializer list -indent_ctor_init = 0 # number +# 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 -# False=treat 'else\nif' as 'else if' for indenting purposes -# True=indent the 'if' one level -indent_else_if = false # false/true +# Add or remove space around boolean operators '&&' and '||'. +sp_bool = ignore # ignore/add/remove/force/not_defined -# Amount to indent variable declarations after an open brace. neg=relative, pos=absolute -indent_var_def_blk = 0 # number +# Add or remove space around compare operator '<', '>', '==', etc. +sp_compare = ignore # ignore/add/remove/force/not_defined -# Indent continued variable declarations instead of aligning. -indent_var_def_cont = false # false/true +# Add or remove space inside '(' and ')'. +sp_inside_paren = ignore # ignore/add/remove/force/not_defined -# True: force indentation of function definition to start in column 1 -# False: use the default behavior -indent_func_def_force_col1 = false # false/true +# Add or remove space between nested parentheses, i.e. '((' vs. ') )'. +sp_paren_paren = ignore # ignore/add/remove/force/not_defined -# True: indent continued function call parameters one indent level -# False: align parameters under the open paren -indent_func_call_param = false # false/true +# Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('. +sp_cparen_oparen = ignore # ignore/add/remove/force/not_defined -# Same as indent_func_call_param, but for function defs -indent_func_def_param = false # false/true +# Whether to balance spaces inside nested parentheses. +sp_balance_nested_parens = false # true/false -# Same as indent_func_call_param, but for function protos -indent_func_proto_param = false # false/true +# Add or remove space between ')' and '{'. +sp_paren_brace = ignore # ignore/add/remove/force/not_defined -# Same as indent_func_call_param, but for class declarations -indent_func_class_param = false # false/true +# Add or remove space between nested braces, i.e. '{{' vs. '{ {'. +sp_brace_brace = ignore # ignore/add/remove/force/not_defined -# Same as indent_func_call_param, but for class variable constructors -indent_func_ctor_var_param = false # false/true +# Add or remove space before pointer star '*'. +sp_before_ptr_star = force # ignore/add/remove/force/not_defined -# Same as indent_func_call_param, but for templates -indent_template_param = false # false/true +# 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 -# Double the indent for indent_func_xxx_param options -indent_func_param_double = false # false/true +# Add or remove space between pointer stars '*', as in 'int ***a;'. +sp_between_ptr_star = ignore # ignore/add/remove/force/not_defined -# Indentation column for standalone 'const' function decl/proto qualifier -indent_func_const = 0 # number +# Add or remove space after pointer star '*', if followed by a word. +# +# Overrides sp_type_func. +sp_after_ptr_star = ignore # ignore/add/remove/force/not_defined -# Indentation column for standalone 'throw' function decl/proto qualifier -indent_func_throw = 0 # number +# Add or remove space after pointer caret '^', if followed by a word. +sp_after_ptr_block_caret = ignore # ignore/add/remove/force/not_defined -# The number of spaces to indent a continued '->' or '.' -# Usually set to 0, 1, or indent_columns. -indent_member = 0 # number +# Add or remove space after pointer star '*', if followed by a qualifier. +sp_after_ptr_star_qualifier = ignore # ignore/add/remove/force/not_defined -# Spaces to indent single line ('//') comments on lines before code -indent_sing_line_comments = 0 # number +# 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 = ignore # ignore/add/remove/force/not_defined -# If set, will indent trailing single line ('//') comments relative -# to the code instead of trying to keep the same absolute column -indent_relative_single_line_comments = false # false/true +# 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 -# Spaces to indent 'case' from 'switch' -# Usually 0 or indent_columns. -indent_switch_case = 0 # number +# 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 -# Spaces to shift the 'case' line, without affecting any other lines -# Usually 0. -indent_case_shift = 0 # number +# Add or remove space before a pointer star '*', if followed by a function +# prototype or function definition. +sp_before_ptr_star_func = ignore # ignore/add/remove/force/not_defined -# Spaces to indent '{' from 'case'. -# By default, the brace will appear under the 'c' in case. -# Usually set to 0 or indent_columns. -indent_case_brace = 0 # number +# 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 -# Whether to indent comments found in first column -indent_col1_comment = true # false/true +# Add or remove space before a reference sign '&'. +sp_before_byref = ignore # ignore/add/remove/force/not_defined -# How to indent goto labels -# >0 : absolute column where 1 is the leftmost column -# <=0 : subtract from brace indent -indent_label = 0 # number +# 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 -# Same as indent_label, but for access specifiers that are followed by a colon -indent_access_spec = 1 # number +# 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 -# Indent the code after an access specifier by one level. -# If set, this option forces 'indent_access_spec=0' -indent_access_spec_body = false # false/true +# 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 -# If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) -indent_paren_nl = false # false/true +# 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 -# Controls the indent of a close paren after a newline. -# 0: Indent to body level -# 1: Align under the open paren -# 2: Indent to the brace level -indent_paren_close = 0 # number +# 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 +# as 'force'. +# +# This also affects some other instances of space following a type that are +# not covered by other options; for example, between the return type and +# parenthesis of a function type template argument, between the type and +# parenthesis of an array parameter, or between 'decltype(...)' and the +# following word. +# +# Default: force +sp_after_type = ignore # ignore/add/remove/force/not_defined -# Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren -indent_comma_paren = false # false/true +# Add or remove space between 'decltype(...)' and word, +# brace or function call. +sp_after_decltype = ignore # ignore/add/remove/force/not_defined -# Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren -indent_bool_paren = false # false/true +# (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 -# If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones -indent_first_bool_expr = false # false/true +# 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 -# If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) -indent_square_nl = false # false/true +# Add or remove space before '<'. +sp_before_angle = ignore # ignore/add/remove/force/not_defined -# Don't change the relative indent of ESQL/C 'EXEC SQL' bodies -indent_preserve_sql = false # false/true +# Add or remove space inside '<' and '>'. +sp_inside_angle = ignore # ignore/add/remove/force/not_defined -# Align continued statements at the '='. Default=True -# If FALSE or the '=' is followed by a newline, the next line is indent one tab. -indent_align_assign = true # false/true +# Add or remove space inside '<>'. +sp_inside_angle_empty = ignore # ignore/add/remove/force/not_defined -# Indent OC blocks at brace level instead of usual rules. -indent_oc_block = false # false/true +# Add or remove space between '>' and ':'. +sp_angle_colon = ignore # ignore/add/remove/force/not_defined -# Indent OC blocks in a message relative to the parameter name. -# 0=use indent_oc_block rules, 1+=spaces to indent -indent_oc_block_msg = 0 # number +# Add or remove space after '>'. +sp_after_angle = ignore # ignore/add/remove/force/not_defined -# Minimum indent for subsequent parameters -indent_oc_msg_colon = 0 # number +# Add or remove space between '>' and '(' as found in 'new List<byte>(foo);'. +sp_angle_paren = ignore # ignore/add/remove/force/not_defined +# Add or remove space between '>' and '()' as found in 'new List<byte>();'. +sp_angle_paren_empty = ignore # ignore/add/remove/force/not_defined + +# 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 + +# Add or remove space between '>' and '>' in '>>' (template stuff). # -# Spacing options -# +# Default: add +sp_angle_shift = ignore # ignore/add/remove/force/not_defined -# Add or remove space around arithmetic operator '+', '-', '/', '*', etc -sp_arith = 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. +sp_permit_cpp11_shift = false # true/false -# Add or remove space around assignment operator '=', '+=', etc -sp_assign = add # ignore/add/remove/force +# Add or remove space before '(' of control statements ('if', 'for', 'switch', +# 'while', etc.). +sp_before_sparen = ignore # ignore/add/remove/force/not_defined -# Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign -sp_cpp_lambda_assign = ignore # ignore/add/remove/force +# Add or remove space inside '(' and ')' of control statements. +sp_inside_sparen = ignore # ignore/add/remove/force/not_defined -# Add or remove space after the capture specification in C++11 lambda. -sp_cpp_lambda_paren = ignore # ignore/add/remove/force +# Add or remove space after '(' of control statements. +# +# Overrides sp_inside_sparen. +sp_inside_sparen_open = remove # ignore/add/remove/force/not_defined -# Add or remove space around assignment operator '=' in a prototype -sp_assign_default = ignore # ignore/add/remove/force +# Add or remove space before ')' of control statements. +# +# Overrides sp_inside_sparen. +sp_inside_sparen_close = ignore # ignore/add/remove/force/not_defined -# Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign. -sp_before_assign = ignore # ignore/add/remove/force +# Add or remove space between '((' or '))' of control statements. +sp_sparen_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign. -sp_after_assign = ignore # ignore/add/remove/force +# Add or remove space after ')' of control statements. +sp_after_sparen = ignore # ignore/add/remove/force/not_defined -# Add or remove space around assignment '=' in enum -sp_enum_assign = add # ignore/add/remove/force +# Add or remove space between ')' and '{' of control statements. +sp_sparen_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove space before assignment '=' in enum. Overrides sp_enum_assign. -sp_enum_before_assign = ignore # ignore/add/remove/force +# Add or remove space between 'do' and '{'. +sp_do_brace_open = ignore # ignore/add/remove/force/not_defined -# Add or remove space after assignment '=' in enum. Overrides sp_enum_assign. -sp_enum_after_assign = ignore # ignore/add/remove/force +# Add or remove space between '}' and 'while'. +sp_brace_close_while = ignore # ignore/add/remove/force/not_defined -# Add or remove space around preprocessor '##' concatenation operator. Default=Add -sp_pp_concat = add # 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 -# Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator. -sp_pp_stringify = add # ignore/add/remove/force +# (D) Add or remove space between 'invariant' and '('. +sp_invariant_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'. -sp_before_pp_stringify = 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 -# Add or remove space around boolean operators '&&' and '||' -sp_bool = add # 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 -# Add or remove space around compare operator '<', '>', '==', etc -sp_compare = add # ignore/add/remove/force +# Add or remove space before ';'. +# +# Default: remove +sp_before_semi = ignore # ignore/add/remove/force/not_defined -# Add or remove space inside '(' and ')' -sp_inside_paren = remove # ignore/add/remove/force +# Add or remove space before ';' in non-empty 'for' statements. +sp_before_semi_for = ignore # ignore/add/remove/force/not_defined -# Add or remove space between nested parens -sp_paren_paren = 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 = ignore # ignore/add/remove/force/not_defined -# Whether to balance spaces inside nested parens -sp_balance_nested_parens = false # false/true +# 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 = ignore # ignore/add/remove/force/not_defined -# Add or remove space between ')' and '{' -sp_paren_brace = add # ignore/add/remove/force +# Add or remove space after ';', except when followed by a comment. +# +# Default: add +sp_after_semi = ignore # ignore/add/remove/force/not_defined -# Add or remove space before pointer star '*' -sp_before_ptr_star = add # ignore/add/remove/force +# Add or remove space after ';' in non-empty 'for' statements. +# +# Default: force +sp_after_semi_for = ignore # ignore/add/remove/force/not_defined -# 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 = add # 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 = ignore # ignore/add/remove/force/not_defined -# Add or remove space between pointer stars '*' -sp_between_ptr_star = remove # ignore/add/remove/force +# Add or remove space before '[' (except '[]'). +sp_before_square = ignore # ignore/add/remove/force/not_defined -# Add or remove space after pointer star '*', if followed by a word. -sp_after_ptr_star = remove # ignore/add/remove/force +# Add or remove space before '[' for a variable definition. +# +# Default: remove +sp_before_vardef_square = ignore # ignore/add/remove/force/not_defined -# Add or remove space after a pointer star '*', if followed by a func proto/def. -sp_after_ptr_star_func = add # ignore/add/remove/force +# Add or remove space before '[' for asm block. +sp_before_square_asm_block = ignore # ignore/add/remove/force/not_defined -# Add or remove space after a pointer star '*', if followed by an open paren (function types). -sp_ptr_star_paren = ignore # ignore/add/remove/force +# Add or remove space before '[]'. +sp_before_squares = ignore # ignore/add/remove/force/not_defined -# Add or remove space before a pointer star '*', if followed by a func proto/def. -sp_before_ptr_star_func = remove # 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 -# Add or remove space before a reference sign '&' -sp_before_byref = remove # ignore/add/remove/force +# Add or remove space inside a non-empty '[' and ']'. +sp_inside_square = ignore # ignore/add/remove/force/not_defined -# 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 = remove # ignore/add/remove/force +# Add or remove space inside '[]'. +sp_inside_square_empty = ignore # ignore/add/remove/force/not_defined -# Add or remove space after reference sign '&', if followed by a word. -sp_after_byref = add # 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 -# Add or remove space after a reference sign '&', if followed by a func proto/def. -sp_after_byref_func = add # 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 -# Add or remove space before a reference sign '&', if followed by a func proto/def. -sp_before_byref_func = remove # 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 -# Add or remove space between type and word. Default=Force -sp_after_type = add # ignore/add/remove/force +# (C#) Add or remove space between ',' and ']' in multidimensional array type +# like 'int[,,]'. +sp_after_mdatype_commas = ignore # ignore/add/remove/force/not_defined -# Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('. -sp_before_template_paren = ignore # ignore/add/remove/force +# (C#) Add or remove space between '[' and ',' in multidimensional array type +# like 'int[,,]'. +sp_before_mdatype_commas = ignore # ignore/add/remove/force/not_defined -# Add or remove space in 'template <' vs 'template<'. -# If set to ignore, sp_before_angle is used. -sp_template_angle = ignore # ignore/add/remove/force +# (C#) Add or remove space between ',' in multidimensional array type +# like 'int[,,]'. +sp_between_mdatype_commas = ignore # ignore/add/remove/force/not_defined -# Add or remove space before '<>' -sp_before_angle = remove # ignore/add/remove/force +# Add or remove space between an open parenthesis and comma, +# i.e. '(,' vs. '( ,'. +# +# Default: force +sp_paren_comma = ignore # ignore/add/remove/force/not_defined -# Add or remove space inside '<' and '>' -sp_inside_angle = remove # ignore/add/remove/force +# Add or remove space after the variadic '...' when preceded by a +# non-punctuator. +# The value REMOVE will be overriden with FORCE +sp_after_ellipsis = ignore # ignore/add/remove/force/not_defined -# Add or remove space after '<>' -sp_after_angle = remove # ignore/add/remove/force +# Add or remove space before the variadic '...' when preceded by a +# non-punctuator. +# The value REMOVE will be overriden with FORCE +sp_before_ellipsis = ignore # ignore/add/remove/force/not_defined -# Add or remove space between '<>' and '(' as found in 'new List<byte>();' -sp_angle_paren = remove # ignore/add/remove/force +# Add or remove space between a type and '...'. +sp_type_ellipsis = ignore # ignore/add/remove/force/not_defined -# Add or remove space between '<>' and a word as in 'List<byte> m;' -sp_angle_word = remove # ignore/add/remove/force +# (D) Add or remove space between a type and '?'. +sp_type_question = ignore # ignore/add/remove/force/not_defined -# Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add -sp_angle_shift = add # ignore/add/remove/force +# Add or remove space between ')' and '...'. +sp_paren_ellipsis = ignore # ignore/add/remove/force/not_defined -# Permit removal of the space between '>>' in 'foo<bar<int> >' (C++11 only). Default=False -# sp_angle_shift cannot remove the space without this option. -sp_permit_cpp11_shift = false # false/true +# Add or remove space between ')' and a qualifier such as 'const'. +sp_paren_qualifier = ignore # ignore/add/remove/force/not_defined -# Add or remove space before '(' of 'if', 'for', 'switch', and 'while' -sp_before_sparen = add # ignore/add/remove/force +# Add or remove space between ')' and 'noexcept'. +sp_paren_noexcept = ignore # ignore/add/remove/force/not_defined -# Add or remove space inside if-condition '(' and ')' -sp_inside_sparen = remove # ignore/add/remove/force +# Add or remove space after class ':'. +sp_after_class_colon = ignore # ignore/add/remove/force/not_defined -# Add or remove space before if-condition ')'. Overrides sp_inside_sparen. -sp_inside_sparen_close = ignore # ignore/add/remove/force +# Add or remove space before class ':'. +sp_before_class_colon = ignore # ignore/add/remove/force/not_defined -# Add or remove space before if-condition '('. Overrides sp_inside_sparen. -sp_inside_sparen_open = ignore # ignore/add/remove/force +# Add or remove space after class constructor ':'. +sp_after_constr_colon = ignore # ignore/add/remove/force/not_defined -# Add or remove space after ')' of 'if', 'for', 'switch', and 'while' -sp_after_sparen = ignore # ignore/add/remove/force +# Add or remove space before class constructor ':'. +sp_before_constr_colon = ignore # ignore/add/remove/force/not_defined -# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' -sp_sparen_brace = add # ignore/add/remove/force +# Add or remove space before case ':'. +# +# Default: remove +sp_before_case_colon = remove # ignore/add/remove/force/not_defined -# Add or remove space between 'invariant' and '(' in the D language. -sp_invariant_paren = ignore # ignore/add/remove/force +# Add or remove space between 'operator' and operator sign. +sp_after_operator = ignore # ignore/add/remove/force/not_defined -# Add or remove space after the ')' in 'invariant (C) c' in the D language. -sp_after_invariant_paren = 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 -# Add or remove space before empty statement ';' on 'if', 'for' and 'while' -sp_special_semi = remove # 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 -# Add or remove space before ';'. Default=Remove -sp_before_semi = remove # 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 -# Add or remove space before ';' in non-empty 'for' statements -sp_before_semi_for = remove # ignore/add/remove/force +# Add or remove spaces inside cast parentheses. +sp_inside_paren_cast = ignore # ignore/add/remove/force/not_defined -# Add or remove space before a semicolon of an empty part of a for statement. -sp_before_semi_for_empty = 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 -# Add or remove space after ';', except when followed by a comment. Default=Add -sp_after_semi = add # ignore/add/remove/force +# Add or remove space between 'sizeof' and '('. +sp_sizeof_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space after ';' in non-empty 'for' statements. Default=Force -sp_after_semi_for = force # ignore/add/remove/force +# Add or remove space between 'sizeof' and '...'. +sp_sizeof_ellipsis = ignore # ignore/add/remove/force/not_defined -# Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; <here> ). -sp_after_semi_for_empty = remove # ignore/add/remove/force +# Add or remove space between 'sizeof...' and '('. +sp_sizeof_ellipsis_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space before '[' (except '[]') -sp_before_square = remove # ignore/add/remove/force +# Add or remove space between 'decltype' and '('. +sp_decltype_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space before '[]' -sp_before_squares = remove # ignore/add/remove/force +# (Pawn) Add or remove space after the tag keyword. +sp_after_tag = ignore # ignore/add/remove/force/not_defined -# Add or remove space inside a non-empty '[' and ']' -sp_inside_square = remove # ignore/add/remove/force +# Add or remove space inside enum '{' and '}'. +sp_inside_braces_enum = ignore # ignore/add/remove/force/not_defined -# Add or remove space after ',' -sp_after_comma = add # ignore/add/remove/force +# Add or remove space inside struct/union '{' and '}'. +sp_inside_braces_struct = ignore # ignore/add/remove/force/not_defined -# Add or remove space before ',' -sp_before_comma = remove # 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 -# Add or remove space between an open paren and comma: '(,' vs '( ,' -sp_paren_comma = force # ignore/add/remove/force +# Add or remove space after open brace in an unnamed temporary +# direct-list-initialization. +sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force/not_defined -# Add or remove space before the variadic '...' when preceded by a non-punctuator -sp_before_ellipsis = ignore # ignore/add/remove/force +# Add or remove space before close brace in an unnamed temporary +# direct-list-initialization. +sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force/not_defined -# Add or remove space after class ':' -sp_after_class_colon = add # ignore/add/remove/force +# Add or remove space inside an unnamed temporary direct-list-initialization. +sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined -# Add or remove space before class ':' -sp_before_class_colon = add # ignore/add/remove/force +# Add or remove space inside '{' and '}'. +sp_inside_braces = add # ignore/add/remove/force/not_defined -# Add or remove space before case ':'. Default=Remove -sp_before_case_colon = remove # ignore/add/remove/force +# Add or remove space inside '{}'. +sp_inside_braces_empty = ignore # ignore/add/remove/force/not_defined -# Add or remove space between 'operator' and operator sign -sp_after_operator = remove # ignore/add/remove/force +# Add or remove space around trailing return operator '->'. +sp_trailing_return = ignore # ignore/add/remove/force/not_defined -# Add or remove space between the operator symbol and the open paren, as in 'operator ++(' -sp_after_operator_sym = remove # 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 -# 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 +# 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 -# Add or remove spaces inside cast parens -sp_inside_paren_cast = remove # ignore/add/remove/force +# Add or remove space between function name and '(' on function declaration. +sp_func_proto_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)' -sp_cpp_cast_paren = remove # ignore/add/remove/force +# Add or remove space between function name and '()' on function declaration +# without parameters. +sp_func_proto_paren_empty = ignore # ignore/add/remove/force/not_defined -# Add or remove space between 'sizeof' and '(' -sp_sizeof_paren = remove # 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 -# Add or remove space after the tag keyword (Pawn) -sp_after_tag = 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 -# Add or remove space inside enum '{' and '}' -sp_inside_braces_enum = add # ignore/add/remove/force +# Add or remove space between function name and '()' on function definition +# without parameters. +sp_func_def_paren_empty = ignore # ignore/add/remove/force/not_defined -# Add or remove space inside struct/union '{' and '}' -sp_inside_braces_struct = add # 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 = ignore # ignore/add/remove/force/not_defined -# Add or remove space inside '{' and '}' -sp_inside_braces = add # ignore/add/remove/force +# Add or remove space inside function '(' and ')'. +sp_inside_fparen = ignore # ignore/add/remove/force/not_defined -# Add or remove space inside '{}' -sp_inside_braces_empty = remove # ignore/add/remove/force +# Add or remove space inside the first parentheses in a function type, as in +# 'void (*x)(...)'. +sp_inside_tparen = ignore # ignore/add/remove/force/not_defined -# Add or remove space between return type and function name -# A minimum of 1 is forced except for pointer return types. -sp_type_func = add # ignore/add/remove/force +# Add or remove space between the ')' and '(' in a function type, as in +# 'void (*x)(...)'. +sp_after_tparen_close = ignore # ignore/add/remove/force/not_defined -# Add or remove space between function name and '(' on function declaration -sp_func_proto_paren = 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 -# Add or remove space between function name and '(' on function definition -sp_func_def_paren = remove # ignore/add/remove/force +# Add or remove space between ')' and '{' of function. +sp_fparen_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove space inside empty function '()' -sp_inside_fparens = remove # 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 -# Add or remove space inside function '(' and ')' -sp_inside_fparen = remove # 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 -# Add or remove space inside the first parens in the function type: 'void (*x)(...)' -sp_inside_tparen = ignore # ignore/add/remove/force +# Add or remove space between function name and '(' on function calls. +sp_func_call_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove between the parens in the function type: 'void (*x)(...)' -sp_after_tparen_close = ignore # 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 -# Add or remove space between ']' and '(' when part of a function call. -sp_square_fparen = remove # 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 -# Add or remove space between ')' and '{' of function -sp_fparen_brace = add # 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 -# Add or remove space between function name and '(' on function calls -sp_func_call_paren = remove # 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 -# 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 +# Add or remove space between a constructor/destructor and the open +# parenthesis. +sp_func_class_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space between the user function name and '(' on function calls -# You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file. -sp_func_call_user_paren = remove # 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 -# Add or remove space between a constructor/destructor and the open paren -sp_func_class_paren = remove # ignore/add/remove/force +# Add or remove space between 'return' and '('. +sp_return_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space between 'return' and '(' -sp_return_paren = add # ignore/add/remove/force +# Add or remove space between 'return' and '{'. +sp_return_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove space between '__attribute__' and '(' -sp_attribute_paren = remove # ignore/add/remove/force +# Add or remove space between '__attribute__' and '('. +sp_attribute_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space between 'defined' and '(' in '#if defined (FOO)' -sp_defined_paren = remove # ignore/add/remove/force +# Add or remove space between 'defined' and '(' in '#if defined (FOO)'. +sp_defined_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space between 'throw' and '(' in 'throw (something)' -sp_throw_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 -# Add or remove space between 'throw' and anything other than '(' as in '@throw [...];' -sp_after_throw = 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 -# Add or remove space between 'catch' and '(' in 'catch (something) { }' +# 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 +sp_catch_paren = ignore # ignore/add/remove/force/not_defined -# Add or remove space between 'version' and '(' in 'version (something) { }' (D language) -# If set to ignore, sp_before_sparen is used. -sp_version_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 -# Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language) -# If set to ignore, sp_before_sparen is used. -sp_scope_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 + +# (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 + +# (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 + +# (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 + +# Add or remove space between 'super' and '(' in 'super (something)'. +# +# Default: remove +sp_super_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'this' and '(' in 'this (something)'. +# +# Default: remove +sp_this_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a macro name and its definition. +sp_macro = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a macro function ')' and its definition. +sp_macro_func = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'else' and '{' if on the same line. +sp_else_brace = add # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and 'else' if on the same line. +sp_brace_else = add # ignore/add/remove/force/not_defined -# Add or remove space between macro and value -sp_macro = add # ignore/add/remove/force +# Add or remove space between '}' and the name of a typedef on the same line. +sp_brace_typedef = ignore # ignore/add/remove/force/not_defined -# Add or remove space between macro function ')' and value -sp_macro_func = remove # 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 -# Add or remove space between 'else' and '{' if on the same line -sp_else_brace = add # 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 -# Add or remove space between '}' and 'else' if on the same line -sp_brace_else = add # 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 -# Add or remove space between '}' and the name of a typedef on the same line -sp_brace_typedef = add # 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 -# Add or remove space between 'catch' and '{' if on the same line -sp_catch_brace = add # 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 -# Add or remove space between '}' and 'catch' if on the same line -sp_brace_catch = add # 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 -# Add or remove space between 'finally' and '{' if on the same line -sp_finally_brace = add # 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 -# Add or remove space between '}' and 'finally' if on the same line -sp_brace_finally = add # 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 -# Add or remove space between 'try' and '{' if on the same line -sp_try_brace = add # 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 -# Add or remove space between get/set and '{' if on the same line -sp_getset_brace = add # ignore/add/remove/force +# Add or remove space between a variable and '{' for a namespace. +# +# Default: add +sp_word_brace_ns = ignore # ignore/add/remove/force/not_defined -# Add or remove space before the '::' operator -sp_before_dc = remove # ignore/add/remove/force +# Add or remove space before the '::' operator. +sp_before_dc = ignore # ignore/add/remove/force/not_defined -# Add or remove space after the '::' operator -sp_after_dc = remove # ignore/add/remove/force +# Add or remove space after the '::' operator. +sp_after_dc = ignore # ignore/add/remove/force/not_defined -# Add or remove around the D named array initializer ':' operator -sp_d_array_colon = 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 -# Add or remove space after the '!' (not) operator. Default=Remove -sp_not = remove # ignore/add/remove/force +# Add or remove space after the '!' (not) unary operator. +# +# Default: remove +sp_not = ignore # ignore/add/remove/force/not_defined -# Add or remove space after the '~' (invert) operator. Default=Remove -sp_inv = remove # ignore/add/remove/force +# Add or remove space after the '~' (invert) unary operator. +# +# Default: remove +sp_inv = ignore # ignore/add/remove/force/not_defined -# Add or remove space after the '&' (address-of) operator. Default=Remove -# This does not affect the spacing after a '&' that is part of a type. -sp_addr = 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 -# Add or remove space around the '.' or '->' operators. Default=Remove -sp_member = remove # ignore/add/remove/force +# Add or remove space around the '.' or '->' operators. +# +# Default: remove +sp_member = ignore # ignore/add/remove/force/not_defined -# Add or remove space after the '*' (dereference) operator. Default=Remove -# This does not affect the spacing after a '*' that is part of a type. -sp_deref = 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 = ignore # ignore/add/remove/force/not_defined -# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove -sp_sign = remove # ignore/add/remove/force +# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. +# +# Default: remove +sp_sign = ignore # ignore/add/remove/force/not_defined -# Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove -sp_incdec = 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 = ignore # ignore/add/remove/force/not_defined -# Add or remove space before a backslash-newline at the end of a line. Default=Add -sp_before_nl_cont = 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 = ignore # ignore/add/remove/force/not_defined -# Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;' -sp_after_oc_scope = remove # 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 -# Add or remove space after the colon in message specs -# '-(int) f:(int) x;' vs '-(int) f: (int) x;' -sp_after_oc_colon = remove # 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 -# Add or remove space before the colon in message specs -# '-(int) f: (int) x;' vs '-(int) f : (int) x;' -sp_before_oc_colon = remove # 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 -# 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 +# (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 -# 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 +# (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 -# Add or remove space after the colon in message specs -# '[object setValue:1];' vs '[object setValue: 1];' -sp_after_send_oc_colon = add # 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 -# Add or remove space before the colon in message specs -# '[object setValue:1];' vs '[object setValue :1];' -sp_before_send_oc_colon = remove # 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 -# Add or remove space after the (type) in message specs -# '-(int)f: (int) x;' vs '-(int)f: (int)x;' -sp_after_oc_type = remove # 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 -# Add or remove space after the first (type) in message specs -# '-(int) f:(int)x;' vs '-(int)f:(int)x;' -sp_after_oc_return_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 -# Add or remove space between '@selector' and '(' -# '@selector(msgName)' vs '@selector (msgName)' -# Also applies to @protocol() constructs -sp_after_oc_at_sel = 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 -# Add or remove space between '@selector(x)' and the following word -# '@selector(foo) a:' vs '@selector(foo)a:' -sp_after_oc_at_sel_parens = 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 -# Add or remove space inside '@selector' parens -# '@selector(foo)' vs '@selector( foo )' -# Also applies to @protocol() constructs -sp_inside_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 -# Add or remove space before a block pointer caret -# '^int (int arg){...}' vs. ' ^int (int arg){...}' -sp_before_oc_block_caret = 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 -# Add or remove space after a block pointer caret -# '^int (int arg){...}' vs. '^ int (int arg){...}' -sp_after_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 -# Add or remove space between the receiver and selector in a message. -# '[receiver selector ...]' -sp_after_oc_msg_receiver = 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 -# Add or remove space after @property. -sp_after_oc_property = ignore # ignore/add/remove/force +# (OC) Add or remove space after '@property'. +sp_after_oc_property = ignore # ignore/add/remove/force/not_defined -# Add or remove space around the ':' in 'b ? t : f' -sp_cond_colon = add # 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 -# Add or remove space around the '?' in 'b ? t : f' -sp_cond_question = add # ignore/add/remove/force +# Add or remove space around the ':' in 'b ? t : f'. +sp_cond_colon = ignore # ignore/add/remove/force/not_defined -# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here. -sp_case_label = ignore # 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 + +# 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 -# Control the space around the D '..' operator. -sp_range = ignore # ignore/add/remove/force +# Add or remove space around the '?' in 'b ? t : f'. +sp_cond_question = ignore # ignore/add/remove/force/not_defined -# Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java) -sp_after_for_colon = ignore # 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 -# Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java) -sp_before_for_colon = 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 -# Control the spacing in 'extern (C)' (D) -sp_extern_paren = 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 -# Control the space after the opening of a C++ comment '// A' vs '//A' -sp_cmt_cpp_start = add # 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 -# Controls the spaces between #else or #endif and a trailing comment -sp_endif_cmt = ignore # ignore/add/remove/force +# (D) Add or remove space around the D '..' operator. +sp_range = ignore # ignore/add/remove/force/not_defined -# Controls the spaces after 'new', 'delete', and 'delete[]' -sp_after_new = 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 -# Controls the spaces before a trailing or embedded comment -sp_before_tr_emb_cmt = 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 -# Number of spaces before a trailing or embedded comment -sp_num_before_tr_emb_cmt = 0 # number +# (D) Add or remove space between 'extern' and '(' as in 'extern <here> (C)'. +sp_extern_paren = ignore # ignore/add/remove/force/not_defined -# Control space between a Java annotation and the open paren. -sp_annotation_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 +# Add or remove space in a C++ region marker comment, as in '// <here> BEGIN'. +# A region marker is defined as a comment which is not preceded by other text +# (i.e. the comment is the first non-whitespace on the line), and which starts +# with either 'BEGIN' or 'END'. # -# Code alignment (not left column spaces/tabs) +# Overrides sp_cmt_cpp_start. +sp_cmt_cpp_region = ignore # ignore/add/remove/force/not_defined + +# If true, space added with sp_cmt_cpp_start will be added after Doxygen +# sequences like '///', '///<', '//!' and '//!<'. +sp_cmt_cpp_doxygen = false # true/false + +# If true, space added with sp_cmt_cpp_start will be added after Qt translator +# or meta-data comments like '//:', '//=', and '//~'. +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 + +# Add or remove space after 'new', 'delete' and 'delete[]'. +sp_after_new = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'new' and '(' in 'new()'. +sp_between_new_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and type in 'new(foo) BAR'. +sp_after_newop_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside parenthesis of the new operator +# as in 'new(foo) BAR'. +sp_inside_newop_paren = ignore # ignore/add/remove/force/not_defined + +# 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 -# Whether to keep non-indenting tabs -align_keep_tabs = false # false/true +# 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 -# Whether to use tabs for aligning -align_with_tabs = false # false/true +# Add or remove space before a trailing or embedded comment. +sp_before_tr_emb_cmt = add # ignore/add/remove/force/not_defined -# Whether to bump out to the next tab when aligning -align_on_tabstop = false # false/true +# Number of spaces before a trailing or embedded comment. +sp_num_before_tr_emb_cmt = 2 # unsigned number -# Whether to left-align numbers -align_number_left = false # false/true +# (Java) Add or remove space between an annotation and the open parenthesis. +sp_annotation_paren = ignore # ignore/add/remove/force/not_defined -# Align variable definitions in prototypes and functions -align_func_params = true # false/true +# If true, vbrace tokens are dropped to the previous token and skipped. +sp_skip_vbrace_tokens = false # true/false -# Align parameters in single-line functions that have the same name. -# The function names must already be aligned with each other. -align_same_func_call_params = true # false/true +# Add or remove space after 'noexcept'. +sp_after_noexcept = ignore # ignore/add/remove/force/not_defined -# The span for aligning variable definitions (0=don't align) -align_var_def_span = 0 # number +# Add or remove space after '_'. +sp_vala_after_translation = ignore # ignore/add/remove/force/not_defined -# How to align the star in variable definitions. -# 0=Part of the type 'void * foo;' -# 1=Part of the variable 'void *foo;' -# 2=Dangling 'void *foo;' -align_var_def_star_style = 1 # number +# If true, a <TAB> is inserted after #define. +force_tab_after_define = false # true/false -# How to align the '&' in variable definitions. -# 0=Part of the type -# 1=Part of the variable -# 2=Dangling -align_var_def_amp_style = 2 # number +# +# Indenting options +# -# The threshold for aligning variable definitions (0=no limit) -align_var_def_thresh = 3 # number +# The number of columns to indent per level. Usually 2, 3, 4, or 8. +# +# Default: 8 +indent_columns = 2 # unsigned number -# The gap for aligning variable definitions -align_var_def_gap = 1 # number +# The continuation indent. If non-zero, this overrides the indent of '(', '[' +# and '=' continuation indents. Negative values are OK; negative value is +# absolute and not increased for each '(' or '[' level. +# +# For FreeBSD, this is set to 4. +indent_continue = 0 # number -# Whether to align the colon in struct bit fields -align_var_def_colon = true # false/true +# The continuation indent, only for class header line(s). If non-zero, this +# overrides the indent of 'class' continuation indents. +indent_continue_class_head = 0 # unsigned number -# Whether to align any attribute after the variable name -align_var_def_attribute = true # false/true +# Whether to indent empty lines (i.e. lines which contain only spaces before +# the newline character). +indent_single_newlines = false # true/false -# Whether to align inline struct/enum/union variable definitions -align_var_def_inline = true # false/true +# The continuation indent for func_*_param if they are true. If non-zero, this +# overrides the indent. +indent_param = 0 # unsigned number -# The span for aligning on '=' in assignments (0=don't align) -align_assign_span = 0 # number +# How to use tabs when indenting code. +# +# 0: Spaces only +# 1: Indent with tabs to brace level, align with spaces (default) +# 2: Indent and align with tabs, using spaces when not on a tabstop +# +# Default: 1 +indent_with_tabs = 0 # unsigned number -# The threshold for aligning on '=' in assignments (0=no limit) -align_assign_thresh = 0 # number +# Whether to indent comments that are not at a brace level with tabs on a +# tabstop. Requires indent_with_tabs=2. If false, will use spaces. +indent_cmt_with_tabs = false # true/false -# The span for aligning on '=' in enums (0=don't align) -align_enum_equ_span = 0 # number +# Whether to indent strings broken by '\' so that they line up. +indent_align_string = false # true/false -# The threshold for aligning on '=' in enums (0=no limit) -align_enum_equ_thresh = 0 # number +# The number of spaces to indent multi-line XML strings. +# Requires indent_align_string=true. +indent_xml_string = 0 # unsigned number -# The span for aligning struct/union (0=don't align) -align_var_struct_span = 0 # number +# Spaces to indent '{' from level. +indent_brace = 0 # unsigned number -# The threshold for aligning struct/union member definitions (0=no limit) -align_var_struct_thresh = 0 # number +# Whether braces are indented to the body level. +indent_braces = false # true/false -# The gap for aligning struct/union member definitions -align_var_struct_gap = 0 # number +# Whether to disable indenting function braces if indent_braces=true. +indent_braces_no_func = false # true/false -# The span for aligning struct initializer values (0=don't align) -align_struct_init_span = 0 # number +# Whether to disable indenting class braces if indent_braces=true. +indent_braces_no_class = false # true/false -# The minimum space between the type and the synonym of a typedef -align_typedef_gap = 0 # number +# Whether to disable indenting struct braces if indent_braces=true. +indent_braces_no_struct = false # true/false -# The span for aligning single-line typedefs (0=don't align) -align_typedef_span = 0 # number +# Whether to indent based on the size of the brace parent, +# i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. +indent_brace_parent = false # true/false -# How to align typedef'd functions with other typedefs -# 0: Don't mix them at all -# 1: align the open paren with the types -# 2: align the function type name with the other type names -align_typedef_func = 0 # number +# Whether to indent based on the open parenthesis instead of the open brace +# in '({\n'. +indent_paren_open_brace = false # true/false -# Controls the positioning of the '*' in typedefs. Just try it. -# 0: Align on typedef type, ignore '*' -# 1: The '*' is part of type name: typedef int *pint; -# 2: The '*' is part of the type, but dangling: typedef int *pint; -align_typedef_star_style = 2 # number +# (C#) Whether to indent the brace of a C# delegate by another level. +indent_cs_delegate_brace = false # true/false -# Controls the positioning of the '&' in typedefs. Just try it. -# 0: Align on typedef type, ignore '&' -# 1: The '&' is part of type name: typedef int &pint; -# 2: The '&' is part of the type, but dangling: typedef int &pint; -align_typedef_amp_style = 2 # number +# (C#) Whether to indent a C# delegate (to handle delegates with no brace) by +# another level. +indent_cs_delegate_body = false # true/false -# The span for aligning comments that end lines (0=don't align) -align_right_cmt_span = 4 # number +# Whether to indent the body of a 'namespace'. +indent_namespace = false # true/false -# If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment -align_right_cmt_mix = false # false/true +# Whether to indent only the first namespace, and not any nested namespaces. +# Requires indent_namespace=true. +indent_namespace_single_indent = false # true/false -# If a trailing comment is more than this number of columns away from the text it follows, -# it will qualify for being aligned. This has to be > 0 to do anything. -align_right_cmt_gap = 0 # number +# The number of spaces to indent a namespace block. +# If set to zero, use the value indent_columns +indent_namespace_level = 0 # unsigned number -# Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore) -align_right_cmt_at_col = 1 # number +# If the body of the namespace is longer than this number, it won't be +# indented. Requires indent_namespace=true. 0 means no limit. +indent_namespace_limit = 0 # unsigned number -# The span for aligning function prototypes (0=don't align) -align_func_proto_span = 0 # number +# Whether the 'extern "C"' body is indented. +indent_extern = false # true/false -# Minimum gap between the return type and the function name. -align_func_proto_gap = 0 # number +# Whether the 'class' body is indented. +indent_class = false # true/false -# Align function protos on the 'operator' keyword instead of what follows -align_on_operator = true # false/true +# Additional indent before the leading base class colon. +# Negative values decrease indent down to the first column. +# Requires a newline break before colon (see pos_class_colon +# and nl_class_colon) +indent_before_class_colon = 0 # number -# Whether to mix aligning prototype and variable declarations. -# If true, align_var_def_XXX options are used instead of align_func_proto_XXX options. -align_mix_var_proto = false # false/true +# Whether to indent the stuff after a leading base class colon. +indent_class_colon = false # true/false -# Align single-line functions with function prototypes, uses align_func_proto_span -align_single_line_func = true # false/true +# Whether to indent based on a class colon instead of the stuff after the +# colon. Requires indent_class_colon=true. +indent_class_on_colon = false # true/false -# Aligning the open brace of single-line functions. -# Requires align_single_line_func=true, uses align_func_proto_span -align_single_line_brace = true # false/true +# Whether to indent the stuff after a leading class initializer colon. +indent_constr_colon = false # true/false -# Gap for align_single_line_brace. -align_single_line_brace_gap = 0 # number +# Virtual indent from the ':' for member initializers. +# +# Default: 2 +indent_ctor_init_leading = 2 # unsigned number + +# Additional indent for constructor initializer list. +# Negative values decrease indent down to the first column. +indent_ctor_init = 0 # number + +# Whether to indent 'if' following 'else' as a new block under the 'else'. +# If false, 'else\nif' is treated as 'else if' for indenting purposes. +indent_else_if = false # true/false + +# Amount to indent variable declarations after a open brace. +# +# <0: Relative +# >=0: Absolute +indent_var_def_blk = 0 # number + +# Whether to indent continued variable declarations instead of aligning. +indent_var_def_cont = false # true/false + +# Whether to indent continued shift expressions ('<<' and '>>') instead of +# aligning. Set align_left_shift=false when enabling this. +indent_shift = false # true/false + +# Whether to force indentation of function definitions to start in column 1. +indent_func_def_force_col1 = false # true/false + +# Whether to indent continued function call parameters one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_call_param = false # true/false -# The span for aligning ObjC msg spec (0=don't align) -align_oc_msg_spec_span = 0 # number +# Whether to indent continued function definition parameters one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_def_param = false # true/false -# Whether to align macros wrapped with a backslash and a newline. -# This will not work right if the macro contains a multi-line comment. -align_nl_cont = true # false/true +# for function definitions, only if indent_func_def_param is false +# Allows to align params when appropriate and indent them when not +# behave as if it was true if paren position is more than this value +# if paren position is more than the option value +indent_func_def_param_paren_pos_threshold = 0 # unsigned number -# # Align macro functions and variables together -align_pp_define_together = false # false/true +# Whether to indent continued function call prototype one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_proto_param = false # true/false -# The minimum space between label and value of a preprocessor define -align_pp_define_gap = 0 # number +# Whether to indent continued function call declaration one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_class_param = false # true/false -# The span for aligning on '#define' bodies (0=don't align) -align_pp_define_span = 0 # number +# Whether to indent continued class variable constructors one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_ctor_var_param = false # true/false -# Align lines that start with '<<' with previous '<<'. Default=true -align_left_shift = true # false/true +# Whether to indent continued template parameter list one indent level, +# rather than aligning parameters under the open parenthesis. +indent_template_param = false # true/false -# Span for aligning parameters in an Obj-C message call on the ':' (0=don't align) -align_oc_msg_colon_span = 0 # number +# Double the indent for indent_func_xxx_param options. +# Use both values of the options indent_columns and indent_param. +indent_func_param_double = false # true/false -# If true, always align with the first parameter, even if it is too short. -align_oc_msg_colon_first = false # false/true +# Indentation column for standalone 'const' qualifier on a function +# prototype. +indent_func_const = 0 # unsigned number -# Aligning parameters in an Obj-C '+' or '-' declaration on the ':' -align_oc_decl_colon = false # false/true +# Indentation column for standalone 'throw' qualifier on a function +# prototype. +indent_func_throw = 0 # unsigned number + +# How to indent within a macro followed by a brace on the same line +# This allows reducing the indent in macros that have (for example) +# `do { ... } while (0)` blocks bracketing them. +# +# true: add an indent for the brace on the same line as the macro +# false: do not add an indent for the brace on the same line as the macro +# +# Default: true +indent_macro_brace = false # true/false + +# The number of spaces to indent a continued '->' or '.'. +# Usually set to 0, 1, or indent_columns. +indent_member = 0 # unsigned number + +# Whether lines broken at '.' or '->' should be indented by a single indent. +# The indent_member option will not be effective if this is set to true. +indent_member_single = false # true/false + +# Spaces to indent single line ('//') comments on lines before code. +indent_single_line_comments_before = 0 # unsigned number + +# Spaces to indent single line ('//') comments on lines after code. +indent_single_line_comments_after = 0 # unsigned number + +# When opening a paren for a control statement (if, for, while, etc), increase +# the indent level by this value. Negative values decrease the indent level. +indent_sparen_extra = 0 # number + +# Whether to indent trailing single line ('//') comments relative to the code +# instead of trying to keep the same absolute column. +indent_relative_single_line_comments = true # true/false + +# Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns. +# It might wise to choose the same value for the option indent_case_brace. +indent_switch_case = 0 # unsigned number + +# Spaces to indent '{' from 'case'. By default, the brace will appear under +# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK. +# It might wise to choose the same value for the option indent_switch_case. +indent_case_brace = 2 # number + +# indent 'break' with 'case' from 'switch'. +indent_switch_break_with_case = false # true/false + +# Whether to indent preprocessor statements inside of switch statements. +# +# Default: true +indent_switch_pp = true # true/false + +# Spaces to shift the 'case' line, without affecting any other lines. +# Usually 0. +indent_case_shift = 0 # unsigned number + +# Whether to indent comments found in first column. +indent_col1_comment = false # true/false + +# Whether to indent multi string literal in first column. +indent_col1_multi_string_literal = false # true/false + +# How to indent goto labels. +# +# >0: Absolute column where 1 is the leftmost column +# <=0: Subtract from brace indent +# +# Default: 1 +indent_label = 1 # number + +# How to indent access specifiers that are followed by a +# colon. +# +# >0: Absolute column where 1 is the leftmost column +# <=0: Subtract from brace indent +# +# Default: 1 +indent_access_spec = 1 # number + +# Whether to indent the code after an access specifier by one level. +# If true, this option forces 'indent_access_spec=0'. +indent_access_spec_body = false # true/false + +# If an open parenthesis is followed by a newline, whether to indent the next +# line so that it lines up after the open parenthesis (not recommended). +indent_paren_nl = false # true/false + +# How to indent a close parenthesis after a newline. +# +# 0: Indent to body level (default) +# 1: Align under the open parenthesis +# 2: Indent to the brace level +indent_paren_close = 0 # unsigned number + +# Whether to indent the open parenthesis of a function definition, +# if the parenthesis is on its own line. +indent_paren_after_func_def = false # true/false + +# Whether to indent the open parenthesis of a function declaration, +# if the parenthesis is on its own line. +indent_paren_after_func_decl = false # true/false + +# Whether to indent the open parenthesis of a function call, +# if the parenthesis is on its own line. +indent_paren_after_func_call = true # true/false + +# Whether to indent a comma when inside a brace. +# If true, aligns under the open brace. +indent_comma_brace = false # true/false + +# Whether to indent a comma when inside a parenthesis. +# If true, aligns under the open parenthesis. +indent_comma_paren = false # true/false + +# Whether to indent a Boolean operator when inside a parenthesis. +# If true, aligns under the open parenthesis. +indent_bool_paren = false # true/false + +# Whether to indent a semicolon when inside a for parenthesis. +# If true, aligns under the open for parenthesis. +indent_semicolon_for_paren = false # true/false + +# Whether to align the first expression to following ones +# if indent_bool_paren=true. +indent_first_bool_expr = false # true/false + +# Whether to align the first expression to following ones +# if indent_semicolon_for_paren=true. +indent_first_for_expr = false # true/false + +# If an open square is followed by a newline, whether to indent the next line +# so that it lines up after the open square (not recommended). +indent_square_nl = false # true/false + +# (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies. +indent_preserve_sql = false # true/false + +# Whether to align continued statements at the '='. If false or if the '=' is +# followed by a newline, the next line is indent one tab. +# +# Default: true +indent_align_assign = true # true/false + +# If true, the indentation of the chunks after a '=' sequence will be set at +# LHS token indentation column before '='. +indent_off_after_assign = false # true/false + +# Whether to align continued statements at the '('. If false or the '(' is +# followed by a newline, the next line indent is one tab. +# +# Default: true +indent_align_paren = true # true/false + +# (OC) Whether to indent Objective-C code inside message selectors. +indent_oc_inside_msg_sel = false # true/false + +# (OC) Whether to indent Objective-C blocks at brace level instead of usual +# rules. +indent_oc_block = false # true/false + +# (OC) Indent for Objective-C blocks in a message relative to the parameter +# name. +# +# =0: Use indent_oc_block rules +# >0: Use specified number of spaces to indent +indent_oc_block_msg = 0 # unsigned number + +# (OC) Minimum indent for subsequent parameters +indent_oc_msg_colon = 0 # unsigned number + +# (OC) Whether to prioritize aligning with initial colon (and stripping spaces +# from lines, if necessary). +# +# Default: true +indent_oc_msg_prioritize_first_colon = true # true/false + +# (OC) Whether to indent blocks the way that Xcode does by default +# (from the keyword if the parameter is on its own line; otherwise, from the +# previous indentation level). Requires indent_oc_block_msg=true. +indent_oc_block_msg_xcode_style = false # true/false + +# (OC) Whether to indent blocks from where the brace is, relative to a +# message keyword. Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_keyword = false # true/false + +# (OC) Whether to indent blocks from where the brace is, relative to a message +# colon. Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_colon = false # true/false + +# (OC) Whether to indent blocks from where the block caret is. +# Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_caret = false # true/false + +# (OC) Whether to indent blocks from where the brace caret is. +# Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_brace = false # true/false + +# When indenting after virtual brace open and newline add further spaces to +# reach this minimum indent. +indent_min_vbrace_open = 0 # unsigned number + +# Whether to add further spaces after regular indent to reach next tabstop +# when indenting after virtual brace open and newline. +indent_vbrace_open_on_tabstop = false # true/false + +# How to indent after a brace followed by another token (not a newline). +# true: indent all contained lines to match the token +# false: indent all contained lines to match the brace +# +# Default: true +indent_token_after_brace = true # true/false + +# Whether to indent the body of a C++11 lambda. +indent_cpp_lambda_body = false # true/false + +# How to indent compound literals that are being returned. +# true: add both the indent from return & the compound literal open brace +# (i.e. 2 indent levels) +# false: only indent 1 level, don't add the indent for the open brace, only +# add the indent for the return. +# +# Default: true +indent_compound_literal_return = false # true/false + +# (C#) Whether to indent a 'using' block if no braces are used. +# +# Default: true +indent_using_block = true # true/false + +# How to indent the continuation of ternary operator. +# +# 0: Off (default) +# 1: When the `if_false` is a continuation, indent it under `if_false` +# 2: When the `:` is a continuation, indent it under `?` +indent_ternary_operator = 2 # unsigned number + +# Whether to indent the statements inside ternary operator. +indent_inside_ternary_operator = false # true/false + +# If true, the indentation of the chunks after a `return` sequence will be set at return indentation column. +indent_off_after_return = false # true/false + +# If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column. +indent_off_after_return_new = false # true/false + +# If true, the tokens after return are indented with regular single indentation. By default (false) the indentation is after the return token. +indent_single_after_return = false # true/false + +# Whether to ignore indent and alignment for 'asm' blocks (i.e. assume they +# have their own indentation). +indent_ignore_asm_block = false # true/false + +# Don't indent the close parenthesis of a function definition, +# if the parenthesis is on its own line. +donot_indent_func_def_close_paren = false # true/false # # Newline adding and removing options # -# Whether to collapse empty blocks between '{' and '}' -nl_collapse_empty_body = true # false/true +# Whether to collapse empty blocks between '{' and '}'. +# If true, overrides nl_inside_empty_func +nl_collapse_empty_body = false # true/false + +# Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'. +nl_assign_leave_one_liners = false # true/false + +# Don't split one-line braced statements inside a 'class xx { }' body. +nl_class_leave_one_liners = false # true/false + +# Don't split one-line enums, as in 'enum foo { BAR = 15 };' +nl_enum_leave_one_liners = false # true/false + +# Don't split one-line get or set functions. +nl_getset_leave_one_liners = false # true/false + +# (C#) Don't split one-line property get or set functions. +nl_cs_property_leave_one_liners = false # true/false + +# Don't split one-line function definitions, as in 'int foo() { return 0; }'. +# might modify nl_func_type_name +nl_func_leave_one_liners = false # true/false + +# Don't split one-line C++11 lambdas, as in '[]() { return 0; }'. +nl_cpp_lambda_leave_one_liners = false # true/false + +# Don't split one-line if/else statements, as in 'if(...) b++;'. +nl_if_leave_one_liners = false # true/false + +# Don't split one-line while statements, as in 'while(...) b++;'. +nl_while_leave_one_liners = false # true/false + +# Don't split one-line do statements, as in 'do { b++; } while(...);'. +nl_do_leave_one_liners = false # true/false + +# Don't split one-line for statements, as in 'for(...) b++;'. +nl_for_leave_one_liners = false # true/false + +# (OC) Don't split one-line Objective-C messages. +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 + +# (OC) Add or remove newline between Objective-C block signature and '{'. +nl_oc_block_brace = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove blank line before '@interface' statement. +nl_oc_before_interface = ignore # ignore/add/remove/force/not_defined -# Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' -nl_assign_leave_one_liners = false # false/true +# (OC) Add or remove blank line before '@implementation' statement. +nl_oc_before_implementation = ignore # ignore/add/remove/force/not_defined -# Don't split one-line braced statements inside a class xx { } body -nl_class_leave_one_liners = false # false/true +# (OC) Add or remove blank line before '@end' statement. +nl_oc_before_end = ignore # ignore/add/remove/force/not_defined -# Don't split one-line enums: 'enum foo { BAR = 15 };' -nl_enum_leave_one_liners = false # false/true +# (OC) Add or remove newline between '@interface' and '{'. +nl_oc_interface_brace = ignore # ignore/add/remove/force/not_defined -# Don't split one-line get or set functions -nl_getset_leave_one_liners = false # false/true +# (OC) Add or remove newline between '@implementation' and '{'. +nl_oc_implementation_brace = ignore # ignore/add/remove/force/not_defined -# Don't split one-line function definitions - 'int foo() { return 0; }' -nl_func_leave_one_liners = false # false/true +# Add or remove newlines at the start of the file. +nl_start_of_file = ignore # ignore/add/remove/force/not_defined -# Don't split one-line if/else statements - 'if(a) b++;' -nl_if_leave_one_liners = true # false/true +# 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 -# Don't split one-line OC messages -nl_oc_msg_leave_one_liner = false # false/true +# Add or remove newline at the end of the file. +nl_end_of_file = ignore # ignore/add/remove/force/not_defined -# Add or remove newlines at the start of the file -nl_start_of_file = ignore # 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 = 0 # unsigned number -# The 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 # number +# Add or remove newline between '=' and '{'. +nl_assign_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline at the end of the file -nl_end_of_file = add # ignore/add/remove/force +# (D) Add or remove newline between '=' and '['. +nl_assign_square = ignore # ignore/add/remove/force/not_defined -# The 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 # number +# Add or remove newline between '[]' and '{'. +nl_tsquare_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between '=' and '{' -nl_assign_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 -# Add or remove newline between '=' and '[' (D only) -nl_assign_square = 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 -# Add or remove newline after '= [' (D only). Will also affect the newline before the ']' -nl_after_square_assign = ignore # ignore/add/remove/force +# Add or remove newline between 'enum' and '{'. +nl_enum_brace = ignore # ignore/add/remove/force/not_defined -# The number of blank lines after a block of variable definitions at the top of a function body -# 0 = No change (default) -nl_func_var_def_blk = 1 # number +# Add or remove newline between 'enum' and 'class'. +nl_enum_class = ignore # ignore/add/remove/force/not_defined -# The number of newlines before a block of typedefs -# 0 = No change (default) -nl_typedef_blk_start = 0 # number +# Add or remove newline between 'enum class' and the identifier. +nl_enum_class_identifier = ignore # ignore/add/remove/force/not_defined -# The number of newlines after a block of typedefs -# 0 = No change (default) -nl_typedef_blk_end = 0 # number +# Add or remove newline between 'enum class' type and ':'. +nl_enum_identifier_colon = ignore # ignore/add/remove/force/not_defined -# The maximum consecutive newlines within a block of typedefs -# 0 = No change (default) -nl_typedef_blk_in = 0 # number +# Add or remove newline between 'enum class identifier :' and type. +nl_enum_colon_type = ignore # ignore/add/remove/force/not_defined -# The number of newlines before a block of variable definitions not at the top of a function body -# 0 = No change (default) -nl_var_def_blk_start = 0 # number +# Add or remove newline between 'struct and '{'. +nl_struct_brace = ignore # ignore/add/remove/force/not_defined -# The number of newlines after a block of variable definitions not at the top of a function body -# 0 = No change (default) -nl_var_def_blk_end = 0 # number +# Add or remove newline between 'union' and '{'. +nl_union_brace = ignore # ignore/add/remove/force/not_defined -# The maximum consecutive newlines within a block of variable definitions -# 0 = No change (default) -nl_var_def_blk_in = 0 # number +# Add or remove newline between 'if' and '{'. +nl_if_brace = remove # ignore/add/remove/force/not_defined -# 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 +# Add or remove newline between '}' and 'else'. +nl_brace_else = remove # ignore/add/remove/force/not_defined -# Add or remove newline between 'enum' and '{' -nl_enum_brace = ignore # 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 -# Add or remove newline between 'struct and '{' -nl_struct_brace = ignore # ignore/add/remove/force +# Add or remove newline between 'else' and '{'. +nl_else_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'union' and '{' -nl_union_brace = ignore # ignore/add/remove/force +# Add or remove newline between 'else' and 'if'. +nl_else_if = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'if' and '{' -nl_if_brace = ignore # 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 -# Add or remove newline between '}' and 'else' -nl_brace_else = remove # ignore/add/remove/force +# Add or remove newline before 'if'/'else if' closing parenthesis. +nl_before_if_closing_paren = ignore # ignore/add/remove/force/not_defined -# 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 +# Add or remove newline between '}' and 'finally'. +nl_brace_finally = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'else' and '{' -nl_else_brace = ignore # ignore/add/remove/force +# Add or remove newline between 'finally' and '{'. +nl_finally_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'else' and 'if' -nl_else_if = ignore # ignore/add/remove/force +# Add or remove newline between 'try' and '{'. +nl_try_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between '}' and 'finally' -nl_brace_finally = ignore # ignore/add/remove/force +# Add or remove newline between get/set and '{'. +nl_getset_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'finally' and '{' -nl_finally_brace = ignore # ignore/add/remove/force +# Add or remove newline between 'for' and '{'. +nl_for_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'try' and '{' -nl_try_brace = ignore # 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 -# Add or remove newline between get/set and '{' -nl_getset_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 -# Add or remove newline between 'for' and '{' -nl_for_brace = ignore # ignore/add/remove/force +# Add or remove newline between '}' and 'catch'. +nl_brace_catch = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'catch' and '{' -nl_catch_brace = 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 -# Add or remove newline between '}' and 'catch' -nl_brace_catch = ignore # ignore/add/remove/force +# Add or remove newline between '}' and ']'. +nl_brace_square = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'while' and '{' -nl_while_brace = ignore # ignore/add/remove/force +# Add or remove newline between '}' and ')' in a function invocation. +nl_brace_fparen = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'scope (x)' and '{' (D) -nl_scope_brace = ignore # ignore/add/remove/force +# Add or remove newline between 'while' and '{'. +nl_while_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'unittest' and '{' (D) -nl_unittest_brace = ignore # ignore/add/remove/force +# (D) Add or remove newline between 'scope (x)' and '{'. +nl_scope_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'version (x)' and '{' (D) -nl_version_brace = ignore # ignore/add/remove/force +# (D) Add or remove newline between 'unittest' and '{'. +nl_unittest_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'using' and '{' -nl_using_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 -# 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 +# (C#) Add or remove newline between 'using' and '{'. +nl_using_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'do' and '{' -nl_do_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 -# Add or remove newline between '}' and 'while' of 'do' statement -nl_brace_while = ignore # ignore/add/remove/force +# Add or remove newline between 'do' and '{'. +nl_do_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'switch' and '{' -nl_switch_brace = ignore # ignore/add/remove/force +# Add or remove newline between '}' and 'while' of 'do' statement. +nl_brace_while = ignore # ignore/add/remove/force/not_defined -# Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc. -# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace. -nl_multi_line_cond = false # false/true +# Add or remove newline between 'switch' and '{'. +nl_switch_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'synchronized' and '{'. +nl_synchronized_brace = ignore # ignore/add/remove/force/not_defined + +# Add a newline between ')' and '{' if the ')' is on a different line than the +# if/for/etc. +# +# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and +# nl_catch_brace. +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 = ignore # ignore/add/remove/force/not_defined + +# 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 # Force a newline in a define after the macro name for multi-line defines. -nl_multi_line_define = false # false/true +nl_multi_line_define = false # true/false -# Whether to put a newline before 'case' statement -nl_before_case = true # false/true +# Whether to add a newline before 'case', and a blank line before a 'case' +# statement that follows a ';' or '}'. +nl_before_case = false # true/false -# Add or remove newline between ')' and 'throw' -nl_before_throw = ignore # ignore/add/remove/force +# Whether to add a newline after a 'case' statement. +nl_after_case = true # true/false -# Whether to put a newline after 'case' statement -nl_after_case = true # false/true +# Add or remove newline between a case ':' and '{'. +# +# Overrides nl_after_case. +nl_case_colon_brace = remove # ignore/add/remove/force/not_defined -# Add or remove a newline between a case ':' and '{'. Overrides nl_after_case. -nl_case_colon_brace = ignore # ignore/add/remove/force +# Add or remove newline between ')' and 'throw'. +nl_before_throw = ignore # ignore/add/remove/force/not_defined -# Newline between namespace and { -nl_namespace_brace = remove # ignore/add/remove/force +# Add or remove newline between 'namespace' and '{'. +nl_namespace_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline between 'template<>' and whatever follows. -nl_template_class = 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 -# Add or remove newline between 'class' and '{' -nl_class_brace = remove # 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 -# Add or remove newline after each ',' in the constructor member initialization -nl_class_init_args = add # 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 -# Add or remove newline between return type and function name in a function definition -nl_func_type_name = 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 -# Add or remove newline between return type and function name inside a class {} -# Uses nl_func_type_name or nl_func_proto_type_name if set to ignore. -nl_func_type_name_class = 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 + +# Add or remove newline after 'template<...>' of a template function. +nl_template_func = ignore # ignore/add/remove/force/not_defined + +# 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 -# Add or remove newline between function scope and name in a definition -# Controls the newline after '::' in 'void A::f() { }' -nl_func_scope_name = 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 + +# 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 -# Add or remove newline between return type and function name in a prototype -nl_func_proto_type_name = 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 -# Add or remove newline between a function name and the opening '(' -nl_func_paren = 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 -# Add or remove newline between a function name and the opening '(' in the definition -nl_func_def_paren = 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 -# Add or remove newline after '(' in a function declaration -nl_func_decl_start = ignore # ignore/add/remove/force +# Add or remove newline between 'class' and '{'. +nl_class_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove newline after '(' in a function definition -nl_func_def_start = 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 + +# 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 + +# 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 + +# 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 + +# 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 + +# 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 + +# 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 + +# 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 + +# Add or remove newline between a function name and the opening '(' in the +# declaration. +nl_func_paren = ignore # ignore/add/remove/force/not_defined + +# Overrides nl_func_paren for functions with no parameters. +nl_func_paren_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between a function name and the opening '(' in the +# definition. +nl_func_def_paren = ignore # ignore/add/remove/force/not_defined + +# Overrides nl_func_def_paren for functions with no parameters. +nl_func_def_paren_empty = ignore # ignore/add/remove/force/not_defined + +# 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 + +# Overrides nl_func_call_paren for functions with no parameters. +nl_func_call_paren_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after '(' in a function declaration. +nl_func_decl_start = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after '(' in a function definition. +nl_func_def_start = remove # ignore/add/remove/force/not_defined # Overrides nl_func_decl_start when there is only one parameter. -nl_func_decl_start_single = ignore # ignore/add/remove/force +nl_func_decl_start_single = ignore # ignore/add/remove/force/not_defined # Overrides nl_func_def_start when there is only one parameter. -nl_func_def_start_single = ignore # ignore/add/remove/force +nl_func_def_start_single = ignore # ignore/add/remove/force/not_defined + +# 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. +nl_func_decl_start_multi_line = false # true/false -# Add or remove newline after each ',' in a function declaration -nl_func_decl_args = add # ignore/add/remove/force +# Whether to add a newline after '(' in a function definition if '(' and ')' +# are in different lines. If false, nl_func_def_start is used instead. +nl_func_def_start_multi_line = false # true/false -# Add or remove newline after each ',' in a function definition -nl_func_def_args = ignore # ignore/add/remove/force +# Add or remove newline after each ',' in a function declaration. +nl_func_decl_args = ignore # ignore/add/remove/force/not_defined -# Add or remove newline before the ')' in a function declaration -nl_func_decl_end = ignore # 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 -# Add or remove newline before the ')' in a function definition -nl_func_def_end = ignore # 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 + +# 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. +nl_func_decl_args_multi_line = false # true/false + +# Whether to add a newline after each ',' in a function definition if '(' +# and ')' are in different lines. If false, nl_func_def_args is used instead. +nl_func_def_args_multi_line = false # true/false + +# Add or remove newline before the ')' in a function declaration. +nl_func_decl_end = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline before the ')' in a function definition. +nl_func_def_end = remove # ignore/add/remove/force/not_defined # Overrides nl_func_decl_end when there is only one parameter. -nl_func_decl_end_single = ignore # ignore/add/remove/force +nl_func_decl_end_single = ignore # ignore/add/remove/force/not_defined # Overrides nl_func_def_end when there is only one parameter. -nl_func_def_end_single = ignore # ignore/add/remove/force +nl_func_def_end_single = ignore # ignore/add/remove/force/not_defined + +# 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. +nl_func_decl_end_multi_line = false # true/false + +# Whether to add a newline before ')' in a function definition if '(' and ')' +# are in different lines. If false, nl_func_def_end is used instead. +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 +nl_func_decl_empty = ignore # ignore/add/remove/force/not_defined # Add or remove newline between '()' in a function definition. -nl_func_def_empty = ignore # ignore/add/remove/force +nl_func_def_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '()' in a function call. +nl_func_call_empty = ignore # ignore/add/remove/force/not_defined + +# 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 + +# Whether to add a newline before ')' in a function call. +nl_func_call_end = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after '(' in a function call if '(' and ')' are in +# different lines. +nl_func_call_start_multi_line = false # true/false + +# Whether to add a newline after each ',' in a function call if '(' and ')' +# are in different lines. +nl_func_call_args_multi_line = false # true/false + +# Whether to add a newline before ')' in a function call if '(' and ')' are in +# different lines. +nl_func_call_end_multi_line = false # true/false + +# Whether to respect nl_func_call_XXX option in case of closure args. +nl_func_call_args_multi_line_ignore_closures = false # true/false + +# Whether to add a newline after '<' of a template parameter list. +nl_template_start = false # true/false + +# Whether to add a newline after each ',' in a template parameter list. +nl_template_args = false # true/false + +# Whether to add a newline before '>' of a template parameter list. +nl_template_end = false # true/false -# Whether to put each OC message parameter on a separate line -# See nl_oc_msg_leave_one_liner -nl_oc_msg_args = false # false/true +# (OC) Whether to put each Objective-C message parameter on a separate line. +# See nl_oc_msg_leave_one_liner. +nl_oc_msg_args = false # true/false -# Add or remove newline between function signature and '{' -nl_fdef_brace = ignore # ignore/add/remove/force +# Add or remove newline between function signature and '{'. +nl_fdef_brace = ignore # ignore/add/remove/force/not_defined -# Add or remove a newline between the return keyword and return expression. -nl_return_expr = ignore # 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 -# Whether to put a newline after semicolons, except in 'for' statements -nl_after_semicolon = false # false/true +# Add or remove newline between C++11 lambda signature and '{'. +nl_cpp_ldef_brace = ignore # ignore/add/remove/force/not_defined -# Whether to put a newline after brace open. -# This also adds a newline before the matching brace close. -nl_after_brace_open = false # false/true +# Add or remove newline between 'return' and the return expression. +nl_return_expr = ignore # ignore/add/remove/force/not_defined -# If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is -# placed between the open brace and a trailing single-line comment. -nl_after_brace_open_cmt = false # false/true +# Whether to add a newline after semicolons, except in 'for' statements. +nl_after_semicolon = false # true/false -# Whether to put a newline after a virtual brace open with a non-empty body. +# (Java) Add or remove newline between the ')' and '{{' of the double brace +# initializer. +nl_paren_dbrace_open = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after the type in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined + +# 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 + +# 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 + +# Whether to add a newline before '{'. +nl_before_brace_open = false # true/false + +# Whether to add a newline after '{'. +nl_after_brace_open = false # true/false + +# Whether to add a newline between the open brace and a trailing single-line +# comment. Requires nl_after_brace_open=true. +nl_after_brace_open_cmt = false # true/false + +# Whether to add a newline after a virtual brace open with a non-empty body. # These occur in un-braced if/while/do/for statement bodies. -nl_after_vbrace_open = false # false/true +nl_after_vbrace_open = false # true/false -# Whether to put a newline after a virtual brace open with an empty body. +# Whether to add a newline after a virtual brace open with an empty body. # These occur in un-braced if/while/do/for statement bodies. -nl_after_vbrace_open_empty = false # false/true +nl_after_vbrace_open_empty = false # true/false -# Whether to put a newline after a brace close. -# Does not apply if followed by a necessary ';'. -nl_after_brace_close = false # false/true +# Whether to add a newline after '}'. Does not apply if followed by a +# necessary ';'. +nl_after_brace_close = false # true/false -# Whether to put a newline after a virtual brace close. -# Would add a newline before return in: 'if (foo) a++; return;' -nl_after_vbrace_close = false # false/true +# Whether to add a newline after a virtual brace close, +# as in 'if (foo) a++; <here> return;'. +nl_after_vbrace_close = false # true/false -# Control the newline between the close brace and 'b' in: 'struct { int a; } b;' -# Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close -nl_brace_struct_var = ignore # ignore/add/remove/force +# 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 -# Whether to alter newlines in '#define' macros -nl_define_macro = true # false/true +# Whether to alter newlines in '#define' macros. +nl_define_macro = false # true/false -# Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' -nl_squeeze_ifdef = false # false/true +# Whether to alter newlines between consecutive parenthesis closes. The number +# of closing parentheses in a line will depend on respective open parenthesis +# lines. +nl_squeeze_paren_close = false # true/false -# Add or remove blank line before 'if' -nl_before_if = add # ignore/add/remove/force +# Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and +# '#endif'. Does not affect top-level #ifdefs. +nl_squeeze_ifdef = false # true/false -# Add or remove blank line after 'if' statement -nl_after_if = ignore # ignore/add/remove/force +# Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well. +nl_squeeze_ifdef_top_level = false # true/false -# Add or remove blank line before 'for' -nl_before_for = add # ignore/add/remove/force +# Add or remove blank line before 'if'. +nl_before_if = ignore # ignore/add/remove/force/not_defined -# Add or remove blank line after 'for' statement -nl_after_for = 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 -# Add or remove blank line before 'while' -nl_before_while = add # ignore/add/remove/force +# Add or remove blank line before 'for'. +nl_before_for = ignore # ignore/add/remove/force/not_defined -# Add or remove blank line after 'while' statement -nl_after_while = ignore # ignore/add/remove/force +# Add or remove blank line after 'for' statement. +nl_after_for = ignore # ignore/add/remove/force/not_defined -# Add or remove blank line before 'switch' -nl_before_switch = add # ignore/add/remove/force +# Add or remove blank line before 'while'. +nl_before_while = ignore # ignore/add/remove/force/not_defined -# Add or remove blank line after 'switch' statement -nl_after_switch = ignore # ignore/add/remove/force +# Add or remove blank line after 'while' statement. +nl_after_while = ignore # ignore/add/remove/force/not_defined -# Add or remove blank line before 'do' -nl_before_do = add # ignore/add/remove/force +# Add or remove blank line before 'switch'. +nl_before_switch = ignore # ignore/add/remove/force/not_defined -# Add or remove blank line after 'do/while' statement -nl_after_do = ignore # ignore/add/remove/force +# Add or remove blank line after 'switch' statement. +nl_after_switch = ignore # ignore/add/remove/force/not_defined -# Whether to double-space commented-entries in struct/enum -nl_ds_struct_enum_cmt = true # false/true +# Add or remove blank line before 'synchronized'. +nl_before_synchronized = ignore # ignore/add/remove/force/not_defined -# Whether to double-space before the close brace of a struct/union/enum -# (lower priority than 'eat_blanks_before_close_brace') -nl_ds_struct_enum_close_brace = true # false/true +# Add or remove blank line after 'synchronized' statement. +nl_after_synchronized = ignore # ignore/add/remove/force/not_defined -# Add or remove a newline around a class colon. -# Related to pos_class_colon, nl_class_init_args, and pos_comma. -nl_class_colon = ignore # ignore/add/remove/force +# Add or remove blank line before 'do'. +nl_before_do = ignore # ignore/add/remove/force/not_defined -# Change simple unbraced if statements into a one-liner -# 'if(b)\n i++;' => 'if(b) i++;' -nl_create_if_one_liner = true # false/true +# Add or remove blank line after 'do/while' statement. +nl_after_do = ignore # ignore/add/remove/force/not_defined -# Change simple unbraced for statements into a one-liner -# 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' -nl_create_for_one_liner = true # false/true +# Ignore nl_before_{if,for,switch,do,synchronized} if the control +# statement is immediately after a case statement. +# if nl_before_{if,for,switch,do} is set to remove, this option +# does nothing. +nl_before_ignore_after_case = false # true/false -# Change simple unbraced while statements into a one-liner -# 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);' -nl_create_while_one_liner = true # false/true +# Whether to put a blank line before 'return' statements, unless after an open +# brace. +nl_before_return = false # true/false -# -# Positioning options -# +# Whether to put a blank line after 'return' statements, unless followed by a +# close brace. +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 + +# (Java) Whether to put a blank line after a member '.' or '->' operators. +nl_after_member = ignore # ignore/add/remove/force/not_defined + +# Whether to double-space commented-entries in 'struct'/'union'/'enum'. +nl_ds_struct_enum_cmt = false # true/false + +# Whether to force a newline before '}' of a 'struct'/'union'/'enum'. +# (Lower priority than eat_blanks_before_close_brace.) +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 + +# 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 -# The position of arithmetic operators in wrapped expressions -pos_arith = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_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 +# such code into four lines. +nl_namespace_two_to_one_liner = false # true/false -# The position of assignment in wrapped expressions. -# Do not affect '=' followed by '{' -pos_assign = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force +# Whether to remove a newline in simple unbraced if statements, turning them +# into one-liners, as in 'if(b)\n i++;' => 'if(b) i++;'. +nl_create_if_one_liner = false # true/false -# The position of boolean operators in wrapped expressions -pos_bool = lead_break # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force +# Whether to remove a newline in simple unbraced for statements, turning them +# into one-liners, as in 'for (...)\n stmt;' => 'for (...) stmt;'. +nl_create_for_one_liner = false # true/false -# The position of comparison operators in wrapped expressions -pos_compare = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force +# Whether to remove a newline in simple unbraced while statements, turning +# them into one-liners, as in 'while (expr)\n stmt;' => 'while (expr) stmt;'. +nl_create_while_one_liner = false # true/false -# The position of conditional (b ? t : f) operators in wrapped expressions -pos_conditional = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force +# Whether to collapse a function definition whose body (not counting braces) +# is only one line so that the entire definition (prototype, braces, body) is +# a single line. +nl_create_func_def_one_liner = false # true/false -# The position of the comma in wrapped expressions -pos_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force +# Whether to split one-line simple list definitions into three lines by +# adding newlines, as in 'int a[12] = { <here> 0 <here> };'. +nl_create_list_one_liner = false # true/false -# The position of the comma in the constructor initialization list -pos_class_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force +# Whether to split one-line simple unbraced if statements into two lines by +# adding a newline, as in 'if(b) <here> i++;'. +nl_split_if_one_liner = false # true/false -# The position of colons between constructor and member initialization -pos_class_colon = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force +# Whether to split one-line simple unbraced for statements into two lines by +# adding a newline, as in 'for (...) <here> stmt;'. +nl_split_for_one_liner = false # true/false + +# Whether to split one-line simple unbraced while statements into two lines by +# adding a newline, as in 'while (expr) <here> stmt;'. +nl_split_while_one_liner = false # true/false + +# Don't add a newline before a cpp-comment in a parameter list of a function +# call. +donot_add_nl_before_cpp_comment = false # true/false # -# Line Splitting options +# Blank line options # -# Try to limit code width to N number of columns -code_width = 80 # number +# The maximum number of consecutive newlines (3 = 2 blank lines). +nl_max = 0 # unsigned number + +# The maximum number of consecutive newlines in a function. +nl_max_blank_in_func = 0 # unsigned number + +# The number of newlines inside an empty function body. +# This option overrides eat_blanks_after_open_brace and +# eat_blanks_before_close_brace, but is ignored when +# nl_collapse_empty_body=true +nl_inside_empty_func = 0 # unsigned number + +# The number of newlines before a function prototype. +nl_before_func_body_proto = 0 # unsigned number + +# The number of newlines before a multi-line function definition. Where +# applicable, this option is overridden with eat_blanks_after_open_brace=true +nl_before_func_body_def = 0 # unsigned number + +# The number of newlines before a class constructor/destructor prototype. +nl_before_func_class_proto = 0 # unsigned number + +# The number of newlines before a class constructor/destructor definition. +nl_before_func_class_def = 0 # unsigned number -# Whether to fully split long 'for' statements at semi-colons -ls_for_split_full = false # false/true +# The number of newlines after a function prototype. +nl_after_func_proto = 0 # unsigned number -# Whether to fully split long function protos/calls at commas -ls_func_split_full = true # false/true +# The number of newlines after a function prototype, if not followed by +# another function prototype. +nl_after_func_proto_group = 0 # unsigned number -# Whether to split lines as close to code_width as possible and ignore some groupings -ls_code_width = false # false/true +# The number of newlines after a class constructor/destructor prototype. +nl_after_func_class_proto = 0 # unsigned number +# The number of newlines after a class constructor/destructor prototype, +# if not followed by another constructor/destructor prototype. +nl_after_func_class_proto_group = 0 # unsigned number + +# Whether one-line method definitions inside a class body should be treated +# as if they were prototypes for the purposes of adding newlines. # -# Blank line options +# Requires nl_class_leave_one_liners=true. Overrides nl_before_func_body_def +# and nl_before_func_class_def for one-liners. +nl_class_leave_one_liner_groups = false # true/false + +# The number of newlines after '}' of a multi-line function body. +nl_after_func_body = 0 # unsigned number + +# The number of newlines after '}' of a multi-line function body in a class +# declaration. Also affects class constructors/destructors. # +# Overrides nl_after_func_body. +nl_after_func_body_class = 0 # unsigned number -# The maximum consecutive newlines -nl_max = 3 # number +# The number of newlines after '}' of a single line function body. Also +# affects class constructors/destructors. +# +# Overrides nl_after_func_body and nl_after_func_body_class. +nl_after_func_body_one_liner = 0 # unsigned number -# The number of newlines after a function prototype, if followed by another function prototype -nl_after_func_proto = 0 # number +# The number of blank lines after a block of variable definitions at the top +# of a function body. +# +# 0: No change (default). +nl_func_var_def_blk = 0 # unsigned number -# The number of newlines after a function prototype, if not followed by another function prototype -nl_after_func_proto_group = 0 # number +# The number of newlines before a block of typedefs. If nl_after_access_spec +# is non-zero, that option takes precedence. +# +# 0: No change (default). +nl_typedef_blk_start = 0 # unsigned number -# The number of newlines after '}' of a multi-line function body -nl_after_func_body = 2 # number +# The number of newlines after a block of typedefs. +# +# 0: No change (default). +nl_typedef_blk_end = 0 # unsigned number -# The number of newlines after '}' of a multi-line function body in a class declaration -nl_after_func_body_class = 0 # number +# The maximum number of consecutive newlines within a block of typedefs. +# +# 0: No change (default). +nl_typedef_blk_in = 0 # unsigned number + +# The number of newlines before a block of variable definitions not at the top +# of a function body. If nl_after_access_spec is non-zero, that option takes +# precedence. +# +# 0: No change (default). +nl_var_def_blk_start = 0 # unsigned number + +# The number of newlines after a block of variable definitions not at the top +# of a function body. +# +# 0: No change (default). +nl_var_def_blk_end = 0 # unsigned number -# The number of newlines after '}' of a single line function body -nl_after_func_body_one_liner = 2 # number +# The maximum number of consecutive newlines within a block of variable +# definitions. +# +# 0: No change (default). +nl_var_def_blk_in = 0 # unsigned number # The minimum number of newlines before a multi-line comment. # Doesn't apply if after a brace open or another multi-line comment. -nl_before_block_comment = 2 # number +nl_before_block_comment = 0 # unsigned number # The minimum number of newlines before a single-line C comment. # Doesn't apply if after a brace open or other single-line C comments. -nl_before_c_comment = 2 # number +nl_before_c_comment = 0 # unsigned number # The minimum number of newlines before a CPP comment. # Doesn't apply if after a brace open or other CPP comments. -nl_before_cpp_comment = 2 # number +nl_before_cpp_comment = 0 # unsigned number # Whether to force a newline after a multi-line comment. -nl_after_multiline_comment = true # false/true +nl_after_multiline_comment = false # true/false + +# Whether to force a newline after a label's colon. +nl_after_label_colon = false # true/false + +# The number of newlines before a struct definition. +nl_before_struct = 0 # unsigned number -# The number of newlines after '}' or ';' of a struct/enum/union definition -nl_after_struct = 0 # number +# The number of newlines after '}' or ';' of a struct/enum/union definition. +nl_after_struct = 0 # unsigned number -# The number of newlines after '}' or ';' of a class definition -nl_after_class = 0 # number +# The number of newlines before a class definition. +nl_before_class = 0 # unsigned number -# The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. -# Will not change the newline count if after a brace open. -# 0 = No change. -nl_before_access_spec = 2 # number +# The number of newlines after '}' or ';' of a class definition. +nl_after_class = 0 # unsigned number -# The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. -# 0 = No change. -nl_after_access_spec = 2 # number +# The number of newlines before a namespace. +nl_before_namespace = 0 # unsigned number + +# The number of newlines after '{' of a namespace. This also adds newlines +# before the matching '}'. +# +# 0: Apply eat_blanks_after_open_brace or eat_blanks_before_close_brace if +# applicable, otherwise no change. +# +# Overrides eat_blanks_after_open_brace and eat_blanks_before_close_brace. +nl_inside_namespace = 0 # unsigned number + +# The number of newlines after '}' of a namespace. +nl_after_namespace = 0 # unsigned number + +# The number of newlines before an access specifier label. This also includes +# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count +# if after a brace open. +# +# 0: No change (default). +nl_before_access_spec = 0 # unsigned number + +# The number of newlines after an access specifier label. This also includes +# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count +# if after a brace open. +# +# 0: No change (default). +# +# Overrides nl_typedef_blk_start and nl_var_def_blk_start. +nl_after_access_spec = 0 # unsigned number -# The number of newlines between a function def and the function comment. -# 0 = No change. -nl_comment_func_def = 1 # number +# The number of newlines between a function definition and the function +# comment, as in '// comment\n <here> void foo() {...}'. +# +# 0: No change (default). +nl_comment_func_def = 0 # unsigned number -# The number of newlines after a try-catch-finally block that isn't followed by a brace close. -# 0 = No change. -nl_after_try_catch_finally = 1 # number +# The number of newlines after a try-catch-finally block that isn't followed +# by a brace close. +# +# 0: No change (default). +nl_after_try_catch_finally = 0 # unsigned number -# The number of newlines before and after a property, indexer or event decl. -# 0 = No change. -nl_around_cs_property = 0 # number +# (C#) The number of newlines before and after a property, indexer or event +# declaration. +# +# 0: No change (default). +nl_around_cs_property = 0 # unsigned number -# The number of newlines between the get/set/add/remove handlers in C#. -# 0 = No change. -nl_between_get_set = 0 # number +# (C#) The number of newlines between the get/set/add/remove handlers. +# +# 0: No change (default). +nl_between_get_set = 0 # unsigned number -# Add or remove newline between C# property and the '{' -nl_property_brace = ignore # ignore/add/remove/force +# (C#) Add or remove newline between property and the '{'. +nl_property_brace = ignore # ignore/add/remove/force/not_defined -# Whether to remove blank lines after '{' -eat_blanks_after_open_brace = true # false/true +# Whether to remove blank lines after '{'. +eat_blanks_after_open_brace = true # true/false -# Whether to remove blank lines before '}' -eat_blanks_before_close_brace = true # false/true +# Whether to remove blank lines before '}'. +eat_blanks_before_close_brace = true # true/false -# How aggressively to remove extra newlines not in preproc. -# 0: No change +# How aggressively to remove extra newlines not in preprocessor. +# +# 0: No change (default) # 1: Remove most newlines not handled by other config # 2: Remove all newlines and reformat completely by config -nl_remove_extra_newlines = 0 # number +nl_remove_extra_newlines = 0 # unsigned number -# Whether to put a blank line before 'return' statements, unless after an open brace. -nl_before_return = false # false/true +# (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 -# Whether to put a blank line after 'return' statements, unless followed by a close brace. -nl_after_return = true # false/true +# (Java) Add or remove newline between two annotations. +nl_between_annotation = ignore # ignore/add/remove/force/not_defined -# Whether to put a newline after a Java annotation statement. -# Only affects annotations that are after a newline. -nl_after_annotation = ignore # ignore/add/remove/force +# The number of newlines before a whole-file #ifdef. +# +# 0: No change (default). +nl_before_whole_file_ifdef = 0 # unsigned number -# Controls the newline between two annotations. -nl_between_annotation = ignore # ignore/add/remove/force +# The number of newlines after a whole-file #ifdef. +# +# 0: No change (default). +nl_after_whole_file_ifdef = 0 # unsigned number +# The number of newlines before a whole-file #endif. # -# Code modifying options (non-whitespace) +# 0: No change (default). +nl_before_whole_file_endif = 0 # unsigned number + +# The number of newlines after a whole-file #endif. +# +# 0: No change (default). +nl_after_whole_file_endif = 0 # unsigned number + +# +# Positioning options # -# Add or remove braces on single-line 'do' statement -mod_full_brace_do = add # ignore/add/remove/force +# The position of arithmetic operators in wrapped expressions. +pos_arith = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force -# Add or remove braces on single-line 'for' statement -mod_full_brace_for = add # ignore/add/remove/force +# The position of assignment in wrapped expressions. Do not affect '=' +# followed by '{'. +pos_assign = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force -# Add or remove braces on single-line function definitions. (Pawn) -mod_full_brace_function = ignore # ignore/add/remove/force +# The position of Boolean operators in wrapped expressions. +pos_bool = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force -# Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. -mod_full_brace_if = add # ignore/add/remove/force +# The position of comparison operators in wrapped expressions. +pos_compare = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force -# Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if. -# If any must be braced, they are all braced. If all can be unbraced, then the braces are removed. -mod_full_brace_if_chain = false # false/true +# The position of conditional operators, as in the '?' and ':' of +# 'expr ? stmt : stmt', in wrapped expressions. +pos_conditional = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force -# Don't remove braces around statements that span N newlines -mod_full_brace_nl = 1 # number +# The position of the comma in wrapped expressions. +pos_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in enum entries. +pos_enum_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in the base class list if there is more than one +# line. Affects nl_class_init_args. +pos_class_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in the constructor initialization list. +# Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon. +pos_constr_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of trailing/leading class colon, between class and base class +# list. Affects nl_class_colon. +pos_class_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of colons between constructor and member initialization. +# Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma. +pos_constr_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of shift operators in wrapped expressions. +pos_shift = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# +# Line splitting options +# + +# Try to limit code width to N columns. +code_width = 100 # unsigned number + +# Whether to fully split long 'for' statements at semi-colons. +ls_for_split_full = false # true/false + +# Whether to fully split long function prototypes/calls at commas. +# The option ls_code_width has priority over the option ls_func_split_full. +ls_func_split_full = false # true/false + +# Whether to split lines as close to code_width as possible and ignore some +# groupings. +# The option ls_code_width has priority over the option ls_func_split_full. +ls_code_width = false # true/false + +# +# Code alignment options (not left column spaces/tabs) +# + +# Whether to keep non-indenting tabs. +align_keep_tabs = false # true/false + +# Whether to use tabs for aligning. +align_with_tabs = false # true/false -# Add or remove braces on single-line 'while' statement -mod_full_brace_while = add # ignore/add/remove/force +# Whether to bump out to the next tab when aligning. +align_on_tabstop = false # true/false -# Add or remove braces on single-line 'using ()' statement -mod_full_brace_using = ignore # ignore/add/remove/force +# Whether to right-align numbers. +align_number_right = false # true/false -# Add or remove unnecessary paren on 'return' statement -mod_paren_on_return = remove # ignore/add/remove/force +# Whether to keep whitespace not required for alignment. +align_keep_extra_space = false # true/false -# Whether to change optional semicolons to real semicolons -mod_pawn_semicolon = false # false/true +# Whether to align variable definitions in prototypes and functions. +align_func_params = false # true/false -# Add parens on 'while' and 'if' statement around bools -mod_full_paren_if_bool = true # false/true +# The span for aligning parameter definitions in function on parameter name. +# +# 0: Don't align (default). +align_func_params_span = 0 # unsigned number + +# The threshold for aligning function parameter definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_func_params_thresh = 0 # number + +# The gap for aligning function parameter definitions. +align_func_params_gap = 0 # unsigned number + +# The span for aligning constructor value. +# +# 0: Don't align (default). +align_constr_value_span = 0 # unsigned number + +# The threshold for aligning constructor value. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_constr_value_thresh = 0 # number + +# The gap for aligning constructor value. +align_constr_value_gap = 0 # unsigned number + +# Whether to align parameters in single-line functions that have the same +# name. The function names must already be aligned with each other. +align_same_func_call_params = false # true/false + +# The span for aligning function-call parameters for single line functions. +# +# 0: Don't align (default). +align_same_func_call_params_span = 0 # unsigned number + +# The threshold for aligning function-call parameters for single line +# functions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_same_func_call_params_thresh = 0 # number + +# The span for aligning variable definitions. +# +# 0: Don't align (default). +align_var_def_span = 0 # unsigned number + +# How to consider (or treat) the '*' in the alignment of variable definitions. +# +# 0: Part of the type 'void * foo;' (default) +# 1: Part of the variable 'void *foo;' +# 2: Dangling 'void *foo;' +# Dangling: the '*' will not be taken into account when aligning. +align_var_def_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of variable definitions. +# +# 0: Part of the type 'long & foo;' (default) +# 1: Part of the variable 'long &foo;' +# 2: Dangling 'long &foo;' +# Dangling: the '&' will not be taken into account when aligning. +align_var_def_amp_style = 0 # unsigned number + +# The threshold for aligning variable definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_def_thresh = 0 # number + +# The gap for aligning variable definitions. +align_var_def_gap = 0 # unsigned number + +# Whether to align the colon in struct bit fields. +align_var_def_colon = false # true/false + +# The gap for aligning the colon in struct bit fields. +align_var_def_colon_gap = 0 # unsigned number + +# Whether to align any attribute after the variable name. +align_var_def_attribute = false # true/false + +# Whether to align inline struct/enum/union variable definitions. +align_var_def_inline = false # true/false + +# The span for aligning on '=' in assignments. +# +# 0: Don't align (default). +align_assign_span = 0 # unsigned number + +# The span for aligning on '=' in function prototype modifier. +# +# 0: Don't align (default). +align_assign_func_proto_span = 0 # unsigned number + +# The threshold for aligning on '=' in assignments. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_assign_thresh = 0 # number + +# How to apply align_assign_span to function declaration "assignments", i.e. +# 'virtual void foo() = 0' or '~foo() = {default|delete}'. +# +# 0: Align with other assignments (default) +# 1: Align with each other, ignoring regular assignments +# 2: Don't align +align_assign_decl_func = 0 # unsigned number -# Whether to remove superfluous semicolons -mod_remove_extra_semicolon = true # false/true +# The span for aligning on '=' in enums. +# +# 0: Don't align (default). +align_enum_equ_span = 0 # unsigned number -# If a function body exceeds the specified number of newlines and doesn't have a comment after -# the close brace, a comment will be added. -mod_add_long_function_closebrace_comment = 0 # number +# The threshold for aligning on '=' in enums. +# Use a negative number for absolute thresholds. +# +# 0: no limit (default). +align_enum_equ_thresh = 0 # number -# If a switch body exceeds the specified number of newlines and doesn't have a comment after -# the close brace, a comment will be added. -mod_add_long_switch_closebrace_comment = 0 # number +# The span for aligning class member definitions. +# +# 0: Don't align (default). +align_var_class_span = 0 # unsigned number -# If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after -# the #endif, a comment will be added. -mod_add_long_ifdef_endif_comment = 1 # number +# The threshold for aligning class member definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_class_thresh = 0 # number -# If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after -# the #else, a comment will be added. -mod_add_long_ifdef_else_comment = 1 # number +# The gap for aligning class member definitions. +align_var_class_gap = 0 # unsigned number -# If TRUE, will sort consecutive single-line 'import' statements [Java, D] -mod_sort_import = false # false/true +# The span for aligning struct/union member definitions. +# +# 0: Don't align (default). +align_var_struct_span = 0 # unsigned number -# If TRUE, will sort consecutive single-line 'using' statements [C#] -mod_sort_using = false # false/true +# The threshold for aligning struct/union member definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_struct_thresh = 0 # number -# If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] -# This is generally a bad idea, as it may break your code. -mod_sort_include = false # false/true +# The gap for aligning struct/union member definitions. +align_var_struct_gap = 0 # unsigned number -# If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace. -mod_move_case_break = true # false/true +# The span for aligning struct initializer values. +# +# 0: Don't align (default). +align_struct_init_span = 0 # unsigned number -# Will add or remove the braces around a fully braced case statement. -# Will only remove the braces if there are no variable declarations in the block. -mod_case_brace = ignore # ignore/add/remove/force +# The span for aligning single-line typedefs. +# +# 0: Don't align (default). +align_typedef_span = 0 # unsigned number -# If TRUE, it will remove a void 'return;' that appears as the last statement in a function. -mod_remove_empty_return = true # false/true +# The minimum space between the type and the synonym of a typedef. +align_typedef_gap = 0 # unsigned number +# How to align typedef'd functions with other typedefs. # -# Comment modifications +# 0: Don't mix them at all (default) +# 1: Align the open parenthesis with the types +# 2: Align the function type name with the other type names +align_typedef_func = 0 # unsigned number + +# How to consider (or treat) the '*' in the alignment of typedefs. +# +# 0: Part of the typedef type, 'typedef int * pint;' (default) +# 1: Part of type name: 'typedef int *pint;' +# 2: Dangling: 'typedef int *pint;' +# Dangling: the '*' will not be taken into account when aligning. +align_typedef_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of typedefs. +# +# 0: Part of the typedef type, 'typedef int & intref;' (default) +# 1: Part of type name: 'typedef int &intref;' +# 2: Dangling: 'typedef int &intref;' +# Dangling: the '&' will not be taken into account when aligning. +align_typedef_amp_style = 0 # unsigned number + +# The span for aligning comments that end lines. # +# 0: Don't align (default). +align_right_cmt_span = 0 # unsigned number -# Try to wrap comments at cmt_width columns -cmt_width = 80 # number +# Minimum number of columns between preceding text and a trailing comment in +# order for the comment to qualify for being aligned. Must be non-zero to have +# an effect. +align_right_cmt_gap = 0 # unsigned number -# Set the comment reflow mode (default: 0) -# 0: no reflowing (apart from the line wrapping due to cmt_width) -# 1: no touching at all -# 2: full reflow -cmt_reflow_mode = 0 # number +# If aligning comments, whether to mix with comments after '}' and #endif with +# less than three spaces before the comment. +align_right_cmt_mix = false # true/false -# If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars. -# Default is true. -cmt_indent_multi = true # false/true +# Whether to only align trailing comments that are at the same brace level. +align_right_cmt_same_level = false # true/false -# Whether to group c-comments that look like they are in a block -cmt_c_group = false # false/true +# Minimum column at which to align trailing comments. Comments which are +# aligned beyond this column, but which can be aligned in a lesser column, +# may be "pulled in". +# +# 0: Ignore (default). +align_right_cmt_at_col = 0 # unsigned number -# Whether to put an empty '/*' on the first line of the combined c-comment -cmt_c_nl_start = false # false/true +# The span for aligning function prototypes. +# +# 0: Don't align (default). +align_func_proto_span = 0 # unsigned number -# Whether to put a newline before the closing '*/' of the combined c-comment -cmt_c_nl_end = false # false/true +# The threshold for aligning function prototypes. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_func_proto_thresh = 0 # number -# Whether to group cpp-comments that look like they are in a block -cmt_cpp_group = false # false/true +# Minimum gap between the return type and the function name. +align_func_proto_gap = 0 # unsigned number + +# Whether to align function prototypes on the 'operator' keyword instead of +# what follows. +align_on_operator = false # true/false + +# Whether to mix aligning prototype and variable declarations. If true, +# align_var_def_XXX options are used instead of align_func_proto_XXX options. +align_mix_var_proto = false # true/false + +# Whether to align single-line functions with function prototypes. +# Uses align_func_proto_span. +align_single_line_func = false # true/false + +# Whether to align the open brace of single-line functions. +# Requires align_single_line_func=true. Uses align_func_proto_span. +align_single_line_brace = false # true/false + +# Gap for align_single_line_brace. +align_single_line_brace_gap = 0 # unsigned number + +# (OC) The span for aligning Objective-C message specifications. +# +# 0: Don't align (default). +align_oc_msg_spec_span = 0 # unsigned number + +# Whether to align macros wrapped with a backslash and a newline. This will +# not work right if the macro contains a multi-line comment. +align_nl_cont = false # true/false + +# Whether to align macro functions and variables together. +align_pp_define_together = false # true/false + +# The span for aligning on '#define' bodies. +# +# =0: Don't align (default) +# >0: Number of lines (including comments) between blocks +align_pp_define_span = 0 # unsigned number + +# The minimum space between label and value of a preprocessor define. +align_pp_define_gap = 0 # unsigned number + +# Whether to align lines that start with '<<' with previous '<<'. +# +# Default: true +align_left_shift = true # true/false + +# Whether to align comma-separated statements following '<<' (as used to +# initialize Eigen matrices). +align_eigen_comma_init = false # true/false + +# Whether to align text after 'asm volatile ()' colons. +align_asm_colon = false # true/false + +# (OC) Span for aligning parameters in an Objective-C message call +# on the ':'. +# +# 0: Don't align. +align_oc_msg_colon_span = 0 # unsigned number + +# (OC) Whether to always align with the first parameter, even if it is too +# short. +align_oc_msg_colon_first = false # true/false + +# (OC) Whether to align parameters in an Objective-C '+' or '-' declaration +# on the ':'. +align_oc_decl_colon = false # true/false + +# (OC) Whether to not align parameters in an Objectve-C message call if first +# colon is not on next line of the message call (the same way Xcode does +# aligment) +align_oc_msg_colon_xcode_like = false # true/false + +# +# Comment modification options +# + +# Try to wrap comments at N columns. +cmt_width = 0 # unsigned number + +# How to reflow comments. +# +# 0: No reflowing (apart from the line wrapping due to cmt_width) (default) +# 1: No touching at all +# 2: Full reflow (enable cmt_indent_multi for indent with line wrapping due to cmt_width) +cmt_reflow_mode = 0 # unsigned number + +# Path to a file that contains regular expressions describing patterns for +# which the end of one line and the beginning of the next will be folded into +# the same sentence or paragraph during full comment reflow. The regular +# expressions are described using ECMAScript syntax. The syntax for this +# specification is as follows, where "..." indicates the custom regular +# expression and "n" indicates the nth end_of_prev_line_regex and +# beg_of_next_line_regex regular expression pair: +# +# end_of_prev_line_regex[1] = "...$" +# beg_of_next_line_regex[1] = "^..." +# end_of_prev_line_regex[2] = "...$" +# beg_of_next_line_regex[2] = "^..." +# . +# . +# . +# end_of_prev_line_regex[n] = "...$" +# beg_of_next_line_regex[n] = "^..." +# +# Note that use of this option overrides the default reflow fold regular +# expressions, which are internally defined as follows: +# +# end_of_prev_line_regex[1] = "[\w,\]\)]$" +# beg_of_next_line_regex[1] = "^[\w,\[\(]" +# end_of_prev_line_regex[2] = "\.$" +# beg_of_next_line_regex[2] = "^[A-Z]" +cmt_reflow_fold_regex_file = "" # string + +# Whether to indent wrapped lines to the start of the encompassing paragraph +# during full comment reflow (cmt_reflow_mode = 2). Overrides the value +# specified by cmt_sp_after_star_cont. +# +# Note that cmt_align_doxygen_javadoc_tags overrides this option for +# paragraphs associated with javadoc tags +cmt_reflow_indent_to_paragraph_start = false # true/false + +# Whether to convert all tabs to spaces in comments. If false, tabs in +# comments are left alone, unless used for indenting. +cmt_convert_tab_to_spaces = true # true/false + +# Whether to apply changes to multi-line comments, including cmt_width, +# keyword substitution and leading chars. +# +# Default: true +cmt_indent_multi = true # true/false + +# Whether to align doxygen javadoc-style tags ('@param', '@return', etc.) +# and corresponding fields such that groups of consecutive block tags, +# parameter names, and descriptions align with one another. Overrides that +# which is specified by the cmt_sp_after_star_cont. If cmt_width > 0, it may +# be necessary to enable cmt_indent_multi and set cmt_reflow_mode = 2 +# in order to achieve the desired alignment for line-wrapping. +cmt_align_doxygen_javadoc_tags = false # true/false + +# The number of spaces to insert after the star and before doxygen +# javadoc-style tags (@param, @return, etc). Requires enabling +# cmt_align_doxygen_javadoc_tags. Overrides that which is specified by the +# cmt_sp_after_star_cont. +# +# Default: 1 +cmt_sp_before_doxygen_javadoc_tags = 1 # unsigned number + +# Whether to change trailing, single-line c-comments into cpp-comments. +cmt_trailing_single_line_c_to_cpp = true # true/false + +# Whether to group c-comments that look like they are in a block. +cmt_c_group = false # true/false + +# Whether to put an empty '/*' on the first line of the combined c-comment. +cmt_c_nl_start = false # true/false + +# Whether to add a newline before the closing '*/' of the combined c-comment. +cmt_c_nl_end = false # true/false + +# Whether to change cpp-comments into c-comments. +cmt_cpp_to_c = false # true/false + +# Whether to group cpp-comments that look like they are in a block. Only +# meaningful if cmt_cpp_to_c=true. +cmt_cpp_group = false # true/false # Whether to put an empty '/*' on the first line of the combined cpp-comment -cmt_cpp_nl_start = false # false/true +# when converting to a c-comment. +# +# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. +cmt_cpp_nl_start = false # true/false + +# Whether to add a newline before the closing '*/' of the combined cpp-comment +# when converting to a c-comment. +# +# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. +cmt_cpp_nl_end = false # true/false + +# Whether to put a star on subsequent comment lines. +cmt_star_cont = false # true/false + +# The number of spaces to insert at the start of subsequent comment lines. +cmt_sp_before_star_cont = 0 # unsigned number + +# The number of spaces to insert after the star on subsequent comment lines. +cmt_sp_after_star_cont = 0 # unsigned number -# Whether to put a newline before the closing '*/' of the combined cpp-comment -cmt_cpp_nl_end = false # false/true +# For multi-line comments with a '*' lead, remove leading spaces if the first +# and last lines of the comment are the same length. +# +# Default: true +cmt_multi_check_last = true # true/false -# Whether to change cpp-comments into c-comments -cmt_cpp_to_c = false # false/true +# For multi-line comments with a '*' lead, remove leading spaces if the first +# and last lines of the comment are the same length AND if the length is +# bigger as the first_len minimum. +# +# Default: 4 +cmt_multi_first_len_minimum = 4 # unsigned number + +# Path to a file that contains text to insert at the beginning of a file if +# the file doesn't start with a C/C++ comment. If the inserted text contains +# '$(filename)', that will be replaced with the current file's name. +cmt_insert_file_header = "" # string + +# Path to a file that contains text to insert at the end of a file if the +# file doesn't end with a C/C++ comment. If the inserted text contains +# '$(filename)', that will be replaced with the current file's name. +cmt_insert_file_footer = "" # string + +# Path to a file that contains text to insert before a function definition if +# the function isn't preceded by a C/C++ comment. If the inserted text +# contains '$(function)', '$(javaparam)' or '$(fclass)', these will be +# replaced with, respectively, the name of the function, the javadoc '@param' +# and '@return' stuff, or the name of the class to which the member function +# belongs. +cmt_insert_func_header = "" # string + +# Path to a file that contains text to insert before a class if the class +# isn't preceded by a C/C++ comment. If the inserted text contains '$(class)', +# that will be replaced with the class name. +cmt_insert_class_header = "" # string + +# Path to a file that contains text to insert before an Objective-C message +# specification, if the method isn't preceded by a C/C++ comment. If the +# inserted text contains '$(message)' or '$(javaparam)', these will be +# replaced with, respectively, the name of the function, or the javadoc +# '@param' and '@return' stuff. +cmt_insert_oc_msg_header = "" # string + +# Whether a comment should be inserted if a preprocessor is encountered when +# stepping backwards from a function name. +# +# Applies to cmt_insert_oc_msg_header, cmt_insert_func_header and +# cmt_insert_class_header. +cmt_insert_before_preproc = false # true/false -# Whether to put a star on subsequent comment lines -cmt_star_cont = false # false/true +# Whether a comment should be inserted if a function is declared inline to a +# class definition. +# +# Applies to cmt_insert_func_header. +# +# Default: true +cmt_insert_before_inlines = true # true/false -# The number of spaces to insert at the start of subsequent comment lines -cmt_sp_before_star_cont = 0 # number +# Whether a comment should be inserted if the function is a class constructor +# or destructor. +# +# Applies to cmt_insert_func_header. +cmt_insert_before_ctor_dtor = false # true/false -# The number of spaces to insert after the star on subsequent comment lines -cmt_sp_after_star_cont = 0 # number +# +# Code modifying options (non-whitespace) +# -# For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of -# the comment are the same length. Default=True -cmt_multi_check_last = true # false/true +# Add or remove braces on a single-line 'do' statement. +mod_full_brace_do = ignore # ignore/add/remove/force/not_defined -# The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. -# Will substitute $(filename) with the current file's name. -cmt_insert_file_header = "" # string +# Add or remove braces on a single-line 'for' statement. +mod_full_brace_for = add # ignore/add/remove/force/not_defined -# The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment. -# Will substitute $(filename) with the current file's name. -cmt_insert_file_footer = "" # string +# (Pawn) Add or remove braces on a single-line function definition. +mod_full_brace_function = ignore # ignore/add/remove/force/not_defined -# The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment. -# Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. -# Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... } -cmt_insert_func_header = "" # string +# 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 -# The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment. -# Will substitute $(class) with the class name. -cmt_insert_class_header = "" # string +# Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either +# have, or do not have, braces. If true, braces will be added if any block +# needs braces, and will only be removed if they can be removed from all +# blocks. +# +# Overrides mod_full_brace_if. +mod_full_brace_if_chain = false # true/false + +# Whether to add braces to all blocks of an 'if'/'else if'/'else' chain. +# If true, mod_full_brace_if_chain will only remove braces from an 'if' that +# does not have an 'else if' or 'else'. +mod_full_brace_if_chain_only = false # true/false -# The filename that contains text to insert before an Obj-C message specification if the method isn't preceded with a C/C++ comment. -# Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff. -cmt_insert_oc_msg_header = "" # string +# Add or remove braces on single-line 'while' statement. +mod_full_brace_while = add # ignore/add/remove/force/not_defined -# If a preprocessor is encountered when stepping backwards from a function name, then -# this option decides whether the comment should be inserted. -# Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header. -cmt_insert_before_preproc = false # false/true +# Add or remove braces on single-line 'using ()' statement. +mod_full_brace_using = ignore # ignore/add/remove/force/not_defined + +# Don't remove braces around statements that span N newlines +mod_full_brace_nl = 0 # unsigned number + +# Whether to prevent removal of braces from 'if'/'for'/'while'/etc. blocks +# which span multiple lines. +# +# Affects: +# mod_full_brace_for +# mod_full_brace_if +# mod_full_brace_if_chain +# mod_full_brace_if_chain_only +# mod_full_brace_while +# mod_full_brace_using +# +# Does not affect: +# mod_full_brace_do +# mod_full_brace_function +mod_full_brace_nl_block_rem_mlcond = false # true/false + +# Add or remove unnecessary parenthesis on 'return' statement. +mod_paren_on_return = ignore # ignore/add/remove/force/not_defined + +# (Pawn) Whether to change optional semicolons to real semicolons. +mod_pawn_semicolon = false # true/false + +# Whether to fully parenthesize Boolean expressions in 'while' and 'if' +# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'. +mod_full_paren_if_bool = false # true/false + +# Whether to remove superfluous semicolons. +mod_remove_extra_semicolon = false # true/false + +# Whether to remove duplicate include. +mod_remove_duplicate_include = false # true/false + +# If a function body exceeds the specified number of newlines and doesn't have +# a comment after the close brace, a comment will be added. +mod_add_long_function_closebrace_comment = 0 # unsigned number + +# If a namespace body exceeds the specified number of newlines and doesn't +# have a comment after the close brace, a comment will be added. +mod_add_long_namespace_closebrace_comment = 0 # unsigned number + +# If a class body exceeds the specified number of newlines and doesn't have a +# comment after the close brace, a comment will be added. +mod_add_long_class_closebrace_comment = 0 # unsigned number + +# If a switch body exceeds the specified number of newlines and doesn't have a +# comment after the close brace, a comment will be added. +mod_add_long_switch_closebrace_comment = 0 # unsigned number + +# If an #ifdef body exceeds the specified number of newlines and doesn't have +# a comment after the #endif, a comment will be added. +mod_add_long_ifdef_endif_comment = 0 # unsigned number + +# If an #ifdef or #else body exceeds the specified number of newlines and +# doesn't have a comment after the #else, a comment will be added. +mod_add_long_ifdef_else_comment = 0 # unsigned number + +# Whether to take care of the case by the mod_sort_xx options. +mod_sort_case_sensitive = false # true/false + +# Whether to sort consecutive single-line 'import' statements. +mod_sort_import = false # true/false + +# (C#) Whether to sort consecutive single-line 'using' statements. +mod_sort_using = false # true/false + +# Whether to sort consecutive single-line '#include' statements (C/C++) and +# '#import' statements (Objective-C). Be aware that this has the potential to +# break your code if your includes/imports have ordering dependencies. +mod_sort_include = true # true/false + +# Whether to prioritize '#include' and '#import' statements that contain +# filename without extension when sorting is enabled. +mod_sort_incl_import_prioritize_filename = false # true/false + +# Whether to prioritize '#include' and '#import' statements that does not +# contain extensions when sorting is enabled. +mod_sort_incl_import_prioritize_extensionless = false # true/false + +# Whether to prioritize '#include' and '#import' statements that contain +# angle over quotes when sorting is enabled. +mod_sort_incl_import_prioritize_angle_over_quotes = true # true/false + +# Whether to ignore file extension in '#include' and '#import' statements +# for sorting comparison. +mod_sort_incl_import_ignore_extension = false # true/false + +# Whether to group '#include' and '#import' statements when sorting is enabled. +mod_sort_incl_import_grouping_enabled = true # true/false + +# Whether to move a 'break' that appears after a fully braced 'case' before +# the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'. +mod_move_case_break = 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 + +# 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 = ignore # ignore/add/remove/force/not_defined + +# (OC) Whether to organize the properties. If true, properties will be +# rearranged according to the mod_sort_oc_property_*_weight factors. +mod_sort_oc_properties = false # true/false + +# (OC) Weight of a class property modifier. +mod_sort_oc_property_class_weight = 0 # number + +# (OC) Weight of 'atomic' and 'nonatomic'. +mod_sort_oc_property_thread_safe_weight = 0 # number + +# (OC) Weight of 'readwrite' when organizing properties. +mod_sort_oc_property_readwrite_weight = 0 # number + +# (OC) Weight of a reference type specifier ('retain', 'copy', 'assign', +# 'weak', 'strong') when organizing properties. +mod_sort_oc_property_reference_weight = 0 # number + +# (OC) Weight of getter type ('getter=') when organizing properties. +mod_sort_oc_property_getter_weight = 0 # number + +# (OC) Weight of setter type ('setter=') when organizing properties. +mod_sort_oc_property_setter_weight = 0 # number + +# (OC) Weight of nullability type ('nullable', 'nonnull', 'null_unspecified', +# 'null_resettable') when organizing properties. +mod_sort_oc_property_nullability_weight = 0 # number # # Preprocessor options # -# Control indent of preprocessors inside #if blocks at brace level 0 -pp_indent = ignore # ignore/add/remove/force +# Add or remove indentation of preprocessor directives inside #if blocks +# at brace level 0 (file-level). +pp_indent = ignore # ignore/add/remove/force/not_defined -# Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) -pp_indent_at_level = false # false/true +# Whether to indent #if/#else/#endif at the brace level. If false, these are +# indented from column 1. +pp_indent_at_level = false # true/false + +# Specifies the number of columns to indent preprocessors per level +# at brace level 0 (file-level). If pp_indent_at_level=false, also specifies +# the number of columns to indent preprocessors per level +# at brace level > 0 (function-level). +# +# Default: 1 +pp_indent_count = 1 # unsigned number -# If pp_indent_at_level=false, specifies the number of columns to indent per level. Default=1. -pp_indent_count = 1 # number +# Add or remove space after # based on pp_level of #if blocks. +pp_space = ignore # ignore/add/remove/force/not_defined -# Add or remove space after # based on pp_level of #if blocks -pp_space = add # ignore/add/remove/force +# Sets the number of spaces per level added with pp_space. +pp_space_count = 0 # unsigned number -# Sets the number of spaces added with pp_space -pp_space_count = 0 # number +# The indent for '#region' and '#endregion' in C# and '#pragma region' in +# C/C++. Negative values decrease indent down to the first column. +pp_indent_region = 0 # number -# The indent for #region and #endregion in C# and '#pragma region' in C/C++ -pp_indent_region = 0 # number +# Whether to indent the code between #region and #endregion. +pp_region_indent_code = false # true/false -# Whether to indent the code between #region and #endregion -pp_region_indent_code = false # false/true +# If pp_indent_at_level=true, sets the indent for #if, #else and #endif when +# not at file-level. Negative values decrease indent down to the first column. +# +# =0: Indent preprocessors using output_tab_size +# >0: Column at which all preprocessors will be indented +pp_indent_if = 0 # number + +# Whether to indent the code between #if, #else and #endif. +pp_if_indent_code = false # true/false + +# Whether to indent the body of an #if that encompasses all the code in the file. +pp_indent_in_guard = false # true/false + +# Whether to indent '#define' at the brace level. If false, these are +# indented from column 1. +pp_define_at_level = false # true/false + +# Whether to ignore the '#define' body while formatting. +pp_ignore_define_body = false # true/false + +# Whether to indent case statements between #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the case statements +# directly inside of. +# +# Default: true +pp_indent_case = true # true/false + +# Whether to indent whole function definitions between #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the function definition +# is directly inside of. +# +# Default: true +pp_indent_func_def = true # true/false + +# Whether to indent extern C blocks between #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the extern block is +# directly inside of. +# +# Default: true +pp_indent_extern = true # true/false + +# Whether to indent braces directly inside #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the braces are directly +# inside of. +# +# Default: true +pp_indent_brace = true # true/false + +# +# Sort includes options +# -# If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level -pp_indent_if = 0 # number +# The regex for include category with priority 0. +include_category_0 = "" # string -# Control whether to indent the code between #if, #else and #endif when not at file-level -pp_if_indent_code = false # false/true +# The regex for include category with priority 1. +include_category_1 = "" # string -# Whether to indent '#define' at the brace level (true) or from column 1 (false) -pp_define_at_level = false # false/true +# The regex for include category with priority 2. +include_category_2 = "" # string -# You can force a token to be a type with the 'type' option. -# Example: -# type myfoo1 myfoo2 # -# You can create custom macro-based indentation using macro-open, -# macro-else and macro-close. -# Example: -# macro-open BEGIN_TEMPLATE_MESSAGE_MAP -# macro-open BEGIN_MESSAGE_MAP -# macro-close END_MESSAGE_MAP +# Use or Do not Use options +# + +# true: indent_func_call_param will be used (default) +# false: indent_func_call_param will NOT be used +# +# Default: true +use_indent_func_call_param = true # true/false + +# The value of the indentation for a continuation line is calculated +# differently if the statement is: +# - a declaration: your case with QString fileName ... +# - an assignment: your case with pSettings = new QSettings( ... +# +# At the second case the indentation value might be used twice: +# - at the assignment +# - at the function call (if present) +# +# To prevent the double use of the indentation value, use this option with the +# value 'true'. +# +# true: indent_continue will be used only once +# false: indent_continue will be used every time (default) +use_indent_continue_only_once = false # true/false + +# The value might be used twice: +# - at the assignment +# - at the opening brace +# +# To prevent the double use of the indentation value, use this option with the +# value 'true'. +# +# true: indentation will be used only once +# false: indentation will be used every time (default) +indent_cpp_lambda_only_once = false # true/false + +# Whether sp_after_angle takes precedence over sp_inside_fparen. This was the +# historic behavior, but is probably not the desired behavior, so this is off +# by default. +use_sp_after_angle_always = false # true/false + +# Whether to apply special formatting for Qt SIGNAL/SLOT macros. Essentially, +# this tries to format these so that they match Qt's normalized form (i.e. the +# result of QMetaObject::normalizedSignature), which can slightly improve the +# performance of the QObject::connect call, rather than how they would +# otherwise be formatted. +# +# See options_for_QT.cpp for details. +# +# Default: true +use_options_overriding_for_qt_macros = true # true/false + +# If true: the form feed character is removed from the list of whitespace +# characters. See https://en.cppreference.com/w/cpp/string/byte/isspace. +use_form_feed_no_more_as_whitespace_character = false # true/false + +# +# Warn levels - 1: error, 2: warning (default), 3: note +# + +# (C#) Warning is given if doing tab-to-\t replacement and we have found one +# in a C# verbatim string literal. +# +# Default: 2 +warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number + +# Limit the number of loops. +# Used by uncrustify.cpp to exit from infinite loop. +# 0: no limit. +debug_max_number_of_loops = 0 # number + +# Set the number of the line to protocol; +# Used in the function prot_the_line if the 2. parameter is zero. +# 0: nothing protocol. +debug_line_number_to_protocol = 0 # number + +# Set the number of second(s) before terminating formatting the current file, +# 0: no timeout. +# only for linux +debug_timeout = 0 # number + +# Set the number of characters to be printed if the text is too long, +# 0: do not truncate. +debug_truncate = 0 # unsigned number + +# Meaning of the settings: +# Ignore - do not do any changes +# Add - makes sure there is 1 or more space/brace/newline/etc +# Force - makes sure there is exactly 1 space/brace/newline/etc, +# behaves like Add in some contexts +# Remove - removes space/brace/newline/etc +# +# +# - Token(s) can be treated as specific type(s) with the 'set' option: +# `set tokenType tokenString [tokenString...]` +# +# Example: +# `set BOOL __AND__ __OR__` +# +# tokenTypes are defined in src/token_enum.h, use them without the +# 'CT_' prefix: 'CT_BOOL' => 'BOOL' +# +# +# - Token(s) can be treated as type(s) with the 'type' option. +# `type tokenString [tokenString...]` +# +# Example: +# `type int c_uint_8 Rectangle` +# +# This can also be achieved with `set TYPE int c_uint_8 Rectangle` +# +# +# To embed whitespace in tokenStrings use the '\' escape character, or quote +# the tokenStrings. These quotes are supported: "'` +# +# +# - Support for the auto detection of languages through the file ending can be +# added using the 'file_ext' command. +# `file_ext langType langString [langString..]` +# +# Example: +# `file_ext CPP .ch .cxx .cpp.in` +# +# langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use +# them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP' # -# You can assign any keyword to any type with the set option. -# set func_call_user _ N_ # -# The full syntax description of all custom definition config entries -# is shown below: +# - Custom macro-based indentation can be set up using 'macro-open', +# 'macro-else' and 'macro-close'. +# `(macro-open | macro-else | macro-close) tokenString` # -# define custom tokens as: -# - embed whitespace in token using '' escape character, or -# put token in quotes -# - these: ' " and ` are recognized as quote delimiters +# Example: +# `macro-open BEGIN_TEMPLATE_MESSAGE_MAP` +# `macro-open BEGIN_MESSAGE_MAP` +# `macro-close END_MESSAGE_MAP` # -# type token1 token2 token3 ... -# ^ optionally specify multiple tokens on a single line -# define def_token output_token -# ^ output_token is optional, then NULL is assumed -# macro-open token -# macro-close token -# macro-else token -# set id token1 token2 ... -# ^ optionally specify multiple tokens on a single line -# ^ id is one of the names in token_enum.h sans the CT_ prefix, -# e.g. PP_PRAGMA # -# all tokens are separated by any mix of ',' commas, '=' equal signs -# and whitespace (space, tab) +set PREPROC FUNC_API_CHECK_TEXTLOCK +set PREPROC FUNC_API_DEPRECATED_SINCE +set PREPROC FUNC_API_FAST +set PREPROC FUNC_API_LUA_ONLY +set PREPROC FUNC_API_NOEXPORT +set PREPROC FUNC_API_REMOTE_ONLY +set PREPROC FUNC_API_SINCE +set PREPROC FUNC_ATTR_ALWAYS_INLINE +set PREPROC FUNC_ATTR_CONST +set PREPROC FUNC_ATTR_MALLOC +set PREPROC FUNC_ATTR_NONNULL_ALL +set PREPROC FUNC_ATTR_NONNULL_ARG +set PREPROC FUNC_ATTR_NONNULL_RET +set PREPROC FUNC_ATTR_NORETURN +set PREPROC FUNC_ATTR_NO_SANITIZE_UNDEFINED +set PREPROC FUNC_ATTR_PRINTF +set PREPROC FUNC_ATTR_PURE +set PREPROC FUNC_ATTR_UNUSED +set PREPROC FUNC_ATTR_WARN_UNUSED_RESULT +set PREPROC REAL_FATTR_ALWAYS_INLINE +set PREPROC REAL_FATTR_CONST +set PREPROC REAL_FATTR_NONNULL_ALL +set PREPROC REAL_FATTR_PURE +set PREPROC REAL_FATTR_WARN_UNUSED_RESULT +# option(s) with 'not default' value: 59 # diff --git a/man/nvim.1 b/man/nvim.1 index ca0f41d489..3f57f397d2 100644 --- a/man/nvim.1 +++ b/man/nvim.1 @@ -100,7 +100,7 @@ Useful for scripting because it does NOT start a UI, unlike .Ic :help silent-mode .It Fl d Diff mode. -Show the difference between two to four files, similar to +Show the difference between two to eight files, similar to .Xr sdiff 1 . .Ic ":help diff" .It Fl R diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 1ac74b5785..48a9657cf5 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -172,6 +172,17 @@ func dist#ft#FTent() setf dtd endfunc +func dist#ft#ExCheck() + let lines = getline(1, min([line("$"), 100])) + if exists('g:filetype_euphoria') + exe 'setf ' . g:filetype_euphoria + elseif match(lines, '^--\|^ifdef\>\|^include\>') > -1 + setf euphoria3 + else + setf elixir + endif +endfunc + func dist#ft#EuphoriaCheck() if exists('g:filetype_euphoria') exe 'setf ' . g:filetype_euphoria @@ -253,6 +264,13 @@ func dist#ft#ProtoCheck(default) endfunc func dist#ft#FTm() + if exists("g:filetype_m") + exe "setf " . g:filetype_m + return + endif + + let octave_block_terminators = '\<end\%(_try_catch\|classdef\|enumeration\|events\|for\|function\|if\|methods\|parfor\|properties\|switch\|while\)\>' + let n = 1 let saw_comment = 0 " Whether we've seen a multiline comment leader. while n < 100 @@ -267,6 +285,13 @@ func dist#ft#FTm() setf objc return endif + if line =~ '^\s*\%(#\|%!\|[#%]{\=\s*$\)' || + \ line =~ '^\s*unwind_protect\>' || + \ line =~ '\%(^\|;\)\s*' .. octave_block_terminators + setf octave + return + endif + " TODO: could be Matlab or Octave if line =~ '^\s*%' setf matlab return @@ -287,11 +312,8 @@ func dist#ft#FTm() " or Murphi based on the comment leader. Assume the former as it is more " common. setf objc - elseif exists("g:filetype_m") - " Use user specified default filetype for .m - exe "setf " . g:filetype_m else - " Default is matlab + " Default is Matlab setf matlab endif endfunc diff --git a/runtime/autoload/health/lsp.vim b/runtime/autoload/health/lsp.vim new file mode 100644 index 0000000000..2d2ba91cdf --- /dev/null +++ b/runtime/autoload/health/lsp.vim @@ -0,0 +1,5 @@ +function! health#lsp#check() abort + call health#report_start('Checking language server client configuration') + lua require 'vim.lsp.health'.check_health() +endfunction + diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 4f556e6e87..8bf95651b7 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -65,6 +65,7 @@ function! man#open_page(count, mods, ...) abort let b:man_sect = sect endfunction +" Called when a man:// buffer is opened. function! man#read_page(ref) abort try let [sect, name] = s:extract_sect_and_name_ref(a:ref) @@ -121,6 +122,15 @@ function! s:system(cmd, ...) abort return opts.stdout endfunction +function! s:set_options(pager) abort + setlocal filetype=man + setlocal noswapfile buftype=nofile bufhidden=hide + setlocal nomodified readonly nomodifiable + if a:pager + nnoremap <silent> <buffer> <nowait> q :lclose<CR>:q<CR> + endif +endfunction + function! s:get_page(path) abort " Disable hard-wrap by using a big $MANWIDTH (max 1000 on some systems #9065). " Soft-wrap: ftplugin/man.vim sets wrap/breakindent/…. @@ -134,9 +144,7 @@ function! s:get_page(path) abort endfunction function! s:put_page(page) abort - setlocal modifiable - setlocal noreadonly - setlocal noswapfile + setlocal modifiable noreadonly noswapfile silent keepjumps %delete _ silent put =a:page while getline(1) =~# '^\s*$' @@ -148,7 +156,7 @@ function! s:put_page(page) abort silent! keeppatterns keepjumps %s/\s\{199,}/\=repeat(' ', 10)/g 1 lua require("man").highlight_man_page() - setlocal filetype=man + call s:set_options(v:false) endfunction function! man#show_toc() abort @@ -397,6 +405,7 @@ function! s:format_candidate(path, psect) abort endif endfunction +" Called when Nvim is invoked as $MANPAGER. function! man#init_pager() abort " https://github.com/neovim/neovim/issues/6828 let og_modifiable = &modifiable @@ -420,6 +429,7 @@ function! man#init_pager() abort execute 'silent file man://'.tolower(fnameescape(ref)) endif + call s:set_options(v:true) let &l:modifiable = og_modifiable endfunction diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 07f37d604f..991bed6bbd 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -156,7 +156,16 @@ function! s:clipboard.get(reg) abort elseif s:selections[a:reg].owner > 0 return s:selections[a:reg].data end - return s:try_cmd(s:paste[a:reg]) + + let clipboard_data = s:try_cmd(s:paste[a:reg]) + if match(&clipboard, '\v(unnamed|unnamedplus)') >= 0 + \ && type(clipboard_data) == v:t_list + \ && get(s:selections[a:reg].data, 0, []) ==# clipboard_data + " When system clipboard return is same as our cache return the cache + " as it contains regtype information + return s:selections[a:reg].data + end + return clipboard_data endfunction function! s:clipboard.set(lines, regtype, reg) abort @@ -175,6 +184,9 @@ function! s:clipboard.set(lines, regtype, reg) abort if s:cache_enabled == 0 call s:try_cmd(s:copy[a:reg], a:lines) + "Cache it anyway we can compare it later to get regtype of the yank + let s:selections[a:reg] = copy(s:selection) + let s:selections[a:reg].data = [a:lines, a:regtype] return 0 end diff --git a/runtime/autoload/rubycomplete.vim b/runtime/autoload/rubycomplete.vim index e8a1879668..3677b25aeb 100644 --- a/runtime/autoload/rubycomplete.vim +++ b/runtime/autoload/rubycomplete.vim @@ -3,7 +3,7 @@ " Maintainer: Mark Guzman <segfault@hasno.info> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Feb 25 +" Last Change: 2020 Apr 12 " ---------------------------------------------------------------------------- " " Ruby IRB/Complete author: Keiju ISHITSUKA(keiju@ishitsuka.com) @@ -501,13 +501,8 @@ class VimRubyCompletion return if rails_base == nil $:.push rails_base unless $:.index( rails_base ) - rails_config = rails_base + "config/" - rails_lib = rails_base + "lib/" - $:.push rails_config unless $:.index( rails_config ) - $:.push rails_lib unless $:.index( rails_lib ) - - bootfile = rails_config + "boot.rb" - envfile = rails_config + "environment.rb" + bootfile = rails_base + "config/boot.rb" + envfile = rails_base + "config/environment.rb" if File.exists?( bootfile ) && File.exists?( envfile ) begin require bootfile diff --git a/runtime/compiler/spectral.vim b/runtime/compiler/spectral.vim new file mode 100644 index 0000000000..bd13c51f43 --- /dev/null +++ b/runtime/compiler/spectral.vim @@ -0,0 +1,17 @@ +" Vim compiler file +" Compiler: Spectral for YAML +" Maintainer: Romain Lafourcade <romainlafourcade@gmail.com> +" Last Change: 2021 July 21 + +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/yamllint.vim b/runtime/compiler/yamllint.vim new file mode 100644 index 0000000000..889b04b63c --- /dev/null +++ b/runtime/compiler/yamllint.vim @@ -0,0 +1,16 @@ +" Vim compiler file +" Compiler: Yamllint for YAML +" Maintainer: Romain Lafourcade <romainlafourcade@gmail.com> +" Last Change: 2021 July 21 + +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/doc/api.txt b/runtime/doc/api.txt index 6b3b0f7762..df345e4981 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -134,6 +134,14 @@ lines, 0-based columns): |nvim_win_get_cursor()| |nvim_win_set_cursor()| +Exception: the following API functions use |extmarks| indexing (0-based +indices, end-inclusive): + + |nvim_buf_del_extmark()| + |nvim_buf_get_extmark_by_id()| + |nvim_buf_get_extmarks()| + |nvim_buf_set_extmark()| + *api-fast* Most API functions are "deferred": they are queued on the main loop and processed sequentially with normal input. So if the editor is waiting for @@ -422,7 +430,7 @@ Buffer text can be highlighted by typical mechanisms (syntax highlighting, options from the current window; specify `style=minimal` in |nvim_open_win()| to disable various visual features such as the 'number' column. -Currently, floating windows don't support widgets like border or scrollbar. +Currently, floating windows don't support some widgets like scrollbar. Example: create a float with scratch buffer: > @@ -436,36 +444,76 @@ Example: create a float with scratch buffer: > > ============================================================================== -Extended marks *api-extended-marks* +Extended marks *api-extended-marks* *extmarks* Extended marks (extmarks) represent buffer annotations that track text changes -in the buffer. They could be used to represent cursors, folds, misspelled -words, and anything else that needs to track a logical location in the buffer -over time. +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: > + + f o o b a r line contents + 0 1 2 3 4 5 character positions (0-based) + 0 1 2 3 4 5 6 extmark positions (0-based) + +Extmarks have "forward gravity": if you place the cursor directly on an +extmark position and enter some text, the extmark migrates forward. > + + f o o|b a r line (| = cursor) + 3 extmark + + f o o z|b a r line (| = cursor) + 4 extmark (after typing "z") + +If an extmark is on the last index of a line and you inputsa newline at that +point, the extmark will accordingly migrate to the next line: > + + f o o z b a r| line (| = cursor) + 7 extmark + + f o o z b a r first line + extmarks (none present) + | second line (| = cursor) + 0 extmark (after typing <CR>) + Example: -We will set an extmark at the first row and third column. |api-indexing| is -zero-indexed, so we use row=0 and column=2. Passing id=0 creates a new mark -and returns the id: > +Let's set an extmark at the first row (row=0) and third column (column=2). +|api-indexing| Passing id=0 creates a new mark and returns the id: > + 01 2345678 + 0 ex|ample.. +< ^ extmark position +> let g:mark_ns = nvim_create_namespace('myplugin') - let g:mark_id = nvim_buf_set_extmark(0, g:mark_ns, 0, 0, 2, {}) - -We can get a mark by its id: > + let g:mark_id = nvim_buf_set_extmark(0, g:mark_ns, 0, 2, {}) +< +We can get the mark by its id: > - echo nvim_buf_get_extmark_by_id(0, g:mark_ns, g:mark_id) + echo nvim_buf_get_extmark_by_id(0, g:mark_ns, g:mark_id, {}) => [0, 2] -We can get all marks in a buffer for our namespace (or by a range): > +We can get all marks in a buffer by |namespace| (or by a range): > echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, {}) => [[1, 0, 2]] -Deleting all text surrounding an extmark does not remove the extmark. To -remove an extmark use |nvim_buf_del_extmark()|. +Deleting all surrounding text does NOT remove an extmark! To remove extmarks +use |nvim_buf_del_extmark()|. Deleting "x" in our example: > -Namespaces allow your plugin to manage only its own extmarks, ignoring those + 0 12345678 + 0 e|ample.. +< ^ extmark position +> + echo nvim_buf_get_extmark_by_id(0, g:mark_ns, g:mark_id, {}) + => [0, 1] +< + Note: Extmark "gravity" decides how it will shift after a text edit. + See |nvim_buf_set_extmark()| + +Namespaces allow any plugin to manage only its own extmarks, ignoring those created by another plugin. Extmark positions changed by an edit will be restored on undo/redo. Creating @@ -661,8 +709,7 @@ nvim_create_namespace({name}) *nvim_create_namespace()* Creates a new namespace, or gets an existing one. Namespaces are used for buffer highlights and virtual text, - see |nvim_buf_add_highlight()| and - |nvim_buf_set_virtual_text()|. + see |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|. Namespaces can be named or anonymous. If `name` matches an existing namespace, the associated id is returned. If `name` @@ -780,10 +827,9 @@ nvim_feedkeys({keys}, {mode}, {escape_csi}) *nvim_feedkeys()* On execution error: does not fail, but updates v:errmsg. - If you need to input sequences like <C-o> use - |nvim_replace_termcodes| to replace the termcodes and then - pass the resulting string to nvim_feedkeys. You'll also want - to enable escape_csi. + To input sequences like <C-o> use |nvim_replace_termcodes()| + (typically with escape_csi=true) to replace the keycodes. Then + pass the result to nvim_feedkeys(). Example: > :let key = nvim_replace_termcodes("<C-o>", v:true, v:false, v:true) @@ -1104,7 +1150,7 @@ nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col}) intermediate mouse positions will be ignored. It should be used to implement real-time mouse input in a GUI. The deprecated pseudokey form ("<LeftMouse><col,row>") of - |nvim_input()| has the same limitiation. + |nvim_input()| has the same limitation. Attributes: ~ {fast} @@ -1183,7 +1229,7 @@ nvim_notify({msg}, {log_level}, {opts}) *nvim_notify()* Notify the user with a message Relays the call to vim.notify . By default forwards your - message in the echo area but can be overriden to trigger + message in the echo area but can be overridden to trigger desktop notifications. Parameters: ~ @@ -1197,7 +1243,7 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()* By default (and currently the only option) the terminal will not be connected to an external process. Instead, input send on the channel will be echoed directly by the terminal. This - is useful to disply ANSI terminal sequences returned as part + is useful to display ANSI terminal sequences returned as part of a rpc message, or similar. Note: to directly initiate the terminal using the right size, @@ -1325,33 +1371,37 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* and clearing the |EndOfBuffer| region in 'winhighlight'. - • `border`: style of (optional) window border. This can - either be a string or an array. the string - values are: - • "none" No border. This is the default - • "single" a single line box - • "double" a double line box - • "shadow" a drop shadow effect by blending - with the background. If it is an array it - should be an array of eight items or any - divisor of eight. The array will specifify - the eight chars building up the border in a - clockwise fashion starting with the top-left - corner. As, an example, the double box style - could be specified as: [ "â•”", "â•" ,"â•—", "â•‘", - "â•", "â•", "╚", "â•‘" ] if the number of chars - are less than eight, they will be repeated. - Thus an ASCII border could be specified as: - [ "/", "-", "\\", "|" ] or all chars the - same as: [ "x" ] An empty string can be used - to turn off a specific border, for instance: - [ "", "", "", ">", "", "", "", "<" ] will - only make vertical borders but not - horizontal ones. By default `FloatBorder` - highlight is used which links to `VertSplit` - when not defined. It could also be specified - by character: [ {"+", "MyCorner"}, {"x", - "MyBorder"} ] + • `border`: Style of (optional) window border. This can + either be a string or an array. The string + values are + • "none": No border (default). + • "single": A single line box. + • "double": A double line box. + • "rounded": Like "single", but with rounded + corners ("â•" etc.). + • "solid": Adds padding by a single whitespace + cell. + • "shadow": A drop shadow effect by blending + with the background. + • If it is an array, it should have a length + of eight or any divisor of eight. The array + will specifify the eight chars building up + the border in a clockwise fashion starting + with the top-left corner. As an example, the + double box style could be specified as [ + "â•”", "â•" ,"â•—", "â•‘", "â•", "â•", "╚", "â•‘" ]. If + the number of chars are less than eight, + they will be repeated. Thus an ASCII border + could be specified as [ "/", "-", "\\", "|" + ], or all chars the same as [ "x" ]. An + empty string can be used to turn off a + specific border, for instance, [ "", "", "", + ">", "", "", "", "<" ] will only make + vertical borders but not horizontal ones. By + default, `FloatBorder` highlight is used, + which links to `VertSplit` when not defined. + It could also be specified by character: [ + {"+", "MyCorner"}, {"x", "MyBorder"} ]. • `noautocmd` : If true then no buffer-related autocommand events such as |BufEnter|, @@ -1415,8 +1465,9 @@ nvim_parse_expression({expr}, {flags}, {highlight}) • "len": Amount of bytes successfully parsed. With flags equal to "" that should be equal to the length of expr - string. (“Sucessfully parsed†here means “participated - in AST creationâ€, not “till the first errorâ€.) + string. (“Successfully parsed†here means + “participated in AST creationâ€, not “till the first + errorâ€.) • "ast": AST, either nil or a dictionary with these keys: • "type": node type, one of the value names from @@ -1677,7 +1728,7 @@ nvim_set_decoration_provider({ns_id}, {opts}) Note: this function should not be called often. Rather, the callbacks themselves can be used to throttle unneeded callbacks. the `on_start` callback can return `false` to - disable the provider until the next redraw. Similarily, return + disable the provider until the next redraw. Similarly, return `false` in `on_win` will skip the `on_lines` calls for that window (but any extmarks set in `on_win` will still be used). A plugin managing multiple sources of decoration should @@ -1717,7 +1768,7 @@ nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()* Parameters: ~ {ns_id} number of namespace for this highlight {name} highlight group name, like ErrorMsg - {val} highlight definiton map, like + {val} highlight definition map, like |nvim_get_hl_by_name|. in addition the following keys are also recognized: `default` : don't override existing definition, like `hi default` @@ -2068,7 +2119,7 @@ nvim_buf_get_commands({buffer}, {opts}) *nvim_buf_get_commands()* *nvim_buf_get_extmark_by_id()* nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}, {opts}) - Returns position for a given extmark id + Gets the position (0-indexed) of an extmark {id}. Parameters: ~ {buffer} Buffer handle, or 0 for current buffer @@ -2078,7 +2129,8 @@ nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}, {opts}) • details: Whether to include the details dict Return: ~ - (row, col) tuple or empty list () if extmark id was absent + 0-indexed (row, col) tuple or empty list () if extmark id + was absent *nvim_buf_get_extmarks()* nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) @@ -2118,10 +2170,12 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) Parameters: ~ {buffer} Buffer handle, or 0 for current buffer {ns_id} Namespace id from |nvim_create_namespace()| - {start} Start of range, given as (row, col) or valid - extmark id (whose position defines the bound) - {end} End of range, given as (row, col) or valid - extmark id (whose position defines the bound) + {start} Start of range, given as 0-indexed (row, col) or + valid extmark id (whose position defines the + bound) + {end} End of range (inclusive), given as 0-indexed + (row, col) or valid extmark id (whose position + defines the bound) {opts} Optional parameters. Keys: • limit: Maximum number of marks to return • details Whether to include the details dict @@ -2285,7 +2339,16 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) • hl_group : name of the highlight group used to highlight this mark. • virt_text : virtual text to link to this mark. - • virt_text_pos : positioning of virtual text. + A list of [text, highlight] tuples, each + representing a text chunk with specified + highlight. `highlight` element can either be a + a single highlight group, or an array of + multiple highlight groups that will be stacked + (highest priority last). A highlight group can + be supplied either as a string or as an + integer, the latter which can be obtained + using |nvim_get_hl_id_by_name|. + • virt_text_pos : position of virtual text. Possible values: • "eol": right after eol character (default) • "overlay": display over the specified @@ -2422,44 +2485,6 @@ nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()* {name} Variable name {value} Variable value - *nvim_buf_set_virtual_text()* -nvim_buf_set_virtual_text({buffer}, {src_id}, {line}, {chunks}, {opts}) - Set the virtual text (annotation) for a buffer line. - - By default (and currently the only option) the text will be - placed after the buffer text. Virtual text will never cause - reflow, rather virtual text will be truncated at the end of - the screen line. The virtual text will begin one cell - (|lcs-eol| or space) after the ordinary text. - - Namespaces are used to support batch deletion/updating of - virtual text. To create a namespace, use - |nvim_create_namespace()|. Virtual text is cleared using - |nvim_buf_clear_namespace()|. The same `ns_id` can be used for - both virtual text and highlights added by - |nvim_buf_add_highlight()|, both can then be cleared with a - single call to |nvim_buf_clear_namespace()|. If the virtual - text never will be cleared by an API call, pass `ns_id = -1` . - - As a shorthand, `ns_id = 0` can be used to create a new - namespace for the virtual text, the allocated id is then - returned. - - Parameters: ~ - {buffer} Buffer handle, or 0 for current buffer - {ns_id} Namespace to use or 0 to create a namespace, or - -1 for a ungrouped annotation - {line} Line to annotate with virtual text - (zero-indexed) - {chunks} A list of [text, hl_group] arrays, each - representing a text chunk with specified - highlight. `hl_group` element can be omitted for - no highlight. - {opts} Optional parameters. Currently not used. - - Return: ~ - The ns_id that was used - ============================================================================== Window Functions *api-window* diff --git a/runtime/doc/arabic.txt b/runtime/doc/arabic.txt index df91b8d065..5d3bf7a761 100644 --- a/runtime/doc/arabic.txt +++ b/runtime/doc/arabic.txt @@ -171,6 +171,13 @@ o Enable Arabic settings [short-cut] and its support is preferred due to its level of offerings. 'arabic' when 'termbidi' is enabled only sets the keymap. + For vertical window isolation while setting 'termbidi' an LTR + vertical separator like "l" or "ð–¨" may be used. It may also be + hidden by changing its color to the foreground color: > + :set fillchars=vert:l + :hi VertSplit ctermbg=White +< Note that this is a workaround, not a proper solution. + If, on the other hand, you'd like to be verbose and explicit and are opting not to use the 'arabic' short-cut command, here's what is needed (i.e. if you use ':set arabic' you can skip this section) - diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index bf94383ec4..7a53f17a78 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -232,7 +232,7 @@ BufDelete Before deleting a buffer from the buffer list. *BufEnter* BufEnter After entering a buffer. Useful for setting options for a file type. Also executed when - starting to edit a buffer, after the + starting to edit a buffer. After |BufAdd|. After |BufReadPost|. *BufFilePost* @@ -499,8 +499,10 @@ CursorMoved After the cursor was moved in Normal or Visual mode or to another window. Also when the text of the cursor line has been changed, e.g. with "x", "rx" or "p". - Not triggered when there is typeahead or when - an operator is pending. + Not triggered when there is typeahead, while + executing a script file, when an operator is + pending, or when moving to another window while + remaining at the same cursor position. For an example see |match-parens|. Note: Cannot be skipped with |:noautocmd|. Careful: This is triggered very often, don't @@ -798,9 +800,10 @@ QuickFixCmdPost Like QuickFixCmdPre, but after a quickfix *QuitPre* QuitPre When using `:quit`, `:wq` or `:qall`, before deciding whether it closes the current window - or quits Vim. Can be used to close any - non-essential window if the current window is - the last ordinary window. + or quits Vim. For `:wq` the buffer is written + before QuitPre is triggered. Can be used to + close any non-essential window if the current + window is the last ordinary window. See also |ExitPre|, ||WinClosed|. *RemoteReply* RemoteReply When a reply from a Vim that functions as @@ -831,16 +834,16 @@ 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 script. |:source| +SourcePre Before sourcing a vim/lua file. |:source| <afile> is the name of the file being sourced. *SourcePost* -SourcePost After sourcing a Vim script. |:source| +SourcePost After sourcing a vim/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 script. |:source| +SourceCmd When sourcing a vim/lua file. |:source| <afile> is the name of the file being sourced. The autocommand must source this file. |Cmd-event| @@ -914,6 +917,8 @@ TermLeave After leaving |Terminal-mode|. After TermClose. *TermClose* TermClose When a |terminal| job ends. + Sets these |v:event| keys: + status *TermResponse* TermResponse After the response to t_RV is received from the terminal. The value of |v:termresponse| diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 924401be74..2b799e3e27 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -568,9 +568,7 @@ with ".". Vim does not recognize a comment (starting with '"') after the option is empty (this is the default), use the internal formatting function |C-indenting| and |'lisp'|. But when 'indentexpr' is not empty, it will - be used instead |indent-expression|. When Vim was - compiled without internal formatting then the "indent" - program is used as a last resort. + be used instead |indent-expression|. *==* == Filter [count] lines like with ={motion}. @@ -749,12 +747,14 @@ For compatibility with Vi these two exceptions are allowed: "\/{string}/" and "\?{string}?" do the same as "//{string}/r". "\&{string}&" does the same as "//{string}/". *pattern-delimiter* *E146* -Instead of the '/' which surrounds the pattern and replacement string, you -can use any other single-byte character, but not an alphanumeric character, -'\', '"' or '|'. This is useful if you want to include a '/' in the search -pattern or replacement string. Example: > +Instead of the '/' which surrounds the pattern and replacement string, you can +use another single-byte character. This is useful if you want to include a +'/' in the search pattern or replacement string. Example: > :s+/+//+ +You can use most characters, but not an alphanumeric character, '\', '"' or +'|'. + For the definition of a pattern, see |pattern|. In Visual block mode, use |/\%V| in the pattern to have the substitute work in the block only. Otherwise it works on whole lines anyway. @@ -913,22 +913,7 @@ This replaces an end-of-line with a new line containing the value of $HOME. > This replaces each 'E' character with a euro sign. Read more in |<Char->|. -4.3 Search and replace *search-replace* - - *:pro* *:promptfind* -:promptf[ind] [string] - Put up a Search dialog. When [string] is given, it is - used as the initial search string. - {only for Win32 GUI} - - *:promptr* *:promptrepl* -:promptr[epl] [string] - Put up a Search/Replace dialog. When [string] is - given, it is used as the initial search string. - {only for Win32 GUI} - - -4.4 Changing tabs *change-tabs* +4.3 Changing tabs *change-tabs* *:ret* *:retab* *:retab!* :[range]ret[ab][!] [new_tabstop] Replace all sequences of white-space containing a @@ -1003,9 +988,13 @@ inside of strings can change! Also see 'softtabstop' option. > *Y* ["x]Y yank [count] lines [into register x] (synonym for - yy, |linewise|). If you like "Y" to work from the - cursor to the end of line (which is more logical, - but not Vi-compatible) use ":map Y y$". + yy, |linewise|). + *Y-default* + Mapped to "y$" by default. |default-mappings| + + *zy* +["x]zy{motion} Yank {motion} text [into register x]. Only differs + from `y` when selecting a block of text, see |v_zy|. *v_y* {Visual}["x]y Yank the highlighted text [into register x] (for @@ -1015,10 +1004,14 @@ inside of strings can change! Also see 'softtabstop' option. > {Visual}["x]Y Yank the highlighted lines [into register x] (for {Visual} see |Visual-mode|). + *v_zy* +{Visual}["x]zy Yank the highlighted text [into register x]. Trailing + whitespace at the end of each line of a selected block + won't be yanked. Especially useful in combination + with `zp`. (for {Visual} see |Visual-mode|) + *:y* *:yank* *E850* -:[range]y[ank] [x] Yank [range] lines [into register x]. Yanking to the - "* or "+ registers is possible only when the - |+clipboard| feature is included. +:[range]y[ank] [x] Yank [range] lines [into register x]. :[range]y[ank] [x] {count} Yank {count} lines, starting with last line number @@ -1094,7 +1087,8 @@ inside of strings can change! Also see 'softtabstop' option. > ["x]zp or *zp* *zP* ["x]zP Like "p" and "P", except without adding trailing spaces when pasting a block. Thus the inserted text will not - always be a rectangle. + always be a rectangle. Especially useful in + combination with |v_zy|. You can use these commands to copy text from one place to another. Do this by first getting the text into a register with a yank, delete or change @@ -1806,8 +1800,7 @@ found here: |sort()|, |uniq()|. With [f] sorting is done on the Float in the line. The value of Float is determined similar to passing the text (after or inside a {pattern} match) to - str2float() function. This option is available only - if Vim was compiled with Floating point support. + str2float() function. With [x] sorting is done on the first hexadecimal number in the line (after or inside a {pattern} diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index dcdc2384dc..70a465f636 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -532,7 +532,6 @@ that see the '"' as part of their argument: :normal :ownsyntax :popup - :promptfind (and the like) :registers :return :sort @@ -571,8 +570,6 @@ followed by another Vim command: :make :normal :perlfile - :promptfind - :promptrepl :pyfile :python :registers @@ -1070,7 +1067,7 @@ in Normal mode and Insert mode. It is possible to use ":", "/" and other commands that use the command-line, but it's not possible to open another command-line window then. There is no nesting. - *E11* + *E11* *E1188* The command-line window is not a normal window. It is not possible to move to another window or edit another buffer. All commands that would do this are disabled in the command-line window. Of course it _is_ possible to execute @@ -1143,6 +1140,11 @@ Thus you can resize the command-line window, but not others. The |getcmdwintype()| function returns the type of the command-line being edited as described in |cmdwin-char|. +Nvim defines this default CmdWinEnter autocmd in the "nvim_cmdwin" group: > + autocmd CmdWinEnter [:>] syntax sync minlines=1 maxlines=1 +< +You can disable this in your config with "autocmd! nvim_cmdwin". |default-autocmds| + AUTOCOMMANDS diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 3b5287ee44..861aed4884 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -14,6 +14,7 @@ updated. 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_exec()| instead. *nvim_execute_lua()* Use |nvim_exec_lua()| instead. @@ -54,6 +55,8 @@ Functions ~ without stopping the job. Use chanclose(id) to close any socket. +Lua ~ +*vim.register_keystroke_callback()* Use |vim.on_key()| instead. Modifiers ~ *cpo-<* diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index aec0178da2..14f35acce3 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -46,7 +46,7 @@ NVIM IS... WELL DOCUMENTED *design-documented* item is easier to find. -NVIM IS... HIGH SPEED AND SMALL IN SIZE *design-speed-size* +NVIM IS... FAST AND SMALL *design-speed-size* Keep Nvim small and fast. - Computers are becoming faster and bigger each year. Vim can grow too, but @@ -127,14 +127,20 @@ Sometimes a GUI or other application may want to force a provider to DOCUMENTATION *dev-doc* -- Do not prefix help tags with "nvim-". Use |vim_diff.txt| to document - differences from Vim; no other distinction is necessary. -- If a Vim feature is removed, delete its help section and move its tag to - |vim_diff.txt|. -- Move deprecated features to |deprecated.txt|. +- "Just say it". Avoid mushy, colloquial phrasing in all documentation + (docstrings, user manual, website materials, newsletters, …). Don't mince + words. Personality and flavor, used sparingly, are welcome--but in general, + optimize for the reader's time and energy: be "precise yet concise". + - Prefer the active voice: "Foo does X", not "X is done by Foo". +- Vim differences: + - Do not prefix help tags with "nvim-". Use |vim_diff.txt| to catalog + differences from Vim; no other distinction is necessary. + - If a Vim feature is removed, delete its help section and move its tag to + |vim_diff.txt|. +- Mention deprecated features in |deprecated.txt| and delete their old doc. - Use consistent language. - - "terminal" in a help tag always means "the embedded terminal emulator", not - "the user host terminal". + - "terminal" in a help tag always means "the embedded terminal emulator", + not "the user host terminal". - Use "tui-" to prefix help tags related to the host terminal, and "TUI" in prose if possible. - Docstrings: do not start parameter descriptions with "The" or "A" unless it @@ -191,8 +197,8 @@ definitions. The |lua-vim| :help is generated from the docstrings. Docstring format: - Lines in the main description start with `---` -- Special tokens start with `--@` followed by the token name: - `--@see`, `--@param`, `--@returns` +- Special tokens start with `---@` followed by the token name: + `---@see`, `---@param`, `---@returns` - Limited markdown is supported. - List-items start with `-` (useful to nest or "indent") - Use `<pre>` for code samples. @@ -211,24 +217,24 @@ vim.paste in src/nvim/lua/vim.lua like this: > --- end)() --- </pre> --- - --@see |paste| + ---@see |paste| --- - --@param lines ... - --@param phase ... - --@returns false if client should cancel the paste. + ---@param lines ... + ---@param phase ... + ---@returns false if client should cancel the paste. LUA *dev-lua* - Keep the core Lua modules |lua-stdlib| simple. Avoid elaborate OOP or pseudo-OOP designs. Plugin authors just want functions to call, they don't - want to learn a big, fancy inheritance hierarchy. So we should avoid complex - objects: tables are usually better. + want to learn a big, fancy inheritance hierarchy. Thus avoid specialized + objects; tables or values are usually better. API *dev-api* -Use this template to name new API functions: +Use this template to name new RPC |API| functions: nvim_{thing}_{action}_{arbitrary-qualifiers} If the function acts on an object then {thing} is the name of that object @@ -356,4 +362,19 @@ External UIs are expected to implement these common features: published in this event. See also "mouse_on", "mouse_off". +NAMING *dev-naming* + +Naming is important. Consistent naming in the API and UI helps both users and +developers discover and intuitively understand related concepts ("families"), +and reduces cognitive burden. Discoverability encourages code re-use and +likewise avoids redundant, overlapping mechanisms, which reduces code +surface-area, and thereby minimizes bugs... + +Naming conventions ~ + +Use the "on_" prefix to name event handlers 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. + + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt index a9e2a0d522..6115a5d235 100644 --- a/runtime/doc/diff.txt +++ b/runtime/doc/diff.txt @@ -334,9 +334,11 @@ between file1 and file2: > The ">" is replaced with the value of 'shellredir'. -The output of "diff" must be a normal "ed" style diff or a unified diff. Do -NOT use a context diff. This example explains the format that Vim expects for -the "ed" style diff: > +The output of "diff" must be a normal "ed" style diff or a unified diff. A +context diff will NOT work. For a unified diff no context lines can be used. +Using "diff -u" will NOT work, use "diff -U0". + +This example explains the format that Vim expects for the "ed" style diff: > 1a2 > bbb diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt index b7dc16341d..dd7e9a331a 100644 --- a/runtime/doc/digraph.txt +++ b/runtime/doc/digraph.txt @@ -165,7 +165,7 @@ ROUBLE The rouble sign was added in 2014 as 0x20bd. Vim supports the digraphs =R and =P for this. Note that R= and P= are other characters. - *digraph-table* + *digraph-table* *digraph-table-mbyte* char digraph hex dec official name ~ ^@ NU 0x00 0 NULL (NUL) ^A SH 0x01 1 START OF HEADING (SOH) @@ -341,12 +341,6 @@ $ DO 0x24 36 DOLLAR SIGN ý y' 0xfd 253 LATIN SMALL LETTER Y WITH ACUTE þ th 0xfe 254 LATIN SMALL LETTER THORN (Icelandic) ÿ y: 0xff 255 LATIN SMALL LETTER Y WITH DIAERESIS - -If your Vim is compiled with |multibyte| support and you are using a multibyte -'encoding', Vim provides this enhanced set of additional digraphs: - - *digraph-table-mbyte* -char digraph hex dec official name ~ Ä€ A- 0100 0256 LATIN CAPITAL LETTER A WITH MACRON Ä a- 0101 0257 LATIN SMALL LETTER A WITH MACRON Ä‚ A( 0102 0258 LATIN CAPITAL LETTER A WITH BREVE diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index b7214d1390..def873a1da 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -20,7 +20,7 @@ There are six types of variables: *Number* *Integer* Number A 32 or 64 bit signed number. |expr-number| The number of bits is available in |v:numbersize|. - Examples: -123 0x10 0177 0b1011 + Examples: -123 0x10 0177 0o177 0b1011 Float A floating point number. |floating-point-format| *Float* Examples: 123.456 1.15e-6 -1.1e3 @@ -54,14 +54,15 @@ the Number. Examples: Number -1 --> String "-1" ~ *octal* Conversion from a String to a Number is done by converting the first digits to -a number. Hexadecimal "0xf9", Octal "017", and Binary "0b10" numbers are -recognized. If the String doesn't start with digits, the result is zero. -Examples: +a number. Hexadecimal "0xf9", Octal "017" or "0o17", and Binary "0b10" +numbers are recognized. If the String doesn't start with digits, the result +is zero. Examples: String "456" --> Number 456 ~ String "6bar" --> Number 6 ~ String "foo" --> Number 0 ~ String "0xf1" --> Number 241 ~ String "0100" --> Number 64 ~ + String "0o100" --> Number 64 ~ String "0b101" --> Number 5 ~ String "-8" --> Number -8 ~ String "+8" --> Number 0 ~ @@ -669,12 +670,14 @@ Expression syntax summary, from least to most significant: expr8[expr1 : expr1] substring of a String or sublist of a |List| expr8.name entry in a |Dictionary| expr8(expr1, ...) function call with |Funcref| variable + expr8->name(expr1, ...) |method| call |expr9| number number constant "string" string constant, backslash is special 'string' string constant, ' is doubled [expr1, ...] |List| {expr1: expr1, ...} |Dictionary| + #{key: expr1, ...} |Dictionary| &option option value (expr1) nested expression variable internal variable @@ -939,9 +942,11 @@ expr8 *expr8* ----- This expression is either |expr9| or a sequence of the alternatives below, in any order. E.g., these are all possible: - expr9[expr1].name - expr9.name[expr1] - expr9(expr1, ...)[expr1].name + expr8[expr1].name + expr8.name[expr1] + expr8(expr1, ...)[expr1].name + expr8->(expr1, ...)[expr1] +Evaluation is always from left to right. expr8[expr1] item of String or |List| *expr-[]* *E111* @@ -1043,6 +1048,36 @@ expr8(expr1, ...) |Funcref| function call When expr8 is a |Funcref| type variable, invoke the function it refers to. +expr8->name([args]) method call *method* *->* +expr8->{lambda}([args]) + +For methods that are also available as global functions this is the same as: > + name(expr8 [, args]) +There can also be methods specifically for the type of "expr8". + +This allows for chaining, passing the value that one method returns to the +next method: > + mylist->filter(filterexpr)->map(mapexpr)->sort()->join() +< +Example of using a lambda: > + GetPercentage->{x -> x * 100}()->printf('%d%%') +< +When using -> the |expr7| operators will be applied first, thus: > + -1.234->string() +Is equivalent to: > + (-1.234)->string() +And NOT: > + -(1.234->string()) +< + *E274* +"->name(" must not contain white space. There can be white space before the +"->" and after the "(", thus you can split the lines like this: > + mylist + \ ->filter(filterexpr) + \ ->map(mapexpr) + \ ->sort() + \ ->join() +< *expr9* number @@ -1051,7 +1086,7 @@ number number constant *expr-number* *hex-number* *octal-number* *binary-number* Decimal, Hexadecimal (starting with 0x or 0X), Binary (starting with 0b or 0B) -and Octal (starting with 0). +and Octal (starting with 0, 0o or 0O). *floating-point-format* Floating point numbers can be written in two forms: @@ -1283,7 +1318,17 @@ The lambda expression is also useful for jobs and timers: > Handler called Handler called -Note how execute() is used to execute an Ex command. That's ugly though. +Note that it is possible to cause memory to be used and not freed if the +closure is referenced by the context it depends on: > + function Function() + let x = 0 + let F = {-> x} + endfunction +The closure uses "x" from the function scope, and "F" in that same scope +refers to the closure. This cycle results in the memory not being freed. +Recommendation: don't do this. + +Notice how execute() is used to execute an Ex command. That's ugly though. Lambda expressions have internal names like '<lambda>42'. If you get an error @@ -1641,6 +1686,7 @@ v:event Dictionary of event data for the current |autocommand|. Valid |v:false| if not. changed_window Is |v:true| if the the event fired while changing window (or tab) on |DirChanged|. + status Job status or exit code, -1 means "unknown". |TermClose| *v:exception* *exception-variable* v:exception The value of the exception most recently caught and not @@ -1687,7 +1733,8 @@ v:fcs_choice What should happen after a |FileChangedShell| event was Vim behaves like it is empty, there is no warning message. *v:fname* *fname-variable* -v:fname The file name set by 'includeexpr'. Empty otherwise. +v:fname When evaluating 'includeexpr': the file name that was + detected. Empty otherwise. *v:fname_in* *fname_in-variable* v:fname_in The name of the input file. Valid while evaluating: @@ -1821,7 +1868,7 @@ v:null Special value used to put "null" in JSON and NIL in msgpack. v:numbermax Maximum value of a number. *v:numbermin* *numbermin-variable* -v:numbermin Minimum value of a number (negative) +v:numbermin Minimum value of a number (negative). *v:numbersize* *numbersize-variable* v:numbersize Number of bits in a Number. This is normally 64, but on some @@ -2114,7 +2161,7 @@ chanclose({id}[, {stream}]) Number Closes a channel or one of its streams chansend({id}, {data}) Number Writes {data} to channel char2nr({expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr} charidx({string}, {idx} [, {countcc}]) - Number char index of byte {idx} in {string} + Number char index of byte {idx} in {string} cindent({lnum}) Number C indent for line {lnum} clearmatches([{win}]) none clear all matches col({expr}) Number column nr of cursor or mark @@ -2203,9 +2250,11 @@ getbufline({expr}, {lnum} [, {end}]) getbufvar({expr}, {varname} [, {def}]) any variable {varname} in buffer {expr} getchangelist({expr}) List list of change list items -getchar([expr]) Number get one character from the user +getchar([expr]) Number or String + get one character from the user getcharmod() Number modifiers for the last typed character getcharsearch() Dict last character search +getcharstr([expr]) String get one character from the user getcmdline() String return the current command-line getcmdpos() Number return cursor position in command-line getcmdtype() String return current command-line type @@ -2331,6 +2380,7 @@ matchstr({expr}, {pat}[, {start}[, {count}]]) matchstrpos({expr}, {pat}[, {start}[, {count}]]) List {count}'th match of {pat} in {expr} max({expr}) Number maximum value of items in {expr} +menu_get({path} [, {modes}]) List description of |menus| matched by {path} min({expr}) Number minimum value of items in {expr} mkdir({name} [, {path} [, {prot}]]) Number create directory {name} @@ -2470,7 +2520,8 @@ stdpath({what}) String/List returns the standard path(s) for {w str2float({expr}) Float convert String to Float str2list({expr} [, {utf8}]) List convert each character of {expr} to ASCII/UTF8 value -str2nr({expr} [, {base}]) Number convert String to Number +str2nr({expr} [, {base} [, {quoted}]]) + Number convert String to Number strchars({expr} [, {skipcc}]) Number character length of the String {expr} strcharpart({str}, {start} [, {len}]) String {len} characters of {str} at @@ -2580,6 +2631,8 @@ abs({expr}) *abs()* echo abs(-4) < 4 + Can also be used as a |method|: > + Compute()->abs() acos({expr}) *acos()* Return the arc cosine of {expr} measured in radians, as a @@ -2592,6 +2645,8 @@ acos({expr}) *acos()* :echo acos(-0.5) < 2.094395 + Can also be used as a |method|: > + Compute()->acos() add({list}, {expr}) *add()* Append the item {expr} to |List| {list}. Returns the @@ -2602,12 +2657,16 @@ add({list}, {expr}) *add()* item. Use |extend()| to concatenate |Lists|. Use |insert()| to add an item at another position. + Can also be used as a |method|: > + mylist->add(val1)->add(val2) and({expr}, {expr}) *and()* Bitwise AND on the two arguments. The arguments are converted to a number. A List, Dict or Float argument causes an error. Example: > :let flag = and(bits, 0x80) +< Can also be used as a |method|: > + :let flag = bits->and(0x80) api_info() *api_info()* Returns Dictionary of |api-metadata|. @@ -2621,11 +2680,15 @@ append({lnum}, {text}) *append()* Otherwise append {text} as one text line below line {lnum} in the current buffer. {lnum} can be zero to insert a line before the first one. + {lnum} is used like with |getline()|. Returns 1 for failure ({lnum} out of range or out of memory), 0 for success. Example: > :let failed = append(line('$'), "# THE END") :let failed = append(0, ["Chapter 1", "the beginning"]) +< Can also be used as a |method| after a List: > + mylist->append(lnum) + appendbufline({expr}, {lnum}, {text}) *appendbufline()* Like |append()| but append the text in buffer {expr}. @@ -2644,8 +2707,10 @@ appendbufline({expr}, {lnum}, {text}) *appendbufline()* error message is given. Example: > :let failed = appendbufline(13, 0, "# THE START") < - *argc()* -argc([{winid}]) + Can also be used as a |method| after a List: > + mylist->appendbufline(buf, lnum) + +argc([{winid}]) *argc()* The result is the number of files in the argument list. See |arglist|. If {winid} is not supplied, the argument list of the current @@ -2699,6 +2764,9 @@ asin({expr}) *asin()* :echo asin(-0.5) < -0.523599 + Can also be used as a |method|: > + Compute()->asin() + assert_ functions are documented here: |assert-functions-details| @@ -2713,6 +2781,8 @@ atan({expr}) *atan()* :echo atan(-4.01) < -1.326405 + Can also be used as a |method|: > + Compute()->atan() atan2({expr1}, {expr2}) *atan2()* Return the arc tangent of {expr1} / {expr2}, measured in @@ -2724,6 +2794,8 @@ atan2({expr1}, {expr2}) *atan2()* :echo atan2(1, -1) < 2.356194 + Can also be used as a |method|: > + Compute()->atan2(1) *browse()* browse({save}, {title}, {initdir}, {default}) @@ -2734,8 +2806,8 @@ browse({save}, {title}, {initdir}, {default}) {title} title for the requester {initdir} directory to start browsing in {default} default file name - When the "Cancel" button is hit, something went wrong, or - browsing is not possible, an empty string is returned. + An empty string is returned when the "Cancel" button is hit, + something went wrong, or browsing is not possible. *browsedir()* browsedir({title}, {initdir}) @@ -2757,6 +2829,8 @@ bufadd({name}) *bufadd()* created buffer. When {name} is an empty string then a new buffer is always created. The buffer will not have' 'buflisted' set. +< Can also be used as a |method|: > + let bufnr = 'somename'->bufadd() bufexists({expr}) *bufexists()* The result is a Number, which is |TRUE| if a buffer called @@ -2780,11 +2854,17 @@ bufexists({expr}) *bufexists()* Use "bufexists(0)" to test for the existence of an alternate file name. + Can also be used as a |method|: > + let exists = 'somename'->bufexists() + buflisted({expr}) *buflisted()* The result is a Number, which is |TRUE| if a buffer called {expr} exists and is listed (has the 'buflisted' option set). The {expr} argument is used like with |bufexists()|. + Can also be used as a |method|: > + let listed = 'somename'->buflisted() + bufload({expr}) *bufload()* Ensure the buffer {expr} is loaded. When the buffer name refers to an existing file then the file is read. Otherwise @@ -2794,15 +2874,22 @@ bufload({expr}) *bufload()* there will be no dialog, the buffer will be loaded anyway. The {expr} argument is used like with |bufexists()|. + Can also be used as a |method|: > + eval 'somename'->bufload() + bufloaded({expr}) *bufloaded()* The result is a Number, which is |TRUE| if a buffer called {expr} exists and is loaded (shown in a window or hidden). The {expr} argument is used like with |bufexists()|. + Can also be used as a |method|: > + let loaded = 'somename'->bufloaded() + bufname([{expr}]) *bufname()* - The result is the name of a buffer, as it is displayed by the - ":ls" command. -+ If {expr} is omitted the current buffer is used. + The result is the name of a buffer. Mostly as it is displayed + by the `:ls` command, but not using special names such as + "[No Name]". + If {expr} is omitted the current buffer is used. If {expr} is a Number, that buffer number's name is given. Number zero is the alternate buffer for the current window. If {expr} is a String, it is used as a |file-pattern| to match @@ -2821,6 +2908,9 @@ bufname([{expr}]) *bufname()* If the {expr} is a String, but you want to use it as a buffer number, force it to be a Number by adding zero to it: > :echo bufname("3" + 0) +< Can also be used as a |method|: > + echo bufnr->bufname() + < If the buffer doesn't exist, or doesn't have a name, an empty string is returned. > bufname("#") alternate buffer name @@ -2831,7 +2921,7 @@ bufname([{expr}]) *bufname()* *bufnr()* bufnr([{expr} [, {create}]]) The result is the number of a buffer, as it is displayed by - the ":ls" command. For the use of {expr}, see |bufname()| + the `:ls` command. For the use of {expr}, see |bufname()| above. If the buffer doesn't exist, -1 is returned. Or, if the {create} argument is present and TRUE, a new, unlisted, @@ -2843,6 +2933,9 @@ bufnr([{expr} [, {create}]]) number necessarily exist, because ":bwipeout" may have removed them. Use bufexists() to test for the existence of a buffer. + Can also be used as a |method|: > + echo bufref->bufnr() + bufwinid({expr}) *bufwinid()* The result is a Number, which is the |window-ID| of the first window associated with buffer {expr}. For the use of {expr}, @@ -2853,18 +2946,22 @@ bufwinid({expr}) *bufwinid()* < Only deals with the current tab page. + Can also be used as a |method|: > + FindBuffer()->bufwinid() + bufwinnr({expr}) *bufwinnr()* - The result is a Number, which is the number of the first - window associated with buffer {expr}. For the use of {expr}, - see |bufname()| above. If buffer {expr} doesn't exist or - there is no such window, -1 is returned. Example: > + Like |bufwinid()| but return the window number instead of the + |window-ID|. + If buffer {expr} doesn't exist or there is no such window, -1 + is returned. Example: > echo "A window containing buffer 1 is " . (bufwinnr(1)) < The number can be used with |CTRL-W_w| and ":wincmd w" |:wincmd|. - Only deals with the current tab page. + Can also be used as a |method|: > + FindBuffer()->bufwinnr() byte2line({byte}) *byte2line()* Return the line number that contains the character at byte @@ -2874,6 +2971,9 @@ byte2line({byte}) *byte2line()* one. Also see |line2byte()|, |go| and |:goto|. + Can also be used as a |method|: > + GetOffset()->byte2line() + byteidx({expr}, {nr}) *byteidx()* Return byte index of the {nr}'th character in the string {expr}. Use zero for the first character, it then returns @@ -2896,6 +2996,9 @@ byteidx({expr}, {nr}) *byteidx()* If there are exactly {nr} characters the length of the string in bytes is returned. + Can also be used as a |method|: > + GetName()->byteidx(idx) + byteidxcomp({expr}, {nr}) *byteidxcomp()* Like byteidx(), except that a composing character is counted as a separate character. Example: > @@ -2909,6 +3012,9 @@ byteidxcomp({expr}, {nr}) *byteidxcomp()* Only works differently from byteidx() when 'encoding' is set to a Unicode encoding. + Can also be used as a |method|: > + GetName()->byteidxcomp(idx) + call({func}, {arglist} [, {dict}]) *call()* *E699* Call function {func} with the items in |List| {arglist} as arguments. @@ -2918,6 +3024,9 @@ call({func}, {arglist} [, {dict}]) *call()* *E699* {dict} is for functions with the "dict" attribute. It will be used to set the local variable "self". |Dictionary-function| + Can also be used as a |method|: > + GetFunc()->call([arg, arg], dict) + ceil({expr}) *ceil()* Return the smallest integral value greater than or equal to {expr} as a |Float| (round up). @@ -2930,6 +3039,9 @@ ceil({expr}) *ceil()* echo ceil(4.0) < 4.0 + Can also be used as a |method|: > + Compute()->ceil() + changenr() *changenr()* Return the number of the most recent change. This is the same number as what is displayed with |:undolist| and can be used @@ -2979,16 +3091,19 @@ char2nr({expr} [, {utf8}]) *char2nr()* A combining character is a separate character. |nr2char()| does the opposite. + Can also be used as a |method|: > + GetChar()->char2nr() + *charidx()* charidx({string}, {idx} [, {countcc}]) Return the character index of the byte at {idx} in {string}. The index of the first character is zero. If there are no multibyte characters the returned value is equal to {idx}. - When {countcc} is omitted or zero, then composing characters - are not counted separately, their byte length is added to the - preceding base character. - When {countcc} is set to 1, then composing characters are + When {countcc} is omitted or |FALSE|, then composing characters + are not counted separately, their byte length is + added to the preceding base character. + When {countcc} is |TRUE|, then composing characters are counted as separate characters. Returns -1 if the arguments are invalid or if {idx} is greater than the index of the last byte in {string}. An error is @@ -3010,12 +3125,18 @@ cindent({lnum}) *cindent()* When {lnum} is invalid -1 is returned. See |C-indenting|. + Can also be used as a |method|: > + GetLnum()->cindent() + clearmatches([{win}]) *clearmatches()* Clears all matches previously defined for the current window by |matchadd()| and the |:match| commands. If {win} is specified, use the window with this number or window ID instead of the current window. + Can also be used as a |method|: > + GetWin()->clearmatches() +< *col()* col({expr}) The result is a Number, which is the byte index of the column position given with {expr}. The accepted positions are: @@ -3051,6 +3172,9 @@ col({expr}) The result is a Number, which is the byte index of the column \<C-O>:set ve=all<CR> \<C-O>:echo col(".") . "\n" <Bar> \let &ve = save_ve<CR> + +< Can also be used as a |method|: > + GetPos()->col() < complete({startcol}, {matches}) *complete()* *E785* @@ -3082,6 +3206,10 @@ complete({startcol}, {matches}) *complete()* *E785* < This isn't very useful, but it shows how it works. Note that an empty string is returned to avoid a zero being inserted. + Can also be used as a |method|, the second argument is passed + in: > + GetMatches()->complete(col('.')) + complete_add({expr}) *complete_add()* Add {expr} to the list of matches. Only to be used by the function specified with the 'completefunc' option. @@ -3091,6 +3219,9 @@ complete_add({expr}) *complete_add()* See |complete-functions| for an explanation of {expr}. It is the same as one item in the list that 'omnifunc' would return. + Can also be used as a |method|: > + GetMoreMatches()->complete_add() + complete_check() *complete_check()* Check for a key typed while looking for completion matches. This is to be used when looking for matches takes some time. @@ -3114,7 +3245,9 @@ complete_info([{what}]) See |complete-items|. selected Selected item index. First index is zero. Index is -1 if no item is selected (showing - typed text only) + typed text only, or the last completion after + no item is selected when using the <Up> or + <Down> keys) inserted Inserted string. [NOT IMPLEMENT YET] *complete_info_mode* @@ -3151,6 +3284,9 @@ complete_info([{what}]) call complete_info(['mode']) " Get only 'mode' and 'pum_visible' call complete_info(['mode', 'pum_visible']) + +< Can also be used as a |method|: > + GetItems()->complete_info() < *confirm()* confirm({msg} [, {choices} [, {default} [, {type}]]]) @@ -3204,6 +3340,9 @@ confirm({msg} [, {choices} [, {default} [, {type}]]]) don't fit, a vertical layout is used anyway. For some systems the horizontal layout is always used. + Can also be used as a |method|in: > + BuildMessage()->confirm("&Yes\n&No") + *copy()* copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't different from using {expr} directly. @@ -3213,6 +3352,8 @@ copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't changing an item changes the contents of both |Lists|. A |Dictionary| is copied in a similar way as a |List|. Also see |deepcopy()|. + Can also be used as a |method|: > + mylist->copy() cos({expr}) *cos()* Return the cosine of {expr}, measured in radians, as a |Float|. @@ -3223,6 +3364,8 @@ cos({expr}) *cos()* :echo cos(-4.01) < -0.646043 + Can also be used as a |method|: > + Compute()->cos() cosh({expr}) *cosh()* Return the hyperbolic cosine of {expr} as a |Float| in the range @@ -3234,6 +3377,8 @@ cosh({expr}) *cosh()* :echo cosh(-0.5) < -1.127626 + Can also be used as a |method|: > + Compute()->cosh() count({comp}, {expr} [, {ic} [, {start}]]) *count()* Return the number of times an item with value {expr} appears @@ -3248,12 +3393,14 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()* occurrences of {expr} is returned. Zero is returned when {expr} is an empty string. + Can also be used as a |method|: > + mylist->count(val) + *cscope_connection()* cscope_connection([{num} , {dbpath} [, {prepend}]]) Checks for the existence of a |cscope| connection. If no parameters are specified, then the function returns: - 0, if cscope was not available (not compiled in), or - if there are no cscope connections; + 0, if there are no cscope connections; 1, if there is at least one cscope connection. If parameters are specified, then the value of {num} @@ -3344,6 +3491,8 @@ cursor({list}) position within a <Tab> or after the last character. Returns 0 when the position could be set, -1 otherwise. + Can also be used as a |method|: > + GetCursorPos()->cursor() deepcopy({expr}[, {noref}]) *deepcopy()* *E698* Make a copy of {expr}. For Numbers and Strings this isn't @@ -3365,6 +3514,9 @@ deepcopy({expr}[, {noref}]) *deepcopy()* *E698* {noref} set to 1 will fail. Also see |copy()|. + Can also be used as a |method|: > + GetObject()->deepcopy() + delete({fname} [, {flags}]) *delete()* Without {flags} or with {flags} empty: Deletes the file by the name {fname}. This also works when {fname} is a symbolic link. @@ -3382,6 +3534,9 @@ delete({fname} [, {flags}]) *delete()* operation was successful and -1/true when the deletion failed or partly failed. + Can also be used as a |method|: > + GetName()->delete() + deletebufline({expr}, {first}[, {last}]) *deletebufline()* Delete lines {first} to {last} (inclusive) from buffer {expr}. If {last} is omitted then delete line {first} only. @@ -3396,6 +3551,9 @@ deletebufline({expr}, {first}[, {last}]) *deletebufline()* when using |line()| this refers to the current buffer. Use "$" to refer to the last line in buffer {expr}. + Can also be used as a |method|: > + GetBuffer()->deletebufline(1) + dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()* Adds a watcher to a dictionary. A dictionary watcher is identified by three components: @@ -3462,6 +3620,9 @@ diff_filler({lnum}) *diff_filler()* line, "'m" mark m, etc. Returns 0 if the current window is not in diff mode. + Can also be used as a |method|: > + GetLnum()->diff_filler() + diff_hlID({lnum}, {col}) *diff_hlID()* Returns the highlight ID for diff mode at line {lnum} column {col} (byte index). When the current line does not have a @@ -3473,11 +3634,16 @@ diff_hlID({lnum}, {col}) *diff_hlID()* The highlight ID can be used with |synIDattr()| to obtain syntax information about the highlighting. + Can also be used as a |method|: > + GetLnum()->diff_hlID(col) + empty({expr}) *empty()* Return the Number 1 if {expr} is empty, zero otherwise. A |List| or |Dictionary| is empty when it does not have any items. A Number is empty when its value is zero. Special variable is empty when it is |v:false| or |v:null|. + Can also be used as a |method|: > + mylist->empty() environ() *environ()* Return all of environment variables as dictionary. You can @@ -3502,6 +3668,9 @@ eval({string}) Evaluate {string} and return the result. Especially useful to them. Also works for |Funcref|s that refer to existing functions. + Can also be used as a |method|: > + argv->join()->eval() + eventhandler() *eventhandler()* Returns 1 when inside an event handler. That is that Vim got interrupted while waiting for the user to type a character, @@ -3659,12 +3828,18 @@ exp({expr}) *exp()* :echo exp(-1) < 0.367879 + Can also be used as a |method|: > + Compute()->exp() + debugbreak({pid}) *debugbreak()* Specifically used to interrupt a program being debugged. It will cause process {pid} to get a SIGTRAP. Behavior for other processes is undefined. See |terminal-debugger|. {Sends a SIGINT to a process {pid} other than MS-Windows} + Can also be used as a |method|: > + GetPid()->debugbreak() + expand({expr} [, {nosuf} [, {list}]]) *expand()* Expand wildcards and the following special keywords in {expr}. 'wildignorecase' applies. @@ -3693,6 +3868,8 @@ expand({expr} [, {nosuf} [, {list}]]) *expand()* line number <sflnum> script file line number, also when in a function + <SID> "<SNR>123_" where "123" is the + current script ID |<SID>| <cword> word under the cursor <cWORD> WORD under the cursor <client> the {clientid} of the last received @@ -3791,6 +3968,8 @@ extend({expr1}, {expr2} [, {expr3}]) *extend()* fails. Returns {expr1}. + Can also be used as a |method|: > + mylist->extend(otherlist) feedkeys({string} [, {mode}]) *feedkeys()* Characters in {string} are queued for processing as if they @@ -3844,7 +4023,11 @@ filereadable({file}) *filereadable()* expression, which is used as a String. If you don't care about the file being readable you can use |glob()|. - + {file} is used as-is, you may want to expand wildcards first: > + echo filereadable('~/.vimrc') + 0 + echo filereadable(expand('~/.vimrc')) + 1 filewritable({file}) *filewritable()* The result is a Number, which is 1 when a file with the @@ -3854,16 +4037,19 @@ filewritable({file}) *filewritable()* filter({expr1}, {expr2}) *filter()* - {expr1} must be a |List| or a |Dictionary|. + {expr1} must be a |List|, |Blob|, or a |Dictionary|. For each item in {expr1} evaluate {expr2} and when the result - is zero remove the item from the |List| or |Dictionary|. + is zero remove the item from the |List| or |Dictionary|. For a + |Blob| each byte is removed. + {expr2} must be a |string| or |Funcref|. If {expr2} is a |string|, inside {expr2} |v:val| has the value of the current item. For a |Dictionary| |v:key| has the key of the current item and for a |List| |v:key| has the index of - the current item. - For a |Dictionary| |v:key| has the key of the current item. + the current item. For a |Blob| |v:key| has the index of the + current byte. + Examples: > call filter(mylist, 'v:val !~ "OLD"') < Removes the items where "OLD" appears. > @@ -3894,12 +4080,14 @@ filter({expr1}, {expr2}) *filter()* |Dictionary| to remain unmodified make a copy first: > :let l = filter(copy(mylist), 'v:val =~ "KEEP"') -< Returns {expr1}, the |List| or |Dictionary| that was filtered. - When an error is encountered while evaluating {expr2} no - further items in {expr1} are processed. When {expr2} is a - Funcref errors inside a function are ignored, unless it was - defined with the "abort" flag. +< Returns {expr1}, the |List| , |Blob| or |Dictionary| that was + filtered. When an error is encountered while evaluating + {expr2} no further items in {expr1} are processed. When + {expr2} is a Funcref errors inside a function are ignored, + unless it was defined with the "abort" flag. + Can also be used as a |method|: > + mylist->filter(expr2) finddir({name} [, {path} [, {count}]]) *finddir()* Find directory {name} in {path}. Supports both downwards and @@ -3962,6 +4150,8 @@ float2nr({expr}) *float2nr()* echo float2nr(1.0e-100) < 0 + Can also be used as a |method|: > + Compute()->float2nr() floor({expr}) *floor()* Return the largest integral value less than or equal to @@ -3975,6 +4165,8 @@ floor({expr}) *floor()* echo floor(4.0) < 4.0 + Can also be used as a |method|: > + Compute()->floor() fmod({expr1}, {expr2}) *fmod()* Return the remainder of {expr1} / {expr2}, even if the @@ -3990,6 +4182,8 @@ fmod({expr1}, {expr2}) *fmod()* :echo fmod(-12.33, 1.22) < -0.13 + Can also be used as a |method|: > + Compute()->fmod(1.22) fnameescape({string}) *fnameescape()* Escape {string} for use as file name command argument. All @@ -4022,11 +4216,15 @@ foldclosed({lnum}) *foldclosed()* The result is a Number. If the line {lnum} is in a closed fold, the result is the number of the first line in that fold. If the line {lnum} is not in a closed fold, -1 is returned. + {lnum} is used like with |getline()|. Thus "." is the current + line, "'m" mark m, etc. foldclosedend({lnum}) *foldclosedend()* The result is a Number. If the line {lnum} is in a closed fold, the result is the number of the last line in that fold. If the line {lnum} is not in a closed fold, -1 is returned. + {lnum} is used like with |getline()|. Thus "." is the current + line, "'m" mark m, etc. foldlevel({lnum}) *foldlevel()* The result is a Number, which is the foldlevel of line {lnum} @@ -4037,6 +4235,8 @@ foldlevel({lnum}) *foldlevel()* returned for lines where folds are still to be updated and the foldlevel is unknown. As a special case the level of the previous line is usually available. + {lnum} is used like with |getline()|. Thus "." is the current + line, "'m" mark m, etc. *foldtext()* foldtext() Returns a String, to be displayed for a closed fold. This is @@ -4156,6 +4356,8 @@ get({list}, {idx} [, {default}]) *get()* Get item {idx} from |List| {list}. When this item is not available return {default}. Return zero when {default} is omitted. + Can also be used as a |method|: > + mylist->get(idx) get({dict}, {key} [, {default}]) Get item with key {key} from |Dictionary| {dict}. When this item is not available return {default}. Return zero when @@ -4307,6 +4509,7 @@ getchar([expr]) *getchar()* Return zero otherwise. 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 special key is returned. If it is a single character, the @@ -4398,6 +4601,20 @@ getcharsearch() *getcharsearch()* :nnoremap <expr> , getcharsearch().forward ? ',' : ';' < Also see |setcharsearch()|. + +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 + available. Return an empty string otherwise. + 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 + result is converted to a string. + + getcmdline() *getcmdline()* Return the current command-line. Only works when the command line is being edited, thus requires use of |c_CTRL-\_e| or @@ -4510,9 +4727,9 @@ getcurpos() Get the position of the cursor. This is like getpos('.'), but |winrestview()| for restoring more state. getcwd([{winnr}[, {tabnr}]]) *getcwd()* - With no arguments the result is a String, which is the name of - the current effective working directory. With {winnr} or - {tabnr} the working directory of that scope is returned. + With no arguments, returns the name of the effective + |current-directory|. With {winnr} or {tabnr} the working + directory of that scope is returned. Tabs and windows are identified by their respective numbers, 0 means current tab or window. Missing argument implies 0. Thus the following are equivalent: > @@ -4671,11 +4888,11 @@ getmarklist([{expr}]) *getmarklist()* see |bufname()|. Each item in the returned List is a |Dict| with the following: - name - name of the mark prefixed by "'" - pos - a |List| with the position of the mark: + mark name of the mark prefixed by "'" + pos a |List| with the position of the mark: [bufnum, lnum, col, off] - Refer to |getpos()| for more information. - file - file name + Refer to |getpos()| for more information. + file file name Refer to |getpos()| for getting information about a specific mark. @@ -4686,6 +4903,8 @@ getmatches([{win}]) *getmatches()* |getmatches()| is useful in combination with |setmatches()|, as |setmatches()| can restore a list of matches saved by |getmatches()|. + If {win} is specified, use the window with this number or + window ID instead of the current window. Example: > :echo getmatches() < [{'group': 'MyGroup1', 'pattern': 'TODO', @@ -4741,7 +4960,10 @@ getqflist([{what}]) *getqflist()* bufname() to get the name module module name lnum line number in the buffer (first line is 1) + end_lnum + end of line number if the item is multiline col column number (first column is 1) + end_col end of column number if the item has range vcol |TRUE|: "col" is visual column |FALSE|: "col" is byte index nr error number @@ -4751,8 +4973,10 @@ getqflist([{what}]) *getqflist()* valid |TRUE|: recognized error message When there is no error list or it's empty, an empty list is - returned. Quickfix list entries with non-existing buffer - number are returned with "bufnr" set to zero. + returned. Quickfix list entries with a non-existing buffer + number are returned with "bufnr" set to zero (Note: some + functions accept buffer number zero for the alternate buffer, + you may need to explicitly check for zero). Useful application: Find pattern matches in multiple files and do something with them: > @@ -4856,12 +5080,12 @@ getregtype([{regname}]) *getregtype()* <CTRL-V> is one character with value 0x16. If {regname} is not specified, |v:register| is used. -gettabinfo([{arg}]) *gettabinfo()* - If {arg} is not specified, then information about all the tab - pages is returned as a |List|. Each List item is a |Dictionary|. - Otherwise, {arg} specifies the tab page number and information - about that one is returned. If the tab page does not exist an - empty List is returned. +gettabinfo([{tabnr}]) *gettabinfo()* + If {tabnr} is not specified, then information about all the + tab pages is returned as a |List|. Each List item is a + |Dictionary|. Otherwise, {tabnr} specifies the tab page + number and information about that one is returned. If the tab + page does not exist an empty List is returned. Each List item is a |Dictionary| with the following entries: tabnr tab page number. @@ -4905,11 +5129,11 @@ gettabwinvar({tabnr}, {winnr}, {varname} [, {def}]) *gettabwinvar()* To obtain all window-local variables use: > gettabwinvar({tabnr}, {winnr}, '&') -gettagstack([{nr}]) *gettagstack()* - The result is a Dict, which is the tag stack of window {nr}. - {nr} can be the window number or the |window-ID|. - When {nr} is not specified, the current window is used. - When window {nr} doesn't exist, an empty Dict is returned. +gettagstack([{winnr}]) *gettagstack()* + The result is a Dict, which is the tag stack of window {winnr}. + {winnr} can be the window number or the |window-ID|. + When {winnr} is not specified, the current window is used. + When window {winnr} doesn't exist, an empty Dict is returned. The returned dictionary contains the following entries: curidx Current index in the stack. When at @@ -5149,6 +5373,9 @@ has_key({dict}, {key}) *has_key()* The result is a Number, which is TRUE if |Dictionary| {dict} has an entry with key {key}. FALSE otherwise. + Can also be used as a |method|: > + mydict->has_key(key) + haslocaldir([{winnr}[, {tabnr}]]) *haslocaldir()* The result is a Number, which is 1 when the tabpage or window has set a local path via |:tcd| or |:lcd|, otherwise 0. @@ -5297,9 +5524,6 @@ iconv({expr}, {from}, {to}) *iconv()* are replaced with "?". The encoding names are whatever the iconv() library function can accept, see ":!man 3 iconv". - Most conversions require Vim to be compiled with the |+iconv| - feature. Otherwise only UTF-8 to latin1 conversion and back - can be done. Note that Vim uses UTF-8 for all Unicode encodings, conversion from/to UCS-2 is automatically changed to use UTF-8. You cannot use UCS-2 in a string anyway, because of the NUL bytes. @@ -5495,6 +5719,9 @@ insert({list}, {item} [, {idx}]) *insert()* Note that when {item} is a |List| it is inserted as a single item. Use |extend()| to concatenate |Lists|. + Can also be used as a |method|: > + mylist->insert(item) + interrupt() *interrupt()* Interrupt script execution. It works more or less like the user typing CTRL-C, most commands won't execute and control @@ -5512,6 +5739,8 @@ invert({expr}) *invert()* Bitwise invert. The argument is converted to a number. A List, Dict or Float argument causes an error. Example: > :let bits = invert(bits) +< Can also be used as a |method|: > + :let bits = bits->invert() isdirectory({directory}) *isdirectory()* The result is a Number, which is |TRUE| when a directory @@ -5527,6 +5756,9 @@ isinf({expr}) *isinf()* :echo isinf(-1.0 / 0.0) < -1 + Can also be used as a |method|: > + Compute()->isinf() + islocked({expr}) *islocked()* *E786* The result is a Number, which is |TRUE| when {expr} is the name of a locked variable. @@ -5562,12 +5794,17 @@ items({dict}) *items()* |List| item is a list with two items: the key of a {dict} entry and the value of this entry. The |List| is in arbitrary order. + Can also be used as a |method|: > + mydict->items() isnan({expr}) *isnan()* Return |TRUE| if {expr} is a float with value NaN. > echo isnan(0.0 / 0.0) < 1 + Can also be used as a |method|: > + Compute()->isnan() + jobpid({job}) *jobpid()* Return the PID (process id) of |job-id| {job}. @@ -5639,6 +5876,9 @@ jobstart({cmd}[, {opts}]) *jobstart()* before invoking `on_stderr`. |channel-buffered| stdout_buffered: (boolean) Collect data until EOF (stream closed) before invoking `on_stdout`. |channel-buffered| + stdin: (string) Either "pipe" (default) to connect the + job's stdin to a channel or "null" to disconnect + stdin. width: (number) Width of the `pty` terminal. {opts} is passed as |self| dictionary to the callback; the @@ -5664,8 +5904,8 @@ jobwait({jobs}[, {timeout}]) *jobwait()* Waits for jobs and their |on_exit| handlers to complete. {jobs} is a List of |job-id|s to wait for. - {timeout} is the maximum waiting time in milliseconds, -1 - means forever. + {timeout} is the maximum waiting time in milliseconds. If + omitted or -1, wait forever. Timeout of 0 can be used to check the status of a job: > let running = jobwait([{job-id}], 0)[0] == -1 @@ -5692,6 +5932,9 @@ join({list} [, {sep}]) *join()* converted into a string like with |string()|. The opposite function is |split()|. + Can also be used as a |method|: > + mylist->join() + json_decode({expr}) *json_decode()* Convert {expr} from JSON object. Accepts |readfile()|-style list as the input, as well as regular string. May output any @@ -5722,8 +5965,10 @@ json_encode({expr}) *json_encode()* keys({dict}) *keys()* Return a |List| with all the keys of {dict}. The |List| is in arbitrary order. + Can also be used as a |method|: > + mydict->keys() - *len()* *E701* +< *len()* *E701* len({expr}) The result is a Number, which is the length of the argument. When {expr} is a String or a Number the length in bytes is used, as with |strlen()|. @@ -5734,7 +5979,10 @@ len({expr}) The result is a Number, which is the length of the argument. |Dictionary| is returned. Otherwise an error is given. - *libcall()* *E364* *E368* + Can also be used as a |method|: > + mylist->len() + +< *libcall()* *E364* *E368* libcall({libname}, {funcname}, {argument}) Call function {funcname} in the run-time library {libname} with single argument {argument}. @@ -5819,8 +6067,8 @@ line2byte({lnum}) *line2byte()* line just below the last line: > line2byte(line("$") + 1) < This is the buffer size plus one. If 'fileencoding' is empty - it is the file size plus one. - When {lnum} is invalid -1 is returned. + it is the file size plus one. {lnum} is used like with + |getline()|. When {lnum} is invalid -1 is returned. Also see |byte2line()|, |go| and |:goto|. lispindent({lnum}) *lispindent()* @@ -5828,8 +6076,7 @@ lispindent({lnum}) *lispindent()* indenting rules, as with 'lisp'. The indent is counted in spaces, the value of 'tabstop' is relevant. {lnum} is used just like in |getline()|. - When {lnum} is invalid or Vim was not compiled the - |+lispindent| feature, -1 is returned. + When {lnum} is invalid, -1 is returned. list2str({list} [, {utf8}]) *list2str()* Convert each number in {list} to a character string can @@ -5860,6 +6107,8 @@ log({expr}) *log()* :echo log(exp(5)) < 5.0 + Can also be used as a |method|: > + Compute()->log() log10({expr}) *log10()* Return the logarithm of Float {expr} to base 10 as a |Float|. @@ -5870,6 +6119,9 @@ log10({expr}) *log10()* :echo log10(0.01) < -2.0 + Can also be used as a |method|: > + Compute()->log10() + luaeval({expr}[, {expr}]) Evaluate Lua expression {expr} and return its result converted to Vim data structures. See |lua-eval| for more details. @@ -5882,7 +6134,8 @@ map({expr1}, {expr2}) *map()* If {expr2} is a |string|, inside {expr2} |v:val| has the value of the current item. For a |Dictionary| |v:key| has the key of the current item and for a |List| |v:key| has the index of - the current item. + the current item. For a |Blob| |v:key| has the index of the + current byte. Example: > :call map(mylist, '"> " . v:val . " <"') < This puts "> " before and " <" after each item in "mylist". @@ -5918,6 +6171,8 @@ map({expr1}, {expr2}) *map()* Funcref errors inside a function are ignored, unless it was defined with the "abort" flag. + Can also be used as a |method|: > + mylist->map(expr2) maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* When {dict} is omitted or zero: Return the rhs of mapping @@ -6177,7 +6432,7 @@ matcharg({nr}) *matcharg()* Highlighting matches using the |:match| commands are limited to three matches. |matchadd()| does not have this limitation. -matchdelete({id} [, {win}) *matchdelete()* *E802* *E803* +matchdelete({id} [, {win}]) *matchdelete()* *E802* *E803* Deletes a match with ID {id} previously defined by |matchadd()| or one of the |:match| commands. Returns 0 if successful, otherwise -1. See example for |matchadd()|. All matches can @@ -6253,7 +6508,10 @@ max({expr}) Return the maximum value of all items in {expr}. items in {expr} cannot be used as a Number this results in an error. An empty |List| or |Dictionary| results in zero. -menu_get({path}, {modes}) *menu_get()* + Can also be used as a |method|: > + mylist->max() + +menu_get({path} [, {modes}]) *menu_get()* Returns a |List| of |Dictionaries| describing |menus| (defined by |:menu|, |:amenu|, …), including |hidden-menus|. @@ -6307,15 +6565,18 @@ min({expr}) Return the minimum value of all items in {expr}. items in {expr} cannot be used as a Number this results in an error. An empty |List| or |Dictionary| results in zero. - *mkdir()* *E739* + Can also be used as a |method|: > + mylist->min() + +< *mkdir()* *E739* mkdir({name} [, {path} [, {prot}]]) Create directory {name}. If {path} is "p" then intermediate directories are created as necessary. Otherwise it must be "". If {prot} is given it is used to set the protection bits of - the new directory. The default is 0755 (rwxr-xr-x: r/w for - the user readable for others). Use 0700 to make it unreadable - for others. + the new directory. The default is 0o755 (rwxr-xr-x: r/w for + the user, readable for others). Use 0o700 to make it + unreadable for others. {prot} is applied for all parts of {name}. Thus if you create /tmp/foo/bar then /tmp/foo will be created with 0700. Example: > @@ -6350,6 +6611,10 @@ mode([expr]) Return a string that indicates the current mode. s Select by character S Select by line CTRL-S Select blockwise + vs Visual by character using |v_CTRL-O| from + Select mode + Vs Visual by line using |v_CTRL-O| from Select mode + CTRL-Vs Visual blockwise using |v_CTRL-O| from Select mode i Insert ic Insert mode completion |compl-generic| ix Insert mode |i_CTRL-X| completion @@ -6358,8 +6623,7 @@ mode([expr]) Return a string that indicates the current mode. Rv Virtual Replace |gR| Rx Replace mode |i_CTRL-X| completion c Command-line editing - cv Vim Ex mode |gQ| - ce Normal Ex mode |Q| + cv Vim Ex mode |Q| or |gQ| r Hit-enter prompt rm The -- more -- prompt r? |:confirm| query of some sort @@ -6467,6 +6731,7 @@ nextnonblank({lnum}) *nextnonblank()* if getline(nextnonblank(1)) =~ "Java" < When {lnum} is invalid or there is no non-blank line at or below it, zero is returned. + {lnum} is used like with |getline()|. See also |prevnonblank()|. nr2char({expr} [, {utf8}]) *nr2char()* @@ -6499,7 +6764,8 @@ or({expr}, {expr}) *or()* to a number. A List, Dict or Float argument causes an error. Example: > :let bits = or(bits, 0x80) - +< Can also be used as a |method|: > + :let bits = bits->or(0x80) pathshorten({expr}) *pathshorten()* Shorten directory names in the path {expr} and return the @@ -6536,12 +6802,16 @@ pow({x}, {y}) *pow()* :echo pow(32, 0.20) < 2.0 + Can also be used as a |method|: > + Compute()->pow(3) + prevnonblank({lnum}) *prevnonblank()* Return the line number of the first line at or above {lnum} that is not blank. Example: > let ind = indent(prevnonblank(v:lnum - 1)) < When {lnum} is invalid or there is no non-blank line at or above it, zero is returned. + {lnum} is used like with |getline()|. Also see |nextnonblank()|. @@ -6552,7 +6822,11 @@ printf({fmt}, {expr1} ...) *printf()* < May result in: " 99: E42 asdfasdfasdfasdfasdfasdfasdfas" ~ - Often used items are: + When used as a |method| the base is passed as the second + argument: > + Compute()->printf("result: %d") + +< Often used items are: %s string %6S string right-aligned in 6 display cells %6s string right-aligned in 6 bytes @@ -6947,7 +7221,8 @@ reltimefloat({time}) *reltimefloat()* call MyFunction() let seconds = reltimefloat(reltime(start)) See the note of reltimestr() about overhead. - Also see |profiling|. + Also see |profiling|. + If there is an error an empty string is returned reltimestr({time}) *reltimestr()* Return a String that represents the time value of {time}. @@ -6961,6 +7236,7 @@ reltimestr({time}) *reltimestr()* can use split() to remove it. > echo split(reltimestr(reltime(start)))[0] < Also see |profiling|. + If there is an error an empty string is returned *remote_expr()* *E449* remote_expr({server}, {string} [, {idvar} [, {timeout}]]) @@ -7062,6 +7338,10 @@ remove({list}, {idx} [, {end}]) *remove()* Example: > :echo "last item: " . remove(mylist, -1) :call remove(mylist, 0, 9) + +< Can also be used as a |method|: > + mylist->remove(idx) + remove({dict}, {key}) Remove the entry from {dict} with key {key} and return it. Example: > @@ -7088,6 +7368,8 @@ repeat({expr}, {count}) *repeat()* :let longlist = repeat(['a', 'b'], 3) < Results in ['a', 'b', 'a', 'b', 'a', 'b']. + Can also be used as a |method|: > + mylist->repeat(count) resolve({filename}) *resolve()* *E655* On MS-Windows, when {filename} is a shortcut (a .lnk file), @@ -7107,6 +7389,8 @@ reverse({list}) Reverse the order of items in {list} in-place. Returns {list}. If you want a list to remain unmodified make a copy first: > :let revlist = reverse(copy(mylist)) +< Can also be used as a |method|: > + mylist->reverse() round({expr}) *round()* Round off {expr} to the nearest integral value and return it @@ -7121,6 +7405,9 @@ round({expr}) *round()* echo round(-4.5) < -5.0 + Can also be used as a |method|: > + Compute()->round() + rpcnotify({channel}, {event}[, {args}...]) *rpcnotify()* Sends {event} to {channel} via |RPC| and returns immediately. If {channel} is 0, the event is broadcast to all channels. @@ -7148,7 +7435,6 @@ rubyeval({expr}) *rubyeval()* Hashes are represented as Vim |Dictionary| type. Other objects are represented as strings resulted from their "Object#to_s" method. - {only available when compiled with the |+ruby| feature} screenattr({row}, {col}) *screenattr()* Like |screenchar()|, but return the attribute. This is a rather @@ -7195,6 +7481,10 @@ screenpos({winid}, {lnum}, {col}) *screenpos()* The "curscol" value is where the cursor would be placed. For a Tab it would be the same as "endcol", while for a double width character it would be the same as "col". + The |conceal| feature is ignored here, the column numbers are + as if 'conceallevel' is zero. You can set the cursor to the + right position and use |screencol()| to get the value with + |conceal| taken into account. screenrow() *screenrow()* The result is a Number, which is the current screen row of the @@ -7877,7 +8167,8 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()* the last quickfix list. quickfixtextfunc function to get the text to display in the - quickfix window. Refer to + quickfix window. The value can be the name of + a function or a funcref or a lambda. Refer to |quickfix-window-function| for an explanation of how to write the function and an example. title quickfix list title text. See |quickfix-title| @@ -8097,6 +8388,8 @@ sin({expr}) *sin()* :echo sin(-4.01) < 0.763301 + Can also be used as a |method|: > + Compute()->sin() sinh({expr}) *sinh()* Return the hyperbolic sine of {expr} as a |Float| in the range @@ -8108,7 +8401,10 @@ sinh({expr}) *sinh()* :echo sinh(-0.9) < -1.026517 -sockconnect({mode}, {address}, {opts}) *sockconnect()* + Can also be used as a |method|: > + Compute()->sinh() + +sockconnect({mode}, {address} [, {opts}]) *sockconnect()* Connect a socket to an address. If {mode} is "pipe" then {address} should be the path of a named pipe. If {mode} is "tcp" then {address} should be of the form "host:port" where @@ -8120,7 +8416,7 @@ sockconnect({mode}, {address}, {opts}) *sockconnect()* |rpcrequest()| and |rpcnotify()| to communicate with a RPC socket. - {opts} is a dictionary with these keys: + {opts} is an optional dictionary with these keys: |on_data| : callback invoked when data was read from socket data_buffered : read socket data in |channel-buffered| mode. rpc : If set, |msgpack-rpc| will be used to communicate @@ -8186,7 +8482,10 @@ sort({list} [, {func} [, {dict}]]) *sort()* *E702* on numbers, text strings will sort next to each other, in the same order as they were originally. - Also see |uniq()|. + Can also be used as a |method|: > + mylist->sort() + +< Also see |uniq()|. Example: > func MyCompare(i1, i2) @@ -8230,9 +8529,8 @@ spellbadword([{sentence}]) echo spellbadword("the quik brown fox") < ['quik', 'bad'] ~ - The spelling information for the current window is used. The - 'spell' option must be set and the value of 'spelllang' is - used. + The spelling information for the current window and the value + of 'spelllang' are used. *spellsuggest()* spellsuggest({word} [, {max} [, {capital}]]) @@ -8254,8 +8552,7 @@ spellsuggest({word} [, {max} [, {capital}]]) although it may appear capitalized. The spelling information for the current window is used. The - 'spell' option must be set and the values of 'spelllang' and - 'spellsuggest' are used. + values of 'spelllang' and 'spellsuggest' are used. split({expr} [, {pattern} [, {keepempty}]]) *split()* @@ -8281,6 +8578,8 @@ split({expr} [, {pattern} [, {keepempty}]]) *split()* :let items = split(line, ':', 1) < The opposite function is |join()|. + Can also be used as a |method|: > + GetString()->split() sqrt({expr}) *sqrt()* Return the non-negative square root of Float {expr} as a @@ -8294,6 +8593,8 @@ sqrt({expr}) *sqrt()* < nan "nan" may be different, it depends on system libraries. + Can also be used as a |method|: > + Compute()->sqrt() stdioopen({opts}) *stdioopen()* With |--headless| this opens stdin and stdout as a |channel|. @@ -8345,6 +8646,9 @@ str2float({expr}) *str2float()* 12.0. You can strip out thousands separators with |substitute()|: > let f = str2float(substitute(text, ',', '', 'g')) +< + Can also be used as a |method|: > + let f = text->substitute(',', '', 'g')->str2float() str2list({expr} [, {utf8}]) *str2list()* Return a list containing the number values which represent @@ -8359,16 +8663,24 @@ str2list({expr} [, {utf8}]) *str2list()* properly: > str2list("aÌ") returns [97, 769] -str2nr({expr} [, {base}]) *str2nr()* +< Can also be used as a |method|: > + GetString()->str2list() + +str2nr({expr} [, {base} [, {quoted}]]) *str2nr()* Convert string {expr} to a number. {base} is the conversion base, it can be 2, 8, 10 or 16. + When {quoted} is present and non-zero then embedded single + quotes are ignored, thus "1'000'000" is a million. + When {base} is omitted base 10 is used. This also means that a leading zero doesn't cause octal conversion to be used, as - with the default String to Number conversion. + with the default String to Number conversion. Example: > + let nr = str2nr('123') +< When {base} is 16 a leading "0x" or "0X" is ignored. With a - different base the result will be zero. Similarly, when {base} - is 8 a leading "0" is ignored, and when {base} is 2 a leading - "0b" or "0B" is ignored. + different base the result will be zero. Similarly, when + {base} is 8 a leading "0", "0o" or "0O" is ignored, and when + {base} is 2 a leading "0b" or "0B" is ignored. Text after the number is silently ignored. @@ -8483,6 +8795,9 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number, method, use |msgpackdump()| or |json_encode()| if you need to share data with other application. + Can also be used as a |method|: > + mylist->string() + *strlen()* strlen({expr}) The result is a Number, which is the length of the String {expr} in bytes. @@ -8492,6 +8807,9 @@ strlen({expr}) The result is a Number, which is the length of the String |strchars()|. Also see |len()|, |strdisplaywidth()| and |strwidth()|. + Can also be used as a |method|: > + GetString()->strlen() + strpart({src}, {start} [, {len} [, {chars}]]) *strpart()* The result is a String, which is part of {src}, starting from byte {start}, with the byte length {len}. @@ -8566,6 +8884,9 @@ strtrans({expr}) *strtrans()* < This displays a newline in register a as "^@" instead of starting a new line. + Can also be used as a |method|: > + GetString()->strtrans() + strwidth({expr}) *strwidth()* The result is a Number, which is the number of display cells String {expr} occupies. A Tab character is counted as one @@ -8574,6 +8895,9 @@ strwidth({expr}) *strwidth()* Ambiguous, this function's return value depends on 'ambiwidth'. Also see |strlen()|, |strdisplaywidth()| and |strchars()|. + Can also be used as a |method|: > + GetString()->strwidth() + submatch({nr} [, {list}]) *submatch()* *E935* Only for an expression in a |:substitute| command or substitute() function. @@ -8641,6 +8965,9 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()* |submatch()| returns. Example: > :echo substitute(s, '%\(\x\x\)', {m -> '0x' . m[1]}, 'g') +< Can also be used as a |method|: > + GetString()->substitute(pat, sub, flags) + swapinfo({fname}) *swapinfo()* The result is a dictionary, which holds information about the swapfile {fname}. The available fields are: @@ -8676,7 +9003,7 @@ synID({lnum}, {col}, {trans}) *synID()* line. 'synmaxcol' applies, in a longer line zero is returned. Note that when the position is after the last character, that's where the cursor can be in Insert mode, synID() returns - zero. + zero. {lnum} is used like with |getline()|. When {trans} is |TRUE|, transparent items are reduced to the item that they reveal. This is useful when wanting to know @@ -8725,17 +9052,23 @@ synIDattr({synID}, {what} [, {mode}]) *synIDattr()* cursor): > :echo synIDattr(synIDtrans(synID(line("."), col("."), 1)), "fg") < + Can also be used as a |method|: > + :echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg") + synIDtrans({synID}) *synIDtrans()* The result is a Number, which is the translated syntax ID of {synID}. This is the syntax group ID of what is being used to highlight the character. Highlight links given with ":highlight link" are followed. + Can also be used as a |method|: > + :echo synID(line("."), col("."), 1)->synIDtrans()->synIDattr("fg") + synconcealed({lnum}, {col}) *synconcealed()* The result is a |List| with currently three items: 1. The first item in the list is 0 if the character at the position {lnum} and {col} is not part of a concealable - region, 1 if it is. + region, 1 if it is. {lnum} is used like with |getline()|. 2. The second item in the list is a string. If the first item is 1, the second item contains the text which will be displayed in place of the concealed text, depending on the @@ -8759,8 +9092,9 @@ synconcealed({lnum}, {col}) *synconcealed()* synstack({lnum}, {col}) *synstack()* Return a |List|, which is the stack of syntax items at the - position {lnum} and {col} in the current window. Each item in - the List is an ID like what |synID()| returns. + position {lnum} and {col} in the current window. {lnum} is + used like with |getline()|. Each item in the List is an ID + like what |synID()| returns. The first item in the List is the outer region, following are items contained in that one. The last one is what |synID()| returns, unless not the whole item is highlighted or it is a @@ -8793,7 +9127,7 @@ system({cmd} [, {input}]) *system()* *E677* *E5677* Note: system() cannot write to or read from backgrounded ("&") shell commands, e.g.: > - :echo system("cat - &", "foo")) + :echo system("cat - &", "foo") < which is equivalent to: > $ echo foo | bash -c 'cat - &' < The pipes are disconnected (unless overridden by shell @@ -8827,6 +9161,8 @@ system({cmd} [, {input}]) *system()* *E677* Unlike ":!cmd" there is no automatic check for changed files. Use |:checktime| to force a check. + Can also be used as a |method|: > + :echo GetCmd()->system() systemlist({cmd} [, {input} [, {keepempty}]]) *systemlist()* Same as |system()|, but returns a |List| with lines (parts of @@ -8842,6 +9178,8 @@ systemlist({cmd} [, {input} [, {keepempty}]]) *systemlist()* < Returns an empty string on error. + Can also be used as a |method|: > + :echo GetCmd()->systemlist() tabpagebuflist([{arg}]) *tabpagebuflist()* The result is a |List|, where each item is the number of the @@ -8965,6 +9303,8 @@ tan({expr}) *tan()* :echo tan(-4.01) < -1.181502 + Can also be used as a |method|: > + Compute()->tan() tanh({expr}) *tanh()* Return the hyperbolic tangent of {expr} as a |Float| in the @@ -8976,7 +9316,9 @@ tanh({expr}) *tanh()* :echo tanh(-1) < -0.761594 - + Can also be used as a |method|: > + Compute()->tanh() +< *timer_info()* timer_info([{id}]) Return a list with information about timers. @@ -9102,6 +9444,9 @@ trunc({expr}) *trunc()* echo trunc(4.0) < 4.0 + Can also be used as a |method|: > + Compute()->trunc() + type({expr}) *type()* The result is a Number representing the type of {expr}. Instead of using the number directly, it is better to use the @@ -9128,6 +9473,9 @@ type({expr}) *type()* < To check if the v:t_ variables exist use this: > :if exists('v:t_number') +< Can also be used as a |method|: > + mylist->type() + undofile({name}) *undofile()* Return the name of the undo file that would be used for a file with name {name} when writing. This uses the 'undodir' @@ -9138,8 +9486,6 @@ undofile({name}) *undofile()* If {name} is empty undofile() returns an empty string, since a buffer without a file name will not write an undo file. Useful in combination with |:wundo| and |:rundo|. - When compiled without the |+persistent_undo| option this always - returns an empty string. undotree() *undotree()* Return the current state of the undo tree in a dictionary with @@ -9192,10 +9538,15 @@ uniq({list} [, {func} [, {dict}]]) *uniq()* *E882* < The default compare function uses the string representation of each item. For the use of {func} and {dict} see |sort()|. + Can also be used as a |method|: > + mylist->uniq() + values({dict}) *values()* Return a |List| with all the values of {dict}. The |List| is in arbitrary order. + Can also be used as a |method|: > + mydict->values() virtcol({expr}) *virtcol()* The result is a Number, which is the screen column of the file @@ -9304,10 +9655,12 @@ win_gettype([{nr}]) *win_gettype()* Return the type of the window: "autocmd" autocommand window. Temporary window used to execute autocommands. - "popup" popup window |popup| - "preview" preview window |preview-window| "command" command-line window |cmdwin| (empty) normal window + "loclist" |location-list-window| + "popup" popup window |popup| + "preview" preview window |preview-window| + "quickfix" |quickfix-window| "unknown" window {nr} not found When {nr} is omitted return the type of the current window. @@ -9338,7 +9691,7 @@ 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. - Return [0, 0] if the window cannot be found in the current + Returns [0, 0] if the window cannot be found in the current tabpage. win_splitmove({nr}, {target} [, {options}]) *win_splitmove()* @@ -9371,6 +9724,9 @@ winbufnr({nr}) The result is a Number, which is the number of the buffer Example: > :echo "The file in the current window is " . bufname(winbufnr(0)) < + Can also be used as a |method|: > + FindWindow()->winbufnr()->bufname() +< *wincol()* wincol() The result is a Number, which is the virtual column of the cursor in the window. This is counting screen cells from the @@ -9584,6 +9940,8 @@ xor({expr}, {expr}) *xor()* to a number. A List, Dict or Float argument causes an error. Example: > :let bits = xor(bits, 0x80) +< Can also be used as a |method|: > + :let bits = bits->xor(0x80) < @@ -9921,7 +10279,9 @@ This function can then be called with: > The recursiveness of user functions is restricted with the |'maxfuncdepth'| option. -It is also possible to use `:eval`. It does not support a range. +It is also possible to use `:eval`. It does not support a range, but does +allow for method chaining, e.g.: > + eval GetList()->Filter()->append('$') AUTOMATICALLY LOADING FUNCTIONS ~ @@ -10077,14 +10437,15 @@ This does NOT work: > When the selected range of items is partly past the end of the list, items will be added. - *:let+=* *:let-=* *:letstar=* - *:let/=* *:let%=* *:let.=* *E734* + *:let+=* *:let-=* *:letstar=* + *:let/=* *:let%=* *:let.=* *:let..=* *E734* :let {var} += {expr1} Like ":let {var} = {var} + {expr1}". :let {var} -= {expr1} Like ":let {var} = {var} - {expr1}". :let {var} *= {expr1} Like ":let {var} = {var} * {expr1}". :let {var} /= {expr1} Like ":let {var} = {var} / {expr1}". :let {var} %= {expr1} Like ":let {var} = {var} % {expr1}". :let {var} .= {expr1} Like ":let {var} = {var} . {expr1}". +:let {var} ..= {expr1} Like ":let {var} = {var} .. {expr1}". These fail if {var} was not set yet and when the type of {var} and {expr1} don't fit the operator. @@ -10195,7 +10556,9 @@ text... {endmarker} Set internal variable {var-name} to a |List| containing the lines of text bounded by the string - {endmarker}. + {endmarker}. The lines of text is used as a + |literal-string|. + {endmarker} must not contain white space. {endmarker} cannot start with a lower case character. The last line should end only with the {endmarker} string without any other character. Watch out for @@ -10662,7 +11025,7 @@ text... < *:eval* :eval {expr} Evaluate {expr} and discard the result. Example: > - :eval append(Filter(Getlist()), '$') + :eval Getlist()->Filter()->append('$') < The expression is supposed to have a side effect, since the resulting value is not used. In the example @@ -11701,7 +12064,7 @@ displayed. *except-several-errors* When several errors appear in a single command, the first error message is -usually the most specific one and therefor converted to the error exception. +usually the most specific one and therefore converted to the error exception. Example: > echo novar causes > diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 36ed6bbac1..4d0fdd71cc 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -135,6 +135,7 @@ can be used to overrule the filetype used for certain extensions: *.inc g:filetype_inc *.w g:filetype_w |ft-cweb-syntax| *.i g:filetype_i |ft-progress-syntax| + *.m g:filetype_m |ft-mathematica-syntax| *.p g:filetype_p |ft-pascal-syntax| *.pp g:filetype_pp |ft-pascal-syntax| *.sh g:bash_is_sh |ft-sh-syntax| @@ -580,7 +581,7 @@ To disable bold highlighting: > MARKDOWN *ft-markdown-plugin* To enable folding use this: > - let g:markdown_folding = 1 + let g:markdown_folding = 1 < PDF *ft-pdf-plugin* diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt index 8e2cb2f728..80c934d13b 100644 --- a/runtime/doc/fold.txt +++ b/runtime/doc/fold.txt @@ -535,6 +535,8 @@ nest, the nested fold is one character right of the fold it's contained in. A closed fold is indicated with a '+'. +These characters can be changed with the 'fillchars' option. + Where the fold column is too narrow to display all nested folds, digits are shown to indicate the nesting level. diff --git a/runtime/doc/ft_ps1.txt b/runtime/doc/ft_ps1.txt index df1480b929..3eb89a4c24 100644 --- a/runtime/doc/ft_ps1.txt +++ b/runtime/doc/ft_ps1.txt @@ -1,4 +1,4 @@ -*ps1.txt* A Windows PowerShell syntax plugin for Vim +*ft_ps1.txt* A Windows PowerShell syntax plugin for Vim Author: Peter Provost <https://www.github.com/PProvost> License: Apache 2.0 diff --git a/runtime/doc/ft_raku.txt b/runtime/doc/ft_raku.txt index 26ada8a140..00b140ee9c 100644 --- a/runtime/doc/ft_raku.txt +++ b/runtime/doc/ft_raku.txt @@ -1,4 +1,4 @@ -*vim-raku.txt* The Raku programming language filetype +*ft_raku.txt* The Raku programming language filetype *vim-raku* @@ -45,7 +45,7 @@ Numbers, subscripts and superscripts are available with 's' and 'S': 1s â‚ 1S ¹ ~ 2s â‚‚ 9S â¹ ~ -But some don´t come defined by default. Those are digraph definitions you can +But some don't come defined by default. Those are digraph definitions you can add in your ~/.vimrc file. > exec 'digraph \\ '.char2nr('∖') exec 'digraph \< '.char2nr('≼') diff --git a/runtime/doc/ft_sql.txt b/runtime/doc/ft_sql.txt index f38c8edbf3..53a99a9e1d 100644 --- a/runtime/doc/ft_sql.txt +++ b/runtime/doc/ft_sql.txt @@ -436,7 +436,7 @@ the space bar): replace the column list with the list of tables. - This allows you to quickly drill down into a table to view its columns and back again. - - <Right> and <Left> can be also be chosen via + - <Right> and <Left> can also be chosen via your |init.vim| > let g:ftplugin_sql_omni_key_right = '<Right>' let g:ftplugin_sql_omni_key_left = '<Left>' diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index 0f1fa2b7a7..812259741f 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -175,7 +175,6 @@ system. To do this, put these commands in your vimrc file: > :map <F4> :emenu <C-Z> Pressing <F4> will start the menu. You can now use the cursor keys to select a menu entry. Hit <Enter> to execute it. Hit <Esc> if you want to cancel. -This does require the |+menu| feature enabled at compile time. Creating New Menus *creating-menus* @@ -473,9 +472,8 @@ Executing Menus *execute-menus* insert-mode menu Eg: > :emenu File.Exit -If the console-mode vim has been compiled with WANT_MENU defined, you can -use :emenu to access useful menu items you may have got used to from GUI -mode. See 'wildmenu' for an option that works well with this. See +You can use :emenu to access useful menu items you may have got used to from +GUI mode. See 'wildmenu' for an option that works well with this. See |console-menus| for an example. When using a range, if the lines match with '<,'>, then the menu is executed diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index 8b096ff28b..6416f49061 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -125,11 +125,12 @@ Advanced editing ~ |windows.txt| commands for using multiple windows and buffers |tabpage.txt| commands for using multiple tab pages |spell.txt| spell checking -|diff.txt| working with two to four versions of the same file +|diff.txt| working with two to eight versions of the same file |autocmd.txt| automatically executing commands on an event |eval.txt| expression evaluation, conditional commands |fold.txt| hide (fold) ranges of lines |lua.txt| Lua API +|api.txt| Nvim API via RPC, Lua and VimL Special issues ~ |testing.txt| testing Vim and Vim scripts @@ -137,14 +138,17 @@ Special issues ~ |remote.txt| using Vim as a server or client Programming language support ~ -|indent.txt| automatic indenting for C and other languages -|lsp.txt| Language Server Protocol (LSP) -|syntax.txt| syntax highlighting -|filetype.txt| settings done specifically for a type of file -|quickfix.txt| commands for a quick edit-compile-fix cycle -|ft_ada.txt| Ada (the programming language) support -|ft_rust.txt| Filetype plugin for Rust -|ft_sql.txt| about the SQL filetype plugin +|indent.txt| automatic indenting for C and other languages +|lsp.txt| Language Server Protocol (LSP) +|treesitter.txt| tree-sitter library for incremental parsing of buffers +|syntax.txt| syntax highlighting +|filetype.txt| settings done specifically for a type of file +|quickfix.txt| commands for a quick edit-compile-fix cycle +|ft_ada.txt| Ada (the programming language) support +|ft_ps1.txt| Filetype plugin for Windows PowerShell +|ft_raku.txt| Filetype plugin for Raku +|ft_rust.txt| Filetype plugin for Rust +|ft_sql.txt| about the SQL filetype plugin Language support ~ |digraph.txt| list of available digraphs diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index 7643d84017..4a94701b2e 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -249,7 +249,6 @@ command: > It is possible to add translated help files, next to the original English help files. Vim will search for all help in "doc" directories in 'runtimepath'. -This is only available when compiled with the |+multi_lang| feature. At this moment translations are available for: Chinese - multiple authors diff --git a/runtime/doc/if_ruby.txt b/runtime/doc/if_ruby.txt index 02edd50ae8..47305c65fb 100644 --- a/runtime/doc/if_ruby.txt +++ b/runtime/doc/if_ruby.txt @@ -32,10 +32,6 @@ downloading Ruby there. This form of the |:ruby| command is mainly useful for including ruby code in vim scripts. - Note: This command doesn't work when the Ruby feature - wasn't compiled in. To avoid errors, see - |script-here|. - Example Vim script: > function! RedGem() diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 17258f896d..baa7bc1992 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -155,6 +155,7 @@ commands in CTRL-X submode *i_CTRL-X_index* |i_CTRL-X_CTRL-Y| CTRL-X CTRL-Y scroll down |i_CTRL-X_CTRL-U| CTRL-X CTRL-U complete with 'completefunc' |i_CTRL-X_CTRL-V| CTRL-X CTRL-V complete like in : command line +|i_CTRL-X_CTRL-Z| CTRL-X CTRL-Z stop completion, keeping the text as-is |i_CTRL-X_CTRL-]| CTRL-X CTRL-] complete tags |i_CTRL-X_s| CTRL-X s spelling suggestions @@ -699,8 +700,7 @@ tag char note action in Normal mode ~ tag char note action in Normal mode ~ ------------------------------------------------------------------------------ -|g_CTRL-A| g CTRL-A only when compiled with MEM_PROFILE - defined: dump a memory profile +|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 @@ -865,6 +865,7 @@ tag char note action in Normal mode ~ |zv| zv open enough folds to view the cursor line |zw| zw permanently mark word as incorrectly spelled |zx| zx re-apply 'foldlevel' and do "zv" +|zy| zy yank without trailing spaces |zz| zz redraw, cursor line at center of window |z<Left>| z<Left> same as "zh" |z<Right>| z<Right> same as "zl" @@ -1096,8 +1097,9 @@ tag command action in Command-line editing mode ~ ============================================================================== 5. Terminal mode *terminal-mode-index* -In a |terminal| buffer all keys except |CTRL-\_CTRL-N| are forwarded to the -terminal job. Use CTRL-\_CTRL-N to go to Normal mode. +In a |terminal| buffer all keys except CTRL-\ are forwarded to the terminal +job. If CTRL-\ is pressed, the next key is forwarded unless it is CTRL-N. +Use |CTRL-\_CTRL-N| to go to Normal mode. You found it, Arthur! *holy-grail* @@ -1200,6 +1202,7 @@ tag command action ~ |:cgetfile| :cg[etfile] read file with error messages |:changes| :changes print the change list |:chdir| :chd[ir] change directory +|:checkhealth| :checkh[ealth] run healthchecks |:checkpath| :che[ckpath] list included files |:checktime| :checkt[ime] check timestamp of loaded buffers |:chistory| :chi[story] list the error lists @@ -1277,6 +1280,7 @@ tag command action ~ |:endtry| :endt[ry] end previous :try |:endwhile| :endw[hile] end previous :while |:enew| :ene[w] edit a new, unnamed buffer +|:eval| :ev[al] evaluate an expression and discard the result |:ex| :ex same as ":edit" |:execute| :exe[cute] execute result of expressions |:exit| :exi[t] same as ":xit" @@ -1450,14 +1454,12 @@ tag command action ~ |:packloadall| :packl[oadall] load all packages under 'packpath' |:pclose| :pc[lose] close preview window |:pedit| :ped[it] edit file in the preview window -|:perl| :perl execute perl command -|:perldo| :perldo execute perl command for each line -|:perfile| :perlfile execute perl script file +|:perl| :pe[rl] execute perl command +|:perldo| :perld[o] execute perl command for each line +|:perlfile| :perlf[ile] execute perl script file |:print| :p[rint] print lines |:profdel| :profd[el] stop profiling a function or script |:profile| :prof[ile] profiling functions and scripts -|:promptfind| :pro[mptfind] open GUI dialog for searching -|:promptrepl| :promptr[epl] open GUI dialog for search/replace |:pop| :po[p] jump to older entry in tag stack |:popup| :popu[p] popup a menu by name |:ppop| :pp[op] ":pop" in preview window @@ -1536,7 +1538,6 @@ tag command action ~ buffer list |:scriptnames| :scr[iptnames] list names of all sourced Vim scripts |:scriptencoding| :scripte[ncoding] encoding used in sourced Vim script -|:scriptversion| :scriptv[ersion] version of Vim script used |:scscope| :scs[cope] split window and execute cscope command |:set| :se[t] show or set options |:setfiletype| :setf[iletype] set 'filetype', unless it was set already @@ -1548,8 +1549,6 @@ tag command action ~ |:sign| :sig[n] manipulate signs |:silent| :sil[ent] run a command silently |:sleep| :sl[eep] do nothing for a few seconds -|:sleep!| :sl[eep]! do nothing for a few seconds, without the - cursor visible |:slast| :sla[st] split window and go to last file in the argument list |:smagic| :sm[agic] :substitute with 'magic' @@ -1611,7 +1610,7 @@ tag command action ~ |:tab| :tab create new tab when opening new window |:tag| :ta[g] jump to tag |:tags| :tags show the contents of the tag stack -|:tcd| :tcd change directory for tab page +|:tcd| :tc[d] change directory for tab page |:tchdir| :tch[dir] change directory for tab page |:terminal| :te[rminal] open a terminal buffer |:tfirst| :tf[irst] jump to first matching tag diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index c8a4168ab2..8f6de3e36f 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -68,12 +68,18 @@ CTRL-A Insert previously inserted text. CTRL-W Delete the word before the cursor (see |i_backspacing| about joining lines). See the section "word motions", |word-motions|, for the definition of a word. + *i_CTRL-W-default* + By default, sets a new undo point before deleting. + |default-mappings| *i_CTRL-U* CTRL-U Delete all entered characters before the cursor in the current line. If there are no newly entered characters and 'backspace' is not empty, delete all characters before the cursor in the current line. See |i_backspacing| about joining lines. + *i_CTRL-U-default* + By default, sets a new undo point before deleting. + |default-mappings| *i_CTRL-I* *i_<Tab>* *i_Tab* <Tab> or CTRL-I Insert a tab. If the 'expandtab' option is on, the equivalent number of spaces is inserted (use CTRL-V <Tab> to @@ -616,6 +622,8 @@ Completion can be done for: 12. Spelling suggestions |i_CTRL-X_s| 13. keywords in 'complete' |i_CTRL-N| |i_CTRL-P| +Additionally, |i_CTRL-X_CTRL-Z| stops completion without changing the text. + All these, except CTRL-N and CTRL-P, are done in CTRL-X mode. This is a sub-mode of Insert and Replace modes. You enter CTRL-X mode by typing CTRL-X and one of the CTRL-X commands. You exit CTRL-X mode by typing a key that is @@ -1016,6 +1024,12 @@ CTRL-P Find previous match for words that start with the other contexts unless a double CTRL-X is used. +Stop completion *compl-stop* + + *i_CTRL-X_CTRL-Z* +CTRL-X CTRL-Z Stop completion without changing the text. + + FUNCTIONS FOR FINDING COMPLETIONS *complete-functions* This applies to 'completefunc' and 'omnifunc'. @@ -1047,7 +1061,8 @@ On the second invocation the arguments are: The function must return a List with the matching words. These matches usually include the "a:base" text. When there are no matches return an empty -List. +List. Note that the cursor may have moved since the first invocation, the +text may have been changed. In order to return more information than the matching words, return a Dict that contains the List. The Dict can have these items: diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index f739e2e88b..2baf3a247f 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -454,9 +454,10 @@ Ex mode Like Command-line mode, but after entering a command command line. |Ex-mode| *Terminal-mode* -Terminal mode In Terminal mode all input (except |c_CTRL-\_CTRL-N|) - is sent to the process running in the current - |terminal| buffer. +Terminal mode In Terminal mode all input (except CTRL-\) is sent to + the process running in the current |terminal| buffer. + If CTRL-\ is pressed, the next key is sent unless it + is CTRL-N (|CTRL-\_CTRL-N|). If the 'showmode' option is on "-- TERMINAL --" is shown at the bottom of the window. diff --git a/runtime/doc/job_control.txt b/runtime/doc/job_control.txt index bf01e8e266..6a9d865c40 100644 --- a/runtime/doc/job_control.txt +++ b/runtime/doc/job_control.txt @@ -19,7 +19,7 @@ Job Id *job-id* Each job is identified by an integer id, unique for the life of the current Nvim session. Each job-id is a valid |channel-id|: they share the same "key space". Functions like |jobstart()| return job ids; functions like -|jobsend()|, |jobstop()|, |rpcnotify()|, and |rpcrequest()| take job ids. +|jobstop()|, |chansend()|, |rpcnotify()|, and |rpcrequest()| take job ids. Job stdio streams form a |channel| which can send and receive raw bytes or |msgpack-rpc| messages. @@ -28,7 +28,7 @@ Job stdio streams form a |channel| which can send and receive raw bytes or Usage *job-control-usage* To control jobs, use the "job…" family of functions: |jobstart()|, -|jobsend()|, |jobstop()|. +|jobstop()|, etc. Example: > diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 531374620a..e76e224596 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -135,12 +135,6 @@ FAQ *lsp-faq* autocmd BufWritePre *.rs lua vim.lsp.buf.formatting_sync(nil, 1000) < - *vim.lsp.callbacks* -- Q: What happened to `vim.lsp.callbacks`? - A: After better defining the interface of |lsp-handler|s, we thought it best - to remove the generic usage of `callbacks` and transform to `handlers`. - Due to this, `vim.lsp.callbacks` was renamed to |vim.lsp.handlers|. - *lsp-vs-treesitter* - Q: How do LSP and Treesitter compare? A: LSP requires a client and language server. The language server uses @@ -208,23 +202,28 @@ responses and notifications from LSP servers. For |lsp-request|, each |lsp-handler| has this signature: > - function(err, method, result, client_id, bufnr, config) + function(err, result, ctx, config) < Parameters: ~ {err} (table|nil) When the language server is unable to complete a request, a table with information about the error is sent. Otherwise, it is `nil`. See |lsp-response|. - {method} (string) - The |lsp-method| name. {result} (Result | Params | nil) When the language server is able to succesfully complete a request, this contains the `result` key of the response. See |lsp-response|. - {client_id} (number) - The ID of the |vim.lsp.client|. - {bufnr} (Buffer) - Buffer handle, or 0 for current. + {ctx} (table) + Context describes additional calling state + associated with the handler. It consists of the + following key, value pairs: + + {method} (string) + The |lsp-method| name. + {client_id} (number) + The ID of the |vim.lsp.client|. + {bufnr} (Buffer) + Buffer handle, or 0 for current. {config} (table) Configuration for the handler. @@ -244,21 +243,24 @@ For |lsp-request|, each |lsp-handler| has this signature: > For |lsp-notification|, each |lsp-handler| has this signature: > - function(err, method, params, client_id, bufnr, config) + function(err, result, ctx, config) < Parameters: ~ {err} (nil) This is always `nil`. See |lsp-notification| - {method} (string) - The |lsp-method| name. - {params} (Params) + {result} (Result) This contains the `params` key of the notification. See |lsp-notification| - {client_id} (number) - The ID of the |vim.lsp.client| - {bufnr} (nil) - `nil`, as the server doesn't have an associated buffer. + {ctx} (table) + Context describes additional calling state + associated with the handler. It consists of the + following key, value pairs: + + {method} (string) + The |lsp-method| name. + {client_id} (number) + The ID of the |vim.lsp.client|. {config} (table) Configuration for the handler. @@ -328,6 +330,18 @@ To configure the behavior of a builtin |lsp-handler|, the convenient method } } < + Some handlers do not have an explicitly named handler function (such as + |on_publish_diagnostics()|). To override these, first create a reference + to the existing handler: > + + local on_references = vim.lsp.handlers["textDocument/references"] + vim.lsp.handlers["textDocument/references"] = vim.lsp.with( + on_references, { + -- Use location list instead of quickfix list + loclist = true, + } + ) +< *lsp-handler-resolution* Handlers can be set by: @@ -525,6 +539,27 @@ LspDiagnosticsSignHint Used for "Hint" signs in sign column. See |vim.lsp.diagnostic.set_signs()| + *lsp-highlight-codelens* + +Highlight groups related to |lsp-codelens| functionality. + + *hl-LspCodeLens* +LspCodeLens + Used to color the virtual text of the codelens. See + |nvim_buf_set_extmark()|. + +LspCodeLensSeparator *hl-LspCodeLensSeparator* + Used to color the seperator between two or more code lens. + + *lsp-highlight-signature* + +Highlight groups related to |vim.lsp.handlers.signature_help()|. + + *hl-LspSignatureActiveParameter* +LspSignatureActiveParameter + Used to highlight the active parameter in the signature help. See + |vim.lsp.handlers.signature_help()|. + ============================================================================== AUTOCOMMANDS *lsp-autocommands* @@ -739,15 +774,6 @@ get_log_path() *vim.lsp.get_log_path()* Return: ~ (String) Path to logfile. -init({client}, {bufnr}) *vim.lsp.init()* - client_id → state - - state pending_change?: function that the timer starts to - trigger didChange pending_changes: list of tables with the - pending changesets; for incremental_sync only - use_incremental_sync: bool buffers?: table (bufnr → lines); - for incremental sync only timer?: uv_timer - omnifunc({findstart}, {base}) *vim.lsp.omnifunc()* Implements 'omnifunc' compatible LSP completion. @@ -801,109 +827,117 @@ start_client({config}) *vim.lsp.start_client()* table. Parameters: ~ - {root_dir} (required, string) Directory where the - LSP server will base its rootUri on - initialization. - {cmd} (required, string or list treated like - |jobstart()|) Base command that - initiates the LSP client. - {cmd_cwd} (string, default=|getcwd()|) Directory - to launch the `cmd` process. Not - related to `root_dir` . - {cmd_env} (table) Environment flags to pass to - the LSP on spawn. Can be specified - using keys like a map or as a list with `k=v` pairs or both. Non-string values are - coerced to string. Example: > + {root_dir} (string) Directory where the LSP + server will base its rootUri on + initialization. + {cmd} (required, string or list treated + like |jobstart()|) Base command that + initiates the LSP client. + {cmd_cwd} (string, default=|getcwd()|) + Directory to launch the `cmd` + process. Not related to `root_dir` . + {cmd_env} (table) Environment flags to pass to + the LSP on spawn. Can be specified + using keys like a map or as a list + with `k=v` pairs or both. Non-string values are + coerced to string. Example: > { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; } < - {capabilities} Map overriding the default capabilities - defined by - |vim.lsp.protocol.make_client_capabilities()|, - passed to the language server on - initialization. Hint: use - make_client_capabilities() and modify - its result. - • Note: To send an empty dictionary use - `{[vim.type_idx]=vim.types.dictionary}` - , else it will be encoded as an - array. - {handlers} Map of language server method names to - |lsp-handler| - {settings} Map with language server specific - settings. These are returned to the - language server if requested via - `workspace/configuration` . Keys are - case-sensitive. - {init_options} Values to pass in the initialization - request as `initializationOptions` . - See `initialize` in the LSP spec. - {name} (string, default=client-id) Name in log - messages. - {get_language_id} function(bufnr, filetype) -> language - ID as string. Defaults to the filetype. - {offset_encoding} (default="utf-16") One of "utf-8", - "utf-16", or "utf-32" which is the - encoding that the LSP server expects. - Client does not verify this is correct. - {on_error} Callback with parameters (code, ...), - invoked when the client operation - throws an error. `code` is a number - describing the error. Other arguments - may be passed depending on the error - kind. See |vim.lsp.client_errors| for - possible errors. Use - `vim.lsp.client_errors[code]` to get - human-friendly name. - {before_init} Callback with parameters - (initialize_params, config) invoked - before the LSP "initialize" phase, - where `params` contains the parameters - being sent to the server and `config` - is the config that was passed to - |vim.lsp.start_client()|. You can use - this to modify parameters before they - are sent. - {on_init} Callback (client, initialize_result) - invoked after LSP "initialize", where - `result` is a table of `capabilities` - and anything else the server may send. - For example, clangd sends - `initialize_result.offsetEncoding` if - `capabilities.offsetEncoding` was sent - to it. You can only modify the - `client.offset_encoding` here before - any notifications are sent. Most - language servers expect to be sent - client specified settings after - initialization. Neovim does not make - this assumption. A - `workspace/didChangeConfiguration` - notification should be sent to the - server during on_init. - {on_exit} Callback (code, signal, client_id) - invoked on client exit. - • code: exit code of the process - • signal: number describing the signal - used to terminate (if any) - • client_id: client handle - {on_attach} Callback (client, bufnr) invoked when - client attaches to a buffer. - {trace} "off" | "messages" | "verbose" | nil - passed directly to the language server - in the initialize request. - Invalid/empty values will default to - "off" - {flags} A table with flags for the client. The - current (experimental) flags are: - • allow_incremental_sync (bool, default - true): Allow using incremental sync - for buffer edits - • debounce_text_changes (number, - default nil): Debounce didChange - notifications to the server by the - given number in milliseconds. No - debounce occurs if nil + {capabilities} Map overriding the default + capabilities defined by + |vim.lsp.protocol.make_client_capabilities()|, + passed to the language server on + initialization. Hint: use + make_client_capabilities() and modify + its result. + • Note: To send an empty dictionary + use + `{[vim.type_idx]=vim.types.dictionary}` + , else it will be encoded as an + array. + {handlers} Map of language server method names + to |lsp-handler| + {settings} Map with language server specific + settings. These are returned to the + language server if requested via + `workspace/configuration` . Keys are + case-sensitive. + {init_options} Values to pass in the initialization + request as `initializationOptions` . + See `initialize` in the LSP spec. + {name} (string, default=client-id) Name in + log messages. + {workspace_folders} (table) List of workspace folders + passed to the language server. + Defaults to root_dir if not set. See + `workspaceFolders` in the LSP spec + {get_language_id} function(bufnr, filetype) -> language + ID as string. Defaults to the + filetype. + {offset_encoding} (default="utf-16") One of "utf-8", + "utf-16", or "utf-32" which is the + encoding that the LSP server expects. + Client does not verify this is + correct. + {on_error} Callback with parameters (code, ...), + invoked when the client operation + throws an error. `code` is a number + describing the error. Other arguments + may be passed depending on the error + kind. See |vim.lsp.client_errors| for + possible errors. Use + `vim.lsp.client_errors[code]` to get + human-friendly name. + {before_init} Callback with parameters + (initialize_params, config) invoked + before the LSP "initialize" phase, + where `params` contains the + parameters being sent to the server + and `config` is the config that was + passed to |vim.lsp.start_client()|. + You can use this to modify parameters + before they are sent. + {on_init} Callback (client, initialize_result) + invoked after LSP "initialize", where + `result` is a table of `capabilities` + and anything else the server may + send. For example, clangd sends + `initialize_result.offsetEncoding` if + `capabilities.offsetEncoding` was + sent to it. You can only modify the + `client.offset_encoding` here before + any notifications are sent. Most + language servers expect to be sent + client specified settings after + initialization. Neovim does not make + this assumption. A + `workspace/didChangeConfiguration` + notification should be sent to the + server during on_init. + {on_exit} Callback (code, signal, client_id) + invoked on client exit. + • code: exit code of the process + • signal: number describing the + signal used to terminate (if any) + • client_id: client handle + {on_attach} Callback (client, bufnr) invoked when + client attaches to a buffer. + {trace} "off" | "messages" | "verbose" | nil + passed directly to the language + server in the initialize request. + Invalid/empty values will default to + "off" + {flags} A table with flags for the client. + The current (experimental) flags are: + • allow_incremental_sync (bool, + default true): Allow using + incremental sync for buffer edits + • debounce_text_changes (number, + default nil): Debounce didChange + notifications to the server by the + given number in milliseconds. No + debounce occurs if nil Return: ~ Client id. |vim.lsp.get_client_by_id()| Note: client may @@ -947,9 +981,6 @@ add_workspace_folder({workspace_folder}) not provided, the user will be prompted for a path using |input()|. -call_hierarchy({method}) *vim.lsp.buf.call_hierarchy()* - TODO: Documentation - clear_references() *vim.lsp.buf.clear_references()* Removes document highlights from current buffer. @@ -1187,7 +1218,27 @@ clear({bufnr}, {client_id}, {diagnostic_ns}, {sign_ns}) namespace {sign_ns} number|nil Associated sign namespace -get({bufnr}, {client_id}) *vim.lsp.diagnostic.get()* +disable({bufnr}, {client_id}) *vim.lsp.diagnostic.disable()* + Disable diagnostics for the given buffer and client + + Parameters: ~ + {bufnr} (optional, number): Buffer handle, defaults + to current + {client_id} (optional, number): Disable diagnostics for + the given client. The default is to disable + diagnostics for all attached clients. + +enable({bufnr}, {client_id}) *vim.lsp.diagnostic.enable()* + Enable diagnostics for the given buffer and client + + Parameters: ~ + {bufnr} (optional, number): Buffer handle, defaults + to current + {client_id} (optional, number): Enable diagnostics for + the given client. The default is to enable + diagnostics for all attached clients. + +get({bufnr}, {client_id}, {predicate}) *vim.lsp.diagnostic.get()* Return associated diagnostics for bufnr Parameters: ~ @@ -1195,6 +1246,8 @@ get({bufnr}, {client_id}) *vim.lsp.diagnostic.get()* {client_id} number|nil If nil, then return all of the diagnostics. Else, return just the diagnostics associated with the client_id. + {predicate} function|nil Optional function for filtering + diagnostics get_all({client_id}) *vim.lsp.diagnostic.get_all()* Get all diagnostics for clients @@ -1239,18 +1292,18 @@ get_line_diagnostics({bufnr}, {line_nr}, {opts}, {client_id}) Get the diagnostics by line Parameters: ~ - {bufnr} number The buffer number - {line_nr} number The line number - {opts} table|nil Configuration keys - • severity: (DiagnosticSeverity, default nil) - • Only return diagnostics with this - severity. Overrides severity_limit - - • severity_limit: (DiagnosticSeverity, default nil) - • Limit severity of diagnostics found. E.g. - "Warning" means { "Error", "Warning" } - will be valid. - {client_id} number the client id + {bufnr} number|nil The buffer number + {line_nr} number|nil The line number + {opts} table|nil Configuration keys + • severity: (DiagnosticSeverity, default nil) + • Only return diagnostics with this + severity. Overrides severity_limit + + • severity_limit: (DiagnosticSeverity, default nil) + • Limit severity of diagnostics found. + E.g. "Warning" means { "Error", + "Warning" } will be valid. + {client_id|nil} number the client id Return: ~ table Table with map of line number to list of @@ -1296,7 +1349,8 @@ get_prev_pos({opts}) *vim.lsp.diagnostic.get_prev_pos()* *vim.lsp.diagnostic.get_virtual_text_chunks_for_line()* get_virtual_text_chunks_for_line({bufnr}, {line}, {line_diags}, {opts}) - Default function to get text chunks to display using `nvim_buf_set_virtual_text` . + Default function to get text chunks to display using + |nvim_buf_set_extmark()|. Parameters: ~ {bufnr} number The buffer to display the virtual @@ -1308,7 +1362,9 @@ get_virtual_text_chunks_for_line({bufnr}, {line}, {line_diags}, {opts}) |vim.lsp.diagnostic.set_virtual_text()| Return: ~ - table chunks, as defined by |nvim_buf_set_virtual_text()| + an array of [text, hl_group] arrays. This can be passed + directly to the {virt_text} option of + |nvim_buf_set_extmark()|. goto_next({opts}) *vim.lsp.diagnostic.goto_next()* Move to the next diagnostic @@ -1355,7 +1411,7 @@ goto_prev({opts}) *vim.lsp.diagnostic.goto_prev()* {opts} table See |vim.lsp.diagnostic.goto_next()| *vim.lsp.diagnostic.on_publish_diagnostics()* -on_publish_diagnostics({_}, {_}, {params}, {client_id}, {_}, {config}) +on_publish_diagnostics({_}, {result}, {ctx}, {config}) |lsp-handler| for the method "textDocument/publishDiagnostics" Note: @@ -1407,6 +1463,22 @@ on_publish_diagnostics({_}, {_}, {params}, {client_id}, {_}, {config}) • Sort diagnostics (and thus signs and virtual text) +redraw({bufnr}, {client_id}) *vim.lsp.diagnostic.redraw()* + Redraw diagnostics for the given buffer and client + + This calls the "textDocument/publishDiagnostics" handler + manually using the cached diagnostics already received from + the server. This can be useful for redrawing diagnostics after + making changes in diagnostics configuration. + |lsp-handler-configuration| + + Parameters: ~ + {bufnr} (optional, number): Buffer handle, defaults + to current + {client_id} (optional, number): Redraw diagnostics for + the given client. The default is to redraw + diagnostics for all attached clients. + reset({client_id}, {buffer_client_map}) *vim.lsp.diagnostic.reset()* Clear diagnotics and diagnostic cache @@ -1434,7 +1506,7 @@ set_loclist({opts}) *vim.lsp.diagnostic.set_loclist()* Parameters: ~ {opts} table|nil Configuration table. Keys: - • {open_loclist}: (boolean, default true) + • {open}: (boolean, default true) • Open loclist after set • {client_id}: (number) @@ -1453,6 +1525,30 @@ set_loclist({opts}) *vim.lsp.diagnostic.set_loclist()* • {workspace}: (boolean, default false) • Set the list with workspace diagnostics +set_qflist({opts}) *vim.lsp.diagnostic.set_qflist()* + Sets the quickfix list + + Parameters: ~ + {opts} table|nil Configuration table. Keys: + • {open}: (boolean, default true) + • Open quickfix list after set + + • {client_id}: (number) + • If nil, will consider all clients attached to + buffer. + + • {severity}: (DiagnosticSeverity) + • Exclusive severity to consider. Overrides + {severity_limit} + + • {severity_limit}: (DiagnosticSeverity) + • Limit severity of diagnostics found. E.g. + "Warning" means { "Error", "Warning" } will be + valid. + + • {workspace}: (boolean, default true) + • Set the list with workspace diagnostics + *vim.lsp.diagnostic.set_signs()* set_signs({diagnostics}, {bufnr}, {client_id}, {sign_ns}, {opts}) Set signs for given diagnostics @@ -1534,48 +1630,116 @@ set_virtual_text({diagnostics}, {bufnr}, {client_id}, {diagnostic_ns}, {opts}) "Warning" } will be valid. *vim.lsp.diagnostic.show_line_diagnostics()* -show_line_diagnostics({opts}, {bufnr}, {line_nr}, {client_id}) - Open a floating window with the diagnostics from {line_nr} +show_line_diagnostics({opts}, {buf_nr}, {line_nr}, {client_id}) + Parameters: ~ + {opts} table Configuration table + • all opts for + |vim.lsp.diagnostic.get_line_diagnostics()| + and |show_diagnostics()| can be used here + {buf_nr} number|nil The buffer number + {line_nr} number|nil The line number + {client_id} number|nil the client id - The floating window can be customized with the following - highlight groups: > + Return: ~ + table {popup_bufnr, win_id} - LspDiagnosticsFloatingError - LspDiagnosticsFloatingWarning - LspDiagnosticsFloatingInformation - LspDiagnosticsFloatingHint -< + *vim.lsp.diagnostic.show_position_diagnostics()* +show_position_diagnostics({opts}, {buf_nr}, {position}) + Open a floating window with the diagnostics from {position} Parameters: ~ - {opts} table Configuration table - • show_header (boolean, default true): Show - "Diagnostics:" header. - {bufnr} number The buffer number - {line_nr} number The line number - {client_id} number|nil the client id + {opts} table|nil Configuration keys + • severity: (DiagnosticSeverity, default nil) + • Only return diagnostics with this + severity. Overrides severity_limit + + • severity_limit: (DiagnosticSeverity, default nil) + • Limit severity of diagnostics found. E.g. + "Warning" means { "Error", "Warning" } + will be valid. + + • all opts for |show_diagnostics()| can be + used here + {buf_nr} number|nil The buffer number + {position} table|nil The (0,0)-indexed position Return: ~ table {popup_bufnr, win_id} ============================================================================== +Lua module: vim.lsp.codelens *lsp-codelens* + +display({lenses}, {bufnr}, {client_id}) *vim.lsp.codelens.display()* + Display the lenses using virtual text + + Parameters: ~ + {lenses} table of lenses to display ( `CodeLens[] | + null` ) + {bufnr} number + {client_id} number + +get({bufnr}) *vim.lsp.codelens.get()* + Return all lenses for the given buffer + + Parameters: ~ + {bufnr} number Buffer number. 0 can be used for the + current buffer. + + Return: ~ + table ( `CodeLens[]` ) + + *vim.lsp.codelens.on_codelens()* +on_codelens({err}, {result}, {ctx}, {_}) + |lsp-handler| for the method `textDocument/codeLens` + +refresh() *vim.lsp.codelens.refresh()* + Refresh the codelens for the current buffer + + It is recommended to trigger this using an autocmd or via + keymap. +> + autocmd BufEnter,CursorHold,InsertLeave <buffer> lua vim.lsp.codelens.refresh() +< + +run() *vim.lsp.codelens.run()* + Run the code lens in the current line + +save({lenses}, {bufnr}, {client_id}) *vim.lsp.codelens.save()* + Store lenses for a specific buffer and client + + Parameters: ~ + {lenses} table of lenses to store ( `CodeLens[] | + null` ) + {bufnr} number + {client_id} number + + +============================================================================== Lua module: vim.lsp.handlers *lsp-handlers* - *vim.lsp.handlers.progress_handler()* -progress_handler({_}, {_}, {params}, {client_id}) - See also: ~ - https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand +hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()* + |lsp-handler| for the method "textDocument/hover" > + + vim.lsp.handlers["textDocument/hover"] = vim.lsp.with( + vim.lsp.handlers.hover, { + -- Use a sharp border with `FloatBorder` highlights + border = "single" + } + ) +< - *vim.lsp.handlers.signature_help()* -signature_help({_}, {method}, {result}, {_}, {bufnr}, {config}) Parameters: ~ {config} table Configuration table. • border: (default=nil) • Add borders to the floating window • See |vim.api.nvim_open_win()| - See also: ~ - https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration@seehttps://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition@seehttps://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_typeDefinition@seehttps://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_implementation|lsp-handler| for the method "textDocument/signatureHelp"> + *vim.lsp.handlers.signature_help()* +signature_help({_}, {result}, {ctx}, {config}) + |lsp-handler| for the method "textDocument/signatureHelp". The + active parameter is highlighted with + |hl-LspSignatureActiveParameter|. > vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with( vim.lsp.handlers.signature_help, { @@ -1585,6 +1749,12 @@ signature_help({_}, {method}, {result}, {_}, {bufnr}, {config}) ) < + Parameters: ~ + {config} table Configuration table. + • border: (default=nil) + • Add borders to the floating window + • See |vim.api.nvim_open_win()| + ============================================================================== Lua module: vim.lsp.util *lsp-util* @@ -1611,6 +1781,9 @@ apply_text_edits({text_edits}, {bufnr}) {text_edits} (table) list of `TextEdit` objects {buf_nr} (number) Buffer id + See also: ~ + https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit + *vim.lsp.util.apply_workspace_edit()* apply_workspace_edit({workspace_edit}) Applies a `WorkspaceEdit` . @@ -1618,12 +1791,6 @@ apply_workspace_edit({workspace_edit}) Parameters: ~ {workspace_edit} (table) `WorkspaceEdit` -border_height({id}) *vim.lsp.util.border_height()* - TODO: Documentation - -border_width({id}) *vim.lsp.util.border_width()* - TODO: Documentation - buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()* Removes document highlights from a buffer. @@ -1639,6 +1806,9 @@ buf_highlight_references({bufnr}, {references}) {references} List of `DocumentHighlight` objects to highlight + See also: ~ + https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight + buf_lines({bufnr}) *vim.lsp.util.buf_lines()* TODO: Documentation @@ -1708,7 +1878,7 @@ convert_input_to_markdown_lines({input}, {contents}) https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover *vim.lsp.util.convert_signature_help_to_markdown_lines()* -convert_signature_help_to_markdown_lines({signature_help}, {ft}) +convert_signature_help_to_markdown_lines({signature_help}, {ft}, {triggers}) Converts `textDocument/SignatureHelp` response to markdown lines. @@ -1717,6 +1887,9 @@ convert_signature_help_to_markdown_lines({signature_help}, {ft}) {ft} optional filetype that will be use as the `lang` for the label markdown code block + {triggers} optional list of trigger characters from + the lsp server. used to better determine + parameter offsets Return: ~ list of lines of converted markdown. @@ -1738,7 +1911,9 @@ diagnostics_to_items({diagnostics_by_bufnr}, {predicate}) Parameters: ~ {diagnostics_by_bufnr} table bufnr -> Diagnostic [] {predicate} an optional function to filter the - diagnostics. + diagnostics. If present, only + diagnostic items matching will be + included. Return: ~ table (A list of items) @@ -1771,6 +1946,8 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()* |softtabstop| get_line({uri}, {row}) *vim.lsp.util.get_line()* + Gets the zero-indexed line from the given uri. + Parameters: ~ {uri} string uri of the resource to get the line from {row} number zero-indexed line number @@ -1779,6 +1956,8 @@ get_line({uri}, {row}) *vim.lsp.util.get_line()* string the line at row in filename get_lines({uri}, {rows}) *vim.lsp.util.get_lines()* + Gets the zero-indexed lines from the given uri. + Parameters: ~ {uri} string uri of the resource to get the lines from {rows} number[] zero-indexed line numbers @@ -1838,14 +2017,14 @@ make_floating_popup_options({width}, {height}, {opts}) *vim.lsp.util.make_formatting_params()* make_formatting_params({options}) - Creates a `FormattingOptions` object for the current buffer - and cursor position. + Creates a `DocumentFormattingParams` object for the current + buffer and cursor position. Parameters: ~ {options} Table with valid `FormattingOptions` entries Return: ~ - `FormattingOptions object + `DocumentFormattingParams` object See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting @@ -1966,6 +2145,8 @@ preview_location({location}, {opts}) *vim.lsp.util.preview_location()* or nil rename({old_fname}, {new_fname}, {opts}) *vim.lsp.util.rename()* + Rename old_fname to new_fname + Parameters: ~ {opts} (table) diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index fd1bedd8ef..5731569947 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -391,6 +391,10 @@ where the args are converted to Lua values. The expression > is equivalent to the Lua chunk > return somemod.func(...) +The `v:lua` prefix may be used to call Lua functions as |method|s. For +example: > + arg1->v:lua.somemod.func(arg2) + You can use `v:lua` in "func" options like 'tagfunc', 'omnifunc', etc. For example consider the following Lua omnifunc handler: > @@ -572,11 +576,11 @@ If you want to exclude visual selections from highlighting on yank, use vim.highlight.on_yank({opts}) *vim.highlight.on_yank()* Highlights the yanked text. The fields of the optional dict {opts} control the highlight: - - {higroup} highlight group for yanked region (default `"IncSearch"`) + - {higroup} highlight group for yanked region (default |hl-IncSearch|) - {timeout} time in ms before highlight is cleared (default `150`) - {on_macro} highlight when executing macro (default `false`) - {on_visual} highlight when yanking visual selection (default `true`) - - {event} event structure (default `vim.v.event`) + - {event} event structure (default |v:event|) vim.highlight.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {rtype}, {inclusive}) *vim.highlight.range()* @@ -616,6 +620,86 @@ regex:match_line({bufnr}, {line_idx}[, {start}, {end}]) *regex:match_line()* indices will be relative {start}. ------------------------------------------------------------------------------ +VIM.DIFF *lua-diff* + +vim.diff({a}, {b}, {opts}) *vim.diff()* + Run diff on strings {a} and {b}. Any indices returned by this + function, either directly or via callback arguments, are + 1-based. + + Examples: > + vim.diff('a\n', 'b\nc\n') + --> + @@ -1 +1,2 @@ + -a + +b + +c + + vim.diff('a\n', 'b\nc\n', {result_type = 'indices'}) + --> + { + {1, 1, 1, 2} + } +< + Parameters: ~ + {a} First string to compare + {b} Second string to compare + {opts} 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. + • `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: ~ + See {opts.result_type}. nil if {opts.on_hunk} is given. + +------------------------------------------------------------------------------ +VIM.MPACK *lua-mpack* + +The *vim.mpack* module provides packing and unpacking of lua objects to +msgpack encoded strings. |vim.NIL| and |vim.empty_dict()| are supported. + +vim.mpack.pack({obj}) *vim.mpack.pack* + Packs a lua object {obj} and returns the msgpack representation as + a string + +vim.mpack.unpack({str}) *vim.mpack.unpack* + Unpacks the msgpack encoded {str} and returns a lua object + +------------------------------------------------------------------------------ VIM *lua-builtin* vim.api.{func}({...}) *vim.api* @@ -823,7 +907,7 @@ vim.fn.{func}({...}) *vim.fn* To call autoload functions, use the syntax: > vim.fn['some#function']({...}) < - Unlike vim.api.|nvim_call_function| this converts directly between Vim + Unlike vim.api.|nvim_call_function()| this converts directly between Vim objects and Lua objects. If the Vim function returns a float, it will be represented directly as a Lua number. Empty lists and dictionaries both are represented by an empty table. @@ -1110,7 +1194,9 @@ make_dict_accessor({scope}) *vim.make_dict_accessor()* TODO: Documentation notify({msg}, {log_level}, {_opts}) *vim.notify()* - Notification provider without a runtime, writes to :Messages + Notification provider + + Without a runtime, writes to :Messages Parameters: ~ {msg} Content of the notification to show to the @@ -1119,6 +1205,9 @@ notify({msg}, {log_level}, {_opts}) *vim.notify()* {opts} Dictionary with optional options (timeout, etc) + See also: ~ + :help nvim_notify + paste({lines}, {phase}) *vim.paste()* Paste handler, invoked by |nvim_paste()| when a conforming UI (such as the |TUI|) pastes text into the editor. @@ -1167,37 +1256,6 @@ region({bufnr}, {pos1}, {pos2}, {regtype}, {inclusive}) *vim.region()* Return: ~ region lua table of the form {linenr = {startcol,endcol}} - *vim.register_keystroke_callback()* -register_keystroke_callback({fn}, {ns_id}) - Register a lua {fn} with an {id} to be run after every - keystroke. - - If {fn} is nil, it removes the callback for the associated - {ns_id} - Note: - {fn} will not be cleared from |nvim_buf_clear_namespace()| - - Note: - {fn} will receive the keystrokes after mappings have been - evaluated - - Parameters: ~ - {fn} function: Function to call. It should take one - argument, which is a string. The string will contain - the literal keys typed. See |i_CTRL-V| - {ns_id} number? Namespace ID. If not passed or 0, will - generate and return a new namespace ID from - |nvim_create_namesapce()| - - Return: ~ - number Namespace ID associated with {fn} - - Note: - {fn} will be automatically removed if an error occurs - while calling. This is to prevent the annoying situation - of every keystroke erroring while trying to remove a - broken callback. - schedule_wrap({cb}) *vim.schedule_wrap()* Defers callback `cb` until the Nvim API is safe to call. @@ -1210,7 +1268,17 @@ schedule_wrap({cb}) *vim.schedule_wrap()* deep_equal({a}, {b}) *vim.deep_equal()* - TODO: Documentation + Deep compare values for equality + + Tables are compared recursively unless they both provide the `eq` methamethod. + All other types are compared using the equality `==` operator. + + Parameters: ~ + {a} first value + {b} second value + + Return: ~ + `true` if values are equals, else `false` . deepcopy({orig}) *vim.deepcopy()* Returns a deep copy of the given object. Non-table objects are @@ -1505,14 +1573,12 @@ validate({opt}) *vim.validate()* vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} => NOP (success) -< -> - vim.validate{arg1={1, 'table'}} - => error('arg1: expected table, got number') -< -> - vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} - => error('arg1: expected even number, got 3') + + vim.validate{arg1={1, 'table'}} + => error('arg1: expected table, got number') + + vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} + => error('arg1: expected even number, got 3') < Parameters: ~ diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 77cbf7d9b7..64c0d96aed 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -855,7 +855,7 @@ Here is an example that counts the number of spaces with <F4>: > set clipboard= selection=inclusive let commands = #{line: "'[V']y", char: "`[v`]y", block: "`[\<c-v>`]y"} silent exe 'noautocmd keepjumps normal! ' .. get(commands, a:type, '') - echom getreg('"')->count(' ') + echom count(getreg('"'), ' ') finally call setreg('"', reg_save) call setpos("'<", visual_marks_save[0]) @@ -1306,6 +1306,7 @@ completion can be enabled: -complete=highlight highlight groups -complete=history :history suboptions -complete=locale locale names (as output of locale -a) + -complete=lua Lua expression -complete=mapclear buffer argument -complete=mapping mapping name -complete=menu menus diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 9f8acff88a..c473244827 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -52,9 +52,14 @@ or change text. The following operators are available: |<| < shift left |zf| zf define a fold |g@| g@ call function set with the 'operatorfunc' option - + *motion-count-multiplied* If the motion includes a count and the operator also had a count before it, the two counts are multiplied. For example: "2d3w" deletes six words. + *operator-doubled* +When doubling the operator it operates on a line. When using a count, before +or after the first character, that many lines are operated upon. Thus `3dd` +deletes three lines. A count before and after the first character is +multiplied, thus `2y3y` yanks six lines. After applying the operator the cursor is mostly left at the start of the text that was operated upon. For example, "yfe" doesn't move the cursor, but "yFe" @@ -187,9 +192,9 @@ l or *l* *$* *<End>* *<kEnd>* $ or <End> To the end of the line. When a count is given also go [count - 1] lines downward, or as far is possible. - |inclusive| motion. If a count of 2 of larger is + |inclusive| motion. If a count of 2 or larger is given and the cursor is on the last line, that is an - error an the cursor doesn't move. + error and the cursor doesn't move. In Visual mode the cursor goes to just after the last character in the line. When 'virtualedit' is active, "$" may move the cursor diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index 5885b20ab7..f61d2905be 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -13,7 +13,7 @@ from the connected program. Terminal buffers behave like normal buffers, except: - With 'modifiable', lines can be edited but not deleted. - 'scrollback' controls how many lines are kept. -- Output is followed if the cursor is on the last line. +- Output is followed ("tailed") if cursor is on the last line. - 'modified' is the default. You can set 'nomodified' to avoid a warning when closing the terminal buffer. - 'bufhidden' defaults to "hide". @@ -47,8 +47,9 @@ Input *terminal-input* To send input, enter |Terminal-mode| using any command that would enter "insert mode" in a normal buffer, such as |i| or |:startinsert|. In this mode all keys -except <C-\><C-N> are sent to the underlying program. Use <C-\><C-N> to return -to normal-mode. |CTRL-\_CTRL-N| +except <C-\> are sent to the underlying program. If <C-\> is pressed, the +next key is sent unless it is <C-N>. Use <C-\><C-N> to return to normal-mode. +|CTRL-\_CTRL-N| Terminal-mode forces these local options: @@ -134,6 +135,10 @@ Example: > programs can set this by emitting an escape sequence. - |'channel'| Terminal PTY |job-id|. Can be used with |chansend()| to send input to the terminal. +- The |TermClose| event gives the terminal job exit code in the |v:event| + "status" field. For example, this autocmd closes terminal buffers if the job + exited without error: > + autocmd TermClose * if !v:event.status | exe 'bdelete! '..expand('<abuf>') | endif Use |jobwait()| to check if the terminal job has finished: > let running = jobwait([&channel], 0)[0] == -1 @@ -324,12 +329,10 @@ This works slightly differently: mode with <Esc>, then you can move around in the buffer, copy/paste, etc. Go back to editing the gdb command with any command that starts Insert mode, such as `a` or `i`. -- The program being debugged will run in a separate window. On MS-Windows - this is a new console window. On Unix, if the |+terminal| feature is - available a Terminal window will be opened to run the debugged program in. +- A separate :terminal window will be opened to run the debugged program in. *termdebug_use_prompt* -Prompt mode can be used even when the |+terminal| feature is present with: > +Prompt mode can be used with: > let g:termdebug_use_prompt = 1 < diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index cc9696e536..c5678cf301 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -824,12 +824,12 @@ A jump table for the options with a short description can be found at |Q_op|. again not rename the file. *'backupdir'* *'bdir'* -'backupdir' 'bdir' string (default ".,$XDG_DATA_HOME/nvim/backup") +'backupdir' 'bdir' string (default ".,$XDG_DATA_HOME/nvim/backup//") global List of directories for the backup file, separated with commas. - The backup file will be created in the first directory in the list - where this is possible. The directory must exist, Vim will not - create it for you. + where this is possible. If none of the directories exist Nvim will + attempt to create the last directory in the list. - Empty means that no backup file will be created ('patchmode' is impossible!). Writing may fail because of this. - A directory "." means to put the backup file in the same directory @@ -1038,7 +1038,12 @@ A jump table for the options with a short description can be found at |Q_op|. continuation (positive). sbr Display the 'showbreak' value before applying the additional indent. - The default value for min is 20 and shift is 0. + list:{n} Adds an additional indent for lines that match a + numbered or bulleted list (using the + 'formatlistpat' setting). + list:-1 Uses the length of a match with 'formatlistpat' + for indentation. + The default value for min is 20, shift and list is 0. *'browsedir'* *'bsdir'* 'browsedir' 'bsdir' string (default: "last") @@ -1802,13 +1807,30 @@ A jump table for the options with a short description can be found at |Q_op|. *'cursorline'* *'cul'* *'nocursorline'* *'nocul'* 'cursorline' 'cul' boolean (default off) local to window - Highlight the screen line of the cursor with CursorLine - |hl-CursorLine|. Useful to easily spot the cursor. Will make screen - redrawing slower. + Highlight the text line of the cursor with CursorLine |hl-CursorLine|. + Useful to easily spot the cursor. Will make screen redrawing slower. When Visual mode is active the highlighting isn't used to make it easier to see the selected text. + *'cursorlineopt'* *'culopt'* +'cursorlineopt' 'culopt' string (default: "number,line") + local to window + Comma separated list of settings for how 'cursorline' is displayed. + Valid values: + "line" Highlight the text line of the cursor with + CursorLine |hl-CursorLine|. + "screenline" Highlight only the screen line of the cursor with + CursorLine |hl-CursorLine|. + "number" Highlight the line number of the cursor with + CursorLineNr |hl-CursorLineNr|. + + Special value: + "both" Alias for the values "line,number". + + "line" and "screenline" cannot be used together. + + *'debug'* 'debug' string (default "") global @@ -2138,8 +2160,7 @@ A jump table for the options with a short description can be found at |Q_op|. global or local to buffer |global-local| External program to use for "=" command. When this option is empty the internal formatting functions are used; either 'lisp', 'cindent' - or 'indentexpr'. When Vim was compiled without internal formatting, - the "indent" program is used. + or 'indentexpr'. Environment variables are expanded |:set_env|. See |option-backslash| about including spaces and backslashes. This option cannot be set from a |modeline| or in the |sandbox|, for @@ -3057,19 +3078,18 @@ A jump table for the options with a short description can be found at |Q_op|. See |help-translated|. *'hidden'* *'hid'* *'nohidden'* *'nohid'* -'hidden' 'hid' boolean (default off) - global - When off a buffer is unloaded when it is |abandon|ed. When on a - buffer becomes hidden when it is |abandon|ed. If the buffer is still - displayed in another window, it does not become hidden, of course. - The commands that move through the buffer list sometimes make a buffer - hidden although the 'hidden' option is off: When the buffer is - modified, 'autowrite' is off or writing is not possible, and the '!' - flag was used. See also |windows.txt|. - To only make one buffer hidden use the 'bufhidden' option. - This option is set for one command with ":hide {command}" |:hide|. - WARNING: It's easy to forget that you have changes in hidden buffers. - Think twice when using ":q!" or ":qa!". +'hidden' 'hid' boolean (default on) + global + When off a buffer is unloaded (including loss of undo information) + when it is |abandon|ed. When on a buffer becomes hidden when it is + |abandon|ed. A buffer displayed in another window does not become + hidden, of course. + Commands that move through the buffer list sometimes hide a buffer + although the 'hidden' option is off: when the buffer is modified, + 'autowrite' is off or writing is not possible, and the '!' flag was + used. See also |windows|. + To hide a specific buffer use the 'bufhidden' option. + 'hidden' is set for one command with ":hide {command}" |:hide|. *'history'* *'hi'* 'history' 'hi' number (Vim default: 10000, Vi default: 0) @@ -3192,7 +3212,7 @@ A jump table for the options with a short description can be found at |Q_op|. option to a valid keymap name. *'inccommand'* *'icm'* -'inccommand' 'icm' string (default "") +'inccommand' 'icm' string (default "nosplit") global "nosplit": Shows the effects of a command incrementally, as you type. @@ -3477,7 +3497,7 @@ A jump table for the options with a short description can be found at |Q_op|. |jumplist-stack| *'joinspaces'* *'js'* *'nojoinspaces'* *'nojs'* -'joinspaces' 'js' boolean (default on) +'joinspaces' 'js' boolean (default off) global Insert two spaces after a '.', '?' and '!' with a join command. Otherwise only one space is inserted. @@ -4332,19 +4352,21 @@ A jump table for the options with a short description can be found at |Q_op|. - abbreviations are disabled - 'autoindent' is reset - 'expandtab' is reset - - 'formatoptions' is used like it is empty + - 'hkmap' is reset - 'revins' is reset - 'ruler' is reset - 'showmatch' is reset - - 'smartindent' is reset - 'smarttab' is reset - 'softtabstop' is set to 0 - 'textwidth' is set to 0 - 'wrapmargin' is set to 0 + - 'varsofttabstop' is made empty These options keep their value, but their effect is disabled: - 'cindent' + - 'formatoptions' is used like it is empty - 'indentexpr' - 'lisp' + - 'smartindent' NOTE: When you start editing another file while the 'paste' option is on, settings from the modelines or autocommands may change the settings again, causing trouble when pasting text. You might want to @@ -4540,11 +4562,6 @@ A jump table for the options with a short description can be found at |Q_op|. List of items that control the format of the output of |:hardcopy|. See |popt-option|. - *'prompt'* *'noprompt'* -'prompt' boolean (default on) - global - When on a ":" prompt is used in Ex mode. - *'pumblend'* *'pb'* 'pumblend' 'pb' number (default 0) global @@ -4607,7 +4624,8 @@ A jump table for the options with a short description can be found at |Q_op|. customize the information displayed in the quickfix or location window for each entry in the corresponding quickfix or location list. See |quickfix-window-function| for an explanation of how to write the - function and an example. + function and an example. The value can be the name of a function or a + lambda. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. @@ -5237,11 +5255,12 @@ A jump table for the options with a short description can be found at |Q_op|. Note that such processing is done after |:set| did its own round of unescaping, so to keep yourself sane use |:let-&| like shown above. *shell-powershell* - To use powershell: > + To use PowerShell: > let &shell = has('win32') ? 'powershell' : 'pwsh' - set shellquote= shellpipe=\| shellxquote= - set shellcmdflag=-NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command - set shellredir=\|\ Out-File\ -Encoding\ UTF8 + let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;' + let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' + let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' + set shellquote= shellxquote= < This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. @@ -5457,7 +5476,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'showbreak'* *'sbr'* *E595* 'showbreak' 'sbr' string (default "") - global + global or local to window |global-local| String to put at the start of lines that have been wrapped. Useful values are "> " or "+++ ": > :set showbreak=>\ @@ -5471,7 +5490,10 @@ A jump table for the options with a short description can be found at |Q_op|. Note that tabs after the showbreak will be displayed differently. If you want the 'showbreak' to appear in between line numbers, add the "n" flag to 'cpoptions'. - + A window-local value overrules a global value. If the global value is + set and you want no value in the current window use NONE: > + :setlocal showbreak=NONE +< *'showcmd'* *'sc'* *'noshowcmd'* *'nosc'* 'showcmd' 'sc' boolean (Vim default: on, Vi default: off) global @@ -6098,9 +6120,11 @@ A jump table for the options with a short description can be found at |Q_op|. specify special kinds of buffers. See |special-buffers|. *'switchbuf'* *'swb'* -'switchbuf' 'swb' string (default "") +'switchbuf' 'swb' string (default "uselast") global This option controls the behavior when switching between buffers. + Mostly for |quickfix| commands some values are also used for other + commands, as mentioned below. Possible values (comma separated list): useopen If included, jump to the first open window that contains the specified buffer (if there is one). @@ -6516,17 +6540,17 @@ A jump table for the options with a short description can be found at |Q_op|. 'ttyfast' 'tf' Removed. |vim-differences| *'undodir'* *'udir'* *E5003* -'undodir' 'udir' string (default "$XDG_DATA_HOME/nvim/undo") +'undodir' 'udir' string (default "$XDG_DATA_HOME/nvim/undo//") global List of directory names for undo files, separated with commas. - See |'backupdir'| for details of the format. + See 'backupdir' for details of the format. "." means using the directory of the file. The undo file name for "file.txt" is ".file.txt.un~". For other directories the file name is the full path of the edited file, with path separators replaced with "%". When writing: The first directory that exists is used. "." always - works, no directories after "." will be used for writing. If none of - the directories exist Neovim will attempt to create last directory in + works, no directories after "." will be used for writing. If none of + the directories exist Nvim will attempt to create the last directory in the list. When reading all entries are tried to find an undo file. The first undo file that exists is used. When it cannot be read an error is @@ -6535,6 +6559,10 @@ A jump table for the options with a short description can be found at |Q_op|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. + Note that unlike 'directory' and 'backupdir', 'undodir' always acts as + though the trailing slashes are present (see 'backupdir' for what this + means). + *'undofile'* *'noundofile'* *'udf'* *'noudf'* 'undofile' 'udf' boolean (default off) local to buffer @@ -6675,14 +6703,14 @@ A jump table for the options with a short description can be found at |Q_op|. displayed when 'verbosefile' is set. *'viewdir'* *'vdir'* -'viewdir' 'vdir' string (default: "$XDG_DATA_HOME/nvim/view") +'viewdir' 'vdir' string (default: "$XDG_DATA_HOME/nvim/view//") global Name of the directory where to store files for |:mkview|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. *'viewoptions'* *'vop'* -'viewoptions' 'vop' string (default: "folds,options,cursor,curdir") +'viewoptions' 'vop' string (default: "folds,cursor,curdir") global Changes the effect of the |:mkview| command. It is a comma separated list of words. Each word enables saving and restoring something: @@ -6721,6 +6749,7 @@ A jump table for the options with a short description can be found at |Q_op|. The `g$` command will move to the end of the screen line. It doesn't make sense to combine "all" with "onemore", but you will not get a warning for it. + When combined with other words, "none" is ignored. *'visualbell'* *'vb'* *'novisualbell'* *'novb'* *beep* 'visualbell' 'vb' boolean (default off) @@ -7091,8 +7120,7 @@ A jump table for the options with a short description can be found at |Q_op|. Allows writing to any file with no need for "!" override. *'writebackup'* *'wb'* *'nowritebackup'* *'nowb'* -'writebackup' 'wb' boolean (default on with |+writebackup| feature, off - otherwise) +'writebackup' 'wb' boolean (default on) global Make a backup before overwriting a file. The backup is removed after the file was successfully written, unless the 'backup' option is diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index e74f3b72bf..c49cc6d540 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -1204,7 +1204,7 @@ x A single character, with no special meaning, matches itself \%d123 Matches the character specified with a decimal number. Must be followed by a non-digit. -\%o40 Matches the character specified with an octal number up to 0377. +\%o40 Matches the character specified with an octal number up to 0o377. Numbers below 0o40 must be followed by a non-octal digit or a non-digit. \%x2a Matches the character specified with up to two hexadecimal characters. diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 4b61cd4c25..a39d1e8dc9 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -3808,7 +3808,7 @@ netrw: Decho.vim is provided as a "vimball"; see |vimball-intro|. You should edit the Decho.vba.gz file and source it in: > - vim Decho.vba.gz + vim Decho.vba.gz :so % :q < diff --git a/runtime/doc/print.txt b/runtime/doc/print.txt index e7de5b9ee3..d9320ad315 100644 --- a/runtime/doc/print.txt +++ b/runtime/doc/print.txt @@ -103,10 +103,9 @@ will use the "latin1" print character encoding file. When 'encoding' is set to a multibyte encoding, Vim will try to convert characters to the printing encoding for printing (if 'printencoding' is empty -then the conversion will be to latin1). Conversion to a printing encoding -other than latin1 will require Vim to be compiled with the |+iconv| feature. -If no conversion is possible then printing will fail. Any characters that -cannot be converted will be replaced with upside down question marks. +then the conversion will be to latin1). If no conversion is possible then +printing will fail. Any characters that cannot be converted will be replaced +with upside down question marks. Two print character encoding files are provided to support default Mac and HPUX character encodings and are used by default on these platforms. Code page @@ -176,9 +175,7 @@ the font. When omitted, the point size is 10. 'printheader' 'pheader' string (default "%<%f%h%m%=Page %N") global This defines the format of the header produced in |:hardcopy| output. The -option is defined in the same way as the 'statusline' option. If Vim has not -been compiled with the |+statusline| feature, this option has no effect and a -simple default header is used, which shows the page number. The same simple +option is defined in the same way as the 'statusline' option. The same simple header is used when this option is empty. *pmbcs-option* diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index be895f9e4e..b785010699 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -207,6 +207,7 @@ registers. Nvim looks for these clipboard tools, in order of priority: - lemonade (for SSH) https://github.com/pocke/lemonade - doitclient (for SSH) http://www.chiark.greenend.org.uk/~sgtatham/doit/ - win32yank (Windows) + - termux (via termux-clipboard-set, termux-clipboard-set) - tmux (if $TMUX is set) *g:clipboard* diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index a937cfee98..9c1f584415 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1208,8 +1208,8 @@ not "b:current_compiler". What the command actually does is the following: - Delete the "current_compiler" and "b:current_compiler" variables. - Define the "CompilerSet" user command. With "!" it does ":set", without "!" it does ":setlocal". -- Execute ":runtime! compiler/{name}.vim". The plugins are expected to set - options with "CompilerSet" and set the "current_compiler" variable to the +- Execute ":runtime! compiler/{name}.(vim|lua)". The plugins are expected to + set options with "CompilerSet" and set the "current_compiler" variable to the name of the compiler. - Delete the "CompilerSet" user command. - Set "b:current_compiler" to the value of "current_compiler". @@ -1333,7 +1333,8 @@ Basic items %o module name (finds a string) %l line number (finds a number) %c column number (finds a number representing character - column of the error, (1 <tab> == 1 character column)) + column of the error, byte index, a <tab> is 1 + character column) %v virtual column number (finds a number representing screen column of the error (1 <tab> == 8 screen columns)) @@ -1919,7 +1920,10 @@ The function should return a single line of text to display in the quickfix window for each entry from start_idx to end_idx. The function can obtain information about the entries using the |getqflist()| function and specifying the quickfix list identifier "id". For a location list, getloclist() function -can be used with the 'winid' argument. +can be used with the 'winid' argument. If an empty list is returned, then the +default format is used to display all the entries. If an item in the returned +list is an empty string, then the default format is used to display the +corresponding entry. If a quickfix or location list specific customization is needed, then the 'quickfixtextfunc' attribute of the list can be set using the |setqflist()| or diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index fb20a583c9..77e69a3eea 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -659,6 +659,7 @@ Short explanation of each option: *option-list* 'cursorbind' 'crb' move cursor in window as it moves in other windows 'cursorcolumn' 'cuc' highlight the screen column of the cursor 'cursorline' 'cul' highlight the screen line of the cursor +'cursorlineopt' 'culopt' settings for 'cursorline' 'debug' set to "msg" to see all error messages 'define' 'def' pattern to be used to find a macro definition 'delcombine' 'deco' delete combining characters on their own @@ -809,7 +810,6 @@ Short explanation of each option: *option-list* 'printmbcharset' 'pmbcs' CJK character set to be used for :hardcopy 'printmbfont' 'pmbfn' font names to be used for CJK output of :hardcopy 'printoptions' 'popt' controls the format of :hardcopy output -'prompt' 'prompt' enable prompt in Ex mode 'pumheight' 'ph' maximum height of the popup menu 'pumwidth' 'pw' minimum width of the popup menu 'pythondll' name of the Python 2 dynamic library diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt deleted file mode 100644 index 6c2ceb45be..0000000000 --- a/runtime/doc/remote.txt +++ /dev/null @@ -1,189 +0,0 @@ -*remote.txt* Nvim - - - VIM REFERENCE MANUAL by Bram Moolenaar - - -Vim client-server communication *client-server* - - Type |gO| to see the table of contents. - -============================================================================== -1. Common functionality *clientserver* - -When compiled with the |+clientserver| option, Vim can act as a command -server. It accepts messages from a client and executes them. At the same -time, Vim can function as a client and send commands to a Vim server. - -The following command line arguments are available: - - argument meaning ~ - - --remote [+{cmd}] {file} ... *--remote* - Open the file list in a remote Vim. When - there is no Vim server, execute locally. - There is one optional init command: +{cmd}. - This must be an Ex command that can be - followed by "|". - The rest of the command line is taken as the - file list. Thus any non-file arguments must - come before this. - You cannot edit stdin this way |--|. - The remote Vim is raised. If you don't want - this use > - vim --remote-send "<C-\><C-N>:n filename<CR>" -< - --remote-silent [+{cmd}] {file} ... *--remote-silent* - As above, but don't complain if there is no - server and the file is edited locally. - --remote-wait [+{cmd}] {file} ... *--remote-wait* - As --remote, but wait for files to complete - (unload) in remote Vim. - --remote-wait-silent [+{cmd}] {file} ... *--remote-wait-silent* - As --remote-wait, but don't complain if there - is no server. - *--remote-tab* - --remote-tab Like --remote but open each file in a new - tabpage. - *--remote-tab-silent* - --remote-tab-silent Like --remote-silent but open each file in a - new tabpage. - *--remote-tab-wait* - --remote-tab-wait Like --remote-wait but open each file in a new - tabpage. - - *--remote-tab-wait-silent* - --remote-tab-wait-silent Like --remote-wait-silent but open each file - in a new tabpage. - *--remote-send* - --remote-send {keys} Send {keys} to server and exit. The {keys} - are not mapped. Special key names are - recognized, e.g., "<CR>" results in a CR - character. - *--remote-expr* - --remote-expr {expr} Evaluate {expr} in server and print the result - on stdout. - -Examples ~ - -Edit "file.txt" in an already running GVIM server: > - gvim --remote file.txt - -Edit "file.txt" in an already running server called FOOBAR: > - gvim --servername FOOBAR --remote file.txt - -Edit "file.txt" in server "FILES" if it exists, become server "FILES" -otherwise: > - gvim --servername FILES --remote-silent file.txt - -This doesn't work, all arguments after --remote will be used as file names: > - gvim --remote --servername FOOBAR file.txt - -Edit file "+foo" in a remote server (note the use of "./" to avoid the special -meaning of the leading plus): > - vim --remote ./+foo - -Tell the remote server "BLA" to write all files and exit: > - vim --servername BLA --remote-send '<C-\><C-N>:wqa<CR>' - - -SERVER NAME *client-server-name* - -By default Vim will try to register the name under which it was invoked (gvim, -egvim ...). This can be overridden with the --servername argument. If the -specified name is not available, a postfix is applied until a free name is -encountered, i.e. "gvim1" for the second invocation of gvim on a particular -X-server. The resulting name is available in the servername builtin variable -|v:servername|. The case of the server name is ignored, thus "gvim" and -"GVIM" are considered equal. - -When Vim is invoked with --remote, --remote-wait or --remote-send it will try -to locate the server name determined by the invocation name and --servername -argument as described above. If an exact match is not available, the first -server with the number postfix will be used. If a name with the number -postfix is specified with the --servername argument, it must match exactly. - -If no server can be located and --remote or --remote-wait was used, Vim will -start up according to the rest of the command line and do the editing by -itself. This way it is not necessary to know whether gvim is already started -when sending command to it. - -The --serverlist argument will cause Vim to print a list of registered command -servers on the standard output (stdout) and exit. - -Win32 Note: Making the Vim server go to the foreground doesn't always work, -because MS-Windows doesn't allow it. The client will move the server to the -foreground when using the --remote or --remote-wait argument and the server -name starts with "g". - - -REMOTE EDITING - -The --remote argument will cause a |:drop| command to be constructed from the -rest of the command line and sent as described above. -The --remote-wait argument does the same thing and additionally sets up to -wait for each of the files to have been edited. This uses the BufUnload -event, thus as soon as a file has been unloaded, Vim assumes you are done -editing it. -Note that the --remote and --remote-wait arguments will consume the rest of -the command line. I.e. all remaining arguments will be regarded as filenames. -You can not put options there! - - -FUNCTIONS - *E240* *E573* -There are a number of Vim functions for scripting the command server. See -the description in |eval.txt| or use CTRL-] on the function name to jump to -the full explanation. - - synopsis explanation ~ - remote_startserver( name) run a server - remote_expr( server, string, idvar) send expression - remote_send( server, string, idvar) send key sequence - serverlist() get a list of available servers - remote_peek( serverid, retvar) check for reply string - remote_read( serverid) read reply string - server2client( serverid, string) send reply string - remote_foreground( server) bring server to the front - -See also the explanation of |CTRL-\_CTRL-N|. Very useful as a leading key -sequence. -The {serverid} for server2client() can be obtained with expand("<client>") - -============================================================================== -2. X11 specific items *x11-clientserver* - *E247* *E248* *E251* *E258* *E277* - -The communication between client and server goes through the X server. The -display of the Vim server must be specified. The usual protection of the X -server is used, you must be able to open a window on the X server for the -communication to work. It is possible to communicate between different -systems. - -By default, a GUI Vim will register a name on the X-server by which it can be -addressed for subsequent execution of injected strings. Vim can also act as -a client and send strings to other instances of Vim on the same X11 display. - -When an X11 GUI Vim (gvim) is started, it will try to register a send-server -name on the 'VimRegistry' property on the root window. - -An empty --servername argument will cause the command server to be disabled. - -To send commands to a Vim server from another application, read the source -file src/if_xcmdsrv.c, it contains some hints about the protocol used. - -============================================================================== -3. Win32 specific items *w32-clientserver* - -Every Win32 Vim can work as a server, also in the console. You do not need a -version compiled with OLE. Windows messages are used, this works on any -version of MS-Windows. But only communication within one system is possible. - -Since MS-Windows messages are used, any other application should be able to -communicate with a Vim server. - -When using gvim, the --remote-wait only works properly this way: > - - start /w gvim --remote-wait file.txt -< - vim:tw=78:sw=4:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index dd05084652..61428aefb0 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -172,43 +172,41 @@ Using Vim scripts *using-scripts* For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. *:so* *:source* *load-vim-script* -:so[urce] {file} Read Ex commands from {file}. These are commands that - start with a ":". +:so[urce] {file} Runs |Ex| commands or Lua code (".lua" files) read + from {file}. Triggers the |SourcePre| autocommand. *:source!* -:so[urce]! {file} Read Vim commands from {file}. These are commands - that are executed from Normal mode, like you type - them. - When used after |:global|, |:argdo|, |:windo|, - |:bufdo|, in a loop or when another command follows - the display won't be updated while executing the - commands. +:so[urce]! {file} Runs |Normal-mode| commands read from {file}. When + used after |:global|, |:argdo|, |:windo|, |:bufdo|, in + a loop or when another command follows the display + won't be updated while executing the commands. Cannot be used in the |sandbox|. *:ru* *:runtime* :ru[ntime][!] [where] {file} .. - Read Ex commands from {file} in each directory given - by 'runtimepath' and/or 'packpath'. There is no error - for non-existing files. + Sources |Ex| commands or Lua code (".lua" files) read + from {file} (a relative path) in each directory given + by 'runtimepath' and/or 'packpath'. + Ignores non-existing files. Example: > :runtime syntax/c.vim + :runtime syntax/c.lua -< There can be multiple {file} arguments, separated by - spaces. Each {file} is searched for in the first +< There can be multiple space-separated {file} + arguments. Each {file} is searched for in the first directory from 'runtimepath', then in the second - directory, etc. Use a backslash to include a space - inside {file} (although it's better not to use spaces - in file names, it causes trouble). + directory, etc. When [!] is included, all found files are sourced. - When it is not included only the first found file is - sourced. + Else only the first found file is sourced. - When [where] is omitted only 'runtimepath' is used. + When [where] is omitted, first 'runtimepath' is + searched, then directories under "start" in 'packpath' + are searched. Other values: - START search under "start" in 'packpath' - OPT search under "opt" in 'packpath' + START search only under "start" in 'packpath' + OPT search only under "opt" in 'packpath' PACK search under "start" and "opt" in 'packpath' ALL first use 'runtimepath', then search @@ -244,6 +242,8 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. Note that {name} is the directory name, not the name of the .vim file. All the files matching the pattern pack/*/opt/{name}/plugin/**/*.vim ~ + and + pack/*/opt/{name}/plugin/**/*.lua ~ will be sourced. This allows for using subdirectories below "plugin", just like with plugins in 'runtimepath'. @@ -257,7 +257,9 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. ftdetect scripts are loaded, only the matching directories are added to 'runtimepath'. This is useful in your .vimrc. The plugins will then be - loaded during initialization, see |load-plugins|. + loaded during initialization, see |load-plugins| (note + that the loading order will be reversed, because each + directory is inserted before others). Note that for ftdetect scripts to be loaded you will need to write `filetype plugin indent on` AFTER all `packadd!` commands. @@ -905,11 +907,9 @@ OBSCURE Profiling *profile* *profiling* Profiling means that Vim measures the time that is spent on executing -functions and/or scripts. The |+profile| feature is required for this. -It is only included when Vim was compiled with "huge" features. +functions and/or scripts. -You can also use the |reltime()| function to measure time. This only requires -the |+reltime| feature, which is present more often. +You can also use the |reltime()| function to measure time. For profiling syntax highlighting see |:syntime|. diff --git a/runtime/doc/rileft.txt b/runtime/doc/rileft.txt index d45a2dce7e..aa11462595 100644 --- a/runtime/doc/rileft.txt +++ b/runtime/doc/rileft.txt @@ -70,7 +70,7 @@ o Invocations o Typing backwards *ins-reverse* ---------------- - In lieu of using full-fledged the 'rightleft' option, one can opt for + In lieu of using the full-fledged 'rightleft' option, one can opt for reverse insertion. When the 'revins' (reverse insert) option is set, inserting happens backwards. This can be used to type right-to-left text. When inserting characters the cursor is not moved and the text diff --git a/runtime/doc/russian.txt b/runtime/doc/russian.txt index 776630a52b..a2bc9f3b5e 100644 --- a/runtime/doc/russian.txt +++ b/runtime/doc/russian.txt @@ -47,10 +47,6 @@ different codepages from http://www.sourceforge.net/projects/ruvim/ -Make sure that your Vim is at least 6.2.506 and use ruvim 0.5 or later for -automatic installs. Vim also needs to be compiled with |+gettext| feature for -user interface items translations to work. - After downloading an archive from RuVim project, unpack it into your $VIMRUNTIME directory. We recommend using UTF-8 archive. diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index f722747ce9..03c00c8495 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -110,6 +110,23 @@ zuG Undo |zW| and |zG|, remove the word from the internal :spellw[rong]! {word} Add {word} as a wrong (bad) word to the internal word list, like with |zW|. + *:spellra* *:spellrare* +:[count]spellr[are] {word} + Add {word} as a rare word to 'spellfile', similar to + |zw|. Without count the first name is used, with + a count of two the second entry, etc. + + There are no normal mode commands to mark words as + rare as this is a fairly uncommon command and all + intuitive commands for this are already taken. If you + want you can add mappings with e.g.: > + nnoremap z? :exe ':spellrare ' . expand('<cWORD>')<CR> + nnoremap z/ :exe ':spellrare! ' . expand('<cWORD>')<CR> +< |:spellundo|, |zuw|, or |zuW| can be used to undo this. + +:spellr[rare]! {word} Add {word} as a rare word to the internal word + list, similar to |zW|. + :[count]spellu[ndo] {word} *:spellu* *:spellundo* Like |zuw|. [count] used as with |:spellgood|. @@ -1110,10 +1127,10 @@ flag to avoid lots of errors. CIRCUMFIX *spell-CIRCUMFIX* The CIRCUMFIX flag means a prefix and suffix must be added at the same time. -If a prefix has the CIRCUMFIX flag than only suffixes with the CIRCUMFIX flag +If a prefix has the CIRCUMFIX flag then only suffixes with the CIRCUMFIX flag can be added, and the other way around. -An alternative is to only specify the suffix, and give the that suffix two -flags: The required prefix and the NEEDAFFIX flag. |spell-NEEDAFFIX| +An alternative is to only specify the suffix, and give that suffix two flags: +the required prefix and the NEEDAFFIX flag. |spell-NEEDAFFIX| PFXPOSTPONE *spell-PFXPOSTPONE* diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index ae9022c56c..87a48e6d2a 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -329,12 +329,12 @@ argument. -w{number} Set the 'window' option to {number}. *-w* --w {scriptout} All the characters that you type are recorded in the file - "scriptout", until you exit Vim. This is useful if you want - to create a script file to be used with "vim -s" or - ":source!". When the "scriptout" file already exists, new - characters are appended. See also |complex-repeat|. - {scriptout} cannot start with a digit. +-w {scriptout} All keys that you type are recorded in the file "scriptout", + until you exit Vim. Useful to create a script file to be used + with "vim -s" or ":source!". Appends to the "scriptout" file + if it already exists. {scriptout} cannot start with a digit. + See also |vim.on_key()|. + See also |complex-repeat|. *-W* -W {scriptout} Like -w, but do not append, overwrite an existing file. @@ -388,8 +388,8 @@ argument. ============================================================================== Initialization *initialization* *startup* -At startup, Vim checks environment variables and files and sets values -accordingly. Vim proceeds in this order: +At startup, Nvim checks environment variables and files and sets values +accordingly, proceeding as follows: 1. Set the 'shell' option *SHELL* *COMSPEC* The environment variable SHELL, if it exists, is used to set the @@ -406,21 +406,21 @@ accordingly. Vim proceeds in this order: Nvim started with |--embed| waits for the UI to connect before proceeding to load user configuration. -4. Load user config (execute Ex commands from files, environment, …). - An environment variable (e.g. $VIMINIT) is read as one Ex command - line, where multiple commands must be separated with '|' or <NL>. +4. Setup |default-mappings| and |default-autocmds|. + +5. Load user config (execute Ex commands from files, environment, …). + $VIMINIT environment variable is read as one Ex command line (separate + multiple commands with '|' or <NL>). *config* *init.vim* *init.lua* *vimrc* *exrc* - A file that contains initialization commands is generically called - a "vimrc" or config file. It can be a Vimscript or Lua file named - "init.vim" or "init.lua" respectively. It is an error to use both at - the same time. Each line in a "init.vim" is executed as an Ex command - line. See also |vimrc-intro| and |base-directories|. - - The Nvim config file is "init.vim", located at: - Unix ~/.config/nvim/init.vim - Windows ~/AppData/Local/nvim/init.vim - or if |$XDG_CONFIG_HOME| is defined: - $XDG_CONFIG_HOME/nvim/init.vim + A file containing initialization commands is generically called + a "vimrc" or config file. It can be either Vimscript ("init.vim") or + Lua ("init.lua"), but not both. *E5422* + See also |vimrc-intro| and |base-directories|. + + The config file is located at: + Unix ~/.config/nvim/init.vim (or init.lua) + Windows ~/AppData/Local/nvim/init.vim (or init.lua) + |$XDG_CONFIG_HOME| $XDG_CONFIG_HOME/nvim/init.vim (or init.lua) If Nvim was started with "-u {file}" then {file} is used as the config and all initializations until 5. are skipped. $MYVIMRC is not set. @@ -452,7 +452,7 @@ accordingly. Vim proceeds in this order: - The file ".nvimrc" - The file ".exrc" -5. Enable filetype and indent plugins. +6. Enable filetype and indent plugins. This does the same as the commands: > :runtime! filetype.vim :runtime! ftplugin.vim @@ -460,19 +460,21 @@ accordingly. Vim proceeds in this order: < Skipped if ":filetype … off" was called or if the "-u NONE" command line argument was given. -6. Enable syntax highlighting. +7. Enable syntax highlighting. This does the same as the command: > :runtime! syntax/syntax.vim < Skipped if ":syntax off" was called or if the "-u NONE" command line argument was given. -7. Load the plugin scripts. *load-plugins* +8. Load the plugin scripts. *load-plugins* This does the same as the command: > :runtime! plugin/**/*.vim -< The result is that all directories in the 'runtimepath' option will be - searched for the "plugin" sub-directory and all files ending in ".vim" - will be sourced (in alphabetical order per directory), also in - subdirectories. + :runtime! plugin/**/*.lua +< The result is that all directories in 'runtimepath' will be searched + for the "plugin" sub-directory and all files ending in ".vim" or + ".lua" will be sourced (in alphabetical order per directory), + also in subdirectories. First "*.vim" are sourced, then "*.lua" files. + However, directories in 'runtimepath' ending in "after" are skipped here and only loaded after packages, see below. Loading plugins won't be done when: @@ -480,7 +482,6 @@ accordingly. Vim proceeds in this order: - The |--noplugin| command line argument is used. - The |--clean| command line argument is used. - The "-u NONE" command line argument is used |-u|. - - When Vim was compiled without the |+eval| feature. Note that using "-c 'set noloadplugins'" doesn't work, because the commands from the command line have not been executed yet. You can use "--cmd 'set noloadplugins'" or "--cmd 'set loadplugins'" |--cmd|. @@ -495,38 +496,33 @@ accordingly. Vim proceeds in this order: if packages have been found, but that should not add a directory ending in "after". -8. Set 'shellpipe' and 'shellredir' +9. Set 'shellpipe' and 'shellredir' The 'shellpipe' and 'shellredir' options are set according to the value of the 'shell' option, unless they have been set before. - This means that Vim will figure out the values of 'shellpipe' and + This means that Nvim will figure out the values of 'shellpipe' and 'shellredir' for you, unless you have set them yourself. -9. Set 'updatecount' to zero, if "-n" command argument used +10. Set 'updatecount' to zero, if "-n" command argument used -10. Set binary options - If the "-b" flag was given to Vim, the options for binary editing will - be set now. See |-b|. +11. Set binary options if the |-b| flag was given. -11. Read the ShaDa file - See |shada-file|. +12. Read the |shada-file|. -12. Read the quickfix file - If the "-q" flag was given to Vim, the quickfix file is read. If this - fails, Vim exits. +13. Read the quickfix file if the |-q| flag was given, or exit on failure. -13. Open all windows +14. Open all windows When the |-o| flag was given, windows will be opened (but not displayed yet). When the |-p| flag was given, tab pages will be created (but not displayed yet). When switching screens, it happens now. Redrawing starts. - If the "-q" flag was given to Vim, the first error is jumped to. + If the |-q| flag was given, the first error is jumped to. Buffers for all windows will be loaded, without triggering |BufAdd| autocommands. -14. Execute startup commands - If a "-t" flag was given to Vim, the tag is jumped to. - The commands given with the |-c| and |+cmd| arguments are executed. +15. Execute startup commands + If a |-t| flag was given, the tag is jumped to. + Commands given with |-c| and |+cmd| are executed. If the 'insertmode' option is set, Insert mode is entered. The starting flag is reset, has("vim_starting") will now return zero. The |v:vim_did_enter| variable is set to 1. diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index b159f655fa..c6a9094792 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -25,11 +25,15 @@ In the User Manual: ============================================================================== 1. Quick start *:syn-qstart* - *:syn-enable* *:syntax-enable* + *:syn-enable* *:syntax-enable* *:syn-on* *:syntax-on* This command switches on syntax highlighting: > :syntax enable +Alternatively: > + + :syntax on + What this command actually does is to execute the command > :source $VIMRUNTIME/syntax/syntax.vim @@ -42,19 +46,11 @@ are in the "/usr/vim/vim82/syntax" directory, set $VIMRUNTIME to This command also sources the |menu.vim| script when the GUI is running or will start soon. See |'go-M'| about avoiding that. - *:syn-on* *:syntax-on* -The `:syntax enable` command will keep most of your current color settings. -This allows using `:highlight` commands to set your preferred colors before or -after using this command. If you want Vim to overrule your settings with the -defaults, use: > - :syntax on -< *:hi-normal* *:highlight-normal* If you are running in the GUI, you can get white text on a black background with: > :highlight Normal guibg=Black guifg=White For a color terminal see |:hi-normal-cterm|. -For setting up your own colors syntax highlighting see |syncolor|. NOTE: The syntax files on MS-Windows have lines that end in <CR><NL>. The files for Unix end in <NL>. This means you should use the right type of @@ -277,12 +273,6 @@ located. This is used here as the variable |$VIMRUNTIME|. | +- Source first syntax/synload.vim in 'runtimepath' | | - | +- Setup the colors for syntax highlighting. If a color scheme is - | | defined it is loaded again with ":colors {name}". Otherwise - | | ":runtime! syntax/syncolor.vim" is used. ":syntax on" overrules - | | existing colors, ":syntax enable" only sets groups that weren't - | | set yet. - | | | +- Set up syntax autocmds to load the appropriate syntax file when | | the 'syntax' option is set. *synload-1* | | @@ -371,9 +361,6 @@ the desired value, or restored to their default by removing the variable using Remarks: - Some truly ancient browsers may not show the background colors. - From most browsers you can also print the file (in color)! -- The latest TOhtml may actually work with older versions of Vim, but some - features such as conceal support will not function, and the colors may be - incorrect for an old Vim without GUI support compiled in. Here is an example how to run the script over all .c and .h files from a Unix shell: > @@ -900,7 +887,7 @@ For Visual Basic use: > BAAN *baan.vim* *baan-syntax* -The baan.vim gives syntax support for BaanC of release BaanIV upto SSA ERP LN +The baan.vim gives syntax support for BaanC of release BaanIV up to SSA ERP LN for both 3 GL and 4 GL programming. Large number of standard defines/constants are supported. @@ -1397,11 +1384,17 @@ To select syntax highlighting file for Euphoria, as well as for auto-detecting the *.e and *.E file extensions as Euphoria file type, add the following line to your startup file: > - :let filetype_euphoria="euphoria3" + :let g:filetype_euphoria = "euphoria3" - or +< or > - :let filetype_euphoria="euphoria4" + :let g:filetype_euphoria = "euphoria4" + +Elixir and Euphoria share the *.ex file extension. If the filetype is +specifically set as Euphoria with the g:filetype_euphoria variable, or the +file is determined to be Euphoria based on keywords in the file, then the +filetype will be set as Euphoria. Otherwise, the filetype will default to +Elixir. ERLANG *erlang.vim* *ft-erlang-syntax* @@ -1419,6 +1412,22 @@ To enable highlighting some special atoms, put this in your vimrc: > :let g:erlang_highlight_special_atoms = 1 +ELIXIR *elixir.vim* *ft-elixir-syntax* + +Elixir is a dynamic, functional language for building scalable and maintainable +applications. + +The following file extensions are auto-detected as Elixir file types: + + *.ex, *.exs, *.eex, *.leex, *.lock + +Elixir and Euphoria share the *.ex file extension. If the filetype is +specifically set as Euphoria with the g:filetype_euphoria variable, or the +file is determined to be Euphoria based on keywords in the file, then the +filetype will be set as Euphoria. Otherwise, the filetype will default to +Elixir. + + FLEXWIKI *flexwiki.vim* *ft-flexwiki-syntax* FlexWiki is an ASP.NET-based wiki package available at http://www.flexwiki.com @@ -3398,8 +3407,8 @@ syntax highlighting script handles this with the following logic: Tex: Match Check Control~ Sometimes one actually wants mismatched parentheses, square braces, - and or curly braces; for example, \text{(1,10] is a range from but - not including 1 to and including 10}. This wish, of course, conflicts + and or curly braces; for example, \text{(1,10]} is a range from but + not including 1 to and including 10. This wish, of course, conflicts with the desire to provide delimiter mismatch detection. To accommodate these conflicting goals, syntax/tex.vim provides > g:tex_matchcheck = '[({[]' @@ -4037,7 +4046,7 @@ match in the same position overrules an earlier one). The "transparent" argument makes the "myVim" match use the same highlighting as "myString". But it does not contain anything. If the "contains=NONE" argument would be left out, then "myVim" would use the contains argument from myString and allow -"myWord" to be contained, which will be highlighted as a Constant. This +"myWord" to be contained, which will be highlighted as a Comment. This happens because a contained match doesn't match inside itself in the same position, thus the "myVim" match doesn't overrule the "myWord" match here. @@ -4745,12 +4754,12 @@ in their own color. This is basically the same as > :echo g:colors_name < In case g:colors_name has not been defined :colo will - output "default". When compiled without the |+eval| - feature it will output "unknown". + output "default". :colo[rscheme] {name} Load color scheme {name}. This searches 'runtimepath' - for the file "colors/{name}.vim". The first one that + for the file "colors/{name}.(vim|lua)". The first one that is found is loaded. + Note: "colors/{name}.vim" is tried first. Also searches all plugins in 'packpath', first below "start" and then under "opt". @@ -5077,9 +5086,15 @@ Substitute |:substitute| replacement text highlighting *hl-LineNr* LineNr Line number for ":number" and ":#" commands, and when 'number' or 'relativenumber' option is set. + *hl-LineNrAbove* +LineNrAbove Line number for when the 'relativenumber' + option is set, above the cursor line. + *hl-LineNrBelow* +LineNrBelow Line number for when the 'relativenumber' + option is set, below the cursor line. *hl-CursorLineNr* -CursorLineNr Like LineNr when 'cursorline' or 'relativenumber' is set for - the cursor line. +CursorLineNr Like LineNr when 'cursorline' is set and 'cursorlineopt' + contains "number" or is "both", for the cursor line. *hl-MatchParen* MatchParen The character under the cursor or just before it, if it is a paired bracket, and its match. |pi_paren.txt| @@ -5274,51 +5289,10 @@ back to their Vim default. Note that if you are using a color scheme, the colors defined by the color scheme for syntax highlighting will be lost. -What this actually does is: > - - let g:syntax_cmd = "reset" - runtime! syntax/syncolor.vim - -Note that this uses the 'runtimepath' option. - - *syncolor* -If you want to use different colors for syntax highlighting, you can add a Vim -script file to set these colors. Put this file in a directory in -'runtimepath' which comes after $VIMRUNTIME, so that your settings overrule -the default colors. This way these colors will be used after the ":syntax -reset" command. - -For Unix you can use the file ~/.config/nvim/after/syntax/syncolor.vim. -Example: > - - if &background == "light" - highlight comment ctermfg=darkgreen guifg=darkgreen - else - highlight comment ctermfg=green guifg=green - endif - - *E679* -Do make sure this syncolor.vim script does not use a "syntax on", set the -'background' option or uses a "colorscheme" command, because it results in an -endless loop. - Note that when a color scheme is used, there might be some confusion whether your defined colors are to be used or the colors from the scheme. This depends on the color scheme file. See |:colorscheme|. - *syntax_cmd* -The "syntax_cmd" variable is set to one of these values when the -syntax/syncolor.vim files are loaded: - "on" ":syntax on" command. Highlight colors are overruled but - links are kept - "enable" ":syntax enable" command. Only define colors for groups that - don't have highlighting yet. Use ":syntax default". - "reset" ":syntax reset" command or loading a color scheme. Define all - the colors. - "skip" Don't define colors. Used to skip the default settings when a - syncolor.vim file earlier in 'runtimepath' has already set - them. - ============================================================================== 16. Highlighting tags *tag-highlight* @@ -5403,9 +5377,6 @@ If your syntax causes redrawing to be slow, here are a few hints on making it faster. To see slowness switch on some features that usually interfere, such as 'relativenumber' and |folding|. -Note: this is only available when compiled with the |+profile| feature. -You many need to build Vim with "huge" features. - To find out what patterns are consuming most time, get an overview with this sequence: > :syntime on diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index 2c1b927e5a..4d938c4a23 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -367,11 +367,11 @@ be a bug. If you really want the old Vi behavior, set the 't' flag in 'cpoptions'. *tag-binary-search* -Vim uses binary searching in the tags file to find the desired tag quickly -(when enabled at compile time |+tag_binary|). But this only works if the -tags file was sorted on ASCII byte value. Therefore, if no match was found, -another try is done with a linear search. If you only want the linear search, -reset the 'tagbsearch' option. Or better: Sort the tags file! +Vim uses binary searching in the tags file to find the desired tag quickly. +But this only works if the tags file was sorted on ASCII byte value. +Therefore, if no match was found, another try is done with a linear search. +If you only want the linear search, reset the 'tagbsearch' option. Or better: +Sort the tags file! Note that the binary searching is disabled when not looking for a tag with a specific name. This happens when ignoring case and when a regular expression @@ -666,9 +666,6 @@ included files (recursively). This can be used to find the definition of a variable, function or macro. If you only want to search in the current buffer, use the commands listed at |pattern-searches|. -These commands are not available when the |+find_in_path| feature was disabled -at compile time. - When a line is encountered that includes another file, that file is searched before continuing in the current buffer. Files included by included files are also searched. When an include file could not be found it is silently diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt index b2ce6d670d..3b59dfa908 100644 --- a/runtime/doc/testing.txt +++ b/runtime/doc/testing.txt @@ -30,8 +30,7 @@ New tests should be added as new style tests. These use functions such as |assert_equal()| to keep the test commands and the expected result in one place. *old-style-testing* -In some cases an old style test needs to be used. E.g. when testing Vim -without the |+eval| feature. +In some cases an old style test needs to be used. Find more information in the file src/testdir/README.txt. @@ -54,6 +53,9 @@ assert_beeps({cmd}) *assert_beeps()* Also see |assert_fails()|, |assert_nobeep()| and |assert-return|. + Can also be used as a |method|: > + GetCmd()->assert_beeps() +< *assert_equal()* assert_equal({expected}, {actual} [, {msg}]) When {expected} and {actual} are not equal an error message is @@ -70,7 +72,10 @@ assert_equal({expected}, {actual} [, {msg}]) < Will result in a string to be added to |v:errors|: test.vim line 12: Expected 'foo' but got 'bar' ~ - *assert_equalfile()* + Can also be used as a |method|: > + mylist->assert_equal([1, 2, 3]) + +< *assert_equalfile()* assert_equalfile({fname-one}, {fname-two}) When the files {fname-one} and {fname-two} do not contain exactly the same text an error message is added to |v:errors|. @@ -78,6 +83,9 @@ assert_equalfile({fname-one}, {fname-two}) When {fname-one} or {fname-two} does not exist the error will mention that. + Can also be used as a |method|: > + GetLog()->assert_equalfile('expected.log') + assert_exception({error} [, {msg}]) *assert_exception()* When v:exception does not contain the string {error} an error message is added to |v:errors|. Also see |assert-return|. @@ -98,6 +106,9 @@ assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()* Note that beeping is not considered an error, and some failing commands only beep. Use |assert_beeps()| for those. + Can also be used as a |method|: > + GetCmd()->assert_fails('E99:') + assert_false({actual} [, {msg}]) *assert_false()* When {actual} is not false an error message is added to |v:errors|, like with |assert_equal()|. @@ -107,6 +118,9 @@ assert_false({actual} [, {msg}]) *assert_false()* When {msg} is omitted an error in the form "Expected False but got {actual}" is produced. + Can also be used as a |method|: > + GetResult()->assert_false() + assert_inrange({lower}, {upper}, {actual} [, {msg}]) *assert_inrange()* This asserts number and |Float| values. When {actual} is lower than {lower} or higher than {upper} an error message is added @@ -135,6 +149,9 @@ assert_match({pattern}, {actual} [, {msg}]) < Will result in a string to be added to |v:errors|: test.vim line 12: Pattern '^f.*o$' does not match 'foobar' ~ + Can also be used as a |method|: > + getFile()->assert_match('foo.*') +< assert_nobeep({cmd}) *assert_nobeep()* Run {cmd} and add an error message to |v:errors| if it produces a beep or visual bell. @@ -146,16 +163,27 @@ assert_notequal({expected}, {actual} [, {msg}]) |v:errors| when {expected} and {actual} are equal. Also see |assert-return|. - *assert_notmatch()* + Can also be used as a |method|: > + mylist->assert_notequal([1, 2, 3]) + +< *assert_notmatch()* assert_notmatch({pattern}, {actual} [, {msg}]) The opposite of `assert_match()`: add an error message to |v:errors| when {pattern} matches {actual}. Also see |assert-return|. + Can also be used as a |method|: > + getFile()->assert_notmatch('bar.*') + + assert_report({msg}) *assert_report()* Report a test failure directly, using {msg}. Always returns one. + Can also be used as a |method|: > + GetMessage()->assert_report() + + assert_true({actual} [, {msg}]) *assert_true()* When {actual} is not true an error message is added to |v:errors|, like with |assert_equal()|. @@ -165,5 +193,8 @@ assert_true({actual} [, {msg}]) *assert_true()* When {msg} is omitted an error in the form "Expected True but got {actual}" is produced. + Can also be used as a |method|: > + GetResult()->assert_true() +< vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/tips.txt b/runtime/doc/tips.txt index bcb2e48cbf..27b6ca2885 100644 --- a/runtime/doc/tips.txt +++ b/runtime/doc/tips.txt @@ -462,7 +462,7 @@ the current window, try this custom `:HelpCurwin` command: execute mods .. ' helpclose' let s:did_open_help = v:true endif - if !getcompletion(a:subject, 'help')->empty() + if !empty(getcompletion(a:subject, 'help')) execute mods .. ' edit ' .. &helpfile endif return 'help ' .. a:subject diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index d6bca55bd6..ac10aeec88 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -13,25 +13,6 @@ VIM.TREESITTER *lua-treesitter* Nvim integrates the tree-sitter library for incremental parsing of buffers. -Currently Nvim does not provide the tree-sitter parsers, instead these must -be built separately, for instance using the tree-sitter utility. The only -exception is a C parser being included in official builds for testing -purposes. Parsers are searched for as `parser/{lang}.*` in any 'runtimepath' -directory. A parser can also be loaded manually using a full path: > - - vim.treesitter.require_language("python", "/path/to/python.so") - -<Create a parser for a buffer and a given language (if another plugin uses the -same buffer/language combination, it will be safely reused). Use > - - parser = vim.treesitter.get_parser(bufnr, lang) - -<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype' (this -doesn't work yet for some filetypes like "cpp") Currently, the parser will be -retained for the lifetime of a buffer but this is subject to change. A plugin -should keep a reference to the parser object as long as it wants incremental -updates. - *vim.treesitter.language_version* To check which language version is compiled with neovim, the number is stored within `vim.treesitter.language_version`. This number is not too helpful @@ -41,10 +22,25 @@ compiled grammars. Parser files *treesitter-parsers* Parsers are the heart of tree-sitter. They are libraries that tree-sitter will -search for in the `parser` runtime directory. +search for in the `parser` runtime directory. Currently Nvim does not provide +the tree-sitter parsers, instead these must be built separately, for instance +using the tree-sitter utility. The only exception is a C parser being included +in official builds for testing purposes. Parsers are searched for as +`parser/{lang}.*` in any 'runtimepath' directory. +A parser can also be loaded manually using a full path: > + + vim.treesitter.require_language("python", "/path/to/python.so") + +<Create a parser for a buffer and a given language (if another plugin uses the +same buffer/language combination, it will be safely reused). Use > + + parser = vim.treesitter.get_parser(bufnr, lang) + +<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype'. +Currently, the parser will be retained for the lifetime of a buffer but this +is subject to change. A plugin should keep a reference to the parser object as +long as it wants incremental updates. -For a parser to be available for a given language, there must be a file named -`{lang}.so` within the parser directory. Parser methods *lua-treesitter-parser* @@ -59,11 +55,11 @@ it should call `parse()` again. If the buffer wasn't edited, the same tree will be returned again without extra work. If the buffer was parsed before, incremental parsing will be done of the changed parts. -NB: to use the parser directly inside a |nvim_buf_attach| Lua callback, you must -call `get_parser()` before you register your callback. But preferably parsing -shouldn't be done directly in the change callback anyway as they will be very -frequent. Rather a plugin that does any kind of analysis on a tree should use -a timer to throttle too frequent updates. +Note: to use the parser directly inside a |nvim_buf_attach| Lua callback, you +must call `get_parser()` before you register your callback. But preferably +parsing shouldn't be done directly in the change callback anyway as they will +be very frequent. Rather a plugin that does any kind of analysis on a tree +should use a timer to throttle too frequent updates. tsparser:set_included_regions({region_list}) *tsparser:set_included_regions()* Changes the regions the parser should consider. This is used for @@ -90,6 +86,18 @@ Node methods *lua-treesitter-node* tsnode:parent() *tsnode:parent()* Get the node's immediate parent. +tsnode:next_sibling() *tsnode:next_sibling()* + Get the node's next sibling. + +tsnode:prev_sibling() *tsnode:prev_sibling()* + Get the node's previous sibling. + +tsnode:next_named_sibling() *tsnode:next_named_sibling()* + Get the node's next named sibling. + +tsnode:prev_named_sibling() *tsnode:prev_named_sibling()* + Get the node's previous named sibling. + tsnode:iter_children() *tsnode:iter_children()* Iterates over all the direct children of {tsnode}, regardless of wether they are named or not. @@ -153,7 +161,8 @@ tsnode:id() *tsnode:id()* except for being a primitive lua type with value equality (so not a table). Presently it is a (non-printable) string. - NB: the id is not guaranteed to be unique for nodes from different trees. + Note: the id is not guaranteed to be unique for nodes from different + trees. tsnode:descendant_for_range({start_row}, {start_col}, {end_row}, {end_col}) *tsnode:descendant_for_range()* @@ -167,9 +176,14 @@ tsnode:named_descendant_for_range({start_row}, {start_col}, {end_row}, {end_col} Query *lua-treesitter-query* -Tree-sitter queries are supported, with some limitations. Currently, the only -supported match predicate is `eq?` (both comparing a capture against a string -and two captures against each other). +Tree-sitter queries are supported, they are a way to do pattern-matching over +a tree, using a simple to write lisp-like format. See +https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax for more +information on how to write queries. + +Note: The predicates listed in the web page above differ from those Neovim +supports. See |lua-treesitter-predicates| for a complete list of predicates +supported by Neovim. A `query` consists of one or more patterns. A `pattern` is defined over node types in the syntax tree. A `match` corresponds to specific elements of the @@ -213,7 +227,10 @@ Here is a list of built-in predicates : ((identifier) @foo-bar (#contains @foo-bar "foo" "bar")) < `any-of?` *ts-predicate-any-of?* - Will check if the text is the same as any of the following + Will check if the text is the same as any of the following. + This is the recommended way to check if the node matches one + of many keywords for example, as it has been optimized for + this. arguments : > ((identifier) @foo (#any-of? @foo "foo" "bar")) < @@ -221,10 +238,22 @@ Here is a list of built-in predicates : Each predicate has a `not-` prefixed predicate that is just the negation of the predicate. + *vim.treesitter.query.add_predicate()* +vim.treesitter.query.add_predicate({name}, {handler}) + +This adds a predicate with the name {name} to be used in queries. +{handler} should be a function whose signature will be : > + handler(match, pattern, bufnr, predicate) +< + *vim.treesitter.query.list_predicates()* +vim.treesitter.query.list_predicates() + +This lists the currently available predicates to use in queries. + Treesitter Query Directive *lua-treesitter-directives* Treesitter queries can also contain `directives`. Directives store metadata for a node -or match and perform side effects. for example, the |set!| predicate sets metadata on +or match and perform side effects. For example, the |set!| predicate sets metadata on the match or node : > ((identifier) @foo (#set! "type" "parameter")) @@ -244,6 +273,21 @@ Here is a list of built-in directives: `({capture_id}, {start_row}, {start_col}, {end_row}, {end_col}, {key?})` The default key is "offset". + *vim.treesitter.query.add_directive()* +vim.treesitter.query.add_directive({name}, {handler}) + +This adds a directive with the name {name} to be used in queries. +{handler} should be a function whose signature will be : > + handler(match, pattern, bufnr, predicate, metadata) +Handlers can set match level data by setting directly on the metadata object `metadata.key = value` +Handlers can set node level data by using the capture id on the metadata table +`metadata[capture_id].key = value` + + *vim.treesitter.query.list_directives()* +vim.treesitter.query.list_directives() + +This lists the currently available directives to use in queries. + Treesitter syntax highlighting (WIP) *lua-treesitter-highlight* NOTE: This is a partially implemented feature, and not usable as a default @@ -252,7 +296,7 @@ for those who want to experiment with this feature and contribute to its development. Highlights are defined in the same query format as in the tree-sitter highlight -crate, which some limitations and additions. Set a highlight query for a +crate, with some limitations and additions. Set a highlight query for a buffer with this code: > local query = [[ @@ -289,6 +333,19 @@ identical identifiers, highlighting both as |hl-WarningMsg|: > ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right) (eq? @WarningMsg.left @WarningMsg.right)) < +Treesitter Highlighting Priority *lua-treesitter-highlight-priority* + +Tree-sitter uses |nvim_buf_set_extmark()| to set highlights with a default +priority of 100. This enables plugins to set a highlighting priority lower or +higher than tree-sitter. It is also possible to change the priority of an +individual query pattern manually by setting its `"priority"` metadata attribute: > + + ( + (super_important_node) @ImportantHighlight + ; Give the whole query highlight priority higher than the default (100) + (set! "priority" 105) + ) +< ============================================================================== Lua module: vim.treesitter *lua-treesitter-core* @@ -388,8 +445,13 @@ get_query_files({lang}, {query_name}, {is_included}) {is_included} Internal parameter, most of the time left as `nil` +list_directives() *list_directives()* + Return: ~ + The list of supported directives. + list_predicates() *list_predicates()* - TODO: Documentation + Return: ~ + The list of supported predicates. parse_query({lang}, {query}) *parse_query()* Parse {query} as a string. (If the query is in a file, the @@ -469,11 +531,9 @@ Query:iter_matches({self}, {node}, {source}, {start}, {stop}) for id, node in pairs(match) do local name = query.captures[id] -- `node` was captured by the `name` capture in the match -< -> - local node_data = metadata[id] -- Node level metadata -< -> + + local node_data = metadata[id] -- Node level metadata + ... use the info here ... end end @@ -707,17 +767,4 @@ new({source}, {lang}, {opts}) *languagetree.new()* the injection language query per language. - -============================================================================== -Lua module: vim.treesitter.health *treesitter-health* - -check_health() *check_health()* - TODO: Documentation - -list_parsers() *list_parsers()* - Lists the parsers currently installed - - Return: ~ - A list of parsers - vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 82406898c8..e7be14e732 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -219,8 +219,8 @@ the editor. ["busy_start"] ["busy_stop"] - Nvim started or stopped being busy, and possibly not responsive to - user input. This could be indicated to the user by hiding the cursor. + Indicates to the UI that it must stop rendering the cursor. This event + is misnamed and does not actually have anything to do with busyness. ["suspend"] |:suspend| command or |CTRL-Z| mapping is used. A terminal client (or @@ -631,11 +631,13 @@ Tabline Events *ui-tabline* Activated by the `ext_tabline` |ui-option|. -["tabline_update", curtab, tabs] +["tabline_update", curtab, tabs, curbuf, buffers] Tabline was updated. UIs should present this data in a custom tabline - widget. - curtab: Current Tabpage - tabs: List of Dicts [{ "tab": Tabpage, "name": String }, ...] + widget. Note: options `curbuf` + `buffers` were added in API7. + curtab: Current Tabpage + tabs: List of Dicts [{ "tab": Tabpage, "name": String }, ...] + curbuf: Current buffer handle. + buffers: List of Dicts [{ "buffer": buffer handle, "name": String}, ...] ============================================================================== Cmdline Events *ui-cmdline* diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index d8634ac6ed..2edef0ca23 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -175,10 +175,8 @@ This switches on three very clever mechanisms: *restore-cursor* *last-position-jump* > - autocmd BufReadPost * - \ if line("'\"") >= 1 && line("'\"") <= line("$") && &ft !~# 'commit' - \ | exe "normal! g`\"" - \ | endif + autocmd BufRead * autocmd FileType <buffer> ++once + \ if &ft !~# 'commit\|rebase' && line("'\"") > 1 && line("'\"") <= line("$") | exe 'normal! g`"' | endif Another autocommand. This time it is used after reading any file. The complicated stuff after it checks if the '" mark is defined, and jumps to it @@ -411,7 +409,7 @@ Examples for the "stuff" filetype on Unix: > The <filetype> part is the name of the filetype the plugin is to be used for. Only files of this filetype will use the settings from the plugin. The <name> part of the plugin file doesn't matter, you can use it to have several plugins -for the same filetype. Note that it must end in ".vim". +for the same filetype. Note that it must end in ".vim" or ".lua". Further reading: diff --git a/runtime/doc/usr_08.txt b/runtime/doc/usr_08.txt index 8e69307a94..8ccaa73006 100644 --- a/runtime/doc/usr_08.txt +++ b/runtime/doc/usr_08.txt @@ -235,7 +235,7 @@ windows like this: +----------------------------------+ Clearly the last one should be at the top. Go to that window (using CTRL-W w) -and the type this command: > +and then type this command: > CTRL-W K diff --git a/runtime/doc/usr_09.txt b/runtime/doc/usr_09.txt index 757d13e0f3..8084d13b5d 100644 --- a/runtime/doc/usr_09.txt +++ b/runtime/doc/usr_09.txt @@ -109,7 +109,7 @@ when the 'wrap' option has been reset (more about that later). When there are vertically split windows, only the windows on the right side will have a scrollbar. However, when you move the cursor to a window on the -left, it will be this one the that scrollbar controls. This takes a bit of +left, it will be this one that the scrollbar controls. This takes a bit of time to get used to. When you work with vertically split windows, consider adding a scrollbar on the left. This can be done with a menu item, or with the 'guioptions' option: diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index f92cb3c509..c9321e8736 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -99,8 +99,6 @@ and the value of the variable i. Since i is one, this will print: Then there is the ":let i += 1" command. This does the same thing as ":let i = i + 1". This adds one to the variable i and assigns the new value to the same variable. -Note: this is how it works in legacy Vim script, which is what we discuss in -this file. The example was given to explain the commands, but would you really want to make such a loop, it can be written much more compact: > @@ -121,12 +119,12 @@ A hexadecimal number starts with "0x" or "0X". For example "0x1f" is decimal 31. An octal number starts with "0o", "0O" or a zero and another digit. "0o17" is -decimal 15. Using just a zero prefix is not supported in Vim9 script. +decimal 15. A binary number starts with "0b" or "0B". For example "0b101" is decimal 5. A decimal number is just digits. Careful: don't put a zero before a decimal -number, it will be interpreted as an octal number in legacy script! +number, it will be interpreted as an octal number! The ":echo" command always prints decimal numbers. Example: > diff --git a/runtime/doc/usr_45.txt b/runtime/doc/usr_45.txt index bc95f3405e..3199c4d8ea 100644 --- a/runtime/doc/usr_45.txt +++ b/runtime/doc/usr_45.txt @@ -31,13 +31,6 @@ this command: > If it replies with "C", this means the default is being used, which is English. - Note: - Using different languages only works when Vim was compiled to handle - it. To find out if it works, use the ":version" command and check the - output for "+gettext" and "+multi_lang". If they are there, you are - OK. If you see "-gettext" or "-multi_lang" you will have to find - another Vim. - What if you would like your messages in a different language? There are several ways. Which one you should use depends on the capabilities of your system. diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index d5c07d9622..29bd6e5e64 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -14,6 +14,10 @@ Various commands *various* *CTRL-L* CTRL-L Clears and redraws the screen. The redraw may happen later, after processing typeahead. + *CTRL-L-default* + By default, also clears search highlighting + |:nohlsearch| and updates diffs |:diffupdate|. + |default-mappings| *:mod* *:mode* :mod[e] Clears and redraws the screen. @@ -456,20 +460,14 @@ defined while executing a function, user command or autocommand, the script in which it was defined is reported. *K* -[count]K Run a program to lookup the keyword under the - cursor. The name of the program is given with the - 'keywordprg' (kp) option (default is "man"). The - keyword is formed of letters, numbers and the - characters in 'iskeyword'. The keyword under or - right of the cursor is used. The same can be done - with the command > - :!{program} {keyword} +[count]K Runs the program given by 'keywordprg' to lookup the + |word| (defined by 'iskeyword') under or right of the + cursor. Default is "man". Works like this: > + :tabnew | terminal {program} {keyword} < Special cases: - If 'keywordprg' begins with ":" it is invoked as a Vim command with [count]. - - If 'keywordprg' is empty, the ":help" command is - used. It's a good idea to include more characters - in 'iskeyword' then, to be able to find more help. + - If 'keywordprg' is empty, |:help| is used. - When 'keywordprg' is equal to "man", a [count] before "K" is inserted after the "man" command and before the keyword. For example, using "2K" while @@ -506,7 +504,8 @@ gO Show a filetype-specific, navigable "outline" of the Queued messages are processed during the sleep. *:sl!* *:sleep!* -:[N]sl[eep]! [N] [m] Same as above, but hide the cursor. +:[N]sl[eep]! [N][m] Same as above. Unlike Vim, it does not hide the + cursor. |vim-differences| ============================================================================== 2. Using Vim like less or more *less* diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index bb30495d77..4dea053bc7 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -6,16 +6,16 @@ Differences between Nvim and Vim *vim-differences* -Nvim differs from Vim in many ways, although editor and VimL features are -mostly identical. This document is a complete and centralized reference of -the 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. ============================================================================== 1. Configuration *nvim-config* -- Use `$XDG_CONFIG_HOME/nvim/init.vim` instead of `.vimrc` for configuration. +- Use `$XDG_CONFIG_HOME/nvim/init.vim` instead of `.vimrc` for your |config|. - Use `$XDG_CONFIG_HOME/nvim` instead of `.vim` to store configuration files. - Use `$XDG_DATA_HOME/nvim/shada/main.shada` instead of `.viminfo` for persistent session information. |shada| @@ -30,7 +30,7 @@ the differences. - 'autoread' is enabled - 'background' defaults to "dark" (unless set automatically by the terminal/UI) - 'backspace' defaults to "indent,eol,start" -- 'backupdir' defaults to .,~/.local/share/nvim/backup (|xdg|) +- 'backupdir' defaults to .,~/.local/share/nvim/backup// (|xdg|), auto-created - 'belloff' defaults to "all" - 'compatible' is always disabled - 'complete' excludes "i" @@ -41,9 +41,11 @@ the differences. - 'fillchars' defaults (in effect) to "vert:│,fold:·,sep:│" - 'formatoptions' defaults to "tcqj" - 'fsync' is disabled +- 'hidden' is enabled - 'history' defaults to 10000 (the maximum) - 'hlsearch' is enabled - 'incsearch' is enabled +- 'joinspaces' is disabled - 'langnoremap' is enabled - 'langremap' is disabled - 'laststatus' defaults to 2 (statusline is always shown) @@ -56,12 +58,13 @@ the differences. - 'sidescroll' defaults to 1 - 'smarttab' is enabled - 'startofline' is disabled +- 'switchbuf' defaults to "uselast" - 'tabpagemax' defaults to 50 - 'tags' defaults to "./tags;,tags" - 'ttimeoutlen' defaults to 50 - 'ttyfast' is always set -- 'viewoptions' includes "unix,slash" -- 'undodir' defaults to ~/.local/share/nvim/undo (|xdg|), auto-created +- 'undodir' defaults to ~/.local/share/nvim/undo// (|xdg|), auto-created +- 'viewoptions' includes "unix,slash", excludes "options" - 'viminfo' includes "!" - 'wildmenu' is enabled - 'wildoptions' defaults to "pum,tagfile" @@ -72,23 +75,47 @@ the differences. - |g:vimsyn_embed| defaults to "l" to enable Lua highlighting + +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". +> + nnoremap Y y$ + nnoremap <C-L> <Cmd>nohlsearch<Bar>diffupdate<CR><C-L> + inoremap <C-U> <C-G>u<C-U> + inoremap <C-W> <C-G>u<C-W> +< +Default Autocommands ~ + *default-autocmds* +Default autocommands exist in the following groups. Use ":autocmd! {group}" to +remove them and ":autocmd {group}" to see how they're defined. + +nvim_terminal: +- BufReadCmd: Treats "term://" buffers as |terminal| buffers. |terminal-start| + +nvim_cmdwin: +- CmdwinEnter: Limits syntax sync to maxlines=1 in the |cmdwin|. + ============================================================================== 3. New Features *nvim-features* MAJOR COMPONENTS ~ API |API| -Lua scripting |lua| Job control |job-control| -Remote plugins |remote-plugin| +LSP framework |lsp| +Lua scripting |lua| +Parsing engine |treesitter| Providers Clipboard |provider-clipboard| Node.js plugins |provider-nodejs| Python plugins |provider-python| Ruby plugins |provider-ruby| +Remote plugins |remote-plugin| Shared data |shada| -Embedded terminal |terminal| -VimL parser |nvim_parse_expression()| +Terminal emulator |terminal| +Vimscript parser |nvim_parse_expression()| XDG base directories |xdg| USER EXPERIENCE ~ @@ -137,7 +164,7 @@ FEATURES ~ Command-line highlighting: The expression prompt (|@=|, |c_CTRL-R_=|, |i_CTRL-R_=|) is highlighted - using a built-in VimL expression parser. |expr-highlight| + using a built-in Vimscript expression parser. |expr-highlight| *E5408* *E5409* |input()|, |inputdialog()| support custom highlighting. |input()-highlight| *g:Nvim_color_cmdline* @@ -150,6 +177,7 @@ Commands: |:drop| is always available |:Man| is available by default, with many improvements such as completion |:sign-define| accepts a `numhl` argument, to highlight the line number + |:match| can be invoked before highlight group is defined Events: |Signal| @@ -167,6 +195,7 @@ Functions: |msgpackdump()|, |msgpackparse()| provide msgpack de/serialization |stdpath()| |system()|, |systemlist()| can run {cmd} directly (without 'shell') + |matchadd()| can be called before highlight group is defined Highlight groups: |highlight-blend| controls blend level for a highlight group @@ -358,6 +387,14 @@ Startup: - works by default: "-" file is optional - works in more cases: |-Es|, file args +Syntax highlighting: + 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 + after/syntax/syncolor.vim file should transition that file into a + colorscheme. |:colorscheme| + TUI: *:set-termcap* Start Nvim with 'verbose' level 3 to show terminal capabilities: > @@ -380,7 +417,7 @@ TUI: UI/Display: |Visual| selection highlights the character at cursor. |visual-use| -VimL (Vim script) compatibility: +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| @@ -421,7 +458,11 @@ Commands: :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 @@ -470,6 +511,7 @@ Options: 'maxmem' Nvim delegates memory-management to the OS. 'maxmemtot' Nvim delegates memory-management to the OS. 'maxcombine' (6 is always used) + *'prompt'* *'noprompt'* *'restorescreen'* *'rs'* *'norestorescreen'* *'nors'* 'shelltype' *'shortname'* *'sn'* *'noshortname'* *'nosn'* diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index f7828f0289..111588e43a 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -419,7 +419,7 @@ abcdefghijklmnopqrstuvwxyz abcdefghijklmnSTRINGopqrstuvwxyz abc STRING defghijklmnopqrstuvwxyz -abcdef ghi STRING jklmnopqrstuvwxyz +abcdef ghi STRING jklmnopqrstuvwxyz abcdefghijklmnSTRINGopqrstuvwxyz 2. fo<C-v>3j$ASTRING<ESC> *v_b_A_example* diff --git a/runtime/filetype.vim b/runtime/filetype.vim index ed70ac5ce8..7358f8f2b3 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2021 Apr 17 +" Last Change: 2021 Jul 03 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -389,7 +389,7 @@ au BufNewFile,BufRead *.cfm,*.cfi,*.cfc setf cf " Configure scripts au BufNewFile,BufRead configure.in,configure.ac setf config -" CUDA Cumpute Unified Device Architecture +" CUDA Compute Unified Device Architecture au BufNewFile,BufRead *.cu,*.cuh setf cuda " Dockerfilb; Podman uses the same syntax with name Containerfile @@ -404,8 +404,15 @@ au BufNewFile,BufRead *enlightenment/*.cfg setf c " Eterm au BufNewFile,BufRead *Eterm/*.cfg setf eterm +" Elixir or Euphoria +au BufNewFile,BufRead *.ex call dist#ft#ExCheck() + +" Elixir +au BufRead,BufNewFile mix.lock,*.exs setf elixir +au BufRead,BufNewFile *.eex,*.leex setf eelixir + " Euphoria 3 or 4 -au BufNewFile,BufRead *.eu,*.ew,*.ex,*.exu,*.exw call dist#ft#EuphoriaCheck() +au BufNewFile,BufRead *.eu,*.ew,*.exu,*.exw call dist#ft#EuphoriaCheck() if has("fname_case") au BufNewFile,BufRead *.EU,*.EW,*.EX,*.EXU,*.EXW call dist#ft#EuphoriaCheck() endif @@ -526,8 +533,13 @@ au BufNewFile,BufRead *.drac,*.drc,*lvs,*lpe setf dracula " Datascript au BufNewFile,BufRead *.ds setf datascript -" dsl -au BufNewFile,BufRead *.dsl setf dsl +" dsl: DSSSL or Structurizr +au BufNewFile,BufRead *.dsl + \ if getline(1) =~ '^\s*<\!' | + \ setf dsl | + \ else | + \ setf structurizr | + \ endif " DTD (Document Type Definition for XML) au BufNewFile,BufRead *.dtd setf dtd @@ -639,6 +651,9 @@ au BufNewFile,BufRead *.mo,*.gdmo setf gdmo " Gedcom au BufNewFile,BufRead *.ged,lltxxxxx.txt setf gedcom +" Gemtext +au BufNewFile,BufRead *.gmi,*.gemini setf gemtext + " Gift (Moodle) autocmd BufRead,BufNewFile *.gift setf gift @@ -851,6 +866,18 @@ au BufNewFile,BufRead *.jov,*.j73,*.jovial setf jovial " JSON au BufNewFile,BufRead *.json,*.jsonp,*.webmanifest setf json +" JSON Patch (RFC 6902) +au BufNewFile,BufRead *.json-patch setf json + +" Jupyter Notebook is also json +au BufNewFile,BufRead *.ipynb setf json + +" JSONC +au BufNewFile,BufRead *.jsonc setf jsonc + +" Julia +au BufNewFile,BufRead *.jl setf julia + " Kixtart au BufNewFile,BufRead *.kix setf kix @@ -998,7 +1025,7 @@ au BufNewFile,BufRead *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md setf markdown " Mason au BufNewFile,BufRead *.mason,*.mhtml,*.comp setf mason -" Mathematica, Matlab, Murphi or Objective C +" Mathematica, Matlab, Murphi, Objective C or Octave au BufNewFile,BufRead *.m call dist#ft#FTm() " Mathematica notebook @@ -1144,6 +1171,9 @@ au BufNewFile,BufRead *.ml,*.mli,*.mll,*.mly,.ocamlinit,*.mlt,*.mlp,*.mlip,*.mli " Occam au BufNewFile,BufRead *.occ setf occam +" Octave +au BufNewFile,BufRead octave.conf,.octaverc,octaverc setf octave + " Omnimark au BufNewFile,BufRead *.xom,*.xin setf omnimark @@ -1492,7 +1522,7 @@ au BufNewFile,BufRead *.sass setf sass au BufNewFile,BufRead *.sa setf sather " Scala -au BufNewFile,BufRead *.scala setf scala +au BufNewFile,BufRead *.scala,*.sc setf scala " SBT - Scala Build Tool au BufNewFile,BufRead *.sbt setf sbt @@ -1500,6 +1530,9 @@ au BufNewFile,BufRead *.sbt setf sbt " Scilab au BufNewFile,BufRead *.sci,*.sce setf scilab +" scdoc +au BufNewFile,BufRead *.scd setf scdoc + " SCSS au BufNewFile,BufRead *.scss setf scss @@ -2303,6 +2336,7 @@ au BufNewFile,BufRead *.txt " Use the filetype detect plugins. They may overrule any of the previously " detected filetypes. runtime! ftdetect/*.vim +runtime! ftdetect/*.lua " NOTE: The above command could have ended the filetypedetect autocmd group " and started another one. Let's make sure it has ended to get to a consistent diff --git a/runtime/ftplugin.vim b/runtime/ftplugin.vim index a434b9372b..feef949dba 100644 --- a/runtime/ftplugin.vim +++ b/runtime/ftplugin.vim @@ -28,7 +28,9 @@ augroup filetypeplugin " When there is a dot it is used to separate filetype names. Thus for " "aaa.bbb" load "aaa" and then "bbb". for name in split(s, '\.') - exe 'runtime! ftplugin/' . name . '.vim ftplugin/' . name . '_*.vim ftplugin/' . name . '/*.vim' + exe 'runtime! ftplugin/' . name . '.vim ftplugin/' . name . '_*.vim ftplugin/' . name . '/*.vim' + " Load lua ftplugins + exe printf('runtime! ftplugin/%s.lua ftplugin/%s_*.lua ftplugin/%s/*.lua', name, name, name) endfor endif endfunc diff --git a/runtime/ftplugin/eruby.vim b/runtime/ftplugin/eruby.vim index 3c18bada78..e67b00b278 100644 --- a/runtime/ftplugin/eruby.vim +++ b/runtime/ftplugin/eruby.vim @@ -3,7 +3,7 @@ " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Jan 06 +" Last Change: 2020 Jun 28 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -118,7 +118,7 @@ endif " TODO: comments= setlocal commentstring=<%#%s%> -let b:undo_ftplugin = "setl cms< " +let b:undo_ftplugin = "setl cms< " . \ " | unlet! b:browsefilter b:match_words | " . s:undo_ftplugin let &cpo = s:save_cpo diff --git a/runtime/ftplugin/jsonc.vim b/runtime/ftplugin/jsonc.vim new file mode 100644 index 0000000000..90d52cd0d3 --- /dev/null +++ b/runtime/ftplugin/jsonc.vim @@ -0,0 +1,27 @@ +" Vim filetype plugin +" Language: JSONC (JSON with Comments) +" Original Author: Izhak Jakov <izhak724@gmail.com> +" Acknowledgement: Based off of vim-jsonc maintained by Kevin Locke <kevin@kevinlocke.name> +" https://github.com/kevinoid/vim-jsonc +" License: MIT +" Last Change: 2021-07-01 + +runtime! ftplugin/json.vim + +if exists('b:did_ftplugin_jsonc') + finish +else + let b:did_ftplugin_jsonc = 1 +endif + +" A list of commands that undo buffer local changes made below. +let s:undo_ftplugin = [] + +" Set comment (formatting) related options. {{{1 +setlocal commentstring=//%s comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// +call add(s:undo_ftplugin, 'commentstring< comments<') + +" Let Vim know how to disable the plug-in. +call map(s:undo_ftplugin, "'execute ' . string(v:val)") +let b:undo_ftplugin = join(s:undo_ftplugin, ' | ') +unlet s:undo_ftplugin diff --git a/runtime/ftplugin/julia.vim b/runtime/ftplugin/julia.vim new file mode 100644 index 0000000000..32e364e436 --- /dev/null +++ b/runtime/ftplugin/julia.vim @@ -0,0 +1,92 @@ +" Vim filetype plugin file +" Language: Julia +" Maintainer: Carlo Baldassi <carlobaldassi@gmail.com> +" Homepage: https://github.com/JuliaEditorSupport/julia-vim +" Last Change: 2014 may 29 +" adapted from upstream 2021 Aug 4 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:save_cpo = &cpo +set cpo-=C + +setlocal include=^\\s*\\%(reload\\\|include\\)\\> +setlocal suffixesadd=.jl +setlocal comments=:# +setlocal commentstring=#\ %s +setlocal cinoptions+=#1 +setlocal define=^\\s*macro\\> +setlocal fo-=t fo+=croql + +let b:julia_vim_loaded = 1 + +let b:undo_ftplugin = "setlocal include< suffixesadd< comments< commentstring<" + \ . " define< fo< shiftwidth< expandtab< indentexpr< indentkeys< cinoptions< completefunc<" + \ . " | unlet! b:julia_vim_loaded" + +" MatchIt plugin support +if exists("loaded_matchit") + let b:match_ignorecase = 0 + + " note: begin_keywords must contain all blocks, in order + " for nested-structures-skipping to work properly + " note: 'mutable struct' and 'struct' are defined separately because + " using \? puts the cursor on 'struct' instead of 'mutable' for some reason + let b:julia_begin_keywords = '\%(\.\s*\|@\)\@<!\<\%(function\|macro\|begin\|mutable\s\+struct\|\%(mutable\s\+\)\@<!struct\|\%(abstract\|primitive\)\s\+type\|let\|do\|\%(bare\)\?module\|quote\|if\|for\|while\|try\)\>' + " note: the following regex not only recognizes macros, but also local/global keywords. + " the purpose is recognizing things like `@inline myfunction()` + " or `global myfunction(...)` etc, for matchit and block movement functionality + let s:macro_regex = '\%(@\%([#(]\@!\S\)\+\|\<\%(local\|global\)\)\s\+' + let s:nomacro = '\%(' . s:macro_regex . '\)\@<!' + let s:yesmacro = s:nomacro . '\%('. s:macro_regex . '\)\+' + let b:julia_begin_keywordsm = '\%(' . s:yesmacro . b:julia_begin_keywords . '\)\|' + \ . '\%(' . s:nomacro . b:julia_begin_keywords . '\)' + let b:julia_end_keywords = '\<end\>' + + " note: this function relies heavily on the syntax file + function! JuliaGetMatchWords() + let [l,c] = [line('.'),col('.')] + let attr = synIDattr(synID(l, c, 1),"name") + let c1 = c + while attr == 'juliaMacro' || expand('<cword>') =~# '\<\%(global\|local\)\>' + normal! W + if line('.') > l || col('.') == c1 + call cursor(l, c) + return '' + endif + let attr = synIDattr(synID(l, col('.'), 1),"name") + let c1 = col('.') + endwhile + call cursor(l, c) + if attr == 'juliaConditional' + return b:julia_begin_keywordsm . ':\<\%(elseif\|else\)\>:' . b:julia_end_keywords + elseif attr =~# '\<\%(juliaRepeat\|juliaRepKeyword\)\>' + return b:julia_begin_keywordsm . ':\<\%(break\|continue\)\>:' . b:julia_end_keywords + elseif attr == 'juliaBlKeyword' + return b:julia_begin_keywordsm . ':' . b:julia_end_keywords + elseif attr == 'juliaException' + return b:julia_begin_keywordsm . ':\<\%(catch\|finally\)\>:' . b:julia_end_keywords + endif + return '\<\>:\<\>' + endfunction + + let b:match_words = 'JuliaGetMatchWords()' + + " we need to skip everything within comments, strings and + " the 'begin' and 'end' keywords when they are used as a range rather than as + " the delimiter of a block + let b:match_skip = 'synIDattr(synID(line("."),col("."),0),"name") =~# ' + \ . '"\\<julia\\%(Comprehension\\%(For\\|If\\)\\|RangeKeyword\\|Comment\\%([LM]\\|Delim\\)\\|\\%([bs]\\|Shell\\|Printf\\|Doc\\)\\?String\\|StringPrefixed\\|DocStringM\\(Raw\\)\\?\\|RegEx\\|SymbolS\\?\\|Dotted\\)\\>"' + + let b:undo_ftplugin = b:undo_ftplugin + \ . " | unlet! b:match_words b:match_skip b:match_ignorecase" + \ . " | unlet! b:julia_begin_keywords b:julia_end_keywords" + \ . " | delfunction JuliaGetMatchWords" + +endif + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim index 5d3e00d033..fce12012b5 100644 --- a/runtime/ftplugin/man.vim +++ b/runtime/ftplugin/man.vim @@ -6,14 +6,6 @@ if exists('b:did_ftplugin') || &filetype !=# 'man' endif let b:did_ftplugin = 1 -let s:pager = !exists('b:man_sect') - -if s:pager - call man#init_pager() -endif - -setlocal noswapfile buftype=nofile bufhidden=hide -setlocal nomodified readonly nomodifiable setlocal noexpandtab tabstop=8 softtabstop=8 shiftwidth=8 setlocal wrap breakindent linebreak @@ -32,11 +24,7 @@ if !exists('g:no_plugin_maps') && !exists('g:no_man_maps') nnoremap <silent> <buffer> k gk nnoremap <silent> <buffer> gO :call man#show_toc()<CR> nnoremap <silent> <buffer> <2-LeftMouse> :Man<CR> - if s:pager - nnoremap <silent> <buffer> <nowait> q :lclose<CR>:q<CR> - else - nnoremap <silent> <buffer> <nowait> q :lclose<CR><C-W>c - endif + nnoremap <silent> <buffer> <nowait> q :lclose<CR><C-W>c endif if get(g:, 'ft_man_folding_enable', 0) diff --git a/runtime/ftplugin/ruby.vim b/runtime/ftplugin/ruby.vim index b4a8eaa0d8..4a476fd8cf 100644 --- a/runtime/ftplugin/ruby.vim +++ b/runtime/ftplugin/ruby.vim @@ -3,7 +3,7 @@ " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Nov 06 +" Last Change: 2020 Feb 13 if (exists("b:did_ftplugin")) finish @@ -112,7 +112,7 @@ else if !exists('g:ruby_default_path') if has("ruby") && has("win32") ruby ::VIM::command( 'let g:ruby_default_path = split("%s",",")' % $:.join(%q{,}) ) - elseif executable('ruby') + elseif executable('ruby') && !empty($HOME) let g:ruby_default_path = s:query_path($HOME) else let g:ruby_default_path = map(split($RUBYLIB,':'), 'v:val ==# "." ? "" : v:val') diff --git a/runtime/indent.vim b/runtime/indent.vim index 12f03648ca..2b64dd44b9 100644 --- a/runtime/indent.vim +++ b/runtime/indent.vim @@ -24,7 +24,8 @@ augroup filetypeindent " When there is a dot it is used to separate filetype names. Thus for " "aaa.bbb" load "indent/aaa.vim" and then "indent/bbb.vim". for name in split(s, '\.') - exe 'runtime! indent/' . name . '.vim' + exe 'runtime! indent/' . name . '.vim' + exe 'runtime! indent/' . name . '.lua' endfor endif endfunc diff --git a/runtime/indent/bzl.vim b/runtime/indent/bzl.vim index 6904bfdedb..cf4cfb5fad 100644 --- a/runtime/indent/bzl.vim +++ b/runtime/indent/bzl.vim @@ -1,7 +1,7 @@ " Vim indent file " Language: Bazel (http://bazel.io) " Maintainer: David Barnett (https://github.com/google/vim-ft-bzl) -" Last Change: 2017 Jun 13 +" Last Change: 2021 Jul 08 if exists('b:did_indent') finish @@ -41,30 +41,41 @@ function GetBzlIndent(lnum) abort if exists('g:pyindent_open_paren') let l:pyindent_open_paren = g:pyindent_open_paren endif - let g:pyindent_nested_paren = 'shiftwidth() * 2' - let g:pyindent_open_paren = 'shiftwidth() * 2' + let g:pyindent_nested_paren = 'shiftwidth()' + let g:pyindent_open_paren = 'shiftwidth()' endif let l:indent = -1 - " Indent inside parens. - " Align with the open paren unless it is at the end of the line. - " E.g. - " open_paren_not_at_EOL(100, - " (200, - " 300), - " 400) - " open_paren_at_EOL( - " 100, 200, 300, 400) call cursor(a:lnum, 1) let [l:par_line, l:par_col] = searchpairpos('(\|{\|\[', '', ')\|}\|\]', 'bW', \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" . \ " synIDattr(synID(line('.'), col('.'), 1), 'name')" . \ " =~ '\\(Comment\\|String\\)$'") if l:par_line > 0 - call cursor(l:par_line, 1) - if l:par_col != col('$') - 1 - let l:indent = l:par_col + " Indent inside parens. + if searchpair('(\|{\|\[', '', ')\|}\|\]', 'W', + \ "line('.') < " . (a:lnum - s:maxoff) . " ? dummy :" . + \ " synIDattr(synID(line('.'), col('.'), 1), 'name')" . + \ " =~ '\\(Comment\\|String\\)$'") && line('.') == a:lnum + " If cursor is at close parens, match indent with open parens. + " E.g. + " foo( + " ) + let l:indent = indent(l:par_line) + else + " Align with the open paren unless it is at the end of the line. + " E.g. + " open_paren_not_at_EOL(100, + " (200, + " 300), + " 400) + " open_paren_at_EOL( + " 100, 200, 300, 400) + call cursor(l:par_line, 1) + if l:par_col != col('$') - 1 + let l:indent = l:par_col + endif endif endif diff --git a/runtime/indent/html.vim b/runtime/indent/html.vim index 7019bd4a82..d4b91f6421 100644 --- a/runtime/indent/html.vim +++ b/runtime/indent/html.vim @@ -1,7 +1,7 @@ " Vim indent script for HTML " Maintainer: Bram Moolenaar " Original Author: Andy Wokula <anwoku@yahoo.de> -" Last Change: 2021 Jan 26 +" Last Change: 2021 Jun 13 " Version: 1.0 "{{{ " Description: HTML indent script with cached state for faster indenting on a " range of lines. @@ -62,7 +62,7 @@ let s:tagname = '\w\+\(-\w\+\)*' " Prefer using buffer-local settings over global settings, so that there can " be defaults for all HTML files and exceptions for specific types of HTML " files. -func! HtmlIndent_CheckUserSettings() +func HtmlIndent_CheckUserSettings() "{{{ let inctags = '' if exists("b:html_indent_inctags") @@ -178,7 +178,7 @@ let s:endtags = [0,0,0,0,0,0,0] " long enough for the highest index "}}} " Add a list of tag names for a pair of <tag> </tag> to "tags". -func! s:AddITags(tags, taglist) +func s:AddITags(tags, taglist) "{{{ for itag in a:taglist let a:tags[itag] = 1 @@ -187,7 +187,7 @@ func! s:AddITags(tags, taglist) endfunc "}}} " Take a list of tag name pairs that are not to be used as tag pairs. -func! s:RemoveITags(tags, taglist) +func s:RemoveITags(tags, taglist) "{{{ for itag in a:taglist let a:tags[itag] = 1 @@ -196,7 +196,7 @@ func! s:RemoveITags(tags, taglist) endfunc "}}} " Add a block tag, that is a tag with a different kind of indenting. -func! s:AddBlockTag(tag, id, ...) +func s:AddBlockTag(tag, id, ...) "{{{ if !(a:id >= 2 && a:id < len(s:endtags)) echoerr 'AddBlockTag ' . a:id @@ -255,7 +255,7 @@ call s:AddBlockTag('<!--[', 6, '![endif]-->') " Return non-zero when "tagname" is an opening tag, not being a block tag, for " which there should be a closing tag. Can be used by scripts that include " HTML indenting. -func! HtmlIndent_IsOpenTag(tagname) +func HtmlIndent_IsOpenTag(tagname) "{{{ if get(s:indent_tags, a:tagname) == 1 return 1 @@ -264,7 +264,7 @@ func! HtmlIndent_IsOpenTag(tagname) endfunc "}}} " Get the value for "tagname", taking care of buffer-local tags. -func! s:get_tag(tagname) +func s:get_tag(tagname) "{{{ let i = get(s:indent_tags, a:tagname) if (i == 1 || i == -1) && get(b:hi_removed_tags, a:tagname) != 0 @@ -277,7 +277,7 @@ func! s:get_tag(tagname) endfunc "}}} " Count the number of start and end tags in "text". -func! s:CountITags(text) +func s:CountITags(text) "{{{ " Store the result in s:curind and s:nextrel. let s:curind = 0 " relative indent steps for current line [unit &sw]: @@ -289,7 +289,7 @@ func! s:CountITags(text) endfunc "}}} " Count the number of start and end tags in text. -func! s:CountTagsAndState(text) +func s:CountTagsAndState(text) "{{{ " Store the result in s:curind and s:nextrel. Update b:hi_newstate.block. let s:curind = 0 " relative indent steps for current line [unit &sw]: @@ -304,7 +304,7 @@ func! s:CountTagsAndState(text) endfunc "}}} " Used by s:CountITags() and s:CountTagsAndState(). -func! s:CheckTag(itag) +func s:CheckTag(itag) "{{{ " Returns an empty string or "SCRIPT". " a:itag can be "tag" or "/tag" or "<!--" or "-->" @@ -338,7 +338,7 @@ func! s:CheckTag(itag) endfunc "}}} " Used by s:CheckTag(). Returns an empty string or "SCRIPT". -func! s:CheckBlockTag(blocktag, ind) +func s:CheckBlockTag(blocktag, ind) "{{{ if a:ind > 0 " a block starts here @@ -366,7 +366,7 @@ func! s:CheckBlockTag(blocktag, ind) endfunc "}}} " Used by s:CheckTag(). -func! s:CheckCustomTag(ctag) +func s:CheckCustomTag(ctag) "{{{ " Returns 1 if ctag is the tag for a custom element, 0 otherwise. " a:ctag can be "tag" or "/tag" or "<!--" or "-->" @@ -396,7 +396,7 @@ func! s:CheckCustomTag(ctag) endfunc "}}} " Return the <script> type: either "javascript" or "" -func! s:GetScriptType(str) +func s:GetScriptType(str) "{{{ if a:str == "" || a:str =~ "java" return "javascript" @@ -407,7 +407,7 @@ endfunc "}}} " Look back in the file, starting at a:lnum - 1, to compute a state for the " start of line a:lnum. Return the new state. -func! s:FreshState(lnum) +func s:FreshState(lnum) "{{{ " A state is to know ALL relevant details about the " lines 1..a:lnum-1, initial calculating (here!) can be slow, but updating is @@ -568,24 +568,29 @@ func! s:FreshState(lnum) endfunc "}}} " Indent inside a <pre> block: Keep indent as-is. -func! s:Alien2() +func s:Alien2() "{{{ return -1 endfunc "}}} " Return the indent inside a <script> block for javascript. -func! s:Alien3() +func s:Alien3() "{{{ let lnum = prevnonblank(v:lnum - 1) while lnum > 1 && getline(lnum) =~ '^\s*/[/*]' " Skip over comments to avoid that cindent() aligns with the <script> tag let lnum = prevnonblank(lnum - 1) endwhile + if lnum < b:hi_indent.blocklnr + " indent for <script> itself + return b:hi_indent.blocktagind + endif if lnum == b:hi_indent.blocklnr " indent for the first line after <script> return eval(b:hi_js1indent) endif if b:hi_indent.scripttype == "javascript" + " indent for further lines return eval(b:hi_js1indent) + GetJavascriptIndent() else return -1 @@ -593,7 +598,7 @@ func! s:Alien3() endfunc "}}} " Return the indent inside a <style> block. -func! s:Alien4() +func s:Alien4() "{{{ if prevnonblank(v:lnum-1) == b:hi_indent.blocklnr " indent for first content line @@ -603,7 +608,7 @@ func! s:Alien4() endfunc "}}} " Indending inside a <style> block. Returns the indent. -func! s:CSSIndent() +func s:CSSIndent() "{{{ " This handles standard CSS and also Closure stylesheets where special lines " start with @. @@ -720,13 +725,13 @@ endfunc "}}} " tag: blah " tag: blah && " tag: blah || -func! s:CssUnfinished(text) +func s:CssUnfinished(text) "{{{ return a:text =~ '\(||\|&&\|:\|\k\)\s*$' endfunc "}}} " Search back for the first unfinished line above "lnum". -func! s:CssFirstUnfinished(lnum, min_lnum) +func s:CssFirstUnfinished(lnum, min_lnum) "{{{ let align_lnum = a:lnum while align_lnum > a:min_lnum && s:CssUnfinished(getline(align_lnum - 1)) @@ -736,7 +741,7 @@ func! s:CssFirstUnfinished(lnum, min_lnum) endfunc "}}} " Find the non-empty line at or before "lnum" that is not a comment. -func! s:CssPrevNonComment(lnum, stopline) +func s:CssPrevNonComment(lnum, stopline) "{{{ " caller starts from a line a:lnum + 1 that is not a comment let lnum = prevnonblank(a:lnum) @@ -761,7 +766,7 @@ func! s:CssPrevNonComment(lnum, stopline) endfunc "}}} " Check the number of {} and () in line "lnum". Return a dict with the counts. -func! HtmlIndent_CountBraces(lnum) +func HtmlIndent_CountBraces(lnum) "{{{ let brs = substitute(getline(a:lnum), '[''"].\{-}[''"]\|/\*.\{-}\*/\|/\*.*$\|[^{}()]', '', 'g') let c_open = 0 @@ -794,7 +799,7 @@ func! HtmlIndent_CountBraces(lnum) endfunc "}}} " Return the indent for a comment: <!-- --> -func! s:Alien5() +func s:Alien5() "{{{ let curtext = getline(v:lnum) if curtext =~ '^\s*\zs-->' @@ -826,7 +831,7 @@ func! s:Alien5() endfunc "}}} " Return the indent for conditional comment: <!--[ ![endif]--> -func! s:Alien6() +func s:Alien6() "{{{ let curtext = getline(v:lnum) if curtext =~ '\s*\zs<!\[endif\]-->' @@ -840,7 +845,7 @@ func! s:Alien6() endfunc "}}} " When the "lnum" line ends in ">" find the line containing the matching "<". -func! HtmlIndent_FindTagStart(lnum) +func HtmlIndent_FindTagStart(lnum) "{{{ " Avoids using the indent of a continuation line. " Moves the cursor. @@ -863,7 +868,7 @@ func! HtmlIndent_FindTagStart(lnum) endfunc "}}} " Find the unclosed start tag from the current cursor position. -func! HtmlIndent_FindStartTag() +func HtmlIndent_FindStartTag() "{{{ " The cursor must be on or before a closing tag. " If found, positions the cursor at the match and returns the line number. @@ -877,7 +882,7 @@ func! HtmlIndent_FindStartTag() endfunc "}}} " Moves the cursor from a "<" to the matching ">". -func! HtmlIndent_FindTagEnd() +func HtmlIndent_FindTagEnd() "{{{ " Call this with the cursor on the "<" of a start tag. " This will move the cursor to the ">" of the matching end tag or, when it's @@ -897,7 +902,7 @@ func! HtmlIndent_FindTagEnd() endfunc "}}} " Indenting inside a start tag. Return the correct indent or -1 if unknown. -func! s:InsideTag(foundHtmlString) +func s:InsideTag(foundHtmlString) "{{{ if a:foundHtmlString " Inside an attribute string. @@ -958,7 +963,7 @@ func! s:InsideTag(foundHtmlString) endfunc "}}} " THE MAIN INDENT FUNCTION. Return the amount of indent for v:lnum. -func! HtmlIndent() +func HtmlIndent() "{{{ if prevnonblank(v:lnum - 1) < 1 " First non-blank line has no indent. diff --git a/runtime/indent/jsonc.vim b/runtime/indent/jsonc.vim new file mode 100644 index 0000000000..bf8e501dd5 --- /dev/null +++ b/runtime/indent/jsonc.vim @@ -0,0 +1,189 @@ +" Vim indent file +" Language: JSONC (JSON with Comments) +" Original Author: Izhak Jakov <izhak724@gmail.com> +" Acknowledgement: Based off of vim-json maintained by Eli Parra <eli@elzr.com> +" https://github.com/elzr/vim-json +" Last Change: 2021-07-01 + +" 0. Initialization {{{1 +" ================= + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nosmartindent + +" Now, set up our indentation expression and keys that trigger it. +setlocal indentexpr=GetJSONCIndent() +setlocal indentkeys=0{,0},0),0[,0],!^F,o,O,e + +" Only define the function once. +if exists("*GetJSONCIndent") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" 1. Variables {{{1 +" ============ + +let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' +" Regex that defines blocks. +let s:block_regex = '\%({\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term + +" 2. Auxiliary Functions {{{1 +" ====================== + +" Check if the character at lnum:col is inside a string. +function s:IsInString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'jsonString' +endfunction + +" Find line above 'lnum' that isn't empty, or in a string. +function s:PrevNonBlankNonString(lnum) + let lnum = prevnonblank(a:lnum) + while lnum > 0 + " If the line isn't empty or in a string, end search. + let line = getline(lnum) + if !(s:IsInString(lnum, 1) && s:IsInString(lnum, strlen(line))) + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return lnum +endfunction + +" Check if line 'lnum' has more opening brackets than closing ones. +function s:LineHasOpeningBrackets(lnum) + let open_0 = 0 + let open_2 = 0 + let open_4 = 0 + let line = getline(a:lnum) + let pos = match(line, '[][(){}]', 0) + while pos != -1 + let idx = stridx('(){}[]', line[pos]) + if idx % 2 == 0 + let open_{idx} = open_{idx} + 1 + else + let open_{idx - 1} = open_{idx - 1} - 1 + endif + let pos = match(line, '[][(){}]', pos + 1) + endwhile + return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) +endfunction + +function s:Match(lnum, regex) + let col = match(getline(a:lnum), a:regex) + 1 + return col > 0 && !s:IsInString(a:lnum, col) ? col : 0 +endfunction + +" 3. GetJSONCIndent Function {{{1 +" ========================= + +function GetJSONCIndent() + if !exists("s:inside_comment") + let s:inside_comment = 0 + endif + + " 3.1. Setup {{{2 + " ---------- + + " Set up variables for restoring position in file. Could use v:lnum here. + let vcol = col('.') + + " 3.2. Work on the current line {{{2 + " ----------------------------- + + + " Get the current line. + let line = getline(v:lnum) + let ind = -1 + if s:inside_comment == 0 + " TODO iterate through all the matches in a line + let col = matchend(line, '\/\*') + if col > 0 && !s:IsInString(v:lnum, col) + let s:inside_comment = 1 + endif + endif + " If we're in the middle of a comment + if s:inside_comment == 1 + let col = matchend(line, '\*\/') + if col > 0 && !s:IsInString(v:lnum, col) + let s:inside_comment = 0 + endif + return ind + endif + if line =~ '^\s*//' + return ind + endif + + " If we got a closing bracket on an empty line, find its match and indent + " according to it. + let col = matchend(line, '^\s*[]}]') + + if col > 0 && !s:IsInString(v:lnum, col) + call cursor(v:lnum, col) + let bs = strpart('{}[]', stridx('}]', line[col - 1]) * 2, 2) + + let pairstart = escape(bs[0], '[') + let pairend = escape(bs[1], ']') + let pairline = searchpair(pairstart, '', pairend, 'bW') + + if pairline > 0 + let ind = indent(pairline) + else + let ind = virtcol('.') - 1 + endif + + return ind + endif + + " If we are in a multi-line string, don't do anything to it. + if s:IsInString(v:lnum, matchend(line, '^\s*') + 1) + return indent('.') + endif + + " 3.3. Work on the previous line. {{{2 + " ------------------------------- + + let lnum = prevnonblank(v:lnum - 1) + + if lnum == 0 + return 0 + endif + + " Set up variables for current line. + let line = getline(lnum) + let ind = indent(lnum) + + " If the previous line ended with a block opening, add a level of indent. + " if s:Match(lnum, s:block_regex) + " return indent(lnum) + shiftwidth() + " endif + + " If the previous line contained an opening bracket, and we are still in it, + " add indent depending on the bracket type. + if line =~ '[[({]' + let counts = s:LineHasOpeningBrackets(lnum) + if counts[0] == '1' || counts[1] == '1' || counts[2] == '1' + return ind + shiftwidth() + else + call cursor(v:lnum, vcol) + end + endif + + " }}}2 + + return ind +endfunction + +" }}}1 + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim:set sw=2 sts=2 ts=8 noet: diff --git a/runtime/indent/julia.vim b/runtime/indent/julia.vim new file mode 100644 index 0000000000..a90cff49e4 --- /dev/null +++ b/runtime/indent/julia.vim @@ -0,0 +1,491 @@ +" Vim indent file +" Language: Julia +" Maintainer: Carlo Baldassi <carlobaldassi@gmail.com> +" Homepage: https://github.com/JuliaEditorSupport/julia-vim +" Last Change: 2016 jun 16 +" Notes: originally based on Bram Molenaar's indent file for vim + +setlocal autoindent + +setlocal indentexpr=GetJuliaIndent() +setlocal indentkeys+==end,=else,=catch,=finally,),],} +setlocal indentkeys-=0# +setlocal indentkeys-=: +setlocal indentkeys-=0{ +setlocal indentkeys-=0} +setlocal nosmartindent + +" Only define the function once. +if exists("*GetJuliaIndent") + finish +endif + +let s:skipPatternsBasic = '\<julia\%(Comment\%([LM]\|Delim\)\)\>' +let s:skipPatterns = '\<julia\%(Comprehension\%(For\|If\)\|RangeKeyword\|Comment\%([LM]\|Delim\)\|\%([bs]\|Shell\|Printf\|Doc\)\?String\|StringPrefixed\|DocStringM\(Raw\)\?\|RegEx\|SymbolS\?\|Macro\|Dotted\)\>' + +function JuliaMatch(lnum, str, regex, st, ...) + let s = a:st + let e = a:0 > 0 ? a:1 : -1 + let basic_skip = a:0 > 1 ? a:2 : 'all' + let skip = basic_skip ==# 'basic' ? s:skipPatternsBasic : s:skipPatterns + while 1 + let f = match(a:str, '\C' . a:regex, s) + if e >= 0 && f >= e + return -1 + endif + if f >= 0 + let attr = synIDattr(synID(a:lnum,f+1,1),"name") + let attrT = synIDattr(synID(a:lnum,f+1,0),"name") + if attr =~# skip || attrT =~# skip + let s = f+1 + continue + endif + endif + break + endwhile + return f +endfunction + +function GetJuliaNestingStruct(lnum, ...) + " Auxiliary function to inspect the block structure of a line + let line = getline(a:lnum) + let s = a:0 > 0 ? a:1 : 0 + let e = a:0 > 1 ? a:2 : -1 + let blocks_stack = [] + let num_closed_blocks = 0 + while 1 + let fb = JuliaMatch(a:lnum, line, '\<\%(if\|else\%(if\)\?\|while\|for\|try\|catch\|finally\|\%(staged\)\?function\|macro\|begin\|mutable\s\+struct\|\%(mutable\s\+\)\@<!struct\|\%(abstract\|primitive\)\s\+type\|let\|\%(bare\)\?module\|quote\|do\)\>', s, e) + let fe = JuliaMatch(a:lnum, line, '\<end\>', s, e) + + if fb < 0 && fe < 0 + " No blocks found + break + end + + if fb >= 0 && (fb < fe || fe < 0) + " The first occurrence is an opening block keyword + " Note: some keywords (elseif,else,catch,finally) are both + " closing blocks and opening new ones + + let i = JuliaMatch(a:lnum, line, '\<if\>', s) + if i >= 0 && i == fb + let s = i+1 + call add(blocks_stack, 'if') + continue + endif + let i = JuliaMatch(a:lnum, line, '\<elseif\>', s) + if i >= 0 && i == fb + let s = i+1 + if len(blocks_stack) > 0 && blocks_stack[-1] == 'if' + let blocks_stack[-1] = 'elseif' + elseif (len(blocks_stack) > 0 && blocks_stack[-1] != 'elseif') || len(blocks_stack) == 0 + call add(blocks_stack, 'elseif') + let num_closed_blocks += 1 + endif + continue + endif + let i = JuliaMatch(a:lnum, line, '\<else\>', s) + if i >= 0 && i == fb + let s = i+1 + if len(blocks_stack) > 0 && blocks_stack[-1] =~# '\<\%(else\)\=if\>' + let blocks_stack[-1] = 'else' + else + call add(blocks_stack, 'else') + let num_closed_blocks += 1 + endif + continue + endif + + let i = JuliaMatch(a:lnum, line, '\<try\>', s) + if i >= 0 && i == fb + let s = i+1 + call add(blocks_stack, 'try') + continue + endif + let i = JuliaMatch(a:lnum, line, '\<catch\>', s) + if i >= 0 && i == fb + let s = i+1 + if len(blocks_stack) > 0 && blocks_stack[-1] == 'try' + let blocks_stack[-1] = 'catch' + else + call add(blocks_stack, 'catch') + let num_closed_blocks += 1 + endif + continue + endif + let i = JuliaMatch(a:lnum, line, '\<finally\>', s) + if i >= 0 && i == fb + let s = i+1 + if len(blocks_stack) > 0 && (blocks_stack[-1] == 'try' || blocks_stack[-1] == 'catch') + let blocks_stack[-1] = 'finally' + else + call add(blocks_stack, 'finally') + let num_closed_blocks += 1 + endif + continue + endif + + let i = JuliaMatch(a:lnum, line, '\<\%(bare\)\?module\>', s) + if i >= 0 && i == fb + let s = i+1 + if i == 0 + call add(blocks_stack, 'col1module') + else + call add(blocks_stack, 'other') + endif + continue + endif + + let i = JuliaMatch(a:lnum, line, '\<\%(while\|for\|function\|macro\|begin\|\%(mutable\s\+\)\?struct\|\%(abstract\|primitive\)\s\+type\|let\|quote\|do\)\>', s) + if i >= 0 && i == fb + if match(line, '\C\<\%(mutable\|abstract\|primitive\)', i) != -1 + let s = i+11 + else + let s = i+1 + endif + call add(blocks_stack, 'other') + continue + endif + + " Note: it should be impossible to get here + break + + else + " The first occurrence is an 'end' + + let s = fe+1 + if len(blocks_stack) == 0 + let num_closed_blocks += 1 + else + call remove(blocks_stack, -1) + endif + continue + + endif + + " Note: it should be impossible to get here + break + endwhile + let num_open_blocks = len(blocks_stack) - count(blocks_stack, 'col1module') + return [num_open_blocks, num_closed_blocks] +endfunction + +function GetJuliaNestingBrackets(lnum, c) + " Auxiliary function to inspect the brackets structure of a line + let line = getline(a:lnum)[0 : (a:c - 1)] + let s = 0 + let brackets_stack = [] + let last_closed_bracket = -1 + while 1 + let fb = JuliaMatch(a:lnum, line, '[([{]', s) + let fe = JuliaMatch(a:lnum, line, '[])}]', s) + + if fb < 0 && fe < 0 + " No brackets found + break + end + + if fb >= 0 && (fb < fe || fe < 0) + " The first occurrence is an opening bracket + + let i = JuliaMatch(a:lnum, line, '(', s) + if i >= 0 && i == fb + let s = i+1 + call add(brackets_stack, ['par',i]) + continue + endif + + let i = JuliaMatch(a:lnum, line, '\[', s) + if i >= 0 && i == fb + let s = i+1 + call add(brackets_stack, ['sqbra',i]) + continue + endif + + let i = JuliaMatch(a:lnum, line, '{', s) + if i >= 0 && i == fb + let s = i+1 + call add(brackets_stack, ['curbra',i]) + continue + endif + + " Note: it should be impossible to get here + break + + else + " The first occurrence is a closing bracket + + let i = JuliaMatch(a:lnum, line, ')', s) + if i >= 0 && i == fe + let s = i+1 + if len(brackets_stack) > 0 && brackets_stack[-1][0] == 'par' + call remove(brackets_stack, -1) + else + let last_closed_bracket = i + 1 + endif + continue + endif + + let i = JuliaMatch(a:lnum, line, ']', s) + if i >= 0 && i == fe + let s = i+1 + if len(brackets_stack) > 0 && brackets_stack[-1][0] == 'sqbra' + call remove(brackets_stack, -1) + else + let last_closed_bracket = i + 1 + endif + continue + endif + + let i = JuliaMatch(a:lnum, line, '}', s) + if i >= 0 && i == fe + let s = i+1 + if len(brackets_stack) > 0 && brackets_stack[-1][0] == 'curbra' + call remove(brackets_stack, -1) + else + let last_closed_bracket = i + 1 + endif + continue + endif + + " Note: it should be impossible to get here + break + + endif + + " Note: it should be impossible to get here + break + endwhile + let first_open_bracket = -1 + let last_open_bracket = -1 + let infuncargs = 0 + if len(brackets_stack) > 0 + let first_open_bracket = brackets_stack[0][1] + let last_open_bracket = brackets_stack[-1][1] + if brackets_stack[-1][0] == 'par' && IsFunctionArgPar(a:lnum, last_open_bracket+1) + let infuncargs = 1 + endif + endif + return [first_open_bracket, last_open_bracket, last_closed_bracket, infuncargs] +endfunction + +let s:bracketBlocks = '\<julia\%(\%(\%(Printf\)\?Par\|SqBra\%(Idx\)\?\|CurBra\)Block\|ParBlockInRange\|StringVars\%(Par\|SqBra\|CurBra\)\|Dollar\%(Par\|SqBra\)\|QuotedParBlockS\?\)\>' + +function IsInBrackets(lnum, c) + let stack = map(synstack(a:lnum, a:c), 'synIDattr(v:val, "name")') + call filter(stack, 'v:val =~# s:bracketBlocks') + return len(stack) > 0 +endfunction + +function IsInDocString(lnum) + let stack = map(synstack(a:lnum, 1), 'synIDattr(v:val, "name")') + call filter(stack, 'v:val =~# "\\<juliaDocString\\(Delim\\|M\\\(Raw\\)\\?\\)\\?\\>"') + return len(stack) > 0 +endfunction + +function IsInContinuationImportLine(lnum) + let stack = map(synstack(a:lnum, 1), 'synIDattr(v:val, "name")') + call filter(stack, 'v:val =~# "\\<juliaImportLine\\>"') + if len(stack) == 0 + return 0 + endif + return JuliaMatch(a:lnum, getline(a:lnum), '\<\%(import\|using\|export\)\>', indent(a:lnum)) == -1 +endfunction + +function IsFunctionArgPar(lnum, c) + if a:c == 0 + return 0 + endif + let stack = map(synstack(a:lnum, a:c-1), 'synIDattr(v:val, "name")') + return len(stack) >= 2 && stack[-2] ==# 'juliaFunctionDef' +endfunction + +function JumpToMatch(lnum, last_closed_bracket) + " we use the % command to skip back (tries to ues matchit if possible, + " otherwise resorts to vim's default, which is buggy but better than + " nothing) + call cursor(a:lnum, a:last_closed_bracket) + let percmap = maparg("%", "n") + if exists("g:loaded_matchit") && percmap =~# 'Match\%(it\|_wrapper\)' + normal % + else + normal! % + end +endfunction + +" Auxiliary function to find a line which does not start in the middle of a +" multiline bracketed expression, to be used as reference for block +" indentation. +function LastBlockIndent(lnum) + let lnum = a:lnum + let ind = 0 + while lnum > 0 + let ind = indent(lnum) + if ind == 0 + return [lnum, 0] + endif + if !IsInBrackets(lnum, 1) + break + endif + let lnum = prevnonblank(lnum - 1) + endwhile + return [max([lnum,1]), ind] +endfunction + +function GetJuliaIndent() + " Do not alter doctrings indentation + if IsInDocString(v:lnum) + return -1 + endif + + " Find a non-blank line above the current line. + let lnum = prevnonblank(v:lnum - 1) + + " At the start of the file use zero indent. + if lnum == 0 + return 0 + endif + + let ind = -1 + let st = -1 + let lim = -1 + + " Multiline bracketed expressions take precedence + let align_brackets = get(g:, "julia_indent_align_brackets", 1) + let align_funcargs = get(g:, "julia_indent_align_funcargs", 0) + let c = len(getline(lnum)) + 1 + while IsInBrackets(lnum, c) + let [first_open_bracket, last_open_bracket, last_closed_bracket, infuncargs] = GetJuliaNestingBrackets(lnum, c) + + " First scenario: the previous line has a hanging open bracket: + " set the indentation to match the opening bracket (plus an extra space) + " unless we're in a function arguments list or alignment is disabled, in + " which case we just add an extra indent + if last_open_bracket != -1 + if (!infuncargs && align_brackets) || (infuncargs && align_funcargs) + let st = last_open_bracket + let ind = virtcol([lnum, st + 1]) + else + let ind = indent(lnum) + shiftwidth() + endif + + " Second scenario: some multiline bracketed expression was closed in the + " previous line. But since we know we are still in a bracketed expression, + " we need to find the line where the bracket was opened + elseif last_closed_bracket != -1 + call JumpToMatch(lnum, last_closed_bracket) + if line(".") == lnum + " something wrong here, give up + let ind = indent(lnum) + else + let lnum = line(".") + let c = col(".") - 1 + if c == 0 + " uhm, give up + let ind = 0 + else + " we skipped a bracket set, keep searching for an opening bracket + let lim = c + continue + endif + endif + + " Third scenario: nothing special: keep the indentation + else + let ind = indent(lnum) + endif + + " Does the current line start with a closing bracket? Then depending on + " the situation we align it with the opening one, or we let the rest of + " the code figure it out (the case in which we're closing a function + " argument list is special-cased) + if JuliaMatch(v:lnum, getline(v:lnum), '[])}]', indent(v:lnum)) == indent(v:lnum) && ind > 0 + if !align_brackets && !align_funcargs + call JumpToMatch(v:lnum, indent(v:lnum)) + return indent(line(".")) + elseif (align_brackets && getline(v:lnum)[indent(v:lnum)] != ')') || align_funcargs + return ind - 1 + else " must be a ')' and align_brackets==1 and align_funcargs==0 + call JumpToMatch(v:lnum, indent(v:lnum)) + if IsFunctionArgPar(line("."), col(".")) + let ind = -1 + else + return ind - 1 + endif + endif + endif + + break + endwhile + + if ind == -1 + " We are not in a multiline bracketed expression. Thus we look for a + " previous line to use as a reference + let [lnum,ind] = LastBlockIndent(lnum) + let c = len(getline(lnum)) + 1 + if IsInBrackets(lnum, c) + let [first_open_bracket, last_open_bracket, last_closed_bracket, infuncargs] = GetJuliaNestingBrackets(lnum, c) + let lim = first_open_bracket + endif + end + + " Analyse the reference line + let [num_open_blocks, num_closed_blocks] = GetJuliaNestingStruct(lnum, st, lim) + " Increase indentation for each newly opened block in the reference line + let ind += shiftwidth() * num_open_blocks + + " Analyse the current line + let [num_open_blocks, num_closed_blocks] = GetJuliaNestingStruct(v:lnum) + " Decrease indentation for each closed block in the current line + let ind -= shiftwidth() * num_closed_blocks + + " Additional special case: multiline import/using/export statements + + let prevline = getline(lnum) + " Are we in a multiline import/using/export statement, right below the + " opening line? + if IsInContinuationImportLine(v:lnum) && !IsInContinuationImportLine(lnum) + if get(g:, 'julia_indent_align_import', 1) + " if the opening line has a colon followed by non-comments, use it as + " reference point + let cind = JuliaMatch(lnum, prevline, ':', indent(lnum), lim) + if cind >= 0 + let nonwhiteind = JuliaMatch(lnum, prevline, '\S', cind+1, -1, 'basic') + if nonwhiteind >= 0 + " return match(prevline, '\S', cind+1) " a bit overkill... + return cind + 2 + endif + else + " if the opening line is not a naked import/using/export statement, use + " it as reference + let iind = JuliaMatch(lnum, prevline, '\<import\|using\|export\>', indent(lnum), lim) + if iind >= 0 + " assuming whitespace after using... so no `using(XYZ)` please! + let nonwhiteind = JuliaMatch(lnum, prevline, '\S', iind+6, -1, 'basic') + if nonwhiteind >= 0 + return match(prevline, '\S', iind+6) + endif + endif + endif + endif + let ind += shiftwidth() + + " Or did we just close a multiline import/using/export statement? + elseif !IsInContinuationImportLine(v:lnum) && IsInContinuationImportLine(lnum) + " find the starting line of the statement + let ilnum = 0 + for iln in range(lnum-1, 1, -1) + if !IsInContinuationImportLine(iln) + let ilnum = iln + break + endif + endfor + if ilnum == 0 + " something went horribly wrong, give up + let ind = indent(lnum) + endif + let ind = indent(ilnum) + endif + + return ind +endfunction diff --git a/runtime/indent/pascal.vim b/runtime/indent/pascal.vim index c7955d669b..1f39fd1cad 100644 --- a/runtime/indent/pascal.vim +++ b/runtime/indent/pascal.vim @@ -2,7 +2,7 @@ " Language: Pascal " Maintainer: Neil Carter <n.carter@swansea.ac.uk> " Created: 2004 Jul 13 -" Last Change: 2017 Jun 13 +" Last Change: 2021 Jul 01 " " This is version 2.0, a complete rewrite. " @@ -20,6 +20,8 @@ setlocal indentkeys+==end;,==const,==type,==var,==begin,==repeat,==until,==for setlocal indentkeys+==program,==function,==procedure,==object,==private setlocal indentkeys+==record,==if,==else,==case +let b:undo_indent = "setl indentkeys< indentexpr<" + if exists("*GetPascalIndent") finish endif diff --git a/runtime/indent/python.vim b/runtime/indent/python.vim index f9236e63c7..307b7f656b 100644 --- a/runtime/indent/python.vim +++ b/runtime/indent/python.vim @@ -2,7 +2,7 @@ " Language: Python " Maintainer: Bram Moolenaar <Bram@vim.org> " Original Author: David Bustos <bustos@caltech.edu> -" Last Change: 2019 Feb 21 +" Last Change: 2021 May 26 " Only load this indent file when no other was loaded. if exists("b:did_indent") @@ -191,7 +191,7 @@ function GetPythonIndent(lnum) if getline(a:lnum) =~ '^\s*\(elif\|else\)\>' " Unless the previous line was a one-liner - if getline(plnumstart) =~ '^\s*\(for\|if\|try\)\>' + if getline(plnumstart) =~ '^\s*\(for\|if\|elif\|try\)\>' return plindent endif diff --git a/runtime/indent/ruby.vim b/runtime/indent/ruby.vim index 657aa763b1..2a267fdab3 100644 --- a/runtime/indent/ruby.vim +++ b/runtime/indent/ruby.vim @@ -4,6 +4,7 @@ " Previous Maintainer: Nikolai Weibull <now at bitwi.se> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2021 Feb 03 " 0. Initialization {{{1 " ================= diff --git a/runtime/indent/testdir/xml.in b/runtime/indent/testdir/xml.in index b6333340e2..88ad51e484 100644 --- a/runtime/indent/testdir/xml.in +++ b/runtime/indent/testdir/xml.in @@ -15,7 +15,7 @@ text comment </tag1> <!-- text comment -end coment --> +end comment --> </tag0> <!-- END_INDENT --> diff --git a/runtime/indent/testdir/xml.ok b/runtime/indent/testdir/xml.ok index cfdf701c11..d5e2289cb3 100644 --- a/runtime/indent/testdir/xml.ok +++ b/runtime/indent/testdir/xml.ok @@ -15,7 +15,7 @@ </tag1> <!-- text comment - end coment --> + end comment --> </tag0> <!-- END_INDENT --> diff --git a/runtime/lua/vim/F.lua b/runtime/lua/vim/F.lua index 7925ff6e44..1a258546a5 100644 --- a/runtime/lua/vim/F.lua +++ b/runtime/lua/vim/F.lua @@ -2,8 +2,8 @@ local F = {} --- Returns {a} if it is not nil, otherwise returns {b}. --- ---@param a ---@param b +---@param a +---@param b function F.if_nil(a, b) if a == nil then return b end return a @@ -27,5 +27,14 @@ function F.nil_wrap(fn) end end +--- like {...} except preserve the lenght explicitly +function F.pack_len(...) + return {n=select('#', ...), ...} +end + +--- like unpack() but use the length set by F.pack_len if present +function F.unpack_len(t) + return unpack(t, 1, t.n) +end return F diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 02d1154df4..f7d47c1030 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -209,12 +209,17 @@ local function get_scoped_option(k, set_type) end if is_window_option(info) then - if vim.api.nvim_get_option_info(k).was_set then - local was_set, value = pcall(a.nvim_win_get_option, 0, k) - if was_set then return value end + local ok, value = pcall(a.nvim_win_get_option, 0, k) + if ok then + return value end - return a.nvim_get_option(k) + local global_ok, global_val = pcall(a.nvim_get_option, k) + if global_ok then + return global_val + end + + error("win_get: This should never happen. File an issue and tag @tjdevries") end error("This fallback case should not be possible. " .. k) @@ -262,7 +267,7 @@ local key_value_options = { winhl = true, } ----@class OptionType +---@class OptionTypes --- Option Type Enum local OptionTypes = setmetatable({ BOOLEAN = 0, @@ -306,6 +311,28 @@ local get_option_type = function(name, info) end +-- Check whether the OptionTypes is allowed for vim.opt +-- If it does not match, throw an error which indicates which option causes the error. +local function assert_valid_value(name, value, types) + local type_of_value = type(value) + for _, valid_type in ipairs(types) do + if valid_type == type_of_value then + return + end + end + + error(string.format("Invalid option type '%s' for '%s', should be %s", type_of_value, name, table.concat(types, " or "))) +end + +local valid_types = { + [OptionTypes.BOOLEAN] = { "boolean" }, + [OptionTypes.NUMBER] = { "number" }, + [OptionTypes.STRING] = { "string" }, + [OptionTypes.SET] = { "string", "table" }, + [OptionTypes.ARRAY] = { "string", "table" }, + [OptionTypes.MAP] = { "string", "table" }, +} + --- Convert a lua value to a vimoption_T value local convert_value_to_vim = (function() -- Map of functions to take a Lua style value and convert to vimoption_T style value. @@ -315,24 +342,41 @@ local convert_value_to_vim = (function() [OptionTypes.NUMBER] = function(_, value) return value end, [OptionTypes.STRING] = function(_, value) return value end, - [OptionTypes.SET] = function(_, value) + [OptionTypes.SET] = function(info, value) if type(value) == "string" then return value end - local result = '' - for k in pairs(value) do - result = result .. k - end - return result + if info.flaglist and info.commalist then + local keys = {} + for k, v in pairs(value) do + if v then + table.insert(keys, k) + end + end + + table.sort(keys) + return table.concat(keys, ",") + else + local result = '' + for k, v in pairs(value) do + if v then + result = result .. k + end + end + + return result + end end, - [OptionTypes.ARRAY] = function(_, value) + [OptionTypes.ARRAY] = function(info, value) if type(value) == "string" then return value end - return table.concat(remove_duplicate_values(value), ",") + if not info.allows_duplicates then + value = remove_duplicate_values(value) + end + return table.concat(value, ",") end, [OptionTypes.MAP] = function(_, value) if type(value) == "string" then return value end - if type(value) == "function" then error(debug.traceback("asdf")) end local result = {} for opt_key, opt_value in pairs(value) do @@ -345,7 +389,10 @@ local convert_value_to_vim = (function() } return function(name, info, value) - return to_vim_value[get_option_type(name, info)](info, value) + local option_type = get_option_type(name, info) + assert_valid_value(name, value, valid_types[option_type]) + + return to_vim_value[option_type](info, value) end end)() @@ -358,26 +405,82 @@ local convert_value_to_lua = (function() [OptionTypes.NUMBER] = function(_, value) return value end, [OptionTypes.STRING] = function(_, value) return value end, - [OptionTypes.ARRAY] = function(_, value) + [OptionTypes.ARRAY] = function(info, value) if type(value) == "table" then - value = remove_duplicate_values(value) + if not info.allows_duplicates then + value = remove_duplicate_values(value) + end + return value end + -- Empty strings mean that there is nothing there, + -- so empty table should be returned. + if value == '' then + return {} + end + + -- Handles unescaped commas in a list. + if string.find(value, ",,,") then + local comma_split = vim.split(value, ",,,") + local left = comma_split[1] + local right = comma_split[2] + + local result = {} + vim.list_extend(result, vim.split(left, ",")) + table.insert(result, ",") + vim.list_extend(result, vim.split(right, ",")) + + table.sort(result) + + return result + end + + if string.find(value, ",^,,", 1, true) then + local comma_split = vim.split(value, ",^,,", true) + local left = comma_split[1] + local right = comma_split[2] + + local result = {} + vim.list_extend(result, vim.split(left, ",")) + table.insert(result, "^,") + vim.list_extend(result, vim.split(right, ",")) + + table.sort(result) + + return result + end + return vim.split(value, ",") end, [OptionTypes.SET] = function(info, value) if type(value) == "table" then return value end + -- Empty strings mean that there is nothing there, + -- so empty table should be returned. + if value == '' then + return {} + end + assert(info.flaglist, "That is the only one I know how to handle") - local result = {} - for i = 1, #value do - result[value:sub(i, i)] = true - end + if info.flaglist and info.commalist then + local split_value = vim.split(value, ",") + local result = {} + for _, v in ipairs(split_value) do + result[v] = true + end - return result + return result + else + local result = {} + for i = 1, #value do + result[value:sub(i, i)] = true + end + + return result + end end, [OptionTypes.MAP] = function(info, raw_value) @@ -391,7 +494,6 @@ local convert_value_to_lua = (function() for _, key_value_str in ipairs(comma_split) do local key, value = unpack(vim.split(key_value_str, ":")) key = vim.trim(key) - value = vim.trim(value) result[key] = value end diff --git a/runtime/lua/vim/highlight.lua b/runtime/lua/vim/highlight.lua index 0012dce081..236f3165f2 100644 --- a/runtime/lua/vim/highlight.lua +++ b/runtime/lua/vim/highlight.lua @@ -2,7 +2,7 @@ local api = vim.api local highlight = {} ---@private +---@private function highlight.create(higroup, hi_info, default) local options = {} -- TODO: Add validation @@ -12,7 +12,7 @@ function highlight.create(higroup, hi_info, default) vim.cmd(string.format([[highlight %s %s %s]], default and "default" or "", higroup, table.concat(options, " "))) end ---@private +---@private function highlight.link(higroup, link_to, force) vim.cmd(string.format([[highlight%s link %s %s]], force and "!" or " default", higroup, link_to)) end @@ -20,11 +20,11 @@ end --- Highlight range between two positions --- ---@param bufnr number of buffer to apply highlighting to ---@param ns namespace to add highlight to ---@param higroup highlight group to use for highlighting ---@param rtype type of range (:help setreg, default charwise) ---@param inclusive boolean indicating whether the range is end-inclusive (default false) +---@param bufnr number of buffer to apply highlighting to +---@param ns namespace to add highlight to +---@param higroup highlight group to use for highlighting +---@param rtype type of range (:help setreg, default charwise) +---@param inclusive boolean indicating whether the range is end-inclusive (default false) function highlight.range(bufnr, ns, higroup, start, finish, rtype, inclusive) rtype = rtype or 'v' inclusive = inclusive or false @@ -85,7 +85,11 @@ function highlight.on_yank(opts) highlight.range(bufnr, yank_ns, higroup, pos1, pos2, event.regtype, event.inclusive) vim.defer_fn( - function() api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) end, + function() + if api.nvim_buf_is_valid(bufnr) then + api.nvim_buf_clear_namespace(bufnr, yank_ns, 0, -1) + end + end, timeout ) end diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5a606188dd..0fdd43e210 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -20,6 +20,7 @@ local lsp = { buf = require'vim.lsp.buf'; diagnostic = require'vim.lsp.diagnostic'; + codelens = require'vim.lsp.codelens'; util = util; -- Allow raw RPC access. @@ -54,21 +55,21 @@ lsp._request_name_to_capability = { -- TODO improve handling of scratch buffers with LSP attached. ---@private +---@private --- Concatenates and writes a list of strings to the Vim error buffer. --- ---@param {...} (List of strings) List to write to the buffer +---@param {...} (List of strings) List to write to the buffer local function err_message(...) nvim_err_writeln(table.concat(vim.tbl_flatten{...})) nvim_command("redraw") end ---@private +---@private --- Returns the buffer number for the given {bufnr}. --- ---@param bufnr (number) Buffer number to resolve. Defaults to the current +---@param bufnr (number) Buffer number to resolve. Defaults to the current ---buffer if not given. ---@returns bufnr (number) Number of requested buffer +---@returns bufnr (number) Number of requested buffer local function resolve_bufnr(bufnr) validate { bufnr = { bufnr, 'n', true } } if bufnr == nil or bufnr == 0 then @@ -77,21 +78,21 @@ local function resolve_bufnr(bufnr) return bufnr end ---@private +---@private --- Called by the client when trying to call a method that's not --- supported in any of the servers registered for the current buffer. ---@param method (string) name of the method +---@param method (string) name of the method function lsp._unsupported_method(method) local msg = string.format("method %s is not supported by any of the servers registered for the current buffer", method) log.warn(msg) return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg) end ---@private +---@private --- Checks whether a given path is a directory. --- ---@param filename (string) path to check ---@returns true if {filename} exists and is a directory, false otherwise +---@param filename (string) path to check +---@returns true if {filename} exists and is a directory, false otherwise local function is_dir(filename) validate{filename={filename,'s'}} local stat = uv.fs_stat(filename) @@ -107,10 +108,10 @@ local valid_encodings = { } local client_index = 0 ---@private +---@private --- Returns a new, unused client id. --- ---@returns (number) client id +---@returns (number) client id local function next_client_id() client_index = client_index + 1 return client_index @@ -123,11 +124,11 @@ local uninitialized_clients = {} -- Tracks all buffers attached to a client. local all_client_active_buffers = {} ---@private +---@private --- Invokes a function for each LSP client attached to the buffer {bufnr}. --- ---@param bufnr (Number) of buffer ---@param fn (function({client}, {client_id}, {bufnr}) Function to run on +---@param bufnr (Number) of buffer +---@param fn (function({client}, {client_id}, {bufnr}) Function to run on ---each client attached to that buffer. local function for_each_buffer_client(bufnr, fn) validate { @@ -153,11 +154,11 @@ lsp.client_errors = tbl_extend("error", lsp_rpc.client_errors, vim.tbl_add_rever ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1; }) ---@private +---@private --- Normalizes {encoding} to valid LSP encoding names. --- ---@param encoding (string) Encoding to normalize ---@returns (string) normalized encoding name +---@param encoding (string) Encoding to normalize +---@returns (string) normalized encoding name local function validate_encoding(encoding) validate { encoding = { encoding, 's' }; @@ -166,13 +167,13 @@ local function validate_encoding(encoding) or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding)) end ---@internal +---@internal --- Parses a command invocation into the command itself and its args. If there --- are no arguments, an empty table is returned as the second argument. --- ---@param input (List) ---@returns (string) the command ---@returns (list of strings) its arguments +---@param input (List) +---@returns (string) the command +---@returns (list of strings) its arguments function lsp._cmd_parts(input) vim.validate{cmd={ input, @@ -191,12 +192,12 @@ function lsp._cmd_parts(input) return cmd, cmd_args end ---@private +---@private --- Augments a validator function with support for optional (nil) values. --- ---@param fn (function(v)) The original validator function; should return a +---@param fn (function(v)) The original validator function; should return a ---bool. ---@returns (function(v)) The augmented function. Also returns true if {v} is +---@returns (function(v)) The augmented function. Also returns true if {v} is ---`nil`. local function optional_validator(fn) return function(v) @@ -204,20 +205,20 @@ local function optional_validator(fn) end end ---@private +---@private --- Validates a client configuration as given to |vim.lsp.start_client()|. --- ---@param config (table) ---@returns (table) "Cleaned" config, containing only the command, its +---@param config (table) +---@returns (table) "Cleaned" config, containing only the command, its ---arguments, and a valid encoding. --- ---@see |vim.lsp.start_client()| +---@see |vim.lsp.start_client()| local function validate_client_config(config) validate { config = { config, 't' }; } validate { - root_dir = { config.root_dir, is_dir, "directory" }; + root_dir = { config.root_dir, optional_validator(is_dir), "directory" }; handlers = { config.handlers, "t", true }; capabilities = { config.capabilities, "t", true }; cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" }; @@ -252,11 +253,11 @@ local function validate_client_config(config) } end ---@private +---@private --- Returns full text of buffer {bufnr} as a string. --- ---@param bufnr (number) Buffer handle, or 0 for current. ---@returns Buffer text as string. +---@param bufnr (number) Buffer handle, or 0 for current. +---@returns Buffer text as string. local function buf_get_full_text(bufnr) local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), '\n') if nvim_buf_get_option(bufnr, 'eol') then @@ -265,14 +266,14 @@ local function buf_get_full_text(bufnr) return text end ---@private +---@private --- Memoizes a function. On first run, the function return value is saved and --- immediately returned on subsequent runs. If the function returns a multival, --- only the first returned value will be memoized and returned. The function will only be run once, --- even if it has side-effects. --- ---@param fn (function) Function to run ---@returns (function) Memoized function +---@param fn (function) Function to run +---@returns (function) Memoized function local function once(fn) local value local ran = false @@ -288,6 +289,7 @@ end local changetracking = {} do + --@private --- client_id → state --- --- state @@ -379,7 +381,7 @@ do end state.pending_change = function() state.pending_change = nil - if client.is_stopped() then + if client.is_stopped() or not vim.api.nvim_buf_is_valid(bufnr) then return end local contentChanges @@ -424,11 +426,11 @@ do end ---@private +---@private --- Default handler for the 'textDocument/didOpen' LSP notification. --- ---@param bufnr (Number) Number of the buffer, or 0 for current ---@param client Client object +---@param bufnr (Number) Number of the buffer, or 0 for current +---@param client Client object local function text_document_did_open_handler(bufnr, client) changetracking.init(client, bufnr) if not client.resolved_capabilities.text_document_open_close then @@ -452,15 +454,7 @@ local function text_document_did_open_handler(bufnr, client) -- Next chance we get, we should re-do the diagnostics vim.schedule(function() - vim.lsp.handlers["textDocument/publishDiagnostics"]( - nil, - "textDocument/publishDiagnostics", - { - diagnostics = vim.lsp.diagnostic.get(bufnr, client.id), - uri = vim.uri_from_bufnr(bufnr), - }, - client.id - ) + vim.lsp.diagnostic.redraw(bufnr, client.id) end) end @@ -554,16 +548,16 @@ end --- --- The following parameters describe fields in the {config} table. --- ---@param root_dir: (required, string) Directory where the LSP server will base +---@param root_dir: (string) Directory where the LSP server will base --- its rootUri on initialization. --- ---@param cmd: (required, string or list treated like |jobstart()|) Base command +---@param cmd: (required, string or list treated like |jobstart()|) Base command --- that initiates the LSP client. --- ---@param cmd_cwd: (string, default=|getcwd()|) Directory to launch +---@param cmd_cwd: (string, default=|getcwd()|) Directory to launch --- the `cmd` process. Not related to `root_dir`. --- ---@param cmd_env: (table) Environment flags to pass to the LSP on +---@param cmd_env: (table) Environment flags to pass to the LSP on --- spawn. Can be specified using keys like a map or as a list with `k=v` --- pairs or both. Non-string values are coerced to string. --- Example: @@ -571,7 +565,7 @@ end --- { "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; } --- </pre> --- ---@param capabilities Map overriding the default capabilities defined by +---@param capabilities Map overriding the default capabilities defined by --- |vim.lsp.protocol.make_client_capabilities()|, passed to the language --- server on initialization. Hint: use make_client_capabilities() and modify --- its result. @@ -579,37 +573,41 @@ end --- `{[vim.type_idx]=vim.types.dictionary}`, else it will be encoded as an --- array. --- ---@param handlers Map of language server method names to |lsp-handler| +---@param handlers Map of language server method names to |lsp-handler| --- ---@param settings Map with language server specific settings. These are +---@param settings Map with language server specific settings. These are --- returned to the language server if requested via `workspace/configuration`. --- Keys are case-sensitive. --- ---@param init_options Values to pass in the initialization request +---@param init_options Values to pass in the initialization request --- as `initializationOptions`. See `initialize` in the LSP spec. --- ---@param name (string, default=client-id) Name in log messages. +---@param name (string, default=client-id) Name in log messages. +-- +---@param workspace_folders (table) List of workspace folders passed to the +--- language server. Defaults to root_dir if not set. See `workspaceFolders` in +--- the LSP spec --- ---@param get_language_id function(bufnr, filetype) -> language ID as string. +---@param get_language_id function(bufnr, filetype) -> language ID as string. --- Defaults to the filetype. --- ---@param offset_encoding (default="utf-16") One of "utf-8", "utf-16", +---@param offset_encoding (default="utf-16") One of "utf-8", "utf-16", --- or "utf-32" which is the encoding that the LSP server expects. Client does --- not verify this is correct. --- ---@param on_error Callback with parameters (code, ...), invoked +---@param on_error Callback with parameters (code, ...), invoked --- when the client operation throws an error. `code` is a number describing --- the error. Other arguments may be passed depending on the error kind. See --- |vim.lsp.client_errors| for possible errors. --- Use `vim.lsp.client_errors[code]` to get human-friendly name. --- ---@param before_init Callback with parameters (initialize_params, config) +---@param before_init Callback with parameters (initialize_params, config) --- invoked before the LSP "initialize" phase, where `params` contains the --- parameters being sent to the server and `config` is the config that was --- passed to |vim.lsp.start_client()|. You can use this to modify parameters before --- they are sent. --- ---@param on_init Callback (client, initialize_result) invoked after LSP +---@param on_init Callback (client, initialize_result) invoked after LSP --- "initialize", where `result` is a table of `capabilities` and anything else --- the server may send. For example, clangd sends --- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was @@ -619,24 +617,24 @@ end --- `workspace/didChangeConfiguration` notification should be sent --- to the server during on_init. --- ---@param on_exit Callback (code, signal, client_id) invoked on client +---@param on_exit Callback (code, signal, client_id) invoked on client --- exit. --- - code: exit code of the process --- - signal: number describing the signal used to terminate (if any) --- - client_id: client handle --- ---@param on_attach Callback (client, bufnr) invoked when client +---@param on_attach Callback (client, bufnr) invoked when client --- attaches to a buffer. --- ---@param trace: "off" | "messages" | "verbose" | nil passed directly to the language +---@param trace: "off" | "messages" | "verbose" | nil passed directly to the language --- server in the initialize request. Invalid/empty values will default to "off" ---@param flags: A table with flags for the client. The current (experimental) flags are: +---@param flags: A table with flags for the client. The current (experimental) flags are: --- - allow_incremental_sync (bool, default true): Allow using incremental sync for buffer edits --- - debounce_text_changes (number, default nil): Debounce didChange --- notifications to the server by the given number in milliseconds. No debounce --- occurs if nil --- ---@returns Client id. |vim.lsp.get_client_by_id()| Note: client may not be +---@returns 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. function lsp.start_client(config) @@ -659,53 +657,53 @@ function lsp.start_client(config) local dispatch = {} - --@private + ---@private --- Returns the handler associated with an LSP method. --- Returns the default handler if the user hasn't set a custom one. --- - --@param method (string) LSP method name - --@returns (fn) The handler for the given method, if defined, or the default from |vim.lsp.handlers| + ---@param method (string) LSP method name + ---@returns (fn) The handler for the given method, if defined, or the default from |vim.lsp.handlers| local function resolve_handler(method) return handlers[method] or default_handlers[method] end - --@private + ---@private --- Handles a notification sent by an LSP server by invoking the --- corresponding handler. --- - --@param method (string) LSP method name - --@param params (table) The parameters for that method. + ---@param method (string) LSP method name + ---@param params (table) The parameters for that method. function dispatch.notification(method, params) local _ = log.debug() and log.debug('notification', method, params) local handler = resolve_handler(method) if handler then -- Method name is provided here for convenience. - handler(nil, method, params, client_id) + handler(nil, params, {method=method, client_id=client_id}) end end - --@private + ---@private --- Handles a request from an LSP server by invoking the corresponding handler. --- - --@param method (string) LSP method name - --@param params (table) The parameters for that method + ---@param method (string) LSP method name + ---@param params (table) The parameters for that method function dispatch.server_request(method, params) local _ = log.debug() and log.debug('server_request', method, params) local handler = resolve_handler(method) if handler then local _ = log.debug() and log.debug("server_request: found handler for", method) - return handler(nil, method, params, client_id) + return handler(nil, params, {method=method, client_id=client_id}) end local _ = log.debug() and log.debug("server_request: no handler found for", method) return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound) end - --@private + ---@private --- Invoked when the client operation throws an error. --- - --@param code (number) Error code - --@param err (...) Other arguments may be passed depending on the error kind - --@see |vim.lsp.client_errors| for possible errors. Use + ---@param code (number) Error code + ---@param err (...) Other arguments may be passed depending on the error kind + ---@see |vim.lsp.client_errors| for possible errors. Use ---`vim.lsp.client_errors[code]` to get a human-friendly name. function dispatch.on_error(code, err) local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err }) @@ -719,11 +717,11 @@ function lsp.start_client(config) end end - --@private + ---@private --- Invoked on client exit. --- - --@param code (number) exit code of the process - --@param signal (number) the signal used to terminate (if any) + ---@param code (number) exit code of the process + ---@param signal (number) the signal used to terminate (if any) function dispatch.on_exit(code, signal) active_clients[client_id] = nil uninitialized_clients[client_id] = nil @@ -768,12 +766,20 @@ function lsp.start_client(config) -- Store the uninitialized_clients for cleanup in case we exit before initialize finishes. uninitialized_clients[client_id] = client; - --@private + ---@private local function initialize() local valid_traces = { off = 'off'; messages = 'messages'; verbose = 'verbose'; } local version = vim.version() + + if config.root_dir and not config.workspace_folders then + config.workspace_folders = {{ + uri = vim.uri_from_fname(config.root_dir); + name = string.format("%s", config.root_dir); + }}; + end + local initialize_params = { -- The process Id of the parent process that started the server. Is null if -- the process has not been started by another process. If the parent @@ -792,7 +798,7 @@ function lsp.start_client(config) rootPath = config.root_dir; -- The rootUri of the workspace. Is null if no folder is open. If both -- `rootPath` and `rootUri` are set `rootUri` wins. - rootUri = vim.uri_from_fname(config.root_dir); + rootUri = config.root_dir and vim.uri_from_fname(config.root_dir); -- User provided initialization options. initializationOptions = config.init_options; -- The capabilities provided by the client (editor or tool) @@ -814,10 +820,7 @@ function lsp.start_client(config) -- -- workspace folder in the user interface. -- name -- } - workspaceFolders = {{ - uri = vim.uri_from_fname(config.root_dir); - name = string.format("%s", config.root_dir); - }}; + workspaceFolders = config.workspace_folders, } if config.before_init then -- TODO(ashkan) handle errors here. @@ -866,23 +869,23 @@ function lsp.start_client(config) end) end - --@private + ---@private --- Sends a request to the server. --- --- This is a thin wrapper around {client.rpc.request} with some additional --- checks for capabilities and handler availability. --- - --@param method (string) LSP method name. - --@param params (table) LSP request params. - --@param handler (function, optional) Response |lsp-handler| for this method. - --@param bufnr (number) Buffer handle (0 for current). - --@returns ({status}, [request_id]): {status} is a bool indicating + ---@param method (string) LSP method name. + ---@param params (table) LSP request params. + ---@param handler (function, optional) Response |lsp-handler| for this method. + ---@param bufnr (number) Buffer handle (0 for current). + ---@returns ({status}, [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 ---second result. You can use this with `client.cancel_request(request_id)` ---to cancel the-request. - --@see |vim.lsp.buf_request()| + ---@see |vim.lsp.buf_request()| function client.request(method, params, handler, bufnr) if not handler then handler = resolve_handler(method) @@ -893,28 +896,28 @@ function lsp.start_client(config) local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, handler, bufnr) return rpc.request(method, params, function(err, result) - handler(err, method, result, client_id, bufnr) + handler(err, result, {method=method, client_id=client_id, bufnr=bufnr}) end) end - --@private + ---@private --- Sends a request to the server and synchronously waits for the response. --- --- This is a wrapper around {client.request} --- - --@param method (string) LSP method name. - --@param params (table) LSP request params. - --@param timeout_ms (number, optional, default=1000) Maximum time in + ---@param method (string) LSP method name. + ---@param params (table) LSP request params. + ---@param timeout_ms (number, optional, default=1000) Maximum time in ---milliseconds to wait for a result. - --@param bufnr (number) Buffer handle (0 for current). - --@returns { err=err, result=result }, a dictionary, where `err` and `result` come from the |lsp-handler|. + ---@param bufnr (number) Buffer handle (0 for current). + ---@returns { err=err, result=result }, a dictionary, where `err` and `result` come from the |lsp-handler|. ---On timeout, cancel or error, returns `(nil, err)` where `err` is a ---string describing the failure reason. If the request was unsuccessful ---returns `nil`. - --@see |vim.lsp.buf_request_sync()| + ---@see |vim.lsp.buf_request_sync()| function client.request_sync(method, params, timeout_ms, bufnr) local request_result = nil - local function _sync_handler(err, _, result) + local function _sync_handler(err, result) request_result = { err = err, result = result } end @@ -933,25 +936,25 @@ function lsp.start_client(config) return request_result end - --@private + ---@private --- Sends a notification to an LSP server. --- - --@param method (string) LSP method name. - --@param params (optional, table) LSP request params. - --@param bufnr (number) Buffer handle, or 0 for current. - --@returns {status} (bool) true if the notification was successful. + ---@param method (string) LSP method name. + ---@param params (optional, table) LSP request params. + ---@param bufnr (number) Buffer handle, or 0 for current. + ---@returns {status} (bool) true if the notification was successful. ---If it is false, then it will always be false ---(the client has shutdown). function client.notify(...) return rpc.notify(...) end - --@private + ---@private --- Cancels a request with a given request id. --- - --@param id (number) id of request to cancel - --@returns true if any client returns true; false otherwise - --@see |vim.lsp.client.notify()| + ---@param id (number) id of request to cancel + ---@returns true if any client returns true; false otherwise + ---@see |vim.lsp.client.notify()| function client.cancel_request(id) validate{id = {id, 'n'}} return rpc.notify("$/cancelRequest", { id = id }) @@ -960,14 +963,14 @@ function lsp.start_client(config) -- Track this so that we can escalate automatically if we've alredy tried a -- graceful shutdown local graceful_shutdown_failed = false - --@private + ---@private --- Stops a client, optionally with force. --- ---By default, it will just ask the - server to shutdown without force. If --- you request to stop a client which has previously been requested to --- shutdown, it will automatically escalate and force shutdown. --- - --@param force (bool, optional) + ---@param force (bool, optional) function client.stop(force) lsp.diagnostic.reset(client_id, all_buffer_active_clients) @@ -997,18 +1000,18 @@ function lsp.start_client(config) end) end - --@private + ---@private --- Checks whether a client is stopped. --- - --@returns (bool) true if client is stopped or in the process of being + ---@returns (bool) true if client is stopped or in the process of being ---stopped; false otherwise function client.is_stopped() return rpc.handle:is_closing() end - --@private + ---@private --- Runs the on_attach function from the client's config if it was defined. - --@param bufnr (number) Buffer number + ---@param bufnr (number) Buffer number function client._on_attach(bufnr) text_document_did_open_handler(bufnr, client) if config.on_attach then @@ -1022,8 +1025,8 @@ function lsp.start_client(config) return client_id end ---@private ---@fn text_document_did_change_handler(_, bufnr, changedtick, firstline, lastline, new_lastline, old_byte_size, old_utf32_size, old_utf16_size) +---@private +---@fn text_document_did_change_handler(_, bufnr, changedtick, firstline, lastline, new_lastline, old_byte_size, old_utf32_size, old_utf16_size) --- Notify all attached clients that a buffer has changed. local text_document_did_change_handler do @@ -1073,8 +1076,8 @@ end --- --- Without calling this, the server won't be notified of changes to a buffer. --- ---- @param bufnr (number) Buffer handle, or 0 for current ---- @param client_id (number) Client id +---@param bufnr (number) Buffer handle, or 0 for current +---@param client_id (number) Client id function lsp.buf_attach_client(bufnr, client_id) validate { bufnr = {bufnr, 'n', true}; @@ -1149,23 +1152,23 @@ end ---@param bufnr (number) Buffer handle, or 0 for current ---@param client_id (number) the client id function lsp.buf_is_attached(bufnr, client_id) - return (all_buffer_active_clients[bufnr] or {})[client_id] == true + return (all_buffer_active_clients[resolve_bufnr(bufnr)] or {})[client_id] == true end --- Gets a client by id, or nil if the id is invalid. --- The returned client may not yet be fully initialized. -- ---@param client_id client id number +---@param client_id client id number --- ---@returns |vim.lsp.client| object, or nil +---@returns |vim.lsp.client| object, or nil function lsp.get_client_by_id(client_id) return active_clients[client_id] or uninitialized_clients[client_id] end --- Returns list of buffers attached to client_id. -- ---@param client_id client id ---@returns list of buffer ids +---@param client_id client id +---@returns list of buffer ids function lsp.get_buffers_by_client_id(client_id) local active_client_buffers = all_client_active_buffers[client_id] if active_client_buffers then @@ -1187,8 +1190,8 @@ end --- By default asks the server to shutdown, unless stop was requested --- already for this client, then force-shutdown is attempted. --- ---@param client_id client id or |vim.lsp.client| object, or list thereof ---@param force boolean (optional) shutdown forcefully +---@param client_id client id or |vim.lsp.client| object, or list thereof +---@param force boolean (optional) shutdown forcefully function lsp.stop_client(client_id, force) local ids = type(client_id) == 'table' and client_id or {client_id} for _, id in ipairs(ids) do @@ -1204,7 +1207,7 @@ end --- Gets all active clients. --- ---@returns Table of |vim.lsp.client| objects +---@returns Table of |vim.lsp.client| objects function lsp.get_active_clients() return vim.tbl_values(active_clients) end @@ -1235,13 +1238,13 @@ nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()") --- Sends an async request for all active clients attached to the --- buffer. --- ---@param bufnr (number) Buffer handle, or 0 for current. ---@param method (string) LSP method name ---@param params (optional, table) Parameters to send to the server ---@param handler (optional, function) See |lsp-handler| +---@param bufnr (number) Buffer handle, or 0 for current. +---@param method (string) LSP method name +---@param params (optional, table) Parameters to send to the server +---@param handler (optional, function) See |lsp-handler| -- If nil, follows resolution strategy defined in |lsp-handler-configuration| -- ---@returns 2-tuple: +---@returns 2-tuple: --- - Map of client-id:request-id pairs for all successful requests. --- - Function which can be used to cancel all the requests. You could instead --- iterate all clients and call their `cancel_request()` methods. @@ -1273,7 +1276,7 @@ function lsp.buf_request(bufnr, method, params, handler) local unsupported_err = lsp._unsupported_method(method) handler = handler or lsp.handlers[method] if handler then - handler(unsupported_err, method, bufnr) + handler(unsupported_err, nil, {method=method, bufnr=bufnr}) end return end @@ -1293,14 +1296,14 @@ end ---Parameters are the same as |vim.lsp.buf_request()| but the return result and callback are ---different. --- ---@param bufnr (number) Buffer handle, or 0 for current. ---@param method (string) LSP method name ---@param params (optional, table) Parameters to send to the server ---@param callback (function) The callback to call when all requests are finished. +---@param bufnr (number) Buffer handle, or 0 for current. +---@param method (string) LSP method name +---@param params (optional, table) Parameters to send to the server +---@param callback (function) The callback to call when all requests are finished. -- Unlike `buf_request`, this will collect all the responses from each server instead of handling them. -- A map of client_id:request_result will be provided to the callback -- ---@returns (function) A function that will cancel all requests which is the same as the one returned from `buf_request`. +---@returns (function) A function that will cancel all requests which is the same as the one returned from `buf_request`. function lsp.buf_request_all(bufnr, method, params, callback) local request_results = {} local result_count = 0 @@ -1313,8 +1316,8 @@ function lsp.buf_request_all(bufnr, method, params, callback) end end) - local function _sync_handler(err, _, result, client_id) - request_results[client_id] = { error = err, result = result } + local function _sync_handler(err, result, ctx) + request_results[ctx.client_id] = { error = err, result = result } result_count = result_count + 1 set_expected_result_count() @@ -1334,13 +1337,13 @@ end --- Parameters are the same as |vim.lsp.buf_request()| but the return result is --- different. Wait maximum of {timeout_ms} (default 1000) ms. --- ---@param bufnr (number) Buffer handle, or 0 for current. ---@param method (string) LSP method name ---@param params (optional, table) Parameters to send to the server ---@param timeout_ms (optional, number, default=1000) Maximum time in +---@param bufnr (number) Buffer handle, or 0 for current. +---@param method (string) LSP method name +---@param params (optional, table) Parameters to send to the server +---@param timeout_ms (optional, number, default=1000) Maximum time in --- milliseconds to wait for a result. --- ---@returns Map of client_id:request_result. On timeout, cancel or error, +---@returns Map of client_id:request_result. On timeout, cancel or error, --- returns `(nil, err)` where `err` is a string describing the failure --- reason. function lsp.buf_request_sync(bufnr, method, params, timeout_ms) @@ -1363,11 +1366,11 @@ function lsp.buf_request_sync(bufnr, method, params, timeout_ms) end --- Send a notification to a server ---@param bufnr [number] (optional): The number of the buffer ---@param method [string]: Name of the request method ---@param params [string]: Arguments to send to the server +---@param bufnr [number] (optional): The number of the buffer +---@param method [string]: Name of the request method +---@param params [string]: Arguments to send to the server --- ---@returns true if any client returns true; false otherwise +---@returns true if any client returns true; false otherwise function lsp.buf_notify(bufnr, method, params) validate { bufnr = { bufnr, 'n', true }; @@ -1382,14 +1385,14 @@ end --- Implements 'omnifunc' compatible LSP completion. --- ---@see |complete-functions| ---@see |complete-items| ---@see |CompleteDone| +---@see |complete-functions| +---@see |complete-items| +---@see |CompleteDone| --- ---@param findstart 0 or 1, decides behavior ---@param base If findstart=0, text to match against +---@param findstart 0 or 1, decides behavior +---@param base If findstart=0, text to match against --- ---@returns (number) Decided by `findstart`: +---@returns (number) Decided by `findstart`: --- - findstart=0: column where the completion starts, or -2 or -3 --- - findstart=1: list of matches (actually just calls |complete()|) function lsp.omnifunc(findstart, base) @@ -1420,7 +1423,7 @@ function lsp.omnifunc(findstart, base) local params = util.make_position_params() local items = {} - lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, _, result) + lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, result) if err or not result or vim.fn.mode() ~= "i" then return end local matches = util.text_document_completion_list_to_complete_items(result, prefix) -- TODO(ashkan): is this the best way to do this? @@ -1435,8 +1438,8 @@ end ---Checks whether a client is stopped. --- ---@param client_id (Number) ---@returns true if client is stopped, false otherwise. +---@param client_id (Number) +---@returns true if client is stopped, false otherwise. function lsp.client_is_stopped(client_id) return active_clients[client_id] == nil end @@ -1444,7 +1447,7 @@ end --- Gets a map of client_id:client pairs for the given buffer, where each value --- is a |vim.lsp.client| object. --- ---@param bufnr (optional, number): Buffer handle, or 0 for current +---@param bufnr (optional, number): Buffer handle, or 0 for current function lsp.buf_get_clients(bufnr) bufnr = resolve_bufnr(bufnr) local result = {} @@ -1469,9 +1472,9 @@ lsp.log_levels = log.levels --- --- Use `lsp.log_levels` for reverse lookup. --- ---@see |vim.lsp.log_levels| +---@see |vim.lsp.log_levels| --- ---@param level [number|string] the case insensitive level name or number +---@param level [number|string] the case insensitive level name or number function lsp.set_log_level(level) if type(level) == 'string' or type(level) == 'number' then log.set_level(level) @@ -1481,7 +1484,7 @@ function lsp.set_log_level(level) end --- Gets the path of the logfile used by the LSP client. ---@returns (String) Path to logfile. +---@returns (String) Path to logfile. function lsp.get_log_path() return log.get_filename() end @@ -1492,11 +1495,11 @@ function lsp.for_each_buffer_client(bufnr, fn) end --- Function to manage overriding defaults for LSP handlers. ---@param handler (function) See |lsp-handler| ---@param override_config (table) Table containing the keys to override behavior of the {handler} +---@param handler (function) See |lsp-handler| +---@param override_config (table) Table containing the keys to override behavior of the {handler} function lsp.with(handler, override_config) - return function(err, method, params, client_id, bufnr, config) - return handler(err, method, params, client_id, bufnr, vim.tbl_deep_extend("force", config or {}, override_config)) + return function(err, result, ctx, config) + return handler(err, result, ctx, vim.tbl_deep_extend("force", config or {}, override_config)) end end diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 5dd7109bb0..8bfcd90f12 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -5,7 +5,7 @@ local util = require 'vim.lsp.util' local M = {} ---@private +---@private --- Returns nil if {status} is false or nil, otherwise returns the rest of the --- arguments. local function ok_or_nil(status, ...) @@ -13,31 +13,31 @@ local function ok_or_nil(status, ...) return ... end ---@private +---@private --- Swallows errors. --- ---@param fn Function to run ---@param ... Function arguments ---@returns Result of `fn(...)` if there are no errors, otherwise nil. +---@param fn Function to run +---@param ... Function arguments +---@returns Result of `fn(...)` if there are no errors, otherwise nil. --- Returns nil if errors occur during {fn}, otherwise returns local function npcall(fn, ...) return ok_or_nil(pcall(fn, ...)) end ---@private +---@private --- Sends an async request to all active clients attached to the current --- buffer. --- ---@param method (string) LSP method name ---@param params (optional, table) Parameters to send to the server ---@param handler (optional, functionnil) See |lsp-handler|. Follows |lsp-handler-resolution| +---@param method (string) LSP method name +---@param params (optional, table) Parameters to send to the server +---@param handler (optional, functionnil) See |lsp-handler|. Follows |lsp-handler-resolution| -- ---@returns 2-tuple: +---@returns 2-tuple: --- - Map of client-id:request-id pairs for all successful requests. --- - Function which can be used to cancel all the requests. You could instead --- iterate all clients and call their `cancel_request()` methods. --- ---@see |vim.lsp.buf_request()| +---@see |vim.lsp.buf_request()| local function request(method, params, handler) validate { method = {method, 's'}; @@ -49,7 +49,7 @@ end --- Checks whether the language servers attached to the current buffer are --- ready. --- ---@returns `true` if server responds. +---@returns `true` if server responds. function M.server_ready() return not not vim.lsp.buf_notify(0, "window/progress", {}) end @@ -62,7 +62,7 @@ function M.hover() 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. +---@note Many servers do not implement this method. Generally, see |vim.lsp.buf.definition()| instead. --- function M.declaration() local params = util.make_position_params() @@ -100,22 +100,22 @@ end --- Retrieves the completion items at the current cursor position. Can only be --- called in Insert mode. --- ---@param context (context support not yet implemented) Additional information +---@param context (context support not yet implemented) Additional information --- about the context in which a completion was triggered (how it was triggered, --- and by which trigger character, if applicable) --- ---@see |vim.lsp.protocol.constants.CompletionTriggerKind| +---@see |vim.lsp.protocol.constants.CompletionTriggerKind| function M.completion(context) local params = util.make_position_params() params.context = context return request('textDocument/completion', params) end ---@private +---@private --- If there is more than one client that supports the given method, --- asks the user to select one. -- ---@returns The client that the user selected or nil +---@returns The client that the user selected or nil local function select_client(method) local clients = vim.tbl_values(vim.lsp.buf_get_clients()); clients = vim.tbl_filter(function (client) @@ -126,7 +126,7 @@ local function select_client(method) if #clients > 1 then local choices = {} - for k,v in ipairs(clients) do + for k,v in pairs(clients) do table.insert(choices, string.format("%d %s", k, v.name)) end local user_choice = vim.fn.confirm( @@ -146,17 +146,17 @@ end --- Formats the current buffer. --- ---@param options (optional, table) Can be used to specify FormattingOptions. +---@param options (optional, table) Can be used to specify FormattingOptions. --- Some unspecified options will be automatically derived from the current --- Neovim options. -- ---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting +---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting function M.formatting(options) local client = select_client("textDocument/formatting") if client == nil then return end local params = util.make_formatting_params(options) - return client.request("textDocument/formatting", params) + return client.request("textDocument/formatting", params, nil, vim.api.nvim_get_current_buf()) end --- Performs |vim.lsp.buf.formatting()| synchronously. @@ -168,15 +168,15 @@ end --- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()]] --- </pre> --- ---@param options Table with valid `FormattingOptions` entries ---@param timeout_ms (number) Request timeout ---@see |vim.lsp.buf.formatting_seq_sync| +---@param options Table with valid `FormattingOptions` entries +---@param timeout_ms (number) Request timeout +---@see |vim.lsp.buf.formatting_seq_sync| function M.formatting_sync(options, timeout_ms) local client = select_client("textDocument/formatting") if client == nil then return end local params = util.make_formatting_params(options) - local result, err = client.request_sync("textDocument/formatting", params, timeout_ms) + local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf()) if result and result.result then util.apply_text_edits(result.result) elseif err then @@ -195,18 +195,18 @@ end --- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]] --- </pre> --- ---@param options (optional, table) `FormattingOptions` entries ---@param timeout_ms (optional, number) Request timeout ---@param order (optional, table) List of client names. Formatting is requested from clients +---@param options (optional, table) `FormattingOptions` entries +---@param timeout_ms (optional, number) Request timeout +---@param order (optional, table) List of client names. Formatting is requested from clients ---in the following order: first all clients that are not in the `order` list, then ---the remaining clients in the order as they occur in the `order` list. function M.formatting_seq_sync(options, timeout_ms, order) local clients = vim.tbl_values(vim.lsp.buf_get_clients()); -- sort the clients according to `order` - for _, client_name in ipairs(order or {}) do + for _, client_name in pairs(order or {}) do -- if the client exists, move to the end of the list - for i, client in ipairs(clients) do + for i, client in pairs(clients) do if client.name == client_name then table.insert(clients, table.remove(clients, i)) break @@ -215,10 +215,10 @@ function M.formatting_seq_sync(options, timeout_ms, order) end -- loop through the clients and make synchronous formatting requests - for _, client in ipairs(clients) do + for _, client in pairs(clients) do if client.resolved_capabilities.document_formatting then local params = util.make_formatting_params(options) - local result, err = client.request_sync("textDocument/formatting", params, timeout_ms) + local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, vim.api.nvim_get_current_buf()) if result and result.result then util.apply_text_edits(result.result) elseif err then @@ -230,10 +230,10 @@ end --- Formats a given range. --- ---@param options Table with valid `FormattingOptions` entries. ---@param start_pos ({number, number}, optional) mark-indexed position. +---@param options Table with valid `FormattingOptions` entries. +---@param start_pos ({number, number}, optional) mark-indexed position. ---Defaults to the start of the last visual selection. ---@param end_pos ({number, number}, optional) mark-indexed position. +---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. function M.range_formatting(options, start_pos, end_pos) local client = select_client("textDocument/rangeFormatting") @@ -246,22 +246,43 @@ end --- Renames all references to the symbol under the cursor. --- ---@param new_name (string) If not provided, the user will be prompted for a new +---@param new_name (string) If not provided, the user will be prompted for a new ---name using |input()|. function M.rename(new_name) - -- TODO(ashkan) use prepareRename - -- * result: [`Range`](#range) \| `{ range: Range, placeholder: string }` \| `null` describing the range of the string to rename and optionally a placeholder text of the string content to be renamed. If `null` is returned then it is deemed that a 'textDocument/rename' request is not valid at the given position. local params = util.make_position_params() - new_name = new_name or npcall(vfn.input, "New Name: ", vfn.expand('<cword>')) - if not (new_name and #new_name > 0) then return end - params.newName = new_name - request('textDocument/rename', params) + local function prepare_rename(err, result) + if err == nil and result == nil then + vim.notify('nothing to rename', vim.log.levels.INFO) + return + end + if result and result.placeholder then + new_name = new_name or npcall(vfn.input, "New Name: ", result.placeholder) + elseif result and result.start and result['end'] and + result.start.line == result['end'].line then + local line = vfn.getline(result.start.line+1) + local start_char = result.start.character+1 + local end_char = result['end'].character + new_name = new_name or npcall(vfn.input, "New Name: ", string.sub(line, start_char, end_char)) + else + -- fallback to guessing symbol using <cword> + -- + -- this can happen if the language server does not support prepareRename, + -- returns an unexpected response, or requests for "default behavior" + -- + -- see https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename + new_name = new_name or npcall(vfn.input, "New Name: ", vfn.expand('<cword>')) + end + if not (new_name and #new_name > 0) then return end + params.newName = new_name + request('textDocument/rename', params) + end + request('textDocument/prepareRename', params, prepare_rename) end --- Lists all the references to the symbol under the cursor in the quickfix window. --- ---@param context (table) Context for the request ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references +---@param context (table) Context for the request +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references function M.references(context) validate { context = { context, 't', true } } local params = util.make_position_params() @@ -279,14 +300,14 @@ function M.document_symbol() request('textDocument/documentSymbol', params) end ---@private +---@private local function pick_call_hierarchy_item(call_hierarchy_items) if not call_hierarchy_items then return end if #call_hierarchy_items == 1 then return call_hierarchy_items[1] end local items = {} - for i, item in ipairs(call_hierarchy_items) do + for i, item in pairs(call_hierarchy_items) do local entry = item.detail or item.name table.insert(items, string.format("%d. %s", i, entry)) end @@ -297,6 +318,7 @@ local function pick_call_hierarchy_item(call_hierarchy_items) return choice end +---@private local function call_hierarchy(method) local params = util.make_position_params() request('textDocument/prepareCallHierarchy', params, function(err, _, result) @@ -327,8 +349,8 @@ end --- function M.list_workspace_folders() local workspace_folders = {} - for _, client in ipairs(vim.lsp.buf_get_clients()) do - for _, folder in ipairs(client.workspaceFolders) do + for _, client in pairs(vim.lsp.buf_get_clients()) do + for _, folder in pairs(client.workspaceFolders) do table.insert(workspace_folders, folder.name) end end @@ -338,7 +360,7 @@ end --- Add the folder at path to the workspace folders. If {path} is --- not provided, the user will be prompted for a path using |input()|. function M.add_workspace_folder(workspace_folder) - workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h')) + workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'), 'dir') vim.api.nvim_command("redraw") if not (workspace_folder and #workspace_folder > 0) then return end if vim.fn.isdirectory(workspace_folder) == 0 then @@ -346,9 +368,9 @@ function M.add_workspace_folder(workspace_folder) return end local params = util.make_workspace_params({{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}, {{}}) - for _, client in ipairs(vim.lsp.buf_get_clients()) do + for _, client in pairs(vim.lsp.buf_get_clients()) do local found = false - for _, folder in ipairs(client.workspaceFolders) do + for _, folder in pairs(client.workspaceFolders) do if folder.name == workspace_folder then found = true print(workspace_folder, "is already part of this workspace") @@ -370,8 +392,8 @@ function M.remove_workspace_folder(workspace_folder) vim.api.nvim_command("redraw") if not (workspace_folder and #workspace_folder > 0) then return end local params = util.make_workspace_params({{}}, {{uri = vim.uri_from_fname(workspace_folder); name = workspace_folder}}) - for _, client in ipairs(vim.lsp.buf_get_clients()) do - for idx, folder in ipairs(client.workspaceFolders) do + for _, client in pairs(vim.lsp.buf_get_clients()) do + for idx, folder in pairs(client.workspaceFolders) do if folder.name == workspace_folder then vim.lsp.buf_notify(0, 'workspace/didChangeWorkspaceFolders', params) client.workspaceFolders[idx] = nil @@ -388,7 +410,7 @@ end --- call, the user is prompted to enter a string on the command line. An empty --- string means no filtering is done. --- ---@param query (string, optional) +---@param query (string, optional) function M.workspace_symbol(query) query = query or npcall(vfn.input, "Query: ") local params = {query = query} @@ -421,38 +443,53 @@ function M.clear_references() util.buf_clear_references() end +--- Requests code actions from all clients and calls the handler exactly once +--- with all aggregated results +---@private +local function code_action_request(params) + local bufnr = vim.api.nvim_get_current_buf() + local method = 'textDocument/codeAction' + vim.lsp.buf_request_all(bufnr, method, params, function(results) + local actions = {} + for _, r in pairs(results) do + vim.list_extend(actions, r.result or {}) + end + vim.lsp.handlers[method](nil, actions, {bufnr=bufnr, method=method}) + end) +end + --- Selects a code action from the input list that is available at the current --- cursor position. --- ---@param context: (table, optional) Valid `CodeActionContext` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction +--- +---@param context: (table, optional) Valid `CodeActionContext` object +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction function M.code_action(context) validate { context = { context, 't', true } } context = context or { diagnostics = vim.lsp.diagnostic.get_line_diagnostics() } local params = util.make_range_params() params.context = context - request('textDocument/codeAction', params) + code_action_request(params) end --- Performs |vim.lsp.buf.code_action()| for a given range. --- ---@param context: (table, optional) Valid `CodeActionContext` object ---@param start_pos ({number, number}, optional) mark-indexed position. +---@param context: (table, optional) Valid `CodeActionContext` object +---@param start_pos ({number, number}, optional) mark-indexed position. ---Defaults to the start of the last visual selection. ---@param end_pos ({number, number}, optional) mark-indexed position. +---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. function M.range_code_action(context, start_pos, end_pos) validate { context = { context, 't', true } } context = context or { diagnostics = vim.lsp.diagnostic.get_line_diagnostics() } local params = util.make_given_range_params(start_pos, end_pos) params.context = context - request('textDocument/codeAction', params) + code_action_request(params) end --- Executes an LSP server command. --- ---@param command A valid `ExecuteCommandParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand +---@param command A valid `ExecuteCommandParams` object +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand function M.execute_command(command) validate { command = { command.command, 's' }, diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua new file mode 100644 index 0000000000..9cedb2f1db --- /dev/null +++ b/runtime/lua/vim/lsp/codelens.lua @@ -0,0 +1,236 @@ +local util = require('vim.lsp.util') +local api = vim.api +local M = {} + +--- bufnr → true|nil +--- to throttle refreshes to at most one at a time +local active_refreshes = {} + +--- bufnr -> client_id -> lenses +local lens_cache_by_buf = setmetatable({}, { + __index = function(t, b) + local key = b > 0 and b or api.nvim_get_current_buf() + return rawget(t, key) + end +}) + +local namespaces = setmetatable({}, { + __index = function(t, key) + local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key) + rawset(t, key, value) + return value + end; +}) + +---@private +M.__namespaces = namespaces + + +---@private +local function execute_lens(lens, bufnr, client_id) + local line = lens.range.start.line + api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1) + + -- Need to use the client that returned the lens → must not use buf_request + local client = vim.lsp.get_client_by_id(client_id) + assert(client, 'Client is required to execute lens, client_id=' .. client_id) + client.request('workspace/executeCommand', lens.command, function(...) + local result = vim.lsp.handlers['workspace/executeCommand'](...) + M.refresh() + return result + end, bufnr) +end + + +--- Return all lenses for the given buffer +--- +---@param bufnr number Buffer number. 0 can be used for the current buffer. +---@return table (`CodeLens[]`) +function M.get(bufnr) + local lenses_by_client = lens_cache_by_buf[bufnr or 0] + if not lenses_by_client then return {} end + local lenses = {} + for _, client_lenses in pairs(lenses_by_client) do + vim.list_extend(lenses, client_lenses) + end + return lenses +end + + +--- Run the code lens in the current line +--- +function M.run() + local line = api.nvim_win_get_cursor(0)[1] + local bufnr = api.nvim_get_current_buf() + local options = {} + 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 + table.insert(options, {client=client, lens=lens}) + end + end + end + if #options == 0 then + vim.notify('No executable codelens found at current line') + elseif #options == 1 then + local option = options[1] + execute_lens(option.lens, bufnr, option.client) + else + local options_strings = {"Code lenses:"} + for i, option in ipairs(options) do + table.insert(options_strings, string.format('%d. %s', i, option.lens.command.title)) + end + local choice = vim.fn.inputlist(options_strings) + if choice < 1 or choice > #options then + return + end + local option = options[choice] + execute_lens(option.lens, bufnr, option.client) + end +end + + +--- Display the lenses using virtual text +--- +---@param lenses table of lenses to display (`CodeLens[] | null`) +---@param bufnr number +---@param client_id number +function M.display(lenses, bufnr, client_id) + if not lenses or not next(lenses) then + return + end + local lenses_by_lnum = {} + for _, lens in pairs(lenses) do + local line_lenses = lenses_by_lnum[lens.range.start.line] + if not line_lenses then + line_lenses = {} + lenses_by_lnum[lens.range.start.line] = line_lenses + end + table.insert(line_lenses, lens) + end + local ns = namespaces[client_id] + local num_lines = api.nvim_buf_line_count(bufnr) + for i = 0, num_lines do + local line_lenses = lenses_by_lnum[i] or {} + api.nvim_buf_clear_namespace(bufnr, ns, i, i + 1) + local chunks = {} + local num_line_lenses = #line_lenses + for j, lens in ipairs(line_lenses) do + local text = lens.command and lens.command.title or 'Unresolved lens ...' + table.insert(chunks, {text, 'LspCodeLens' }) + if j < num_line_lenses then + table.insert(chunks, {' | ', 'LspCodeLensSeparator' }) + end + end + if #chunks > 0 then + api.nvim_buf_set_extmark(bufnr, ns, i, 0, { virt_text = chunks }) + end + end +end + + +--- Store lenses for a specific buffer and client +--- +---@param lenses table of lenses to store (`CodeLens[] | null`) +---@param bufnr number +---@param client_id number +function M.save(lenses, bufnr, client_id) + local lenses_by_client = lens_cache_by_buf[bufnr] + if not lenses_by_client then + lenses_by_client = {} + lens_cache_by_buf[bufnr] = lenses_by_client + local ns = namespaces[client_id] + api.nvim_buf_attach(bufnr, false, { + on_detach = function(b) lens_cache_by_buf[b] = nil end, + on_lines = function(_, b, _, first_lnum, last_lnum) + api.nvim_buf_clear_namespace(b, ns, first_lnum, last_lnum) + end + }) + end + lenses_by_client[client_id] = lenses +end + + +---@private +local function resolve_lenses(lenses, bufnr, client_id, callback) + lenses = lenses or {} + local num_lens = vim.tbl_count(lenses) + if num_lens == 0 then + callback() + return + end + + ---@private + local function countdown() + num_lens = num_lens - 1 + if num_lens == 0 then + callback() + end + end + local ns = namespaces[client_id] + local client = vim.lsp.get_client_by_id(client_id) + for _, lens in pairs(lenses or {}) do + if lens.command then + countdown() + else + client.request('codeLens/resolve', lens, function(_, result) + if result and result.command then + lens.command = result.command + -- Eager display to have some sort of incremental feedback + -- Once all lenses got resolved there will be a full redraw for all lenses + -- So that multiple lens per line are properly displayed + api.nvim_buf_set_extmark( + bufnr, + ns, + lens.range.start.line, + 0, + { virt_text = {{ lens.command.title, 'LspCodeLens' }} } + ) + end + countdown() + end, bufnr) + end + end +end + + +--- |lsp-handler| for the method `textDocument/codeLens` +--- +function M.on_codelens(err, result, ctx, _) + assert(not err, vim.inspect(err)) + + M.save(result, ctx.bufnr, ctx.client_id) + + -- Eager display for any resolved (and unresolved) lenses and refresh them + -- once resolved. + M.display(result, ctx.bufnr, ctx.client_id) + resolve_lenses(result, ctx.bufnr, ctx.client_id, function() + M.display(result, ctx.bufnr, ctx.client_id) + active_refreshes[ctx.bufnr] = nil + end) +end + + +--- Refresh the codelens for the current buffer +--- +--- It is recommended to trigger this using an autocmd or via keymap. +--- +--- <pre> +--- autocmd BufEnter,CursorHold,InsertLeave <buffer> lua vim.lsp.codelens.refresh() +--- </pre> +--- +function M.refresh() + local params = { + textDocument = util.make_text_document_params() + } + local bufnr = api.nvim_get_current_buf() + if active_refreshes[bufnr] then + return + end + active_refreshes[bufnr] = true + vim.lsp.buf_request(0, 'textDocument/codeLens', params) +end + + +return M diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index dabe400e0d..ccd325b1ac 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -8,7 +8,7 @@ local util = require('vim.lsp.util') local if_nil = vim.F.if_nil ---@class DiagnosticSeverity +---@class DiagnosticSeverity local DiagnosticSeverity = protocol.DiagnosticSeverity local to_severity = function(severity) @@ -46,14 +46,14 @@ end ---@brief lsp-diagnostic --- ---@class Diagnostic ---@field range Range ---@field message string ---@field severity DiagnosticSeverity|nil ---@field code number | string ---@field source string ---@field tags DiagnosticTag[] ---@field relatedInformation DiagnosticRelatedInformation[] +---@class Diagnostic +---@field range Range +---@field message string +---@field severity DiagnosticSeverity|nil +---@field code number | string +---@field source string +---@field tags DiagnosticTag[] +---@field relatedInformation DiagnosticRelatedInformation[] local M = {} @@ -167,12 +167,12 @@ end local _diagnostic_namespaces = _make_namespace_table("vim_lsp_diagnostics", true) local _sign_namespaces = _make_namespace_table("vim_lsp_signs", false) ---@private +---@private function M._get_diagnostic_namespace(client_id) return _diagnostic_namespaces[client_id] end ---@private +---@private function M._get_sign_namespace(client_id) return _sign_namespaces[client_id] end @@ -203,8 +203,13 @@ local bufnr_and_client_cacher_mt = { -- Diagnostic Saving & Caching {{{ local _diagnostic_cleanup = setmetatable({}, bufnr_and_client_cacher_mt) local diagnostic_cache = setmetatable({}, bufnr_and_client_cacher_mt) +local diagnostic_cache_extmarks = setmetatable({}, bufnr_and_client_cacher_mt) local diagnostic_cache_lines = setmetatable({}, bufnr_and_client_cacher_mt) local diagnostic_cache_counts = setmetatable({}, bufnr_and_client_cacher_mt) +local diagnostic_attached_buffers = {} + +-- Disabled buffers and clients +local diagnostic_disabled = setmetatable({}, bufnr_and_client_cacher_mt) local _bufs_waiting_to_update = setmetatable({}, bufnr_and_client_cacher_mt) @@ -250,7 +255,7 @@ local _diagnostic_counts = function(diagnostics) return counts end ---@private +---@private --- Set the different diagnostic cache after `textDocument/publishDiagnostics` ---@param diagnostics Diagnostic[] ---@param bufnr number @@ -271,8 +276,12 @@ local function set_diagnostic_cache(diagnostics, bufnr, client_id) end -- Account for servers that place diagnostics on terminating newline if buf_line_count > 0 then - local start = diagnostic.range.start - start.line = math.min(start.line, buf_line_count - 1) + diagnostic.range.start.line = math.max(math.min( + diagnostic.range.start.line, buf_line_count - 1 + ), 0) + diagnostic.range["end"].line = math.max(math.min( + diagnostic.range["end"].line, buf_line_count - 1 + ), 0) end end @@ -282,7 +291,7 @@ local function set_diagnostic_cache(diagnostics, bufnr, client_id) end ---@private +---@private --- Clear the cached diagnostics ---@param bufnr number ---@param client_id number @@ -317,9 +326,9 @@ function M.save(diagnostics, bufnr, client_id) -- Clean up our data when the buffer unloads. api.nvim_buf_attach(bufnr, false, { - on_detach = function(b) + on_detach = function(_, b) clear_diagnostic_cache(b, client_id) - _diagnostic_cleanup[bufnr][client_id] = nil + _diagnostic_cleanup[b][client_id] = nil end }) end @@ -353,11 +362,12 @@ end ---@param bufnr number ---@param client_id number|nil If nil, then return all of the diagnostics. --- Else, return just the diagnostics associated with the client_id. -function M.get(bufnr, client_id) +---@param predicate function|nil Optional function for filtering diagnostics +function M.get(bufnr, client_id, predicate) if client_id == nil then local all_diagnostics = {} for iter_client_id, _ in pairs(diagnostic_cache[bufnr]) do - local iter_diagnostics = M.get(bufnr, iter_client_id) + local iter_diagnostics = M.get(bufnr, iter_client_id, predicate) for _, diagnostic in ipairs(iter_diagnostics) do table.insert(all_diagnostics, diagnostic) @@ -367,19 +377,26 @@ function M.get(bufnr, client_id) return all_diagnostics end - return diagnostic_cache[bufnr][client_id] or {} + predicate = predicate or function(_) return true end + local client_diagnostics = {} + for _, diagnostic in ipairs(diagnostic_cache[bufnr][client_id] or {}) do + if predicate(diagnostic) then + table.insert(client_diagnostics, diagnostic) + end + end + return client_diagnostics end --- Get the diagnostics by line --- ----@param bufnr number The buffer number ----@param line_nr number The line number +---@param bufnr number|nil The buffer number +---@param line_nr number|nil The line number ---@param opts table|nil Configuration keys --- - severity: (DiagnosticSeverity, default nil) --- - Only return diagnostics with this severity. Overrides severity_limit --- - severity_limit: (DiagnosticSeverity, default nil) --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. ----@param client_id number the client id +---@param client_id|nil number the client id ---@return table Table with map of line number to list of diagnostics. -- Structured: { [1] = {...}, [5] = {.... } } function M.get_line_diagnostics(bufnr, line_nr, opts, client_id) @@ -432,7 +449,7 @@ end --- endif --- return sl --- endfunction ---- let &l:statusline = '%#MyStatuslineLSP#LSP '.LspStatus() +--- autocmd BufWinEnter * let &l:statusline = '%#MyStatuslineLSP#LSP '.LspStatus() --- </pre> --- ---@param bufnr number The buffer number @@ -455,63 +472,64 @@ end -- }}} -- Diagnostic Movements {{{ ---- Helper function to iterate through all of the diagnostic lines ----@return table list of diagnostics -local _iter_diagnostic_lines = function(start, finish, step, bufnr, opts, client_id) - if bufnr == nil then - bufnr = vim.api.nvim_get_current_buf() - end - +--- Helper function to find the next diagnostic relative to a position +---@return table the next diagnostic if found +local _next_diagnostic = function(position, search_forward, bufnr, opts, client_id) + position[1] = position[1] - 1 + bufnr = bufnr or vim.api.nvim_get_current_buf() local wrap = if_nil(opts.wrap, true) - - local search = function(search_start, search_finish, search_step) - for line_nr = search_start, search_finish, search_step do - local line_diagnostics = M.get_line_diagnostics(bufnr, line_nr, opts, client_id) - if line_diagnostics and not vim.tbl_isempty(line_diagnostics) then - return line_diagnostics + local line_count = vim.api.nvim_buf_line_count(bufnr) + for i = 0, line_count do + local offset = i * (search_forward and 1 or -1) + local line_nr = position[1] + offset + if line_nr < 0 or line_nr >= line_count then + if not wrap then + return end + line_nr = (line_nr + line_count) % line_count end - end - - local result = search(start, finish, step) - - if wrap then - local wrap_start, wrap_finish - if step == 1 then - wrap_start, wrap_finish = 1, start - else - wrap_start, wrap_finish = vim.api.nvim_buf_line_count(bufnr), start - end - - if not result then - result = search(wrap_start, wrap_finish, step) + local line_diagnostics = M.get_line_diagnostics(bufnr, line_nr, opts, client_id) + if line_diagnostics and not vim.tbl_isempty(line_diagnostics) then + local sort_diagnostics, is_next + if search_forward then + sort_diagnostics = function(a, b) return a.range.start.character < b.range.start.character end + is_next = function(diagnostic) return diagnostic.range.start.character > position[2] end + else + sort_diagnostics = function(a, b) return a.range.start.character > b.range.start.character end + is_next = function(diagnostic) return diagnostic.range.start.character < position[2] end + end + table.sort(line_diagnostics, sort_diagnostics) + if i == 0 then + for _, v in pairs(line_diagnostics) do + if is_next(v) then + return v + end + end + else + return line_diagnostics[1] + end end end - - return result end ---@private ---- Helper function to ierate through diagnostic lines and return a position +---@private +--- Helper function to return a position from a diagnostic --- ---@return table {row, col} -local function _iter_diagnostic_lines_pos(opts, line_diagnostics) +local function _diagnostic_pos(opts, diagnostic) opts = opts or {} local win_id = opts.win_id or vim.api.nvim_get_current_win() local bufnr = vim.api.nvim_win_get_buf(win_id) - if line_diagnostics == nil or vim.tbl_isempty(line_diagnostics) then - return false - end + if not diagnostic then return false end - local iter_diagnostic = line_diagnostics[1] - return to_position(iter_diagnostic.range.start, bufnr) + return to_position(diagnostic.range.start, bufnr) end ---@private +---@private -- Move to the diagnostic position -local function _iter_diagnostic_move_pos(name, opts, pos) +local function _diagnostic_move_pos(name, opts, pos) opts = opts or {} local enable_popup = if_nil(opts.enable_popup, true) @@ -527,7 +545,7 @@ local function _iter_diagnostic_move_pos(name, opts, pos) if enable_popup then -- This is a bit weird... I'm surprised that we need to wait til the next tick to do this. vim.schedule(function() - M.show_line_diagnostics(opts.popup_opts, vim.api.nvim_win_get_buf(win_id)) + M.show_position_diagnostics(opts.popup_opts, vim.api.nvim_win_get_buf(win_id)) end) end end @@ -543,14 +561,14 @@ function M.get_prev(opts) local bufnr = vim.api.nvim_win_get_buf(win_id) local cursor_position = opts.cursor_position or vim.api.nvim_win_get_cursor(win_id) - return _iter_diagnostic_lines(cursor_position[1] - 2, 0, -1, bufnr, opts, opts.client_id) + return _next_diagnostic(cursor_position, false, bufnr, opts, opts.client_id) end --- Return the pos, {row, col}, for the prev diagnostic in the current buffer. ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic position function M.get_prev_pos(opts) - return _iter_diagnostic_lines_pos( + return _diagnostic_pos( opts, M.get_prev(opts) ) @@ -559,7 +577,7 @@ end --- Move to the previous diagnostic ---@param opts table See |vim.lsp.diagnostic.goto_next()| function M.goto_prev(opts) - return _iter_diagnostic_move_pos( + return _diagnostic_move_pos( "DiagnosticPrevious", opts, M.get_prev_pos(opts) @@ -576,14 +594,14 @@ function M.get_next(opts) local bufnr = vim.api.nvim_win_get_buf(win_id) local cursor_position = opts.cursor_position or vim.api.nvim_win_get_cursor(win_id) - return _iter_diagnostic_lines(cursor_position[1], vim.api.nvim_buf_line_count(bufnr), 1, bufnr, opts, opts.client_id) + return _next_diagnostic(cursor_position, true, bufnr, opts, opts.client_id) end --- Return the pos, {row, col}, for the next diagnostic in the current buffer. ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic position function M.get_next_pos(opts) - return _iter_diagnostic_lines_pos( + return _diagnostic_pos( opts, M.get_next(opts) ) @@ -608,7 +626,7 @@ end --- - {win_id}: (number, default 0) --- - Window ID function M.goto_next(opts) - return _iter_diagnostic_move_pos( + return _diagnostic_move_pos( "DiagnosticNext", opts, M.get_next_pos(opts) @@ -757,17 +775,20 @@ function M.set_virtual_text(diagnostics, bufnr, client_id, diagnostic_ns, opts) local virt_texts = M.get_virtual_text_chunks_for_line(bufnr, line, line_diagnostics, opts) if virt_texts then - api.nvim_buf_set_virtual_text(bufnr, diagnostic_ns, line, virt_texts, {}) + api.nvim_buf_set_extmark(bufnr, diagnostic_ns, line, 0, { + virt_text = virt_texts, + }) end end end ---- Default function to get text chunks to display using `nvim_buf_set_virtual_text`. +--- Default function to get text chunks to display using |nvim_buf_set_extmark()|. ---@param bufnr number The buffer to display the virtual text in ---@param line number The line number to display the virtual text on ---@param line_diags Diagnostic[] The diagnostics associated with the line ---@param opts table See {opts} from |vim.lsp.diagnostic.set_virtual_text()| ----@return table chunks, as defined by |nvim_buf_set_virtual_text()| +---@return an array of [text, hl_group] arrays. This can be passed directly to +--- the {virt_text} option of |nvim_buf_set_extmark()|. function M.get_virtual_text_chunks_for_line(bufnr, line, line_diags, opts) assert(bufnr or line) @@ -810,10 +831,7 @@ end ---@param diagnostic_ns number|nil Associated diagnostic namespace ---@param sign_ns number|nil Associated sign namespace function M.clear(bufnr, client_id, diagnostic_ns, sign_ns) - validate { bufnr = { bufnr, 'n' } } - - bufnr = (bufnr == 0 and api.nvim_get_current_buf()) or bufnr - + bufnr = get_bufnr(bufnr) if client_id == nil then return vim.lsp.for_each_buffer_client(bufnr, function(_, iter_client_id, _) return M.clear(bufnr, iter_client_id) @@ -822,6 +840,7 @@ function M.clear(bufnr, client_id, diagnostic_ns, sign_ns) diagnostic_ns = diagnostic_ns or M._get_diagnostic_namespace(client_id) sign_ns = sign_ns or M._get_sign_namespace(client_id) + diagnostic_cache_extmarks[bufnr][client_id] = {} assert(bufnr, "bufnr is required") assert(diagnostic_ns, "Need diagnostic_ns, got nil") @@ -839,7 +858,7 @@ end --- Callback scheduled for after leaving insert mode --- --- Used to handle ---@private +---@private function M._execute_scheduled_display(bufnr, client_id) local args = _bufs_waiting_to_update[bufnr][client_id] if not args then @@ -905,20 +924,20 @@ end -- Diagnostic Private Highlight Utilies {{{ --- Get the severity highlight name ---@private +---@private function M._get_severity_highlight_name(severity) return virtual_text_highlight_map[severity] end --- Get floating severity highlight name ---@private +---@private function M._get_floating_severity_highlight_name(severity) return floating_highlight_map[severity] end --- This should be called to update the highlights for the LSP client. function M._define_default_signs_and_highlights() - --@private + ---@private local function define_default_sign(name, properties) if vim.tbl_isempty(vim.fn.sign_getdefined(name)) then vim.fn.sign_define(name, properties) @@ -1001,15 +1020,16 @@ end --- - Update diagnostics in InsertMode or wait until InsertLeave --- - severity_sort: (default=false) --- - Sort diagnostics (and thus signs and virtual text) -function M.on_publish_diagnostics(_, _, params, client_id, _, config) - local uri = params.uri +function M.on_publish_diagnostics(_, result, ctx, config) + local client_id = ctx.client_id + local uri = result.uri local bufnr = vim.uri_to_bufnr(uri) if not bufnr then return end - local diagnostics = params.diagnostics + local diagnostics = result.diagnostics if config and if_nil(config.severity_sort, false) then table.sort(diagnostics, function(a, b) return a.severity > b.severity end) @@ -1034,9 +1054,61 @@ function M.on_publish_diagnostics(_, _, params, client_id, _, config) M.display(diagnostics, bufnr, client_id, config) end ---@private +-- restores the extmarks set by M.display +---@param last number last line that was changed +---@private +local function restore_extmarks(bufnr, last) + for client_id, extmarks in pairs(diagnostic_cache_extmarks[bufnr]) do + local ns = M._get_diagnostic_namespace(client_id) + local extmarks_current = api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, {details = true}) + local found = {} + for _, extmark in ipairs(extmarks_current) do + -- nvim_buf_set_lines will move any extmark to the line after the last + -- nvim_buf_set_text will move any extmark to the last line + if extmark[2] ~= last + 1 then + found[extmark[1]] = true + end + end + for _, extmark in ipairs(extmarks) do + if not found[extmark[1]] then + local opts = extmark[4] + opts.id = extmark[1] + -- HACK: end_row should be end_line + if opts.end_row then + opts.end_line = opts.end_row + opts.end_row = nil + end + pcall(api.nvim_buf_set_extmark, bufnr, ns, extmark[2], extmark[3], opts) + end + end + end +end + +-- caches the extmarks set by M.display +---@private +local function save_extmarks(bufnr, client_id) + bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr + if not diagnostic_attached_buffers[bufnr] then + api.nvim_buf_attach(bufnr, false, { + on_lines = function(_, _, _, _, _, last) + restore_extmarks(bufnr, last - 1) + end, + on_detach = function() + diagnostic_cache_extmarks[bufnr] = nil + end}) + diagnostic_attached_buffers[bufnr] = true + end + local ns = M._get_diagnostic_namespace(client_id) + diagnostic_cache_extmarks[bufnr][client_id] = api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, {details = true}) +end + +---@private --- Display diagnostics for the buffer, given a configuration. function M.display(diagnostics, bufnr, client_id, config) + if diagnostic_disabled[bufnr][client_id] then + return + end + config = vim.lsp._with_extend('vim.lsp.diagnostic.on_publish_diagnostics', { signs = true, underline = true, @@ -1104,11 +1176,50 @@ function M.display(diagnostics, bufnr, client_id, config) if signs_opts then M.set_signs(diagnostics, bufnr, client_id, nil, signs_opts) end + + -- cache extmarks + save_extmarks(bufnr, client_id) end --- }}} --- Diagnostic User Functions {{{ ---- Open a floating window with the diagnostics from {line_nr} +--- Redraw diagnostics for the given buffer and client +--- +--- This calls the "textDocument/publishDiagnostics" handler manually using +--- the cached diagnostics already received from the server. This can be useful +--- for redrawing diagnostics after making changes in diagnostics +--- configuration. |lsp-handler-configuration| +--- +---@param bufnr (optional, number): Buffer handle, defaults to current +---@param client_id (optional, number): Redraw diagnostics for the given +--- client. The default is to redraw diagnostics for all attached +--- clients. +function M.redraw(bufnr, client_id) + bufnr = get_bufnr(bufnr) + if not client_id then + return vim.lsp.for_each_buffer_client(bufnr, function(client) + M.redraw(bufnr, client.id) + end) + end + + -- We need to invoke the publishDiagnostics handler directly instead of just + -- calling M.display so that we can preserve any custom configuration options + -- the user may have set with vim.lsp.with. + vim.lsp.handlers["textDocument/publishDiagnostics"]( + nil, + { + uri = vim.uri_from_bufnr(bufnr), + diagnostics = M.get(bufnr, client_id), + }, + { + method = "textDocument/publishDiagnostics", + client_id = client_id, + bufnr = bufnr, + } + ) + end + + +---@private +--- Open a floating window with the provided diagnostics --- --- The floating window can be customized with the following highlight groups: --- <pre> @@ -1118,30 +1229,21 @@ end --- LspDiagnosticsFloatingHint --- </pre> ---@param opts table Configuration table ---- - show_header (boolean, default true): Show "Diagnostics:" header. ----@param bufnr number The buffer number ----@param line_nr number The line number ----@param client_id number|nil the client id +--- - show_header (boolean, default true): Show "Diagnostics:" header +--- - all opts for |vim.lsp.util.open_floating_preview()| can be used here +---@param diagnostics table: The diagnostics to display ---@return table {popup_bufnr, win_id} -function M.show_line_diagnostics(opts, bufnr, line_nr, client_id) - opts = opts or {} - - local show_header = if_nil(opts.show_header, true) - - bufnr = bufnr or 0 - line_nr = line_nr or (vim.api.nvim_win_get_cursor(0)[1] - 1) - +local function show_diagnostics(opts, diagnostics) + if vim.tbl_isempty(diagnostics) then return end local lines = {} local highlights = {} + local show_header = if_nil(opts.show_header, true) if show_header then table.insert(lines, "Diagnostics:") table.insert(highlights, {0, "Bold"}) end - local line_diagnostics = M.get_line_diagnostics(bufnr, line_nr, opts, client_id) - if vim.tbl_isempty(line_diagnostics) then return end - - for i, diagnostic in ipairs(line_diagnostics) do + for i, diagnostic in ipairs(diagnostics) do local prefix = string.format("%d. ", i) local hiname = M._get_floating_severity_highlight_name(diagnostic.severity) assert(hiname, 'unknown severity: ' .. tostring(diagnostic.severity)) @@ -1150,12 +1252,11 @@ function M.show_line_diagnostics(opts, bufnr, line_nr, client_id) table.insert(lines, prefix..message_lines[1]) table.insert(highlights, {#prefix, hiname}) for j = 2, #message_lines do - table.insert(lines, message_lines[j]) + table.insert(lines, string.rep(' ', #prefix) .. message_lines[j]) table.insert(highlights, {0, hiname}) end end - opts.focus_id = "line_diagnostics" local popup_bufnr, winnr = util.open_floating_preview(lines, 'plaintext', opts) for i, hi in ipairs(highlights) do local prefixlen, hiname = unpack(hi) @@ -1167,6 +1268,60 @@ function M.show_line_diagnostics(opts, bufnr, line_nr, client_id) end +-- }}} +-- Diagnostic User Functions {{{ + +--- Open a floating window with the diagnostics from {position} +---@param opts table|nil Configuration keys +--- - severity: (DiagnosticSeverity, default nil) +--- - Only return diagnostics with this severity. Overrides severity_limit +--- - severity_limit: (DiagnosticSeverity, default nil) +--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. +--- - all opts for |show_diagnostics()| can be used here +---@param buf_nr number|nil The buffer number +---@param position table|nil The (0,0)-indexed position +---@return table {popup_bufnr, win_id} +function M.show_position_diagnostics(opts, buf_nr, position) + opts = opts or {} + opts.focus_id = "position_diagnostics" + buf_nr = buf_nr or vim.api.nvim_get_current_buf() + if not position then + local curr_position = vim.api.nvim_win_get_cursor(0) + curr_position[1] = curr_position[1] - 1 + position = curr_position + end + local match_position_predicate = function(diag) + return position[1] == diag.range['start'].line and + position[2] >= diag.range['start'].character and + (position[2] <= diag.range['end'].character or position[1] < diag.range['end'].line) + end + local position_diagnostics = M.get(buf_nr, nil, match_position_predicate) + if opts.severity then + position_diagnostics = filter_to_severity_limit(opts.severity, position_diagnostics) + elseif opts.severity_limit then + position_diagnostics = filter_by_severity_limit(opts.severity_limit, position_diagnostics) + end + table.sort(position_diagnostics, function(a, b) return a.severity < b.severity end) + return show_diagnostics(opts, position_diagnostics) +end + +--- Open a floating window with the diagnostics from {line_nr} + +---@param opts table Configuration table +--- - all opts for |vim.lsp.diagnostic.get_line_diagnostics()| and +--- |show_diagnostics()| can be used here +---@param buf_nr number|nil The buffer number +---@param line_nr number|nil The line number +---@param client_id number|nil the client id +---@return table {popup_bufnr, win_id} +function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id) + opts = opts or {} + opts.focus_id = "line_diagnostics" + line_nr = line_nr or (vim.api.nvim_win_get_cursor(0)[1] - 1) + local line_diagnostics = M.get_line_diagnostics(buf_nr, line_nr, opts, client_id) + return show_diagnostics(opts, line_diagnostics) +end + --- Clear diagnotics and diagnostic cache --- --- Handles saving diagnostics from multiple clients in the same buffer. @@ -1184,10 +1339,11 @@ function M.reset(client_id, buffer_client_map) end) end ---- Sets the location list +---@private +--- Gets diagnostics, converts them to quickfix/location list items, and applies the item_handler callback to the items. +---@param item_handler function Callback to apply to the diagnostic items +---@param command string|nil Command to execute after applying the item_handler ---@param opts table|nil Configuration table. Keys: ---- - {open_loclist}: (boolean, default true) ---- - Open loclist after set --- - {client_id}: (number) --- - If nil, will consider all clients attached to buffer. --- - {severity}: (DiagnosticSeverity) @@ -1196,9 +1352,8 @@ end --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. --- - {workspace}: (boolean, default false) --- - Set the list with workspace diagnostics -function M.set_loclist(opts) +local function apply_to_diagnostic_items(item_handler, command, opts) opts = opts or {} - local open_loclist = if_nil(opts.open_loclist, true) local current_bufnr = api.nvim_get_current_buf() local diags = opts.workspace and M.get_all(opts.client_id) or { [current_bufnr] = M.get(current_bufnr, opts.client_id) @@ -1208,19 +1363,97 @@ function M.set_loclist(opts) if severity then return d.severity == severity end - severity = to_severity(opts.severity_limit) - if severity then - return d.severity == severity + local severity_limit = to_severity(opts.severity_limit) + if severity_limit then + return d.severity <= severity_limit end return true end local items = util.diagnostics_to_items(diags, predicate) - local win_id = vim.api.nvim_get_current_win() - util.set_loclist(items, win_id) - if open_loclist then - vim.cmd [[lopen]] + item_handler(items) + if command then + vim.cmd(command) end end + +--- Sets the quickfix list +---@param opts table|nil Configuration table. Keys: +--- - {open}: (boolean, default true) +--- - Open quickfix list after set +--- - {client_id}: (number) +--- - If nil, will consider all clients attached to buffer. +--- - {severity}: (DiagnosticSeverity) +--- - Exclusive severity to consider. Overrides {severity_limit} +--- - {severity_limit}: (DiagnosticSeverity) +--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. +--- - {workspace}: (boolean, default true) +--- - Set the list with workspace diagnostics +function M.set_qflist(opts) + opts = opts or {} + opts.workspace = if_nil(opts.workspace, true) + local open_qflist = if_nil(opts.open, true) + local command = open_qflist and [[copen]] or nil + apply_to_diagnostic_items(util.set_qflist, command, opts) +end + +--- Sets the location list +---@param opts table|nil Configuration table. Keys: +--- - {open}: (boolean, default true) +--- - Open loclist after set +--- - {client_id}: (number) +--- - If nil, will consider all clients attached to buffer. +--- - {severity}: (DiagnosticSeverity) +--- - Exclusive severity to consider. Overrides {severity_limit} +--- - {severity_limit}: (DiagnosticSeverity) +--- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. +--- - {workspace}: (boolean, default false) +--- - Set the list with workspace diagnostics +function M.set_loclist(opts) + opts = opts or {} + local open_loclist = if_nil(opts.open, true) + local command = open_loclist and [[lopen]] or nil + apply_to_diagnostic_items(util.set_loclist, command, opts) +end + +--- Disable diagnostics for the given buffer and client +---@param bufnr (optional, number): Buffer handle, defaults to current +---@param client_id (optional, number): Disable diagnostics for the given +--- client. The default is to disable diagnostics for all attached +--- clients. +-- Note that when diagnostics are disabled for a buffer, the server will still +-- send diagnostic information and the client will still process it. The +-- diagnostics are simply not displayed to the user. +function M.disable(bufnr, client_id) + if not client_id then + return vim.lsp.for_each_buffer_client(bufnr, function(client) + M.disable(bufnr, client.id) + end) + end + + diagnostic_disabled[bufnr][client_id] = true + M.clear(bufnr, client_id) +end + +--- Enable diagnostics for the given buffer and client +---@param bufnr (optional, number): Buffer handle, defaults to current +---@param client_id (optional, number): Enable diagnostics for the given +--- client. The default is to enable diagnostics for all attached +--- clients. +function M.enable(bufnr, client_id) + if not client_id then + return vim.lsp.for_each_buffer_client(bufnr, function(client) + M.enable(bufnr, client.id) + end) + end + + if not diagnostic_disabled[bufnr][client_id] then + return + end + + diagnostic_disabled[bufnr][client_id] = nil + + M.redraw(bufnr, client_id) +end -- }}} return M diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 6ae54ea253..8fa6f6d024 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -9,31 +9,29 @@ local M = {} -- FIXME: DOC: Expose in vimdocs ---@private +---@private --- Writes to error buffer. ---@param ... (table of strings) Will be concatenated before being written +---@param ... (table of strings) Will be concatenated before being written local function err_message(...) vim.notify(table.concat(vim.tbl_flatten{...}), vim.log.levels.ERROR) api.nvim_command("redraw") end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand -M['workspace/executeCommand'] = function(err, _) - if err then - error("Could not execute code action: "..err.message) - end +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_executeCommand +M['workspace/executeCommand'] = function(_, _, _, _) + -- Error handling is done implicitly by wrapping all handlers; see end of this file end --- @msg of type ProgressParams --- Basically a token of type number/string -local function progress_handler(_, _, params, client_id) +---@private +local function progress_handler(_, result, ctx, _) + local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) local client_name = client and client.name or string.format("id=%d", client_id) if not client then err_message("LSP[", client_name, "] client has shut down after sending the message") end - local val = params.value -- unspecified yet - local token = params.token -- string or number + local val = result.value -- unspecified yet + local token = result.token -- string or number if val.kind then @@ -61,13 +59,14 @@ local function progress_handler(_, _, params, client_id) vim.api.nvim_command("doautocmd <nomodeline> User LspProgressUpdate") end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress M['$/progress'] = progress_handler ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create -M['window/workDoneProgress/create'] = function(_, _, params, client_id) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create +M['window/workDoneProgress/create'] = function(_, result, ctx) + local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) - local token = params.token -- string or number + local token = result.token -- string or number local client_name = client and client.name or string.format("id=%d", client_id) if not client then err_message("LSP[", client_name, "] client has shut down after sending the message") @@ -76,12 +75,12 @@ M['window/workDoneProgress/create'] = function(_, _, params, client_id) return vim.NIL end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest -M['window/showMessageRequest'] = function(_, _, params) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessageRequest +M['window/showMessageRequest'] = function(_, result) - local actions = params.actions - print(params.message) - local option_strings = {params.message, "\nRequest Actions:"} + local actions = result.actions + print(result.message) + local option_strings = {result.message, "\nRequest Actions:"} for i, action in ipairs(actions) do local title = action.title:gsub('\r\n', '\\r\\n') title = title:gsub('\n', '\\n') @@ -97,8 +96,9 @@ M['window/showMessageRequest'] = function(_, _, params) end end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability -M['client/registerCapability'] = function(_, _, _, client_id) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#client_registerCapability +M['client/registerCapability'] = function(_, _, ctx) + local client_id = ctx.client_id local warning_tpl = "The language server %s triggers a registerCapability ".. "handler despite dynamicRegistration set to false. ".. "Report upstream, this warning is harmless" @@ -109,25 +109,25 @@ M['client/registerCapability'] = function(_, _, _, client_id) return vim.NIL end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction -M['textDocument/codeAction'] = function(_, _, actions) - if actions == nil or vim.tbl_isempty(actions) then +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction +M['textDocument/codeAction'] = function(_, result) + if result == nil or vim.tbl_isempty(result) then print("No code actions available") return end - local option_strings = {"Code Actions:"} - for i, action in ipairs(actions) do + local option_strings = {"Code actions:"} + for i, action in ipairs(result) do local title = action.title:gsub('\r\n', '\\r\\n') title = title:gsub('\n', '\\n') table.insert(option_strings, string.format("%d. %s", i, title)) end local choice = vim.fn.inputlist(option_strings) - if choice < 1 or choice > #actions then + if choice < 1 or choice > #result then return end - local action_chosen = actions[choice] + local action_chosen = result[choice] -- textDocument/codeAction can return either Command[] or CodeAction[]. -- If it is a CodeAction, it can have either an edit, a command or both. -- Edits should be executed first @@ -143,8 +143,8 @@ M['textDocument/codeAction'] = function(_, _, actions) end end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit -M['workspace/applyEdit'] = function(_, _, workspace_edit) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit +M['workspace/applyEdit'] = function(_, workspace_edit) if not workspace_edit then return end -- TODO(ashkan) Do something more with label? if workspace_edit.label then @@ -157,83 +157,99 @@ M['workspace/applyEdit'] = function(_, _, workspace_edit) } end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration -M['workspace/configuration'] = function(err, _, params, client_id) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration +M['workspace/configuration'] = function(_, result, ctx) + local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) if not client then err_message("LSP[id=", client_id, "] client has shut down after sending the message") return end - if err then error(vim.inspect(err)) end - if not params.items then + if not result.items then return {} end - local result = {} - for _, item in ipairs(params.items) do + local response = {} + for _, item in ipairs(result.items) do if item.section then local value = util.lookup_section(client.config.settings, item.section) or vim.NIL -- For empty sections with no explicit '' key, return settings as is if value == vim.NIL and item.section == '' then value = client.config.settings or vim.NIL end - table.insert(result, value) + table.insert(response, value) end end - return result + return response end M['textDocument/publishDiagnostics'] = function(...) return require('vim.lsp.diagnostic').on_publish_diagnostics(...) end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references -M['textDocument/references'] = function(_, _, result) - if not result then return end - util.set_qflist(util.locations_to_items(result)) - api.nvim_command("copen") - api.nvim_command("wincmd p") +M['textDocument/codeLens'] = function(...) + return require('vim.lsp.codelens').on_codelens(...) end ---@private ---- Prints given list of symbols to the quickfix list. ---@param _ (not used) ---@param _ (not used) ---@param result (list of Symbols) LSP method name ---@param result (table) result of LSP method; a location or a list of locations. ----(`textDocument/definition` can return `Location` or `Location[]` -local symbol_handler = function(_, _, result, _, bufnr) - if not result or vim.tbl_isempty(result) then return end - util.set_qflist(util.symbols_to_items(result, bufnr)) - api.nvim_command("copen") - api.nvim_command("wincmd p") + +---@private +--- 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) +--- +---@param map_result function `((resp, bufnr) -> list)` to convert the response +---@param entity name of the resource used in a `not found` error message +local function response_to_list(map_result, entity) + return function(_,result, ctx, config) + if not result or vim.tbl_isempty(result) then + vim.notify('No ' .. entity .. ' found') + else + config = config or {} + if config.loclist then + util.set_loclist(map_result(result, ctx.bufnr)) + api.nvim_command("lopen") + else + util.set_qflist(map_result(result, ctx.bufnr)) + api.nvim_command("copen") + end + end + end end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol -M['textDocument/documentSymbol'] = symbol_handler ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol -M['workspace/symbol'] = symbol_handler ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename -M['textDocument/rename'] = function(_, _, result) + +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references +M['textDocument/references'] = response_to_list(util.locations_to_items, 'references') + +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol +M['textDocument/documentSymbol'] = response_to_list(util.symbols_to_items, 'document symbols') + +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_symbol +M['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols') + +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rename +M['textDocument/rename'] = function(_, result, _) if not result then return end util.apply_workspace_edit(result) end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting -M['textDocument/rangeFormatting'] = function(_, _, result) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_rangeFormatting +M['textDocument/rangeFormatting'] = function(_, result, ctx, _) if not result then return end - util.apply_text_edits(result) + util.apply_text_edits(result, ctx.bufnr) end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting -M['textDocument/formatting'] = function(_, _, result) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting +M['textDocument/formatting'] = function(_, result, ctx, _) if not result then return end - util.apply_text_edits(result) + util.apply_text_edits(result, ctx.bufnr) end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion -M['textDocument/completion'] = function(_, _, result) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +M['textDocument/completion'] = function(_, result, _, _) if vim.tbl_isempty(result or {}) then return end local row, col = unpack(api.nvim_win_get_cursor(0)) local line = assert(api.nvim_buf_get_lines(0, row-1, row, false)[1]) @@ -258,9 +274,9 @@ end --- - border: (default=nil) --- - Add borders to the floating window --- - See |vim.api.nvim_open_win()| -function M.hover(_, method, result, _, _, config) +function M.hover(_, result, ctx, config) config = config or {} - config.focus_id = method + config.focus_id = ctx.method if not (result and result.contents) then -- return { 'No information available' } return @@ -274,18 +290,18 @@ function M.hover(_, method, result, _, _, config) return util.open_floating_preview(markdown_lines, "markdown", config) end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover M['textDocument/hover'] = M.hover ---@private +---@private --- Jumps to a location. Used as a handler for multiple LSP methods. ---@param _ (not used) ---@param method (string) LSP method name ---@param result (table) result of LSP method; a location or a list of locations. +---@param _ (not used) +---@param result (table) result of LSP method; a location or a list of locations. +---@param ctx (table) table containing the context of the request, including the method ---(`textDocument/definition` can return `Location` or `Location[]` -local function location_handler(_, method, result) +local function location_handler(_, result, ctx, _) if result == nil or vim.tbl_isempty(result) then - local _ = log.info() and log.info(method, 'No location found') + local _ = log.info() and log.info(ctx.method, 'No location found') return nil end @@ -298,23 +314,23 @@ local function location_handler(_, method, result) if #result > 1 then util.set_qflist(util.locations_to_items(result)) api.nvim_command("copen") - api.nvim_command("wincmd p") end else util.jump_to_location(result) end end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_declaration M['textDocument/declaration'] = location_handler ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition M['textDocument/definition'] = location_handler ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_typeDefinition +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_typeDefinition M['textDocument/typeDefinition'] = location_handler ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_implementation +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_implementation M['textDocument/implementation'] = location_handler ---- |lsp-handler| for the method "textDocument/signatureHelp" +--- |lsp-handler| for the method "textDocument/signatureHelp". +--- The active parameter is highlighted with |hl-LspSignatureActiveParameter|. --- <pre> --- vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with( --- vim.lsp.handlers.signature_help, { @@ -327,43 +343,53 @@ M['textDocument/implementation'] = location_handler --- - border: (default=nil) --- - Add borders to the floating window --- - See |vim.api.nvim_open_win()| -function M.signature_help(_, method, result, _, bufnr, config) +function M.signature_help(_, result, ctx, config) config = config or {} - config.focus_id = method + config.focus_id = ctx.method -- When use `autocmd CompleteDone <silent><buffer> lua vim.lsp.buf.signature_help()` to call signatureHelp handler -- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `<silent>` to ignore if not (result and result.signatures and result.signatures[1]) then - print('No signature help available') + if config.silent ~= true then + print('No signature help available') + end return end - local ft = api.nvim_buf_get_option(bufnr, 'filetype') - local lines = util.convert_signature_help_to_markdown_lines(result, ft) + local client = vim.lsp.get_client_by_id(ctx.client_id) + local triggers = client.resolved_capabilities.signature_help_trigger_characters + local ft = api.nvim_buf_get_option(ctx.bufnr, 'filetype') + local lines, hl = util.convert_signature_help_to_markdown_lines(result, ft, triggers) lines = util.trim_empty_lines(lines) if vim.tbl_isempty(lines) then - print('No signature help available') + if config.silent ~= true then + print('No signature help available') + end return end - return util.open_floating_preview(lines, "markdown", config) + local fbuf, fwin = util.open_floating_preview(lines, "markdown", config) + if hl then + api.nvim_buf_add_highlight(fbuf, -1, "LspSignatureActiveParameter", 0, unpack(hl)) + end + return fbuf, fwin end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp M['textDocument/signatureHelp'] = M.signature_help ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight -M['textDocument/documentHighlight'] = function(_, _, result, _, bufnr, _) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentHighlight +M['textDocument/documentHighlight'] = function(_, result, ctx, _) if not result then return end - util.buf_highlight_references(bufnr, result) + util.buf_highlight_references(ctx.bufnr, result) end ---@private +---@private --- --- Displays call hierarchy in the quickfix window. --- ---@param direction `"from"` for incoming calls and `"to"` for outgoing calls ---@returns `CallHierarchyIncomingCall[]` if {direction} is `"from"`, ---@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`, +---@param direction `"from"` for incoming calls and `"to"` for outgoing calls +---@returns `CallHierarchyIncomingCall[]` if {direction} is `"from"`, +---@returns `CallHierarchyOutgoingCall[]` if {direction} is `"to"`, local make_call_hierarchy_handler = function(direction) - return function(_, _, result) + return function(_, result) if not result then return end local items = {} for _, call_hierarchy_call in pairs(result) do @@ -379,20 +405,20 @@ local make_call_hierarchy_handler = function(direction) end util.set_qflist(items) api.nvim_command("copen") - api.nvim_command("wincmd p") end end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy/incomingCalls +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_incomingCalls M['callHierarchy/incomingCalls'] = make_call_hierarchy_handler('from') ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy/outgoingCalls +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_outgoingCalls M['callHierarchy/outgoingCalls'] = make_call_hierarchy_handler('to') ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/logMessage -M['window/logMessage'] = function(_, _, result, client_id) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_logMessage +M['window/logMessage'] = function(_, result, ctx, _) local message_type = result.type local message = result.message + local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) local client_name = client and client.name or string.format("id=%d", client_id) if not client then @@ -410,10 +436,11 @@ M['window/logMessage'] = function(_, _, result, client_id) return result end ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window/showMessage -M['window/showMessage'] = function(_, _, result, client_id) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_showMessage +M['window/showMessage'] = function(_, result, ctx, _) local message_type = result.type local message = result.message + local client_id = ctx.client_id local client = vim.lsp.get_client_by_id(client_id) local client_name = client and client.name or string.format("id=%d", client_id) if not client then @@ -430,16 +457,23 @@ end -- Add boilerplate error validation and logging for all of these. for k, fn in pairs(M) do - M[k] = function(err, method, params, client_id, bufnr, config) - local _ = log.debug() and log.debug('default_handler', method, { - params = params, client_id = client_id, err = err, bufnr = bufnr, config = config + M[k] = function(err, result, ctx, config) + local _ = log.debug() and log.debug('default_handler', ctx.method, { + err = err, result = result, ctx=vim.inspect(ctx), config = config }) if err then - return err_message(tostring(err)) + local client = vim.lsp.get_client_by_id(ctx.client_id) + local client_name = client and client.name or string.format("client_id=%d", ctx.client_id) + -- LSP spec: + -- interface ResponseError: + -- code: integer; + -- message: string; + -- data?: string | number | boolean | array | object | null; + return err_message(client_name .. ': ' .. tostring(err.code) .. ': ' .. err.message) end - return fn(err, method, params, client_id, bufnr, config) + return fn(err, result, ctx, config) end end diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua new file mode 100644 index 0000000000..855679a2df --- /dev/null +++ b/runtime/lua/vim/lsp/health.lua @@ -0,0 +1,27 @@ +local M = {} + +--- Performs a healthcheck for LSP +function M.check_health() + local report_info = vim.fn['health#report_info'] + local report_warn = vim.fn['health#report_warn'] + + local log = require('vim.lsp.log') + local current_log_level = log.get_level() + local log_level_string = log.levels[current_log_level] + report_info(string.format("LSP log level : %s", log_level_string)) + + if current_log_level < log.levels.WARN then + report_warn(string.format("Log level %s will cause degraded performance and high disk usage", log_level_string)) + end + + local log_path = vim.lsp.get_log_path() + report_info(string.format("Log path: %s", log_path)) + + local log_size = vim.loop.fs_stat(log_path).size + + local report_fn = (log_size / 1000000 > 100 and report_warn or report_info) + report_fn(string.format("Log size: %d KB", log_size / 1000 )) +end + +return M + diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 471a311c16..5d2e396cc5 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -17,21 +17,32 @@ local current_log_level = log.levels.WARN local log_date_format = "%FT%H:%M:%S%z" do - local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/" - --@private + local path_sep = vim.loop.os_uname().version:match("Windows") and "\\" or "/" + ---@private local function path_join(...) return table.concat(vim.tbl_flatten{...}, path_sep) end local logfilename = path_join(vim.fn.stdpath('cache'), 'lsp.log') --- Returns the log filename. - --@returns (string) log filename + ---@returns (string) log filename function log.get_filename() return logfilename end vim.fn.mkdir(vim.fn.stdpath('cache'), "p") local logfile = assert(io.open(logfilename, "a+")) + + local log_info = vim.loop.fs_stat(logfilename) + if log_info and log_info.size > 1e9 then + local warn_msg = string.format( + "LSP client log is large (%d MB): %s", + log_info.size / (1000 * 1000), + logfilename + ) + vim.notify(warn_msg) + end + -- Start message for logging logfile:write(string.format("[ START ] %s ] LSP logging initiated\n", os.date(log_date_format))) for level, levelnr in pairs(log.levels) do @@ -77,7 +88,7 @@ end vim.tbl_add_reverse_lookup(log.levels) --- Sets the current log level. ---@param level (string or number) One of `vim.lsp.log.levels` +---@param level (string or number) One of `vim.lsp.log.levels` function log.set_level(level) if type(level) == 'string' then current_log_level = assert(log.levels[level:upper()], string.format("Invalid log level: %q", level)) @@ -88,9 +99,14 @@ function log.set_level(level) end end +--- Gets the current log level. +function log.get_level() + return current_log_level +end + --- Checks whether the level is sufficient for logging. ---@param level number log level ---@returns (bool) true if would log, false if not +---@param level number log level +---@returns (bool) true if would log, false if not function log.should_log(level) return level >= current_log_level end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 7e43eb84de..27703b4503 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -5,14 +5,14 @@ local if_nil = vim.F.if_nil local protocol = {} --[=[ ---@private +---@private --- Useful for interfacing with: --- https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md function transform_schema_comments() nvim.command [[silent! '<,'>g/\/\*\*\|\*\/\|^$/d]] nvim.command [[silent! '<,'>s/^\(\s*\) \* \=\(.*\)/\1--\2/]] end ---@private +---@private function transform_schema_to_table() transform_schema_comments() nvim.command [[silent! '<,'>s/: \S\+//]] @@ -691,10 +691,11 @@ function protocol.make_client_capabilities() signatureHelp = { dynamicRegistration = false; signatureInformation = { + activeParameterSupport = true; documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText }; - -- parameterInformation = { - -- labelOffsetSupport = false; - -- }; + parameterInformation = { + labelOffsetSupport = true; + }; }; }; references = { @@ -1002,8 +1003,7 @@ function protocol.resolve_capabilities(server_capabilities) elseif type(server_capabilities.declarationProvider) == 'boolean' then general_properties.declaration = server_capabilities.declarationProvider elseif type(server_capabilities.declarationProvider) == 'table' then - -- TODO: support more detailed declarationProvider options. - general_properties.declaration = false + general_properties.declaration = server_capabilities.declarationProvider else error("The server sent invalid declarationProvider") end @@ -1013,8 +1013,7 @@ function protocol.resolve_capabilities(server_capabilities) elseif type(server_capabilities.typeDefinitionProvider) == 'boolean' then general_properties.type_definition = server_capabilities.typeDefinitionProvider elseif type(server_capabilities.typeDefinitionProvider) == 'table' then - -- TODO: support more detailed typeDefinitionProvider options. - general_properties.type_definition = false + general_properties.type_definition = server_capabilities.typeDefinitionProvider else error("The server sent invalid typeDefinitionProvider") end @@ -1024,8 +1023,7 @@ function protocol.resolve_capabilities(server_capabilities) elseif type(server_capabilities.implementationProvider) == 'boolean' then general_properties.implementation = server_capabilities.implementationProvider elseif type(server_capabilities.implementationProvider) == 'table' then - -- TODO(ashkan) support more detailed implementation options. - general_properties.implementation = false + general_properties.implementation = server_capabilities.implementationProvider else error("The server sent invalid implementationProvider") end diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 98835d6708..eedb708118 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -5,11 +5,11 @@ local protocol = require('vim.lsp.protocol') local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap -- TODO replace with a better implementation. ---@private +---@private --- Encodes to JSON. --- ---@param data (table) Data to encode ---@returns (string) Encoded object +---@param data (table) Data to encode +---@returns (string) Encoded object local function json_encode(data) local status, result = pcall(vim.fn.json_encode, data) if status then @@ -18,11 +18,11 @@ local function json_encode(data) return nil, result end end ---@private +---@private --- Decodes from JSON. --- ---@param data (string) Data to decode ---@returns (table) Decoded JSON object +---@param data (string) Data to decode +---@returns (table) Decoded JSON object local function json_decode(data) local status, result = pcall(vim.fn.json_decode, data) if status then @@ -32,10 +32,10 @@ local function json_decode(data) end end ---@private +---@private --- Checks whether a given path exists and is a directory. ---@param filename (string) path to check ---@returns (bool) +---@param filename (string) path to check +---@returns (bool) local function is_dir(filename) local stat = vim.loop.fs_stat(filename) return stat and stat.type == 'directory' or false @@ -43,30 +43,35 @@ end local NIL = vim.NIL ---@private +---@private local recursive_convert_NIL recursive_convert_NIL = function(v, tbl_processed) if v == NIL then return nil elseif not tbl_processed[v] and type(v) == 'table' then tbl_processed[v] = true + local inside_list = vim.tbl_islist(v) return vim.tbl_map(function(x) - return recursive_convert_NIL(x, tbl_processed) + if not inside_list or (inside_list and type(x) == "table") then + return recursive_convert_NIL(x, tbl_processed) + else + return x + end end, v) end return v end ---@private +---@private --- Returns its argument, but converts `vim.NIL` to Lua `nil`. ---@param v (any) Argument ---@returns (any) +---@param v (any) Argument +---@returns (any) local function convert_NIL(v) return recursive_convert_NIL(v, {}) end ---@private +---@private --- Merges current process env with the given env and returns the result as --- a list of "k=v" strings. --- @@ -76,8 +81,8 @@ end --- in: { PRODUCTION="false", PATH="/usr/bin/", PORT=123, HOST="0.0.0.0", } --- out: { "PRODUCTION=false", "PATH=/usr/bin/", "PORT=123", "HOST=0.0.0.0", } --- </pre> ---@param env (table) table of environment variable assignments ---@returns (table) list of `"k=v"` strings +---@param env (table) table of environment variable assignments +---@returns (table) list of `"k=v"` strings local function env_merge(env) if env == nil then return env @@ -92,11 +97,11 @@ local function env_merge(env) return final_env end ---@private +---@private --- Embeds the given string into a table and correctly computes `Content-Length`. --- ---@param encoded_message (string) ---@returns (table) table containing encoded message and `Content-Length` attribute +---@param encoded_message (string) +---@returns (table) table containing encoded message and `Content-Length` attribute local function format_message_with_content_length(encoded_message) return table.concat { 'Content-Length: '; tostring(#encoded_message); '\r\n\r\n'; @@ -104,11 +109,11 @@ local function format_message_with_content_length(encoded_message) } end ---@private +---@private --- Parses an LSP Message's header --- ---@param header: The header to parse. ---@returns Parsed headers +---@param header: The header to parse. +---@returns Parsed headers local function parse_headers(header) if type(header) ~= 'string' then return nil @@ -136,7 +141,7 @@ end -- case insensitive pattern. local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end) ---@private +---@private --- The actual workhorse. local function request_parser_loop() local buffer = '' -- only for header part @@ -198,8 +203,8 @@ local client_errors = vim.tbl_add_reverse_lookup { --- Constructs an error message from an LSP error object. --- ---@param err (table) The error object ---@returns (string) The formatted error message +---@param err (table) The error object +---@returns (string) The formatted error message local function format_rpc_error(err) validate { err = { err, 't' }; @@ -228,9 +233,9 @@ end --- Creates an RPC response object/table. --- ---@param code RPC error code defined in `vim.lsp.protocol.ErrorCodes` ---@param message (optional) arbitrary message to send to server ---@param data (optional) arbitrary data to send to server +---@param code RPC error code defined in `vim.lsp.protocol.ErrorCodes` +---@param message (optional) arbitrary message to send to server +---@param data (optional) arbitrary data to send to server local function rpc_response_error(code, message, data) -- TODO should this error or just pick a sane error (like InternalError)? local code_name = assert(protocol.ErrorCodes[code], 'Invalid RPC error code') @@ -245,38 +250,38 @@ end local default_dispatchers = {} ---@private +---@private --- Default dispatcher for notifications sent to an LSP server. --- ---@param method (string) The invoked LSP method ---@param params (table): Parameters for the invoked LSP method +---@param method (string) The invoked LSP method +---@param params (table): Parameters for the invoked LSP method function default_dispatchers.notification(method, params) local _ = log.debug() and log.debug('notification', method, params) end ---@private +---@private --- Default dispatcher for requests sent to an LSP server. --- ---@param method (string) The invoked LSP method ---@param params (table): Parameters for the invoked LSP method ---@returns `nil` and `vim.lsp.protocol.ErrorCodes.MethodNotFound`. +---@param method (string) The invoked LSP method +---@param params (table): Parameters for the invoked LSP method +---@returns `nil` and `vim.lsp.protocol.ErrorCodes.MethodNotFound`. function default_dispatchers.server_request(method, params) local _ = log.debug() and log.debug('server_request', method, params) return nil, rpc_response_error(protocol.ErrorCodes.MethodNotFound) end ---@private +---@private --- Default dispatcher for when a client exits. --- ---@param code (number): Exit code ---@param signal (number): Number describing the signal used to terminate (if +---@param code (number): Exit code +---@param signal (number): Number describing the signal used to terminate (if ---any) function default_dispatchers.on_exit(code, signal) local _ = log.info() and log.info("client_exit", { code = code, signal = signal }) end ---@private +---@private --- Default dispatcher for client errors. --- ---@param code (number): Error code ---@param err (any): Details about the error +---@param code (number): Error code +---@param err (any): Details about the error ---any) function default_dispatchers.on_error(code, err) local _ = log.error() and log.error('client_error:', client_errors[code], err) @@ -285,25 +290,25 @@ end --- Starts an LSP server process and create an LSP RPC client object to --- interact with it. --- ---@param cmd (string) Command to start the LSP server. ---@param cmd_args (table) List of additional string arguments to pass to {cmd}. ---@param dispatchers (table, optional) Dispatchers for LSP message types. Valid +---@param cmd (string) Command to start the LSP server. +---@param cmd_args (table) List of additional string arguments to pass to {cmd}. +---@param dispatchers (table, optional) Dispatchers for LSP message types. Valid ---dispatcher names are: --- - `"notification"` --- - `"server_request"` --- - `"on_error"` --- - `"on_exit"` ---@param extra_spawn_params (table, optional) Additional context for the LSP +---@param extra_spawn_params (table, optional) Additional context for the LSP --- server process. May contain: --- - {cwd} (string) Working directory for the LSP server process --- - {env} (table) Additional environment variables for LSP server process ---@returns Client RPC object. +---@returns Client RPC object. --- ---@returns Methods: +---@returns Methods: --- - `notify()` |vim.lsp.rpc.notify()| --- - `request()` |vim.lsp.rpc.request()| --- ---@returns Members: +---@returns Members: --- - {pid} (number) The LSP server's PID. --- - {handle} A handle for low-level interaction with the LSP server process --- |vim.loop|. @@ -353,10 +358,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) local handle, pid do - --@private + ---@private --- Callback for |vim.loop.spawn()| Closes all streams and runs the `on_exit` dispatcher. - --@param code (number) Exit code - --@param signal (number) Signal that was used to terminate (if any) + ---@param code (number) Exit code + ---@param signal (number) Signal that was used to terminate (if any) local function onexit(code, signal) stdin:close() stdout:close() @@ -380,12 +385,12 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end end - --@private + ---@private --- Encodes {payload} into a JSON-RPC message and sends it to the remote --- process. --- - --@param payload (table) Converted into a JSON string, see |json_encode()| - --@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing. + ---@param payload (table) Converted into a JSON string, see |json_encode()| + ---@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing. local function encode_and_send(payload) local _ = log.debug() and log.debug("rpc.send.payload", payload) if handle == nil or handle:is_closing() then return false end @@ -401,9 +406,9 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) -- `start()` -- --- Sends a notification to the LSP server. - --@param method (string) The invoked LSP method - --@param params (table): Parameters for the invoked LSP method - --@returns (bool) `true` if notification could be sent, `false` if not + ---@param method (string) The invoked LSP method + ---@param params (table): Parameters for the invoked LSP method + ---@returns (bool) `true` if notification could be sent, `false` if not local function notify(method, params) return encode_and_send { jsonrpc = "2.0"; @@ -412,7 +417,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) } end - --@private + ---@private --- sends an error object to the remote LSP process. local function send_response(request_id, err, result) return encode_and_send { @@ -428,10 +433,10 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) -- --- Sends a request to the LSP server and runs {callback} upon response. --- - --@param method (string) The invoked LSP method - --@param params (table) Parameters for the invoked LSP method - --@param callback (function) Callback to invoke - --@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not + ---@param method (string) The invoked LSP method + ---@param params (table) Parameters for the invoked LSP method + ---@param callback (function) Callback to invoke + ---@returns (bool, number) `(true, message_id)` if request could be sent, `false` if not local function request(method, params, callback) validate { callback = { callback, 'f' }; @@ -444,7 +449,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) method = method; params = params; } - if result then + if result and message_callbacks then message_callbacks[message_id] = schedule_wrap(callback) return result, message_id else @@ -458,13 +463,13 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end end) - --@private + ---@private local function on_error(errkind, ...) assert(client_errors[errkind]) -- TODO what to do if this fails? pcall(dispatchers.on_error, errkind, ...) end - --@private + ---@private local function pcall_handler(errkind, status, head, ...) if not status then on_error(errkind, head, ...) @@ -472,7 +477,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) end return status, head, ... end - --@private + ---@private local function try_call(errkind, fn, ...) return pcall_handler(errkind, pcall(fn, ...)) end @@ -481,7 +486,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) -- time and log them. This would require storing the timestamp. I could call -- them with an error then, perhaps. - --@private + ---@private local function handle_body(body) local decoded, err = json_decode(body) if not decoded then @@ -543,14 +548,14 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) -- - The server will not send a result callback after this cancellation. -- - If the server sent this cancellation ACK after sending the result, the user of this RPC -- client will ignore the result themselves. - if result_id then + if result_id and message_callbacks then message_callbacks[result_id] = nil end return end end - local callback = message_callbacks[result_id] + local callback = message_callbacks and message_callbacks[result_id] if callback then message_callbacks[result_id] = nil validate { diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index cb9a7cbed5..a4c8b69f6c 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -40,24 +40,30 @@ local loclist_type_map = { } ---@private --- Check the border given by opts or the default border for the additional --- size it adds to a float. ---@returns size of border in height and width +---@private +--- Check the border given by opts or the default border for the additional +--- size it adds to a float. +---@param opts (table, optional) options for the floating window +--- - border (string or table) the border +---@returns (table) size of border in the form of { height = height, width = width } local function get_border_size(opts) local border = opts and opts.border or default_border local height = 0 local width = 0 if type(border) == 'string' then - local border_size = {none = {0, 0}, single = {2, 2}, double = {2, 2}, shadow = {1, 1}} + local border_size = {none = {0, 0}, single = {2, 2}, double = {2, 2}, rounded = {2, 2}, solid = {2, 2}, shadow = {1, 1}} if border_size[border] == nil then - error("floating preview border is not correct. Please refer to the docs |vim.api.nvim_open_win()|" - .. vim.inspect(border)) + error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border))) end height, width = unpack(border_size[border]) else + if 8 % #border ~= 0 then + error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border))) + end + ---@private local function border_width(id) + id = (id - 1) % #border + 1 if type(border[id]) == "table" then -- border specified as a table of <character, highlight group> return vim.fn.strdisplaywidth(border[id][1]) @@ -65,9 +71,11 @@ local function get_border_size(opts) -- border specified as a list of border characters return vim.fn.strdisplaywidth(border[id]) end - error("floating preview border is not correct. Please refer to the docs |vim.api.nvim_open_win()|" .. vim.inspect(border)) + error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border))) end + ---@private local function border_height(id) + id = (id - 1) % #border + 1 if type(border[id]) == "table" then -- border specified as a table of <character, highlight group> return #border[id][1] > 0 and 1 or 0 @@ -75,7 +83,7 @@ local function get_border_size(opts) -- border specified as a list of border characters return #border[id] > 0 and 1 or 0 end - error("floating preview border is not correct. Please refer to the docs |vim.api.nvim_open_win()|" .. vim.inspect(border)) + error(string.format("invalid floating preview border: %s. :help vim.api.nvim_open_win()", vim.inspect(border))) end height = height + border_height(2) -- top height = height + border_height(6) -- bottom @@ -86,7 +94,7 @@ local function get_border_size(opts) return { height = height, width = width } end ---@private +---@private local function split_lines(value) return split(value, '\n', true) end @@ -95,11 +103,11 @@ end --- --- CAUTION: Changes in-place! --- ---@param lines (table) Original list of strings ---@param A (table) Start position; a 2-tuple of {line, col} numbers ---@param B (table) End position; a 2-tuple of {line, col} numbers ---@param new_lines A list of strings to replace the original ---@returns (table) The modified {lines} object +---@param lines (table) Original list of strings +---@param A (table) Start position; a 2-tuple of {line, col} numbers +---@param B (table) End position; a 2-tuple of {line, col} numbers +---@param new_lines A list of strings to replace the original +---@returns (table) The modified {lines} object function M.set_lines(lines, A, B, new_lines) -- 0-indexing to 1-indexing local i_0 = A[1] + 1 @@ -133,7 +141,7 @@ function M.set_lines(lines, A, B, new_lines) return lines end ---@private +---@private local function sort_by_key(fn) return function(a,b) local ka, kb = fn(a), fn(b) @@ -147,12 +155,12 @@ local function sort_by_key(fn) return false end end ---@private +---@private local edit_sort_key = sort_by_key(function(e) return {e.A[1], e.A[2], e.i} end) ---@private +---@private --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position --- Returns a zero-indexed column, since set_lines() does the conversion to --- 1-indexed @@ -238,8 +246,9 @@ function M.get_progress_messages() end --- Applies a list of text edits to a buffer. ---@param text_edits (table) list of `TextEdit` objects ---@param buf_nr (number) Buffer id +---@param text_edits (table) list of `TextEdit` objects +---@param buf_nr (number) Buffer id +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit function M.apply_text_edits(text_edits, bufnr) if not next(text_edits) then return end if not api.nvim_buf_is_loaded(bufnr) then @@ -294,11 +303,11 @@ end -- function M.glob_to_regex(glob) -- end ---@private +---@private --- Finds the first line and column of the difference between old and new lines ---@param old_lines table list of lines ---@param new_lines table list of lines ---@returns (int, int) start_line_idx and start_col_idx of range +---@param old_lines table list of lines +---@param new_lines table list of lines +---@returns (int, int) start_line_idx and start_col_idx of range local function first_difference(old_lines, new_lines, start_line_idx) local line_count = math.min(#old_lines, #new_lines) if line_count == 0 then return 1, 1 end @@ -324,12 +333,12 @@ local function first_difference(old_lines, new_lines, start_line_idx) end ---@private +---@private --- Finds the last line and column of the differences between old and new lines ---@param old_lines table list of lines ---@param new_lines table list of lines ---@param start_char integer First different character idx of range ---@returns (int, int) end_line_idx and end_col_idx of range +---@param old_lines table list of lines +---@param new_lines table list of lines +---@param start_char integer First different character idx of range +---@returns (int, int) end_line_idx and end_col_idx of range local function last_difference(old_lines, new_lines, start_char, end_line_idx) local line_count = math.min(#old_lines, #new_lines) if line_count == 0 then return 0,0 end @@ -368,14 +377,14 @@ local function last_difference(old_lines, new_lines, start_char, end_line_idx) end ---@private +---@private --- Get the text of the range defined by start and end line/column ---@param lines table list of lines ---@param start_char integer First different character idx of range ---@param end_char integer Last different character idx of range ---@param start_line integer First different line idx of range ---@param end_line integer Last different line idx of range ---@returns string text extracted from defined region +---@param lines table list of lines +---@param start_char integer First different character idx of range +---@param end_char integer Last different character idx of range +---@param start_line integer First different line idx of range +---@param end_line integer Last different line idx of range +---@returns string text extracted from defined region local function extract_text(lines, start_line, start_char, end_line, end_char) if start_line == #lines + end_line + 1 then if end_line == 0 then return '' end @@ -395,14 +404,14 @@ local function extract_text(lines, start_line, start_char, end_line, end_char) return result end ---@private +---@private --- Compute the length of the substituted range ---@param lines table list of lines ---@param start_char integer First different character idx of range ---@param end_char integer Last different character idx of range ---@param start_line integer First different line idx of range ---@param end_line integer Last different line idx of range ---@returns (int, int) end_line_idx and end_col_idx of range +---@param lines table list of lines +---@param start_char integer First different character idx of range +---@param end_char integer Last different character idx of range +---@param start_line integer First different line idx of range +---@param end_line integer Last different line idx of range +---@returns (int, int) end_line_idx and end_col_idx of range local function compute_length(lines, start_line, start_char, end_line, end_char) local adj_end_line = #lines + end_line + 1 local adj_end_char @@ -423,12 +432,12 @@ local function compute_length(lines, start_line, start_char, end_line, end_char) end --- Returns the range table for the difference between old and new lines ---@param old_lines table list of lines ---@param new_lines table list of lines ---@param start_line_idx int line to begin search for first difference ---@param end_line_idx int line to begin search for last difference ---@param offset_encoding string encoding requested by language server ---@returns table start_line_idx and start_col_idx of range +---@param old_lines table list of lines +---@param new_lines table list of lines +---@param start_line_idx int line to begin search for first difference +---@param end_line_idx int line to begin search for last difference +---@param offset_encoding string encoding requested by language server +---@returns table start_line_idx and start_col_idx of range function M.compute_diff(old_lines, new_lines, start_line_idx, end_line_idx, offset_encoding) local start_line, start_char = first_difference(old_lines, new_lines, start_line_idx) local end_line, end_char = last_difference(vim.list_slice(old_lines, start_line, #old_lines), @@ -468,9 +477,9 @@ end --- Can be used to extract the completion items from a --- `textDocument/completion` request, which may return one of --- `CompletionItem[]`, `CompletionList` or null. ---@param result (table) The result of a `textDocument/completion` request ---@returns (table) List of completion items ---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_completion +---@param result (table) The result of a `textDocument/completion` request +---@returns (table) List of completion items +---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_completion function M.extract_completion_items(result) if type(result) == 'table' and result.items then -- result is a `CompletionList` @@ -514,12 +523,12 @@ function M.apply_text_document_edit(text_document_edit, index) M.apply_text_edits(text_document_edit.edits, bufnr) end ---@private +---@private --- Recursively parses snippets in a completion entry. --- ---@param input (string) Snippet text to parse for snippets ---@param inner (bool) Whether this function is being called recursively ---@returns 2-tuple of strings: The first is the parsed result, the second is the +---@param input (string) Snippet text to parse for snippets +---@param inner (bool) Whether this function is being called recursively +---@returns 2-tuple of strings: The first is the parsed result, the second is the ---unparsed rest of the input local function parse_snippet_rec(input, inner) local res = "" @@ -576,28 +585,28 @@ end --- Parses snippets in a completion entry. --- ---@param input (string) unparsed snippet ---@returns (string) parsed snippet +---@param input (string) unparsed snippet +---@returns (string) parsed snippet function M.parse_snippet(input) local res, _ = parse_snippet_rec(input, false) return res end ---@private +---@private --- Sorts by CompletionItem.sortText. --- ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +--see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion local function sort_completion_items(items) table.sort(items, function(a, b) return (a.sortText or a.label) < (b.sortText or b.label) end) end ---@private +---@private --- Returns text that should be inserted when selecting completion item. The --- precedence is as follows: textEdit.newText > insertText > label ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +--see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion local function get_completion_word(item) if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= "" then local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat] @@ -617,7 +626,7 @@ local function get_completion_word(item) return item.label end ---@private +---@private --- Some language servers return complementary candidates whose prefixes do not --- match are also returned. So we exclude completion candidates whose prefix --- does not match. @@ -632,9 +641,9 @@ end --- the client must handle it properly even if it receives a value outside the --- specification. --- ---@param completion_item_kind (`vim.lsp.protocol.completionItemKind`) ---@returns (`vim.lsp.protocol.completionItemKind`) ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion +---@param completion_item_kind (`vim.lsp.protocol.completionItemKind`) +---@returns (`vim.lsp.protocol.completionItemKind`) +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion function M._get_completion_item_kind_name(completion_item_kind) return protocol.CompletionItemKind[completion_item_kind] or "Unknown" end @@ -642,12 +651,12 @@ end --- Turns the result of a `textDocument/completion` request into vim-compatible --- |complete-items|. --- ---@param result The result of a `textDocument/completion` call, e.g. from +---@param result 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 ---@returns { matches = complete-items table, incomplete = bool } ---@see |complete-items| +---@param prefix (string) the prefix to filter the completion items +---@returns { matches = complete-items table, incomplete = bool } +---@see |complete-items| function M.text_document_completion_list_to_complete_items(result, prefix) local items = M.extract_completion_items(result) if vim.tbl_isempty(items) then @@ -697,8 +706,8 @@ end --- Rename old_fname to new_fname --- ---@param opts (table) +--- +---@param opts (table) -- overwrite? bool -- ignoreIfExists? bool function M.rename(old_fname, new_fname, opts) @@ -753,8 +762,8 @@ end --- Applies a `WorkspaceEdit`. --- ---@param workspace_edit (table) `WorkspaceEdit` --- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit +---@param workspace_edit (table) `WorkspaceEdit` +--see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit function M.apply_workspace_edit(workspace_edit) if workspace_edit.documentChanges then for idx, change in ipairs(workspace_edit.documentChanges) do @@ -793,10 +802,10 @@ end --- window for `textDocument/hover`, for parsing the result of --- `textDocument/signatureHelp`, and potentially others. --- ---@param input (`MarkedString` | `MarkedString[]` | `MarkupContent`) ---@param contents (table, optional, default `{}`) List of strings to extend with converted lines ---@returns {contents}, extended with lines of converted markdown. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover +---@param input (`MarkedString` | `MarkedString[]` | `MarkupContent`) +---@param contents (table, optional, default `{}`) List of strings to extend with converted lines +---@returns {contents}, extended with lines of converted markdown. +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover function M.convert_input_to_markdown_lines(input, contents) contents = contents or {} -- MarkedString variation 1 @@ -806,14 +815,20 @@ function M.convert_input_to_markdown_lines(input, contents) assert(type(input) == 'table', "Expected a table for Hover.contents") -- MarkupContent if input.kind then - -- The kind can be either plaintext or markdown. However, either way we - -- will just be rendering markdown, so we handle them both the same way. - -- TODO these can have escaped/sanitized html codes in markdown. We - -- should make sure we handle this correctly. + -- The kind can be either plaintext or markdown. + -- If it's plaintext, then wrap it in a <text></text> block -- Some servers send input.value as empty, so let's ignore this :( - -- assert(type(input.value) == 'string') - list_extend(contents, split_lines(input.value or '')) + local value = input.value or '' + + if input.kind == "plaintext" then + -- wrap this in a <text></text> block so that stylize_markdown + -- can properly process it as plaintext + value = string.format("<text>\n%s\n</text>", value) + end + + -- assert(type(value) == 'string') + list_extend(contents, split_lines(value)) -- MarkupString variation 2 elseif input.language then -- Some servers send input.value as empty, so let's ignore this :( @@ -837,11 +852,12 @@ end --- Converts `textDocument/SignatureHelp` response to markdown lines. --- ---@param signature_help Response of `textDocument/SignatureHelp` ---@param ft optional filetype that will be use as the `lang` for the label markdown code block ---@returns list of lines of converted markdown. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp -function M.convert_signature_help_to_markdown_lines(signature_help, ft) +---@param signature_help Response of `textDocument/SignatureHelp` +---@param ft optional filetype that will be use as the `lang` for the label markdown code block +---@param triggers optional list of trigger characters from the lsp server. used to better determine parameter offsets +---@returns list of lines of converted markdown. +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp +function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers) if not signature_help.signatures then return end @@ -850,6 +866,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft) --=== 0`. Whenever possible implementors should make an active decision about --the active signature and shouldn't rely on a default value. local contents = {} + local active_hl local active_signature = signature_help.activeSignature or 0 -- If the activeSignature is not inside the valid range, then clip it. if active_signature >= #signature_help.signatures then @@ -861,7 +878,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft) end local label = signature.label if ft then - -- wrap inside a code block so fancy_markdown can render it properly + -- wrap inside a code block so stylize_markdown can render it properly label = ("```%s\n%s\n```"):format(ft, label) end vim.list_extend(contents, vim.split(label, '\n', true)) @@ -869,11 +886,17 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft) M.convert_input_to_markdown_lines(signature.documentation, contents) end if signature.parameters and #signature.parameters > 0 then - local active_parameter = signature_help.activeParameter or 0 - -- If the activeParameter is not inside the valid range, then clip it. + local active_parameter = (signature.activeParameter or signature_help.activeParameter or 0) + if active_parameter < 0 + then active_parameter = 0 + end + + -- If the activeParameter is > #parameters, then set it to the last + -- NOTE: this is not fully according to the spec, but a client-side interpretation if active_parameter >= #signature.parameters then - active_parameter = 0 + active_parameter = #signature.parameters - 1 end + local parameter = signature.parameters[active_parameter + 1] if parameter then --[=[ @@ -894,22 +917,44 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft) documentation?: string | MarkupContent; } --]=] - -- TODO highlight parameter + if parameter.label then + if type(parameter.label) == "table" then + active_hl = parameter.label + else + local offset = 1 + -- try to set the initial offset to the first found trigger character + for _, t in ipairs(triggers or {}) do + local trigger_offset = signature.label:find(t, 1, true) + if trigger_offset and (offset == 1 or trigger_offset < offset) then + offset = trigger_offset + end + end + for p, param in pairs(signature.parameters) do + offset = signature.label:find(param.label, offset, true) + if not offset then break end + if p == active_parameter + 1 then + active_hl = {offset - 1, offset + #parameter.label - 1} + break + end + offset = offset + #param.label + 1 + end + end + end if parameter.documentation then M.convert_input_to_markdown_lines(parameter.documentation, contents) end end end - return contents + return contents, active_hl end --- Creates a table with sensible default options for a floating window. The --- table can be passed to |nvim_open_win()|. --- ---@param width (number) window width (in character cells) ---@param height (number) window height (in character cells) ---@param opts (table, optional) ---@returns (table) Options +---@param width (number) window width (in character cells) +---@param height (number) window height (in character cells) +---@param opts (table, optional) +---@returns (table) Options function M.make_floating_popup_options(width, height, opts) validate { opts = { opts, 't', true }; @@ -936,7 +981,7 @@ function M.make_floating_popup_options(width, height, opts) row = -get_border_size(opts).height end - if vim.fn.wincol() + width <= api.nvim_get_option('columns') then + if vim.fn.wincol() + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then anchor = anchor..'W' col = 0 else @@ -954,13 +999,14 @@ function M.make_floating_popup_options(width, height, opts) style = 'minimal', width = width, border = opts.border or default_border, + zindex = opts.zindex or 50, } end --- Jumps to a location. --- ---@param location (`Location`|`LocationLink`) ---@returns `true` if the jump succeeded +---@param location (`Location`|`LocationLink`) +---@returns `true` if the jump succeeded function M.jump_to_location(location) -- location may be Location or LocationLink local uri = location.uri or location.targetUri @@ -990,8 +1036,8 @@ end --- - for Location, range is shown (e.g., function definition) --- - for LocationLink, targetRange is shown (e.g., body of function definition) --- ---@param location a single `Location` or `LocationLink` ---@returns (bufnr,winnr) buffer and window number of floating window or nil +---@param location a single `Location` or `LocationLink` +---@returns (bufnr,winnr) buffer and window number of floating window or nil function M.preview_location(location, opts) -- location may be LocationLink or Location (more useful for the former) local uri = location.targetUri or location.uri @@ -1005,7 +1051,7 @@ function M.preview_location(location, opts) local syntax = api.nvim_buf_get_option(bufnr, 'syntax') if syntax == "" then -- When no syntax is set, we use filetype as fallback. This might not result - -- in a valid syntax definition. See also ft detection in fancy_floating_win. + -- in a valid syntax definition. See also ft detection in stylize_markdown. -- An empty syntax is more common now with TreeSitter, since TS disables syntax. syntax = api.nvim_buf_get_option(bufnr, 'filetype') end @@ -1014,7 +1060,7 @@ function M.preview_location(location, opts) return M.open_floating_preview(contents, syntax, opts) end ---@private +---@private local function find_window_by_var(name, value) for _, win in ipairs(api.nvim_list_wins()) do if npcall(api.nvim_win_get_var, win, name) == value then @@ -1023,53 +1069,6 @@ local function find_window_by_var(name, value) end end ---- Enters/leaves the focusable window associated with the current buffer via the ---window - variable `unique_name`. If no such window exists, run the function ---{fn}. ---- ---@param unique_name (string) Window variable ---@param fn (function) should return create a new window and return a tuple of ----({focusable_buffer_id}, {window_id}). if {focusable_buffer_id} is a valid ----buffer id, the newly created window will be the new focus associated with ----the current buffer via the tag `unique_name`. ---@returns (pbufnr, pwinnr) if `fn()` has created a new window; nil otherwise ----@deprecated please use open_floating_preview directly -function M.focusable_float(unique_name, fn) - vim.notify("focusable_float is deprecated. Please use open_floating_preview and pass focus_id = [unique_name] instead", vim.log.levels.WARN) - -- Go back to previous window if we are in a focusable one - if npcall(api.nvim_win_get_var, 0, unique_name) then - return api.nvim_command("wincmd p") - end - local bufnr = api.nvim_get_current_buf() - do - local win = find_window_by_var(unique_name, bufnr) - if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then - api.nvim_set_current_win(win) - api.nvim_command("stopinsert") - return - end - end - local pbufnr, pwinnr = fn() - if pbufnr then - api.nvim_win_set_var(pwinnr, unique_name, bufnr) - return pbufnr, pwinnr - end -end - ---- Focuses/unfocuses the floating preview window associated with the current ---- buffer via the window variable `unique_name`. If no such preview window ---- exists, makes a new one. ---- ---@param unique_name (string) Window variable ---@param fn (function) The return values of this function will be passed ----directly to |vim.lsp.util.open_floating_preview()|, in the case that a new ----floating window should be created ----@deprecated please use open_floating_preview directly -function M.focusable_preview(unique_name, fn) - vim.notify("focusable_preview is deprecated. Please use open_floating_preview and pass focus_id = [unique_name] instead", vim.log.levels.WARN) - return M.open_floating_preview(fn(), {focus_id = unique_name}) -end - --- Trims empty lines from input and pad top and bottom with empty lines --- ---@param contents table of lines to trim and pad @@ -1097,12 +1096,19 @@ function M._trim(contents, opts) return contents end - - ---- @deprecated please use open_floating_preview directly -function M.fancy_floating_markdown(contents, opts) - vim.notify("fancy_floating_markdown is deprecated. Please use open_floating_preview and pass focus_id = [unique_name] instead", vim.log.levels.WARN) - return M.open_floating_preview(contents, "markdown", opts) +--- Generates a table mapping markdown code block lang to vim syntax, +--- based on g:markdown_fenced_languages +---@return a table of lang -> syntax mappings +---@private +local function get_markdown_fences() + local fences = {} + for _, fence in pairs(vim.g.markdown_fenced_languages or {}) do + local lang, syntax = fence:match("^(.*)=(.*)$") + if lang then + fences[lang] = syntax + end + end + return fences end --- Converts markdown into syntax highlighted regions by stripping the code @@ -1134,26 +1140,52 @@ function M.stylize_markdown(bufnr, contents, opts) } opts = opts or {} + -- table of fence types to {ft, begin, end} + -- when ft is nil, we get the ft from the regex match + local matchers = { + block = {nil, "```+([a-zA-Z0-9_]*)", "```+"}, + pre = {"", "<pre>", "</pre>"}, + code = {"", "<code>", "</code>"}, + text = {"plaintex", "<text>", "</text>"}, + } + + local match_begin = function(line) + for type, pattern in pairs(matchers) do + local ret = line:match(string.format("^%%s*%s%%s*$", pattern[2])) + if ret then + return { + type = type, + ft = pattern[1] or ret + } + end + end + end + + local match_end = function(line, match) + local pattern = matchers[match.type] + return line:match(string.format("^%%s*%s%%s*$", pattern[3])) + end + + -- Clean up + contents = M._trim(contents, opts) + + -- Insert blank line separator after code block? + local add_sep = opts.separator == nil and true or opts.separator local stripped = {} local highlights = {} + -- keep track of lnums that contain markdown + local markdown_lines = {} do local i = 1 while i <= #contents do local line = contents[i] - -- TODO(ashkan): use a more strict regex for filetype? - local ft = line:match("^```([a-zA-Z0-9_]*)$") - -- local ft = line:match("^```(.*)$") - -- TODO(ashkan): validate the filetype here. - local is_pre = line:match("^%s*<pre>%s*$") - if is_pre then - ft = "" - end - if ft then + local match = match_begin(line) + if match then local start = #stripped i = i + 1 while i <= #contents do line = contents[i] - if line == "```" or (is_pre and line:match("^%s*</pre>%s*$")) then + if match_end(line, match) then i = i + 1 break end @@ -1161,56 +1193,59 @@ function M.stylize_markdown(bufnr, contents, opts) i = i + 1 end table.insert(highlights, { - ft = ft; + ft = match.ft; start = start + 1; - finish = #stripped + 1 - 1; + finish = #stripped; }) + -- add a separator, but not on the last line + if add_sep and i < #contents then + table.insert(stripped, "---") + markdown_lines[#stripped] = true + end else - table.insert(stripped, line) + -- strip any emty lines or separators prior to this separator in actual markdown + if line:match("^---+$") then + while markdown_lines[#stripped] and (stripped[#stripped]:match("^%s*$") or stripped[#stripped]:match("^---+$")) do + markdown_lines[#stripped] = false + table.remove(stripped, #stripped) + end + end + -- add the line if its not an empty line following a separator + if not (line:match("^%s*$") and markdown_lines[#stripped] and stripped[#stripped]:match("^---+$")) then + table.insert(stripped, line) + markdown_lines[#stripped] = true + end i = i + 1 end end end - -- Clean up - stripped = M._trim(stripped, opts) -- Compute size of float needed to show (wrapped) lines opts.wrap_at = opts.wrap_at or (vim.wo["wrap"] and api.nvim_win_get_width(0)) - local width, height = M._make_floating_popup_size(stripped, opts) + local width = M._make_floating_popup_size(stripped, opts) - -- Insert blank line separator after code block - local insert_separator = opts.separator - if insert_separator == nil then insert_separator = true end - if insert_separator then - local offset = 0 - for _, h in ipairs(highlights) do - h.start = h.start + offset - h.finish = h.finish + offset - -- check if a seperator already exists and use that one instead of creating a new one - if h.finish + 1 <= #stripped then - if stripped[h.finish + 1]:match("^---+$") then - stripped[h.finish + 1] = string.rep("─", math.min(width, opts.wrap_at or width)) - else - table.insert(stripped, h.finish + 1, string.rep("─", math.min(width, opts.wrap_at or width))) - offset = offset + 1 - height = height + 1 - end - end + local sep_line = string.rep("─", math.min(width, opts.wrap_at or width)) + + for l in pairs(markdown_lines) do + if stripped[l]:match("^---+$") then + stripped[l] = sep_line end end vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, stripped) local idx = 1 - --@private + ---@private -- keep track of syntaxes we already inlcuded. -- no need to include the same syntax more than once local langs = {} + local fences = get_markdown_fences() local function apply_syntax_to_region(ft, start, finish) if ft == "" then vim.cmd(string.format("syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend", start, finish + 1)) return end + ft = fences[ft] or ft local name = ft..idx idx = idx + 1 local lang = "@"..ft:upper() @@ -1239,7 +1274,7 @@ function M.stylize_markdown(bufnr, contents, opts) apply_syntax_to_region(h.ft, h.start, h.finish) last = h.finish + 1 end - if last < #stripped then + if last <= #stripped then apply_syntax_to_region("lsp_markdown", last, #stripped) end end) @@ -1249,26 +1284,26 @@ end --- Creates autocommands to close a preview window when events happen. --- ---@param events (table) list of events ---@param winnr (number) window id of preview window ---@see |autocmd-events| +---@param events (table) list of events +---@param winnr (number) window id of preview window +---@see |autocmd-events| function M.close_preview_autocmd(events, winnr) if #events > 0 then api.nvim_command("autocmd "..table.concat(events, ',').." <buffer> ++once lua pcall(vim.api.nvim_win_close, "..winnr..", true)") end end ---@internal +---@internal --- Computes size of float needed to show contents (with optional wrapping) --- ---@param contents table of lines to show in window ---@param opts dictionary with optional fields --- - height of floating window --- - width of floating window --- - wrap_at character to wrap at for computing height --- - max_width maximal width of floating window --- - max_height maximal height of floating window ---@returns width,height size of float +---@param contents table of lines to show in window +---@param opts dictionary with optional fields +--- - height of floating window +--- - width of floating window +--- - wrap_at character to wrap at for computing height +--- - max_width maximal width of floating window +--- - max_height maximal height of floating window +---@returns width,height size of float function M._make_floating_popup_size(contents, opts) validate { contents = { contents, 't' }; @@ -1335,9 +1370,9 @@ end --- Shows contents in a floating window. --- ---@param contents table of lines to show in window ---@param syntax string of syntax to set for opened buffer ---@param opts dictionary with optional fields +---@param contents table of lines to show in window +---@param syntax string of syntax to set for opened buffer +---@param opts dictionary with optional fields --- - height of floating window --- - width of floating window --- - wrap boolean enable wrapping of long lines (defaults to true) @@ -1351,7 +1386,7 @@ end --- - focus_id if a popup with this id is opened, then focus it --- - close_events list of events that closes the floating window --- - focusable (boolean, default true): Make float focusable ---@returns bufnr,winnr buffer and window number of the newly created floating +---@returns bufnr,winnr buffer and window number of the newly created floating ---preview window function M.open_floating_preview(contents, syntax, opts) validate { @@ -1447,7 +1482,7 @@ do --[[ References ]] --- Removes document highlights from a buffer. --- - --@param bufnr buffer id + ---@param bufnr buffer id function M.buf_clear_references(bufnr) validate { bufnr = {bufnr, 'n', true} } api.nvim_buf_clear_namespace(bufnr, reference_ns, 0, -1) @@ -1455,8 +1490,9 @@ do --[[ References ]] --- Shows a list of document highlights for a certain buffer. --- - --@param bufnr buffer id - --@param references List of `DocumentHighlight` objects to highlight + ---@param bufnr buffer id + ---@param references List of `DocumentHighlight` objects to highlight + ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight function M.buf_highlight_references(bufnr, references) validate { bufnr = {bufnr, 'n', true} } for _, reference in ipairs(references) do @@ -1477,24 +1513,24 @@ local position_sort = sort_by_key(function(v) return {v.start.line, v.start.character} end) --- Gets the zero-indexed line from the given uri. +--- Gets the zero-indexed line from the given uri. +---@param uri string uri of the resource to get the line from +---@param row number zero-indexed line number +---@return string the line at row in filename -- For non-file uris, we load the buffer and get the line. -- If a loaded buffer exists, then that is used. -- Otherwise we get the line using libuv which is a lot faster than loading the buffer. ---@param uri string uri of the resource to get the line from ---@param row number zero-indexed line number ---@return string the line at row in filename function M.get_line(uri, row) return M.get_lines(uri, { row })[row] end --- Gets the zero-indexed lines from the given uri. +--- Gets the zero-indexed lines from the given uri. +---@param uri string uri of the resource to get the lines from +---@param rows number[] zero-indexed line numbers +---@return table<number string> a table mapping rows to lines -- For non-file uris, we load the buffer and get the lines. -- If a loaded buffer exists, then that is used. -- Otherwise we get the lines using libuv which is a lot faster than loading the buffer. ---@param uri string uri of the resource to get the lines from ---@param rows number[] zero-indexed line numbers ---@return table<number string> a table mapping rows to lines function M.get_lines(uri, rows) rows = type(rows) == "table" and rows or { rows } @@ -1562,8 +1598,8 @@ end --- Returns the items with the byte position calculated correctly and in sorted --- order, for display in quickfix and location lists. --- ---@param locations (table) list of `Location`s or `LocationLink`s ---@returns (table) list of items +---@param locations (table) list of `Location`s or `LocationLink`s +---@returns (table) list of items function M.locations_to_items(locations) local items = {} local grouped = setmetatable({}, { @@ -1620,7 +1656,7 @@ end --- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|. --- Defaults to current window. --- ---@param items (table) list of items +---@param items (table) list of items function M.set_loclist(items, win_id) vim.fn.setloclist(win_id or 0, {}, ' ', { title = 'Language Server'; @@ -1631,7 +1667,7 @@ end --- Fills quickfix list with given list of items. --- Can be obtained with e.g. |vim.lsp.util.locations_to_items()|. --- ---@param items (table) list of items +---@param items (table) list of items function M.set_qflist(items) vim.fn.setqflist({}, ' ', { title = 'Language Server'; @@ -1648,9 +1684,9 @@ end --- Converts symbols to quickfix list items. --- ---@param symbols DocumentSymbol[] or SymbolInformation[] +---@param symbols DocumentSymbol[] or SymbolInformation[] function M.symbols_to_items(symbols, bufnr) - --@private + ---@private local function _symbols_to_items(_symbols, _items, _bufnr) for _, symbol in ipairs(_symbols) do if symbol.location then -- SymbolInformation type @@ -1686,19 +1722,19 @@ function M.symbols_to_items(symbols, bufnr) end --- Removes empty lines from the beginning and end. ---@param lines (table) list of lines to trim ---@returns (table) trimmed list of lines +---@param lines (table) list of lines to trim +---@returns (table) trimmed list of lines function M.trim_empty_lines(lines) local start = 1 for i = 1, #lines do - if #lines[i] > 0 then + if lines[i] ~= nil and #lines[i] > 0 then start = i break end end local finish = 1 for i = #lines, 1, -1 do - if #lines[i] > 0 then + if lines[i] ~= nil and #lines[i] > 0 then finish = i break end @@ -1711,8 +1747,8 @@ end --- --- CAUTION: Modifies the input in-place! --- ---@param lines (table) list of lines ---@returns (string) filetype or 'markdown' if it was unchanged. +---@param lines (table) list of lines +---@returns (string) filetype or 'markdown' if it was unchanged. function M.try_trim_markdown_code_blocks(lines) local language_id = lines[1]:match("^```(.*)") if language_id then @@ -1735,7 +1771,7 @@ function M.try_trim_markdown_code_blocks(lines) end local str_utfindex = vim.str_utfindex ---@private +---@private local function make_position_param() local row, col = unpack(api.nvim_win_get_cursor(0)) row = row - 1 @@ -1749,8 +1785,8 @@ end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- ---@returns `TextDocumentPositionParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams +---@returns `TextDocumentPositionParams` object +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params() return { textDocument = M.make_text_document_params(); @@ -1763,7 +1799,7 @@ end --- `textDocument/codeAction`, `textDocument/colorPresentation`, --- `textDocument/rangeFormatting`. --- ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = +---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } function M.make_range_params() local position = make_position_param() @@ -1776,11 +1812,11 @@ end --- Using the given range in the current buffer, creates an object that --- is similar to |vim.lsp.util.make_range_params()|. --- ---@param start_pos ({number, number}, optional) mark-indexed position. +---@param start_pos ({number, number}, optional) mark-indexed position. ---Defaults to the start of the last visual selection. ---@param end_pos ({number, number}, optional) mark-indexed position. +---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. ---@returns { textDocument = { uri = `current_file_uri` }, range = { start = +---@returns { textDocument = { uri = `current_file_uri` }, range = { start = ---`start_position`, end = `end_position` } } function M.make_given_range_params(start_pos, end_pos) validate { @@ -1816,23 +1852,23 @@ end --- Creates a `TextDocumentIdentifier` object for the current buffer. --- ---@returns `TextDocumentIdentifier` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier +---@returns `TextDocumentIdentifier` +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier function M.make_text_document_params() return { uri = vim.uri_from_bufnr(0) } end --- Create the workspace params ---@param added ---@param removed +---@param added +---@param removed function M.make_workspace_params(added, removed) return { event = { added = added; removed = removed; } } end --- Returns visual width of tabstop. --- ---@see |softtabstop| ---@param bufnr (optional, number): Buffer handle, defaults to current ---@returns (number) tabstop visual width +---@see |softtabstop| +---@param bufnr (optional, number): Buffer handle, defaults to current +---@returns (number) tabstop visual width function M.get_effective_tabstop(bufnr) validate { bufnr = {bufnr, 'n', true} } local bo = bufnr and vim.bo[bufnr] or vim.bo @@ -1840,11 +1876,11 @@ function M.get_effective_tabstop(bufnr) return (sts > 0 and sts) or (sts < 0 and bo.shiftwidth) or bo.tabstop end ---- Creates a `FormattingOptions` object for the current buffer and cursor position. +--- Creates a `DocumentFormattingParams` object for the current buffer and cursor position. --- ---@param options Table with valid `FormattingOptions` entries ---@returns `FormattingOptions object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting +---@param options Table with valid `FormattingOptions` entries +---@returns `DocumentFormattingParams` object +---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting function M.make_formatting_params(options) validate { options = {options, 't', true} } options = vim.tbl_extend('keep', options or {}, { @@ -1859,10 +1895,10 @@ end --- Returns the UTF-32 and UTF-16 offsets for a position in a certain buffer. --- ---@param buf buffer id (0 for current) ---@param row 0-indexed line ---@param col 0-indexed byte offset in line ---@returns (number, number) UTF-32 and UTF-16 index of the character in line {row} column {col} in buffer {buf} +---@param buf buffer id (0 for current) +---@param row 0-indexed line +---@param col 0-indexed byte offset in line +---@returns (number, number) UTF-32 and UTF-16 index of the character in line {row} column {col} in buffer {buf} function M.character_offset(bufnr, row, col) local uri = vim.uri_from_bufnr(bufnr) local line = M.get_line(uri, row) @@ -1875,9 +1911,9 @@ end --- Helper function to return nested values in language server settings --- ---@param settings a table of language server settings ---@param section a string indicating the field of the settings table ---@returns (table or string) The value of settings accessed via section +---@param settings a table of language server settings +---@param section a string indicating the field of the settings table +---@returns (table or string) The value of settings accessed via section function M.lookup_section(settings, section) for part in vim.gsplit(section, '.', true) do settings = settings[part] @@ -1892,10 +1928,10 @@ end --- Convert diagnostics grouped by bufnr to a list of items for use in the --- quickfix or location list. --- ---@param diagnostics_by_bufnr table bufnr -> Diagnostic[] ---@param predicate an optional function to filter the diagnostics. --- If present, only diagnostic items matching will be included. ---@return table (A list of items) +---@param diagnostics_by_bufnr table bufnr -> Diagnostic[] +---@param predicate an optional function to filter the diagnostics. +--- If present, only diagnostic items matching will be included. +---@return table (A list of items) function M.diagnostics_to_items(diagnostics_by_bufnr, predicate) local items = {} for bufnr, diagnostics in pairs(diagnostics_by_bufnr or {}) do diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 0a663628a5..18c1e21049 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -12,8 +12,8 @@ local vim = vim or {} --- same functions as those in the input table. Userdata and threads are not --- copied and will throw an error. --- ---@param orig Table to copy ---@returns New table of copied keys and (nested) values. +---@param orig Table to copy +---@returns New table of copied keys and (nested) values. function vim.deepcopy(orig) end -- luacheck: no unused vim.deepcopy = (function() local function _id(v) @@ -52,14 +52,14 @@ end)() --- Splits a string at each instance of a separator. --- ---@see |vim.split()| ---@see https://www.lua.org/pil/20.2.html ---@see http://lua-users.org/wiki/StringLibraryTutorial +---@see |vim.split()| +---@see https://www.lua.org/pil/20.2.html +---@see http://lua-users.org/wiki/StringLibraryTutorial --- ---@param s String to split ---@param sep Separator string or pattern ---@param plain If `true` use `sep` literally (passed to String.find) ---@returns Iterator over the split components +---@param s String to split +---@param sep Separator string or pattern +---@param plain If `true` use `sep` literally (passed to String.find) +---@returns Iterator over the split components function vim.gsplit(s, sep, plain) vim.validate{s={s,'s'},sep={sep,'s'},plain={plain,'b',true}} @@ -101,12 +101,12 @@ end --- split(x*yz*o, "*", true) --> {'x','yz','o'} --- </pre> -- ---@see |vim.gsplit()| +---@see |vim.gsplit()| --- ---@param s String to split ---@param sep Separator string or pattern ---@param plain If `true` use `sep` literally (passed to String.find) ---@returns List-like table of the split components. +---@param s String to split +---@param sep Separator string or pattern +---@param plain If `true` use `sep` literally (passed to String.find) +---@returns List-like table of the split components. function vim.split(s,sep,plain) local t={} for c in vim.gsplit(s, sep, plain) do table.insert(t,c) end return t @@ -115,10 +115,10 @@ end --- Return a list of all keys used in a table. --- However, the order of the return table of keys is not guaranteed. --- ---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua +---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua --- ---@param t Table ---@returns list of keys +---@param t Table +---@returns list of keys function vim.tbl_keys(t) assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) @@ -132,8 +132,8 @@ end --- Return a list of all values used in a table. --- However, the order of the return table of values is not guaranteed. --- ---@param t Table ---@returns list of values +---@param t Table +---@returns list of values function vim.tbl_values(t) assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) @@ -146,8 +146,8 @@ end --- Apply a function to all values of a table. --- ---@param func function or callable table ---@param t table +---@param func function or callable table +---@param t table function vim.tbl_map(func, t) vim.validate{func={func,'c'},t={t,'t'}} @@ -160,8 +160,8 @@ end --- Filter a table using a predicate function --- ---@param func function or callable table ---@param t table +---@param func function or callable table +---@param t table function vim.tbl_filter(func, t) vim.validate{func={func,'c'},t={t,'t'}} @@ -176,9 +176,9 @@ end --- Checks if a list-like (vector) table contains `value`. --- ---@param t Table to check ---@param value Value to compare ---@returns true if `t` contains `value` +---@param t Table to check +---@param value Value to compare +---@returns true if `t` contains `value` function vim.tbl_contains(t, value) vim.validate{t={t,'t'}} @@ -192,14 +192,20 @@ end --- Checks if a table is empty. --- ---@see https://github.com/premake/premake-core/blob/master/src/base/table.lua +---@see https://github.com/premake/premake-core/blob/master/src/base/table.lua --- ---@param t Table to check +---@param t Table to check function vim.tbl_isempty(t) assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) return next(t) == nil end +--- we only merge empty tables or tables that are not a list +---@private +local function can_merge(v) + return type(v) == "table" and (vim.tbl_isempty(v) or not vim.tbl_islist(v)) +end + local function tbl_extend(behavior, deep_extend, ...) if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then error('invalid "behavior": '..tostring(behavior)) @@ -219,8 +225,8 @@ local function tbl_extend(behavior, deep_extend, ...) vim.validate{["after the second argument"] = {tbl,'t'}} if tbl then for k, v in pairs(tbl) do - if type(v) == 'table' and deep_extend and not vim.tbl_islist(v) then - ret[k] = tbl_extend(behavior, true, ret[k] or vim.empty_dict(), v) + if deep_extend and can_merge(v) and can_merge(ret[k]) then + ret[k] = tbl_extend(behavior, true, ret[k], v) elseif behavior ~= 'force' and ret[k] ~= nil then if behavior == 'error' then error('key found in more than one map: '..k) @@ -236,43 +242,48 @@ end --- Merges two or more map-like tables. --- ---@see |extend()| +---@see |extend()| --- ---@param behavior Decides what to do if a key is found in more than one map: +---@param behavior 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 ---@param ... Two or more map-like tables. +---@param ... Two or more map-like tables. function vim.tbl_extend(behavior, ...) return tbl_extend(behavior, false, ...) end --- Merges recursively two or more map-like tables. --- ---@see |tbl_extend()| +---@see |tbl_extend()| --- ---@param behavior Decides what to do if a key is found in more than one map: +---@param behavior 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 ---@param ... Two or more map-like tables. +---@param ... Two or more map-like tables. function vim.tbl_deep_extend(behavior, ...) return tbl_extend(behavior, true, ...) end --- Deep compare values for equality +--- +--- Tables are compared recursively unless they both provide the `eq` methamethod. +--- All other types are compared using the equality `==` operator. +---@param a first value +---@param b second value +---@returns `true` if values are equals, else `false`. function vim.deep_equal(a, b) if a == b then return true end if type(a) ~= type(b) then return false end if type(a) == 'table' then - -- TODO improve this algorithm's performance. for k, v in pairs(a) do if not vim.deep_equal(v, b[k]) then return false end end - for k, v in pairs(b) do - if not vim.deep_equal(v, a[k]) then + for k, _ in pairs(b) do + if a[k] == nil then return false end end @@ -286,7 +297,7 @@ end --- `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = 1 }` -- --Do note that it *modifies* the input. ---@param o table The table to add the reverse to. +---@param o table The table to add the reverse to. function vim.tbl_add_reverse_lookup(o) local keys = vim.tbl_keys(o) for _, k in ipairs(keys) do @@ -303,13 +314,13 @@ end --- --- NOTE: This mutates dst! --- ---@see |vim.tbl_extend()| +---@see |vim.tbl_extend()| --- ---@param dst list which will be modified and appended to. ---@param src list from which values will be inserted. ---@param start Start index on src. defaults to 1 ---@param finish Final index on src. defaults to #src ---@returns dst +---@param dst list which will be modified and appended to. +---@param src list from which values will be inserted. +---@param start Start index on src. defaults to 1 +---@param finish Final index on src. defaults to #src +---@returns dst function vim.list_extend(dst, src, start, finish) vim.validate { dst = {dst, 't'}; @@ -326,10 +337,10 @@ end --- Creates a copy of a list-like table such that any nested tables are --- "unrolled" and appended to the result. --- ---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua +---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua --- ---@param t List-like table ---@returns Flattened copy of the given list-like table. +---@param t List-like table +---@returns Flattened copy of the given list-like table. function vim.tbl_flatten(t) local result = {} local function _tbl_flatten(_t) @@ -353,8 +364,8 @@ end --- |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result, --- for example from |rpcrequest()| or |vim.fn|. --- ---@param t Table ---@returns `true` if array-like table, else `false`. +---@param t Table +---@returns `true` if array-like table, else `false`. function vim.tbl_islist(t) if type(t) ~= 'table' then return false @@ -389,9 +400,9 @@ end --- vim.tbl_count({ 1, 2 }) => 2 --- </pre> --- ---@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua ---@param t Table ---@returns Number that is the number of the value in table +---@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua +---@param t Table +---@returns Number that is the number of the value in table function vim.tbl_count(t) vim.validate{t={t,'t'}} @@ -402,10 +413,10 @@ end --- Creates a copy of a table containing only elements from start to end (inclusive) --- ---@param list table table ---@param start integer Start range of slice ---@param finish integer End range of slice ---@returns Copy of table sliced from start to finish (inclusive) +---@param list table table +---@param start integer Start range of slice +---@param finish integer End range of slice +---@returns Copy of table sliced from start to finish (inclusive) function vim.list_slice(list, start, finish) local new_list = {} for i = start or 1, finish or #list do @@ -416,9 +427,9 @@ end --- Trim whitespace (Lua pattern "%s") from both sides of a string. --- ---@see https://www.lua.org/pil/20.2.html ---@param s String to trim ---@returns String with whitespace removed from its beginning and end +---@see https://www.lua.org/pil/20.2.html +---@param s String to trim +---@returns String with whitespace removed from its beginning and end function vim.trim(s) vim.validate{s={s,'s'}} return s:match('^%s*(.*%S)') or '' @@ -426,9 +437,9 @@ end --- Escapes magic chars in a Lua pattern. --- ---@see https://github.com/rxi/lume ---@param s String to escape ---@returns %-escaped pattern string +---@see https://github.com/rxi/lume +---@param s String to escape +---@returns %-escaped pattern string function vim.pesc(s) vim.validate{s={s,'s'}} return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') @@ -436,9 +447,9 @@ end --- Tests if `s` starts with `prefix`. --- ---@param s (string) a string ---@param prefix (string) a prefix ---@return (boolean) true if `prefix` is a prefix of s +---@param s (string) a string +---@param prefix (string) a prefix +---@return (boolean) true if `prefix` is a prefix of s function vim.startswith(s, prefix) vim.validate { s = {s, 's'}; prefix = {prefix, 's'}; } return s:sub(1, #prefix) == prefix @@ -446,9 +457,9 @@ end --- Tests if `s` ends with `suffix`. --- ---@param s (string) a string ---@param suffix (string) a suffix ---@return (boolean) true if `suffix` is a suffix of s +---@param s (string) a string +---@param suffix (string) a suffix +---@return (boolean) true if `suffix` is a suffix of s function vim.endswith(s, suffix) vim.validate { s = {s, 's'}; suffix = {suffix, 's'}; } return #suffix == 0 or s:sub(-#suffix) == suffix @@ -480,7 +491,7 @@ end --- => error('arg1: expected even number, got 3') --- </pre> --- ---@param opt Map of parameter names to validations. Each key is a parameter +---@param opt Map of parameter names to validations. Each key is a parameter --- name; each value is a tuple in one of these forms: --- 1. (arg_value, type_name, optional) --- - arg_value: argument value @@ -564,8 +575,8 @@ do end --- Returns true if object `f` can be called as a function. --- ---@param f Any object ---@return true if `f` is callable, else false +---@param f Any object +---@return true if `f` is callable, else false function vim.is_callable(f) if type(f) == 'function' then return true end local m = getmetatable(f) diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index de997b2d86..66999c5f7f 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -20,6 +20,9 @@ setmetatable(M, { elseif k == "language" then t[k] = require"vim.treesitter.language" return t[k] + elseif k == "query" then + t[k] = require"vim.treesitter.query" + return t[k] end end }) @@ -28,9 +31,9 @@ setmetatable(M, { --- --- It is not recommended to use this, use vim.treesitter.get_parser() instead. --- ---- @param bufnr The buffer the parser will be tied to ---- @param lang The language of the parser ---- @param opts Options to pass to the created language tree +---@param bufnr The buffer the parser will be tied to +---@param lang The language of the parser +---@param opts Options to pass to the created language tree function M._create_parser(bufnr, lang, opts) language.require_language(lang) if bufnr == 0 then @@ -71,11 +74,11 @@ end --- If needed this will create the parser. --- Unconditionnally attach the provided callback --- ---- @param bufnr The buffer the parser should be tied to ---- @param lang The filetype of this parser ---- @param opts Options object to pass to the created language tree +---@param bufnr The buffer the parser should be tied to +---@param lang The filetype of this parser +---@param opts Options object to pass to the created language tree --- ---- @returns The parser +---@returns The parser function M.get_parser(bufnr, lang, opts) opts = opts or {} @@ -97,9 +100,9 @@ end --- Gets a string parser --- ---- @param str The string to parse ---- @param lang The language of this string ---- @param opts Options to pass to the created language tree +---@param str The string to parse +---@param lang The language of this string +---@param opts Options to pass to the created language tree function M.get_string_parser(str, lang, opts) vim.validate { str = { str, 'string' }, diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 84b6a5f135..22b528838c 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -22,8 +22,6 @@ local _link_default_highlight_once = function(from, to) return from end --- These are conventions defined by nvim-treesitter, though it --- needs to be user extensible also. TSHighlighter.hl_map = { ["error"] = "Error", @@ -87,8 +85,10 @@ function TSHighlighterQuery.new(lang, query_string) hl = _link_default_highlight_once(lang .. hl, hl) end - rawset(table, capture, hl) - return hl + local id = a.nvim_get_hl_id_by_name(hl) + + rawset(table, capture, id) + return id end }) @@ -116,14 +116,14 @@ function TSHighlighterQuery:_get_hl_from_capture(capture) -- From "Normal.left" only keep "Normal" return vim.split(name, '.', true)[1], true else - return TSHighlighter.hl_map[name] or name, false + return TSHighlighter.hl_map[name] or 0, false end end --- Creates a new highlighter using @param tree --- ---- @param tree The language tree to use for highlighting ---- @param opts Table used to configure the highlighter +---@param tree The language tree to use for highlighting +---@param opts Table used to configure the highlighter --- - queries: Table to overwrite queries used by the highlighter function TSHighlighter.new(tree, opts) local self = setmetatable({}, TSHighlighter) @@ -217,7 +217,7 @@ end --- Gets the query used for @param lang --- ---- @param lang A language used by the highlighter. +---@param lang A language used by the highlighter. function TSHighlighter:get_query(lang) if not self._queries[lang] then self._queries[lang] = TSHighlighterQuery.new(lang) @@ -248,7 +248,7 @@ local function on_line_impl(self, buf, line) end while line >= state.next_row do - local capture, node = state.iter() + local capture, node, metadata = state.iter() if capture == nil then break end @@ -260,7 +260,7 @@ local function on_line_impl(self, buf, line) { end_line = end_row, end_col = end_col, hl_group = hl, ephemeral = true, - priority = 100 -- Low but leaves room below + priority = tonumber(metadata.priority) or 100 -- Low but leaves room below }) end if start_row > line then diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index 6dc37c7848..89ddd6cd5a 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -6,9 +6,9 @@ local M = {} --- --- Parsers are searched in the `parser` runtime directory. --- ---- @param lang The language the parser should parse ---- @param path Optional path the parser is located at ---- @param silent Don't throw an error if language not found +---@param lang The language the parser should parse +---@param path Optional path the parser is located at +---@param silent Don't throw an error if language not found function M.require_language(lang, path, silent) if vim._ts_has_language(lang) then return true @@ -40,7 +40,7 @@ end --- --- Inspecting provides some useful informations on the language like node names, ... --- ---- @param lang The language. +---@param lang The language. function M.inspect_language(lang) M.require_language(lang) return vim._ts_inspect_language(lang) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 899d90e464..7e392f72a4 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -9,12 +9,12 @@ LanguageTree.__index = LanguageTree --- The language can contain child languages with in its range, --- hence the tree. --- ---- @param source Can be a bufnr or a string of text to parse ---- @param lang The language this tree represents ---- @param opts Options table ---- @param opts.injections A table of language to injection query strings. ---- This is useful for overriding the built-in runtime file ---- searching for the injection language query per language. +---@param source Can be a bufnr or a string of text to parse +---@param lang The language this tree represents +---@param opts Options table +---@param opts.injections A table of language to injection query strings. +--- This is useful for overriding the built-in runtime file +--- searching for the injection language query per language. function LanguageTree.new(source, lang, opts) language.require_language(lang) opts = opts or {} @@ -171,8 +171,8 @@ end --- Invokes the callback for each LanguageTree and it's children recursively --- ---- @param fn The function to invoke. This is invoked with arguments (tree: LanguageTree, lang: string) ---- @param include_self Whether to include the invoking tree in the results. +---@param fn The function to invoke. This is invoked with arguments (tree: LanguageTree, lang: string) +---@param include_self Whether to include the invoking tree in the results. function LanguageTree:for_each_child(fn, include_self) if include_self then fn(self, self._lang) @@ -187,8 +187,8 @@ end --- --- Note, this includes the invoking language tree's trees as well. --- ---- @param fn The callback to invoke. The callback is invoked with arguments ---- (tree: TSTree, languageTree: LanguageTree) +---@param fn The callback to invoke. The callback is invoked with arguments +--- (tree: TSTree, languageTree: LanguageTree) function LanguageTree:for_each_tree(fn) for _, tree in ipairs(self._trees) do fn(tree, self) @@ -203,7 +203,7 @@ end --- --- If the language already exists as a child, it will first be removed. --- ---- @param lang The language to add. +---@param lang The language to add. function LanguageTree:add_child(lang) if self._children[lang] then self:remove_child(lang) @@ -219,7 +219,7 @@ end --- Removes a child language from this tree. --- ---- @param lang The language to remove. +---@param lang The language to remove. function LanguageTree:remove_child(lang) local child = self._children[lang] @@ -259,7 +259,7 @@ end --- --- Note, this call invalidates the tree and requires it to be parsed again. --- ---- @param regions A list of regions this tree should manage and parse. +---@param regions A list of regions this tree should manage and parse. function LanguageTree:set_included_regions(regions) -- TODO(vigoux): I don't think string parsers are useful for now if type(self._source) == "number" then @@ -299,7 +299,7 @@ end --- --- TODO: Allow for an offset predicate to tailor the injection range --- instead of using the entire nodes range. ---- @private +---@private function LanguageTree:_get_injections() if not self._injection_query then return {} end @@ -449,7 +449,7 @@ function LanguageTree:_on_detach(...) end --- Registers callbacks for the parser ---- @param cbs An `nvim_buf_attach`-like table argument with the following keys : +---@param cbs An `nvim_buf_attach`-like table argument with the following keys : --- `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 only be passed one argument, that is a table of the ranges (as node ranges) that @@ -497,7 +497,7 @@ end --- --- This goes down the tree to recursively check childs. --- ---- @param range A range, that is a `{ start_line, start_col, end_line, end_col }` table. +---@param range A range, that is a `{ start_line, start_col, end_line, end_col }` table. function LanguageTree:contains(range) for _, tree in pairs(self._trees) do if tree_contains(tree, range) then @@ -510,7 +510,7 @@ end --- Gets the appropriate language that contains @param range --- ---- @param range A text range, see |LanguageTree:contains| +---@param range A text range, see |LanguageTree:contains| 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 b81eb18945..66da179ea3 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -36,9 +36,9 @@ end --- Gets the list of files used to make up a query --- ---- @param lang The language ---- @param query_name The name of the query to load ---- @param is_included Internal parameter, most of the time left as `nil` +---@param lang The language +---@param query_name The name of the query to load +---@param is_included Internal parameter, most of the time left as `nil` function M.get_query_files(lang, query_name, is_included) local query_path = string.format('queries/%s/%s.scm', lang, query_name) local lang_files = dedupe_files(a.nvim_get_runtime_file(query_path, true)) @@ -112,19 +112,19 @@ local explicit_queries = setmetatable({}, { --- This allows users to override any runtime files and/or configuration --- set by plugins. --- ---- @param lang string: The language to use for the query ---- @param query_name string: The name of the query (i.e. "highlights") ---- @param text string: The query text (unparsed). +---@param lang string: The language to use for the query +---@param query_name string: The name of the query (i.e. "highlights") +---@param text string: The query text (unparsed). function M.set_query(lang, query_name, text) explicit_queries[lang][query_name] = M.parse_query(lang, text) end --- Returns the runtime query {query_name} for {lang}. --- ---- @param lang The language to use for the query ---- @param query_name The name of the query (i.e. "highlights") +---@param lang The language to use for the query +---@param query_name The name of the query (i.e. "highlights") --- ---- @return The corresponding query, parsed. +---@return The corresponding query, parsed. function M.get_query(lang, query_name) if explicit_queries[lang][query_name] then return explicit_queries[lang][query_name] @@ -151,10 +151,10 @@ end --- -` info.captures` also points to `captures`. --- - `info.patterns` contains information about predicates. --- ---- @param lang The language ---- @param query A string containing the query (s-expr syntax) +---@param lang The language +---@param query A string containing the query (s-expr syntax) --- ---- @returns The query +---@returns The query function M.parse_query(lang, query) language.require_language(lang) local self = setmetatable({}, Query) @@ -168,8 +168,8 @@ end --- Gets the text corresponding to a given node --- ---- @param node the node ---- @param bsource The buffer or string from which the node is extracted +---@param node the node +---@param bsource The buffer or string from which the node is extracted function M.get_node_text(node, source) local start_row, start_col, start_byte = node:start() local end_row, end_col, end_byte = node:end_() @@ -327,9 +327,9 @@ local directive_handlers = { --- Adds a new predicate to be used in queries --- ---- @param name the name of the predicate, without leading # ---- @param handler the handler function to be used ---- signature will be (match, pattern, bufnr, predicate) +---@param name the name of the predicate, without leading # +---@param handler the handler function to be used +--- signature will be (match, pattern, bufnr, predicate) function M.add_predicate(name, handler, force) if predicate_handlers[name] and not force then error(string.format("Overriding %s", name)) @@ -340,9 +340,9 @@ end --- Adds a new directive to be used in queries --- ---- @param name the name of the directive, without leading # ---- @param handler the handler function to be used ---- signature will be (match, pattern, bufnr, predicate) +---@param name the name of the directive, without leading # +---@param handler the handler function to be used +--- signature will be (match, pattern, bufnr, predicate) function M.add_directive(name, handler, force) if directive_handlers[name] and not force then error(string.format("Overriding %s", name)) @@ -351,7 +351,12 @@ function M.add_directive(name, handler, force) directive_handlers[name] = handler end ---- Returns the list of currently supported predicates +---@return The list of supported directives. +function M.list_directives() + return vim.tbl_keys(directive_handlers) +end + +---@return The list of supported predicates. function M.list_predicates() return vim.tbl_keys(predicate_handlers) end @@ -460,13 +465,13 @@ end --- end --- </pre> --- ---- @param node The node under which the search will occur ---- @param source The source buffer or string to exctract text from ---- @param start The starting line of the search ---- @param stop The stopping line of the search (end-exclusive) +---@param node The node under which the search will occur +---@param source The source buffer or string to exctract text from +---@param start The starting line of the search +---@param stop The stopping line of the search (end-exclusive) --- ---- @returns The matching capture id ---- @returns The captured node +---@returns The matching capture id +---@returns The captured node function Query:iter_captures(node, source, start, stop) if type(source) == "number" and source == 0 then source = vim.api.nvim_get_current_buf() @@ -517,13 +522,13 @@ end --- end --- </pre> --- ---- @param node The node under which the search will occur ---- @param source The source buffer or string to search ---- @param start The starting line of the search ---- @param stop The stopping line of the search (end-exclusive) +---@param node The node under which the search will occur +---@param source The source buffer or string to search +---@param start The starting line of the search +---@param stop The stopping line of the search (end-exclusive) --- ---- @returns The matching pattern id ---- @returns The matching match +---@returns The matching pattern id +---@returns The matching match function Query:iter_matches(node, source, start, stop) if type(source) == "number" and source == 0 then source = vim.api.nvim_get_current_buf() diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua index f1a12c72ec..a3e79a0f2b 100644 --- a/runtime/lua/vim/uri.lua +++ b/runtime/lua/vim/uri.lua @@ -9,7 +9,7 @@ do local schar = string.char --- Convert hex to char - --@private + ---@private local function hex_to_char(hex) return schar(tonumber(hex, 16)) end @@ -38,7 +38,7 @@ do tohex = function(b) return string.format("%02x", b) end end - --@private + ---@private local function percent_encode_char(char) return "%"..tohex(sbyte(char), 2) end @@ -50,14 +50,14 @@ do end ---@private +---@private local function is_windows_file_uri(uri) - return uri:match('^file:///[a-zA-Z]:') ~= nil + return uri:match('^file:/+[a-zA-Z]:') ~= nil end --- Get a URI from a file path. ---@param path (string): Path to file ---@return URI +---@param path (string): Path to file +---@return URI local function uri_from_fname(path) local volume_path, fname = path:match("^([a-zA-Z]:)(.*)") local is_windows = volume_path ~= nil @@ -74,11 +74,11 @@ local function uri_from_fname(path) return table.concat(uri_parts) end -local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*)://.*' +local URI_SCHEME_PATTERN = '^([a-zA-Z]+[a-zA-Z0-9+-.]*):.*' --- Get a URI from a bufnr ---@param bufnr (number): Buffer number ---@return URI +---@param bufnr (number): Buffer number +---@return URI local function uri_from_bufnr(bufnr) local fname = vim.api.nvim_buf_get_name(bufnr) local scheme = fname:match(URI_SCHEME_PATTERN) @@ -90,8 +90,8 @@ local function uri_from_bufnr(bufnr) end --- Get a filename from a URI ---@param uri (string): The URI ---@return Filename +---@param uri (string): The URI +---@return Filename local function uri_to_fname(uri) local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri) if scheme ~= 'file' then @@ -100,18 +100,18 @@ local function uri_to_fname(uri) uri = uri_decode(uri) -- TODO improve this. if is_windows_file_uri(uri) then - uri = uri:gsub('^file:///', '') + uri = uri:gsub('^file:/+', '') uri = uri:gsub('/', '\\') else - uri = uri:gsub('^file://', '') + uri = uri:gsub('^file:/+', '/') end return uri end --- Return or create a buffer for a uri. ---@param uri (string): The URI ---@return bufnr. ---@note Creates buffer but does not load it +---@param uri (string): The URI +---@return bufnr. +---@note Creates buffer but does not load it local function uri_to_bufnr(uri) local scheme = assert(uri:match(URI_SCHEME_PATTERN), 'URI must contain a scheme: ' .. uri) if scheme == 'file' then diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index e99c76a930..099c9a57c0 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,7 @@ </screenshots> <releases> + <release date="2021-07-02" version="0.5.0"/> <release date="2020-08-04" version="0.4.4"/> <release date="2019-11-06" version="0.4.3"/> <release date="2019-09-15" version="0.4.2"/> diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 633cb9e509..d4c10f7afa 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -440,6 +440,9 @@ if has("syntax") call append("$", "cursorline\thighlight the screen line of the cursor") call append("$", "\t(local to window)") call <SID>BinOptionL("cul") + call append("$", "cursorlineopt\tspecifies which area 'cursorline' highlights") + call append("$", "\t(local to window)") + call <SID>OptionL("culopt") call append("$", "colorcolumn\tcolumns to highlight") call append("$", "\t(local to window)") call <SID>OptionL("cc") diff --git a/runtime/pack/dist/opt/matchit/doc/matchit.txt b/runtime/pack/dist/opt/matchit/doc/matchit.txt index 3cd2c8e2a7..58a47780ef 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 8.1. Last change: 2020 Mar 01 +For Vim version 8.1. Last change: 2021 May 17 VIM REFERENCE MANUAL by Benji Fisher et al @@ -322,7 +322,7 @@ should work (and have the same effect as "foobar:barfoo:endfoobar"), although this has not been thoroughly tested. You can use |zero-width| patterns such as |\@<=| and |\zs|. (The latter has -not been thouroughly tested in matchit.vim.) For example, if the keyword "if" +not been thoroughly tested in matchit.vim.) For example, if the keyword "if" must occur at the start of the line, with optional white space, you might use the pattern "\(^\s*\)\@<=if" so that the cursor will end on the "i" instead of at the start of the line. For another example, if HTML had only one tag then diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index ae1274f81f..2914e2bc4d 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -2,7 +2,7 @@ " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" -" Last Change: 2021 May 16 +" Last Change: 2021 May 18 " " WORK IN PROGRESS - Only the basics work " Note: On MS-Windows you need a recent version of gdb. The one included with @@ -181,6 +181,15 @@ func s:CloseBuffers() unlet! s:gdbwin endfunc +func s:CheckGdbRunning() + if nvim_get_chan_info(s:gdb_job_id) == {} + echoerr string(g:termdebugger) . ' exited unexpectedly' + call s:CloseBuffers() + return '' + endif + return 'ok' +endfunc + func s:StartDebug_term(dict) " Open a terminal window without a job, to run the debugged program in. execute s:vertical ? 'vnew' : 'new' @@ -229,7 +238,7 @@ func s:StartDebug_term(dict) let gdb_args = get(a:dict, 'gdb_args', []) let proc_args = get(a:dict, 'proc_args', []) - let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args + let cmd = [g:termdebugger, '-quiet', '-tty', pty, '--eval-command', 'echo startupdone\n'] + gdb_args "call ch_log('executing "' . join(cmd) . '"') execute 'new' let s:gdb_job_id = termopen(cmd, {'on_exit': function('s:EndTermDebug')}) @@ -246,9 +255,28 @@ func s:StartDebug_term(dict) let s:gdbbuf = gdb_job_info['buffer'] let s:gdbwin = win_getid(winnr()) - " Set arguments to be run. First wait a bit to make detecting gdb a bit - " more reliable. - sleep 200m + " Wait for the "startupdone" message before sending any commands. + let try_count = 0 + while 1 + if s:CheckGdbRunning() != 'ok' + return + endif + + for lnum in range(1, 200) + if get(getbufline(s:gdbbuf, lnum), 0, '') =~ 'startupdone' + let try_count = 9999 + break + endif + endfor + let try_count += 1 + if try_count > 300 + " done or give up after five seconds + break + endif + sleep 10m + endwhile + + " Set arguments to be run. if len(proc_args) call chansend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r") endif @@ -260,9 +288,7 @@ func s:StartDebug_term(dict) " why the debugger doesn't work. let try_count = 0 while 1 - if nvim_get_chan_info(s:gdb_job_id) == {} - echoerr string(g:termdebugger) . ' exited unexpectedly' - call s:CloseBuffers() + if s:CheckGdbRunning() != 'ok' return endif diff --git a/runtime/plugin/man.vim b/runtime/plugin/man.vim index 689aa32ef3..b10677593f 100644 --- a/runtime/plugin/man.vim +++ b/runtime/plugin/man.vim @@ -5,8 +5,8 @@ if exists('g:loaded_man') endif let g:loaded_man = 1 -command! -bang -bar -range=-1 -complete=customlist,man#complete -nargs=* Man - \ if <bang>0 | set ft=man | +command! -bang -bar -addr=other -complete=customlist,man#complete -nargs=* Man + \ if <bang>0 | call man#init_pager() | \ else | call man#open_page(<count>, <q-mods>, <f-args>) | endif augroup man diff --git a/runtime/spell/en.utf-8.spl b/runtime/spell/en.utf-8.spl Binary files differindex 83b9b8f7c2..e4b1e1cce7 100644 --- a/runtime/spell/en.utf-8.spl +++ b/runtime/spell/en.utf-8.spl diff --git a/runtime/syntax/8th.vim b/runtime/syntax/8th.vim index ddc1084c9f..d543489b72 100644 --- a/runtime/syntax/8th.vim +++ b/runtime/syntax/8th.vim @@ -293,7 +293,7 @@ syn region eighthComment start="\zs\\" end="$" contains=eighthTodo " Define the default highlighting. if !exists("did_eighth_syntax_inits") let did_eighth_syntax_inits=1 - " The default methods for highlighting. Can be overriden later. + " The default methods for highlighting. Can be overridden later. hi def link eighthTodo Todo hi def link eighthOperators Operator hi def link eighthMath Number diff --git a/runtime/syntax/aptconf.vim b/runtime/syntax/aptconf.vim index 8cb14321e2..d51e7bdfa9 100644 --- a/runtime/syntax/aptconf.vim +++ b/runtime/syntax/aptconf.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: APT config file " Maintainer: Yann Amar <quidame@poivron.org> -" Last Change: 2015 Dec 22 +" Last Change: 2021 Jul 12 " quit when a syntax file was already loaded if !exists("main_syntax") @@ -396,10 +396,13 @@ syn cluster aptconfSynaptic_ contains=aptconfSynaptic, " }}} " Unattended Upgrade: {{{ syn keyword aptconfUnattendedUpgrade contained - \ AutoFixInterruptedDpkg Automatic-Reboot Automatic-Reboot-Time - \ Automatic-Reboot-WithUsers InstallOnShutdown Mail MailOnlyOnError - \ MinimalSteps Origins-Pattern Package-Blacklist - \ Remove-Unused-Dependencies + \ Allow-APT-Mark-Fallback Allow-downgrade AutoFixInterruptedDpkg + \ Automatic-Reboot Automatic-Reboot-Time Automatic-Reboot-WithUsers + \ Debug InstallOnShutdown Mail MailOnlyOnError MailReport MinimalSteps + \ OnlyOnACPower Origins-Pattern Package-Blacklist + \ Remove-New-Unused-Dependencies Remove-Unused-Dependencies + \ Remove-Unused-Kernel-Packages Skip-Updates-On-Metered-Connections + \ SyslogEnable SyslogFacility Verbose syn cluster aptconfUnattendedUpgrade_ contains=aptconfUnattendedUpgrade " }}} diff --git a/runtime/syntax/c.vim b/runtime/syntax/c.vim index d07aaf2658..20f8632006 100644 --- a/runtime/syntax/c.vim +++ b/runtime/syntax/c.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: C " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2021 Jan 11 +" Last Change: 2021 May 24 " Quit when a (custom) syntax file was already loaded if exists("b:current_syntax") @@ -414,6 +414,9 @@ if exists("c_autodoc") syn cluster cPreProcGroup add=cAutodocReal endif +" be able to fold #pragma regions +syn region cPragma start="^\s*#pragma\s\+region\>" end="^\s*#pragma\s\+endregion\>" transparent keepend extend fold + " Highlight User Labels syn cluster cMultiGroup contains=cIncluded,cSpecial,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cUserCont,cUserLabel,cBitField,cOctalZero,cCppOutWrapper,cCppInWrapper,@cCppOutInGroup,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom,cCppParen,cCppBracket,cCppString if s:ft ==# 'c' || exists("cpp_no_cpp11") diff --git a/runtime/syntax/cpp.vim b/runtime/syntax/cpp.vim index ed38913f29..3ad79d5545 100644 --- a/runtime/syntax/cpp.vim +++ b/runtime/syntax/cpp.vim @@ -2,7 +2,7 @@ " Language: C++ " Current Maintainer: vim-jp (https://github.com/vim-jp/vim-cpp) " Previous Maintainer: Ken Shan <ccshan@post.harvard.edu> -" Last Change: 2021 Jan 12 +" Last Change: 2021 May 04 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -44,22 +44,40 @@ if !exists("cpp_no_cpp11") syn keyword cppConstant ATOMIC_WCHAR_T_LOCK_FREE ATOMIC_SHORT_LOCK_FREE syn keyword cppConstant ATOMIC_INT_LOCK_FREE ATOMIC_LONG_LOCK_FREE syn keyword cppConstant ATOMIC_LLONG_LOCK_FREE ATOMIC_POINTER_LOCK_FREE - syn region cppRawString matchgroup=cppRawStringDelimiter start=+\%(u8\|[uLU]\)\=R"\z([[:alnum:]_{}[\]#<>%:;.?*\+\-/\^&|~!=,"']\{,16}\)(+ end=+)\z1"+ contains=@Spell + syn region cppRawString matchgroup=cppRawStringDelimiter start=+\%(u8\|[uLU]\)\=R"\z([[:alnum:]_{}[\]#<>%:;.?*\+\-/\^&|~!=,"']\{,16}\)(+ end=+)\z1"\(sv\|s\|_[_a-zA-Z][_a-zA-Z0-9]*\)\=+ contains=@Spell syn match cppCast "\<\(const\|static\|dynamic\)_pointer_cast\s*<"me=e-1 syn match cppCast "\<\(const\|static\|dynamic\)_pointer_cast\s*$" endif " C++ 14 extensions if !exists("cpp_no_cpp14") - syn case ignore - syn match cppNumber display "\<0b[01]\('\=[01]\+\)*\(u\=l\{0,2}\|ll\=u\)\>" - syn match cppNumber display "\<[1-9]\('\=\d\+\)*\(u\=l\{0,2}\|ll\=u\)\>" contains=cFloat - syn match cppNumber display "\<0x\x\('\=\x\+\)*\(u\=l\{0,2}\|ll\=u\)\>" - syn case match + 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 "\<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\+e[-+]\=\d\+\([FfLl]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" + syn region cppString start=+\(L\|u\|u8\|U\|R\|LR\|u8R\|uR\|UR\)\="+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"\(sv\|s\|_\i*\)\=+ end='$' contains=cSpecial,cFormat,@Spell +endif + +" C++ 17 extensions +if !exists("cpp_no_cpp17") + syn match cppCast "\<reinterpret_pointer_cast\s*<"me=e-1 + syn match cppCast "\<reinterpret_pointer_cast\s*$" + syn match cppFloat display contained "\<0x\x*\.\x\+p[-+]\=\d\+\([FfLl]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" + syn match cppFloat display contained "\<0x\x\+\.\=p[-+]\=\d\+\([FfLl]\|i[fl]\=\|h\|min\|s\|ms\|us\|ns\|_\i*\)\=\>" endif " C++ 20 extensions if !exists("cpp_no_cpp20") + syn match cppNumber display contained "\<0\(y\|d\)\>" + syn match cppNumber display contained "\<[1-9]\('\=\d\+\)*\(y\|d\)\>" + syn match cppNumber display contained "\<0\o\+\(y\|d\)\>" + syn match cppNumber display contained "\<0b[01]\('\=[01]\+\)*\(y\|d\)\>" + syn match cppNumber display contained "\<0x\x\('\=\x\+\)*\(y\|d\)\>" syn keyword cppStatement co_await co_return co_yield requires syn keyword cppStorageClass consteval constinit syn keyword cppStructure concept @@ -67,12 +85,6 @@ if !exists("cpp_no_cpp20") syn keyword cppModule import module export endif -" C++ 17 extensions -if !exists("cpp_no_cpp17") - syn match cppCast "\<reinterpret_pointer_cast\s*<"me=e-1 - syn match cppCast "\<reinterpret_pointer_cast\s*$" -endif - " The minimum and maximum operators in GNU C++ syn match cppMinMax "[<>]?" @@ -90,7 +102,9 @@ hi def link cppBoolean Boolean hi def link cppConstant Constant hi def link cppRawStringDelimiter Delimiter hi def link cppRawString String +hi def link cppString String hi def link cppNumber Number +hi def link cppFloat Number hi def link cppModule Include let b:current_syntax = "cpp" diff --git a/runtime/syntax/go.vim b/runtime/syntax/go.vim index e78f8cf27c..1439487f69 100644 --- a/runtime/syntax/go.vim +++ b/runtime/syntax/go.vim @@ -1,58 +1,109 @@ -" Vim syntax file -" Language: Go -" Maintainer: David Barnett (https://github.com/google/vim-ft-go) -" Last Change: 2014 Aug 16 - -" Options: -" There are some options for customizing the highlighting; the recommended -" settings are the default values, but you can write: -" let OPTION_NAME = 0 -" in your ~/.vimrc file to disable particular options. You can also write: -" let OPTION_NAME = 1 -" to enable particular options. At present, all options default to on. +" Copyright 2009 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. " -" - g:go_highlight_array_whitespace_error -" Highlights white space after "[]". -" - g:go_highlight_chan_whitespace_error -" Highlights white space around the communications operator that don't -" follow the standard style. -" - g:go_highlight_extra_types -" Highlights commonly used library types (io.Reader, etc.). -" - g:go_highlight_space_tab_error -" Highlights instances of tabs following spaces. -" - g:go_highlight_trailing_whitespace_error -" Highlights trailing white space. +" go.vim: Vim syntax file for Go. +" Language: Go +" Maintainer: Billie Cleek <bhcleek@gmail.com> +" Latest Revision: 2021-06-26 +" License: BSD-style. See LICENSE file in source repository. +" Repository: https://github.com/fatih/vim-go " Quit when a (custom) syntax file was already loaded -if exists('b:current_syntax') +if exists("b:current_syntax") finish endif -if !exists('g:go_highlight_array_whitespace_error') - let g:go_highlight_array_whitespace_error = 1 -endif -if !exists('g:go_highlight_chan_whitespace_error') - let g:go_highlight_chan_whitespace_error = 1 -endif -if !exists('g:go_highlight_extra_types') - let g:go_highlight_extra_types = 1 -endif -if !exists('g:go_highlight_space_tab_error') - let g:go_highlight_space_tab_error = 1 -endif -if !exists('g:go_highlight_trailing_whitespace_error') - let g:go_highlight_trailing_whitespace_error = 1 -endif +let s:keepcpo = &cpo +set cpo&vim + +function! s:FoldEnable(...) abort + if a:0 > 0 + return index(s:FoldEnable(), a:1) > -1 + endif + return get(g:, 'go_fold_enable', ['block', 'import', 'varconst', 'package_comment']) +endfunction + +function! s:HighlightArrayWhitespaceError() abort + return get(g:, 'go_highlight_array_whitespace_error', 0) +endfunction + +function! s:HighlightChanWhitespaceError() abort + return get(g:, 'go_highlight_chan_whitespace_error', 0) +endfunction + +function! s:HighlightExtraTypes() abort + return get(g:, 'go_highlight_extra_types', 0) +endfunction + +function! s:HighlightSpaceTabError() abort + return get(g:, 'go_highlight_space_tab_error', 0) +endfunction + +function! s:HighlightTrailingWhitespaceError() abort + return get(g:, 'go_highlight_trailing_whitespace_error', 0) +endfunction + +function! s:HighlightOperators() abort + return get(g:, 'go_highlight_operators', 0) +endfunction + +function! s:HighlightFunctions() abort + return get(g:, 'go_highlight_functions', 0) +endfunction + +function! s:HighlightFunctionParameters() abort + return get(g:, 'go_highlight_function_parameters', 0) +endfunction + +function! s:HighlightFunctionCalls() abort + return get(g:, 'go_highlight_function_calls', 0) +endfunction + +function! s:HighlightFields() abort + return get(g:, 'go_highlight_fields', 0) +endfunction + +function! s:HighlightTypes() abort + return get(g:, 'go_highlight_types', 0) +endfunction + +function! s:HighlightBuildConstraints() abort + return get(g:, 'go_highlight_build_constraints', 0) +endfunction + +function! s:HighlightStringSpellcheck() abort + return get(g:, 'go_highlight_string_spellcheck', 1) +endfunction + +function! s:HighlightFormatStrings() abort + return get(g:, 'go_highlight_format_strings', 1) +endfunction + +function! s:HighlightGenerateTags() abort + return get(g:, 'go_highlight_generate_tags', 0) +endfunction + +function! s:HighlightVariableAssignments() abort + return get(g:, 'go_highlight_variable_assignments', 0) +endfunction + +function! s:HighlightVariableDeclarations() abort + return get(g:, 'go_highlight_variable_declarations', 0) +endfunction syn case match -syn keyword goDirective package import -syn keyword goDeclaration var const type -syn keyword goDeclType struct interface +syn keyword goPackage package +syn keyword goImport import contained +syn keyword goVar var contained +syn keyword goConst const contained -hi def link goDirective Statement +hi def link goPackage Statement +hi def link goImport Statement +hi def link goVar Keyword +hi def link goConst Keyword hi def link goDeclaration Keyword -hi def link goDeclType Keyword " Keywords within functions syn keyword goStatement defer go goto return break continue fallthrough @@ -78,28 +129,38 @@ hi def link goUnsignedInts Type hi def link goFloats Type hi def link goComplexes Type -" Treat func specially: it's a declaration at the start of a line, but a type -" elsewhere. Order matters here. -syn match goType /\<func\>/ -syn match goDeclaration /^func\>/ - " Predefined functions and values -syn keyword goBuiltins append cap close complex copy delete imag len -syn keyword goBuiltins make new panic print println real recover -syn keyword goConstants iota true false nil +syn keyword goBuiltins append cap close complex copy delete imag len +syn keyword goBuiltins make new panic print println real recover +syn keyword goBoolean true false +syn keyword goPredefinedIdentifiers nil iota -hi def link goBuiltins Keyword -hi def link goConstants Keyword +hi def link goBuiltins Identifier +hi def link goBoolean Boolean +hi def link goPredefinedIdentifiers goBoolean " Comments; their contents syn keyword goTodo contained TODO FIXME XXX BUG syn cluster goCommentGroup contains=goTodo -syn region goComment start="/\*" end="\*/" contains=@goCommentGroup,@Spell -syn region goComment start="//" end="$" contains=@goCommentGroup,@Spell + +syn region goComment start="//" end="$" contains=goGenerate,@goCommentGroup,@Spell +if s:FoldEnable('comment') + syn region goComment start="/\*" end="\*/" contains=@goCommentGroup,@Spell fold + syn match goComment "\v(^\s*//.*\n)+" contains=goGenerate,@goCommentGroup,@Spell fold +else + syn region goComment start="/\*" end="\*/" contains=@goCommentGroup,@Spell +endif hi def link goComment Comment hi def link goTodo Todo +if s:HighlightGenerateTags() + syn match goGenerateVariables contained /\%(\$GOARCH\|\$GOOS\|\$GOFILE\|\$GOLINE\|\$GOPACKAGE\|\$DOLLAR\)\>/ + syn region goGenerate start="^\s*//go:generate" end="$" contains=goGenerateVariables + hi def link goGenerate PreProc + hi def link goGenerateVariables Special +endif + " Go escapes syn match goEscapeOctal display contained "\\[0-7]\{3}" syn match goEscapeC display contained +\\[abfnrtv\\'"]+ @@ -118,8 +179,30 @@ hi def link goEscapeError Error " Strings and their contents syn cluster goStringGroup contains=goEscapeOctal,goEscapeC,goEscapeX,goEscapeU,goEscapeBigU,goEscapeError -syn region goString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@goStringGroup -syn region goRawString start=+`+ end=+`+ +if s:HighlightStringSpellcheck() + syn region goString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@goStringGroup,@Spell + syn region goRawString start=+`+ end=+`+ contains=@Spell +else + syn region goString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@goStringGroup + syn region goRawString start=+`+ end=+`+ +endif + +if s:HighlightFormatStrings() + " [n] notation is valid for specifying explicit argument indexes + " 1. Match a literal % not preceded by a %. + " 2. Match any number of -, #, 0, space, or + + " 3. Match * or [n]* or any number or nothing before a . + " 4. Match * or [n]* or any number or nothing after a . + " 5. Match [n] or nothing before a verb + " 6. Match a formatting verb + syn match goFormatSpecifier /\ + \%([^%]\%(%%\)*\)\ + \@<=%[-#0 +]*\ + \%(\%(\%(\[\d\+\]\)\=\*\)\|\d\+\)\=\ + \%(\.\%(\%(\%(\[\d\+\]\)\=\*\)\|\d\+\)\=\)\=\ + \%(\[\d\+\]\)\=[vTtbcdoqxXUeEfFgGspw]/ contained containedin=goString,goRawString + hi def link goFormatSpecifier goSpecialString +endif hi def link goString String hi def link goRawString String @@ -131,71 +214,263 @@ syn region goCharacter start=+'+ skip=+\\\\\|\\'+ end=+'+ contains= hi def link goCharacter Character " Regions -syn region goBlock start="{" end="}" transparent fold syn region goParen start='(' end=')' transparent +if s:FoldEnable('block') + syn region goBlock start="{" end="}" transparent fold +else + syn region goBlock start="{" end="}" transparent +endif + +" import +if s:FoldEnable('import') + syn region goImport start='import (' end=')' transparent fold contains=goImport,goString,goComment +else + syn region goImport start='import (' end=')' transparent contains=goImport,goString,goComment +endif + +" var, const +if s:FoldEnable('varconst') + syn region goVar start='var (' end='^\s*)$' transparent fold + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goParamName,goParamType,goSimpleParams,goPointerOperator + syn region goConst start='const (' end='^\s*)$' transparent fold + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goParamName,goParamType,goSimpleParams,goPointerOperator +else + syn region goVar start='var (' end='^\s*)$' transparent + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goParamName,goParamType,goSimpleParams,goPointerOperator + syn region goConst start='const (' end='^\s*)$' transparent + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goParamName,goParamType,goSimpleParams,goPointerOperator +endif + +" Single-line var, const, and import. +syn match goSingleDecl /\%(import\|var\|const\) [^(]\@=/ contains=goImport,goVar,goConst " Integers -syn match goDecimalInt "\<\d\+\([Ee]\d\+\)\?\>" -syn match goHexadecimalInt "\<0x\x\+\>" -syn match goOctalInt "\<0\o\+\>" -syn match goOctalError "\<0\o*[89]\d*\>" +syn match goDecimalInt "\<-\=\(0\|[1-9]_\?\(\d\|\d\+_\?\d\+\)*\)\%([Ee][-+]\=\d\+\)\=\>" +syn match goDecimalError "\<-\=\(_\(\d\+_*\)\+\|\([1-9]\d*_*\)\+__\(\d\+_*\)\+\|\([1-9]\d*_*\)\+_\+\)\%([Ee][-+]\=\d\+\)\=\>" +syn match goHexadecimalInt "\<-\=0[xX]_\?\(\x\+_\?\)\+\>" +syn match goHexadecimalError "\<-\=0[xX]_\?\(\x\+_\?\)*\(\([^ \t0-9A-Fa-f_)]\|__\)\S*\|_\)\>" +syn match goOctalInt "\<-\=0[oO]\?_\?\(\o\+_\?\)\+\>" +syn match goOctalError "\<-\=0[0-7oO_]*\(\([^ \t0-7oOxX_/)\]\}\:]\|[oO]\{2,\}\|__\)\S*\|_\|[oOxX]\)\>" +syn match goBinaryInt "\<-\=0[bB]_\?\([01]\+_\?\)\+\>" +syn match goBinaryError "\<-\=0[bB]_\?[01_]*\([^ \t01_)]\S*\|__\S*\|_\)\>" hi def link goDecimalInt Integer +hi def link goDecimalError Error hi def link goHexadecimalInt Integer +hi def link goHexadecimalError Error hi def link goOctalInt Integer +hi def link goOctalError Error +hi def link goBinaryInt Integer +hi def link goBinaryError Error hi def link Integer Number " Floating point -syn match goFloat "\<\d\+\.\d*\([Ee][-+]\d\+\)\?\>" -syn match goFloat "\<\.\d\+\([Ee][-+]\d\+\)\?\>" -syn match goFloat "\<\d\+[Ee][-+]\d\+\>" +syn match goFloat "\<-\=\d\+\.\d*\%([Ee][-+]\=\d\+\)\=\>" +syn match goFloat "\<-\=\.\d\+\%([Ee][-+]\=\d\+\)\=\>" hi def link goFloat Float " Imaginary literals -syn match goImaginary "\<\d\+i\>" -syn match goImaginary "\<\d\+\.\d*\([Ee][-+]\d\+\)\?i\>" -syn match goImaginary "\<\.\d\+\([Ee][-+]\d\+\)\?i\>" -syn match goImaginary "\<\d\+[Ee][-+]\d\+i\>" +syn match goImaginary "\<-\=\d\+i\>" +syn match goImaginary "\<-\=\d\+[Ee][-+]\=\d\+i\>" +syn match goImaginaryFloat "\<-\=\d\+\.\d*\%([Ee][-+]\=\d\+\)\=i\>" +syn match goImaginaryFloat "\<-\=\.\d\+\%([Ee][-+]\=\d\+\)\=i\>" hi def link goImaginary Number +hi def link goImaginaryFloat Float " Spaces after "[]" -if go_highlight_array_whitespace_error != 0 - syn match goSpaceError display "\(\[\]\)\@<=\s\+" +if s:HighlightArrayWhitespaceError() + syn match goSpaceError display "\%(\[\]\)\@<=\s\+" endif " Spacing errors around the 'chan' keyword -if go_highlight_chan_whitespace_error != 0 +if s:HighlightChanWhitespaceError() " receive-only annotation on chan type - syn match goSpaceError display "\(<-\)\@<=\s\+\(chan\>\)\@=" + " + " \(\<chan\>\)\@<!<- (only pick arrow when it doesn't come after a chan) + " this prevents picking up 'chan<- chan<-' but not '<- chan' + syn match goSpaceError display "\%(\%(\<chan\>\)\@<!<-\)\@<=\s\+\%(\<chan\>\)\@=" + " send-only annotation on chan type - syn match goSpaceError display "\(\<chan\)\@<=\s\+\(<-\)\@=" + " + " \(<-\)\@<!\<chan\> (only pick chan when it doesn't come after an arrow) + " this prevents picking up '<-chan <-chan' but not 'chan <-' + syn match goSpaceError display "\%(\%(<-\)\@<!\<chan\>\)\@<=\s\+\%(<-\)\@=" + " value-ignoring receives in a few contexts - syn match goSpaceError display "\(\(^\|[={(,;]\)\s*<-\)\@<=\s\+" + syn match goSpaceError display "\%(\%(^\|[={(,;]\)\s*<-\)\@<=\s\+" endif " Extra types commonly seen -if go_highlight_extra_types != 0 - syn match goExtraType /\<bytes\.\(Buffer\)\>/ - syn match goExtraType /\<io\.\(Reader\|Writer\|ReadWriter\|ReadWriteCloser\)\>/ - syn match goExtraType /\<reflect\.\(Kind\|Type\|Value\)\>/ +if s:HighlightExtraTypes() + syn match goExtraType /\<bytes\.\%(Buffer\)\>/ + syn match goExtraType /\<context\.\%(Context\)\>/ + syn match goExtraType /\<io\.\%(Reader\|ReadSeeker\|ReadWriter\|ReadCloser\|ReadWriteCloser\|Writer\|WriteCloser\|Seeker\)\>/ + syn match goExtraType /\<reflect\.\%(Kind\|Type\|Value\)\>/ syn match goExtraType /\<unsafe\.Pointer\>/ endif " Space-tab error -if go_highlight_space_tab_error != 0 +if s:HighlightSpaceTabError() syn match goSpaceError display " \+\t"me=e-1 endif " Trailing white space error -if go_highlight_trailing_whitespace_error != 0 +if s:HighlightTrailingWhitespaceError() syn match goSpaceError display excludenl "\s\+$" endif hi def link goExtraType Type hi def link goSpaceError Error + + +" included from: https://github.com/athom/more-colorful.vim/blob/master/after/syntax/go.vim +" +" Comments; their contents +syn keyword goTodo contained NOTE +hi def link goTodo Todo + +syn match goVarArgs /\.\.\./ + +" Operators; +if s:HighlightOperators() + " match single-char operators: - + % < > ! & | ^ * = + " and corresponding two-char operators: -= += %= <= >= != &= |= ^= *= == + syn match goOperator /[-+%<>!&|^*=]=\?/ + " match / and /= + syn match goOperator /\/\%(=\|\ze[^/*]\)/ + " match two-char operators: << >> &^ + " and corresponding three-char operators: <<= >>= &^= + syn match goOperator /\%(<<\|>>\|&^\)=\?/ + " match remaining two-char operators: := && || <- ++ -- + syn match goOperator /:=\|||\|<-\|++\|--/ + " match ... + + hi def link goPointerOperator goOperator + hi def link goVarArgs goOperator +endif +hi def link goOperator Operator + +" Functions; +if s:HighlightFunctions() || s:HighlightFunctionParameters() + syn match goDeclaration /\<func\>/ nextgroup=goReceiver,goFunction,goSimpleParams skipwhite skipnl + syn match goReceiverVar /\w\+\ze\s\+\%(\w\|\*\)/ nextgroup=goPointerOperator,goReceiverType skipwhite skipnl contained + syn match goPointerOperator /\*/ nextgroup=goReceiverType contained skipwhite skipnl + syn match goFunction /\w\+/ nextgroup=goSimpleParams contained skipwhite skipnl + syn match goReceiverType /\w\+/ contained + if s:HighlightFunctionParameters() + syn match goSimpleParams /(\%(\w\|\_s\|[*\.\[\],\{\}<>-]\)*)/ contained contains=goParamName,goType nextgroup=goFunctionReturn skipwhite skipnl + syn match goFunctionReturn /(\%(\w\|\_s\|[*\.\[\],\{\}<>-]\)*)/ contained contains=goParamName,goType skipwhite skipnl + syn match goParamName /\w\+\%(\s*,\s*\w\+\)*\ze\s\+\%(\w\|\.\|\*\|\[\)/ contained nextgroup=goParamType skipwhite skipnl + syn match goParamType /\%([^,)]\|\_s\)\+,\?/ contained nextgroup=goParamName skipwhite skipnl + \ contains=goVarArgs,goType,goSignedInts,goUnsignedInts,goFloats,goComplexes,goDeclType,goBlock + hi def link goReceiverVar goParamName + hi def link goParamName Identifier + endif + syn match goReceiver /(\s*\w\+\%(\s\+\*\?\s*\w\+\)\?\s*)\ze\s*\w/ contained nextgroup=goFunction contains=goReceiverVar skipwhite skipnl +else + syn keyword goDeclaration func +endif +hi def link goFunction Function + +" Function calls; +if s:HighlightFunctionCalls() + syn match goFunctionCall /\w\+\ze(/ contains=goBuiltins,goDeclaration +endif +hi def link goFunctionCall Type + +" Fields; +if s:HighlightFields() + " 1. Match a sequence of word characters coming after a '.' + " 2. Require the following but dont match it: ( \@= see :h E59) + " - The symbols: / - + * % OR + " - The symbols: [] {} <> ) OR + " - The symbols: \n \r space OR + " - The symbols: , : . + " 3. Have the start of highlight (hs) be the start of matched + " pattern (s) offsetted one to the right (+1) (see :h E401) + syn match goField /\.\w\+\ + \%(\%([\/\-\+*%]\)\|\ + \%([\[\]{}<\>\)]\)\|\ + \%([\!=\^|&]\)\|\ + \%([\n\r\ ]\)\|\ + \%([,\:.]\)\)\@=/hs=s+1 +endif +hi def link goField Identifier + +" Structs & Interfaces; +if s:HighlightTypes() + syn match goTypeConstructor /\<\w\+{\@=/ + syn match goTypeDecl /\<type\>/ nextgroup=goTypeName skipwhite skipnl + syn match goTypeName /\w\+/ contained nextgroup=goDeclType skipwhite skipnl + syn match goDeclType /\<\%(interface\|struct\)\>/ skipwhite skipnl + hi def link goReceiverType Type +else + syn keyword goDeclType struct interface + syn keyword goDeclaration type +endif +hi def link goTypeConstructor Type +hi def link goTypeName Type +hi def link goTypeDecl Keyword +hi def link goDeclType Keyword + +" Variable Assignments +if s:HighlightVariableAssignments() + syn match goVarAssign /\v[_.[:alnum:]]+(,\s*[_.[:alnum:]]+)*\ze(\s*([-^+|^\/%&]|\*|\<\<|\>\>|\&\^)?\=[^=])/ + hi def link goVarAssign Special +endif + +" Variable Declarations +if s:HighlightVariableDeclarations() + syn match goVarDefs /\v\w+(,\s*\w+)*\ze(\s*:\=)/ + hi def link goVarDefs Special +endif + +" Build Constraints +if s:HighlightBuildConstraints() + syn match goBuildKeyword display contained "+build" + " Highlight the known values of GOOS, GOARCH, and other +build options. + syn keyword goBuildDirectives contained + \ android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 + \ solaris windows 386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 + \ ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc + \ s390 s390x sparc sparc64 cgo ignore race + + " Other words in the build directive are build tags not listed above, so + " avoid highlighting them as comments by using a matchgroup just for the + " start of the comment. + " The rs=s+2 option lets the \s*+build portion be part of the inner region + " instead of the matchgroup so it will be highlighted as a goBuildKeyword. + syn region goBuildComment matchgroup=goBuildCommentStart + \ start="//\s*+build\s"rs=s+2 end="$" + \ contains=goBuildKeyword,goBuildDirectives + hi def link goBuildCommentStart Comment + hi def link goBuildDirectives Type + hi def link goBuildKeyword PreProc +endif + +if s:HighlightBuildConstraints() || s:FoldEnable('package_comment') + " One or more line comments that are followed immediately by a "package" + " declaration are treated like package documentation, so these must be + " 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' + \ . ' 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' + \ . ' contains=@goCommentGroup,@Spell' + \ . (s:FoldEnable('package_comment') ? ' fold' : '') + hi def link goPackageComment Comment +endif + +" :GoCoverage commands +hi def link goCoverageNormalText Comment + " Search backwards for a global declaration to start processing the syntax. "syn sync match goSync grouphere NONE /^\(const\|var\|type\|func\)\>/ @@ -203,6 +478,9 @@ hi def link goSpaceError Error " following as a more expensive/less precise workaround. syn sync minlines=500 -let b:current_syntax = 'go' +let b:current_syntax = "go" + +let &cpo = s:keepcpo +unlet s:keepcpo " vim: sw=2 sts=2 et diff --git a/runtime/syntax/gvpr.vim b/runtime/syntax/gvpr.vim new file mode 100644 index 0000000000..a7378916f9 --- /dev/null +++ b/runtime/syntax/gvpr.vim @@ -0,0 +1,85 @@ +" Vim syntax file +" Language: Graphviz program +" Maintainer: Matthew Fernandez <matthew.fernandez@gmail.com> +" Last Change: Tue, 28 Jul 2020 17:20:44 -0700 + +if exists("b:current_syntax") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +syn keyword gvArg ARGC ARGV +syn keyword gvBeg BEGIN BEG_G N E END END_G +syn keyword gvFunc + \ graph fstsubg isDirect isStrict isSubg nEdges nNodes nxtsubg subg + \ degreeOf fstnode indegreeOf isNode isSubnode node nxtnode nxtnode_sg + \ outDegreeOf subnode + \ edge edge_sg fstedge fstedge_sg fstin fstin_sg fstout fstout_sg isEdge + \ isEdge_sg isSubedge nxtedge nxtedge_sg nxtin nxtin_sg nxtout nxtout_sg opp + \ subedge + \ freadG fwriteG readG write[] writeG + \ aget aset clone cloneG compOf copy[] copyA delete[] fstAttr getDflt hasAttr + \ induce isAttr isIn kindOf lock[] nxtAttr setDflt + \ canon gsub html index ishtml length llOf match[] rindex split[] sprintf + \ sscanf strcmp sub substr tokens tolower toupper urOf xOf yOf + \ closeF openF print[] printf scanf readL + \ atan2 cos exp log MAX MIN pow sin[] sqrt + \ in[] unset + \ colorx exit[] rand srand system +syn keyword gvCons + \ NULL TV_bfs TV_dfs TV_en TV_flat TV_fwd TV_ne TV_prepostdfs TV_prepostfwd + \ TV_prepostrev TV_postdfs TV_postfwd tv_postrev TV_rev +syn keyword gvType char double float int long unsigned void + \ string + \ edge_t graph_t node_t obj_t +syn match gvVar + \ "\$\(\(F\|G\|NG\|O\|T\|tgtname\|tvedge\|tvnext\|tvroot\|tvtype\)\>\)\?\(\<\)\@!" +syn keyword gvWord break continue else for forr if return switch while + +" numbers adapted from c.vim's cNumbers and friends +syn match gvNums transparent "\<\d\|\.\d" contains=gvNumber,gvFloat,gvOctal +syn match gvNumber contained "\d\+\(u\=l\{0,2}\|ll\=u\)\>" +syn match gvNumber contained "0x\x\+\(u\=l\{0,2}\|ll\=u\)\>" +syn match gvOctal contained "0\o\+\(u\=l\{0,2}\|ll\=u\)\>" contains=gvOctalZero +syn match gvOctalZero contained "\<0" +syn match gvFloat contained "\d\+f" +syn match gvFloat contained "\d\+\.\d*\(e[-+]\=\d\+\)\=[fl]\=" +syn match gvFloat contained "\.\d\+\(e[-+]\=\d\+\)\=[fl]\=\>" +syn match gvFloat contained "\d\+e[-+]\=\d\+[fl]\=\>" + +syn region gvString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=gvFormat,gvSpecial extend +syn region gvString start="'" skip="\\\\\|\\'" end="'" contains=gvFormat,gvSpecial extend + +" adapted from c.vim's cFormat for c_no_c99 +syn match gvFormat "%\(\d\+\$\)\=[-+' #0*]*\(\d*\|\*\|\*\d\+\$\)\(\.\(\d*\|\*\|\*\d\+\$\)\)\=\([hlL]\|ll\)\=\([bdiuoxXDOUfeEgGcCsSpn]\|\[\^\=.[^]]*\]\)" contained + +syn match gvSpecial "\\." contained + +syn region gvCComment start="//" skip="\\$" end="$" keepend +syn region gvCPPComment start="#" skip="\\$" end="$" keepend +syn region gvCXXComment start="/\*" end="\*/" fold + +hi def link gvArg Identifier +hi def link gvBeg Keyword +hi def link gvFloat Number +hi def link gvFunc Identifier +hi def link gvCons Number +hi def link gvNumber Number +hi def link gvType Type +hi def link gvVar Statement +hi def link gvWord Keyword + +hi def link gvString String +hi def link gvFormat Special +hi def link gvSpecial Special + +hi def link gvCComment Comment +hi def link gvCPPComment Comment +hi def link gvCXXComment Comment + +let b:current_syntax = "gvpr" + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/syntax/help.vim b/runtime/syntax/help.vim index d3d8f4f435..01915d23d7 100644 --- a/runtime/syntax/help.vim +++ b/runtime/syntax/help.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Vim help file " Maintainer: Bram Moolenaar (Bram@vim.org) -" Last Change: 2020 Jul 28 +" Last Change: 2021 Jun 13 " Quit when a (custom) syntax file was already loaded if exists("b:current_syntax") diff --git a/runtime/syntax/jsonc.vim b/runtime/syntax/jsonc.vim new file mode 100644 index 0000000000..d0df16bbf1 --- /dev/null +++ b/runtime/syntax/jsonc.vim @@ -0,0 +1,44 @@ +" Vim syntax file +" Language: JSONC (JSON with Comments) +" Original Author: Izhak Jakov <izhak724@gmail.com> +" Acknowledgement: Based off of vim-jsonc maintained by Kevin Locke <kevin@kevinlocke.name> +" https://github.com/kevinoid/vim-jsonc +" License: MIT +" Last Change: 2021-07-01 + +" Ensure syntax is loaded once, unless nested inside another (main) syntax +" For description of main_syntax, see https://stackoverflow.com/q/16164549 +if !exists('g:main_syntax') + if exists('b:current_syntax') && b:current_syntax ==# 'jsonc' + finish + endif + let g:main_syntax = 'jsonc' +endif + +" Based on vim-json syntax +runtime! syntax/json.vim + +" Remove syntax group for comments treated as errors +if !exists("g:vim_json_warnings") || g:vim_json_warnings + syn clear jsonCommentError +endif + +syn match jsonStringMatch /"\([^"]\|\\\"\)\+"\ze\(\_s*\/\/.*\_s*\)*[}\]]/ contains=jsonString +syn match jsonStringMatch /"\([^"]\|\\\"\)\+"\ze\_s*\/\*\_.*\*\/\_s*[}\]]/ contains=jsonString +syn match jsonTrailingCommaError /\(,\)\+\ze\(\_s*\/\/.*\_s*\)*[}\]]/ +syn match jsonTrailingCommaError /\(,\)\+\ze\_s*\/\*\_.*\*\/\_s*[}\]]/ + +" Define syntax matching comments and their contents +syn keyword jsonCommentTodo FIXME NOTE TBD TODO XXX +syn region jsonLineComment start=+\/\/+ end=+$+ contains=@Spell,jsonCommentTodo keepend +syn region jsonComment start='/\*' end='\*/' contains=@Spell,jsonCommentTodo fold + +" Link comment syntax comment to highlighting +hi! def link jsonLineComment Comment +hi! def link jsonComment Comment + +" Set/Unset syntax to avoid duplicate inclusion and correctly handle nesting +let b:current_syntax = 'jsonc' +if g:main_syntax ==# 'jsonc' + unlet g:main_syntax +endif diff --git a/runtime/syntax/julia.vim b/runtime/syntax/julia.vim new file mode 100644 index 0000000000..2c2d36a97a --- /dev/null +++ b/runtime/syntax/julia.vim @@ -0,0 +1,550 @@ +" Vim syntax file +" Language: julia +" Maintainer: Carlo Baldassi <carlobaldassi@gmail.com> +" Homepage: https://github.com/JuliaEditorSupport/julia-vim +" Last Change: 2013 feb 11 + +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +if version < 704 + " this is used to disable regex syntax like `\@3<=' + " on older vim versions + function! s:d(x) + return '' + endfunction +else + function! s:d(x) + return string(a:x) + endfunction +endif + +scriptencoding utf-8 + +let s:julia_spellcheck_strings = get(g:, "julia_spellcheck_strings", 0) +let s:julia_spellcheck_docstrings = get(g:, "julia_spellcheck_docstrings", 1) +let s:julia_spellcheck_comments = get(g:, "julia_spellcheck_comments", 1) + +let s:julia_highlight_operators = get(g:, "julia_highlight_operators", 1) + +" List of characters, up to \UFF, which cannot be used in identifiers. +" (It includes operator characters; we don't consider them identifiers.) +" This is used mostly in lookbehinds with `\@<=`, e.g. when we need to check +" that that we're not in the middle of an identifier. +" It doesn't include a few characters (spaces and all closing parentheses) +" because those may or may not be valid in the lookbehind on a case-by-case +" basis. +let s:nonid_chars = '\U00-\U08' . '\U0A-\U1F' + \ . '\U21-\U28' . '\U2A-\U2F' . '\U3A-\U40' . '\U5B-\U5E' . '\U60' . '\U7B\U7C' + \ . '\U7E-\UA1' . '\UA7\UA8' . '\UAB-\UAD' . '\UAF\UB1\UB4' . '\UB6-\UB8' . '\UBB\UBF' . '\UD7\UF7' + +" The complete list +let s:nonidS_chars = '[:space:])\U5D}' . s:nonid_chars + + +" List of all valid operator chars up to \UFF (NOTE: they must all be included +" in s:nonidS_chars, so that if we include that, then this is redundant) +" It does not include '!' since it can be used in an identifier. +" The list contains the following characters: '%&*+-/<=>\\^|~¬±×÷' +let s:op_chars = '\U25\U26\U2A\U2B\U2D\U2F\U3C-\U3E\U5C\U5E\U7C\U7E\UAC\UB1\UD7\UF7' + +" List of all valid operator chars above \UFF +" Written with ranges for performance reasons +" The list contains the following characters: '…ââ…‹â†â†‘→↓↔↚↛↜â†â†žâ† ↢↣↤↦↩↪↫↬↮↶↷↺↻↼↽⇀â‡â‡„⇆⇇⇉⇋⇌â‡â‡Žâ‡â‡â‡’⇔⇚⇛⇜â‡â‡ ⇢⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿∈∉∊∋∌âˆâˆ“∔∗∘∙√∛∜âˆâˆ¤âˆ¥âˆ¦âˆ§âˆ¨âˆ©âˆªâˆ·âˆ¸âˆºâˆ»âˆ½âˆ¾â‰€â‰â‰‚≃≄≅≆≇≈≉≊≋≌â‰â‰Žâ‰â‰â‰‘≒≓≔≕≖≗≘≙≚≛≜â‰â‰žâ‰Ÿâ‰ ≡≢≣≤≥≦≧≨≩≪≫≬â‰â‰®â‰¯â‰°â‰±â‰²â‰³â‰´â‰µâ‰¶â‰·â‰¸â‰¹â‰ºâ‰»â‰¼â‰½â‰¾â‰¿âŠ€âŠâŠ‚âŠƒâŠ„âŠ…âŠ†âŠ‡âŠˆâŠ‰âŠŠâŠ‹âŠâŠŽâŠâŠâŠ‘âŠ’âŠ“âŠ”âŠ•âŠ–âŠ—âŠ˜âŠ™âŠšâŠ›âŠœâŠžâŠŸâŠ âŠ¡âŠ¢âŠ£âŠ©âŠ¬âŠ®âŠ°âŠ±âŠ²âŠ³âŠ´âŠµâŠ¶âŠ·âŠ»âŠ¼âŠ½â‹„â‹…â‹†â‹‡â‹‰â‹Šâ‹‹â‹Œâ‹â‹Žâ‹â‹â‹‘⋒⋓⋕⋖⋗⋘⋙⋚⋛⋜â‹â‹žâ‹Ÿâ‹ ⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬â‹â‹®â‹¯â‹°â‹±â‹²â‹³â‹´â‹µâ‹¶â‹·â‹¸â‹¹â‹ºâ‹»â‹¼â‹½â‹¾â‹¿âŒ¿â–·âŸ‚⟈⟉⟑⟒⟕⟖⟗⟰⟱⟵⟶⟷⟹⟺⟻⟼⟽⟾⟿⤀â¤â¤‚⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌â¤â¤Žâ¤â¤â¤‘⤒⤓⤔⤕⤖⤗⤘â¤â¤žâ¤Ÿâ¤ ⥄⥅⥆⥇⥈⥉⥊⥋⥌â¥â¥Žâ¥â¥â¥‘⥒⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜â¥â¥žâ¥Ÿâ¥ ⥡⥢⥣⥤⥥⥦⥧⥨⥩⥪⥫⥬â¥â¥®â¥¯â¥°â¦·â¦¸â¦¼â¦¾â¦¿â§€â§â§¡â§£â§¤â§¥â§´â§¶â§·â§ºâ§»â¨‡â¨ˆâ¨â¨Ÿâ¨¢â¨£â¨¤â¨¥â¨¦â¨§â¨¨â¨©â¨ªâ¨«â¨¬â¨â¨®â¨°â¨±â¨²â¨³â¨´â¨µâ¨¶â¨·â¨¸â¨¹â¨ºâ¨»â¨¼â¨½â©€â©â©‚⩃⩄⩅⩊⩋⩌â©â©Žâ©â©â©‘⩒⩓⩔⩕⩖⩗⩘⩚⩛⩜â©â©žâ©Ÿâ© ⩡⩢⩣⩦⩧⩪⩫⩬â©â©®â©¯â©°â©±â©²â©³â©´â©µâ©¶â©·â©¸â©¹â©ºâ©»â©¼â©½â©¾â©¿âª€âªâª‚⪃⪄⪅⪆⪇⪈⪉⪊⪋⪌âªâªŽâªâªâª‘⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜âªâªžâªŸâª ⪡⪢⪣⪤⪥⪦⪧⪨⪩⪪⪫⪬âªâª®âª¯âª°âª±âª²âª³âª´âªµâª¶âª·âª¸âª¹âªºâª»âª¼âª½âª¾âª¿â«€â«â«‚⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌â«â«Žâ«â«â«‘⫒⫓⫔⫕⫖⫗⫘⫙⫛⫷⫸⫹⫺⬰⬱⬲⬳⬴⬵⬶⬷⬸⬹⬺⬻⬼⬽⬾⬿â€ââ‚âƒâ„â‡âˆâ‰âŠâ‹âŒï¿©ï¿ªï¿«ï¿¬' +let s:op_chars_wc = '\U2026\U205D\U214B\U2190-\U2194\U219A-\U219E\U21A0\U21A2-\U21A4\U21A6\U21A9-\U21AC\U21AE\U21B6\U21B7\U21BA-\U21BD\U21C0\U21C1\U21C4\U21C6\U21C7\U21C9\U21CB-\U21D0\U21D2\U21D4\U21DA-\U21DD\U21E0\U21E2\U21F4-\U21FF\U2208-\U220D\U2213\U2214\U2217-\U221D\U2224-\U222A\U2237\U2238\U223A\U223B\U223D\U223E\U2240-\U228B\U228D-\U229C\U229E-\U22A3\U22A9\U22AC\U22AE\U22B0-\U22B7\U22BB-\U22BD\U22C4-\U22C7\U22C9-\U22D3\U22D5-\U22FF\U233F\U25B7\U27C2\U27C8\U27C9\U27D1\U27D2\U27D5-\U27D7\U27F0\U27F1\U27F5-\U27F7\U27F9-\U27FF\U2900-\U2918\U291D-\U2920\U2944-\U2970\U29B7\U29B8\U29BC\U29BE-\U29C1\U29E1\U29E3-\U29E5\U29F4\U29F6\U29F7\U29FA\U29FB\U2A07\U2A08\U2A1D\U2A1F\U2A22-\U2A2E\U2A30-\U2A3D\U2A40-\U2A45\U2A4A-\U2A58\U2A5A-\U2A63\U2A66\U2A67\U2A6A-\U2AD9\U2ADB\U2AF7-\U2AFA\U2B30-\U2B44\U2B47-\U2B4C\UFFE9-\UFFEC' + +" Full operators regex +let s:operators = '\%(' . '\.\%([-+*/^÷%|&⊻]\|//\|\\\|>>\|>>>\?\)\?=' . + \ '\|' . '[:<>]=\|||\|&&\||>\|<|\|[<>:]:\|<<\|>>>\?\|//\|[-=]>\|\.\.\.\?' . + \ '\|' . '\.\?[!' . s:op_chars . s:op_chars_wc . ']' . + \ '\)' + + +" Characters that can be used to start an identifier. Above \UBF we don't +" bother checking. (If a UTF8 operator is used, it will take precedence anyway.) +let s:id_charsH = '\%([A-Za-z_\UA2-\UA6\UA9\UAA\UAE\UB0\UB5\UBA]\|[^\U00-\UBF]\)' +" Characters that can appear in an identifier, starting in 2nd position. Above +" \UBF we check for operators since we need to stop the identifier if one +" appears. We don't check for invalid characters though. +let s:id_charsW = '\%([0-9A-Za-z_!\UA2-\UA6\UA9\UAA\UAE-\UB0\UB2-\UB5\UB8-\UBA\UBC-\UBE]\|[^\U00-\UBF]\@=[^' . s:op_chars_wc . ']\)' + +" A valid julia identifier, more or less +let s:idregex = '\%(' . s:id_charsH . s:id_charsW . '*\)' + + + +syn case match + +syntax cluster juliaExpressions contains=@juliaParItems,@juliaStringItems,@juliaKeywordItems,@juliaBlocksItems,@juliaTypesItems,@juliaConstItems,@juliaMacroItems,@juliaSymbolItems,@juliaOperatorItems,@juliaNumberItems,@juliaCommentItems,@juliaErrorItems,@juliaSyntaxRegions +syntax cluster juliaExprsPrintf contains=@juliaExpressions,@juliaPrintfItems +syntax cluster juliaExprsNodot contains=@juliaParItems,@juliaStringItems,@juliaMacroItems,@juliaSymbolItems,@juliaOperatorItems,@juliaCommentItems,juliaIdSymbol + +syntax cluster juliaParItems contains=juliaParBlock,juliaSqBraIdxBlock,juliaSqBraBlock,juliaCurBraBlock,juliaQuotedParBlock,juliaQuotedQMarkPar +syntax cluster juliaKeywordItems contains=juliaKeyword,juliaWhereKeyword,juliaImportLine,juliaInfixKeyword,juliaRepKeyword +syntax cluster juliaBlocksItems contains=juliaConditionalBlock,juliaWhileBlock,juliaForBlock,juliaBeginBlock,juliaFunctionBlock,juliaMacroBlock,juliaQuoteBlock,juliaTypeBlock,juliaImmutableBlock,juliaExceptionBlock,juliaLetBlock,juliaDoBlock,juliaModuleBlock,juliaStructBlock,juliaMutableStructBlock,juliaAbstractBlock,juliaPrimitiveBlock +syntax cluster juliaTypesItems contains=juliaBaseTypeBasic,juliaBaseTypeNum,juliaBaseTypeC,juliaBaseTypeError,juliaBaseTypeIter,juliaBaseTypeString,juliaBaseTypeArray,juliaBaseTypeDict,juliaBaseTypeSet,juliaBaseTypeIO,juliaBaseTypeProcess,juliaBaseTypeRange,juliaBaseTypeRegex,juliaBaseTypeFact,juliaBaseTypeFact,juliaBaseTypeSort,juliaBaseTypeRound,juliaBaseTypeSpecial,juliaBaseTypeRandom,juliaBaseTypeDisplay,juliaBaseTypeTime,juliaBaseTypeOther + +syntax cluster juliaConstItems contains=juliaConstNum,juliaConstBool,juliaConstEnv,juliaConstMMap,juliaConstC,juliaConstGeneric,juliaConstIO,juliaPossibleEuler + +syntax cluster juliaMacroItems contains=juliaPossibleMacro,juliaDollarVar,juliaDollarPar,juliaDollarSqBra +syntax cluster juliaSymbolItems contains=juliaPossibleSymbol +syntax cluster juliaNumberItems contains=juliaNumbers +syntax cluster juliaStringItems contains=juliaChar,juliaString,juliabString,juliasString,juliaShellString,juliaDocString,juliaRegEx +syntax cluster juliaPrintfItems contains=juliaPrintfParBlock,juliaPrintfString +syntax cluster juliaOperatorItems contains=juliaOperator,juliaRangeOperator,juliaCTransOperator,juliaTernaryRegion,juliaColon,juliaSemicolon,juliaComma +syntax cluster juliaCommentItems contains=juliaCommentL,juliaCommentM +syntax cluster juliaErrorItems contains=juliaErrorPar,juliaErrorEnd,juliaErrorElse,juliaErrorCatch,juliaErrorFinally + +syntax cluster juliaSyntaxRegions contains=juliaIdSymbol,juliaTypeOperatorR2,juliaTypeOperatorR3,juliaWhereR,juliaDotted + +syntax cluster juliaSpellcheckStrings contains=@spell +syntax cluster juliaSpellcheckDocStrings contains=@spell +syntax cluster juliaSpellcheckComments contains=@spell + +if !s:julia_spellcheck_docstrings + syntax cluster juliaSpellcheckDocStrings remove=@spell +endif +if !s:julia_spellcheck_strings + syntax cluster juliaSpellcheckStrings remove=@spell +endif +if !s:julia_spellcheck_comments + syntax cluster juliaSpellcheckComments remove=@spell +endif + +syntax match juliaSemicolon display ";" +syntax match juliaComma display "," +syntax match juliaColon display ":" + +" A dot can introduce a sort of 'environment' such that words after it are not +" recognized as keywords. This has low precedence so that it can be overridden +" by operators +syntax match juliaDotted transparent "\.\s*[^])}.]" contains=@juliaExprsNodot +syntax match juliaDottedT contained transparent "\.\s*[^])}.]" contains=@juliaExprsNodot,juliaType + +syntax match juliaErrorPar display "[])}]" +syntax match juliaErrorEnd display "\<end\>" +syntax match juliaErrorElse display "\<\%(else\|elseif\)\>" +syntax match juliaErrorCatch display "\<catch\>" +syntax match juliaErrorFinally display "\<finally\>" +syntax match juliaErrorSemicol display contained ";" + +syntax region juliaParBlock matchgroup=juliaParDelim start="(" end=")" contains=@juliaExpressions,juliaComprehensionFor +syntax region juliaParBlockInRange matchgroup=juliaParDelim contained start="(" end=")" contains=@juliaExpressions,juliaParBlockInRange,juliaRangeKeyword,juliaComprehensionFor +syntax region juliaSqBraIdxBlock matchgroup=juliaParDelim start="\[" end="\]" contains=@juliaExpressions,juliaParBlockInRange,juliaRangeKeyword,juliaComprehensionFor,juliaSymbolS,juliaQuotedParBlockS,juliaQuotedQMarkParS +exec 'syntax region juliaSqBraBlock matchgroup=juliaParDelim start="\%(^\|\s\|' . s:operators . '\)\@'.s:d(3).'<=\[" end="\]" contains=@juliaExpressions,juliaComprehensionFor,juliaSymbolS,juliaQuotedParBlockS,juliaQuotedQMarkParS' +syntax region juliaCurBraBlock matchgroup=juliaParDelim start="{" end="}" contains=juliaType,juliaDottedT,@juliaExpressions + +exec 'syntax match juliaType contained "\%(' . s:idregex . '\.\)*\zs' . s:idregex . '"' + +" This is a generic identifier followed by some symbol, either a type +" operator (<: or >:), or an open parenthesis, or an open curly bracket. +" It's used to recognize one of the contained regions looking for identifiers +" only once. Once recognized, those regions no longer need to use the +" expensive s:idregex. +exec 'syntax match juliaIdSymbol transparent "' . s:idregex . '\%(\s*[<>]:\|\.\?(\|{\|\"\)\@=" contains=juliaFunctionCall,juliaParamType,juliaStringPrefixed,juliaTypeOperatorR1' + +syntax match juliaFunctionCall contained "[^{([:space:]<>\"]\+(\@=" nextgroup=juliaParBlock + +exec 'syntax match juliaFunctionDef contained transparent "\%(\<\%(function\|macro\)\)\@'.s:d(8).'<=\s\+\zs' . s:idregex . '\%(\.' . s:idregex . '\)*\ze\s*\%((\|\send\>\|$\)" contains=juliaFunctionName' +exec 'syntax match juliaFunctionName contained "\%(\<\%(function\|macro\)\s\+\)\@'.s:d(20).'<=\%(' . s:idregex . '\.\)*\zs' . s:idregex . '"' + +exec 'syntax match juliaStructR contained transparent "\%(\<\%(\%(mutable\s\+\)\?struct\|\%(abstract\|primitive\)\s\+type\)\s\+\)\@'.s:d(20).'<=\%(' . s:idregex . '\.\)*' . s:idregex . '\>\(\s*(\)\@!" contains=juliaType' + +syntax match juliaKeyword display "\<\%(return\|local\|global\|const\)\>" +syntax match juliaInfixKeyword display "\%(=\s*\)\@<!\<\%(in\|isa\)\>\S\@!\%(\s*=\)\@!" + +" The import/export/using keywords introduce a sort of special parsing +" environment with its own rules +exec 'syntax region juliaImportLine matchgroup=juliaKeyword excludenl start="\<\%(import\|using\|export\)\>" skip="\%(\%(\<\%(import\|using\|export\)\>\)\|^\)\@'.s:d(6).'<=$" end="$" end="\%([])}]\)\@=" contains=@juliaExpressions,juliaAsKeyword,@juliaContinuationItems,juliaMacroName' +syntax match juliaAsKeyword display contained "\<as\>" + +syntax match juliaRepKeyword display "\<\%(break\|continue\)\>" +syntax region juliaConditionalBlock matchgroup=juliaConditional start="\<if\>" end="\<end\>" contains=@juliaExpressions,juliaConditionalEIBlock,juliaConditionalEBlock fold +syntax region juliaConditionalEIBlock matchgroup=juliaConditional transparent contained start="\<elseif\>" end="\<\%(end\|else\|elseif\)\>"me=s-1 contains=@juliaExpressions,juliaConditionalEIBlock,juliaConditionalEBlock +syntax region juliaConditionalEBlock matchgroup=juliaConditional transparent contained start="\<else\>" end="\<end\>"me=s-1 contains=@juliaExpressions +syntax region juliaWhileBlock matchgroup=juliaRepeat start="\<while\>" end="\<end\>" contains=@juliaExpressions fold +syntax region juliaForBlock matchgroup=juliaRepeat start="\<for\>" end="\<end\>" contains=@juliaExpressions,juliaOuter fold +syntax region juliaBeginBlock matchgroup=juliaBlKeyword start="\<begin\>" end="\<end\>" contains=@juliaExpressions fold +syntax region juliaFunctionBlock matchgroup=juliaBlKeyword start="\<function\>" end="\<end\>" contains=@juliaExpressions,juliaFunctionDef fold +syntax region juliaMacroBlock matchgroup=juliaBlKeyword start="\<macro\>" end="\<end\>" contains=@juliaExpressions,juliaFunctionDef fold +syntax region juliaQuoteBlock matchgroup=juliaBlKeyword start="\<quote\>" end="\<end\>" contains=@juliaExpressions fold +syntax region juliaStructBlock matchgroup=juliaBlKeyword start="\<struct\>" end="\<end\>" contains=@juliaExpressions,juliaStructR fold +syntax region juliaMutableStructBlock matchgroup=juliaBlKeyword start="\<mutable\s\+struct\>" end="\<end\>" contains=@juliaExpressions,juliaStructR fold +syntax region juliaLetBlock matchgroup=juliaBlKeyword start="\<let\>" end="\<end\>" contains=@juliaExpressions fold +syntax region juliaDoBlock matchgroup=juliaBlKeyword start="\<do\>" end="\<end\>" contains=@juliaExpressions fold +syntax region juliaModuleBlock matchgroup=juliaBlKeyword start="\<\%(bare\)\?module\>" end="\<end\>" contains=@juliaExpressions fold +syntax region juliaExceptionBlock matchgroup=juliaException start="\<try\>" end="\<end\>" contains=@juliaExpressions,juliaCatchBlock,juliaFinallyBlock fold +syntax region juliaCatchBlock matchgroup=juliaException transparent contained start="\<catch\>" end="\<end\>"me=s-1 contains=@juliaExpressions,juliaFinallyBlock +syntax region juliaFinallyBlock matchgroup=juliaException transparent contained start="\<finally\>" end="\<end\>"me=s-1 contains=@juliaExpressions +syntax region juliaAbstractBlock matchgroup=juliaBlKeyword start="\<abstract\s\+type\>" end="\<end\>" fold contains=@juliaExpressions,juliaStructR +syntax region juliaPrimitiveBlock matchgroup=juliaBlKeyword start="\<primitive\s\+type\>" end="\<end\>" fold contains=@juliaExpressions,juliaStructR + +exec 'syntax region juliaComprehensionFor matchgroup=juliaComprehensionFor transparent contained start="\%([^[:space:],;:({[]\_s*\)\@'.s:d(80).'<=\<for\>" end="\ze[]);]" contains=@juliaExpressions,juliaComprehensionIf,juliaComprehensionFor' +syntax match juliaComprehensionIf contained "\<if\>" + +exec 'syntax match juliaOuter contained "\<outer\ze\s\+' . s:idregex . '\>"' + +syntax match juliaRangeKeyword contained "\<\%(begin\|end\)\>" + +syntax match juliaBaseTypeBasic display "\<\%(\%(N\|Named\)\?Tuple\|Symbol\|Function\|Union\%(All\)\?\|Type\%(Name\|Var\)\?\|Any\|ANY\|Vararg\|Ptr\|Exception\|Module\|Expr\|DataType\|\%(LineNumber\|Quote\)Node\|\%(Weak\|Global\)\?Ref\|Method\|Pair\|Val\|Nothing\|Some\|Missing\)\>" +syntax match juliaBaseTypeNum display "\<\%(U\?Int\%(8\|16\|32\|64\|128\)\?\|Float\%(16\|32\|64\)\|Complex\|Bool\|Char\|Number\|Signed\|Unsigned\|Integer\|AbstractFloat\|Real\|Rational\|\%(Abstract\)\?Irrational\|Enum\|BigInt\|BigFloat\|MathConst\|ComplexF\%(16\|32\|64\)\)\>" +syntax match juliaBaseTypeC display "\<\%(FileOffset\|C\%(u\?\%(char\|short\|int\|long\(long\)\?\|w\?string\)\|float\|double\|\%(ptrdiff\|s\?size\|wchar\|off\|u\?intmax\)_t\|void\)\)\>" +syntax match juliaBaseTypeError display "\<\%(\%(Bounds\|Divide\|Domain\|\%(Stack\)\?Overflow\|EOF\|Undef\%(Ref\|Var\)\|System\|Type\|Parse\|Argument\|Key\|Load\|Method\|Inexact\|OutOfMemory\|Init\|Assertion\|ReadOnlyMemory\|StringIndex\)Error\|\%(Interrupt\|Error\|ProcessExited\|Captured\|Composite\|InvalidState\|Missing\|\%(Process\|Task\)Failed\)Exception\|DimensionMismatch\|SegmentationFault\)\>" +syntax match juliaBaseTypeIter display "\<\%(EachLine\|Enumerate\|Cartesian\%(Index\|Range\)\|LinSpace\|CartesianIndices\)\>" +syntax match juliaBaseTypeString display "\<\%(DirectIndex\|Sub\|Rep\|Rev\|Abstract\|Substitution\)\?String\>" +syntax match juliaBaseTypeArray display "\<\%(\%(Sub\)\?Array\|\%(Abstract\|Dense\|Strided\)\?\%(Array\|Matrix\|Vec\%(tor\|OrMat\)\)\|SparseMatrixCSC\|\%(AbstractSparse\|Bit\|Shared\)\%(Array\|Vector\|Matrix\)\|\%\(D\|Bid\|\%(Sym\)\?Trid\)iagonal\|Hermitian\|Symmetric\|UniformScaling\|\%(Lower\|Upper\)Triangular\|\%(Sparse\|Row\)Vector\|VecElement\|Conj\%(Array\|Matrix\|Vector\)\|Index\%(Cartesian\|Linear\|Style\)\|PermutedDimsArray\|Broadcasted\|Adjoint\|Transpose\|LinearIndices\)\>" +syntax match juliaBaseTypeDict display "\<\%(WeakKey\|Id\|Abstract\)\?Dict\>" +syntax match juliaBaseTypeSet display "\<\%(\%(Abstract\|Bit\)\?Set\)\>" +syntax match juliaBaseTypeIO display "\<\%(IO\%(Stream\|Buffer\|Context\)\?\|RawFD\|StatStruct\|FileMonitor\|PollingFileWatcher\|Timer\|Base64\%(Decode\|Encode\)Pipe\|\%(UDP\|TCP\)Socket\|\%(Abstract\)\?Channel\|BufferStream\|ReentrantLock\|GenericIOBuffer\)\>" +syntax match juliaBaseTypeProcess display "\<\%(Pipe\|Cmd\|PipeBuffer\)\>" +syntax match juliaBaseTypeRange display "\<\%(Dims\|RangeIndex\|\%(Abstract\|Lin\|Ordinal\|Step\|\%(Abstract\)\?Unit\)Range\|Colon\|ExponentialBackOff\|StepRangeLen\)\>" +syntax match juliaBaseTypeRegex display "\<Regex\%(Match\)\?\>" +syntax match juliaBaseTypeFact display "\<\%(Factorization\|BunchKaufman\|\%(Cholesky\|QR\)\%(Pivoted\)\?\|\%(Generalized\)\?\%(Eigen\|SVD\|Schur\)\|Hessenberg\|LDLt\|LQ\|LU\)\>" +syntax match juliaBaseTypeSort display "\<\%(Insertion\|\(Partial\)\?Quick\|Merge\)Sort\>" +syntax match juliaBaseTypeRound display "\<Round\%(ingMode\|FromZero\|Down\|Nearest\%(Ties\%(Away\|Up\)\)\?\|ToZero\|Up\)\>" +syntax match juliaBaseTypeSpecial display "\<\%(LocalProcess\|ClusterManager\)\>" +syntax match juliaBaseTypeRandom display "\<\%(AbstractRNG\|MersenneTwister\|RandomDevice\)\>" +syntax match juliaBaseTypeDisplay display "\<\%(Text\(Display\)\?\|\%(Abstract\)\?Display\|MIME\|HTML\)\>" +syntax match juliaBaseTypeTime display "\<\%(Date\%(Time\)\?\|DateFormat\)\>" +syntax match juliaBaseTypeOther display "\<\%(RemoteRef\|Task\|Condition\|VersionNumber\|IPv[46]\|SerializationState\|WorkerConfig\|Future\|RemoteChannel\|IPAddr\|Stack\%(Trace\|Frame\)\|\(Caching\|Worker\)Pool\|AbstractSerializer\)\>" + +syntax match juliaConstNum display "\%(\<\%(\%(NaN\|Inf\)\%(16\|32\|64\)\?\|pi\|Ï€\)\>\)" +" Note: recognition of ℯ, which Vim does not consider a valid identifier, is +" complicated. We detect possible uses by just looking for the character (for +" performance) and then check that it's actually used by its own. +" (This also tries to detect preceding number constants; it does so in a crude +" way.) +syntax match juliaPossibleEuler "ℯ" contains=juliaEuler +exec 'syntax match juliaEuler contained "\%(\%(^\|[' . s:nonidS_chars . s:op_chars_wc . ']\)\%(.\?[0-9][.0-9eEf_]*\d\)\?\)\@'.s:d(80).'<=ℯ\ze[' . s:nonidS_chars . s:op_chars_wc . ']"' +syntax match juliaConstBool display "\<\%(true\|false\)\>" +syntax match juliaConstEnv display "\<\%(ARGS\|ENV\|ENDIAN_BOM\|LOAD_PATH\|VERSION\|PROGRAM_FILE\|DEPOT_PATH\)\>" +syntax match juliaConstIO display "\<\%(std\%(out\|in\|err\)\|devnull\)\>" +syntax match juliaConstC display "\<\%(C_NULL\)\>" +syntax match juliaConstGeneric display "\<\%(nothing\|Main\|undef\|missing\)\>" + +syntax match juliaParamType contained "[^{([:space:]<>\"]\+\ze{" nextgroup=juliaCurBraBlock + +syntax match juliaPossibleMacro transparent "@" contains=juliaMacroCall,juliaMacroCallP,juliaPrintfMacro,juliaDocMacro,juliaDocMacroPre + +exec 'syntax match juliaMacro contained "@' . s:idregex . '\%(\.' . s:idregex . '\)*"' +syntax match juliaMacro contained "@[!.~$%^*/\\|<>+-]\ze[^0-9]" +exec 'syntax region juliaMacroCall contained transparent start="\(@' . s:idregex . '\%(\.' . s:idregex . '\)*\)\@=\1\%([^(]\|$\)" end="\ze\%([])};#]\|$\|\<for\>\|\<end\>\)" contains=@juliaExpressions,juliaMacro,juliaSymbolS,juliaQuotedParBlockS' +exec 'syntax region juliaMacroCall contained transparent start="\(@.\)\@=\1\%([^(]\|$\)" end="\ze\%([])};#]\|$\|\<for\>\|\<end\>\)" contains=@juliaExpressions,juliaMacro,juliaSymbolS,juliaQuotedParBlockS' +exec 'syntax region juliaMacroCallP contained transparent start="@' . s:idregex . '\%(\.' . s:idregex . '\)*(" end=")\@'.s:d(1).'<=" contains=juliaMacro,juliaParBlock' +exec 'syntax region juliaMacroCallP contained transparent start="@.(" end=")\@'.s:d(1).'<=" contains=juliaMacro,juliaParBlock' + +exec 'syntax match juliaNumbers transparent "\%(^\|[' . s:nonidS_chars . s:op_chars_wc . ']\)\@'.s:d(1).'<=\d\|\.\d\|im\>" contains=juliaNumber,juliaFloat,juliaComplexUnit' + +"integer regexes +let s:dec_regex = '\d\%(_\?\d\)*\%(\>\|im\>\|\ze\D\)' +let s:hex_regex = '0x\x\%(_\?\x\)*\%(\>\|im\>\|\ze\X\)' +let s:bin_regex = '0b[01]\%(_\?[01]\)*\%(\>\|im\>\|\ze[^01]\)' +let s:oct_regex = '0o\o\%(_\?\o\)*\%(\>\|im\>\|\ze\O\)' + +let s:int_regex = '\%(' . s:hex_regex . + \ '\|' . s:bin_regex . + \ '\|' . s:oct_regex . + \ '\|' . s:dec_regex . + \ '\)' + +"floating point regexes +" starting with a dot, optional exponent +let s:float_regex1 = '\.\d\%(_\?\d\)*\%([eEf][-+]\?\d\+\)\?\%(\>\|im\>\|\ze\D\)' +" with dot, optional exponent +let s:float_regex2 = '\d\%(_\?\d\)*\.\%(\d\%(_\?\d\)*\)\?\%([eEf][-+]\?\d\+\)\?\%(\>\|im\>\|\ze\D\)' +" without dot, with exponent +let s:float_regex3 = '\d\%(_\?\d\)*[eEf][-+]\?\d\+\%(\>\|im\>\|\ze\D\)' + +"hex floating point numbers +" starting with a dot +let s:hexfloat_regex1 = '0x\.\%\(\x\%(_\?\x\)*\)\?[pP][-+]\?\d\+\%(\>\|im\>\|\ze\X\)' +" starting with a digit +let s:hexfloat_regex2 = '0x\x\%(_\?\x\)*\%\(\.\%\(\x\%(_\?\x\)*\)\?\)\?[pP][-+]\?\d\+\%(\>\|im\>\|\ze\X\)' + +let s:float_regex = '\%(' . s:float_regex3 . + \ '\|' . s:float_regex2 . + \ '\|' . s:float_regex1 . + \ '\|' . s:hexfloat_regex2 . + \ '\|' . s:hexfloat_regex1 . + \ '\)' + +exec 'syntax match juliaNumber contained "' . s:int_regex . '" contains=juliaComplexUnit' +exec 'syntax match juliaFloat contained "' . s:float_regex . '" contains=juliaComplexUnit' +syntax match juliaComplexUnit display contained "\<im\>" + +syntax match juliaRangeOperator display ":" +exec 'syntax match juliaOperator "' . s:operators . '"' + +exec 'syntax region juliaTernaryRegion matchgroup=juliaTernaryOperator start="\s\zs?\ze\s" skip="\%(:\(:\|[^:[:space:]'."'".'"({[]\+\s*\ze:\)\|\%(?\s*\)\@'.s:d(6).'<=:(\)" end=":" contains=@juliaExpressions,juliaErrorSemicol' + +let s:interp_dollar = '\([' . s:nonidS_chars . s:op_chars_wc . '!]\|^\)\@'.s:d(1).'<=\$' + +exec 'syntax match juliaDollarVar display contained "' . s:interp_dollar . s:idregex . '"' +exec 'syntax region juliaDollarPar matchgroup=juliaDollarVar contained start="' .s:interp_dollar . '(" end=")" contains=@juliaExpressions' +exec 'syntax region juliaDollarSqBra matchgroup=juliaDollarVar contained start="' .s:interp_dollar . '\[" end="\]" contains=@juliaExpressions,juliaComprehensionFor,juliaSymbolS,juliaQuotedParBlockS' + +syntax match juliaChar "'\\\?.'" contains=juliaSpecialChar +syntax match juliaChar display "'\\\o\{3\}'" contains=juliaOctalEscapeChar +syntax match juliaChar display "'\\x\x\{2\}'" contains=juliaHexEscapeChar +syntax match juliaChar display "'\\u\x\{1,4\}'" contains=juliaUniCharSmall +syntax match juliaChar display "'\\U\x\{1,8\}'" contains=juliaUniCharLarge + +exec 'syntax match juliaCTransOperator "[[:space:]}' . s:nonid_chars . s:op_chars_wc . '!]\@'.s:d(1).'<!\.\?' . "'" . 'áµ€\?"' + +" TODO: some of these might be specialized; the rest could be just left to the +" generic juliaStringPrefixed fallback +syntax region juliaString matchgroup=juliaStringDelim start=+\z("\(""\)\?\)+ skip=+\%(\\\\\)*\\"+ end=+\z1+ contains=@juliaStringVars,@juliaSpecialChars,@juliaSpellcheckStrings +syntax region juliaStringPrefixed contained matchgroup=juliaStringDelim start=+[^{([:space:]<>"]\+\z("\(""\)\?\)+ skip=+\%(\\\\\)*\\"+ end=+\z1+ contains=@juliaSpecialCharsRaw +syntax region juliabString matchgroup=juliaStringDelim start=+\<b\z("\(""\)\?\)+ skip=+\%(\\\\\)*\\"+ end=+\z1+ contains=@juliaSpecialChars +syntax region juliasString matchgroup=juliaStringDelim start=+\<s\z("\(""\)\?\)+ skip=+\%(\\\\\)*\\"+ end=+\z1+ contains=@juliaSpecialChars + +syntax region juliaDocString matchgroup=juliaDocStringDelim fold start=+^"""+ skip=+\%(\\\\\)*\\"+ end=+"""+ contains=@juliaStringVars,@juliaSpecialChars,@juliaSpellcheckDocStrings + +exec 'syntax region juliaPrintfMacro contained transparent start="@s\?printf(" end=")\@'.s:d(1).'<=" contains=juliaMacro,juliaPrintfParBlock' +syntax region juliaPrintfMacro contained transparent start="@s\?printf\s\+" end="\ze\%([])};#]\|$\|\<for\>\)" contains=@juliaExprsPrintf,juliaMacro,juliaSymbolS,juliaQuotedParBlockS +syntax region juliaPrintfParBlock contained matchgroup=juliaParDelim start="(" end=")" contains=@juliaExprsPrintf +syntax region juliaPrintfString contained matchgroup=juliaStringDelim start=+"+ skip=+\%(\\\\\)*\\"+ end=+"+ contains=@juliaSpecialChars,@juliaPrintfChars + +exec 'syntax region juliaDocMacroPre contained transparent start=+@doc\s\+\%(' . s:idregex . '\%(\.' . s:idregex . '\)*\)\z("\%(""\)\?\)+ skip=+\%(\\\\\)*\\"+ end=+\(\z1\)\@'.s:d(3).'<=+ contains=juliaMacro,juliaDocStringMRaw' +exec 'syntax region juliaDocMacro contained transparent start=+@doc\s\+\z("\%(""\)\?\)+ skip=+\%(\\\\\)*\\"+ end=+\(\z1\)\@'.s:d(3).'<=+ contains=juliaMacro,juliaDocStringM' +syntax region juliaDocStringMRaw contained fold matchgroup=juliaDocStringDelim fold start=+\z\("\(""\)\?\)+ skip=+\%(\\\\\)*\\"+ end=+\z1+ contains=@juliaSpellcheckDocStrings +syntax region juliaDocStringM contained fold matchgroup=juliaDocStringDelim fold start=+\z\("\(""\)\?\)+ skip=+\%(\\\\\)*\\"+ end=+\z1+ contains=@juliaStringVars,@juliaSpecialChars,@juliaSpellcheckDocStrings + +syntax region juliaShellString matchgroup=juliaStringDelim start=+`+ skip=+\%(\\\\\)*\\`+ end=+`+ contains=@juliaStringVars,juliaSpecialChar + +syntax cluster juliaStringVars contains=juliaStringVarsPar,juliaStringVarsSqBra,juliaStringVarsCurBra,juliaStringVarsPla +syntax region juliaStringVarsPar contained matchgroup=juliaStringVarDelim start="$(" end=")" contains=@juliaExpressions +syntax region juliaStringVarsSqBra contained matchgroup=juliaStringVarDelim start="$\[" end="\]" contains=@juliaExpressions,juliaComprehensionFor,juliaSymbolS,juliaQuotedParBlockS +syntax region juliaStringVarsCurBra contained matchgroup=juliaStringVarDelim start="${" end="}" contains=@juliaExpressions +exec 'syntax match juliaStringVarsPla contained "\$' . s:idregex . '"' + +" TODO improve RegEx +syntax region juliaRegEx matchgroup=juliaStringDelim start=+\<r\z("\(""\)\?\)+ skip=+\%(\\\\\)*\\"+ end=+\z1[imsx]*+ + +syntax cluster juliaSpecialChars contains=juliaSpecialChar,juliaDoubleBackslash,juliaEscapedQuote,juliaOctalEscapeChar,juliaHexEscapeChar,juliaUniCharSmall,juliaUniCharLarge +syntax match juliaSpecialChar display contained "\\." +syntax match juliaOctalEscapeChar display contained "\\\o\{3\}" +syntax match juliaHexEscapeChar display contained "\\x\x\{2\}" +syntax match juliaUniCharSmall display contained "\\u\x\{1,4\}" +syntax match juliaUniCharLarge display contained "\\U\x\{1,8\}" +syntax cluster juliaSpecialCharsRaw contains=juliaDoubleBackslash,juliaEscapedQuote +syntax match juliaDoubleBackslash contained "\\\\" +syntax match juliaEscapedQuote contained "\\\"" + +syntax cluster juliaPrintfChars contains=juliaErrorPrintfFmt,juliaPrintfFmt +syntax match juliaErrorPrintfFmt display contained "\\\?%." +syntax match juliaPrintfFmt display contained "%\%(\d\+\$\)\=[-+' #0]*\%(\d*\|\*\|\*\d\+\$\)\%(\.\%(\d*\|\*\|\*\d\+\$\)\)\=\%([hlLjqzt]\|ll\|hh\)\=[aAbdiuoxXDOUfFeEgGcCsSpn]" +syntax match juliaPrintfFmt display contained "%%" +syntax match juliaPrintfFmt display contained "\\%\%(\d\+\$\)\=[-+' #0]*\%(\d*\|\*\|\*\d\+\$\)\%(\.\%(\d*\|\*\|\*\d\+\$\)\)\=\%([hlLjqzt]\|ll\|hh\)\=[aAbdiuoxXDOUfFeEgGcCsSpn]"hs=s+1 +syntax match juliaPrintfFmt display contained "\\%%"hs=s+1 + +" this is used to restrict the search for Symbols to when colons appear at all +" (for performance reasons) +syntax match juliaPossibleSymbol transparent ":\ze[^:]" contains=juliaSymbol,juliaQuotedParBlock,juliaQuotedQMarkPar,juliaColon + +let s:quotable = '\%(' . s:idregex . '\|' . s:operators . '\|[?.]\|' . s:float_regex . '\|' . s:int_regex . '\)' +let s:quoting_colon = '\%(\%(^\s*\|\s\{6,\}\|[' . s:nonid_chars . s:op_chars_wc . ']\s*\)\@'.s:d(6).'<=\|\%(\<\%(return\|if\|else\%(if\)\?\|while\|try\|begin\)\s\+\)\@'.s:d(9).'<=\)\zs:' +let s:quoting_colonS = '\s\@'.s:d(1).'<=:' + +" note: juliaSymbolS only works within whitespace-sensitive contexts, +" such as in macro calls without parentheses, or within square brackets. +" It is used to override the recognition of expressions like `a :b` as +" ranges rather than symbols in those contexts. +" (Note that such `a :b` expressions only allows at most 5 spaces between +" the identifier and the colon anyway.) + +exec 'syntax match juliaSymbol contained "' . s:quoting_colon . s:quotable . '"' +exec 'syntax match juliaSymbolS contained "' . s:quoting_colonS . s:quotable . '"' + +" same as above for quoted expressions such as :(expr) +exec 'syntax region juliaQuotedParBlock matchgroup=juliaQParDelim start="' . s:quoting_colon . '(" end=")" contains=@juliaExpressions' +exec 'syntax match juliaQuotedQMarkPar "' . s:quoting_colon . '(\s*?\s*)" contains=juliaQuotedQMark' +exec 'syntax region juliaQuotedParBlockS matchgroup=juliaQParDelim contained start="' . s:quoting_colonS . '(" end=")" contains=@juliaExpressions' + + +syntax match juliaTypeOperatorR1 contained "[^{([:space:]<>\"]\+\%(\s*[<>]:\)\@=" + +" force precedence over Symbols +syntax match juliaTypeOperator contained "[<>:]:" +exec 'syntax match juliaTypeOperatorR2 transparent "[<>:]:\s*\%(' . s:idregex . '\.\)*' . s:idregex . '" contains=juliaTypeOperator,juliaType,juliaDottedT,@juliaExpressions nextgroup=juliaTypeOperator' +syntax match juliaIsaKeyword contained "\<isa\>" +exec 'syntax match juliaTypeOperatorR3 transparent "\<isa\s\+\%(' . s:idregex . '\.\)*' . s:idregex . '" contains=juliaIsaKeyword,juliaType,juliaDottedT,@juliaExpressions nextgroup=juliaIsaKeyword' + +syntax match juliaWhereKeyword "\<where\>" +exec 'syntax match juliaWhereR transparent "\<where\s\+' . s:idregex . '" contains=juliaWhereKeyword,juliaType,juliaDottedT,juliaIdSymbol' + +syntax region juliaCommentL matchgroup=juliaCommentDelim excludenl start="#\ze\%([^=]\|$\)" end="$" contains=juliaTodo,@juliaSpellcheckComments +syntax region juliaCommentM matchgroup=juliaCommentDelim fold start="#=\ze\%([^#]\|$\)" end="=#" contains=juliaTodo,juliaCommentM,@juliaSpellcheckComments +syntax keyword juliaTodo contained TODO FIXME XXX + +" detect an end-of-line with only whitespace or comments before it +let s:eol = '\s*\%(\%(\%(#=\%(=#\@!\|[^=]\|\n\)\{-}=#\)\s*\)\+\)\?\%(#=\@!.*\)\?\n' + +" a trailing comma, or colon, or an empty line in an import/using/export +" multi-line command. Used to recognize the as keyword, and for indentation +" (this needs to take precedence over normal commas and colons, and comments) +syntax cluster juliaContinuationItems contains=juliaContinuationComma,juliaContinuationColon,juliaContinuationNone +exec 'syntax region juliaContinuationComma matchgroup=juliaComma contained start=",\ze'.s:eol.'" end="\n\+\ze." contains=@juliaCommentItems' +exec 'syntax region juliaContinuationColon matchgroup=juliaColon contained start=":\ze'.s:eol.'" end="\n\+\ze." contains=@juliaCommentItems' +exec 'syntax region juliaContinuationNone matchgroup=NONE contained start="\%(\<\%(import\|using\|export\)\>\|^\)\@'.s:d(6).'<=\ze'.s:eol.'" end="\n\+\ze." contains=@juliaCommentItems,juliaAsKeyword' +exec 'syntax match juliaMacroName contained "@' . s:idregex . '\%(\.' . s:idregex . '\)*"' + +" the following are disabled by default, but +" can be enabled by entering e.g. +" :hi link juliaParDelim Delimiter +hi def link juliaParDelim juliaNone +hi def link juliaSemicolon juliaNone +hi def link juliaComma juliaNone +hi def link juliaFunctionCall juliaNone + +hi def link juliaColon juliaOperator + +hi def link juliaFunctionName juliaFunction +hi def link juliaFunctionName1 juliaFunction +hi def link juliaMacroName juliaMacro + + +hi def link juliaKeyword Keyword +hi def link juliaWhereKeyword Keyword +hi def link juliaInfixKeyword Keyword +hi def link juliaIsaKeyword Keyword +hi def link juliaAsKeyword Keyword +hi def link juliaRepKeyword Keyword +hi def link juliaBlKeyword Keyword +hi def link juliaConditional Conditional +hi def link juliaRepeat Repeat +hi def link juliaException Exception +hi def link juliaOuter Keyword +hi def link juliaBaseTypeBasic Type +hi def link juliaBaseTypeNum Type +hi def link juliaBaseTypeC Type +hi def link juliaBaseTypeError Type +hi def link juliaBaseTypeIter Type +hi def link juliaBaseTypeString Type +hi def link juliaBaseTypeArray Type +hi def link juliaBaseTypeDict Type +hi def link juliaBaseTypeSet Type +hi def link juliaBaseTypeIO Type +hi def link juliaBaseTypeProcess Type +hi def link juliaBaseTypeRange Type +hi def link juliaBaseTypeRegex Type +hi def link juliaBaseTypeFact Type +hi def link juliaBaseTypeSort Type +hi def link juliaBaseTypeRound Type +hi def link juliaBaseTypeSpecial Type +hi def link juliaBaseTypeRandom Type +hi def link juliaBaseTypeDisplay Type +hi def link juliaBaseTypeTime Type +hi def link juliaBaseTypeOther Type + +hi def link juliaType Type +hi def link juliaParamType Type +hi def link juliaTypeOperatorR1 Type + +" NOTE: deprecated constants are not highlighted as such. For once, +" one can still legitimately use them by importing Base.MathConstants. +" Plus, one-letter variables like `e` and `γ` can be used with other +" meanings. +hi def link juliaConstNum Constant +hi def link juliaEuler Constant + +hi def link juliaConstEnv Constant +hi def link juliaConstC Constant +hi def link juliaConstLimits Constant +hi def link juliaConstGeneric Constant +hi def link juliaRangeKeyword Constant +hi def link juliaConstBool Boolean +hi def link juliaConstIO Boolean + +hi def link juliaComprehensionFor Keyword +hi def link juliaComprehensionIf Keyword + +hi def link juliaDollarVar Identifier + +hi def link juliaFunction Function +hi def link juliaMacro Macro +hi def link juliaSymbol Identifier +hi def link juliaSymbolS Identifier +hi def link juliaQParDelim Identifier +hi def link juliaQuotedQMarkPar Identifier +hi def link juliaQuotedQMark juliaOperatorHL + +hi def link juliaNumber Number +hi def link juliaFloat Float +hi def link juliaComplexUnit Constant + +hi def link juliaChar Character + +hi def link juliaString String +hi def link juliaStringPrefixed juliaString +hi def link juliabString juliaString +hi def link juliasString juliaString +hi def link juliavString juliaString +hi def link juliarString juliaString +hi def link juliaipString juliaString +hi def link juliabigString juliaString +hi def link juliaMIMEString juliaString +hi def link juliarawString juliaString +hi def link juliatestString juliaString +hi def link juliahtmlString juliaString +hi def link juliaint128String juliaString +hi def link juliaPrintfString juliaString +hi def link juliaShellString juliaString +hi def link juliaDocString juliaString +hi def link juliaDocStringM juliaDocString +hi def link juliaDocStringMRaw juliaDocString +hi def link juliaStringDelim juliaString +hi def link juliaDocStringDelim juliaDocString +hi def link juliaStringVarsPla Identifier +hi def link juliaStringVarDelim Identifier + +hi def link juliaRegEx String + +hi def link juliaSpecialChar SpecialChar +hi def link juliaOctalEscapeChar SpecialChar +hi def link juliaHexEscapeChar SpecialChar +hi def link juliaUniCharSmall SpecialChar +hi def link juliaUniCharLarge SpecialChar +hi def link juliaDoubleBackslash SpecialChar +hi def link juliaEscapedQuote SpecialChar + +hi def link juliaPrintfFmt SpecialChar + +if s:julia_highlight_operators + hi! def link juliaOperatorHL Operator +else + hi! def link juliaOperatorHL juliaNone +endif +hi def link juliaOperator juliaOperatorHL +hi def link juliaRangeOperator juliaOperatorHL +hi def link juliaCTransOperator juliaOperatorHL +hi def link juliaTernaryOperator juliaOperatorHL +hi def link juliaTypeOperator juliaOperatorHL + +hi def link juliaCommentL Comment +hi def link juliaCommentM Comment +hi def link juliaCommentDelim Comment +hi def link juliaTodo Todo + +hi def link juliaErrorPar juliaError +hi def link juliaErrorEnd juliaError +hi def link juliaErrorElse juliaError +hi def link juliaErrorCatch juliaError +hi def link juliaErrorFinally juliaError +hi def link juliaErrorSemicol juliaError +hi def link juliaErrorPrintfFmt juliaError + +hi def link juliaError Error + +syntax sync fromstart + +let b:current_syntax = "julia" + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/syntax/lsp_markdown.vim b/runtime/syntax/lsp_markdown.vim index d9b50be54c..90d3185673 100644 --- a/runtime/syntax/lsp_markdown.vim +++ b/runtime/syntax/lsp_markdown.vim @@ -10,8 +10,14 @@ execute 'source' expand('<sfile>:p:h') .. '/markdown.vim' syn cluster mkdNonListItem add=mkdEscape,mkdNbsp -syntax region mkdEscape matchgroup=mkdEscape start=/\\\ze[\\\x60*{}\[\]()#+\-,.!_>~|"$%&'\/:;<=?@^ ]/ end=/.\zs/ keepend contains=mkdEscapeCh oneline concealends -syntax match mkdEscapeCh /./ contained +syn clear markdownEscape +syntax region markdownEscape matchgroup=markdownEscape start=/\\\ze[\\\x60*{}\[\]()#+\-,.!_>~|"$%&'\/:;<=?@^ ]/ end=/./ containedin=ALL keepend oneline concealends + +" conceal html entities syntax match mkdNbsp / / conceal cchar= +syntax match mkdLt /</ conceal cchar=< +syntax match mkdGt />/ conceal cchar=> +syntax match mkdAmp /&/ conceal cchar=& +syntax match mkdQuot /"/ conceal cchar=" hi def link mkdEscape special diff --git a/runtime/syntax/man.vim b/runtime/syntax/man.vim index a01bd1c0e7..55b0e16de9 100644 --- a/runtime/syntax/man.vim +++ b/runtime/syntax/man.vim @@ -6,7 +6,7 @@ if exists('b:current_syntax') endif syntax case ignore -syntax match manReference display '[^()[:space:]]\+([0-9nx][a-z]*)' +syntax match manReference display '[^()[:space:]]\+(\%([0-9][a-z]*\|[nlpox]\))' syntax match manSectionHeading display '^\S.*$' syntax match manHeader display '^\%1l.*$' syntax match manSubHeading display '^ \{3\}\S.*$' @@ -27,11 +27,7 @@ if &filetype != 'man' finish endif -if !exists('b:man_sect') - call man#init_pager() -endif - -if b:man_sect =~# '^[023]' +if get(b:, 'man_sect', '') =~# '^[023]' syntax case match syntax include @c $VIMRUNTIME/syntax/c.vim syntax match manCFuncDefinition display '\<\h\w*\>\ze\(\s\|\n\)*(' contained diff --git a/runtime/syntax/pascal.vim b/runtime/syntax/pascal.vim index 3ab5c2e661..206df213a6 100644 --- a/runtime/syntax/pascal.vim +++ b/runtime/syntax/pascal.vim @@ -3,7 +3,7 @@ " Maintainer: Doug Kearns <dougkearns@gmail.com> " Previous Maintainers: Xavier Crégut <xavier.cregut@enseeiht.fr> " Mario Eusebio <bio@dq.fct.unl.pt> -" Last Change: 2021 Apr 23 +" Last Change: 2021 May 20 " Contributors: Tim Chase <tchase@csc.com>, " Stas Grabois <stsi@vtrails.com>, diff --git a/runtime/syntax/redif.vim b/runtime/syntax/redif.vim index 725067fd32..9fa9064a86 100644 --- a/runtime/syntax/redif.vim +++ b/runtime/syntax/redif.vim @@ -932,7 +932,7 @@ highlight redifFieldDeprecated term=undercurl cterm=undercurl gui=undercurl guis " Sync: The template-type (ReDIF-Paper, ReDIF-Archive, etc.) influences which " fields can follow. Thus sync must search backwards for it. " -" I would like to simply ask VIM to search backward for the first occurrence of +" I would like to simply ask VIM to search backward for the first occurence of " /^Template-Type:/, but it does not seem to be possible, so I have to start " from the beginning of the file... This might slow down a lot for files that " contain a lot of Template-Type statements. diff --git a/runtime/syntax/ruby.vim b/runtime/syntax/ruby.vim index 0de63d0ef3..13d6d9efd8 100644 --- a/runtime/syntax/ruby.vim +++ b/runtime/syntax/ruby.vim @@ -3,7 +3,7 @@ " Maintainer: Doug Kearns <dougkearns@gmail.com> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2019 Jul 13 +" Last Change: 2021 Jun 06 " ---------------------------------------------------------------------------- " " Previous Maintainer: Mirko Nasato @@ -66,7 +66,7 @@ endfunction com! -nargs=* SynFold call s:run_syntax_fold(<q-args>) " Not-Top Cluster {{{1 -syn cluster rubyNotTop contains=@rubyCommentNotTop,@rubyStringNotTop,@rubyRegexpSpecial,@rubyDeclaration,@rubyExceptionHandler,@rubyClassOperator,rubyConditional,rubyModuleName,rubyClassName,rubySymbolDelimiter,rubyParentheses +syn cluster rubyNotTop contains=@rubyCommentNotTop,@rubyStringNotTop,@rubyRegexpSpecial,@rubyDeclaration,@rubyExceptionHandler,@rubyClassOperator,rubyConditional,rubyModuleName,rubyClassName,rubySymbolDelimiter,rubyParentheses,@Spell " Whitespace Errors {{{1 if exists("ruby_space_errors") @@ -92,7 +92,7 @@ if exists("ruby_operators") || exists("ruby_pseudo_operators") syn match rubyBooleanOperator "\%(\w\|[^\x00-\x7F]\)\@1<!!\|&&\|||" syn match rubyRangeOperator "\.\.\.\=" syn match rubyAssignmentOperator "=>\@!\|-=\|/=\|\*\*=\|\*=\|&&=\|&=\|||=\||=\|%=\|+=\|>>=\|<<=\|\^=" - syn match rubyAssignmentOperator "=>\@!" containedin=rubyBlockParameterList " TODO: this is inelegant + syn match rubyAssignmentOperator "=>\@!" contained containedin=rubyBlockParameterList " TODO: this is inelegant syn match rubyEqualityOperator "===\|==\|!=\|!\~\|=\~" syn region rubyBracketOperator matchgroup=rubyOperator start="\%(\%(\w\|[^\x00-\x7F]\)[?!]\=\|[]})]\)\@2<=\[" end="]" contains=ALLBUT,@rubyNotTop @@ -134,10 +134,10 @@ syn match rubyCurlyBraceEscape "\\[{}]" contained display syn match rubyAngleBracketEscape "\\[<>]" contained display syn match rubySquareBracketEscape "\\[[\]]" contained display -syn region rubyNestedParentheses start="(" skip="\\\\\|\\)" matchgroup=rubyString end=")" transparent contained -syn region rubyNestedCurlyBraces start="{" skip="\\\\\|\\}" matchgroup=rubyString end="}" transparent contained -syn region rubyNestedAngleBrackets start="<" skip="\\\\\|\\>" matchgroup=rubyString end=">" transparent contained -syn region rubyNestedSquareBrackets start="\[" skip="\\\\\|\\\]" matchgroup=rubyString end="\]" transparent contained +syn region rubyNestedParentheses start="(" skip="\\\\\|\\)" end=")" transparent contained +syn region rubyNestedCurlyBraces start="{" skip="\\\\\|\\}" end="}" transparent contained +syn region rubyNestedAngleBrackets start="<" skip="\\\\\|\\>" end=">" transparent contained +syn region rubyNestedSquareBrackets start="\[" skip="\\\\\|\\\]" end="\]" transparent contained syn cluster rubySingleCharEscape contains=rubyBackslashEscape,rubyQuoteEscape,rubySpaceEscape,rubyParenthesisEscape,rubyCurlyBraceEscape,rubyAngleBracketEscape,rubySquareBracketEscape syn cluster rubyNestedBrackets contains=rubyNested.\+ @@ -193,7 +193,7 @@ SynFold ':' syn region rubySymbol matchgroup=rubySymbolDelimiter start="[]})\"': syn match rubyCapitalizedMethod "\%(\%(^\|[^.]\)\.\s*\)\@<!\<\u\%(\w\|[^\x00-\x7F]\)*\>\%(\s*(\)\@=" -syn region rubyParentheses start="(" end=")" contains=ALLBUT,@rubyNotTop containedin=rubyBlockParameterList +syn region rubyParentheses start="(" end=")" contains=ALLBUT,@rubyNotTop contained containedin=rubyBlockParameterList syn region rubyBlockParameterList start="\%(\%(\<do\>\|{\)\_s*\)\@32<=|" end="|" contains=ALLBUT,@rubyNotTop,@rubyProperOperator if exists('ruby_global_variable_error') @@ -332,7 +332,7 @@ SynFold '<<' syn region rubyString start=+\%(\%(class\|::\|\.\@1<!\.\)\_s*\|\%([ syn match rubyAliasDeclaration "[^[:space:];#.()]\+" contained contains=rubySymbol,@rubyGlobalVariable nextgroup=rubyAliasDeclaration2 skipwhite syn match rubyAliasDeclaration2 "[^[:space:];#.()]\+" contained contains=rubySymbol,@rubyGlobalVariable syn match rubyMethodDeclaration "[^[:space:];#(]\+" contained contains=rubyConstant,rubyBoolean,rubyPseudoVariable,rubyInstanceVariable,rubyClassVariable,rubyGlobalVariable -syn match rubyClassDeclaration "[^[:space:];#<]\+" contained contains=rubyClassName,rubyScopeOperator nextgroup=rubySuperClassOperator skipwhite skipnl +syn match rubyClassDeclaration "[^[:space:];#<]\+" contained contains=rubyClassName,rubyScopeOperator nextgroup=rubySuperClassOperator skipwhite syn match rubyModuleDeclaration "[^[:space:];#<]\+" contained contains=rubyModuleName,rubyScopeOperator syn match rubyMethodName "\<\%([_[:alpha:]]\|[^\x00-\x7F]\)\%([_[:alnum:]]\|[^\x00-\x7F]\)*[?!=]\=\%([[:alnum:]_.:?!=]\|[^\x00-\x7F]\)\@!" contained containedin=rubyMethodDeclaration @@ -462,7 +462,7 @@ endif syn match rubyDefinedOperator "\%#=1\<defined?" display " 1.9-style Hash Keys and Keyword Parameters {{{1 -syn match rubySymbol "\%([{(|,]\_s*\)\@<=\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[?!]\=::\@!"he=e-1 +syn match rubySymbol "\%(\w\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[?!]\=::\@!"he=e-1 contained containedin=rubyBlockParameterList,rubyCurlyBlock syn match rubySymbol "[]})\"':]\@1<!\<\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[!?]\=:[[:space:],;]\@="he=e-1 syn match rubySymbol "[[:space:],{(]\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[!?]\=:[[:space:],;]\@="hs=s+1,he=e-1 diff --git a/runtime/syntax/scala.vim b/runtime/syntax/scala.vim index c5a175fd77..89a936ad17 100644 --- a/runtime/syntax/scala.vim +++ b/runtime/syntax/scala.vim @@ -3,7 +3,8 @@ " Maintainer: Derek Wyatt " URL: https://github.com/derekwyatt/vim-scala " License: Same as Vim -" Last Change: 20 May 2016 +" Last Change: 2021 Aug 11 +" by Jesse Atkinson, PR #8746 " ---------------------------------------------------------------------------- if !exists('main_syntax') @@ -66,7 +67,7 @@ syn match scalaChar /'\\u[A-Fa-f0-9]\{4}'/ contains=scalaUnicodeChar syn match scalaEscapedChar /\\[\\"'ntbrf]/ syn match scalaUnicodeChar /\\u[A-Fa-f0-9]\{4}/ hi link scalaChar Character -hi link scalaEscapedChar Function +hi link scalaEscapedChar Special hi link scalaUnicodeChar Special syn match scalaOperator "||" diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim index 48a0024b00..0ab9c0ad58 100644 --- a/runtime/syntax/sh.vim +++ b/runtime/syntax/sh.vim @@ -2,8 +2,8 @@ " Language: shell (sh) Korn shell (ksh) bash (sh) " Maintainer: Charles E. Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> " Previous Maintainer: Lennart Schultz <Lennart.Schultz@ecmwf.int> -" Last Change: Nov 24, 2020 -" Version: 196 +" Last Change: Feb 18, 2021 +" Version: 198 " URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH " For options and settings, please use: :help ft-sh-syntax " This file includes many ideas from Eric Brunet (eric.brunet@ens.fr) and heredoc fixes from Felipe Contreras @@ -13,33 +13,35 @@ if exists("b:current_syntax") finish endif -" trying to answer the question: which shell is /bin/sh, really? -" If the user has not specified any of g:is_kornshell, g:is_bash, g:is_posix, g:is_sh, then guess. -if getline(1) =~ '\<ksh$' +" If the shell script itself specifies which shell to use, use it +if getline(1) =~ '\<ksh\>' let b:is_kornshell = 1 -elseif getline(1) =~ '\<bash$' +elseif getline(1) =~ '\<bash\>' let b:is_bash = 1 -elseif getline(1) =~ '\<dash$' +elseif getline(1) =~ '\<dash\>' let b:is_dash = 1 elseif !exists("g:is_kornshell") && !exists("g:is_bash") && !exists("g:is_posix") && !exists("g:is_sh") && !exists("g:is_dash") + " user did not specify which shell to use, and + " the script itself does not specify which shell to use. FYI: /bin/sh is ambiguous. + " Assuming /bin/sh is executable, and if its a link, find out what it links to. let s:shell = "" if executable("/bin/sh") let s:shell = resolve("/bin/sh") elseif executable("/usr/bin/sh") let s:shell = resolve("/usr/bin/sh") endif - if s:shell =~ 'ksh$' + if s:shell =~ '\<ksh\>' let b:is_kornshell= 1 - elseif s:shell =~ 'bash$' + elseif s:shell =~ '\<bash\>' let b:is_bash = 1 - elseif s:shell =~ 'dash$' + elseif s:shell =~ '\<dash\>' let b:is_dash = 1 endif unlet s:shell endif " handling /bin/sh with is_kornshell/is_sh {{{1 -" b:is_sh is set when "#! /bin/sh" is found; +" b:is_sh will be set when "#! /bin/sh" is found; " However, it often is just a masquerade by bash (typically Linux) " or kornshell (typically workstations with Posix "sh"). " So, when the user sets "g:is_bash", "g:is_kornshell", @@ -98,12 +100,14 @@ if g:sh_fold_enabled && &fdm == "manual" setl fdm=syntax endif -" set up the syntax-highlighting iskeyword +" set up the syntax-highlighting for iskeyword if (v:version == 704 && has("patch-7.4.1142")) || v:version > 704 - if exists("b:is_bash") - exe "syn iskeyword ".&iskeyword.",-,:" - else - exe "syn iskeyword ".&iskeyword.",-" + if !exists("g:sh_syntax_isk") || (exists("g:sh_syntax_isk") && g:sh_syntax_isk) + if exists("b:is_bash") + exe "syn iskeyword ".&iskeyword.",-,:" + else + exe "syn iskeyword ".&iskeyword.",-" + endif endif endif @@ -374,12 +378,11 @@ elseif !exists("g:sh_no_error") syn region shExDoubleQuote matchGroup=Error start=+\$"+ skip=+\\\\\|\\.+ end=+"+ contains=shStringSpecial endif syn region shSingleQuote matchgroup=shQuote start=+'+ end=+'+ contains=@Spell nextgroup=shSpecialStart,shSpecialSQ -syn region shDoubleQuote matchgroup=shQuote start=+\%(\%(\\\\\)*\\\)\@<!"+ skip=+\\.+ end=+"+ contains=@shDblQuoteList,shStringSpecial,@Spell nextgroup=shSpecialStart -syn region shDoubleQuote matchgroup=shQuote start=+"+ matchgroup=shSpecial skip=+\\"+ matchgroup=shQuote end=+"+ contained contains=@shDblQuoteList,shStringSpecial,@Spell nextgroup=shSpecialStart +syn region shDoubleQuote matchgroup=shQuote start=+\%(\%(\\\\\)*\\\)\@<!"+ skip=+\\.+ end=+"+ contains=@shDblQuoteList,shStringSpecial,@Spell nextgroup=shSpecialStart syn match shStringSpecial "[^[:print:] \t]" contained -syn match shStringSpecial "[^\\]\zs\%(\\\\\)*\\[\\"'`$()#]" nextgroup=shComment -syn match shSpecialSQ "[^\\]\zs\%(\\\\\)*\\[\\"'`$()#]" contained nextgroup=shBkslshSnglQuote,@shNoZSList -syn match shSpecialDQ "[^\\]\zs\%(\\\\\)*\\[\\"'`$()#]" contained nextgroup=shBkslshDblQuote,@shNoZSList +syn match shStringSpecial "[^\\]\zs\%(\\\\\)*\(\\[\\"'`$()#]\)\+" nextgroup=shComment +syn match shSpecialSQ "[^\\]\zs\%(\\\\\)*\(\\[\\"'`$()#]\)\+" contained nextgroup=shBkslshSnglQuote,@shNoZSList +syn match shSpecialDQ "[^\\]\zs\%(\\\\\)*\(\\[\\"'`$()#]\)\+" contained nextgroup=shBkslshDblQuote,@shNoZSList syn match shSpecialStart "\%(\\\\\)*\\[\\"'`$()#]" contained nextgroup=shBkslshSnglQuote,shBkslshDblQuote,@shNoZSList syn match shSpecial "^\%(\\\\\)*\\[\\"'`$()#]" syn match shSpecialNoZS contained "\%(\\\\\)*\\[\\"'`$()#]" @@ -402,6 +405,7 @@ syn match shQuickComment contained "#.*$" syn match shBQComment contained "#.\{-}\ze`" contains=@shCommentGroup " Here Documents: {{{1 +" (modified by Felipe Contreras) " ========================================= ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc01 start="<<\s*\z([^ \t|>]\+\)" matchgroup=shHereDoc01 end="^\z1\s*$" contains=@shDblQuoteList ShFoldHereDoc syn region shHereDoc matchgroup=shHereDoc02 start="<<-\s*\z([^ \t|>]\+\)" matchgroup=shHereDoc02 end="^\s*\z1\s*$" contains=@shDblQuoteList diff --git a/runtime/syntax/syncolor.vim b/runtime/syntax/syncolor.vim deleted file mode 100644 index 5b907a3b83..0000000000 --- a/runtime/syntax/syncolor.vim +++ /dev/null @@ -1,87 +0,0 @@ -" Vim syntax support file -" Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2020 Feb 13 - -" This file sets up the default methods for highlighting. -" It is loaded from "synload.vim" and from Vim for ":syntax reset". -" Also used from init_highlight(). - -if !exists("syntax_cmd") || syntax_cmd == "on" - " ":syntax on" works like in Vim 5.7: set colors but keep links - command -nargs=* SynColor hi <args> - command -nargs=* SynLink hi link <args> -else - if syntax_cmd == "enable" - " ":syntax enable" keeps any existing colors - command -nargs=* SynColor hi def <args> - command -nargs=* SynLink hi def link <args> - elseif syntax_cmd == "reset" - " ":syntax reset" resets all colors to the default - command -nargs=* SynColor hi <args> - command -nargs=* SynLink hi! link <args> - else - " User defined syncolor file has already set the colors. - finish - endif -endif - -" Many terminals can only use six different colors (plus black and white). -" Therefore the number of colors used is kept low. It doesn't look nice with -" too many colors anyway. -" Careful with "cterm=bold", it changes the color to bright for some terminals. -" There are two sets of defaults: for a dark and a light background. -if &background == "dark" - SynColor Comment term=bold cterm=NONE ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#80a0ff guibg=NONE - SynColor Constant term=underline cterm=NONE ctermfg=Magenta ctermbg=NONE gui=NONE guifg=#ffa0a0 guibg=NONE - SynColor Special term=bold cterm=NONE ctermfg=LightRed ctermbg=NONE gui=NONE guifg=Orange guibg=NONE - SynColor Identifier term=underline cterm=bold ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#40ffff guibg=NONE - SynColor Statement term=bold cterm=NONE ctermfg=Yellow ctermbg=NONE gui=bold guifg=#ffff60 guibg=NONE - SynColor PreProc term=underline cterm=NONE ctermfg=LightBlue ctermbg=NONE gui=NONE guifg=#ff80ff guibg=NONE - SynColor Type term=underline cterm=NONE ctermfg=LightGreen ctermbg=NONE gui=bold guifg=#60ff60 guibg=NONE - SynColor Underlined term=underline cterm=underline ctermfg=LightBlue gui=underline guifg=#80a0ff - SynColor Ignore term=NONE cterm=NONE ctermfg=black ctermbg=NONE gui=NONE guifg=bg guibg=NONE -else - SynColor Comment term=bold cterm=NONE ctermfg=DarkBlue ctermbg=NONE gui=NONE guifg=Blue guibg=NONE - SynColor Constant term=underline cterm=NONE ctermfg=DarkRed ctermbg=NONE gui=NONE guifg=Magenta guibg=NONE - " #6a5acd is SlateBlue - SynColor Special term=bold cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a5acd guibg=NONE - SynColor Identifier term=underline cterm=NONE ctermfg=DarkCyan ctermbg=NONE gui=NONE guifg=DarkCyan guibg=NONE - SynColor Statement term=bold cterm=NONE ctermfg=Brown ctermbg=NONE gui=bold guifg=Brown guibg=NONE - " #6a0dad is Purple - SynColor PreProc term=underline cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a0dad guibg=NONE - SynColor Type term=underline cterm=NONE ctermfg=DarkGreen ctermbg=NONE gui=bold guifg=SeaGreen guibg=NONE - SynColor Underlined term=underline cterm=underline ctermfg=DarkMagenta gui=underline guifg=SlateBlue - SynColor Ignore term=NONE cterm=NONE ctermfg=white ctermbg=NONE gui=NONE guifg=bg guibg=NONE -endif -SynColor Error term=reverse cterm=NONE ctermfg=White ctermbg=Red gui=NONE guifg=White guibg=Red -SynColor Todo term=standout cterm=NONE ctermfg=Black ctermbg=Yellow gui=NONE guifg=Blue guibg=Yellow - -" Common groups that link to default highlighting. -" You can specify other highlighting easily. -SynLink String Constant -SynLink Character Constant -SynLink Number Constant -SynLink Boolean Constant -SynLink Float Number -SynLink Function Identifier -SynLink Conditional Statement -SynLink Repeat Statement -SynLink Label Statement -SynLink Operator Statement -SynLink Keyword Statement -SynLink Exception Statement -SynLink Include PreProc -SynLink Define PreProc -SynLink Macro PreProc -SynLink PreCondit PreProc -SynLink StorageClass Type -SynLink Structure Type -SynLink Typedef Type -SynLink Tag Special -SynLink SpecialChar Special -SynLink Delimiter Special -SynLink SpecialComment Special -SynLink Debug Special - -delcommand SynColor -delcommand SynLink diff --git a/runtime/syntax/synload.vim b/runtime/syntax/synload.vim index f373161c7c..bfcd3b06da 100644 --- a/runtime/syntax/synload.vim +++ b/runtime/syntax/synload.vim @@ -14,13 +14,6 @@ endif " let others know that syntax has been switched on let syntax_on = 1 -" Set the default highlighting colors. Use a color scheme if specified. -if exists("colors_name") - exe "colors " . colors_name -else - runtime! syntax/syncolor.vim -endif - " Line continuation is used here, remove 'C' from 'cpoptions' let s:cpo_save = &cpo set cpo&vim @@ -55,7 +48,8 @@ fun! s:SynSet() " load each in sequence. Skip empty entries. for name in split(s, '\.') if !empty(name) - exe "runtime! syntax/" . name . ".vim syntax/" . name . "/*.vim" + exe "runtime! syntax/" . name . ".vim syntax/" . name . "/*.vim" + exe "runtime! syntax/" . name . ".lua syntax/" . name . "/*.lua" endif endfor endif diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index d395c6b1c3..f695a1a1bf 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -12,7 +12,7 @@ if exists("b:current_syntax") finish endif -let s:keepcpo= &cpo +let s:keepcpo = &cpo set cpo&vim " vimTodo: contains common special-notices for comments {{{2 @@ -149,7 +149,7 @@ syn match vimNumber '\<0[xX]\x\+' skipwhite nextgroup=vimGlobal,vimSubst syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment syn match vimNumber '\<0[zZ][a-zA-Z0-9.]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment -syn match vimNumber '0b[01]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment +syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment " All vimCommands are contained by vimIsCommand. {{{2 syn match vimCmdSep "[:|]\+" skipwhite nextgroup=vimAddress,vimAutoCmd,vimEcho,vimIsCommand,vimExtCmd,vimFilter,vimLet,vimMap,vimMark,vimSet,vimSyntax,vimUserCmd @@ -200,7 +200,7 @@ syn keyword vimAugroupKey contained aug[roup] " Operators: {{{2 " ========= -syn cluster vimOperGroup contains=vimEnvvar,vimFunc,vimFuncVar,vimOper,vimOperParen,vimNumber,vimString,vimRegister,vimContinue,vim9Comment +syn cluster vimOperGroup contains=vimEnvvar,vimFunc,vimFuncVar,vimOper,vimOperParen,vimNumber,vimString,vimType,vimRegister,vimContinue,vim9Comment syn match vimOper "\%#=1\(==\|!=\|>=\|<=\|=\~\|!\~\|>\|<\|=\)[?#]\{0,2}" skipwhite nextgroup=vimString,vimSpecFile syn match vimOper "\(\<is\|\<isnot\)[?#]\{0,2}\>" skipwhite nextgroup=vimString,vimSpecFile syn match vimOper "||\|&&\|[-+.!]" skipwhite nextgroup=vimString,vimSpecFile @@ -214,12 +214,13 @@ endif " ========= syn cluster vimFuncList contains=vimCommand,vimFunctionError,vimFuncKey,Tag,vimFuncSID syn cluster vimFuncBodyList contains=vimAbb,vimAddress,vimAugroupKey,vimAutoCmd,vimCmplxRepeat,vimComment,vim9Comment,vimContinue,vimCtrlChar,vimEcho,vimEchoHL,vimEnvvar,vimExecute,vimIsCommand,vimFBVar,vimFunc,vimFunction,vimFuncVar,vimGlobal,vimHighlight,vimIsCommand,vimLet,vimLetHereDoc,vimLineComment,vimMap,vimMark,vimNorm,vimNotation,vimNotFunc,vimNumber,vimOper,vimOperParen,vimRegion,vimRegister,vimSearch,vimSet,vimSpecFile,vimString,vimSubst,vimSynLine,vimUnmap,vimUserCommand -syn match vimFunction "\<fu\%[nction]!\=\s\+\%(<[sS][iI][dD]>\|[sSgGbBwWtTlL]:\)\=\%(\i\|[#.]\|{.\{-1,}}\)*\ze\s*(" contains=@vimFuncList nextgroup=vimFuncBody +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 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 vimFuncBody contained start="\ze\s*(" matchgroup=vimCommand end="\<\(endf\>\|endfu\%[nction]\>\|enddef\>\)" contains=@vimFuncBodyList endif syn match vimFuncVar contained "a:\(\K\k*\|\d\+\)" syn match vimFuncSID contained "\c<sid>\|\<s:" @@ -228,6 +229,9 @@ syn match vimFuncBlank contained "\s\+" syn keyword vimPattern contained start skip end +" vimTypes : new for vim9 + syn match vimType ":\s*\zs\<\(bool\|number\|float\|string\|blob\|list<\|dict<\|job\|channel\|func\)\>" + " Special Filenames, Modifiers, Extension Removal: {{{2 " =============================================== syn match vimSpecFile "<c\(word\|WORD\)>" nextgroup=vimSpecFileMod,vimSubst @@ -355,8 +359,8 @@ 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="\%(\\\\\)*\\." end="$" end="|" matchgroup=vimNotation end="<[cC][rR]>" keepend oneline contains=vimSetEqual,vimOption,vimErrSetting,vimComment,vim9Comment,vimSetString,vimSetMod -syn region vimSetEqual contained start="[=:]\|[-+^]=" skip="\\\\\|\\\s" end="[| \t]\|$"me=e-1 contains=vimCtrlChar,vimSetSep,vimNotation,vimEnvvar oneline +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 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 "[,:]" syn match vimSetMod contained "&vim\=\|[!&?<]\|all&" @@ -390,7 +394,7 @@ syn case match " Maps: {{{2 " ==== syn match vimMap "\<map\>!\=\ze\s*[^(]" skipwhite nextgroup=vimMapMod,vimMapLhs -syn keyword vimMap cm[ap] cno[remap] im[ap] ino[remap] lm[ap] ln[oremap] nm[ap] nn[oremap] no[remap] om[ap] ono[remap] smap snor[emap] vm[ap] vn[oremap] xm[ap] xn[oremap] skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs +syn keyword vimMap cm[ap] cno[remap] im[ap] ino[remap] lm[ap] ln[oremap] nm[ap] nn[oremap] no[remap] om[ap] ono[remap] smap snor[emap] tno[remap] tm[ap] vm[ap] vmapc[lear] vn[oremap] xm[ap] xn[oremap] skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs syn keyword nvimMap tn[oremap] tm[ap] skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs syn keyword vimMap mapc[lear] smapc[lear] syn keyword vimUnmap cu[nmap] iu[nmap] lu[nmap] nun[map] ou[nmap] sunm[ap] unm[ap] unm[ap] vu[nmap] xu[nmap] skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs @@ -435,7 +439,8 @@ syn match vimFunc "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA syn match vimUserFunc contained "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA-Z0-9_.]*\)\|\<\u[a-zA-Z0-9.]*\>\|\<if\>" contains=vimNotation " User Command Highlighting: {{{2 -syn match vimUsrCmd '^\s*\zs\u\w*.*$' +"syn match vimUsrCmd '^\s*\zs\u\w*.*$' +syn match vimUsrCmd '^\s*\zs\u\%(\w*\)\@>\%([(#[]\|\s\+\%([-+*/%]\=\|\.\.\)=\)\@!' " Errors And Warnings: {{{2 " ==================== @@ -573,7 +578,7 @@ syn case match syn match vimHiAttribList contained "\i\+" contains=vimHiAttrib syn match vimHiAttribList contained "\i\+,"he=e-1 contains=vimHiAttrib nextgroup=vimHiAttribList syn case ignore -syn keyword vimHiCtermColor contained black blue brown cyan darkblue darkcyan darkgray darkgreen darkgrey darkmagenta darkred darkyellow gray green grey lightblue lightcyan lightgray lightgreen lightgrey lightmagenta lightred magenta red white yellow +syn keyword vimHiCtermColor contained black blue brown cyan darkblue darkcyan darkgray darkgreen darkgrey darkmagenta darkred darkyellow gray green grey grey40 grey50 grey90 lightblue lightcyan lightgray lightgreen lightgrey lightmagenta lightred lightyellow magenta red seagreen white yellow syn match vimHiCtermColor contained "\<color\d\{1,3}\>" syn case match @@ -980,6 +985,7 @@ if !exists("skip_vim_syntax_inits") hi def link vimSyntax vimCommand hi def link vimSynType vimSpecial hi def link vimTodo Todo + hi def link vimType Type hi def link vimUnmap vimMap hi def link vimUserAttrbCmpltFunc Special hi def link vimUserAttrbCmplt vimSpecial diff --git a/runtime/tutor/en/vim-01-beginner.tutor b/runtime/tutor/en/vim-01-beginner.tutor index 5ae0fde0da..7c0c357e80 100644 --- a/runtime/tutor/en/vim-01-beginner.tutor +++ b/runtime/tutor/en/vim-01-beginner.tutor @@ -1,4 +1,4 @@ -# Welcome to the VIM Tutor +# Welcome to the VIM Tutor Vim is a very powerful editor that has many commands, too many to explain in a tutor such as this. This tutor is designed to describe enough of the @@ -21,7 +21,9 @@ This tutorial is interactive, and there are a few things you should know. - Type [<Enter>](<Enter>) on links [like this](holy-grail ) to open the linked help section. - Or simply type [K](K) on any word to find its documentation! - Sometimes you will be required to modify text like -this here + + this here + Once you have done the changes correctly, the ✗ sign at the left will change to ✓. I imagine you can already see how neat Vim can be. ;) Other times, you'll be prompted to run a command (I'll explain this later): @@ -32,7 +34,6 @@ or press a sequence of keys ~~~ normal <Esc>0f<Space>d3wP$P ~~~ - Text within <'s and >'s (like `<Enter>`{normal}) describes a key to press instead of text to type. @@ -48,12 +49,12 @@ Now, move to the next lesson (use the `j`{normal} key to scroll down). j The `j`{normal} key looks like a down arrow. ↓ - 1. Move the cursor around the screen until you are comfortable. + 1. Move the cursor around the screen until you are comfortable. - 2. Hold down the down key (`j`{normal}) until it repeats. - Now you know how to move to the next lesson. + 2. Hold down the down key (`j`{normal}) until it repeats. + Now you know how to move to the next lesson. - 3. Using the down key, move to Lesson 1.2. + 3. Using the down key, move to Lesson 1.2. NOTE: If you are ever unsure about something you typed, press <Esc> to place you in Normal mode. Then retype the command you wanted. @@ -63,8 +64,7 @@ NOTE: The cursor keys should also work. But using hjkl you will be able to # Lesson 1.2: EXITING VIM -!! NOTE: Before executing any of the steps below, -read this entire lesson !! +!! NOTE: Before executing any of the steps below, read this entire lesson !! 1. Press the <Esc> key (to make sure you are in Normal mode). @@ -72,18 +72,18 @@ read this entire lesson !! `:q!`{vim} `<Enter>`{normal}. - This exits the editor, DISCARDING any changes you have made. + This exits the editor, DISCARDING any changes you have made. 3. Open vim and get back here by executing the command that got you into - this tutor. That might be: + this tutor. That might be: - :Tutor <Enter> + :Tutor <Enter> 4. If you have these steps memorized and are confident, execute steps - 1 through 3 to exit and re-enter the editor. + 1 through 3 to exit and re-enter the editor. NOTE: [:q!](:q) <Enter> discards any changes you made. In a few lessons you - will learn how to save the changes to a file. + will learn how to save the changes to a file. 5. Move the cursor down to Lesson 1.3. @@ -94,7 +94,7 @@ NOTE: [:q!](:q) <Enter> discards any changes you made. In a few lessons you 1. Move the cursor to the line below marked ✗. 2. To fix the errors, move the cursor until it is on top of the - character to be deleted. + character to be deleted. 3. Press [the x key](x) to delete the unwanted character. @@ -104,8 +104,9 @@ The ccow jumpedd ovverr thhe mooon. 5. Now that the line is correct, go on to Lesson 1.4. -NOTE: As you go through this tutor, do not try to memorize, learn by - usage. +NOTE: As you go through this tutor, do not try to memorize everything, + your Vim vocabulary will expand with usage. Consider returning to + this tutor periodically for a refresher. # Lesson 1.4: TEXT EDITING: INSERTION @@ -114,12 +115,12 @@ NOTE: As you go through this tutor, do not try to memorize, learn by 1. Move the cursor to the first line below marked ✗. 2. To make the first line the same as the second, move the cursor on top - of the first character AFTER where the text is to be inserted. + of the first character AFTER where the text is to be inserted. 3. Press `i`{normal} and type in the necessary additions. 4. As each error is fixed press `<Esc>`{normal} to return to Normal mode. - Repeat steps 2 through 4 to correct the sentence. + Repeat steps 2 through 4 to correct the sentence. There is text misng this . There is some text missing from this line. @@ -136,7 +137,7 @@ There is some text missing from this line. 2. Press [A](A) and type in the necessary additions. 3. As the text has been appended press `<Esc>`{normal} to return to Normal - mode. + mode. 4. Move the cursor to the second line marked ✗ and repeat steps 2 and 3 to correct this sentence. @@ -159,7 +160,7 @@ There is also some text missing here. 2. At the shell prompt type this command: ~~~ sh - $ nvim tutor + $ nvim tutor ~~~ 'nvim' is the command to start the Nvim editor, 'tutor' is the name of the file you wish to edit. Use a file that may be changed. @@ -168,13 +169,12 @@ There is also some text missing here. 4. Save the file with changes and exit Vim with: ~~~ cmd - :wq + :wq ~~~ - Note you'll need to press `<Enter>` to execute the command. 5. If you have quit vimtutor in step 1 restart the vimtutor and move down - to the following summary. + to the following summary. 6. After reading the above steps and understanding them: do it. @@ -184,15 +184,11 @@ There is also some text missing here. h (left) j (down) k (up) l (right) 2. To start Vim from the shell prompt type: - ~~~ sh $ nvim FILENAME ~~~ - - 3. To exit Vim type: `<Esc>`{normal} `:q!`{vim} `<Enter>`{normal} to trash - all changes. - OR type: `<Esc>`{normal} `:wq`{vim} `<Enter>`{normal} to save - the changes. + 3. To exit Vim type: `<Esc>`{normal} `:q!`{vim} `<Enter>`{normal} to trash all changes. + OR type: `<Esc>`{normal} `:wq`{vim} `<Enter>`{normal} to save the changes. 4. To delete the character at the cursor type: `x`{normal} @@ -239,8 +235,7 @@ Somebody typed the end of this line twice. end of this line twice. # Lesson 2.3: ON OPERATORS AND MOTIONS -Many commands that change text are made from an [operator](operator) and -a [motion](navigation). +Many commands that change text are made from an [operator](operator) and a [motion](navigation). The format for a delete command with the [d](d) delete operator is as follows: d motion @@ -318,16 +313,13 @@ it would be easier to simply type two d's to delete a line. ** Press `u`{normal} to undo the last commands, `U`{normal} to fix a whole line. ** - 1. Move the cursor to the line below marked ✗ and place it on the - first error. + 1. Move the cursor to the line below marked ✗ and place it on the first error. 2. Type `x`{normal} to delete the first unwanted character. 3. Now type `u`{normal} to undo the last command executed. 4. This time fix all the errors on the line using the `x`{normal} command. 5. Now type a capital `U`{normal} to return the line to its original state. - 6. Now type `u`{normal} a few times to undo the `U`{normal} and preceding - commands. - 7. Now type `<C-r>`{normal} (Control + R) a few times to redo the commands - (undo the undos). + 6. Now type `u`{normal} a few times to undo the `U`{normal} and preceding commands. + 7. Now type `<C-r>`{normal} (Control + R) a few times to redo the commands. Fiix the errors oon thhis line and reeplace them witth undo. @@ -341,11 +333,14 @@ Fiix the errors oon thhis line and reeplace them witth undo. 4. To repeat a motion prepend it with a number: `2w`{normal} 5. The format for a change command is: - operator [number] motion + + operator [number] motion + where: - operator - is what to do, such as [d](d) for delete - [number] - is an optional count to repeat the motion - motion - moves over the text to operate on, such as: + + operator - is what to do, such as [d](d) for delete + [number] - is an optional count to repeat the motion + motion - moves over the text to operate on, such as: [w](w) (word), [$]($) (to the end of line), etc. @@ -403,8 +398,7 @@ NOTE: Remember that you should be learning by doing, not memorization. 3. Type `ce`{normal} and the correct word (in this case, type "ine" ). - 4. Press `<Esc>`{normal} and move to the next character that needs to be - changed. + 4. Press `<Esc>`{normal} and move to the next character that needs to be changed. 5. Repeat steps 3 and 4 until the first sentence is the same as the second. @@ -419,7 +413,7 @@ Notice that [c](c)e deletes the word and places you in Insert mode. 1. The change operator works in the same way as delete. The format is: - c [number] motion + c [number] motion 2. The motions are the same, such as `w`{normal} (word) and `$`{normal} (end of line). @@ -449,7 +443,7 @@ NOTE: You can use the Backspace key to correct mistakes while typing. 4. The format for change is: - c [number] motion + c [number] motion Now go on to the next lesson. @@ -460,13 +454,13 @@ Now go on to the next lesson. NOTE: Read this entire lesson before executing any of the steps!! - 1. Hold down the `<Ctrl>`{normal} key and press `g`{normal}. We call this - `<C-g>`{normal}. A message will appear at the bottom of the page with the - filename and the position in the file. Remember the line number for - Step 3. + 1. Hold down the `<Ctrl>`{normal} key and press `g`{normal}. We call this `<C-g>`{normal}. + A message will appear at the bottom of the page with the filename and + the position in the file. Remember the line number for Step 3. NOTE: You may see the cursor position in the lower right corner of the screen. This happens when the ['ruler']('ruler') option is set. + 2. Press [G](G) to move you to the bottom of the file. Type [gg](gg) to move you to the start of the file. @@ -482,17 +476,16 @@ NOTE: You may see the cursor position in the lower right corner of the 1. In Normal mode type the `/`{normal} character. Notice that it and the cursor appear at the bottom of the screen as with the `:`{normal} command. - 2. Now type 'errroor' `<Enter>`{normal}. This is the word you want to search - for. + 2. Now type 'errroor' `<Enter>`{normal}. This is the word you want to search for. 3. To search for the same phrase again, simply type [n](n). To search for the same phrase in the opposite direction, type [N](N). - 4. To search for a phrase in the backward direction, use [?](?) instead - of `/`{normal}. + 4. To search for a phrase in the backward direction, use [?](?) instead of `/`{normal}. - 5. To go back to where you came from press `<C-o>`{normal} (keep `<Ctrl>`{normal} pressed down while pressing the letter `o`{normal}). Repeat to go back - further. `<C-i>`{normal} goes forward. + 5. To go back to where you came from press `<C-o>`{normal}. + (keep `<Ctrl>`{normal} pressed down while pressing the letter `o`{normal}). + Repeat to go back further. `<C-i>`{normal} goes forward. "errroor" is not the way to spell error; errroor is an error. @@ -525,16 +518,14 @@ NOTE: This is very useful in debugging a program with unmatched parentheses! 2. Type ~~~ cmd - :s/thee/the/ + :s/thee/the/ ~~~ - - NOTE that the [:s](:s) command only changed the first occurrence of "thee" in the line. + NOTE: the [:s](:s) command only changed the first match of "thee" in the line. 3. Now type ~~~ cmd - :s/thee/the/g + :s/thee/the/g ~~~ - Adding the g [flag](:s_flags) means to substitute globally in the line, change all occurrences of "thee" in the line. @@ -542,20 +533,20 @@ Usually thee best time to see thee flowers is in thee spring. 4. To change every occurrence of a character string between two lines, type ~~~ cmd - :#,#s/old/new/g + :#,#s/old/new/g ~~~ where #,# are the line numbers of the range of lines where the substitution is to be done. Type ~~~ cmd - :%s/old/new/g + :%s/old/new/g ~~~ to change every occurrence in the whole file. Type ~~~ cmd - :%s/old/new/gc + :%s/old/new/gc ~~~ to find every occurrence in the whole file, with a prompt whether to substitute or not. @@ -564,7 +555,7 @@ Usually thee best time to see thee flowers is in thee spring. 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. @@ -643,7 +634,6 @@ NOTE: If you were to exit Vim and start it again with `nvim TEST`, the file ~~~ cmd :!rm TEST ~~~ - # Lesson 5.3: SELECTING TEXT TO WRITE ** To save part of the file, type `v`{normal} motion `:w FILENAME`{vim}. ** @@ -655,7 +645,7 @@ NOTE: If you were to exit Vim and start it again with `nvim TEST`, the file 3. Press the `:`{normal} character. At the bottom of the screen - :'<,'> + `:'<,'>`{vim} will appear. @@ -669,12 +659,12 @@ NOTE: If you were to exit Vim and start it again with `nvim TEST`, the file before you press `<Enter>`{normal}. - 5. Vim will write the selected lines to the file TEST. Use `:!ls`{vim} to see it. Do not remove it yet! We will use it in the next lesson. + 5. Vim will write the selected lines to the file TEST. Use `:!ls`{vim} to see it. + Do not remove it yet! We will use it in the next lesson. -NOTE: Pressing [v](v) starts [Visual selection](visual-mode). You can move - the cursor around to make the selection bigger or smaller. Then you can - use an operator to do something with the text. For example, `d`{normal} - deletes the text. +NOTE: Pressing [v](v) starts [Visual selection](visual-mode). You can move the cursor around to + make the selection bigger or smaller. Then you can use an operator to + do something with the text. For example, `d`{normal} deletes the text. # Lesson 5.4: RETRIEVING AND MERGING FILES @@ -689,8 +679,8 @@ NOTE: After executing Step 2 you will see text from Lesson 5.3. Then move `:r TEST`{vim} - where TEST is the name of the file you used. - The file you retrieve is placed below the cursor line. + where TEST is the name of the file you used. + The file you retrieve is placed below the cursor line. 3. To verify that a file was retrieved, cursor back and notice that there are now two copies of Lesson 5.3, the original and the file version. @@ -706,20 +696,20 @@ NOTE: You can also read the output of an external command. For example, 1. [:!command](:!cmd) executes an external command. Some useful examples are: - `:!ls`{vim} - shows a directory listing - `:!rm FILENAME`{vim} - removes file FILENAME + `:!ls`{vim} - shows a directory listing + `:!rm FILENAME`{vim} - removes file FILENAME - 2. [:w](:w) FILENAME writes the current Vim file to disk with - name FILENAME. + 2. [:w](:w) FILENAME writes the current Vim file to disk with + name FILENAME. 3. [v](v) motion :w FILENAME saves the Visually selected lines in file FILENAME. - 4. [:r](:r) FILENAME retrieves disk file FILENAME and puts it - below the cursor position. + 4. [:r](:r) FILENAME retrieves disk file FILENAME and puts it + below the cursor position. - 5. [:r !dir](:r!) reads the output of the dir command and - puts it below the cursor position. + 5. [:r !dir](:r!) reads the output of the dir command and + puts it below the cursor position. # Lesson 6.1: THE OPEN COMMAND @@ -747,14 +737,11 @@ Open up a line above this by typing O while the cursor is on this line. 2. Press `e`{normal} until the cursor is on the end of "li". - 3. Type the lowercase letter `a`{normal} to [append](a) text AFTER the - cursor. + 3. Type the lowercase letter `a`{normal} to [append](a) text AFTER the cursor. - 4. Complete the word like the line below it. Press `<Esc>`{normal} to exit - Insert mode. + 4. Complete the word like the line below it. Press `<Esc>`{normal} to exit Insert mode. - 5. Use `e`{normal} to move to the next incomplete word and repeat steps 3 - and 4. + 5. Use `e`{normal} to move to the next incomplete word and repeat steps 3 and 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. @@ -767,21 +754,21 @@ NOTE: [a](a), [i](i) and [A](A) all go to the same Insert mode, the only ** Type a capital `R`{normal} to replace more than one character. ** 1. Move the cursor to the first line below marked ✗. Move the cursor to - the beginning of the first "xxx". + the beginning of the first "xxx". 2. Now press `R`{normal} ([capital R](R)) and type the number below it in the - second line, so that it replaces the "xxx". + second line, so that it replaces the "xxx". 3. Press `<Esc>`{normal} to leave [Replace mode](mode-replace). Notice that - the rest of the line remains unmodified. + the rest of the line remains unmodified. 4. Repeat the steps to replace the remaining "xxx". Adding 123 to xxx gives you xxx. Adding 123 to 456 gives you 579. -NOTE: Replace mode is like Insert mode, but every typed character deletes an - existing character. +NOTE: Replace mode is like Insert mode, but every typed character + deletes an existing character. # Lesson 6.4: COPY AND PASTE TEXT @@ -875,17 +862,17 @@ NOTE: If you want to ignore case for just one search command, use [\c](/\c) ~~~ cmd :set invic ~~~ - # Lesson 7.1: GETTING HELP ** Use the on-line help system. ** -Vim has a comprehensive on-line help system. To get started, try one of -these three: - - press the `<HELP>`{normal} key (if you have one) - - press the `<F1>`{normal} key (if you have one) - - type - `:help`{vim} +Vim has a comprehensive on-line help system. + +To get started, try one of these three: + + - press the `<HELP>`{normal} key (if you have one) + - press the `<F1>`{normal} key (if you have one) + - type `:help`{vim} Read the text in the help window to find out how the help works. Type `<C-w><C-w>`{normal} to jump from one window to another. @@ -907,10 +894,12 @@ Vim has many more features than Vi, but most of them are disabled by default. To start using more features you have to create a "vimrc" file. 1. Start editing the "vimrc" file. + `:call mkdir(stdpath('config'),'p')`{vim} `:exe 'edit' stdpath('config').'/init.vim'`{vim} 2. Write the file with: + `:w`{vim} You can add all your preferred settings to this "vimrc" file. @@ -924,17 +913,15 @@ default. To start using more features you have to create a "vimrc" file. 2. Type the start of a command: `:e`{vim} - 3. Press `<C-d>`{normal} and Vim will show a list of commands that start - with "e". + 3. Press `<C-d>`{normal} and Vim will show a list of commands beginning with "e". 4. Press `<Tab>`{normal} and Vim will complete the command name to ":edit". 5. Now add a space and the start of an existing file name: `:edit FIL`{vim} - 6. Press `<Tab>`{normal}. Vim will complete the name (if it is unique). + 6. Press `<Tab>`{normal}. Vim will complete the name ("FIL" -> "FILE", if it is unique). -NOTE: Completion works for many commands. It is especially useful for - `:help`{vim}. +NOTE: Completion works for many commands. It is especially useful for `:help`{vim}. # Lesson 7 SUMMARY @@ -950,7 +937,7 @@ NOTE: Completion works for many commands. It is especially useful for 5. Create a vimrc startup script to keep your preferred settings. 6. While in command mode, press `<C-d>`{normal} to see possible completions. - Press `<Tab>`{normal} to use one completion. + Press `<Tab>`{normal} to use one completion. # CONCLUSION @@ -961,13 +948,20 @@ many many more commands. Consult the help often. There are many resources online to learn more about vim. Here's a bunch of them: -- *Learn Vim Progressively*: http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/ -- *Learning Vim in 2014*: http://benmccormick.org/learning-vim-in-2014/ -- *Vimcasts*: http://vimcasts.org/ -- *Vim Video-Tutorials by Derek Wyatt*: http://derekwyatt.org/vim/tutorials/ -- *Learn Vimscript the Hard Way*: http://learnvimscriptthehardway.stevelosh.com/ -- *7 Habits of Effective Text Editing*: http://www.moolenaar.net/habits.html -- *vim-galore*: https://github.com/mhinz/vim-galore +- *Learn Vim Progressively*: + http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/ +- *Learning Vim in 2013*: + http://benmccormick.org/learning-vim-in-2014/ +- *Vimcasts*: + http://vimcasts.org/ +- *Vim Video-Tutorials by Derek Wyatt*: + http://derekwyatt.org/vim/tutorials/ +- *Learn Vimscript the Hard Way*: + http://learnvimscriptthehardway.stevelosh.com/ +- *7 Habits of Effective Text Editing*: + http://www.moolenaar.net/habits.html +- *vim-galore*: + https://github.com/mhinz/vim-galore If you prefer a book, *Practical Vim* by Drew Neil is recommended often (the sequel, *Modern Vim*, includes material specific to nvim). @@ -978,3 +972,5 @@ University. E-mail: bware@mines.colorado.edu. Modified for Vim by Bram Moolenaar. Modified for vim-tutor-mode by Felipe Morales. + +// vim: nowrap diff --git a/runtime/tutor/en/vim-01-beginner.tutor.json b/runtime/tutor/en/vim-01-beginner.tutor.json index af22cf2aca..e71ead976d 100644 --- a/runtime/tutor/en/vim-01-beginner.tutor.json +++ b/runtime/tutor/en/vim-01-beginner.tutor.json @@ -1,45 +1,45 @@ { "expect": { - "24": -1, + "25": -1, "103": "The cow jumped over the moon.", - "124": "There is some text missing from this line.", "125": "There is some text missing from this line.", - "144": "There is some text missing from this line.", + "126": "There is some text missing from this line.", "145": "There is some text missing from this line.", - "146": "There is also some text missing here.", + "146": "There is some text missing from this line.", "147": "There is also some text missing here.", - "220": "There are some words that don't belong in this sentence.", - "236": "Somebody typed the end of this line twice.", - "276": -1, - "295": "This line of words is cleaned up.", + "148": "There is also some text missing here.", + "216": "There are some words that don't belong in this sentence.", + "232": "Somebody typed the end of this line twice.", + "271": -1, + "290": "This line of words is cleaned up.", + "304": -1, + "305": -1, + "306": -1, + "307": -1, + "308": -1, "309": -1, "310": -1, - "311": -1, - "312": -1, - "313": -1, - "314": -1, - "315": -1, - "332": "Fix the errors on this line and replace them with undo.", - "372": -1, - "373": -1, - "374": -1, - "375": -1, - "389": "When this line was typed in, someone pressed some wrong keys!", - "390": "When this line was typed in, someone pressed some wrong keys!", - "411": "This line has a few words that need changing using the change operator.", - "412": "This line has a few words that need changing using the change operator.", - "432": "The end of this line needs to be corrected using the `c$` command.", - "433": "The end of this line needs to be corrected using the `c$` command.", - "497": -1, - "516": -1, - "541": "Usually the best time to see the flowers is in the spring.", - "735": -1, - "740": -1, - "759": "This line will allow you to practice appending text to a line.", - "760": "This line will allow you to practice appending text to a line.", - "780": "Adding 123 to 456 gives you 579.", - "781": "Adding 123 to 456 gives you 579.", - "807": "a) This is the first item.", - "808": "b) This is the second item." + "324": "Fix the errors on this line and replace them with undo.", + "367": -1, + "368": -1, + "369": -1, + "370": -1, + "384": "When this line was typed in, someone pressed some wrong keys!", + "385": "When this line was typed in, someone pressed some wrong keys!", + "405": "This line has a few words that need changing using the change operator.", + "406": "This line has a few words that need changing using the change operator.", + "426": "The end of this line needs to be corrected using the `c$` command.", + "427": "The end of this line needs to be corrected using the `c$` command.", + "490": -1, + "509": -1, + "532": "Usually the best time to see the flowers is in the spring.", + "725": -1, + "730": -1, + "746": "This line will allow you to practice appending text to a line.", + "747": "This line will allow you to practice appending text to a line.", + "767": "Adding 123 to 456 gives you 579.", + "768": "Adding 123 to 456 gives you 579.", + "794": "a) This is the first item.", + "795": "b) This is the second item." } } diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index d46306d41a..320c44e860 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -154,6 +154,7 @@ CONFIG = { 'lsp.lua', 'buf.lua', 'diagnostic.lua', + 'codelens.lua', 'handlers.lua', 'util.lua', 'log.lua', @@ -196,7 +197,6 @@ CONFIG = { 'query.lua', 'highlighter.lua', 'languagetree.lua', - 'health.lua', ], 'files': ' '.join([ os.path.join(base_dir, 'runtime/lua/vim/treesitter.lua'), @@ -949,6 +949,8 @@ def main(config, args): os.remove(mpack_file) output_dir = out_dir.format(target=target) + log.info("Generating documentation for %s in folder %s", + target, output_dir) debug = args.log_level >= logging.DEBUG p = subprocess.Popen( ['doxygen', '-'], @@ -1104,7 +1106,8 @@ def filter_source(filename): def parse_args(): targets = ', '.join(CONFIG.keys()) - ap = argparse.ArgumentParser() + ap = argparse.ArgumentParser( + description="Generate helpdoc from source code") ap.add_argument( "--log-level", "-l", choices=LOG_LEVELS.keys(), default=logging.getLevelName(logging.ERROR), help="Set log verbosity" @@ -1127,7 +1130,7 @@ Doxyfile = textwrap.dedent(''' INPUT_FILTER = "{filter}" EXCLUDE = EXCLUDE_SYMLINKS = NO - EXCLUDE_PATTERNS = */private/* + EXCLUDE_PATTERNS = */private/* */health.lua EXCLUDE_SYMBOLS = EXTENSION_MAPPING = lua=C EXTRACT_PRIVATE = NO @@ -1158,6 +1161,7 @@ if __name__ == "__main__": print("Setting log level to %s" % args.log_level) args.log_level = LOG_LEVELS[args.log_level] log.setLevel(args.log_level) + log.addHandler(logging.StreamHandler()) if len(args.source_filter) > 0: filter_source(args.source_filter[0]) diff --git a/scripts/genvimvim.lua b/scripts/genvimvim.lua index ccd5489fdc..2c3701bf0c 100644 --- a/scripts/genvimvim.lua +++ b/scripts/genvimvim.lua @@ -123,7 +123,7 @@ end w('\n\nsyn case match') local vimfun_start = 'syn keyword vimFuncName contained ' w('\n\n' .. vimfun_start) -funcs = mpack.unpack(io.open(funcs_file):read("*all")) +funcs = mpack.unpack(io.open(funcs_file, 'rb'):read("*all")) local started = 0 for name, def in pairs(funcs) do if name then diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh index f3371b485e..aa27c94f29 100755 --- a/scripts/pvscheck.sh +++ b/scripts/pvscheck.sh @@ -379,7 +379,7 @@ run_analysis() {( --sourcetree-root . || true rm -rf PVS-studio.{xml,err,tsk,html.d} - local plog_args="PVS-studio.log --srcRoot . --excludedCodes V011" + local plog_args="PVS-studio.log --srcRoot . --excludedCodes V011,V1042" plog-converter $plog_args --renderTypes xml --output PVS-studio.xml plog-converter $plog_args --renderTypes errorfile --output PVS-studio.err plog-converter $plog_args --renderTypes tasklist --output PVS-studio.tsk diff --git a/scripts/release.sh b/scripts/release.sh index 5b4902a2d7..4ec959d697 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -12,6 +12,7 @@ # - CMakeLists.txt: Unset NVIM_VERSION_PRERELEASE # - CMakeLists.txt: Unset NVIM_API_PRERELEASE # - Create test/functional/fixtures/api_level_N.mpack +# - Add date and version to runtime/nvim.appdata.xml # - Tag the commit. # Create the "version bump" commit: # - CMakeLists.txt: Set NVIM_VERSION_PRERELEASE to "-dev" @@ -62,12 +63,15 @@ _do_release_commit() { git add test/functional/fixtures/api_level_$__API_LEVEL.mpack fi + $__sed -i.bk 's,(<releases>),\1\ + <release date="'"${__DATE}"'" version="'"${__VERSION}"'"/>,' runtime/nvim.appdata.xml + git add runtime/nvim.appdata.xml + if ! test "$ARG1" = '--use-current-commit' ; then echo "Building changelog since ${__LAST_TAG}..." - __CHANGELOG="$(./scripts/git-log-pretty-since.sh "$__LAST_TAG" 'vim-patch:[^[:space:]]')" git add CMakeLists.txt - git commit --edit -m "${__RELEASE_MSG} ${__CHANGELOG}" + (echo "${__RELEASE_MSG}"; ./scripts/git-log-pretty-since.sh "$__LAST_TAG" 'vim-patch:[^[:space:]]') | git commit --edit -F - fi git tag --sign -a v"${__VERSION}" -m "NVIM v${__VERSION}" @@ -76,14 +80,12 @@ _do_release_commit() { _do_bump_commit() { $__sed -i.bk 's/(NVIM_VERSION_PRERELEASE) ""/\1 "-dev"/' CMakeLists.txt $__sed -i.bk 's/set\((NVIM_VERSION_PATCH) [[:digit:]]/set(\1 ?/' CMakeLists.txt - $__sed -i.bk 's,(<releases>),\1\ - <release date="'"${__DATE}"'" version="xxx"/>,' runtime/nvim.appdata.xml rm CMakeLists.txt.bk rm runtime/nvim.appdata.xml.bk nvim +'/NVIM_VERSION' +1new +'exe "norm! iUpdate version numbers!!!"' \ - -O CMakeLists.txt runtime/nvim.appdata.xml + -O CMakeLists.txt - git add CMakeLists.txt runtime/nvim.appdata.xml + git add CMakeLists.txt git commit -m "$__BUMP_MSG" } @@ -93,11 +95,7 @@ fi _do_bump_commit echo " Next steps: - - Update runtime/nvim.appdata.xml on _master_ - Run tests/CI (version_spec.lua)! - Push the tag: git push --follow-tags - - Update the 'stable' tag: - git push --force upstream HEAD^:refs/tags/stable - git fetch --tags - Update website: index.html" diff --git a/scripts/squash_typos.py b/scripts/squash_typos.py new file mode 100644 index 0000000000..26be6010a2 --- /dev/null +++ b/scripts/squash_typos.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +""" + +This script squashes a PR tagged with the "typo" label into a single, dedicated +"squash PR". + +""" + +import subprocess +import sys +import os + + +def get_authors_and_emails_from_pr(): + """ + + Return all contributing authors and their emails for the PR on current branch. + This includes co-authors, meaning that if two authors are credited for a + single commit, which is possible with GitHub, then both will get credited. + + """ + + # Get a list of all authors involved in the pull request (including co-authors). + authors = subprocess.check_output( + ["gh", "pr", "view", "--json", "commits", "--jq", ".[][].authors.[].name"], + text=True, + ).splitlines() + + # Get a list of emails of the aforementioned authors. + emails = subprocess.check_output( + ["gh", "pr", "view", "--json", "commits", "--jq", ".[][].authors.[].email"], + text=True, + ).splitlines() + + authors_and_emails_unique = { + (author, mail) for author, mail in zip(authors, emails) + } + + return sorted(authors_and_emails_unique) + + +def rebase_squash_branch_onto_pr(): + """ + + Rebase current branch onto the PR. + + """ + + # Check out the pull request. + subprocess.call(["gh", "pr", "checkout", os.environ["PR_NUMBER"]]) + + # Rebase onto master + default_branch = f"{os.environ['GITHUB_BASE_REF']}" + subprocess.check_call(["git", "rebase", default_branch]) + + # Change back to the original branch. + subprocess.call(["git", "switch", "-"]) + + # Rebase onto the pull request, aka include the commits in the pull request + # in the current branch. Abort with error message if rebase fails. + + try: + subprocess.check_call(["git", "rebase", "-"]) + except subprocess.CalledProcessError: + subprocess.call(["git", "rebase", "--abort"]) + squash_url = subprocess.check_output( + ["gh", "pr", "view", "--json", "url", "--jq", ".url"], text=True + ).strip() + + subprocess.call( + [ + "gh", + "pr", + "comment", + os.environ["PR_NUMBER"], + "--body", + f"Your edit conflicts with an already scheduled fix \ + ({squash_url}). Please check that batch PR whether your fix is \ + already included; if not, then please wait until the batch PR \ + is merged and then rebase your PR on top of master.", + ] + ) + + sys.exit( + f"\n\nERROR: Your edit conflicts with an already scheduled fix \ +{squash_url} \n\n" + ) + + +def rebase_squash_branch_onto_master(): + """ + + Rebase current branch onto the master i.e. make sure current branch is up + to date. Abort on error. + + """ + + default_branch = f"{os.environ['GITHUB_BASE_REF']}" + subprocess.check_call(["git", "rebase", default_branch]) + + +def squash_all_commits(): + """ + + Squash all commits on the PR into a single commit. Credit all authors by + name and email. + + """ + + default_branch = f"{os.environ['GITHUB_BASE_REF']}" + subprocess.call(["git", "reset", "--soft", default_branch]) + + authors_and_emails = get_authors_and_emails_from_pr() + commit_message_coauthors = "\n" + "\n".join( + [f"Co-authored-by: {i[0]} <{i[1]}>" for i in authors_and_emails] + ) + subprocess.call( + ["git", "commit", "-m", "chore: typo fixes", "-m", commit_message_coauthors] + ) + + +def force_push(branch): + """ + + Like the name implies, force push <branch>. + + """ + + gh_actor = os.environ["GITHUB_ACTOR"] + gh_token = os.environ["GITHUB_TOKEN"] + gh_repo = os.environ["GITHUB_REPOSITORY"] + subprocess.call( + [ + "git", + "push", + "--force", + f"https://{gh_actor}:{gh_token}@github.com/{gh_repo}", + branch, + ] + ) + + +def checkout_branch(branch): + """ + + Create and checkout <branch>. Check if branch exists on remote, if so then + sync local branch to remote. + + Return True if remote branch exists, else False. + + """ + + # FIXME I'm not sure why the local branch isn't tracking the remote branch + # automatically. This works but I'm pretty sure it can be done in a more + # "elegant" fashion + + show_ref_output = subprocess.check_output(["git", "show-ref"], text=True).strip() + + if branch in show_ref_output: + subprocess.call(["git", "checkout", "-b", branch, f"origin/{branch}"]) + return True + + subprocess.call(["git", "checkout", "-b", branch]) + return False + + +def get_all_pr_urls(squash_branch_exists): + """ + + Return a list of URLs for the pull requests with the typo fixes. If a + squash branch exists then extract the URLs from the body text. + + """ + + all_pr_urls = "" + if squash_branch_exists: + all_pr_urls += subprocess.check_output( + ["gh", "pr", "view", "--json", "body", "--jq", ".body"], text=True + ) + + all_pr_urls += subprocess.check_output( + ["gh", "pr", "view", os.environ["PR_NUMBER"], "--json", "url", "--jq", ".url"], + text=True, + ).strip() + + return all_pr_urls + + +def main(): + squash_branch = "marvim/squash-typos" + + squash_branch_exists = checkout_branch(squash_branch) + + rebase_squash_branch_onto_master() + force_push(squash_branch) + + rebase_squash_branch_onto_pr() + force_push(squash_branch) + + subprocess.call( + [ + "gh", + "pr", + "create", + "--fill", + "--head", + squash_branch, + "--title", + "chore: typo fixes (automated)", + ] + ) + + squash_all_commits() + force_push(squash_branch) + + all_pr_urls = get_all_pr_urls(squash_branch_exists) + subprocess.call(["gh", "pr", "edit", "--add-label", "typo", "--body", all_pr_urls]) + + subprocess.call(["gh", "pr", "close", os.environ["PR_NUMBER"]]) + + +if __name__ == "__main__": + main() diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 86552c0c8d..f4b817dfff 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -14,7 +14,8 @@ fi readonly NVIM_SOURCE_DIR="${NVIM_SOURCE_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" readonly VIM_SOURCE_DIR_DEFAULT="${NVIM_SOURCE_DIR}/.vim-src" readonly VIM_SOURCE_DIR="${VIM_SOURCE_DIR:-${VIM_SOURCE_DIR_DEFAULT}}" -readonly BASENAME="$(basename "${0}")" +BASENAME="$(basename "${0}")" +readonly BASENAME readonly BRANCH_PREFIX="vim-" CREATED_FILES=() @@ -189,7 +190,7 @@ preprocess_patch() { 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\S*\<\%(testdir/\)\@<!\%('"${na_src}"'\)@norm! d/\v(^diff)|%$
' +w +q "$file" # Remove unwanted Vim doc files. - local na_doc='channel\.txt\|netbeans\.txt\|os_\w\+\.txt\|term\.txt\|todo\.txt\|version\d\.txt\|sponsor\.txt\|intro\.txt\|tags' + local na_doc='channel\.txt\|netbeans\.txt\|os_\w\+\.txt\|term\.txt\|todo\.txt\|version\d\.txt\|vim9\.txt\|sponsor\.txt\|intro\.txt\|tags' 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/runtime/doc/\<\%('"${na_doc}"'\)\>@norm! d/\v(^diff)|%$
' +w +q "$file" # Remove "Last change ..." changes in doc files. @@ -325,12 +326,14 @@ stage_patch() { return $ret } -hub_pr() { - hub pull-request -m "$1" +gh_pr() { + gh pr create --title "$1" --body "$2" } git_hub_pr() { - git hub pull new -m "$1" + local pr_message + pr_message="$(printf '%s\n\n%s\n' "$1" "$2")" + git hub pull new -m "${pr_message}" } # shellcheck disable=SC2015 @@ -340,14 +343,14 @@ submit_pr() { local push_first push_first=1 local submit_fn - if check_executable hub; then - submit_fn="hub_pr" + if check_executable gh; then + submit_fn="gh_pr" elif check_executable git-hub; then push_first=0 submit_fn="git_hub_pr" else - >&2 echo "${BASENAME}: 'hub' or 'git-hub' not found in PATH or not executable." - >&2 echo " Get it here: https://hub.github.com/" + >&2 echo "${BASENAME}: 'gh' or 'git-hub' not found in PATH or not executable." + >&2 echo " Get it here: https://cli.github.com/" exit 1 fi @@ -370,9 +373,7 @@ submit_pr() { patches=(${patches[@]//vim-patch:}) # Remove 'vim-patch:' prefix for each item in array. local pr_title="${patches[*]}" # Create space-separated string from array. pr_title="${pr_title// /,}" # Replace spaces with commas. - - local pr_message - pr_message="$(printf '[RFC] vim-patch:%s\n\n%s\n' "${pr_title#,}" "${pr_body}")" + pr_title="$(printf 'vim-patch:%s' "${pr_title#,}")" if [[ $push_first -ne 0 ]]; then echo "Pushing to 'origin/${checked_out_branch}'." @@ -384,7 +385,7 @@ submit_pr() { fi echo "Creating pull request." - output="$(${submit_fn} "${pr_message}" 2>&1)" && + output="$(${submit_fn} "${pr_title}" "${pr_body}" 2>&1)" && msg_ok "${output}" || (msg_err "${output}"; false) diff --git a/src/clint.py b/src/clint.py index 9b4128a0c9..e7d76366b0 100755 --- a/src/clint.py +++ b/src/clint.py @@ -68,7 +68,7 @@ Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] <file> [file] ... The style guidelines this tries to follow are those in - http://neovim.io/development-wiki/style-guide/style-guide.xml + http://neovim.io/develop/style-guide.xml Note: This is Google's cpplint.py modified for use with the Neovim project, which follows the Google C++ coding convention except with the following @@ -264,7 +264,7 @@ _error_suppressions_2 = set() # The allowed line length of files. # This is set by --linelength flag. -_line_length = 80 +_line_length = 100 # The allowed extensions for file names # This is set by --extensions flag. diff --git a/src/mpack/LICENSE-MIT b/src/mpack/LICENSE-MIT new file mode 100644 index 0000000000..030ba872c5 --- /dev/null +++ b/src/mpack/LICENSE-MIT @@ -0,0 +1,22 @@ +Copyright (c) 2016 Thiago de Arruda + +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. diff --git a/src/mpack/conv.c b/src/mpack/conv.c new file mode 100644 index 0000000000..203b13fadb --- /dev/null +++ b/src/mpack/conv.c @@ -0,0 +1,375 @@ +#include "conv.h" + +static int mpack_fits_single(double v); +static mpack_value_t mpack_pack_ieee754(double v, unsigned m, unsigned e); +static int mpack_is_be(void) FPURE; +static double mpack_fmod_pow2_32(double a); + + +#define POW2(n) \ + ((double)(1 << (n / 2)) * (double)(1 << (n / 2)) * (double)(1 << (n % 2))) + +#define MPACK_SWAP_VALUE(val) \ + do { \ + mpack_uint32_t lo = val.lo; \ + val.lo = val.hi; \ + val.hi = lo; \ + } while (0) + +MPACK_API mpack_token_t mpack_pack_nil(void) +{ + mpack_token_t rv; + rv.type = MPACK_TOKEN_NIL; + return rv; +} + +MPACK_API mpack_token_t mpack_pack_boolean(unsigned v) +{ + mpack_token_t rv; + rv.type = MPACK_TOKEN_BOOLEAN; + rv.data.value.lo = v ? 1 : 0; + rv.data.value.hi = 0; + return rv; +} + +MPACK_API mpack_token_t mpack_pack_uint(mpack_uintmax_t v) +{ + mpack_token_t rv; + rv.data.value.lo = v & 0xffffffff; + rv.data.value.hi = (mpack_uint32_t)((v >> 31) >> 1); + rv.type = MPACK_TOKEN_UINT; + return rv; +} + +MPACK_API mpack_token_t mpack_pack_sint(mpack_sintmax_t v) +{ + if (v < 0) { + mpack_token_t rv; + mpack_uintmax_t tc = -((mpack_uintmax_t)(v + 1)) + 1; + tc = ~tc + 1; + rv = mpack_pack_uint(tc); + rv.type = MPACK_TOKEN_SINT; + return rv; + } + + return mpack_pack_uint((mpack_uintmax_t)v); +} + +MPACK_API mpack_token_t mpack_pack_float_compat(double v) +{ + /* ieee754 single-precision limits to determine if "v" can be fully + * represented in 4 bytes */ + mpack_token_t rv; + + if (mpack_fits_single(v)) { + rv.length = 4; + rv.data.value = mpack_pack_ieee754(v, 23, 8); + } else { + rv.length = 8; + rv.data.value = mpack_pack_ieee754(v, 52, 11); + } + + rv.type = MPACK_TOKEN_FLOAT; + return rv; +} + +MPACK_API mpack_token_t mpack_pack_float_fast(double v) +{ + /* ieee754 single-precision limits to determine if "v" can be fully + * represented in 4 bytes */ + mpack_token_t rv; + + if (mpack_fits_single(v)) { + union { + float f; + mpack_uint32_t m; + } conv; + conv.f = (float)v; + rv.length = 4; + rv.data.value.lo = conv.m; + rv.data.value.hi = 0; + } else { + union { + double d; + mpack_value_t m; + } conv; + conv.d = v; + rv.length = 8; + rv.data.value = conv.m; + if (mpack_is_be()) { + MPACK_SWAP_VALUE(rv.data.value); + } + } + + rv.type = MPACK_TOKEN_FLOAT; + return rv; +} + +MPACK_API mpack_token_t mpack_pack_number(double v) +{ + mpack_token_t tok; + double vabs; + vabs = v < 0 ? -v : v; + assert(v <= 9007199254740991. && v >= -9007199254740991.); + tok.data.value.hi = (mpack_uint32_t)(vabs / POW2(32)); + tok.data.value.lo = (mpack_uint32_t)mpack_fmod_pow2_32(vabs); + + if (v < 0) { + /* Compute the two's complement */ + tok.type = MPACK_TOKEN_SINT; + tok.data.value.hi = ~tok.data.value.hi; + tok.data.value.lo = ~tok.data.value.lo + 1; + if (!tok.data.value.lo) tok.data.value.hi++; + if (tok.data.value.lo == 0 && tok.data.value.hi == 0) tok.length = 1; + else if (tok.data.value.lo < 0x80000000) tok.length = 8; + else if (tok.data.value.lo < 0xffff7fff) tok.length = 4; + else if (tok.data.value.lo < 0xffffff7f) tok.length = 2; + else tok.length = 1; + } else { + tok.type = MPACK_TOKEN_UINT; + if (tok.data.value.hi) tok.length = 8; + else if (tok.data.value.lo > 0xffff) tok.length = 4; + else if (tok.data.value.lo > 0xff) tok.length = 2; + else tok.length = 1; + } + + if (mpack_unpack_number(tok) != v) { + return mpack_pack_float(v); + } + + return tok; +} + +MPACK_API mpack_token_t mpack_pack_chunk(const char *p, mpack_uint32_t l) +{ + mpack_token_t rv; + rv.type = MPACK_TOKEN_CHUNK; + rv.data.chunk_ptr = p; + rv.length = l; + return rv; +} + +MPACK_API mpack_token_t mpack_pack_str(mpack_uint32_t l) +{ + mpack_token_t rv; + rv.type = MPACK_TOKEN_STR; + rv.length = l; + return rv; +} + +MPACK_API mpack_token_t mpack_pack_bin(mpack_uint32_t l) +{ + mpack_token_t rv; + rv.type = MPACK_TOKEN_BIN; + rv.length = l; + return rv; +} + +MPACK_API mpack_token_t mpack_pack_ext(int t, mpack_uint32_t l) +{ + mpack_token_t rv; + rv.type = MPACK_TOKEN_EXT; + rv.length = l; + rv.data.ext_type = t; + return rv; +} + +MPACK_API mpack_token_t mpack_pack_array(mpack_uint32_t l) +{ + mpack_token_t rv; + rv.type = MPACK_TOKEN_ARRAY; + rv.length = l; + return rv; +} + +MPACK_API mpack_token_t mpack_pack_map(mpack_uint32_t l) +{ + mpack_token_t rv; + rv.type = MPACK_TOKEN_MAP; + rv.length = l; + return rv; +} + +MPACK_API bool mpack_unpack_boolean(mpack_token_t t) +{ + return t.data.value.lo || t.data.value.hi; +} + +MPACK_API mpack_uintmax_t mpack_unpack_uint(mpack_token_t t) +{ + return (((mpack_uintmax_t)t.data.value.hi << 31) << 1) | t.data.value.lo; +} + +/* unpack signed integer without relying on two's complement as internal + * representation */ +MPACK_API mpack_sintmax_t mpack_unpack_sint(mpack_token_t t) +{ + mpack_uint32_t hi = t.data.value.hi; + mpack_uint32_t lo = t.data.value.lo; + mpack_uintmax_t rv = lo; + assert(t.length <= sizeof(mpack_sintmax_t)); + + if (t.length == 8) { + rv |= (((mpack_uintmax_t)hi) << 31) << 1; + } + /* reverse the two's complement so that lo/hi contain the absolute value. + * note that we have to mask ~rv so that it reflects the two's complement + * of the appropriate byte length */ + rv = (~rv & (((mpack_uintmax_t)1 << ((t.length * 8) - 1)) - 1)) + 1; + /* negate and return the absolute value, making sure mpack_sintmax_t can + * represent the positive cast. */ + return -((mpack_sintmax_t)(rv - 1)) - 1; +} + +MPACK_API double mpack_unpack_float_compat(mpack_token_t t) +{ + mpack_uint32_t sign; + mpack_sint32_t exponent, bias; + unsigned mantbits; + unsigned expbits; + double mant; + + if (t.data.value.lo == 0 && t.data.value.hi == 0) + /* nothing to do */ + return 0; + + if (t.length == 4) mantbits = 23, expbits = 8; + else mantbits = 52, expbits = 11; + bias = (1 << (expbits - 1)) - 1; + + /* restore sign/exponent/mantissa */ + if (mantbits == 52) { + sign = t.data.value.hi >> 31; + exponent = (t.data.value.hi >> 20) & ((1 << 11) - 1); + mant = (t.data.value.hi & ((1 << 20) - 1)) * POW2(32); + mant += t.data.value.lo; + } else { + sign = t.data.value.lo >> 31; + exponent = (t.data.value.lo >> 23) & ((1 << 8) - 1); + mant = t.data.value.lo & ((1 << 23) - 1); + } + + mant /= POW2(mantbits); + if (exponent) mant += 1.0; /* restore leading 1 */ + else exponent = 1; /* subnormal */ + exponent -= bias; + + /* restore original value */ + while (exponent > 0) mant *= 2.0, exponent--; + while (exponent < 0) mant /= 2.0, exponent++; + return mant * (sign ? -1 : 1); +} + +MPACK_API double mpack_unpack_float_fast(mpack_token_t t) +{ + if (t.length == 4) { + union { + float f; + mpack_uint32_t m; + } conv; + conv.m = t.data.value.lo; + return conv.f; + } else { + union { + double d; + mpack_value_t m; + } conv; + conv.m = t.data.value; + + if (mpack_is_be()) { + MPACK_SWAP_VALUE(conv.m); + } + + return conv.d; + } +} + +MPACK_API double mpack_unpack_number(mpack_token_t t) +{ + double rv; + mpack_uint32_t hi, lo; + if (t.type == MPACK_TOKEN_FLOAT) return mpack_unpack_float(t); + assert(t.type == MPACK_TOKEN_UINT || t.type == MPACK_TOKEN_SINT); + hi = t.data.value.hi; + lo = t.data.value.lo; + if (t.type == MPACK_TOKEN_SINT) { + /* same idea as mpack_unpack_sint, except here we shouldn't rely on + * mpack_uintmax_t having 64-bits, operating on the 32-bit words separately. + */ + if (!hi) { + assert(t.length <= 4); + hi = 0; + lo = (~lo & (((mpack_uint32_t)1 << ((t.length * 8) - 1)) - 1)); + } else { + hi = ~hi; + lo = ~lo; + } + lo++; + if (!lo) hi++; + } + rv = (double)lo + POW2(32) * hi; + return t.type == MPACK_TOKEN_SINT ? -rv : rv; +} + +static int mpack_fits_single(double v) +{ + return (float)v == v; +} + +static mpack_value_t mpack_pack_ieee754(double v, unsigned mantbits, + unsigned expbits) +{ + mpack_value_t rv = {0, 0}; + mpack_sint32_t exponent, bias = (1 << (expbits - 1)) - 1; + mpack_uint32_t sign; + double mant; + + if (v == 0) { + rv.lo = 0; + rv.hi = 0; + goto end; + } + + if (v < 0) sign = 1, mant = -v; + else sign = 0, mant = v; + + exponent = 0; + while (mant >= 2.0) mant /= 2.0, exponent++; + while (mant < 1.0 && exponent > -(bias - 1)) mant *= 2.0, exponent--; + + if (mant < 1.0) exponent = -bias; /* subnormal value */ + else mant = mant - 1.0; /* remove leading 1 */ + exponent += bias; + mant *= POW2(mantbits); + + if (mantbits == 52) { + rv.hi = (mpack_uint32_t)(mant / POW2(32)); + rv.lo = (mpack_uint32_t)(mant - rv.hi * POW2(32)); + rv.hi |= ((mpack_uint32_t)exponent << 20) | (sign << 31); + } else if (mantbits == 23) { + rv.hi = 0; + rv.lo = (mpack_uint32_t)mant; + rv.lo |= ((mpack_uint32_t)exponent << 23) | (sign << 31); + } + +end: + return rv; +} + +static int mpack_is_be(void) +{ + union { + mpack_uint32_t i; + char c[sizeof(mpack_uint32_t)]; + } test; + + test.i = 1; + return test.c[0] == 0; +} + +/* this simplified version of `fmod` that returns the remainder of double + * division by 0xffffffff, which is enough for our purposes */ +static double mpack_fmod_pow2_32(double a) +{ + return a - ((double)(mpack_uint32_t)(a / POW2(32)) * POW2(32)); +} diff --git a/src/mpack/conv.h b/src/mpack/conv.h new file mode 100644 index 0000000000..71f14a067e --- /dev/null +++ b/src/mpack/conv.h @@ -0,0 +1,55 @@ +#ifndef MPACK_CONV_H +#define MPACK_CONV_H + +#include "mpack_core.h" + +#if ULLONG_MAX == 0xffffffffffffffff +typedef long long mpack_sintmax_t; +typedef unsigned long long mpack_uintmax_t; +#elif UINT64_MAX == 0xffffffffffffffff +typedef int64_t mpack_sintmax_t; +typedef uint64_t mpack_uintmax_t; +#else +typedef mpack_sint32_t mpack_sintmax_t; +typedef mpack_uint32_t mpack_uintmax_t; +#endif + +#ifndef bool +# define bool unsigned +#endif + +MPACK_API mpack_token_t mpack_pack_nil(void) FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_boolean(unsigned v) FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_uint(mpack_uintmax_t v) FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_sint(mpack_sintmax_t v) FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_float_compat(double v) FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_float_fast(double v) FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_number(double v) FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_chunk(const char *p, mpack_uint32_t l) + FUNUSED FPURE FNONULL; +MPACK_API mpack_token_t mpack_pack_str(mpack_uint32_t l) FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_bin(mpack_uint32_t l) FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_ext(int type, mpack_uint32_t l) + FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_array(mpack_uint32_t l) FUNUSED FPURE; +MPACK_API mpack_token_t mpack_pack_map(mpack_uint32_t l) FUNUSED FPURE; +MPACK_API bool mpack_unpack_boolean(mpack_token_t t) FUNUSED FPURE; +MPACK_API mpack_uintmax_t mpack_unpack_uint(mpack_token_t t) FUNUSED FPURE; +MPACK_API mpack_sintmax_t mpack_unpack_sint(mpack_token_t t) FUNUSED FPURE; +MPACK_API double mpack_unpack_float_fast(mpack_token_t t) FUNUSED FPURE; +MPACK_API double mpack_unpack_float_compat(mpack_token_t t) FUNUSED FPURE; +MPACK_API double mpack_unpack_number(mpack_token_t t) FUNUSED FPURE; + +/* The mpack_{pack,unpack}_float_fast functions should work in 99% of the + * platforms. When compiling for a platform where floats don't use ieee754 as + * the internal format, pass + * -Dmpack_{pack,unpack}_float=mpack_{pack,unpack}_float_compat to the + * compiler.*/ +#ifndef mpack_pack_float +# define mpack_pack_float mpack_pack_float_fast +#endif +#ifndef mpack_unpack_float +# define mpack_unpack_float mpack_unpack_float_fast +#endif + +#endif /* MPACK_CONV_H */ diff --git a/src/mpack/lmpack.c b/src/mpack/lmpack.c new file mode 100644 index 0000000000..99207246c8 --- /dev/null +++ b/src/mpack/lmpack.c @@ -0,0 +1,1215 @@ +/* + * This module exports three classes, and each instance of those classes has its + * own private registry for temporary reference storage(keeping state between + * calls). A private registry makes managing memory much easier since all we + * have to do is call luaL_unref passing the registry reference when the + * instance is collected by the __gc metamethod. + * + * This private registry is manipulated with `lmpack_ref` / `lmpack_unref` / + * `lmpack_geti`, which are analogous to `luaL_ref` / `luaL_unref` / + * `lua_rawgeti` but operate on the private registry passed as argument. + * + * In order to simplify debug registry leaks during normal operation(with the + * leak_test.lua script), these `lmpack_*` registry functions will target the + * normal lua registry when MPACK_DEBUG_REGISTRY_LEAK is defined during + * compilation. + */ +#define LUA_LIB +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <lauxlib.h> +#include <lua.h> +#include <luaconf.h> + +#include "nvim/macros.h" + +#include "lmpack.h" + +#include "rpc.h" + +#define UNPACKER_META_NAME "mpack.Unpacker" +#define PACKER_META_NAME "mpack.Packer" +#define SESSION_META_NAME "mpack.Session" +#define NIL_NAME "mpack.NIL" +#define EMPTY_DICT_NAME "mpack.empty_dict" + +/* + * TODO(tarruda): When targeting lua 5.3 and being compiled with `long long` + * support(not -ansi), we should make use of lua 64 bit integers for + * representing msgpack integers, since `double` can't represent the full range. + */ + +#ifndef luaL_reg +/* Taken from Lua5.1's lauxlib.h */ +#define luaL_reg luaL_Reg +#endif + +#if LUA_VERSION_NUM > 501 +#ifndef luaL_register +#define luaL_register(L,n,f) luaL_setfuncs(L,f,0) +#endif +#endif + +typedef struct { + lua_State *L; + mpack_parser_t *parser; + int reg, ext, unpacking, mtdict; + char *string_buffer; +} Unpacker; + +typedef struct { + lua_State *L; + mpack_parser_t *parser; + int reg, ext, root, packing, mtdict; + int is_bin, is_bin_fn; +} Packer; + +typedef struct { + lua_State *L; + int reg; + mpack_rpc_session_t *session; + struct { + int type; + mpack_rpc_message_t msg; + int method_or_error; + int args_or_result; + } unpacked; + int unpacker; +} Session; + +static int lmpack_ref(lua_State *L, int reg) +{ +#ifdef MPACK_DEBUG_REGISTRY_LEAK + return luaL_ref(L, LUA_REGISTRYINDEX); +#else + int rv; + lua_rawgeti(L, LUA_REGISTRYINDEX, reg); + lua_pushvalue(L, -2); + rv = luaL_ref(L, -2); + lua_pop(L, 2); + return rv; +#endif +} + +static void lmpack_unref(lua_State *L, int reg, int ref) +{ +#ifdef MPACK_DEBUG_REGISTRY_LEAK + luaL_unref(L, LUA_REGISTRYINDEX, ref); +#else + lua_rawgeti(L, LUA_REGISTRYINDEX, reg); + luaL_unref(L, -1, ref); + lua_pop(L, 1); +#endif +} + +static void lmpack_geti(lua_State *L, int reg, int ref) +{ +#ifdef MPACK_DEBUG_REGISTRY_LEAK + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); +#else + lua_rawgeti(L, LUA_REGISTRYINDEX, reg); + lua_rawgeti(L, -1, ref); + lua_replace(L, -2); +#endif +} + +/* make a shallow copy of the table on stack and remove it after the copy is + * done */ +static void lmpack_shallow_copy(lua_State *L) +{ + lua_newtable(L); + lua_pushnil(L); + while (lua_next(L, -3)) { + lua_pushvalue(L, -2); + lua_insert(L, -2); + lua_settable(L, -4); + } + lua_remove(L, -2); +} + +static mpack_parser_t *lmpack_grow_parser(mpack_parser_t *parser) +{ + mpack_parser_t *old = parser; + mpack_uint32_t new_capacity = old->capacity * 2; + parser = malloc(MPACK_PARSER_STRUCT_SIZE(new_capacity)); + if (!parser) goto end; + mpack_parser_init(parser, new_capacity); + mpack_parser_copy(parser, old); + free(old); +end: + return parser; +} + +static mpack_rpc_session_t *lmpack_grow_session(mpack_rpc_session_t *session) +{ + mpack_rpc_session_t *old = session; + mpack_uint32_t new_capacity = old->capacity * 2; + session = malloc(MPACK_RPC_SESSION_STRUCT_SIZE(new_capacity)); + if (!session) goto end; + mpack_rpc_session_init(session, new_capacity); + mpack_rpc_session_copy(session, old); + free(old); +end: + return session; +} + +static Unpacker *lmpack_check_unpacker(lua_State *L, int index) +{ + return luaL_checkudata(L, index, UNPACKER_META_NAME); +} + +static Packer *lmpack_check_packer(lua_State *L, int index) +{ + return luaL_checkudata(L, index, PACKER_META_NAME); +} + +static Session *lmpack_check_session(lua_State *L, int index) +{ + return luaL_checkudata(L, index, SESSION_META_NAME); +} + +static int lmpack_isnil(lua_State *L, int index) +{ + int rv; + if (!lua_isuserdata(L, index)) return 0; + lua_getfield(L, LUA_REGISTRYINDEX, NIL_NAME); + rv = lua_rawequal(L, -1, -2); + lua_pop(L, 1); + return rv; +} + +static int lmpack_isunpacker(lua_State *L, int index) +{ + int rv; + if (!lua_isuserdata(L, index) || !lua_getmetatable(L, index)) return 0; + luaL_getmetatable(L, UNPACKER_META_NAME); + rv = lua_rawequal(L, -1, -2); + lua_pop(L, 2); + return rv; +} + +static void lmpack_pushnil(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, NIL_NAME); +} + +/* adapted from + * https://github.com/antirez/lua-cmsgpack/blob/master/lua_cmsgpack.c */ +static mpack_uint32_t lmpack_objlen(lua_State *L, int *is_array) +{ + size_t len, max; + int isarr, type; + lua_Number n; +#ifndef NDEBUG + int top = lua_gettop(L); + assert(top); +#endif + + if ((type = lua_type(L, -1)) != LUA_TTABLE) { +#if LUA_VERSION_NUM >= 502 + len = lua_rawlen(L, -1); +#elif LUA_VERSION_NUM == 501 + len = lua_objlen(L, -1); +#else + #error You have either broken or too old Lua installation. This library requires Lua>=5.1 +#endif + goto end; + } + + /* count the number of keys and determine if it is an array */ + len = 0; + max = 0; + isarr = 1; + lua_pushnil(L); + + while (lua_next(L, -2)) { + lua_pop(L, 1); /* pop value */ + isarr = isarr + && lua_isnumber(L, -1) /* lua number */ + && (n = lua_tonumber(L, -1)) > 0 /* greater than 0 */ + && (size_t)n == n; /* and integer */ + max = isarr && (size_t)n > max ? (size_t)n : max; + len++; + } + + // when len==0, the caller should guess the type! + if (len > 0) { + *is_array = isarr && max == len; + } + +end: + if ((size_t)-1 > (mpack_uint32_t)-1 && len > (mpack_uint32_t)-1) + /* msgpack spec doesn't allow lengths > 32 bits */ + len = (mpack_uint32_t)-1; + assert(top == lua_gettop(L)); + return (mpack_uint32_t)len; +} + +static int lmpack_unpacker_new(lua_State *L) +{ + Unpacker *rv; + + if (lua_gettop(L) > 1) + return luaL_error(L, "expecting at most 1 table argument"); + + rv = lua_newuserdata(L, sizeof(*rv)); + rv->parser = malloc(sizeof(*rv->parser)); + if (!rv->parser) return luaL_error(L, "Failed to allocate memory"); + mpack_parser_init(rv->parser, 0); + rv->parser->data.p = rv; + rv->string_buffer = NULL; + rv->L = L; + rv->unpacking = 0; + luaL_getmetatable(L, UNPACKER_META_NAME); + lua_setmetatable(L, -2); + +#ifndef MPACK_DEBUG_REGISTRY_LEAK + lua_newtable(L); + rv->reg = luaL_ref(L, LUA_REGISTRYINDEX); +#endif + rv->ext = LUA_NOREF; + + lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME); + rv->mtdict = lmpack_ref(L, rv->reg); + + if (lua_istable(L, 1)) { + /* parse options */ + lua_getfield(L, 1, "ext"); + if (!lua_isnil(L, -1)) { + if (!lua_istable(L, -1)) + return luaL_error(L, "\"ext\" option must be a table"); + lmpack_shallow_copy(L); + } + rv->ext = lmpack_ref(L, rv->reg); + } + + return 1; +} + +static int lmpack_unpacker_delete(lua_State *L) +{ + Unpacker *unpacker = lmpack_check_unpacker(L, 1); + if (unpacker->ext != LUA_NOREF) + lmpack_unref(L, unpacker->reg, unpacker->ext); +#ifndef MPACK_DEBUG_REGISTRY_LEAK + luaL_unref(L, LUA_REGISTRYINDEX, unpacker->reg); +#endif + free(unpacker->parser); + return 0; +} + +static void lmpack_parse_enter(mpack_parser_t *parser, mpack_node_t *node) +{ + Unpacker *unpacker = parser->data.p; + lua_State *L = unpacker->L; + + switch (node->tok.type) { + case MPACK_TOKEN_NIL: + lmpack_pushnil(L); break; + case MPACK_TOKEN_BOOLEAN: + lua_pushboolean(L, (int)mpack_unpack_boolean(node->tok)); break; + case MPACK_TOKEN_UINT: + case MPACK_TOKEN_SINT: + case MPACK_TOKEN_FLOAT: + lua_pushnumber(L, mpack_unpack_number(node->tok)); break; + case MPACK_TOKEN_CHUNK: + assert(unpacker->string_buffer); + memcpy(unpacker->string_buffer + MPACK_PARENT_NODE(node)->pos, + node->tok.data.chunk_ptr, node->tok.length); + break; + case MPACK_TOKEN_BIN: + case MPACK_TOKEN_STR: + case MPACK_TOKEN_EXT: + unpacker->string_buffer = malloc(node->tok.length); + if (!unpacker->string_buffer) luaL_error(L, "Failed to allocate memory"); + break; + case MPACK_TOKEN_ARRAY: + case MPACK_TOKEN_MAP: + lua_newtable(L); + node->data[0].i = lmpack_ref(L, unpacker->reg); + break; + } +} + +static void lmpack_parse_exit(mpack_parser_t *parser, mpack_node_t *node) +{ + Unpacker *unpacker = parser->data.p; + lua_State *L = unpacker->L; + mpack_node_t *parent = MPACK_PARENT_NODE(node); + + switch (node->tok.type) { + case MPACK_TOKEN_BIN: + case MPACK_TOKEN_STR: + case MPACK_TOKEN_EXT: + lua_pushlstring(L, unpacker->string_buffer, node->tok.length); + free(unpacker->string_buffer); + unpacker->string_buffer = NULL; + if (node->tok.type == MPACK_TOKEN_EXT && unpacker->ext != LUA_NOREF) { + /* check if there's a handler for this type */ + lmpack_geti(L, unpacker->reg, unpacker->ext); + lua_rawgeti(L, -1, node->tok.data.ext_type); + if (lua_isfunction(L, -1)) { + /* stack: + * + * -1: ext unpacker function + * -2: ext unpackers table + * -3: ext string + * + * We want to call the ext unpacker function with the type and string + * as arguments, so push those now + */ + lua_pushinteger(L, node->tok.data.ext_type); + lua_pushvalue(L, -4); + lua_call(L, 2, 1); + /* stack: + * + * -1: returned object + * -2: ext unpackers table + * -3: ext string + */ + lua_replace(L, -3); + } else { + /* the last lua_rawgeti should have pushed nil on the stack, + * remove it */ + lua_pop(L, 1); + } + /* pop the ext unpackers table */ + lua_pop(L, 1); + } + break; + case MPACK_TOKEN_ARRAY: + case MPACK_TOKEN_MAP: + lmpack_geti(L, unpacker->reg, (int)node->data[0].i); + lmpack_unref(L, unpacker->reg, (int)node->data[0].i); + if (node->key_visited == 0 && node->tok.type == MPACK_TOKEN_MAP) { + lmpack_geti(L, unpacker->reg, unpacker->mtdict); // [table, mtdict] + lua_setmetatable(L, -2); // [table] + } + + break; + default: + break; + } + + if (parent && parent->tok.type < MPACK_TOKEN_BIN) { + /* At this point the parsed object is on the stack. Add it to the parent + * container. First put the container on the stack. */ + lmpack_geti(L, unpacker->reg, (int)parent->data[0].i); + + if (parent->tok.type == MPACK_TOKEN_ARRAY) { + /* Array, save the value on key equal to `parent->pos` */ + lua_pushnumber(L, (lua_Number)parent->pos); + lua_pushvalue(L, -3); + lua_settable(L, -3); + } else { + assert(parent->tok.type == MPACK_TOKEN_MAP); + if (parent->key_visited) { + /* save the key on the registry */ + lua_pushvalue(L, -2); + parent->data[1].i = lmpack_ref(L, unpacker->reg); + } else { + /* set the key/value pair */ + lmpack_geti(L, unpacker->reg, (int)parent->data[1].i); + lmpack_unref(L, unpacker->reg, (int)parent->data[1].i); + lua_pushvalue(L, -3); + lua_settable(L, -3); + } + } + lua_pop(L, 2); /* pop the container/object */ + } +} + +static int lmpack_unpacker_unpack_str(lua_State *L, Unpacker *unpacker, + const char **str, size_t *len) +{ + int rv; + + if (unpacker->unpacking) { + return luaL_error(L, "Unpacker instance already working. Use another " + "Unpacker or the module's \"unpack\" function if you " + "need to unpack from the ext handler"); + } + + do { + unpacker->unpacking = 1; + rv = mpack_parse(unpacker->parser, str, len, lmpack_parse_enter, + lmpack_parse_exit); + unpacker->unpacking = 0; + + if (rv == MPACK_NOMEM) { + unpacker->parser = lmpack_grow_parser(unpacker->parser); + if (!unpacker->parser) { + unpacker->unpacking = 0; + return luaL_error(L, "failed to grow Unpacker capacity"); + } + } + } while (rv == MPACK_NOMEM); + + if (rv == MPACK_ERROR) + return luaL_error(L, "invalid msgpack string"); + + return rv; +} + +static int lmpack_unpacker_unpack(lua_State *L) +{ + int result, argc; + lua_Number startpos; + size_t len, offset; + const char *str, *str_init; + Unpacker *unpacker; + + if ((argc = lua_gettop(L)) > 3 || argc < 2) + return luaL_error(L, "expecting between 2 and 3 arguments"); + + unpacker = lmpack_check_unpacker(L, 1); + unpacker->L = L; + + str_init = str = luaL_checklstring(L, 2, &len); + startpos = lua_gettop(L) == 3 ? luaL_checknumber(L, 3) : 1; + + luaL_argcheck(L, startpos > 0, 3, + "start position must be greater than zero"); + luaL_argcheck(L, (size_t)startpos == startpos, 3, + "start position must be an integer"); + luaL_argcheck(L, (size_t)startpos <= len, 3, + "start position must be less than or equal to the input string length"); + + offset = (size_t)startpos - 1 ; + str += offset; + len -= offset; + result = lmpack_unpacker_unpack_str(L, unpacker, &str, &len); + + if (result == MPACK_EOF) + /* if we hit EOF, return nil as the object */ + lua_pushnil(L); + + /* also return the new position in the input string */ + lua_pushinteger(L, str - str_init + 1); + assert(lua_gettop(L) == argc + 2); + return 2; +} + +static int lmpack_packer_new(lua_State *L) +{ + Packer *rv; + + if (lua_gettop(L) > 1) + return luaL_error(L, "expecting at most 1 table argument"); + + rv = lua_newuserdata(L, sizeof(*rv)); + rv->parser = malloc(sizeof(*rv->parser)); + if (!rv->parser) return luaL_error(L, "failed to allocate parser memory"); + mpack_parser_init(rv->parser, 0); + rv->parser->data.p = rv; + rv->L = L; + rv->packing = 0; + rv->is_bin = 0; + rv->is_bin_fn = LUA_NOREF; + luaL_getmetatable(L, PACKER_META_NAME); + lua_setmetatable(L, -2); + +#ifndef MPACK_DEBUG_REGISTRY_LEAK + lua_newtable(L); + rv->reg = luaL_ref(L, LUA_REGISTRYINDEX); +#endif + rv->ext = LUA_NOREF; + + lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME); + rv->mtdict = lmpack_ref(L, rv->reg); + + if (lua_istable(L, 1)) { + /* parse options */ + lua_getfield(L, 1, "ext"); + if (!lua_isnil(L, -1)) { + if (!lua_istable(L, -1)) + return luaL_error(L, "\"ext\" option must be a table"); + lmpack_shallow_copy(L); + } + rv->ext = lmpack_ref(L, rv->reg); + lua_getfield(L, 1, "is_bin"); + if (!lua_isnil(L, -1)) { + if (!lua_isboolean(L, -1) && !lua_isfunction(L, -1)) + return luaL_error(L, + "\"is_bin\" option must be a boolean or function"); + rv->is_bin = lua_toboolean(L, -1); + if (lua_isfunction(L, -1)) rv->is_bin_fn = lmpack_ref(L, rv->reg); + else lua_pop(L, 1); + } else { + lua_pop(L, 1); + } + + } + + return 1; +} + +static int lmpack_packer_delete(lua_State *L) +{ + Packer *packer = lmpack_check_packer(L, 1); + if (packer->ext != LUA_NOREF) + lmpack_unref(L, packer->reg, packer->ext); +#ifndef MPACK_DEBUG_REGISTRY_LEAK + luaL_unref(L, LUA_REGISTRYINDEX, packer->reg); +#endif + free(packer->parser); + return 0; +} + +static void lmpack_unparse_enter(mpack_parser_t *parser, mpack_node_t *node) +{ + int type; + Packer *packer = parser->data.p; + lua_State *L = packer->L; + mpack_node_t *parent = MPACK_PARENT_NODE(node); + + if (parent) { + /* get the parent */ + lmpack_geti(L, packer->reg, (int)parent->data[0].i); + + if (parent->tok.type > MPACK_TOKEN_MAP) { + /* strings are a special case, they are packed as single child chunk + * node */ + const char *str = lua_tolstring(L, -1, NULL); + node->tok = mpack_pack_chunk(str, parent->tok.length); + lua_pop(L, 1); + return; + } + + if (parent->tok.type == MPACK_TOKEN_ARRAY) { + /* push the next index */ + lua_pushnumber(L, (lua_Number)(parent->pos + 1)); + /* push the element */ + lua_gettable(L, -2); + } else if (parent->tok.type == MPACK_TOKEN_MAP) { + int result; + /* push the previous key */ + lmpack_geti(L, packer->reg, (int)parent->data[1].i); + /* push the pair */ + result = lua_next(L, -2); + assert(result); /* should not be here if the map was fully processed */ + if (parent->key_visited) { + /* release the current key */ + lmpack_unref(L, packer->reg, (int)parent->data[1].i); + /* push key to the top */ + lua_pushvalue(L, -2); + /* set the key for the next iteration, leaving value on top */ + parent->data[1].i = lmpack_ref(L, packer->reg); + /* replace key by the value */ + lua_replace(L, -2); + } else { + /* pop value */ + lua_pop(L, 1); + } + } + /* remove parent, leaving only the object which will be serialized */ + lua_remove(L, -2); + } else { + /* root object */ + lmpack_geti(L, packer->reg, packer->root); + } + + type = lua_type(L, -1); + + switch (type) { + case LUA_TBOOLEAN: + node->tok = mpack_pack_boolean((unsigned)lua_toboolean(L, -1)); + break; + case LUA_TNUMBER: + node->tok = mpack_pack_number(lua_tonumber(L, -1)); + break; + case LUA_TSTRING: { + int is_bin = packer->is_bin; + if (is_bin && packer->is_bin_fn != LUA_NOREF) { + lmpack_geti(L, packer->reg, packer->is_bin_fn); + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + is_bin = lua_toboolean(L, -1); + lua_pop(L, 1); + } + if (is_bin) node->tok = mpack_pack_bin(lmpack_objlen(L, NULL)); + else node->tok = mpack_pack_str(lmpack_objlen(L, NULL)); + break; + } + case LUA_TTABLE: { + mpack_uint32_t len; + mpack_node_t *n; + + int has_meta = lua_getmetatable(L, -1); + if (packer->ext != LUA_NOREF && has_meta) { + /* check if there's a handler for this metatable */ + lmpack_geti(L, packer->reg, packer->ext); + lua_pushvalue(L, -2); + lua_gettable(L, -2); + if (lua_isfunction(L, -1)) { + lua_Number ext = -1; + /* stack: + * + * -1: ext packer function + * -2: ext packers table + * -3: metatable + * -4: original object + * + * We want to call the ext packer function with the original object as + * argument, so push it on the top + */ + lua_pushvalue(L, -4); + /* handler should return type code and string */ + lua_call(L, 1, 2); + if (!lua_isnumber(L, -2) || (ext = lua_tonumber(L, -2)) < 0 + || ext > 127 || (int)ext != ext) + luaL_error(L, + "the first result from ext packer must be an integer " + "between 0 and 127"); + if (!lua_isstring(L, -1)) + luaL_error(L, + "the second result from ext packer must be a string"); + node->tok = mpack_pack_ext((int)ext, lmpack_objlen(L, NULL)); + /* stack: + * + * -1: ext string + * -2: ext type + * -3: ext packers table + * -4: metatable + * -5: original table + * + * We want to leave only the returned ext string, so + * replace -5 with the string and pop 3 + */ + lua_replace(L, -5); + lua_pop(L, 3); + break; /* done */ + } else { + /* stack: + * + * -1: ext packers table + * -2: metatable + * -3: original table + * + * We want to leave only the original table and metatable since they + * will be handled below, so pop 1 + */ + lua_pop(L, 1); + } + } + + int is_array = 1; + if (has_meta) { + // stack: [table, metatable] + if (packer->mtdict != LUA_NOREF) { + lmpack_geti(L, packer->reg, packer->mtdict); // [table, metatable, mtdict] + is_array = !lua_rawequal(L, -1, -2); + lua_pop(L, 1); // [table, metatable]; + } + lua_pop(L, 1); // [table] + } + + /* check for cycles */ + n = node; + while ((n = MPACK_PARENT_NODE(n))) { + lmpack_geti(L, packer->reg, (int)n->data[0].i); + if (lua_rawequal(L, -1, -2)) { + /* break out of cycles with NIL */ + node->tok = mpack_pack_nil(); + lua_pop(L, 2); + lmpack_pushnil(L); + goto end; + } + lua_pop(L, 1); + } + + len = lmpack_objlen(L, &is_array); + if (is_array) { + node->tok = mpack_pack_array(len); + } else { + node->tok = mpack_pack_map(len); + /* save nil as the previous key to start iteration */ + node->data[1].i = LUA_REFNIL; + } + break; + } + case LUA_TUSERDATA: + if (lmpack_isnil(L, -1)) { + node->tok = mpack_pack_nil(); + break; + } + FALLTHROUGH; + default: + { + /* #define FMT */ + char errmsg[50]; + snprintf(errmsg, 50, "can't serialize object of type %d", type); + luaL_error(L, errmsg); + } + } + +end: + node->data[0].i = lmpack_ref(L, packer->reg); +} + +static void lmpack_unparse_exit(mpack_parser_t *parser, mpack_node_t *node) +{ + Packer *packer = parser->data.p; + lua_State *L = packer->L; + if (node->tok.type != MPACK_TOKEN_CHUNK) { + /* release the object */ + lmpack_unref(L, packer->reg, (int)node->data[0].i); + if (node->tok.type == MPACK_TOKEN_MAP) + lmpack_unref(L, packer->reg, (int)node->data[1].i); + } +} + +static int lmpack_packer_pack(lua_State *L) +{ + char *b; + size_t bl; + int result, argc; + Packer *packer; + luaL_Buffer buffer; + + if ((argc = lua_gettop(L)) != 2) + return luaL_error(L, "expecting exactly 2 arguments"); + + packer = lmpack_check_packer(L, 1); + packer->L = L; + packer->root = lmpack_ref(L, packer->reg); + luaL_buffinit(L, &buffer); + b = luaL_prepbuffer(&buffer); + bl = LUAL_BUFFERSIZE; + + if (packer->packing) { + return luaL_error(L, "Packer instance already working. Use another Packer " + "or the module's \"pack\" function if you need to " + "pack from the ext handler"); + } + + do { + size_t bl_init = bl; + packer->packing = 1; + result = mpack_unparse(packer->parser, &b, &bl, lmpack_unparse_enter, + lmpack_unparse_exit); + packer->packing = 0; + + if (result == MPACK_NOMEM) { + packer->parser = lmpack_grow_parser(packer->parser); + if (!packer->parser) { + packer->packing = 0; + return luaL_error(L, "Failed to grow Packer capacity"); + } + } + + luaL_addsize(&buffer, bl_init - bl); + + if (!bl) { + /* buffer empty, resize */ + b = luaL_prepbuffer(&buffer); + bl = LUAL_BUFFERSIZE; + } + } while (result == MPACK_EOF || result == MPACK_NOMEM); + + lmpack_unref(L, packer->reg, packer->root); + luaL_pushresult(&buffer); + assert(lua_gettop(L) == argc); + return 1; +} + +static int lmpack_session_new(lua_State *L) +{ + Session *rv = lua_newuserdata(L, sizeof(*rv)); + rv->session = malloc(sizeof(*rv->session)); + if (!rv->session) return luaL_error(L, "Failed to allocate memory"); + mpack_rpc_session_init(rv->session, 0); + rv->L = L; + luaL_getmetatable(L, SESSION_META_NAME); + lua_setmetatable(L, -2); +#ifndef MPACK_DEBUG_REGISTRY_LEAK + lua_newtable(L); + rv->reg = luaL_ref(L, LUA_REGISTRYINDEX); +#endif + rv->unpacker = LUA_REFNIL; + rv->unpacked.args_or_result = LUA_NOREF; + rv->unpacked.method_or_error = LUA_NOREF; + rv->unpacked.type = MPACK_EOF; + + if (lua_istable(L, 1)) { + /* parse options */ + lua_getfield(L, 1, "unpack"); + if (!lmpack_isunpacker(L, -1)) { + return luaL_error(L, + "\"unpack\" option must be a " UNPACKER_META_NAME " instance"); + } + rv->unpacker = lmpack_ref(L, rv->reg); + } + + return 1; +} + +static int lmpack_session_delete(lua_State *L) +{ + Session *session = lmpack_check_session(L, 1); + lmpack_unref(L, session->reg, session->unpacker); +#ifndef MPACK_DEBUG_REGISTRY_LEAK + luaL_unref(L, LUA_REGISTRYINDEX, session->reg); +#endif + free(session->session); + return 0; +} + +static int lmpack_session_receive(lua_State *L) +{ + int argc, done, rcount = 3; + lua_Number startpos; + size_t len; + const char *str, *str_init; + Session *session; + Unpacker *unpacker = NULL; + + if ((argc = lua_gettop(L)) > 3 || argc < 2) + return luaL_error(L, "expecting between 2 and 3 arguments"); + + session = lmpack_check_session(L, 1); + str_init = str = luaL_checklstring(L, 2, &len); + startpos = lua_gettop(L) == 3 ? luaL_checknumber(L, 3) : 1; + + luaL_argcheck(L, startpos > 0, 3, + "start position must be greater than zero"); + luaL_argcheck(L, (size_t)startpos == startpos, 3, + "start position must be an integer"); + luaL_argcheck(L, (size_t)startpos <= len, 3, + "start position must be less than or equal to the input string length"); + + str += (size_t)startpos - 1; + + if (session->unpacker != LUA_REFNIL) { + lmpack_geti(L, session->reg, session->unpacker); + unpacker = lmpack_check_unpacker(L, -1); + unpacker->L = L; + rcount += 2; + lua_pop(L, 1); + } + + for (;;) { + int result; + + if (session->unpacked.type == MPACK_EOF) { + session->unpacked.type = + mpack_rpc_receive(session->session, &str, &len, &session->unpacked.msg); + + if (!unpacker || session->unpacked.type == MPACK_EOF) + break; + } + + result = lmpack_unpacker_unpack_str(L, unpacker, &str, &len); + + if (result == MPACK_EOF) break; + + if (session->unpacked.method_or_error == LUA_NOREF) { + session->unpacked.method_or_error = lmpack_ref(L, session->reg); + } else { + session->unpacked.args_or_result = lmpack_ref(L, session->reg); + break; + } + } + + done = session->unpacked.type != MPACK_EOF + && (session->unpacked.args_or_result != LUA_NOREF || !unpacker); + + if (!done) { + lua_pushnil(L); + lua_pushnil(L); + if (unpacker) { + lua_pushnil(L); + lua_pushnil(L); + } + goto end; + } + + switch (session->unpacked.type) { + case MPACK_RPC_REQUEST: + lua_pushstring(L, "request"); + lua_pushnumber(L, session->unpacked.msg.id); + break; + case MPACK_RPC_RESPONSE: + lua_pushstring(L, "response"); + lmpack_geti(L, session->reg, (int)session->unpacked.msg.data.i); + break; + case MPACK_RPC_NOTIFICATION: + lua_pushstring(L, "notification"); + lua_pushnil(L); + break; + default: + /* In most cases the only sane thing to do when receiving invalid + * msgpack-rpc is to close the connection, so handle all errors with + * this generic message. Later may add more detailed information. */ + return luaL_error(L, "invalid msgpack-rpc string"); + } + + session->unpacked.type = MPACK_EOF; + + if (unpacker) { + lmpack_geti(L, session->reg, session->unpacked.method_or_error); + lmpack_geti(L, session->reg, session->unpacked.args_or_result); + lmpack_unref(L, session->reg, session->unpacked.method_or_error); + lmpack_unref(L, session->reg, session->unpacked.args_or_result); + session->unpacked.method_or_error = LUA_NOREF; + session->unpacked.args_or_result = LUA_NOREF; + } + +end: + lua_pushinteger(L, str - str_init + 1); + return rcount; +} + +static int lmpack_session_request(lua_State *L) +{ + int result; + char buf[16], *b = buf; + size_t bl = sizeof(buf); + Session *session; + mpack_data_t data; + + if (lua_gettop(L) > 2 || lua_gettop(L) < 1) + return luaL_error(L, "expecting 1 or 2 arguments"); + + session = lmpack_check_session(L, 1); + data.i = lua_isnoneornil(L, 2) ? LUA_NOREF : lmpack_ref(L, session->reg); + do { + result = mpack_rpc_request(session->session, &b, &bl, data); + if (result == MPACK_NOMEM) { + session->session = lmpack_grow_session(session->session); + if (!session->session) + return luaL_error(L, "Failed to grow Session capacity"); + } + } while (result == MPACK_NOMEM); + + assert(result == MPACK_OK); + lua_pushlstring(L, buf, sizeof(buf) - bl); + return 1; +} + +static int lmpack_session_reply(lua_State *L) +{ + int result; + char buf[16], *b = buf; + size_t bl = sizeof(buf); + Session *session; + lua_Number id; + + if (lua_gettop(L) != 2) + return luaL_error(L, "expecting exactly 2 arguments"); + + session = lmpack_check_session(L, 1); + id = lua_tonumber(L, 2); + luaL_argcheck(L, ((size_t)id == id && id >= 0 && id <= 0xffffffff), 2, + "invalid request id"); + result = mpack_rpc_reply(session->session, &b, &bl, (mpack_uint32_t)id); + assert(result == MPACK_OK); + lua_pushlstring(L, buf, sizeof(buf) - bl); + return 1; +} + +static int lmpack_session_notify(lua_State *L) +{ + int result; + char buf[16], *b = buf; + size_t bl = sizeof(buf); + Session *session; + + if (lua_gettop(L) != 1) + return luaL_error(L, "expecting exactly 1 argument"); + + session = lmpack_check_session(L, 1); + result = mpack_rpc_notify(session->session, &b, &bl); + assert(result == MPACK_OK); + lua_pushlstring(L, buf, sizeof(buf) - bl); + return 1; +} + +static int lmpack_nil_tostring(lua_State* L) +{ + lua_pushfstring(L, NIL_NAME, lua_topointer(L, 1)); + return 1; +} + +static int lmpack_unpack(lua_State *L) +{ + int result; + size_t len; + const char *str; + Unpacker unpacker; + mpack_parser_t parser; + + if (lua_gettop(L) != 1) + return luaL_error(L, "expecting exactly 1 argument"); + + str = luaL_checklstring(L, 1, &len); + + /* initialize unpacker */ + lua_newtable(L); + unpacker.reg = luaL_ref(L, LUA_REGISTRYINDEX); + unpacker.ext = LUA_NOREF; + unpacker.parser = &parser; + mpack_parser_init(unpacker.parser, 0); + unpacker.parser->data.p = &unpacker; + unpacker.string_buffer = NULL; + unpacker.L = L; + + lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME); + unpacker.mtdict = lmpack_ref(L, unpacker.reg); + + result = mpack_parse(&parser, &str, &len, lmpack_parse_enter, + lmpack_parse_exit); + + luaL_unref(L, LUA_REGISTRYINDEX, unpacker.reg); + + if (result == MPACK_NOMEM) + return luaL_error(L, "object was too deep to unpack"); + else if (result == MPACK_EOF) + return luaL_error(L, "incomplete msgpack string"); + else if (result == MPACK_ERROR) + return luaL_error(L, "invalid msgpack string"); + else if (result == MPACK_OK && len) + return luaL_error(L, "trailing data in msgpack string"); + + assert(result == MPACK_OK); + return 1; +} + +static int lmpack_pack(lua_State *L) +{ + char *b; + size_t bl; + int result; + Packer packer; + mpack_parser_t parser; + luaL_Buffer buffer; + + if (lua_gettop(L) != 1) + return luaL_error(L, "expecting exactly 1 argument"); + + /* initialize packer */ + lua_newtable(L); + packer.reg = luaL_ref(L, LUA_REGISTRYINDEX); + packer.ext = LUA_NOREF; + packer.parser = &parser; + mpack_parser_init(packer.parser, 0); + packer.parser->data.p = &packer; + packer.is_bin = 0; + packer.L = L; + packer.root = lmpack_ref(L, packer.reg); + + lua_getfield(L, LUA_REGISTRYINDEX, EMPTY_DICT_NAME); + packer.mtdict = lmpack_ref(L, packer.reg); + + + luaL_buffinit(L, &buffer); + b = luaL_prepbuffer(&buffer); + bl = LUAL_BUFFERSIZE; + + do { + size_t bl_init = bl; + result = mpack_unparse(packer.parser, &b, &bl, lmpack_unparse_enter, + lmpack_unparse_exit); + + if (result == MPACK_NOMEM) { + lmpack_unref(L, packer.reg, packer.root); + luaL_unref(L, LUA_REGISTRYINDEX, packer.reg); + return luaL_error(L, "object was too deep to pack"); + } + + luaL_addsize(&buffer, bl_init - bl); + + if (!bl) { + /* buffer empty, resize */ + b = luaL_prepbuffer(&buffer); + bl = LUAL_BUFFERSIZE; + } + } while (result == MPACK_EOF); + + lmpack_unref(L, packer.reg, packer.root); + luaL_unref(L, LUA_REGISTRYINDEX, packer.reg); + luaL_pushresult(&buffer); + return 1; +} + +static const luaL_reg unpacker_methods[] = { + {"__call", lmpack_unpacker_unpack}, + {"__gc", lmpack_unpacker_delete}, + {NULL, NULL} +}; + +static const luaL_reg packer_methods[] = { + {"__call", lmpack_packer_pack}, + {"__gc", lmpack_packer_delete}, + {NULL, NULL} +}; + +static const luaL_reg session_methods[] = { + {"receive", lmpack_session_receive}, + {"request", lmpack_session_request}, + {"reply", lmpack_session_reply}, + {"notify", lmpack_session_notify}, + {"__gc", lmpack_session_delete}, + {NULL, NULL} +}; + +static const luaL_reg mpack_functions[] = { + {"Unpacker", lmpack_unpacker_new}, + {"Packer", lmpack_packer_new}, + {"Session", lmpack_session_new}, + {"unpack", lmpack_unpack}, + {"pack", lmpack_pack}, + {NULL, NULL} +}; + +int luaopen_mpack(lua_State *L) +{ + /* Unpacker */ + luaL_newmetatable(L, UNPACKER_META_NAME); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_register(L, NULL, unpacker_methods); + lua_pop(L, 1); + /* Packer */ + luaL_newmetatable(L, PACKER_META_NAME); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_register(L, NULL, packer_methods); + lua_pop(L, 1); + /* Session */ + luaL_newmetatable(L, SESSION_META_NAME); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + luaL_register(L, NULL, session_methods); + lua_pop(L, 1); + /* NIL */ + /* Check if NIL is already stored in the registry */ + lua_getfield(L, LUA_REGISTRYINDEX, NIL_NAME); + /* If it isn't, create it */ + if (lua_isnil(L, -1)) { + /* Use a constant userdata to represent NIL */ + (void)lua_newuserdata(L, sizeof(void *)); + /* Create a metatable for NIL userdata */ + lua_createtable(L, 0, 1); + lua_pushstring(L, "__tostring"); + lua_pushcfunction(L, lmpack_nil_tostring); + lua_settable(L, -3); + /* Assign the metatable to the userdata object */ + lua_setmetatable(L, -2); + /* Save NIL on the registry so we can access it easily from other functions */ + lua_setfield(L, LUA_REGISTRYINDEX, NIL_NAME); + } + + lua_pop(L, 1); + + /* module */ + lua_newtable(L); + luaL_register(L, NULL, mpack_functions); + /* save NIL on the module */ + lua_getfield(L, LUA_REGISTRYINDEX, NIL_NAME); + lua_setfield(L, -2, "NIL"); + return 1; +} diff --git a/src/mpack/lmpack.h b/src/mpack/lmpack.h new file mode 100644 index 0000000000..e35f40fab6 --- /dev/null +++ b/src/mpack/lmpack.h @@ -0,0 +1,3 @@ +#include <lua.h> + +int luaopen_mpack(lua_State *L); diff --git a/src/mpack/mpack_core.c b/src/mpack/mpack_core.c new file mode 100644 index 0000000000..0ad09bd46a --- /dev/null +++ b/src/mpack/mpack_core.c @@ -0,0 +1,575 @@ +#include <string.h> + +#include "mpack_core.h" + +#define UNUSED(p) (void)p; +#define ADVANCE(buf, buflen) ((*buflen)--, (unsigned char)*((*buf)++)) +#define TLEN(val, range_start) ((mpack_uint32_t)(1 << (val - range_start))) +#ifndef MIN +# define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#endif + +static int mpack_rtoken(const char **buf, size_t *buflen, + mpack_token_t *tok); +static int mpack_rpending(const char **b, size_t *nl, mpack_tokbuf_t *tb); +static int mpack_rvalue(mpack_token_type_t t, mpack_uint32_t l, + const char **b, size_t *bl, mpack_token_t *tok); +static int mpack_rblob(mpack_token_type_t t, mpack_uint32_t l, + const char **b, size_t *bl, mpack_token_t *tok); +static int mpack_wtoken(const mpack_token_t *tok, char **b, size_t *bl); +static int mpack_wpending(char **b, size_t *bl, mpack_tokbuf_t *tb); +static int mpack_wpint(char **b, size_t *bl, mpack_value_t v); +static int mpack_wnint(char **b, size_t *bl, mpack_value_t v); +static int mpack_wfloat(char **b, size_t *bl, const mpack_token_t *v); +static int mpack_wstr(char **buf, size_t *buflen, mpack_uint32_t len); +static int mpack_wbin(char **buf, size_t *buflen, mpack_uint32_t len); +static int mpack_wext(char **buf, size_t *buflen, int type, + mpack_uint32_t len); +static int mpack_warray(char **buf, size_t *buflen, mpack_uint32_t len); +static int mpack_wmap(char **buf, size_t *buflen, mpack_uint32_t len); +static int mpack_w1(char **b, size_t *bl, mpack_uint32_t v); +static int mpack_w2(char **b, size_t *bl, mpack_uint32_t v); +static int mpack_w4(char **b, size_t *bl, mpack_uint32_t v); +static mpack_value_t mpack_byte(unsigned char b); +static int mpack_value(mpack_token_type_t t, mpack_uint32_t l, + mpack_value_t v, mpack_token_t *tok); +static int mpack_blob(mpack_token_type_t t, mpack_uint32_t l, int et, + mpack_token_t *tok); + +MPACK_API void mpack_tokbuf_init(mpack_tokbuf_t *tokbuf) +{ + tokbuf->ppos = 0; + tokbuf->plen = 0; + tokbuf->passthrough = 0; +} + +MPACK_API int mpack_read(mpack_tokbuf_t *tokbuf, const char **buf, + size_t *buflen, mpack_token_t *tok) +{ + int status; + size_t initial_ppos, ptrlen, advanced; + const char *ptr, *ptr_save; + assert(*buf && *buflen); + + if (tokbuf->passthrough) { + /* pass data from str/bin/ext directly as a MPACK_TOKEN_CHUNK, adjusting + * *buf and *buflen */ + tok->type = MPACK_TOKEN_CHUNK; + tok->data.chunk_ptr = *buf; + tok->length = MIN((mpack_uint32_t)*buflen, tokbuf->passthrough); + tokbuf->passthrough -= tok->length; + *buf += tok->length; + *buflen -= tok->length; + goto done; + } + + initial_ppos = tokbuf->ppos; + + if (tokbuf->plen) { + if (!mpack_rpending(buf, buflen, tokbuf)) { + return MPACK_EOF; + } + ptr = tokbuf->pending; + ptrlen = tokbuf->ppos; + } else { + ptr = *buf; + ptrlen = *buflen; + } + + ptr_save = ptr; + + if ((status = mpack_rtoken(&ptr, &ptrlen, tok))) { + if (status != MPACK_EOF) return MPACK_ERROR; + /* need more data */ + assert(!tokbuf->plen); + /* read the remainder of *buf to tokbuf->pending so it can be parsed + * later with more data. only required when tokbuf->plen == 0 or else + * it would have been done already. */ + tokbuf->plen = tok->length + 1; + assert(tokbuf->plen <= sizeof(tokbuf->pending)); + tokbuf->ppos = 0; + status = mpack_rpending(buf, buflen, tokbuf); + assert(!status); + return MPACK_EOF; + } + + advanced = (size_t)(ptr - ptr_save) - initial_ppos; + tokbuf->plen = tokbuf->ppos = 0; + *buflen -= advanced; + *buf += advanced; + + if (tok->type > MPACK_TOKEN_MAP) { + tokbuf->passthrough = tok->length; + } + +done: + return MPACK_OK; +} + +MPACK_API int mpack_write(mpack_tokbuf_t *tokbuf, char **buf, size_t *buflen, + const mpack_token_t *t) +{ + int status; + char *ptr; + size_t ptrlen; + mpack_token_t tok = tokbuf->plen ? tokbuf->pending_tok : *t; + assert(*buf && *buflen); + + if (tok.type == MPACK_TOKEN_CHUNK) { + size_t written, pending, count; + if (!tokbuf->plen) tokbuf->ppos = 0; + written = tokbuf->ppos; + pending = tok.length - written; + count = MIN(pending, *buflen); + memcpy(*buf, tok.data.chunk_ptr + written, count); + *buf += count; + *buflen -= count; + tokbuf->ppos += count; + tokbuf->plen = count == pending ? 0 : tok.length; + if (count == pending) { + return MPACK_OK; + } else { + tokbuf->pending_tok = tok; + return MPACK_EOF; + } + } + + if (tokbuf->plen) return mpack_wpending(buf, buflen, tokbuf); + + if (*buflen < MPACK_MAX_TOKEN_LEN) { + ptr = tokbuf->pending; + ptrlen = sizeof(tokbuf->pending); + } else { + ptr = *buf; + ptrlen = *buflen; + } + + if ((status = mpack_wtoken(&tok, &ptr, &ptrlen))) return status; + + if (*buflen < MPACK_MAX_TOKEN_LEN) { + size_t toklen = sizeof(tokbuf->pending) - ptrlen; + size_t write_cnt = MIN(toklen, *buflen); + memcpy(*buf, tokbuf->pending, write_cnt); + *buf += write_cnt; + *buflen -= write_cnt; + if (write_cnt < toklen) { + assert(!*buflen); + tokbuf->plen = toklen; + tokbuf->ppos = write_cnt; + tokbuf->pending_tok = tok; + return MPACK_EOF; + } + } else { + *buflen -= (size_t)(ptr - *buf); + *buf = ptr; + } + + return MPACK_OK; +} + +static int mpack_rtoken(const char **buf, size_t *buflen, + mpack_token_t *tok) +{ + unsigned char t = ADVANCE(buf, buflen); + if (t < 0x80) { + /* positive fixint */ + return mpack_value(MPACK_TOKEN_UINT, 1, mpack_byte(t), tok); + } else if (t < 0x90) { + /* fixmap */ + return mpack_blob(MPACK_TOKEN_MAP, t & 0xf, 0, tok); + } else if (t < 0xa0) { + /* fixarray */ + return mpack_blob(MPACK_TOKEN_ARRAY, t & 0xf, 0, tok); + } else if (t < 0xc0) { + /* fixstr */ + return mpack_blob(MPACK_TOKEN_STR, t & 0x1f, 0, tok); + } else if (t < 0xe0) { + switch (t) { + case 0xc0: /* nil */ + return mpack_value(MPACK_TOKEN_NIL, 0, mpack_byte(0), tok); + case 0xc2: /* false */ + return mpack_value(MPACK_TOKEN_BOOLEAN, 1, mpack_byte(0), tok); + case 0xc3: /* true */ + return mpack_value(MPACK_TOKEN_BOOLEAN, 1, mpack_byte(1), tok); + case 0xc4: /* bin 8 */ + case 0xc5: /* bin 16 */ + case 0xc6: /* bin 32 */ + return mpack_rblob(MPACK_TOKEN_BIN, TLEN(t, 0xc4), buf, buflen, tok); + case 0xc7: /* ext 8 */ + case 0xc8: /* ext 16 */ + case 0xc9: /* ext 32 */ + return mpack_rblob(MPACK_TOKEN_EXT, TLEN(t, 0xc7), buf, buflen, tok); + case 0xca: /* float 32 */ + case 0xcb: /* float 64 */ + return mpack_rvalue(MPACK_TOKEN_FLOAT, TLEN(t, 0xc8), buf, buflen, tok); + case 0xcc: /* uint 8 */ + case 0xcd: /* uint 16 */ + case 0xce: /* uint 32 */ + case 0xcf: /* uint 64 */ + return mpack_rvalue(MPACK_TOKEN_UINT, TLEN(t, 0xcc), buf, buflen, tok); + case 0xd0: /* int 8 */ + case 0xd1: /* int 16 */ + case 0xd2: /* int 32 */ + case 0xd3: /* int 64 */ + return mpack_rvalue(MPACK_TOKEN_SINT, TLEN(t, 0xd0), buf, buflen, tok); + case 0xd4: /* fixext 1 */ + case 0xd5: /* fixext 2 */ + case 0xd6: /* fixext 4 */ + case 0xd7: /* fixext 8 */ + case 0xd8: /* fixext 16 */ + if (*buflen == 0) { + /* require only one extra byte for the type code */ + tok->length = 1; + return MPACK_EOF; + } + tok->length = TLEN(t, 0xd4); + tok->type = MPACK_TOKEN_EXT; + tok->data.ext_type = ADVANCE(buf, buflen); + return MPACK_OK; + case 0xd9: /* str 8 */ + case 0xda: /* str 16 */ + case 0xdb: /* str 32 */ + return mpack_rblob(MPACK_TOKEN_STR, TLEN(t, 0xd9), buf, buflen, tok); + case 0xdc: /* array 16 */ + case 0xdd: /* array 32 */ + return mpack_rblob(MPACK_TOKEN_ARRAY, TLEN(t, 0xdb), buf, buflen, tok); + case 0xde: /* map 16 */ + case 0xdf: /* map 32 */ + return mpack_rblob(MPACK_TOKEN_MAP, TLEN(t, 0xdd), buf, buflen, tok); + default: + return MPACK_ERROR; + } + } else { + /* negative fixint */ + return mpack_value(MPACK_TOKEN_SINT, 1, mpack_byte(t), tok); + } +} + +static int mpack_rpending(const char **buf, size_t *buflen, + mpack_tokbuf_t *state) +{ + size_t count; + assert(state->ppos < state->plen); + count = MIN(state->plen - state->ppos, *buflen); + memcpy(state->pending + state->ppos, *buf, count); + state->ppos += count; + if (state->ppos < state->plen) { + /* consume buffer since no token will be parsed yet. */ + *buf += *buflen; + *buflen = 0; + return 0; + } + return 1; +} + +static int mpack_rvalue(mpack_token_type_t type, mpack_uint32_t remaining, + const char **buf, size_t *buflen, mpack_token_t *tok) +{ + if (*buflen < remaining) { + tok->length = remaining; + return MPACK_EOF; + } + + mpack_value(type, remaining, mpack_byte(0), tok); + + while (remaining) { + mpack_uint32_t byte = ADVANCE(buf, buflen), byte_idx, byte_shift; + byte_idx = (mpack_uint32_t)--remaining; + byte_shift = (byte_idx % 4) * 8; + tok->data.value.lo |= byte << byte_shift; + if (remaining == 4) { + /* unpacked the first half of a 8-byte value, shift what was parsed to the + * "hi" field and reset "lo" for the trailing 4 bytes. */ + tok->data.value.hi = tok->data.value.lo; + tok->data.value.lo = 0; + } + } + + if (type == MPACK_TOKEN_SINT) { + mpack_uint32_t hi = tok->data.value.hi; + mpack_uint32_t lo = tok->data.value.lo; + mpack_uint32_t msb = (tok->length == 8 && hi >> 31) || + (tok->length == 4 && lo >> 31) || + (tok->length == 2 && lo >> 15) || + (tok->length == 1 && lo >> 7); + if (!msb) { + tok->type = MPACK_TOKEN_UINT; + } + } + + return MPACK_OK; +} + +static int mpack_rblob(mpack_token_type_t type, mpack_uint32_t tlen, + const char **buf, size_t *buflen, mpack_token_t *tok) +{ + mpack_token_t l; + mpack_uint32_t required = tlen + (type == MPACK_TOKEN_EXT ? 1 : 0); + + if (*buflen < required) { + tok->length = required; + return MPACK_EOF; + } + + l.data.value.lo = 0; + mpack_rvalue(MPACK_TOKEN_UINT, tlen, buf, buflen, &l); + tok->type = type; + tok->length = l.data.value.lo; + + if (type == MPACK_TOKEN_EXT) { + tok->data.ext_type = ADVANCE(buf, buflen); + } + + return MPACK_OK; +} + +static int mpack_wtoken(const mpack_token_t *tok, char **buf, + size_t *buflen) +{ + switch (tok->type) { + case MPACK_TOKEN_NIL: + return mpack_w1(buf, buflen, 0xc0); + case MPACK_TOKEN_BOOLEAN: + return mpack_w1(buf, buflen, tok->data.value.lo ? 0xc3 : 0xc2); + case MPACK_TOKEN_UINT: + return mpack_wpint(buf, buflen, tok->data.value); + case MPACK_TOKEN_SINT: + return mpack_wnint(buf, buflen, tok->data.value); + case MPACK_TOKEN_FLOAT: + return mpack_wfloat(buf, buflen, tok); + case MPACK_TOKEN_BIN: + return mpack_wbin(buf, buflen, tok->length); + case MPACK_TOKEN_STR: + return mpack_wstr(buf, buflen, tok->length); + case MPACK_TOKEN_EXT: + return mpack_wext(buf, buflen, tok->data.ext_type, tok->length); + case MPACK_TOKEN_ARRAY: + return mpack_warray(buf, buflen, tok->length); + case MPACK_TOKEN_MAP: + return mpack_wmap(buf, buflen, tok->length); + default: + return MPACK_ERROR; + } +} + +static int mpack_wpending(char **buf, size_t *buflen, mpack_tokbuf_t *state) +{ + size_t count; + assert(state->ppos < state->plen); + count = MIN(state->plen - state->ppos, *buflen); + memcpy(*buf, state->pending + state->ppos, count); + state->ppos += count; + *buf += count; + *buflen -= count; + if (state->ppos == state->plen) { + state->plen = 0; + return MPACK_OK; + } + return MPACK_EOF; +} + +static int mpack_wpint(char **buf, size_t *buflen, mpack_value_t val) +{ + mpack_uint32_t hi = val.hi; + mpack_uint32_t lo = val.lo; + + if (hi) { + /* uint 64 */ + return mpack_w1(buf, buflen, 0xcf) || + mpack_w4(buf, buflen, hi) || + mpack_w4(buf, buflen, lo); + } else if (lo > 0xffff) { + /* uint 32 */ + return mpack_w1(buf, buflen, 0xce) || + mpack_w4(buf, buflen, lo); + } else if (lo > 0xff) { + /* uint 16 */ + return mpack_w1(buf, buflen, 0xcd) || + mpack_w2(buf, buflen, lo); + } else if (lo > 0x7f) { + /* uint 8 */ + return mpack_w1(buf, buflen, 0xcc) || + mpack_w1(buf, buflen, lo); + } else { + return mpack_w1(buf, buflen, lo); + } +} + +static int mpack_wnint(char **buf, size_t *buflen, mpack_value_t val) +{ + mpack_uint32_t hi = val.hi; + mpack_uint32_t lo = val.lo; + + if (lo < 0x80000000) { + /* int 64 */ + return mpack_w1(buf, buflen, 0xd3) || + mpack_w4(buf, buflen, hi) || + mpack_w4(buf, buflen, lo); + } else if (lo < 0xffff7fff) { + /* int 32 */ + return mpack_w1(buf, buflen, 0xd2) || + mpack_w4(buf, buflen, lo); + } else if (lo < 0xffffff7f) { + /* int 16 */ + return mpack_w1(buf, buflen, 0xd1) || + mpack_w2(buf, buflen, lo); + } else if (lo < 0xffffffe0) { + /* int 8 */ + return mpack_w1(buf, buflen, 0xd0) || + mpack_w1(buf, buflen, lo); + } else { + /* negative fixint */ + return mpack_w1(buf, buflen, (mpack_uint32_t)(0x100 + lo)); + } +} + +static int mpack_wfloat(char **buf, size_t *buflen, + const mpack_token_t *tok) +{ + if (tok->length == 4) { + return mpack_w1(buf, buflen, 0xca) || + mpack_w4(buf, buflen, tok->data.value.lo); + } else if (tok->length == 8) { + return mpack_w1(buf, buflen, 0xcb) || + mpack_w4(buf, buflen, tok->data.value.hi) || + mpack_w4(buf, buflen, tok->data.value.lo); + } else { + return MPACK_ERROR; + } +} + +static int mpack_wstr(char **buf, size_t *buflen, mpack_uint32_t len) +{ + if (len < 0x20) { + return mpack_w1(buf, buflen, 0xa0 | len); + } else if (len < 0x100) { + return mpack_w1(buf, buflen, 0xd9) || + mpack_w1(buf, buflen, len); + } else if (len < 0x10000) { + return mpack_w1(buf, buflen, 0xda) || + mpack_w2(buf, buflen, len); + } else { + return mpack_w1(buf, buflen, 0xdb) || + mpack_w4(buf, buflen, len); + } +} + +static int mpack_wbin(char **buf, size_t *buflen, mpack_uint32_t len) +{ + if (len < 0x100) { + return mpack_w1(buf, buflen, 0xc4) || + mpack_w1(buf, buflen, len); + } else if (len < 0x10000) { + return mpack_w1(buf, buflen, 0xc5) || + mpack_w2(buf, buflen, len); + } else { + return mpack_w1(buf, buflen, 0xc6) || + mpack_w4(buf, buflen, len); + } +} + +static int mpack_wext(char **buf, size_t *buflen, int type, + mpack_uint32_t len) +{ + mpack_uint32_t t; + assert(type >= 0 && type < 0x80); + t = (mpack_uint32_t)type; + switch (len) { + case 1: mpack_w1(buf, buflen, 0xd4); return mpack_w1(buf, buflen, t); + case 2: mpack_w1(buf, buflen, 0xd5); return mpack_w1(buf, buflen, t); + case 4: mpack_w1(buf, buflen, 0xd6); return mpack_w1(buf, buflen, t); + case 8: mpack_w1(buf, buflen, 0xd7); return mpack_w1(buf, buflen, t); + case 16: mpack_w1(buf, buflen, 0xd8); return mpack_w1(buf, buflen, t); + default: + if (len < 0x100) { + return mpack_w1(buf, buflen, 0xc7) || + mpack_w1(buf, buflen, len) || + mpack_w1(buf, buflen, t); + } else if (len < 0x10000) { + return mpack_w1(buf, buflen, 0xc8) || + mpack_w2(buf, buflen, len) || + mpack_w1(buf, buflen, t); + } else { + return mpack_w1(buf, buflen, 0xc9) || + mpack_w4(buf, buflen, len) || + mpack_w1(buf, buflen, t); + } + } +} + +static int mpack_warray(char **buf, size_t *buflen, mpack_uint32_t len) +{ + if (len < 0x10) { + return mpack_w1(buf, buflen, 0x90 | len); + } else if (len < 0x10000) { + return mpack_w1(buf, buflen, 0xdc) || + mpack_w2(buf, buflen, len); + } else { + return mpack_w1(buf, buflen, 0xdd) || + mpack_w4(buf, buflen, len); + } +} + +static int mpack_wmap(char **buf, size_t *buflen, mpack_uint32_t len) +{ + if (len < 0x10) { + return mpack_w1(buf, buflen, 0x80 | len); + } else if (len < 0x10000) { + return mpack_w1(buf, buflen, 0xde) || + mpack_w2(buf, buflen, len); + } else { + return mpack_w1(buf, buflen, 0xdf) || + mpack_w4(buf, buflen, len); + } +} + +static int mpack_w1(char **b, size_t *bl, mpack_uint32_t v) +{ + (*bl)--; + *(*b)++ = (char)(v & 0xff); + return MPACK_OK; +} + +static int mpack_w2(char **b, size_t *bl, mpack_uint32_t v) +{ + *bl -= 2; + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); + return MPACK_OK; +} + +static int mpack_w4(char **b, size_t *bl, mpack_uint32_t v) +{ + *bl -= 4; + *(*b)++ = (char)((v >> 24) & 0xff); + *(*b)++ = (char)((v >> 16) & 0xff); + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); + return MPACK_OK; +} + +static int mpack_value(mpack_token_type_t type, mpack_uint32_t length, + mpack_value_t value, mpack_token_t *tok) +{ + tok->type = type; + tok->length = length; + tok->data.value = value; + return MPACK_OK; +} + +static int mpack_blob(mpack_token_type_t type, mpack_uint32_t length, + int ext_type, mpack_token_t *tok) +{ + tok->type = type; + tok->length = length; + tok->data.ext_type = ext_type; + return MPACK_OK; +} + +static mpack_value_t mpack_byte(unsigned char byte) +{ + mpack_value_t rv; + rv.lo = byte; + rv.hi = 0; + return rv; +} diff --git a/src/mpack/mpack_core.h b/src/mpack/mpack_core.h new file mode 100644 index 0000000000..9edd13c41e --- /dev/null +++ b/src/mpack/mpack_core.h @@ -0,0 +1,87 @@ +#ifndef MPACK_CORE_H +#define MPACK_CORE_H + +#ifndef MPACK_API +# define MPACK_API extern +#endif + +#include <assert.h> +#include <limits.h> +#include <stddef.h> + +#ifdef __GNUC__ +# define FPURE __attribute__((const)) +# define FNONULL __attribute__((nonnull)) +# define FNONULL_ARG(x) __attribute__((nonnull x)) +# define FUNUSED __attribute__((unused)) +#else +# define FPURE +# define FNONULL +# define FNONULL_ARG(x) +# define FUNUSED +#endif + +#if UINT_MAX == 0xffffffff +typedef int mpack_sint32_t; +typedef unsigned int mpack_uint32_t; +#elif ULONG_MAX == 0xffffffff +typedef long mpack_sint32_t; +typedef unsigned long mpack_uint32_t; +#else +# error "can't find unsigned 32-bit integer type" +#endif + +typedef struct mpack_value_s { + mpack_uint32_t lo, hi; +} mpack_value_t; + + +enum { + MPACK_OK = 0, + MPACK_EOF = 1, + MPACK_ERROR = 2 +}; + +#define MPACK_MAX_TOKEN_LEN 9 /* 64-bit ints/floats plus type code */ + +typedef enum { + MPACK_TOKEN_NIL = 1, + MPACK_TOKEN_BOOLEAN = 2, + MPACK_TOKEN_UINT = 3, + MPACK_TOKEN_SINT = 4, + MPACK_TOKEN_FLOAT = 5, + MPACK_TOKEN_CHUNK = 6, + MPACK_TOKEN_ARRAY = 7, + MPACK_TOKEN_MAP = 8, + MPACK_TOKEN_BIN = 9, + MPACK_TOKEN_STR = 10, + MPACK_TOKEN_EXT = 11 +} mpack_token_type_t; + +typedef struct mpack_token_s { + mpack_token_type_t type; /* Type of token */ + mpack_uint32_t length; /* Byte length for str/bin/ext/chunk/float/int/uint. + Item count for array/map. */ + union { + mpack_value_t value; /* 32-bit parts of primitives (bool,int,float) */ + const char *chunk_ptr; /* Chunk of data from str/bin/ext */ + int ext_type; /* Type field for ext tokens */ + } data; +} mpack_token_t; + +typedef struct mpack_tokbuf_s { + char pending[MPACK_MAX_TOKEN_LEN]; + mpack_token_t pending_tok; + size_t ppos, plen; + mpack_uint32_t passthrough; +} mpack_tokbuf_t; + +#define MPACK_TOKBUF_INITIAL_VALUE { { 0 }, { 0, 0, { { 0, 0 } } }, 0, 0, 0 } + +MPACK_API void mpack_tokbuf_init(mpack_tokbuf_t *tb) FUNUSED FNONULL; +MPACK_API int mpack_read(mpack_tokbuf_t *tb, const char **b, size_t *bl, + mpack_token_t *tok) FUNUSED FNONULL; +MPACK_API int mpack_write(mpack_tokbuf_t *tb, char **b, size_t *bl, + const mpack_token_t *tok) FUNUSED FNONULL; + +#endif /* MPACK_CORE_H */ diff --git a/src/mpack/object.c b/src/mpack/object.c new file mode 100644 index 0000000000..0c7759ee51 --- /dev/null +++ b/src/mpack/object.c @@ -0,0 +1,195 @@ +#include <string.h> + +#include "object.h" + +static int mpack_parser_full(mpack_parser_t *w); +static mpack_node_t *mpack_parser_push(mpack_parser_t *w); +static mpack_node_t *mpack_parser_pop(mpack_parser_t *w); + +MPACK_API void mpack_parser_init(mpack_parser_t *parser, + mpack_uint32_t capacity) +{ + mpack_tokbuf_init(&parser->tokbuf); + parser->data.p = NULL; + parser->capacity = capacity ? capacity : MPACK_MAX_OBJECT_DEPTH; + parser->size = 0; + parser->exiting = 0; + memset(parser->items, 0, sizeof(mpack_node_t) * (parser->capacity + 1)); + parser->items[0].pos = (size_t)-1; + parser->status = 0; +} + +#define MPACK_EXCEPTION_CHECK(parser) \ + do { \ + if (parser->status == MPACK_EXCEPTION) { \ + return MPACK_EXCEPTION; \ + } \ + } while (0) + +#define MPACK_WALK(action) \ + do { \ + mpack_node_t *n; \ + \ + if (parser->exiting) goto exit; \ + if (mpack_parser_full(parser)) return MPACK_NOMEM; \ + n = mpack_parser_push(parser); \ + action; \ + MPACK_EXCEPTION_CHECK(parser); \ + parser->exiting = 1; \ + return MPACK_EOF; \ + \ +exit: \ + parser->exiting = 0; \ + while ((n = mpack_parser_pop(parser))) { \ + exit_cb(parser, n); \ + MPACK_EXCEPTION_CHECK(parser); \ + if (!parser->size) return MPACK_OK; \ + } \ + \ + return MPACK_EOF; \ + } while (0) + +MPACK_API int mpack_parse_tok(mpack_parser_t *parser, mpack_token_t tok, + mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) +{ + MPACK_EXCEPTION_CHECK(parser); + MPACK_WALK({n->tok = tok; enter_cb(parser, n);}); +} + +MPACK_API int mpack_unparse_tok(mpack_parser_t *parser, mpack_token_t *tok, + mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) +{ + MPACK_EXCEPTION_CHECK(parser); + MPACK_WALK({enter_cb(parser, n); *tok = n->tok;}); +} + +MPACK_API int mpack_parse(mpack_parser_t *parser, const char **buf, + size_t *buflen, mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) +{ + int status = MPACK_EOF; + MPACK_EXCEPTION_CHECK(parser); + + while (*buflen && status) { + mpack_token_t tok; + mpack_tokbuf_t *tb = &parser->tokbuf; + const char *buf_save = *buf; + size_t buflen_save = *buflen; + + if ((status = mpack_read(tb, buf, buflen, &tok)) == MPACK_EOF) continue; + else if (status == MPACK_ERROR) goto rollback; + + do { + status = mpack_parse_tok(parser, tok, enter_cb, exit_cb); + MPACK_EXCEPTION_CHECK(parser); + } while (parser->exiting); + + if (status != MPACK_NOMEM) continue; + +rollback: + /* restore buf/buflen so the next call will try to read the same token */ + *buf = buf_save; + *buflen = buflen_save; + break; + } + + return status; +} + +MPACK_API int mpack_unparse(mpack_parser_t *parser, char **buf, size_t *buflen, + mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) +{ + int status = MPACK_EOF; + MPACK_EXCEPTION_CHECK(parser); + + while (*buflen && status) { + int write_status; + mpack_token_t tok; + mpack_tokbuf_t *tb = &parser->tokbuf; + + if (!tb->plen) + parser->status = mpack_unparse_tok(parser, &tok, enter_cb, exit_cb); + + MPACK_EXCEPTION_CHECK(parser); + + status = parser->status; + + if (status == MPACK_NOMEM) + break; + + if (parser->exiting) { + write_status = mpack_write(tb, buf, buflen, &tok); + status = write_status ? write_status : status; + } + } + + return status; +} + +MPACK_API void mpack_parser_copy(mpack_parser_t *dst, mpack_parser_t *src) +{ + mpack_uint32_t i; + mpack_uint32_t dst_capacity = dst->capacity; + assert(src->capacity <= dst_capacity); + /* copy all fields except the stack */ + memcpy(dst, src, sizeof(mpack_one_parser_t) - sizeof(mpack_node_t)); + /* reset capacity */ + dst->capacity = dst_capacity; + /* copy the stack */ + for (i = 0; i <= src->capacity; i++) { + dst->items[i] = src->items[i]; + } +} + +static int mpack_parser_full(mpack_parser_t *parser) +{ + return parser->size == parser->capacity; +} + +static mpack_node_t *mpack_parser_push(mpack_parser_t *parser) +{ + mpack_node_t *top; + assert(parser->size < parser->capacity); + top = parser->items + parser->size + 1; + top->data[0].p = NULL; + top->data[1].p = NULL; + top->pos = 0; + top->key_visited = 0; + /* increase size and invoke callback, passing parent node if any */ + parser->size++; + return top; +} + +static mpack_node_t *mpack_parser_pop(mpack_parser_t *parser) +{ + mpack_node_t *top, *parent; + assert(parser->size); + top = parser->items + parser->size; + + if (top->tok.type > MPACK_TOKEN_CHUNK && top->pos < top->tok.length) { + /* continue processing children */ + return NULL; + } + + parent = MPACK_PARENT_NODE(top); + if (parent) { + /* we use parent->tok.length to keep track of how many children remain. + * update it to reflect the processed node. */ + if (top->tok.type == MPACK_TOKEN_CHUNK) { + parent->pos += top->tok.length; + } else if (parent->tok.type == MPACK_TOKEN_MAP) { + /* maps allow up to 2^32 - 1 pairs, so to allow this many items in a + * 32-bit length variable we use an additional flag to determine if the + * key of a certain position was visited */ + if (parent->key_visited) { + parent->pos++; + } + parent->key_visited = !parent->key_visited; + } else { + parent->pos++; + } + } + + parser->size--; + return top; +} + diff --git a/src/mpack/object.h b/src/mpack/object.h new file mode 100644 index 0000000000..5327e56e18 --- /dev/null +++ b/src/mpack/object.h @@ -0,0 +1,86 @@ +#ifndef MPACK_OBJECT_H +#define MPACK_OBJECT_H + +#include "mpack_core.h" +#include "conv.h" + +#ifndef MPACK_MAX_OBJECT_DEPTH +# define MPACK_MAX_OBJECT_DEPTH 32 +#endif + +#define MPACK_PARENT_NODE(n) (((n) - 1)->pos == (size_t)-1 ? NULL : (n) - 1) + +#define MPACK_THROW(parser) \ + do { \ + parser->status = MPACK_EXCEPTION; \ + return; \ + } while (0) + +enum { + MPACK_EXCEPTION = -1, + MPACK_NOMEM = MPACK_ERROR + 1 +}; + +/* Storing integer in pointers in undefined behavior according to the C + * standard. Define a union type to accomodate arbitrary user data associated + * with nodes(and with requests in rpc.h). */ +typedef union { + void *p; + mpack_uintmax_t u; + mpack_sintmax_t i; + double d; +} mpack_data_t; + +typedef struct mpack_node_s { + mpack_token_t tok; + size_t pos; + /* flag to determine if the key was visited when traversing a map */ + int key_visited; + /* allow 2 instances mpack_data_t per node. the reason is that when + * serializing, the user may need to keep track of traversal state besides the + * parent node reference */ + mpack_data_t data[2]; +} mpack_node_t; + +#define MPACK_PARSER_STRUCT(c) \ + struct { \ + mpack_data_t data; \ + mpack_uint32_t size, capacity; \ + int status; \ + int exiting; \ + mpack_tokbuf_t tokbuf; \ + mpack_node_t items[c + 1]; \ + } + +/* Some compilers warn against anonymous structs: + * https://github.com/libmpack/libmpack/issues/6 */ +typedef MPACK_PARSER_STRUCT(0) mpack_one_parser_t; + +#define MPACK_PARSER_STRUCT_SIZE(c) \ + (sizeof(mpack_node_t) * c + \ + sizeof(mpack_one_parser_t)) + +typedef MPACK_PARSER_STRUCT(MPACK_MAX_OBJECT_DEPTH) mpack_parser_t; +typedef void(*mpack_walk_cb)(mpack_parser_t *w, mpack_node_t *n); + +MPACK_API void mpack_parser_init(mpack_parser_t *p, mpack_uint32_t c) + FUNUSED FNONULL; + +MPACK_API int mpack_parse_tok(mpack_parser_t *walker, mpack_token_t tok, + mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) + FUNUSED FNONULL_ARG((1,3,4)); +MPACK_API int mpack_unparse_tok(mpack_parser_t *walker, mpack_token_t *tok, + mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) + FUNUSED FNONULL_ARG((1,2,3,4)); + +MPACK_API int mpack_parse(mpack_parser_t *parser, const char **b, size_t *bl, + mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) + FUNUSED FNONULL_ARG((1,2,3,4,5)); +MPACK_API int mpack_unparse(mpack_parser_t *parser, char **b, size_t *bl, + mpack_walk_cb enter_cb, mpack_walk_cb exit_cb) + FUNUSED FNONULL_ARG((1,2,3,4,5)); + +MPACK_API void mpack_parser_copy(mpack_parser_t *d, mpack_parser_t *s) + FUNUSED FNONULL; + +#endif /* MPACK_OBJECT_H */ diff --git a/src/mpack/rpc.c b/src/mpack/rpc.c new file mode 100644 index 0000000000..3b2b328065 --- /dev/null +++ b/src/mpack/rpc.c @@ -0,0 +1,331 @@ +#include <string.h> + +#include "rpc.h" + +enum { + MPACK_RPC_RECEIVE_ARRAY = 1, + MPACK_RPC_RECEIVE_TYPE, + MPACK_RPC_RECEIVE_ID +}; + +static mpack_rpc_header_t mpack_rpc_request_hdr(void); +static mpack_rpc_header_t mpack_rpc_reply_hdr(void); +static mpack_rpc_header_t mpack_rpc_notify_hdr(void); +static int mpack_rpc_put(mpack_rpc_session_t *s, mpack_rpc_message_t m); +static int mpack_rpc_pop(mpack_rpc_session_t *s, mpack_rpc_message_t *m); +static void mpack_rpc_reset_hdr(mpack_rpc_header_t *hdr); + +MPACK_API void mpack_rpc_session_init(mpack_rpc_session_t *session, + mpack_uint32_t capacity) +{ + session->capacity = capacity ? capacity : MPACK_RPC_MAX_REQUESTS; + session->request_id = 0; + mpack_tokbuf_init(&session->reader); + mpack_tokbuf_init(&session->writer); + mpack_rpc_reset_hdr(&session->receive); + mpack_rpc_reset_hdr(&session->send); + memset(session->slots, 0, + sizeof(struct mpack_rpc_slot_s) * session->capacity); +} + +MPACK_API int mpack_rpc_receive_tok(mpack_rpc_session_t *session, + mpack_token_t tok, mpack_rpc_message_t *msg) +{ + int type; + + if (session->receive.index == 0) { + if (tok.type != MPACK_TOKEN_ARRAY) + /* not an array */ + return MPACK_RPC_EARRAY; + + if (tok.length < 3 || tok.length > 4) + /* invalid array length */ + return MPACK_RPC_EARRAYL; + + session->receive.toks[0] = tok; + session->receive.index++; + return MPACK_EOF; /* get the type */ + } + + if (session->receive.index == 1) { + + if (tok.type != MPACK_TOKEN_UINT || tok.length > 1 || tok.data.value.lo > 2) + /* invalid type */ + return MPACK_RPC_ETYPE; + + if (tok.data.value.lo < 2 && session->receive.toks[0].length != 4) + /* request or response with array length != 4 */ + return MPACK_RPC_EARRAYL; + + if (tok.data.value.lo == 2 && session->receive.toks[0].length != 3) + /* notification with array length != 3 */ + return MPACK_RPC_EARRAYL; + + session->receive.toks[1] = tok; + session->receive.index++; + + if (tok.data.value.lo < 2) return MPACK_EOF; + + type = MPACK_RPC_NOTIFICATION; + goto end; + } + + assert(session->receive.index == 2); + + if (tok.type != MPACK_TOKEN_UINT || tok.length > 4) + /* invalid request/response id */ + return MPACK_RPC_EMSGID; + + msg->id = tok.data.value.lo; + msg->data.p = NULL; + type = (int)session->receive.toks[1].data.value.lo + MPACK_RPC_REQUEST; + + if (type == MPACK_RPC_RESPONSE && !mpack_rpc_pop(session, msg)) + /* response with invalid id */ + return MPACK_RPC_ERESPID; + +end: + mpack_rpc_reset_hdr(&session->receive); + return type; +} + +MPACK_API int mpack_rpc_request_tok(mpack_rpc_session_t *session, + mpack_token_t *tok, mpack_data_t data) +{ + if (session->send.index == 0) { + int status; + mpack_rpc_message_t msg; + do { + msg.id = session->request_id; + msg.data = data; + session->send = mpack_rpc_request_hdr(); + session->send.toks[2].type = MPACK_TOKEN_UINT; + session->send.toks[2].data.value.lo = msg.id; + session->send.toks[2].data.value.hi = 0; + *tok = session->send.toks[0]; + status = mpack_rpc_put(session, msg); + if (status == -1) return MPACK_NOMEM; + session->request_id = (session->request_id + 1) % 0xffffffff; + } while (!status); + session->send.index++; + return MPACK_EOF; + } + + if (session->send.index == 1) { + *tok = session->send.toks[1]; + session->send.index++; + return MPACK_EOF; + } + + assert(session->send.index == 2); + *tok = session->send.toks[2]; + mpack_rpc_reset_hdr(&session->send); + return MPACK_OK; +} + +MPACK_API int mpack_rpc_reply_tok(mpack_rpc_session_t *session, + mpack_token_t *tok, mpack_uint32_t id) +{ + if (session->send.index == 0) { + session->send = mpack_rpc_reply_hdr(); + session->send.toks[2].type = MPACK_TOKEN_UINT; + session->send.toks[2].data.value.lo = id; + session->send.toks[2].data.value.hi = 0; + *tok = session->send.toks[0]; + session->send.index++; + return MPACK_EOF; + } + + if (session->send.index == 1) { + *tok = session->send.toks[1]; + session->send.index++; + return MPACK_EOF; + } + + assert(session->send.index == 2); + *tok = session->send.toks[2]; + mpack_rpc_reset_hdr(&session->send); + return MPACK_OK; +} + +MPACK_API int mpack_rpc_notify_tok(mpack_rpc_session_t *session, + mpack_token_t *tok) +{ + if (session->send.index == 0) { + session->send = mpack_rpc_notify_hdr(); + *tok = session->send.toks[0]; + session->send.index++; + return MPACK_EOF; + } + + assert(session->send.index == 1); + *tok = session->send.toks[1]; + mpack_rpc_reset_hdr(&session->send); + return MPACK_OK; +} + +MPACK_API int mpack_rpc_receive(mpack_rpc_session_t *session, const char **buf, + size_t *buflen, mpack_rpc_message_t *msg) +{ + int status; + + do { + mpack_token_t tok; + status = mpack_read(&session->reader, buf, buflen, &tok); + if (status) break; + status = mpack_rpc_receive_tok(session, tok, msg); + if (status >= MPACK_RPC_REQUEST) break; + } while (*buflen); + + return status; +} + +MPACK_API int mpack_rpc_request(mpack_rpc_session_t *session, char **buf, + size_t *buflen, mpack_data_t data) +{ + int status = MPACK_EOF; + + while (status && *buflen) { + int write_status; + mpack_token_t tok; + if (!session->writer.plen) { + status = mpack_rpc_request_tok(session, &tok, data); + } + if (status == MPACK_NOMEM) break; + write_status = mpack_write(&session->writer, buf, buflen, &tok); + status = write_status ? write_status : status; + } + + return status; +} + +MPACK_API int mpack_rpc_reply(mpack_rpc_session_t *session, char **buf, + size_t *buflen, mpack_uint32_t id) +{ + int status = MPACK_EOF; + + while (status && *buflen) { + int write_status; + mpack_token_t tok; + if (!session->writer.plen) { + status = mpack_rpc_reply_tok(session, &tok, id); + } + write_status = mpack_write(&session->writer, buf, buflen, &tok); + status = write_status ? write_status : status; + } + + return status; +} + +MPACK_API int mpack_rpc_notify(mpack_rpc_session_t *session, char **buf, + size_t *buflen) +{ + int status = MPACK_EOF; + + while (status && *buflen) { + int write_status; + mpack_token_t tok; + if (!session->writer.plen) { + status = mpack_rpc_notify_tok(session, &tok); + } + write_status = mpack_write(&session->writer, buf, buflen, &tok); + status = write_status ? write_status : status; + } + + return status; +} + +MPACK_API void mpack_rpc_session_copy(mpack_rpc_session_t *dst, + mpack_rpc_session_t *src) +{ + mpack_uint32_t i; + mpack_uint32_t dst_capacity = dst->capacity; + assert(src->capacity <= dst_capacity); + /* copy all fields except slots */ + memcpy(dst, src, sizeof(mpack_rpc_one_session_t) - + sizeof(struct mpack_rpc_slot_s)); + /* reset capacity */ + dst->capacity = dst_capacity; + /* reinsert requests */ + memset(dst->slots, 0, sizeof(struct mpack_rpc_slot_s) * dst->capacity); + for (i = 0; i < src->capacity; i++) { + if (src->slots[i].used) mpack_rpc_put(dst, src->slots[i].msg); + } +} + +static mpack_rpc_header_t mpack_rpc_request_hdr(void) +{ + mpack_rpc_header_t hdr; + hdr.index = 0; + hdr.toks[0].type = MPACK_TOKEN_ARRAY; + hdr.toks[0].length = 4; + hdr.toks[1].type = MPACK_TOKEN_UINT; + hdr.toks[1].data.value.lo = 0; + hdr.toks[1].data.value.hi = 0; + return hdr; +} + +static mpack_rpc_header_t mpack_rpc_reply_hdr(void) +{ + mpack_rpc_header_t hdr = mpack_rpc_request_hdr(); + hdr.toks[1].data.value.lo = 1; + hdr.toks[1].data.value.hi = 0; + return hdr; +} + +static mpack_rpc_header_t mpack_rpc_notify_hdr(void) +{ + mpack_rpc_header_t hdr = mpack_rpc_request_hdr(); + hdr.toks[0].length = 3; + hdr.toks[1].data.value.lo = 2; + hdr.toks[1].data.value.hi = 0; + return hdr; +} + +static int mpack_rpc_put(mpack_rpc_session_t *session, mpack_rpc_message_t msg) +{ + struct mpack_rpc_slot_s *slot = NULL; + mpack_uint32_t i; + mpack_uint32_t hash = msg.id % session->capacity; + + for (i = 0; i < session->capacity; i++) { + if (!session->slots[hash].used || session->slots[hash].msg.id == msg.id) { + slot = session->slots + hash; + break; + } + hash = hash > 0 ? hash - 1 : session->capacity - 1; + } + + if (!slot) return -1; /* no space */ + if (slot->msg.id == msg.id && slot->used) return 0; /* duplicate key */ + slot->msg = msg; + slot->used = 1; + return 1; +} + +static int mpack_rpc_pop(mpack_rpc_session_t *session, mpack_rpc_message_t *msg) +{ + struct mpack_rpc_slot_s *slot = NULL; + mpack_uint32_t i; + mpack_uint32_t hash = msg->id % session->capacity; + + for (i = 0; i < session->capacity; i++) { + if (session->slots[hash].used && session->slots[hash].msg.id == msg->id) { + slot = session->slots + hash; + break; + } + hash = hash > 0 ? hash - 1 : session->capacity - 1; + } + + if (!slot) return 0; + + *msg = slot->msg; + slot->used = 0; + + return 1; +} + +static void mpack_rpc_reset_hdr(mpack_rpc_header_t *hdr) +{ + hdr->index = 0; +} diff --git a/src/mpack/rpc.h b/src/mpack/rpc.h new file mode 100644 index 0000000000..c1e8d656b5 --- /dev/null +++ b/src/mpack/rpc.h @@ -0,0 +1,83 @@ +#ifndef MPACK_RPC_H +#define MPACK_RPC_H + +#include "mpack_core.h" +#include "object.h" + +#ifndef MPACK_RPC_MAX_REQUESTS +# define MPACK_RPC_MAX_REQUESTS 32 +#endif + +enum { + MPACK_RPC_REQUEST = MPACK_NOMEM + 1, + MPACK_RPC_RESPONSE, + MPACK_RPC_NOTIFICATION, + MPACK_RPC_ERROR +}; + +enum { + MPACK_RPC_EARRAY = MPACK_RPC_ERROR, + MPACK_RPC_EARRAYL, + MPACK_RPC_ETYPE, + MPACK_RPC_EMSGID, + MPACK_RPC_ERESPID +}; + +typedef struct mpack_rpc_header_s { + mpack_token_t toks[3]; + int index; +} mpack_rpc_header_t; + +typedef struct mpack_rpc_message_s { + mpack_uint32_t id; + mpack_data_t data; +} mpack_rpc_message_t; + +struct mpack_rpc_slot_s { + int used; + mpack_rpc_message_t msg; +}; + +#define MPACK_RPC_SESSION_STRUCT(c) \ + struct { \ + mpack_tokbuf_t reader, writer; \ + mpack_rpc_header_t receive, send; \ + mpack_uint32_t request_id, capacity; \ + struct mpack_rpc_slot_s slots[c]; \ + } + +/* Some compilers warn against anonymous structs: + * https://github.com/libmpack/libmpack/issues/6 */ +typedef MPACK_RPC_SESSION_STRUCT(1) mpack_rpc_one_session_t; + +#define MPACK_RPC_SESSION_STRUCT_SIZE(c) \ + (sizeof(struct mpack_rpc_slot_s) * (c - 1) + \ + sizeof(mpack_rpc_one_session_t)) + +typedef MPACK_RPC_SESSION_STRUCT(MPACK_RPC_MAX_REQUESTS) mpack_rpc_session_t; + +MPACK_API void mpack_rpc_session_init(mpack_rpc_session_t *s, mpack_uint32_t c) + FUNUSED FNONULL; + +MPACK_API int mpack_rpc_receive_tok(mpack_rpc_session_t *s, mpack_token_t t, + mpack_rpc_message_t *msg) FUNUSED FNONULL; +MPACK_API int mpack_rpc_request_tok(mpack_rpc_session_t *s, mpack_token_t *t, + mpack_data_t d) FUNUSED FNONULL_ARG((1,2)); +MPACK_API int mpack_rpc_reply_tok(mpack_rpc_session_t *s, mpack_token_t *t, + mpack_uint32_t i) FUNUSED FNONULL; +MPACK_API int mpack_rpc_notify_tok(mpack_rpc_session_t *s, mpack_token_t *t) + FUNUSED FNONULL; + +MPACK_API int mpack_rpc_receive(mpack_rpc_session_t *s, const char **b, + size_t *bl, mpack_rpc_message_t *m) FUNUSED FNONULL; +MPACK_API int mpack_rpc_request(mpack_rpc_session_t *s, char **b, size_t *bl, + mpack_data_t d) FUNUSED FNONULL_ARG((1,2,3)); +MPACK_API int mpack_rpc_reply(mpack_rpc_session_t *s, char **b, size_t *bl, + mpack_uint32_t i) FNONULL FUNUSED; +MPACK_API int mpack_rpc_notify(mpack_rpc_session_t *s, char **b, size_t *bl) + FNONULL FUNUSED; + +MPACK_API void mpack_rpc_session_copy(mpack_rpc_session_t *d, + mpack_rpc_session_t *s) FUNUSED FNONULL; + +#endif /* MPACK_RPC_H */ diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index e4d7115654..331ab16dd7 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -29,7 +29,7 @@ set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua) set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua) set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack) set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) -set(MSGPACK_LUA_C_BINDINGS ${GENERATED_DIR}/msgpack_lua_c_bindings.generated.c) +set(LUA_API_C_BINDINGS ${GENERATED_DIR}/lua_api_c_bindings.generated.c) set(HEADER_GENERATOR ${GENERATOR_DIR}/gen_declarations.lua) set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include) set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.generated.h) @@ -56,6 +56,8 @@ set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h) set(LUA_VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/lua/vim.lua) set(LUA_SHARED_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/shared.lua) set(LUA_INSPECT_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/inspect.lua) +set(LUA_F_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/F.lua) +set(LUA_META_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_meta.lua) set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua) set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint") @@ -85,8 +87,8 @@ file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src) file(GLOB NVIM_SOURCES *.c) file(GLOB NVIM_HEADERS *.h) -file(GLOB XDIFF_SOURCES xdiff/*.c) -file(GLOB XDIFF_HEADERS xdiff/*.h) +file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c) +file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h) foreach(subdir os @@ -169,8 +171,8 @@ foreach(sfile ${CONV_SOURCES}) message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") endif() endforeach() -# xdiff: inlined external project, we don't maintain it. #9306 -list(APPEND CONV_SOURCES ${XDIFF_SOURCES}) +# xdiff, mpack: inlined external project, we don't maintain it. #9306 +list(APPEND CONV_SOURCES ${EXTERNAL_SOURCES}) if(NOT MSVC) set_source_files_properties( @@ -305,11 +307,11 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES} add_custom_command( OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} - ${API_METADATA} ${MSGPACK_LUA_C_BINDINGS} + ${API_METADATA} ${LUA_API_C_BINDINGS} COMMAND ${LUA_PRG} ${API_DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} ${API_METADATA} - ${MSGPACK_LUA_C_BINDINGS} + ${LUA_API_C_BINDINGS} ${API_HEADERS} DEPENDS ${API_HEADERS} @@ -325,15 +327,19 @@ add_custom_command( ${LUA_VIM_MODULE_SOURCE} vim_module ${LUA_SHARED_MODULE_SOURCE} shared_module ${LUA_INSPECT_MODULE_SOURCE} inspect_module + ${LUA_F_MODULE_SOURCE} lua_F_module + ${LUA_META_MODULE_SOURCE} lua_meta_module DEPENDS ${CHAR_BLOB_GENERATOR} ${LUA_VIM_MODULE_SOURCE} ${LUA_SHARED_MODULE_SOURCE} ${LUA_INSPECT_MODULE_SOURCE} + ${LUA_F_MODULE_SOURCE} + ${LUA_META_MODULE_SOURCE} ) list(APPEND NVIM_GENERATED_SOURCES - "${MSGPACK_LUA_C_BINDINGS}" + "${LUA_API_C_BINDINGS}" ) add_custom_command( @@ -465,7 +471,7 @@ endif() add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS} - ${XDIFF_SOURCES} ${XDIFF_HEADERS}) + ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) @@ -596,7 +602,7 @@ add_library( EXCLUDE_FROM_ALL ${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} - ${XDIFF_SOURCES} ${XDIFF_HEADERS} + ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS} ) set_property(TARGET libnvim APPEND PROPERTY INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS}) @@ -626,7 +632,7 @@ else() EXCLUDE_FROM_ALL ${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} - ${XDIFF_SOURCES} ${XDIFF_HEADERS} + ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS} ${UNIT_TEST_FIXTURES} ) target_link_libraries(nvim-test ${NVIM_TEST_LINK_LIBRARIES}) diff --git a/src/nvim/README.md b/src/nvim/README.md index affc5c79cc..4efb42b896 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -75,8 +75,108 @@ Logs will be written to `${HOME}/logs/*san.PID` then. For more information: https://github.com/google/sanitizers/wiki/SanitizerCommonFlags -TUI debugging -------------- +Debug: Performance +------------------ + +### Profiling (easy) + +For debugging performance bottlenecks in any code, there is a simple (and very +effective) approach: + +1. Run the slow code in a loop. +2. Break execution ~5 times and save the stacktrace. +3. The cause of the bottleneck will (almost always) appear in most of the stacktraces. + +### Profiling (fancy) + +For more advanced profiling, consider `perf` + `flamegraph`. + +### USDT profiling (powerful) + +Or you can use USDT probes via `NVIM_PROBE` ([#12036](https://github.com/neovim/neovim/pull/12036)). + +> USDT is basically a way to define stable probe points in userland binaries. +> The benefit of bcc is the ability to define logic to go along with the probe +> points. + +Tools: +- bpftrace provides an awk-like language to the kernel bytecode, BPF. +- BCC provides a subset of C. Provides more complex logic than bpftrace, but takes a bit more effort. + +Example using bpftrace to track slow vim functions, and print out any files +that were opened during the trace. At the end, it prints a histogram of +function timing: + + #!/usr/bin/env bpftrace + + BEGIN { + @depth = -1; + } + + tracepoint:sched:sched_process_fork /@pidmap[args->parent_pid]/ { + @pidmap[args->child_pid] = 1; + } + + tracepoint:sched:sched_process_exit /@pidmap[args->pid]/ { + delete(@pidmap[args->pid]); + } + + usdt:build/bin/nvim:neovim:eval__call_func__entry { + @pidmap[pid] = 1; + @depth++; + @funcentry[@depth] = nsecs; + } + + usdt:build/bin/nvim:neovim:eval__call_func__return { + $func = str(arg0); + $msecs = (nsecs - @funcentry[@depth]) / 1000000; + + @time_histo = hist($msecs); + + if ($msecs >= 1000) { + printf("%u ms for %s\n", $msecs, $func); + print(@files); + } + + clear(@files); + delete(@funcentry[@depth]); + @depth--; + } + + tracepoint:syscalls:sys_enter_open, + tracepoint:syscalls:sys_enter_openat { + if (@pidmap[pid] == 1 && @depth >= 0) { + @files[str(args->filename)] = count(); + } + } + + END { + clear(@depth); + } + + $ sudo bpftrace funcslower.bt + 1527 ms for Slower + @files[/usr/lib/libstdc++.so.6]: 2 + @files[/etc/fish/config.fish]: 2 + <snip> + + ^C + @time_histo: + [0] 71430 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| + [1] 346 | | + [2, 4) 208 | | + [4, 8) 91 | | + [8, 16) 22 | | + [16, 32) 85 | | + [32, 64) 7 | | + [64, 128) 0 | | + [128, 256) 0 | | + [256, 512) 6 | | + [512, 1K) 1 | | + [1K, 2K) 5 | | + +Debug: TUI +---------- ### TUI troubleshoot diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index f84e8c99a4..3808f601d9 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -315,7 +315,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, } rv.size = (size_t)(end - start); - rv.items = xcalloc(sizeof(Object), rv.size); + rv.items = xcalloc(rv.size, sizeof(Object)); if (!buf_collect_lines(buf, rv.size, start, (channel_id != VIML_INTERNAL_CALL), &rv, err)) { @@ -1232,7 +1232,7 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict) return rv; } -/// Returns position for a given extmark id +/// Gets the position (0-indexed) of an extmark. /// /// @param buffer Buffer handle, or 0 for current buffer /// @param ns_id Namespace id from |nvim_create_namespace()| @@ -1240,7 +1240,8 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict) /// @param opts Optional parameters. Keys: /// - details: Whether to include the details dict /// @param[out] err Error details, if any -/// @return (row, col) tuple or empty list () if extmark id was absent +/// @return 0-indexed (row, col) tuple or empty list () if extmark id was +/// absent ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, Integer id, Dictionary opts, Error *err) @@ -1320,10 +1321,10 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// /// @param buffer Buffer handle, or 0 for current buffer /// @param ns_id Namespace id from |nvim_create_namespace()| -/// @param start Start of range, given as (row, col) or valid extmark id -/// (whose position defines the bound) -/// @param end End of range, given as (row, col) or valid extmark id -/// (whose position defines the bound) +/// @param start Start of range: a 0-indexed (row, col) or valid extmark id +/// (whose position defines the bound). |api-indexing| +/// @param end End of range (inclusive): a 0-indexed (row, col) or valid +/// extmark id (whose position defines the bound). |api-indexing| /// @param opts Optional parameters. Keys: /// - limit: Maximum number of marks to return /// - details Whether to include the details dict @@ -1424,8 +1425,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, /// /// @param buffer Buffer handle, or 0 for current buffer /// @param ns_id Namespace id from |nvim_create_namespace()| -/// @param line Line where to place the mark, 0-based -/// @param col Column where to place the mark, 0-based +/// @param line Line where to place the mark, 0-based. |api-indexing| +/// @param col Column where to place the mark, 0-based. |api-indexing| /// @param opts Optional parameters. /// - id : id of the extmark to edit. /// - end_line : ending line of the mark, 0-based inclusive. @@ -1433,8 +1434,14 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, /// - hl_group : name of the highlight group used to highlight /// this mark. /// - virt_text : virtual text to link to this mark. -/// - virt_text_pos : positioning of virtual text. Possible -/// values: +/// A list of [text, highlight] tuples, each representing a +/// text chunk with specified highlight. `highlight` element +/// can either be a a single highlight group, or an array of +/// multiple highlight groups that will be stacked +/// (highest priority last). A highlight group can be supplied +/// either as a string or as an integer, the latter which +/// can be obtained using |nvim_get_hl_id_by_name|. +/// - virt_text_pos : position of virtual text. Possible values: /// - "eol": right after eol character (default) /// - "overlay": display over the specified column, without /// shifting the underlying text. @@ -1480,7 +1487,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { - api_set_error(err, kErrorTypeValidation, "Invalid buffer id"); return 0; } @@ -1489,21 +1495,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, return 0; } - size_t len = 0; - if (line < 0 || line > buf->b_ml.ml_line_count) { - api_set_error(err, kErrorTypeValidation, "line value outside range"); - return 0; - } else if (line < buf->b_ml.ml_line_count) { - len = STRLEN(ml_get_buf(buf, (linenr_T)line+1, false)); - } - - if (col == -1) { - col = (Integer)len; - } else if (col < -1 || col > (Integer)len) { - api_set_error(err, kErrorTypeValidation, "col value outside range"); - return 0; - } - bool ephemeral = false; uint64_t id = 0; @@ -1575,7 +1566,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, "virt_text is not an Array"); goto error; } - decor.virt_text = parse_virt_text(v->data.array, err); + decor.virt_text = parse_virt_text(v->data.array, err, + &decor.virt_text_width); if (ERROR_SET(err)) { goto error; } @@ -1674,6 +1666,22 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, } } + size_t len = 0; + if (line < 0 || line > buf->b_ml.ml_line_count) { + api_set_error(err, kErrorTypeValidation, "line value outside range"); + return 0; + } else if (line < buf->b_ml.ml_line_count) { + len = ephemeral ? MAXCOL : STRLEN(ml_get_buf(buf, (linenr_T)line+1, false)); + } + + if (col == -1) { + col = (Integer)len; + } else if (col < -1 || col > (Integer)len) { + api_set_error(err, kErrorTypeValidation, "col value outside range"); + return 0; + } + + // Only error out if they try to set end_right_gravity without // setting end_col or end_line if (line2 == -1 && col2 == -1 && end_gravity_set) { @@ -1684,7 +1692,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, if (col2 >= 0) { if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) { - len = STRLEN(ml_get_buf(buf, (linenr_T)line2 + 1, false)); + len = ephemeral ? MAXCOL : STRLEN( + ml_get_buf(buf, (linenr_T)line2 + 1, false)); } else if (line2 == buf->b_ml.ml_line_count) { // We are trying to add an extmark past final newline len = 0; @@ -1890,80 +1899,6 @@ void nvim_buf_clear_namespace(Buffer buffer, (int)line_end-1, MAXCOL); } -/// Set the virtual text (annotation) for a buffer line. -/// -/// By default (and currently the only option) the text will be placed after -/// the buffer text. Virtual text will never cause reflow, rather virtual -/// text will be truncated at the end of the screen line. The virtual text will -/// begin one cell (|lcs-eol| or space) after the ordinary text. -/// -/// Namespaces are used to support batch deletion/updating of virtual text. -/// To create a namespace, use |nvim_create_namespace()|. Virtual text is -/// cleared using |nvim_buf_clear_namespace()|. The same `ns_id` can be used for -/// both virtual text and highlights added by |nvim_buf_add_highlight()|, both -/// can then be cleared with a single call to |nvim_buf_clear_namespace()|. If -/// the virtual text never will be cleared by an API call, pass `ns_id = -1`. -/// -/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the -/// virtual text, the allocated id is then returned. -/// -/// @param buffer Buffer handle, or 0 for current buffer -/// @param ns_id Namespace to use or 0 to create a namespace, -/// or -1 for a ungrouped annotation -/// @param line Line to annotate with virtual text (zero-indexed) -/// @param chunks A list of [text, hl_group] arrays, each representing a -/// text chunk with specified highlight. `hl_group` element -/// can be omitted for no highlight. -/// @param opts Optional parameters. Currently not used. -/// @param[out] err Error details, if any -/// @return The ns_id that was used -Integer nvim_buf_set_virtual_text(Buffer buffer, - Integer src_id, - Integer line, - Array chunks, - Dictionary opts, - Error *err) - FUNC_API_SINCE(5) -{ - buf_T *buf = find_buffer_by_handle(buffer, err); - if (!buf) { - return 0; - } - - if (line < 0 || line >= MAXLNUM) { - api_set_error(err, kErrorTypeValidation, "Line number outside range"); - return 0; - } - - if (opts.size > 0) { - api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); - return 0; - } - - uint64_t ns_id = src2ns(&src_id); - - VirtText virt_text = parse_virt_text(chunks, err); - if (ERROR_SET(err)) { - return 0; - } - - - VirtText *existing = decor_find_virttext(buf, (int)line, ns_id); - - if (existing) { - clear_virttext(existing); - *existing = virt_text; - return src_id; - } - - Decoration *decor = xcalloc(1, sizeof(*decor)); - decor->virt_text = virt_text; - - extmark_set(buf, ns_id, 0, (int)line, 0, -1, -1, decor, true, - false, kExtmarkNoUndo); - return src_id; -} - /// call a function with buffer as temporary current buffer /// /// This temporarily switches current buffer to "buffer". diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 3989386bb9..554966e266 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -12,6 +12,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" #include "nvim/lua/executor.h" +#include "nvim/extmark.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/deprecated.c.generated.h" @@ -80,6 +81,87 @@ void nvim_buf_clear_highlight(Buffer buffer, } +/// Set the virtual text (annotation) for a buffer line. +/// +/// @deprecated use nvim_buf_set_extmark to use full virtual text +/// functionality. +/// +/// The text will be placed after the buffer text. Virtual text will never +/// cause reflow, rather virtual text will be truncated at the end of the screen +/// line. The virtual text will begin one cell (|lcs-eol| or space) after the +/// ordinary text. +/// +/// Namespaces are used to support batch deletion/updating of virtual text. +/// To create a namespace, use |nvim_create_namespace()|. Virtual text is +/// cleared using |nvim_buf_clear_namespace()|. The same `ns_id` can be used for +/// both virtual text and highlights added by |nvim_buf_add_highlight()|, both +/// can then be cleared with a single call to |nvim_buf_clear_namespace()|. If +/// the virtual text never will be cleared by an API call, pass `ns_id = -1`. +/// +/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the +/// virtual text, the allocated id is then returned. +/// +/// @param buffer Buffer handle, or 0 for current buffer +/// @param ns_id Namespace to use or 0 to create a namespace, +/// or -1 for a ungrouped annotation +/// @param line Line to annotate with virtual text (zero-indexed) +/// @param chunks A list of [text, hl_group] arrays, each representing a +/// text chunk with specified highlight. `hl_group` element +/// can be omitted for no highlight. +/// @param opts Optional parameters. Currently not used. +/// @param[out] err Error details, if any +/// @return The ns_id that was used +Integer nvim_buf_set_virtual_text(Buffer buffer, + Integer src_id, + Integer line, + Array chunks, + Dictionary opts, + Error *err) + FUNC_API_SINCE(5) + FUNC_API_DEPRECATED_SINCE(8) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + if (!buf) { + return 0; + } + + if (line < 0 || line >= MAXLNUM) { + api_set_error(err, kErrorTypeValidation, "Line number outside range"); + return 0; + } + + if (opts.size > 0) { + api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); + return 0; + } + + uint64_t ns_id = src2ns(&src_id); + int width; + + VirtText virt_text = parse_virt_text(chunks, err, &width); + if (ERROR_SET(err)) { + return 0; + } + + + Decoration *existing = decor_find_virttext(buf, (int)line, ns_id); + + if (existing) { + clear_virttext(&existing->virt_text); + existing->virt_text = virt_text; + existing->virt_text_width = width; + return src_id; + } + + Decoration *decor = xcalloc(1, sizeof(*decor)); + decor->virt_text = virt_text; + decor->virt_text_width = width; + + extmark_set(buf, ns_id, 0, (int)line, 0, -1, -1, decor, true, + false, kExtmarkNoUndo); + return src_id; +} + /// Inserts a sequence of lines to a buffer at a certain index /// /// @deprecated use nvim_buf_set_lines(buffer, lnum, lnum, true, lines) diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index eae4581f42..9f16da4078 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -21,12 +21,12 @@ #include "nvim/api/window.h" #include "nvim/api/deprecated.h" -static Map(String, MsgpackRpcRequestHandler) *methods = NULL; +static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT; static void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandler handler) { - map_put(String, MsgpackRpcRequestHandler)(methods, method, handler); + map_put(String, MsgpackRpcRequestHandler)(&methods, method, handler); } /// @param name API method name @@ -37,7 +37,7 @@ MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, { String m = { .data = (char *)name, .size = name_len }; MsgpackRpcRequestHandler rv = - map_get(String, MsgpackRpcRequestHandler)(methods, m); + map_get(String, MsgpackRpcRequestHandler)(&methods, m); if (!rv.fn) { api_set_error(error, kErrorTypeException, "Invalid method: %.*s", diff --git a/src/nvim/api/private/handle.c b/src/nvim/api/private/handle.c deleted file mode 100644 index eb96192af2..0000000000 --- a/src/nvim/api/private/handle.c +++ /dev/null @@ -1,40 +0,0 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - -#include <assert.h> -#include <stdint.h> - -#include "nvim/vim.h" -#include "nvim/map.h" -#include "nvim/api/private/handle.h" - -#define HANDLE_INIT(name) name##_handles = pmap_new(handle_T)() - -#define HANDLE_IMPL(type, name) \ - static PMap(handle_T) *name##_handles = NULL; /* NOLINT */ \ - \ - type *handle_get_##name(handle_T handle) \ - { \ - return pmap_get(handle_T)(name##_handles, handle); \ - } \ - \ - void handle_register_##name(type *name) \ - { \ - pmap_put(handle_T)(name##_handles, name->handle, name); \ - } \ - \ - void handle_unregister_##name(type *name) \ - { \ - pmap_del(handle_T)(name##_handles, name->handle); \ - } - -HANDLE_IMPL(buf_T, buffer) -HANDLE_IMPL(win_T, window) -HANDLE_IMPL(tabpage_T, tabpage) - -void handle_init(void) -{ - HANDLE_INIT(buffer); - HANDLE_INIT(window); - HANDLE_INIT(tabpage); -} diff --git a/src/nvim/api/private/handle.h b/src/nvim/api/private/handle.h deleted file mode 100644 index 26e9dc3314..0000000000 --- a/src/nvim/api/private/handle.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef NVIM_API_PRIVATE_HANDLE_H -#define NVIM_API_PRIVATE_HANDLE_H - -#include "nvim/vim.h" -#include "nvim/buffer_defs.h" -#include "nvim/api/private/defs.h" - -#define HANDLE_DECLS(type, name) \ - type *handle_get_##name(handle_T handle); \ - void handle_register_##name(type *name); \ - void handle_unregister_##name(type *name); - -// handle_get_buffer handle_register_buffer, handle_unregister_buffer -HANDLE_DECLS(buf_T, buffer) -// handle_get_window handle_register_window, handle_unregister_window -HANDLE_DECLS(win_T, window) -// handle_get_tabpage handle_register_tabpage, handle_unregister_tabpage -HANDLE_DECLS(tabpage_T, tabpage) - -void handle_init(void); - - -#endif // NVIM_API_PRIVATE_HANDLE_H - diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 4b1c2d4baa..0ed5e6408b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -9,7 +9,6 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" -#include "nvim/api/private/handle.h" #include "nvim/api/vim.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/lua/executor.h" @@ -38,7 +37,7 @@ /// Helper structure for vim_to_object typedef struct { - kvec_t(Object) stack; ///< Object stack. + kvec_withinit_t(Object, 2) stack; ///< Object stack. } EncodedData; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -419,28 +418,25 @@ void set_option_to(uint64_t channel_id, void *to, int type, #define TYPVAL_ENCODE_ALLOW_SPECIALS false #define TYPVAL_ENCODE_CONV_NIL(tv) \ - kv_push(edata->stack, NIL) + kvi_push(edata->stack, NIL) #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ - kv_push(edata->stack, BOOLEAN_OBJ((Boolean)(num))) + kvi_push(edata->stack, BOOLEAN_OBJ((Boolean)(num))) #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ - kv_push(edata->stack, INTEGER_OBJ((Integer)(num))) + kvi_push(edata->stack, INTEGER_OBJ((Integer)(num))) #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ - kv_push(edata->stack, FLOAT_OBJ((Float)(flt))) + kvi_push(edata->stack, FLOAT_OBJ((Float)(flt))) #define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ do { \ const size_t len_ = (size_t)(len); \ const char *const str_ = (const char *)(str); \ assert(len_ == 0 || str_ != NULL); \ - kv_push(edata->stack, STRING_OBJ(((String) { \ - .data = xmemdupz((len_?str_:""), len_), \ - .size = len_ \ - }))); \ + kvi_push(edata->stack, STRING_OBJ(cbuf_to_string((len_?str_:""), len_))); \ } while (0) #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING @@ -459,17 +455,17 @@ void set_option_to(uint64_t channel_id, void *to, int type, #define TYPVAL_ENCODE_CONV_FUNC_END(tv) #define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ - kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 }))) + kvi_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 }))) #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - kv_push(edata->stack, \ - DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 }))) + kvi_push(edata->stack, \ + DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 }))) static inline void typval_encode_list_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kv_push(edata->stack, ARRAY_OBJ(((Array) { + kvi_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = len, .size = 0, .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.array.items)), @@ -511,7 +507,7 @@ static inline void typval_encode_dict_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kv_push(edata->stack, DICTIONARY_OBJ(((Dictionary) { + kvi_push(edata->stack, DICTIONARY_OBJ(((Dictionary) { .capacity = len, .size = 0, .items = xmalloc(len * sizeof( @@ -619,14 +615,15 @@ static inline void typval_encode_dict_end(EncodedData *const edata) /// @return The converted value Object vim_to_object(typval_T *obj) { - EncodedData edata = { .stack = KV_INITIAL_VALUE }; + EncodedData edata; + kvi_init(edata.stack); const int evo_ret = encode_vim_to_object(&edata, obj, "vim_to_object argument"); (void)evo_ret; assert(evo_ret == OK); Object ret = kv_A(edata.stack, 0); assert(kv_size(edata.stack) == 1); - kv_destroy(edata.stack); + kvi_destroy(edata.stack); return ret; } @@ -1592,9 +1589,10 @@ bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int } } -VirtText parse_virt_text(Array chunks, Error *err) +VirtText parse_virt_text(Array chunks, Error *err, int *width) { VirtText virt_text = KV_INITIAL_VALUE; + int w = 0; for (size_t i = 0; i < chunks.size; i++) { if (chunks.items[i].type != kObjectTypeArray) { api_set_error(err, kErrorTypeValidation, "Chunk is not an array"); @@ -1602,26 +1600,44 @@ VirtText parse_virt_text(Array chunks, Error *err) } Array chunk = chunks.items[i].data.array; if (chunk.size == 0 || chunk.size > 2 - || chunk.items[0].type != kObjectTypeString - || (chunk.size == 2 && chunk.items[1].type != kObjectTypeString)) { + || chunk.items[0].type != kObjectTypeString) { api_set_error(err, kErrorTypeValidation, "Chunk is not an array with one or two strings"); goto free_exit; } String str = chunk.items[0].data.string; - char *text = transstr(str.size > 0 ? str.data : ""); // allocates int hl_id = 0; if (chunk.size == 2) { - String hl = chunk.items[1].data.string; - if (hl.size > 0) { - hl_id = syn_check_group((char_u *)hl.data, (int)hl.size); + Object hl = chunk.items[1]; + if (hl.type == kObjectTypeArray) { + Array arr = hl.data.array; + for (size_t j = 0; j < arr.size; j++) { + hl_id = object_to_hl_id(arr.items[j], "virt_text highlight", err); + if (ERROR_SET(err)) { + goto free_exit; + } + if (j < arr.size-1) { + kv_push(virt_text, ((VirtTextChunk){ .text = NULL, + .hl_id = hl_id })); + } + } + } else { + hl_id = object_to_hl_id(hl, "virt_text highlight", err); + if (ERROR_SET(err)) { + goto free_exit; + } } } + + char *text = transstr(str.size > 0 ? str.data : ""); // allocates + w += (int)mb_string2cells((char_u *)text); + kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id })); } + *width = w; return virt_text; free_exit: @@ -1656,7 +1672,7 @@ int object_to_hl_id(Object obj, const char *what, Error *err) String str = obj.data.string; return str.size ? syn_check_group((char_u *)str.data, (int)str.size) : 0; } else if (obj.type == kObjectTypeInteger) { - return (int)obj.data.integer; + return MAX((int)obj.data.integer, 0); } else { api_set_error(err, kErrorTypeValidation, "%s is not a valid highlight", what); @@ -1687,6 +1703,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err) if (chunk.size == 2) { String hl = chunk.items[1].data.string; if (hl.size > 0) { + // TODO(bfredl): use object_to_hl_id and allow integer int hl_id = syn_check_group((char_u *)hl.data, (int)hl.size); attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; } @@ -1705,7 +1722,7 @@ const char *describe_ns(NS ns_id) { String name; handle_T id; - map_foreach(namespace_ids, name, id, { + map_foreach(&namespace_ids, name, id, { if ((NS)id == ns_id && name.size) { return name.data; } @@ -1770,6 +1787,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) { "double", { "â•”", "â•", "â•—", "â•‘", "â•", "â•", "╚", "â•‘" }, false }, { "single", { "┌", "─", "â”", "│", "┘", "─", "â””", "│" }, false }, { "shadow", { "", "", " ", " ", " ", " ", " ", "" }, true }, + { "rounded", { "â•", "─", "â•®", "│", "╯", "─", "â•°", "│" }, false }, { "solid", { " ", " ", " ", " ", " ", " ", " ", " " }, false }, { NULL, { { NUL } } , false }, }; @@ -1789,7 +1807,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) } for (size_t i = 0; i < size; i++) { Object iytem = arr.items[i]; - String string = NULL_STRING; + String string; int hl_id = 0; if (iytem.type == kObjectTypeArray) { Array iarr = iytem.data.array; diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 055abb797f..ecce6afa26 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -101,6 +101,14 @@ #define api_free_window(value) #define api_free_tabpage(value) +EXTERN PMap(handle_T) buffer_handles INIT(= MAP_INIT); +EXTERN PMap(handle_T) window_handles INIT(= MAP_INIT); +EXTERN PMap(handle_T) tabpage_handles INIT(= MAP_INIT); + +#define handle_get_buffer(h) pmap_get(handle_T)(&buffer_handles, (h)) +#define handle_get_window(h) pmap_get(handle_T)(&window_handles, (h)) +#define handle_get_tabpage(h) pmap_get(handle_T)(&tabpage_handles, (h)) + /// Structure used for saving state for :try /// /// Used when caller is supposed to be operating when other VimL code is being diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 51f1af4eb5..713980ca68 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -37,24 +37,18 @@ typedef struct { bool wildmenu_active; } UIData; -static PMap(uint64_t) *connected_uis = NULL; - -void remote_ui_init(void) - FUNC_API_NOEXPORT -{ - connected_uis = pmap_new(uint64_t)(); -} +static PMap(uint64_t) connected_uis = MAP_INIT; void remote_ui_disconnect(uint64_t channel_id) FUNC_API_NOEXPORT { - UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); + UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui) { return; } UIData *data = ui->data; api_free_array(data->buffer); // Destroy pending screen updates. - pmap_del(uint64_t)(connected_uis, channel_id); + pmap_del(uint64_t)(&connected_uis, channel_id); xfree(ui->data); ui->data = NULL; // Flag UI as "stopped". ui_detach_impl(ui, channel_id); @@ -73,7 +67,7 @@ void remote_ui_wait_for_attach(void) } LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, - pmap_has(uint64_t)(connected_uis, CHAN_STDIO)); + pmap_has(uint64_t)(&connected_uis, CHAN_STDIO)); } /// Activates UI events on the channel. @@ -95,7 +89,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictionary options, Error *err) FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { - if (pmap_has(uint64_t)(connected_uis, channel_id)) { + if (pmap_has(uint64_t)(&connected_uis, channel_id)) { api_set_error(err, kErrorTypeException, "UI already attached to channel: %" PRId64, channel_id); return; @@ -158,7 +152,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, } if (ui->ui_ext[kUIMessages]) { - // This uses attribute indicies, so ext_linegrid is needed. + // This uses attribute indices, so ext_linegrid is needed. ui->ui_ext[kUILinegrid] = true; // Cmdline uses the messages area, so it should be externalized too. ui->ui_ext[kUICmdline] = true; @@ -172,7 +166,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, data->wildmenu_active = false; ui->data = data; - pmap_put(uint64_t)(connected_uis, channel_id, ui); + pmap_put(uint64_t)(&connected_uis, channel_id, ui); ui_attach_impl(ui, channel_id); } @@ -195,7 +189,7 @@ void ui_attach(uint64_t channel_id, Integer width, Integer height, void nvim_ui_detach(uint64_t channel_id, Error *err) FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { - if (!pmap_has(uint64_t)(connected_uis, channel_id)) { + if (!pmap_has(uint64_t)(&connected_uis, channel_id)) { api_set_error(err, kErrorTypeException, "UI not attached to channel: %" PRId64, channel_id); return; @@ -208,7 +202,7 @@ void nvim_ui_try_resize(uint64_t channel_id, Integer width, Integer height, Error *err) FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { - if (!pmap_has(uint64_t)(connected_uis, channel_id)) { + if (!pmap_has(uint64_t)(&connected_uis, channel_id)) { api_set_error(err, kErrorTypeException, "UI not attached to channel: %" PRId64, channel_id); return; @@ -220,7 +214,7 @@ void nvim_ui_try_resize(uint64_t channel_id, Integer width, return; } - UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); + UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); ui->width = (int)width; ui->height = (int)height; ui_refresh(); @@ -230,12 +224,12 @@ void nvim_ui_set_option(uint64_t channel_id, String name, Object value, Error *error) FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { - if (!pmap_has(uint64_t)(connected_uis, channel_id)) { + if (!pmap_has(uint64_t)(&connected_uis, channel_id)) { api_set_error(error, kErrorTypeException, "UI not attached to channel: %" PRId64, channel_id); return; } - UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); + UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); ui_set_option(ui, false, name, value, error); } @@ -310,7 +304,7 @@ void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, Integer height, Error *err) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY { - if (!pmap_has(uint64_t)(connected_uis, channel_id)) { + if (!pmap_has(uint64_t)(&connected_uis, channel_id)) { api_set_error(err, kErrorTypeException, "UI not attached to channel: %" PRId64, channel_id); return; @@ -328,7 +322,7 @@ void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, void nvim_ui_pum_set_height(uint64_t channel_id, Integer height, Error *err) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY { - if (!pmap_has(uint64_t)(connected_uis, channel_id)) { + if (!pmap_has(uint64_t)(&connected_uis, channel_id)) { api_set_error(err, kErrorTypeException, "UI not attached to channel: %" PRId64, channel_id); return; @@ -339,7 +333,7 @@ void nvim_ui_pum_set_height(uint64_t channel_id, Integer height, Error *err) return; } - UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); + UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui->ui_ext[kUIPopupmenu]) { api_set_error(err, kErrorTypeValidation, "It must support the ext_popupmenu option"); @@ -369,13 +363,13 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Float row, Float col, Error *err) FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY { - if (!pmap_has(uint64_t)(connected_uis, channel_id)) { + if (!pmap_has(uint64_t)(&connected_uis, channel_id)) { api_set_error(err, kErrorTypeException, "UI not attached to channel: %" PRId64, channel_id); return; } - UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); + UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); if (!ui->ui_ext[kUIPopupmenu]) { api_set_error(err, kErrorTypeValidation, "UI must support the ext_popupmenu option"); diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 11e21a88ea..03fe5c5058 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -119,7 +119,8 @@ void msg_set_pos(Integer grid, Integer row, Boolean scrolled, String sep_char) FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL; void win_viewport(Integer grid, Window win, Integer topline, - Integer botline, Integer curline, Integer curcol) + Integer botline, Integer curline, Integer curcol, + Integer line_count) FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY; void popupmenu_show(Array items, Integer selected, @@ -130,7 +131,8 @@ void popupmenu_hide(void) void popupmenu_select(Integer selected) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; -void tabline_update(Tabpage current, Array tabs) +void tabline_update(Tabpage current, Array tabs, + Buffer current_buffer, Array buffers) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void cmdline_show(Array content, Integer pos, String firstc, String prompt, diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 60535b13b3..f65d5cc185 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -58,22 +58,16 @@ # include "api/vim.c.generated.h" #endif -void api_vim_init(void) - FUNC_API_NOEXPORT -{ - namespace_ids = map_new(String, handle_T)(); -} - void api_vim_free_all_mem(void) FUNC_API_NOEXPORT { String name; handle_T id; - map_foreach(namespace_ids, name, id, { + map_foreach(&namespace_ids, name, id, { (void)id; xfree(name.data); }) - map_free(String, handle_T)(namespace_ids); + map_destroy(String, handle_T)(&namespace_ids); } /// Executes Vimscript (multiline block of Ex-commands), like anonymous @@ -217,7 +211,7 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) /// /// @param ns_id number of namespace for this highlight /// @param name highlight group name, like ErrorMsg -/// @param val highlight definiton map, like |nvim_get_hl_by_name|. +/// @param val highlight definition map, like |nvim_get_hl_by_name|. /// in addition the following keys are also recognized: /// `default`: don't override existing definition, /// like `hi default` @@ -281,9 +275,9 @@ static void on_redraw_event(void **argv) /// /// On execution error: does not fail, but updates v:errmsg. /// -/// If you need to input sequences like <C-o> use |nvim_replace_termcodes| to -/// replace the termcodes and then pass the resulting string to nvim_feedkeys. -/// You'll also want to enable escape_csi. +/// To input sequences like <C-o> use |nvim_replace_termcodes()| (typically +/// with escape_csi=true) to replace |keycodes|, then pass the result to +/// nvim_feedkeys(). /// /// Example: /// <pre> @@ -384,7 +378,7 @@ Integer nvim_input(String keys) /// by calling it multiple times in a loop: the intermediate mouse /// positions will be ignored. It should be used to implement real-time /// mouse input in a GUI. The deprecated pseudokey form -/// ("<LeftMouse><col,row>") of |nvim_input()| has the same limitiation. +/// ("<LeftMouse><col,row>") of |nvim_input()| has the same limitation. /// /// @param button Mouse button: one of "left", "right", "middle", "wheel". /// @param action For ordinary buttons, one of "press", "drag", "release". @@ -558,7 +552,7 @@ Object nvim_exec_lua(String code, Array args, Error *err) /// Notify the user with a message /// /// Relays the call to vim.notify . By default forwards your message in the -/// echo area but can be overriden to trigger desktop notifications. +/// echo area but can be overridden to trigger desktop notifications. /// /// @param msg Message to display to the user /// @param log_level The log level @@ -614,12 +608,15 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) recursive++; try_start(); typval_T rettv; - int dummy; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.selfdict = self; // call_func() retval is deceptive, ignore it. Instead we set `msg_list` // (see above) to capture abort-causing non-exception errors. (void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size, - vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, NULL, self); + vim_args, &funcexe); if (!try_end(err)) { rv = vim_to_object(&rettv); } @@ -1260,7 +1257,7 @@ fail: /// /// By default (and currently the only option) the terminal will not be /// connected to an external process. Instead, input send on the channel -/// will be echoed directly by the terminal. This is useful to disply +/// will be echoed directly by the terminal. This is useful to display /// ANSI terminal sequences returned as part of a rpc message, or similar. /// /// Note: to directly initiate the terminal using the right size, display the @@ -1290,7 +1287,7 @@ Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err) TerminalOptions topts; Channel *chan = channel_alloc(kChannelStreamInternal); topts.data = chan; - // NB: overriden in terminal_check_size if a window is already + // NB: overridden in terminal_check_size if a window is already // displaying the buffer topts.width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0); topts.height = (uint16_t)curwin->w_height_inner; @@ -1437,28 +1434,30 @@ void nvim_chan_send(Integer chan, String data, Error *err) /// end-of-buffer region is hidden by setting `eob` flag of /// 'fillchars' to a space char, and clearing the /// |EndOfBuffer| region in 'winhighlight'. -/// - `border`: style of (optional) window border. This can either be a string -/// or an array. the string values are: -/// - "none" No border. This is the default -/// - "single" a single line box -/// - "double" a double line box -/// - "shadow" a drop shadow effect by blending with the background. -/// If it is an array it should be an array of eight items or any divisor of +/// - `border`: Style of (optional) window border. This can either be a string +/// or an array. The string values are +/// - "none": No border (default). +/// - "single": A single line box. +/// - "double": A double line box. +/// - "rounded": Like "single", but with rounded corners ("â•" etc.). +/// - "solid": Adds padding by a single whitespace cell. +/// - "shadow": A drop shadow effect by blending with the background. +/// - If it is an array, it should have a length of eight or any divisor of /// eight. The array will specifify the eight chars building up the border -/// in a clockwise fashion starting with the top-left corner. As, an -/// example, the double box style could be specified as: -/// [ "â•”", "â•" ,"â•—", "â•‘", "â•", "â•", "╚", "â•‘" ] -/// if the number of chars are less than eight, they will be repeated. Thus -/// an ASCII border could be specified as: -/// [ "/", "-", "\\", "|" ] -/// or all chars the same as: -/// [ "x" ] -/// An empty string can be used to turn off a specific border, for instance: +/// in a clockwise fashion starting with the top-left corner. As an +/// example, the double box style could be specified as +/// [ "â•”", "â•" ,"â•—", "â•‘", "â•", "â•", "╚", "â•‘" ]. +/// If the number of chars are less than eight, they will be repeated. Thus +/// an ASCII border could be specified as +/// [ "/", "-", "\\", "|" ], +/// or all chars the same as +/// [ "x" ]. +/// An empty string can be used to turn off a specific border, for instance, /// [ "", "", "", ">", "", "", "", "<" ] /// will only make vertical borders but not horizontal ones. -/// By default `FloatBorder` highlight is used which links to `VertSplit` +/// By default, `FloatBorder` highlight is used, which links to `VertSplit` /// when not defined. It could also be specified by character: -/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ] +/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ]. /// - `noautocmd`: If true then no buffer-related autocommand events such as /// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from /// calling this function. @@ -1552,10 +1551,10 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err) } } -/// Creates a new namespace, or gets an existing one. +/// Creates a new *namespace*, or gets an existing one. /// /// Namespaces are used for buffer highlights and virtual text, see -/// |nvim_buf_add_highlight()| and |nvim_buf_set_virtual_text()|. +/// |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|. /// /// Namespaces can be named or anonymous. If `name` matches an existing /// namespace, the associated id is returned. If `name` is an empty string @@ -1566,14 +1565,14 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err) Integer nvim_create_namespace(String name) FUNC_API_SINCE(5) { - handle_T id = map_get(String, handle_T)(namespace_ids, name); + handle_T id = map_get(String, handle_T)(&namespace_ids, name); if (id > 0) { return id; } id = next_namespace_id++; if (name.size > 0) { String name_alloc = copy_string(name); - map_put(String, handle_T)(namespace_ids, name_alloc, id); + map_put(String, handle_T)(&namespace_ids, name_alloc, id); } return (Integer)id; } @@ -1588,7 +1587,7 @@ Dictionary nvim_get_namespaces(void) String name; handle_T id; - map_foreach(namespace_ids, name, id, { + map_foreach(&namespace_ids, name, id, { PUT(retval, name.data, INTEGER_OBJ(id)); }) @@ -1694,7 +1693,7 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, FUNC_API_SINCE(6) FUNC_API_CHECK_TEXTLOCK { - yankreg_T *reg = xcalloc(sizeof(yankreg_T), 1); + yankreg_T *reg = xcalloc(1, sizeof(yankreg_T)); if (!prepare_yankreg_from_object(reg, type, lines.size)) { api_set_error(err, kErrorTypeValidation, "Invalid type: '%s'", type.data); goto cleanup; @@ -2058,27 +2057,28 @@ void nvim_set_client_info(uint64_t channel_id, String name, rpc_set_client_info(channel_id, info); } -/// Get information about a channel. +/// Gets information about a channel. /// /// @returns Dictionary describing a channel, with these keys: -/// - "stream" the stream underlying the channel +/// - "id" Channel id. +/// - "argv" (optional) Job arguments list. +/// - "stream" Stream underlying the channel. /// - "stdio" stdin and stdout of this Nvim instance /// - "stderr" stderr of this Nvim instance /// - "socket" TCP/IP socket or named pipe -/// - "job" job with communication over its stdio -/// - "mode" how data received on the channel is interpreted -/// - "bytes" send and receive raw bytes -/// - "terminal" a |terminal| instance interprets ASCII sequences -/// - "rpc" |RPC| communication on the channel is active -/// - "pty" Name of pseudoterminal, if one is used (optional). -/// On a POSIX system, this will be a device path like -/// /dev/pts/1. Even if the name is unknown, the key will -/// still be present to indicate a pty is used. This is -/// currently the case when using winpty on windows. -/// - "buffer" buffer with connected |terminal| instance (optional) -/// - "client" information about the client on the other end of the -/// RPC channel, if it has added it using -/// |nvim_set_client_info()|. (optional) +/// - "job" Job with communication over its stdio. +/// - "mode" How data received on the channel is interpreted. +/// - "bytes" Send and receive raw bytes. +/// - "terminal" |terminal| instance interprets ASCII sequences. +/// - "rpc" |RPC| communication on the channel is active. +/// - "pty" (optional) Name of pseudoterminal. On a POSIX system this +/// is a device path like "/dev/pts/1". If the name is unknown, +/// the key will still be present if a pty is used (e.g. for +/// winpty on Windows). +/// - "buffer" (optional) Buffer with connected |terminal| instance. +/// - "client" (optional) Info about the peer (client on the other end of +/// the RPC channel), if provided by it via +/// |nvim_set_client_info()|. /// Dictionary nvim_get_chan_info(Integer chan, Error *err) FUNC_API_SINCE(4) @@ -2235,7 +2235,7 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// - "arg": String, error message argument. /// - "len": Amount of bytes successfully parsed. With flags equal to "" /// that should be equal to the length of expr string. -/// (“Sucessfully parsed†here means “participated in AST +/// (“Successfully parsed†here means “participated in AST /// creationâ€, not “till the first errorâ€.) /// - "ast": AST, either nil or a dictionary with these keys: /// - "type": node type, one of the value names from ExprASTNodeType @@ -2299,7 +2299,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, } } } - ParserLine plines[] = { + ParserLine parser_lines[] = { { .data = expr.data, .size = expr.size, @@ -2307,7 +2307,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, }, { NULL, 0, false }, }; - ParserLine *plines_p = plines; + ParserLine *plines_p = parser_lines; ParserHighlight colors; kvi_init(colors); ParserHighlight *const colors_p = (highlight ? &colors : NULL); @@ -2333,7 +2333,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, ret.items[ret.size++] = (KeyValuePair) { .key = STATIC_CSTR_TO_STRING("len"), .value = INTEGER_OBJ((Integer)(pstate.pos.line == 1 - ? plines[0].size + ? parser_lines[0].size : pstate.pos.col)), }; if (east.err.msg != NULL) { @@ -2923,7 +2923,7 @@ void nvim__screenshot(String path) /// Note: this function should not be called often. Rather, the callbacks /// themselves can be used to throttle unneeded callbacks. the `on_start` /// callback can return `false` to disable the provider until the next redraw. -/// Similarily, return `false` in `on_win` will skip the `on_lines` calls +/// Similarly, return `false` in `on_win` will skip the `on_lines` calls /// for that window (but any extmarks set in `on_win` will still be used). /// A plugin managing multiple sources of decoration should ideally only set /// one provider, and merge the sources internally. You can use multiple `ns_id` diff --git a/src/nvim/api/vim.h b/src/nvim/api/vim.h index d6873da6d2..4fd353ce5c 100644 --- a/src/nvim/api/vim.h +++ b/src/nvim/api/vim.h @@ -6,7 +6,7 @@ #include "nvim/api/private/defs.h" #include "nvim/map.h" -EXTERN Map(String, handle_T) *namespace_ids INIT(= NULL); +EXTERN Map(String, handle_T) namespace_ids INIT(= MAP_INIT); EXTERN handle_T next_namespace_id INIT(= 1); #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 0729024b45..069dfae233 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -394,7 +394,7 @@ void nvim_win_set_config(Window window, Dictionary config, Error *err) return; } bool new_float = !win->w_floating; - // reuse old values, if not overriden + // reuse old values, if not overridden FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config; if (!parse_float_config(config, &fconfig, !new_float, false, err)) { @@ -456,6 +456,7 @@ Dictionary nvim_win_get_config(Window window, Error *err) float_anchor_str[config->anchor]))); PUT(rv, "row", FLOAT_OBJ(config->row)); PUT(rv, "col", FLOAT_OBJ(config->col)); + PUT(rv, "zindex", INTEGER_OBJ(config->zindex)); } if (config->border) { Array border = ARRAY_DICT_INIT; diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c index 9fba38a49f..7ea2e0cf9b 100644 --- a/src/nvim/arabic.c +++ b/src/nvim/arabic.c @@ -793,7 +793,7 @@ bool arabic_maycombine(int two) return false; } -// A_firstc_laa returns first character of LAA combination if it ex.ists +// A_firstc_laa returns first character of LAA combination if it exists // in: "c" base character // in: "c1" first composing character static int A_firstc_laa(int c, int c1) diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 7e4dee3d34..7b5e82cd3f 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -31,9 +31,7 @@ #define CSI 0x9b // Control Sequence Introducer #define CSI_STR "\233" #define DCS 0x90 // Device Control String -#define DCS_STR "\033P" #define STERM 0x9c // String Terminator -#define STERM_STR "\033\\" #define POUND 0xA3 @@ -171,6 +169,14 @@ static inline bool ascii_isbdigit(int c) return (c == '0' || c == '1'); } +/// Checks if `c` is an octal digit, that is, 0-7. +/// +/// @see {ascii_isdigit} +static inline bool ascii_isodigit(int c) +{ + return (c >= '0' && c <= '7'); +} + /// Checks if `c` is a white-space character, that is, /// one of \f, \n, \r, \t, \v. /// diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c index 32c77fa288..802fc9de57 100644 --- a/src/nvim/aucmd.c +++ b/src/nvim/aucmd.c @@ -8,6 +8,7 @@ #include "nvim/ui.h" #include "nvim/aucmd.h" #include "nvim/eval.h" +#include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/buffer.h" @@ -35,6 +36,29 @@ void do_autocmd_uienter(uint64_t chanid, bool attached) recursive = false; } +void init_default_autocmds(void) +{ + // open terminals when opening files that start with term:// +#define PROTO "term://" + do_cmdline_cmd("augroup nvim_terminal"); + do_cmdline_cmd("autocmd BufReadCmd " PROTO "* ++nested " + "if !exists('b:term_title')|call termopen(" + // Capture the command string + "matchstr(expand(\"<amatch>\"), " + "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " + // capture the working directory + "{'cwd': expand(get(matchlist(expand(\"<amatch>\"), " + "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, ''))})" + "|endif"); + do_cmdline_cmd("augroup END"); +#undef PROTO + + // limit syntax synchronization in the command window + do_cmdline_cmd("augroup nvim_cmdwin"); + do_cmdline_cmd("autocmd! CmdwinEnter [:>] syntax sync minlines=1 maxlines=1"); + do_cmdline_cmd("augroup END"); +} + static void focusgained_event(void **argv) { bool *gainedp = argv[0]; diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 145f6f5601..417953eb14 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -5,7 +5,7 @@ #include "nvim/autocmd.h" -#include "nvim/api/private/handle.h" +#include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/charset.h" @@ -1150,7 +1150,7 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) block_autocmds(); // We don't want BufEnter/WinEnter autocommands. if (need_append) { win_append(lastwin, aucmd_win); - handle_register_window(aucmd_win); + pmap_put(handle_T)(&window_handles, aucmd_win->handle, aucmd_win); win_config_float(aucmd_win, aucmd_win->w_float_config); } // Prevent chdir() call in win_enter_ext(), through do_autochdir() @@ -1191,7 +1191,7 @@ void aucmd_restbuf(aco_save_T *aco) win_found: win_remove(curwin, NULL); - handle_unregister_window(curwin); + pmap_del(handle_T)(&window_handles, curwin->handle); if (curwin->w_grid_alloc.chars != NULL) { ui_comp_remove_grid(&curwin->w_grid_alloc); ui_call_win_hide(curwin->w_grid_alloc.handle); @@ -1724,7 +1724,7 @@ BYPASS_AU: void block_autocmds(void) { // Remember the value of v:termresponse. - if (is_autocmd_blocked()) { + if (!is_autocmd_blocked()) { old_termresponse = get_vim_var_str(VV_TERMRESPONSE); } autocmd_blocked++; @@ -1737,7 +1737,7 @@ void unblock_autocmds(void) // When v:termresponse was set while autocommands were blocked, trigger // the autocommands now. Esp. useful when executing a shell command // during startup (nvim -d). - if (is_autocmd_blocked() + if (!is_autocmd_blocked() && get_vim_var_str(VV_TERMRESPONSE) != old_termresponse) { apply_autocmds(EVENT_TERMRESPONSE, NULL, NULL, false, curbuf); } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 6a50264e0f..81f8b9073e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -24,7 +24,6 @@ #include <inttypes.h> #include <assert.h> -#include "nvim/api/private/handle.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/ascii.h" @@ -55,7 +54,6 @@ #include "nvim/mark.h" #include "nvim/extmark.h" #include "nvim/mbyte.h" -#include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" @@ -139,7 +137,7 @@ read_buffer( if (read_stdin) { // Set or reset 'modified' before executing autocommands, so that // it can be changed there. - if (!readonlymode && !BUFEMPTY()) { + if (!readonlymode && !buf_is_empty(curbuf)) { changed(); } else if (retval != FAIL) { unchanged(curbuf, false, true); @@ -533,7 +531,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) } if (buf->terminal) { - terminal_close(buf->terminal, NULL); + terminal_close(buf->terminal, -1); } // Always remove the buffer when there is no file name. @@ -541,12 +539,10 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) del_buf = true; } - /* - * Free all things allocated for this buffer. - * Also calls the "BufDelete" autocommands when del_buf is TRUE. - */ - /* Remember if we are closing the current buffer. Restore the number of - * windows, so that autocommands in buf_freeall() don't get confused. */ + // Free all things allocated for this buffer. + // Also calls the "BufDelete" autocommands when del_buf is true. + // Remember if we are closing the current buffer. Restore the number of + // windows, so that autocommands in buf_freeall() don't get confused. bool is_curbuf = (buf == curbuf); // When closing the current buffer stop Visual mode before freeing @@ -760,7 +756,7 @@ void buf_freeall(buf_T *buf, int flags) */ static void free_buffer(buf_T *buf) { - handle_unregister_buffer(buf); + pmap_del(handle_T)(&buffer_handles, buf->b_fnum); buf_free_count++; // b:changedtick uses an item in buf_T. free_buffer_stuff(buf, kBffClearWinInfo); @@ -840,11 +836,7 @@ static void clear_wininfo(buf_T *buf) while (buf->b_wininfo != NULL) { wip = buf->b_wininfo; buf->b_wininfo = wip->wi_next; - if (wip->wi_optset) { - clear_winopt(&wip->wi_opt); - deleteFoldRecurse(buf, &wip->wi_folds); - } - xfree(wip); + free_wininfo(wip, buf); } } @@ -905,7 +897,10 @@ void handle_swap_exists(bufref_T *old_curbuf) if (old_curbuf == NULL || !bufref_valid(old_curbuf) || old_curbuf->br_buf == curbuf) { + // Block autocommands here because curwin->w_buffer is NULL. + block_autocmds(); buf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); + unblock_autocmds(); } else { buf = old_curbuf->br_buf; } @@ -1223,8 +1218,8 @@ do_buffer( return FAIL; } - if (!forceit && (buf->terminal || bufIsChanged(buf))) { - if ((p_confirm || cmdmod.confirm) && p_write && !buf->terminal) { + if (!forceit && bufIsChanged(buf)) { + if ((p_confirm || cmdmod.confirm) && p_write) { dialog_changed(buf, false); if (!bufref_valid(&bufref)) { // Autocommand deleted buffer, oops! It's not changed now. @@ -1236,22 +1231,22 @@ do_buffer( return FAIL; } } else { - if (buf->terminal) { - if (p_confirm || cmdmod.confirm) { - if (!dialog_close_terminal(buf)) { - return FAIL; - } - } else { - EMSG2(_("E89: %s will be killed (add ! to override)"), - (char *)buf->b_fname); - return FAIL; - } - } else { - EMSGN(_("E89: No write since last change for buffer %" PRId64 - " (add ! to override)"), - buf->b_fnum); + EMSGN(_("E89: No write since last change for buffer %" PRId64 + " (add ! to override)"), + buf->b_fnum); + return FAIL; + } + } + + if (!forceit && buf->terminal && terminal_running(buf->terminal)) { + if (p_confirm || cmdmod.confirm) { + if (!dialog_close_terminal(buf)) { return FAIL; } + } else { + EMSG2(_("E89: %s will be killed (add ! to override)"), + (char *)buf->b_fname); + return FAIL; } } @@ -1493,7 +1488,7 @@ void set_curbuf(buf_T *buf, int action) set_bufref(&prevbufref, prevbuf); set_bufref(&newbufref, buf); - // Autocommands may delete the curren buffer and/or the buffer we wan to go + // Autocommands may delete the curren buffer and/or the buffer we want to go // to. In those cases don't close the buffer. if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf) || (bufref_valid(&prevbufref) && bufref_valid(&newbufref) @@ -1674,7 +1669,7 @@ static int top_file_num = 1; ///< highest file number /// Initialize b:changedtick and changedtick_val attribute /// -/// @param[out] buf Buffer to intialize for. +/// @param[out] buf Buffer to initialize for. static inline void buf_init_changedtick(buf_T *const buf) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { @@ -1787,7 +1782,7 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, buf = xcalloc(1, sizeof(buf_T)); // init b: variables buf->b_vars = tv_dict_alloc(); - buf->b_signcols_max = -1; + buf->b_signcols_valid = false; init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); buf_init_changedtick(buf); } @@ -1845,7 +1840,7 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, lastbuf = buf; buf->b_fnum = top_file_num++; - handle_register_buffer(buf); + pmap_put(handle_T)(&buffer_handles, buf->b_fnum, buf); if (top_file_num < 0) { // wrap around (may cause duplicates) EMSG(_("W14: Warning: List of file names overflow")); if (emsg_silent == 0) { @@ -1922,7 +1917,7 @@ bool curbuf_reusable(void) return (curbuf != NULL && curbuf->b_ffname == NULL && curbuf->b_nwindows <= 1 - && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY()) + && (curbuf->b_ml.ml_mfp == NULL || buf_is_empty(curbuf)) && !bt_quickfix(curbuf) && !curbufIsChanged()); } @@ -2062,7 +2057,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit) // If 'switchbuf' contains "split", "vsplit" or "newtab" and the // current buffer isn't empty: open new tab or window if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB)) - && !BUFEMPTY()) { + && !buf_is_empty(curbuf)) { if (swb_flags & SWB_NEWTAB) { tabpage_new(); } else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0) @@ -3591,7 +3586,7 @@ int build_stl_str_hl( // Proceed character by character through the statusline format string - // fmt_p is the current positon in the input buffer + // fmt_p is the current position in the input buffer for (char_u *fmt_p = usefmt; *fmt_p; ) { if (curitem == (int)stl_items_len) { size_t new_len = stl_items_len * 3 / 2; @@ -4257,7 +4252,7 @@ int build_stl_str_hl( if (*fmt_p == '#') { stl_items[curitem].type = Highlight; stl_items[curitem].start = out_p; - stl_items[curitem].minwid = -syn_namen2id(t, (int)(fmt_p - t)); + stl_items[curitem].minwid = -syn_name2id_len(t, (size_t)(fmt_p - t)); curitem++; fmt_p++; } @@ -4739,7 +4734,7 @@ static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file) // When resolving a link both "*sfname" and "*ffname" will point to the same // allocated memory. // The "*ffname" and "*sfname" pointer values on call will not be freed. -// Note that the resulting "*ffname" pointer should be considered not allocaed. +// Note that the resulting "*ffname" pointer should be considered not allocated. void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) { if (*ffname == NULL) { // no file name given, nothing to do @@ -4952,7 +4947,7 @@ do_arg_all( win_enter(lastwin, false); // ":tab drop file" should re-use an empty window to avoid "--remote-tab" // leaving an empty tab page when executed locally. - if (keep_tabs && BUFEMPTY() && curbuf->b_nwindows == 1 + if (keep_tabs && buf_is_empty(curbuf) && curbuf->b_nwindows == 1 && curbuf->b_ffname == NULL && !curbuf->b_changed) { use_firstwin = true; tab_drop_empty_window = true; @@ -5048,8 +5043,8 @@ do_arg_all( xfree(opened); } -// Return TRUE if "buf" is a prompt buffer. -int bt_prompt(buf_T *buf) +/// @return true if "buf" is a prompt buffer. +bool bt_prompt(buf_T *buf) { return buf != NULL && buf->b_p_bt[0] == 'p'; } @@ -5548,16 +5543,16 @@ bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) int buf_signcols(buf_T *buf) { - if (buf->b_signcols_max == -1) { + if (!buf->b_signcols_valid) { sign_entry_T *sign; // a sign in the sign list - buf->b_signcols_max = 0; + int signcols = 0; int linesum = 0; linenr_T curline = 0; FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->se_lnum > curline) { - if (linesum > buf->b_signcols_max) { - buf->b_signcols_max = linesum; + if (linesum > signcols) { + signcols = linesum; } curline = sign->se_lnum; linesum = 0; @@ -5566,15 +5561,17 @@ int buf_signcols(buf_T *buf) linesum++; } } - if (linesum > buf->b_signcols_max) { - buf->b_signcols_max = linesum; + if (linesum > signcols) { + signcols = linesum; } // Check if we need to redraw - if (buf->b_signcols_max != buf->b_signcols) { - buf->b_signcols = buf->b_signcols_max; + if (signcols != buf->b_signcols) { + buf->b_signcols = signcols; redraw_buf_later(buf, NOT_VALID); } + + buf->b_signcols_valid = true; } return buf->b_signcols; @@ -5665,7 +5662,7 @@ bool buf_contents_changed(buf_T *buf) void wipe_buffer( buf_T *buf, - int aucmd // When true trigger autocommands. + bool aucmd // When true trigger autocommands. ) { if (!aucmd) { @@ -5695,3 +5692,4 @@ void buf_open_scratch(handle_T bufnr, char *bufname) set_option_value("swf", 0L, NULL, OPT_LOCAL); RESET_BINDING(curwin); } + diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index ac7ead5f92..02a2ac36f7 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -9,6 +9,7 @@ #include "nvim/func_attr.h" #include "nvim/eval.h" #include "nvim/macros.h" +#include "nvim/memline.h" // Values for buflist_getfile() enum getf_values { @@ -128,4 +129,10 @@ static inline void buf_inc_changedtick(buf_T *const buf) buf_set_changedtick(buf, buf_get_changedtick(buf) + 1); } +static inline bool buf_is_empty(buf_T *buf) +{ + return buf->b_ml.ml_line_count == 1 + && *ml_get_buf(buf, (linenr_T)1, false) == '\0'; +} + #endif // NVIM_BUFFER_H diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index e3e538bd12..89f7448188 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -226,8 +226,12 @@ typedef struct { # define w_p_cuc w_onebuf_opt.wo_cuc // 'cursorcolumn' int wo_cul; # define w_p_cul w_onebuf_opt.wo_cul // 'cursorline' + char_u *wo_culopt; +# define w_p_culopt w_onebuf_opt.wo_culopt // 'cursorlineopt' char_u *wo_cc; # define w_p_cc w_onebuf_opt.wo_cc // 'colorcolumn' + char_u *wo_sbr; +# define w_p_sbr w_onebuf_opt.wo_sbr // 'showbreak' char_u *wo_stl; #define w_p_stl w_onebuf_opt.wo_stl // 'statusline' int wo_scb; @@ -525,6 +529,8 @@ struct file_buffer { int b_flags; // various BF_ flags int b_locked; // Buffer is being closed or referenced, don't // let autocommands wipe it out. + int b_ro_locked; // Non-zero when the buffer can't be changed. + // Used for FileChangedRO // // b_ffname has the full path of the file (NULL for no name). @@ -849,8 +855,8 @@ struct file_buffer { // may use a different synblock_T. sign_entry_T *b_signlist; // list of placed signs - int b_signcols_max; // cached maximum number of sign columns int b_signcols; // last calculated number of sign columns + bool b_signcols_valid; // calculated sign columns is valid Terminal *terminal; // Terminal instance associated with the buffer @@ -859,8 +865,8 @@ struct file_buffer { int b_mapped_ctrl_c; // modes where CTRL-C is mapped MarkTree b_marktree[1]; - Map(uint64_t, ExtmarkItem) *b_extmark_index; - Map(uint64_t, ExtmarkNs) *b_extmark_ns; // extmark namespaces + Map(uint64_t, ExtmarkItem) b_extmark_index[1]; + Map(uint64_t, ExtmarkNs) b_extmark_ns[1]; // extmark namespaces // array of channel_id:s which have asked to receive updates for this // buffer. @@ -1294,7 +1300,7 @@ struct window_S { /* * w_cline_height is the number of physical lines taken by the buffer line - * that the cursor is on. We use this to avoid extra calls to plines(). + * that the cursor is on. We use this to avoid extra calls to plines_win(). */ int w_cline_height; // current size of cursor line bool w_cline_folded; // cursor line is folded @@ -1384,12 +1390,14 @@ struct window_S { uint32_t w_p_fde_flags; // flags for 'foldexpr' uint32_t w_p_fdt_flags; // flags for 'foldtext' int *w_p_cc_cols; // array of columns to highlight or NULL + char_u w_p_culopt_flags; // flags for cursorline highlighting long w_p_siso; // 'sidescrolloff' local value long w_p_so; // 'scrolloff' local value int w_briopt_min; // minimum width for breakindent int w_briopt_shift; // additional shift for breakindent bool w_briopt_sbr; // sbr in 'briopt' + int w_briopt_list; // additional indent for lists // transform a pointer to a "onebuf" option into a "allbuf" option #define GLOBAL_WO(p) ((char *)p + sizeof(winopt_T)) diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c index 5c573530d1..1d131cc4b1 100644 --- a/src/nvim/buffer_updates.c +++ b/src/nvim/buffer_updates.c @@ -50,7 +50,7 @@ bool buf_updates_register(buf_T *buf, uint64_t channel_id, if (send_buffer) { Array args = ARRAY_DICT_INIT; args.size = 6; - args.items = xcalloc(sizeof(Object), args.size); + args.items = xcalloc(args.size, sizeof(Object)); // the first argument is always the buffer handle args.items[0] = BUFFER_OBJ(buf->handle); @@ -68,7 +68,7 @@ bool buf_updates_register(buf_T *buf, uint64_t channel_id, if (line_count >= 1) { linedata.size = line_count; - linedata.items = xcalloc(sizeof(Object), line_count); + linedata.items = xcalloc(line_count, sizeof(Object)); buf_collect_lines(buf, line_count, 1, true, &linedata, NULL); } @@ -93,7 +93,7 @@ void buf_updates_send_end(buf_T *buf, uint64_t channelid) { Array args = ARRAY_DICT_INIT; args.size = 1; - args.items = xcalloc(sizeof(Object), args.size); + args.items = xcalloc(args.size, sizeof(Object)); args.items[0] = BUFFER_OBJ(buf->handle); rpc_send_event(channelid, "nvim_buf_detach_event", args); } @@ -211,7 +211,7 @@ void buf_updates_send_changes(buf_T *buf, // send through the changes now channel contents now Array args = ARRAY_DICT_INIT; args.size = 6; - args.items = xcalloc(sizeof(Object), args.size); + args.items = xcalloc(args.size, sizeof(Object)); // the first argument is always the buffer handle args.items[0] = BUFFER_OBJ(buf->handle); @@ -230,7 +230,7 @@ void buf_updates_send_changes(buf_T *buf, if (num_added > 0) { STATIC_ASSERT(SIZE_MAX >= MAXLNUM, "size_t smaller than MAXLNUM"); linedata.size = (size_t)num_added; - linedata.items = xcalloc(sizeof(Object), (size_t)num_added); + linedata.items = xcalloc((size_t)num_added, sizeof(Object)); buf_collect_lines(buf, (size_t)num_added, firstline, true, &linedata, NULL); } @@ -313,7 +313,7 @@ void buf_updates_send_splice( return; } - // notify each of the active callbakcs + // notify each of the active callbacks size_t j = 0; for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) { BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); @@ -394,7 +394,7 @@ void buf_updates_changedtick_single(buf_T *buf, uint64_t channel_id) { Array args = ARRAY_DICT_INIT; args.size = 2; - args.items = xcalloc(sizeof(Object), args.size); + args.items = xcalloc(args.size, sizeof(Object)); // the first argument is always the buffer handle args.items[0] = BUFFER_OBJ(buf->handle); diff --git a/src/nvim/change.c b/src/nvim/change.c index c0183d4317..13b86e9244 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -22,6 +22,7 @@ #include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" +#include "nvim/plines.h" #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/state.h" @@ -40,18 +41,18 @@ /// "col" is the column for the message; non-zero when in insert mode and /// 'showmode' is on. /// Careful: may trigger autocommands that reload the buffer. -void change_warning(int col) +void change_warning(buf_T *buf, int col) { static char *w_readonly = N_("W10: Warning: Changing a readonly file"); - if (curbuf->b_did_warn == false + if (buf->b_did_warn == false && curbufIsChanged() == 0 && !autocmd_busy - && curbuf->b_p_ro) { - curbuf_lock++; - apply_autocmds(EVENT_FILECHANGEDRO, NULL, NULL, false, curbuf); - curbuf_lock--; - if (!curbuf->b_p_ro) { + && buf->b_p_ro) { + buf->b_ro_locked++; + apply_autocmds(EVENT_FILECHANGEDRO, NULL, NULL, false, buf); + buf->b_ro_locked--; + if (!buf->b_p_ro) { return; } // Do what msg() does, but with a column offset if the warning should @@ -70,7 +71,7 @@ void change_warning(int col) ui_flush(); os_delay(1002L, true); // give the user time to think about it } - curbuf->b_did_warn = true; + buf->b_did_warn = true; redraw_cmdline = false; // don't redraw and erase the message if (msg_row < Rows - 1) { showmode(); @@ -91,14 +92,14 @@ void changed(void) // Give a warning about changing a read-only file. This may also // check-out the file, thus change "curbuf"! - change_warning(0); + change_warning(curbuf, 0); // Create a swap file if that is wanted. // Don't do this for "nofile" and "nowrite" buffer types. if (curbuf->b_may_swap && !bt_dontwrite(curbuf) ) { - int save_need_wait_return = need_wait_return; + bool save_need_wait_return = need_wait_return; need_wait_return = false; ml_open_file(curbuf); @@ -290,13 +291,21 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, set_topline(wp, wp->w_topline); } - // Relative numbering may require updating more. Cursor line - // highlighting probably needs to be updated if it's below the - // change. - if (wp->w_p_rnu - || (wp->w_p_cul && lnum <= wp->w_last_cursorline)) { + // Relative numbering may require updating more. + if (wp->w_p_rnu) { redraw_later(wp, SOME_VALID); } + + // Cursor line highlighting probably need to be updated with + // "VALID" if it's below the change. + // If the cursor line is inside the change we need to redraw more. + if (wp->w_p_cul) { + if (xtra == 0) { + redraw_later(wp, VALID); + } else if (lnum <= wp->w_last_cursorline) { + redraw_later(wp, SOME_VALID); + } + } } } @@ -585,9 +594,9 @@ void ins_char_bytes(char_u *buf, size_t charlen) // cells. May result in adding spaces to fill a gap. colnr_T vcol; getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL); - colnr_T new_vcol = vcol + chartabsize(buf, vcol); + colnr_T new_vcol = vcol + win_chartabsize(curwin, buf, vcol); while (oldp[col + oldlen] != NUL && vcol < new_vcol) { - vcol += chartabsize(oldp + col + oldlen, vcol); + vcol += win_chartabsize(curwin, oldp + col + oldlen, vcol); // Don't need to remove a TAB that takes us to the right // position. if (vcol > new_vcol && oldp[col + oldlen] == TAB) { @@ -779,7 +788,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) int movelen = oldlen - col - count + 1; // includes trailing NUL if (movelen <= 1) { // If we just took off the last character of a non-blank line, and - // fixpos is TRUE, we don't want to end up positioned at the NUL, + // 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 && (ve_flags & VE_ONEMORE) == 0 @@ -1732,7 +1741,7 @@ int open_line( } if (did_append) { changed_lines(curwin->w_cursor.lnum, 0, curwin->w_cursor.lnum, 1L, true); - // bail out and just get the final lenght of the line we just manipulated + // 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)); extmark_splice(curbuf, (int)curwin->w_cursor.lnum-1, 0, 0, 0, 0, 1, 0, 1+extra, kExtmarkUndo); @@ -1828,7 +1837,7 @@ void truncate_line(int fixpos) /// Delete "nlines" lines at the cursor. /// Saves the lines for undo first if "undo" is true. -void del_lines(long nlines, int undo) +void del_lines(long nlines, bool undo) { long n; linenr_T first = curwin->w_cursor.lnum; diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 60af11e94b..94db7fb3b9 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -32,13 +32,9 @@ static uint64_t next_chan_id = CHAN_STDERR+1; /// Teardown the module void channel_teardown(void) { - if (!channels) { - return; - } - Channel *channel; - map_foreach_value(channels, channel, { + map_foreach_value(&channels, channel, { channel_close(channel->id, kChannelPartAll, NULL); }); } @@ -152,7 +148,6 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error) /// Initializes the module void channel_init(void) { - channels = pmap_new(uint64_t)(); channel_alloc(kChannelStreamStderr); rpc_init(); } @@ -177,7 +172,7 @@ Channel *channel_alloc(ChannelStreamType type) chan->exit_status = -1; chan->streamtype = type; assert(chan->id <= VARNUMBER_MAX); - pmap_put(uint64_t)(channels, chan->id, chan); + pmap_put(uint64_t)(&channels, chan->id, chan); return chan; } @@ -245,11 +240,15 @@ static void free_channel_event(void **argv) rpc_free(chan); } + if (chan->streamtype == kChannelStreamProc) { + process_free(&chan->stream.proc); + } + callback_reader_free(&chan->on_data); callback_reader_free(&chan->on_stderr); callback_free(&chan->on_exit); - pmap_del(uint64_t)(channels, chan->id); + pmap_del(uint64_t)(&channels, chan->id); multiqueue_free(chan->events); xfree(chan); } @@ -259,7 +258,7 @@ static void channel_destroy_early(Channel *chan) if ((chan->id != --next_chan_id)) { abort(); } - pmap_del(uint64_t)(channels, chan->id); + pmap_del(uint64_t)(&channels, chan->id); chan->id = 0; if ((--chan->refcount != 0)) { @@ -289,6 +288,9 @@ static void close_cb(Stream *stream, void *data) /// `on_stdout` is ignored /// @param[in] detach True if the job should not be killed when nvim exits, /// ignored if `pty` is true +/// @param[in] stdin_mode Stdin mode. Either kChannelStdinPipe to open a +/// channel for stdin or kChannelStdinNull to leave +/// stdin disconnected. /// @param[in] cwd Initial working directory for the job. Nvim's working /// directory if `cwd` is NULL /// @param[in] pty_width Width of the pty, ignored if `pty` is false @@ -302,7 +304,7 @@ static void close_cb(Stream *stream, void *data) Channel *channel_job_start(char **argv, CallbackReader on_stdout, CallbackReader on_stderr, Callback on_exit, bool pty, bool rpc, bool overlapped, bool detach, - const char *cwd, + ChannelStdinMode stdin_mode, const char *cwd, uint16_t pty_width, uint16_t pty_height, dict_T *env, varnumber_T *status_out) { @@ -345,7 +347,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, proc->overlapped = overlapped; char *cmd = xstrdup(proc->argv[0]); - bool has_out, has_err; + bool has_in, has_out, has_err; if (proc->type == kProcessTypePty) { has_out = true; has_err = false; @@ -353,7 +355,17 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, has_out = rpc || callback_reader_set(chan->on_data); has_err = callback_reader_set(chan->on_stderr); } - int status = process_spawn(proc, true, has_out, has_err); + + switch (stdin_mode) { + case kChannelStdinPipe: + has_in = true; + break; + case kChannelStdinNull: + has_in = false; + break; + } + + int status = process_spawn(proc, has_in, has_out, has_err); if (status) { EMSG3(_(e_jobspawn), os_strerror(status), cmd); xfree(cmd); @@ -369,7 +381,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, tv_dict_free(proc->env); } - wstream_init(&proc->in, 0); + if (has_in) { + wstream_init(&proc->in, 0); + } if (has_out) { rstream_init(&proc->out, 0); } @@ -683,9 +697,7 @@ static void channel_process_exit_cb(Process *proc, int status, void *data) { Channel *chan = data; if (chan->term) { - char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; - snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); - terminal_close(chan->term, msg); + terminal_close(chan->term, status); } // If process did not exit, we only closed the handle of a detached process. @@ -839,13 +851,24 @@ Dictionary channel_info(uint64_t id) const char *stream_desc, *mode_desc; switch (chan->streamtype) { - case kChannelStreamProc: + case kChannelStreamProc: { stream_desc = "job"; if (chan->stream.proc.type == kProcessTypePty) { const char *name = pty_process_tty_name(&chan->stream.pty); PUT(info, "pty", STRING_OBJ(cstr_to_string(name))); } + + char **p = chan->stream.proc.argv; + Array argv = ARRAY_DICT_INIT; + if (p != NULL) { + while (*p != NULL) { + ADD(argv, STRING_OBJ(cstr_to_string(*p))); + p++; + } + } + PUT(info, "argv", ARRAY_OBJ(argv)); break; + } case kChannelStreamStdio: stream_desc = "stdio"; @@ -886,7 +909,7 @@ Array channel_all_info(void) { Channel *channel; Array ret = ARRAY_DICT_INIT; - map_foreach_value(channels, channel, { + map_foreach_value(&channels, channel, { ADD(ret, DICTIONARY_OBJ(channel_info(channel->id))); }); return ret; diff --git a/src/nvim/channel.h b/src/nvim/channel.h index 9d26852ce5..9bc0df3615 100644 --- a/src/nvim/channel.h +++ b/src/nvim/channel.h @@ -28,6 +28,10 @@ typedef enum { kChannelPartAll } ChannelPart; +typedef enum { + kChannelStdinPipe, + kChannelStdinNull, +} ChannelStdinMode; typedef struct { Stream in; @@ -85,7 +89,7 @@ struct Channel { bool callback_scheduled; }; -EXTERN PMap(uint64_t) *channels INIT(= NULL); +EXTERN PMap(uint64_t) channels INIT(= MAP_INIT); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "channel.h.generated.h" @@ -94,7 +98,7 @@ EXTERN PMap(uint64_t) *channels INIT(= NULL); /// @returns Channel with the id or NULL if not found static inline Channel *find_channel(uint64_t id) { - return pmap_get(uint64_t)(channels, id); + return pmap_get(uint64_t)(&channels, id); } static inline Stream *channel_instream(Channel *chan) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index e2d844a351..ab4e4ad4bd 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -25,6 +25,7 @@ #include "nvim/move.h" #include "nvim/option.h" #include "nvim/os_unix.h" +#include "nvim/plines.h" #include "nvim/state.h" #include "nvim/strings.h" #include "nvim/path.h" @@ -733,80 +734,6 @@ int vim_strnsize(char_u *s, int len) return size; } -/// Return the number of characters 'c' will take on the screen, taking -/// into account the size of a tab. -/// Use a define to make it fast, this is used very often!!! -/// Also see getvcol() below. -/// -/// @param p -/// @param col -/// -/// @return Number of characters. -#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \ - if (*(p) == TAB && (!(wp)->w_p_list || wp->w_p_lcs_chars.tab1)) { \ - return tabstop_padding(col, (buf)->b_p_ts, (buf)->b_p_vts_array); \ - } else { \ - return ptr2cells(p); \ - } - -int chartabsize(char_u *p, colnr_T col) -{ - RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, p, col) -} - -static int win_chartabsize(win_T *wp, char_u *p, colnr_T col) -{ - RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, p, col) -} - -/// Return the number of characters the string 's' will take on the screen, -/// taking into account the size of a tab. -/// -/// @param s -/// -/// @return Number of characters the string will take on the screen. -int linetabsize(char_u *s) -{ - return linetabsize_col(0, s); -} - -/// Like linetabsize(), but starting at column "startcol". -/// -/// @param startcol -/// @param s -/// -/// @return Number of characters the string will take on the screen. -int linetabsize_col(int startcol, char_u *s) -{ - colnr_T col = startcol; - char_u *line = s; /* pointer to start of line, for breakindent */ - - while (*s != NUL) { - col += lbr_chartabsize_adv(line, &s, col); - } - return (int)col; -} - -/// Like linetabsize(), but for a given window instead of the current one. -/// -/// @param wp -/// @param line -/// @param len -/// -/// @return Number of characters the string will take on the screen. -unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len) -{ - colnr_T col = 0; - - for (char_u *s = line; - *s != NUL && (len == MAXCOL || s < line + len); - MB_PTR_ADV(s)) { - col += win_lbr_chartabsize(wp, line, s, col, NULL); - } - - return (unsigned int)col; -} - /// Check that "c" is a normal identifier character: /// Letters and characters from the 'isident' option. /// @@ -936,229 +863,6 @@ bool vim_isprintc_strict(int c) return c > 0 && (g_chartab[c] & CT_PRINT_CHAR); } -/// like chartabsize(), but also check for line breaks on the screen -/// -/// @param line -/// @param s -/// @param col -/// -/// @return The number of characters taken up on the screen. -int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col) -{ - if (!curwin->w_p_lbr && (*p_sbr == NUL) && !curwin->w_p_bri) { - if (curwin->w_p_wrap) { - return win_nolbr_chartabsize(curwin, s, col, NULL); - } - RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, s, col) - } - return win_lbr_chartabsize(curwin, line == NULL ? s: line, s, col, NULL); -} - -/// Call lbr_chartabsize() and advance the pointer. -/// -/// @param line -/// @param s -/// @param col -/// -/// @return The number of characters take up on the screen. -int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col) -{ - int retval; - - retval = lbr_chartabsize(line, *s, col); - MB_PTR_ADV(*s); - return retval; -} - -/// This function is used very often, keep it fast!!!! -/// -/// If "headp" not NULL, set *headp to the size of what we for 'showbreak' -/// string at start of line. Warning: *headp is only set if it's a non-zero -/// value, init to 0 before calling. -/// -/// @param wp -/// @param line -/// @param s -/// @param col -/// @param headp -/// -/// @return The number of characters taken up on the screen. -int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *headp) -{ - colnr_T col2; - colnr_T col_adj = 0; /* col + screen size of tab */ - colnr_T colmax; - int added; - int mb_added = 0; - int numberextra; - char_u *ps; - int n; - - // No 'linebreak', 'showbreak' and 'breakindent': return quickly. - if (!wp->w_p_lbr && !wp->w_p_bri && (*p_sbr == NUL)) { - if (wp->w_p_wrap) { - return win_nolbr_chartabsize(wp, s, col, headp); - } - RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, s, col) - } - - // First get normal size, without 'linebreak' - int size = win_chartabsize(wp, s, col); - int c = *s; - if (*s == TAB) { - col_adj = size - 1; - } - - // If 'linebreak' set check at a blank before a non-blank if the line - // needs a break here - if (wp->w_p_lbr - && vim_isbreak(c) - && !vim_isbreak((int)s[1]) - && wp->w_p_wrap - && (wp->w_width_inner != 0)) { - // Count all characters from first non-blank after a blank up to next - // non-blank after a blank. - numberextra = win_col_off(wp); - col2 = col; - colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj); - - if (col >= colmax) { - colmax += col_adj; - n = colmax + win_col_off2(wp); - - if (n > 0) { - colmax += (((col - colmax) / n) + 1) * n - col_adj; - } - } - - for (;;) { - ps = s; - MB_PTR_ADV(s); - c = *s; - - if (!(c != NUL - && (vim_isbreak(c) || col2 == col || !vim_isbreak((int)(*ps))))) { - break; - } - - col2 += win_chartabsize(wp, s, col2); - - if (col2 >= colmax) { /* doesn't fit */ - size = colmax - col + col_adj; - break; - } - } - } else if ((size == 2) - && (MB_BYTE2LEN(*s) > 1) - && wp->w_p_wrap - && in_win_border(wp, col)) { - // Count the ">" in the last column. - ++size; - mb_added = 1; - } - - // May have to add something for 'breakindent' and/or 'showbreak' - // string at start of line. - // Set *headp to the size of what we add. - added = 0; - - if ((*p_sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && (col != 0)) { - colnr_T sbrlen = 0; - int numberwidth = win_col_off(wp); - - numberextra = numberwidth; - col += numberextra + mb_added; - - if (col >= (colnr_T)wp->w_width_inner) { - col -= wp->w_width_inner; - numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp)); - if (col >= numberextra && numberextra > 0) { - col %= numberextra; - } - if (*p_sbr != NUL) { - sbrlen = (colnr_T)MB_CHARLEN(p_sbr); - if (col >= sbrlen) { - col -= sbrlen; - } - } - if (col >= numberextra && numberextra > 0) { - col %= numberextra; - } else if (col > 0 && numberextra > 0) { - col += numberwidth - win_col_off2(wp); - } - - numberwidth -= win_col_off2(wp); - } - - if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_width_inner)) { - if (*p_sbr != NUL) { - if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) { - // Calculate effective window width. - int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth; - int prev_width = col ? ((colnr_T)wp->w_width_inner - (sbrlen + col)) - : 0; - - if (width <= 0) { - width = 1; - } - added += ((size - prev_width) / width) * vim_strsize(p_sbr); - if ((size - prev_width) % width) { - // Wrapped, add another length of 'sbr'. - added += vim_strsize(p_sbr); - } - } else { - added += vim_strsize(p_sbr); - } - } - - if (wp->w_p_bri) - added += get_breakindent_win(wp, line); - - size += added; - if (col != 0) { - added = 0; - } - } - } - - if (headp != NULL) { - *headp = added + mb_added; - } - return size; -} - -/// Like win_lbr_chartabsize(), except that we know 'linebreak' is off and -/// 'wrap' is on. This means we need to check for a double-byte character that -/// doesn't fit at the end of the screen line. -/// -/// @param wp -/// @param s -/// @param col -/// @param headp -/// -/// @return The number of characters take up on the screen. -static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) -{ - int n; - - if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { - return tabstop_padding(col, - wp->w_buffer->b_p_ts, - wp->w_buffer->b_p_vts_array); - } - n = ptr2cells(s); - - // Add one cell for a double-width character in the last column of the - // window, displayed with a ">". - if ((n == 2) && (MB_BYTE2LEN(*s) > 1) && in_win_border(wp, col)) { - if (headp != NULL) { - *headp = 1; - } - return 3; - } - return n; -} - /// Check that virtual column "vcol" is in the rightmost column of window "wp". /// /// @param wp window @@ -1237,7 +941,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, // Also use this when 'list' is set but tabs take their normal size. if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL)) && !wp->w_p_lbr - && (*p_sbr == NUL) + && *get_showbreak_value(wp) == NUL && !wp->w_p_bri ) { for (;;) { head = 0; @@ -1681,6 +1385,8 @@ bool vim_isblankline(char_u *lbuf) /// If "prep" is not NULL, returns a flag to indicate the type of the number: /// 0 decimal /// '0' octal +/// 'O' octal +/// 'o' octal /// 'B' bin /// 'b' bin /// 'X' hex @@ -1692,20 +1398,25 @@ bool vim_isblankline(char_u *lbuf) /// If "what" contains STR2NR_OCT recognize octal numbers. /// If "what" contains STR2NR_HEX recognize hex numbers. /// If "what" contains STR2NR_FORCE always assume bin/oct/hex. +/// If "what" contains STR2NR_QUOTE ignore embedded single quotes /// If maxlen > 0, check at a maximum maxlen chars. +/// If strict is true, check the number strictly. return *len = 0 if fail. /// /// @param start /// @param prep Returns guessed type of number 0 = decimal, 'x' or 'X' is -/// hexadecimal, '0' = octal, 'b' or 'B' is binary. When using -/// STR2NR_FORCE is always zero. +/// hexadecimal, '0', 'o' or 'O' is octal, 'b' or 'B' is binary. +/// When using STR2NR_FORCE is always zero. /// @param len Returns the detected length of number. /// @param what Recognizes what number passed, @see ChStr2NrFlags. /// @param nptr Returns the signed result. /// @param unptr Returns the unsigned result. /// @param maxlen Max length of string to check. +/// @param strict If true, fail if the number has unexpected trailing +/// alpha-numeric chars: *len is set to 0 and nothing else is +/// returned. void vim_str2nr(const char_u *const start, int *const prep, int *const len, const int what, varnumber_T *const nptr, - uvarnumber_T *const unptr, const int maxlen) + uvarnumber_T *const unptr, const int maxlen, const bool strict) FUNC_ATTR_NONNULL_ARG(1) { const char *ptr = (const char *)start; @@ -1715,14 +1426,18 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, const bool negative = (ptr[0] == '-'); uvarnumber_T un = 0; + if (len != NULL) { + *len = 0; + } + if (negative) { ptr++; } if (what & STR2NR_FORCE) { - // When forcing main consideration is skipping the prefix. Octal and decimal - // numbers have no prefixes to skip. pre is not set. - switch ((unsigned)what & (~(unsigned)STR2NR_FORCE)) { + // When forcing main consideration is skipping the prefix. Decimal numbers + // have no prefixes to skip. pre is not set. + switch (what & ~(STR2NR_FORCE | STR2NR_QUOTE)) { case STR2NR_HEX: { if (!STRING_ENDED(ptr + 2) && ptr[0] == '0' @@ -1741,7 +1456,16 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } goto vim_str2nr_bin; } - case STR2NR_OCT: { + // Make STR2NR_OOCT work the same as STR2NR_OCT when forcing. + case STR2NR_OCT: + case STR2NR_OOCT: + case STR2NR_OCT | STR2NR_OOCT: { + if (!STRING_ENDED(ptr + 2) + && ptr[0] == '0' + && (ptr[1] == 'o' || ptr[1] == 'O') + && ascii_isodigit(ptr[2])) { + ptr += 2; + } goto vim_str2nr_oct; } case 0: { @@ -1751,9 +1475,9 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, abort(); } } - } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) - && !STRING_ENDED(ptr + 1) - && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { + } else if ((what & (STR2NR_HEX | STR2NR_OCT | STR2NR_OOCT | STR2NR_BIN)) + && !STRING_ENDED(ptr + 1) && ptr[0] == '0' && ptr[1] != '8' + && ptr[1] != '9') { pre = ptr[1]; // Detect hexadecimal: 0x or 0X followed by hex digit. if ((what & STR2NR_HEX) @@ -1771,10 +1495,18 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, ptr += 2; goto vim_str2nr_bin; } - // Detect octal number: zero followed by octal digits without '8' or '9'. + // Detect octal: 0o or 0O followed by octal digits (without '8' or '9'). + if ((what & STR2NR_OOCT) + && !STRING_ENDED(ptr + 2) + && (pre == 'O' || pre == 'o') + && ascii_isodigit(ptr[2])) { + ptr += 2; + goto vim_str2nr_oct; + } + // Detect old octal format: 0 followed by octal digits. pre = 0; if (!(what & STR2NR_OCT) - || !('0' <= ptr[1] && ptr[1] <= '7')) { + || !ascii_isodigit(ptr[1])) { goto vim_str2nr_dec; } for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) { @@ -1788,11 +1520,22 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, goto vim_str2nr_dec; } - // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. + // Do the conversion manually to avoid sscanf() quirks. abort(); // Should’ve used goto earlier. #define PARSE_NUMBER(base, cond, conv) \ do { \ - while (!STRING_ENDED(ptr) && (cond)) { \ + const char *const after_prefix = ptr; \ + while (!STRING_ENDED(ptr)) { \ + if ((what & STR2NR_QUOTE) && ptr > after_prefix && *ptr == '\'') { \ + ptr++; \ + if (!STRING_ENDED(ptr) && (cond)) { \ + continue; \ + } \ + ptr--; \ + } \ + if (!(cond)) { \ + break; \ + } \ const uvarnumber_T digit = (uvarnumber_T)(conv); \ /* avoid ubsan error for overflow */ \ if (un < UVARNUMBER_MAX / base \ @@ -1809,7 +1552,7 @@ vim_str2nr_bin: PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0')); goto vim_str2nr_proceed; vim_str2nr_oct: - PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0')); + PARSE_NUMBER(8, (ascii_isodigit(*ptr)), (*ptr - '0')); goto vim_str2nr_proceed; vim_str2nr_dec: PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0')); @@ -1820,6 +1563,12 @@ vim_str2nr_hex: #undef PARSE_NUMBER vim_str2nr_proceed: + // Check for an alpha-numeric character immediately following, that is + // most likely a typo. + if (strict && ptr - (const char *)start != maxlen && ASCII_ISALNUM(*ptr)) { + return; + } + if (prep != NULL) { *prep = pre; } diff --git a/src/nvim/charset.h b/src/nvim/charset.h index e657ce19b6..2fef4d78a2 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -23,13 +23,20 @@ typedef enum { STR2NR_BIN = (1 << 0), ///< Allow binary numbers. STR2NR_OCT = (1 << 1), ///< Allow octal numbers. STR2NR_HEX = (1 << 2), ///< Allow hexadecimal numbers. + STR2NR_OOCT = (1 << 3), ///< Octal with prefix "0o": 0o777 /// Force one of the above variants. /// /// STR2NR_FORCE|STR2NR_DEC is actually not different from supplying zero /// as flags, but still present for completeness. - STR2NR_FORCE = (1 << 3), + /// + /// STR2NR_FORCE|STR2NR_OCT|STR2NR_OOCT is the same as STR2NR_FORCE|STR2NR_OCT + /// or STR2NR_FORCE|STR2NR_OOCT. + STR2NR_FORCE = (1 << 7), /// Recognize all formats vim_str2nr() can recognize. - STR2NR_ALL = STR2NR_BIN | STR2NR_OCT | STR2NR_HEX, + STR2NR_ALL = STR2NR_BIN | STR2NR_OCT | STR2NR_HEX | STR2NR_OOCT, + /// Disallow octals numbers without the 0o prefix. + STR2NR_NO_OCT = STR2NR_BIN | STR2NR_HEX | STR2NR_OOCT, + STR2NR_QUOTE = (1 << 4), ///< Ignore embedded single quotes. } ChStr2NrFlags; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 5d2210dc7d..d4a68adeda 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -13,6 +13,7 @@ #include "nvim/memory.h" #include "nvim/misc1.h" #include "nvim/move.h" +#include "nvim/plines.h" #include "nvim/screen.h" #include "nvim/extmark.h" #include "nvim/state.h" diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index f3000f4430..5168ed6d0f 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -13,12 +13,7 @@ # include "decoration.c.generated.h" #endif -static PMap(uint64_t) *hl_decors; - -void decor_init(void) -{ - hl_decors = pmap_new(uint64_t)(); -} +static PMap(uint64_t) hl_decors; /// Add highlighting to a buffer, bounded by two cursor positions, /// with an offset. @@ -30,7 +25,7 @@ void decor_init(void) /// @param src_id src_id to use or 0 to use a new src_id group, /// or -1 for ungrouped highlight. /// @param hl_id Highlight group id -/// @param pos_start Cursor position to start the hightlighting at +/// @param pos_start Cursor position to start the highlighting at /// @param pos_end Cursor position to end the highlighting at /// @param offset Move the whole highlighting this many columns to the right void bufhl_add_hl_pos_offset(buf_T *buf, @@ -77,7 +72,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, Decoration *decor_hl(int hl_id) { assert(hl_id > 0); - Decoration **dp = (Decoration **)pmap_ref(uint64_t)(hl_decors, + Decoration **dp = (Decoration **)pmap_ref(uint64_t)(&hl_decors, (uint64_t)hl_id, true); if (*dp) { return *dp; @@ -119,7 +114,7 @@ void clear_virttext(VirtText *text) *text = (VirtText)KV_INITIAL_VALUE; } -VirtText *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id) +Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id) { MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, row, 0, itr); @@ -132,7 +127,7 @@ VirtText *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id) mark.id, false); if (item && (ns_id == 0 || ns_id == item->ns_id) && item->decor && kv_size(item->decor->virt_text)) { - return &item->decor->virt_text; + return item->decor; } marktree_itr_next(buf->b_marktree, itr); } @@ -150,7 +145,7 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state) } } kv_size(state->active) = 0; - return buf->b_extmark_index; + return map_size(buf->b_extmark_index); } @@ -218,6 +213,7 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state) } state->row = row; state->col_until = -1; + state->eol_col = -1; return true; // TODO(bfredl): be more precise } @@ -230,10 +226,6 @@ static void decor_add(DecorState *state, int start_row, int start_col, *decor, attr_id, kv_size(decor->virt_text) && owned, -1 }; - if (decor->virt_text_pos == kVTEndOfLine) { - range.win_col = -2; // handled separately - } - kv_pushp(state->active); size_t index; for (index = kv_size(state->active)-1; index > 0; index--) { @@ -345,29 +337,22 @@ void decor_redraw_end(DecorState *state) state->buf = NULL; } -VirtText decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, - bool *aligned) +bool decor_redraw_eol(buf_T *buf, DecorState *state, int *eol_attr, int eol_col) { decor_redraw_col(buf, MAXCOL, MAXCOL, false, state); - VirtText text = VIRTTEXT_EMPTY; + state->eol_col = eol_col; + bool has_virttext = false; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange item = kv_A(state->active, i); if (item.start_row == state->row && kv_size(item.decor.virt_text)) { - if (!kv_size(text) && item.decor.virt_text_pos == kVTEndOfLine) { - text = item.decor.virt_text; - } else if (item.decor.virt_text_pos == kVTRightAlign - || item.decor.virt_text_pos == kVTWinCol) { - *aligned = true; - } + has_virttext = true; } - if (item.decor.hl_eol && item.start_row <= state->row) { *eol_attr = hl_combine_attr(*eol_attr, item.attr_id); } } - - return text; + return has_virttext; } void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 4cebc0b731..28dabeeada 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -34,19 +34,20 @@ typedef enum { struct Decoration { - int hl_id; // highlight group VirtText virt_text; + int hl_id; // highlight group VirtTextPos virt_text_pos; - bool virt_text_hide; HlMode hl_mode; + bool virt_text_hide; bool hl_eol; + bool shared; // shared decoration, don't free // TODO(bfredl): style, signs, etc DecorPriority priority; - bool shared; // shared decoration, don't free int col; // fixed col value, like win_col + int virt_text_width; // width of virt_text }; -#define DECORATION_INIT { 0, KV_INITIAL_VALUE, kVTEndOfLine, false, \ - kHlModeUnknown, false, DECOR_PRIORITY_BASE, false, 0 } +#define DECORATION_INIT { KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \ + false, false, false, DECOR_PRIORITY_BASE, 0, 0 } typedef struct { int start_row; @@ -67,6 +68,8 @@ typedef struct { int row; int col_until; int current; + + int eol_col; VirtText *virt_text; } DecorState; diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 5f8b81822b..26269a44d2 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1059,7 +1059,7 @@ static int diff_file_internal(diffio_T *diffio) emit_cfg.ctxlen = 0; // don't need any diff_context here emit_cb.priv = &diffio->dio_diff; - emit_cb.outf = xdiff_out; + emit_cb.out_line = xdiff_out; if (xdl_diff(&diffio->dio_orig.din_mmfile, &diffio->dio_new.din_mmfile, ¶m, &emit_cfg, &emit_cb) < 0) { @@ -1334,9 +1334,9 @@ static void set_diff_option(win_T *wp, int value) curwin = wp; curbuf = curwin->w_buffer; - curbuf_lock++; + curbuf->b_ro_locked++; set_option_value("diff", (long)value, NULL, OPT_LOCAL); - curbuf_lock--; + curbuf->b_ro_locked--; curwin = old_curwin; curbuf = curwin->w_buffer; } @@ -1869,7 +1869,7 @@ int diff_check(win_T *wp, linenr_T lnum) /// @param idx1 first entry in diff "dp" /// @param idx2 second entry in diff "dp" /// -/// @return true if two entires are equal. +/// @return true if two entries are equal. static bool diff_equal_entry(diff_T *dp, int idx1, int idx2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { @@ -2603,7 +2603,7 @@ void ex_diffgetput(exarg_T *eap) // FileChangedRO autocommand, which may do nasty things and mess // everything up. if (!curbuf->b_changed) { - change_warning(0); + change_warning(curbuf, 0); if (diff_buf_idx(curbuf) != idx_to) { EMSG(_("E787: Buffer changed unexpectedly")); goto theend; @@ -2669,7 +2669,7 @@ void ex_diffgetput(exarg_T *eap) } } - buf_empty = BUFEMPTY(); + buf_empty = buf_is_empty(curbuf); added = 0; for (i = 0; i < count; ++i) { diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index dd32cef1e3..07e484c178 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1502,10 +1502,10 @@ char_u *get_digraph_for_char(int val_arg) /// Get a digraph. Used after typing CTRL-K on the command line or in normal /// mode. /// -/// @param cmdline TRUE when called from the cmdline +/// @param cmdline true when called from the cmdline /// /// @returns composed character, or NUL when ESC was used. -int get_digraph(int cmdline) +int get_digraph(bool cmdline) { int cc; no_mapping++; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 1579f3ff98..f39a6a281b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -6,59 +6,60 @@ */ #include <assert.h> -#include <string.h> #include <inttypes.h> #include <stdbool.h> +#include <string.h> -#include "nvim/vim.h" #include "nvim/ascii.h" -#include "nvim/edit.h" #include "nvim/buffer.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/digraph.h" +#include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/event/loop.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" +#include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/keymap.h" #include "nvim/main.h" -#include "nvim/extmark.h" +#include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/keymap.h" +#include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/time.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/spell.h" -#include "nvim/strings.h" #include "nvim/state.h" +#include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" -#include "nvim/ui.h" -#include "nvim/mouse.h" #include "nvim/terminal.h" +#include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/event/loop.h" -#include "nvim/mark.h" -#include "nvim/os/input.h" -#include "nvim/os/time.h" // Definitions used for CTRL-X submode. // Note: If you change CTRL-X submode, you must also maintain ctrl_x_msgs[] @@ -83,6 +84,7 @@ #define CTRL_X_SPELL 14 #define CTRL_X_LOCAL_MSG 15 ///< only used in "ctrl_x_msgs" #define CTRL_X_EVAL 16 ///< for builtin function complete() +#define CTRL_X_CMDLINE_CTRL_X 17 ///< CTRL-X typed in CTRL_X_CMDLINE #define CTRL_X_MSG(i) ctrl_x_msgs[(i) & ~CTRL_X_WANT_IDENT] #define CTRL_X_MODE_LINE_OR_EVAL(m) \ @@ -108,6 +110,7 @@ static char *ctrl_x_msgs[] = N_(" Spelling suggestion (s^N^P)"), N_(" Keyword Local completion (^N^P)"), NULL, // CTRL_X_EVAL doesn't use msg. + N_(" Command-line completion (^V^N^P)"), }; static char *ctrl_x_mode_names[] = { @@ -127,7 +130,8 @@ static char *ctrl_x_mode_names[] = { "omni", "spell", NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" - "eval" + "eval", + "cmdline", }; static char e_hitend[] = N_("Hit end of paragraph"); @@ -139,13 +143,13 @@ static char e_compldel[] = N_("E840: Completion function deleted text"); */ typedef struct compl_S compl_T; struct compl_S { - compl_T *cp_next; - compl_T *cp_prev; - char_u *cp_str; // matched text - char_u *(cp_text[CPT_COUNT]); // text for the menu + compl_T *cp_next; + compl_T *cp_prev; + char_u *cp_str; // matched text + char_u *(cp_text[CPT_COUNT]); // text for the menu typval_T cp_user_data; - char_u *cp_fname; // file containing the match, allocated when - // cp_flags has CP_FREE_FNAME + char_u *cp_fname; // file containing the match, allocated when + // cp_flags has CP_FREE_FNAME int cp_flags; // CP_ values int cp_number; // sequence number }; @@ -157,10 +161,10 @@ struct compl_S { * "compl_shown_match" is different from compl_curr_match during * ins_compl_get_exp(). */ -static compl_T *compl_first_match = NULL; -static compl_T *compl_curr_match = NULL; -static compl_T *compl_shown_match = NULL; -static compl_T *compl_old_match = NULL; +static compl_T *compl_first_match = NULL; +static compl_T *compl_curr_match = NULL; +static compl_T *compl_shown_match = NULL; +static compl_T *compl_old_match = NULL; /* After using a cursor key <Enter> selects a match in the popup menu, * otherwise it inserts a line break. */ @@ -168,7 +172,7 @@ static int compl_enter_selects = FALSE; /* When "compl_leader" is not NULL only matches that start with this string * are used. */ -static char_u *compl_leader = NULL; +static char_u *compl_leader = NULL; static int compl_get_longest = FALSE; /* put longest common string in compl_leader */ @@ -195,15 +199,15 @@ static bool compl_started = false; static int ctrl_x_mode = CTRL_X_NORMAL; static int compl_matches = 0; -static char_u *compl_pattern = NULL; +static char_u *compl_pattern = NULL; static Direction compl_direction = FORWARD; static Direction compl_shows_dir = FORWARD; static int compl_pending = 0; // > 1 for postponed CTRL-N static pos_T compl_startpos; static colnr_T compl_col = 0; /* column where the text starts * that is being completed */ -static char_u *compl_orig_text = NULL; /* text as it was before - * completion started */ +static char_u *compl_orig_text = NULL; /* text as it was before + * completion started */ static int compl_cont_mode = 0; static expand_T compl_xp; @@ -256,8 +260,8 @@ static colnr_T Insstart_textlen; // length of line when insert started static colnr_T Insstart_blank_vcol; // vcol for first inserted blank static bool update_Insstart_orig = true; // set Insstart_orig to Insstart -static char_u *last_insert = NULL; // the text of the previous insert, - // K_SPECIAL and CSI are escaped +static char_u *last_insert = NULL; // the text of the previous insert, + // K_SPECIAL and CSI are escaped static int last_insert_skip; // nr of chars in front of previous insert static int new_insert_skip; // nr of chars in front of current insert static int did_restart_edit; // "restart_edit" when calling edit() @@ -309,7 +313,7 @@ static void insert_enter(InsertState *s) s->ptr = (char_u *)"i"; } - set_vim_var_string(VV_INSERTMODE, (char *) s->ptr, 1); + set_vim_var_string(VV_INSERTMODE, (char *)s->ptr, 1); set_vim_var_string(VV_CHAR, NULL, -1); ins_apply_autocmds(EVENT_INSERTENTER); @@ -467,7 +471,7 @@ static void insert_enter(InsertState *s) } if (!p_im && did_restart_edit == 0) { - change_warning(s->i == 0 ? 0 : s->i + 1); + change_warning(curbuf, s->i == 0 ? 0 : s->i + 1); } ui_cursor_shape(); // may show different cursor shape @@ -761,7 +765,8 @@ static int insert_execute(VimState *state, int key) s->c = do_digraph(s->c); - if ((s->c == Ctrl_V || s->c == Ctrl_Q) && ctrl_x_mode == CTRL_X_CMDLINE) { + if ((s->c == Ctrl_V || s->c == Ctrl_Q) + && (ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X)) { insert_do_complete(s); return 1; } @@ -790,15 +795,22 @@ static int insert_execute(VimState *state, int key) } } - if (curwin->w_p_rl) + if (curwin->w_p_rl) { switch (s->c) { - case K_LEFT: s->c = K_RIGHT; break; - case K_S_LEFT: s->c = K_S_RIGHT; break; - case K_C_LEFT: s->c = K_C_RIGHT; break; - case K_RIGHT: s->c = K_LEFT; break; - case K_S_RIGHT: s->c = K_S_LEFT; break; - case K_C_RIGHT: s->c = K_C_LEFT; break; + case K_LEFT: + s->c = K_RIGHT; break; + case K_S_LEFT: + s->c = K_S_RIGHT; break; + case K_C_LEFT: + s->c = K_C_RIGHT; break; + case K_RIGHT: + s->c = K_LEFT; break; + case K_S_RIGHT: + s->c = K_S_LEFT; break; + case K_C_RIGHT: + s->c = K_C_LEFT; break; } + } // If 'keymodel' contains "startsel", may start selection. If it // does, a CTRL-O and c will be stuffed, we need to get these @@ -1287,8 +1299,9 @@ normalchar: // If the new value is already inserted or an empty string // then don't insert any character. - if (s->c == NUL) + if (s->c == NUL) { break; + } } // Try to perform smart-indenting. ins_try_si(s->c); @@ -1308,8 +1321,8 @@ normalchar: // special character. Let CTRL-] expand abbreviations without // inserting it. if (vim_iswordc(s->c) - // Add ABBR_OFF for characters above 0x100, this is - // what check_abbr() expects. + // Add ABBR_OFF for characters above 0x100, this is + // what check_abbr() expects. || (!echeck_abbr((s->c >= 0x100) ? (s->c + ABBR_OFF) : s->c) && s->c != Ctrl_RSB)) { insert_special(s->c, false, false); @@ -1414,21 +1427,20 @@ bool edit(int cmdchar, bool startln, long count) return s->c == Ctrl_O; } -/* - * Redraw for Insert mode. - * This is postponed until getting the next character to make '$' in the 'cpo' - * option work correctly. - * Only redraw when there are no characters available. This speeds up - * inserting sequences of characters (e.g., for CTRL-R). - */ -static void ins_redraw( - bool ready // not busy with something -) +/// Redraw for Insert mode. +/// This is postponed until getting the next character to make '$' in the 'cpo' +/// option work correctly. +/// Only redraw when there are no characters available. This speeds up +/// inserting sequences of characters (e.g., for CTRL-R). +/// +/// @param ready not busy with something +static void ins_redraw(bool ready) { bool conceal_cursor_moved = false; - if (char_avail()) + if (char_avail()) { return; + } // Trigger CursorMoved if the cursor moved. Not when the popup menu is // visible, the command might delete it. @@ -1651,11 +1663,11 @@ static void init_prompt(int cmdchar_todo) check_cursor(); } -// Return TRUE if the cursor is in the editable position of the prompt line. -int prompt_curpos_editable(void) +/// @return true if the cursor is in the editable position of the prompt line. +bool prompt_curpos_editable(void) { - return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count - && curwin->w_cursor.col >= (int)STRLEN(prompt_text()); + return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count + && curwin->w_cursor.col >= (int)STRLEN(prompt_text()); } /* @@ -1684,8 +1696,9 @@ void display_dollar(colnr_T col) { colnr_T save_col; - if (!redrawing()) + if (!redrawing()) { return; + } save_col = curwin->w_cursor.col; curwin->w_cursor.col = col; @@ -1713,34 +1726,28 @@ static void undisplay_dollar(void) } } -/* - * Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D). - * Keep the cursor on the same character. - * type == INDENT_INC increase indent (for CTRL-T or <Tab>) - * type == INDENT_DEC decrease indent (for CTRL-D) - * type == INDENT_SET set indent to "amount" - * if round is TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec). - */ -void -change_indent ( - int type, - int amount, - int round, - int replaced, // replaced character, put on replace stack - int call_changed_bytes // call changed_bytes() -) +/// Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D). +/// Keep the cursor on the same character. +/// type == INDENT_INC increase indent (for CTRL-T or <Tab>) +/// type == INDENT_DEC decrease indent (for CTRL-D) +/// type == INDENT_SET set indent to "amount" +/// +/// @param round if TRUE, round the indent to 'shiftwidth' (only with _INC and _Dec). +/// @param replaced replaced character, put on replace stack +/// @param call_changed_bytes call changed_bytes() +void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes) { int vcol; int last_vcol; int insstart_less; // reduction for Insstart.col int new_cursor_col; int i; - char_u *ptr; + char_u *ptr; int save_p_list; int start_col; colnr_T vc; colnr_T orig_col = 0; // init for GCC - char_u *new_line, *orig_line = NULL; // init for GCC + char_u *new_line, *orig_line = NULL; // init for GCC // VREPLACE mode needs to know what the line was like before changing if (State & VREPLACE_FLAG) { @@ -1772,8 +1779,9 @@ change_indent ( * If the cursor is in the indent, compute how many screen columns the * cursor is to the left of the first non-blank. */ - if (new_cursor_col < 0) + if (new_cursor_col < 0) { vcol = get_indent() - vcol; + } if (new_cursor_col > 0) { // can't fix replace stack start_col = -1; @@ -1782,9 +1790,9 @@ change_indent ( /* * Set the new indent. The cursor will be put on the first non-blank. */ - if (type == INDENT_SET) + if (type == INDENT_SET) { (void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0); - else { + } else { int save_State = State; // Avoid being called recursively. @@ -1810,12 +1818,13 @@ change_indent ( * When changing the indent while the cursor is touching it, reset * Insstart_col to 0. */ - if (new_cursor_col == 0) + if (new_cursor_col == 0) { insstart_less = MAXCOL; + } new_cursor_col += curwin->w_cursor.col; - } else if (!(State & INSERT)) + } else if (!(State & INSERT)) { new_cursor_col = curwin->w_cursor.col; - else { + } else { /* * Compute the screen column where the cursor should be. */ @@ -1862,10 +1871,11 @@ change_indent ( curwin->w_p_list = save_p_list; - if (new_cursor_col <= 0) + if (new_cursor_col <= 0) { curwin->w_cursor.col = 0; - else + } else { curwin->w_cursor.col = (colnr_T)new_cursor_col; + } curwin->w_set_curswant = TRUE; changed_cline_bef_curs(); @@ -1874,15 +1884,17 @@ change_indent ( */ if (State & INSERT) { if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) { - if ((int)Insstart.col <= insstart_less) + if ((int)Insstart.col <= insstart_less) { Insstart.col = 0; - else + } else { Insstart.col -= insstart_less; + } } - if ((int)ai_col <= insstart_less) + if ((int)ai_col <= insstart_less) { ai_col = 0; - else + } else { ai_col -= insstart_less; + } } /* @@ -1974,10 +1986,11 @@ void backspace_until_column(int col) { while ((int)curwin->w_cursor.col > col) { curwin->w_cursor.col--; - if (State & REPLACE_FLAG) + if (State & REPLACE_FLAG) { replace_do_bs(col); - else if (!del_char_after_col(col)) + } else if (!del_char_after_col(col)) { break; + } } } @@ -2019,20 +2032,22 @@ static bool del_char_after_col(int limit_col) */ static void ins_ctrl_x(void) { - /* CTRL-X after CTRL-X CTRL-V doesn't do anything, so that CTRL-X - * CTRL-V works like CTRL-N */ - if (ctrl_x_mode != CTRL_X_CMDLINE) { - /* if the next ^X<> won't ADD nothing, then reset - * compl_cont_status */ - if (compl_cont_status & CONT_N_ADDS) + if (ctrl_x_mode != CTRL_X_CMDLINE && ctrl_x_mode != CTRL_X_CMDLINE_CTRL_X) { + // if the next ^X<> won't ADD nothing, then reset compl_cont_status + if (compl_cont_status & CONT_N_ADDS) { compl_cont_status |= CONT_INTRPT; - else + } else { compl_cont_status = 0; + } // We're not sure which CTRL-X mode it will be yet ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); edit_submode_pre = NULL; showmode(); + } else { + // CTRL-X in CTRL-X CTRL-V mode behaves differently to make CTRL-X + // CTRL-V look like CTRL-N + ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X; } } @@ -2042,7 +2057,8 @@ bool ctrl_x_mode_not_default(void) return ctrl_x_mode != CTRL_X_NORMAL; } -// Whether CTRL-X was typed without a following character. +// Whether CTRL-X was typed without a following character, +// not including when in CTRL-X CTRL-V mode. bool ctrl_x_mode_not_defined_yet(void) { return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET; @@ -2094,12 +2110,14 @@ bool vim_is_ctrl_x_key(int c) case 0: // Not in any CTRL-X mode return c == Ctrl_N || c == Ctrl_P || c == Ctrl_X; case CTRL_X_NOT_DEFINED_YET: + case CTRL_X_CMDLINE_CTRL_X: return c == Ctrl_X || c == Ctrl_Y || c == Ctrl_E || c == Ctrl_L || c == Ctrl_F || c == Ctrl_RSB || c == Ctrl_I || c == Ctrl_D || c == Ctrl_P || c == Ctrl_N || c == Ctrl_T || c == Ctrl_V || c == Ctrl_Q || c == Ctrl_U || c == Ctrl_O - || c == Ctrl_S || c == Ctrl_K || c == 's'; + || c == Ctrl_S || c == Ctrl_K || c == 's' + || c == Ctrl_Z; case CTRL_X_SCROLL: return c == Ctrl_Y || c == Ctrl_E; case CTRL_X_WHOLE_LINE: @@ -2153,6 +2171,7 @@ static bool ins_compl_accept_char(int c) return vim_isfilec(c) && !vim_ispathsep(c); case CTRL_X_CMDLINE: + case CTRL_X_CMDLINE_CTRL_X: case CTRL_X_OMNI: // Command line and Omni completion can work with just about any // printable character, but do stop at white space. @@ -2171,8 +2190,8 @@ static bool ins_compl_accept_char(int c) /// the rest of the word to be in -- webb /// /// @param[in] cont_s_ipos next ^X<> will set initial_pos -int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, - Direction dir, bool cont_s_ipos) +int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, Direction dir, + bool cont_s_ipos) FUNC_ATTR_NONNULL_ARG(1) { char_u *str = str_arg; @@ -2315,15 +2334,12 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname, /// @return NOTDONE if the given string is already in the list of completions, /// otherwise it is added to the list and OK is returned. FAIL will be /// returned in case of error. -static int ins_compl_add(char_u *const str, int len, - char_u *const fname, - char_u *const *const cptext, - const bool cptext_allocated, - typval_T *user_data, - const Direction cdir, int flags_arg, const bool adup) +static int ins_compl_add(char_u *const str, int len, char_u *const fname, + char_u *const *const cptext, const bool cptext_allocated, + typval_T *user_data, const Direction cdir, int flags_arg, const bool adup) FUNC_ATTR_NONNULL_ARG(1) { - compl_T *match; + compl_T *match; const Direction dir = (cdir == kDirectionNotSet ? compl_direction : cdir); int flags = flags_arg; @@ -2419,9 +2435,9 @@ static int ins_compl_add(char_u *const str, int len, /* * Link the new match structure in the list of matches. */ - if (compl_first_match == NULL) + if (compl_first_match == NULL) { match->cp_next = match->cp_prev = NULL; - else if (dir == FORWARD) { + } else if (dir == FORWARD) { match->cp_next = compl_curr_match->cp_next; match->cp_prev = compl_curr_match; } else { // BACKWARD @@ -2453,7 +2469,7 @@ static int ins_compl_add(char_u *const str, int len, /// /// @param match completion match /// @param str character string to check -/// @param len lenth of "str" +/// @param len length of "str" static bool ins_compl_equal(compl_T *match, char_u *str, size_t len) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { @@ -2471,7 +2487,7 @@ static bool ins_compl_equal(compl_T *match, char_u *str, size_t len) */ static void ins_compl_longest_match(compl_T *match) { - char_u *p, *s; + char_u *p, *s; int c1, c2; int had_match; @@ -2485,8 +2501,9 @@ static void ins_compl_longest_match(compl_T *match) /* When the match isn't there (to avoid matching itself) remove it * again after redrawing. */ - if (!had_match) + if (!had_match) { ins_compl_delete(); + } compl_used_match = false; } else { // Reduce the text if this match differs from compl_leader. @@ -2515,8 +2532,9 @@ static void ins_compl_longest_match(compl_T *match) /* When the match isn't there (to avoid matching itself) remove it * again after redrawing. */ - if (!had_match) + if (!had_match) { ins_compl_delete(); + } } compl_used_match = false; @@ -2600,8 +2618,9 @@ void set_completion(colnr_T startcol, list_T *list) ins_compl_free(); compl_direction = FORWARD; - if (startcol > curwin->w_cursor.col) + if (startcol > curwin->w_cursor.col) { startcol = curwin->w_cursor.col; + } compl_col = startcol; compl_length = (int)curwin->w_cursor.col - (int)startcol; // compl_pattern doesn't need to be set @@ -2724,8 +2743,8 @@ static void trigger_complete_changed_event(int cur) /// Also adjusts "compl_shown_match" to an entry that is actually displayed. void ins_compl_show_pum(void) { - compl_T *compl; - compl_T *shown_compl = NULL; + compl_T *compl; + compl_T *shown_compl = NULL; bool did_find_shown_match = false; bool shown_match_ok = false; int i; @@ -2734,8 +2753,9 @@ void ins_compl_show_pum(void) int lead_len = 0; bool array_changed = false; - if (!pum_wanted() || !pum_enough_matches()) + if (!pum_wanted() || !pum_enough_matches()) { return; + } // Dirty hard-coded hack: remove any matchparen highlighting. do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif"); @@ -2766,8 +2786,9 @@ void ins_compl_show_pum(void) } compl = compl->cp_next; } while (compl != NULL && compl != compl_first_match); - if (compl_match_arraysize == 0) + if (compl_match_arraysize == 0) { return; + } assert(compl_match_arraysize >= 0); compl_match_array = xcalloc(compl_match_arraysize, sizeof(pumitem_T)); @@ -2798,18 +2819,20 @@ void ins_compl_show_pum(void) cur = i; } - if (compl->cp_text[CPT_ABBR] != NULL) + if (compl->cp_text[CPT_ABBR] != NULL) { compl_match_array[i].pum_text = compl->cp_text[CPT_ABBR]; - else + } else { compl_match_array[i].pum_text = compl->cp_str; + } compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND]; compl_match_array[i].pum_info = compl->cp_text[CPT_INFO]; - if (compl->cp_text[CPT_MENU] != NULL) + if (compl->cp_text[CPT_MENU] != NULL) { compl_match_array[i++].pum_extra = compl->cp_text[CPT_MENU]; - else + } else { compl_match_array[i++].pum_extra = compl->cp_fname; + } } if (compl == compl_shown_match) { @@ -2866,23 +2889,18 @@ void ins_compl_show_pum(void) #define DICT_FIRST (1) // use just first element in "dict" #define DICT_EXACT (2) // "dict" is the exact name of a file -/* - * Add any identifiers that match the given pattern in the list of dictionary - * files "dict_start" to the list of completions. - */ -static void -ins_compl_dictionaries ( - char_u *dict_start, - char_u *pat, - int flags, // DICT_FIRST and/or DICT_EXACT - int thesaurus // Thesaurus completion -) -{ - char_u *dict = dict_start; - char_u *ptr; - char_u *buf; +/// Add any identifiers that match the given pattern in the list of dictionary +/// files "dict_start" to the list of completions. +/// +/// @param flags DICT_FIRST and/or DICT_EXACT +/// @param thesaurus Thesaurus completion +static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, int thesaurus) +{ + char_u *dict = dict_start; + char_u *ptr; + char_u *buf; regmatch_T regmatch; - char_u **files; + char_u **files; int count; int save_p_scs; Direction dir = compl_direction; @@ -2890,10 +2908,11 @@ ins_compl_dictionaries ( if (*dict == NUL) { /* When 'dictionary' is empty and spell checking is enabled use * "spell". */ - if (!thesaurus && curwin->w_p_spell) + if (!thesaurus && curwin->w_p_spell) { dict = (char_u *)"spell"; - else + } else { return; + } } buf = xmalloc(LSIZE); @@ -2901,8 +2920,9 @@ ins_compl_dictionaries ( // If 'infercase' is set, don't use 'smartcase' here save_p_scs = p_scs; - if (curbuf->b_p_inf) + if (curbuf->b_p_inf) { p_scs = FALSE; + } /* When invoked to match whole lines for CTRL-X CTRL-L adjust the pattern * to only match at the start of a line. Otherwise just match the @@ -2918,8 +2938,9 @@ ins_compl_dictionaries ( xfree(ptr); } else { regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); - if (regmatch.regprog == NULL) + if (regmatch.regprog == NULL) { goto theend; + } } // ignore case depends on 'ignorecase', 'smartcase' and "pat" @@ -2934,30 +2955,34 @@ ins_compl_dictionaries ( * backticks (for security, the 'dict' option may have been set in * a modeline). */ copy_option_part(&dict, buf, LSIZE, ","); - if (!thesaurus && STRCMP(buf, "spell") == 0) + if (!thesaurus && STRCMP(buf, "spell") == 0) { count = -1; - else if (vim_strchr(buf, '`') != NULL - || expand_wildcards(1, &buf, &count, &files, - EW_FILE|EW_SILENT) != OK) + } else if (vim_strchr(buf, '`') != NULL + || expand_wildcards(1, &buf, &count, &files, + EW_FILE|EW_SILENT) != OK) { count = 0; + } } if (count == -1) { /* Complete from active spelling. Skip "\<" in the pattern, we * don't use it as a RE. */ - if (pat[0] == '\\' && pat[1] == '<') + if (pat[0] == '\\' && pat[1] == '<') { ptr = pat + 2; - else + } else { ptr = pat; + } spell_dump_compl(ptr, regmatch.rm_ic, &dir, 0); } else if (count > 0) { // avoid warning for using "files" uninit ins_compl_files(count, files, thesaurus, flags, - ®match, buf, &dir); - if (flags != DICT_EXACT) + ®match, buf, &dir); + if (flags != DICT_EXACT) { FreeWild(count, files); + } } - if (flags != 0) + if (flags != 0) { break; + } } theend: @@ -2966,14 +2991,13 @@ theend: xfree(buf); } -static void ins_compl_files(int count, char_u **files, int thesaurus, - int flags, regmatch_T *regmatch, char_u *buf, - Direction *dir) +static void ins_compl_files(int count, char_u **files, int thesaurus, int flags, + regmatch_T *regmatch, char_u *buf, Direction *dir) FUNC_ATTR_NONNULL_ARG(2, 7) { - char_u *ptr; + char_u *ptr; int i; - FILE *fp; + FILE *fp; int add_r; for (i = 0; i < count && !got_int && !compl_interrupted; i++) { @@ -3016,8 +3040,9 @@ static void ins_compl_files(int count, char_u **files, int thesaurus, /* Find start of the next word. Skip white * space and punctuation. */ ptr = find_word_start(ptr); - if (*ptr == NUL || *ptr == NL) + if (*ptr == NUL || *ptr == NL) { break; + } wstart = ptr; // Find end of the word. @@ -3095,11 +3120,12 @@ char_u *find_word_end(char_u *ptr) */ static char_u *find_line_end(char_u *ptr) { - char_u *s; + char_u *s; s = ptr + STRLEN(ptr); - while (s > ptr && (s[-1] == CAR || s[-1] == NL)) + while (s > ptr && (s[-1] == CAR || s[-1] == NL)) { --s; + } return s; } @@ -3113,8 +3139,9 @@ static void ins_compl_free(void) XFREE_CLEAR(compl_pattern); XFREE_CLEAR(compl_leader); - if (compl_first_match == NULL) + if (compl_first_match == NULL) { return; + } ins_compl_del_pum(); pum_clear(); @@ -3276,9 +3303,9 @@ void get_complete_info(list_T *what_list, dict_T *retdict) tv_dict_add_str(di, S_LEN("info"), (char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); if (match->cp_user_data.v_type == VAR_UNKNOWN) { - tv_dict_add_str(di, S_LEN("user_data"), ""); + tv_dict_add_str(di, S_LEN("user_data"), ""); } else { - tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); + tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data); } } match = match->cp_next; @@ -3318,8 +3345,8 @@ static char_u * ins_compl_mode(void) */ static int ins_compl_bs(void) { - char_u *line; - char_u *p; + char_u *line; + char_u *p; line = get_cursor_line_ptr(); p = line + curwin->w_cursor.col; @@ -3339,8 +3366,9 @@ static int ins_compl_bs(void) /* Deleted more than what was used to find matches or didn't finish * finding all matches: need to look for matches all over again. */ if (curwin->w_cursor.col <= compl_col + compl_length - || ins_compl_need_restart()) + || ins_compl_need_restart()) { ins_compl_restart(); + } xfree(compl_leader); compl_leader = vim_strnsave(line + compl_col, (int)(p - line) - compl_col); @@ -3397,8 +3425,9 @@ static void ins_compl_new_leader(void) /* Don't let Enter select the original text when there is no popup menu. * Don't let Enter select when use user function and refresh_always is set */ - if (compl_match_array == NULL || ins_compl_need_restart()) + if (compl_match_array == NULL || ins_compl_need_restart()) { compl_enter_selects = FALSE; + } } /* @@ -3409,8 +3438,9 @@ static int ins_compl_len(void) { int off = (int)curwin->w_cursor.col - (int)compl_col; - if (off < 0) + if (off < 0) { return 0; + } return off; } @@ -3423,7 +3453,7 @@ static void ins_compl_addleader(int c) int cc; if (stop_arrow() == FAIL) { - return; + return; } if ((cc = utf_char2len(c)) > 1) { char_u buf[MB_MAXBYTES + 1]; @@ -3488,10 +3518,10 @@ static void ins_compl_set_original_text(char_u *str) */ static void ins_compl_addfrommatch(void) { - char_u *p; + char_u *p; int len = (int)curwin->w_cursor.col - (int)compl_col; int c; - compl_T *cp; + compl_T *cp; assert(compl_shown_match != NULL); p = compl_shown_match->cp_str; if ((int)STRLEN(p) <= len) { // the match is too short @@ -3503,15 +3533,17 @@ static void ins_compl_addfrommatch(void) && cp != compl_first_match; cp = cp->cp_next) { if (compl_leader == NULL || ins_compl_equal(cp, compl_leader, - (int)STRLEN(compl_leader))) { + (int)STRLEN(compl_leader))) { p = cp->cp_str; break; } } - if (p == NULL || (int)STRLEN(p) <= len) + if (p == NULL || (int)STRLEN(p) <= len) { return; - } else + } + } else { return; + } } p += len; c = PTR2CHAR(p); @@ -3533,8 +3565,9 @@ static bool ins_compl_prep(int c) /* Forget any previous 'special' messages if this is actually * a ^X mode key - bar ^R, in which case we wait to see what it gives us. */ - if (c != Ctrl_R && vim_is_ctrl_x_key(c)) + if (c != Ctrl_R && vim_is_ctrl_x_key(c)) { edit_submode_extra = NULL; + } // Ignore end of Select mode mapping and mouse scroll buttons. if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP @@ -3543,6 +3576,26 @@ static bool ins_compl_prep(int c) return retval; } + if (ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X && c != Ctrl_X) { + if (c == Ctrl_V || c == Ctrl_Q || c == Ctrl_Z || ins_compl_pum_key(c) + || !vim_is_ctrl_x_key(c)) { + // Not starting another completion mode. + ctrl_x_mode = CTRL_X_CMDLINE; + + // CTRL-X CTRL-Z should stop completion without inserting anything + if (c == Ctrl_Z) { + retval = true; + } + } else { + ctrl_x_mode = CTRL_X_CMDLINE; + + // Other CTRL-X keys first stop completion, then start another + // completion mode. + ins_compl_prep(' '); + ctrl_x_mode = CTRL_X_NOT_DEFINED_YET; + } + } + // Set "compl_get_longest" when finding the first matches. if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || (ctrl_x_mode == CTRL_X_NORMAL && !compl_started)) { @@ -3559,10 +3612,11 @@ static bool ins_compl_prep(int c) case Ctrl_E: case Ctrl_Y: ctrl_x_mode = CTRL_X_SCROLL; - if (!(State & REPLACE_FLAG)) + if (!(State & REPLACE_FLAG)) { edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)"); - else + } else { edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)"); + } edit_submode_pre = NULL; showmode(); break; @@ -3608,6 +3662,12 @@ static bool ins_compl_prep(int c) case Ctrl_Q: ctrl_x_mode = CTRL_X_CMDLINE; break; + case Ctrl_Z: + ctrl_x_mode = CTRL_X_NORMAL; + edit_submode = NULL; + showmode(); + retval = true; + break; case Ctrl_P: case Ctrl_N: /* ^X^P means LOCAL expansion if nothing interrupted (eg we @@ -3617,10 +3677,11 @@ static bool ins_compl_prep(int c) * ^X^F^X^P or ^P^X^X^P, see below) * nothing changes if interrupting mode 0, (eg, the flag * doesn't change when going to ADDING mode -- Acevedo */ - if (!(compl_cont_status & CONT_INTRPT)) + if (!(compl_cont_status & CONT_INTRPT)) { compl_cont_status |= CONT_LOCAL; - else if (compl_cont_mode != 0) + } else if (compl_cont_mode != 0) { compl_cont_status &= ~CONT_LOCAL; + } FALLTHROUGH; default: /* If we have typed at least 2 ^X's... for modes != 0, we set @@ -3634,10 +3695,11 @@ static bool ins_compl_prep(int c) * In mode 0 an extra ^X is needed since ^X^P goes to ADDING * mode -- Acevedo */ if (c == Ctrl_X) { - if (compl_cont_mode != 0) + if (compl_cont_mode != 0) { compl_cont_status = 0; - else + } else { compl_cont_mode = CTRL_X_NOT_DEFINED_YET; + } } ctrl_x_mode = CTRL_X_NORMAL; edit_submode = NULL; @@ -3680,10 +3742,11 @@ static bool ins_compl_prep(int c) * When using the longest match, edited the match or used * CTRL-E then don't use the current match. */ - if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) + if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) { ptr = compl_curr_match->cp_str; - else + } else { ptr = NULL; + } ins_compl_fixRedoBufForLeader(ptr); } @@ -3765,16 +3828,18 @@ static bool ins_compl_prep(int c) /* * Indent now if a key was typed that is in 'cinkeys'. */ - if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) + if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) { do_c_expr_indent(); + } // Trigger the CompleteDone event to give scripts a chance to act // upon the end of completion. ins_apply_autocmds(EVENT_COMPLETEDONE); } - } else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) + } 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); + } /* reset continue_* if we left expansion-mode, if we stay they'll be * (re)set properly in ins_complete() */ @@ -3794,8 +3859,8 @@ static bool ins_compl_prep(int c) static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg) { int len; - char_u *p; - char_u *ptr = ptr_arg; + char_u *p; + char_u *ptr = ptr_arg; if (ptr == NULL) { if (compl_leader != NULL) { @@ -3822,7 +3887,7 @@ static void ins_compl_fixRedoBufForLeader(char_u *ptr_arg) /* * Loops through the list of windows, loaded-buffers or non-loaded-buffers * (depending on flag) starting from buf and looking for a non-scanned - * buffer (other than curbuf). curbuf is special, if it is called with + * buffer (other than curbuf). curbuf is special, if it is called with * buf=curbuf then it has to be the first call for a given flag/expansion. * * Returns the buffer to scan, if any, otherwise returns curbuf -- Acevedo @@ -3837,10 +3902,11 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag) } assert(wp); while ((wp = (wp->w_next != NULL ? wp->w_next : firstwin)) != curwin - && wp->w_buffer->b_scanned) + && wp->w_buffer->b_scanned) { ; + } buf = wp->w_buffer; - } else + } else { /* 'b' (just loaded buffers), 'u' (just non-loaded buffers) or 'U' * (unlisted buffers) * When completing whole lines skip unloaded buffers. */ @@ -3849,19 +3915,19 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag) ? buf->b_p_bl : (!buf->b_p_bl || (buf->b_ml.ml_mfp == NULL) != (flag == 'u'))) - || buf->b_scanned)) + || buf->b_scanned)) { ; + } + } return buf; } -// Execute user defined complete function 'completefunc' or 'omnifunc', and -// get matches in "matches". -static void -expand_by_function( - int type, // CTRL_X_OMNI or CTRL_X_FUNCTION - char_u *base -) +/// Execute user defined complete function 'completefunc' or 'omnifunc', and +/// get matches in "matches". +/// +/// @param type CTRL_X_OMNI or CTRL_X_FUNCTION +static void expand_by_function(int type, char_u *base) { list_T *matchlist = NULL; dict_T *matchdict = NULL; @@ -3919,10 +3985,11 @@ expand_by_function( goto theend; } - if (matchlist != NULL) + if (matchlist != NULL) { ins_compl_add_list(matchlist); - else if (matchdict != NULL) + } else if (matchdict != NULL) { ins_compl_add_dict(matchdict); + } theend: // Restore State, it might have been changed. @@ -3959,8 +4026,8 @@ static void ins_compl_add_list(list_T *const list) */ static void ins_compl_add_dict(dict_T *dict) { - dictitem_T *di_refresh; - dictitem_T *di_words; + dictitem_T *di_refresh; + dictitem_T *di_words; // Check for optional "refresh" item. compl_opt_refresh_always = false; @@ -4095,8 +4162,9 @@ static int ins_compl_get_exp(pos_T *ini) || CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) && (!compl_started || found_all)) { found_all = FALSE; - while (*e_cpt == ',' || *e_cpt == ' ') + while (*e_cpt == ',' || *e_cpt == ' ') { e_cpt++; + } if (*e_cpt == '.' && !curbuf->b_scanned) { ins_buf = curbuf; first_match_pos = *ini; @@ -4117,7 +4185,7 @@ static int ins_compl_get_exp(pos_T *ini) set_match_pos = true; } else if (vim_strchr((char_u *)"buwU", *e_cpt) != NULL && (ins_buf = - ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) { + ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) { // Scan a buffer, but not the current one. if (ins_buf->b_ml.ml_mfp != NULL) { // loaded buffer compl_started = true; @@ -4148,10 +4216,11 @@ static int ins_compl_get_exp(pos_T *ini) if (CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) { type = -1; } else if (*e_cpt == 'k' || *e_cpt == 's') { - if (*e_cpt == 'k') + if (*e_cpt == 'k') { type = CTRL_X_DICTIONARY; - else + } else { type = CTRL_X_THESAURUS; + } if (*++e_cpt != ',' && *e_cpt != NUL) { dict = e_cpt; dict_f = DICT_FIRST; @@ -4173,8 +4242,9 @@ static int ins_compl_get_exp(pos_T *ini) (void)copy_option_part(&e_cpt, IObuff, IOSIZE, ","); found_all = TRUE; - if (type == -1) + if (type == -1) { continue; + } } } @@ -4200,18 +4270,17 @@ static int ins_compl_get_exp(pos_T *ini) case CTRL_X_DICTIONARY: case CTRL_X_THESAURUS: - ins_compl_dictionaries( - dict != NULL ? dict - : (type == CTRL_X_THESAURUS + ins_compl_dictionaries(dict != NULL ? dict + : (type == CTRL_X_THESAURUS ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr) - : (*curbuf->b_p_dict == NUL + : (*curbuf->b_p_dict == NUL ? p_dict : curbuf->b_p_dict)), - compl_pattern, - dict != NULL ? dict_f - : 0, type == CTRL_X_THESAURUS); + compl_pattern, + dict != NULL ? dict_f + : 0, type == CTRL_X_THESAURUS); dict = NULL; break; @@ -4241,7 +4310,7 @@ static int ins_compl_get_exp(pos_T *ini) #ifdef BACKSLASH_IN_FILENAME if (curbuf->b_p_csl[0] != NUL) { for (int i = 0; i < num_matches; i++) { - char_u *ptr = matches[i]; + char_u *ptr = matches[i]; while (*ptr != NUL) { if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') { *ptr = '/'; @@ -4258,10 +4327,12 @@ static int ins_compl_get_exp(pos_T *ini) break; case CTRL_X_CMDLINE: + case CTRL_X_CMDLINE_CTRL_X: if (expand_cmdline(&compl_xp, compl_pattern, - (int)STRLEN(compl_pattern), - &num_matches, &matches) == EXPAND_OK) - ins_compl_add_matches(num_matches, matches, FALSE); + (int)STRLEN(compl_pattern), + &num_matches, &matches) == EXPAND_OK) { + ins_compl_add_matches(num_matches, matches, false); + } break; case CTRL_X_FUNCTION: @@ -4271,27 +4342,30 @@ static int ins_compl_get_exp(pos_T *ini) case CTRL_X_SPELL: num_matches = expand_spelling(first_match_pos.lnum, - compl_pattern, &matches); - if (num_matches > 0) + compl_pattern, &matches); + if (num_matches > 0) { ins_compl_add_matches(num_matches, matches, p_ic); + } break; default: // normal ^P/^N and ^X^L // If 'infercase' is set, don't use 'smartcase' here save_p_scs = p_scs; assert(ins_buf); - if (ins_buf->b_p_inf) + if (ins_buf->b_p_inf) { p_scs = FALSE; + } // Buffers other than curbuf are scanned from the beginning or the // end but never from the middle, thus setting nowrapscan in this // buffers is a good idea, on the other hand, we always set // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb save_p_ws = p_ws; - if (ins_buf != curbuf) + if (ins_buf != curbuf) { p_ws = false; - else if (*e_cpt == '.') + } else if (*e_cpt == '.') { p_ws = true; + } for (;; ) { bool cont_s_ipos = false; @@ -4322,8 +4396,9 @@ static int ins_compl_get_exp(pos_T *ini) found_new_match = FAIL; } if (found_new_match == FAIL) { - if (ins_buf == curbuf) + if (ins_buf == curbuf) { found_all = TRUE; + } break; } @@ -4398,13 +4473,13 @@ static int ins_compl_get_exp(pos_T *ini) IObuff[len] = NUL; ptr = IObuff; } - if (len == compl_length) + if (len == compl_length) { continue; + } } } - if (ins_compl_add_infercase( - ptr, len, p_ic, ins_buf == curbuf ? NULL : ins_buf->b_sfname, - 0, cont_s_ipos) != NOTDONE) { + if (ins_compl_add_infercase(ptr, len, p_ic, ins_buf == curbuf ? NULL : ins_buf->b_sfname, + 0, cont_s_ipos) != NOTDONE) { found_new_match = OK; break; } @@ -4424,8 +4499,9 @@ static int ins_compl_get_exp(pos_T *ini) if ((l_ctrl_x_mode != CTRL_X_NORMAL && !CTRL_X_MODE_LINE_OR_EVAL(l_ctrl_x_mode)) || found_new_match != FAIL) { - if (got_int) + if (got_int) { break; + } // Fill the popup menu as soon as possible. if (type != -1) { ins_compl_check_keys(0, false); @@ -4517,21 +4593,16 @@ 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"), - (const char *)EMPTY_IF_NULL(match->cp_str)); - tv_dict_add_str( - dict, S_LEN("abbr"), - (const char *)EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); - tv_dict_add_str( - dict, S_LEN("menu"), - (const char *)EMPTY_IF_NULL(match->cp_text[CPT_MENU])); - tv_dict_add_str( - dict, S_LEN("kind"), - (const char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND])); - tv_dict_add_str( - dict, S_LEN("info"), - (const char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); + tv_dict_add_str(dict, S_LEN("word"), + (const char *)EMPTY_IF_NULL(match->cp_str)); + tv_dict_add_str(dict, S_LEN("abbr"), + (const char *)EMPTY_IF_NULL(match->cp_text[CPT_ABBR])); + tv_dict_add_str(dict, S_LEN("menu"), + (const char *)EMPTY_IF_NULL(match->cp_text[CPT_MENU])); + tv_dict_add_str(dict, S_LEN("kind"), + (const char *)EMPTY_IF_NULL(match->cp_text[CPT_KIND])); + tv_dict_add_str(dict, S_LEN("info"), + (const char *)EMPTY_IF_NULL(match->cp_text[CPT_INFO])); if (match->cp_user_data.v_type == VAR_UNKNOWN) { tv_dict_add_str(dict, S_LEN("user_data"), ""); } else { @@ -4540,30 +4611,25 @@ static dict_T *ins_compl_dict_alloc(compl_T *match) return dict; } -/* - * Fill in the next completion in the current direction. - * If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to - * get more completions. If it is FALSE, then we just do nothing when there - * are no more completions in a given direction. The latter case is used when - * we are still in the middle of finding completions, to allow browsing - * through the ones found so far. - * Return the total number of matches, or -1 if still unknown -- webb. - * - * compl_curr_match is currently being used by ins_compl_get_exp(), so we use - * compl_shown_match here. - * - * Note that this function may be called recursively once only. First with - * "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn - * calls this function with "allow_get_expansion" FALSE. - */ -static int -ins_compl_next ( - int allow_get_expansion, - int count, // Repeat completion this many times; should - // be at least 1 - int insert_match, // Insert the newly selected match - int in_compl_func // Called from complete_check() -) +/// Fill in the next completion in the current direction. +/// If "allow_get_expansion" is TRUE, then we may call ins_compl_get_exp() to +/// get more completions. If it is FALSE, then we just do nothing when there +/// are no more completions in a given direction. The latter case is used when +/// we are still in the middle of finding completions, to allow browsing +/// through the ones found so far. +/// @return the total number of matches, or -1 if still unknown -- webb. +/// +/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use +/// compl_shown_match here. +/// +/// Note that this function may be called recursively once only. First with +/// "allow_get_expansion" TRUE, which calls ins_compl_get_exp(), which in turn +/// calls this function with "allow_get_expansion" FALSE. +/// +/// @param count Repeat completion this many times; should be at least 1 +/// @param insert_match Insert the newly selected match +/// @param in_compl_func Called from complete_check() +static int ins_compl_next(int allow_get_expansion, int count, int insert_match, int in_compl_func) { int num_matches = -1; int todo = count; @@ -4573,8 +4639,9 @@ ins_compl_next ( /* When user complete function return -1 for findstart which is next * time of 'always', compl_shown_match become NULL. */ - if (compl_shown_match == NULL) + if (compl_shown_match == NULL) { return -1; + } if (compl_leader != NULL && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) { @@ -4591,21 +4658,23 @@ ins_compl_next ( * backward, find the last match. */ if (compl_shows_dir == BACKWARD && !ins_compl_equal(compl_shown_match, - compl_leader, (int)STRLEN(compl_leader)) + compl_leader, (int)STRLEN(compl_leader)) && (compl_shown_match->cp_next == NULL || compl_shown_match->cp_next == compl_first_match)) { while (!ins_compl_equal(compl_shown_match, - compl_leader, (int)STRLEN(compl_leader)) + compl_leader, (int)STRLEN(compl_leader)) && compl_shown_match->cp_prev != NULL - && compl_shown_match->cp_prev != compl_first_match) + && compl_shown_match->cp_prev != compl_first_match) { compl_shown_match = compl_shown_match->cp_prev; + } } } if (allow_get_expansion && insert_match - && (!(compl_get_longest || compl_restarting) || compl_used_match)) + && (!(compl_get_longest || compl_restarting) || compl_used_match)) { // Delete old text to be replaced ins_compl_delete(); + } // When finding the longest common text we stick at the original text, // don't let CTRL-N or CTRL-P move to the first match. @@ -4633,19 +4702,21 @@ ins_compl_next ( } else { if (!allow_get_expansion) { if (advance) { - if (compl_shows_dir == BACKWARD) + if (compl_shows_dir == BACKWARD) { compl_pending -= todo + 1; - else + } else { compl_pending += todo + 1; + } } return -1; } if (!compl_no_select && advance) { - if (compl_shows_dir == BACKWARD) + if (compl_shows_dir == BACKWARD) { --compl_pending; - else + } else { ++compl_pending; + } } // Find matches. @@ -4661,8 +4732,9 @@ ins_compl_next ( if (compl_pending < 0 && compl_shown_match->cp_prev != NULL) { compl_shown_match = compl_shown_match->cp_prev; ++compl_pending; - } else + } else { break; + } } found_end = false; } @@ -4727,8 +4799,8 @@ ins_compl_next ( if (compl_shown_match->cp_fname != NULL) { char *lead = _("match in file"); int space = sc_col - vim_strsize((char_u *)lead) - 2; - char_u *s; - char_u *e; + char_u *s; + char_u *e; if (space > 0) { // We need the tail that fits. With double-byte encoding going @@ -4796,7 +4868,7 @@ void ins_compl_check_keys(int frequency, int in_compl_func) c = safe_vgetc(); // Eat the character compl_shows_dir = ins_compl_key2dir(c); (void)ins_compl_next(false, ins_compl_key2count(c), - c != K_UP && c != K_DOWN, in_compl_func); + c != K_UP && c != K_DOWN, in_compl_func); } else { /* Need to get the character to have KeyTyped set. We'll put it * back with vungetc() below. But skip K_IGNORE. */ @@ -4804,8 +4876,9 @@ void ins_compl_check_keys(int frequency, int in_compl_func) if (c != K_IGNORE) { /* Don't interrupt completion when the character wasn't typed, * e.g., when doing @q to replay keys. */ - if (c != Ctrl_R && KeyTyped) + if (c != Ctrl_R && KeyTyped) { compl_interrupted = TRUE; + } vungetc(c); } @@ -4901,7 +4974,7 @@ static bool ins_compl_use_match(int c) */ static int ins_complete(int c, bool enable_pum) { - char_u *line; + char_u *line; int startcol = 0; // column where searched text starts colnr_T curs_col; // cursor column int n; @@ -4960,9 +5033,8 @@ static int ins_complete(int c, bool enable_pum) * mode but first we need to redefine compl_startpos */ if (compl_cont_status & CONT_S_IPOS) { compl_cont_status |= CONT_SOL; - compl_startpos.col = (colnr_T)(skipwhite( - line + compl_length - + compl_startpos.col) - line); + compl_startpos.col = (colnr_T)(skipwhite(line + compl_length + + compl_startpos.col) - line); } compl_col = compl_startpos.col; } @@ -4976,14 +5048,17 @@ static int ins_complete(int c, bool enable_pum) compl_col = curwin->w_cursor.col - compl_length; } compl_cont_status |= CONT_ADDING | CONT_N_ADDS; - if (compl_length < 1) + if (compl_length < 1) { compl_cont_status &= CONT_LOCAL; + } } else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { compl_cont_status = CONT_ADDING | CONT_N_ADDS; - } else + } else { compl_cont_status = 0; - } else + } + } else { compl_cont_status &= CONT_LOCAL; + } if (!(compl_cont_status & CONT_ADDING)) { // normal expansion compl_cont_mode = ctrl_x_mode; @@ -5002,27 +5077,30 @@ static int ins_complete(int c, bool enable_pum) if ((compl_cont_status & CONT_SOL) || ctrl_x_mode == CTRL_X_PATH_DEFINES) { if (!(compl_cont_status & CONT_ADDING)) { - while (--startcol >= 0 && vim_isIDc(line[startcol])) + while (--startcol >= 0 && vim_isIDc(line[startcol])) { ; + } compl_col += ++startcol; compl_length = curs_col - startcol; } - if (p_ic) + if (p_ic) { compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0); - else + } else { compl_pattern = vim_strnsave(line + compl_col, compl_length); + } } else if (compl_cont_status & CONT_ADDING) { - char_u *prefix = (char_u *)"\\<"; + char_u *prefix = (char_u *)"\\<"; // we need up to 2 extra chars for the prefix compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, - compl_length) + 2); + compl_length) + 2); if (!vim_iswordp(line + compl_col) || (compl_col > 0 && ( - vim_iswordp(mb_prevptr(line, line + compl_col)) - ))) + vim_iswordp(mb_prevptr(line, line + compl_col)) + ))) { prefix = (char_u *)""; + } STRCPY((char *)compl_pattern, prefix); (void)quote_meta(compl_pattern + STRLEN(prefix), line + compl_col, compl_length); @@ -5057,10 +5135,10 @@ static int ins_complete(int c, bool enable_pum) STRCAT((char *)compl_pattern, "\\k"); } else { compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, - compl_length) + 2); + compl_length) + 2); STRCPY((char *)compl_pattern, "\\<"); (void)quote_meta(compl_pattern + 2, line + compl_col, - compl_length); + compl_length); } } } else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode)) { @@ -5077,7 +5155,7 @@ static int ins_complete(int c, bool enable_pum) } else if (ctrl_x_mode == CTRL_X_FILES) { // Go back to just before the first filename character. if (startcol > 0) { - char_u *p = line + startcol; + char_u *p = line + startcol; MB_PTR_BACK(line, p); while (p > line && vim_isfilec(PTR2CHAR(p))) { @@ -5093,7 +5171,7 @@ static int ins_complete(int c, bool enable_pum) compl_col += startcol; compl_length = (int)curs_col - startcol; compl_pattern = addstar(line + compl_col, compl_length, EXPAND_FILES); - } else if (ctrl_x_mode == CTRL_X_CMDLINE) { + } else if (ctrl_x_mode == CTRL_X_CMDLINE || ctrl_x_mode == CTRL_X_CMDLINE_CTRL_X) { compl_pattern = vim_strnsave(line, curs_col); set_cmd_context(&compl_xp, compl_pattern, (int)STRLEN(compl_pattern), curs_col, false); @@ -5112,10 +5190,10 @@ static int ins_complete(int c, bool enable_pum) * Call user defined function 'completefunc' with "a:findstart" * set to 1 to obtain the length of text to use for completion. */ - char_u *funcname; + char_u *funcname; pos_T pos; - win_T *curwin_save; - buf_T *curbuf_save; + win_T *curwin_save; + buf_T *curbuf_save; const int save_State = State; /* Call 'completefunc' or 'omnifunc' and get pattern length as a @@ -5157,8 +5235,9 @@ static int ins_complete(int c, bool enable_pum) /* Return value -2 means the user complete function wants to * cancel the complete without an error. * Return value -3 does the same as -2 and leaves CTRL-X mode.*/ - if (col == -2) + if (col == -2) { return FAIL; + } if (col == -3) { ctrl_x_mode = CTRL_X_NORMAL; edit_submode = NULL; @@ -5174,11 +5253,13 @@ static int ins_complete(int c, bool enable_pum) */ compl_opt_refresh_always = FALSE; - if (col < 0) + if (col < 0) { col = curs_col; + } compl_col = col; - if (compl_col > curs_col) + if (compl_col > curs_col) { compl_col = curs_col; + } /* Setup variables for completion. Need to obtain "line" again, * it may have become invalid. */ @@ -5189,9 +5270,9 @@ static int ins_complete(int c, bool enable_pum) if (spell_bad_len > 0) { assert(spell_bad_len <= INT_MAX); compl_col = curs_col - (int)spell_bad_len; - } - else + } else { compl_col = spell_word_start(startcol); + } if (compl_col >= (colnr_T)startcol) { compl_length = 0; compl_col = curs_col; @@ -5226,10 +5307,11 @@ static int ins_complete(int c, bool enable_pum) compl_startpos.col = compl_col; } - if (compl_cont_status & CONT_LOCAL) + if (compl_cont_status & CONT_LOCAL) { edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]); - else + } else { edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode)); + } /* If any of the original typed text has been changed we need to fix * the redo buffer. */ @@ -5334,14 +5416,15 @@ static int ins_complete(int c, bool enable_pum) * Translations may need more than twice that. */ static char_u match_ref[81]; - if (compl_matches > 0) + if (compl_matches > 0) { vim_snprintf((char *)match_ref, sizeof(match_ref), - _("match %d of %d"), - compl_curr_match->cp_number, compl_matches); - else + _("match %d of %d"), + compl_curr_match->cp_number, compl_matches); + } else { vim_snprintf((char *)match_ref, sizeof(match_ref), - _("match %d"), - compl_curr_match->cp_number); + _("match %d"), + compl_curr_match->cp_number); + } edit_submode_extra = match_ref; edit_submode_highl = HLF_R; if (dollar_vcol >= 0) { @@ -5393,8 +5476,9 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len) case '*': case '[': if (ctrl_x_mode == CTRL_X_DICTIONARY - || ctrl_x_mode == CTRL_X_THESAURUS) + || ctrl_x_mode == CTRL_X_THESAURUS) { break; + } FALLTHROUGH; case '~': if (!p_magic) { // quote these only if magic is set @@ -5403,8 +5487,9 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len) FALLTHROUGH; case '\\': if (ctrl_x_mode == CTRL_X_DICTIONARY - || ctrl_x_mode == CTRL_X_THESAURUS) + || ctrl_x_mode == CTRL_X_THESAURUS) { break; + } FALLTHROUGH; case '^': // currently it's not needed. case '$': @@ -5429,8 +5514,9 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len) } } } - if (dest != NULL) + if (dest != NULL) { *dest = NUL; + } return m; } @@ -5450,8 +5536,9 @@ int get_literal(void) int octal = FALSE; int unicode = 0; - if (got_int) + if (got_int) { return Ctrl_C; + } no_mapping++; // don't map the next key hits cc = 0; @@ -5459,29 +5546,31 @@ int get_literal(void) for (;; ) { nc = plain_vgetc(); if (!(State & CMDLINE) - && MB_BYTE2LEN_CHECK(nc) == 1 - ) + && MB_BYTE2LEN_CHECK(nc) == 1) { add_to_showcmd(nc); - if (nc == 'x' || nc == 'X') + } + if (nc == 'x' || nc == 'X') { hex = TRUE; - else if (nc == 'o' || nc == 'O') + } else if (nc == 'o' || nc == 'O') { octal = TRUE; - else if (nc == 'u' || nc == 'U') + } else if (nc == 'u' || nc == 'U') { unicode = nc; - else { + } else { if (hex - || unicode != 0 - ) { - if (!ascii_isxdigit(nc)) + || unicode != 0) { + if (!ascii_isxdigit(nc)) { break; + } cc = cc * 16 + hex2nr(nc); } else if (octal) { - if (nc < '0' || nc > '7') + if (nc < '0' || nc > '7') { break; + } cc = cc * 8 + nc - '0'; } else { - if (!ascii_isdigit(nc)) + if (!ascii_isdigit(nc)) { break; + } cc = cc * 10 + nc - '0'; } @@ -5489,9 +5578,9 @@ int get_literal(void) } if (cc > 255 - && unicode == 0 - ) + && unicode == 0) { cc = 255; // limit range to 0-255 + } nc = 0; if (hex) { // hex: up to two chars @@ -5521,8 +5610,9 @@ int get_literal(void) } --no_mapping; - if (nc) + if (nc) { vungetc(nc); + } got_int = false; // CTRL-C typed after CTRL-V is not an interrupt return cc; } @@ -5532,7 +5622,7 @@ int get_literal(void) /// @param ctrlv `c` was typed after CTRL-V static void insert_special(int c, int allow_modmask, int ctrlv) { - char_u *p; + char_u *p; int len; // Special function key, translate into "<Key>". Up to the last '>' is @@ -5548,16 +5638,18 @@ static void insert_special(int c, int allow_modmask, int ctrlv) len = (int)STRLEN(p); c = p[len - 1]; if (len > 2) { - if (stop_arrow() == FAIL) + if (stop_arrow() == FAIL) { return; + } p[len - 1] = NUL; ins_str(p); AppendToRedobuffLit(p, -1); ctrlv = FALSE; } } - if (stop_arrow() == OK) + if (stop_arrow() == OK) { insertchar(c, ctrlv ? INSCHAR_CTRLV : 0, -1); + } } /* @@ -5572,26 +5664,25 @@ static void insert_special(int c, int allow_modmask, int ctrlv) # define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^') #define WHITECHAR(cc) ( \ - ascii_iswhite(cc) \ - && !utf_iscomposing(utf_ptr2char(get_cursor_pos_ptr() + 1))) + ascii_iswhite(cc) \ + && !utf_iscomposing(utf_ptr2char(get_cursor_pos_ptr() + 1))) -/* - * "flags": INSCHAR_FORMAT - force formatting - * INSCHAR_CTRLV - char typed just after CTRL-V - * INSCHAR_NO_FEX - don't use 'formatexpr' - * - * NOTE: passes the flags value straight through to internal_format() which, - * beside INSCHAR_FORMAT (above), is also looking for these: - * INSCHAR_DO_COM - format comments - * INSCHAR_COM_LIST - format comments with num list or 2nd line indent - */ -void insertchar( - int c, // character to insert or NUL - int flags, // INSCHAR_FORMAT, etc. - int second_indent // indent for second line if >= 0 -) +/// +/// "flags": INSCHAR_FORMAT - force formatting +/// INSCHAR_CTRLV - char typed just after CTRL-V +/// INSCHAR_NO_FEX - don't use 'formatexpr' +/// +/// NOTE: passes the flags value straight through to internal_format() which, +/// beside INSCHAR_FORMAT (above), is also looking for these: +/// INSCHAR_DO_COM - format comments +/// INSCHAR_COM_LIST - format comments with num list or 2nd line indent +/// +/// @param c character to insert or NUL +/// @param flags INSCHAR_FORMAT, etc. +/// @param second_indent indent for second line if >= 0 +void insertchar(int c, int flags, int second_indent) { - char_u *p; + char_u *p; int force_format = flags & INSCHAR_FORMAT; const int textwidth = comp_textwidth(force_format); @@ -5603,14 +5694,14 @@ void insertchar( * - Always do this when 'formatoptions' has the 'a' flag and the line * ends in white space. * - Otherwise: - * - Don't do this if inserting a blank - * - Don't do this if an existing character is being replaced, unless - * we're in VREPLACE mode. - * - Do this if the cursor is not on the line where insert started - * or - 'formatoptions' doesn't have 'l' or the line was not too long - * before the insert. - * - 'formatoptions' doesn't have 'b' or a blank was inserted at or - * before 'textwidth' + * - Don't do this if inserting a blank + * - Don't do this if an existing character is being replaced, unless + * we're in VREPLACE mode. + * - Do this if the cursor is not on the line where insert started + * or - 'formatoptions' doesn't have 'l' or the line was not too long + * before the insert. + * - 'formatoptions' doesn't have 'b' or a blank was inserted at or + * before 'textwidth' */ if (textwidth > 0 && (force_format @@ -5627,7 +5718,7 @@ void insertchar( // when 'formatexpr' isn't set or it returns non-zero. bool do_internal = true; colnr_T virtcol = get_nolist_virtcol() - + char2cells(c != NUL ? c : gchar_cursor()); + + char2cells(c != NUL ? c : gchar_cursor()); if (*curbuf->b_p_fex != NUL && (flags & INSCHAR_NO_FEX) == 0 && (force_format || virtcol > (colnr_T)textwidth)) { @@ -5636,8 +5727,9 @@ void insertchar( // was called. ins_need_undo = true; } - if (do_internal) + if (do_internal) { internal_format(textwidth, second_indent, flags, c == NUL, c); + } } if (c == NUL) { // only formatting was wanted @@ -5646,7 +5738,7 @@ void insertchar( // Check whether this character should end a comment. if (did_ai && c == end_comment_pending) { - char_u *line; + char_u *line; char_u lead_end[COM_MAX_LEN]; // end-comment string int middle_len, end_len; int i; @@ -5675,8 +5767,9 @@ void insertchar( // Skip white space before the cursor i = curwin->w_cursor.col; - while (--i >= 0 && ascii_iswhite(line[i])) + while (--i >= 0 && ascii_iswhite(line[i])) { ; + } i++; // Skip to before the middle leader @@ -5753,10 +5846,12 @@ void insertchar( if (flags & INSCHAR_CTRLV) { redo_literal(*buf); i = 1; - } else + } else { i = 0; - if (buf[i] != NUL) + } + if (buf[i] != NUL) { AppendToRedobuffLit(buf + i, -1); + } } else { int cc; @@ -5778,20 +5873,13 @@ void insertchar( } } -/* - * Format text at the current insert position. - * - * If the INSCHAR_COM_LIST flag is present, then the value of second_indent - * will be the comment leader length sent to open_line(). - */ -static void -internal_format ( - int textwidth, - int second_indent, - int flags, - int format_only, - int c // character to be inserted (can be NUL) -) +/// Format text at the current insert position. +/// +/// If the INSCHAR_COM_LIST flag is present, then the value of second_indent +/// will be the comment leader length sent to open_line(). +/// +/// @param c character to be inserted (can be NUL) +static void internal_format(int textwidth, int second_indent, int flags, int format_only, int c) { int cc; int save_char = NUL; @@ -5814,8 +5902,7 @@ internal_format ( * deleted. Replace it with an 'x' temporarily. */ if (!curbuf->b_p_ai - && !(State & VREPLACE_FLAG) - ) { + && !(State & VREPLACE_FLAG)) { cc = gchar_cursor(); if (ascii_iswhite(cc)) { save_char = cc; @@ -5834,14 +5921,15 @@ internal_format ( colnr_T len; colnr_T virtcol; int orig_col = 0; - char_u *saved_text = NULL; + char_u *saved_text = NULL; colnr_T col; colnr_T end_col; virtcol = get_nolist_virtcol() + char2cells(c != NUL ? c : gchar_cursor()); - if (virtcol <= (colnr_T)textwidth) + if (virtcol <= (colnr_T)textwidth) { break; + } if (no_leader) { do_comments = false; @@ -5889,10 +5977,11 @@ internal_format ( || (flags & INSCHAR_FORMAT) || curwin->w_cursor.lnum != Insstart.lnum || curwin->w_cursor.col >= Insstart.col) { - if (curwin->w_cursor.col == startcol && c != NUL) + if (curwin->w_cursor.col == startcol && c != NUL) { cc = c; - else + } else { cc = gchar_cursor(); + } if (WHITECHAR(cc)) { // remember position of blank just before text end_col = curwin->w_cursor.col; @@ -5947,8 +6036,9 @@ internal_format ( end_foundcol = end_col + 1; foundcol = curwin->w_cursor.col; - if (curwin->w_cursor.col <= (colnr_T)wantcol) + if (curwin->w_cursor.col <= (colnr_T)wantcol) { break; + } } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) && fo_multibyte) { int ncc; @@ -5969,14 +6059,16 @@ internal_format ( if (curwin->w_cursor.col != skip_pos && allow_break) { foundcol = curwin->w_cursor.col; end_foundcol = foundcol; - if (curwin->w_cursor.col <= (colnr_T)wantcol) + if (curwin->w_cursor.col <= (colnr_T)wantcol) { break; + } } curwin->w_cursor.col = col; } - if (curwin->w_cursor.col == 0) + if (curwin->w_cursor.col == 0) { break; + } ncc = cc; col = curwin->w_cursor.col; @@ -6038,8 +6130,9 @@ internal_format ( } } } - if (curwin->w_cursor.col == 0) + if (curwin->w_cursor.col == 0) { break; + } dec_cursor(); } @@ -6068,11 +6161,13 @@ internal_format ( */ curwin->w_cursor.col = foundcol; while ((cc = gchar_cursor(), WHITECHAR(cc)) - && (!fo_white_par || curwin->w_cursor.col < startcol)) + && (!fo_white_par || curwin->w_cursor.col < startcol)) { inc_cursor(); + } startcol -= curwin->w_cursor.col; - if (startcol < 0) + if (startcol < 0) { startcol = 0; + } if (State & VREPLACE_FLAG) { /* @@ -6099,12 +6194,13 @@ internal_format ( * Only insert/delete lines, but don't really redraw the window. */ open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX - + (fo_white_par ? OPENLINE_KEEPTRAIL : 0) - + (do_comments ? OPENLINE_DO_COM : 0) - + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0) - , ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent)); - if (!(flags & INSCHAR_COM_LIST)) + + (fo_white_par ? OPENLINE_KEEPTRAIL : 0) + + (do_comments ? OPENLINE_DO_COM : 0) + + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0) + , ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent)); + if (!(flags & INSCHAR_COM_LIST)) { old_indent = 0; + } replace_offset = 0; if (first_line) { @@ -6155,8 +6251,9 @@ internal_format ( */ curwin->w_cursor.col += startcol; len = (colnr_T)STRLEN(get_cursor_line_ptr()); - if (curwin->w_cursor.col > len) + if (curwin->w_cursor.col > len) { curwin->w_cursor.col = len; + } } haveto_redraw = true; @@ -6181,27 +6278,26 @@ internal_format ( } } -/* - * Called after inserting or deleting text: When 'formatoptions' includes the - * 'a' flag format from the current line until the end of the paragraph. - * Keep the cursor at the same position relative to the text. - * The caller must have saved the cursor line for undo, following ones will be - * saved here. - */ -void auto_format( - bool trailblank, // when true also format with trailing blank - bool prev_line // may start in previous line -) +/// Called after inserting or deleting text: When 'formatoptions' includes the +/// 'a' flag format from the current line until the end of the paragraph. +/// Keep the cursor at the same position relative to the text. +/// The caller must have saved the cursor line for undo, following ones will be +/// saved here. +/// +/// @param trailblank when true also format with trailing blank +/// @param prev_line may start in previous line +void auto_format(bool trailblank, bool prev_line) { pos_T pos; colnr_T len; - char_u *old; - char_u *new, *pnew; + char_u *old; + char_u *new, *pnew; int wasatend; int cc; - if (!has_format_option(FO_AUTO)) + if (!has_format_option(FO_AUTO)) { return; + } pos = curwin->w_cursor; old = get_cursor_line_ptr(); @@ -6219,8 +6315,9 @@ void auto_format( dec_cursor(); cc = gchar_cursor(); if (!WHITECHAR(cc) && curwin->w_cursor.col > 0 - && has_format_option(FO_ONE_LETTER)) + && has_format_option(FO_ONE_LETTER)) { dec_cursor(); + } cc = gchar_cursor(); if (WHITECHAR(cc)) { curwin->w_cursor = pos; @@ -6232,8 +6329,9 @@ void auto_format( /* With the 'c' flag in 'formatoptions' and 't' missing: only format * comments. */ if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP) - && get_leader_len(old, NULL, FALSE, TRUE) == 0) + && get_leader_len(old, NULL, FALSE, TRUE) == 0) { return; + } /* * May start formatting in a previous line, so that after "x" a word is @@ -6242,8 +6340,9 @@ void auto_format( */ if (prev_line && !paragraph_start(curwin->w_cursor.lnum)) { --curwin->w_cursor.lnum; - if (u_save_cursor() == FAIL) + if (u_save_cursor() == FAIL) { return; + } } /* @@ -6286,14 +6385,12 @@ void auto_format( check_cursor(); } -/* - * When an extra space was added to continue a paragraph for auto-formatting, - * delete it now. The space must be under the cursor, just after the insert - * position. - */ -static void check_auto_format( - bool end_insert // true when ending Insert mode -) +/// When an extra space was added to continue a paragraph for auto-formatting, +/// delete it now. The space must be under the cursor, just after the insert +/// position. +/// +/// @param end_insert true when ending Insert mode +static void check_auto_format(bool end_insert) { int c = ' '; int cc; @@ -6318,16 +6415,14 @@ static void check_auto_format( } } -/* - * Find out textwidth to be used for formatting: - * if 'textwidth' option is set, use it - * else if 'wrapmargin' option is set, use curwin->w_width_inner-'wrapmargin' - * if invalid value, use 0. - * Set default to window width (maximum 79) for "gq" operator. - */ -int comp_textwidth( - bool ff // force formatting (for "gq" command) -) +/// Find out textwidth to be used for formatting: +/// if 'textwidth' option is set, use it +/// else if 'wrapmargin' option is set, use curwin->w_width_inner-'wrapmargin' +/// if invalid value, use 0. +/// Set default to window width (maximum 79) for "gq" operator. +/// +/// @param ff force formatting (for "gq" command) +int comp_textwidth(bool ff) { int textwidth = curbuf->b_p_tw; if (textwidth == 0 && curbuf->b_p_wm) { @@ -6340,11 +6435,13 @@ int comp_textwidth( textwidth -= win_fdccol_count(curwin); textwidth -= win_signcol_count(curwin); - if (curwin->w_p_nu || curwin->w_p_rnu) + if (curwin->w_p_nu || curwin->w_p_rnu) { textwidth -= 8; + } } - if (textwidth < 0) + if (textwidth < 0) { textwidth = 0; + } if (ff && textwidth == 0) { textwidth = curwin->w_width_inner - 1; if (textwidth > 79) { @@ -6371,11 +6468,11 @@ static void redo_literal(int c) } } -// start_arrow() is called when an arrow key is used in insert mode. -// For undo/redo it resembles hitting the <ESC> key. -static void start_arrow( - pos_T *end_insert_pos // can be NULL -) +/// start_arrow() is called when an arrow key is used in insert mode. +/// For undo/redo it resembles hitting the <ESC> key. +/// +/// @param end_insert_pos can be NULL +static void start_arrow(pos_T *end_insert_pos) { start_arrow_common(end_insert_pos, true); } @@ -6427,8 +6524,9 @@ static void spell_back_to_badword(void) { pos_T tpos = curwin->w_cursor; spell_bad_len = spell_move_to(curwin, BACKWARD, TRUE, TRUE, NULL); - if (curwin->w_cursor.col != tpos.col) + if (curwin->w_cursor.col != tpos.col) { start_arrow(&tpos); + } } /* @@ -6471,20 +6569,16 @@ int stop_arrow(void) return arrow_used || ins_need_undo ? FAIL : OK; } -/* - * Do a few things to stop inserting. - * "end_insert_pos" is where insert ended. It is NULL when we already jumped - * to another window/buffer. - */ -static void -stop_insert ( - pos_T *end_insert_pos, - int esc, // called by ins_esc() - int nomove // <c-\><c-o>, don't move cursor -) +/// Do a few things to stop inserting. +/// "end_insert_pos" is where insert ended. It is NULL when we already jumped +/// to another window/buffer. +/// +/// @param esc called by ins_esc() +/// @param nomove <c-\><c-o>, don't move cursor +static void stop_insert(pos_T *end_insert_pos, int esc, int nomove) { int cc; - char_u *ptr; + char_u *ptr; stop_redo_ins(); replace_flush(); // abandon replace stack @@ -6500,8 +6594,9 @@ stop_insert ( xfree(last_insert); last_insert = ptr; last_insert_skip = new_insert_skip; - } else + } else { xfree(ptr); + } if (!arrow_used && end_insert_pos != NULL) { // Auto-format now. It may seem strange to do this when stopping an @@ -6518,21 +6613,24 @@ stop_insert ( if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL) { dec_cursor(); cc = gchar_cursor(); - if (!ascii_iswhite(cc)) + if (!ascii_iswhite(cc)) { curwin->w_cursor = tpos; + } } auto_format(true, false); if (ascii_iswhite(cc)) { - if (gchar_cursor() != NUL) + if (gchar_cursor() != NUL) { inc_cursor(); + } /* If the cursor is still at the same character, also keep * the "coladd". */ if (gchar_cursor() == NUL && curwin->w_cursor.lnum == tpos.lnum - && curwin->w_cursor.col == tpos.col) + && curwin->w_cursor.col == tpos.col) { curwin->w_cursor.coladd = tpos.coladd; + } } } @@ -6553,8 +6651,9 @@ stop_insert ( curwin->w_cursor = *end_insert_pos; check_cursor_col(); // make sure it is not past the line for (;; ) { - if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) + if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) { --curwin->w_cursor.col; + } cc = gchar_cursor(); if (!ascii_iswhite(cc)) { break; @@ -6606,7 +6705,7 @@ stop_insert ( */ void set_last_insert(int c) { - char_u *s; + char_u *s; xfree(last_insert); last_insert = xmalloc(MB_MAXBYTES * 3 + 5); @@ -6660,25 +6759,26 @@ char_u *add_char2buf(int c, char_u *s) /* * move cursor to start of line - * if flags & BL_WHITE move to first non-white - * if flags & BL_SOL move to first non-white if startofline is set, - * otherwise keep "curswant" column - * if flags & BL_FIX don't leave the cursor on a NUL. + * if flags & BL_WHITE move to first non-white + * if flags & BL_SOL move to first non-white if startofline is set, + * otherwise keep "curswant" column + * if flags & BL_FIX don't leave the cursor on a NUL. */ void beginline(int flags) { - if ((flags & BL_SOL) && !p_sol) + if ((flags & BL_SOL) && !p_sol) { coladvance(curwin->w_curswant); - else { + } else { curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; if (flags & (BL_WHITE | BL_SOL)) { - char_u *ptr; + char_u *ptr; for (ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr) - && !((flags & BL_FIX) && ptr[1] == NUL); ++ptr) + && !((flags & BL_FIX) && ptr[1] == NUL); ++ptr) { ++curwin->w_cursor.col; + } } curwin->w_set_curswant = TRUE; } @@ -6694,7 +6794,7 @@ void beginline(int flags) int oneright(void) { - char_u *ptr; + char_u *ptr; int l; if (virtual_active()) { @@ -6735,8 +6835,9 @@ int oneleft(void) int width; int v = getviscol(); - if (v == 0) + if (v == 0) { return FAIL; + } // We might get stuck on 'showbreak', skip over it. width = 1; @@ -6766,8 +6867,9 @@ int oneleft(void) return OK; } - if (curwin->w_cursor.col == 0) + if (curwin->w_cursor.col == 0) { return FAIL; + } curwin->w_set_curswant = TRUE; --curwin->w_cursor.col; @@ -6778,11 +6880,8 @@ int oneleft(void) return OK; } -int -cursor_up ( - long n, - int upd_topline // When TRUE: update topline -) +/// @oaram upd_topline When TRUE: update topline +int cursor_up(long n, int upd_topline) { linenr_T lnum; @@ -6793,9 +6892,9 @@ cursor_up ( if (lnum <= 1) { return FAIL; } - if (n >= lnum) + if (n >= lnum) { lnum = 1; - else if (hasAnyFolding(curwin)) { + } else if (hasAnyFolding(curwin)) { /* * Count each sequence of folded lines as one logical line. */ @@ -6815,10 +6914,12 @@ cursor_up ( (void)hasFolding(lnum, &lnum, NULL); } } - if (lnum < 1) + if (lnum < 1) { lnum = 1; - } else + } + } else { lnum -= n; + } curwin->w_cursor.lnum = lnum; } @@ -6832,14 +6933,10 @@ cursor_up ( return OK; } -/* - * Cursor down a number of logical lines. - */ -int -cursor_down ( - long n, - int upd_topline // When TRUE: update topline -) +/// Cursor down a number of logical lines. +/// +/// @param upd_topline When TRUE: update topline +int cursor_down(long n, int upd_topline) { linenr_T lnum; @@ -6852,24 +6949,28 @@ cursor_down ( if (lnum >= curbuf->b_ml.ml_line_count) { return FAIL; } - if (lnum + n >= curbuf->b_ml.ml_line_count) + if (lnum + n >= curbuf->b_ml.ml_line_count) { lnum = curbuf->b_ml.ml_line_count; - else if (hasAnyFolding(curwin)) { + } else if (hasAnyFolding(curwin)) { linenr_T last; // count each sequence of folded lines as one logical line while (n--) { - if (hasFolding(lnum, NULL, &last)) + if (hasFolding(lnum, NULL, &last)) { lnum = last + 1; - else + } else { ++lnum; - if (lnum >= curbuf->b_ml.ml_line_count) + } + if (lnum >= curbuf->b_ml.ml_line_count) { break; + } } - if (lnum > curbuf->b_ml.ml_line_count) + if (lnum > curbuf->b_ml.ml_line_count) { lnum = curbuf->b_ml.ml_line_count; - } else + } + } else { lnum += n; + } curwin->w_cursor.lnum = lnum; } @@ -6883,20 +6984,18 @@ cursor_down ( return OK; } -/* - * Stuff the last inserted text in the read buffer. - * Last_insert actually is a copy of the redo buffer, so we - * first have to remove the command. - */ -int stuff_inserted( - int c, // Command character to be inserted - long count, // Repeat this many times - int no_esc // Don't add an ESC at the end -) -{ - char_u *esc_ptr; - char_u *ptr; - char_u *last_ptr; +/// Stuff the last inserted text in the read buffer. +/// Last_insert actually is a copy of the redo buffer, so we +/// first have to remove the command. +/// +/// @param c Command character to be inserted +/// @param count Repeat this many times +/// @param no_esc Don't add an ESC at the end +int stuff_inserted(int c, long count, int no_esc) +{ + char_u *esc_ptr; + char_u *ptr; + char_u *last_ptr; char_u last = NUL; ptr = get_last_insert(); @@ -6934,8 +7033,9 @@ int stuff_inserted( } } while (--count > 0); - if (last) + if (last) { *last_ptr = last; + } if (esc_ptr != NULL) { *esc_ptr = ESC; // put the ESC back @@ -6951,8 +7051,9 @@ int stuff_inserted( char_u *get_last_insert(void) { - if (last_insert == NULL) + if (last_insert == NULL) { return NULL; + } return last_insert + last_insert_skip; } @@ -6962,11 +7063,12 @@ char_u *get_last_insert(void) */ char_u *get_last_insert_save(void) { - char_u *s; + char_u *s; int len; - if (last_insert == NULL) + if (last_insert == NULL) { return NULL; + } s = vim_strsave(last_insert + last_insert_skip); len = (int)STRLEN(s); if (len > 0 && s[len - 1] == ESC) { // remove trailing ESC @@ -6994,7 +7096,7 @@ static bool echeck_abbr(int c) } return check_abbr(c, get_cursor_line_ptr(), curwin->w_cursor.col, - curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0); + curwin->w_cursor.lnum == Insstart.lnum ? Insstart.col : 0); } /* @@ -7012,7 +7114,7 @@ static bool echeck_abbr(int c) * that were deleted (always white space). */ -static char_u *replace_stack = NULL; +static char_u *replace_stack = NULL; static ssize_t replace_stack_nr = 0; // next entry in replace stack static ssize_t replace_stack_len = 0; // max. number of entries @@ -7051,8 +7153,9 @@ int replace_push_mb(char_u *p) int l = (*mb_ptr2len)(p); int j; - for (j = l - 1; j >= 0; --j) + for (j = l - 1; j >= 0; --j) { replace_push(p[j]); + } return l; } @@ -7064,23 +7167,22 @@ static int replace_pop(void) return (replace_stack_nr == 0) ? -1 : (int)replace_stack[--replace_stack_nr]; } -/* - * Join the top two items on the replace stack. This removes to "off"'th NUL - * encountered. - */ -static void replace_join( - int off // offset for which NUL to remove -) +/// Join the top two items on the replace stack. This removes to "off"'th NUL +/// encountered. +/// +/// @param off offset for which NUL to remove +static void replace_join(int off) { int i; - for (i = replace_stack_nr; --i >= 0; ) + for (i = replace_stack_nr; --i >= 0; ) { if (replace_stack[i] == NUL && off-- <= 0) { --replace_stack_nr; memmove(replace_stack + i, replace_stack + i + 1, - (size_t)(replace_stack_nr - i)); + (size_t)(replace_stack_nr - i)); return; } + } } /* @@ -7113,8 +7215,9 @@ static void mb_replace_pop_ins(int cc) if ((n = MB_BYTE2LEN(cc)) > 1) { buf[0] = cc; - for (i = 1; i < n; ++i) + for (i = 1; i < n; ++i) { buf[i] = replace_pop(); + } ins_bytes_len(buf, n); } else { ins_char(cc); @@ -7176,7 +7279,7 @@ static void replace_do_bs(int limit_col) int ins_len; int orig_vcols = 0; colnr_T start_vcol; - char_u *p; + char_u *p; int i; int vcol; const int l_State = State; @@ -7187,7 +7290,7 @@ static void replace_do_bs(int limit_col) // Get the number of screen cells used by the character we are // going to delete. getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL); - orig_vcols = chartabsize(get_cursor_pos_ptr(), start_vcol); + orig_vcols = win_chartabsize(curwin, get_cursor_pos_ptr(), start_vcol); } (void)del_char_after_col(limit_col); if (l_State & VREPLACE_FLAG) { @@ -7201,8 +7304,8 @@ static void replace_do_bs(int limit_col) p = get_cursor_pos_ptr(); ins_len = (int)STRLEN(p) - orig_len; vcol = start_vcol; - for (i = 0; i < ins_len; ++i) { - vcol += chartabsize(p + i, vcol); + for (i = 0; i < ins_len; i++) { + vcol += win_chartabsize(curwin, p + i, vcol); i += (*mb_ptr2len)(p) - 1; } vcol -= start_vcol; @@ -7219,8 +7322,9 @@ static void replace_do_bs(int limit_col) // mark the buffer as changed and prepare for displaying changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); - } else if (cc == 0) + } else if (cc == 0) { (void)del_char_after_col(limit_col); + } } /// Check that C-indenting is on. @@ -7238,23 +7342,25 @@ static bool cindent_on(void) */ void fixthisline(IndentGetter get_the_indent) { - int amount = get_the_indent(); + int amount = get_the_indent(); - if (amount >= 0) { - change_indent(INDENT_SET, amount, false, 0, true); - if (linewhite(curwin->w_cursor.lnum)) { - did_ai = true; // delete the indent if the line stays empty - } + if (amount >= 0) { + change_indent(INDENT_SET, amount, false, 0, true); + if (linewhite(curwin->w_cursor.lnum)) { + did_ai = true; // delete the indent if the line stays empty } + } } void fix_indent(void) { - if (p_paste) + if (p_paste) { return; - if (curbuf->b_p_lisp && curbuf->b_p_ai) + } + if (curbuf->b_p_lisp && curbuf->b_p_ai) { fixthisline(get_lisp_indent); - else if (cindent_on()) + } else if (cindent_on()) { do_c_expr_indent(); + } } /// Check that "cinkeys" contains the key "keytyped", @@ -7295,9 +7401,12 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) * 'when' and a '*' or '!' before the key. */ switch (when) { - case '*': try_match = (*look == '*'); break; - case '!': try_match = (*look == '!'); break; - default: try_match = (*look != '*'); break; + case '*': + try_match = (*look == '*'); break; + case '!': + try_match = (*look == '!'); break; + default: + try_match = (*look != '*'); break; } if (*look == '*' || *look == '!') { look++; @@ -7322,8 +7431,8 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) } look += 2; - // 'o' means "o" command, open forward. - // 'O' means "O" command, open backward. + // 'o' means "o" command, open forward. + // 'O' means "O" command, open backward. } else if (*look == 'o') { if (try_match && keytyped == KEY_OPEN_FORW) { return true; @@ -7335,8 +7444,8 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) } look++; - // 'e' means to check for "else" at start of line and just before the - // cursor. + // 'e' means to check for "else" at start of line and just before the + // cursor. } else if (*look == 'e') { if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) { p = get_cursor_line_ptr(); @@ -7347,9 +7456,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) } look++; - // ':' only causes an indent if it is at the end of a label or case - // statement, or when it was before typing the ':' (to fix - // class::method for C++). + // ':' only causes an indent if it is at the end of a label or case + // statement, or when it was before typing the ':' (to fix + // class::method for C++). } else if (*look == ':') { if (try_match && keytyped == ':') { p = get_cursor_line_ptr(); @@ -7363,8 +7472,8 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) && p[curwin->w_cursor.col - 2] == ':') { p[curwin->w_cursor.col - 1] = ' '; const bool i = cin_iscase(p, false) - || cin_isscopedecl(p) - || cin_islabel(); + || cin_isscopedecl(p) + || cin_islabel(); p = get_cursor_line_ptr(); p[curwin->w_cursor.col - 1] = ':'; if (i) { @@ -7374,7 +7483,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) } look++; - // Is it a key in <>, maybe? + // Is it a key in <>, maybe? } else if (*look == '<') { if (try_match) { // make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>, @@ -7389,10 +7498,12 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) return true; } } - while (*look && *look != '>') + while (*look && *look != '>') { look++; - while (*look == '>') + } + while (*look == '>') { look++; + } } /* * Is it a word: "=word"? @@ -7402,11 +7513,13 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) if (*look == '~') { icase = TRUE; ++look; - } else + } else { icase = FALSE; + } p = vim_strchr(look, ','); - if (p == NULL) + if (p == NULL) { p = look + STRLEN(look); + } if ((try_match || try_match_word) && curwin->w_cursor.col >= (colnr_T)(p - look)) { bool match = false; @@ -7427,8 +7540,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) if (s + (p - look) <= line + curwin->w_cursor.col && (icase ? mb_strnicmp(s, look, (size_t)(p - look)) - : STRNCMP(s, look, p - look)) == 0) + : STRNCMP(s, look, p - look)) == 0) { match = true; + } } else { // TODO(@brammool): multi-byte if (keytyped == (int)p[-1] @@ -7459,7 +7573,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) } look = p; - // Ok, it's a boring generic character. + // Ok, it's a boring generic character. } else { if (try_match && *look == keytyped) { return true; @@ -7489,15 +7603,15 @@ int hkmap(int c) PEIsofit, PEI, ZADIsofit, ZADI, KOF, RESH, hSHIN, TAV }; static char_u map[26] = - {(char_u)hALEF /*a*/, (char_u)BET /*b*/, (char_u)hKAF /*c*/, - (char_u)DALET /*d*/, (char_u)-1 /*e*/, (char_u)PEIsofit /*f*/, - (char_u)GIMEL /*g*/, (char_u)HEI /*h*/, (char_u)IUD /*i*/, - (char_u)HET /*j*/, (char_u)KOF /*k*/, (char_u)LAMED /*l*/, - (char_u)MEM /*m*/, (char_u)NUN /*n*/, (char_u)SAMEH /*o*/, - (char_u)PEI /*p*/, (char_u)-1 /*q*/, (char_u)RESH /*r*/, - (char_u)ZAIN /*s*/, (char_u)TAV /*t*/, (char_u)TET /*u*/, - (char_u)VAV /*v*/, (char_u)hSHIN /*w*/, (char_u)-1 /*x*/, - (char_u)AIN /*y*/, (char_u)ZADI /*z*/}; + { (char_u)hALEF /*a*/, (char_u)BET /*b*/, (char_u)hKAF /*c*/, + (char_u)DALET /*d*/, (char_u)-1 /*e*/, (char_u)PEIsofit /*f*/, + (char_u)GIMEL /*g*/, (char_u)HEI /*h*/, (char_u)IUD /*i*/, + (char_u)HET /*j*/, (char_u)KOF /*k*/, (char_u)LAMED /*l*/, + (char_u)MEM /*m*/, (char_u)NUN /*n*/, (char_u)SAMEH /*o*/, + (char_u)PEI /*p*/, (char_u)-1 /*q*/, (char_u)RESH /*r*/, + (char_u)ZAIN /*s*/, (char_u)TAV /*t*/, (char_u)TET /*u*/, + (char_u)VAV /*v*/, (char_u)hSHIN /*w*/, (char_u)-1 /*x*/, + (char_u)AIN /*y*/, (char_u)ZADI /*z*/ }; if (c == 'N' || c == 'M' || c == 'P' || c == 'C' || c == 'Z') { return (int)(map[CharOrd(c)] - 1 + p_aleph); @@ -7522,24 +7636,33 @@ int hkmap(int c) } } else { switch (c) { - case '`': return ';'; - case '/': return '.'; - case '\'': return ','; - case 'q': return '/'; - case 'w': return '\''; + case '`': + return ';'; + case '/': + return '.'; + case '\'': + return ','; + case 'q': + return '/'; + case 'w': + return '\''; // Hebrew letters - set offset from 'a' - case ',': c = '{'; break; - case '.': c = 'v'; break; - case ';': c = 't'; break; + case ',': + c = '{'; break; + case '.': + c = 'v'; break; + case ';': + c = 't'; break; default: { - static char str[] = "zqbcxlsjphmkwonu ydafe rig"; + static char str[] = "zqbcxlsjphmkwonu ydafe rig"; - if (c < 'a' || c > 'z') - return c; - c = str[CharOrdLow(c)]; - break; - } + if (c < 'a' || c > 'z') { + return c; + } + c = str[CharOrdLow(c)]; + break; + } } return (int)(CharOrdLow(c) + p_aleph); @@ -7659,13 +7782,15 @@ static void ins_ctrl_g(void) // CTRL-G k and CTRL-G <Up>: cursor up to Insstart.col case K_UP: case Ctrl_K: - case 'k': ins_up(TRUE); + case 'k': + ins_up(TRUE); break; // CTRL-G j and CTRL-G <Down>: cursor down to Insstart.col case K_DOWN: case Ctrl_J: - case 'j': ins_down(TRUE); + case 'j': + ins_down(TRUE); break; // CTRL-G u: start new undoable edit @@ -7687,7 +7812,8 @@ static void ins_ctrl_g(void) break; // Unknown CTRL-G command, reserved for future expansion. - default: vim_beep(BO_CTRLG); + default: + vim_beep(BO_CTRLG); } } @@ -7745,8 +7871,9 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) */ if (*count > 0) { line_breakcheck(); - if (got_int) + if (got_int) { *count = 0; + } } if (--*count > 0) { // repeat what was typed @@ -7795,8 +7922,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) || (gchar_cursor() == NUL && !VIsual_active )) - && !revins_on - ) { + && !revins_on) { if (curwin->w_cursor.coladd > 0 || ve_flags == VE_ALL) { oneleft(); if (restart_edit != NUL) { @@ -7835,8 +7961,9 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) static void ins_ctrl_(void) { if (revins_on && revins_chars && revins_scol >= 0) { - while (gchar_cursor() != NUL && revins_chars--) + while (gchar_cursor() != NUL && revins_chars--) { ++curwin->w_cursor.col; + } } p_ri = !p_ri; revins_on = (State == INSERT && p_ri); @@ -7845,8 +7972,9 @@ static void ins_ctrl_(void) revins_legal++; revins_chars = 0; undisplay_dollar(); - } else + } else { revins_scol = -1; + } p_hkmap = curwin->w_p_rl ^ p_ri; // be consistent! showmode(); } @@ -7869,8 +7997,9 @@ static bool ins_start_select(int c) case K_KPAGEUP: case K_PAGEDOWN: case K_KPAGEDOWN: - if (!(mod_mask & MOD_MASK_SHIFT)) + if (!(mod_mask & MOD_MASK_SHIFT)) { break; + } FALLTHROUGH; case K_S_LEFT: case K_S_RIGHT: @@ -7919,12 +8048,13 @@ static void ins_insert(int replaceState) */ static void ins_ctrl_o(void) { - if (State & VREPLACE_FLAG) + if (State & VREPLACE_FLAG) { restart_edit = 'V'; - else if (State & REPLACE_FLAG) + } else if (State & REPLACE_FLAG) { restart_edit = 'R'; - else + } else { restart_edit = 'I'; + } if (virtual_active()) { ins_at_eol = false; // cursor always keeps its column } else { @@ -7934,15 +8064,16 @@ static void ins_ctrl_o(void) /* * If the cursor is on an indent, ^T/^D insert/delete one - * shiftwidth. Otherwise ^T/^D behave like a "<<" or ">>". + * shiftwidth. Otherwise ^T/^D behave like a "<<" or ">>". * Always round the indent to 'shiftwidth', this is compatible * with vi. But vi only supports ^T and ^D after an * autoindent, we support it everywhere. */ static void ins_shift(int c, int lastc) { - if (stop_arrow() == FAIL) + if (stop_arrow() == FAIL) { return; + } AppendCharToRedobuff(c); /* @@ -7960,8 +8091,9 @@ static void ins_shift(int c, int lastc) old_indent = get_indent(); // remember curr. indent } change_indent(INDENT_SET, 0, TRUE, 0, TRUE); - } else + } else { change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, TRUE, 0, TRUE); + } if (did_ai && *skipwhite(get_cursor_line_ptr()) != NUL) { did_ai = false; @@ -8024,7 +8156,7 @@ static void ins_bs_one(colnr_T *vcolp) /// Handle Backspace, delete-word and delete-line in Insert mode. /// -/// @param c charcter that was typed +/// @param c character that was typed /// @param mode backspace mode to use /// @param[in,out] inserted_space_p whether a space was the last // character inserted @@ -8047,7 +8179,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) // can't backup past first character in buffer // can't backup past starting point unless 'backspace' > 1 // can backup to a previous line if 'backspace' == 0 - if (BUFEMPTY() + if (buf_is_empty(curbuf) || (!revins_on && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) || (!can_bs(BS_START) @@ -8123,20 +8255,22 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) // again when auto-formatting. if (has_format_option(FO_AUTO) && has_format_option(FO_WHITE_PAR)) { - char_u *ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, - TRUE); + char_u *ptr = ml_get_buf(curbuf, curwin->w_cursor.lnum, true); int len; len = (int)STRLEN(ptr); - if (len > 0 && ptr[len - 1] == ' ') + if (len > 0 && ptr[len - 1] == ' ') { ptr[len - 1] = NUL; + } } do_join(2, FALSE, FALSE, FALSE, false); - if (temp == NUL && gchar_cursor() != NUL) + if (temp == NUL && gchar_cursor() != NUL) { inc_cursor(); - } else + } + } else { dec_cursor(); + } /* * In REPLACE mode we have to put back the text that was replaced @@ -8178,12 +8312,12 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) && (curbuf->b_p_ai || cindent_on() ) - && !revins_on - ) { + && !revins_on) { save_col = curwin->w_cursor.col; beginline(BL_WHITE); - if (curwin->w_cursor.col < save_col) + if (curwin->w_cursor.col < save_col) { mincol = curwin->w_cursor.col; + } curwin->w_cursor.col = save_col; } @@ -8239,8 +8373,9 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) ins_char(' '); } else { ins_str((char_u *)" "); - if ((State & REPLACE_FLAG)) + if ((State & REPLACE_FLAG)) { replace_push(NUL); + } } getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); } @@ -8251,7 +8386,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) ins_bs_one(&vcol); } } else { - // Delete upto starting point, start of line or previous word. + // Delete up to starting point, start of line or previous word. int prev_cclass = 0; int cclass = mb_get_class(get_cursor_pos_ptr()); @@ -8294,8 +8429,9 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) revins_chars--; revins_legal++; } - if (revins_on && gchar_cursor() == NUL) + if (revins_on && gchar_cursor() == NUL) { break; + } } // Just a single backspace?: if (mode == BACKSPACE_CHAR) { @@ -8349,12 +8485,12 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) static void ins_mouse(int c) { pos_T tpos; - win_T *old_curwin = curwin; + win_T *old_curwin = curwin; undisplay_dollar(); tpos = curwin->w_cursor; if (do_mouse(NULL, c, BACKWARD, 1, 0)) { - win_T *new_curwin = curwin; + win_T *new_curwin = curwin; if (curwin != old_curwin && win_valid(old_curwin)) { // Mouse took us to another window. We need to go back to the @@ -8394,21 +8530,22 @@ static void ins_mousescroll(int dir) curwin = wp; curbuf = curwin->w_buffer; } - if (curwin == old_curwin) + if (curwin == old_curwin) { undisplay_dollar(); + } // Don't scroll the window in which completion is being done. if (!pum_visible() - || curwin != old_curwin - ) { + || curwin != old_curwin) { if (dir == MSCR_DOWN || dir == MSCR_UP) { - if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) + if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) { scroll_redraw(dir, - (long)(curwin->w_botline - curwin->w_topline)); - else + (long)(curwin->w_botline - curwin->w_topline)); + } else { scroll_redraw(dir, 3L); + } } else { - mouse_scroll_horiz(dir); + mouse_scroll_horiz(dir); } } @@ -8430,8 +8567,9 @@ static void ins_left(void) pos_T tpos; const bool end_change = dont_sync_undo == kFalse; // end undoable change - if ((fdo_flags & FDO_HOR) && KeyTyped) + if ((fdo_flags & FDO_HOR) && KeyTyped) { foldOpenCursor(); + } undisplay_dollar(); tpos = curwin->w_cursor; if (oneleft() == OK) { @@ -8461,12 +8599,14 @@ static void ins_home(int c) { pos_T tpos; - if ((fdo_flags & FDO_HOR) && KeyTyped) + if ((fdo_flags & FDO_HOR) && KeyTyped) { foldOpenCursor(); + } undisplay_dollar(); tpos = curwin->w_cursor; - if (c == K_C_HOME) + if (c == K_C_HOME) { curwin->w_cursor.lnum = 1; + } curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; curwin->w_curswant = 0; @@ -8477,12 +8617,14 @@ static void ins_end(int c) { pos_T tpos; - if ((fdo_flags & FDO_HOR) && KeyTyped) + if ((fdo_flags & FDO_HOR) && KeyTyped) { foldOpenCursor(); + } undisplay_dollar(); tpos = curwin->w_cursor; - if (c == K_C_END) + if (c == K_C_END) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + } coladvance(MAXCOL); curwin->w_curswant = MAXCOL; @@ -8530,8 +8672,9 @@ static void ins_right(void) } revins_legal++; - if (revins_chars) + if (revins_chars) { revins_chars--; + } } else if (vim_strchr(p_ww, ']') != NULL && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { // if 'whichwrap' set for cursor in insert mode, may move the @@ -8567,9 +8710,8 @@ static void ins_s_right(void) dont_sync_undo = kFalse; } -static void ins_up( - bool startcol // when true move to Insstart.col -) +/// @param startcol when true move to Insstart.col +static void ins_up(bool startcol) { pos_T tpos; linenr_T old_topline = curwin->w_topline; @@ -8578,12 +8720,13 @@ static void ins_up( undisplay_dollar(); tpos = curwin->w_cursor; if (cursor_up(1L, TRUE) == OK) { - if (startcol) + if (startcol) { coladvance(getvcol_nolist(&Insstart)); + } if (old_topline != curwin->w_topline - || old_topfill != curwin->w_topfill - ) + || old_topfill != curwin->w_topfill) { redraw_later(curwin, VALID); + } start_arrow(&tpos); can_cindent = true; } else { @@ -8615,9 +8758,8 @@ static void ins_pageup(void) } } -static void ins_down( - bool startcol // when true move to Insstart.col -) +/// @param startcol when true move to Insstart.col +static void ins_down(bool startcol) { pos_T tpos; linenr_T old_topline = curwin->w_topline; @@ -8626,12 +8768,13 @@ static void ins_down( undisplay_dollar(); tpos = curwin->w_cursor; if (cursor_down(1L, TRUE) == OK) { - if (startcol) + if (startcol) { coladvance(getvcol_nolist(&Insstart)); + } if (old_topline != curwin->w_topline - || old_topfill != curwin->w_topfill - ) + || old_topfill != curwin->w_topfill) { redraw_later(curwin, VALID); + } start_arrow(&tpos); can_cindent = true; } else { @@ -8687,15 +8830,15 @@ static bool ins_tab(void) // When nothing special, insert TAB like a normal character. if (!curbuf->b_p_et && !( - p_sta - && ind - // These five lines mean 'tabstop' != 'shiftwidth' - && ((tabstop_count(curbuf->b_p_vts_array) > 1) - || (tabstop_count(curbuf->b_p_vts_array) == 1 - && tabstop_first(curbuf->b_p_vts_array) - != get_sw_value(curbuf)) - || (tabstop_count(curbuf->b_p_vts_array) == 0 - && curbuf->b_p_ts != get_sw_value(curbuf)))) + p_sta + && ind + // These five lines mean 'tabstop' != 'shiftwidth' + && ((tabstop_count(curbuf->b_p_vts_array) > 1) + || (tabstop_count(curbuf->b_p_vts_array) == 1 + && tabstop_first(curbuf->b_p_vts_array) + != get_sw_value(curbuf)) + || (tabstop_count(curbuf->b_p_vts_array) == 0 + && curbuf->b_p_ts != get_sw_value(curbuf)))) && tabstop_count(curbuf->b_p_vsts_array) == 0 && get_sts_value() == 0) { return true; } @@ -8727,7 +8870,7 @@ static bool ins_tab(void) } /* - * Insert the first space with ins_char(). It will delete one char in + * Insert the first space with ins_char(). It will delete one char in * replace mode. Insert the rest with ins_str(); it will not delete any * chars. For VREPLACE mode, we use ins_char() for all characters. */ @@ -8749,11 +8892,11 @@ static bool ins_tab(void) if (!curbuf->b_p_et && (tabstop_count(curbuf->b_p_vsts_array) > 0 || get_sts_value() > 0 || (p_sta && ind))) { - char_u *ptr; - char_u *saved_line = NULL; // init for GCC + char_u *ptr; + char_u *saved_line = NULL; // init for GCC pos_T pos; pos_T fpos; - pos_T *cursor; + pos_T *cursor; colnr_T want_vcol, vcol; int change_col = -1; int save_list = curwin->w_p_list; @@ -8800,8 +8943,9 @@ static bool ins_tab(void) // and 'linebreak' adding extra virtual columns. while (ascii_iswhite(*ptr)) { i = lbr_chartabsize(NULL, (char_u *)"\t", vcol); - if (vcol + i > want_vcol) + if (vcol + i > want_vcol) { break; + } if (*ptr != TAB) { *ptr = TAB; if (change_col < 0) { @@ -8865,12 +9009,13 @@ static bool ins_tab(void) // Insert each char in saved_line from changed_col to // ptr-cursor ins_bytes_len(saved_line + change_col, - cursor->col - change_col); + cursor->col - change_col); } } - if (State & VREPLACE_FLAG) + if (State & VREPLACE_FLAG) { xfree(saved_line); + } curwin->w_p_list = save_list; } @@ -8896,9 +9041,9 @@ static bool ins_eol(int c) * nothing to put back when the NL is deleted. */ if ((State & REPLACE_FLAG) - && !(State & VREPLACE_FLAG) - ) + && !(State & VREPLACE_FLAG)) { replace_push(NUL); + } /* * In VREPLACE mode, a NL replaces the rest of the line, and starts @@ -9008,8 +9153,8 @@ int ins_copychar(linenr_T lnum) { int c; int temp; - char_u *ptr, *prev_ptr; - char_u *line; + char_u *ptr, *prev_ptr; + char_u *line; if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { vim_beep(BO_COPY); @@ -9025,8 +9170,9 @@ int ins_copychar(linenr_T lnum) prev_ptr = ptr; temp += lbr_chartabsize_adv(line, &ptr, (colnr_T)temp); } - if ((colnr_T)temp > curwin->w_virtcol) + if ((colnr_T)temp > curwin->w_virtcol) { ptr = prev_ptr; + } c = utf_ptr2char(ptr); if (c == NUL) { @@ -9043,10 +9189,11 @@ static int ins_ctrl_ey(int tc) int c = tc; if (ctrl_x_mode == CTRL_X_SCROLL) { - if (c == Ctrl_Y) + if (c == Ctrl_Y) { scrolldown_clamp(); - else + } else { scrollup_clamp(); + } redraw_later(curwin, VALID); } else { c = ins_copychar(curwin->w_cursor.lnum + (c == Ctrl_Y ? -1 : 1)); @@ -9079,8 +9226,8 @@ static int ins_ctrl_ey(int tc) */ static void ins_try_si(int c) { - pos_T *pos, old_pos; - char_u *ptr; + pos_T *pos, old_pos; + char_u *ptr; int i; int temp; @@ -9108,14 +9255,16 @@ static void ins_try_si(int c) } curwin->w_cursor.lnum = pos->lnum; curwin->w_cursor.col = i; - if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) + if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) { curwin->w_cursor = *pos; + } i = get_indent(); curwin->w_cursor = old_pos; - if (State & VREPLACE_FLAG) + if (State & VREPLACE_FLAG) { change_indent(INDENT_SET, i, FALSE, NUL, TRUE); - else + } else { (void)set_indent(i, SIN_CHANGED); + } } else if (curwin->w_cursor.col > 0) { /* * when inserting '{' after "O" reduce indent, but not @@ -9133,12 +9282,14 @@ static void ins_try_si(int c) break; } } - if (get_indent() >= i) + if (get_indent() >= i) { temp = FALSE; + } curwin->w_cursor = old_pos; } - if (temp) + if (temp) { shift_line(TRUE, FALSE, 1, TRUE); + } } } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a3fa9c986f..dfbb187b5b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -65,6 +65,8 @@ static char *e_missbrac = N_("E111: Missing ']'"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_cannot_mod = N_("E995: Cannot modify existing variable"); +static char *e_nowhitespace + = N_("E274: No white space allowed before parenthesis"); static char *e_invalwindow = N_("E957: Invalid window number"); static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); @@ -265,7 +267,7 @@ static partial_T *vvlua_partial; #endif static uint64_t last_timer_id = 1; -static PMap(uint64_t) *timers = NULL; +static PMap(uint64_t) timers = MAP_INIT; static const char *const msgpack_type_names[] = { [kMPNil] = "nil", @@ -326,7 +328,6 @@ void eval_init(void) { vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; - timers = pmap_new(uint64_t)(); struct vimvar *p; init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE); @@ -737,15 +738,15 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(1, 2, 4) { - int dummy; + funcexe_T funcexe = FUNCEXE_INIT; if (expr->v_type == VAR_FUNC) { const char_u *const s = expr->vval.v_string; if (s == NULL || *s == NUL) { return FAIL; } - if (call_func(s, -1, rettv, argc, argv, NULL, - 0L, 0L, &dummy, true, NULL, NULL) == FAIL) { + funcexe.evaluate = true; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { return FAIL; } } else if (expr->v_type == VAR_PARTIAL) { @@ -754,8 +755,9 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, if (s == NULL || *s == NUL) { return FAIL; } - if (call_func(s, -1, rettv, argc, argv, NULL, - 0L, 0L, &dummy, true, partial, NULL) == FAIL) { + funcexe.evaluate = true; + funcexe.partial = partial; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { return FAIL; } } else { @@ -1051,7 +1053,6 @@ int call_vim_function( ) FUNC_ATTR_NONNULL_ALL { - int doesrange; int ret; int len = (int)STRLEN(func); partial_T *pt = NULL; @@ -1067,9 +1068,12 @@ int call_vim_function( } rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. - ret = call_func(func, len, rettv, argc, argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, true, pt, NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = pt; + ret = call_func(func, len, rettv, argc, argv, &funcexe); fail: if (ret == FAIL) { @@ -1452,21 +1456,18 @@ static void ex_let_const(exarg_T *eap, const bool is_const) /* * Assign the typevalue "tv" to the variable or variables at "arg_start". * Handles both "var" with any type and "[var, var; var]" with a list type. - * When "nextchars" is not NULL it points to a string with characters that + * When "op" is not NULL it points to a string with characters that * must appear after the variable(s). Use "+", "-" or "." for add, subtract * or concatenate. * Returns OK or FAIL; */ -static int -ex_let_vars( - char_u *arg_start, - typval_T *tv, - int copy, // copy values from "tv", don't move - int semicolon, // from skip_var_list() - int var_count, // from skip_var_list() - int is_const, // lock variables for :const - char_u *nextchars -) +static int ex_let_vars(char_u *arg_start, + typval_T *tv, + int copy, // copy values from "tv", don't move + int semicolon, // from skip_var_list() + int var_count, // from skip_var_list() + int is_const, // lock variables for :const + char_u *op) { char_u *arg = arg_start; typval_T ltv; @@ -1475,7 +1476,7 @@ ex_let_vars( /* * ":let var = expr" or ":for var in list" */ - if (ex_let_one(arg, tv, copy, is_const, nextchars, nextchars) == NULL) { + if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL) { return FAIL; } return OK; @@ -1506,7 +1507,7 @@ ex_let_vars( while (*arg != ']') { arg = skipwhite(arg + 1); arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, is_const, - (const char_u *)",;]", nextchars); + (const char_u *)",;]", op); if (arg == NULL) { return FAIL; } @@ -1528,8 +1529,8 @@ ex_let_vars( ltv.vval.v_list = rest_list; tv_list_ref(rest_list); - arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, - (char_u *)"]", nextchars); + arg = ex_let_one(skipwhite(arg + 1), <v, false, is_const, (char_u *)"]", + op); tv_clear(<v); if (arg == NULL) { return FAIL; @@ -1725,7 +1726,9 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) } else { // handle d.key, l[idx], f(expr) const char *const arg_subsc = arg; - if (handle_subscript(&arg, &tv, true, true) == FAIL) { + if (handle_subscript(&arg, &tv, true, true, (const char_u *)name, + (const char_u **)&name) + == FAIL) { error = true; } else { if (arg == arg_subsc && len == 2 && name[1] == ':') { @@ -2340,10 +2343,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name), &tv, &di, true, false) == OK) { if ((di == NULL - || (!var_check_ro(di->di_flags, (const char *)lp->ll_name, - TV_CSTRING) - && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, - TV_CSTRING))) + || (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING) + && !tv_check_lock(&di->di_tv, lp->ll_name, TV_CSTRING))) && eexe_mod_op(&tv, rettv, op) == OK) { set_var(lp->ll_name, lp->ll_name_len, &tv, false); } @@ -2353,10 +2354,10 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, set_var_const(lp->ll_name, lp->ll_name_len, rettv, copy, is_const); } *endp = cc; - } else if (tv_check_lock(lp->ll_newkey == NULL - ? lp->ll_tv->v_lock - : lp->ll_tv->vval.v_dict->dv_lock, - (const char *)lp->ll_name, TV_CSTRING)) { + } else if (var_check_lock(lp->ll_newkey == NULL + ? lp->ll_tv->v_lock + : lp->ll_tv->vval.v_dict->dv_lock, + lp->ll_name, TV_CSTRING)) { } else if (lp->ll_range) { listitem_T *ll_li = lp->ll_li; int ll_n1 = lp->ll_n1; @@ -2369,9 +2370,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, // Check whether any of the list items is locked for (ri = tv_list_first(rettv->vval.v_list); ri != NULL && ll_li != NULL; ) { - if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, - (const char *)lp->ll_name, - TV_CSTRING)) { + if (var_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name, + TV_CSTRING)) { return; } ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); @@ -2795,13 +2795,13 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, } else if ((lp->ll_list != NULL // ll_list is not NULL when lvalue is not in a list, NULL lists // yield E689. - && tv_check_lock(tv_list_locked(lp->ll_list), - (const char *)lp->ll_name, - lp->ll_name_len)) + && var_check_lock(tv_list_locked(lp->ll_list), + lp->ll_name, + lp->ll_name_len)) || (lp->ll_dict != NULL - && tv_check_lock(lp->ll_dict->dv_lock, - (const char *)lp->ll_name, - lp->ll_name_len))) { + && var_check_lock(lp->ll_dict->dv_lock, + lp->ll_name, + lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { assert(lp->ll_list != NULL); @@ -2810,9 +2810,9 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, listitem_T *last_li = first_li; for (;;) { listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - if (tv_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, - (const char *)lp->ll_name, - lp->ll_name_len)) { + if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, + lp->ll_name, + lp->ll_name_len)) { return false; } lp->ll_li = li; @@ -2897,11 +2897,11 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit) dictitem_T *const di = TV_DICT_HI2DI(hi); if (var_check_fixed(di->di_flags, (const char *)name, TV_CSTRING) || var_check_ro(di->di_flags, (const char *)name, TV_CSTRING) - || tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) { + || var_check_lock(d->dv_lock, name, TV_CSTRING)) { return FAIL; } - if (tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) { + if (var_check_lock(d->dv_lock, name, TV_CSTRING)) { return FAIL; } @@ -3146,6 +3146,65 @@ static int pattern_match(char_u *pat, char_u *text, bool ic) return matches; } +/// Handle a name followed by "(". Both for just "name(arg)" and for +/// "expr->name(arg)". +// +/// @param arg Points to "(", will be advanced +/// @param basetv "expr" for "expr->name(arg)" +// +/// @return OK or FAIL. +static int eval_func(char_u **const arg, char_u *const name, const int name_len, + typval_T *const rettv, const bool evaluate, + typval_T *const basetv) + FUNC_ATTR_NONNULL_ARG(1, 2, 4) +{ + char_u *s = name; + int len = name_len; + + if (!evaluate) { + check_vars((const char *)s, len); + } + + // If "s" is the name of a variable of type VAR_FUNC + // use its contents. + partial_T *partial; + s = deref_func_name((const char *)s, &len, &partial, !evaluate); + + // Need to make a copy, in case evaluating the arguments makes + // the name invalid. + s = xmemdupz(s, len); + + // Invoke the function. + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = partial; + funcexe.basetv = basetv; + int ret = get_func_tv(s, len, rettv, arg, &funcexe); + + xfree(s); + + // If evaluate is false rettv->v_type was not set in + // get_func_tv, but it's needed in handle_subscript() to parse + // what follows. So set it here. + if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { + rettv->vval.v_string = (char_u *)tv_empty_string; + rettv->v_type = VAR_FUNC; + } + + // Stop the expression evaluation when immediately + // aborting on error, or when an interrupt occurred or + // an exception was thrown but not caught. + if (evaluate && aborting()) { + if (ret == OK) { + tv_clear(rettv); + } + ret = FAIL; + } + return ret; +} + // TODO(ZyX-I): move to eval/expressions /* @@ -3165,6 +3224,8 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) { int ret; char_u *p; + const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; p = skipwhite(arg); ret = eval1(&p, rettv, evaluate); @@ -3174,8 +3235,10 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) } // Report the invalid expression unless the expression evaluation has // been cancelled due to an aborting error, an interrupt, or an - // exception. - if (!aborting()) { + // exception, or we already gave a more specific error. + // Also check called_emsg for when using assert_fails(). + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { emsgf(_(e_invexpr2), arg); } ret = FAIL; @@ -3805,6 +3868,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string) // + in front unary plus (ignored) // trailing [] subscript in String or List // trailing .name entry in Dictionary +// trailing ->name() method call // // "arg" must point to the first non-white of the expression. // "arg" is advanced to the next non-white after the recognized expression. @@ -3819,10 +3883,10 @@ static int eval7( { varnumber_T n; int len; - char_u *s; - char_u *start_leader, *end_leader; + char_u *s; + const char_u *start_leader, *end_leader; int ret = OK; - char_u *alias; + char_u *alias; // Initialise variable so that tv_clear() can't mistake this for a // string and free a string that isn't there. @@ -3883,7 +3947,12 @@ static int eval7( rettv->vval.v_float = f; } } else { - vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0); + vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true); + if (len == 0) { + EMSG2(_(e_invexpr2), *arg); + ret = FAIL; + break; + } *arg += len; if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -3972,44 +4041,7 @@ static int eval7( ret = FAIL; } else { if (**arg == '(') { // recursive! - partial_T *partial; - - if (!evaluate) { - check_vars((const char *)s, len); - } - - // If "s" is the name of a variable of type VAR_FUNC - // use its contents. - s = deref_func_name((const char *)s, &len, &partial, !evaluate); - - // Need to make a copy, in case evaluating the arguments makes - // the name invalid. - s = xmemdupz(s, len); - - // Invoke the function. - ret = get_func_tv(s, len, rettv, arg, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate, partial, NULL); - - xfree(s); - - // If evaluate is false rettv->v_type was not set in - // get_func_tv, but it's needed in handle_subscript() to parse - // what follows. So set it here. - if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { - rettv->vval.v_string = (char_u *)tv_empty_string; - rettv->v_type = VAR_FUNC; - } - - // Stop the expression evaluation when immediately - // aborting on error, or when an interrupt occurred or - // an exception was thrown but not caught. - if (evaluate && aborting()) { - if (ret == OK) { - tv_clear(rettv); - } - ret = FAIL; - } + ret = eval_func(arg, s, len, rettv, evaluate, NULL); } else if (evaluate) { ret = get_var_tv((const char *)s, len, rettv, NULL, true, false); } else { @@ -4023,51 +4055,230 @@ static int eval7( *arg = skipwhite(*arg); // Handle following '[', '(' and '.' for expr[expr], expr.name, - // expr(expr). + // expr(expr), expr->name(expr) if (ret == OK) { - ret = handle_subscript((const char **)arg, rettv, evaluate, true); + ret = handle_subscript((const char **)arg, rettv, evaluate, true, + start_leader, &end_leader); } // Apply logical NOT and unary '-', from right to left, ignore '+'. if (ret == OK && evaluate && end_leader > start_leader) { - bool error = false; - varnumber_T val = 0; - float_T f = 0.0; + ret = eval7_leader(rettv, start_leader, &end_leader); + } + return ret; +} + +/// Apply the leading "!" and "-" before an eval7 expression to "rettv". +/// Adjusts "end_leaderp" until it is at "start_leader". +/// @return OK on success, FAIL on failure. +static int eval7_leader(typval_T *const rettv, const char_u *const start_leader, + const char_u **const end_leaderp) + FUNC_ATTR_NONNULL_ALL +{ + const char_u *end_leader = *end_leaderp; + int ret = OK; + bool error = false; + varnumber_T val = 0; + float_T f = 0.0; + if (rettv->v_type == VAR_FLOAT) { + f = rettv->vval.v_float; + } else { + val = tv_get_number_chk(rettv, &error); + } + if (error) { + tv_clear(rettv); + ret = FAIL; + } else { + while (end_leader > start_leader) { + end_leader--; + if (*end_leader == '!') { + if (rettv->v_type == VAR_FLOAT) { + f = !f; + } else { + val = !val; + } + } else if (*end_leader == '-') { + if (rettv->v_type == VAR_FLOAT) { + f = -f; + } else { + val = -val; + } + } + } if (rettv->v_type == VAR_FLOAT) { - f = rettv->vval.v_float; + tv_clear(rettv); + rettv->vval.v_float = f; } else { - val = tv_get_number_chk(rettv, &error); - } - if (error) { tv_clear(rettv); - ret = FAIL; + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = val; + } + } + + *end_leaderp = end_leader; + return ret; +} + +/// Call the function referred to in "rettv". +/// @param lua_funcname If `rettv` refers to a v:lua function, this must point +/// to the name of the Lua function to call (after the +/// "v:lua." prefix). +/// @return OK on success, FAIL on failure. +static int call_func_rettv(char_u **const arg, + typval_T *const rettv, + const bool evaluate, + dict_T *const selfdict, + typval_T *const basetv, + const char_u *const lua_funcname) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + partial_T *pt = NULL; + typval_T functv; + const char_u *funcname; + bool is_lua = false; + + // need to copy the funcref so that we can clear rettv + if (evaluate) { + functv = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Invoke the function. Recursive! + if (functv.v_type == VAR_PARTIAL) { + pt = functv.vval.v_partial; + is_lua = is_luafunc(pt); + funcname = is_lua ? lua_funcname : partial_name(pt); } else { - while (end_leader > start_leader) { - --end_leader; - if (*end_leader == '!') { - if (rettv->v_type == VAR_FLOAT) { - f = !f; - } else { - val = !val; - } - } else if (*end_leader == '-') { - if (rettv->v_type == VAR_FLOAT) { - f = -f; - } else { - val = -val; - } - } + funcname = functv.vval.v_string; + } + } else { + funcname = (char_u *)""; + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = evaluate; + funcexe.partial = pt; + funcexe.selfdict = selfdict; + funcexe.basetv = basetv; + const int ret = get_func_tv(funcname, is_lua ? *arg - funcname : -1, rettv, + (char_u **)arg, &funcexe); + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&functv); + } + + return ret; +} + +/// Evaluate "->method()". +/// @param verbose if true, give error messages. +/// @note "*arg" points to the '-'. +/// @return FAIL or OK. @note "*arg" is advanced to after the ')'. +static int eval_lambda(char_u **const arg, typval_T *const rettv, + const bool evaluate, const bool verbose) + FUNC_ATTR_NONNULL_ALL +{ + // Skip over the ->. + *arg += 2; + typval_T base = *rettv; + rettv->v_type = VAR_UNKNOWN; + + int ret = get_lambda_tv(arg, rettv, evaluate); + if (ret == NOTDONE) { + return FAIL; + } else if (**arg != '(') { + if (verbose) { + if (*skipwhite(*arg) == '(') { + EMSG(_(e_nowhitespace)); + } else { + EMSG2(_(e_missingparen), "lambda"); } - if (rettv->v_type == VAR_FLOAT) { - tv_clear(rettv); - rettv->vval.v_float = f; + } + tv_clear(rettv); + ret = FAIL; + } else { + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL); + } + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&base); + } + + return ret; +} + +/// Evaluate "->method()" or "->v:lua.method()". +/// @note "*arg" points to the '-'. +/// @return FAIL or OK. "*arg" is advanced to after the ')'. +static int eval_method(char_u **const arg, typval_T *const rettv, + const bool evaluate, const bool verbose) + FUNC_ATTR_NONNULL_ALL +{ + // Skip over the ->. + *arg += 2; + typval_T base = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Locate the method name. + int len; + char_u *name = *arg; + char_u *lua_funcname = NULL; + if (STRNCMP(name, "v:lua.", 6) == 0) { + lua_funcname = name + 6; + *arg = (char_u *)skip_luafunc_name((const char *)lua_funcname); + *arg = skipwhite(*arg); // to detect trailing whitespace later + len = *arg - lua_funcname; + } else { + char_u *alias; + len = get_name_len((const char **)arg, (char **)&alias, evaluate, true); + if (alias != NULL) { + name = alias; + } + } + + int ret; + if (len <= 0) { + if (verbose) { + if (lua_funcname == NULL) { + EMSG(_("E260: Missing name after ->")); } else { - tv_clear(rettv); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = val; + EMSG2(_(e_invexpr2), name); } } + ret = FAIL; + } else { + if (**arg != '(') { + if (verbose) { + EMSG2(_(e_missingparen), name); + } + ret = FAIL; + } else if (ascii_iswhite((*arg)[-1])) { + if (verbose) { + EMSG(_(e_nowhitespace)); + } + ret = FAIL; + } else if (lua_funcname != NULL) { + if (evaluate) { + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = vvlua_partial; + rettv->vval.v_partial->pt_refcount++; + } + ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname); + } else { + ret = eval_func(arg, name, len, rettv, evaluate, &base); + } + } + + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&base); } return ret; @@ -4886,7 +5097,7 @@ bool garbage_collect(bool testing) // Channels { Channel *data; - map_foreach_value(channels, data, { + map_foreach_value(&channels, data, { set_ref_in_callback_reader(&data->on_data, copyID, NULL, NULL); set_ref_in_callback_reader(&data->on_stderr, copyID, NULL, NULL); set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); @@ -4896,7 +5107,7 @@ bool garbage_collect(bool testing) // Timers { timer_T *timer; - map_foreach_value(timers, timer, { + map_foreach_value(&timers, timer, { set_ref_in_callback(&timer->callback, copyID, NULL, NULL); }) } @@ -5962,14 +6173,14 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) if (argvars[0].v_type == VAR_LIST) { tv_copy(&argvars[0], rettv); if ((l = argvars[0].vval.v_list) == NULL - || (!map && tv_check_lock(tv_list_locked(l), arg_errmsg, - TV_TRANSLATE))) { + || (!map + && var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { return; } } else if (argvars[0].v_type == VAR_DICT) { tv_copy(&argvars[0], rettv); if ((d = argvars[0].vval.v_dict) == NULL - || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { + || (!map && var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; } } else { @@ -6002,7 +6213,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) di = TV_DICT_HI2DI(hi); if (map - && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) + && (var_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { break; } @@ -6029,8 +6240,8 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) for (listitem_T *li = tv_list_first(l); li != NULL;) { if (map - && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, - TV_TRANSLATE)) { + && var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, + TV_TRANSLATE)) { break; } vimvars[VV_KEY].vv_nr = idx; @@ -7200,12 +7411,15 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) r = FAIL; } else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) { char_u *name = arg->vval.v_string; - if (name != NULL) { + if (name == NULL) { + r = FAIL; + } else if (*name == NUL) { + callback->type = kCallbackNone; + callback->data.funcref = NULL; + } else { func_ref(name); callback->data.funcref = vim_strsave(name); callback->type = kCallbackFuncref; - } else { - r = FAIL; } } else if (nlua_is_table_from_lua(arg)) { char_u *name = nlua_register_table_as_callable(arg); @@ -7216,8 +7430,10 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) } else { r = FAIL; } - } else if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) { + } else if (arg->v_type == VAR_SPECIAL + || (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)) { callback->type = kCallbackNone; + callback->data.funcref = NULL; } else { r = FAIL; } @@ -7254,10 +7470,12 @@ bool callback_call(Callback *const callback, const int argcount_in, abort(); } - int dummy; - return call_func(name, -1, rettv, argcount_in, argvars_in, - NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, - true, partial, NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = partial; + return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe); } static bool set_ref_in_callback(Callback *callback, int copyID, @@ -7302,7 +7520,7 @@ static bool set_ref_in_callback_reader(CallbackReader *reader, int copyID, timer_T *find_timer_by_nr(varnumber_T xx) { - return pmap_get(uint64_t)(timers, xx); + return pmap_get(uint64_t)(&timers, xx); } void add_timer_info(typval_T *rettv, timer_T *timer) @@ -7324,21 +7542,14 @@ void add_timer_info(typval_T *rettv, timer_T *timer) return; } - if (timer->callback.type == kCallbackPartial) { - di->di_tv.v_type = VAR_PARTIAL; - di->di_tv.vval.v_partial = timer->callback.data.partial; - timer->callback.data.partial->pt_refcount++; - } else if (timer->callback.type == kCallbackFuncref) { - di->di_tv.v_type = VAR_FUNC; - di->di_tv.vval.v_string = vim_strsave(timer->callback.data.funcref); - } + callback_put(&timer->callback, &di->di_tv); } void add_timer_info_all(typval_T *rettv) { - tv_list_alloc_ret(rettv, timers->table->n_occupied); + tv_list_alloc_ret(rettv, map_size(&timers)); timer_T *timer; - map_foreach_value(timers, timer, { + map_foreach_value(&timers, timer, { if (!timer->stopped) { add_timer_info(rettv, timer); } @@ -7418,7 +7629,7 @@ uint64_t timer_start(const long timeout, timer->tw.blockable = true; time_watcher_start(&timer->tw, timer_due_cb, timeout, timeout); - pmap_put(uint64_t)(timers, timer->timer_id, timer); + pmap_put(uint64_t)(&timers, timer->timer_id, timer); return timer->timer_id; } @@ -7440,7 +7651,7 @@ static void timer_close_cb(TimeWatcher *tw, void *data) timer_T *timer = (timer_T *)data; multiqueue_free(timer->tw.events); callback_free(&timer->callback); - pmap_del(uint64_t)(timers, timer->timer_id); + pmap_del(uint64_t)(&timers, timer->timer_id); timer_decref(timer); } @@ -7454,7 +7665,7 @@ static void timer_decref(timer_T *timer) void timer_stop_all(void) { timer_T *timer; - map_foreach_value(timers, timer, { + map_foreach_value(&timers, timer, { timer_stop(timer); }) } @@ -8156,7 +8367,7 @@ void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) /// /// @param[in] idx Index of variable to set. /// @param[in] val Value to set to. Will be copied. -/// @param[in] len Legth of that value or -1 in which case strlen() will be +/// @param[in] len Length of that value or -1 in which case strlen() will be /// used. void set_vim_var_string(const VimVarIndex idx, const char *const val, const ptrdiff_t len) @@ -8399,13 +8610,23 @@ static bool tv_is_luafunc(typval_T *tv) return tv->v_type == VAR_PARTIAL && is_luafunc(tv->vval.v_partial); } -/// check the function name after "v:lua." -int check_luafunc_name(const char *str, bool paren) +/// Skips one character past the end of the name of a v:lua function. +/// @param p Pointer to the char AFTER the "v:lua." prefix. +/// @return Pointer to the char one past the end of the function's name. +const char *skip_luafunc_name(const char *p) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - const char *p = str; while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') { p++; } + return p; +} + +/// check the function name after "v:lua." +int check_luafunc_name(const char *const str, const bool paren) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *const p = skip_luafunc_name(str); if (*p != (paren ? '(' : NUL)) { return 0; } else { @@ -8413,24 +8634,26 @@ int check_luafunc_name(const char *str, bool paren) } } -/// Handle expr[expr], expr[expr:expr] subscript and .name lookup. -/// Also handle function call with Funcref variable: func(expr) -/// Can all be combined: dict.func(expr)[idx]['func'](expr) +/// Handle: +/// - expr[expr], expr[expr:expr] subscript +/// - ".name" lookup +/// - function call with Funcref variable: func(expr) +/// - method call: var->method() +/// +/// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len() int handle_subscript( const char **const arg, typval_T *rettv, - int evaluate, // do more than finding the end - int verbose // give error messages + int evaluate, // do more than finding the end + int verbose, // give error messages + const char_u *const start_leader, // start of '!' and '-' prefixes + const char_u **const end_leaderp // end of '!' and '-' prefixes ) { int ret = OK; - dict_T *selfdict = NULL; - const char_u *s; - int len; - typval_T functv; - int slen = 0; - bool lua = false; + dict_T *selfdict = NULL; + const char_u *lua_funcname = NULL; if (tv_is_luafunc(rettv)) { if (**arg != '.') { @@ -8439,55 +8662,29 @@ handle_subscript( } else { (*arg)++; - lua = true; - s = (char_u *)(*arg); - slen = check_luafunc_name(*arg, true); - if (slen == 0) { + lua_funcname = (char_u *)(*arg); + const int len = check_luafunc_name(*arg, true); + if (len == 0) { tv_clear(rettv); ret = FAIL; } - (*arg) += slen; + (*arg) += len; } } - + // "." is ".name" lookup when we found a dict. while (ret == OK - && (**arg == '[' - || (**arg == '.' && rettv->v_type == VAR_DICT) - || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) - && !ascii_iswhite(*(*arg - 1))) { + && (((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) + || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) + && !ascii_iswhite(*(*arg - 1))) + || (**arg == '-' && (*arg)[1] == '>'))) { if (**arg == '(') { - partial_T *pt = NULL; - // need to copy the funcref so that we can clear rettv - if (evaluate) { - functv = *rettv; - rettv->v_type = VAR_UNKNOWN; - - // Invoke the function. Recursive! - if (functv.v_type == VAR_PARTIAL) { - pt = functv.vval.v_partial; - if (!lua) { - s = partial_name(pt); - } - } else { - s = functv.vval.v_string; - } - } else { - s = (char_u *)""; - } - ret = get_func_tv(s, lua ? slen : -1, rettv, (char_u **)arg, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &len, evaluate, pt, selfdict); + ret = call_func_rettv((char_u **)arg, rettv, evaluate, selfdict, NULL, + lua_funcname); - // Clear the funcref afterwards, so that deleting it while - // evaluating the arguments is possible (see test55). - if (evaluate) { - tv_clear(&functv); - } - - /* Stop the expression evaluation when immediately aborting on - * error, or when an interrupt occurred or an exception was thrown - * but not caught. */ + // Stop the expression evaluation when immediately aborting on + // error, or when an interrupt occurred or an exception was thrown + // but not caught. if (aborting()) { if (ret == OK) { tv_clear(rettv); @@ -8496,6 +8693,21 @@ handle_subscript( } tv_dict_unref(selfdict); selfdict = NULL; + } else if (**arg == '-') { + // Expression "-1.0->method()" applies the leader "-" before + // applying ->. + if (evaluate && *end_leaderp > start_leader) { + ret = eval7_leader(rettv, start_leader, end_leaderp); + } + if (ret == OK) { + if ((*arg)[2] == '{') { + // expr->{lambda}() + ret = eval_lambda((char_u **)arg, rettv, evaluate, verbose); + } else { + // expr->name() + ret = eval_method((char_u **)arg, rettv, evaluate, verbose); + } + } } else { // **arg == '[' || **arg == '.' tv_dict_unref(selfdict); if (rettv->v_type == VAR_DICT) { @@ -8949,7 +9161,7 @@ static void set_var_const(const char *name, const size_t name_len, // existing variable, need to clear the value if (var_check_ro(v->di_flags, name, name_len) - || tv_check_lock(v->di_tv.v_lock, name, name_len)) { + || var_check_lock(v->di_tv.v_lock, name, name_len)) { return; } @@ -8962,7 +9174,7 @@ static void set_var_const(const char *name, const size_t name_len, const char *const val = tv_get_string(tv); // Careful: when assigning to v:errmsg and tv_get_string() - // causes an error message the variable will alrady be set. + // causes an error message the variable will already be set. if (v->di_tv.vval.v_string == NULL) { v->di_tv.vval.v_string = (char_u *)xstrdup(val); } @@ -9280,6 +9492,7 @@ void ex_echo(exarg_T *eap) bool atstart = true; bool need_clear = true; const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; if (eap->skip) ++emsg_skip; @@ -9294,7 +9507,8 @@ void ex_echo(exarg_T *eap) // Report the invalid expression unless the expression evaluation // has been cancelled due to an aborting error, an interrupt, or an // exception. - if (!aborting() && did_emsg == did_emsg_before) { + if (!aborting() && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { EMSG2(_(e_invexpr2), p); } need_clr_eos = false; @@ -10415,19 +10629,11 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; tv_list_ref(arguments); - int dummy; - (void)call_func((const char_u *)func, - name_len, - &rettv, - 2, - argvars, - NULL, - curwin->w_cursor.lnum, - curwin->w_cursor.lnum, - &dummy, - true, - NULL, - NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + (void)call_func((const char_u *)func, name_len, &rettv, 2, argvars, &funcexe); tv_list_unref(arguments); // Restore caller scope information @@ -10785,7 +10991,9 @@ bool var_exists(const char *var) n = get_var_tv(name, len, &tv, NULL, false, true) == OK; if (n) { // Handle d.key, l[idx], f(expr). - n = handle_subscript(&var, &tv, true, false) == OK; + n = handle_subscript(&var, &tv, true, false, (const char_u *)name, + (const char_u **)&name) + == OK; if (n) { tv_clear(&tv); } diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 33c6fae5cf..a7242ba73a 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -5,6 +5,9 @@ -- args Number of arguments, list with maximum and minimum number of arguments -- or list with a minimum number of arguments only. Defaults to zero -- arguments. +-- base For methods: the argument to use as the base argument (1-indexed): +-- base->method() +-- Defaults to BASE_NONE (function cannot be used as a method). -- func Name of the C function which implements the VimL function. Defaults to -- `f_{funcname}`. @@ -12,111 +15,115 @@ local varargs = function(nr) return {nr} end +-- Usable with the base key: use the last function argument as the method base. +-- Value is from funcs.h file. "BASE_" prefix is omitted. +local LAST = "BASE_LAST" + return { funcs={ - abs={args=1}, - acos={args=1, func="float_op_wrapper", data="&acos"}, -- WJMc - add={args=2}, - ['and']={args=2}, + abs={args=1, base=1}, + acos={args=1, base=1, func="float_op_wrapper", data="&acos"}, -- WJMc + add={args=2, base=1}, + ['and']={args=2, base=1}, api_info={}, - append={args=2}, - appendbufline={args=3}, + append={args=2, base=LAST}, + appendbufline={args=3, base=LAST}, argc={args={0, 1}}, argidx={}, arglistid={args={0, 2}}, argv={args={0, 2}}, - asin={args=1, func="float_op_wrapper", data="&asin"}, -- WJMc - assert_beeps={args={1}}, - assert_equal={args={2, 3}}, - assert_equalfile={args={2, 3}}, + asin={args=1, base=1, func="float_op_wrapper", data="&asin"}, -- WJMc + assert_beeps={args={1}, base=1}, + assert_equal={args={2, 3}, base=2}, + assert_equalfile={args={2, 3}, base=1}, assert_exception={args={1, 2}}, - assert_fails={args={1, 3}}, - assert_false={args={1, 2}}, - assert_inrange={args={3, 4}}, - assert_match={args={2, 3}}, + assert_fails={args={1, 3}, base=1}, + assert_false={args={1, 2}, base=1}, + assert_inrange={args={3, 4}, base=3}, + assert_match={args={2, 3}, base=2}, assert_nobeep={args={1}}, - assert_notequal={args={2, 3}}, - assert_notmatch={args={2, 3}}, - assert_report={args=1}, - assert_true={args={1, 2}}, - atan={args=1, func="float_op_wrapper", data="&atan"}, - atan2={args=2}, + assert_notequal={args={2, 3}, base=2}, + assert_notmatch={args={2, 3}, base=2}, + assert_report={args=1, base=1}, + assert_true={args={1, 2}, base=1}, + atan={args=1, base=1, func="float_op_wrapper", data="&atan"}, + atan2={args=2, base=1}, browse={args=4}, browsedir={args=2}, - bufadd={args=1}, - bufexists={args=1}, - buffer_exists={args=1, func='f_bufexists'}, -- obsolete + bufadd={args=1, base=1}, + bufexists={args=1, base=1}, + buffer_exists={args=1, base=1, func='f_bufexists'}, -- obsolete buffer_name={args={0, 1}, func='f_bufname'}, -- obsolete buffer_number={args={0, 1}, func='f_bufnr'}, -- obsolete - buflisted={args=1}, - bufload={args=1}, - bufloaded={args=1}, - bufname={args={0, 1}}, - bufnr={args={0, 2}}, - bufwinid={args=1}, - bufwinnr={args=1}, - byte2line={args=1}, - byteidx={args=2}, - byteidxcomp={args=2}, - call={args={2, 3}}, - ceil={args=1, func="float_op_wrapper", data="&ceil"}, + buflisted={args=1, base=1}, + bufload={args=1, base=1}, + bufloaded={args=1, base=1}, + bufname={args={0, 1}, base=1}, + bufnr={args={0, 2}, base=1}, + bufwinid={args=1, base=1}, + bufwinnr={args=1, base=1}, + byte2line={args=1, base=1}, + byteidx={args=2, base=1}, + byteidxcomp={args=2, base=1}, + call={args={2, 3}, base=1}, + ceil={args=1, base=1, func="float_op_wrapper", data="&ceil"}, changenr={}, chanclose={args={1, 2}}, chansend={args=2}, - char2nr={args={1, 2}}, + char2nr={args={1, 2}, base=1}, charidx={args={2, 3}}, - cindent={args=1}, - clearmatches={args={0, 1}}, - col={args=1}, - complete={args=2}, - complete_add={args=1}, + cindent={args=1, base=1}, + clearmatches={args={0, 1}, base=1}, + col={args=1, base=1}, + complete={args=2, base=2}, + complete_add={args=1, base=1}, complete_check={}, - complete_info={args={0, 1}}, - confirm={args={1, 4}}, - copy={args=1}, - cos={args=1, func="float_op_wrapper", data="&cos"}, - cosh={args=1, func="float_op_wrapper", data="&cosh"}, - count={args={2, 4}}, + complete_info={args={0, 1}, base=1}, + confirm={args={1, 4}, base=1}, + copy={args=1, base=1}, + cos={args=1, base=1, func="float_op_wrapper", data="&cos"}, + cosh={args=1, base=1, func="float_op_wrapper", data="&cosh"}, + count={args={2, 4}, base=1}, cscope_connection={args={0, 3}}, ctxget={args={0, 1}}, ctxpop={}, ctxpush={args={0, 1}}, ctxset={args={1, 2}}, ctxsize={}, - cursor={args={1, 3}}, - debugbreak={args={1, 1}}, - deepcopy={args={1, 2}}, - delete={args={1,2}}, - deletebufline={args={2,3}}, + cursor={args={1, 3}, base=1}, + debugbreak={args={1, 1}, base=1}, + deepcopy={args={1, 2}, base=1}, + delete={args={1,2}, base=1}, + deletebufline={args={2,3}, base=1}, dictwatcheradd={args=3}, dictwatcherdel={args=3}, did_filetype={}, - diff_filler={args=1}, - diff_hlID={args=2}, - empty={args=1}, + diff_filler={args=1, base=1}, + diff_hlID={args=2, base=1}, + empty={args=1, base=1}, environ={}, escape={args=2}, - eval={args=1}, + eval={args=1, base=1}, eventhandler={}, executable={args=1}, execute={args={1, 2}}, exepath={args=1}, exists={args=1}, - exp={args=1, func="float_op_wrapper", data="&exp"}, + exp={args=1, base=1, func="float_op_wrapper", data="&exp"}, expand={args={1, 3}}, expandcmd={args=1}, - extend={args={2, 3}}, + extend={args={2, 3}, base=1}, feedkeys={args={1, 2}}, file_readable={args=1, func='f_filereadable'}, -- obsolete filereadable={args=1}, filewritable={args=1}, - filter={args=2}, + filter={args=2, base=1}, finddir={args={1, 3}}, findfile={args={1, 3}}, flatten={args={1, 2}}, - float2nr={args=1}, - floor={args=1, func="float_op_wrapper", data="&floor"}, - fmod={args=2}, + float2nr={args=1, base=1}, + floor={args=1, base=1, func="float_op_wrapper", data="&floor"}, + fmod={args=2, base=1}, fnameescape={args=1}, fnamemodify={args=2}, foldclosed={args=1}, @@ -128,7 +135,7 @@ return { funcref={args={1, 3}}, ['function']={args={1, 3}}, garbagecollect={args={0, 1}}, - get={args={2, 3}}, + get={args={2, 3}, base=1}, getbufinfo={args={0, 1}}, getbufline={args={2, 3}}, getbufvar={args={2, 3}}, @@ -136,6 +143,7 @@ return { getchar={args={0, 1}}, getcharmod={}, getcharsearch={}, + getcharstr={args={0, 1}}, getcmdline={}, getcmdpos={}, getcmdtype={}, @@ -172,7 +180,7 @@ return { glob2regpat={args=1}, globpath={args={2, 5}}, has={args=1}, - has_key={args=2}, + has_key={args=2, base=1}, haslocaldir={args={0,2}}, hasmapto={args={1, 3}}, highlightID={args=1, func='f_hlID'}, -- obsolete @@ -186,22 +194,22 @@ return { hostname={}, iconv={args=3}, indent={args=1}, - index={args={2, 4}}, + index={args={2, 4}, base=1}, input={args={1, 3}}, inputdialog={args={1, 3}}, inputlist={args=1}, inputrestore={}, inputsave={}, inputsecret={args={1, 2}}, - insert={args={2, 3}}, + insert={args={2, 3}, base=1}, interrupt={args=0}, - invert={args=1}, + invert={args=1, base=1}, isdirectory={args=1}, - isinf={args=1}, + isinf={args=1, base=1}, islocked={args=1}, - isnan={args=1}, + isnan={args=1, base=1}, id={args=1}, - items={args=1}, + items={args=1, base=1}, jobclose={args={1, 2}, func="f_chanclose"}, jobpid={args=1}, jobresize={args=3}, @@ -209,12 +217,12 @@ return { jobstart={args={1, 2}}, jobstop={args=1}, jobwait={args={1, 2}}, - join={args={1, 2}}, + join={args={1, 2}, base=1}, json_decode={args=1}, json_encode={args=1}, - keys={args=1}, + keys={args=1, base=1}, last_buffer_nr={}, -- obsolete - len={args=1}, + len={args=1, base=1}, libcall={args=3}, libcallnr={args=3}, line={args={1, 2}}, @@ -222,10 +230,10 @@ return { lispindent={args=1}, list2str={args={1, 2}}, localtime={}, - log={args=1, func="float_op_wrapper", data="&log"}, - log10={args=1, func="float_op_wrapper", data="&log10"}, + log={args=1, base=1, func="float_op_wrapper", data="&log"}, + log10={args=1, base=1, func="float_op_wrapper", data="&log10"}, luaeval={args={1, 2}}, - map={args=2}, + map={args=2, base=1}, maparg={args={1, 4}}, mapcheck={args={1, 3}}, match={args={2, 4}}, @@ -237,20 +245,20 @@ return { matchlist={args={2, 4}}, matchstr={args={2, 4}}, matchstrpos={args={2,4}}, - max={args=1}, + max={args=1, base=1}, menu_get={args={1, 2}}, - min={args=1}, + min={args=1, base=1}, mkdir={args={1, 3}}, mode={args={0, 1}}, msgpackdump={args=1}, msgpackparse={args=1}, nextnonblank={args=1}, nr2char={args={1, 2}}, - ['or']={args=2}, + ['or']={args=2, base=1}, pathshorten={args=1}, - pow={args=2}, + pow={args=2, base=1}, prevnonblank={args=1}, - printf={args=varargs(1)}, + printf={args=varargs(1), base=2}, prompt_getprompt={args=1}, prompt_setcallback={args={2, 2}}, prompt_setinterrupt={args={2, 2}}, @@ -269,12 +277,12 @@ return { reltime={args={0, 2}}, reltimefloat={args=1}, reltimestr={args=1}, - remove={args={2, 3}}, + remove={args={2, 3}, base=1}, rename={args=2}, - ['repeat']={args=2}, + ['repeat']={args=2, base=1}, resolve={args=1}, - reverse={args=1}, - round={args=1, func="float_op_wrapper", data="&round"}, + reverse={args=1, base=1}, + round={args=1, base=1, func="float_op_wrapper", data="&round"}, rpcnotify={args=varargs(2)}, rpcrequest={args=varargs(2)}, rpcstart={args={1, 2}}, @@ -323,51 +331,51 @@ return { sign_unplace={args={1, 2}}, sign_unplacelist={args={1}}, simplify={args=1}, - sin={args=1, func="float_op_wrapper", data="&sin"}, - sinh={args=1, func="float_op_wrapper", data="&sinh"}, + sin={args=1, base=1, func="float_op_wrapper", data="&sin"}, + sinh={args=1, base=1, func="float_op_wrapper", data="&sinh"}, sockconnect={args={2,3}}, - sort={args={1, 3}}, + sort={args={1, 3}, base=1}, soundfold={args=1}, stdioopen={args=1}, spellbadword={args={0, 1}}, spellsuggest={args={1, 3}}, - split={args={1, 3}}, - sqrt={args=1, func="float_op_wrapper", data="&sqrt"}, + split={args={1, 3}, base=1}, + sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"}, stdpath={args=1}, - str2float={args=1}, - str2list={args={1, 2}}, - str2nr={args={1, 2}}, + str2float={args=1, base=1}, + str2list={args={1, 2}, base=1}, + str2nr={args={1, 3}}, strcharpart={args={2, 3}}, strchars={args={1,2}}, strdisplaywidth={args={1, 2}}, strftime={args={1, 2}}, strgetchar={args={2, 2}}, stridx={args={2, 3}}, - string={args=1}, - strlen={args=1}, + string={args=1, base=1}, + strlen={args=1, base=1}, strpart={args={2, 4}}, strptime={args=2}, strridx={args={2, 3}}, - strtrans={args=1}, - strwidth={args=1}, + strtrans={args=1, base=1}, + strwidth={args=1, base=1}, submatch={args={1, 2}}, - substitute={args=4}, + substitute={args=4, base=1}, swapinfo={args={1}}, swapname={args={1}}, synID={args=3}, - synIDattr={args={2, 3}}, - synIDtrans={args=1}, + synIDattr={args={2, 3}, base=1}, + synIDtrans={args=1, base=1}, synconcealed={args=2}, synstack={args=2}, - system={args={1, 2}}, - systemlist={args={1, 3}}, + system={args={1, 2}, base=1}, + systemlist={args={1, 3}, base=1}, tabpagebuflist={args={0, 1}}, tabpagenr={args={0, 1}}, tabpagewinnr={args={1, 2}}, tagfiles={}, taglist={args={1, 2}}, - tan={args=1, func="float_op_wrapper", data="&tan"}, - tanh={args=1, func="float_op_wrapper", data="&tanh"}, + tan={args=1, base=1, func="float_op_wrapper", data="&tan"}, + tanh={args=1, base=1, func="float_op_wrapper", data="&tanh"}, tempname={}, termopen={args={1, 2}}, test_garbagecollect_now={}, @@ -381,12 +389,12 @@ return { toupper={args=1}, tr={args=3}, trim={args={1,3}}, - trunc={args=1, func="float_op_wrapper", data="&trunc"}, - type={args=1}, + trunc={args=1, base=1, func="float_op_wrapper", data="&trunc"}, + type={args=1, base=1}, undofile={args=1}, undotree={}, - uniq={args={1, 3}}, - values={args=1}, + uniq={args={1, 3}, base=1}, + values={args=1, base=1}, virtcol={args=1}, visualmode={args={0, 1}}, wait={args={2,3}}, @@ -400,7 +408,7 @@ return { win_id2win={args=1}, win_screenpos={args=1}, win_splitmove={args={2, 3}}, - winbufnr={args=1}, + winbufnr={args=1, base=1}, wincol={}, windowsversion={}, winheight={args=1}, @@ -413,6 +421,6 @@ return { winwidth={args=1}, wordcount={}, writefile={args={2, 3}}, - xor={args=2}, + xor={args=2, base=1}, }, } diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index bd4dc87d31..89e1f04bfd 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -437,7 +437,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, t += 4; uvarnumber_T ch; vim_str2nr((char_u *)ubuf, NULL, NULL, - STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4); + STR2NR_HEX | STR2NR_FORCE, NULL, &ch, 4, true); if (ch == 0) { hasnul = true; } @@ -611,8 +611,8 @@ parse_json_number_check: // Convert integer varnumber_T nr; int num_len; - vim_str2nr((char_u *) s, NULL, &num_len, 0, &nr, NULL, (int) (p - s)); - if ((int) exp_num_len != num_len) { + vim_str2nr((char_u *)s, NULL, &num_len, 0, &nr, NULL, (int)(p - s), true); + if ((int)exp_num_len != num_len) { emsgf(_("E685: internal error: while converting number \"%.*s\" " "to integer vim_str2nr consumed %i bytes in place of %zu"), (int) exp_num_len, s, num_len, exp_num_len); diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index bbba9d12f2..8ac2c3b8eb 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -16,7 +16,7 @@ static char *e_letwrong = N_("E734: Wrong variable type for %s="); char *e_listidx = N_("E684: list index out of range: %" PRId64); -/// Hanle tv1 += tv2, -=, *=, /=, %=, .= +/// Handle tv1 += tv2, -=, *=, /=, %=, .= /// /// @param[in,out] tv1 First operand, modified typval. /// @param[in] tv2 Second operand. diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index ce09d268ea..801b0f9d1c 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -47,6 +47,7 @@ #include "nvim/os/input.h" #include "nvim/os/shell.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" @@ -174,6 +175,53 @@ const VimLFuncDef *find_internal_func(const char *const name) return find_internal_func_gperf(name, len); } +int call_internal_func(const char_u *const fname, const int argcount, + typval_T *const argvars, typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL +{ + const VimLFuncDef *const fdef = find_internal_func((const char *)fname); + if (fdef == NULL) { + return ERROR_UNKNOWN; + } else if (argcount < fdef->min_argc) { + return ERROR_TOOFEW; + } else if (argcount > fdef->max_argc) { + return ERROR_TOOMANY; + } + argvars[argcount].v_type = VAR_UNKNOWN; + fdef->func(argvars, rettv, fdef->data); + return ERROR_NONE; +} + +/// Invoke a method for base->method(). +int call_internal_method(const char_u *const fname, const int argcount, + typval_T *const argvars, typval_T *const rettv, + typval_T *const basetv) + FUNC_ATTR_NONNULL_ALL +{ + const VimLFuncDef *const fdef = find_internal_func((const char *)fname); + if (fdef == NULL) { + return ERROR_UNKNOWN; + } else if (fdef->base_arg == BASE_NONE) { + return ERROR_NOTMETHOD; + } else if (argcount + 1 < fdef->min_argc) { + return ERROR_TOOFEW; + } else if (argcount + 1 > fdef->max_argc) { + return ERROR_TOOMANY; + } + + typval_T argv[MAX_FUNC_ARGS + 1]; + const ptrdiff_t base_index + = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; + memcpy(argv, argvars, base_index * sizeof(typval_T)); + argv[base_index] = *basetv; + memcpy(argv + base_index + 1, argvars + base_index, + (argcount - base_index) * sizeof(typval_T)); + argv[argcount + 1].v_type = VAR_UNKNOWN; + + fdef->func(argv, rettv, fdef->data); + return ERROR_NONE; +} + /* * Return TRUE for a non-zero Number and a non-empty String. */ @@ -268,7 +316,8 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; // Default: failed. if (argvars[0].v_type == VAR_LIST) { list_T *const l = argvars[0].vval.v_list; - if (!tv_check_lock(tv_list_locked(l), N_("add() argument"), TV_TRANSLATE)) { + if (!var_check_lock(tv_list_locked(l), N_("add() argument"), + TV_TRANSLATE)) { tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } @@ -1054,8 +1103,10 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (virtual_active() && fp == &curwin->w_cursor) { char_u *p = get_cursor_pos_ptr(); - if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p, - curwin->w_virtcol - curwin->w_cursor.coladd)) { + if (curwin->w_cursor.coladd + >= (colnr_T)win_chartabsize(curwin, p, + (curwin->w_virtcol + - curwin->w_cursor.coladd))) { int l; if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL) @@ -1077,10 +1128,11 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - /* Check for undo allowed here, because if something was already inserted - * the line was already saved for undo and this check isn't done. */ - if (!undo_allowed()) + // Check for undo allowed here, because if something was already inserted + // the line was already saved for undo and this check isn't done. + if (!undo_allowed(curbuf)) { return; + } if (argvars[1].v_type != VAR_LIST) { EMSG(_(e_invarg)); @@ -1920,7 +1972,7 @@ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (expr_start != NULL && !aborting()) { EMSG2(_(e_invexpr2), expr_start); } - need_clr_eos = FALSE; + need_clr_eos = false; rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; } else if (*s != NUL) { @@ -2277,9 +2329,9 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr) list = argvars[0].vval.v_list; if (list != NULL - && !tv_check_lock(tv_list_locked(list), - N_("flatten() argument"), - TV_TRANSLATE) + && !var_check_lock(tv_list_locked(list), + N_("flatten() argument"), + TV_TRANSLATE) && tv_list_flatten(list, maxdepth) == OK) { tv_copy(&argvars[0], rettv); } @@ -2299,7 +2351,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; - if (!tv_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { + if (!var_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { before = (long)tv_get_number_chk(&argvars[2], &error); @@ -2328,13 +2380,13 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *const d1 = argvars[0].vval.v_dict; dict_T *const d2 = argvars[1].vval.v_dict; if (d1 == NULL) { - const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); + const bool locked = var_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); (void)locked; assert(locked == true); } else if (d2 == NULL) { // Do nothing tv_copy(&argvars[0], rettv); - } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { + } else if (!var_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { const char *action = "force"; // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { @@ -3026,10 +3078,9 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "getchar()" function - */ -static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +// "getchar()" and "getcharstr()" functions +static void getchar_common(typval_T *argvars, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL { varnumber_T n; bool error = false; @@ -3096,6 +3147,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { i += utf_char2bytes(n, temp + i); } + assert(i < 10); temp[i++] = NUL; rettv->v_type = VAR_STRING; rettv->vval.v_string = vim_strsave(temp); @@ -3104,15 +3156,14 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) int row = mouse_row; int col = mouse_col; int grid = mouse_grid; - win_T *win; linenr_T lnum; win_T *wp; int winnr = 1; if (row >= 0 && col >= 0) { - /* Find the window at the mouse coordinates and compute the - * text position. */ - win = mouse_find_win(&grid, &row, &col); + // Find the window at the mouse coordinates and compute the + // text position. + win_T *const win = mouse_find_win(&grid, &row, &col); if (win == NULL) { return; } @@ -3128,6 +3179,32 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +// "getchar()" function +static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getchar_common(argvars, rettv); +} + +// "getcharstr()" function +static void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getchar_common(argvars, rettv); + + if (rettv->v_type == VAR_NUMBER) { + char_u temp[7]; // mbyte-char: 6, NUL: 1 + const varnumber_T n = rettv->vval.v_number; + int i = 0; + + if (n != 0) { + i += utf_char2bytes(n, temp); + } + assert(i < 7); + temp[i++] = NUL; + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(temp); + } +} + /* * "getcharmod()" function */ @@ -4845,8 +4922,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "insert()"); - } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - N_("insert() argument"), TV_TRANSLATE)) { + } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("insert() argument"), TV_TRANSLATE)) { long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); @@ -5180,6 +5257,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool pty = false; bool clear_env = false; bool overlapped = false; + ChannelStdinMode stdin_mode = kChannelStdinPipe; CallbackReader on_stdout = CALLBACK_READER_INIT, on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; @@ -5194,6 +5272,17 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) clear_env = tv_dict_get_number(job_opts, "clear_env") != 0; overlapped = tv_dict_get_number(job_opts, "overlapped") != 0; + char *s = tv_dict_get_string(job_opts, "stdin", false); + if (s) { + if (!strncmp(s, "null", NUMBUFLEN)) { + stdin_mode = kChannelStdinNull; + } else if (!strncmp(s, "pipe", NUMBUFLEN)) { + // Nothing to do, default value + } else { + EMSG3(_(e_invargNval), "stdin", s); + } + } + if (pty && rpc) { EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); shell_free_argv(argv); @@ -5250,8 +5339,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) env = create_environment(job_env, clear_env, pty, term_name); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, - rpc, overlapped, detach, cwd, width, height, - env, &rettv->vval.v_number); + rpc, overlapped, detach, stdin_mode, cwd, + width, height, env, &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); } @@ -5315,14 +5404,19 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER_CONST(args, arg, { Channel *chan = NULL; if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER - || !(chan = find_job(TV_LIST_ITEM_TV(arg)->vval.v_number, false))) { + || !(chan = find_channel(TV_LIST_ITEM_TV(arg)->vval.v_number)) + || chan->streamtype != kChannelStreamProc) { + jobs[i] = NULL; // Invalid job. + } else if (process_is_stopped(&chan->stream.proc)) { + // Job is stopped but not fully destroyed. + // Ensure all callbacks on its event queue are executed. #15402 + process_wait(&chan->stream.proc, -1, NULL); jobs[i] = NULL; // Invalid job. } else { jobs[i] = chan; channel_incref(chan); if (chan->stream.proc.status < 0) { - // Process any pending events on the job's queue before temporarily - // replacing it. + // Flush any events in the job's queue before temporarily replacing it. multiqueue_process_events(chan->events); multiqueue_replace_parent(chan->events, waiting_jobs); } @@ -7079,7 +7173,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { EMSG2(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL - && !tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { + && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { const char *key = tv_get_string_chk(&argvars[1]); if (key != NULL) { di = tv_dict_find(d, key, -1); @@ -7098,8 +7192,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listdictarg), "remove()"); - } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - arg_errmsg, TV_TRANSLATE)) { + } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + arg_errmsg, TV_TRANSLATE)) { bool error = false; idx = tv_get_number_chk(&argvars[1], &error); @@ -7374,8 +7468,8 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l; if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "reverse()"); - } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - N_("reverse() argument"), TV_TRANSLATE)) { + } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("reverse() argument"), TV_TRANSLATE)) { tv_list_reverse(l); tv_list_set_ret(rettv, l); } @@ -7731,8 +7825,9 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT, CALLBACK_READER_INIT, CALLBACK_NONE, - false, true, false, false, NULL, 0, 0, - NULL, &rettv->vval.v_number); + false, true, false, false, + kChannelStdinPipe, NULL, 0, 0, NULL, + &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); } @@ -9377,7 +9472,6 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) int res; typval_T rettv; typval_T argv[3]; - int dummy; const char *func_name; partial_T *partial = sortinfo->item_compare_partial; @@ -9401,10 +9495,11 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - res = call_func((const char_u *)func_name, - -1, - &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, - partial, sortinfo->item_compare_selfdict); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = sortinfo->item_compare_selfdict; + res = call_func((const char_u *)func_name, -1, &rettv, 2, argv, &funcexe); tv_clear(&argv[0]); tv_clear(&argv[1]); @@ -9462,7 +9557,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { list_T *const l = argvars[0].vval.v_list; - if (tv_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { + if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { goto theend; } tv_list_set_ret(rettv, l); @@ -9661,6 +9756,18 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *word = ""; hlf_T attr = HLF_COUNT; size_t len = 0; + const int wo_spell_save = curwin->w_p_spell; + + if (!curwin->w_p_spell) { + did_set_spelllang(curwin); + curwin->w_p_spell = true; + } + + if (*curwin->w_s->b_p_spl == NUL) { + EMSG(_(e_no_spell)); + curwin->w_p_spell = wo_spell_save; + return; + } if (argvars[0].v_type == VAR_UNKNOWN) { // Find the start and length of the badly spelled word. @@ -9669,7 +9776,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) word = (char *)get_cursor_pos_ptr(); curwin->w_set_curswant = true; } - } else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) { + } else if (*curbuf->b_s.b_p_spl != NUL) { const char *str = tv_get_string_chk(&argvars[0]); int capcol = -1; @@ -9687,6 +9794,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } + curwin->w_p_spell = wo_spell_save; assert(len <= INT_MAX); tv_list_alloc_ret(rettv, 2); @@ -9708,8 +9816,20 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) int maxcount; garray_T ga = GA_EMPTY_INIT_VALUE; bool need_capital = false; + const int wo_spell_save = curwin->w_p_spell; + + if (!curwin->w_p_spell) { + did_set_spelllang(curwin); + curwin->w_p_spell = true; + } + + if (*curwin->w_s->b_p_spl == NUL) { + EMSG(_(e_no_spell)); + curwin->w_p_spell = wo_spell_save; + return; + } - if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { + if (*curwin->w_s->b_p_spl != NUL) { const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { maxcount = tv_get_number_chk(&argvars[1], &typeerr); @@ -9736,6 +9856,7 @@ f_spellsuggest_return: tv_list_append_allocated_string(rettv->vval.v_list, p); } ga_clear(&ga); + curwin->w_p_spell = wo_spell_save; } static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -9877,7 +9998,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int base = 10; varnumber_T n; - int what; + int what = 0; if (argvars[1].v_type != VAR_UNKNOWN) { base = tv_get_number(&argvars[1]); @@ -9885,6 +10006,9 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_invarg)); return; } + if (argvars[2].v_type != VAR_UNKNOWN && tv_get_number(&argvars[2])) { + what |= STR2NR_QUOTE; + } } char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); @@ -9894,22 +10018,20 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } switch (base) { case 2: { - what = STR2NR_BIN | STR2NR_FORCE; + what |= STR2NR_BIN | STR2NR_FORCE; break; } case 8: { - what = STR2NR_OCT | STR2NR_FORCE; + what |= STR2NR_OCT | STR2NR_OOCT | STR2NR_FORCE; break; } case 16: { - what = STR2NR_HEX | STR2NR_FORCE; + what |= STR2NR_HEX | STR2NR_FORCE; break; } - default: { - what = 0; - } } - vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); + vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false); + // Text after the number is silently ignored. if (isneg) { rettv->vval.v_number = -n; } else { @@ -10822,10 +10944,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) const bool rpc = false; const bool overlapped = false; const bool detach = false; + ChannelStdinMode stdin_mode = kChannelStdinPipe; uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin)); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, - pty, rpc, overlapped, detach, cwd, - term_width, curwin->w_height_inner, + pty, rpc, overlapped, detach, stdin_mode, + cwd, term_width, curwin->w_height_inner, env, &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { return; @@ -11325,6 +11448,9 @@ static void f_win_gettype(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = vim_strsave((char_u *)"popup"); } else if (wp == curwin && cmdwin_type != 0) { rettv->vval.v_string = vim_strsave((char_u *)"command"); + } else if (bt_quickfix(wp->w_buffer)) { + rettv->vval.v_string = vim_strsave((char_u *)(wp->w_llist_ref != NULL ? + "loclist" : "quickfix")); } } diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index a343290734..c6a0cb959e 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -9,11 +9,16 @@ typedef void (*FunPtr)(void); /// Prototype of C function that implements VimL function typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data); +/// Special flags for base_arg @see VimLFuncDef +#define BASE_NONE 0 ///< Not a method (no base argument). +#define BASE_LAST UINT8_MAX ///< Use the last argument as the method base. + /// Structure holding VimL function definition typedef struct fst { char *name; ///< Name of the function. uint8_t min_argc; ///< Minimal number of arguments. uint8_t max_argc; ///< Maximal number of arguments. + uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST. VimLFunc func; ///< Function implementation. FunPtr data; ///< Userdata for function implementation. } VimLFuncDef; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 61de83fc21..22b3bf026b 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -836,7 +836,7 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) return retval; } -/// Chech whether two lists are equal +/// Check whether two lists are equal /// /// @param[in] l1 First list to compare. /// @param[in] l2 Second list to compare. @@ -1162,6 +1162,49 @@ void callback_free(Callback *callback) } } callback->type = kCallbackNone; + callback->data.funcref = NULL; +} + +/// Copy a callback into a typval_T. +void callback_put(Callback *cb, typval_T *tv) + FUNC_ATTR_NONNULL_ALL +{ + switch (cb->type) { + case kCallbackPartial: + tv->v_type = VAR_PARTIAL; + tv->vval.v_partial = cb->data.partial; + cb->data.partial->pt_refcount++; + break; + case kCallbackFuncref: + tv->v_type = VAR_FUNC; + tv->vval.v_string = vim_strsave(cb->data.funcref); + func_ref(cb->data.funcref); + break; + default: + tv->v_type = VAR_SPECIAL; + tv->vval.v_special = kSpecialVarNull; + break; + } +} + +// Copy callback from "src" to "dest", incrementing the refcounts. +void callback_copy(Callback *dest, Callback *src) + FUNC_ATTR_NONNULL_ALL +{ + dest->type = src->type; + switch (src->type) { + case kCallbackPartial: + dest->data.partial = src->data.partial; + dest->data.partial->pt_refcount++; + break; + case kCallbackFuncref: + dest->data.funcref = vim_strsave(src->data.funcref); + func_ref(src->data.funcref); + break; + default: + dest->data.funcref = NULL; + break; + } } /// Remove watcher from a dictionary @@ -1951,7 +1994,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, } else if (*action == 'f' && di2 != di1) { typval_T oldtv; - if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) + if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { break; } @@ -2582,7 +2625,7 @@ bool tv_islocked(const typval_T *const tv) /// /// Also gives an error message when typval is locked. /// -/// @param[in] lock Lock status. +/// @param[in] tv Typval. /// @param[in] name Variable name, used in the error message. /// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate /// variable name and compute the length. Use #TV_CSTRING @@ -2596,10 +2639,37 @@ bool tv_islocked(const typval_T *const tv) /// gettext. /// /// @return true if variable is locked, false otherwise. -bool tv_check_lock(const VarLockStatus lock, const char *name, +bool tv_check_lock(const typval_T *tv, const char *name, size_t name_len) FUNC_ATTR_WARN_UNUSED_RESULT { + VarLockStatus lock = VAR_UNLOCKED; + + switch (tv->v_type) { + // case VAR_BLOB: + // if (tv->vval.v_blob != NULL) + // lock = tv->vval.v_blob->bv_lock; + // break; + case VAR_LIST: + if (tv->vval.v_list != NULL) { + lock = tv->vval.v_list->lv_lock; + } + break; + case VAR_DICT: + if (tv->vval.v_dict != NULL) { + lock = tv->vval.v_dict->dv_lock; + } + break; + default: + break; + } + return var_check_lock(tv->v_lock, name, name_len) + || (lock != VAR_UNLOCKED && var_check_lock(lock, name, name_len)); +} + +/// @return true if variable "name" is locked (immutable) +bool var_check_lock(VarLockStatus lock, const char *name, size_t name_len) +{ const char *error_message = NULL; switch (lock) { case VAR_UNLOCKED: { @@ -2920,7 +2990,8 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) case VAR_STRING: { varnumber_T n = 0; if (tv->vval.v_string != NULL) { - vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0); + vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, + false); } return n; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 2b4612016b..ef49fa1de6 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -55,7 +55,7 @@ enum ListLenSpecials { #define VARNUMBER_MAX INT64_MAX #define UVARNUMBER_MAX UINT64_MAX -/// Mimimal possible value of varnumber_T variable +/// Minimal possible value of varnumber_T variable #define VARNUMBER_MIN INT64_MIN /// %d printf format specifier for varnumber_T @@ -120,7 +120,7 @@ typedef enum { VAR_DICT, ///< Dictionary, .v_dict is used. VAR_FLOAT, ///< Floating-point value, .v_float is used. VAR_BOOL, ///< true, false - VAR_SPECIAL, ///< Special value (true, false, null), .v_special + VAR_SPECIAL, ///< Special value (null), .v_special ///< is used. VAR_PARTIAL, ///< Partial, .v_partial is used. } VarType; @@ -322,7 +322,7 @@ struct ufunc { int uf_prof_initialized; // Managing cfuncs cfunc_T uf_cb; ///< C function extension callback - cfunc_free_T uf_cb_free; ///< C function extesion free callback + cfunc_free_T uf_cb_free; ///< C function extension free callback void *uf_cb_state; ///< State of C function extension. // Profiling the function as a whole. int uf_tm_count; ///< nr of calls diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index f5d1b1e870..4184e4d922 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -414,12 +414,7 @@ get_func_tv( int len, // length of "name" or -1 to use strlen() typval_T *rettv, char_u **arg, // argument, pointing to the '(' - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - int *doesrange, // return: function handled range - int evaluate, - partial_T *partial, // for extra arguments - dict_T *selfdict // Dictionary for "self" + funcexe_T *funcexe // various values ) { char_u *argp; @@ -431,12 +426,13 @@ get_func_tv( * Get the arguments. */ argp = *arg; - while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { + while (argcount < MAX_FUNC_ARGS + - (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) { argp = skipwhite(argp + 1); // skip the '(' or ',' if (*argp == ')' || *argp == ',' || *argp == NUL) { break; } - if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) { + if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) { ret = FAIL; break; } @@ -463,9 +459,7 @@ get_func_tv( ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i]; } } - ret = call_func(name, len, rettv, argcount, argvars, NULL, - firstline, lastline, doesrange, evaluate, - partial, selfdict); + ret = call_func(name, len, rettv, argcount, argvars, funcexe); funcargs.ga_len -= i; } else if (!aborting()) { @@ -1367,7 +1361,6 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, { typval_T argv[MAX_FUNC_ARGS + 1]; int argc = 0; - int dummy; int r = 0; TV_LIST_ITER(args->vval.v_list, item, { @@ -1380,9 +1373,13 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]); }); - r = call_func(name, -1, rettv, argc, argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, partial, selfdict); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = selfdict; + r = call_func(name, -1, rettv, argc, argv, &funcexe); func_call_skip_call: // Free the arguments. @@ -1402,6 +1399,9 @@ static void user_func_error(int error, const char_u *name) case ERROR_UNKNOWN: emsg_funcname(N_("E117: Unknown function: %s"), name); break; + case ERROR_NOTMETHOD: + emsg_funcname(N_("E276: Cannot use function as a method: %s"), name); + break; case ERROR_DELETED: emsg_funcname(N_("E933: Function was deleted: %s"), name); break; @@ -1423,12 +1423,25 @@ static void user_func_error(int error, const char_u *name) } } +/// Used by call_func to add a method base (if any) to a function argument list +/// as the first argument. @see call_func +static void argv_add_base(typval_T *const basetv, typval_T **const argvars, + int *const argcount, typval_T *const new_argvars, + int *const argv_base) + FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5) +{ + if (basetv != NULL) { + // Method call: base->Method() + memmove(&new_argvars[1], *argvars, sizeof(typval_T) * (*argcount)); + new_argvars[0] = *basetv; + (*argcount)++; + *argvars = new_argvars; + *argv_base = 1; + } +} + /// Call a function with its resolved parameters /// -/// "argv_func", when not NULL, can be used to fill in arguments only when the -/// invoked function uses them. It is called like this: -/// new_argcount = argv_func(current_argcount, argv, called_func_argcount) -/// /// @return FAIL if function cannot be called, else OK (even if an error /// occurred while executing the function! Set `msg_list` to capture /// the error, see do_cmdline()). @@ -1440,15 +1453,9 @@ call_func( int argcount_in, // number of "argvars" typval_T *argvars_in, // vars for arguments, must have "argcount" // PLUS ONE elements! - ArgvFunc argv_func, // function to fill in argvars - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - int *doesrange, // [out] function handled range - bool evaluate, - partial_T *partial, // optional, can be NULL - dict_T *selfdict_in // Dictionary for "self" + funcexe_T *funcexe // more arguments ) - FUNC_ATTR_NONNULL_ARG(1, 3, 5, 9) + FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6) { int ret = FAIL; int error = ERROR_NONE; @@ -1459,9 +1466,12 @@ call_func( char_u *name = NULL; int argcount = argcount_in; typval_T *argvars = argvars_in; - dict_T *selfdict = selfdict_in; - typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" is not NULL + dict_T *selfdict = funcexe->selfdict; + typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or + // "funcexe->basetv" is not NULL int argv_clear = 0; + int argv_base = 0; + partial_T *partial = funcexe->partial; // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) // even when call_func() returns FAIL. @@ -1480,14 +1490,15 @@ call_func( fname = fname_trans_sid(name, fname_buf, &tofree, &error); } - *doesrange = false; + if (funcexe->doesrange != NULL) { + *funcexe->doesrange = false; + } if (partial != NULL) { // When the function has a partial with a dict and there is a dict // argument, use the dict argument. That is backwards compatible. // When the dict was bound explicitly use the one from the partial. - if (partial->pt_dict != NULL - && (selfdict_in == NULL || !partial->pt_auto)) { + if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) { selfdict = partial->pt_dict; } if (error == ERROR_NONE && partial->pt_argc > 0) { @@ -1506,7 +1517,7 @@ call_func( } } - if (error == ERROR_NONE && evaluate) { + if (error == ERROR_NONE && funcexe->evaluate) { char_u *rfname = fname; // Ignore "g:" before a function name. @@ -1521,7 +1532,12 @@ call_func( if (is_luafunc(partial)) { if (len > 0) { error = ERROR_NONE; + argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base); nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv); + } else { + // v:lua was called directly; show its name in the emsg + XFREE_CLEAR(name); + funcname = (const char_u *)"v:lua"; } } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) { // User defined function. @@ -1549,13 +1565,16 @@ call_func( cfunc_T cb = fp->uf_cb; error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state); } else if (fp != NULL) { - if (argv_func != NULL) { + if (funcexe->argv_func != NULL) { // postponed filling in the arguments, do it now - argcount = argv_func(argcount, argvars, argv_clear, - fp->uf_args.ga_len); + argcount = funcexe->argv_func(argcount, argvars, argv_clear, + fp->uf_args.ga_len); } - if (fp->uf_flags & FC_RANGE) { - *doesrange = true; + + argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base); + + if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) { + *funcexe->doesrange = true; } if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) { error = ERROR_TOOFEW; @@ -1565,25 +1584,20 @@ call_func( error = ERROR_DICT; } else { // Call the user function. - call_user_func(fp, argcount, argvars, rettv, firstline, lastline, + call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, + funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); error = ERROR_NONE; } } + } else if (funcexe->basetv != NULL) { + // expr->method(): Find the method name in the table, call its + // implementation with the base as one of the arguments. + error = call_internal_method(fname, argcount, argvars, rettv, + funcexe->basetv); } else { // Find the function name in the table, call its implementation. - const VimLFuncDef *const fdef = find_internal_func((const char *)fname); - if (fdef != NULL) { - if (argcount < fdef->min_argc) { - error = ERROR_TOOFEW; - } else if (argcount > fdef->max_argc) { - error = ERROR_TOOMANY; - } else { - argvars[argcount].v_type = VAR_UNKNOWN; - fdef->func(argvars, rettv, fdef->data); - error = ERROR_NONE; - } - } + error = call_internal_func(fname, argcount, argvars, rettv); } /* * The function call (or "FuncUndefined" autocommand sequence) might @@ -1607,9 +1621,11 @@ theend: user_func_error(error, (name != NULL) ? name : funcname); } + // clear the copies made from the partial while (argv_clear > 0) { - tv_clear(&argv[--argv_clear]); + tv_clear(&argv[--argv_clear + argv_base]); } + xfree(tofree); xfree(name); @@ -1902,7 +1918,7 @@ void ex_function(exarg_T *eap) char_u *line_to_free = NULL; int c; int saved_did_emsg; - int saved_wait_return = need_wait_return; + bool saved_wait_return = need_wait_return; char_u *name = NULL; char_u *p; char_u *arg; @@ -2455,13 +2471,13 @@ void ex_function(exarg_T *eap) goto erret; } if (fudi.fd_di == NULL) { - if (tv_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg, - TV_CSTRING)) { + if (var_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg, + TV_CSTRING)) { // Can't add a function to a locked dictionary goto erret; } - } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg, - TV_CSTRING)) { + } else if (var_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg, + TV_CSTRING)) { // Can't change an existing function if it is locked goto erret; } @@ -2901,7 +2917,7 @@ void ex_call(exarg_T *eap) int len; typval_T rettv; linenr_T lnum; - int doesrange; + bool doesrange; bool failed = false; funcdict_T fudi; partial_T *partial = NULL; @@ -2947,7 +2963,7 @@ void ex_call(exarg_T *eap) rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this. if (*startarg != '(') { - EMSG2(_("E107: Missing parentheses: %s"), eap->arg); + EMSG2(_(e_missingparen), eap->arg); goto end; } @@ -2965,15 +2981,22 @@ void ex_call(exarg_T *eap) curwin->w_cursor.coladd = 0; } arg = startarg; - if (get_func_tv(name, -1, &rettv, &arg, - eap->line1, eap->line2, &doesrange, - true, partial, fudi.fd_dict) == FAIL) { + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = eap->line1; + funcexe.lastline = eap->line2; + funcexe.doesrange = &doesrange; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = fudi.fd_dict; + if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) { failed = true; break; } // Handle a function returning a Funcref, Dictionary or List. - if (handle_subscript((const char **)&arg, &rettv, true, true) + if (handle_subscript((const char **)&arg, &rettv, true, true, + (const char_u *)name, (const char_u **)&name) == FAIL) { failed = true; break; @@ -3029,13 +3052,13 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) current_funccal->returned = false; } - /* - * Cleanup (and inactivate) conditionals, but stop when a try conditional - * not in its finally clause (which then is to be executed next) is found. - * In this case, make the ":return" pending for execution at the ":endtry". - * Otherwise, return normally. - */ - idx = cleanup_conditionals(eap->cstack, 0, TRUE); + // + // Cleanup (and deactivate) conditionals, but stop when a try conditional + // not in its finally clause (which then is to be executed next) is found. + // In this case, make the ":return" pending for execution at the ":endtry". + // Otherwise, return normally. + // + idx = cleanup_conditionals(eap->cstack, 0, true); if (idx >= 0) { cstack->cs_pending[idx] = CSTP_RETURN; @@ -3459,54 +3482,54 @@ dictitem_T *find_var_in_scoped_ht(const char *name, const size_t namelen, /// Set "copyID + 1" in previous_funccal and callers. bool set_ref_in_previous_funccal(int copyID) { - bool abort = false; - - for (funccall_T *fc = previous_funccal; !abort && fc != NULL; + for (funccall_T *fc = previous_funccal; fc != NULL; fc = fc->caller) { fc->fc_copyID = copyID + 1; - abort = abort - || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL) - || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL) - || set_ref_in_list(&fc->l_varlist, copyID + 1, NULL); + if (set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL) + || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL) + || set_ref_in_list(&fc->l_varlist, copyID + 1, NULL)) { + return true; + } } - return abort; + return false; } static bool set_ref_in_funccal(funccall_T *fc, int copyID) { - bool abort = false; - if (fc->fc_copyID != copyID) { fc->fc_copyID = copyID; - abort = abort - || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL) - || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL) - || set_ref_in_list(&fc->l_varlist, copyID, NULL) - || set_ref_in_func(NULL, fc->func, copyID); + if (set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL) + || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL) + || set_ref_in_list(&fc->l_varlist, copyID, NULL) + || set_ref_in_func(NULL, fc->func, copyID)) { + return true; + } } - return abort; + return false; } /// Set "copyID" in all local vars and arguments in the call stack. bool set_ref_in_call_stack(int copyID) { - bool abort = false; - - for (funccall_T *fc = current_funccal; !abort && fc != NULL; + for (funccall_T *fc = current_funccal; fc != NULL; fc = fc->caller) { - abort = abort || set_ref_in_funccal(fc, copyID); + if (set_ref_in_funccal(fc, copyID)) { + return true; + } } // Also go through the funccal_stack. - for (funccal_entry_T *entry = funccal_stack; !abort && entry != NULL; + for (funccal_entry_T *entry = funccal_stack; entry != NULL; entry = entry->next) { - for (funccall_T *fc = entry->top_funccal; !abort && fc != NULL; + for (funccall_T *fc = entry->top_funccal; fc != NULL; fc = fc->caller) { - abort = abort || set_ref_in_funccal(fc, copyID); + if (set_ref_in_funccal(fc, copyID)) { + return true; + } } } - return abort; + return false; } /// Set "copyID" in all functions available by name. @@ -3514,7 +3537,6 @@ bool set_ref_in_functions(int copyID) { int todo; hashitem_T *hi = NULL; - bool abort = false; ufunc_T *fp; todo = (int)func_hashtab.ht_used; @@ -3522,24 +3544,25 @@ bool set_ref_in_functions(int copyID) if (!HASHITEM_EMPTY(hi)) { todo--; fp = HI2UF(hi); - if (!func_name_refcount(fp->uf_name)) { - abort = abort || set_ref_in_func(NULL, fp, copyID); + if (!func_name_refcount(fp->uf_name) + && set_ref_in_func(NULL, fp, copyID)) { + return true; } } } - return abort; + return false; } /// Set "copyID" in all function arguments. bool set_ref_in_func_args(int copyID) { - bool abort = false; - for (int i = 0; i < funcargs.ga_len; i++) { - abort = abort || set_ref_in_item(((typval_T **)funcargs.ga_data)[i], - copyID, NULL, NULL); + if (set_ref_in_item(((typval_T **)funcargs.ga_data)[i], + copyID, NULL, NULL)) { + return true; + } } - return abort; + return false; } /// Mark all lists and dicts referenced through function "name" with "copyID". diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index e8ad0bf1da..3f111343d2 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -28,11 +28,37 @@ typedef enum { ERROR_OTHER, ERROR_BOTH, ERROR_DELETED, + ERROR_NOTMETHOD, } FnameTransError; +/// Used in funcexe_T. Returns the new argcount. typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int argskip, int called_func_argcount); +/// Structure passed between functions dealing with function call execution. +typedef struct { + ArgvFunc argv_func; ///< when not NULL, can be used to fill in arguments only + ///< when the invoked function uses them + linenr_T firstline; ///< first line of range + linenr_T lastline; ///< last line of range + bool *doesrange; ///< [out] if not NULL: function handled range + bool evaluate; ///< actually evaluate expressions + partial_T *partial; ///< for extra arguments + dict_T *selfdict; ///< Dictionary for "self" + typval_T *basetv; ///< base for base->method() +} funcexe_T; + +#define FUNCEXE_INIT (funcexe_T) { \ + .argv_func = NULL, \ + .firstline = 0, \ + .lastline = 0, \ + .doesrange = NULL, \ + .evaluate = false, \ + .partial = NULL, \ + .selfdict = NULL, \ + .basetv = NULL, \ +} + #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index 1e6d62135c..f534fc483f 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -73,7 +73,7 @@ struct multiqueue_item { struct multiqueue { MultiQueue *parent; QUEUE headtail; // circularly-linked - put_callback put_cb; + PutCallback put_cb; void *data; size_t size; }; @@ -91,7 +91,7 @@ typedef struct { static Event NILEVENT = { .handler = NULL, .argv = {NULL} }; -MultiQueue *multiqueue_new_parent(put_callback put_cb, void *data) +MultiQueue *multiqueue_new_parent(PutCallback put_cb, void *data) { return multiqueue_new(NULL, put_cb, data); } @@ -104,7 +104,7 @@ MultiQueue *multiqueue_new_child(MultiQueue *parent) return multiqueue_new(parent, NULL, NULL); } -static MultiQueue *multiqueue_new(MultiQueue *parent, put_callback put_cb, +static MultiQueue *multiqueue_new(MultiQueue *parent, PutCallback put_cb, void *data) { MultiQueue *rv = xmalloc(sizeof(MultiQueue)); diff --git a/src/nvim/event/multiqueue.h b/src/nvim/event/multiqueue.h index a688107665..dc60fbb4c7 100644 --- a/src/nvim/event/multiqueue.h +++ b/src/nvim/event/multiqueue.h @@ -7,7 +7,7 @@ #include "nvim/lib/queue.h" typedef struct multiqueue MultiQueue; -typedef void (*put_callback)(MultiQueue *multiq, void *data); +typedef void (*PutCallback)(MultiQueue *multiq, void *data); #define multiqueue_put(q, h, ...) \ multiqueue_put_event(q, event_create(h, __VA_ARGS__)); diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index b93d6cc0ab..8b366d0f3c 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -88,7 +88,7 @@ int process_spawn(Process *proc, bool in, bool out, bool err) } else { process_close(proc); } - shell_free_argv(proc->argv); + process_free(proc); proc->status = -1; return status; } @@ -200,9 +200,9 @@ int process_wait(Process *proc, int ms, MultiQueue *events) if (proc->refcount == 1) { // Job exited, free its resources. decref(proc); - if (events) { - // the decref call created an exit event, process it now - multiqueue_process_events(events); + if (proc->events) { + // decref() created an exit event, process it now. + multiqueue_process_events(proc->events); } } else { proc->refcount--; @@ -239,6 +239,15 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL KILL_TIMEOUT_MS, 0); } +// Frees process-owned resources. +void process_free(Process *proc) FUNC_ATTR_NONNULL_ALL +{ + if (proc->argv != NULL) { + shell_free_argv(proc->argv); + proc->argv = NULL; + } +} + /// Sends SIGKILL (or SIGTERM..SIGKILL for PTY jobs) to processes that did /// not terminate after process_stop(). static void children_kill_cb(uv_timer_t *handle) @@ -269,9 +278,12 @@ static void children_kill_cb(uv_timer_t *handle) static void process_close_event(void **argv) { Process *proc = argv[0]; - shell_free_argv(proc->argv); - if (proc->cb) { // "on_exit" for jobstart(). See channel_job_start(). + if (proc->cb) { + // User (hint: channel_job_start) is responsible for calling + // process_free(). proc->cb(proc, proc->status, proc->data); + } else { + process_free(proc); } } diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 24debdb276..20c02e4900 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -26,6 +26,7 @@ struct process { char **argv; dict_T *env; Stream in, out, err; + /// Exit handler. If set, user must call process_free(). process_exit_cb cb; internal_process_cb internal_exit_cb, internal_close_cb; bool closed, detach, overlapped; diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c index 1e9e530a42..a8ded66ea5 100644 --- a/src/nvim/event/stream.c +++ b/src/nvim/event/stream.c @@ -19,7 +19,7 @@ # include "event/stream.c.generated.h" #endif -// For compatbility with libuv < 1.19.0 (tested on 1.18.0) +// For compatibility with libuv < 1.19.0 (tested on 1.18.0) #if UV_VERSION_MINOR < 19 #define uv_stream_get_write_queue_size(stream) stream->write_queue_size #endif diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 6a0a08eee8..2ea16f0d50 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -7,54 +7,58 @@ #include <assert.h> #include <float.h> -#include <stdbool.h> -#include <string.h> -#include <stdlib.h> #include <inttypes.h> #include <math.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include "nvim/api/buffer.h" #include "nvim/api/private/defs.h" #include "nvim/api/vim.h" -#include "nvim/api/buffer.h" -#include "nvim/log.h" -#include "nvim/vim.h" #include "nvim/ascii.h" -#include "nvim/ex_cmds.h" #include "nvim/buffer.h" +#include "nvim/buffer_updates.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/decoration.h" #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/edit.h" #include "nvim/eval.h" +#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/extmark.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/highlight.h" #include "nvim/indent.h" -#include "nvim/buffer_updates.h" +#include "nvim/log.h" #include "nvim/main.h" #include "nvim/mark.h" -#include "nvim/extmark.h" -#include "nvim/decoration.h" #include "nvim/mbyte.h" #include "nvim/memline.h" +#include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/garray.h" -#include "nvim/memory.h" -#include "nvim/move.h" #include "nvim/mouse.h" +#include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/os.h" +#include "nvim/os/shell.h" +#include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/screen.h" @@ -65,11 +69,8 @@ #include "nvim/tag.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/os/os.h" -#include "nvim/os/shell.h" -#include "nvim/os/input.h" -#include "nvim/os/time.h" /// Case matching style to use for :substitute @@ -103,7 +104,7 @@ typedef struct { // the preview window typedef struct { kvec_t(SubResult) subresults; - linenr_T lines_needed; // lines neede in the preview window + linenr_T lines_needed; // lines needed in the preview window } PreviewLines; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -146,17 +147,17 @@ void do_ascii(const exarg_T *const eap) dig = get_digraph_for_char(cval); if (dig != NULL) { - iobuff_len += ( - vim_snprintf((char *)IObuff + iobuff_len, - sizeof(IObuff) - iobuff_len, - _("<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s"), - transchar(c), buf1, buf2, cval, cval, cval, dig)); + iobuff_len += ( + vim_snprintf((char *)IObuff + iobuff_len, + sizeof(IObuff) - iobuff_len, + _("<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s"), + transchar(c), buf1, buf2, cval, cval, cval, dig)); } else { - iobuff_len += ( - vim_snprintf((char *)IObuff + iobuff_len, - sizeof(IObuff) - iobuff_len, - _("<%s>%s%s %d, Hex %02x, Octal %03o"), - transchar(c), buf1, buf2, cval, cval, cval)); + iobuff_len += ( + vim_snprintf((char *)IObuff + iobuff_len, + sizeof(IObuff) - iobuff_len, + _("<%s>%s%s %d, Hex %02x, Octal %03o"), + transchar(c), buf1, buf2, cval, cval, cval)); } c = cc[ci++]; @@ -196,21 +197,21 @@ void do_ascii(const exarg_T *const eap) dig = get_digraph_for_char(c); if (dig != NULL) { - iobuff_len += ( - vim_snprintf((char *)IObuff + iobuff_len, - sizeof(IObuff) - iobuff_len, - (c < 0x10000 + iobuff_len += ( + vim_snprintf((char *)IObuff + iobuff_len, + sizeof(IObuff) - iobuff_len, + (c < 0x10000 ? _("> %d, Hex %04x, Oct %o, Digr %s") : _("> %d, Hex %08x, Oct %o, Digr %s")), - c, c, c, dig)); + c, c, c, dig)); } else { - iobuff_len += ( - vim_snprintf((char *)IObuff + iobuff_len, - sizeof(IObuff) - iobuff_len, - (c < 0x10000 + iobuff_len += ( + vim_snprintf((char *)IObuff + iobuff_len, + sizeof(IObuff) - iobuff_len, + (c < 0x10000 ? _("> %d, Hex %04x, Octal %o") : _("> %d, Hex %08x, Octal %o")), - c, c, c)); + c, c, c)); } if (ci == MAX_MCO) { break; @@ -237,26 +238,29 @@ void ex_align(exarg_T *eap) int width; if (curwin->w_p_rl) { - /* switch left and right aligning */ - if (eap->cmdidx == CMD_right) + // switch left and right aligning + if (eap->cmdidx == CMD_right) { eap->cmdidx = CMD_left; - else if (eap->cmdidx == CMD_left) + } else if (eap->cmdidx == CMD_left) { eap->cmdidx = CMD_right; + } } width = atoi((char *)eap->arg); save_curpos = curwin->w_cursor; - if (eap->cmdidx == CMD_left) { /* width is used for new indent */ - if (width >= 0) + if (eap->cmdidx == CMD_left) { // width is used for new indent + if (width >= 0) { indent = width; + } } else { /* * if 'textwidth' set, use it * else if 'wrapmargin' set, use it * if invalid value, use 80 */ - if (width <= 0) + if (width <= 0) { width = curbuf->b_p_tw; + } if (width == 0 && curbuf->b_p_wm > 0) { width = curwin->w_width_inner - curbuf->b_p_wm; } @@ -265,31 +269,33 @@ void ex_align(exarg_T *eap) } } - if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) + if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL) { return; + } for (curwin->w_cursor.lnum = eap->line1; curwin->w_cursor.lnum <= eap->line2; ++curwin->w_cursor.lnum) { - if (eap->cmdidx == CMD_left) /* left align */ + if (eap->cmdidx == CMD_left) { // left align new_indent = indent; - else { - has_tab = FALSE; /* avoid uninit warnings */ + } else { + has_tab = FALSE; // avoid uninit warnings len = linelen(eap->cmdidx == CMD_right ? &has_tab - : NULL) - get_indent(); + : NULL) - get_indent(); - if (len <= 0) /* skip blank lines */ + if (len <= 0) { // skip blank lines continue; + } - if (eap->cmdidx == CMD_center) + if (eap->cmdidx == CMD_center) { new_indent = (width - len) / 2; - else { - new_indent = width - len; /* right align */ + } else { + new_indent = width - len; // right align /* * Make sure that embedded TABs don't make the text go too far * to the right. */ - if (has_tab) + if (has_tab) { while (new_indent > 0) { (void)set_indent(new_indent, 0); if (linelen(NULL) <= width) { @@ -305,11 +311,13 @@ void ex_align(exarg_T *eap) } --new_indent; } + } } } - if (new_indent < 0) + if (new_indent < 0) { new_indent = 0; - (void)set_indent(new_indent, 0); /* set indent */ + } + (void)set_indent(new_indent, 0); // set indent } changed_lines(eap->line1, 0, eap->line2 + 1, 0L, true); curwin->w_cursor = save_curpos; @@ -321,9 +329,9 @@ void ex_align(exarg_T *eap) */ static int linelen(int *has_tab) { - char_u *line; - char_u *first; - char_u *last; + char_u *line; + char_u *first; + char_u *last; int save; int len; @@ -355,8 +363,8 @@ static int linelen(int *has_tab) /* Buffer for two lines used during sorting. They are allocated to * contain the longest line being sorted. */ -static char_u *sortbuf1; -static char_u *sortbuf2; +static char_u *sortbuf1; +static char_u *sortbuf2; static int sort_lc; ///< sort using locale static int sort_ic; ///< ignore case @@ -399,11 +407,13 @@ static int sort_compare(const void *s1, const void *s2) /* If the user interrupts, there's no way to stop qsort() immediately, but * if we return 0 every time, qsort will assume it's done sorting and * exit. */ - if (sort_abort) + if (sort_abort) { return 0; + } fast_breakcheck(); - if (got_int) + if (got_int) { sort_abort = TRUE; + } // When sorting numbers "start_col_nr" is the number, not the column // number. @@ -435,9 +445,10 @@ static int sort_compare(const void *s1, const void *s2) result = string_compare(sortbuf1, sortbuf2); } - /* If two lines have the same value, preserve the original line order. */ - if (result == 0) + // If two lines have the same value, preserve the original line order. + if (result == 0) { return (int)(l1.lnum - l2.lnum); + } return result; } @@ -450,9 +461,9 @@ void ex_sort(exarg_T *eap) long maxlen = 0; size_t count = (size_t)(eap->line2 - eap->line1 + 1); size_t i; - char_u *p; - char_u *s; - char_u *s2; + char_u *p; + char_u *s; + char_u *s2; char_u c; // temporary character storage bool unique = false; long deleted; @@ -598,7 +609,7 @@ void ex_sort(exarg_T *eap) } else { nrs[lnum - eap->line1].st_u.num.is_number = true; vim_str2nr(s, NULL, NULL, sort_what, - &nrs[lnum - eap->line1].st_u.num.value, NULL, 0); + &nrs[lnum - eap->line1].st_u.num.value, NULL, 0, false); } } else { s = skipwhite(p); @@ -622,10 +633,12 @@ void ex_sort(exarg_T *eap) nrs[lnum - eap->line1].lnum = lnum; - if (regmatch.regprog != NULL) + if (regmatch.regprog != NULL) { fast_breakcheck(); - if (got_int) + } + if (got_int) { goto sortend; + } } // Allocate a buffer that can hold the longest line. @@ -635,8 +648,9 @@ void ex_sort(exarg_T *eap) // Sort the array of line numbers. Note: can't be interrupted! qsort((void *)nrs, count, sizeof(sorti_T), sort_compare); - if (sort_abort) + if (sort_abort) { goto sortend; + } bcount_t old_count = 0, new_count = 0; @@ -664,8 +678,9 @@ void ex_sort(exarg_T *eap) new_count += bytelen; } fast_breakcheck(); - if (got_int) + if (got_int) { goto sortend; + } } // delete the original lines if appending worked @@ -714,7 +729,7 @@ sortend: void ex_retab(exarg_T *eap) { linenr_T lnum; - int got_tab = FALSE; + bool got_tab = false; long num_spaces = 0; long num_tabs; long len; @@ -723,18 +738,18 @@ void ex_retab(exarg_T *eap) long start_col = 0; // For start of white-space string long start_vcol = 0; // For start of white-space string long old_len; - char_u *ptr; - char_u *new_line = (char_u *)1; // init to non-NULL + char_u *ptr; + char_u *new_line = (char_u *)1; // init to non-NULL int did_undo; // called u_save for current line long *new_vts_array = NULL; char_u *new_ts_str; // string value of tab argument int save_list; - linenr_T first_line = 0; /* first changed line */ - linenr_T last_line = 0; /* last changed line */ + linenr_T first_line = 0; // first changed line + linenr_T last_line = 0; // last changed line save_list = curwin->w_p_list; - curwin->w_p_list = 0; /* don't want list mode here */ + curwin->w_p_list = 0; // don't want list mode here new_ts_str = eap->arg; if (!tabstop_set(eap->arg, &new_vts_array)) { @@ -761,19 +776,20 @@ void ex_retab(exarg_T *eap) for (;; ) { if (ascii_iswhite(ptr[col])) { if (!got_tab && num_spaces == 0) { - /* First consecutive white-space */ + // First consecutive white-space start_vcol = vcol; start_col = col; } - if (ptr[col] == ' ') + if (ptr[col] == ' ') { num_spaces++; - else - got_tab = TRUE; + } else { + got_tab = true; + } } else { if (got_tab || (eap->forceit && num_spaces > 1)) { - /* Retabulate this string of white-space */ + // Retabulate this string of white-space - /* len is virtual length of white string */ + // len is virtual length of white string len = num_spaces = vcol - start_vcol; num_tabs = 0; if (!curbuf->b_p_et) { @@ -795,16 +811,17 @@ void ex_retab(exarg_T *eap) } } - /* len is actual number of white characters used */ + // len is actual number of white characters used len = num_spaces + num_tabs; old_len = (long)STRLEN(ptr); long new_len = old_len - col + start_col + len + 1; new_line = xmalloc(new_len); - if (start_col > 0) + if (start_col > 0) { memmove(new_line, ptr, (size_t)start_col); + } memmove(new_line + start_col + len, - ptr + col, (size_t)(old_len - col + 1)); + ptr + col, (size_t)(old_len - col + 1)); ptr = new_line + start_col; for (col = 0; col < len; col++) { ptr[col] = (col < num_tabs) ? '\t' : ' '; @@ -823,20 +840,23 @@ void ex_retab(exarg_T *eap) col = start_col + len; } } - got_tab = FALSE; + got_tab = false; num_spaces = 0; } - if (ptr[col] == NUL) + if (ptr[col] == NUL) { break; - vcol += chartabsize(ptr + col, (colnr_T)vcol); + } + vcol += win_chartabsize(curwin, ptr + col, (colnr_T)vcol); col += utfc_ptr2len(ptr + col); } - if (new_line == NULL) /* out of memory */ + if (new_line == NULL) { // out of memory break; + } line_breakcheck(); } - if (got_int) + if (got_int) { EMSG(_(e_interr)); + } // If a single value was given then it can be considered equal to // either the value of 'tabstop' or the value of 'vartabstop'. @@ -854,7 +874,7 @@ void ex_retab(exarg_T *eap) changed_lines(first_line, 0, last_line + 1, 0L, true); } - curwin->w_p_list = save_list; /* restore 'list' */ + curwin->w_p_list = save_list; // restore 'list' if (new_ts_str != NULL) { // set the new tabstop // If 'vartabstop' is in use or if the value given to retab has more @@ -886,7 +906,7 @@ void ex_retab(exarg_T *eap) */ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) { - char_u *str; + char_u *str; linenr_T l; linenr_T extra; // Num lines added before line1 linenr_T num_lines; // Num lines moved @@ -921,14 +941,16 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) * First we copy the old text to its new location -- webb * Also copy the flag that ":global" command uses. */ - if (u_save(dest, dest + 1) == FAIL) + if (u_save(dest, dest + 1) == FAIL) { return FAIL; + } for (extra = 0, l = line1; l <= line2; l++) { str = vim_strsave(ml_get(l + extra)); ml_append(dest + l - line1, str, (colnr_T)0, false); xfree(str); - if (dest < line1) + if (dest < line1) { extra++; + } } /* @@ -984,17 +1006,19 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) /* * Now we delete the original text -- webb */ - if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL) + if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL) { return FAIL; + } for (l = line1; l <= line2; l++) { ml_delete(line1 + extra, true); } if (!global_busy && num_lines > p_report) { - if (num_lines == 1) + if (num_lines == 1) { MSG(_("1 line moved")); - else + } else { smsg(_("%" PRId64 " lines moved"), (int64_t)num_lines); + } } extmark_move_region(curbuf, line1-1, 0, start_byte, @@ -1005,16 +1029,18 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) /* * Leave the cursor on the last of the moved lines. */ - if (dest >= line1) + if (dest >= line1) { curwin->w_cursor.lnum = dest; - else + } else { curwin->w_cursor.lnum = dest + (line2 - line1) + 1; + } if (line1 < dest) { dest += num_lines + 1; last_line = curbuf->b_ml.ml_line_count; - if (dest > last_line + 1) + if (dest > last_line + 1) { dest = last_line + 1; + } changed_lines(line1, 0, dest, 0L, false); } else { changed_lines(dest + 1, 0, line1 + num_lines, 0L, false); @@ -1032,7 +1058,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) { linenr_T count; - char_u *p; + char_u *p; count = line2 - line1 + 1; curbuf->b_op_start.lnum = n + 1; @@ -1050,8 +1076,9 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) * line1 = start of source (while copying) * line2 = end of source (while copying) */ - if (u_save(n, n + 1) == FAIL) + if (u_save(n, n + 1) == FAIL) { return; + } curwin->w_cursor.lnum = n; while (line1 <= line2) { @@ -1061,14 +1088,17 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, false); xfree(p); - /* situation 2: skip already copied lines */ - if (line1 == n) + // situation 2: skip already copied lines + if (line1 == n) { line1 = curwin->w_cursor.lnum; + } ++line1; - if (curwin->w_cursor.lnum < line1) + if (curwin->w_cursor.lnum < line1) { ++line1; - if (curwin->w_cursor.lnum < line2) + } + if (curwin->w_cursor.lnum < line2) { ++line2; + } ++curwin->w_cursor.lnum; } @@ -1077,7 +1107,7 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) msgmore((long)count); } -static char_u *prevcmd = NULL; /* the previous command */ +static char_u *prevcmd = NULL; // the previous command #if defined(EXITFREE) void free_prev_shellcmd(void) @@ -1088,12 +1118,11 @@ void free_prev_shellcmd(void) #endif /* - * Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd" + * Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd" * Bangs in the argument are replaced with the previously entered command. * Remember the argument. */ -void do_bang(int addr_count, exarg_T *eap, bool forceit, - bool do_in, bool do_out) +void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out) FUNC_ATTR_NONNULL_ALL { char_u *arg = eap->arg; // command @@ -1101,9 +1130,9 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, linenr_T line2 = eap->line2; // end of range char_u *newcmd = NULL; // the new command bool free_newcmd = false; // need to free() newcmd - char_u *t; - char_u *p; - char_u *trailarg; + char_u *t; + char_u *p; + char_u *trailarg; int len; int scroll_save = msg_scroll; @@ -1115,8 +1144,8 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, return; } - if (addr_count == 0) { /* :! */ - msg_scroll = FALSE; /* don't scroll here */ + if (addr_count == 0) { // :! + msg_scroll = FALSE; // don't scroll here autowrite_all(); msg_scroll = scroll_save; } @@ -1129,8 +1158,9 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, trailarg = arg; do { len = (int)STRLEN(trailarg) + 1; - if (newcmd != NULL) + if (newcmd != NULL) { len += (int)STRLEN(newcmd); + } if (ins_prevcmd) { if (prevcmd == NULL) { EMSG(_(e_noprev)); @@ -1141,10 +1171,12 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, } t = xmalloc(len); *t = NUL; - if (newcmd != NULL) + if (newcmd != NULL) { STRCAT(t, newcmd); - if (ins_prevcmd) + } + if (ins_prevcmd) { STRCAT(t, prevcmd); + } p = t + STRLEN(t); STRCAT(t, trailarg); xfree(newcmd); @@ -1157,9 +1189,9 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, trailarg = NULL; while (*p) { if (*p == '!') { - if (p > newcmd && p[-1] == '\\') + if (p > newcmd && p[-1] == '\\') { STRMOVE(p - 1, p); - else { + } else { trailarg = p; *trailarg++ = NUL; ins_prevcmd = true; @@ -1173,7 +1205,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, xfree(prevcmd); prevcmd = newcmd; - if (bangredo) { /* put cmd in redo buffer for ! command */ + if (bangredo) { // put cmd in redo buffer for ! command /* If % or # appears in the command, it must have been escaped. * Reescape them, so that redoing them does not substitute them by the * buffername. */ @@ -1194,8 +1226,8 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, STRCAT(newcmd, p_shq); free_newcmd = true; } - if (addr_count == 0) { /* :! */ - /* echo the command */ + if (addr_count == 0) { // :! + // echo the command msg_start(); msg_putchar(':'); msg_putchar('!'); @@ -1204,49 +1236,48 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, ui_cursor_goto(msg_row, msg_col); do_shell(newcmd, 0); - } else { /* :range! */ + } else { // :range! /* Careful: This may recursively call do_bang() again! (because of * autocommands) */ do_filter(line1, line2, eap, newcmd, do_in, do_out); apply_autocmds(EVENT_SHELLFILTERPOST, NULL, NULL, FALSE, curbuf); } - if (free_newcmd) + if (free_newcmd) { xfree(newcmd); + } } -// do_filter: filter lines through a command given by the user -// -// We mostly use temp files and the call_shell() routine here. This would -// normally be done using pipes on a Unix system, but this is more portable -// to non-Unix systems. The call_shell() routine needs to be able -// to deal with redirection somehow, and should handle things like looking -// at the PATH env. variable, and adding reasonable extensions to the -// command name given by the user. All reasonable versions of call_shell() -// do this. -// Alternatively, if on Unix and redirecting input or output, but not both, -// and the 'shelltemp' option isn't set, use pipes. -// We use input redirection if do_in is true. -// We use output redirection if do_out is true. -static void do_filter( - linenr_T line1, - linenr_T line2, - exarg_T *eap, /* for forced 'ff' and 'fenc' */ - char_u *cmd, - bool do_in, - bool do_out) +/// do_filter: filter lines through a command given by the user +/// +/// We mostly use temp files and the call_shell() routine here. This would +/// normally be done using pipes on a Unix system, but this is more portable +/// to non-Unix systems. The call_shell() routine needs to be able +/// to deal with redirection somehow, and should handle things like looking +/// at the PATH env. variable, and adding reasonable extensions to the +/// command name given by the user. All reasonable versions of call_shell() +/// do this. +/// Alternatively, if on Unix and redirecting input or output, but not both, +/// and the 'shelltemp' option isn't set, use pipes. +/// We use input redirection if do_in is true. +/// We use output redirection if do_out is true. +/// +/// @param eap for forced 'ff' and 'fenc' +static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd, bool do_in, + bool do_out) { - char_u *itmp = NULL; - char_u *otmp = NULL; + char_u *itmp = NULL; + char_u *otmp = NULL; linenr_T linecount; linenr_T read_linecount; pos_T cursor_save; - char_u *cmd_buf; - buf_T *old_curbuf = curbuf; + char_u *cmd_buf; + buf_T *old_curbuf = curbuf; int shell_flags = 0; const int stmp = p_stmp; - if (*cmd == NUL) /* no filter command */ + if (*cmd == NUL) { // no filter command return; + } cursor_save = curwin->w_cursor; @@ -1269,8 +1300,9 @@ static void do_filter( * pipe only need to do 3. */ - if (do_out) + if (do_out) { shell_flags |= kShellOptDoOut; + } if (!do_in && do_out && !stmp) { // Use a pipe to fetch stdout of the command, do not use a temp file. @@ -1289,7 +1321,7 @@ static void do_filter( curbuf->b_op_end.lnum = line2; curwin->w_cursor.lnum = line2; } else if ((do_in && (itmp = vim_tempname()) == NULL) - || (do_out && (otmp = vim_tempname()) == NULL)) { + || (do_out && (otmp = vim_tempname()) == NULL)) { EMSG(_(e_notmp)); goto filterend; } @@ -1298,7 +1330,7 @@ static void do_filter( * The writing and reading of temp files will not be shown. * Vi also doesn't do this and the messages are not very informative. */ - ++no_wait_return; /* don't call wait_return() while busy */ + ++no_wait_return; // don't call wait_return() while busy if (itmp != NULL && buf_write(curbuf, itmp, NULL, line1, line2, eap, false, false, false, true) == FAIL) { msg_putchar('\n'); // Keep message from buf_write(). @@ -1308,13 +1340,15 @@ static void do_filter( } goto filterend; } - if (curbuf != old_curbuf) + if (curbuf != old_curbuf) { goto filterend; + } - if (!do_out) + if (!do_out) { msg_putchar('\n'); + } - /* Create the shell command in allocated memory. */ + // Create the shell command in allocated memory. cmd_buf = make_filter_cmd(cmd, itmp, otmp); ui_cursor_goto(Rows - 1, 0); @@ -1350,8 +1384,9 @@ static void do_filter( } goto error; } - if (curbuf != old_curbuf) + if (curbuf != old_curbuf) { goto filterend; + } } read_linecount = curbuf->b_ml.ml_line_count - read_linecount; @@ -1385,11 +1420,11 @@ static void do_filter( * Adjust '[ and '] (set by buf_write()). */ curwin->w_cursor.lnum = line1; - del_lines(linecount, TRUE); - curbuf->b_op_start.lnum -= linecount; /* adjust '[ */ - curbuf->b_op_end.lnum -= linecount; /* adjust '] */ - write_lnum_adjust(-linecount); /* adjust last line - for next write */ + del_lines(linecount, true); + curbuf->b_op_start.lnum -= linecount; // adjust '[ + curbuf->b_op_end.lnum -= linecount; // adjust '] + write_lnum_adjust(-linecount); // adjust last line + // for next write foldUpdate(curwin, curbuf->b_op_start.lnum, curbuf->b_op_end.lnum); } else { /* @@ -1399,22 +1434,24 @@ static void do_filter( curwin->w_cursor.lnum = curbuf->b_op_end.lnum; } - beginline(BL_WHITE | BL_FIX); /* cursor on first non-blank */ + beginline(BL_WHITE | BL_FIX); // cursor on first non-blank --no_wait_return; if (linecount > p_report) { if (do_in) { vim_snprintf((char *)msg_buf, sizeof(msg_buf), - _("%" PRId64 " lines filtered"), (int64_t)linecount); - if (msg(msg_buf) && !msg_scroll) - /* save message to display it after redraw */ + _("%" PRId64 " lines filtered"), (int64_t)linecount); + if (msg(msg_buf) && !msg_scroll) { + // save message to display it after redraw set_keep_msg(msg_buf, 0); - } else + } + } else { msgmore((long)linecount); + } } } else { error: - /* put cursor back in same position for ":w !cmd" */ + // put cursor back in same position for ":w !cmd" curwin->w_cursor = cursor_save; --no_wait_return; wait_return(FALSE); @@ -1426,21 +1463,21 @@ filterend: --no_wait_return; EMSG(_("E135: *Filter* Autocommands must not change current buffer")); } - if (itmp != NULL) + if (itmp != NULL) { os_remove((char *)itmp); - if (otmp != NULL) + } + if (otmp != NULL) { os_remove((char *)otmp); + } xfree(itmp); xfree(otmp); } -// Call a shell to execute a command. -// When "cmd" is NULL start an interactive shell. -void -do_shell( - char_u *cmd, - int flags // may be SHELL_DOOUT when output is redirected -) +/// Call a shell to execute a command. +/// When "cmd" is NULL start an interactive shell. +/// +/// @param flags may be SHELL_DOOUT when output is redirected +void do_shell(char_u *cmd, int flags) { // Disallow shell commands from .exrc and .vimrc in current directory for // security reasons. @@ -1522,11 +1559,11 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) size_t len = STRLEN(cmd) + 1; // At least enough space for cmd + NULL. - len += is_fish_shell ? sizeof("begin; ""; end") - 1 - : sizeof("("")") - 1; + len += is_fish_shell ? sizeof("begin; " "; end") - 1 + : sizeof("(" ")") - 1; if (itmp != NULL) { - len += STRLEN(itmp) + sizeof(" { "" < "" } ") - 1; + len += STRLEN(itmp) + sizeof(" { " " < " " } ") - 1; } if (otmp != NULL) { len += STRLEN(otmp) + STRLEN(p_srr) + 2; // two extra spaces (" "), @@ -1574,9 +1611,9 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) } #endif if (otmp != NULL) { - append_redir(buf, len, (char *) p_srr, (char *) otmp); + append_redir(buf, len, (char *)p_srr, (char *)otmp); } - return (char_u *) buf; + return (char_u *)buf; } /// Append output redirection for the given file to the end of the buffer @@ -1588,8 +1625,8 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) /// a space, opt, a space and then fname if `%s` is not found /// there. /// @param[in] fname File name to append. -void append_redir(char *const buf, const size_t buflen, - const char *const opt, const char *const fname) +void append_redir(char *const buf, const size_t buflen, const char *const opt, + const char *const fname) { char *const end = buf + strlen(buf); // find "%s" @@ -1603,9 +1640,9 @@ void append_redir(char *const buf, const size_t buflen, } if (p != NULL) { *end = ' '; // not really needed? Not with sh, ksh or bash - vim_snprintf(end + 1, (size_t) (buflen - (end + 1 - buf)), opt, fname); + vim_snprintf(end + 1, (size_t)(buflen - (end + 1 - buf)), opt, fname); } else { - vim_snprintf(end, (size_t) (buflen - (end - buf)), " %s %s", opt, fname); + vim_snprintf(end, (size_t)(buflen - (end - buf)), " %s %s", opt, fname); } } @@ -1635,28 +1672,30 @@ void print_line(linenr_T lnum, int use_number, int list) msg_start(); silent_mode = FALSE; - info_message = TRUE; /* use mch_msg(), not mch_errmsg() */ + info_message = true; // use mch_msg(), not mch_errmsg() print_line_no_prefix(lnum, use_number, list); if (save_silent) { msg_putchar('\n'); ui_flush(); silent_mode = save_silent; } - info_message = FALSE; + info_message = false; } int rename_buffer(char_u *new_fname) { - char_u *fname, *sfname, *xfname; - buf_T *buf; + char_u *fname, *sfname, *xfname; + buf_T *buf; buf = curbuf; apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf); - /* buffer changed, don't change name now */ - if (buf != curbuf) + // buffer changed, don't change name now + if (buf != curbuf) { return FAIL; - if (aborting()) /* autocmds may abort script processing */ + } + if (aborting()) { // autocmds may abort script processing return FAIL; + } /* * The name of the current buffer will be changed. * A new (unlisted) buffer entry needs to be made to hold the old file @@ -1684,7 +1723,7 @@ int rename_buffer(char_u *new_fname) xfree(fname); xfree(sfname); apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, FALSE, curbuf); - /* Change directories when the 'acd' option is set. */ + // Change directories when the 'acd' option is set. do_autochdir(); return OK; } @@ -1722,8 +1761,9 @@ void ex_file(exarg_T *eap) */ void ex_update(exarg_T *eap) { - if (curbufIsChanged()) + if (curbufIsChanged()) { (void)do_write(eap); + } } /* @@ -1755,15 +1795,16 @@ void ex_write(exarg_T *eap) int do_write(exarg_T *eap) { int other; - char_u *fname = NULL; /* init to shut up gcc */ - char_u *ffname; + char_u *fname = NULL; // init to shut up gcc + char_u *ffname; int retval = FAIL; - char_u *free_fname = NULL; - buf_T *alt_buf = NULL; + char_u *free_fname = NULL; + buf_T *alt_buf = NULL; int name_was_missing; - if (not_writing()) /* check 'write' option */ + if (not_writing()) { // check 'write' option return FAIL; + } ffname = eap->arg; if (*ffname == NUL) { @@ -1779,8 +1820,9 @@ int do_write(exarg_T *eap) * When out-of-memory, keep unexpanded file name, because we MUST be * able to write the file in this situation. */ - if (free_fname != NULL) + if (free_fname != NULL) { ffname = free_fname; + } other = otherfile(ffname); } @@ -1789,10 +1831,11 @@ int do_write(exarg_T *eap) */ if (other) { if (vim_strchr(p_cpo, CPO_ALTWRITE) != NULL - || eap->cmdidx == CMD_saveas) + || eap->cmdidx == CMD_saveas) { alt_buf = setaltfname(ffname, fname, (linenr_T)1); - else + } else { alt_buf = buflist_findname(ffname); + } if (alt_buf != NULL && alt_buf->b_ml.ml_mfp != NULL) { /* Overwriting a file that is loaded in another buffer is not a * good idea. */ @@ -1823,8 +1866,9 @@ int do_write(exarg_T *eap) && !p_wa) { if (p_confirm || cmdmod.confirm) { if (vim_dialog_yesno(VIM_QUESTION, NULL, - (char_u *)_("Write partial file?"), 2) != VIM_YES) + (char_u *)_("Write partial file?"), 2) != VIM_YES) { goto theend; + } eap->forceit = TRUE; } else { EMSG(_("E140: Use ! to write partial buffer")); @@ -1835,12 +1879,12 @@ int do_write(exarg_T *eap) if (check_overwrite(eap, curbuf, fname, ffname, other) == OK) { if (eap->cmdidx == CMD_saveas && alt_buf != NULL) { - buf_T *was_curbuf = curbuf; + buf_T *was_curbuf = curbuf; apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, curbuf); apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, FALSE, alt_buf); if (curbuf != was_curbuf || aborting()) { - /* buffer changed, don't change name now */ + // buffer changed, don't change name now retval = FAIL; goto theend; } @@ -1866,7 +1910,7 @@ int do_write(exarg_T *eap) apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, alt_buf); } if (curbuf != was_curbuf || aborting()) { - /* buffer changed, don't write the file */ + // buffer changed, don't write the file retval = FAIL; goto theend; } @@ -1886,13 +1930,13 @@ int do_write(exarg_T *eap) name_was_missing = curbuf->b_ffname == NULL; retval = buf_write(curbuf, ffname, fname, eap->line1, eap->line2, - eap, eap->append, eap->forceit, TRUE, FALSE); + eap, eap->append, eap->forceit, TRUE, FALSE); - /* After ":saveas fname" reset 'readonly'. */ + // After ":saveas fname" reset 'readonly'. if (eap->cmdidx == CMD_saveas) { if (retval == OK) { curbuf->b_p_ro = FALSE; - redraw_tabline = TRUE; + redraw_tabline = true; } } @@ -1908,21 +1952,16 @@ theend: return retval; } -/* - * Check if it is allowed to overwrite a file. If b_flags has BF_NOTEDITED, - * BF_NEW or BF_READERR, check for overwriting current file. - * May set eap->forceit if a dialog says it's OK to overwrite. - * Return OK if it's OK, FAIL if it is not. - */ -int -check_overwrite( - exarg_T *eap, - buf_T *buf, - char_u *fname, // file name to be used (can differ from - // buf->ffname) - char_u *ffname, // full path version of fname - int other // writing under other name -) +/// Check if it is allowed to overwrite a file. If b_flags has BF_NOTEDITED, +/// BF_NEW or BF_READERR, check for overwriting current file. +/// May set eap->forceit if a dialog says it's OK to overwrite. +/// +/// @param fname file name to be used (can differ from buf->ffname) +/// @param ffname full path version of fname +/// @param other writing under other name +/// +/// @return OK if it's OK, FAIL if it is not. +int check_overwrite(exarg_T *eap, buf_T *buf, char_u *fname, char_u *ffname, int other) { /* * write to other file or b_flags set or not writing the whole file: @@ -1948,8 +1987,9 @@ check_overwrite( char_u buff[DIALOG_MSG_SIZE]; dialog_msg(buff, _("Overwrite existing file \"%s\"?"), fname); - if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) != VIM_YES) + if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) != VIM_YES) { return FAIL; + } eap->forceit = TRUE; } else { EMSG(_(e_exists)); @@ -1957,11 +1997,11 @@ check_overwrite( } } - /* For ":w! filename" check that no swap file exists for "filename". */ + // For ":w! filename" check that no swap file exists for "filename". if (other && !emsg_silent) { - char_u *dir; - char_u *p; - char_u *swapname; + char_u *dir; + char_u *p; + char_u *swapname; /* We only try the first entry in 'directory', without checking if * it's writable. If the "." directory is not writable the write @@ -1983,8 +2023,8 @@ check_overwrite( char_u buff[DIALOG_MSG_SIZE]; dialog_msg(buff, - _("Swap file \"%s\" exists, overwrite anyway?"), - swapname); + _("Swap file \"%s\" exists, overwrite anyway?"), + swapname); if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) != VIM_YES) { xfree(swapname); @@ -1993,7 +2033,7 @@ check_overwrite( eap->forceit = TRUE; } else { EMSG2(_("E768: Swap file exists: %s (:silent! overrides)"), - swapname); + swapname); xfree(swapname); return FAIL; } @@ -2011,14 +2051,16 @@ void ex_wnext(exarg_T *eap) { int i; - if (eap->cmd[1] == 'n') + if (eap->cmd[1] == 'n') { i = curwin->w_arg_idx + (int)eap->line2; - else + } else { i = curwin->w_arg_idx - (int)eap->line2; + } eap->line1 = 1; eap->line2 = curbuf->b_ml.ml_line_count; - if (do_write(eap) != FAIL) + if (do_write(eap) != FAIL) { do_argfile(eap, i); + } } /* @@ -2058,7 +2100,7 @@ void do_wqall(exarg_T *eap) ++error; } else if (check_readonly(&eap->forceit, buf) || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname, - FALSE) == FAIL) { + FALSE) == FAIL) { ++error; } else { bufref_T bufref; @@ -2071,11 +2113,12 @@ void do_wqall(exarg_T *eap) buf = firstbuf; } } - eap->forceit = save_forceit; /* check_overwrite() may set it */ + eap->forceit = save_forceit; // check_overwrite() may set it } if (exiting) { - if (!error) - getout(0); /* exit Vim */ + if (!error) { + getout(0); // exit Vim + } not_exiting(); } } @@ -2086,8 +2129,9 @@ void do_wqall(exarg_T *eap) */ int not_writing(void) { - if (p_write) + if (p_write) { return FALSE; + } EMSG(_("E142: File not written: Writing is disabled by 'write' option")); return TRUE; } @@ -2107,28 +2151,30 @@ static int check_readonly(int *forceit, buf_T *buf) if ((p_confirm || cmdmod.confirm) && buf->b_fname != NULL) { char_u buff[DIALOG_MSG_SIZE]; - if (buf->b_p_ro) + if (buf->b_p_ro) { dialog_msg(buff, - _( - "'readonly' option is set for \"%s\".\nDo you wish to write anyway?"), - buf->b_fname); - else + _( "'readonly' option is set for \"%s\".\nDo you wish to write anyway?"), + buf->b_fname); + } else { dialog_msg(buff, - _( - "File permissions of \"%s\" are read-only.\nIt may still be possible to write it.\nDo you wish to try?"), - buf->b_fname); + _( + "File permissions of \"%s\" are read-only.\nIt may still be possible to write it.\nDo you wish to try?"), + buf->b_fname); + } if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 2) == VIM_YES) { - /* Set forceit, to force the writing of a readonly file */ + // Set forceit, to force the writing of a readonly file *forceit = TRUE; return FALSE; - } else + } else { return TRUE; - } else if (buf->b_p_ro) + } + } else if (buf->b_p_ro) { EMSG(_(e_readonly)); - else + } else { EMSG2(_("E505: \"%s\" is read-only (add ! to override)"), - buf->b_fname); + buf->b_fname); + } return TRUE; } @@ -2144,14 +2190,13 @@ static int check_readonly(int *forceit, buf_T *buf) // GETFILE_NOT_WRITTEN for "not written" error, // GETFILE_SAME_FILE for success // GETFILE_OPEN_OTHER for successfully opening another file. -int getfile(int fnum, char_u *ffname_arg, char_u *sfname_arg, int setpm, - linenr_T lnum, int forceit) +int getfile(int fnum, char_u *ffname_arg, char_u *sfname_arg, int setpm, linenr_T lnum, int forceit) { char_u *ffname = ffname_arg; char_u *sfname = sfname_arg; int other; int retval; - char_u *free_me = NULL; + char_u *free_me = NULL; if (text_locked()) { return GETFILE_ERROR; @@ -2161,12 +2206,13 @@ int getfile(int fnum, char_u *ffname_arg, char_u *sfname_arg, int setpm, } if (fnum == 0) { - /* make ffname full path, set sfname */ + // make ffname full path, set sfname fname_expand(curbuf, &ffname, &sfname); other = otherfile(ffname); - free_me = ffname; /* has been allocated, free() later */ - } else + free_me = ffname; // has been allocated, free() later + } else { other = (fnum != curbuf->b_fnum); + } if (other) { no_wait_return++; // don't wait for autowrite message @@ -2183,10 +2229,12 @@ int getfile(int fnum, char_u *ffname_arg, char_u *sfname_arg, int setpm, goto theend; } } - if (other) + if (other) { --no_wait_return; - if (setpm) + } + if (setpm) { setpcmark(); + } if (!other) { if (lnum != 0) { curwin->w_cursor.lnum = lnum; @@ -2218,7 +2266,7 @@ theend: /// - NULL to start an empty buffer /// @param sfname the short file name (or NULL) /// @param eap contains the command to be executed after loading the file -/// and forced 'ff' and 'fenc' +/// and forced 'ff' and 'fenc'. Can be NULL! /// @param newlnum if > 0: put cursor on this line number (if possible) /// ECMD_LASTL: use last position in loaded file /// ECMD_LAST: use last position in all files @@ -2236,55 +2284,52 @@ theend: /// info of the previous buffer for "oldwin" is stored. /// /// @return FAIL for failure, OK otherwise -int do_ecmd( - int fnum, - char_u *ffname, - char_u *sfname, - exarg_T *eap, /* can be NULL! */ - linenr_T newlnum, - int flags, - win_T *oldwin -) +int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T newlnum, int flags, + win_T *oldwin) { - int other_file; /* TRUE if editing another file */ - int oldbuf; /* TRUE if using existing buffer */ + int other_file; // TRUE if editing another file + int oldbuf; // TRUE if using existing buffer int auto_buf = FALSE; /* TRUE if autocommands brought us into the buffer unexpectedly */ - char_u *new_name = NULL; + char_u *new_name = NULL; int did_set_swapcommand = FALSE; - buf_T *buf; + buf_T *buf; bufref_T bufref; bufref_T old_curbuf; - char_u *free_fname = NULL; + char_u *free_fname = NULL; int retval = FAIL; long n; pos_T orig_pos; linenr_T topline = 0; int newcol = -1; int solcol = -1; - pos_T *pos; - char_u *command = NULL; + pos_T *pos; + char_u *command = NULL; int did_get_winopts = FALSE; int readfile_flags = 0; bool did_inc_redrawing_disabled = false; long *so_ptr = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so; - if (eap != NULL) + if (eap != NULL) { command = eap->do_ecmd_cmd; + } set_bufref(&old_curbuf, curbuf); if (fnum != 0) { - if (fnum == curbuf->b_fnum) /* file is already being edited */ - return OK; /* nothing to do */ + if (fnum == curbuf->b_fnum) { // file is already being edited + return OK; // nothing to do + } other_file = TRUE; } else { - /* if no short name given, use ffname for short name */ - if (sfname == NULL) + // if no short name given, use ffname for short name + if (sfname == NULL) { sfname = ffname; + } #ifdef USE_FNAME_CASE - if (sfname != NULL) + if (sfname != NULL) { path_fix_case(sfname); // set correct case for sfname + } #endif if ((flags & (ECMD_ADDBUF | ECMD_ALTBUF)) @@ -2292,19 +2337,21 @@ int do_ecmd( goto theend; } - if (ffname == NULL) + if (ffname == NULL) { other_file = TRUE; - /* there is no file name */ - else if (*ffname == NUL && curbuf->b_ffname == NULL) + } + // there is no file name + else if (*ffname == NUL && curbuf->b_ffname == NULL) { other_file = FALSE; - else { - if (*ffname == NUL) { /* re-edit with same file name */ + } else { + if (*ffname == NUL) { // re-edit with same file name ffname = curbuf->b_ffname; sfname = curbuf->b_fname; } - free_fname = (char_u *)fix_fname((char *)ffname); /* may expand to full path name */ - if (free_fname != NULL) + free_fname = (char_u *)fix_fname((char *)ffname); // may expand to full path name + if (free_fname != NULL) { ffname = free_fname; + } other_file = otherfile(ffname); } } @@ -2378,13 +2425,14 @@ int do_ecmd( if (command != NULL) { tlnum = atol((char *)command); - if (tlnum <= 0) + if (tlnum <= 0) { tlnum = 1L; + } } // Add BLN_NOCURWIN to avoid a new wininfo items are associated // with the current window. const buf_T *const newbuf - = buflist_new(ffname, sfname, tlnum, BLN_LISTED | BLN_NOCURWIN); + = buflist_new(ffname, sfname, tlnum, BLN_LISTED | BLN_NOCURWIN); if (newbuf != NULL && (flags & ECMD_ALTBUF)) { curwin->w_alt_fnum = newbuf->b_fnum; } @@ -2398,8 +2446,9 @@ int do_ecmd( } set_bufref(&old_curbuf, curbuf); } - if (buf == NULL) + if (buf == NULL) { goto theend; + } if (buf->b_ml.ml_mfp == NULL) { // No memfile yet. oldbuf = false; @@ -2458,7 +2507,7 @@ int do_ecmd( delbuf_msg(new_name); // Frees new_name. goto theend; } - if (aborting()) { /* autocmds may abort script processing */ + if (aborting()) { // autocmds may abort script processing xfree(new_name); goto theend; } @@ -2480,10 +2529,9 @@ int do_ecmd( // Close the link to the current buffer. This will set // oldwin->w_buffer to NULL. u_sync(false); - const bool did_decrement = close_buffer( - oldwin, curbuf, - (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, - false); + const bool did_decrement = close_buffer(oldwin, curbuf, + (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, + false); // Autocommands may have closed the window. if (win_valid(the_curwin)) { @@ -2524,7 +2572,7 @@ int do_ecmd( curbuf = buf; ++curbuf->b_nwindows; - /* Set 'fileformat', 'binary' and 'fenc' when forced. */ + // Set 'fileformat', 'binary' and 'fenc' when forced. if (!oldbuf && eap != NULL) { set_file_options(TRUE, eap); set_forced_fenc(eap); @@ -2537,7 +2585,6 @@ int do_ecmd( * values. Also restores old folding stuff. */ get_winopts(curbuf); did_get_winopts = TRUE; - } xfree(new_name); au_new_curbuf.br_buf = NULL; @@ -2569,10 +2616,12 @@ int do_ecmd( /* If autocommands change buffers under our fingers, forget about * editing the file. */ - if (buf != curbuf) + if (buf != curbuf) { goto theend; - if (aborting()) /* autocmds may abort script processing */ + } + if (aborting()) { // autocmds may abort script processing goto theend; + } /* Since we are starting to edit a file, consider the filetype to be * unset. Helps for when an autocommand changes files and expects syntax @@ -2580,14 +2629,14 @@ int do_ecmd( did_filetype = FALSE; /* - * other_file oldbuf - * FALSE FALSE re-edit same file, buffer is re-used - * FALSE TRUE re-edit same file, nothing changes - * TRUE FALSE start editing new file, new buffer - * TRUE TRUE start editing in existing buffer (nothing to do) + * other_file oldbuf + * FALSE FALSE re-edit same file, buffer is re-used + * FALSE TRUE re-edit same file, nothing changes + * TRUE FALSE start editing new file, new buffer + * TRUE TRUE start editing in existing buffer (nothing to do) */ - if (!other_file && !oldbuf) { /* re-use the buffer */ - set_last_cursor(curwin); /* may set b_last_cursor */ + if (!other_file && !oldbuf) { // re-use the buffer + set_last_cursor(curwin); // may set b_last_cursor if (newlnum == ECMD_LAST || newlnum == ECMD_LASTL) { newlnum = curwin->w_cursor.lnum; solcol = curwin->w_cursor.col; @@ -2607,7 +2656,7 @@ int do_ecmd( && (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur)) { // Sync first so that this is a separate undo-able action. u_sync(false); - if (u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, true) + if (u_savecommon(curbuf, 0, curbuf->b_ml.ml_line_count + 1, 0, true) == FAIL) { xfree(new_name); goto theend; @@ -2633,12 +2682,14 @@ int do_ecmd( /* If autocommands change buffers under our fingers, forget about * re-editing the file. Should do the buf_clear_file(), but perhaps * the autocommands changed the buffer... */ - if (buf != curbuf) + if (buf != curbuf) { goto theend; - if (aborting()) /* autocmds may abort script processing */ + } + if (aborting()) { // autocmds may abort script processing goto theend; + } buf_clear_file(curbuf); - curbuf->b_op_start.lnum = 0; /* clear '[ and '] marks */ + curbuf->b_op_start.lnum = 0; // clear '[ and '] marks curbuf->b_op_end.lnum = 0; } @@ -2646,7 +2697,7 @@ int do_ecmd( * If we get here we are sure to start editing */ - /* Assume success now */ + // Assume success now retval = OK; /* @@ -2670,7 +2721,7 @@ int do_ecmd( } } - /* Change directories when the 'acd' option is set. */ + // Change directories when the 'acd' option is set. do_autochdir(); /* @@ -2679,18 +2730,20 @@ int do_ecmd( */ orig_pos = curwin->w_cursor; topline = curwin->w_topline; - if (!oldbuf) { /* need to read the file */ + if (!oldbuf) { // need to read the file swap_exists_action = SEA_DIALOG; - curbuf->b_flags |= BF_CHECK_RO; /* set/reset 'ro' flag */ + curbuf->b_flags |= BF_CHECK_RO; // set/reset 'ro' flag /* * Open the buffer and read the file. */ - if (should_abort(open_buffer(FALSE, eap, readfile_flags))) + if (should_abort(open_buffer(FALSE, eap, readfile_flags))) { retval = FAIL; + } - if (swap_exists_action == SEA_QUIT) + if (swap_exists_action == SEA_QUIT) { retval = FAIL; + } handle_swap_exists(&old_curbuf); } else { /* Read the modelines, but only to set window-local options. Any @@ -2699,9 +2752,9 @@ int do_ecmd( do_modelines(OPT_WINONLY); apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, - &retval); + &retval); apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf, - &retval); + &retval); } check_arg_idx(curwin); @@ -2717,10 +2770,11 @@ int do_ecmd( newcol = curwin->w_cursor.col; } } - if (curwin->w_topline == topline) + if (curwin->w_topline == topline) { topline = 0; + } - /* Even when cursor didn't move we need to recompute topline. */ + // Even when cursor didn't move we need to recompute topline. changed_line_abv_curs(); maketitle(); @@ -2736,50 +2790,54 @@ int do_ecmd( /* If the window options were changed may need to set the spell language. * Can only do this after the buffer has been properly setup. */ - if (did_get_winopts && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) + if (did_get_winopts && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { (void)did_set_spelllang(curwin); + } if (command == NULL) { - if (newcol >= 0) { /* position set by autocommands */ + if (newcol >= 0) { // position set by autocommands curwin->w_cursor.lnum = newlnum; curwin->w_cursor.col = newcol; check_cursor(); - } else if (newlnum > 0) { /* line number from caller or old position */ + } else if (newlnum > 0) { // line number from caller or old position curwin->w_cursor.lnum = newlnum; check_cursor_lnum(); if (solcol >= 0 && !p_sol) { - /* 'sol' is off: Use last known column. */ + // 'sol' is off: Use last known column. curwin->w_cursor.col = solcol; check_cursor_col(); curwin->w_cursor.coladd = 0; curwin->w_set_curswant = TRUE; - } else + } else { beginline(BL_SOL | BL_FIX); - } else { /* no line number, go to last line in Ex mode */ - if (exmode_active) + } + } else { // no line number, go to last line in Ex mode + if (exmode_active) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + } beginline(BL_WHITE | BL_FIX); } } - /* Check if cursors in other windows on the same buffer are still valid */ - check_lnums(FALSE); + // Check if cursors in other windows on the same buffer are still valid + check_lnums(false); /* * Did not read the file, need to show some info about the file. * Do this after setting the cursor. */ if (oldbuf - && !auto_buf - ) { + && !auto_buf) { int msg_scroll_save = msg_scroll; /* Obey the 'O' flag in 'cpoptions': overwrite any previous file * message. */ - if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) + if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) { msg_scroll = FALSE; - if (!msg_scroll) /* wait a bit when overwriting an error msg */ + } + if (!msg_scroll) { // wait a bit when overwriting an error msg check_for_delay(FALSE); + } msg_start(); msg_scroll = msg_scroll_save; msg_scrolled_ign = TRUE; @@ -2793,11 +2851,13 @@ int do_ecmd( curbuf->b_last_used = time(NULL); - if (command != NULL) + if (command != NULL) { do_cmdline(command, NULL, NULL, DOCMD_VERBOSE); + } - if (curbuf->b_kmap_state & KEYMAP_INIT) + if (curbuf->b_kmap_state & KEYMAP_INIT) { (void)keymap_init(); + } RedrawingDisabled--; did_inc_redrawing_disabled = false; @@ -2812,10 +2872,11 @@ int do_ecmd( redraw_curbuf_later(NOT_VALID); // redraw this buffer later } - if (p_im) - need_start_insertmode = TRUE; + if (p_im) { + need_start_insertmode = true; + } - /* Change directories when the 'acd' option is set. */ + // Change directories when the 'acd' option is set. do_autochdir(); @@ -2837,65 +2898,72 @@ theend: static void delbuf_msg(char_u *name) { EMSG2(_("E143: Autocommands unexpectedly deleted new buffer %s"), - name == NULL ? (char_u *)"" : name); + name == NULL ? (char_u *)"" : name); xfree(name); au_new_curbuf.br_buf = NULL; au_new_curbuf.br_buf_free_count = 0; } -static int append_indent = 0; /* autoindent for first line */ +static int append_indent = 0; // autoindent for first line /* * ":insert" and ":append", also used by ":change" */ void ex_append(exarg_T *eap) { - char_u *theline; + char_u *theline; bool did_undo = false; linenr_T lnum = eap->line2; int indent = 0; - char_u *p; + char_u *p; int vcol; int empty = (curbuf->b_ml.ml_flags & ML_EMPTY); - /* the ! flag toggles autoindent */ - if (eap->forceit) + // the ! flag toggles autoindent + if (eap->forceit) { curbuf->b_p_ai = !curbuf->b_p_ai; + } - /* First autoindent comes from the line we start on */ - if (eap->cmdidx != CMD_change && curbuf->b_p_ai && lnum > 0) + // First autoindent comes from the line we start on + if (eap->cmdidx != CMD_change && curbuf->b_p_ai && lnum > 0) { append_indent = get_indent_lnum(lnum); + } - if (eap->cmdidx != CMD_append) + if (eap->cmdidx != CMD_append) { --lnum; + } // when the buffer is empty need to delete the dummy line - if (empty && lnum == 1) + if (empty && lnum == 1) { lnum = 0; + } - State = INSERT; /* behave like in Insert mode */ - if (curbuf->b_p_iminsert == B_IMODE_LMAP) + State = INSERT; // behave like in Insert mode + if (curbuf->b_p_iminsert == B_IMODE_LMAP) { State |= LANGMAP; + } for (;; ) { msg_scroll = TRUE; - need_wait_return = FALSE; + need_wait_return = false; if (curbuf->b_p_ai) { if (append_indent >= 0) { indent = append_indent; append_indent = -1; - } else if (lnum > 0) + } else if (lnum > 0) { indent = get_indent_lnum(lnum); + } } - ex_keep_indent = FALSE; if (eap->getline == NULL) { /* No getline() function, use the lines that follow. This ends * when there is no more. */ - if (eap->nextcmd == NULL || *eap->nextcmd == NUL) + if (eap->nextcmd == NULL || *eap->nextcmd == NUL) { break; + } p = vim_strchr(eap->nextcmd, NL); - if (p == NULL) + if (p == NULL) { p = eap->nextcmd + STRLEN(eap->nextcmd); + } theline = vim_strnsave(eap->nextcmd, p - eap->nextcmd); if (*p != NUL) { p++; @@ -2906,28 +2974,25 @@ void ex_append(exarg_T *eap) // when getline() returns. int save_State = State; State = CMDLINE; - theline = eap->getline( - eap->cstack->cs_looplevel > 0 ? -1 : - NUL, eap->cookie, indent, true); + theline = eap->getline(eap->cstack->cs_looplevel > 0 ? -1 : + NUL, eap->cookie, indent, true); State = save_State; } lines_left = Rows - 1; - if (theline == NULL) + if (theline == NULL) { break; + } - /* Using ^ CTRL-D in getexmodeline() makes us repeat the indent. */ - if (ex_keep_indent) - append_indent = indent; - - /* Look for the "." after automatic indent. */ + // Look for the "." after automatic indent. vcol = 0; for (p = theline; indent > vcol; ++p) { - if (*p == ' ') + if (*p == ' ') { ++vcol; - else if (*p == TAB) + } else if (*p == TAB) { vcol += 8 - vcol % 8; - else + } else { break; + } } if ((p[0] == '.' && p[1] == NUL) || (!did_undo && u_save(lnum, lnum + 1 + (empty ? 1 : 0)) @@ -2936,9 +3001,10 @@ void ex_append(exarg_T *eap) break; } - /* don't use autoindent if nothing was typed. */ - if (p[0] == NUL) + // don't use autoindent if nothing was typed. + if (p[0] == NUL) { theline[0] = NUL; + } did_undo = true; ml_append(lnum, theline, (colnr_T)0, false); @@ -2954,8 +3020,9 @@ void ex_append(exarg_T *eap) } State = NORMAL; - if (eap->forceit) + if (eap->forceit) { curbuf->b_p_ai = !curbuf->b_p_ai; + } /* "start" is set to eap->line2+1 unless that position is invalid (when * eap->line2 pointed to the end of the buffer and nothing was appended) @@ -2963,8 +3030,9 @@ void ex_append(exarg_T *eap) * it is the same than "start" -- Acevedo */ curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ? eap->line2 + 1 : curbuf->b_ml.ml_line_count; - if (eap->cmdidx != CMD_append) + if (eap->cmdidx != CMD_append) { --curbuf->b_op_start.lnum; + } curbuf->b_op_end.lnum = (eap->line2 < lnum) ? lnum : curbuf->b_op_start.lnum; curbuf->b_op_start.col = curbuf->b_op_end.col = 0; @@ -2972,8 +3040,8 @@ void ex_append(exarg_T *eap) check_cursor_lnum(); beginline(BL_SOL | BL_FIX); - need_wait_return = FALSE; /* don't use wait_return() now */ - ex_no_reprint = TRUE; + need_wait_return = false; // don't use wait_return() now + ex_no_reprint = true; } /* @@ -2984,33 +3052,36 @@ void ex_change(exarg_T *eap) linenr_T lnum; if (eap->line2 >= eap->line1 - && u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) + && u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) { return; + } - /* the ! flag toggles autoindent */ - if (eap->forceit ? !curbuf->b_p_ai : curbuf->b_p_ai) + // the ! flag toggles autoindent + if (eap->forceit ? !curbuf->b_p_ai : curbuf->b_p_ai) { append_indent = get_indent_lnum(eap->line1); + } for (lnum = eap->line2; lnum >= eap->line1; --lnum) { - if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */ + if (curbuf->b_ml.ml_flags & ML_EMPTY) { // nothing to delete break; + } ml_delete(eap->line1, false); } - /* make sure the cursor is not beyond the end of the file now */ + // make sure the cursor is not beyond the end of the file now check_cursor_lnum(); deleted_lines_mark(eap->line1, (long)(eap->line2 - lnum)); - /* ":append" on the line above the deleted lines. */ + // ":append" on the line above the deleted lines. eap->line2 = eap->line1; ex_append(eap); } void ex_z(exarg_T *eap) { - char_u *x; + char_u *x; int64_t bigness; - char_u *kind; + char_u *kind; int minus = 0; linenr_T start, end, curs, i; int j; @@ -3032,10 +3103,12 @@ void ex_z(exarg_T *eap) x = eap->arg; kind = x; if (*kind == '-' || *kind == '+' || *kind == '=' - || *kind == '^' || *kind == '.') + || *kind == '^' || *kind == '.') { ++x; - while (*x == '-' || *x == '+') + } + while (*x == '-' || *x == '+') { ++x; + } if (*x != 0) { if (!ascii_isdigit(*x)) { @@ -3055,10 +3128,12 @@ void ex_z(exarg_T *eap) } } - /* the number of '-' and '+' multiplies the distance */ - if (*kind == '-' || *kind == '+') - for (x = kind + 1; *x == *kind; ++x) + // the number of '-' and '+' multiplies the distance + if (*kind == '-' || *kind == '+') { + for (x = kind + 1; *x == *kind; ++x) { ; + } + } switch (*kind) { case '-': @@ -3086,22 +3161,25 @@ void ex_z(exarg_T *eap) curs = end; break; - default: /* '+' */ + default: // '+' start = lnum; - if (*kind == '+') + if (*kind == '+') { start += bigness * (linenr_T)(x - kind - 1) + 1; - else if (eap->addr_count == 0) + } else if (eap->addr_count == 0) { ++start; + } end = start + bigness - 1; curs = end; break; } - if (start < 1) + if (start < 1) { start = 1; + } - if (end > curbuf->b_ml.ml_line_count) + if (end > curbuf->b_ml.ml_line_count) { end = curbuf->b_ml.ml_line_count; + } if (curs > curbuf->b_ml.ml_line_count) { curs = curbuf->b_ml.ml_line_count; @@ -3113,8 +3191,9 @@ void ex_z(exarg_T *eap) if (minus && i == lnum) { msg_putchar('\n'); - for (j = 1; j < Columns; j++) + for (j = 1; j < Columns; j++) { msg_putchar('-'); + } } print_line(i, eap->flags & EXFLAG_NR, eap->flags & EXFLAG_LIST); @@ -3122,8 +3201,9 @@ void ex_z(exarg_T *eap) if (minus && i == lnum) { msg_putchar('\n'); - for (j = 1; j < Columns; j++) + for (j = 1; j < Columns; j++) { msg_putchar('-'); + } } } @@ -3157,7 +3237,7 @@ int check_secure(void) } /// Previous substitute replacement string -static SubReplacementString old_sub = {NULL, 0, NULL}; +static SubReplacementString old_sub = { NULL, 0, NULL }; static int global_need_beginline; // call beginline() after ":g" @@ -3194,8 +3274,7 @@ 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_u *pat, char_u *sub, - char_u *cmd, bool save) +static bool sub_joining_lines(exarg_T *eap, char_u *pat, char_u *sub, char_u *cmd, bool save) FUNC_ATTR_NONNULL_ARG(1, 3, 4) { // TODO(vim): find a generic solution to make line-joining operations more @@ -3222,8 +3301,8 @@ static bool sub_joining_lines(exarg_T *eap, char_u *pat, char_u *sub, // The number of lines joined is the number of lines in the range linenr_T joined_lines_count = eap->line2 - eap->line1 + 1 - // plus one extra line if not at the end of file. - + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0); + // plus one extra line if not at the end of file. + + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0); if (joined_lines_count > 1) { do_join(joined_lines_count, FALSE, TRUE, FALSE, true); sub_nsubs = joined_lines_count - 1; @@ -3290,8 +3369,7 @@ static char_u *sub_grow_buf(char_u **new_start, int needed_len) /// @param[in,out] which_pat pattern type from which to get default search /// /// @returns pointer to the end of the flags, which may be the end of the string -static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags, - int *which_pat) +static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags, int *which_pat) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { // Find trailing options. When '&' is used, keep old options. @@ -3344,6 +3422,15 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags, return cmd; } +static int check_regexp_delim(int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (isalpha(c)) { + EMSG(_("E146: Regular expressions can't be delimited by letters")); + return FAIL; + } + return OK; +} /// Perform a substitution from line eap->line1 to line eap->line2 using the /// command pointed to by eap->arg which should be of the form: @@ -3354,8 +3441,7 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags, /// /// @param do_buf_event If `true`, send buffer updates. /// @return buffer used for 'inccommand' preview -static buf_T *do_sub(exarg_T *eap, proftime_T timeout, - bool do_buf_event, handle_T bufnr) +static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle_T bufnr) { long i = 0; regmmatch_T regmatch; @@ -3400,24 +3486,22 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, } start_nsubs = sub_nsubs; - if (eap->cmdidx == CMD_tilde) - which_pat = RE_LAST; /* use last used regexp */ - else - which_pat = RE_SUBST; /* use last substitute regexp */ - - /* new pattern and substitution */ + if (eap->cmdidx == CMD_tilde) { + which_pat = RE_LAST; // use last used regexp + } else { + which_pat = RE_SUBST; // use last substitute regexp + } + // new pattern and substitution if (eap->cmd[0] == 's' && *cmd != NUL && !ascii_iswhite(*cmd) && vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL) { - /* don't accept alphanumeric for separator */ - if (isalpha(*cmd)) { - EMSG(_("E146: Regular expressions can't be delimited by letters")); + // don't accept alphanumeric for separator + if (check_regexp_delim(*cmd) == FAIL) { return NULL; } - /* - * undocumented vi feature: - * "\/sub/" and "\?sub?" use last used search pattern (almost like - * //sub/r). "\&sub&" use last substitute pattern (like //sub/). - */ + + // undocumented vi feature: + // "\/sub/" and "\?sub?" use last used search pattern (almost like + // //sub/r). "\&sub&" use last substitute pattern (like //sub/). if (*cmd == '\\') { ++cmd; if (vim_strchr((char_u *)"/?&", *cmd) == NULL) { @@ -3445,11 +3529,11 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, * Small incompatibility: vi sees '\n' as end of the command, but in * Vim we want to use '\n' to find/substitute a NUL. */ - sub = cmd; /* remember the start of the substitution */ + sub = cmd; // remember the start of the substitution while (cmd[0]) { - if (cmd[0] == delimiter) { /* end delimiter found */ - *cmd++ = NUL; /* replace it with a NUL */ + if (cmd[0] == delimiter) { // end delimiter found + *cmd++ = NUL; // replace it with a NUL break; } if (cmd[0] == '\\' && cmd[1] != 0) { // skip escaped characters @@ -3460,18 +3544,18 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, if (!eap->skip && !preview) { sub_set_replacement((SubReplacementString) { - .sub = xstrdup((char *) sub), + .sub = xstrdup((char *)sub), .timestamp = os_time(), .additional_elements = NULL, }); } - } else if (!eap->skip) { /* use previous pattern and substitution */ - if (old_sub.sub == NULL) { /* there is no previous command */ + } else if (!eap->skip) { // use previous pattern and substitution + if (old_sub.sub == NULL) { // there is no previous command EMSG(_(e_nopresub)); return NULL; } - pat = NULL; /* search_regcomp() will use previous pattern */ - sub = (char_u *) old_sub.sub; + pat = NULL; // search_regcomp() will use previous pattern + sub = (char_u *)old_sub.sub; /* Vi compatibility quirk: repeating with ":s" keeps the cursor in the * last column after using "$". */ @@ -3497,15 +3581,16 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, } eap->line1 = eap->line2; eap->line2 += i - 1; - if (eap->line2 > curbuf->b_ml.ml_line_count) + if (eap->line2 > curbuf->b_ml.ml_line_count) { eap->line2 = curbuf->b_ml.ml_line_count; + } } /* * check for trailing command or garbage */ cmd = skipwhite(cmd); - if (*cmd && *cmd != '"') { /* if not end-of-line or comment */ + if (*cmd && *cmd != '"') { // if not end-of-line or comment eap->nextcmd = check_nextcmd(cmd); if (eap->nextcmd == NULL) { EMSG(_(e_trailing)); @@ -3563,8 +3648,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, colnr_T copycol; colnr_T matchcol; colnr_T prev_matchcol = MAXCOL; - char_u *new_end, *new_start = NULL; - char_u *p1; + char_u *new_end, *new_start = NULL; + char_u *p1; int did_sub = FALSE; int lastone; long nmatch_tl = 0; // nr of lines matched below lnum @@ -3575,29 +3660,29 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, /* * The new text is build up step by step, to avoid too much * copying. There are these pieces: - * sub_firstline The old text, unmodified. - * copycol Column in the old text where we started - * looking for a match; from here old text still - * needs to be copied to the new text. - * matchcol Column number of the old text where to look - * for the next match. It's just after the - * previous match or one further. - * prev_matchcol Column just after the previous match (if any). - * Mostly equal to matchcol, except for the first - * match and after skipping an empty match. - * regmatch.*pos Where the pattern matched in the old text. - * new_start The new text, all that has been produced so - * far. - * new_end The new text, where to append new text. + * sub_firstline The old text, unmodified. + * copycol Column in the old text where we started + * looking for a match; from here old text still + * needs to be copied to the new text. + * matchcol Column number of the old text where to look + * for the next match. It's just after the + * previous match or one further. + * prev_matchcol Column just after the previous match (if any). + * Mostly equal to matchcol, except for the first + * match and after skipping an empty match. + * regmatch.*pos Where the pattern matched in the old text. + * new_start The new text, all that has been produced so + * far. + * new_end The new text, where to append new text. * - * lnum The line number where we found the start of - * the match. Can be below the line we searched - * when there is a \n before a \zs in the - * pattern. - * sub_firstlnum The line number in the buffer where to look - * for a match. Can be different from "lnum" - * when the pattern or substitute string contains - * line breaks. + * lnum The line number where we found the start of + * the match. Can be below the line we searched + * when there is a \n before a \zs in the + * pattern. + * sub_firstlnum The line number in the buffer where to look + * for a match. Can be different from "lnum" + * when the pattern or substitute string contains + * line breaks. * * Special situations: * - When the substitute string contains a line break, the part up @@ -3620,7 +3705,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, copycol = 0; matchcol = 0; - /* At first match, remember current cursor position. */ + // At first match, remember current cursor position. if (!got_match) { setpcmark(); got_match = TRUE; @@ -3716,8 +3801,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, did_sub = TRUE; /* Skip the substitution, unless an expression is used, * then it is evaluated in the sandbox. */ - if (!(sub[0] == '\\' && sub[1] == '=')) + if (!(sub[0] == '\\' && sub[1] == '=')) { goto skip; + } } if (subflags.do_ask && !preview) { @@ -3727,7 +3813,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, * properly */ int save_State = State; State = CONFIRM; - setmouse(); /* disable mouse in xterm */ + setmouse(); // disable mouse in xterm curwin->w_cursor.col = regmatch.startpos[0].col; if (curwin->w_p_crb) { @@ -3736,15 +3822,17 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, /* When 'cpoptions' contains "u" don't sync undo when * asking for confirmation. */ - if (vim_strchr(p_cpo, CPO_UNDO) != NULL) + if (vim_strchr(p_cpo, CPO_UNDO) != NULL) { ++no_u_sync; + } /* * Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed. */ while (subflags.do_ask) { if (exmode_active) { - char_u *resp; + char *prompt; + char_u *resp; colnr_T sc, ec; print_line_no_prefix(lnum, subflags.do_number, subflags.do_list); @@ -3760,13 +3848,14 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, sc += numw; ec += numw; } - msg_start(); - for (i = 0; i < (long)sc; ++i) - msg_putchar(' '); - for (; i <= (long)ec; ++i) - msg_putchar('^'); - resp = getexmodeline('?', NULL, 0, true); + prompt = xmallocz(ec + 1); + memset(prompt, ' ', sc); + memset(prompt + sc, '^', ec - sc + 1); + resp = (char_u *)getcmdline_prompt(NUL, prompt, 0, EXPAND_NOTHING, + NULL, CALLBACK_NONE); + msg_putchar('\n'); + xfree(prompt); if (resp != NULL) { typed = *resp; xfree(resp); @@ -3816,8 +3905,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, redraw_later(curwin, SOME_VALID); curwin->w_p_fen = save_p_fen; - if (msg_row == Rows - 1) - msg_didout = FALSE; /* avoid a scroll-up */ + if (msg_row == Rows - 1) { + msg_didout = false; // avoid a scroll-up + } msg_starthere(); i = msg_scroll; msg_scroll = 0; /* truncate msg when @@ -3828,7 +3918,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub); msg_no_more = FALSE; msg_scroll = i; - showruler(TRUE); + showruler(true); ui_cursor_goto(msg_row, msg_col); RedrawingDisabled = temp; @@ -3836,8 +3926,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, typed = plain_vgetc(); no_mapping--; - /* clear the question */ - msg_didout = FALSE; /* don't scroll up */ + // clear the question + msg_didout = false; // don't scroll up msg_col = 0; gotocmdline(true); p_lz = save_p_lz; @@ -3853,10 +3943,12 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, got_quit = true; break; } - if (typed == 'n') + if (typed == 'n') { break; - if (typed == 'y') + } + if (typed == 'y') { break; + } if (typed == 'l') { // last: replace and then stop subflags.do_all = false; @@ -3867,15 +3959,17 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, subflags.do_ask = false; break; } - if (typed == Ctrl_E) + if (typed == Ctrl_E) { scrollup_clamp(); - else if (typed == Ctrl_Y) + } else if (typed == Ctrl_Y) { scrolldown_clamp(); + } } State = save_State; setmouse(); - if (vim_strchr(p_cpo, CPO_UNDO) != NULL) + if (vim_strchr(p_cpo, CPO_UNDO) != NULL) { --no_u_sync; + } if (typed == 'n') { /* For a multi-line match, put matchcol at the NUL at @@ -3889,8 +3983,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, } goto skip; } - if (got_quit) + if (got_quit) { goto skip; + } } /* Move the cursor to the start of the match, so that we can @@ -3906,28 +4001,28 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, } #define ADJUST_SUB_FIRSTLNUM() \ - do { \ - /* For a multi-line match, make a copy of the last matched */ \ - /* line and continue in that one. */ \ - if (nmatch > 1) { \ - sub_firstlnum += nmatch - 1; \ - xfree(sub_firstline); \ - sub_firstline = vim_strsave(ml_get(sub_firstlnum)); \ - /* When going beyond the last line, stop substituting. */ \ - if (sub_firstlnum <= line2) { \ - do_again = true; \ - } else { \ - subflags.do_all = false; \ - } \ - } \ - if (skip_match) { \ - /* Already hit end of the buffer, sub_firstlnum is one */ \ - /* less than what it ought to be. */ \ - xfree(sub_firstline); \ - sub_firstline = vim_strsave((char_u *)""); \ - copycol = 0; \ - } \ - } while (0) + do { \ + /* For a multi-line match, make a copy of the last matched */ \ + /* line and continue in that one. */ \ + if (nmatch > 1) { \ + sub_firstlnum += nmatch - 1; \ + xfree(sub_firstline); \ + sub_firstline = vim_strsave(ml_get(sub_firstlnum)); \ + /* When going beyond the last line, stop substituting. */ \ + if (sub_firstlnum <= line2) { \ + do_again = true; \ + } else { \ + subflags.do_all = false; \ + } \ + } \ + if (skip_match) { \ + /* Already hit end of the buffer, sub_firstlnum is one */ \ + /* less than what it ought to be. */ \ + xfree(sub_firstline); \ + sub_firstline = vim_strsave((char_u *)""); \ + copycol = 0; \ + } \ + } while (0) // Save the line numbers for the preview buffer // NOTE: If the pattern matches a final newline, the next line will @@ -4140,8 +4235,9 @@ skip: * it in the buffer. */ ++lnum; - if (u_savedel(lnum, nmatch_tl) != OK) + if (u_savedel(lnum, nmatch_tl) != OK) { break; + } for (i = 0; i < nmatch_tl; i++) { ml_delete(lnum, false); } @@ -4167,7 +4263,7 @@ skip: } sub_firstlnum = lnum; - xfree(sub_firstline); /* free the temp buffer */ + xfree(sub_firstline); // free the temp buffer sub_firstline = new_start; new_start = NULL; matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol; @@ -4175,9 +4271,10 @@ skip: - prev_matchcol; copycol = 0; } - if (nmatch == -1 && !lastone) + if (nmatch == -1 && !lastone) { nmatch = vim_regexec_multi(®match, curwin, curbuf, sub_firstlnum, matchcol, NULL, NULL); + } /* * 5. break if there isn't another match in this line @@ -4186,23 +4283,24 @@ skip: /* If the match found didn't start where we were * searching, do the next search in the line where we * found the match. */ - if (nmatch == -1) + if (nmatch == -1) { lnum -= regmatch.startpos[0].lnum; + } #define PUSH_PREVIEW_LINES() \ - do { \ - linenr_T match_lines = current_match.end.lnum \ - - current_match.start.lnum +1; \ - if (preview_lines.subresults.size > 0) { \ - linenr_T last = kv_last(preview_lines.subresults).end.lnum; \ - if (last == current_match.start.lnum) { \ - preview_lines.lines_needed += match_lines - 1; \ - } \ - } else { \ - preview_lines.lines_needed += match_lines; \ - } \ - kv_push(preview_lines.subresults, current_match); \ - } while (0) + do { \ + linenr_T match_lines = current_match.end.lnum \ + - current_match.start.lnum +1; \ + if (preview_lines.subresults.size > 0) { \ + linenr_T last = kv_last(preview_lines.subresults).end.lnum; \ + if (last == current_match.start.lnum) { \ + preview_lines.lines_needed += match_lines - 1; \ + } \ + } else { \ + preview_lines.lines_needed += match_lines; \ + } \ + kv_push(preview_lines.subresults, current_match); \ + } while (0) // Push the match to preview_lines. PUSH_PREVIEW_LINES(); @@ -4230,6 +4328,8 @@ skip: } } + curbuf->deleted_bytes2 = 0; + if (first_line != 0) { /* Need to subtract the number of added lines from "last_line" to get * the line number before the change (same as adding the number of @@ -4243,7 +4343,7 @@ skip: do_buf_event); } - xfree(sub_firstline); /* may have to free allocated copy of the line */ + xfree(sub_firstline); // may have to free allocated copy of the line // ":s/pat//n" doesn't move the cursor if (subflags.do_count) { @@ -4251,7 +4351,7 @@ skip: } if (sub_nsubs > start_nsubs) { - /* Set the '[ and '] marks. */ + // Set the '[ and '] marks. curbuf->b_op_start.lnum = eap->line1; curbuf->b_op_end.lnum = line2; curbuf->b_op_start.col = curbuf->b_op_end.col = 0; @@ -4330,15 +4430,13 @@ skip: #undef PUSH_PREVIEW_LINES } // NOLINT(readability/fn_size) -/* - * Give message for number of substitutions. - * Can also be used after a ":global" command. - * Return TRUE if a message was given. - */ -bool -do_sub_msg ( - bool count_only /* used 'n' flag for ":s" */ -) +/// Give message for number of substitutions. +/// Can also be used after a ":global" command. +/// +/// @param count_only used 'n' flag for ":s" +/// +/// @return TRUE if a message was given. +bool do_sub_msg(bool count_only) { /* * Only report substitutions when: @@ -4349,27 +4447,31 @@ do_sub_msg ( if (((sub_nsubs > p_report && (KeyTyped || sub_nlines > 1 || p_report < 1)) || count_only) && messaging()) { - if (got_int) + if (got_int) { STRCPY(msg_buf, _("(Interrupted) ")); - else + } else { *msg_buf = NUL; - if (sub_nsubs == 1) + } + if (sub_nsubs == 1) { vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), - "%s", count_only ? _("1 match") : _("1 substitution")); - else + "%s", count_only ? _("1 match") : _("1 substitution")); + } else { vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), - count_only ? _("%" PRId64 " matches") - : _("%" PRId64 " substitutions"), - (int64_t)sub_nsubs); - if (sub_nlines == 1) + count_only ? _("%" PRId64 " matches") + : _("%" PRId64 " substitutions"), + (int64_t)sub_nsubs); + } + if (sub_nlines == 1) { vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), - "%s", _(" on 1 line")); - else + "%s", _(" on 1 line")); + } else { vim_snprintf_add((char *)msg_buf, sizeof(msg_buf), - _(" on %" PRId64 " lines"), (int64_t)sub_nlines); - if (msg(msg_buf)) - /* save message to display it after redraw */ + _(" on %" PRId64 " lines"), (int64_t)sub_nlines); + } + if (msg(msg_buf)) { + // save message to display it after redraw set_keep_msg(msg_buf, 0); + } return true; } if (got_int) { @@ -4408,13 +4510,13 @@ static void global_exe_one(char_u *const cmd, const linenr_T lnum) */ void ex_global(exarg_T *eap) { - linenr_T lnum; /* line number according to old situation */ + linenr_T lnum; // line number according to old situation int ndone = 0; - int type; /* first char of cmd: 'v' or 'g' */ - char_u *cmd; /* command argument */ + int type; // first char of cmd: 'v' or 'g' + char_u *cmd; // command argument - char_u delim; /* delimiter, normally '/' */ - char_u *pat; + char_u delim; // delimiter, normally '/' + char_u *pat; regmmatch_T regmatch; int match; int which_pat; @@ -4428,17 +4530,18 @@ void ex_global(exarg_T *eap) return; } - if (eap->forceit) /* ":global!" is like ":vglobal" */ + if (eap->forceit) { // ":global!" is like ":vglobal" type = 'v'; - else + } else { type = *eap->cmd; + } cmd = eap->arg; - which_pat = RE_LAST; /* default: use last used regexp */ + which_pat = RE_LAST; // default: use last used regexp /* * undocumented vi feature: - * "\/" and "\?": use previous search pattern. - * "\&": use previous substitute pattern. + * "\/" and "\?": use previous search pattern. + * "\&": use previous substitute pattern. */ if (*cmd == '\\') { ++cmd; @@ -4446,23 +4549,28 @@ void ex_global(exarg_T *eap) EMSG(_(e_backslash)); return; } - if (*cmd == '&') - which_pat = RE_SUBST; /* use previous substitute pattern */ - else - which_pat = RE_SEARCH; /* use previous search pattern */ + if (*cmd == '&') { + which_pat = RE_SUBST; // use previous substitute pattern + } else { + which_pat = RE_SEARCH; // use previous search pattern + } ++cmd; pat = (char_u *)""; } else if (*cmd == NUL) { EMSG(_("E148: Regular expression missing from global")); return; + } else if (check_regexp_delim(*cmd) == FAIL) { + return; } else { - delim = *cmd; /* get the delimiter */ - if (delim) - ++cmd; /* skip delimiter if there is one */ - pat = cmd; /* remember start of pattern */ + delim = *cmd; // get the delimiter + if (delim) { + ++cmd; // skip delimiter if there is one + } + pat = cmd; // remember start of pattern cmd = skip_regexp(cmd, delim, p_magic, &eap->arg); - if (cmd[0] == delim) /* end delimiter found */ - *cmd++ = NUL; /* replace it with a NUL */ + if (cmd[0] == delim) { // end delimiter found + *cmd++ = NUL; // replace it with a NUL + } } if (search_regcomp(pat, RE_BOTH, which_pat, SEARCH_HIS, ®match) == FAIL) { @@ -4566,19 +4674,17 @@ void global_exe(char_u *cmd) #if defined(EXITFREE) void free_old_sub(void) { - sub_set_replacement((SubReplacementString) {NULL, 0, NULL}); + sub_set_replacement((SubReplacementString) { NULL, 0, NULL }); } #endif -/* - * Set up for a tagpreview. - * Return TRUE when it was created. - */ -bool -prepare_tagpreview ( - bool undo_sync /* sync undo when leaving the window */ -) +/// Set up for a tagpreview. +/// +/// @param undo_sync sync undo when leaving the window +/// +/// @return TRUE when it was created. +bool prepare_tagpreview(bool undo_sync) { /* * If there is already a preview window open, use that one. @@ -4597,8 +4703,9 @@ prepare_tagpreview ( * There is no preview window open yet. Create one. */ if (win_split(g_do_tagpreview > 0 ? g_do_tagpreview : 0, 0) - == FAIL) + == FAIL) { return false; + } curwin->w_p_pvw = TRUE; curwin->w_p_wfh = TRUE; RESET_BINDING(curwin); /* don't take over 'scrollbind' @@ -4619,20 +4726,20 @@ prepare_tagpreview ( */ void ex_help(exarg_T *eap) { - char_u *arg; - char_u *tag; - FILE *helpfd; /* file descriptor of help file */ + char_u *arg; + char_u *tag; + FILE *helpfd; // file descriptor of help file int n; int i; - win_T *wp; + win_T *wp; int num_matches; - char_u **matches; - char_u *p; + char_u **matches; + char_u *p; int empty_fnum = 0; int alt_fnum = 0; - buf_T *buf; + buf_T *buf; int len; - char_u *lang; + char_u *lang; const bool old_KeyTyped = KeyTyped; if (eap != NULL) { @@ -4655,49 +4762,57 @@ void ex_help(exarg_T *eap) return; } - if (eap->skip) /* not executing commands */ + if (eap->skip) { // not executing commands return; - } else + } + } else { arg = (char_u *)""; + } - /* remove trailing blanks */ + // remove trailing blanks p = arg + STRLEN(arg) - 1; - while (p > arg && ascii_iswhite(*p) && p[-1] != '\\') + while (p > arg && ascii_iswhite(*p) && p[-1] != '\\') { *p-- = NUL; + } - /* Check for a specified language */ + // Check for a specified language lang = check_help_lang(arg); - /* When no argument given go to the index. */ - if (*arg == NUL) + // When no argument given go to the index. + if (*arg == NUL) { arg = (char_u *)"help.txt"; + } /* * Check if there is a match for the argument. */ n = find_help_tags(arg, &num_matches, &matches, - eap != NULL && eap->forceit); + eap != NULL && eap->forceit); i = 0; - if (n != FAIL && lang != NULL) - /* Find first item with the requested language. */ + if (n != FAIL && lang != NULL) { + // Find first item with the requested language. for (i = 0; i < num_matches; ++i) { len = (int)STRLEN(matches[i]); if (len > 3 && matches[i][len - 3] == '@' - && STRICMP(matches[i] + len - 2, lang) == 0) + && STRICMP(matches[i] + len - 2, lang) == 0) { break; + } } + } if (i >= num_matches || n == FAIL) { - if (lang != NULL) + if (lang != NULL) { EMSG3(_("E661: Sorry, no '%s' help for %s"), lang, arg); - else + } else { EMSG2(_("E149: Sorry, no help for %s"), arg); - if (n != FAIL) + } + if (n != FAIL) { FreeWild(num_matches, matches); + } return; } - /* The first match (in the requested language) is the best match. */ + // The first match (in the requested language) is the best match. tag = vim_strsave(matches[i]); FreeWild(num_matches, matches); @@ -4706,8 +4821,7 @@ void ex_help(exarg_T *eap) * Always open a new one for ":tab help". */ if (!bt_help(curwin->w_buffer) - || cmdmod.tab != 0 - ) { + || cmdmod.tab != 0) { if (cmdmod.tab != 0) { wp = NULL; } else { @@ -4735,13 +4849,16 @@ void ex_help(exarg_T *eap) * narrow. */ n = WSP_HELP; if (cmdmod.split == 0 && curwin->w_width != Columns - && curwin->w_width < 80) + && curwin->w_width < 80) { n |= WSP_TOP; - if (win_split(0, n) == FAIL) + } + if (win_split(0, n) == FAIL) { goto erret; + } - if (curwin->w_height < p_hh) + if (curwin->w_height < p_hh) { win_setheight((int)p_hh); + } /* * Open help file (do_ecmd() will set b_help flag, readfile() will @@ -4750,18 +4867,19 @@ void ex_help(exarg_T *eap) */ alt_fnum = curbuf->b_fnum; (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL, - ECMD_HIDE + ECMD_SET_HELP, - NULL /* buffer is still open, don't store info */ - ); - if (!cmdmod.keepalt) + ECMD_HIDE + ECMD_SET_HELP, + NULL // buffer is still open, don't store info + ); + if (!cmdmod.keepalt) { curwin->w_alt_fnum = alt_fnum; + } empty_fnum = curbuf->b_fnum; } } - if (!p_im) - restart_edit = 0; /* don't want insert mode in help file */ - + if (!p_im) { + restart_edit = 0; // don't want insert mode in help file + } /* Restore KeyTyped, setting 'filetype=help' may reset it. * It is needed for do_tag top open folds under the cursor. */ KeyTyped = old_KeyTyped; @@ -4773,13 +4891,15 @@ void ex_help(exarg_T *eap) * window. */ if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum) { buf = buflist_findnr(empty_fnum); - if (buf != NULL && buf->b_nwindows == 0) - wipe_buffer(buf, TRUE); + if (buf != NULL && buf->b_nwindows == 0) { + wipe_buffer(buf, true); + } } - /* keep the previous alternate file */ - if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum && !cmdmod.keepalt) + // keep the previous alternate file + if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum && !cmdmod.keepalt) { curwin->w_alt_fnum = alt_fnum; + } erret: xfree(tag); @@ -4797,37 +4917,37 @@ char_u *check_help_lang(char_u *arg) if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2]) && ASCII_ISALPHA(arg[len - 1])) { - arg[len - 3] = NUL; /* remove the '@' */ + arg[len - 3] = NUL; // remove the '@' return arg + len - 2; } return NULL; } -/* - * Return a heuristic indicating how well the given string matches. The - * smaller the number, the better the match. This is the order of priorities, - * from best match to worst match: - * - Match with least alpha-numeric characters is better. - * - Match with least total characters is better. - * - Match towards the start is better. - * - Match starting with "+" is worse (feature instead of command) - * Assumption is made that the matched_string passed has already been found to - * match some string for which help is requested. webb. - */ -int -help_heuristic( - char_u *matched_string, - int offset, // offset for match - int wrong_case // no matching case -) +/// Return a heuristic indicating how well the given string matches. The +/// smaller the number, the better the match. This is the order of priorities, +/// from best match to worst match: +/// - Match with least alphanumeric characters is better. +/// - Match with least total characters is better. +/// - Match towards the start is better. +/// - Match starting with "+" is worse (feature instead of command) +/// Assumption is made that the matched_string passed has already been found to +/// match some string for which help is requested. webb. +/// +/// @param offset offset for match +/// @param wrong_case no matching case +/// +/// @return a heuristic indicating how well the given string matches. +int help_heuristic(char_u *matched_string, int offset, int wrong_case) { int num_letters; - char_u *p; + char_u *p; num_letters = 0; - for (p = matched_string; *p; p++) - if (ASCII_ISALNUM(*p)) + for (p = matched_string; *p; p++) { + if (ASCII_ISALNUM(*p)) { num_letters++; + } + } /* * Multiply the number of letters by 100 to give it a much bigger @@ -4862,8 +4982,8 @@ help_heuristic( */ static int help_compare(const void *s1, const void *s2) { - char *p1; - char *p2; + char *p1; + char *p2; p1 = *(char **)s1 + strlen(*(char **)s1) + 1; p2 = *(char **)s2 + strlen(*(char **)s2) + 1; @@ -4874,39 +4994,38 @@ static int help_compare(const void *s1, const void *s2) // the number of matches in num_matches. // The matches will be sorted with a "best" match algorithm. // When "keep_lang" is true try keeping the language of the current buffer. -int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, - bool keep_lang) +int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool keep_lang) { int i; static const char *(mtable[]) = { - "*", "g*", "[*", "]*", - "/*", "/\\*", "\"*", "**", - "/\\(\\)", "/\\%(\\)", - "?", ":?", "?<CR>", "g?", "g?g?", "g??", - "-?", "q?", "v_g?", - "/\\?", "/\\z(\\)", "\\=", ":s\\=", - "[count]", "[quotex]", - "[range]", ":[range]", - "[pattern]", "\\|", "\\%$", - "s/\\~", "s/\\U", "s/\\L", - "s/\\1", "s/\\2", "s/\\3", "s/\\9" + "*", "g*", "[*", "]*", + "/*", "/\\*", "\"*", "**", + "/\\(\\)", "/\\%(\\)", + "?", ":?", "?<CR>", "g?", "g?g?", "g??", + "-?", "q?", "v_g?", + "/\\?", "/\\z(\\)", "\\=", ":s\\=", + "[count]", "[quotex]", + "[range]", ":[range]", + "[pattern]", "\\|", "\\%$", + "s/\\~", "s/\\U", "s/\\L", + "s/\\1", "s/\\2", "s/\\3", "s/\\9" }; static const char *(rtable[]) = { - "star", "gstar", "[star", "]star", - "/star", "/\\\\star", "quotestar", "starstar", - "/\\\\(\\\\)", "/\\\\%(\\\\)", - "?", ":?", "?<CR>", "g?", "g?g?", "g??", - "-?", "q?", "v_g?", - "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=", - "\\[count]", "\\[quotex]", - "\\[range]", ":\\[range]", - "\\[pattern]", "\\\\bar", "/\\\\%\\$", - "s/\\\\\\~", "s/\\\\U", "s/\\\\L", - "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9" + "star", "gstar", "[star", "]star", + "/star", "/\\\\star", "quotestar", "starstar", + "/\\\\(\\\\)", "/\\\\%(\\\\)", + "?", ":?", "?<CR>", "g?", "g?g?", "g??", + "-?", "q?", "v_g?", + "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=", + "\\[count]", "\\[quotex]", + "\\[range]", ":\\[range]", + "\\[pattern]", "\\\\bar", "/\\\\%\\$", + "s/\\\\\\~", "s/\\\\U", "s/\\\\L", + "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9" }; static const char *(expr_table[]) = { - "!=?", "!~?", "<=?", "<?", "==?", "=~?", - ">=?", ">?", "is?", "isnot?" + "!=?", "!~?", "<=?", "<?", "==?", "=~?", + ">=?", ">?", "is?", "isnot?" }; char_u *d = IObuff; // assume IObuff is long enough! @@ -4939,7 +5058,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, } } - if (i < 0) { /* no match in table */ + if (i < 0) { // no match in table /* Replace "\S" with "/\\S", etc. Otherwise every tag is matched. * Also replace "\%^" and "\%(", they match every tag too. * Also "\zs", "\z1", etc. @@ -4951,9 +5070,10 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, && arg[2] != NUL))) { STRCPY(d, "/\\\\"); STRCPY(d + 3, arg + 1); - /* Check for "/\\_$", should be "/\\_\$" */ - if (d[3] == '_' && d[4] == '$') + // Check for "/\\_$", should be "/\\_\$" + if (d[3] == '_' && d[4] == '$') { STRCPY(d + 4, "\\$"); + } } else { /* Replace: * "[:...:]" with "\[:...:]" @@ -4962,12 +5082,13 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, */ if ((arg[0] == '[' && (arg[1] == ':' || (arg[1] == '+' && arg[2] == '+'))) - || (arg[0] == '\\' && arg[1] == '{')) + || (arg[0] == '\\' && arg[1] == '{')) { *d++ = '\\'; + } // If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'. if (*arg == '(' && arg[1] == '\'') { - arg++; + arg++; } for (const char_u *s = arg; *s; s++) { // Replace "|" with "bar" and '"' with "quote" to match the name of @@ -4980,19 +5101,24 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, break; } switch (*s) { - case '|': STRCPY(d, "bar"); + case '|': + STRCPY(d, "bar"); d += 3; continue; - case '"': STRCPY(d, "quote"); + case '"': + STRCPY(d, "quote"); d += 5; continue; - case '*': *d++ = '.'; + case '*': + *d++ = '.'; break; - case '?': *d++ = '.'; + case '?': + *d++ = '.'; continue; case '$': case '.': - case '~': *d++ = '\\'; + case '~': + *d++ = '\\'; break; } @@ -5003,31 +5129,36 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, */ if (*s < ' ' || (*s == '^' && s[1] && (ASCII_ISALPHA(s[1]) || vim_strchr((char_u *) - "?@[\\]^", - s[1]) != NULL))) { - if (d > IObuff && d[-1] != '_' && d[-1] != '\\') - *d++ = '_'; /* prepend a '_' to make x_CTRL-x */ + "?@[\\]^", + s[1]) != NULL))) { + if (d > IObuff && d[-1] != '_' && d[-1] != '\\') { + *d++ = '_'; // prepend a '_' to make x_CTRL-x + } STRCPY(d, "CTRL-"); d += 5; if (*s < ' ') { *d++ = *s + '@'; - if (d[-1] == '\\') - *d++ = '\\'; /* double a backslash */ - } else + if (d[-1] == '\\') { + *d++ = '\\'; // double a backslash + } + } else { *d++ = *++s; - if (s[1] != NUL && s[1] != '_') - *d++ = '_'; /* append a '_' */ + } + if (s[1] != NUL && s[1] != '_') { + *d++ = '_'; // append a '_' + } continue; - } else if (*s == '^') /* "^" or "CTRL-^" or "^_" */ + } else if (*s == '^') { // "^" or "CTRL-^" or "^_" *d++ = '\\'; - + } /* * Insert a backslash before a backslash after a slash, for search * pattern tags: "/\|" --> "/\\|". */ else if (s[0] == '\\' && s[1] != '\\' - && *arg == '/' && s == arg + 1) + && *arg == '/' && s == arg + 1) { *d++ = '\\'; + } /* "CTRL-\_" -> "CTRL-\\_" to avoid the special meaning of "\_" in * "CTRL-\_CTRL-N" */ @@ -5059,16 +5190,16 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, if (*IObuff == '`') { if (d > IObuff + 2 && d[-1] == '`') { - /* remove the backticks from `command` */ + // remove the backticks from `command` memmove(IObuff, IObuff + 1, STRLEN(IObuff)); d[-2] = NUL; } else if (d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') { - /* remove the backticks and comma from `command`, */ + // remove the backticks and comma from `command`, memmove(IObuff, IObuff + 1, STRLEN(IObuff)); d[-3] = NUL; } else if (d > IObuff + 4 && d[-3] == '`' && d[-2] == '\\' && d[-1] == '.') { - /* remove the backticks and dot from `command`\. */ + // remove the backticks and dot from `command`\. memmove(IObuff, IObuff + 1, STRLEN(IObuff)); d[-4] = NUL; } @@ -5087,10 +5218,11 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, /* Sort the matches found on the heuristic number that is after the * tag name. */ qsort((void *)*matches, (size_t)*num_matches, - sizeof(char_u *), help_compare); - /* Delete more than TAG_MANY to reduce the size of the listing. */ - while (*num_matches > TAG_MANY) + sizeof(char_u *), help_compare); + // Delete more than TAG_MANY to reduce the size of the listing. + while (*num_matches > TAG_MANY) { xfree((*matches)[--*num_matches]); + } } return OK; } @@ -5142,14 +5274,14 @@ static void prepare_help_buffer(void) void fix_help_buffer(void) { linenr_T lnum; - char_u *line; + char_u *line; bool in_example = false; // Set filetype to "help". if (STRCMP(curbuf->b_p_ft, "help") != 0) { - curbuf_lock++; + curbuf->b_ro_locked++; set_option_value("ft", 0L, "help", OPT_LOCAL); - curbuf_lock--; + curbuf->b_ro_locked--; } if (!syntax_present(curwin)) { @@ -5157,23 +5289,23 @@ void fix_help_buffer(void) line = ml_get_buf(curbuf, lnum, false); const size_t len = STRLEN(line); if (in_example && len > 0 && !ascii_iswhite(line[0])) { - /* End of example: non-white or '<' in first column. */ + // End of example: non-white or '<' in first column. if (line[0] == '<') { - /* blank-out a '<' in the first column */ - line = ml_get_buf(curbuf, lnum, TRUE); + // blank-out a '<' in the first column + line = ml_get_buf(curbuf, lnum, true); line[0] = ' '; } in_example = false; } if (!in_example && len > 0) { if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) { - /* blank-out a '>' in the last column (start of example) */ - line = ml_get_buf(curbuf, lnum, TRUE); + // blank-out a '>' in the last column (start of example) + line = ml_get_buf(curbuf, lnum, true); line[len - 1] = ' '; in_example = true; } else if (line[len - 1] == '~') { - /* blank-out a '~' at the end of line (header marker) */ - line = ml_get_buf(curbuf, lnum, TRUE); + // blank-out a '~' at the end of line (header marker) + line = ml_get_buf(curbuf, lnum, true); line[len - 1] = ' '; } } @@ -5190,12 +5322,12 @@ void fix_help_buffer(void) && ASCII_ISALPHA(fname[5]) && ASCII_ISALPHA(fname[6]) && TOLOWER_ASC(fname[7]) == 'x' - && fname[8] == NUL) - ) { - for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; ++lnum) { - line = ml_get_buf(curbuf, lnum, FALSE); - if (strstr((char *)line, "*local-additions*") == NULL) + && fname[8] == NUL)) { + for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; lnum++) { + line = ml_get_buf(curbuf, lnum, false); + if (strstr((char *)line, "*local-additions*") == NULL) { continue; + } /* Go through all directories in 'runtimepath', skipping * $VIMRUNTIME. */ @@ -5206,10 +5338,10 @@ void fix_help_buffer(void) if (rt != NULL && path_full_compare(rt, NameBuff, false, true) != kEqualFiles) { int fcount; - char_u **fnames; - char_u *s; + char_u **fnames; + char_u *s; vimconv_T vc; - char_u *cp; + char_u *cp; // Find all "doc/ *.txt" files in this directory. if (!add_pathsep((char *)NameBuff) @@ -5221,9 +5353,9 @@ void fix_help_buffer(void) // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. - char_u *buff_list[1] = {NameBuff}; + char_u *buff_list[1] = { NameBuff }; if (gen_expand_wildcards(1, buff_list, &fcount, - &fnames, EW_FILE|EW_SILENT) == OK + &fnames, EW_FILE|EW_SILENT) == OK && fcount > 0) { // If foo.abx is found use it instead of foo.txt in // the same directory. @@ -5280,8 +5412,9 @@ void fix_help_buffer(void) IObuff[0] = '|'; *s = '|'; while (*s != NUL) { - if (*s == '\r' || *s == '\n') + if (*s == '\r' || *s == '\n') { *s = NUL; + } /* The text is utf-8 when a byte * above 127 is found and no * illegal byte sequence is found. @@ -5300,10 +5433,9 @@ void fix_help_buffer(void) * conversion to the current * 'encoding' may be required. */ vc.vc_type = CONV_NONE; - convert_setup( - &vc, - (char_u *)(this_utf == kTrue ? "utf-8" : "latin1"), - p_enc); + convert_setup(&vc, + (char_u *)(this_utf == kTrue ? "utf-8" : "latin1"), + p_enc); if (vc.vc_type == CONV_NONE) { // No conversion needed. cp = IObuff; @@ -5360,15 +5492,15 @@ void ex_viusage(exarg_T *eap) /// French) /// @param add_help_tags Whether to add the "help-tags" tag /// @param ignore_writeerr ignore write error -static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, - bool add_help_tags, bool ignore_writeerr) +static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, bool add_help_tags, + bool ignore_writeerr) FUNC_ATTR_NONNULL_ALL { garray_T ga; int filecount; - char_u **files; - char_u *p1, *p2; - char_u *s; + char_u **files; + char_u *p1, *p2; + char_u *s; TriState utf8 = kNone; bool mix = false; // detected mixed encodings @@ -5383,9 +5515,9 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. - char_u *buff_list[1] = {NameBuff}; + char_u *buff_list[1] = { NameBuff }; if (gen_expand_wildcards(1, buff_list, &filecount, &files, - EW_FILE|EW_SILENT) == FAIL + EW_FILE|EW_SILENT) == FAIL || filecount == 0) { if (!got_int) { EMSG2(_("E151: No match: %s"), NameBuff); @@ -5456,9 +5588,8 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, if (utf8 == kNone) { // first file utf8 = this_utf8; } else if (utf8 != this_utf8) { - EMSG2(_( - "E670: Mix of help file encodings within a language: %s"), - files[fi]); + EMSG2(_("E670: Mix of help file encodings within a language: %s"), + files[fi]); mix = !got_int; got_int = TRUE; } @@ -5513,8 +5644,8 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, if (*p2 == '\t') { *p2 = NUL; vim_snprintf((char *)NameBuff, MAXPATHL, - _("E154: Duplicate tag \"%s\" in file %s/%s"), - ((char_u **)ga.ga_data)[i], dir, p2 + 1); + _("E154: Duplicate tag \"%s\" in file %s/%s"), + ((char_u **)ga.ga_data)[i], dir, p2 + 1); EMSG(NameBuff); *p2 = '\t'; break; @@ -5556,8 +5687,7 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, } /// Generate tags in one help directory, taking care of translations. -static void do_helptags(char_u *dirname, bool add_help_tags, - bool ignore_writeerr) +static void do_helptags(char_u *dirname, bool add_help_tags, bool ignore_writeerr) FUNC_ATTR_NONNULL_ALL { int len; @@ -5578,7 +5708,7 @@ static void do_helptags(char_u *dirname, bool add_help_tags, // Note: We cannot just do `&NameBuff` because it is a statically sized array // so `NameBuff == &NameBuff` according to C semantics. - char_u *buff_list[1] = {NameBuff}; + char_u *buff_list[1] = { NameBuff }; if (gen_expand_wildcards(1, buff_list, &filecount, &files, EW_FILE|EW_SILENT) == FAIL || filecount == 0) { @@ -5596,18 +5726,19 @@ static void do_helptags(char_u *dirname, bool add_help_tags, continue; } if (STRICMP(files[i] + len - 4, ".txt") == 0) { - /* ".txt" -> language "en" */ + // ".txt" -> language "en" lang[0] = 'e'; lang[1] = 'n'; } else if (files[i][len - 4] == '.' && ASCII_ISALPHA(files[i][len - 3]) && ASCII_ISALPHA(files[i][len - 2]) && TOLOWER_ASC(files[i][len - 1]) == 'x') { - /* ".abx" -> language "ab" */ + // ".abx" -> language "ab" lang[0] = TOLOWER_ASC(files[i][len - 3]); lang[1] = TOLOWER_ASC(files[i][len - 2]); - } else + } else { continue; + } // Did we find this language already? for (j = 0; j < ga.ga_len; j += 2) { @@ -5631,11 +5762,11 @@ static void do_helptags(char_u *dirname, bool add_help_tags, fname[5] = ((char_u *)ga.ga_data)[j]; fname[6] = ((char_u *)ga.ga_data)[j + 1]; if (fname[5] == 'e' && fname[6] == 'n') { - /* English is an exception: use ".txt" and "tags". */ + // English is an exception: use ".txt" and "tags". fname[4] = NUL; STRCPY(ext, ".txt"); } else { - /* Language "ab" uses ".abx" and "tags-ab". */ + // Language "ab" uses ".abx" and "tags-ab". STRCPY(ext, ".xxx"); ext[1] = fname[5]; ext[2] = fname[6]; @@ -5650,7 +5781,7 @@ static void do_helptags(char_u *dirname, bool add_help_tags, static void helptags_cb(char_u *fname, void *cookie) FUNC_ATTR_NONNULL_ALL { - do_helptags(fname, *(bool *)cookie, true); + do_helptags(fname, *(bool *)cookie, true); } /* @@ -5662,7 +5793,7 @@ void ex_helptags(exarg_T *eap) char_u *dirname; bool add_help_tags = false; - /* Check for ":helptags ++t {dir}". */ + // Check for ":helptags ++t {dir}". if (STRNCMP(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) { add_help_tags = true; eap->arg = skipwhite(eap->arg + 3); @@ -5718,9 +5849,8 @@ int sub_preview_win(buf_T *preview_buf) /// Shows the effects of the :substitute command being typed ('inccommand'). /// If inccommand=split, shows a preview window and later restores the layout. -static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, - PreviewLines *preview_lines, int hl_id, int src_id, - handle_T bufnr) +static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, int hl_id, + int src_id, handle_T bufnr) FUNC_ATTR_NONNULL_ALL { win_T *save_curwin = curwin; @@ -5869,12 +5999,12 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, /// Closes any open windows for inccommand preview buffer. void close_preview_windows(void) { - block_autocmds(); - buf_T *buf = preview_bufnr ? buflist_findnr(preview_bufnr) : NULL; - if (buf != NULL) { - close_windows(buf, false); - } - unblock_autocmds(); + block_autocmds(); + buf_T *buf = preview_bufnr ? buflist_findnr(preview_bufnr) : NULL; + if (buf != NULL) { + close_windows(buf, false); + } + unblock_autocmds(); } /// :substitute command @@ -5922,7 +6052,9 @@ void ex_substitute(exarg_T *eap) if (save_changedtick != buf_get_changedtick(curbuf)) { // Undo invisibly. This also moves the cursor! - if (!u_undo_and_forget(1)) { abort(); } + if (!u_undo_and_forget(1)) { + abort(); + } // Restore newhead. It is meaningless when curhead is valid, but we must // restore it so that undotree() is identical before/after the preview. curbuf->b_u_newhead = save_b_u_newhead; @@ -5993,7 +6125,7 @@ char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags) /// List v:oldfiles in a nice way. void ex_oldfiles(exarg_T *eap) { - list_T *l = get_vim_var_list(VV_OLDFILES); + list_T *l = get_vim_var_list(VV_OLDFILES); long nr = 0; if (l == NULL) { diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index d99383303b..7b971f464f 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2568,6 +2568,12 @@ module.cmds = { func='ex_spellrepall', }, { + command='spellrare', + flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), + addr_type='ADDR_OTHER', + func='ex_spell', + }, + { command='spellundo', flags=bit.bor(BANG, RANGE, NEEDARG, EXTRA, TRLBAR), addr_type='ADDR_OTHER', diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 56a14887df..e06a62e0f6 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -27,6 +27,7 @@ #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -53,6 +54,7 @@ #include "nvim/os/fs_defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/defs.h" +#include "nvim/lua/executor.h" /// Growarray to store info about already sourced scripts. @@ -133,7 +135,7 @@ void do_debug(char_u *cmd) const bool save_cmd_silent = cmd_silent; int save_msg_silent = msg_silent; int save_emsg_silent = emsg_silent; - int save_redir_off = redir_off; + bool save_redir_off = redir_off; tasave_T typeaheadbuf; bool typeahead_saved = false; int save_ignore_script = 0; @@ -1458,7 +1460,7 @@ bool dialog_close_terminal(buf_T *buf) int ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1); - return (ret == VIM_YES) ? true : false; + return ret == VIM_YES; } /// Return true if the buffer "buf" can be abandoned, either by making it @@ -2421,6 +2423,7 @@ void ex_compiler(exarg_T *eap) if (*eap->arg == NUL) { // List all compiler scripts. do_cmdline_cmd("echo globpath(&rtp, 'compiler/*.vim')"); // NOLINT + do_cmdline_cmd("echo globpath(&rtp, 'compiler/*.lua')"); // NOLINT } else { size_t bufsize = STRLEN(eap->arg) + 14; buf = xmalloc(bufsize); @@ -2445,7 +2448,11 @@ void ex_compiler(exarg_T *eap) snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg); if (source_in_path(p_rtp, buf, DIP_ALL) == FAIL) { - EMSG2(_("E666: compiler not supported: %s"), eap->arg); + // Try lua compiler + snprintf((char *)buf, bufsize, "compiler/%s.lua", eap->arg); + if (source_in_path(p_rtp, buf, DIP_ALL) == FAIL) { + EMSG2(_("E666: compiler not supported: %s"), eap->arg); + } } xfree(buf); @@ -2656,8 +2663,14 @@ static void cmd_source_buffer(const exarg_T *eap) .curr_lnum = eap->line1, .final_lnum = eap->line2, }; - source_using_linegetter((void *)&cookie, get_buffer_line, - ":source (no file)"); + if (curbuf != NULL && curbuf->b_fname + && path_with_extension((const char *)curbuf->b_fname, "lua")) { + nlua_source_using_linegetter(get_buffer_line, (void *)&cookie, + ":source (no file)"); + } else { + source_using_linegetter((void *)&cookie, get_buffer_line, + ":source (no file)"); + } } /// ":source" and associated commands. @@ -2769,7 +2782,8 @@ int do_source_str(const char *cmd, const char *traceback_name) return source_using_linegetter((void *)&cookie, get_str_line, traceback_name); } -/// Reads the file `fname` and executes its lines as Ex commands. +/// When fname is a 'lua' file nlua_exec_file() is invoked to source it. +/// Otherwise reads the file `fname` and executes its lines as Ex commands. /// /// This function may be called recursively! /// @@ -2988,9 +3002,23 @@ int do_source(char_u *fname, int check_other, int is_vimrc) firstline = p; } - // Call do_cmdline, which will call getsourceline() to get the lines. - do_cmdline(firstline, getsourceline, (void *)&cookie, - DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT); + if (path_with_extension((const char *)fname, "lua")) { + // TODO(shadmansaleh): Properly handle :verbose for lua + // For now change currennt_sctx before sourcing lua files + // So verbose doesn't say everything was done in line 1 since we don't know + const sctx_T current_sctx_backup = current_sctx; + const linenr_T sourcing_lnum_backup = sourcing_lnum; + current_sctx.sc_lnum = 0; + sourcing_lnum = 0; + // Source the file as lua + nlua_exec_file((const char *)fname); + current_sctx = current_sctx_backup; + sourcing_lnum = sourcing_lnum_backup; + } else { + // Call do_cmdline, which will call getsourceline() to get the lines. + do_cmdline(firstline, getsourceline, (void *)&cookie, + DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT); + } retval = OK; if (l_do_profiling == PROF_YES) { @@ -3473,7 +3501,7 @@ void do_finish(exarg_T *eap, int reanimate) eap->cookie))->finished = false; } - // Cleanup (and inactivate) conditionals, but stop when a try conditional + // Cleanup (and deactivate) conditionals, but stop when a try conditional // not in its finally clause (which then is to be executed next) is found. // In this case, make the ":finish" pending for execution at the ":endtry". // Otherwise, finish normally. diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index f928c61ea4..d64b14c9c5 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -58,7 +58,7 @@ #define EX_SBOXOK 0x40000 // allowed in the sandbox #define EX_CMDWIN 0x80000 // allowed in cmdline window; when missing // disallows editing another buffer when - // curbuf_lock is set + // current buffer is locked #define EX_MODIFY 0x100000 // forbidden in non-'modifiable' buffer #define EX_FLAGS 0x200000 // allow flags after count in argument #define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 29347def4c..779a5b471f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4,14 +4,13 @@ // ex_docmd.c: functions for executing an Ex command line. #include <assert.h> -#include <string.h> +#include <inttypes.h> #include <stdbool.h> #include <stdlib.h> -#include <inttypes.h> +#include <string.h> -#include "nvim/vim.h" +#include "nvim/api/private/helpers.h" #include "nvim/ascii.h" -#include "nvim/ex_docmd.h" #include "nvim/buffer.h" #include "nvim/change.h" #include "nvim/charset.h" @@ -21,16 +20,26 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/userfunc.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" +#include "nvim/ex_cmds_defs.h" +#include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" +#include "nvim/ex_session.h" +#include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/func_attr.h" +#include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/hardcopy.h" #include "nvim/if_cscope.h" +#include "nvim/keymap.h" +#include "nvim/lua/executor.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -39,20 +48,21 @@ #include "nvim/menu.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/ex_session.h" -#include "nvim/keymap.h" -#include "nvim/file_search.h" -#include "nvim/garray.h" +#include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/os.h" +#include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/search.h" +#include "nvim/shada.h" #include "nvim/sign.h" #include "nvim/spell.h" #include "nvim/spellfile.h" @@ -63,47 +73,37 @@ #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/version.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/os/os.h" -#include "nvim/os/input.h" -#include "nvim/os/time.h" -#include "nvim/ex_cmds_defs.h" -#include "nvim/mouse.h" -#include "nvim/event/rstream.h" -#include "nvim/event/wstream.h" -#include "nvim/shada.h" -#include "nvim/lua/executor.h" -#include "nvim/globals.h" -#include "nvim/api/private/helpers.h" static int quitmore = 0; static bool ex_pressedreturn = false; typedef struct ucmd { - char_u *uc_name; // The command name + char_u *uc_name; // The command name uint32_t uc_argt; // The argument type - char_u *uc_rep; // The command's replacement string + char_u *uc_rep; // The command's replacement string long uc_def; // The default value for a range/count int uc_compl; // completion type cmd_addr_T uc_addr_type; // The command's address type sctx_T uc_script_ctx; // SCTX where the command was defined - char_u *uc_compl_arg; // completion argument if any + char_u *uc_compl_arg; // completion argument if any } ucmd_T; -#define UC_BUFFER 1 /* -buffer: local to current buffer */ +#define UC_BUFFER 1 // -buffer: local to current buffer -static garray_T ucmds = {0, 0, sizeof(ucmd_T), 4, NULL}; +static garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL }; #define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i]) #define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i]) -/* Wether a command index indicates a user command. */ +// Whether a command index indicates a user command. # define IS_USER_CMDIDX(idx) ((int)(idx) < 0) -/* Struct for storing a line inside a while/for loop */ +// Struct for storing a line inside a while/for loop typedef struct { - char_u *line; /* command line */ - linenr_T lnum; /* sourcing_lnum of the line */ + char_u *line; // command line + linenr_T lnum; // sourcing_lnum of the line } wcmd_T; #define FREE_WCMD(wcmd) xfree((wcmd)->line) @@ -114,27 +114,27 @@ typedef struct { * reads more lines that may come from the while/for loop. */ struct loop_cookie { - garray_T *lines_gap; // growarray with line info + garray_T *lines_gap; // growarray with line info 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_u *(*getline)(int, void *, int, bool); - void *cookie; + char_u *(*getline)(int, void *, int, bool); + void *cookie; }; -/* Struct to save a few things while debugging. Used in do_cmdline() only. */ +// Struct to save a few things while debugging. Used in do_cmdline() only. struct dbg_stuff { int trylevel; int force_abort; - except_T *caught_stack; - char_u *vv_exception; - char_u *vv_throwpoint; + except_T *caught_stack; + char_u *vv_exception; + char_u *vv_throwpoint; int did_emsg; int got_int; int need_rethrow; int check_cstack; - except_T *current_exception; + except_T *current_exception; }; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -152,7 +152,7 @@ struct dbg_stuff { # include "ex_cmds_defs.generated.h" #endif -static char_u dollar_command[2] = {'$', 0}; +static char_u dollar_command[2] = { '$', 0 }; static void save_dbg_stuff(struct dbg_stuff *dsp) { @@ -186,23 +186,21 @@ static void restore_dbg_stuff(struct dbg_stuff *dsp) } /// Repeatedly get commands for Ex mode, until the ":vi" command is given. -void do_exmode(int improved) +void do_exmode(void) { int save_msg_scroll; int prev_msg_row; linenr_T prev_line; int changedtick; - if (improved) - exmode_active = EXMODE_VIM; - else - exmode_active = EXMODE_NORMAL; + exmode_active = true; State = NORMAL; /* When using ":global /pat/ visual" and then "Q" we return to continue * the :global command. */ - if (global_busy) + if (global_busy) { return; + } save_msg_scroll = msg_scroll; RedrawingDisabled++; // don't redisplay the window @@ -210,9 +208,9 @@ void do_exmode(int improved) MSG(_("Entering Ex mode. Type \"visual\" to go to Normal mode.")); while (exmode_active) { - /* Check for a ":normal" command and no more characters left. */ + // Check for a ":normal" command and no more characters left. if (ex_normal_busy > 0 && typebuf.tb_len == 0) { - exmode_active = 0; + exmode_active = false; break; } msg_scroll = true; @@ -235,18 +233,20 @@ void do_exmode(int improved) /* go up one line, to overwrite the ":<CR>" line, so the * output doesn't contain empty lines. */ msg_row = prev_msg_row; - if (prev_msg_row == Rows - 1) + if (prev_msg_row == Rows - 1) { msg_row--; + } } msg_col = 0; print_line_no_prefix(curwin->w_cursor.lnum, FALSE, FALSE); msg_clr_eos(); } - } else if (ex_pressedreturn && !ex_no_reprint) { /* must be at EOF */ - if (curbuf->b_ml.ml_flags & ML_EMPTY) + } else if (ex_pressedreturn && !ex_no_reprint) { // must be at EOF + if (curbuf->b_ml.ml_flags & ML_EMPTY) { EMSG(_(e_emptybuf)); - else + } else { EMSG(_("E501: At end-of-file")); + } } } @@ -304,39 +304,39 @@ int do_cmdline_cmd(const char *cmd) /// DOCMD_EXCRESET - Reset the exception environment (used for debugging). /// DOCMD_KEEPLINE - Store first typed line (for repeating with "."). /// +/// @param cookie argument for fgetline() +/// /// @return FAIL if cmdline could not be executed, OK otherwise -int do_cmdline(char_u *cmdline, LineGetter fgetline, - void *cookie, /* argument for fgetline() */ - int flags) -{ - char_u *next_cmdline; /* next cmd to execute */ - char_u *cmdline_copy = NULL; /* copy of cmd line */ - int used_getline = FALSE; /* used "fgetline" to obtain command */ - static int recursive = 0; /* recursive depth */ - int msg_didout_before_start = 0; - int count = 0; /* line number count */ - int did_inc = FALSE; /* incremented RedrawingDisabled */ +int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) +{ + char_u *next_cmdline; // next cmd to execute + char_u *cmdline_copy = NULL; // copy of cmd line + bool used_getline = false; // used "fgetline" to obtain command + static int recursive = 0; // recursive depth + bool msg_didout_before_start = false; + int count = 0; // line number count + int did_inc = FALSE; // incremented RedrawingDisabled int retval = OK; cstack_T cstack = { // conditional stack .cs_idx = -1, }; garray_T lines_ga; // keep lines for ":while"/":for" int current_line = 0; // active line in lines_ga - char_u *fname = NULL; // function or script name + char_u *fname = NULL; // function or script name linenr_T *breakpoint = NULL; // ptr to breakpoint field in cookie - int *dbg_tick = NULL; // ptr to dbg_tick field in cookie + int *dbg_tick = NULL; // ptr to dbg_tick field in cookie struct dbg_stuff debug_saved; // saved things for debug mode int initial_trylevel; - struct msglist **saved_msg_list = NULL; - struct msglist *private_msg_list; + struct msglist **saved_msg_list = NULL; + struct msglist *private_msg_list; // "fgetline" and "cookie" passed to do_one_cmd() - char_u *(*cmd_getline)(int, void *, int, bool); - void *cmd_cookie; + char_u *(*cmd_getline)(int, void *, int, bool); + void *cmd_cookie; struct loop_cookie cmd_loop_cookie; - void *real_cookie; + void *real_cookie; int getline_is_func; - static int call_depth = 0; /* recursiveness */ + static int call_depth = 0; // recursiveness /* For every pair of do_cmdline()/do_one_cmd() calls, use an extra memory * location for storing error messages to be converted to an exception. @@ -366,10 +366,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, real_cookie = getline_cookie(fgetline, cookie); - /* Inside a function use a higher nesting level. */ + // Inside a function use a higher nesting level. getline_is_func = getline_equal(fgetline, cookie, get_func_line); - if (getline_is_func && ex_nesting_level == func_level(real_cookie)) + if (getline_is_func && ex_nesting_level == func_level(real_cookie)) { ++ex_nesting_level; + } /* Get the function or script name and the address where the next breakpoint * line and the debug tick for a function or script are stored. */ @@ -425,13 +426,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, do { getline_is_func = getline_equal(fgetline, cookie, get_func_line); - /* stop skipping cmds for an error msg after all endif/while/for */ + // stop skipping cmds for an error msg after all endif/while/for if (next_cmdline == NULL && !force_abort && cstack.cs_idx < 0 - && !(getline_is_func && func_has_abort(real_cookie)) - ) + && !(getline_is_func && + func_has_abort(real_cookie))) { did_emsg = FALSE; + } /* * 1. If repeating a line in a loop, get a line from lines_ga. @@ -439,7 +441,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * 3. If a line is given: Make a copy, so we can mess with it. */ - /* 1. If repeating, get a previous line from lines_ga. */ + // 1. If repeating, get a previous line from lines_ga. if (cstack.cs_looplevel > 0 && current_line < lines_ga.ga_len) { /* Each '|' separated command is stored separately in lines_ga, to * be able to jump to it. Don't use next_cmdline now. */ @@ -448,49 +450,50 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, /* Check if a function has returned or, unless it has an unclosed * try conditional, aborted. */ if (getline_is_func) { - if (do_profiling == PROF_YES) + if (do_profiling == PROF_YES) { func_line_end(real_cookie); + } if (func_has_ended(real_cookie)) { retval = FAIL; break; } } else if (do_profiling == PROF_YES - && getline_equal(fgetline, cookie, getsourceline)) + && getline_equal(fgetline, cookie, getsourceline)) { script_line_end(); + } - /* Check if a sourced file hit a ":finish" command. */ + // Check if a sourced file hit a ":finish" command. if (source_finished(fgetline, cookie)) { retval = FAIL; break; } - /* If breakpoints have been added/deleted need to check for it. */ + // If breakpoints have been added/deleted need to check for it. if (breakpoint != NULL && dbg_tick != NULL && *dbg_tick != debug_tick) { - *breakpoint = dbg_find_breakpoint( - getline_equal(fgetline, cookie, getsourceline), - fname, sourcing_lnum); + *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline), + fname, sourcing_lnum); *dbg_tick = debug_tick; } next_cmdline = ((wcmd_T *)(lines_ga.ga_data))[current_line].line; sourcing_lnum = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum; - /* Did we encounter a breakpoint? */ + // Did we encounter a breakpoint? if (breakpoint != NULL && *breakpoint != 0 && *breakpoint <= sourcing_lnum) { dbg_breakpoint(fname, sourcing_lnum); - /* Find next breakpoint. */ - *breakpoint = dbg_find_breakpoint( - getline_equal(fgetline, cookie, getsourceline), - fname, sourcing_lnum); + // Find next breakpoint. + *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline), + fname, sourcing_lnum); *dbg_tick = debug_tick; } if (do_profiling == PROF_YES) { - if (getline_is_func) + if (getline_is_func) { func_line_start(real_cookie); - else if (getline_equal(fgetline, cookie, getsourceline)) + } else if (getline_equal(fgetline, cookie, getsourceline)) { script_line_start(); + } } } @@ -512,7 +515,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, cmd_cookie = cookie; } - /* 2. If no line given, get an allocated line with fgetline(). */ + // 2. If no line given, get an allocated line with fgetline(). if (next_cmdline == NULL) { /* * Need to set msg_didout for the first line after an ":if", @@ -535,20 +538,21 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, retval = FAIL; break; } - used_getline = TRUE; + used_getline = true; /* * Keep the first typed line. Clear it when more lines are typed. */ if (flags & DOCMD_KEEPLINE) { xfree(repeat_cmdline); - if (count == 0) + if (count == 0) { repeat_cmdline = vim_strsave(next_cmdline); - else + } else { repeat_cmdline = NULL; + } } } - /* 3. Make a copy of the command so we can mess with it. */ + // 3. Make a copy of the command so we can mess with it. else if (cmdline_copy == NULL) { next_cmdline = vim_strsave(next_cmdline); } @@ -565,7 +569,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, && (cstack.cs_looplevel || has_loop_cmd(next_cmdline))) { store_loop_line(&lines_ga, next_cmdline); } - did_endif = FALSE; + did_endif = false; if (count++ == 0) { /* @@ -576,10 +580,10 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, */ if (!(flags & DOCMD_NOWAIT) && !recursive) { msg_didout_before_start = msg_didout; - msg_didany = FALSE; /* no output yet */ + msg_didany = false; // no output yet msg_start(); - msg_scroll = TRUE; /* put messages below each other */ - ++no_wait_return; /* don't wait for return until finished */ + msg_scroll = TRUE; // put messages below each other + ++no_wait_return; // don't wait for return until finished ++RedrawingDisabled; did_inc = TRUE; } @@ -605,10 +609,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, next_cmdline = NULL; } - if (cmd_cookie == (void *)&cmd_loop_cookie) + if (cmd_cookie == (void *)&cmd_loop_cookie) { /* Use "current_line" from "cmd_loop_cookie", it may have been * incremented when defining a function. */ current_line = cmd_loop_cookie.current_line; + } if (next_cmdline == NULL) { XFREE_CLEAR(cmdline_copy); @@ -630,11 +635,12 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, } - /* reset did_emsg for a function that is not aborted by an error */ + // reset did_emsg for a function that is not aborted by an error if (did_emsg && !force_abort && getline_equal(fgetline, cookie, get_func_line) - && !func_has_abort(real_cookie)) + && !func_has_abort(real_cookie)) { did_emsg = FALSE; + } if (cstack.cs_looplevel > 0) { ++current_line; @@ -659,24 +665,24 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, && cstack.cs_line[cstack.cs_idx] >= 0 && (cstack.cs_flags[cstack.cs_idx] & CSF_ACTIVE)) { current_line = cstack.cs_line[cstack.cs_idx]; - /* remember we jumped there */ + // remember we jumped there cstack.cs_lflags |= CSL_HAD_LOOP; - line_breakcheck(); /* check if CTRL-C typed */ + line_breakcheck(); // check if CTRL-C typed /* Check for the next breakpoint at or after the ":while" * or ":for". */ if (breakpoint != NULL) { - *breakpoint = dbg_find_breakpoint( - getline_equal(fgetline, cookie, getsourceline), - fname, - ((wcmd_T *)lines_ga.ga_data)[current_line].lnum-1); + *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline), + fname, + ((wcmd_T *)lines_ga.ga_data)[current_line].lnum-1); *dbg_tick = debug_tick; } } else { - /* can only get here with ":endwhile" or ":endfor" */ - if (cstack.cs_idx >= 0) + // can only get here with ":endwhile" or ":endfor" + if (cstack.cs_idx >= 0) { rewind_conditionals(&cstack, cstack.cs_idx - 1, - CSF_WHILE | CSF_FOR, &cstack.cs_looplevel); + CSF_WHILE | CSF_FOR, &cstack.cs_looplevel); + } } } /* @@ -729,9 +735,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, force_abort = false; } - /* Convert an interrupt to an exception if appropriate. */ + // Convert an interrupt to an exception if appropriate. (void)do_intthrow(&cstack); - } /* * Continue executing command lines when: @@ -750,14 +755,13 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * the :endtry to be missed. */ && (cstack.cs_trylevel == 0 || did_emsg_syntax) && used_getline - && (getline_equal(fgetline, cookie, getexmodeline) - || getline_equal(fgetline, cookie, getexline))) + && getline_equal(fgetline, cookie, getexline)) && (next_cmdline != NULL || cstack.cs_idx >= 0 || (flags & DOCMD_REPEAT))); xfree(cmdline_copy); - did_emsg_syntax = FALSE; + did_emsg_syntax = false; GA_DEEP_CLEAR(&lines_ga, wcmd_T, FREE_WCMD); if (cstack.cs_idx >= 0) { @@ -770,14 +774,15 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, && !source_finished(fgetline, cookie)) || (getline_equal(fgetline, cookie, get_func_line) && !func_has_ended(real_cookie)))) { - if (cstack.cs_flags[cstack.cs_idx] & CSF_TRY) + if (cstack.cs_flags[cstack.cs_idx] & CSF_TRY) { EMSG(_(e_endtry)); - else if (cstack.cs_flags[cstack.cs_idx] & CSF_WHILE) + } else if (cstack.cs_flags[cstack.cs_idx] & CSF_WHILE) { EMSG(_(e_endwhile)); - else if (cstack.cs_flags[cstack.cs_idx] & CSF_FOR) + } else if (cstack.cs_flags[cstack.cs_idx] & CSF_FOR) { EMSG(_(e_endfor)); - else + } else { EMSG(_(e_endif)); + } } /* @@ -790,10 +795,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, do { int idx = cleanup_conditionals(&cstack, 0, TRUE); - if (idx >= 0) - --idx; /* remove try block not in its finally clause */ + if (idx >= 0) { + --idx; // remove try block not in its finally clause + } rewind_conditionals(&cstack, idx, CSF_WHILE | CSF_FOR, - &cstack.cs_looplevel); + &cstack.cs_looplevel); } while (cstack.cs_idx >= 0); trylevel = initial_trylevel; } @@ -825,8 +831,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, switch (current_exception->type) { case ET_USER: vim_snprintf((char *)IObuff, IOSIZE, - _("E605: Exception not caught: %s"), - current_exception->value); + _("E605: Exception not caught: %s"), + current_exception->value); p = vim_strsave(IObuff); break; case ET_ERROR: @@ -890,27 +896,30 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, check_cstack = true; } } else { - /* When leaving a function, reduce nesting level. */ - if (getline_equal(fgetline, cookie, get_func_line)) + // When leaving a function, reduce nesting level. + if (getline_equal(fgetline, cookie, get_func_line)) { --ex_nesting_level; + } /* * Go to debug mode when returning from a function in which we are * single-stepping. */ if ((getline_equal(fgetline, cookie, getsourceline) || getline_equal(fgetline, cookie, get_func_line)) - && ex_nesting_level + 1 <= debug_break_level) + && ex_nesting_level + 1 <= debug_break_level) { do_debug(getline_equal(fgetline, cookie, getsourceline) ? (char_u *)_("End of sourced file") : (char_u *)_("End of function")); + } } /* * Restore the exception environment (done after returning from the * debugger). */ - if (flags & DOCMD_EXCRESET) + if (flags & DOCMD_EXCRESET) { restore_dbg_stuff(&debug_saved); + } msg_list = saved_msg_list; @@ -929,10 +938,9 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * wait for hit-return. Also for an error situation. */ if (retval == FAIL - || (did_endif && KeyTyped && !did_emsg) - ) { - need_wait_return = FALSE; - msg_didany = FALSE; /* don't wait when restarting edit */ + || (did_endif && KeyTyped && !did_emsg)) { + need_wait_return = false; + msg_didany = false; // don't wait when restarting edit } else if (need_wait_return) { /* * The msg_start() above clears msg_didout. The wait_return we do @@ -944,7 +952,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, } } - did_endif = FALSE; /* in case do_cmdline used recursively */ + did_endif = false; // in case do_cmdline used recursively call_depth--; end_batch_changes(); @@ -956,14 +964,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, */ static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat) { - struct loop_cookie *cp = (struct loop_cookie *)cookie; - wcmd_T *wp; - char_u *line; + struct loop_cookie *cp = (struct loop_cookie *)cookie; + wcmd_T *wp; + char_u *line; if (cp->current_line + 1 >= cp->lines_gap->ga_len) { - if (cp->repeating) - return NULL; /* trying to read past ":endwhile"/":endfor" */ - + if (cp->repeating) { + return NULL; // trying to read past ":endwhile"/":endfor" + } // First time inside the ":while"/":for": get line normally. if (cp->getline == NULL) { line = getcmdline(c, 0L, indent, do_concat); @@ -995,13 +1003,11 @@ static void store_loop_line(garray_T *gap, char_u *line) p->lnum = sourcing_lnum; } -/* - * If "fgetline" is get_loop_line(), return TRUE if the getline it uses equals - * "func". * Otherwise return TRUE when "fgetline" equals "func". - */ -int getline_equal(LineGetter fgetline, - void *cookie, /* argument for fgetline() */ - LineGetter func) +/// If "fgetline" is get_loop_line(), return TRUE if the getline it uses equals +/// "func". * Otherwise return TRUE when "fgetline" equals "func". +/// +/// @param cookie argument for fgetline() +int getline_equal(LineGetter fgetline, void *cookie, LineGetter func) { LineGetter gp; struct loop_cookie *cp; @@ -1018,13 +1024,11 @@ int getline_equal(LineGetter fgetline, return gp == func; } -/* - * If "fgetline" is get_loop_line(), return the cookie used by the original - * getline function. Otherwise return "cookie". - */ -void * getline_cookie(LineGetter fgetline, - void *cookie /* argument for fgetline() */ - ) +/// If "fgetline" is get_loop_line(), return the cookie used by the original +/// getline function. Otherwise return "cookie". +/// +/// @param cookie argument for fgetline() +void * getline_cookie(LineGetter fgetline, void *cookie) { LineGetter gp; struct loop_cookie *cp; @@ -1053,16 +1057,18 @@ static int compute_buffer_local_count(int addr_type, int lnum, int offset) int count = offset; buf = firstbuf; - while (buf->b_next != NULL && buf->b_fnum < lnum) + while (buf->b_next != NULL && buf->b_fnum < lnum) { buf = buf->b_next; + } while (count != 0) { count += (count < 0) ? 1 : -1; nextbuf = (offset < 0) ? buf->b_prev : buf->b_next; - if (nextbuf == NULL) + if (nextbuf == NULL) { break; + } buf = nextbuf; - if (addr_type == ADDR_LOADED_BUFFERS) - /* skip over unloaded buffers */ + if (addr_type == ADDR_LOADED_BUFFERS) { + // skip over unloaded buffers while (buf->b_ml.ml_mfp == NULL) { nextbuf = (offset < 0) ? buf->b_prev : buf->b_next; if (nextbuf == NULL) { @@ -1070,13 +1076,15 @@ static int compute_buffer_local_count(int addr_type, int lnum, int offset) } buf = nextbuf; } + } } // we might have gone too far, last buffer is not loaded if (addr_type == ADDR_LOADED_BUFFERS) { while (buf->b_ml.ml_mfp == NULL) { nextbuf = (offset >= 0) ? buf->b_prev : buf->b_next; - if (nextbuf == NULL) + if (nextbuf == NULL) { break; + } buf = nextbuf; } } @@ -1092,8 +1100,9 @@ static int current_win_nr(const win_T *win) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { ++nr; - if (wp == win) + if (wp == win) { break; + } } return nr; } @@ -1104,8 +1113,9 @@ static int current_tab_nr(tabpage_T *tab) FOR_ALL_TABS(tp) { ++nr; - if (tp == tab) + if (tp == tab) { break; + } } return nr; } @@ -1120,87 +1130,87 @@ static int current_tab_nr(tabpage_T *tab) static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) { switch (*arg) { - case 'S': - case Ctrl_S: - case 's': - case Ctrl_N: - case 'n': - case 'j': - case Ctrl_J: - case 'k': - case Ctrl_K: - case 'T': - case Ctrl_R: - case 'r': - case 'R': - case 'K': - case 'J': - case '+': - case '-': - case Ctrl__: - case '_': - case '|': - case ']': - case Ctrl_RSB: - case 'g': - case Ctrl_G: - case Ctrl_V: - case 'v': - case 'h': - case Ctrl_H: - case 'l': - case Ctrl_L: - case 'H': - case 'L': - case '>': - case '<': - case '}': - case 'f': - case 'F': - case Ctrl_F: - case 'i': - case Ctrl_I: - case 'd': - case Ctrl_D: - // window size or any count - eap->addr_type = ADDR_OTHER; // -V1037 - break; + case 'S': + case Ctrl_S: + case 's': + case Ctrl_N: + case 'n': + case 'j': + case Ctrl_J: + case 'k': + case Ctrl_K: + case 'T': + case Ctrl_R: + case 'r': + case 'R': + case 'K': + case 'J': + case '+': + case '-': + case Ctrl__: + case '_': + case '|': + case ']': + case Ctrl_RSB: + case 'g': + case Ctrl_G: + case Ctrl_V: + case 'v': + case 'h': + case Ctrl_H: + case 'l': + case Ctrl_L: + case 'H': + case 'L': + case '>': + case '<': + case '}': + case 'f': + case 'F': + case Ctrl_F: + case 'i': + case Ctrl_I: + case 'd': + case Ctrl_D: + // window size or any count + eap->addr_type = ADDR_OTHER; // -V1037 + break; - case Ctrl_HAT: - case '^': - // buffer number - eap->addr_type = ADDR_BUFFERS; - break; + case Ctrl_HAT: + case '^': + // buffer number + eap->addr_type = ADDR_BUFFERS; + break; - case Ctrl_Q: - case 'q': - case Ctrl_C: - case 'c': - case Ctrl_O: - case 'o': - case Ctrl_W: - case 'w': - case 'W': - case 'x': - case Ctrl_X: - // window number - eap->addr_type = ADDR_WINDOWS; - break; + case Ctrl_Q: + case 'q': + case Ctrl_C: + case 'c': + case Ctrl_O: + case 'o': + case Ctrl_W: + case 'w': + case 'W': + case 'x': + case Ctrl_X: + // window number + eap->addr_type = ADDR_WINDOWS; + break; - case Ctrl_Z: - case 'z': - case 'P': - case 't': - case Ctrl_T: - case 'b': - case Ctrl_B: - case 'p': - case Ctrl_P: - case '=': - case CAR: - // no count - eap->addr_type = ADDR_NONE; - break; + case Ctrl_Z: + case 'z': + case 'P': + case 't': + case Ctrl_T: + case 'b': + case Ctrl_B: + case 'p': + case Ctrl_P: + case '=': + case CAR: + // no count + eap->addr_type = ADDR_NONE; + break; } } @@ -1221,54 +1231,51 @@ static char_u *skip_colon_white(const char_u *p, bool skipleadingwhite) return (char_u *)p; } -/* - * Execute one Ex command. - * - * If 'sourcing' is TRUE, the command will be included in the error message. - * - * 1. skip comment lines and leading space - * 2. handle command modifiers - * 3. skip over the range to find the command - * 4. parse the range - * 5. parse the command - * 6. parse arguments - * 7. switch on command name - * - * Note: "fgetline" can be NULL. - * - * This function may be called recursively! - */ -static char_u * do_one_cmd(char_u **cmdlinep, - int flags, - cstack_T *cstack, - LineGetter fgetline, - void *cookie /* argument for fgetline() */ - ) -{ - char_u *p; +/// Execute one Ex command. +/// +/// If 'sourcing' is TRUE, the command will be included in the error message. +/// +/// 1. skip comment lines and leading space +/// 2. handle command modifiers +/// 3. skip over the range to find the command +/// 4. parse the range +/// 5. parse the command +/// 6. parse arguments +/// 7. switch on command name +/// +/// Note: "fgetline" can be NULL. +/// +/// This function may be called recursively! +/// +/// @param cookie argument for fgetline() +static char_u * do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline, + void *cookie) +{ + char_u *p; linenr_T lnum; long n; - char_u *errormsg = NULL; // error message - char_u *after_modifier = NULL; + char_u *errormsg = NULL; // error message + char_u *after_modifier = NULL; exarg_T ea; const int save_msg_scroll = msg_scroll; cmdmod_T save_cmdmod; const int save_reg_executing = reg_executing; - char_u *cmd; + char_u *cmd; memset(&ea, 0, sizeof(ea)); ea.line1 = 1; ea.line2 = 1; ex_nesting_level++; - /* When the last file has not been edited :q has to be typed twice. */ + // When the last file has not been edited :q has to be typed twice. if (quitmore - /* avoid that a function call in 'statusline' does this */ + // avoid that a function call in 'statusline' does this && !getline_equal(fgetline, cookie, get_func_line) - /* avoid that an autocommand, e.g. QuitPre, does this */ - && !getline_equal(fgetline, cookie, getnextac) - ) + // avoid that an autocommand, e.g. QuitPre, does this + && !getline_equal(fgetline, cookie, + getnextac)) { --quitmore; + } /* * Reset browse, confirm, etc.. They are restored when returning, for @@ -1425,13 +1432,14 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line2 = curbuf->b_ml.ml_line_count; } - if (ea.line2 < 0) + if (ea.line2 < 0) { errormsg = (char_u *)_(e_invrange); - else { - if (ea.line2 == 0) + } else { + if (ea.line2 == 0) { curwin->w_cursor.lnum = 1; - else + } else { curwin->w_cursor.lnum = ea.line2; + } beginline(BL_SOL | BL_FIX); } } @@ -1456,33 +1464,32 @@ static char_u * do_one_cmd(char_u **cmdlinep, } if (p == NULL) { - if (!ea.skip) + if (!ea.skip) { errormsg = (char_u *)_("E464: Ambiguous use of user-defined command"); + } goto doend; } // Check for wrong commands. if (ea.cmdidx == CMD_SIZE) { if (!ea.skip) { STRCPY(IObuff, _("E492: Not an editor command")); + // If the modifier was parsed OK the error must be in the following + // command + char_u *cmdname = after_modifier ? after_modifier : *cmdlinep; if (!(flags & DOCMD_VERBOSE)) { - // If the modifier was parsed OK the error must be in the following - // command - if (after_modifier != NULL) { - append_command(after_modifier); - } else { - append_command(*cmdlinep); - } + append_command(cmdname); } errormsg = IObuff; - did_emsg_syntax = TRUE; + did_emsg_syntax = true; + verify_command(cmdname); } goto doend; } // set when Not Implemented const int ni = !IS_USER_CMDIDX(ea.cmdidx) - && (cmdnames[ea.cmdidx].cmd_func == ex_ni - || cmdnames[ea.cmdidx].cmd_func == ex_script_ni); + && (cmdnames[ea.cmdidx].cmd_func == ex_ni + || cmdnames[ea.cmdidx].cmd_func == ex_script_ni); // Forced commands. @@ -1508,7 +1515,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, if (!MODIFIABLE(curbuf) && (ea.argt & EX_MODIFY) // allow :put in terminals && (!curbuf->terminal || ea.cmdidx != CMD_put)) { - /* Command not allowed in non-'modifiable' buffer */ + // Command not allowed in non-'modifiable' buffer errormsg = (char_u *)_(e_modifiable); goto doend; } @@ -1520,7 +1527,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } - // Disallow editing another buffer when "curbuf_lock" is set. + // Disallow editing another buffer when "curbuf->b_ro_locked" is set. // Do allow ":checktime" (it is postponed). // Do allow ":edit" (check for an argument later). // Do allow ":file" with no arguments (check for an argument later). @@ -1567,8 +1574,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line1 = ea.line2; ea.line2 = lnum; } - if ((errormsg = invalid_range(&ea)) != NULL) + if ((errormsg = invalid_range(&ea)) != NULL) { goto doend; + } } if ((ea.addr_type == ADDR_OTHER) && ea.addr_count == 0) { @@ -1591,19 +1599,21 @@ static char_u * do_one_cmd(char_u **cmdlinep, * option here, so things like % get expanded. */ p = replace_makeprg(&ea, p, cmdlinep); - if (p == NULL) + if (p == NULL) { goto doend; + } /* * Skip to start of argument. * Don't do this for the ":!" command, because ":!! -l" needs the space. */ - if (ea.cmdidx == CMD_bang) + if (ea.cmdidx == CMD_bang) { ea.arg = p; - else + } else { ea.arg = skipwhite(p); + } - // ":file" cannot be run with an argument when "curbuf_lock" is set + // ":file" cannot be run with an argument when "curbuf->b_ro_locked" is set if (ea.cmdidx == CMD_file && *ea.arg != NUL && curbuf_locked()) { goto doend; } @@ -1622,14 +1632,14 @@ static char_u * do_one_cmd(char_u **cmdlinep, } if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { - if (*ea.arg == '>') { /* append */ - if (*++ea.arg != '>') { /* typed wrong */ + if (*ea.arg == '>') { // append + if (*++ea.arg != '>') { // typed wrong errormsg = (char_u *)_("E494: Use w or w>>"); goto doend; } ea.arg = skipwhite(ea.arg + 1); ea.append = TRUE; - } else if (*ea.arg == '!' && ea.cmdidx == CMD_write) { /* :w !filter */ + } else if (*ea.arg == '!' && ea.cmdidx == CMD_write) { // :w !filter ++ea.arg; ea.usefilter = TRUE; } @@ -1637,9 +1647,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, if (ea.cmdidx == CMD_read) { if (ea.forceit) { - ea.usefilter = TRUE; /* :r! filter if ea.forceit */ + ea.usefilter = TRUE; // :r! filter if ea.forceit ea.forceit = FALSE; - } else if (*ea.arg == '!') { /* :r !filter */ + } else if (*ea.arg == '!') { // :r !filter ++ea.arg; ea.usefilter = TRUE; } @@ -1647,7 +1657,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) { ea.amount = 1; - while (*ea.arg == *ea.cmd) { /* count number of '>' or '<' */ + while (*ea.arg == *ea.cmd) { // count number of '>' or '<' ++ea.arg; ++ea.amount; } @@ -1698,67 +1708,67 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.line1 = 1; switch (ea.addr_type) { - case ADDR_LINES: - case ADDR_OTHER: - ea.line2 = curbuf->b_ml.ml_line_count; - break; - case ADDR_LOADED_BUFFERS: - buf = firstbuf; - while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) { - buf = buf->b_next; - } - ea.line1 = buf->b_fnum; - buf = lastbuf; - while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) { - buf = buf->b_prev; - } - ea.line2 = buf->b_fnum; - break; - case ADDR_BUFFERS: - ea.line1 = firstbuf->b_fnum; - ea.line2 = lastbuf->b_fnum; - break; - case ADDR_WINDOWS: - ea.line2 = LAST_WIN_NR; - break; - case ADDR_TABS: - ea.line2 = LAST_TAB_NR; - break; - case ADDR_TABS_RELATIVE: + case ADDR_LINES: + case ADDR_OTHER: + ea.line2 = curbuf->b_ml.ml_line_count; + break; + case ADDR_LOADED_BUFFERS: + buf = firstbuf; + while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) { + buf = buf->b_next; + } + ea.line1 = buf->b_fnum; + buf = lastbuf; + while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) { + buf = buf->b_prev; + } + ea.line2 = buf->b_fnum; + break; + case ADDR_BUFFERS: + ea.line1 = firstbuf->b_fnum; + ea.line2 = lastbuf->b_fnum; + break; + case ADDR_WINDOWS: + ea.line2 = LAST_WIN_NR; + break; + case ADDR_TABS: + ea.line2 = LAST_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + ea.line2 = 1; + break; + case ADDR_ARGUMENTS: + if (ARGCOUNT == 0) { + ea.line1 = ea.line2 = 0; + } else { + ea.line2 = ARGCOUNT; + } + break; + case ADDR_QUICKFIX_VALID: + ea.line2 = qf_get_valid_size(&ea); + if (ea.line2 == 0) { ea.line2 = 1; - break; - case ADDR_ARGUMENTS: - if (ARGCOUNT == 0) { - ea.line1 = ea.line2 = 0; - } else { - ea.line2 = ARGCOUNT; - } - break; - case ADDR_QUICKFIX_VALID: - ea.line2 = qf_get_valid_size(&ea); - if (ea.line2 == 0) { - ea.line2 = 1; - } - break; - case ADDR_NONE: - case ADDR_UNSIGNED: - case ADDR_QUICKFIX: - IEMSG(_("INTERNAL: Cannot use EX_DFLALL " - "with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX")); - break; + } + break; + case ADDR_NONE: + case ADDR_UNSIGNED: + case ADDR_QUICKFIX: + IEMSG(_("INTERNAL: Cannot use EX_DFLALL " + "with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX")); + break; } } // accept numbered register only when no count allowed (:put) if ((ea.argt & EX_REGSTR) && *ea.arg != NUL - /* Do not allow register = for user commands */ + // Do not allow register = for user commands && (!IS_USER_CMDIDX(ea.cmdidx) || *ea.arg != '=') && !((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg))) { if (valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put && !IS_USER_CMDIDX(ea.cmdidx)))) { ea.regname = *ea.arg++; - /* for '=' register: accept the rest of the line as an expression */ + // for '=' register: accept the rest of the line as an expression if (ea.arg[-1] == '=' && ea.arg[0] != NUL) { set_expr_line(vim_strsave(ea.arg)); ea.arg += STRLEN(ea.arg); @@ -1782,8 +1792,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, } if (ea.addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3 ea.line2 = n; - if (ea.addr_count == 0) + if (ea.addr_count == 0) { ea.addr_count = 1; + } } else { ea.line1 = ea.line2; ea.line2 += n - 1; @@ -1821,7 +1832,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, */ if (ea.skip) { switch (ea.cmdidx) { - /* commands that need evaluation */ + // commands that need evaluation case CMD_while: case CMD_endwhile: case CMD_for: @@ -1922,20 +1933,20 @@ static char_u * do_one_cmd(char_u **cmdlinep, * number. Don't do this for a user command. */ if ((ea.argt & EX_BUFNAME) && *ea.arg != NUL && ea.addr_count == 0 - && !IS_USER_CMDIDX(ea.cmdidx) - ) { + && !IS_USER_CMDIDX(ea.cmdidx)) { /* * :bdelete, :bwipeout and :bunload take several arguments, separated * by spaces: find next space (skipping over escaped characters). * The others take one argument: ignore trailing spaces. */ if (ea.cmdidx == CMD_bdelete || ea.cmdidx == CMD_bwipeout - || ea.cmdidx == CMD_bunload) + || ea.cmdidx == CMD_bunload) { p = skiptowhite_esc(ea.arg); - else { + } else { p = ea.arg + STRLEN(ea.arg); - while (p > ea.arg && ascii_iswhite(p[-1])) + while (p > ea.arg && ascii_iswhite(p[-1])) { --p; + } } ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & EX_BUFUNL) != 0, false, false); @@ -1968,8 +1979,9 @@ static char_u * do_one_cmd(char_u **cmdlinep, */ ea.errmsg = NULL; (cmdnames[ea.cmdidx].cmd_func)(&ea); - if (ea.errmsg != NULL) + if (ea.errmsg != NULL) { errormsg = (char_u *)_(ea.errmsg); + } } /* @@ -1979,14 +1991,15 @@ static char_u * do_one_cmd(char_u **cmdlinep, * exception, or reanimate a returned function or finished script file and * return or finish it again. */ - if (need_rethrow) + if (need_rethrow) { do_throw(cstack); - else if (check_cstack) { - if (source_finished(fgetline, cookie)) + } else if (check_cstack) { + if (source_finished(fgetline, cookie)) { do_finish(&ea, TRUE); - else if (getline_equal(fgetline, cookie, get_func_line) - && current_func_returned()) + } else if (getline_equal(fgetline, cookie, get_func_line) + && current_func_returned()) { do_return(&ea, TRUE, FALSE, NULL); + } } need_rethrow = check_cstack = FALSE; @@ -2008,7 +2021,7 @@ doend: emsg(errormsg); } do_errthrow(cstack, - (ea.cmdidx != CMD_SIZE && !IS_USER_CMDIDX(ea.cmdidx)) + (ea.cmdidx != CMD_SIZE && !IS_USER_CMDIDX(ea.cmdidx)) ? cmdnames[(int)ea.cmdidx].cmd_name : (char_u *)NULL); @@ -2020,8 +2033,9 @@ doend: sandbox--; } - if (ea.nextcmd && *ea.nextcmd == NUL) /* not really a next command */ + if (ea.nextcmd && *ea.nextcmd == NUL) { // not really a next command ea.nextcmd = NULL; + } --ex_nesting_level; @@ -2058,8 +2072,7 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) // in ex mode, an empty line works like :+ if (*eap->cmd == NUL && exmode_active - && (getline_equal(eap->getline, eap->cookie, getexmodeline) - || getline_equal(eap->getline, eap->cookie, getexline)) + && getline_equal(eap->getline, eap->cookie, getexline) && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { eap->cmd = (char_u *)"+"; if (!skip_only) { @@ -2081,12 +2094,15 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) p = skip_range(eap->cmd, NULL); switch (*p) { // When adding an entry, also modify cmd_exists(). - case 'a': if (!checkforcmd(&eap->cmd, "aboveleft", 3)) + case 'a': + if (!checkforcmd(&eap->cmd, "aboveleft", 3)) { break; + } cmdmod.split |= WSP_ABOVE; continue; - case 'b': if (checkforcmd(&eap->cmd, "belowright", 3)) { + case 'b': + if (checkforcmd(&eap->cmd, "belowright", 3)) { cmdmod.split |= WSP_BELOW; continue; } @@ -2100,15 +2116,18 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) cmdmod.split |= WSP_BOT; continue; - case 'c': if (!checkforcmd(&eap->cmd, "confirm", 4)) + case 'c': + if (!checkforcmd(&eap->cmd, "confirm", 4)) { break; + } cmdmod.confirm = true; continue; - case 'k': if (checkforcmd(&eap->cmd, "keepmarks", 3)) { + case 'k': + if (checkforcmd(&eap->cmd, "keepmarks", 3)) { cmdmod.keepmarks = true; continue; - } + } if (checkforcmd(&eap->cmd, "keepalt", 5)) { cmdmod.keepalt = true; continue; @@ -2124,49 +2143,52 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) continue; case 'f': { // only accept ":filter {pat} cmd" - char_u *reg_pat; + char_u *reg_pat; - if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) { - break; - } - if (*p == '!') { - cmdmod.filter_force = true; - p = skipwhite(p + 1); - if (*p == NUL || ends_excmd(*p)) { + if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) { break; } - } - if (skip_only) { - p = skip_vimgrep_pat(p, NULL, NULL); - } else { - // NOTE: This puts a NUL after the pattern. - p = skip_vimgrep_pat(p, ®_pat, NULL); - } - if (p == NULL || *p == NUL) { - break; - } - if (!skip_only) { - cmdmod.filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC); - if (cmdmod.filter_regmatch.regprog == NULL) { + if (*p == '!') { + cmdmod.filter_force = true; + p = skipwhite(p + 1); + if (*p == NUL || ends_excmd(*p)) { + break; + } + } + if (skip_only) { + p = skip_vimgrep_pat(p, NULL, NULL); + } else { + // NOTE: This puts a NUL after the pattern. + p = skip_vimgrep_pat(p, ®_pat, NULL); + } + if (p == NULL || *p == NUL) { break; } + if (!skip_only) { + cmdmod.filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC); + if (cmdmod.filter_regmatch.regprog == NULL) { + break; + } + } + eap->cmd = p; + continue; } - eap->cmd = p; - continue; - } // ":hide" and ":hide | cmd" are not modifiers - case 'h': if (p != eap->cmd || !checkforcmd(&p, "hide", 3) - || *p == NUL || ends_excmd(*p)) + case 'h': + if (p != eap->cmd || !checkforcmd(&p, "hide", 3) + || *p == NUL || ends_excmd(*p)) { break; + } eap->cmd = p; cmdmod.hide = true; continue; - case 'l': if (checkforcmd(&eap->cmd, "lockmarks", 3)) { + case 'l': + if (checkforcmd(&eap->cmd, "lockmarks", 3)) { cmdmod.lockmarks = true; continue; - } + } if (!checkforcmd(&eap->cmd, "leftabove", 5)) { break; @@ -2191,12 +2213,15 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) cmdmod.noswapfile = true; continue; - case 'r': if (!checkforcmd(&eap->cmd, "rightbelow", 6)) + case 'r': + if (!checkforcmd(&eap->cmd, "rightbelow", 6)) { break; + } cmdmod.split |= WSP_BELOW; continue; - case 's': if (checkforcmd(&eap->cmd, "sandbox", 3)) { + case 's': + if (checkforcmd(&eap->cmd, "sandbox", 3)) { if (!skip_only) { if (!eap->did_sandbox) { sandbox++; @@ -2204,7 +2229,7 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) eap->did_sandbox = true; } continue; - } + } if (!checkforcmd(&eap->cmd, "silent", 3)) { break; } @@ -2224,32 +2249,34 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) } continue; - case 't': if (checkforcmd(&p, "tab", 3)) { - if (!skip_only) { - long tabnr = get_address( - eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only, false, 1); + case 't': + if (checkforcmd(&p, "tab", 3)) { + if (!skip_only) { + long tabnr = get_address(eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only, false, 1); - if (tabnr == MAXLNUM) { - cmdmod.tab = tabpage_index(curtab) + 1; - } else { - if (tabnr < 0 || tabnr > LAST_TAB_NR) { - *errormsg = (char_u *)_(e_invrange); - return false; + if (tabnr == MAXLNUM) { + cmdmod.tab = tabpage_index(curtab) + 1; + } else { + if (tabnr < 0 || tabnr > LAST_TAB_NR) { + *errormsg = (char_u *)_(e_invrange); + return false; + } + cmdmod.tab = tabnr + 1; } - cmdmod.tab = tabnr + 1; } + eap->cmd = p; + continue; } - eap->cmd = p; - continue; - } if (!checkforcmd(&eap->cmd, "topleft", 2)) { break; } cmdmod.split |= WSP_TOP; continue; - case 'u': if (!checkforcmd(&eap->cmd, "unsilent", 3)) + case 'u': + if (!checkforcmd(&eap->cmd, "unsilent", 3)) { break; + } if (!skip_only) { if (eap->save_msg_silent == -1) { eap->save_msg_silent = msg_silent; @@ -2258,12 +2285,14 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) } continue; - case 'v': if (checkforcmd(&eap->cmd, "vertical", 4)) { + case 'v': + if (checkforcmd(&eap->cmd, "vertical", 4)) { cmdmod.split |= WSP_VERT; continue; - } - if (!checkforcmd(&p, "verbose", 4)) + } + if (!checkforcmd(&p, "verbose", 4)) { break; + } if (!skip_only) { if (eap->verbose_save < 0) { eap->verbose_save = p_verbose; @@ -2335,40 +2364,40 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) for (;;) { eap->line1 = eap->line2; switch (eap->addr_type) { - case ADDR_LINES: - case ADDR_OTHER: - // default is current line number - eap->line2 = curwin->w_cursor.lnum; - break; - case ADDR_WINDOWS: - eap->line2 = CURRENT_WIN_NR; - break; - case ADDR_ARGUMENTS: - eap->line2 = curwin->w_arg_idx + 1; - if (eap->line2 > ARGCOUNT) { - eap->line2 = ARGCOUNT; - } - break; - case ADDR_LOADED_BUFFERS: - case ADDR_BUFFERS: - eap->line2 = curbuf->b_fnum; - break; - case ADDR_TABS: - eap->line2 = CURRENT_TAB_NR; - break; - case ADDR_TABS_RELATIVE: - case ADDR_UNSIGNED: - eap->line2 = 1; - break; - case ADDR_QUICKFIX: - eap->line2 = qf_get_cur_idx(eap); - break; - case ADDR_QUICKFIX_VALID: - eap->line2 = qf_get_cur_valid_idx(eap); - break; - case ADDR_NONE: - // Will give an error later if a range is found. - break; + case ADDR_LINES: + case ADDR_OTHER: + // default is current line number + eap->line2 = curwin->w_cursor.lnum; + break; + case ADDR_WINDOWS: + eap->line2 = CURRENT_WIN_NR; + break; + case ADDR_ARGUMENTS: + eap->line2 = curwin->w_arg_idx + 1; + if (eap->line2 > ARGCOUNT) { + eap->line2 = ARGCOUNT; + } + break; + case ADDR_LOADED_BUFFERS: + case ADDR_BUFFERS: + eap->line2 = curbuf->b_fnum; + break; + case ADDR_TABS: + eap->line2 = CURRENT_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + case ADDR_UNSIGNED: + eap->line2 = 1; + break; + case ADDR_QUICKFIX: + eap->line2 = qf_get_cur_idx(eap); + break; + case ADDR_QUICKFIX_VALID: + eap->line2 = qf_get_cur_valid_idx(eap); + break; + case ADDR_NONE: + // Will give an error later if a range is found. + break; } eap->cmd = skipwhite(eap->cmd); lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent, @@ -2380,12 +2409,12 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) if (*eap->cmd == '%') { // '%' - all lines eap->cmd++; switch (eap->addr_type) { - case ADDR_LINES: - case ADDR_OTHER: - eap->line1 = 1; - eap->line2 = curbuf->b_ml.ml_line_count; - break; - case ADDR_LOADED_BUFFERS: { + case ADDR_LINES: + case ADDR_OTHER: + eap->line1 = 1; + eap->line2 = curbuf->b_ml.ml_line_count; + break; + case ADDR_LOADED_BUFFERS: { buf_T *buf = firstbuf; while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) { @@ -2399,46 +2428,46 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) eap->line2 = buf->b_fnum; break; } - case ADDR_BUFFERS: - eap->line1 = firstbuf->b_fnum; - eap->line2 = lastbuf->b_fnum; - break; - case ADDR_WINDOWS: - case ADDR_TABS: - if (IS_USER_CMDIDX(eap->cmdidx)) { - eap->line1 = 1; - eap->line2 = eap->addr_type == ADDR_WINDOWS + case ADDR_BUFFERS: + eap->line1 = firstbuf->b_fnum; + eap->line2 = lastbuf->b_fnum; + break; + case ADDR_WINDOWS: + case ADDR_TABS: + if (IS_USER_CMDIDX(eap->cmdidx)) { + eap->line1 = 1; + eap->line2 = eap->addr_type == ADDR_WINDOWS ? LAST_WIN_NR : LAST_TAB_NR; - } else { - // there is no Vim command which uses '%' and - // ADDR_WINDOWS or ADDR_TABS - *errormsg = (char_u *)_(e_invrange); - return FAIL; - } - break; - case ADDR_TABS_RELATIVE: - case ADDR_UNSIGNED: - case ADDR_QUICKFIX: + } else { + // there is no Vim command which uses '%' and + // ADDR_WINDOWS or ADDR_TABS *errormsg = (char_u *)_(e_invrange); return FAIL; - case ADDR_ARGUMENTS: - if (ARGCOUNT == 0) { - eap->line1 = eap->line2 = 0; - } else { - eap->line1 = 1; - eap->line2 = ARGCOUNT; - } - break; - case ADDR_QUICKFIX_VALID: + } + break; + case ADDR_TABS_RELATIVE: + case ADDR_UNSIGNED: + case ADDR_QUICKFIX: + *errormsg = (char_u *)_(e_invrange); + return FAIL; + case ADDR_ARGUMENTS: + if (ARGCOUNT == 0) { + eap->line1 = eap->line2 = 0; + } else { eap->line1 = 1; - eap->line2 = qf_get_valid_size(eap); - if (eap->line2 == 0) { - eap->line2 = 1; - } - break; - case ADDR_NONE: - // Will give an error later if a range is found. - break; + eap->line2 = ARGCOUNT; + } + break; + case ADDR_QUICKFIX_VALID: + eap->line1 = 1; + eap->line2 = qf_get_valid_size(eap); + if (eap->line2 == 0) { + eap->line2 = 1; + } + break; + case ADDR_NONE: + // Will give an error later if a range is found. + break; } eap->addr_count++; } else if (*eap->cmd == '*') { @@ -2494,22 +2523,21 @@ int parse_cmd_address(exarg_T *eap, char_u **errormsg, bool silent) return OK; } -/* - * Check for an Ex command with optional tail. - * If there is a match advance "pp" to the argument and return TRUE. - */ -int -checkforcmd( - char_u **pp, // start of command - char *cmd, // name of command - int len // required length -) +/// Check for an Ex command with optional tail. +/// If there is a match advance "pp" to the argument and return TRUE. +/// +/// @param pp start of command +/// @param cmd name of command +/// @param len required length +int checkforcmd(char_u **pp, char *cmd, int len) { int i; - for (i = 0; cmd[i] != NUL; ++i) - if (((char_u *)cmd)[i] != (*pp)[i]) + for (i = 0; cmd[i] != NUL; ++i) { + if (((char_u *)cmd)[i] != (*pp)[i]) { break; + } + } if (i >= len && !isalpha((*pp)[i])) { *pp = skipwhite(*pp + i); return TRUE; @@ -2534,8 +2562,9 @@ static void append_command(char_u *cmd) s += 2; STRCPY(d, "<a0>"); d += 4; - } else + } else { MB_COPY_CHAR(s, d); + } } *d = NUL; } @@ -2549,7 +2578,7 @@ static char_u *find_command(exarg_T *eap, int *full) FUNC_ATTR_NONNULL_ARG(1) { int len; - char_u *p; + char_u *p; int i; /* @@ -2557,8 +2586,8 @@ static char_u *find_command(exarg_T *eap, int *full) * Exceptions: * - the 'k' command can directly be followed by any character. * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' - * but :sre[wind] is another command, as are :scr[iptnames], - * :scs[cope], :sim[alt], :sig[ns] and :sil[ent]. + * but :sre[wind] is another command, as are :scr[iptnames], + * :scs[cope], :sim[alt], :sig[ns] and :sil[ent]. * - the "d" command can directly be followed by 'l' or 'p' flag. */ p = eap->cmd; @@ -2578,29 +2607,36 @@ static char_u *find_command(exarg_T *eap, int *full) eap->cmdidx = CMD_substitute; ++p; } else { - while (ASCII_ISALPHA(*p)) + while (ASCII_ISALPHA(*p)) { ++p; - /* for python 3.x support ":py3", ":python3", ":py3file", etc. */ - if (eap->cmd[0] == 'p' && eap->cmd[1] == 'y') - while (ASCII_ISALNUM(*p)) + } + // for python 3.x support ":py3", ":python3", ":py3file", etc. + if (eap->cmd[0] == 'p' && eap->cmd[1] == 'y') { + while (ASCII_ISALNUM(*p)) { ++p; + } + } - /* check for non-alpha command */ - if (p == eap->cmd && vim_strchr((char_u *)"@!=><&~#", *p) != NULL) + // check for non-alpha command + if (p == eap->cmd && vim_strchr((char_u *)"@!=><&~#", *p) != NULL) { ++p; + } len = (int)(p - eap->cmd); if (*eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) { /* Check for ":dl", ":dell", etc. to ":deletel": that's * :delete with the 'l' flag. Same for 'p'. */ - for (i = 0; i < len; ++i) - if (eap->cmd[i] != ((char_u *)"delete")[i]) + for (i = 0; i < len; ++i) { + if (eap->cmd[i] != ((char_u *)"delete")[i]) { break; + } + } if (i == len - 1) { --len; - if (p[-1] == 'l') + if (p[-1] == 'l') { eap->flags |= EXFLAG_LIST; - else + } else { eap->flags |= EXFLAG_PRINT; + } } } @@ -2624,52 +2660,52 @@ static char_u *find_command(exarg_T *eap, int *full) } for (; (int)eap->cmdidx < (int)CMD_SIZE; - eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) + eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) { if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, (char *)eap->cmd, - (size_t)len) == 0) { + (size_t)len) == 0) { if (full != NULL - && cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL) + && cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL) { *full = TRUE; + } break; } + } // Look for a user defined command as a last resort. if ((eap->cmdidx == CMD_SIZE) && *eap->cmd >= 'A' && *eap->cmd <= 'Z') { - /* User defined commands may contain digits. */ - while (ASCII_ISALNUM(*p)) + // User defined commands may contain digits. + while (ASCII_ISALNUM(*p)) { ++p; + } p = find_ucmd(eap, p, full, NULL, NULL); } - if (p == eap->cmd) + if (p == eap->cmd) { eap->cmdidx = CMD_SIZE; + } } return p; } -/* - * Search for a user command that matches "eap->cmd". - * Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx". - * Return a pointer to just after the command. - * Return NULL if there is no matching command. - */ -static char_u * -find_ucmd ( - exarg_T *eap, - char_u *p, // end of the command (possibly including count) - int *full, // set to TRUE for a full match - expand_T *xp, // used for completion, NULL otherwise - int *complp // completion flags or NULL -) +/// Search for a user command that matches "eap->cmd". +/// Return cmdidx in "eap->cmdidx", flags in "eap->argt", idx in "eap->useridx". +/// Return a pointer to just after the command. +/// Return NULL if there is no matching command. +/// +/// @param *p end of the command (possibly including count) +/// @param full set to TRUE for a full match +/// @param xp used for completion, NULL otherwise +/// @param complp completion flags or NULL +static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *complp) { int len = (int)(p - eap->cmd); int j, k, matchlen = 0; - ucmd_T *uc; + ucmd_T *uc; int found = FALSE; int possible = FALSE; - char_u *cp, *np; /* Point into typed cmd and test name */ - garray_T *gap; + char_u *cp, *np; // Point into typed cmd and test name + garray_T *gap; int amb_local = FALSE; /* Found ambiguous buffer-local command, only full match global is accepted. */ @@ -2683,15 +2719,17 @@ find_ucmd ( cp = eap->cmd; np = uc->uc_name; k = 0; - while (k < len && *np != NUL && *cp++ == *np++) + while (k < len && *np != NUL && *cp++ == *np++) { k++; + } if (k == len || (*np == NUL && ascii_isdigit(eap->cmd[k]))) { /* If finding a second match, the command is ambiguous. But * not if a buffer-local command wasn't a full match and a * global command is a full match. */ if (k == len && found && *np != NUL) { - if (gap == &ucmds) + if (gap == &ucmds) { return NULL; + } amb_local = TRUE; } @@ -2700,15 +2738,17 @@ find_ucmd ( * be another command including the digit that we * should use instead. */ - if (k == len) + if (k == len) { found = TRUE; - else + } else { possible = TRUE; + } - if (gap == &ucmds) + if (gap == &ucmds) { eap->cmdidx = CMD_USER; - else + } else { eap->cmdidx = CMD_USER_BUF; + } eap->argt = uc->uc_argt; eap->useridx = j; eap->addr_type = uc->uc_addr_type; @@ -2725,8 +2765,9 @@ find_ucmd ( * if this is an exact match. */ matchlen = k; if (k == len && *np == NUL) { - if (full != NULL) + if (full != NULL) { *full = TRUE; + } amb_local = FALSE; break; } @@ -2734,30 +2775,33 @@ find_ucmd ( } } - /* Stop if we found a full match or searched all. */ - if (j < gap->ga_len || gap == &ucmds) + // Stop if we found a full match or searched all. + if (j < gap->ga_len || gap == &ucmds) { break; + } gap = &ucmds; } - /* Only found ambiguous matches. */ + // Only found ambiguous matches. if (amb_local) { - if (xp != NULL) + if (xp != NULL) { xp->xp_context = EXPAND_UNSUCCESSFUL; + } return NULL; } /* The match we found may be followed immediately by a number. Move "p" * back to point to it. */ - if (found || possible) + if (found || possible) { return p + (matchlen - len); + } return p; } static struct cmdmod { - char *name; + char *name; int minlen; - int has_count; /* :123verbose :3tab */ + int has_count; // :123verbose :3tab } cmdmods[] = { { "aboveleft", 3, false }, { "belowright", 3, false }, @@ -2790,7 +2834,7 @@ static struct cmdmod { */ int modifier_len(char_u *cmd) { - char_u *p = cmd; + char_u *p = cmd; if (ascii_isdigit(*cmd)) { p = skipwhite(skipdigits(cmd + 1)); @@ -2819,7 +2863,7 @@ int modifier_len(char_u *cmd) int cmd_exists(const char *const name) { exarg_T ea; - char_u *p; + char_u *p; // Check command modifiers. for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) { @@ -2840,26 +2884,26 @@ int cmd_exists(const char *const name) ea.cmdidx = (cmdidx_T)0; int full = false; p = find_command(&ea, &full); - if (p == NULL) + if (p == NULL) { return 3; - if (ascii_isdigit(*name) && ea.cmdidx != CMD_match) + } + if (ascii_isdigit(*name) && ea.cmdidx != CMD_match) { return 0; - if (*skipwhite(p) != NUL) - return 0; /* trailing garbage */ + } + if (*skipwhite(p) != NUL) { + return 0; // trailing garbage + } return ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1); } -/* - * This is all pretty much copied from do_one_cmd(), with all the extra stuff - * we don't need/want deleted. Maybe this could be done better if we didn't - * repeat all this stuff. The only problem is that they may not stay - * perfectly compatible with each other, but then the command line syntax - * probably won't change that much -- webb. - */ -const char * set_one_cmd_context( - expand_T *xp, - const char *buff // buffer for command string -) +/// This is all pretty much copied from do_one_cmd(), with all the extra stuff +/// we don't need/want deleted. Maybe this could be done better if we didn't +/// repeat all this stuff. The only problem is that they may not stay +/// perfectly compatible with each other, but then the command line syntax +/// probably won't change that much -- webb. +/// +/// @param buff buffer for command string +const char * set_one_cmd_context(expand_T *xp, const char *buff) { size_t len = 0; exarg_T ea; @@ -2878,9 +2922,10 @@ const char * set_one_cmd_context( } xp->xp_pattern = (char_u *)cmd; - if (*cmd == NUL) + if (*cmd == NUL) { return NULL; - if (*cmd == '"') { /* ignore comment lines */ + } + if (*cmd == '"') { // ignore comment lines xp->xp_context = EXPAND_NOTHING; return NULL; } @@ -2902,9 +2947,9 @@ const char * set_one_cmd_context( return NULL; } - if (*cmd == '|' || *cmd == '\n') - return cmd + 1; /* There's another command */ - + if (*cmd == '|' || *cmd == '\n') { + return cmd + 1; // There's another command + } /* * Isolate the command and search for it in the command table. * Exceptions: @@ -2958,12 +3003,13 @@ const char * set_one_cmd_context( } } - /* - * If the cursor is touching the command, and it ends in an alpha-numeric - * character, complete the command name. - */ - if (*p == NUL && ASCII_ISALNUM(p[-1])) + // + // If the cursor is touching the command, and it ends in an alphanumeric + // character, complete the command name. + // + if (*p == NUL && ASCII_ISALNUM(p[-1])) { return NULL; + } if (ea.cmdidx == CMD_SIZE) { if (*cmd == 's' && vim_strchr((const char_u *)"cgriI", cmd[1]) != NULL) { @@ -2978,12 +3024,12 @@ const char * set_one_cmd_context( } } if (ea.cmdidx == CMD_SIZE) { - /* Not still touching the command and it was an illegal one */ + // Not still touching the command and it was an illegal one xp->xp_context = EXPAND_UNSUCCESSFUL; return NULL; } - xp->xp_context = EXPAND_NOTHING; /* Default now that we're past command */ + xp->xp_context = EXPAND_NOTHING; // Default now that we're past command if (*p == '!') { // forced commands forceit = true; @@ -3056,9 +3102,10 @@ const char * set_one_cmd_context( */ if ((ea.argt & EX_TRLBAR) && !usefilter) { p = arg; - /* ":redir @" is not the start of a comment */ - if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') + // ":redir @" is not the start of a comment + if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') { p += 2; + } while (*p) { if (*p == Ctrl_V) { if (p[1] != NUL) { @@ -3068,9 +3115,10 @@ const char * set_one_cmd_context( || *p == '|' || *p == '\n') { if (*(p - 1) != '\\') { - if (*p == '|' || *p == '\n') + if (*p == '|' || *p == '\n') { return p + 1; - return NULL; /* It's a comment */ + } + return NULL; // It's a comment } } MB_PTR_ADV(p); @@ -3082,7 +3130,7 @@ const char * set_one_cmd_context( return NULL; } - /* Find start of last argument (argument just before cursor): */ + // Find start of last argument (argument just before cursor): p = buff; xp->xp_pattern = (char_u *)p; len = strlen(buff); @@ -3123,10 +3171,10 @@ const char * set_one_cmd_context( /* An argument can contain just about everything, except * characters that end the command and white space. */ else if (c == '|' - || c == '\n' - || c == '"' - || ascii_iswhite(c)) { - len = 0; /* avoid getting stuck when space is in 'isfname' */ + || c == '\n' + || c == '"' + || ascii_iswhite(c)) { + len = 0; // avoid getting stuck when space is in 'isfname' while (*p != NUL) { c = utf_ptr2char((const char_u *)p); if (c == '`' || vim_isfilec_or_wc(c)) { @@ -3154,7 +3202,7 @@ const char * set_one_cmd_context( } xp->xp_context = EXPAND_FILES; - /* For a shell command more chars need to be escaped. */ + // For a shell command more chars need to be escaped. if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal) { #ifndef BACKSLASH_IN_FILENAME xp->xp_shell = TRUE; @@ -3181,7 +3229,7 @@ const char * set_one_cmd_context( } } } - /* Check for user names */ + // Check for user names if (*xp->xp_pattern == '~') { for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) { } @@ -3203,8 +3251,9 @@ const char * set_one_cmd_context( case CMD_find: case CMD_sfind: case CMD_tabfind: - if (xp->xp_context == EXPAND_FILES) + if (xp->xp_context == EXPAND_FILES) { xp->xp_context = EXPAND_FILES_IN_PATH; + } break; case CMD_cd: case CMD_chdir: @@ -3269,7 +3318,7 @@ const char * set_one_cmd_context( case CMD_match: if (*arg == NUL || !ends_excmd(*arg)) { - /* also complete "None" */ + // also complete "None" set_context_in_echohl_cmd(xp, arg); arg = (const char *)skipwhite(skiptowhite((const char_u *)arg)); if (*arg != NUL) { @@ -3285,7 +3334,7 @@ const char * set_one_cmd_context( */ case CMD_command: - /* Check for attributes */ + // Check for attributes while (*arg == '-') { arg++; // Skip "-". p = (const char *)skiptowhite((const char_u *)arg); @@ -3337,47 +3386,48 @@ const char * set_one_cmd_context( case CMD_global: case CMD_vglobal: { - const int delim = (uint8_t)(*arg); // Get the delimiter. - if (delim) { - arg++; // Skip delimiter if there is one. - } + const int delim = (uint8_t)(*arg); // Get the delimiter. + if (delim) { + arg++; // Skip delimiter if there is one. + } - while (arg[0] != NUL && (uint8_t)arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) { + while (arg[0] != NUL && (uint8_t)arg[0] != delim) { + if (arg[0] == '\\' && arg[1] != NUL) { + arg++; + } arg++; } - arg++; + if (arg[0] != NUL) { + return arg + 1; + } + break; } - if (arg[0] != NUL) - return arg + 1; - break; - } case CMD_and: case CMD_substitute: { - const int delim = (uint8_t)(*arg); - if (delim) { - // Skip "from" part. - arg++; - arg = (const char *)skip_regexp((char_u *)arg, delim, p_magic, NULL); - } - // Skip "to" part. - while (arg[0] != NUL && (uint8_t)arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) { + const int delim = (uint8_t)(*arg); + if (delim) { + // Skip "from" part. arg++; + arg = (const char *)skip_regexp((char_u *)arg, delim, p_magic, NULL); } - arg++; - } - if (arg[0] != NUL) { // Skip delimiter. - arg++; - } - while (arg[0] && strchr("|\"#", arg[0]) == NULL) { - arg++; - } - if (arg[0] != NUL) { - return arg; + // Skip "to" part. + while (arg[0] != NUL && (uint8_t)arg[0] != delim) { + if (arg[0] == '\\' && arg[1] != NUL) { + arg++; + } + arg++; + } + if (arg[0] != NUL) { // Skip delimiter. + arg++; + } + while (arg[0] && strchr("|\"#", arg[0]) == NULL) { + arg++; + } + if (arg[0] != NUL) { + return arg; + } + break; } - break; - } case CMD_isearch: case CMD_dsearch: case CMD_ilist: @@ -3534,9 +3584,9 @@ const char * set_one_cmd_context( } else if (context == EXPAND_COMMANDS) { return arg; } else if (context == EXPAND_MAPPINGS) { - return (const char *)set_context_in_map_cmd( - xp, (char_u *)"map", (char_u *)arg, forceit, false, false, - CMD_map); + return (const char *)set_context_in_map_cmd(xp, (char_u *)"map", (char_u *)arg, forceit, + false, false, + CMD_map); } // Find start of last argument. p = arg; @@ -3554,17 +3604,26 @@ const char * set_one_cmd_context( xp->xp_context = context; } break; - case CMD_map: case CMD_noremap: - case CMD_nmap: case CMD_nnoremap: - case CMD_vmap: case CMD_vnoremap: - case CMD_omap: case CMD_onoremap: - case CMD_imap: case CMD_inoremap: - case CMD_cmap: case CMD_cnoremap: - case CMD_lmap: case CMD_lnoremap: - case CMD_smap: case CMD_snoremap: - case CMD_xmap: case CMD_xnoremap: - return (const char *)set_context_in_map_cmd( - xp, (char_u *)cmd, (char_u *)arg, forceit, false, false, ea.cmdidx); + case CMD_map: + case CMD_noremap: + case CMD_nmap: + case CMD_nnoremap: + case CMD_vmap: + case CMD_vnoremap: + case CMD_omap: + case CMD_onoremap: + case CMD_imap: + case CMD_inoremap: + case CMD_cmap: + case CMD_cnoremap: + case CMD_lmap: + case CMD_lnoremap: + case CMD_smap: + case CMD_snoremap: + case CMD_xmap: + case CMD_xnoremap: + return (const char *)set_context_in_map_cmd(xp, (char_u *)cmd, (char_u *)arg, forceit, false, + false, ea.cmdidx); case CMD_unmap: case CMD_nunmap: case CMD_vunmap: @@ -3574,8 +3633,8 @@ const char * set_one_cmd_context( case CMD_lunmap: case CMD_sunmap: case CMD_xunmap: - return (const char *)set_context_in_map_cmd( - xp, (char_u *)cmd, (char_u *)arg, forceit, false, true, ea.cmdidx); + return (const char *)set_context_in_map_cmd(xp, (char_u *)cmd, (char_u *)arg, forceit, false, + true, ea.cmdidx); case CMD_mapclear: case CMD_nmapclear: case CMD_vmapclear: @@ -3589,27 +3648,45 @@ const char * set_one_cmd_context( xp->xp_pattern = (char_u *)arg; break; - case CMD_abbreviate: case CMD_noreabbrev: - case CMD_cabbrev: case CMD_cnoreabbrev: - case CMD_iabbrev: case CMD_inoreabbrev: - return (const char *)set_context_in_map_cmd( - xp, (char_u *)cmd, (char_u *)arg, forceit, true, false, ea.cmdidx); + case CMD_abbreviate: + case CMD_noreabbrev: + case CMD_cabbrev: + case CMD_cnoreabbrev: + case CMD_iabbrev: + case CMD_inoreabbrev: + return (const char *)set_context_in_map_cmd(xp, (char_u *)cmd, (char_u *)arg, forceit, true, + false, ea.cmdidx); case CMD_unabbreviate: case CMD_cunabbrev: case CMD_iunabbrev: - return (const char *)set_context_in_map_cmd( - xp, (char_u *)cmd, (char_u *)arg, forceit, true, true, ea.cmdidx); - case CMD_menu: case CMD_noremenu: case CMD_unmenu: - case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu: - case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu: - case CMD_vmenu: case CMD_vnoremenu: case CMD_vunmenu: - case CMD_omenu: case CMD_onoremenu: case CMD_ounmenu: - case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu: - case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu: - case CMD_tmenu: case CMD_tunmenu: - case CMD_popup: case CMD_emenu: - return (const char *)set_context_in_menu_cmd( - xp, cmd, (char_u *)arg, forceit); + return (const char *)set_context_in_map_cmd(xp, (char_u *)cmd, (char_u *)arg, forceit, true, + true, ea.cmdidx); + case CMD_menu: + case CMD_noremenu: + case CMD_unmenu: + case CMD_amenu: + case CMD_anoremenu: + case CMD_aunmenu: + case CMD_nmenu: + case CMD_nnoremenu: + case CMD_nunmenu: + case CMD_vmenu: + case CMD_vnoremenu: + case CMD_vunmenu: + case CMD_omenu: + case CMD_onoremenu: + case CMD_ounmenu: + case CMD_imenu: + case CMD_inoremenu: + case CMD_iunmenu: + case CMD_cmenu: + case CMD_cnoremenu: + case CMD_cunmenu: + case CMD_tmenu: + case CMD_tunmenu: + case CMD_popup: + case CMD_emenu: + return (const char *)set_context_in_menu_cmd(xp, cmd, (char_u *)arg, forceit); case CMD_colorscheme: xp->xp_context = EXPAND_COLORS; @@ -3699,17 +3776,17 @@ const char * set_one_cmd_context( return NULL; } -// Skip a range specifier of the form: addr [,addr] [;addr] .. -// -// Backslashed delimiters after / or ? will be skipped, and commands will -// not be expanded between /'s and ?'s or after "'". -// -// Also skip white space and ":" characters. -// Returns the "cmd" pointer advanced to beyond the range. -char_u *skip_range( - const char_u *cmd, - int *ctx // pointer to xp_context or NULL -) +/// Skip a range specifier of the form: addr [,addr] [;addr] .. +/// +/// Backslashed delimiters after / or ? will be skipped, and commands will +/// not be expanded between /'s and ?'s or after "'". +/// +/// Also skip white space and ":" characters. +/// +/// @param ctx pointer to xp_context or NULL +/// +/// @return the "cmd" pointer advanced to beyond the range. +char_u *skip_range(const char_u *cmd, int *ctx) { unsigned delim; @@ -3726,14 +3803,18 @@ char_u *skip_range( } } else if (*cmd == '/' || *cmd == '?') { delim = *cmd++; - while (*cmd != NUL && *cmd != delim) - if (*cmd++ == '\\' && *cmd != NUL) + while (*cmd != NUL && *cmd != delim) { + if (*cmd++ == '\\' && *cmd != NUL) { ++cmd; - if (*cmd == NUL && ctx != NULL) + } + } + if (*cmd == NUL && ctx != NULL) { *ctx = EXPAND_NOTHING; + } } - if (*cmd != NUL) + if (*cmd != NUL) { ++cmd; + } } // Skip ":" and white space. @@ -3751,28 +3832,28 @@ static void addr_error(cmd_addr_T addr_type) } } -// Get a single EX address -// -// Set ptr to the next character after the part that was interpreted. -// Set ptr to NULL when an error is encountered. -// This may set the last used search pattern. -// -// Return MAXLNUM when no Ex address was found. -static linenr_T get_address(exarg_T *eap, - char_u **ptr, - cmd_addr_T addr_type, - int skip, // only skip the address, don't use it - bool silent, // no errors or side effects - int to_other_file, // flag: may jump to other file - int address_count) // 1 for first, >1 after comma +/// Get a single EX address +/// +/// Set ptr to the next character after the part that was interpreted. +/// Set ptr to NULL when an error is encountered. +/// This may set the last used search pattern. +/// +/// @param skip only skip the address, don't use it +/// @param silent no errors or side effects +/// @param to_other_file flag: may jump to other file +/// @param address_count 1 for first, >1 after comma +/// +/// @return MAXLNUM when no Ex address was found. +static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, int skip, bool silent, + int to_other_file, int address_count) FUNC_ATTR_NONNULL_ALL { int c; int i; long n; - char_u *cmd; + char_u *cmd; pos_T pos; - pos_T *fp; + pos_T *fp; linenr_T lnum; buf_T *buf; @@ -3780,94 +3861,94 @@ static linenr_T get_address(exarg_T *eap, lnum = MAXLNUM; do { switch (*cmd) { - case '.': /* '.' - Cursor position */ + case '.': // '.' - Cursor position ++cmd; switch (addr_type) { - case ADDR_LINES: - case ADDR_OTHER: - lnum = curwin->w_cursor.lnum; - break; - case ADDR_WINDOWS: - lnum = CURRENT_WIN_NR; - break; - case ADDR_ARGUMENTS: - lnum = curwin->w_arg_idx + 1; - break; - case ADDR_LOADED_BUFFERS: - case ADDR_BUFFERS: - lnum = curbuf->b_fnum; - break; - case ADDR_TABS: - lnum = CURRENT_TAB_NR; - break; - case ADDR_NONE: - case ADDR_TABS_RELATIVE: - case ADDR_UNSIGNED: - addr_error(addr_type); - cmd = NULL; - goto error; - break; - case ADDR_QUICKFIX: - lnum = qf_get_cur_idx(eap); - break; - case ADDR_QUICKFIX_VALID: - lnum = qf_get_cur_valid_idx(eap); - break; + case ADDR_LINES: + case ADDR_OTHER: + lnum = curwin->w_cursor.lnum; + break; + case ADDR_WINDOWS: + lnum = CURRENT_WIN_NR; + break; + case ADDR_ARGUMENTS: + lnum = curwin->w_arg_idx + 1; + break; + case ADDR_LOADED_BUFFERS: + case ADDR_BUFFERS: + lnum = curbuf->b_fnum; + break; + case ADDR_TABS: + lnum = CURRENT_TAB_NR; + break; + case ADDR_NONE: + case ADDR_TABS_RELATIVE: + case ADDR_UNSIGNED: + addr_error(addr_type); + cmd = NULL; + goto error; + break; + case ADDR_QUICKFIX: + lnum = qf_get_cur_idx(eap); + break; + case ADDR_QUICKFIX_VALID: + lnum = qf_get_cur_valid_idx(eap); + break; } break; - case '$': /* '$' - last line */ + case '$': // '$' - last line ++cmd; switch (addr_type) { - case ADDR_LINES: - case ADDR_OTHER: - lnum = curbuf->b_ml.ml_line_count; - break; - case ADDR_WINDOWS: - lnum = LAST_WIN_NR; - break; - case ADDR_ARGUMENTS: - lnum = ARGCOUNT; - break; - case ADDR_LOADED_BUFFERS: - buf = lastbuf; - while (buf->b_ml.ml_mfp == NULL) { - if (buf->b_prev == NULL) { - break; - } - buf = buf->b_prev; - } - lnum = buf->b_fnum; - break; - case ADDR_BUFFERS: - lnum = lastbuf->b_fnum; - break; - case ADDR_TABS: - lnum = LAST_TAB_NR; - break; - case ADDR_NONE: - case ADDR_TABS_RELATIVE: - case ADDR_UNSIGNED: - addr_error(addr_type); - cmd = NULL; - goto error; - break; - case ADDR_QUICKFIX: - lnum = qf_get_size(eap); - if (lnum == 0) { - lnum = 1; - } - break; - case ADDR_QUICKFIX_VALID: - lnum = qf_get_valid_size(eap); - if (lnum == 0) { - lnum = 1; + case ADDR_LINES: + case ADDR_OTHER: + lnum = curbuf->b_ml.ml_line_count; + break; + case ADDR_WINDOWS: + lnum = LAST_WIN_NR; + break; + case ADDR_ARGUMENTS: + lnum = ARGCOUNT; + break; + case ADDR_LOADED_BUFFERS: + buf = lastbuf; + while (buf->b_ml.ml_mfp == NULL) { + if (buf->b_prev == NULL) { + break; } - break; + buf = buf->b_prev; + } + lnum = buf->b_fnum; + break; + case ADDR_BUFFERS: + lnum = lastbuf->b_fnum; + break; + case ADDR_TABS: + lnum = LAST_TAB_NR; + break; + case ADDR_NONE: + case ADDR_TABS_RELATIVE: + case ADDR_UNSIGNED: + addr_error(addr_type); + cmd = NULL; + goto error; + break; + case ADDR_QUICKFIX: + lnum = qf_get_size(eap); + if (lnum == 0) { + lnum = 1; + } + break; + case ADDR_QUICKFIX_VALID: + lnum = qf_get_valid_size(eap); + if (lnum == 0) { + lnum = 1; + } + break; } break; - case '\'': /* ''' - mark */ + case '\'': // ''' - mark if (*++cmd == NUL) { cmd = NULL; goto error; @@ -3877,17 +3958,17 @@ static linenr_T get_address(exarg_T *eap, cmd = NULL; goto error; } - if (skip) + if (skip) { ++cmd; - else { + } else { /* Only accept a mark in another file when it is * used by itself: ":'M". */ fp = getmark(*cmd, to_other_file && cmd[1] == NUL); ++cmd; - if (fp == (pos_T *)-1) - /* Jumped to another file. */ + if (fp == (pos_T *)-1) { + // Jumped to another file. lnum = curwin->w_cursor.lnum; - else { + } else { if (check_mark(fp) == FAIL) { cmd = NULL; goto error; @@ -3898,17 +3979,18 @@ static linenr_T get_address(exarg_T *eap, break; case '/': - case '?': /* '/' or '?' - search */ + case '?': // '/' or '?' - search c = *cmd++; if (addr_type != ADDR_LINES) { addr_error(addr_type); cmd = NULL; goto error; } - if (skip) { /* skip "/pat/" */ + if (skip) { // skip "/pat/" cmd = skip_regexp(cmd, c, p_magic, NULL); - if (*cmd == c) + if (*cmd == c) { ++cmd; + } } else { int flags; @@ -3940,23 +4022,23 @@ static linenr_T get_address(exarg_T *eap, } lnum = curwin->w_cursor.lnum; curwin->w_cursor = pos; - /* adjust command string pointer */ + // adjust command string pointer cmd += searchcmdlen; } break; - case '\\': /* "\?", "\/" or "\&", repeat search */ + case '\\': // "\?", "\/" or "\&", repeat search ++cmd; if (addr_type != ADDR_LINES) { addr_error(addr_type); cmd = NULL; goto error; } - if (*cmd == '&') + if (*cmd == '&') { i = RE_SUBST; - else if (*cmd == '?' || *cmd == '/') + } else if (*cmd == '?' || *cmd == '/') { i = RE_SEARCH; - else { + } else { EMSG(_(e_backslash)); cmd = NULL; goto error; @@ -3994,37 +4076,37 @@ static linenr_T get_address(exarg_T *eap, if (lnum == MAXLNUM) { switch (addr_type) { - case ADDR_LINES: - case ADDR_OTHER: - // "+1" is same as ".+1" - lnum = curwin->w_cursor.lnum; - break; - case ADDR_WINDOWS: - lnum = CURRENT_WIN_NR; - break; - case ADDR_ARGUMENTS: - lnum = curwin->w_arg_idx + 1; - break; - case ADDR_LOADED_BUFFERS: - case ADDR_BUFFERS: - lnum = curbuf->b_fnum; - break; - case ADDR_TABS: - lnum = CURRENT_TAB_NR; - break; - case ADDR_TABS_RELATIVE: - lnum = 1; - break; - case ADDR_QUICKFIX: - lnum = qf_get_cur_idx(eap); - break; - case ADDR_QUICKFIX_VALID: - lnum = qf_get_cur_valid_idx(eap); - break; - case ADDR_NONE: - case ADDR_UNSIGNED: - lnum = 0; - break; + case ADDR_LINES: + case ADDR_OTHER: + // "+1" is same as ".+1" + lnum = curwin->w_cursor.lnum; + break; + case ADDR_WINDOWS: + lnum = CURRENT_WIN_NR; + break; + case ADDR_ARGUMENTS: + lnum = curwin->w_arg_idx + 1; + break; + case ADDR_LOADED_BUFFERS: + case ADDR_BUFFERS: + lnum = curbuf->b_fnum; + break; + case ADDR_TABS: + lnum = CURRENT_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + lnum = 1; + break; + case ADDR_QUICKFIX: + lnum = qf_get_cur_idx(eap); + break; + case ADDR_QUICKFIX_VALID: + lnum = qf_get_cur_valid_idx(eap); + break; + case ADDR_NONE: + case ADDR_UNSIGNED: + lnum = 0; + break; } } @@ -4044,8 +4126,7 @@ static linenr_T get_address(exarg_T *eap, cmd = NULL; goto error; } else if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) { - lnum = compute_buffer_local_count( - addr_type, lnum, (i == '-') ? -1 * n : n); + lnum = compute_buffer_local_count(addr_type, lnum, (i == '-') ? -1 * n : n); } else { // Relative line addressing, need to adjust for folded lines // now, but only do it after the first address. @@ -4073,12 +4154,13 @@ error: static void get_flags(exarg_T *eap) { while (vim_strchr((char_u *)"lp#", *eap->arg) != NULL) { - if (*eap->arg == 'l') + if (*eap->arg == 'l') { eap->flags |= EXFLAG_LIST; - else if (*eap->arg == 'p') + } else if (*eap->arg == 'p') { eap->flags |= EXFLAG_PRINT; - else + } else { eap->flags |= EXFLAG_NR; + } eap->arg = skipwhite(eap->arg + 1); } } @@ -4086,9 +4168,9 @@ static void get_flags(exarg_T *eap) /// Stub function for command which is Not Implemented. NI! void ex_ni(exarg_T *eap) { - if (!eap->skip) - eap->errmsg = (char_u *)N_( - "E319: The command is not available in this version"); + if (!eap->skip) { + eap->errmsg = (char_u *)N_("E319: The command is not available in this version"); + } } /// Stub function for script command which is Not Implemented. NI! @@ -4116,81 +4198,77 @@ static char_u *invalid_range(exarg_T *eap) if (eap->argt & EX_RANGE) { switch (eap->addr_type) { - case ADDR_LINES: - if (eap->line2 > (curbuf->b_ml.ml_line_count - + (eap->cmdidx == CMD_diffget))) { - return (char_u *)_(e_invrange); - } - break; - case ADDR_ARGUMENTS: - // add 1 if ARGCOUNT is 0 - if (eap->line2 > ARGCOUNT + (!ARGCOUNT)) { - return (char_u *)_(e_invrange); - } - break; - case ADDR_BUFFERS: - if (eap->line1 < firstbuf->b_fnum - || eap->line2 > lastbuf->b_fnum) { - return (char_u *)_(e_invrange); - } - break; - case ADDR_LOADED_BUFFERS: - buf = firstbuf; - while (buf->b_ml.ml_mfp == NULL) { - if (buf->b_next == NULL) { - return (char_u *)_(e_invrange); - } - buf = buf->b_next; - } - if (eap->line1 < buf->b_fnum) { - return (char_u *)_(e_invrange); - } - buf = lastbuf; - while (buf->b_ml.ml_mfp == NULL) { - if (buf->b_prev == NULL) { - return (char_u *)_(e_invrange); - } - buf = buf->b_prev; - } - if (eap->line2 > buf->b_fnum) { - return (char_u *)_(e_invrange); - } - break; - case ADDR_WINDOWS: - if (eap->line2 > LAST_WIN_NR) { - return (char_u *)_(e_invrange); - } - break; - case ADDR_TABS: - if (eap->line2 > LAST_TAB_NR) { - return (char_u *)_(e_invrange); - } - break; - case ADDR_TABS_RELATIVE: - case ADDR_OTHER: - // Any range is OK. - break; - case ADDR_QUICKFIX: - assert(eap->line2 >= 0); - // No error for value that is too big, will use the last entry. - if (eap->line2 <= 0) { - return (char_u *)_(e_invrange); - } - break; - case ADDR_QUICKFIX_VALID: - if ((eap->line2 != 1 && (size_t)eap->line2 > qf_get_valid_size(eap)) - || eap->line2 < 0) { + case ADDR_LINES: + if (eap->line2 > (curbuf->b_ml.ml_line_count + + (eap->cmdidx == CMD_diffget))) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_ARGUMENTS: + // add 1 if ARGCOUNT is 0 + if (eap->line2 > ARGCOUNT + (!ARGCOUNT)) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_BUFFERS: + if (eap->line1 < firstbuf->b_fnum + || eap->line2 > lastbuf->b_fnum) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_LOADED_BUFFERS: + buf = firstbuf; + while (buf->b_ml.ml_mfp == NULL) { + if (buf->b_next == NULL) { return (char_u *)_(e_invrange); } - break; - case ADDR_UNSIGNED: - if (eap->line2 < 0) { + buf = buf->b_next; + } + if (eap->line1 < buf->b_fnum) { + return (char_u *)_(e_invrange); + } + buf = lastbuf; + while (buf->b_ml.ml_mfp == NULL) { + if (buf->b_prev == NULL) { return (char_u *)_(e_invrange); } - break; - case ADDR_NONE: - // Will give an error elsewhere. - break; + buf = buf->b_prev; + } + if (eap->line2 > buf->b_fnum) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_WINDOWS: + if (eap->line2 > LAST_WIN_NR) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_TABS: + if (eap->line2 > LAST_TAB_NR) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_TABS_RELATIVE: + case ADDR_OTHER: + // Any range is OK. + break; + case ADDR_QUICKFIX: + assert(eap->line2 >= 0); + // No error for value that is too big, will use the last entry. + if (eap->line2 <= 0) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_QUICKFIX_VALID: + if ((eap->line2 != 1 && (size_t)eap->line2 > qf_get_valid_size(eap)) + || eap->line2 < 0) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_UNSIGNED: + case ADDR_NONE: + // Will give an error elsewhere. + break; } } return NULL; @@ -4218,15 +4296,16 @@ static void correct_range(exarg_T *eap) */ static char_u *skip_grep_pat(exarg_T *eap) { - char_u *p = eap->arg; + char_u *p = eap->arg; if (*p != NUL && (eap->cmdidx == CMD_vimgrep || eap->cmdidx == CMD_lvimgrep || eap->cmdidx == CMD_vimgrepadd || eap->cmdidx == CMD_lvimgrepadd || grep_internal(eap->cmdidx))) { p = skip_vimgrep_pat(p, NULL, NULL); - if (p == NULL) + if (p == NULL) { p = eap->arg; + } } return p; } @@ -4237,10 +4316,10 @@ static char_u *skip_grep_pat(exarg_T *eap) */ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) { - char_u *new_cmdline; - char_u *program; - char_u *pos; - char_u *ptr; + char_u *new_cmdline; + char_u *program; + char_u *pos; + char_u *ptr; int len; int i; @@ -4254,24 +4333,27 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) && !grep_internal(eap->cmdidx)) { if (eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep || eap->cmdidx == CMD_grepadd || eap->cmdidx == CMD_lgrepadd) { - if (*curbuf->b_p_gp == NUL) + if (*curbuf->b_p_gp == NUL) { program = p_gp; - else + } else { program = curbuf->b_p_gp; + } } else { - if (*curbuf->b_p_mp == NUL) + if (*curbuf->b_p_mp == NUL) { program = p_mp; - else + } else { program = curbuf->b_p_mp; + } } p = skipwhite(p); if ((pos = (char_u *)strstr((char *)program, "$*")) != NULL) { - /* replace $* by given arguments */ + // replace $* by given arguments i = 1; - while ((pos = (char_u *)strstr((char *)pos + 2, "$*")) != NULL) + while ((pos = (char_u *)strstr((char *)pos + 2, "$*")) != NULL) { ++i; + } len = (int)STRLEN(p); new_cmdline = xmalloc(STRLEN(program) + i * (len - 2) + 1); ptr = new_cmdline; @@ -4291,7 +4373,7 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) } msg_make(p); - /* 'eap->cmd' is not set here, because it is not used at CMD_make */ + // 'eap->cmd' is not set here, because it is not used at CMD_make xfree(*cmdlinep); *cmdlinep = new_cmdline; p = new_cmdline; @@ -4304,13 +4386,13 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) // Return FAIL for failure, OK otherwise. int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) { - int has_wildcards; /* need to expand wildcards */ - char_u *repl; + int has_wildcards; // need to expand wildcards + char_u *repl; size_t srclen; - char_u *p; + char_u *p; int escaped; - /* Skip a regexp pattern for ":vimgrep[add] pat file..." */ + // Skip a regexp pattern for ":vimgrep[add] pat file..." p = skip_grep_pat(eap); /* @@ -4320,12 +4402,13 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) */ has_wildcards = path_has_wildcard(p); while (*p != NUL) { - /* Skip over `=expr`, wildcards in it are not expanded. */ + // Skip over `=expr`, wildcards in it are not expanded. if (p[0] == '`' && p[1] == '=') { p += 2; (void)skip_expr(&p); - if (*p == '`') + if (*p == '`') { ++p; + } continue; } /* @@ -4341,10 +4424,11 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) * Try to find a match at this position. */ repl = eval_vars(p, eap->arg, &srclen, &(eap->do_ecmd_lnum), - errormsgp, &escaped); - if (*errormsgp != NULL) /* error detected */ + errormsgp, &escaped); + if (*errormsgp != NULL) { // error detected return FAIL; - if (repl == NULL) { /* no match found */ + } + if (repl == NULL) { // no match found p += srclen; continue; } @@ -4374,9 +4458,8 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) && eap->cmdidx != CMD_lmake && eap->cmdidx != CMD_make && eap->cmdidx != CMD_terminal - && !(eap->argt & EX_NOSPC) - ) { - char_u *l; + && !(eap->argt & EX_NOSPC)) { + char_u *l; #ifdef BACKSLASH_IN_FILENAME /* Don't escape a backslash here, because rem_backslash() doesn't * remove it later. */ @@ -4386,13 +4469,14 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) # define ESCAPE_CHARS escape_chars #endif - for (l = repl; *l; ++l) + for (l = repl; *l; ++l) { if (vim_strchr(ESCAPE_CHARS, *l) != NULL) { l = vim_strsave_escaped(repl, ESCAPE_CHARS); xfree(repl); repl = l; break; } + } } // For a shell command a '!' must be escaped. @@ -4400,7 +4484,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal) && vim_strpbrk(repl, (char_u *)"!") != NULL) { - char_u *l; + char_u *l; l = vim_strsave_escaped(repl, (char_u *)"!"); xfree(repl); @@ -4428,11 +4512,12 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) if (vim_strchr(eap->arg, '$') != NULL || vim_strchr(eap->arg, '~') != NULL) { expand_env_esc(eap->arg, NameBuff, MAXPATHL, - TRUE, TRUE, NULL); + TRUE, TRUE, NULL); has_wildcards = path_has_wildcard(NameBuff); p = NameBuff; - } else + } else { p = NULL; + } if (p != NULL) { (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, cmdlinep); } @@ -4454,8 +4539,9 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; - if (p_wic) + if (p_wic) { options += WILD_ICASE; + } p = ExpandOne(&xpc, eap->arg, NULL, options, WILD_EXPAND_FREE); if (p == NULL) { return FAIL; @@ -4474,8 +4560,8 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char_u **errormsgp) * "repl" is the replacement string. * Returns a pointer to the character after the replaced string. */ -static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, - char_u *repl, char_u **cmdlinep) +static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *repl, + char_u **cmdlinep) { /* * The new command line is build in new_cmdline[]. @@ -4484,8 +4570,9 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, */ size_t len = STRLEN(repl); size_t i = (size_t)(src - *cmdlinep) + STRLEN(src + srclen) + len + 3; - if (eap->nextcmd != NULL) - i += STRLEN(eap->nextcmd); /* add space for next command */ + if (eap->nextcmd != NULL) { + i += STRLEN(eap->nextcmd); // add space for next command + } char_u *new_cmdline = xmalloc(i); /* @@ -4494,23 +4581,24 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, * Copy what came after the expanded part. * Copy the next commands, if there are any. */ - i = (size_t)(src - *cmdlinep); /* length of part before match */ + i = (size_t)(src - *cmdlinep); // length of part before match memmove(new_cmdline, *cmdlinep, i); memmove(new_cmdline + i, repl, len); - i += len; /* remember the end of the string */ + i += len; // remember the end of the string STRCPY(new_cmdline + i, src + srclen); - src = new_cmdline + i; /* remember where to continue */ + src = new_cmdline + i; // remember where to continue - if (eap->nextcmd != NULL) { /* append next command */ + if (eap->nextcmd != NULL) { // append next command i = STRLEN(new_cmdline) + 1; STRCPY(new_cmdline + i, eap->nextcmd); eap->nextcmd = new_cmdline + i; } eap->cmd = new_cmdline + (eap->cmd - *cmdlinep); eap->arg = new_cmdline + (eap->arg - *cmdlinep); - if (eap->do_ecmd_cmd != NULL && eap->do_ecmd_cmd != dollar_command) + if (eap->do_ecmd_cmd != NULL && eap->do_ecmd_cmd != dollar_command) { eap->do_ecmd_cmd = new_cmdline + (eap->do_ecmd_cmd - *cmdlinep); + } xfree(*cmdlinep); *cmdlinep = new_cmdline; @@ -4522,7 +4610,7 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, */ void separate_nextcmd(exarg_T *eap) { - char_u *p; + char_u *p; p = skip_grep_pat(eap); @@ -4545,16 +4633,16 @@ void separate_nextcmd(exarg_T *eap) break; } } else if ( - // Check for '"': start of comment or '|': next command */ - // :@" does not start a comment! - // :redir @" doesn't either. - (*p == '"' - && !(eap->argt & EX_NOTRLCOM) - && (eap->cmdidx != CMD_at || p != eap->arg) - && (eap->cmdidx != CMD_redir - || p != eap->arg + 1 || p[-1] != '@')) - || *p == '|' - || *p == '\n') { + // Check for '"': start of comment or '|': next command */ + // :@" does not start a comment! + // :redir @" doesn't either. + (*p == '"' + && !(eap->argt & EX_NOTRLCOM) + && (eap->cmdidx != CMD_at || p != eap->arg) + && (eap->cmdidx != CMD_redir + || p != eap->arg + 1 || p[-1] != '@')) + || *p == '|' + || *p == '\n') { // We remove the '\' before the '|', unless EX_CTRLV is used // AND 'b' is present in 'cpoptions'. if ((vim_strchr(p_cpo, CPO_BAR) == NULL @@ -4582,38 +4670,36 @@ static char_u *getargcmd(char_u **argp) char_u *arg = *argp; char_u *command = NULL; - if (*arg == '+') { /* +[command] */ + if (*arg == '+') { // +[command] ++arg; - if (ascii_isspace(*arg) || *arg == '\0') + if (ascii_isspace(*arg) || *arg == '\0') { command = dollar_command; - else { + } else { command = arg; arg = skip_cmd_arg(command, TRUE); - if (*arg != NUL) - *arg++ = NUL; /* terminate command with NUL */ + if (*arg != NUL) { + *arg++ = NUL; // terminate command with NUL + } } - arg = skipwhite(arg); /* skip over spaces */ + arg = skipwhite(arg); // skip over spaces *argp = arg; } return command; } -/* - * Find end of "+command" argument. Skip over "\ " and "\\". - */ -static char_u * -skip_cmd_arg ( - char_u *p, - int rembs /* TRUE to halve the number of backslashes */ -) +/// Find end of "+command" argument. Skip over "\ " and "\\". +/// +/// @param rembs TRUE to halve the number of backslashes +static char_u *skip_cmd_arg(char_u *p, int rembs) { while (*p && !ascii_isspace(*p)) { if (*p == '\\' && p[1] != NUL) { - if (rembs) + if (rembs) { STRMOVE(p, p + 1); - else + } else { ++p; + } } MB_PTR_ADV(p); } @@ -4641,25 +4727,27 @@ int get_bad_opt(const char_u *p, exarg_T *eap) */ static int getargopt(exarg_T *eap) { - char_u *arg = eap->arg + 2; - int *pp = NULL; + char_u *arg = eap->arg + 2; + int *pp = NULL; int bad_char_idx; - char_u *p; + char_u *p; - /* ":edit ++[no]bin[ary] file" */ + // ":edit ++[no]bin[ary] file" if (STRNCMP(arg, "bin", 3) == 0 || STRNCMP(arg, "nobin", 5) == 0) { if (*arg == 'n') { arg += 2; eap->force_bin = FORCE_NOBIN; - } else + } else { eap->force_bin = FORCE_BIN; - if (!checkforcmd(&arg, "binary", 3)) + } + if (!checkforcmd(&arg, "binary", 3)) { return FAIL; + } eap->arg = skipwhite(arg); return OK; } - /* ":read ++edit file" */ + // ":read ++edit file" if (STRNCMP(arg, "edit", 4) == 0) { eap->read_edit = TRUE; eap->arg = skipwhite(arg + 4); @@ -4673,18 +4761,20 @@ static int getargopt(exarg_T *eap) arg += 10; pp = &eap->force_ff; } else if (STRNCMP(arg, "enc", 3) == 0) { - if (STRNCMP(arg, "encoding", 8) == 0) + if (STRNCMP(arg, "encoding", 8) == 0) { arg += 8; - else + } else { arg += 3; + } pp = &eap->force_enc; } else if (STRNCMP(arg, "bad", 3) == 0) { arg += 3; pp = &bad_char_idx; } - if (pp == NULL || *arg != '=') + if (pp == NULL || *arg != '=') { return FAIL; + } ++arg; *pp = (int)(arg - eap->cmd); @@ -4698,9 +4788,10 @@ static int getargopt(exarg_T *eap) } eap->force_ff = eap->cmd[eap->force_ff]; } else if (pp == &eap->force_enc) { - /* Make 'fileencoding' lower case. */ - for (p = eap->cmd + eap->force_enc; *p != NUL; ++p) + // Make 'fileencoding' lower case. + for (p = eap->cmd + eap->force_enc; *p != NUL; ++p) { *p = TOLOWER_ASC(*p); + } } else { /* Check ++bad= argument. Must be a single-byte character, "keep" or * "drop". */ @@ -4723,8 +4814,8 @@ static int get_tabpage_arg(exarg_T *eap) if (eap->arg && *eap->arg != NUL) { char_u *p = eap->arg; char_u *p_save; - int relative = 0; // argument +N/-N means: go to N places to the - // right/left relative to the current position. + int relative = 0; // argument +N/-N means: go to N places to the + // right/left relative to the current position. if (*p == '-') { relative = -1; @@ -4751,8 +4842,7 @@ static int get_tabpage_arg(exarg_T *eap) } else { if (*p_save == NUL) { tab_number = 1; - } - else if (p == p_save || *p_save == '-' || *p != NUL || tab_number == 0) { + } else if (p == p_save || *p_save == '-' || *p != NUL || tab_number == 0) { // No numbers as argument. eap->errmsg = e_invarg; goto theend; @@ -4782,8 +4872,9 @@ static int get_tabpage_arg(exarg_T *eap) switch (eap->cmdidx) { case CMD_tabnext: tab_number = tabpage_index(curtab) + 1; - if (tab_number > LAST_TAB_NR) + if (tab_number > LAST_TAB_NR) { tab_number = 1; + } break; case CMD_tabmove: tab_number = LAST_TAB_NR; @@ -4802,7 +4893,7 @@ theend: */ static void ex_abbreviate(exarg_T *eap) { - do_exmap(eap, TRUE); /* almost the same as mapping */ + do_exmap(eap, TRUE); // almost the same as mapping } /* @@ -4853,10 +4944,11 @@ static void ex_autocmd(exarg_T *eap) if (secure) { secure = 2; eap->errmsg = e_curdir; - } else if (eap->cmdidx == CMD_autocmd) + } else if (eap->cmdidx == CMD_autocmd) { do_autocmd(eap->arg, eap->forceit); - else + } else { do_augroup(eap->arg, eap->forceit); + } } /* @@ -4882,16 +4974,16 @@ static void ex_doautocmd(exarg_T *eap) */ static void ex_bunload(exarg_T *eap) { - eap->errmsg = do_bufdel( - eap->cmdidx == CMD_bdelete ? DOBUF_DEL - : eap->cmdidx == CMD_bwipeout ? DOBUF_WIPE - : DOBUF_UNLOAD, eap->arg, - eap->addr_count, (int)eap->line1, (int)eap->line2, eap->forceit); + eap->errmsg = do_bufdel(eap->cmdidx == CMD_bdelete ? DOBUF_DEL + : eap->cmdidx == CMD_bwipeout ? DOBUF_WIPE + : DOBUF_UNLOAD, + eap->arg, + eap->addr_count, (int)eap->line1, (int)eap->line2, eap->forceit); } /* - * :[N]buffer [N] to buffer N - * :[N]sbuffer [N] to buffer N + * :[N]buffer [N] to buffer N + * :[N]sbuffer [N] to buffer N */ static void ex_buffer(exarg_T *eap) { @@ -4910,8 +5002,8 @@ static void ex_buffer(exarg_T *eap) } /* - * :[N]bmodified [N] to next mod. buffer - * :[N]sbmodified [N] to next mod. buffer + * :[N]bmodified [N] to next mod. buffer + * :[N]sbmodified [N] to next mod. buffer */ static void ex_bmodified(exarg_T *eap) { @@ -4922,8 +5014,8 @@ static void ex_bmodified(exarg_T *eap) } /* - * :[N]bnext [N] to next buffer - * :[N]sbnext [N] split and to next buffer + * :[N]bnext [N] to next buffer + * :[N]sbnext [N] split and to next buffer */ static void ex_bnext(exarg_T *eap) { @@ -4934,10 +5026,10 @@ static void ex_bnext(exarg_T *eap) } /* - * :[N]bNext [N] to previous buffer - * :[N]bprevious [N] to previous buffer - * :[N]sbNext [N] split and to previous buffer - * :[N]sbprevious [N] split and to previous buffer + * :[N]bNext [N] to previous buffer + * :[N]bprevious [N] to previous buffer + * :[N]sbNext [N] split and to previous buffer + * :[N]sbprevious [N] split and to previous buffer */ static void ex_bprevious(exarg_T *eap) { @@ -4948,10 +5040,10 @@ static void ex_bprevious(exarg_T *eap) } /* - * :brewind to first buffer - * :bfirst to first buffer - * :sbrewind split and to first buffer - * :sbfirst split and to first buffer + * :brewind to first buffer + * :bfirst to first buffer + * :sbrewind split and to first buffer + * :sbfirst split and to first buffer */ static void ex_brewind(exarg_T *eap) { @@ -4962,8 +5054,8 @@ static void ex_brewind(exarg_T *eap) } /* - * :blast to last buffer - * :sblast split and to last buffer + * :blast to last buffer + * :sblast split and to last buffer */ static void ex_blast(exarg_T *eap) { @@ -4997,28 +5089,23 @@ char_u *find_nextcmd(const char_u *p) /// Return NULL if it isn't, the following character if it is. char_u *check_nextcmd(char_u *p) { - char_u *s = skipwhite(p); + char_u *s = skipwhite(p); - if (*s == '|' || *s == '\n') { - return (s + 1); - } else { - return NULL; - } + if (*s == '|' || *s == '\n') { + return (s + 1); + } else { + return NULL; + } } -/* - * - if there are more files to edit - * - and this is the last window - * - and forceit not used - * - and not repeated twice on a row - * return FAIL and give error message if 'message' TRUE - * return OK otherwise - */ -static int -check_more( - int message, // when FALSE check only, no messages - bool forceit -) +/// - if there are more files to edit +/// - and this is the last window +/// - and forceit not used +/// - and not repeated twice on a row +/// @return FAIL and give error message if 'message' TRUE, return OK otherwise +/// +/// @param message when FALSE check only, no messages +static int check_more(int message, bool forceit) { int n = ARGCOUNT - curwin->w_arg_idx - 1; @@ -5028,21 +5115,24 @@ check_more( if ((p_confirm || cmdmod.confirm) && curbuf->b_fname != NULL) { char_u buff[DIALOG_MSG_SIZE]; - if (n == 1) + if (n == 1) { STRLCPY(buff, _("1 more file to edit. Quit anyway?"), - DIALOG_MSG_SIZE); - else + DIALOG_MSG_SIZE); + } else { vim_snprintf((char *)buff, DIALOG_MSG_SIZE, - _("%d more files to edit. Quit anyway?"), n); - if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1) == VIM_YES) + _("%d more files to edit. Quit anyway?"), n); + } + if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1) == VIM_YES) { return OK; + } return FAIL; } - if (n == 1) + if (n == 1) { EMSG(_("E173: 1 more file to edit")); - else + } else { EMSGN(_("E173: %" PRId64 " more files to edit"), n); - quitmore = 2; /* next try to quit is allowed */ + } + quitmore = 2; // next try to quit is allowed } return FAIL; } @@ -5054,38 +5144,40 @@ check_more( */ char_u *get_command_name(expand_T *xp, int idx) { - if (idx >= (int)CMD_SIZE) + if (idx >= (int)CMD_SIZE) { return get_user_command_name(idx); + } return cmdnames[idx].cmd_name; } -static int uc_add_command(char_u *name, size_t name_len, char_u *rep, - uint32_t argt, long def, int flags, int compl, - char_u *compl_arg, cmd_addr_T addr_type, bool force) +static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, + int flags, int compl, char_u *compl_arg, cmd_addr_T addr_type, bool force) FUNC_ATTR_NONNULL_ARG(1, 3) { - ucmd_T *cmd = NULL; + ucmd_T *cmd = NULL; int i; int cmp = 1; - char_u *rep_buf = NULL; - garray_T *gap; + char_u *rep_buf = NULL; + garray_T *gap; replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, true, CPO_TO_CPO_FLAGS); if (rep_buf == NULL) { - /* Can't replace termcodes - try using the string as is */ + // Can't replace termcodes - try using the string as is rep_buf = vim_strsave(rep); } - /* get address of growarray: global or in curbuf */ + // get address of growarray: global or in curbuf if (flags & UC_BUFFER) { gap = &curbuf->b_ucmds; - if (gap->ga_itemsize == 0) + if (gap->ga_itemsize == 0) { ga_init(gap, (int)sizeof(ucmd_T), 4); - } else + } + } else { gap = &ucmds; + } - /* Search for the command in the already defined commands. */ + // Search for the command in the already defined commands. for (i = 0; i < gap->ga_len; ++i) { size_t len; @@ -5093,10 +5185,11 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, len = STRLEN(cmd->uc_name); cmp = STRNCMP(name, cmd->uc_name, name_len); if (cmp == 0) { - if (name_len < len) + if (name_len < len) { cmp = -1; - else if (name_len > len) + } else if (name_len > len) { cmp = 1; + } } if (cmp == 0) { @@ -5115,12 +5208,13 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, break; } - /* Stop as soon as we pass the name to add */ - if (cmp < 0) + // Stop as soon as we pass the name to add + if (cmp < 0) { break; + } } - /* Extend the array unless we're replacing an existing command */ + // Extend the array unless we're replacing an existing command if (cmp != 0) { ga_grow(gap, 1); @@ -5231,7 +5325,7 @@ static void uc_list(char_u *name, size_t name_len) { int i, j; bool found = false; - ucmd_T *cmd; + ucmd_T *cmd; uint32_t a; // In cmdwin, the alternative buffer should be used. @@ -5257,8 +5351,9 @@ static void uc_list(char_u *name, size_t name_len) } found = true; msg_putchar('\n'); - if (got_int) + if (got_int) { break; + } // Special cases int len = 4; @@ -5297,21 +5392,21 @@ static void uc_list(char_u *name, size_t name_len) // Arguments switch (a & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { - case 0: - IObuff[len++] = '0'; - break; - case (EX_EXTRA): - IObuff[len++] = '*'; - break; - case (EX_EXTRA | EX_NOSPC): - IObuff[len++] = '?'; - break; - case (EX_EXTRA | EX_NEEDARG): - IObuff[len++] = '+'; - break; - case (EX_EXTRA | EX_NOSPC | EX_NEEDARG): - IObuff[len++] = '1'; - break; + case 0: + IObuff[len++] = '0'; + break; + case (EX_EXTRA): + IObuff[len++] = '*'; + break; + case (EX_EXTRA | EX_NOSPC): + IObuff[len++] = '?'; + break; + case (EX_EXTRA | EX_NEEDARG): + IObuff[len++] = '+'; + break; + case (EX_EXTRA | EX_NOSPC | EX_NEEDARG): + IObuff[len++] = '1'; + break; } do { @@ -5379,21 +5474,22 @@ static void uc_list(char_u *name, size_t name_len) break; } } - if (gap == &ucmds || i < gap->ga_len) + if (gap == &ucmds || i < gap->ga_len) { break; + } gap = &ucmds; } - if (!found) + if (!found) { MSG(_("No user-defined commands found")); + } } -static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, - int *flags, int *complp, char_u **compl_arg, - cmd_addr_T *addr_type_arg) +static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int *flags, + int *complp, char_u **compl_arg, cmd_addr_T *addr_type_arg) FUNC_ATTR_NONNULL_ALL { - char_u *p; + char_u *p; if (len == 0) { EMSG(_("E175: No attribute specified")); @@ -5411,7 +5507,7 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, *argt |= EX_TRLBAR; } else { int i; - char_u *val = NULL; + char_u *val = NULL; size_t vallen = 0; size_t attrlen = len; @@ -5479,17 +5575,20 @@ invalid_count: if (val != NULL) { p = val; - if (*def >= 0) + if (*def >= 0) { goto two_count; + } *def = getdigits_long(&p, true, 0); - if (p != val + vallen) + if (p != val + vallen) { goto invalid_count; + } } - if (*def < 0) + if (*def < 0) { *def = 0; + } } else if (STRNICMP(attr, "complete", attrlen) == 0) { if (val == NULL) { EMSG(_("E179: argument required for -complete")); @@ -5524,26 +5623,28 @@ invalid_count: return OK; } +static char e_complete_used_without_nargs[] = N_("E1208: -complete used without -nargs"); + /* * ":command ..." */ static void ex_command(exarg_T *eap) { - char_u *name; - char_u *end; - char_u *p; + char_u *name; + char_u *end; + char_u *p; uint32_t argt = 0; long def = -1; int flags = 0; int compl = EXPAND_NOTHING; - char_u *compl_arg = NULL; + char_u *compl_arg = NULL; cmd_addr_T addr_type_arg = ADDR_NONE; int has_attr = (eap->arg[0] == '-'); int name_len; p = eap->arg; - /* Check for attributes */ + // Check for attributes while (*p == '-') { ++p; end = skiptowhite(p); @@ -5575,10 +5676,10 @@ static void ex_command(exarg_T *eap) uc_list(name, end - name); } else if (!ASCII_ISUPPER(*name)) { EMSG(_("E183: User defined commands must start with an uppercase letter")); - return; } else if (name_len <= 4 && STRNCMP(name, "Next", name_len) == 0) { EMSG(_("E841: Reserved name, cannot be used for user defined command")); - return; + } else if (compl > 0 && (argt & EX_EXTRA) == 0) { + EMSG(_(e_complete_used_without_nargs)); } else { uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, addr_type_arg, eap->forceit); @@ -5595,7 +5696,7 @@ void ex_comclear(exarg_T *eap) uc_clear(&curbuf->b_ucmds); } -static void free_ucmd(ucmd_T* cmd) { +static void free_ucmd(ucmd_T * cmd) { xfree(cmd->uc_name); xfree(cmd->uc_rep); xfree(cmd->uc_compl_arg); @@ -5612,20 +5713,22 @@ void uc_clear(garray_T *gap) static void ex_delcommand(exarg_T *eap) { int i = 0; - ucmd_T *cmd = NULL; + ucmd_T *cmd = NULL; int cmp = -1; - garray_T *gap; + garray_T *gap; gap = &curbuf->b_ucmds; for (;; ) { for (i = 0; i < gap->ga_len; ++i) { cmd = USER_CMD_GA(gap, i); cmp = STRCMP(eap->arg, cmd->uc_name); - if (cmp <= 0) + if (cmp <= 0) { break; + } } - if (gap == &ucmds || cmp == 0) + if (gap == &ucmds || cmp == 0) { break; + } gap = &ucmds; } @@ -5640,8 +5743,9 @@ static void ex_delcommand(exarg_T *eap) --gap->ga_len; - if (i < gap->ga_len) + if (i < gap->ga_len) { memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T)); + } } /* @@ -5654,9 +5758,9 @@ static char_u *uc_split_args(char_u *arg, size_t *lenp) char_u *q; int len; - /* Precalculate length */ + // Precalculate length p = arg; - len = 2; /* Initial and final quotes */ + len = 2; // Initial and final quotes while (*p) { if (p[0] == '\\' && p[1] == '\\') { @@ -5670,9 +5774,10 @@ static char_u *uc_split_args(char_u *arg, size_t *lenp) p += 1; } else if (ascii_iswhite(*p)) { p = skipwhite(p); - if (*p == NUL) + if (*p == NUL) { break; - len += 3; /* "," */ + } + len += 3; // "," } else { const int charlen = utfc_ptr2len(p); @@ -5699,8 +5804,9 @@ static char_u *uc_split_args(char_u *arg, size_t *lenp) *q++ = *p++; } else if (ascii_iswhite(*p)) { p = skipwhite(p); - if (*p == NUL) + if (*p == NUL) { break; + } *q++ = '"'; *q++ = ','; *q++ = '"'; @@ -5733,28 +5839,22 @@ static size_t add_cmd_modifier(char_u *buf, char *mod_str, bool *multi_mods) return result; } -/* - * Check for a <> code in a user command. - * "code" points to the '<'. "len" the length of the <> (inclusive). - * "buf" is where the result is to be added. - * "split_buf" points to a buffer used for splitting, caller should free it. - * "split_len" is the length of what "split_buf" contains. - * Returns the length of the replacement, which has been added to "buf". - * Returns -1 if there was no match, and only the "<" has been copied. - */ -static size_t -uc_check_code( - char_u *code, - size_t len, - char_u *buf, - ucmd_T *cmd, /* the user command we're expanding */ - exarg_T *eap, /* ex arguments */ - char_u **split_buf, - size_t *split_len -) +/// Check for a <> code in a user command. +/// +/// @param code points to the '<'. "len" the length of the <> (inclusive). +/// @param buf is where the result is to be added. +/// @param cmd the user command we're expanding +/// @param eap ex arguments +/// @param split_buf points to a buffer used for splitting, caller should free it. +/// @param split_len is the length of what "split_buf" contains. +/// +/// @return the length of the replacement, which has been added to "buf". +/// Return -1 if there was no match, and only the "<" has been copied. +static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, exarg_T *eap, + char_u **split_buf, size_t *split_len) { size_t result = 0; - char_u *p = code + 1; + char_u *p = code + 1; size_t l = len - 2; int quote = 0; enum { @@ -5801,14 +5901,16 @@ uc_check_code( switch (type) { case ct_ARGS: - /* Simple case first */ + // Simple case first if (*eap->arg == NUL) { if (quote == 1) { result = 2; - if (buf != NULL) + if (buf != NULL) { STRCPY(buf, "''"); - } else + } + } else { result = 0; + } break; } @@ -5819,12 +5921,13 @@ uc_check_code( } switch (quote) { - case 0: /* No quoting, no splitting */ + case 0: // No quoting, no splitting result = STRLEN(eap->arg); - if (buf != NULL) + if (buf != NULL) { STRCPY(buf, eap->arg); + } break; - case 1: /* Quote, but don't split */ + case 1: // Quote, but don't split result = STRLEN(eap->arg) + 2; for (p = eap->arg; *p; p++) { if (*p == '\\' || *p == '"') { @@ -5844,14 +5947,16 @@ uc_check_code( } break; - case 2: /* Quote and split (<f-args>) */ - /* This is hard, so only do it once, and cache the result */ - if (*split_buf == NULL) + case 2: // Quote and split (<f-args>) + // This is hard, so only do it once, and cache the result + if (*split_buf == NULL) { *split_buf = uc_split_args(eap->arg, split_len); + } result = *split_len; - if (buf != NULL && result != 0) + if (buf != NULL && result != 0) { STRCPY(buf, *split_buf); + } break; } @@ -5859,155 +5964,166 @@ uc_check_code( case ct_BANG: result = eap->forceit ? 1 : 0; - if (quote) + if (quote) { result += 2; + } if (buf != NULL) { - if (quote) + if (quote) { *buf++ = '"'; - if (eap->forceit) + } + if (eap->forceit) { *buf++ = '!'; - if (quote) + } + if (quote) { *buf = '"'; + } } break; case ct_LINE1: case ct_LINE2: case ct_RANGE: - case ct_COUNT: - { - char num_buf[20]; - long num = (type == ct_LINE1) ? eap->line1 : - (type == ct_LINE2) ? eap->line2 : - (type == ct_RANGE) ? eap->addr_count : - (eap->addr_count > 0) ? eap->line2 : cmd->uc_def; - size_t num_len; - - sprintf(num_buf, "%" PRId64, (int64_t)num); - num_len = STRLEN(num_buf); - result = num_len; - - if (quote) - result += 2; - - if (buf != NULL) { - if (quote) - *buf++ = '"'; - STRCPY(buf, num_buf); - buf += num_len; - if (quote) - *buf = '"'; - } - - break; - } + case ct_COUNT: { + char num_buf[20]; + long num = (type == ct_LINE1) ? eap->line1 : + (type == ct_LINE2) ? eap->line2 : + (type == ct_RANGE) ? eap->addr_count : + (eap->addr_count > 0) ? eap->line2 : cmd->uc_def; + size_t num_len; + + sprintf(num_buf, "%" PRId64, (int64_t)num); + num_len = STRLEN(num_buf); + result = num_len; - case ct_MODS: - { - result = quote ? 2 : 0; - if (buf != NULL) { if (quote) { - *buf++ = '"'; + result += 2; } - *buf = '\0'; - } - bool multi_mods = false; + if (buf != NULL) { + if (quote) { + *buf++ = '"'; + } + STRCPY(buf, num_buf); + buf += num_len; + if (quote) { + *buf = '"'; + } + } - // :aboveleft and :leftabove - if (cmdmod.split & WSP_ABOVE) { - result += add_cmd_modifier(buf, "aboveleft", &multi_mods); - } - // :belowright and :rightbelow - if (cmdmod.split & WSP_BELOW) { - result += add_cmd_modifier(buf, "belowright", &multi_mods); - } - // :botright - if (cmdmod.split & WSP_BOT) { - result += add_cmd_modifier(buf, "botright", &multi_mods); + break; } - typedef struct { - bool *set; - char *name; - } mod_entry_T; - static mod_entry_T mod_entries[] = { - { &cmdmod.browse, "browse" }, - { &cmdmod.confirm, "confirm" }, - { &cmdmod.hide, "hide" }, - { &cmdmod.keepalt, "keepalt" }, - { &cmdmod.keepjumps, "keepjumps" }, - { &cmdmod.keepmarks, "keepmarks" }, - { &cmdmod.keeppatterns, "keeppatterns" }, - { &cmdmod.lockmarks, "lockmarks" }, - { &cmdmod.noswapfile, "noswapfile" } - }; - // the modifiers that are simple flags - for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) { - if (*mod_entries[i].set) { - result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods); + case ct_MODS: { + result = quote ? 2 : 0; + if (buf != NULL) { + if (quote) { + *buf++ = '"'; + } + *buf = '\0'; + } + + bool multi_mods = false; + + // :aboveleft and :leftabove + if (cmdmod.split & WSP_ABOVE) { + result += add_cmd_modifier(buf, "aboveleft", &multi_mods); + } + // :belowright and :rightbelow + if (cmdmod.split & WSP_BELOW) { + result += add_cmd_modifier(buf, "belowright", &multi_mods); + } + // :botright + if (cmdmod.split & WSP_BOT) { + result += add_cmd_modifier(buf, "botright", &multi_mods); + } + + typedef struct { + bool *set; + char *name; + } mod_entry_T; + static mod_entry_T mod_entries[] = { + { &cmdmod.browse, "browse" }, + { &cmdmod.confirm, "confirm" }, + { &cmdmod.hide, "hide" }, + { &cmdmod.keepalt, "keepalt" }, + { &cmdmod.keepjumps, "keepjumps" }, + { &cmdmod.keepmarks, "keepmarks" }, + { &cmdmod.keeppatterns, "keeppatterns" }, + { &cmdmod.lockmarks, "lockmarks" }, + { &cmdmod.noswapfile, "noswapfile" } + }; + // the modifiers that are simple flags + for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) { + if (*mod_entries[i].set) { + result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods); + } } - } - // TODO(vim): How to support :noautocmd? - // TODO(vim): How to support :sandbox? + // TODO(vim): How to support :noautocmd? + // TODO(vim): How to support :sandbox? - // :silent - if (msg_silent > 0) { - result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent", - &multi_mods); - } - // :tab - if (cmdmod.tab > 0) { - result += add_cmd_modifier(buf, "tab", &multi_mods); - } - // :topleft - if (cmdmod.split & WSP_TOP) { - result += add_cmd_modifier(buf, "topleft", &multi_mods); - } + // :silent + if (msg_silent > 0) { + result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent", + &multi_mods); + } + // :tab + if (cmdmod.tab > 0) { + result += add_cmd_modifier(buf, "tab", &multi_mods); + } + // :topleft + if (cmdmod.split & WSP_TOP) { + result += add_cmd_modifier(buf, "topleft", &multi_mods); + } - // TODO(vim): How to support :unsilent? + // TODO(vim): How to support :unsilent? - // :verbose - if (p_verbose > 0) { - result += add_cmd_modifier(buf, "verbose", &multi_mods); - } - // :vertical - if (cmdmod.split & WSP_VERT) { - result += add_cmd_modifier(buf, "vertical", &multi_mods); - } - if (quote && buf != NULL) { - buf += result - 2; - *buf = '"'; + // :verbose + if (p_verbose > 0) { + result += add_cmd_modifier(buf, "verbose", &multi_mods); + } + // :vertical + if (cmdmod.split & WSP_VERT) { + result += add_cmd_modifier(buf, "vertical", &multi_mods); + } + if (quote && buf != NULL) { + buf += result - 2; + *buf = '"'; + } + break; } - break; - } case ct_REGISTER: result = eap->regname ? 1 : 0; - if (quote) + if (quote) { result += 2; + } if (buf != NULL) { - if (quote) + if (quote) { *buf++ = '\''; - if (eap->regname) + } + if (eap->regname) { *buf++ = eap->regname; - if (quote) + } + if (quote) { *buf = '\''; + } } break; case ct_LT: result = 1; - if (buf != NULL) + if (buf != NULL) { *buf = '<'; + } break; default: - /* Not recognized: just copy the '<' and return -1. */ + // Not recognized: just copy the '<' and return -1. result = (size_t)-1; - if (buf != NULL) + if (buf != NULL) { *buf = '<'; + } break; } @@ -6016,24 +6132,25 @@ uc_check_code( static void do_ucmd(exarg_T *eap) { - char_u *buf; - char_u *p; - char_u *q; + char_u *buf; + char_u *p; + char_u *q; - char_u *start; - char_u *end = NULL; - char_u *ksp; + char_u *start; + char_u *end = NULL; + char_u *ksp; size_t len, totlen; size_t split_len = 0; - char_u *split_buf = NULL; - ucmd_T *cmd; + char_u *split_buf = NULL; + ucmd_T *cmd; const sctx_T save_current_sctx = current_sctx; - if (eap->cmdidx == CMD_USER) + if (eap->cmdidx == CMD_USER) { cmd = USER_CMD(eap->useridx); - else + } else { cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx); + } /* * Replace <> in the command by the arguments. @@ -6042,14 +6159,15 @@ static void do_ucmd(exarg_T *eap) */ buf = NULL; for (;; ) { - p = cmd->uc_rep; /* source */ - q = buf; /* destination */ + p = cmd->uc_rep; // source + q = buf; // destination totlen = 0; for (;; ) { start = vim_strchr(p, '<'); - if (start != NULL) + if (start != NULL) { end = vim_strchr(start + 1, '>'); + } if (buf != NULL) { for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ksp++) { } @@ -6071,41 +6189,44 @@ static void do_ucmd(exarg_T *eap) } } - /* break if there no <item> is found */ - if (start == NULL || end == NULL) + // break if there no <item> is found + if (start == NULL || end == NULL) { break; + } - /* Include the '>' */ + // Include the '>' ++end; - /* Take everything up to the '<' */ + // Take everything up to the '<' len = start - p; - if (buf == NULL) + if (buf == NULL) { totlen += len; - else { + } else { memmove(q, p, len); q += len; } len = uc_check_code(start, end - start, q, cmd, eap, - &split_buf, &split_len); + &split_buf, &split_len); if (len == (size_t)-1) { - /* no match, continue after '<' */ + // no match, continue after '<' p = start + 1; len = 1; - } else + } else { p = end; - if (buf == NULL) + } + if (buf == NULL) { totlen += len; - else + } else { q += len; + } } - if (buf != NULL) { /* second time here, finished */ + if (buf != NULL) { // second time here, finished STRCPY(q, p); break; } - totlen += STRLEN(p); /* Add on the trailing characters */ + totlen += STRLEN(p); // Add on the trailing characters buf = xmalloc(totlen + 1); } @@ -6156,12 +6277,13 @@ char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) */ char_u *get_user_cmd_flags(expand_T *xp, int idx) { - static char *user_cmd_flags[] = {"addr", "bang", "bar", - "buffer", "complete", "count", - "nargs", "range", "register"}; + static char *user_cmd_flags[] = { "addr", "bang", "bar", + "buffer", "complete", "count", + "nargs", "range", "register" }; - if (idx >= (int)ARRAY_SIZE(user_cmd_flags)) + if (idx >= (int)ARRAY_SIZE(user_cmd_flags)) { return NULL; + } return (char_u *)user_cmd_flags[idx]; } @@ -6170,10 +6292,11 @@ char_u *get_user_cmd_flags(expand_T *xp, int idx) */ char_u *get_user_cmd_nargs(expand_T *xp, int idx) { - static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"}; + static char *user_cmd_nargs[] = { "0", "1", "*", "?", "+" }; - if (idx >= (int)ARRAY_SIZE(user_cmd_nargs)) + if (idx >= (int)ARRAY_SIZE(user_cmd_nargs)) { return NULL; + } return (char_u *)user_cmd_nargs[idx]; } @@ -6229,8 +6352,8 @@ int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg) * copied to allocated memory and stored in "*compl_arg". * Returns FAIL if something is wrong. */ -int parse_compl_arg(const char_u *value, int vallen, int *complp, - uint32_t *argt, char_u **compl_arg) +int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt, + char_u **compl_arg) FUNC_ATTR_NONNULL_ALL { const char_u *arg = NULL; @@ -6238,7 +6361,7 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, int i; int valend = vallen; - /* Look for any argument part - which is the part after any ',' */ + // Look for any argument part - which is the part after any ',' for (i = 0; i < vallen; ++i) { if (value[i] == ',') { arg = &value[i + 1]; @@ -6289,17 +6412,17 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, int cmdcomplete_str_to_type(const char *complete_str) { - for (int i = 0; i < (int)(ARRAY_SIZE(command_complete)); i++) { - char *cmd_compl = get_command_complete(i); - if (cmd_compl == NULL) { - continue; - } - if (strcmp(complete_str, command_complete[i]) == 0) { - return i; - } + for (int i = 0; i < (int)(ARRAY_SIZE(command_complete)); i++) { + char *cmd_compl = get_command_complete(i); + if (cmd_compl == NULL) { + continue; } + if (strcmp(complete_str, command_complete[i]) == 0) { + return i; + } + } - return EXPAND_NOTHING; + return EXPAND_NOTHING; } static void ex_colorscheme(exarg_T *eap) @@ -6316,10 +6439,12 @@ static void ex_colorscheme(exarg_T *eap) if (p != NULL) { MSG(p); xfree(p); - } else + } else { MSG("default"); - } else if (load_colors(eap->arg) == FAIL) + } + } else if (load_colors(eap->arg) == FAIL) { EMSG2(_("E185: Cannot find color scheme '%s'"), eap->arg); + } } static void ex_highlight(exarg_T *eap) @@ -6377,7 +6502,7 @@ static void ex_quit(exarg_T *eap) cmdwin_result = Ctrl_C; return; } - /* Don't quit while editing the command line. */ + // Don't quit while editing the command line. if (text_locked()) { text_locked_msg(); return; @@ -6389,8 +6514,9 @@ static void ex_quit(exarg_T *eap) int wnr = eap->line2; for (wp = firstwin; wp->w_next != NULL; wp = wp->w_next) { - if (--wnr <= 0) + if (--wnr <= 0) { break; + } } } else { wp = curwin; @@ -6452,7 +6578,7 @@ static void ex_quit_all(exarg_T *eap) return; } - /* Don't quit while editing the command line. */ + // Don't quit while editing the command line. if (text_locked()) { text_locked_msg(); return; @@ -6489,8 +6615,9 @@ static void ex_close(exarg_T *eap) break; } } - if (win == NULL) + if (win == NULL) { win = lastwin; + } ex_win_close(eap->forceit, win, NULL); } } @@ -6509,19 +6636,14 @@ static void ex_pclose(exarg_T *eap) } } -/* - * Close window "win" and take care of handling closing the last window for a - * modified buffer. - */ -void -ex_win_close( - int forceit, - win_T *win, - tabpage_T *tp /* NULL or the tab page "win" is in */ -) +/// Close window "win" and take care of handling closing the last window for a +/// modified buffer. +/// +/// @param tp NULL or the tab page "win" is in +void ex_win_close(int forceit, win_T *win, tabpage_T *tp) { int need_hide; - buf_T *buf = win->w_buffer; + buf_T *buf = win->w_buffer; // Never close the autocommand window. if (win == aucmd_win) { @@ -6560,13 +6682,13 @@ ex_win_close( */ static void ex_tabclose(exarg_T *eap) { - tabpage_T *tp; + tabpage_T *tp; - if (cmdwin_type != 0) + if (cmdwin_type != 0) { cmdwin_result = K_IGNORE; - else if (first_tabpage->tp_next == NULL) + } else if (first_tabpage->tp_next == NULL) { EMSG(_("E784: Cannot close last tab page")); - else { + } else { int tab_number = get_tabpage_arg(eap); if (eap->errmsg == NULL) { tp = find_tabpage(tab_number); @@ -6590,7 +6712,7 @@ static void ex_tabonly(exarg_T *eap) if (cmdwin_type != 0) { cmdwin_result = K_IGNORE; } else if (first_tabpage->tp_next == NULL) { - MSG(_("Already only one tab page")); + MSG(_("Already only one tab page")); } else { int tab_number = get_tabpage_arg(eap); if (eap->errmsg == NULL) { @@ -6645,7 +6767,7 @@ void tabpage_close(int forceit) void tabpage_close_other(tabpage_T *tp, int forceit) { int done = 0; - win_T *wp; + win_T *wp; int h = tabline_height(); char_u prev_idx[NUMBUFLEN]; @@ -6658,13 +6780,15 @@ void tabpage_close_other(tabpage_T *tp, int forceit) /* Autocommands may delete the tab page under our fingers and we may * fail to close a window with a modified buffer. */ - if (!valid_tabpage(tp) || tp->tp_firstwin == wp) + if (!valid_tabpage(tp) || tp->tp_firstwin == wp) { break; + } } - redraw_tabline = TRUE; - if (h != tabline_height()) + redraw_tabline = true; + if (h != tabline_height()) { shell_new_rows(); + } } /* @@ -6678,10 +6802,11 @@ static void ex_only(exarg_T *eap) if (eap->addr_count > 0) { wnr = eap->line2; for (wp = firstwin; --wnr > 0;) { - if (wp->w_next == NULL) + if (wp->w_next == NULL) { break; - else + } else { wp = wp->w_next; + } } } else { wp = curwin; @@ -6698,34 +6823,35 @@ static void ex_only(exarg_T *eap) */ void ex_all(exarg_T *eap) { - if (eap->addr_count == 0) + if (eap->addr_count == 0) { eap->line2 = 9999; + } do_arg_all((int)eap->line2, eap->forceit, eap->cmdidx == CMD_drop); } static void ex_hide(exarg_T *eap) { - // ":hide" or ":hide | cmd": hide current window - if (!eap->skip) { - if (eap->addr_count == 0) { - win_close(curwin, false); // don't free buffer - } else { - int winnr = 0; - win_T *win = NULL; - - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - winnr++; - if (winnr == eap->line2) { - win = wp; - break; - } - } - if (win == NULL) { - win = lastwin; - } - win_close(win, false); + // ":hide" or ":hide | cmd": hide current window + if (!eap->skip) { + if (eap->addr_count == 0) { + win_close(curwin, false); // don't free buffer + } else { + int winnr = 0; + win_T *win = NULL; + + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + winnr++; + if (winnr == eap->line2) { + win = wp; + break; } + } + if (win == NULL) { + win = lastwin; + } + win_close(win, false); } + } } /// ":stop" and ":suspend": Suspend Vim. @@ -6756,7 +6882,7 @@ static void ex_exit(exarg_T *eap) cmdwin_result = Ctrl_C; return; } - /* Don't quit while editing the command line. */ + // Don't quit while editing the command line. if (text_locked()) { text_locked_msg(); return; @@ -6790,25 +6916,26 @@ static void ex_exit(exarg_T *eap) */ static void ex_print(exarg_T *eap) { - if (curbuf->b_ml.ml_flags & ML_EMPTY) + if (curbuf->b_ml.ml_flags & ML_EMPTY) { EMSG(_(e_emptybuf)); - else { + } else { for (; !got_int; os_breakcheck()) { print_line(eap->line1, - (eap->cmdidx == CMD_number || eap->cmdidx == CMD_pound - || (eap->flags & EXFLAG_NR)), - eap->cmdidx == CMD_list || (eap->flags & EXFLAG_LIST)); - if (++eap->line1 > eap->line2) + (eap->cmdidx == CMD_number || eap->cmdidx == CMD_pound + || (eap->flags & EXFLAG_NR)), + eap->cmdidx == CMD_list || (eap->flags & EXFLAG_LIST)); + if (++eap->line1 > eap->line2) { break; - ui_flush(); /* show one line at a time */ + } + ui_flush(); // show one line at a time } setpcmark(); - /* put cursor at last line */ + // put cursor at last line curwin->w_cursor.lnum = eap->line2; beginline(BL_SOL | BL_FIX); } - ex_no_reprint = TRUE; + ex_no_reprint = true; } static void ex_goto(exarg_T *eap) @@ -6866,11 +6993,11 @@ void alist_new(void) */ void alist_expand(int *fnum_list, int fnum_len) { - char_u **old_arg_files; + char_u **old_arg_files; int old_arg_count; - char_u **new_arg_files; + char_u **new_arg_files; int new_arg_file_count; - char_u *save_p_su = p_su; + char_u *save_p_su = p_su; int i; /* Don't use 'suffixes' here. This should work like the shell did the @@ -6878,15 +7005,16 @@ void alist_expand(int *fnum_list, int fnum_len) * can't set the options. */ p_su = empty_option; old_arg_files = xmalloc(sizeof(*old_arg_files) * GARGCOUNT); - for (i = 0; i < GARGCOUNT; ++i) + for (i = 0; i < GARGCOUNT; ++i) { old_arg_files[i] = vim_strsave(GARGLIST[i].ae_fname); + } old_arg_count = GARGCOUNT; if (expand_wildcards(old_arg_count, old_arg_files, - &new_arg_file_count, &new_arg_files, - EW_FILE|EW_NOTFOUND|EW_ADDSLASH|EW_NOERROR) == OK + &new_arg_file_count, &new_arg_files, + EW_FILE|EW_NOTFOUND|EW_ADDSLASH|EW_NOERROR) == OK && new_arg_file_count > 0) { alist_set(&global_alist, new_arg_file_count, new_arg_files, - TRUE, fnum_list, fnum_len); + TRUE, fnum_list, fnum_len); FreeWild(old_arg_count, old_arg_files); } p_su = save_p_su; @@ -6915,15 +7043,17 @@ void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum if (got_int) { /* When adding many buffers this can take a long time. Allow * interrupting here. */ - while (i < count) + while (i < count) { xfree(files[i++]); + } break; } /* May set buffer name of a buffer previously used for the * argument list, so that it's re-used by alist_add. */ - if (fnum_list != NULL && i < fnum_len) + if (fnum_list != NULL && i < fnum_len) { buf_set_name(fnum_list[i], files[i]); + } alist_add(al, files[i], use_curbuf ? 2 : 1); os_breakcheck(); @@ -6937,26 +7067,23 @@ void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum recursive--; } -/* - * Add file "fname" to argument list "al". - * "fname" must have been allocated and "al" must have been checked for room. - */ -void -alist_add( - alist_T *al, - char_u *fname, - int set_fnum /* 1: set buffer number; 2: re-use curbuf */ -) -{ - if (fname == NULL) /* don't add NULL file names */ +/// Add file "fname" to argument list "al". +/// "fname" must have been allocated and "al" must have been checked for room. +/// +/// @param set_fnum 1: set buffer number; 2: re-use curbuf +void alist_add(alist_T *al, char_u *fname, int set_fnum) +{ + if (fname == NULL) { // don't add NULL file names return; + } #ifdef BACKSLASH_IN_FILENAME slash_adjust(fname); #endif AARGLIST(al)[al->al_ga.ga_len].ae_fname = fname; - if (set_fnum > 0) + if (set_fnum > 0) { AARGLIST(al)[al->al_ga.ga_len].ae_fnum = buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0)); + } ++al->al_ga.ga_len; } @@ -6998,9 +7125,9 @@ static void ex_recover(exarg_T *eap) // Set recoverymode right away to avoid the ATTENTION prompt. recoverymode = true; if (!check_changed(curbuf, (p_awa ? CCGD_AW : 0) - | CCGD_MULTWIN - | (eap->forceit ? CCGD_FORCEIT : 0) - | CCGD_EXCMD) + | CCGD_MULTWIN + | (eap->forceit ? CCGD_FORCEIT : 0) + | CCGD_EXCMD) && (*eap->arg == NUL || setfname(curbuf, eap->arg, NULL, true) == OK)) { @@ -7018,40 +7145,43 @@ static void ex_wrongmodifier(exarg_T *eap) } /* - * :sview [+command] file split window with new file, read-only - * :split [[+command] file] split window with current or new file - * :vsplit [[+command] file] split window vertically with current or new file - * :new [[+command] file] split window with no or new file - * :vnew [[+command] file] split vertically window with no or new file - * :sfind [+command] file split window with file in 'path' + * :sview [+command] file split window with new file, read-only + * :split [[+command] file] split window with current or new file + * :vsplit [[+command] file] split window vertically with current or new file + * :new [[+command] file] split window with no or new file + * :vnew [[+command] file] split vertically window with no or new file + * :sfind [+command] file split window with file in 'path' * - * :tabedit open new Tab page with empty window - * :tabedit [+command] file open new Tab page and edit "file" - * :tabnew [[+command] file] just like :tabedit - * :tabfind [+command] file open new Tab page and find "file" + * :tabedit open new Tab page with empty window + * :tabedit [+command] file open new Tab page and edit "file" + * :tabnew [[+command] file] just like :tabedit + * :tabfind [+command] file open new Tab page and find "file" */ void ex_splitview(exarg_T *eap) { - win_T *old_curwin = curwin; - char_u *fname = NULL; + win_T *old_curwin = curwin; + char_u *fname = NULL; const bool use_tab = eap->cmdidx == CMD_tabedit - || eap->cmdidx == CMD_tabfind - || eap->cmdidx == CMD_tabnew; + || eap->cmdidx == CMD_tabfind + || eap->cmdidx == CMD_tabnew; /* A ":split" in the quickfix window works like ":new". Don't want two * quickfix windows. But it's OK when doing ":tab split". */ if (bt_quickfix(curbuf) && cmdmod.tab == 0) { - if (eap->cmdidx == CMD_split) + if (eap->cmdidx == CMD_split) { eap->cmdidx = CMD_new; - if (eap->cmdidx == CMD_vsplit) + } + if (eap->cmdidx == CMD_vsplit) { eap->cmdidx = CMD_vnew; + } } if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) { fname = find_file_in_path(eap->arg, STRLEN(eap->arg), FNAME_MESS, TRUE, curbuf->b_ffname); - if (fname == NULL) + if (fname == NULL) { goto theend; + } eap->arg = fname; } @@ -7064,15 +7194,16 @@ void ex_splitview(exarg_T *eap) do_exedit(eap, old_curwin); apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, FALSE, curbuf); - /* set the alternate buffer for the window we came from */ + // set the alternate buffer for the window we came from if (curwin != old_curwin && win_valid(old_curwin) && old_curwin->w_buffer != curbuf - && !cmdmod.keepalt) + && !cmdmod.keepalt) { old_curwin->w_alt_fnum = curbuf->b_fnum; + } } } else if (win_split(eap->addr_count > 0 ? (int)eap->line2 : 0, - *eap->cmd == 'v' ? WSP_VERT : 0) != FAIL) { + *eap->cmd == 'v' ? WSP_VERT : 0) != FAIL) { /* Reset 'scrollbind' when editing another file, but keep it when * doing ":split" without arguments. */ if (*eap->arg != NUL @@ -7179,7 +7310,7 @@ static void ex_tabs(exarg_T *eap) FOR_ALL_TABS(tp) { if (got_int) { - break; + break; } msg_putchar('\n'); @@ -7198,13 +7329,14 @@ static void ex_tabs(exarg_T *eap) msg_putchar(' '); msg_putchar(bufIsChanged(wp->w_buffer) ? '+' : ' '); msg_putchar(' '); - if (buf_spname(wp->w_buffer) != NULL) + if (buf_spname(wp->w_buffer) != NULL) { STRLCPY(IObuff, buf_spname(wp->w_buffer), IOSIZE); - else + } else { home_replace(wp->w_buffer, wp->w_buffer->b_fname, - IObuff, IOSIZE, TRUE); + IObuff, IOSIZE, TRUE); + } msg_outtrans(IObuff); - ui_flush(); /* output one line at a time */ + ui_flush(); // output one line at a time os_breakcheck(); } } @@ -7232,12 +7364,13 @@ static void ex_mode(exarg_T *eap) static void ex_resize(exarg_T *eap) { int n; - win_T *wp = curwin; + win_T *wp = curwin; if (eap->addr_count > 0) { n = eap->line2; - for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next) + for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next) { ; + } } n = atol((char *)eap->arg); @@ -7263,7 +7396,7 @@ static void ex_resize(exarg_T *eap) */ static void ex_find(exarg_T *eap) { - char_u *fname; + char_u *fname; int count; fname = find_file_in_path(eap->arg, STRLEN(eap->arg), @@ -7291,14 +7424,10 @@ static void ex_edit(exarg_T *eap) do_exedit(eap, NULL); } -/* - * ":edit <file>" command and alikes. - */ -void -do_exedit( - exarg_T *eap, - win_T *old_curwin /* curwin before doing a split or NULL */ -) +/// ":edit <file>" command and alikes. +/// +/// @param old_curwin curwin before doing a split or NULL +void do_exedit(exarg_T *eap, win_T *old_curwin) { int n; int need_hide; @@ -7308,10 +7437,10 @@ do_exedit( */ if (exmode_active && (eap->cmdidx == CMD_visual || eap->cmdidx == CMD_view)) { - exmode_active = 0; + exmode_active = false; ex_pressedreturn = false; if (*eap->arg == NUL) { - /* Special case: ":global/pat/visual\NLvi-commands" */ + // Special case: ":global/pat/visual\NLvi-commands" if (global_busy) { int rd = RedrawingDisabled; int nwr = no_wait_return; @@ -7324,7 +7453,7 @@ do_exedit( RedrawingDisabled = 0; no_wait_return = 0; - need_wait_return = FALSE; + need_wait_return = false; msg_scroll = 0; redraw_all_later(NOT_VALID); @@ -7343,23 +7472,25 @@ do_exedit( || eap->cmdidx == CMD_tabedit || eap->cmdidx == CMD_vnew ) && *eap->arg == NUL) { - /* ":new" or ":tabnew" without argument: edit an new empty buffer */ + // ":new" or ":tabnew" without argument: edit an new empty buffer setpcmark(); (void)do_ecmd(0, NULL, NULL, eap, ECMD_ONE, - ECMD_HIDE + (eap->forceit ? ECMD_FORCEIT : 0), - old_curwin == NULL ? curwin : NULL); + ECMD_HIDE + (eap->forceit ? ECMD_FORCEIT : 0), + old_curwin == NULL ? curwin : NULL); } else if ((eap->cmdidx != CMD_split && eap->cmdidx != CMD_vsplit) || *eap->arg != NUL) { - /* Can't edit another file when "curbuf_lock" is set. Only ":edit" - * can bring us here, others are stopped earlier. */ - if (*eap->arg != NUL && curbuf_locked()) + // Can't edit another file when "curbuf->b_ro_lockec" is set. Only ":edit" + // can bring us here, others are stopped earlier. + if (*eap->arg != NUL && curbuf_locked()) { return; + } n = readonlymode; - if (eap->cmdidx == CMD_view || eap->cmdidx == CMD_sview) - readonlymode = TRUE; - else if (eap->cmdidx == CMD_enew) - readonlymode = FALSE; /* 'readonly' doesn't make sense in an - empty buffer */ + if (eap->cmdidx == CMD_view || eap->cmdidx == CMD_sview) { + readonlymode = true; + } else if (eap->cmdidx == CMD_enew) { + readonlymode = false; // 'readonly' doesn't make sense + // in an empty buffer + } if (eap->cmdidx != CMD_balt && eap->cmdidx != CMD_badd) { setpcmark(); } @@ -7398,12 +7529,14 @@ do_exedit( } readonlymode = n; } else { - if (eap->do_ecmd_cmd != NULL) + if (eap->do_ecmd_cmd != NULL) { do_cmdline_cmd((char *)eap->do_ecmd_cmd); + } n = curwin->w_arg_idx_invalid; check_arg_idx(curwin); - if (n != curwin->w_arg_idx_invalid) + if (n != curwin->w_arg_idx_invalid) { maketitle(); + } } /* @@ -7415,10 +7548,11 @@ do_exedit( && curwin != old_curwin && win_valid(old_curwin) && old_curwin->w_buffer != curbuf - && !cmdmod.keepalt) + && !cmdmod.keepalt) { old_curwin->w_alt_fnum = curbuf->b_fnum; + } - ex_no_reprint = TRUE; + ex_no_reprint = true; } /// ":gui" and ":gvim" when there is no GUI. @@ -7431,10 +7565,11 @@ static void ex_nogui(exarg_T *eap) static void ex_swapname(exarg_T *eap) { - if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) + if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) { MSG(_("No swap file")); - else + } else { msg(curbuf->b_ml.ml_mfp->mf_fname); + } } /* @@ -7444,8 +7579,8 @@ static void ex_swapname(exarg_T *eap) */ static void ex_syncbind(exarg_T *eap) { - win_T *save_curwin = curwin; - buf_T *save_curbuf = curbuf; + win_T *save_curwin = curwin; + buf_T *save_curbuf = curbuf; long topline; long y; linenr_T old_linenr = curwin->w_cursor.lnum; @@ -7481,10 +7616,11 @@ static void ex_syncbind(exarg_T *eap) if (curwin->w_p_scb) { curbuf = curwin->w_buffer; y = topline - curwin->w_topline; - if (y > 0) + if (y > 0) { scrollup(y, TRUE); - else + } else { scrolldown(-y, TRUE); + } curwin->w_scbind_pos = topline; redraw_later(curwin, VALID); cursor_correct(); @@ -7494,7 +7630,7 @@ static void ex_syncbind(exarg_T *eap) curwin = save_curwin; curbuf = save_curbuf; if (curwin->w_p_scb) { - did_syncbind = TRUE; + did_syncbind = true; checkpcmark(); if (old_linenr != curwin->w_cursor.lnum) { char_u ctrl_o[2]; @@ -7525,13 +7661,13 @@ static void ex_read(exarg_T *eap) return; } i = readfile(curbuf->b_ffname, curbuf->b_fname, - eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); + eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); } else { - if (vim_strchr(p_cpo, CPO_ALTREAD) != NULL) + if (vim_strchr(p_cpo, CPO_ALTREAD) != NULL) { (void)setaltfname(eap->arg, eap->arg, (linenr_T)1); + } i = readfile(eap->arg, NULL, - eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); - + eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); } if (i != OK) { if (!aborting()) { @@ -7541,10 +7677,11 @@ static void ex_read(exarg_T *eap) if (empty && exmode_active) { /* Delete the empty line that remains. Historically ex does * this but vi doesn't. */ - if (eap->line2 == 0) + if (eap->line2 == 0) { lnum = curbuf->b_ml.ml_line_count; - else + } else { lnum = 1; + } if (*ml_get(lnum) == NUL && u_savedel(lnum, 1L) == OK) { ml_delete(lnum, false); if (curwin->w_cursor.lnum > 1 @@ -7559,7 +7696,7 @@ static void ex_read(exarg_T *eap) } } -static char_u *prev_dir = NULL; +static char_u *prev_dir = NULL; #if defined(EXITFREE) void free_cd_dir(void) @@ -7619,21 +7756,22 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) /// `:cd`, `:tcd`, `:lcd`, `:chdir`, `:tchdir` and `:lchdir`. void ex_cd(exarg_T *eap) { - char_u *new_dir; - char_u *tofree; + char_u *new_dir; + char_u *tofree; new_dir = eap->arg; #if !defined(UNIX) - /* for non-UNIX ":cd" means: print current directory */ - if (*new_dir == NUL) + // for non-UNIX ":cd" means: print current directory + if (*new_dir == NUL) { ex_pwd(NULL); - else + } else #endif { - if (allbuf_locked()) + if (allbuf_locked()) { return; + } - /* ":cd -": Change to previous directory */ + // ":cd -": Change to previous directory if (STRCMP(new_dir, "-") == 0) { if (prev_dir == NULL) { EMSG(_("E186: No previous directory")); @@ -7642,12 +7780,13 @@ void ex_cd(exarg_T *eap) new_dir = prev_dir; } - /* Save current directory for next ":cd -" */ + // Save current directory for next ":cd -" tofree = prev_dir; - if (os_dirname(NameBuff, MAXPATHL) == OK) + if (os_dirname(NameBuff, MAXPATHL) == OK) { prev_dir = vim_strsave(NameBuff); - else + } else { prev_dir = NULL; + } #if defined(UNIX) // On Unix ":cd" means: go to home directory. @@ -7696,8 +7835,9 @@ static void ex_pwd(exarg_T *eap) slash_adjust(NameBuff); #endif msg(NameBuff); - } else + } else { EMSG(_("E187: Unknown")); + } } /* @@ -7716,15 +7856,19 @@ static void ex_sleep(exarg_T *eap) if (cursor_valid()) { n = curwin->w_winrow + curwin->w_wrow - msg_scrolled; - if (n >= 0) + if (n >= 0) { ui_cursor_goto(n, curwin->w_wincol + curwin->w_wcol); + } } len = eap->line2; switch (*eap->arg) { - case 'm': break; - case NUL: len *= 1000L; break; - default: EMSG2(_(e_invarg2), eap->arg); return; + case 'm': + break; + case NUL: + len *= 1000L; break; + default: + EMSG2(_(e_invarg2), eap->arg); return; } do_sleep(len); } @@ -7751,16 +7895,18 @@ void do_sleep(long msec) static void do_exmap(exarg_T *eap, int isabbrev) { int mode; - char_u *cmdp; + char_u *cmdp; cmdp = eap->cmd; mode = get_map_mode(&cmdp, eap->forceit || isabbrev); switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'), - eap->arg, mode, isabbrev)) { - case 1: EMSG(_(e_invarg)); + eap->arg, mode, isabbrev)) { + case 1: + EMSG(_(e_invarg)); break; - case 2: EMSG(isabbrev ? _(e_noabbr) : _(e_nomap)); + case 2: + EMSG(isabbrev ? _(e_noabbr) : _(e_nomap)); break; } } @@ -7790,25 +7936,26 @@ static void ex_winsize(exarg_T *eap) static void ex_wincmd(exarg_T *eap) { int xchar = NUL; - char_u *p; + char_u *p; if (*eap->arg == 'g' || *eap->arg == Ctrl_G) { - /* CTRL-W g and CTRL-W CTRL-G have an extra command character */ + // CTRL-W g and CTRL-W CTRL-G have an extra command character if (eap->arg[1] == NUL) { EMSG(_(e_invarg)); return; } xchar = eap->arg[1]; p = eap->arg + 2; - } else + } else { p = eap->arg + 1; + } eap->nextcmd = check_nextcmd(p); p = skipwhite(p); - if (*p != NUL && *p != '"' && eap->nextcmd == NULL) + if (*p != NUL && *p != '"' && eap->nextcmd == NULL) { EMSG(_(e_invarg)); - else if (!eap->skip) { - /* Pass flags on for ":vertical wincmd ]". */ + } else if (!eap->skip) { + // Pass flags on for ":vertical wincmd ]". postponed_split_flags = cmdmod.split; postponed_split_tab = cmdmod.tab; do_window(*eap->arg, eap->addr_count > 0 ? eap->line2 : 0L, xchar); @@ -7837,8 +7984,9 @@ static void ex_operators(exarg_T *eap) beginline(BL_SOL | BL_FIX); } - if (VIsual_active) + if (VIsual_active) { end_visual_mode(); + } switch (eap->cmdidx) { case CMD_delete: @@ -7851,13 +7999,14 @@ static void ex_operators(exarg_T *eap) (void)op_yank(&oa, true, false); break; - default: /* CMD_rshift or CMD_lshift */ + default: // CMD_rshift or CMD_lshift if ( - (eap->cmdidx == CMD_rshift) ^ curwin->w_p_rl - ) + (eap->cmdidx == CMD_rshift) ^ curwin->w_p_rl + ) { oa.op_type = OP_RSHIFT; - else + } else { oa.op_type = OP_LSHIFT; + } op_shift(&oa, FALSE, eap->amount); break; } @@ -7870,7 +8019,7 @@ static void ex_operators(exarg_T *eap) */ static void ex_put(exarg_T *eap) { - /* ":0put" works like ":1put!". */ + // ":0put" works like ":1put!". if (eap->line2 == 0) { eap->line2 = 1; eap->forceit = TRUE; @@ -7901,10 +8050,12 @@ static void ex_copymove(exarg_T *eap) } if (eap->cmdidx == CMD_move) { - if (do_move(eap->line1, eap->line2, n) == FAIL) + if (do_move(eap->line1, eap->line2, n) == FAIL) { return; - } else + } + } else { ex_copy(eap->line1, eap->line2, n); + } u_clearline(); beginline(BL_SOL | BL_FIX); ex_may_print(eap); @@ -7917,8 +8068,8 @@ void ex_may_print(exarg_T *eap) { if (eap->flags != 0) { print_line(curwin->w_cursor.lnum, (eap->flags & EXFLAG_NR), - (eap->flags & EXFLAG_LIST)); - ex_no_reprint = TRUE; + (eap->flags & EXFLAG_LIST)); + ex_no_reprint = true; } } @@ -7939,8 +8090,9 @@ static void ex_join(exarg_T *eap) { curwin->w_cursor.lnum = eap->line1; if (eap->line1 == eap->line2) { - if (eap->addr_count >= 2) /* :2,2join does nothing */ + if (eap->addr_count >= 2) { // :2,2join does nothing return; + } if (eap->line2 == curbuf->b_ml.ml_line_count) { beep_flush(); return; @@ -7968,22 +8120,23 @@ static void ex_at(exarg_T *eap) c = '@'; } - /* Put the register in the typeahead buffer with the "silent" flag. */ + // Put the register in the typeahead buffer with the "silent" flag. if (do_execreg(c, TRUE, vim_strchr(p_cpo, CPO_EXECBUF) != NULL, TRUE) == FAIL) { beep_flush(); } else { - int save_efr = exec_from_reg; + bool save_efr = exec_from_reg; - exec_from_reg = TRUE; + exec_from_reg = true; /* * Execute from the typeahead buffer. * Continue until the stuff buffer is empty and all added characters * have been consumed. */ - while (!stuff_empty() || typebuf.tb_len > prev_len) + while (!stuff_empty() || typebuf.tb_len > prev_len) { (void)do_cmdline(NULL, getexline, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE); + } exec_from_reg = save_efr; } @@ -8013,16 +8166,16 @@ static void ex_wundo(exarg_T *eap) { char_u hash[UNDO_HASH_SIZE]; - u_compute_hash(hash); - u_write_undo((char *) eap->arg, eap->forceit, curbuf, hash); + u_compute_hash(curbuf, hash); + u_write_undo((char *)eap->arg, eap->forceit, curbuf, hash); } static void ex_rundo(exarg_T *eap) { char_u hash[UNDO_HASH_SIZE]; - u_compute_hash(hash); - u_read_undo((char *) eap->arg, hash, NULL); + u_compute_hash(curbuf, hash); + u_read_undo((char *)eap->arg, hash, NULL); } /// ":redo". @@ -8037,18 +8190,23 @@ static void ex_later(exarg_T *eap) long count = 0; bool sec = false; bool file = false; - char_u *p = eap->arg; + char_u *p = eap->arg; if (*p == NUL) { count = 1; } else if (isdigit(*p)) { count = getdigits_long(&p, false, 0); switch (*p) { - case 's': ++p; sec = true; break; - case 'm': ++p; sec = true; count *= 60; break; - case 'h': ++p; sec = true; count *= 60 * 60; break; - case 'd': ++p; sec = true; count *= 24 * 60 * 60; break; - case 'f': ++p; file = true; break; + case 's': + ++p; sec = true; break; + case 'm': + ++p; sec = true; count *= 60; break; + case 'h': + ++p; sec = true; count *= 60 * 60; break; + case 'd': + ++p; sec = true; count *= 24 * 60 * 60; break; + case 'f': + ++p; file = true; break; } } @@ -8065,43 +8223,46 @@ static void ex_later(exarg_T *eap) */ static void ex_redir(exarg_T *eap) { - char *mode; - char_u *fname; - char_u *arg = eap->arg; + char *mode; + char_u *fname; + char_u *arg = eap->arg; - if (STRICMP(eap->arg, "END") == 0) + if (STRICMP(eap->arg, "END") == 0) { close_redir(); - else { + } else { if (*arg == '>') { ++arg; if (*arg == '>') { ++arg; mode = "a"; - } else + } else { mode = "w"; + } arg = skipwhite(arg); close_redir(); - /* Expand environment variables and "~/". */ + // Expand environment variables and "~/". fname = expand_env_save(arg); - if (fname == NULL) + if (fname == NULL) { return; + } redir_fd = open_exfile(fname, eap->forceit, mode); xfree(fname); } else if (*arg == '@') { - /* redirect to a register a-z (resp. A-Z for appending) */ + // redirect to a register a-z (resp. A-Z for appending) close_redir(); ++arg; if (valid_yank_reg(*arg, true) && *arg != '_') { redir_reg = *arg++; - if (*arg == '>' && arg[1] == '>') /* append */ + if (*arg == '>' && arg[1] == '>') { // append arg += 2; - else { - /* Can use both "@a" and "@a>". */ - if (*arg == '>') + } else { + // Can use both "@a" and "@a>". + if (*arg == '>') { arg++; + } // Make register empty when not using @A-@Z and the // command is valid. if (*arg == NUL && !isupper(redir_reg)) { @@ -8116,30 +8277,33 @@ static void ex_redir(exarg_T *eap) } else if (*arg == '=' && arg[1] == '>') { int append; - /* redirect to a variable */ + // redirect to a variable close_redir(); arg += 2; if (*arg == '>') { ++arg; append = TRUE; - } else + } else { append = FALSE; + } - if (var_redir_start(skipwhite(arg), append) == OK) + if (var_redir_start(skipwhite(arg), append) == OK) { redir_vname = 1; + } } - /* TODO: redirect to a buffer */ - else + // TODO: redirect to a buffer + else { EMSG2(_(e_invarg2), eap->arg); + } } /* Make sure redirection is not off. Can happen for cmdline completion * that indirectly invokes a command to catch its output. */ if (redir_fd != NULL - || redir_reg || redir_vname - ) - redir_off = FALSE; + || redir_reg || redir_vname) { + redir_off = false; + } } /// ":redraw": force redraw @@ -8159,19 +8323,19 @@ static void ex_redraw(exarg_T *eap) redraw_all_later(NOT_VALID); } update_screen(eap->forceit ? NOT_VALID - : VIsual_active ? INVERTED : 0); + : VIsual_active ? INVERTED : 0); if (need_maketitle) { maketitle(); } RedrawingDisabled = r; p_lz = p; - /* Reset msg_didout, so that a message that's there is overwritten. */ - msg_didout = FALSE; + // Reset msg_didout, so that a message that's there is overwritten. + msg_didout = false; msg_col = 0; - /* No need to wait after an intentional redraw. */ - need_wait_return = FALSE; + // No need to wait after an intentional redraw. + need_wait_return = false; ui_flush(); } @@ -8187,13 +8351,13 @@ static void ex_redrawstatus(exarg_T *eap) RedrawingDisabled = 0; p_lz = FALSE; - if (eap->forceit) + if (eap->forceit) { status_redraw_all(); - else + } else { status_redraw_curbuf(); - update_screen( - VIsual_active ? INVERTED : - 0); + } + update_screen(VIsual_active ? INVERTED : + 0); RedrawingDisabled = r; p_lz = p; ui_flush(); @@ -8245,21 +8409,17 @@ int vim_mkdir_emsg(const char *const name, const int prot) return OK; } -/* - * Open a file for writing for an Ex command, with some checks. - * Return file descriptor, or NULL on failure. - */ -FILE * -open_exfile ( - char_u *fname, - int forceit, - char *mode /* "w" for create new file or "a" for append */ -) +/// Open a file for writing for an Ex command, with some checks. +/// +/// @param mode "w" for create new file or "a" for append +/// +/// @return file descriptor, or NULL on failure. +FILE *open_exfile(char_u *fname, int forceit, char *mode) { - FILE *fd; + FILE *fd; #ifdef UNIX - /* with Unix it is possible to open a directory */ + // with Unix it is possible to open a directory if (os_isdir(fname)) { EMSG2(_(e_isadir2), fname); return NULL; @@ -8284,17 +8444,18 @@ static void ex_mark(exarg_T *eap) { pos_T pos; - if (*eap->arg == NUL) /* No argument? */ + if (*eap->arg == NUL) { // No argument? EMSG(_(e_argreq)); - else if (eap->arg[1] != NUL) /* more than one character? */ + } else if (eap->arg[1] != NUL) { // more than one character? EMSG(_(e_trailing)); - else { - pos = curwin->w_cursor; /* save curwin->w_cursor */ + } else { + pos = curwin->w_cursor; // save curwin->w_cursor curwin->w_cursor.lnum = eap->line2; beginline(BL_WHITE | BL_FIX); - if (setmark(*eap->arg) == FAIL) /* set mark */ + if (setmark(*eap->arg) == FAIL) { // set mark EMSG(_("E191: Argument must be a letter or forward/backward quote")); - curwin->w_cursor = pos; /* restore curwin->w_cursor */ + } + curwin->w_cursor = pos; // restore curwin->w_cursor } } @@ -8354,7 +8515,9 @@ void restore_current_state(save_state_T *sst) finish_op = sst->save_finish_op; opcount = sst->save_opcount; reg_executing = sst->save_reg_executing; - msg_didout |= sst->save_msg_didout; // don't reset msg_didout now + + // don't reset msg_didout now + msg_didout |= sst->save_msg_didout; // Restore the state (needed when called from a function executed for // 'indentexpr'). Update the mouse and cursor, they may have changed. @@ -8372,9 +8535,9 @@ static void ex_normal(exarg_T *eap) return; } save_state_T save_state; - char_u *arg = NULL; + char_u *arg = NULL; int l; - char_u *p; + char_u *p; if (ex_normal_lock > 0) { EMSG(_(e_secure)); @@ -8391,12 +8554,14 @@ static void ex_normal(exarg_T *eap) { int len = 0; - /* Count the number of characters to be escaped. */ + // Count the number of characters to be escaped. for (p = eap->arg; *p != NUL; ++p) { - for (l = (*mb_ptr2len)(p) - 1; l > 0; --l) - if (*++p == K_SPECIAL /* trailbyte K_SPECIAL or CSI */ - ) + for (l = (*mb_ptr2len)(p) - 1; l > 0; --l) { + if (*++p == K_SPECIAL // trailbyte K_SPECIAL or CSI + ) { len += 2; + } + } } if (len > 0) { arg = xmalloc(STRLEN(eap->arg) + len + 1); @@ -8432,7 +8597,7 @@ static void ex_normal(exarg_T *eap) } while (eap->addr_count > 0 && eap->line1 <= eap->line2 && !got_int); } - /* Might not return to the main loop when in an event handler. */ + // Might not return to the main loop when in an event handler. update_topline_cursor(); restore_current_state(&save_state); @@ -8463,16 +8628,18 @@ static void ex_startinsert(exarg_T *eap) return; } - if (eap->cmdidx == CMD_startinsert) + if (eap->cmdidx == CMD_startinsert) { restart_edit = 'a'; - else if (eap->cmdidx == CMD_startreplace) + } else if (eap->cmdidx == CMD_startreplace) { restart_edit = 'R'; - else + } else { restart_edit = 'V'; + } if (!eap->forceit) { - if (eap->cmdidx == CMD_startinsert) + if (eap->cmdidx == CMD_startinsert) { restart_edit = 'i'; + } curwin->w_curswant = 0; // avoid MAXCOL } @@ -8541,23 +8708,24 @@ static void ex_findpat(exarg_T *eap) { int whole = TRUE; long n; - char_u *p; + char_u *p; int action; switch (cmdnames[eap->cmdidx].cmd_name[2]) { - case 'e': /* ":psearch", ":isearch" and ":dsearch" */ - if (cmdnames[eap->cmdidx].cmd_name[0] == 'p') + case 'e': // ":psearch", ":isearch" and ":dsearch" + if (cmdnames[eap->cmdidx].cmd_name[0] == 'p') { action = ACTION_GOTO; - else + } else { action = ACTION_SHOW; + } break; - case 'i': /* ":ilist" and ":dlist" */ + case 'i': // ":ilist" and ":dlist" action = ACTION_SHOW_ALL; break; - case 'u': /* ":ijump" and ":djump" */ + case 'u': // ":ijump" and ":djump" action = ACTION_GOTO; break; - default: /* ":isplit" and ":dsplit" */ + default: // ":isplit" and ":dsplit" action = ACTION_SPLIT; break; } @@ -8583,10 +8751,11 @@ static void ex_findpat(exarg_T *eap) } } } - if (!eap->skip) + 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); + } } @@ -8595,7 +8764,7 @@ static void ex_findpat(exarg_T *eap) */ static void ex_ptag(exarg_T *eap) { - g_do_tagpreview = p_pvh; /* will be reset to 0 in ex_tag_cmd() */ + g_do_tagpreview = p_pvh; // will be reset to 0 in ex_tag_cmd() ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1); } @@ -8604,7 +8773,7 @@ static void ex_ptag(exarg_T *eap) */ static void ex_pedit(exarg_T *eap) { - win_T *curwin_save = curwin; + win_T *curwin_save = curwin; // Open the preview window or popup and make it the current window. g_do_tagpreview = p_pvh; @@ -8648,21 +8817,28 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name) int cmd; switch (name[1]) { - case 'j': cmd = DT_JUMP; // ":tjump" + case 'j': + cmd = DT_JUMP; // ":tjump" break; - case 's': cmd = DT_SELECT; // ":tselect" + case 's': + cmd = DT_SELECT; // ":tselect" break; case 'p': // ":tprevious" - case 'N': cmd = DT_PREV; // ":tNext" + case 'N': + cmd = DT_PREV; // ":tNext" break; - case 'n': cmd = DT_NEXT; // ":tnext" + case 'n': + cmd = DT_NEXT; // ":tnext" break; - case 'o': cmd = DT_POP; // ":pop" + case 'o': + cmd = DT_POP; // ":pop" break; case 'f': // ":tfirst" - case 'r': cmd = DT_FIRST; // ":trewind" + case 'r': + cmd = DT_FIRST; // ":trewind" break; - case 'l': cmd = DT_LAST; // ":tlast" + case 'l': + cmd = DT_LAST; // ":tlast" break; default: // ":tag" if (p_cst && *eap->arg != NUL) { @@ -8678,7 +8854,7 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name) } do_tag(eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1, - eap->forceit, TRUE); + eap->forceit, TRUE); } enum { @@ -8737,58 +8913,57 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen) return -1; } -/* - * Evaluate cmdline variables. - * - * change '%' to curbuf->b_ffname - * '#' to curwin->w_alt_fnum - * '<cword>' to word under the cursor - * '<cWORD>' to WORD under the cursor - * '<cexpr>' to C-expression under the cursor - * '<cfile>' to path name under the cursor - * '<sfile>' to sourced file name - * '<slnum>' to sourced file line number - * '<afile>' to file name for autocommand - * '<abuf>' to buffer number for autocommand - * '<amatch>' to matching name for autocommand - * - * When an error is detected, "errormsg" is set to a non-NULL pointer (may be - * "" for error without a message) and NULL is returned. - * Returns an allocated string if a valid match was found. - * Returns NULL if no match was found. "usedlen" then still contains the - * number of characters to skip. - */ -char_u * -eval_vars ( - char_u *src, /* pointer into commandline */ - char_u *srcstart, /* beginning of valid memory for src */ - size_t *usedlen, /* characters after src that are used */ - linenr_T *lnump, /* line number for :e command, or NULL */ - char_u **errormsg, /* pointer to error message */ - int *escaped /* return value has escaped white space (can - * be NULL) */ -) +/// Evaluate cmdline variables. +/// +/// change '%' to curbuf->b_ffname +/// '#' to curwin->w_alt_fnum +/// '<cword>' to word under the cursor +/// '<cWORD>' to WORD under the cursor +/// '<cexpr>' to C-expression under the cursor +/// '<cfile>' to path name under the cursor +/// '<sfile>' to sourced file name +/// '<slnum>' to sourced file line number +/// '<afile>' to file name for autocommand +/// '<abuf>' to buffer number for autocommand +/// '<amatch>' to matching name for autocommand +/// +/// When an error is detected, "errormsg" is set to a non-NULL pointer (may be +/// "" for error without a message) and NULL is returned. +/// +/// @param src pointer into commandline +/// @param srcstart beginning of valid memory for src +/// @param usedlen characters after src that are used +/// @param lnump line number for :e command, or NULL +/// @param errormsg pointer to error message +/// @param escaped return value has escaped white space (can be NULL) +/// +/// @return an allocated string if a valid match was found. +/// Returns NULL if no match was found. "usedlen" then still contains the +/// number of characters to skip. +char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnump, + char_u **errormsg, int *escaped) { int i; - char_u *s; - char_u *result; - char_u *resultbuf = NULL; + char_u *s; + char_u *result; + char_u *resultbuf = NULL; size_t resultlen; - buf_T *buf; + buf_T *buf; int valid = VALID_HEAD | VALID_PATH; // Assume valid result. bool tilde_file = false; int skip_mod = false; char strbuf[30]; *errormsg = NULL; - if (escaped != NULL) + if (escaped != NULL) { *escaped = FALSE; + } /* * Check if there is something to do. */ ssize_t spec_idx = find_cmdline_var(src, usedlen); - if (spec_idx < 0) { /* no match */ + if (spec_idx < 0) { // no match *usedlen = 1; return NULL; } @@ -8799,7 +8974,7 @@ eval_vars ( */ if (src > srcstart && src[-1] == '\\') { *usedlen = 0; - STRMOVE(src - 1, src); /* remove backslash */ + STRMOVE(src - 1, src); // remove backslash return NULL; } @@ -8809,9 +8984,8 @@ eval_vars ( if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD || spec_idx == SPEC_CEXPR) { - resultlen = find_ident_under_cursor( - &result, - spec_idx == SPEC_CWORD + resultlen = find_ident_under_cursor(&result, + spec_idx == SPEC_CWORD ? (FIND_IDENT | FIND_STRING) : (spec_idx == SPEC_CEXPR ? (FIND_IDENT | FIND_STRING | FIND_EVAL) @@ -8820,13 +8994,13 @@ eval_vars ( *errormsg = (char_u *)""; return NULL; } - // - // '#': Alternate file name - // '%': Current file name - // File name under the cursor - // File name for autocommand - // and following modifiers - // + // + // '#': Alternate file name + // '%': Current file name + // File name under the cursor + // File name for autocommand + // and following modifiers + // } else { switch (spec_idx) { case SPEC_PERC: // '%': current file @@ -8839,13 +9013,14 @@ eval_vars ( } break; - case SPEC_HASH: /* '#' or "#99": alternate file */ - if (src[1] == '#') { /* "##": the argument list */ + case SPEC_HASH: // '#' or "#99": alternate file + if (src[1] == '#') { // "##": the argument list result = arg_all(); resultbuf = result; *usedlen = 2; - if (escaped != NULL) + if (escaped != NULL) { *escaped = TRUE; + } skip_mod = TRUE; break; } @@ -8862,7 +9037,7 @@ eval_vars ( if (src[1] == '<' && i != 0) { if (*usedlen < 2) { - /* Should we give an error message for #<text? */ + // Should we give an error message for #<text? *usedlen = 1; return NULL; } @@ -8878,12 +9053,12 @@ eval_vars ( } buf = buflist_findnr(i); if (buf == NULL) { - *errormsg = (char_u *)_( - "E194: No alternate file name to substitute for '#'"); + *errormsg = (char_u *)_("E194: No alternate file name to substitute for '#'"); return NULL; } - if (lnump != NULL) + if (lnump != NULL) { *lnump = ECMD_LAST; + } if (buf->b_fname == NULL) { result = (char_u *)""; valid = 0; // Must have ":p:h" to be valid @@ -8894,13 +9069,13 @@ eval_vars ( } break; - case SPEC_CFILE: /* file name under cursor */ + case SPEC_CFILE: // file name under cursor result = file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL); if (result == NULL) { *errormsg = (char_u *)""; return NULL; } - resultbuf = result; /* remember allocated string */ + resultbuf = result; // remember allocated string break; case SPEC_AFILE: // file name for autocommand @@ -8917,37 +9092,33 @@ eval_vars ( } result = autocmd_fname; if (result == NULL) { - *errormsg = (char_u *)_( - "E495: no autocommand file name to substitute for \"<afile>\""); + *errormsg = (char_u *)_("E495: no autocommand file name to substitute for \"<afile>\""); return NULL; } result = path_try_shorten_fname(result); break; - case SPEC_ABUF: /* buffer number for autocommand */ + case SPEC_ABUF: // buffer number for autocommand if (autocmd_bufnr <= 0) { - *errormsg = (char_u *)_( - "E496: no autocommand buffer number to substitute for \"<abuf>\""); + *errormsg = (char_u *)_("E496: no autocommand buffer number to substitute for \"<abuf>\""); return NULL; } snprintf(strbuf, sizeof(strbuf), "%d", autocmd_bufnr); result = (char_u *)strbuf; break; - case SPEC_AMATCH: /* match name for autocommand */ + case SPEC_AMATCH: // match name for autocommand result = autocmd_match; if (result == NULL) { - *errormsg = (char_u *)_( - "E497: no autocommand match name to substitute for \"<amatch>\""); + *errormsg = (char_u *)_("E497: no autocommand match name to substitute for \"<amatch>\""); return NULL; } break; - case SPEC_SFILE: /* file name for ":so" command */ + case SPEC_SFILE: // file name for ":so" command result = sourcing_name; if (result == NULL) { - *errormsg = (char_u *)_( - "E498: no :source file name to substitute for \"<sfile>\""); + *errormsg = (char_u *)_("E498: no :source file name to substitute for \"<sfile>\""); return NULL; } break; @@ -9007,15 +9178,16 @@ eval_vars ( } if (resultlen == 0 || valid != VALID_HEAD + VALID_PATH) { - if (valid != VALID_HEAD + VALID_PATH) - /* xgettext:no-c-format */ - *errormsg = (char_u *)_( - "E499: Empty file name for '%' or '#', only works with \":p:h\""); - else + if (valid != VALID_HEAD + VALID_PATH) { + // xgettext:no-c-format + *errormsg = (char_u *)_("E499: Empty file name for '%' or '#', only works with \":p:h\""); + } else { *errormsg = (char_u *)_("E500: Evaluates to an empty string"); + } result = NULL; - } else + } else { result = vim_strnsave(result, resultlen); + } xfree(resultbuf); return result; } @@ -9029,8 +9201,8 @@ static char_u *arg_all(void) { int len; int idx; - char_u *retval = NULL; - char_u *p; + char_u *retval = NULL; + char_u *p; /* * Do this loop two times: @@ -9045,9 +9217,10 @@ static char_u *arg_all(void) continue; } if (len > 0) { - /* insert a space in between names */ - if (retval != NULL) + // insert a space in between names + if (retval != NULL) { retval[len] = ' '; + } ++len; } for (; *p != NUL; p++) { @@ -9069,13 +9242,13 @@ static char_u *arg_all(void) } } - /* second time: break here */ + // second time: break here if (retval != NULL) { retval[len] = NUL; break; } - /* allocate memory */ + // allocate memory retval = xmalloc(len + 1); } @@ -9089,29 +9262,30 @@ static char_u *arg_all(void) */ char_u *expand_sfile(char_u *arg) { - char_u *errormsg; + char_u *errormsg; size_t len; - char_u *result; - char_u *newres; - char_u *repl; + char_u *result; + char_u *newres; + char_u *repl; size_t srclen; - char_u *p; + char_u *p; result = vim_strsave(arg); for (p = result; *p; ) { - if (STRNCMP(p, "<sfile>", 7) != 0) + if (STRNCMP(p, "<sfile>", 7) != 0) { ++p; - else { - /* replace "<sfile>" with the sourced file name, and do ":" stuff */ + } else { + // replace "<sfile>" with the sourced file name, and do ":" stuff repl = eval_vars(p, result, &srclen, NULL, &errormsg, NULL); if (errormsg != NULL) { - if (*errormsg) + if (*errormsg) { emsg(errormsg); + } xfree(result); return NULL; } - if (repl == NULL) { /* no match (cannot happen) */ + if (repl == NULL) { // no match (cannot happen) p += srclen; continue; } @@ -9124,7 +9298,7 @@ char_u *expand_sfile(char_u *arg) xfree(repl); xfree(result); result = newres; - p = newres + len; /* continue after the match */ + p = newres + len; // continue after the match } } @@ -9136,15 +9310,16 @@ char_u *expand_sfile(char_u *arg) */ static void ex_shada(exarg_T *eap) { - char_u *save_shada; + char_u *save_shada; save_shada = p_shada; - if (*p_shada == NUL) + if (*p_shada == NUL) { p_shada = (char_u *)"'100"; + } if (eap->cmdidx == CMD_rviminfo || eap->cmdidx == CMD_rshada) { - (void) shada_read_everything((char *) eap->arg, eap->forceit, false); + (void)shada_read_everything((char *)eap->arg, eap->forceit, false); } else { - shada_write_file((char *) eap->arg, eap->forceit); + shada_write_file((char *)eap->arg, eap->forceit); } p_shada = save_shada; } @@ -9155,8 +9330,9 @@ static void ex_shada(exarg_T *eap) */ void dialog_msg(char_u *buff, char *format, char_u *fname) { - if (fname == NULL) + if (fname == NULL) { fname = (char_u *)_("Untitled"); + } vim_snprintf((char *)buff, DIALOG_MSG_SIZE, format, fname); } @@ -9186,10 +9362,12 @@ static void ex_behave(exarg_T *eap) */ char_u *get_behave_arg(expand_T *xp, int idx) { - if (idx == 0) + if (idx == 0) { return (char_u *)"mswin"; - if (idx == 1) + } + if (idx == 1) { return (char_u *)"xterm"; + } return NULL; } @@ -9226,12 +9404,12 @@ static TriState filetype_indent = kNone; */ static void ex_filetype(exarg_T *eap) { - char_u *arg = eap->arg; + char_u *arg = eap->arg; bool plugin = false; bool indent = false; if (*eap->arg == NUL) { - /* Print current status. */ + // Print current status. smsg("filetype detection:%s plugin:%s indent:%s", filetype_detect == kTrue ? "ON" : "OFF", filetype_plugin == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF", // NOLINT(whitespace/line_length) @@ -9239,7 +9417,7 @@ static void ex_filetype(exarg_T *eap) return; } - /* Accept "plugin" and "indent" in any order. */ + // Accept "plugin" and "indent" in any order. for (;; ) { if (STRNCMP(arg, "plugin", 6) == 0) { plugin = true; @@ -9284,8 +9462,9 @@ static void ex_filetype(exarg_T *eap) source_runtime((char_u *)FTOFF_FILE, DIP_ALL); filetype_detect = kFalse; } - } else + } else { EMSG2(_(e_invarg2), arg); + } } /// Set all :filetype options ON if user did not explicitly set any to OFF. @@ -9335,10 +9514,11 @@ static void ex_set(exarg_T *eap) { int flags = 0; - if (eap->cmdidx == CMD_setlocal) + if (eap->cmdidx == CMD_setlocal) { flags = OPT_LOCAL; - else if (eap->cmdidx == CMD_setglobal) + } else if (eap->cmdidx == CMD_setglobal) { flags = OPT_GLOBAL; + } (void)do_set(eap->arg, flags); } @@ -9538,16 +9718,16 @@ bool cmd_can_preview(char_u *cmd) char_u *end = find_command(&ea, NULL); switch (ea.cmdidx) { - case CMD_substitute: - case CMD_smagic: - case CMD_snomagic: - // Only preview once the pattern delimiter has been typed - if (*end && !ASCII_ISALNUM(*end)) { - return true; - } - break; - default: - break; + case CMD_substitute: + case CMD_smagic: + case CMD_snomagic: + // Only preview once the pattern delimiter has been typed + if (*end && !ASCII_ISALNUM(*end)) { + return true; + } + break; + default: + break; } return false; @@ -9578,11 +9758,16 @@ Dictionary commands_array(buf_T *buf) PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR))); switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { - case 0: arg[0] = '0'; break; - case(EX_EXTRA): arg[0] = '*'; break; - case(EX_EXTRA | EX_NOSPC): arg[0] = '?'; break; - case(EX_EXTRA | EX_NEEDARG): arg[0] = '+'; break; - case(EX_EXTRA | EX_NOSPC | EX_NEEDARG): arg[0] = '1'; break; + case 0: + arg[0] = '0'; break; + case (EX_EXTRA): + arg[0] = '*'; break; + case (EX_EXTRA | EX_NOSPC): + arg[0] = '?'; break; + case (EX_EXTRA | EX_NEEDARG): + arg[0] = '+'; break; + case (EX_EXTRA | EX_NOSPC | EX_NEEDARG): + arg[0] = '1'; break; } PUT(d, "nargs", STRING_OBJ(cstr_to_string(arg))); @@ -9630,3 +9815,293 @@ Dictionary commands_array(buf_T *buf) } return rv; } + +void verify_command(char_u *cmd) +{ + if (strcmp("smile", (char *)cmd)) { + return; // acceptable non-existing command + } + MSG(" #xxn` #xnxx` ,+x@##@Mz;` .xxx" + "xxxxxxnz+, znnnnnnnnnnnnnnnn."); + MSG(" n###z x####` :x##########W+` ,###" + "##########M; W################."); + MSG(" n####; x####` `z##############W: ,###" + "############# W################."); + MSG(" n####W. x####` ,W#################+ ,###" + "############## W################."); + MSG(" n#####n x####` @################### ,###" + "##############i W################."); + MSG(" n######i x####` .#########@W@########* ,###" + "##############W`W################."); + MSG(" n######@. x####` x######W*. `;n#######: ,###" + "#x,,,,:*M######iW###@:,,,,,,,,,,,`"); + MSG(" n#######n x####` *######+` :M#####M ,###" + "#n `x#####xW###@`"); + MSG(" n########* x####``@####@; `x#####i ,###" + "#n ,#####@W###@`"); + MSG(" n########@ x####`*#####i `M####M ,###" + "#n x#########@`"); + MSG(" n######### x####`M####z :#####:,###" + "#n z#########@`"); + MSG(" n#########* x####,#####. n####+,###" + "#n n#########@`"); + MSG(" n####@####@, x####i####x ;####x,###" + "#n `W#####@####+++++++++++i"); + MSG(" n####*#####M` x#########* `####@,###" + "#n i#####MW###############W"); + MSG(" n####.######+ x####z####; W####,###" + "#n i@######W###############W"); + MSG(" n####.`W#####: x####n####: M####:###" + "#@nnnnnW#######,W###############W"); + MSG(" n####. :#####M`x####z####; W####,###" + "##############z W###############W"); + MSG(" n####. #######x#########* `####W,###" + "#############W` W###############W"); + MSG(" n####. `M#####W####i####x ;####x,###" + "############W, W####+**********i"); + MSG(" n####. ,##########,#####. n####+,###" + "###########n. W###@`"); + MSG(" n####. ##########`M####z :#####:,###" + "########Wz: W###@`"); + MSG(" n####. x#########`*#####i `M####M ,###" + "#x.....` W###@`"); + MSG(" n####. ,@########``@####@; `x#####i ,###" + "#n W###@`"); + MSG(" n####. *########` *#####@+` ,M#####M ,###" + "#n W###@`"); + MSG(" n####. x#######` x######W*. `;n######@: ,###" + "#n W###@,,,,,,,,,,,,`"); + MSG(" n####. .@######` .#########@W@########* ,###" + "#n W################,"); + MSG(" n####. i######` @################### ,###" + "#n W################,"); + MSG(" n####. n#####` ,W#################+ ,###" + "#n W################,"); + MSG(" n####. .@####` .n##############W; ,###" + "#n W################,"); + MSG(" n####. i####` :x##########W+` ,###" + "#n W################,"); + MSG(" +nnnn` +nnn` ,+x@##@Mz;` .nnn" + "n+ zxxxxxxxxxxxxxxxx."); + MSG(" "); + MSG(" " + " ,+M@#Mi"); + MSG(" " + " .z########"); + MSG(" " + " i@#########i"); + MSG(" " + " `############W`"); + MSG(" " + " `n#############i"); + MSG(" " + " `n##############n"); + MSG(" `` " + " z###############@`"); + MSG(" `W@z, " + " ##################,"); + MSG(" *#####` " + " i############@x@###i"); + MSG(" ######M. " + " :#############n`,W##+"); + MSG(" +######@: " + " .W#########M@##+ *##z"); + MSG(" :#######@: " + " `x########@#x###* ,##n"); + MSG(" `@#######@; " + " z#########M*@nW#i .##x"); + MSG(" z########@i " + " *###########WM#@#, `##x"); + MSG(" i##########+ " + " ;###########*n###@ `##x"); + MSG(" `@#MM#######x, " + " ,@#########zM,`z##M `@#x"); + MSG(" n##M#W#######n. " + " `.:i*+#zzzz##+i:.` ,W#########Wii,`n@#@` n@##n"); + MSG(" ;###@#x#######n `,i" + "#nW@#####@@WWW@@####@Mzi. ,W##########@z.. ;zM#+i####z"); + MSG(" x####nz######## .;#x@##" + "@Wn#*;,.` ``,:*#x@##M+, ;@########xz@WM+#` `n@#######"); + MSG(" ,@####M########xi#@##@Mzi," + "` .+x###Mi:n##########Mz```.:i *@######*"); + MSG(" *#####W#########ix+:` " + " :n#############z: `*.`M######i"); + MSG(" i#W##nW@+@##@#M@; " + " ;W@@##########W, i`x@#####,"); + MSG(" `@@n@Wn#@iMW*#*: " + " `iz#z@######x. M######`"); + MSG(" z##zM###x`*, .` " + " `iW#####W;:` +#####M"); + MSG(" ,###nn##n` " + " ,#####x;` ,;@######"); + MSG(" x###xz#. " + " in###+ `:######@."); + MSG(" ;####n+ " + " `Mnx##xi` , zM#######"); + MSG(" `W####+ " + "i. `.+x###@#. :n,z######:"); + MSG(" z####@` ;" + "#: .ii@###@;.*M*z####@`"); + MSG(" i####M ` `i@" + "#, :: +#n##@+@##W####n"); + MSG(" :####x ,i. ##xzM###" + "@` i. .@@, .z####x#######*"); + MSG(" ,###W; i##Wz########" + "# :## z##n ,@########x###:"); + MSG(" n##n `W###########M" + "`;n, i#x ,###@i *W########W#@`"); + MSG(" .@##+ `x###########@." + " z#+ .M#W``x#####n` `;#######@z#x"); + MSG(" n###z :W############@ " + " z#* @##xM#######@n; `########nW+"); + MSG(" ;####nW##############W " + ":@#* `@#############* :########z@i`"); + MSG(" M##################### " + "M##: @#############@: *W########M#"); + MSG(" ;#####################i." + "##x` W#############W, :n########zx"); + MSG(" x####################@.`" + "x; @#############z. .@########W#"); + MSG(" ,######################` " + " W###############x*,` W######zM#i"); + MSG(" #######################: " + " z##################@x+*#zzi `@#########."); + MSG(" W########W#z#M#########; " + " *##########################z :@#######@`"); + MSG(" `@#######x`;#z ,x#######; " + " z###########M###xnM@########* :M######@"); + MSG(" i########, x#@` z######; " + " *##########i *#@` `+########+` n######."); + MSG(" n#######@` M##, `W#####. " + " *#########z ###; z########M: :W####n"); + MSG(" M#######M n##. x####x " + " `x########: z##+ M#########@; .n###+"); + MSG(" W#######@` :#W `@####: " + " `@######W i### ;###########@. n##n"); + MSG(" W########z` ,, .x####z " + " @######@` `W#; `W############* *###;"); + MSG(" `@#########Mi,:*n@####W` " + " W#######* .. `n#############i i###x"); + MSG(" .#####################z " + " `@#######@*` .x############n:` ;####."); + MSG(" :####################x`,,` " + " `W#########@x#+#@#############i ,####:"); + MSG(" ;###################x#@###x" + "i` *############################: `####i"); + MSG(" i##################+#######" + "#M, x##########################@` W###i"); + MSG(" *################@; @######" + "##@, .W#########################@ x###:"); + MSG(" .+M#############z. M######" + "###x ,W########################@` ####."); + MSG(" *M*;z@########x: :W#####" + "##i .M########################i i###:"); + MSG(" *##@z;#@####x: :z###" + "@i `########################x .###;"); + MSG(" *#####n;#@## ;##" + "* ,x#####################@` W##*"); + MSG(" *#######n;* :M##" + "W*, *W####################` n##z"); + MSG(" i########@. ,*n####" + "###M*` `###################M *##M"); + MSG(" i########n `z#####@@" + "#####Wi ,M################; ,##@`"); + MSG(" ;WMWW@###* .x##@ni.``" + ".:+zW##z` `n##############z @##,"); + MSG(" .*++*i;;;. .M#@+` " + " .##n `x############x` n##i"); + MSG(" :########* x#W, " + " *#+ *###########M` +##+"); + MSG(" ,######### :#@: " + " ##: #nzzzzzzzzzz. :##x"); + MSG(" .#####Wz+` ##+ " + " `MM` .znnnnnnnnn. `@#@`"); + MSG(" `@@ni;*nMz` @W` " + " :#+ .x#######n x##,"); + MSG(" i;z@#####, .#* " + " z#: ;;;*zW##; ###i"); + MSG(" z########: :#; " + " `Wx +###Wni;n. ;##z"); + MSG(" n########W: .#* " + " ,#, ;#######@+ `@#M"); + MSG(" .###########n;.MM " + " n* ;iM#######* x#@`"); + MSG(" :#############@;; " + " .n` ,#W*iW#####W` +##,"); + MSG(" ,##############. " + " ix. `x###M;####### ,##i"); + MSG(" .#############@` " + " x@n**#W######z;M###@. W##"); + MSG(" .##############W: " + " .x############@*;zW#; z#x"); + MSG(" ,###############@; " + " `##############@n*;. i#@"); + MSG(" ,#################i " + " :n##############W` .##,"); + MSG(" ,###################` " + " .+W##########W, `##i"); + MSG(" :###################@zi,` " + " ;zM@@@WMn*` @#z"); + MSG(" :#######################@x+" + "*i;;:i#M, `` M#W"); + MSG(" ;##########################" + "######@x. n##,"); + MSG(" i#####################@W@@@" + "@Wxz*:` *##+"); + MSG(" *######################+```" + " :##M"); + MSG(" ########################M; " + " `@##,"); + MSG(" z#########################x" + ", z###"); + MSG(" n##########################" + "#n: ;##W`"); + MSG(" x##########################" + "###Mz#++##* `W##i"); + MSG(" M##########################" + "##########@` ###x"); + MSG(" W##########################" + "###########` .###,"); + MSG(" @##########################" + "##########M n##z"); + MSG(" @##################z*i@WMMM" + "x#x@#####,. :##@."); + MSG(" `#####################@xi` " + " `::,* x##+"); + MSG(" .#####################@#M. " + " ;##@`"); + MSG(" ,#####################:. " + " M##i"); + MSG(" ;###################ni` " + " i##M"); + MSG(" *#################W#` " + " `W##,"); + MSG(" z#################@Wx+. " + " +###"); + MSG(" x######################z. " + " .@#@`"); + MSG(" `@#######################@; " + " z##;"); + MSG(" :##########################: " + " :##z"); + MSG(" +#########################W# " + " M#W"); + MSG(" W################@n+*i;:,` " + " +##,"); + MSG(" :##################WMxz+, " + " ,##i"); + MSG(" n#######################W.., " + " W##"); + MSG(" +#########################WW@+. .:. " + " z#x"); + MSG(" `@#############################@@###: " + " *#W"); + MSG(" #################################Wz: " + " :#@"); + MSG(",@###############################i " + " .##"); + MSG("n@@@@@@@#########################+ " + " `##"); + MSG("` `.:.`.,:iii;;;;;;;;iii;;;:` `.`` " + " `nW"); +} diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index f6bd2adcd5..292e01dd6b 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -16,16 +16,12 @@ #define VALID_PATH 1 #define VALID_HEAD 2 -/* Values for exmode_active (0 is no exmode) */ -#define EXMODE_NORMAL 1 -#define EXMODE_VIM 2 - // Structure used to save the current state. Used when executing Normal mode // commands while in any other mode. typedef struct { int save_msg_scroll; int save_restart_edit; - int save_msg_didout; + bool save_msg_didout; int save_State; int save_insertmode; bool save_finish_op; diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 5ca88002f1..1ceccac2bb 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -210,7 +210,7 @@ bool cause_errthrow(const char_u *mesg, bool severe, bool *ignore) * not skipped. Errors in those commands may affect what of the subsequent * commands are regarded part of catch and finally clauses. Catching the * exception would then cause execution of commands not intended by the - * user, who wouldn't even get aware of the problem. Therefor, discard the + * user, who wouldn't even get aware of the problem. Therefore, discard the * exception currently being thrown to prevent it from being caught. Just * execute finally clauses and terminate. */ @@ -832,24 +832,23 @@ void ex_if(exarg_T *eap) */ void ex_endif(exarg_T *eap) { - did_endif = TRUE; + did_endif = true; if (eap->cstack->cs_idx < 0 || (eap->cstack->cs_flags[eap->cstack->cs_idx] - & (CSF_WHILE | CSF_FOR | CSF_TRY))) + & (CSF_WHILE | CSF_FOR | CSF_TRY))) { eap->errmsg = (char_u *)N_("E580: :endif without :if"); - else { - /* - * When debugging or a breakpoint was encountered, display the debug - * prompt (if not already done). This shows the user that an ":endif" - * is executed when the ":if" or a previous ":elseif" was not TRUE. - * Handle a ">quit" debug command as if an interrupt had occurred before - * the ":endif". That is, throw an interrupt exception if appropriate. - * Doing this here prevents an exception for a parsing error being - * discarded by throwing the interrupt exception later on. - */ + } else { + // When debugging or a breakpoint was encountered, display the debug + // prompt (if not already done). This shows the user that an ":endif" + // is executed when the ":if" or a previous ":elseif" was not TRUE. + // Handle a ">quit" debug command as if an interrupt had occurred before + // the ":endif". That is, throw an interrupt exception if appropriate. + // Doing this here prevents an exception for a parsing error being + // discarded by throwing the interrupt exception later on. if (!(eap->cstack->cs_flags[eap->cstack->cs_idx] & CSF_TRUE) - && dbg_check_skipped(eap)) + && dbg_check_skipped(eap)) { (void)do_intthrow(eap->cstack); + } --eap->cstack->cs_idx; } @@ -1022,7 +1021,7 @@ void ex_continue(exarg_T *eap) else { /* Try to find the matching ":while". This might stop at a try * conditional not in its finally clause (which is then to be executed - * next). Therefor, inactivate all conditionals except the ":while" + * next). Therefore, deactivate all conditionals except the ":while" * itself (if reached). */ idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, FALSE); assert(idx >= 0); @@ -1051,14 +1050,14 @@ void ex_break(exarg_T *eap) int idx; cstack_T *const cstack = eap->cstack; - if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) + if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) { eap->errmsg = (char_u *)N_("E587: :break without :while or :for"); - else { - /* Inactivate conditionals until the matching ":while" or a try - * conditional not in its finally clause (which is then to be - * executed next) is found. In the latter case, make the ":break" - * pending for execution at the ":endtry". */ - idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, TRUE); + } else { + // Deactivate conditionals until the matching ":while" or a try + // conditional not in its finally clause (which is then to be + // executed next) is found. In the latter case, make the ":break" + // pending for execution at the ":endtry". */ + idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, true); if (idx >= 0 && !(cstack->cs_flags[idx] & (CSF_WHILE | CSF_FOR))) { cstack->cs_pending[idx] = CSTP_BREAK; report_make_pending(CSTP_BREAK, NULL); @@ -1179,15 +1178,15 @@ void do_throw(cstack_T *cstack) int idx; int inactivate_try = FALSE; - /* - * Cleanup and inactivate up to the next surrounding try conditional that - * is not in its finally clause. Normally, do not inactivate the try - * conditional itself, so that its ACTIVE flag can be tested below. But - * if a previous error or interrupt has not been converted to an exception, - * inactivate the try conditional, too, as if the conversion had been done, - * and reset the did_emsg or got_int flag, so this won't happen again at - * the next surrounding try conditional. - */ + // + // Cleanup and deactivate up to the next surrounding try conditional that + // is not in its finally clause. Normally, do not deactivate the try + // conditional itself, so that its ACTIVE flag can be tested below. But + // if a previous error or interrupt has not been converted to an exception, + // deactivate the try conditional, too, as if the conversion had been done, + // and reset the did_emsg or got_int flag, so this won't happen again at + // the next surrounding try conditional. + // #ifndef THROW_ON_ERROR_TRUE if (did_emsg && !THROW_ON_ERROR) { inactivate_try = TRUE; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 75ed5dc0e5..b90773ce83 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2267,7 +2267,7 @@ static int command_line_changed(CommandLineState *s) close_preview_windows(); update_screen(SOME_VALID); // Clear 'inccommand' preview. } else { - if (s->xpc.xp_context == EXPAND_NOTHING) { + if (s->xpc.xp_context == EXPAND_NOTHING && (KeyTyped || vpeekc() == NUL)) { may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state); } } @@ -2412,13 +2412,11 @@ char_u * get_text_locked_msg(void) { } } -/* - * Check if "curbuf_lock" or "allbuf_lock" is set and return TRUE when it is - * and give an error message. - */ +/// Check if "curbuf->b_ro_locked" or "allbuf_lock" is set and +/// return TRUE when it is and give an error message. int curbuf_locked(void) { - if (curbuf_lock > 0) { + if (curbuf->b_ro_locked > 0) { EMSG(_("E788: Not allowed to edit another buffer now")); return TRUE; } @@ -2512,266 +2510,6 @@ getexline( return getcmdline(c, 1L, indent, do_concat); } -/* - * Get an Ex command line for Ex mode. - * In Ex mode we only use the OS supplied line editing features and no - * mappings or abbreviations. - * Returns a string in allocated memory or NULL. - */ -char_u * -getexmodeline( - int promptc, // normally ':', NUL for ":append" and '?' - // for :s prompt - void *cookie, - int indent, // indent for inside conditionals - bool do_concat -) -{ - garray_T line_ga; - char_u *pend; - int startcol = 0; - int c1 = 0; - int escaped = FALSE; /* CTRL-V typed */ - int vcol = 0; - char_u *p; - int prev_char; - int len; - - /* always start in column 0; write a newline if necessary */ - compute_cmdrow(); - if ((msg_col || msg_didout) && promptc != '?') - msg_putchar('\n'); - if (promptc == ':') { - /* indent that is only displayed, not in the line itself */ - if (p_prompt) - msg_putchar(':'); - while (indent-- > 0) - msg_putchar(' '); - startcol = msg_col; - } - - ga_init(&line_ga, 1, 30); - - /* autoindent for :insert and :append is in the line itself */ - if (promptc <= 0) { - vcol = indent; - while (indent >= 8) { - ga_append(&line_ga, TAB); - msg_puts(" "); - indent -= 8; - } - while (indent-- > 0) { - ga_append(&line_ga, ' '); - msg_putchar(' '); - } - } - no_mapping++; - - /* - * Get the line, one character at a time. - */ - got_int = FALSE; - while (!got_int) { - ga_grow(&line_ga, 40); - - /* Get one character at a time. Don't use inchar(), it can't handle - * special characters. */ - prev_char = c1; - - // Check for a ":normal" command and no more characters left. - if (ex_normal_busy > 0 && typebuf.tb_len == 0) { - c1 = '\n'; - } else { - c1 = vgetc(); - } - - /* - * Handle line editing. - * Previously this was left to the system, putting the terminal in - * cooked mode, but then CTRL-D and CTRL-T can't be used properly. - */ - if (got_int) { - msg_putchar('\n'); - break; - } - - if (!escaped) { - /* CR typed means "enter", which is NL */ - if (c1 == '\r') - c1 = '\n'; - - if (c1 == BS || c1 == K_BS || c1 == DEL || c1 == K_DEL || c1 == K_KDEL) { - if (!GA_EMPTY(&line_ga)) { - p = (char_u *)line_ga.ga_data; - p[line_ga.ga_len] = NUL; - len = utf_head_off(p, p + line_ga.ga_len - 1) + 1; - line_ga.ga_len -= len; - goto redraw; - } - continue; - } - - if (c1 == Ctrl_U) { - msg_col = startcol; - msg_clr_eos(); - line_ga.ga_len = 0; - goto redraw; - } - - int num_spaces; - if (c1 == Ctrl_T) { - int sw = get_sw_value(curbuf); - - p = (char_u *)line_ga.ga_data; - p[line_ga.ga_len] = NUL; - indent = get_indent_str(p, 8, FALSE); - num_spaces = sw - indent % sw; -add_indent: - if (num_spaces > 0) { - ga_grow(&line_ga, num_spaces + 1); - p = (char_u *)line_ga.ga_data; - char_u *s = skipwhite(p); - - // Insert spaces after leading whitespaces. - long move_len = line_ga.ga_len - (s - p) + 1; - assert(move_len >= 0); - memmove(s + num_spaces, s, (size_t)move_len); - memset(s, ' ', (size_t)num_spaces); - - line_ga.ga_len += num_spaces; - } -redraw: - /* redraw the line */ - msg_col = startcol; - vcol = 0; - p = (char_u *)line_ga.ga_data; - p[line_ga.ga_len] = NUL; - while (p < (char_u *)line_ga.ga_data + line_ga.ga_len) { - if (*p == TAB) { - do { - msg_putchar(' '); - } while (++vcol % 8); - p++; - } else { - len = utfc_ptr2len(p); - msg_outtrans_len(p, len); - vcol += ptr2cells(p); - p += len; - } - } - msg_clr_eos(); - cmd_cursor_goto(msg_row, msg_col); - continue; - } - - if (c1 == Ctrl_D) { - /* Delete one shiftwidth. */ - p = (char_u *)line_ga.ga_data; - if (prev_char == '0' || prev_char == '^') { - if (prev_char == '^') - ex_keep_indent = TRUE; - indent = 0; - p[--line_ga.ga_len] = NUL; - } else { - p[line_ga.ga_len] = NUL; - indent = get_indent_str(p, 8, FALSE); - if (indent == 0) { - continue; - } - --indent; - indent -= indent % get_sw_value(curbuf); - } - - // reduce the line's indentation - char_u *from = skipwhite(p); - char_u *to = from; - int old_indent; - while ((old_indent = get_indent_str(p, 8, FALSE)) > indent) { - *--to = NUL; - } - long move_len = line_ga.ga_len - (from - p) + 1; - assert(move_len > 0); - memmove(to, from, (size_t)move_len); - line_ga.ga_len -= (int)(from - to); - - // Removed to much indentation, fix it before redrawing. - num_spaces = indent - old_indent; - goto add_indent; - } - - if (c1 == Ctrl_V || c1 == Ctrl_Q) { - escaped = TRUE; - continue; - } - - if (IS_SPECIAL(c1)) { - // Ignore other special key codes - continue; - } - } - - if (IS_SPECIAL(c1)) { - c1 = '?'; - } - len = utf_char2bytes(c1, (char_u *)line_ga.ga_data + line_ga.ga_len); - if (c1 == '\n') { - msg_putchar('\n'); - } else if (c1 == TAB) { - // Don't use chartabsize(), 'ts' can be different. - do { - msg_putchar(' '); - } while (++vcol % 8); - } else { - msg_outtrans_len(((char_u *)line_ga.ga_data) + line_ga.ga_len, len); - vcol += char2cells(c1); - } - line_ga.ga_len += len; - escaped = FALSE; - - cmd_cursor_goto(msg_row, msg_col); - pend = (char_u *)(line_ga.ga_data) + line_ga.ga_len; - - /* We are done when a NL is entered, but not when it comes after an - * odd number of backslashes, that results in a NUL. */ - if (!GA_EMPTY(&line_ga) && pend[-1] == '\n') { - int bcount = 0; - - while (line_ga.ga_len - 2 >= bcount && pend[-2 - bcount] == '\\') - ++bcount; - - if (bcount > 0) { - /* Halve the number of backslashes: "\NL" -> "NUL", "\\NL" -> - * "\NL", etc. */ - line_ga.ga_len -= (bcount + 1) / 2; - pend -= (bcount + 1) / 2; - pend[-1] = '\n'; - } - - if ((bcount & 1) == 0) { - --line_ga.ga_len; - --pend; - *pend = NUL; - break; - } - } - } - - no_mapping--; - - /* make following messages go to the next line */ - msg_didout = FALSE; - msg_col = 0; - if (msg_row < Rows - 1) { - msg_row++; - } - emsg_on_display = false; // don't want os_delay() - - if (got_int) - ga_clear(&line_ga); - - return (char_u *)line_ga.ga_data; -} - bool cmdline_overstrike(void) { return ccline.overstrike; @@ -2855,7 +2593,7 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline, ColoredCmdline *const ret_ccline_colors) FUNC_ATTR_NONNULL_ALL { - ParserLine plines[] = { + ParserLine parser_lines[] = { { .data = (const char *)colored_ccline->cmdbuff, .size = STRLEN(colored_ccline->cmdbuff), @@ -2863,7 +2601,7 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline, }, { NULL, 0, false }, }; - ParserLine *plines_p = plines; + ParserLine *plines_p = parser_lines; ParserHighlight colors; kvi_init(colors); ParserState pstate; @@ -3717,7 +3455,7 @@ void redrawcmdline(void) { if (cmd_silent) return; - need_wait_return = FALSE; + need_wait_return = false; compute_cmdrow(); redrawcmd(); cursorcmd(); @@ -4487,13 +4225,13 @@ static int showmatches(expand_T *xp, int wildmenu) } if (!wildmenu) { - msg_didany = FALSE; /* lines_left will be set */ - msg_start(); /* prepare for paging */ + msg_didany = false; // lines_left will be set + msg_start(); // prepare for paging msg_putchar('\n'); ui_flush(); cmdline_row = msg_row; - msg_didany = FALSE; /* lines_left will be set again */ - msg_start(); /* prepare for paging */ + msg_didany = false; // lines_left will be set again + msg_start(); // prepare for paging } if (got_int) { @@ -5115,11 +4853,12 @@ ExpandFromContext ( } if (xp->xp_context == EXPAND_COLORS) { char *directories[] = { "colors", NULL }; - return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file, directories); + return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, + directories); } if (xp->xp_context == EXPAND_COMPILER) { char *directories[] = { "compiler", NULL }; - return ExpandRTDir(pat, 0, num_file, file, directories); + return ExpandRTDir(pat, DIP_LUA, num_file, file, directories); } if (xp->xp_context == EXPAND_OWNSYNTAX) { char *directories[] = { "syntax", NULL }; @@ -5127,7 +4866,7 @@ ExpandFromContext ( } if (xp->xp_context == EXPAND_FILETYPE) { char *directories[] = { "syntax", "indent", "ftplugin", NULL }; - return ExpandRTDir(pat, 0, num_file, file, directories); + return ExpandRTDir(pat, DIP_LUA, num_file, file, directories); } if (xp->xp_context == EXPAND_CHECKHEALTH) { char *directories[] = { "autoload/health", NULL }; @@ -5567,6 +5306,7 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) /// 'packpath'/pack/ * /start/ * /{dirnames}/{pat}.vim /// When "flags" has DIP_OPT: search also from 'opt' of 'packpath': /// 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim +/// When "flags" has DIP_LUA: search also performed for .lua files /// "dirnames" is an array with one or more directory names. static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char *dirnames[]) @@ -5584,6 +5324,10 @@ static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char_u *s = xmalloc(size); snprintf((char *)s, size, "%s/%s*.vim", dirnames[i], pat); globpath(p_rtp, s, &ga, 0); + if (flags & DIP_LUA) { + snprintf((char *)s, size, "%s/%s*.lua", dirnames[i], pat); + globpath(p_rtp, s, &ga, 0); + } xfree(s); } @@ -5593,6 +5337,10 @@ static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char_u *s = xmalloc(size); snprintf((char *)s, size, "pack/*/start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT globpath(p_pp, s, &ga, 0); + if (flags & DIP_LUA) { + snprintf((char *)s, size, "pack/*/start/*/%s/%s*.lua", dirnames[i], pat); // NOLINT + globpath(p_pp, s, &ga, 0); + } xfree(s); } @@ -5601,6 +5349,10 @@ static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char_u *s = xmalloc(size); snprintf((char *)s, size, "start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT globpath(p_pp, s, &ga, 0); + if (flags & DIP_LUA) { + snprintf((char *)s, size, "start/*/%s/%s*.lua", dirnames[i], pat); // NOLINT + globpath(p_pp, s, &ga, 0); + } xfree(s); } } @@ -5611,6 +5363,10 @@ static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char_u *s = xmalloc(size); snprintf((char *)s, size, "pack/*/opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT globpath(p_pp, s, &ga, 0); + if (flags & DIP_LUA) { + snprintf((char *)s, size, "pack/*/opt/*/%s/%s*.lua", dirnames[i], pat); // NOLINT + globpath(p_pp, s, &ga, 0); + } xfree(s); } @@ -5619,6 +5375,10 @@ static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char_u *s = xmalloc(size); snprintf((char *)s, size, "opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT globpath(p_pp, s, &ga, 0); + if (flags & DIP_LUA) { + snprintf((char *)s, size, "opt/*/%s/%s*.lua", dirnames[i], pat); // NOLINT + globpath(p_pp, s, &ga, 0); + } xfree(s); } } @@ -5627,7 +5387,9 @@ static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char_u *match = ((char_u **)ga.ga_data)[i]; char_u *s = match; char_u *e = s + STRLEN(s); - if (e - s > 4 && STRNICMP(e - 4, ".vim", 4) == 0) { + if (e - s > 4 && (STRNICMP(e - 4, ".vim", 4) == 0 + || ((flags & DIP_LUA) + && STRNICMP(e - 4, ".lua", 4) == 0))) { e -= 4; for (s = e; s > match; MB_PTR_BACK(match, s)) { if (vim_ispathsep(*s)) { @@ -5972,18 +5734,13 @@ HistoryType get_histtype(const char *const name, const size_t len, static int last_maptick = -1; /* last seen maptick */ -/* - * Add the given string to the given history. If the string is already in the - * history then it is moved to the front. "histype" may be one of he HIST_ - * values. - */ -void -add_to_history ( - int histype, - char_u *new_entry, - int in_map, /* consider maptick when inside a mapping */ - int sep /* separator character used (search hist) */ -) +/// Add the given string to the given history. If the string is already in the +/// history then it is moved to the front. "histype" may be one of he HIST_ +/// values. +/// +/// @parma in_map consider maptick when inside a mapping +/// @param sep separator character used (search hist) +void add_to_history(int histype, char_u *new_entry, int in_map, int sep) { histentry_T *hisptr; @@ -6299,7 +6056,7 @@ int get_list_range(char_u **str, int *num1, int *num2) *str = skipwhite(*str); if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range - vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false); *str += len; *num1 = (int)num; first = true; @@ -6307,7 +6064,7 @@ int get_list_range(char_u **str, int *num1, int *num2) *str = skipwhite(*str); if (**str == ',') { // parse "to" part of range *str = skipwhite(*str + 1); - vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0); + vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false); if (len > 0) { *num2 = (int)num; *str = skipwhite(*str + len); @@ -6447,7 +6204,7 @@ static int open_cmdwin(void) char_u typestr[2]; int save_restart_edit = restart_edit; int save_State = State; - int save_exmode = exmode_active; + bool save_exmode = exmode_active; int save_cmdmsg_rl = cmdmsg_rl; /* Can't do this recursively. Can't do it when typing a password. */ @@ -6489,7 +6246,7 @@ static int open_cmdwin(void) curwin->w_p_fen = false; // Don't allow switching to another buffer. - curbuf_lock++; + curbuf->b_ro_locked++; // Showing the prompt may have set need_wait_return, reset it. need_wait_return = false; @@ -6497,12 +6254,12 @@ static int open_cmdwin(void) const int histtype = hist_char2type(cmdwin_type); if (histtype == HIST_CMD || histtype == HIST_DEBUG) { if (p_wc == TAB) { - add_map((char_u *)"<buffer> <Tab> <C-X><C-V>", INSERT); - add_map((char_u *)"<buffer> <Tab> a<C-X><C-V>", NORMAL); + add_map((char_u *)"<buffer> <Tab> <C-X><C-V>", INSERT, false); + add_map((char_u *)"<buffer> <Tab> a<C-X><C-V>", NORMAL, false); } set_option_value("ft", 0L, "vim", OPT_LOCAL); } - curbuf_lock--; + curbuf->b_ro_locked--; // Reset 'textwidth' after setting 'filetype' (the Vim filetype plugin // sets 'textwidth' to 78). @@ -6541,7 +6298,7 @@ static int open_cmdwin(void) save_cmdline(&save_ccline); // No Ex mode here! - exmode_active = 0; + exmode_active = false; State = NORMAL; setmouse(); diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 67b8e7e92f..3038ed3947 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -583,22 +583,6 @@ static int makeopens(FILE *fd, char_u *dirnow) return FAIL; } - // Now put the other buffers into the buffer list. - FOR_ALL_BUFFERS(buf) { - if (!(only_save_windows && buf->b_nwindows == 0) - && !(buf->b_help && !(ssop_flags & SSOP_HELP)) - && buf->b_fname != NULL - && buf->b_p_bl) { - if (fprintf(fd, "badd +%" PRId64 " ", - buf->b_wininfo == NULL - ? (int64_t)1L - : (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0 - || ses_fname(fd, buf, &ssop_flags, true) == FAIL) { - return FAIL; - } - } - } - // the global argument list if (ses_arglist(fd, "argglobal", &global_alist.al_ga, !(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL) { @@ -813,12 +797,31 @@ static int makeopens(FILE *fd, char_u *dirnow) return FAIL; } + // Now put the remaining buffers into the buffer list. + // This is near the end, so that when 'hidden' is set we don't create extra + // buffers. If the buffer was already created with another command the + // ":badd" will have no effect. + FOR_ALL_BUFFERS(buf) { + if (!(only_save_windows && buf->b_nwindows == 0) + && !(buf->b_help && !(ssop_flags & SSOP_HELP)) + && buf->b_fname != NULL + && buf->b_p_bl) { + if (fprintf(fd, "badd +%" PRId64 " ", + buf->b_wininfo == NULL + ? (int64_t)1L + : (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0 + || ses_fname(fd, buf, &ssop_flags, true) == FAIL) { + return FAIL; + } + } + } + // // Wipe out an empty unnamed buffer we started in. // if (fprintf(fd, "%s", "if exists('s:wipebuf') " - "&& len(win_findbuf(s:wipebuf)) == 0" + "&& len(win_findbuf(s:wipebuf)) == 0 " "&& getbufvar(s:wipebuf, '&buftype') isnot# 'terminal'\n" " silent exe 'bwipe ' . s:wipebuf\n" "endif\n" diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 2906a2196b..b4f22dbf33 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -19,7 +19,7 @@ // Marks live in namespaces that allow plugins/users to segregate marks // from other users. // -// Deleting marks only happens when explicitly calling extmark_del, deleteing +// Deleting marks only happens when explicitly calling extmark_del, deleting // over a range of marks will only move the marks. Deleting on a mark will // leave it in same position unless it is on the EOL of a line. // @@ -48,20 +48,7 @@ #endif static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) { - if (!buf->b_extmark_ns) { - if (!put) { - return NULL; - } - buf->b_extmark_ns = map_new(uint64_t, ExtmarkNs)(); - buf->b_extmark_index = map_new(uint64_t, ExtmarkItem)(); - } - - ExtmarkNs *ns = map_ref(uint64_t, ExtmarkNs)(buf->b_extmark_ns, ns_id, put); - if (put && ns->map == NULL) { - ns->map = map_new(uint64_t, uint64_t)(); - ns->free_id = 1; - } - return ns; + return map_ref(uint64_t, ExtmarkNs)(buf->b_extmark_ns, ns_id, put); } @@ -195,7 +182,7 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_row, colnr_T u_col) { - if (!buf->b_extmark_ns) { + if (!map_size(buf->b_extmark_ns)) { return false; } @@ -215,12 +202,9 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, } // the value is either zero or the lnum (row+1) if highlight was present. - static Map(uint64_t, ssize_t) *delete_set = NULL; + static Map(uint64_t, ssize_t) delete_set = MAP_INIT; typedef struct { Decoration *decor; int row1; } DecorItem; static kvec_t(DecorItem) decors; - if (delete_set == NULL) { - delete_set = map_new(uint64_t, ssize_t)(); - } MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, l_row, l_col, itr); @@ -231,7 +215,7 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, || (mark.row == u_row && mark.col > u_col)) { break; } - ssize_t *del_status = map_ref(uint64_t, ssize_t)(delete_set, mark.id, + ssize_t *del_status = map_ref(uint64_t, ssize_t)(&delete_set, mark.id, false); if (del_status) { marktree_del_itr(buf->b_marktree, itr, false); @@ -240,7 +224,7 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, decor_redraw(buf, it.row1, mark.row, it.decor); decor_free(it.decor); } - map_del(uint64_t, ssize_t)(delete_set, mark.id); + map_del(uint64_t, ssize_t)(&delete_set, mark.id); continue; } @@ -261,14 +245,14 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, kv_push(decors, ((DecorItem) { .decor = item.decor, .row1 = mark.row })); } - map_put(uint64_t, ssize_t)(delete_set, other, decor_id); + map_put(uint64_t, ssize_t)(&delete_set, other, decor_id); } else if (item.decor) { decor_redraw(buf, mark.row, mark.row, item.decor); decor_free(item.decor); } ExtmarkNs *my_ns = all_ns ? buf_ns_ref(buf, item.ns_id, false) : ns; map_del(uint64_t, uint64_t)(my_ns->map, item.mark_id); - map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark.id); + map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, start_id); marktree_del_itr(buf->b_marktree, itr, false); } else { marktree_itr_next(buf->b_marktree, itr); @@ -276,7 +260,7 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, } uint64_t id; ssize_t decor_id; - map_foreach(delete_set, id, decor_id, { + map_foreach(&delete_set, id, decor_id, { mtpos_t pos = marktree_lookup(buf->b_marktree, id, itr); assert(itr->node); marktree_del_itr(buf->b_marktree, itr, false); @@ -286,7 +270,7 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, decor_free(it.decor); } }); - map_clear(uint64_t, ssize_t)(delete_set); + map_clear(uint64_t, ssize_t)(&delete_set); kv_size(decors) = 0; return marks_cleared; } @@ -383,7 +367,7 @@ ExtmarkInfo extmark_from_id(buf_T *buf, uint64_t ns_id, uint64_t id) // free extmarks from the buffer void extmark_free_all(buf_T *buf) { - if (!buf->b_extmark_ns) { + if (!map_size(buf->b_extmark_ns)) { return; } @@ -395,17 +379,17 @@ void extmark_free_all(buf_T *buf) map_foreach(buf->b_extmark_ns, id, ns, { (void)id; - map_free(uint64_t, uint64_t)(ns.map); + map_destroy(uint64_t, uint64_t)(ns.map); }); - map_free(uint64_t, ExtmarkNs)(buf->b_extmark_ns); - buf->b_extmark_ns = NULL; + map_destroy(uint64_t, ExtmarkNs)(buf->b_extmark_ns); + map_init(uint64_t, ExtmarkNs, buf->b_extmark_ns); map_foreach(buf->b_extmark_index, id, item, { (void)id; decor_free(item.decor); }); - map_free(uint64_t, ExtmarkItem)(buf->b_extmark_index); - buf->b_extmark_index = NULL; + map_destroy(uint64_t, ExtmarkItem)(buf->b_extmark_index); + map_init(uint64_t, ExtmarkItem, buf->b_extmark_index); } diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h index 784280dace..b5d91382ec 100644 --- a/src/nvim/extmark_defs.h +++ b/src/nvim/extmark_defs.h @@ -23,8 +23,8 @@ typedef kvec_t(ExtmarkUndoObject) extmark_undo_vec_t; typedef enum { kExtmarkNOOP, // Extmarks shouldn't be moved - kExtmarkUndo, // Operation should be reversable/undoable - kExtmarkNoUndo, // Operation should not be reversable + kExtmarkUndo, // Operation should be reversible/undoable + kExtmarkNoUndo, // Operation should not be reversible kExtmarkUndoNoRedo, // Operation should be undoable, but not redoable } ExtmarkOp; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 29c29a2884..e87520359e 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5,15 +5,13 @@ #include <assert.h> #include <errno.h> +#include <fcntl.h> +#include <inttypes.h> #include <stdbool.h> #include <string.h> -#include <inttypes.h> -#include <fcntl.h> -#include "nvim/vim.h" -#include "nvim/api/private/handle.h" +#include "nvim/api/private/helpers.h" #include "nvim/ascii.h" -#include "nvim/fileio.h" #include "nvim/buffer.h" #include "nvim/buffer_updates.h" #include "nvim/change.h" @@ -25,8 +23,10 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" +#include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/func_attr.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/iconv.h" @@ -36,10 +36,13 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/garray.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/os.h" +#include "nvim/os/os_defs.h" +#include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" #include "nvim/quickfix.h" @@ -47,21 +50,18 @@ #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/sha256.h" +#include "nvim/shada.h" #include "nvim/state.h" #include "nvim/strings.h" +#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" -#include "nvim/types.h" #include "nvim/undo.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/shada.h" -#include "nvim/os/os.h" -#include "nvim/os/os_defs.h" -#include "nvim/os/time.h" -#include "nvim/os/input.h" -#define BUFSIZE 8192 /* size of normal write buffer */ -#define SMBUFSIZE 256 /* size of emergency write buffer */ +#define BUFSIZE 8192 // size of normal write buffer +#define SMBUFSIZE 256 // size of emergency write buffer // For compatibility with libuv < 1.20.0 (tested on 1.18.0) #ifndef UV_FS_COPYFILE_FICLONE @@ -69,15 +69,15 @@ #endif #define HAS_BW_FLAGS -#define FIO_LATIN1 0x01 /* convert Latin1 */ -#define FIO_UTF8 0x02 /* convert UTF-8 */ -#define FIO_UCS2 0x04 /* convert UCS-2 */ -#define FIO_UCS4 0x08 /* convert UCS-4 */ -#define FIO_UTF16 0x10 /* convert UTF-16 */ -#define FIO_ENDIAN_L 0x80 /* little endian */ -#define FIO_NOCONVERT 0x2000 /* skip encoding conversion */ -#define FIO_UCSBOM 0x4000 /* check for BOM at start of file */ -#define FIO_ALL -1 /* allow all formats */ +#define FIO_LATIN1 0x01 // convert Latin1 +#define FIO_UTF8 0x02 // convert UTF-8 +#define FIO_UCS2 0x04 // convert UCS-2 +#define FIO_UCS4 0x08 // convert UCS-4 +#define FIO_UTF16 0x10 // convert UTF-16 +#define FIO_ENDIAN_L 0x80 // little endian +#define FIO_NOCONVERT 0x2000 // skip encoding conversion +#define FIO_UCSBOM 0x4000 // check for BOM at start of file +#define FIO_ALL -1 // allow all formats /* When converting, a read() or write() may leave some bytes to be converted * for the next call. The value is guessed... */ @@ -92,7 +92,7 @@ */ struct bw_info { int bw_fd; // file descriptor - char_u *bw_buf; // buffer with data to be written + char_u *bw_buf; // buffer with data to be written int bw_len; // length of data #ifdef HAS_BW_FLAGS int bw_flags; // FIO_ flags @@ -100,7 +100,7 @@ struct bw_info { char_u bw_rest[CONV_RESTLEN]; // not converted bytes int bw_restlen; // nr of bytes in bw_rest[] int bw_first; // first write call - char_u *bw_conv_buf; // buffer for writing converted chars + char_u *bw_conv_buf; // buffer for writing converted chars int bw_conv_buflen; // size of bw_conv_buf int bw_conv_error; // set for conversion error linenr_T bw_conv_error_lnum; // first line with error or zero @@ -114,8 +114,7 @@ struct bw_info { # include "fileio.c.generated.h" #endif -static char *e_auchangedbuf = N_( - "E812: Autocommands changed buffer or buffer name"); +static char *e_auchangedbuf = N_("E812: Autocommands changed buffer or buffer name"); void filemess(buf_T *buf, char_u *name, char_u *s, int attr) { @@ -131,14 +130,16 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) // For further ones overwrite the previous one, reset msg_scroll before // calling filemess(). msg_scroll_save = msg_scroll; - if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) + if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) { msg_scroll = FALSE; - if (!msg_scroll) /* wait a bit when overwriting an error msg */ + } + if (!msg_scroll) { // wait a bit when overwriting an error msg check_for_delay(FALSE); + } msg_start(); msg_scroll = msg_scroll_save; msg_scrolled_ign = TRUE; - /* may truncate the message to avoid a hit-return prompt */ + // may truncate the message to avoid a hit-return prompt msg_outtrans_attr(msg_may_trunc(FALSE, IObuff), attr); msg_clr_eos(); ui_flush(); @@ -146,41 +147,33 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) } -/* - * Read lines from file "fname" into the buffer after line "from". - * - * 1. We allocate blocks with try_malloc, as big as possible. - * 2. Each block is filled with characters from the file with a single read(). - * 3. The lines are inserted in the buffer with ml_append(). - * - * (caller must check that fname != NULL, unless READ_STDIN is used) - * - * "lines_to_skip" is the number of lines that must be skipped - * "lines_to_read" is the number of lines that are appended - * When not recovering lines_to_skip is 0 and lines_to_read MAXLNUM. - * - * flags: - * READ_NEW starting to edit a new buffer - * READ_FILTER reading filter output - * READ_STDIN read from stdin instead of a file - * READ_BUFFER read from curbuf instead of a file (converting after reading - * stdin) - * READ_DUMMY read into a dummy buffer (to check if file contents changed) - * READ_KEEP_UNDO don't clear undo info or read it from a file - * READ_FIFO read from fifo/socket instead of a file - * - * return FAIL for failure, NOTDONE for directory (failure), or OK - */ -int -readfile( - char_u *fname, - char_u *sfname, - linenr_T from, - linenr_T lines_to_skip, - linenr_T lines_to_read, - exarg_T *eap, // can be NULL! - int flags -) +/// Read lines from file "fname" into the buffer after line "from". +/// +/// 1. We allocate blocks with try_malloc, as big as possible. +/// 2. Each block is filled with characters from the file with a single read(). +/// 3. The lines are inserted in the buffer with ml_append(). +/// +/// (caller must check that fname != NULL, unless READ_STDIN is used) +/// +/// "lines_to_skip" is the number of lines that must be skipped +/// "lines_to_read" is the number of lines that are appended +/// When not recovering lines_to_skip is 0 and lines_to_read MAXLNUM. +/// +/// flags: +/// READ_NEW starting to edit a new buffer +/// READ_FILTER reading filter output +/// READ_STDIN read from stdin instead of a file +/// READ_BUFFER read from curbuf instead of a file (converting after reading +/// stdin) +/// READ_DUMMY read into a dummy buffer (to check if file contents changed) +/// READ_KEEP_UNDO don't clear undo info or read it from a file +/// READ_FIFO read from fifo/socket instead of a file +/// +/// @param eap can be NULL! +/// +/// @return FAIL for failure, NOTDONE for directory (failure), or OK +int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_skip, + linenr_T lines_to_read, exarg_T *eap, int flags) { int fd = 0; int newfile = (flags & READ_NEW); @@ -191,15 +184,15 @@ readfile( int read_fifo = (flags & READ_FIFO); int set_options = newfile || read_buffer || (eap != NULL && eap->read_edit); - linenr_T read_buf_lnum = 1; /* next line to read from curbuf */ - colnr_T read_buf_col = 0; /* next char to read from this line */ + linenr_T read_buf_lnum = 1; // next line to read from curbuf + colnr_T read_buf_col = 0; // next char to read from this line char_u c; linenr_T lnum = from; - char_u *ptr = NULL; /* pointer into read buffer */ - char_u *buffer = NULL; /* read buffer */ - char_u *new_buffer = NULL; /* init to shut up gcc */ - char_u *line_start = NULL; /* init to shut up gcc */ - int wasempty; /* buffer was empty before reading */ + char_u *ptr = NULL; // pointer into read buffer + char_u *buffer = NULL; // read buffer + char_u *new_buffer = NULL; // init to shut up gcc + char_u *line_start = NULL; // init to shut up gcc + int wasempty; // buffer was empty before reading colnr_T len; long size = 0; uint8_t *p = NULL; @@ -209,12 +202,12 @@ readfile( int read_undo_file = false; int split = 0; // number of split lines linenr_T linecnt; - int error = FALSE; /* errors encountered */ - int ff_error = EOL_UNKNOWN; /* file format with errors */ - long linerest = 0; /* remaining chars in line */ + int error = FALSE; // errors encountered + int ff_error = EOL_UNKNOWN; // file format with errors + long linerest = 0; // remaining chars in line int perm = 0; #ifdef UNIX - int swap_mode = -1; /* protection bits for swap file */ + int swap_mode = -1; // protection bits for swap file #endif int fileformat = 0; // end-of-line format bool keep_fileformat = false; @@ -234,11 +227,11 @@ readfile( int bad_char_behavior = BAD_REPLACE; /* BAD_KEEP, BAD_DROP or character to * replace with */ - char_u *tmpname = NULL; /* name of 'charconvert' output file */ + char_u *tmpname = NULL; // name of 'charconvert' output file int fio_flags = 0; - char_u *fenc; // fileencoding to use + char_u *fenc; // fileencoding to use bool fenc_alloced; // fenc_next is in allocated memory - char_u *fenc_next = NULL; // next item in 'fencs' or NULL + char_u *fenc_next = NULL; // next item in 'fencs' or NULL bool advance_fenc = false; long real_size = 0; # ifdef HAVE_ICONV @@ -246,21 +239,21 @@ readfile( int did_iconv = false; // TRUE when iconv() failed and trying // 'charconvert' next # endif - int converted = FALSE; /* TRUE if conversion done */ + int converted = FALSE; // TRUE if conversion done int notconverted = FALSE; /* TRUE if conversion wanted but it wasn't possible */ char_u conv_rest[CONV_RESTLEN]; - int conv_restlen = 0; /* nr of bytes in conv_rest[] */ - buf_T *old_curbuf; - char_u *old_b_ffname; - char_u *old_b_fname; + int conv_restlen = 0; // nr of bytes in conv_rest[] + buf_T *old_curbuf; + char_u *old_b_ffname; + char_u *old_b_fname; int using_b_ffname; 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_no_eol_lnum = 0; /* in case it was set by the previous read */ + curbuf->b_no_eol_lnum = 0; // in case it was set by the previous read /* * If there is no file name yet, use the one for the read file. @@ -273,8 +266,9 @@ readfile( && fname != NULL && vim_strchr(p_cpo, CPO_FNAMER) != NULL && !(flags & READ_DUMMY)) { - if (set_rw_fname(fname, sfname) == FAIL) + if (set_rw_fname(fname, sfname) == FAIL) { return FAIL; + } } /* Remember the initial values of curbuf, curbuf->b_ffname and @@ -290,10 +284,10 @@ readfile( /* After reading a file the cursor line changes but we don't want to * display the line. */ - ex_no_reprint = TRUE; + ex_no_reprint = true; - /* don't display the file info for another buffer now */ - need_fileinfo = FALSE; + // don't display the file info for another buffer now + need_fileinfo = false; // For Unix: Use the short file name whenever possible. // Avoids problems with networks and when directory names are changed. @@ -315,7 +309,7 @@ readfile( pos = curbuf->b_op_start; - /* Set '[ mark to the line above where the lines go (line 1 if zero). */ + // Set '[ mark to the line above where the lines go (line 1 if zero). curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); curbuf->b_op_start.col = 0; @@ -346,11 +340,11 @@ readfile( curbuf->b_op_start = pos; } - if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) - msg_scroll = FALSE; /* overwrite previous file message */ - else - msg_scroll = TRUE; /* don't overwrite previous file message */ - + if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) { + msg_scroll = FALSE; // overwrite previous file message + } else { + msg_scroll = TRUE; // don't overwrite previous file message + } // If the name is too long we might crash further on, quit here. if (fname != NULL && *fname != NUL) { size_t namelen = STRLEN(fname); @@ -397,7 +391,7 @@ readfile( } } - /* Set default or forced 'fileformat' and 'binary'. */ + // Set default or forced 'fileformat' and 'binary'. set_file_options(set_options, eap); /* @@ -407,8 +401,9 @@ readfile( * Only set/reset b_p_ro when BF_CHECK_RO is set. */ check_readonly = (newfile && (curbuf->b_flags & BF_CHECK_RO)); - if (check_readonly && !readonlymode) + if (check_readonly && !readonlymode) { curbuf->b_p_ro = FALSE; + } if (newfile && !read_stdin && !read_buffer && !read_fifo) { // Remember time of file. @@ -466,7 +461,7 @@ readfile( * "nofile" or "nowrite" buffer type. */ if (!bt_dontwrite(curbuf)) { check_need_swap(newfile); - /* SwapExists autocommand may mess things up */ + // SwapExists autocommand may mess things up if (curbuf != old_curbuf || (using_b_ffname && (old_b_ffname != curbuf->b_ffname)) @@ -493,19 +488,20 @@ readfile( // remember the current fileformat save_file_ff(curbuf); - if (aborting()) /* autocmds may abort script processing */ + if (aborting()) { // autocmds may abort script processing return FAIL; - return OK; /* a new file is not an error */ + } + return OK; // a new file is not an error } else { filemess(curbuf, sfname, (char_u *)( - (fd == UV_EFBIG) ? _("[File too big]") : + (fd == UV_EFBIG) ? _("[File too big]") : # if defined(UNIX) && defined(EOVERFLOW) - // libuv only returns -errno in Unix and in Windows open() does not - // set EOVERFLOW - (fd == -EOVERFLOW) ? _("[File too big]") : + // libuv only returns -errno in Unix and in Windows open() does not + // set EOVERFLOW + (fd == -EOVERFLOW) ? _("[File too big]") : # endif - _("[Permission Denied]")), 0); - curbuf->b_p_ro = TRUE; /* must use "w!" now */ + _("[Permission Denied]")), 0); + curbuf->b_p_ro = TRUE; // must use "w!" now } return FAIL; @@ -513,10 +509,11 @@ readfile( /* * Only set the 'ro' flag for readonly files the first time they are - * loaded. Help files always get readonly mode + * loaded. Help files always get readonly mode */ - if ((check_readonly && file_readonly) || curbuf->b_help) + if ((check_readonly && file_readonly) || curbuf->b_help) { curbuf->b_p_ro = TRUE; + } if (set_options) { /* Don't change 'eol' if reading from buffer as it will already be @@ -573,12 +570,13 @@ readfile( // If "Quit" selected at ATTENTION dialog, don't load the file. if (swap_exists_action == SEA_QUIT) { - if (!read_buffer && !read_stdin) + if (!read_buffer && !read_stdin) { close(fd); + } return FAIL; } - ++no_wait_return; /* don't wait for return yet */ + ++no_wait_return; // don't wait for return yet /* * Set '[ mark to the line above where the lines go (line 1 if zero). @@ -627,10 +625,10 @@ readfile( msg_scroll = m; } - if (aborting()) { /* autocmds may abort script processing */ + if (aborting()) { // autocmds may abort script processing --no_wait_return; msg_scroll = msg_save; - curbuf->b_p_ro = TRUE; /* must use "w!" now */ + curbuf->b_p_ro = TRUE; // must use "w!" now return FAIL; } /* @@ -646,16 +644,17 @@ readfile( || (fd = os_open((char *)fname, O_RDONLY, 0)) < 0)) { --no_wait_return; msg_scroll = msg_save; - if (fd < 0) + if (fd < 0) { EMSG(_("E200: *ReadPre autocommands made the file unreadable")); - else + } else { EMSG(_("E201: *ReadPre autocommands must not change current buffer")); - curbuf->b_p_ro = TRUE; /* must use "w!" now */ + } + curbuf->b_p_ro = TRUE; // must use "w!" now return FAIL; } } - /* Autocommands may add lines to the file, need to check if it is empty */ + // Autocommands may add lines to the file, need to check if it is empty wasempty = (curbuf->b_ml.ml_flags & ML_EMPTY); if (!recoverymode && !filtering && !(flags & READ_DUMMY)) { @@ -664,7 +663,7 @@ readfile( } } - msg_scroll = FALSE; /* overwrite the file message */ + msg_scroll = FALSE; // overwrite the file message /* * Set linecnt now, before the "retry" caused by a wrong guess for @@ -672,13 +671,15 @@ readfile( */ linecnt = curbuf->b_ml.ml_line_count; - /* "++bad=" argument. */ + // "++bad=" argument. if (eap != NULL && eap->bad_char != 0) { bad_char_behavior = eap->bad_char; - if (set_options) + if (set_options) { curbuf->b_bad_char = eap->bad_char; - } else + } + } else { curbuf->b_bad_char = 0; + } /* * Decide which 'encoding' to use or use first. @@ -715,16 +716,16 @@ readfile( * - "fileformat" check failed: try another * * Variables set for special retry actions: - * "file_rewind" Rewind the file to start reading it again. - * "advance_fenc" Advance "fenc" using "fenc_next". - * "skip_read" Re-use already read bytes (BOM detected). - * "did_iconv" iconv() conversion failed, try 'charconvert'. + * "file_rewind" Rewind the file to start reading it again. + * "advance_fenc" Advance "fenc" using "fenc_next". + * "skip_read" Re-use already read bytes (BOM detected). + * "did_iconv" iconv() conversion failed, try 'charconvert'. * "keep_fileformat" Don't reset "fileformat". * * Other status indicators: - * "tmpname" When != NULL did conversion with 'charconvert'. - * Output file has to be deleted afterwards. - * "iconv_fd" When != -1 did conversion with iconv(). + * "tmpname" When != NULL did conversion with 'charconvert'. + * Output file has to be deleted afterwards. + * "iconv_fd" When != -1 did conversion with iconv(). */ retry: @@ -759,17 +760,19 @@ retry: if (eap != NULL && eap->force_ff != 0) { fileformat = get_fileformat_force(curbuf, eap); try_unix = try_dos = try_mac = FALSE; - } else if (curbuf->b_p_bin) - fileformat = EOL_UNIX; /* binary: use Unix format */ - else if (*p_ffs == NUL) - fileformat = get_fileformat(curbuf); /* use format from buffer */ - else - fileformat = EOL_UNKNOWN; /* detect from file */ + } else if (curbuf->b_p_bin) { + fileformat = EOL_UNIX; // binary: use Unix format + } else if (*p_ffs == + NUL) { + fileformat = get_fileformat(curbuf); // use format from buffer + } else { + fileformat = EOL_UNKNOWN; // detect from file + } } # ifdef HAVE_ICONV if (iconv_fd != (iconv_t)-1) { - /* aborted conversion with iconv(), close the descriptor */ + // aborted conversion with iconv(), close the descriptor iconv_close(iconv_fd); iconv_fd = (iconv_t)-1; } @@ -786,13 +789,15 @@ retry: * without conversion. */ notconverted = TRUE; conv_error = 0; - if (fenc_alloced) + if (fenc_alloced) { xfree(fenc); + } fenc = (char_u *)""; fenc_alloced = false; } else { - if (fenc_alloced) + if (fenc_alloced) { xfree(fenc); + } if (fenc_next != NULL) { fenc = next_fenc(&fenc_next, &fenc_alloced); } else { @@ -813,7 +818,6 @@ retry: fio_flags = 0; converted = need_conversion(fenc); if (converted) { - /* "ucs-bom" means we need to check the first bytes of the file * for a BOM. */ if (STRCMP(fenc, ENC_UCSBOM) == 0) { @@ -834,8 +838,7 @@ retry: # ifdef HAVE_ICONV // Try using iconv() if we can't convert internally. if (fio_flags == 0 - && !did_iconv - ) { + && !did_iconv) { iconv_fd = (iconv_t)my_iconv_open((char_u *)"utf-8", fenc); } # endif @@ -861,7 +864,7 @@ retry: // Conversion failed. Try another one. advance_fenc = true; if (fd < 0) { - /* Re-opening the original file failed! */ + // Re-opening the original file failed! EMSG(_("E202: Conversion made file unreadable!")); error = TRUE; goto failed; @@ -901,8 +904,9 @@ retry: && !read_fifo && !read_stdin && !read_buffer); - if (read_undo_file) + if (read_undo_file) { sha256_start(&sha_ctx); + } } while (!error && !got_int) { @@ -939,8 +943,9 @@ retry: error = TRUE; break; } - if (linerest) /* copy characters from the previous buffer */ + if (linerest) { // copy characters from the previous buffer memmove(new_buffer, ptr - linerest, (size_t)linerest); + } xfree(buffer); buffer = new_buffer; ptr = buffer + linerest; @@ -972,7 +977,7 @@ retry: size = size / ICONV_MULT; // worst case } # ifdef HAVE_ICONV - } + } # endif if (conv_restlen > 0) { // Insert unconverted bytes from previous line. @@ -986,9 +991,9 @@ retry: * Read bytes from curbuf. Used for converting text read * from stdin. */ - if (read_buf_lnum > from) + if (read_buf_lnum > from) { size = 0; - else { + } else { int n, ni; long tlen; @@ -1002,10 +1007,11 @@ retry: * below. */ n = (int)(size - tlen); for (ni = 0; ni < n; ++ni) { - if (p[ni] == NL) + if (p[ni] == NL) { ptr[tlen++] = NUL; - else + } else { ptr[tlen++] = p[ni]; + } } read_buf_col += n; break; @@ -1013,18 +1019,20 @@ retry: /* Append whole line and new-line. Change NL * to NUL to reverse the effect done below. */ for (ni = 0; ni < n; ++ni) { - if (p[ni] == NL) + if (p[ni] == NL) { ptr[tlen++] = NUL; - else + } else { ptr[tlen++] = p[ni]; + } } ptr[tlen++] = NL; read_buf_col = 0; if (++read_buf_lnum > from) { /* When the last line didn't have an * end-of-line don't add it now either. */ - if (!curbuf->b_p_eol) + if (!curbuf->b_p_eol) { --tlen; + } size = tlen; break; } @@ -1039,30 +1047,33 @@ retry: } if (size <= 0) { - if (size < 0) /* read error */ + if (size < 0) { // read error error = TRUE; - else if (conv_restlen > 0) { + } else if (conv_restlen > 0) { /* * Reached end-of-file but some trailing bytes could * not be converted. Truncated file? */ - /* When we did a conversion report an error. */ + // When we did a conversion report an error. if (fio_flags != 0 # ifdef HAVE_ICONV || iconv_fd != (iconv_t)-1 # endif ) { - if (can_retry) + if (can_retry) { goto rewind_retry; - if (conv_error == 0) + } + if (conv_error == 0) { conv_error = curbuf->b_ml.ml_line_count - linecnt + 1; + } } - /* Remember the first linenr with an illegal byte */ - else if (illegal_byte == 0) + // Remember the first linenr with an illegal byte + else if (illegal_byte == 0) { illegal_byte = curbuf->b_ml.ml_line_count - linecnt + 1; + } if (bad_char_behavior == BAD_DROP) { *(ptr - conv_restlen) = NUL; conv_restlen = 0; @@ -1106,17 +1117,18 @@ retry: || (!curbuf->b_p_bomb && tmpname == NULL && (*fenc == 'u' || *fenc == NUL)))) { - char_u *ccname; + char_u *ccname; int blen; - /* no BOM detection in a short file or in binary mode */ - if (size < 2 || curbuf->b_p_bin) + // no BOM detection in a short file or in binary mode + if (size < 2 || curbuf->b_p_bin) { ccname = NULL; - else + } else { ccname = check_for_bom(ptr, size, &blen, - fio_flags == FIO_UCSBOM ? FIO_ALL : get_fio_flags(fenc)); + fio_flags == FIO_UCSBOM ? FIO_ALL : get_fio_flags(fenc)); + } if (ccname != NULL) { - /* Remove BOM from the text */ + // Remove BOM from the text filesize += blen; size -= blen; memmove(ptr, ptr + blen, (size_t)size); @@ -1131,27 +1143,29 @@ retry: // No BOM detected: retry with next encoding. advance_fenc = true; } else { - /* BOM detected: set "fenc" and jump back */ - if (fenc_alloced) + // BOM detected: set "fenc" and jump back + if (fenc_alloced) { xfree(fenc); + } fenc = ccname; fenc_alloced = false; } - /* retry reading without getting new bytes or rewinding */ + // retry reading without getting new bytes or rewinding skip_read = TRUE; goto retry; } } - /* Include not converted bytes. */ + // Include not converted bytes. ptr -= conv_restlen; size += conv_restlen; conv_restlen = 0; /* * Break here for a read error or end-of-file. */ - if (size <= 0) + if (size <= 0) { break; + } # ifdef HAVE_ICONV if (iconv_fd != (iconv_t)-1) { @@ -1159,8 +1173,8 @@ retry: * Attempt conversion of the read bytes to 'encoding' using * iconv(). */ - const char *fromp; - char *top; + const char *fromp; + char *top; size_t from_size; size_t to_size; @@ -1176,16 +1190,18 @@ retry: * alternative (help files). */ while ((iconv(iconv_fd, (void *)&fromp, &from_size, - &top, &to_size) + &top, &to_size) == (size_t)-1 && ICONV_ERRNO != ICONV_EINVAL) || from_size > CONV_RESTLEN) { - if (can_retry) + if (can_retry) { goto rewind_retry; - if (conv_error == 0) + } + if (conv_error == 0) { conv_error = readfile_linenr(linecnt, - ptr, (char_u *)top); + ptr, (char_u *)top); + } - /* Deal with a bad byte and continue with the next. */ + // Deal with a bad byte and continue with the next. ++fromp; --from_size; if (bad_char_behavior == BAD_KEEP) { @@ -1204,7 +1220,7 @@ retry: conv_restlen = (int)from_size; } - /* move the linerest to before the converted characters */ + // move the linerest to before the converted characters line_start = ptr - linerest; memmove(line_start, buffer, (size_t)linerest); size = (long)((char_u *)top - ptr); @@ -1213,8 +1229,8 @@ retry: if (fio_flags != 0) { unsigned int u8c; - char_u *dest; - char_u *tail = NULL; + char_u *dest; + char_u *tail = NULL; // Convert Unicode or Latin1 to UTF-8. // Go from end to start through the buffer, because the number @@ -1225,22 +1241,25 @@ retry: if (fio_flags == FIO_LATIN1 || fio_flags == FIO_UTF8) { p = ptr + size; if (fio_flags == FIO_UTF8) { - /* Check for a trailing incomplete UTF-8 sequence */ + // Check for a trailing incomplete UTF-8 sequence tail = ptr + size - 1; - while (tail > ptr && (*tail & 0xc0) == 0x80) + while (tail > ptr && (*tail & 0xc0) == 0x80) { --tail; - if (tail + utf_byte2len(*tail) <= ptr + size) + } + if (tail + utf_byte2len(*tail) <= ptr + size) { tail = NULL; - else + } else { p = tail; + } } } else if (fio_flags & (FIO_UCS2 | FIO_UTF16)) { - /* Check for a trailing byte */ + // Check for a trailing byte p = ptr + (size & ~1); - if (size & 1) + if (size & 1) { tail = p; + } if ((fio_flags & FIO_UTF16) && p > ptr) { - /* Check for a trailing leading word */ + // Check for a trailing leading word if (fio_flags & FIO_ENDIAN_L) { u8c = (*--p << 8); u8c += *--p; @@ -1248,16 +1267,18 @@ retry: u8c = *--p; u8c += (*--p << 8); } - if (u8c >= 0xd800 && u8c <= 0xdbff) + if (u8c >= 0xd800 && u8c <= 0xdbff) { tail = p; - else + } else { p += 2; + } } - } else { /* FIO_UCS4 */ - /* Check for trailing 1, 2 or 3 bytes */ + } else { // FIO_UCS4 + // Check for trailing 1, 2 or 3 bytes p = ptr + (size & ~3); - if (size & 3) + if (size & 3) { tail = p; + } } /* If there is a trailing incomplete sequence move it to @@ -1270,9 +1291,9 @@ retry: while (p > ptr) { - if (fio_flags & FIO_LATIN1) + if (fio_flags & FIO_LATIN1) { u8c = *--p; - else if (fio_flags & (FIO_UCS2 | FIO_UTF16)) { + } else if (fio_flags & (FIO_UCS2 | FIO_UTF16)) { if (fio_flags & FIO_ENDIAN_L) { u8c = (*--p << 8); u8c += *--p; @@ -1285,16 +1306,20 @@ retry: int u16c; if (p == ptr) { - /* Missing leading word. */ - if (can_retry) + // Missing leading word. + if (can_retry) { goto rewind_retry; - if (conv_error == 0) + } + if (conv_error == 0) { conv_error = readfile_linenr(linecnt, - ptr, p); - if (bad_char_behavior == BAD_DROP) + ptr, p); + } + if (bad_char_behavior == BAD_DROP) { continue; - if (bad_char_behavior != BAD_KEEP) + } + if (bad_char_behavior != BAD_KEEP) { u8c = bad_char_behavior; + } } /* found second word of double-word, get the first @@ -1309,17 +1334,21 @@ retry: u8c = 0x10000 + ((u16c & 0x3ff) << 10) + (u8c & 0x3ff); - /* Check if the word is indeed a leading word. */ + // Check if the word is indeed a leading word. if (u16c < 0xd800 || u16c > 0xdbff) { - if (can_retry) + if (can_retry) { goto rewind_retry; - if (conv_error == 0) + } + if (conv_error == 0) { conv_error = readfile_linenr(linecnt, - ptr, p); - if (bad_char_behavior == BAD_DROP) + ptr, p); + } + if (bad_char_behavior == BAD_DROP) { continue; - if (bad_char_behavior != BAD_KEEP) + } + if (bad_char_behavior != BAD_KEEP) { u8c = bad_char_behavior; + } } } } else if (fio_flags & FIO_UCS4) { @@ -1328,16 +1357,16 @@ retry: u8c += (unsigned)(*--p) << 16; u8c += (unsigned)(*--p) << 8; u8c += *--p; - } else { /* big endian */ + } else { // big endian u8c = *--p; u8c += (unsigned)(*--p) << 8; u8c += (unsigned)(*--p) << 16; u8c += (unsigned)(*--p) << 24; } - } else { /* UTF-8 */ - if (*--p < 0x80) + } else { // UTF-8 + if (*--p < 0x80) { u8c = *p; - else { + } else { len = utf_head_off(ptr, p); p -= len; u8c = utf_ptr2char(p); @@ -1345,15 +1374,19 @@ retry: /* Not a valid UTF-8 character, retry with * another fenc when possible, otherwise just * report the error. */ - if (can_retry) + if (can_retry) { goto rewind_retry; - if (conv_error == 0) + } + if (conv_error == 0) { conv_error = readfile_linenr(linecnt, - ptr, p); - if (bad_char_behavior == BAD_DROP) + ptr, p); + } + if (bad_char_behavior == BAD_DROP) { continue; - if (bad_char_behavior != BAD_KEEP) + } + if (bad_char_behavior != BAD_KEEP) { u8c = bad_char_behavior; + } } } } @@ -1407,31 +1440,35 @@ retry: /* Illegal byte. If we can try another encoding * do that, unless at EOF where a truncated * file is more likely than a conversion error. */ - if (can_retry && !incomplete_tail) + if (can_retry && !incomplete_tail) { break; + } # ifdef HAVE_ICONV // When we did a conversion report an error. if (iconv_fd != (iconv_t)-1 && conv_error == 0) { conv_error = readfile_linenr(linecnt, ptr, p); } # endif - /* Remember the first linenr with an illegal byte */ - if (conv_error == 0 && illegal_byte == 0) + // Remember the first linenr with an illegal byte + if (conv_error == 0 && illegal_byte == 0) { illegal_byte = readfile_linenr(linecnt, ptr, p); + } - /* Drop, keep or replace the bad byte. */ + // Drop, keep or replace the bad byte. if (bad_char_behavior == BAD_DROP) { memmove(p, p + 1, todo - 1); --p; --size; - } else if (bad_char_behavior != BAD_KEEP) + } else if (bad_char_behavior != BAD_KEEP) { *p = bad_char_behavior; - } else + } + } else { p += l - 1; + } } } if (p < ptr + size && !incomplete_tail) { - /* Detected a UTF-8 error. */ + // Detected a UTF-8 error. rewind_retry: // Retry reading with another conversion. # ifdef HAVE_ICONV @@ -1443,21 +1480,21 @@ rewind_retry: // use next item from 'fileencodings' advance_fenc = true; # ifdef HAVE_ICONV - } + } # endif file_rewind = true; goto retry; } } - /* count the number of characters (after conversion!) */ + // count the number of characters (after conversion!) filesize += size; /* * when reading the first part of a file: guess EOL type */ if (fileformat == EOL_UNKNOWN) { - /* First try finding a NL, for Dos and Unix */ + // First try finding a NL, for Dos and Unix if (try_dos || try_unix) { // Reset the carriage return counter. if (try_mac) { @@ -1467,32 +1504,36 @@ rewind_retry: for (p = ptr; p < ptr + size; ++p) { if (*p == NL) { if (!try_unix - || (try_dos && p > ptr && p[-1] == CAR)) + || (try_dos && p > ptr && p[-1] == CAR)) { fileformat = EOL_DOS; - else + } else { fileformat = EOL_UNIX; + } break; } else if (*p == CAR && try_mac) { try_mac++; } } - /* Don't give in to EOL_UNIX if EOL_MAC is more likely */ + // Don't give in to EOL_UNIX if EOL_MAC is more likely if (fileformat == EOL_UNIX && try_mac) { - /* Need to reset the counters when retrying fenc. */ + // Need to reset the counters when retrying fenc. try_mac = 1; try_unix = 1; - for (; p >= ptr && *p != CAR; p--) + for (; p >= ptr && *p != CAR; p--) { ; + } if (p >= ptr) { for (p = ptr; p < ptr + size; ++p) { - if (*p == NL) + if (*p == NL) { try_unix++; - else if (*p == CAR) + } else if (*p == CAR) { try_mac++; + } } - if (try_mac > try_unix) + if (try_mac > try_unix) { fileformat = EOL_MAC; + } } } else if (fileformat == EOL_UNKNOWN && try_mac == 1) { // Looking for CR but found no end-of-line markers at all: @@ -1501,13 +1542,15 @@ rewind_retry: } } - /* No NL found: may use Mac format */ - if (fileformat == EOL_UNKNOWN && try_mac) + // No NL found: may use Mac format + if (fileformat == EOL_UNKNOWN && try_mac) { fileformat = EOL_MAC; + } - /* Still nothing found? Use first format in 'ffs' */ - if (fileformat == EOL_UNKNOWN) + // Still nothing found? Use first format in 'ffs' + if (fileformat == EOL_UNKNOWN) { fileformat = default_fileformat(); + } // May set 'p_ff' if editing a new file. if (set_options) { @@ -1523,44 +1566,48 @@ rewind_retry: if (fileformat == EOL_MAC) { --ptr; while (++ptr, --size >= 0) { - /* catch most common case first */ - if ((c = *ptr) != NUL && c != CAR && c != NL) + // catch most common case first + if ((c = *ptr) != NUL && c != CAR && c != NL) { continue; - if (c == NUL) - *ptr = NL; /* NULs are replaced by newlines! */ - else if (c == NL) - *ptr = CAR; /* NLs are replaced by CRs! */ - else { + } + if (c == NUL) { + *ptr = NL; // NULs are replaced by newlines! + } else if (c == NL) { + *ptr = CAR; // NLs are replaced by CRs! + } else { if (skip_count == 0) { - *ptr = NUL; /* end of line */ - len = (colnr_T) (ptr - line_start + 1); + *ptr = NUL; // end of line + len = (colnr_T)(ptr - line_start + 1); if (ml_append(lnum, line_start, len, newfile) == FAIL) { error = TRUE; break; } - if (read_undo_file) + if (read_undo_file) { sha256_update(&sha_ctx, line_start, len); + } ++lnum; if (--read_count == 0) { - error = TRUE; /* break loop */ - line_start = ptr; /* nothing left to write */ + error = TRUE; // break loop + line_start = ptr; // nothing left to write break; } - } else + } else { --skip_count; + } line_start = ptr + 1; } } } else { --ptr; while (++ptr, --size >= 0) { - if ((c = *ptr) != NUL && c != NL) /* catch most common case */ + if ((c = *ptr) != NUL && c != NL) { // catch most common case continue; - if (c == NUL) - *ptr = NL; /* NULs are replaced by newlines! */ - else { + } + if (c == NUL) { + *ptr = NL; // NULs are replaced by newlines! + } else { if (skip_count == 0) { - *ptr = NUL; /* end of line */ + *ptr = NUL; // end of line len = (colnr_T)(ptr - line_start + 1); if (fileformat == EOL_DOS) { if (ptr > line_start && ptr[-1] == CAR) { @@ -1577,8 +1624,9 @@ rewind_retry: && (read_buffer || vim_lseek(fd, (off_T)0L, SEEK_SET) == 0)) { fileformat = EOL_UNIX; - if (set_options) + if (set_options) { set_fileformat(EOL_UNIX, OPT_LOCAL); + } file_rewind = true; keep_fileformat = true; goto retry; @@ -1590,16 +1638,18 @@ rewind_retry: error = TRUE; break; } - if (read_undo_file) + if (read_undo_file) { sha256_update(&sha_ctx, line_start, len); + } ++lnum; if (--read_count == 0) { - error = TRUE; /* break loop */ - line_start = ptr; /* nothing left to write */ + error = TRUE; // break loop + line_start = ptr; // nothing left to write break; } - } else + } else { --skip_count; + } line_start = ptr + 1; } } @@ -1609,9 +1659,10 @@ rewind_retry: } failed: - /* not an error, max. number of lines reached */ - if (error && read_count == 0) + // not an error, max. number of lines reached + if (error && read_count == 0) { error = FALSE; + } /* * If we get EOF in the middle of a line, note the fact and @@ -1625,16 +1676,18 @@ failed: && fileformat == EOL_DOS && *line_start == Ctrl_Z && ptr == line_start + 1)) { - /* remember for when writing */ - if (set_options) + // remember for when writing + if (set_options) { curbuf->b_p_eol = FALSE; + } *ptr = NUL; len = (colnr_T)(ptr - line_start + 1); - if (ml_append(lnum, line_start, len, newfile) == FAIL) + if (ml_append(lnum, line_start, len, newfile) == FAIL) { error = TRUE; - else { - if (read_undo_file) + } else { + if (read_undo_file) { sha256_update(&sha_ctx, line_start, len); + } read_no_eol_lnum = ++lnum; } } @@ -1646,8 +1699,9 @@ failed: // Also for ":read ++edit file". set_string_option_direct("fenc", -1, fenc, OPT_FREE | OPT_LOCAL, 0); } - if (fenc_alloced) + if (fenc_alloced) { xfree(fenc); + } # ifdef HAVE_ICONV if (iconv_fd != (iconv_t)-1) { iconv_close(iconv_fd); @@ -1679,13 +1733,13 @@ failed: os_remove((char *)tmpname); // delete converted file xfree(tmpname); } - --no_wait_return; /* may wait for return now */ + --no_wait_return; // may wait for return now /* * In recovery mode everything but autocommands is skipped. */ if (!recoverymode) { - /* need to delete the last line, which comes from the empty buffer */ + // need to delete the last line, which comes from the empty buffer if (newfile && wasempty && !(curbuf->b_ml.ml_flags & ML_EMPTY)) { ml_delete(curbuf->b_ml.ml_line_count, false); linecnt--; @@ -1695,8 +1749,9 @@ failed: curbuf->deleted_codepoints = 0; curbuf->deleted_codeunits = 0; linecnt = curbuf->b_ml.ml_line_count - linecnt; - if (filesize == 0) + if (filesize == 0) { linecnt = 0; + } if (newfile || read_buffer) { redraw_curbuf_later(NOT_VALID); /* After reading the text into the buffer the diff info needs to @@ -1705,8 +1760,9 @@ failed: /* All folds in the window are invalid now. Mark them for update * before triggering autocommands. */ foldUpdateAll(curwin); - } else if (linecnt) /* appended at least one line */ + } else if (linecnt) { // appended at least one line appended_lines_mark(from, linecnt); + } /* * If we were reading from the same terminal as where messages go, @@ -1720,12 +1776,13 @@ failed: if (got_int) { if (!(flags & READ_DUMMY)) { filemess(curbuf, sfname, (char_u *)_(e_interr), 0); - if (newfile) - curbuf->b_p_ro = TRUE; /* must use "w!" now */ + if (newfile) { + curbuf->b_p_ro = TRUE; // must use "w!" now + } } msg_scroll = msg_save; check_marks_read(); - return OK; /* an interrupt isn't really an error */ + return OK; // an interrupt isn't really an error } if (!filtering && !(flags & READ_DUMMY)) { @@ -1742,7 +1799,7 @@ failed: c = TRUE; } # ifdef OPEN_CHR_FILES - if (S_ISCHR(perm)) { /* or character special */ + if (S_ISCHR(perm)) { // or character special STRCAT(IObuff, _("[character special]")); c = TRUE; } @@ -1773,18 +1830,19 @@ failed: } if (conv_error != 0) { sprintf((char *)IObuff + STRLEN(IObuff), - _("[CONVERSION ERROR in line %" PRId64 "]"), (int64_t)conv_error); + _("[CONVERSION ERROR in line %" PRId64 "]"), (int64_t)conv_error); c = TRUE; } else if (illegal_byte > 0) { sprintf((char *)IObuff + STRLEN(IObuff), - _("[ILLEGAL BYTE in line %" PRId64 "]"), (int64_t)illegal_byte); + _("[ILLEGAL BYTE in line %" PRId64 "]"), (int64_t)illegal_byte); c = TRUE; - } else if (error) { + } else if (error) { STRCAT(IObuff, _("[READ ERRORS]")); c = TRUE; } - if (msg_add_fileformat(fileformat)) + if (msg_add_fileformat(fileformat)) { c = TRUE; + } msg_add_lines(c, (long)linecnt, filesize); @@ -1808,25 +1866,27 @@ failed: msg_scrolled_ign = FALSE; } - /* with errors writing the file requires ":w!" */ + // with errors writing the file requires ":w!" if (newfile && (error || conv_error != 0 || (illegal_byte > 0 && bad_char_behavior != BAD_KEEP) - )) + )) { curbuf->b_p_ro = TRUE; + } - u_clearline(); /* cannot use "U" command after adding lines */ + u_clearline(); // cannot use "U" command after adding lines /* * In Ex mode: cursor at last new line. * Otherwise: cursor at first new line. */ - if (exmode_active) + if (exmode_active) { curwin->w_cursor.lnum = from + linecnt; - else + } else { curwin->w_cursor.lnum = from + 1; + } check_cursor_lnum(); - beginline(BL_WHITE | BL_FIX); /* on first non-blank */ + beginline(BL_WHITE | BL_FIX); // on first non-blank /* * Set '[ and '] marks to the newly read lines. @@ -1835,7 +1895,6 @@ failed: curbuf->b_op_start.col = 0; curbuf->b_op_end.lnum = from + linecnt; curbuf->b_op_end.col = 0; - } msg_scroll = msg_save; @@ -1854,8 +1913,9 @@ failed: /* When reloading a buffer put the cursor at the first line that is * different. */ - if (flags & READ_KEEP_UNDO) + if (flags & READ_KEEP_UNDO) { u_find_first_changed(); + } /* * When opening a new file locate undo info and read it. @@ -1873,8 +1933,9 @@ failed: /* Save the fileformat now, otherwise the buffer will be considered * modified if the format/encoding was automatically detected. */ - if (set_options) + if (set_options) { save_file_ff(curbuf); + } /* * The output from the autocommands should not overwrite anything and @@ -1906,8 +1967,9 @@ failed: } } - if (recoverymode && error) + if (recoverymode && error) { return FAIL; + } return OK; } @@ -1930,25 +1992,24 @@ bool is_dev_fd_file(char_u *fname) #endif -/* - * From the current line count and characters read after that, estimate the - * line number where we are now. - * Used for error messages that include a line number. - */ -static linenr_T -readfile_linenr( - linenr_T linecnt, // line count before reading more bytes - char_u *p, // start of more bytes read - char_u *endp // end of more bytes read -) +/// From the current line count and characters read after that, estimate the +/// line number where we are now. +/// Used for error messages that include a line number. +/// +/// @param linecnt line count before reading more bytes +/// @param p start of more bytes read +/// @param endp end of more bytes read +static linenr_T readfile_linenr(linenr_T linecnt, char_u *p, char_u *endp) { - char_u *s; + char_u *s; linenr_T lnum; lnum = curbuf->b_ml.ml_line_count - linecnt + 1; - for (s = p; s < endp; ++s) - if (*s == '\n') + for (s = p; s < endp; ++s) { + if (*s == '\n') { ++lnum; + } + } return lnum; } @@ -1977,15 +2038,16 @@ void prep_exarg(exarg_T *eap, const buf_T *buf) */ void set_file_options(int set_options, exarg_T *eap) { - /* set default 'fileformat' */ + // set default 'fileformat' if (set_options) { - if (eap != NULL && eap->force_ff != 0) + if (eap != NULL && eap->force_ff != 0) { set_fileformat(get_fileformat_force(curbuf, eap), OPT_LOCAL); - else if (*p_ffs != NUL) + } else if (*p_ffs != NUL) { set_fileformat(default_fileformat(), OPT_LOCAL); + } } - /* set or reset 'binary' */ + // set or reset 'binary' if (eap != NULL && eap->force_bin != 0) { int oldval = curbuf->b_p_bin; @@ -2015,8 +2077,8 @@ void set_forced_fenc(exarg_T *eap) static char_u *next_fenc(char_u **pp, bool *alloced) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { - char_u *p; - char_u *r; + char_u *p; + char_u *r; *alloced = false; if (**pp == NUL) { @@ -2038,29 +2100,26 @@ static char_u *next_fenc(char_u **pp, bool *alloced) return r; } -/* - * Convert a file with the 'charconvert' expression. - * This closes the file which is to be read, converts it and opens the - * resulting file for reading. - * Returns name of the resulting converted file (the caller should delete it - * after reading it). - * Returns NULL if the conversion failed ("*fdp" is not set) . - */ -static char_u * -readfile_charconvert ( - char_u *fname, /* name of input file */ - char_u *fenc, /* converted from */ - int *fdp /* in/out: file descriptor of file */ -) +/// Convert a file with the 'charconvert' expression. +/// This closes the file which is to be read, converts it and opens the +/// resulting file for reading. +/// +/// @param fname name of input file +/// @param fenc converted from +/// @param fdp in/out: file descriptor of file +/// +/// @return name of the resulting converted file (the caller should delete it after reading it). +/// Returns NULL if the conversion failed ("*fdp" is not set) . +static char_u *readfile_charconvert(char_u *fname, char_u *fenc, int *fdp) { - char_u *tmpname; - char_u *errmsg = NULL; + char_u *tmpname; + char_u *errmsg = NULL; tmpname = vim_tempname(); - if (tmpname == NULL) + if (tmpname == NULL) { errmsg = (char_u *)_("Can't find temp file for conversion"); - else { - close(*fdp); /* close the input file, ignore errors */ + } else { + close(*fdp); // close the input file, ignore errors *fdp = -1; if (eval_charconvert((char *)fenc, "utf-8", (char *)fname, (char *)tmpname) == FAIL) { @@ -2081,7 +2140,7 @@ readfile_charconvert ( } } - /* If the input file is closed, open it (caller should check for error). */ + // If the input file is closed, open it (caller should check for error). if (*fdp < 0) { *fdp = os_open((char *)fname, O_RDONLY, 0); } @@ -2111,45 +2170,35 @@ char *new_file_message(void) return shortmess(SHM_NEW) ? _("[New]") : _("[New File]"); } -/* - * buf_write() - write to file "fname" lines "start" through "end" - * - * We do our own buffering here because fwrite() is so slow. - * - * If "forceit" is true, we don't care for errors when attempting backups. - * In case of an error everything possible is done to restore the original - * file. But when "forceit" is TRUE, we risk losing it. - * - * When "reset_changed" is TRUE and "append" == FALSE and "start" == 1 and - * "end" == curbuf->b_ml.ml_line_count, reset curbuf->b_changed. - * - * This function must NOT use NameBuff (because it's called by autowrite()). - * - * return FAIL for failure, OK otherwise - */ -int -buf_write( - buf_T *buf, - char_u *fname, - char_u *sfname, - linenr_T start, - linenr_T end, - exarg_T *eap, /* for forced 'ff' and 'fenc', can be - NULL! */ - int append, /* append to the file */ - int forceit, - int reset_changed, - int filtering -) +/// buf_write() - write to file "fname" lines "start" through "end" +/// +/// We do our own buffering here because fwrite() is so slow. +/// +/// If "forceit" is true, we don't care for errors when attempting backups. +/// In case of an error everything possible is done to restore the original +/// file. But when "forceit" is TRUE, we risk losing it. +/// +/// When "reset_changed" is TRUE and "append" == FALSE and "start" == 1 and +/// "end" == curbuf->b_ml.ml_line_count, reset curbuf->b_changed. +/// +/// This function must NOT use NameBuff (because it's called by autowrite()). +/// +/// +/// @param eap for forced 'ff' and 'fenc', can be NULL! +/// @param append append to the file +/// +/// @return FAIL for failure, OK otherwise +int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_T end, exarg_T *eap, + int append, int forceit, int reset_changed, int filtering) { int fd; - char_u *backup = NULL; - int backup_copy = FALSE; /* copy the original file? */ + char_u *backup = NULL; + int backup_copy = FALSE; // copy the original file? int dobackup; - char_u *ffname; - char_u *wfname = NULL; /* name of file to write to */ - char_u *s; - char_u *ptr; + char_u *ffname; + char_u *wfname = NULL; // name of file to write to + char_u *s; + char_u *ptr; char_u c; int len; linenr_T lnum; @@ -2164,9 +2213,9 @@ buf_write( char *errmsg = NULL; int errmsgarg = 0; bool errmsg_allocated = false; - char_u *buffer; + char_u *buffer; char_u smallbuf[SMBUFSIZE]; - char_u *backup_ext; + char_u *backup_ext; int bufsize; long perm; // file permissions int retval = OK; @@ -2178,21 +2227,21 @@ buf_write( int prev_got_int = got_int; int checking_conversion; bool file_readonly = false; // overwritten file is read-only - static char *err_readonly = + static char *err_readonly = "is read-only (cannot override: \"W\" in 'cpoptions')"; #if defined(UNIX) - int made_writable = FALSE; /* 'w' bit has been set */ + int made_writable = FALSE; // 'w' bit has been set #endif - /* writing everything */ + // writing everything int whole = (start == 1 && end == buf->b_ml.ml_line_count); linenr_T old_line_count = buf->b_ml.ml_line_count; int fileformat; int write_bin; - struct bw_info write_info; /* info for buf_write_bytes() */ + struct bw_info write_info; // info for buf_write_bytes() int converted = FALSE; int notconverted = FALSE; - char_u *fenc; /* effective 'fileencoding' */ - char_u *fenc_tofree = NULL; /* allocated "fenc" */ + char_u *fenc; // effective 'fileencoding' + char_u *fenc_tofree = NULL; // allocated "fenc" #ifdef HAS_BW_FLAGS int wb_flags = 0; #endif @@ -2204,8 +2253,9 @@ buf_write( context_sha256_T sha_ctx; unsigned int bkc = get_bkc_value(buf); - if (fname == NULL || *fname == NUL) /* safety check */ + if (fname == NULL || *fname == NUL) { // safety check return FAIL; + } if (buf->b_ml.ml_mfp == NULL) { /* This can happen during startup when there is a stray "w" in the * vimrc file. */ @@ -2217,16 +2267,17 @@ buf_write( * Disallow writing from .exrc and .vimrc in current directory for * security reasons. */ - if (check_secure()) + if (check_secure()) { return FAIL; + } - /* Avoid a crash for a long name. */ + // Avoid a crash for a long name. if (STRLEN(fname) >= MAXPATHL) { EMSG(_(e_longname)); return FAIL; } - /* must init bw_conv_buf and bw_iconv_fd before jumping to "fail" */ + // must init bw_conv_buf and bw_iconv_fd before jumping to "fail" write_info.bw_conv_buf = NULL; write_info.bw_conv_error = FALSE; write_info.bw_conv_error_lnum = 0; @@ -2237,7 +2288,7 @@ buf_write( /* After writing a file changedtick changes but we don't want to display * the line. */ - ex_no_reprint = TRUE; + ex_no_reprint = true; /* * If there is no file name yet, use the one for the written file. @@ -2254,13 +2305,15 @@ buf_write( && !filtering && (!append || vim_strchr(p_cpo, CPO_FNAMEAPP) != NULL) && vim_strchr(p_cpo, CPO_FNAMEW) != NULL) { - if (set_rw_fname(fname, sfname) == FAIL) + if (set_rw_fname(fname, sfname) == FAIL) { return FAIL; - buf = curbuf; /* just in case autocmds made "buf" invalid */ + } + buf = curbuf; // just in case autocmds made "buf" invalid } - if (sfname == NULL) + if (sfname == NULL) { sfname = fname; + } // For Unix: Use the short file name whenever possible. // Avoids problems with networks and when directory names are changed. @@ -2271,12 +2324,13 @@ buf_write( fname = sfname; #endif - if (buf->b_ffname != NULL && fnamecmp(ffname, buf->b_ffname) == 0) + if (buf->b_ffname != NULL && fnamecmp(ffname, buf->b_ffname) == 0) { overwriting = TRUE; - else + } else { overwriting = FALSE; + } - ++no_wait_return; /* don't wait for return yet */ + ++no_wait_return; // don't wait for return yet /* * Set '[ and '] marks to the lines to be written. @@ -2302,14 +2356,18 @@ buf_write( * Set curbuf to the buffer to be written. * Careful: The autocommands may call buf_write() recursively! */ - if (ffname == buf->b_ffname) + if (ffname == buf->b_ffname) { buf_ffname = TRUE; - if (sfname == buf->b_sfname) + } + if (sfname == buf->b_sfname) { buf_sfname = TRUE; - if (fname == buf->b_ffname) + } + if (fname == buf->b_ffname) { buf_fname_f = TRUE; - if (fname == buf->b_sfname) + } + if (fname == buf->b_sfname) { buf_fname_s = TRUE; + } // Set curwin/curbuf to buf and save a few things. aucmd_prepbuf(&aco, buf); @@ -2317,21 +2375,22 @@ buf_write( if (append) { if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEAPPENDCMD, - sfname, sfname, FALSE, curbuf, eap))) { - if (overwriting && bt_nofile(curbuf)) + sfname, sfname, FALSE, curbuf, eap))) { + if (overwriting && bt_nofile(curbuf)) { nofile_err = TRUE; - else + } else { apply_autocmds_exarg(EVENT_FILEAPPENDPRE, - sfname, sfname, FALSE, curbuf, eap); + sfname, sfname, FALSE, curbuf, eap); + } } } else if (filtering) { apply_autocmds_exarg(EVENT_FILTERWRITEPRE, - NULL, sfname, FALSE, curbuf, eap); - } else if (reset_changed && whole) { + NULL, sfname, FALSE, curbuf, eap); + } else if (reset_changed && whole) { int was_changed = curbufIsChanged(); did_cmd = apply_autocmds_exarg(EVENT_BUFWRITECMD, - sfname, sfname, FALSE, curbuf, eap); + sfname, sfname, FALSE, curbuf, eap); if (did_cmd) { if (was_changed && !curbufIsChanged()) { /* Written everything correctly and BufWriteCmd has reset @@ -2341,24 +2400,26 @@ buf_write( u_update_save_nr(curbuf); } } else { - if (overwriting && bt_nofile(curbuf)) + if (overwriting && bt_nofile(curbuf)) { nofile_err = TRUE; - else + } else { apply_autocmds_exarg(EVENT_BUFWRITEPRE, - sfname, sfname, FALSE, curbuf, eap); + sfname, sfname, FALSE, curbuf, eap); + } } } else { if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEWRITECMD, - sfname, sfname, FALSE, curbuf, eap))) { - if (overwriting && bt_nofile(curbuf)) + sfname, sfname, FALSE, curbuf, eap))) { + if (overwriting && bt_nofile(curbuf)) { nofile_err = TRUE; - else + } else { apply_autocmds_exarg(EVENT_FILEWRITEPRE, - sfname, sfname, FALSE, curbuf, eap); + sfname, sfname, FALSE, curbuf, eap); + } } } - /* restore curwin/curbuf and a few other things */ + // restore curwin/curbuf and a few other things aucmd_restbuf(&aco); // In three situations we return here and don't write the file: @@ -2370,41 +2431,45 @@ buf_write( } if (buf == NULL || (buf->b_ml.ml_mfp == NULL && !empty_memline) || did_cmd || nofile_err - || aborting() - ) { + || aborting()) { --no_wait_return; msg_scroll = msg_save; - if (nofile_err) + if (nofile_err) { EMSG(_("E676: No matching autocommands for acwrite buffer")); + } if (nofile_err - || aborting() - ) + || aborting()) { /* An aborting error, interrupt or exception in the * autocommands. */ return FAIL; + } if (did_cmd) { - if (buf == NULL) + if (buf == NULL) { /* The buffer was deleted. We assume it was written * (can't retry anyway). */ return OK; + } if (overwriting) { - /* Assume the buffer was written, update the timestamp. */ + // Assume the buffer was written, update the timestamp. ml_timestamp(buf); - if (append) + if (append) { buf->b_flags &= ~BF_NEW; - else + } else { buf->b_flags &= ~BF_WRITE_MASK; + } } if (reset_changed && buf->b_changed && !append - && (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL)) + && (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL)) { /* Buffer still changed, the autocommands didn't work * properly. */ return FAIL; + } return OK; } - if (!aborting()) + if (!aborting()) { EMSG(_("E203: Autocommands deleted or unloaded buffer to be written")); + } return FAIL; } @@ -2415,11 +2480,11 @@ buf_write( * changed the number of lines that are to be written (tricky!). */ if (buf->b_ml.ml_line_count != old_line_count) { - if (whole) /* write all */ + if (whole) { // write all end = buf->b_ml.ml_line_count; - else if (buf->b_ml.ml_line_count > old_line_count) /* more lines */ + } else if (buf->b_ml.ml_line_count > old_line_count) { // more lines end += buf->b_ml.ml_line_count - old_line_count; - else { /* less lines */ + } else { // less lines end -= old_line_count - buf->b_ml.ml_line_count; if (end < start) { --no_wait_return; @@ -2434,30 +2499,36 @@ buf_write( * The autocommands may have changed the name of the buffer, which may * be kept in fname, ffname and sfname. */ - if (buf_ffname) + if (buf_ffname) { ffname = buf->b_ffname; - if (buf_sfname) + } + if (buf_sfname) { sfname = buf->b_sfname; - if (buf_fname_f) + } + if (buf_fname_f) { fname = buf->b_ffname; - if (buf_fname_s) + } + if (buf_fname_s) { fname = buf->b_sfname; + } } - if (shortmess(SHM_OVER) && !exiting) - msg_scroll = FALSE; /* overwrite previous file message */ - else - msg_scroll = TRUE; /* don't overwrite previous file message */ - if (!filtering) + if (shortmess(SHM_OVER) && !exiting) { + msg_scroll = FALSE; // overwrite previous file message + } else { + msg_scroll = TRUE; // don't overwrite previous file message + } + if (!filtering) { filemess(buf, #ifndef UNIX - sfname, + sfname, #else - fname, + fname, #endif - (char_u *)"", 0); /* show that we are busy */ - msg_scroll = FALSE; /* always overwrite the file message now */ + (char_u *)"", 0); // show that we are busy + } + msg_scroll = FALSE; // always overwrite the file message now buffer = verbose_try_malloc(BUFSIZE); // can't allocate big buffer, use small one (to be able to write when out of @@ -2465,8 +2536,9 @@ buf_write( if (buffer == NULL) { buffer = smallbuf; bufsize = SMBUFSIZE; - } else + } else { bufsize = BUFSIZE; + } /* * Get information about original file (if there is one). @@ -2478,7 +2550,7 @@ buf_write( newfile = TRUE; } else { perm = file_info_old.stat.st_mode; - if (!S_ISREG(file_info_old.stat.st_mode)) { /* not a file */ + if (!S_ISREG(file_info_old.stat.st_mode)) { // not a file if (S_ISDIR(file_info_old.stat.st_mode)) { SET_ERRMSG_NUM("E502", _("is a directory")); goto fail; @@ -2540,8 +2612,9 @@ buf_write( */ if (overwriting) { retval = check_mtime(buf, &file_info_old); - if (retval == FAIL) + if (retval == FAIL) { goto fail; + } } } @@ -2549,16 +2622,18 @@ buf_write( /* * For systems that support ACL: get the ACL from the original file. */ - if (!newfile) + if (!newfile) { acl = mch_get_acl(fname); + } #endif /* * If 'backupskip' is not empty, don't make a backup for some files. */ dobackup = (p_wb || p_bk || *p_pm != NUL); - if (dobackup && *p_bsk != NUL && match_file_list(p_bsk, sfname, ffname)) + if (dobackup && *p_bsk != NUL && match_file_list(p_bsk, sfname, ffname)) { dobackup = FALSE; + } /* * Save the value of got_int and reset it. We don't want a previous @@ -2568,7 +2643,7 @@ buf_write( prev_got_int = got_int; got_int = FALSE; - /* Mark the buffer as 'being saved' to prevent changed buffer warnings */ + // Mark the buffer as 'being saved' to prevent changed buffer warnings buf->b_saving = true; /* @@ -2583,9 +2658,9 @@ buf_write( FileInfo file_info; const bool no_prepend_dot = false; - if ((bkc & BKC_YES) || append) { /* "yes" */ + if ((bkc & BKC_YES) || append) { // "yes" backup_copy = TRUE; - } else if ((bkc & BKC_AUTO)) { /* "auto" */ + } else if ((bkc & BKC_AUTO)) { // "auto" int i; /* @@ -2613,10 +2688,10 @@ buf_write( } } fd = os_open((char *)IObuff, - O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, perm); - if (fd < 0) /* can't write in directory */ + O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, perm); + if (fd < 0) { // can't write in directory backup_copy = TRUE; - else { + } else { # ifdef UNIX os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); if (!os_fileinfo((char *)IObuff, &file_info) @@ -2641,14 +2716,14 @@ buf_write( # ifdef UNIX bool file_info_link_ok = os_fileinfo_link((char *)fname, &file_info); - /* Symlinks. */ + // Symlinks. if ((bkc & BKC_BREAKSYMLINK) && file_info_link_ok && !os_fileinfo_id_equal(&file_info, &file_info_old)) { backup_copy = FALSE; } - /* Hardlinks. */ + // Hardlinks. if ((bkc & BKC_BREAKHARDLINK) && os_fileinfo_hardlinks(&file_info_old) > 1 && (!file_info_link_ok @@ -2658,18 +2733,19 @@ buf_write( # endif } - /* make sure we have a valid backup extension to use */ - if (*p_bex == NUL) + // make sure we have a valid backup extension to use + if (*p_bex == NUL) { backup_ext = (char_u *)".bak"; - else + } else { backup_ext = p_bex; + } if (backup_copy) { char_u *wp; int some_error = false; - char_u *dirp; - char_u *rootname; - char_u *p; + char_u *dirp; + char_u *rootname; + char_u *p; /* * Try to make the backup in each directory in the 'bdir' option. @@ -2688,9 +2764,22 @@ buf_write( /* * Isolate one directory name, using an entry in 'bdir'. */ - (void)copy_option_part(&dirp, IObuff, IOSIZE, ","); - p = IObuff + STRLEN(IObuff); - if (after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2]) { + size_t dir_len = copy_option_part(&dirp, IObuff, IOSIZE, ","); + p = IObuff + dir_len; + bool trailing_pathseps = after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2]; + if (trailing_pathseps) { + IObuff[dir_len - 2] = NUL; + } + if (*dirp == NUL && !os_isdir(IObuff)) { + int ret; + char *failed_dir; + if ((ret = os_mkdir_recurse((char *)IObuff, 0755, &failed_dir)) != 0) { + EMSG3(_("E303: Unable to create directory \"%s\" for backup file: %s"), + failed_dir, os_strerror(ret)); + xfree(failed_dir); + } + } + if (trailing_pathseps) { // Ends with '//', Use Full path if ((p = (char_u *)make_percent_swname((char *)IObuff, (char *)fname)) != NULL) { @@ -2702,7 +2791,7 @@ buf_write( rootname = get_file_in_dir(fname, IObuff); if (rootname == NULL) { - some_error = TRUE; /* out of memory */ + some_error = TRUE; // out of memory goto nobackup; } @@ -2718,7 +2807,7 @@ buf_write( if (backup == NULL) { xfree(rootname); - some_error = TRUE; /* out of memory */ + some_error = TRUE; // out of memory goto nobackup; } @@ -2761,7 +2850,7 @@ buf_write( * Try to create the backup file */ if (backup != NULL) { - /* remove old backup, if present */ + // remove old backup, if present os_remove((char *)backup); // set file protection same as original file, but @@ -2802,8 +2891,7 @@ buf_write( nobackup: if (backup == NULL && errmsg == NULL) { - SET_ERRMSG(_( - "E509: Cannot create backup file (add ! to override)")); + SET_ERRMSG(_("E509: Cannot create backup file (add ! to override)")); } // Ignore errors when forceit is TRUE. if ((some_error || errmsg != NULL) && !forceit) { @@ -2812,9 +2900,9 @@ nobackup: } SET_ERRMSG(NULL); } else { - char_u *dirp; - char_u *p; - char_u *rootname; + char_u *dirp; + char_u *p; + char_u *rootname; /* * Make a backup by renaming the original file. @@ -2840,9 +2928,22 @@ nobackup: /* * Isolate one directory name and make the backup file name. */ - (void)copy_option_part(&dirp, IObuff, IOSIZE, ","); - p = IObuff + STRLEN(IObuff); - if (after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2]) { + size_t dir_len = copy_option_part(&dirp, IObuff, IOSIZE, ","); + p = IObuff + dir_len; + bool trailing_pathseps = after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2]; + if (trailing_pathseps) { + IObuff[dir_len - 2] = NUL; + } + if (*dirp == NUL && !os_isdir(IObuff)) { + int ret; + char *failed_dir; + if ((ret = os_mkdir_recurse((char *)IObuff, 0755, &failed_dir)) != 0) { + EMSG3(_("E303: Unable to create directory \"%s\" for backup file: %s"), + failed_dir, os_strerror(ret)); + xfree(failed_dir); + } + } + if (trailing_pathseps) { // path ends with '//', use full path if ((p = (char_u *)make_percent_swname((char *)IObuff, (char *)fname)) != NULL) { @@ -2871,8 +2972,9 @@ nobackup: */ if (!p_bk && os_path_exists(backup)) { p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext); - if (p < backup) /* empty file name ??? */ + if (p < backup) { // empty file name ??? p = backup; + } *p = 'z'; while (*p > 'a' && os_path_exists(backup)) { (*p)--; @@ -2925,10 +3027,12 @@ nobackup: status_redraw_all(); // redraw status lines later } - if (end > buf->b_ml.ml_line_count) + if (end > buf->b_ml.ml_line_count) { end = buf->b_ml.ml_line_count; - if (buf->b_ml.ml_flags & ML_EMPTY) + } + if (buf->b_ml.ml_flags & ML_EMPTY) { start = end + 1; + } // If the original file is being overwritten, there is a small chance that // we crash in the middle of writing. Therefore the file is preserved now. @@ -2988,7 +3092,7 @@ nobackup: // internally. write_info.bw_iconv_fd = (iconv_t)my_iconv_open(fenc, (char_u *)"utf-8"); if (write_info.bw_iconv_fd != (iconv_t)-1) { - /* We're going to use iconv(), allocate a buffer to convert in. */ + // We're going to use iconv(), allocate a buffer to convert in. write_info.bw_conv_buflen = bufsize * ICONV_MULT; write_info.bw_conv_buf = verbose_try_malloc(write_info.bw_conv_buflen); if (!write_info.bw_conv_buf) { @@ -3015,11 +3119,9 @@ nobackup: # ifdef HAVE_ICONV && write_info.bw_iconv_fd == (iconv_t)-1 # endif - && wfname == fname - ) { + && wfname == fname) { if (!forceit) { - SET_ERRMSG(_( - "E213: Cannot convert (add ! to write without conversion)")); + SET_ERRMSG(_("E213: Cannot convert (add ! to write without conversion)")); goto restore_backup; } notconverted = TRUE; @@ -3054,7 +3156,7 @@ nobackup: O_WRONLY | (append ? (forceit ? (O_APPEND | O_CREAT) : O_APPEND) - : (O_CREAT | O_TRUNC)) + : (O_CREAT | O_TRUNC)) , perm < 0 ? 0666 : (perm & 0777))) < 0) { // A forced write will try to create a new file if the old one // is still readonly. This may also happen when the directory @@ -3070,28 +3172,28 @@ nobackup: SET_ERRMSG(_("E166: Can't open linked file for writing")); } else { #endif - SET_ERRMSG_ARG(_("E212: Can't open file for writing: %s"), fd); - if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL - && perm >= 0) { + SET_ERRMSG_ARG(_("E212: Can't open file for writing: %s"), fd); + if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL + && perm >= 0) { #ifdef UNIX - // we write to the file, thus it should be marked - // writable after all - if (!(perm & 0200)) { - made_writable = true; - } - perm |= 0200; - if (file_info_old.stat.st_uid != getuid() - || file_info_old.stat.st_gid != getgid()) { - perm &= 0777; - } + // we write to the file, thus it should be marked + // writable after all + if (!(perm & 0200)) { + made_writable = true; + } + perm |= 0200; + if (file_info_old.stat.st_uid != getuid() + || file_info_old.stat.st_gid != getgid()) { + perm &= 0777; + } #endif - if (!append) { // don't remove when appending - os_remove((char *)wfname); - } - continue; + if (!append) { // don't remove when appending + os_remove((char *)wfname); } -#ifdef UNIX + continue; } +#ifdef UNIX + } #endif } @@ -3256,7 +3358,7 @@ restore_backup: // Stop when writing done or an error was encountered. if (!checking_conversion || end == 0) { - break; + break; } // If no error happened until now, writing should be ok, so loop to @@ -3345,17 +3447,15 @@ restore_backup: if (errmsg == NULL) { if (write_info.bw_conv_error) { if (write_info.bw_conv_error_lnum == 0) { - SET_ERRMSG(_( - "E513: write error, conversion failed " - "(make 'fenc' empty to override)")); + SET_ERRMSG(_("E513: write error, conversion failed " + "(make 'fenc' empty to override)")); } else { errmsg_allocated = true; SET_ERRMSG(xmalloc(300)); - vim_snprintf( - errmsg, 300, - _("E513: write error, conversion failed in line %" PRIdLINENR - " (make 'fenc' empty to override)"), - write_info.bw_conv_error_lnum); + vim_snprintf(errmsg, 300, + _("E513: write error, conversion failed in line %" PRIdLINENR + " (make 'fenc' empty to override)"), + write_info.bw_conv_error_lnum); } } else if (got_int) { SET_ERRMSG(_(e_interr)); @@ -3394,11 +3494,11 @@ restore_backup: goto fail; } - lnum -= start; /* compute number of written lines */ - --no_wait_return; /* may wait for return now */ + lnum -= start; // compute number of written lines + --no_wait_return; // may wait for return now #if !defined(UNIX) - fname = sfname; /* use shortname now, for the messages */ + fname = sfname; // use shortname now, for the messages #endif if (!filtering) { add_quoted_fname((char *)IObuff, IOSIZE, buf, (const char *)fname); @@ -3406,9 +3506,10 @@ restore_backup: if (write_info.bw_conv_error) { STRCAT(IObuff, _(" CONVERSION ERROR")); c = TRUE; - if (write_info.bw_conv_error_lnum != 0) + if (write_info.bw_conv_error_lnum != 0) { vim_snprintf_add((char *)IObuff, IOSIZE, _(" in line %" PRId64 ";"), - (int64_t)write_info.bw_conv_error_lnum); + (int64_t)write_info.bw_conv_error_lnum); + } } else if (notconverted) { STRCAT(IObuff, _("[NOT converted]")); c = TRUE; @@ -3427,15 +3528,17 @@ restore_backup: msg_add_eol(); c = TRUE; } - /* may add [unix/dos/mac] */ - if (msg_add_fileformat(fileformat)) + // may add [unix/dos/mac] + if (msg_add_fileformat(fileformat)) { c = TRUE; - msg_add_lines(c, (long)lnum, nchars); /* add line/char count */ + } + msg_add_lines(c, (long)lnum, nchars); // add line/char count if (!shortmess(SHM_WRITE)) { - if (append) + if (append) { STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [a]") : _(" appended")); - else + } else { STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [w]") : _(" written")); + } } set_keep_msg(msg_trunc_attr(IObuff, FALSE, 0), 0); @@ -3463,10 +3566,11 @@ restore_backup: */ if (overwriting) { ml_timestamp(buf); - if (append) + if (append) { buf->b_flags &= ~BF_NEW; - else + } else { buf->b_flags &= ~BF_WRITE_MASK; + } } /* @@ -3502,11 +3606,12 @@ restore_backup: if (org == NULL || (empty_fd = os_open(org, - O_CREAT | O_EXCL | O_NOFOLLOW, - perm < 0 ? 0666 : (perm & 0777))) < 0) + O_CREAT | O_EXCL | O_NOFOLLOW, + perm < 0 ? 0666 : (perm & 0777))) < 0) { EMSG(_("E206: patchmode: can't touch empty original file")); - else + } else { close(empty_fd); + } } if (org != NULL) { os_setperm(org, os_getperm((const char *)fname) & 0777); @@ -3529,15 +3634,16 @@ restore_backup: * Finish up. We get here either after failure or success. */ fail: - --no_wait_return; /* may wait for return now */ + --no_wait_return; // may wait for return now nofail: - /* Done saving, we accept changed buffer warnings again */ + // Done saving, we accept changed buffer warnings again buf->b_saving = false; xfree(backup); - if (buffer != smallbuf) + if (buffer != smallbuf) { xfree(buffer); + } xfree(fenc_tofree); xfree(write_info.bw_conv_buf); # ifdef HAVE_ICONV @@ -3577,9 +3683,8 @@ nofail: const int attr = HL_ATTR(HLF_E); // Set highlight for error messages. MSG_PUTS_ATTR(_("\nWARNING: Original file may be lost or damaged\n"), attr | MSG_HIST); - MSG_PUTS_ATTR(_( - "don't quit the editor until the file is successfully written!"), - attr | MSG_HIST); + MSG_PUTS_ATTR(_("don't quit the editor until the file is successfully written!"), + attr | MSG_HIST); /* Update the timestamp to avoid an "overwrite changed file" * prompt when writing again. */ @@ -3605,7 +3710,7 @@ nofail: if (!should_abort(retval)) { aco_save_T aco; - curbuf->b_no_eol_lnum = 0; /* in case it was set by the previous read */ + curbuf->b_no_eol_lnum = 0; // in case it was set by the previous read /* * Apply POST autocommands. @@ -3613,24 +3718,26 @@ nofail: */ aucmd_prepbuf(&aco, buf); - if (append) + if (append) { apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname, - FALSE, curbuf, eap); - else if (filtering) + FALSE, curbuf, eap); + } else if (filtering) { apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname, - FALSE, curbuf, eap); - else if (reset_changed && whole) + FALSE, curbuf, eap); + } else if (reset_changed && whole) { apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname, - FALSE, curbuf, eap); - else + FALSE, curbuf, eap); + } else { apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname, - FALSE, curbuf, eap); + FALSE, curbuf, eap); + } - /* restore curwin/curbuf and a few other things */ + // restore curwin/curbuf and a few other things aucmd_restbuf(&aco); - if (aborting()) /* autocmds may abort script processing */ + if (aborting()) { // autocmds may abort script processing retval = FALSE; + } } got_int |= prev_got_int; @@ -3647,16 +3754,18 @@ nofail: */ static int set_rw_fname(char_u *fname, char_u *sfname) { - buf_T *buf = curbuf; + buf_T *buf = curbuf; - /* It's like the unnamed buffer is deleted.... */ - if (curbuf->b_p_bl) + // It's like the unnamed buffer is deleted.... + if (curbuf->b_p_bl) { apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf); + } apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf); - if (aborting()) /* autocmds may abort script processing */ + if (aborting()) { // autocmds may abort script processing return FAIL; + } if (curbuf != buf) { - /* We are in another buffer now, don't do the renaming. */ + // We are in another buffer now, don't do the renaming. EMSG(_(e_auchangedbuf)); return FAIL; } @@ -3665,14 +3774,16 @@ static int set_rw_fname(char_u *fname, char_u *sfname) curbuf->b_flags |= BF_NOTEDITED; } - /* ....and a new named one is created */ + // ....and a new named one is created apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, curbuf); - if (curbuf->b_p_bl) + if (curbuf->b_p_bl) { apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf); - if (aborting()) /* autocmds may abort script processing */ + } + if (aborting()) { // autocmds may abort script processing return FAIL; + } - /* Do filetype detection now if 'filetype' is empty. */ + // Do filetype detection now if 'filetype' is empty. if (*curbuf->b_p_ft == NUL) { if (au_has_group((char_u *)"filetypedetect")) { (void)do_doautocmd((char_u *)"filetypedetect BufRead", false, NULL); @@ -3691,8 +3802,8 @@ static int set_rw_fname(char_u *fname, char_u *sfname) /// @param[in] buf_len ret_buf length. /// @param[in] buf buf_T file name is coming from. /// @param[in] fname File name to write. -static void add_quoted_fname(char *const ret_buf, const size_t buf_len, - const buf_T *const buf, const char *fname) +static void add_quoted_fname(char *const ret_buf, const size_t buf_len, const buf_T *const buf, + const char *fname) FUNC_ATTR_NONNULL_ARG(1) { if (fname == NULL) { @@ -3735,25 +3846,26 @@ static bool msg_add_fileformat(int eol_type) */ void msg_add_lines(int insert_space, long lnum, off_T nchars) { - char_u *p; + char_u *p; p = IObuff + STRLEN(IObuff); - if (insert_space) + if (insert_space) { *p++ = ' '; - if (shortmess(SHM_LINES)) { - sprintf((char *)p, "%" PRId64 "L, %" PRId64 "C", - (int64_t)lnum, (int64_t)nchars); } - else { - if (lnum == 1) + if (shortmess(SHM_LINES)) { + sprintf((char *)p, "%" PRId64 "L, %" PRId64 "C", + (int64_t)lnum, (int64_t)nchars); + } else { + if (lnum == 1) { STRCPY(p, _("1 line, ")); - else + } else { sprintf((char *)p, _("%" PRId64 " lines, "), (int64_t)lnum); + } p += STRLEN(p); - if (nchars == 1) + if (nchars == 1) { STRCPY(p, _("1 character")); - else { + } else { sprintf((char *)p, _("%" PRId64 " characters"), (int64_t)nchars); } } @@ -3765,7 +3877,7 @@ void msg_add_lines(int insert_space, long lnum, off_T nchars) static void msg_add_eol(void) { STRCAT(IObuff, - shortmess(SHM_LAST) ? _("[noeol]") : _("[Incomplete last line]")); + shortmess(SHM_LAST) ? _("[noeol]") : _("[Incomplete last line]")); } /* @@ -3816,17 +3928,17 @@ static bool time_differs(long t1, long t2) FUNC_ATTR_CONST static int buf_write_bytes(struct bw_info *ip) { int wlen; - char_u *buf = ip->bw_buf; /* data to write */ - int len = ip->bw_len; /* length of data */ + char_u *buf = ip->bw_buf; // data to write + int len = ip->bw_len; // length of data #ifdef HAS_BW_FLAGS - int flags = ip->bw_flags; /* extra flags */ + int flags = ip->bw_flags; // extra flags #endif /* * Skip conversion when writing the BOM. */ if (!(flags & FIO_NOCONVERT)) { - char_u *p; + char_u *p; unsigned c; int n; @@ -3834,9 +3946,10 @@ static int buf_write_bytes(struct bw_info *ip) /* * Convert latin1 in the buffer to UTF-8 in the file. */ - p = ip->bw_conv_buf; /* translate to buffer */ - for (wlen = 0; wlen < len; ++wlen) + p = ip->bw_conv_buf; // translate to buffer + for (wlen = 0; wlen < len; ++wlen) { p += utf_char2bytes(buf[wlen], p); + } buf = ip->bw_conv_buf; len = (int)(p - ip->bw_conv_buf); } else if (flags & (FIO_UCS4 | FIO_UTF16 | FIO_UCS2 | FIO_LATIN1)) { @@ -3844,10 +3957,11 @@ static int buf_write_bytes(struct bw_info *ip) * Convert UTF-8 bytes in the buffer to UCS-2, UCS-4, UTF-16 or * Latin1 chars in the file. */ - if (flags & FIO_LATIN1) - p = buf; /* translate in-place (can only get shorter) */ - else - p = ip->bw_conv_buf; /* translate to buffer */ + if (flags & FIO_LATIN1) { + p = buf; // translate in-place (can only get shorter) + } else { + p = ip->bw_conv_buf; // translate to buffer + } for (wlen = 0; wlen < len; wlen += n) { if (wlen == 0 && ip->bw_restlen != 0) { int l; @@ -3856,30 +3970,33 @@ static int buf_write_bytes(struct bw_info *ip) * buf[] to get a full sequence. Might still be too * short! */ l = CONV_RESTLEN - ip->bw_restlen; - if (l > len) + if (l > len) { l = len; + } memmove(ip->bw_rest + ip->bw_restlen, buf, (size_t)l); n = utf_ptr2len_len(ip->bw_rest, ip->bw_restlen + l); if (n > ip->bw_restlen + len) { /* We have an incomplete byte sequence at the end to * be written. We can't convert it without the * remaining bytes. Keep them for the next call. */ - if (ip->bw_restlen + len > CONV_RESTLEN) + if (ip->bw_restlen + len > CONV_RESTLEN) { return FAIL; + } ip->bw_restlen += len; break; } - if (n > 1) + if (n > 1) { c = utf_ptr2char(ip->bw_rest); - else + } else { c = ip->bw_rest[0]; + } if (n >= ip->bw_restlen) { n -= ip->bw_restlen; ip->bw_restlen = 0; } else { ip->bw_restlen -= n; memmove(ip->bw_rest, ip->bw_rest + n, - (size_t)ip->bw_restlen); + (size_t)ip->bw_restlen); n = 0; } } else { @@ -3888,29 +4005,32 @@ static int buf_write_bytes(struct bw_info *ip) /* We have an incomplete byte sequence at the end to * be written. We can't convert it without the * remaining bytes. Keep them for the next call. */ - if (len - wlen > CONV_RESTLEN) + if (len - wlen > CONV_RESTLEN) { return FAIL; + } ip->bw_restlen = len - wlen; memmove(ip->bw_rest, buf + wlen, - (size_t)ip->bw_restlen); + (size_t)ip->bw_restlen); break; } - if (n > 1) + if (n > 1) { c = utf_ptr2char(buf + wlen); - else + } else { c = buf[wlen]; + } } if (ucs2bytes(c, &p, flags) && !ip->bw_conv_error) { ip->bw_conv_error = TRUE; ip->bw_conv_error_lnum = ip->bw_start_lnum; } - if (c == NL) + if (c == NL) { ++ip->bw_start_lnum; + } } - if (flags & FIO_LATIN1) + if (flags & FIO_LATIN1) { len = (int)(p - buf); - else { + } else { buf = ip->bw_conv_buf; len = (int)(p - ip->bw_conv_buf); } @@ -3918,12 +4038,12 @@ static int buf_write_bytes(struct bw_info *ip) # ifdef HAVE_ICONV if (ip->bw_iconv_fd != (iconv_t)-1) { - const char *from; + const char *from; size_t fromlen; - char *to; + char *to; size_t tolen; - /* Convert with iconv(). */ + // Convert with iconv(). if (ip->bw_restlen > 0) { char *fp; @@ -3946,7 +4066,7 @@ static int buf_write_bytes(struct bw_info *ip) if (ip->bw_first) { size_t save_len = tolen; - /* output the initial shift state sequence */ + // output the initial shift state sequence (void)iconv(ip->bw_iconv_fd, NULL, NULL, &to, &tolen); /* There is a bug in iconv() on Linux (which appears to be @@ -3969,9 +4089,10 @@ static int buf_write_bytes(struct bw_info *ip) return FAIL; } - /* copy remainder to ip->bw_rest[] to be used for the next call. */ - if (fromlen > 0) + // copy remainder to ip->bw_rest[] to be used for the next call. + if (fromlen > 0) { memmove(ip->bw_rest, (void *)from, fromlen); + } ip->bw_restlen = (int)fromlen; buf = ip->bw_conv_buf; @@ -3997,7 +4118,7 @@ static int buf_write_bytes(struct bw_info *ip) /// @return true for an error, false when it's OK. static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL { - char_u *p = *pp; + char_u *p = *pp; bool error = false; int cc; @@ -4043,12 +4164,13 @@ static bool ucs2bytes(unsigned c, char_u **pp, int flags) FUNC_ATTR_NONNULL_ALL *p++ = (c >> 8); *p++ = c; } - } else { /* Latin1 */ + } else { // Latin1 if (c >= 0x100) { error = true; *p++ = 0xBF; - } else + } else { *p++ = c; + } } *pp = p; @@ -4103,25 +4225,29 @@ static int get_fio_flags(const char_u *name) prop = enc_canon_props(name); if (prop & ENC_UNICODE) { if (prop & ENC_2BYTE) { - if (prop & ENC_ENDIAN_L) + if (prop & ENC_ENDIAN_L) { return FIO_UCS2 | FIO_ENDIAN_L; + } return FIO_UCS2; } if (prop & ENC_4BYTE) { - if (prop & ENC_ENDIAN_L) + if (prop & ENC_ENDIAN_L) { return FIO_UCS4 | FIO_ENDIAN_L; + } return FIO_UCS4; } if (prop & ENC_2WORD) { - if (prop & ENC_ENDIAN_L) + if (prop & ENC_ENDIAN_L) { return FIO_UTF16 | FIO_ENDIAN_L; + } return FIO_UTF16; } return FIO_UTF8; } - if (prop & ENC_LATIN1) + if (prop & ENC_LATIN1) { return FIO_LATIN1; - /* must be ENC_DBCS, requires iconv() */ + } + // must be ENC_DBCS, requires iconv() return 0; } @@ -4135,34 +4261,37 @@ static int get_fio_flags(const char_u *name) */ static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags) { - char *name = NULL; + char *name = NULL; int len = 2; if (p[0] == 0xef && p[1] == 0xbb && size >= 3 && p[2] == 0xbf && (flags == FIO_ALL || flags == FIO_UTF8 || flags == 0)) { - name = "utf-8"; /* EF BB BF */ + name = "utf-8"; // EF BB BF len = 3; } else if (p[0] == 0xff && p[1] == 0xfe) { if (size >= 4 && p[2] == 0 && p[3] == 0 && (flags == FIO_ALL || flags == (FIO_UCS4 | FIO_ENDIAN_L))) { - name = "ucs-4le"; /* FF FE 00 00 */ + name = "ucs-4le"; // FF FE 00 00 len = 4; - } else if (flags == (FIO_UCS2 | FIO_ENDIAN_L)) - name = "ucs-2le"; /* FF FE */ - else if (flags == FIO_ALL || flags == (FIO_UTF16 | FIO_ENDIAN_L)) - /* utf-16le is preferred, it also works for ucs-2le text */ - name = "utf-16le"; /* FF FE */ + } else if (flags == (FIO_UCS2 | FIO_ENDIAN_L)) { + name = "ucs-2le"; // FF FE + } else if (flags == FIO_ALL || + flags == (FIO_UTF16 | FIO_ENDIAN_L)) { + // utf-16le is preferred, it also works for ucs-2le text + name = "utf-16le"; // FF FE + } } else if (p[0] == 0xfe && p[1] == 0xff && (flags == FIO_ALL || flags == FIO_UCS2 || flags == FIO_UTF16)) { - /* Default to utf-16, it works also for ucs-2 text. */ - if (flags == FIO_UCS2) - name = "ucs-2"; /* FE FF */ - else - name = "utf-16"; /* FE FF */ + // Default to utf-16, it works also for ucs-2 text. + if (flags == FIO_UCS2) { + name = "ucs-2"; // FE FF + } else { + name = "utf-16"; // FE FF + } } else if (size >= 4 && p[0] == 0 && p[1] == 0 && p[2] == 0xfe && p[3] == 0xff && (flags == FIO_ALL || flags == FIO_UCS4)) { - name = "ucs-4"; /* 00 00 FE FF */ + name = "ucs-4"; // 00 00 FE FF len = 4; } @@ -4177,15 +4306,16 @@ static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags) static int make_bom(char_u *buf, char_u *name) { int flags; - char_u *p; + char_u *p; flags = get_fio_flags(name); - /* Can't put a BOM in a non-Unicode file. */ - if (flags == FIO_LATIN1 || flags == 0) + // Can't put a BOM in a non-Unicode file. + if (flags == FIO_LATIN1 || flags == 0) { return 0; + } - if (flags == FIO_UTF8) { /* UTF-8 */ + if (flags == FIO_UTF8) { // UTF-8 buf[0] = 0xef; buf[1] = 0xbb; buf[2] = 0xbf; @@ -4205,7 +4335,7 @@ static int make_bom(char_u *buf, char_u *name) /// name. void shorten_buf_fname(buf_T *buf, char_u *dirname, int force) { - char_u *p; + char_u *p; if (buf->b_fname != NULL && !bt_nofile(buf) @@ -4234,14 +4364,14 @@ void shorten_fnames(int force) os_dirname(dirname, MAXPATHL); FOR_ALL_BUFFERS(buf) { - shorten_buf_fname(buf, dirname, force); + shorten_buf_fname(buf, dirname, force); // Always make the swap file name a full path, a "nofile" buffer may // also have a swap file. mf_fullname(buf->b_ml.ml_mfp); } status_redraw_all(); - redraw_tabline = TRUE; + redraw_tabline = true; } /// Get new filename ended by given extension. @@ -4507,11 +4637,11 @@ int vim_rename(const char_u *from, const char_u *to) int fd_in; int fd_out; int n; - char *errmsg = NULL; - char *buffer; + char *errmsg = NULL; + char *buffer; long perm; #ifdef HAVE_ACL - vim_acl_T acl; /* ACL from original file */ + vim_acl_T acl; // ACL from original file #endif bool use_tmp_file = false; @@ -4551,8 +4681,9 @@ int vim_rename(const char_u *from, const char_u *to) * Find a name that doesn't exist and is in the same directory. * Rename "from" to "tempname" and then rename "tempname" to "to". */ - if (STRLEN(from) >= MAXPATHL - 5) + if (STRLEN(from) >= MAXPATHL - 5) { return -1; + } STRCPY(tempname, from); for (n = 123; n < 99999; n++) { char * tail = (char *)path_tail(tempname); @@ -4587,8 +4718,9 @@ int vim_rename(const char_u *from, const char_u *to) /* * First try a normal rename, return if it works. */ - if (os_rename(from, to) == OK) + if (os_rename(from, to) == OK) { return 0; + } /* * Rename() failed, try copying the file. @@ -4606,9 +4738,9 @@ int vim_rename(const char_u *from, const char_u *to) return -1; } - /* Create the new file with same permissions as the original. */ + // Create the new file with same permissions as the original. fd_out = os_open((char *)to, - O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, (int)perm); + O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, (int)perm); if (fd_out < 0) { close(fd_in); #ifdef HAVE_ACL @@ -4629,16 +4761,18 @@ int vim_rename(const char_u *from, const char_u *to) return -1; } - while ((n = read_eintr(fd_in, buffer, BUFSIZE)) > 0) + while ((n = read_eintr(fd_in, buffer, BUFSIZE)) > 0) { if (write_eintr(fd_out, buffer, n) != n) { errmsg = _("E208: Error writing to \"%s\""); break; } + } xfree(buffer); close(fd_in); - if (close(fd_out) < 0) + if (close(fd_out) < 0) { errmsg = _("E209: Error closing \"%s\""); + } if (n < 0) { errmsg = _("E210: Error reading \"%s\""); to = from; @@ -4660,23 +4794,23 @@ int vim_rename(const char_u *from, const char_u *to) static int already_warned = FALSE; -// Check if any not hidden buffer has been changed. -// Postpone the check if there are characters in the stuff buffer, a global -// command is being executed, a mapping is being executed or an autocommand is -// busy. -// Returns TRUE if some message was written (screen should be redrawn and -// cursor positioned). -int -check_timestamps( - int focus // called for GUI focus event -) +/// Check if any not hidden buffer has been changed. +/// Postpone the check if there are characters in the stuff buffer, a global +/// command is being executed, a mapping is being executed or an autocommand is +/// busy. +/// +/// @param focus called for GUI focus event +/// +/// @return TRUE if some message was written (screen should be redrawn and cursor positioned). +int check_timestamps(int focus) { int didit = 0; /* Don't check timestamps while system() or another low-level function may * cause us to lose and gain focus. */ - if (no_check_timestamps > 0) + if (no_check_timestamps > 0) { return FALSE; + } /* Avoid doing a check twice. The OK/Reload dialog can cause a focus * event and we would keep on checking if the file is steadily growing. @@ -4687,8 +4821,8 @@ check_timestamps( } if (!stuff_empty() || global_busy || !typebuf_typed() - || autocmd_busy || curbuf_lock > 0 || allbuf_lock > 0 - ) { + || autocmd_busy || curbuf->b_ro_locked > 0 || + allbuf_lock > 0) { need_check_timestamps = true; // check later } else { no_wait_return++; @@ -4728,12 +4862,12 @@ check_timestamps( */ static int move_lines(buf_T *frombuf, buf_T *tobuf) { - buf_T *tbuf = curbuf; + buf_T *tbuf = curbuf; int retval = OK; linenr_T lnum; - char_u *p; + char_u *p; - /* Copy the lines in "frombuf" to "tobuf". */ + // Copy the lines in "frombuf" to "tobuf". curbuf = tobuf; for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; lnum++) { p = vim_strsave(ml_get_buf(frombuf, lnum, false)); @@ -4745,7 +4879,7 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf) xfree(p); } - /* Delete all the lines in "frombuf". */ + // Delete all the lines in "frombuf". if (retval != FAIL) { curbuf = frombuf; for (lnum = curbuf->b_ml.ml_line_count; lnum > 0; lnum--) { @@ -4773,17 +4907,17 @@ int buf_check_timestamp(buf_T *buf) FUNC_ATTR_NONNULL_ALL { int retval = 0; - char_u *path; - char *mesg = NULL; - char *mesg2 = ""; + char_u *path; + char *mesg = NULL; + char *mesg2 = ""; bool helpmesg = false; bool reload = false; bool can_reload = false; uint64_t orig_size = buf->b_orig_size; int orig_mode = buf->b_orig_mode; static bool busy = false; - char_u *s; - char *reason; + char_u *s; + char *reason; bufref_T bufref; set_bufref(&bufref, buf); @@ -4796,9 +4930,9 @@ int buf_check_timestamp(buf_T *buf) || buf->b_ml.ml_mfp == NULL || !bt_normal(buf) || buf->b_saving - || busy - ) + || busy) { return 0; + } FileInfo file_info; bool file_info_ok; @@ -4884,24 +5018,22 @@ int buf_check_timestamp(buf_T *buf) // changed. if (reason[2] == 'n') { mesg = _( - "W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as well"); + "W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as well"); mesg2 = _("See \":help W12\" for more info."); } else if (reason[1] == 'h') { - mesg = _( - "W11: Warning: File \"%s\" has changed since editing started"); + mesg = _("W11: Warning: File \"%s\" has changed since editing started"); mesg2 = _("See \":help W11\" for more info."); } else if (*reason == 'm') { - mesg = _( - "W16: Warning: Mode of file \"%s\" has changed since editing started"); + mesg = _("W16: Warning: Mode of file \"%s\" has changed since editing started"); mesg2 = _("See \":help W16\" for more info."); - } else + } else { /* Only timestamp changed, store it to avoid a warning * in check_mtime() later. */ buf->b_mtime_read = buf->b_mtime; + } } } } - } else if ((buf->b_flags & BF_NEW) && !(buf->b_flags & BF_NEW_W) && os_path_exists(buf->b_ffname)) { retval = 1; @@ -4926,8 +5058,8 @@ int buf_check_timestamp(buf_T *buf) xstrlcat(tbuf, "\n", tbuf_len - 1); xstrlcat(tbuf, mesg2, tbuf_len - 1); } - if (do_dialog(VIM_WARNING, (char_u *) _("Warning"), (char_u *) tbuf, - (char_u *) _("&OK\n&Load File"), 1, NULL, true) == 2) { + if (do_dialog(VIM_WARNING, (char_u *)_("Warning"), (char_u *)tbuf, + (char_u *)_("&OK\n&Load File"), 1, NULL, true) == 2) { reload = true; } } else if (State > NORMAL_BUSY || (State & CMDLINE) || already_warned) { @@ -4963,17 +5095,14 @@ int buf_check_timestamp(buf_T *buf) } if (reload) { - /* Reload the buffer. */ + // Reload the buffer. buf_reload(buf, orig_mode); if (buf->b_p_udf && buf->b_ffname != NULL) { char_u hash[UNDO_HASH_SIZE]; - buf_T *save_curbuf = curbuf; - /* Any existing undo file is unusable, write it now. */ - curbuf = buf; - u_compute_hash(hash); - u_write_undo(NULL, FALSE, buf, hash); - curbuf = save_curbuf; + // Any existing undo file is unusable, write it now. + u_compute_hash(buf, hash); + u_write_undo(NULL, false, buf, hash); } } @@ -4997,13 +5126,13 @@ void buf_reload(buf_T *buf, int orig_mode) pos_T old_cursor; linenr_T old_topline; int old_ro = buf->b_p_ro; - buf_T *savebuf; + buf_T *savebuf; bufref_T bufref; int saved = OK; aco_save_T aco; int flags = READ_NEW; - /* set curwin/curbuf for "buf" and save some things */ + // set curwin/curbuf for "buf" and save some things aucmd_prepbuf(&aco, buf); // We only want to read the text from the file, not reset the syntax @@ -5015,10 +5144,10 @@ void buf_reload(buf_T *buf, int orig_mode) old_topline = curwin->w_topline; if (p_ur < 0 || curbuf->b_ml.ml_line_count <= p_ur) { - /* Save all the text, so that the reload can be undone. - * Sync first so that this is a separate undo-able action. */ - u_sync(FALSE); - saved = u_savecommon(0, curbuf->b_ml.ml_line_count + 1, 0, TRUE); + // Save all the text, so that the reload can be undone. + // Sync first so that this is a separate undo-able action. + u_sync(false); + saved = u_savecommon(curbuf, 0, curbuf->b_ml.ml_line_count + 1, 0, true); flags |= READ_KEEP_UNDO; } @@ -5027,14 +5156,14 @@ void buf_reload(buf_T *buf, int orig_mode) // buffer contents. But if reading the file fails we should keep // the old contents. Can't use memory only, the file might be // too big. Use a hidden buffer to move the buffer contents to. - if (BUFEMPTY() || saved == FAIL) { + if (buf_is_empty(curbuf) || saved == FAIL) { savebuf = NULL; } else { // Allocate a buffer without putting it in the buffer list. savebuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); set_bufref(&bufref, savebuf); if (savebuf != NULL && buf == curbuf) { - /* Open the memline. */ + // Open the memline. curbuf = savebuf; curwin->w_buffer = savebuf; saved = ml_open(curbuf); @@ -5044,7 +5173,7 @@ void buf_reload(buf_T *buf, int orig_mode) if (savebuf == NULL || saved == FAIL || buf != curbuf || move_lines(buf, savebuf) == FAIL) { EMSG2(_("E462: Could not prepare for reloading \"%s\""), - buf->b_fname); + buf->b_fname); saved = FAIL; } } @@ -5060,7 +5189,7 @@ void buf_reload(buf_T *buf, int orig_mode) if (savebuf != NULL && bufref_valid(&bufref) && buf == curbuf) { // Put the text back from the save buffer. First // delete any lines that readfile() added. - while (!BUFEMPTY()) { + while (!buf_is_empty(curbuf)) { if (ml_delete(buf->b_ml.ml_line_count, false) == FAIL) { break; } @@ -5087,21 +5216,22 @@ void buf_reload(buf_T *buf, int orig_mode) wipe_buffer(savebuf, false); } - /* Invalidate diff info if necessary. */ + // Invalidate diff info if necessary. diff_invalidate(curbuf); /* Restore the topline and cursor position and check it (lines may * have been removed). */ - if (old_topline > curbuf->b_ml.ml_line_count) + if (old_topline > curbuf->b_ml.ml_line_count) { curwin->w_topline = curbuf->b_ml.ml_line_count; - else + } else { curwin->w_topline = old_topline; + } curwin->w_cursor = old_cursor; check_cursor(); update_topline(curwin); keep_filetype = false; - /* Update folds unless they are defined manually. */ + // Update folds unless they are defined manually. FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == curwin->w_buffer && !foldmethodIsManual(wp)) { @@ -5112,15 +5242,16 @@ void buf_reload(buf_T *buf, int orig_mode) /* If the mode didn't change and 'readonly' was set, keep the old * value; the user probably used the ":view" command. But don't * reset it, might have had a read error. */ - if (orig_mode == curbuf->b_orig_mode) + if (orig_mode == curbuf->b_orig_mode) { curbuf->b_p_ro |= old_ro; + } - /* Modelines must override settings done by autocommands. */ + // Modelines must override settings done by autocommands. do_modelines(0); - /* restore curwin/curbuf and a few other things */ + // restore curwin/curbuf and a few other things aucmd_restbuf(&aco); - /* Careful: autocommands may have made "buf" invalid! */ + // Careful: autocommands may have made "buf" invalid! } void buf_store_file_info(buf_T *buf, FileInfo *file_info) @@ -5137,8 +5268,9 @@ void buf_store_file_info(buf_T *buf, FileInfo *file_info) */ void write_lnum_adjust(linenr_T offset) { - if (curbuf->b_no_eol_lnum != 0) /* only if there is a missing eol */ + if (curbuf->b_no_eol_lnum != 0) { // only if there is a missing eol curbuf->b_no_eol_lnum += offset; + } } #if defined(BACKSLASH_IN_FILENAME) @@ -5146,7 +5278,7 @@ void write_lnum_adjust(linenr_T offset) /// unless when it looks like a URL. void forward_slash(char_u *fname) { - char_u *p; + char_u *p; if (path_with_url((const char *)fname)) { return; @@ -5321,18 +5453,19 @@ char_u *vim_tempname(void) /// @param allow_dirs Allow matching with dir /// /// @return true if there is a match, false otherwise -bool match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname, - char_u *sfname, char_u *tail, int allow_dirs) +bool match_file_pat(char_u *pattern, regprog_T **prog, char_u *fname, char_u *sfname, char_u *tail, + int allow_dirs) { regmatch_T regmatch; bool result = false; - regmatch.rm_ic = p_fic; /* ignore case if 'fileignorecase' is set */ + regmatch.rm_ic = p_fic; // ignore case if 'fileignorecase' is set { - if (prog != NULL) + if (prog != NULL) { regmatch.regprog = *prog; - else + } else { regmatch.regprog = vim_regcomp(pattern, RE_MAGIC); + } } /* @@ -5371,11 +5504,11 @@ bool match_file_list(char_u *list, char_u *sfname, char_u *ffname) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 3) { char_u buf[100]; - char_u *tail; - char_u *regpat; + char_u *tail; + char_u *regpat; char allow_dirs; bool match; - char_u *p; + char_u *p; tail = path_tail(sfname); @@ -5402,25 +5535,27 @@ bool match_file_list(char_u *list, char_u *sfname, char_u *ffname) /// allow_dirs, otherwise FALSE is put there -- webb. /// Handle backslashes before special characters, like "\*" and "\ ". /// -/// Returns NULL on failure. -char_u * file_pat_to_reg_pat( - const char_u *pat, - const char_u *pat_end, // first char after pattern or NULL - char *allow_dirs, // Result passed back out in here - int no_bslash // Don't use a backward slash as pathsep -) +/// @param pat_end first char after pattern or NULL +/// @param allow_dirs Result passed back out in here +/// @param no_bslash Don't use a backward slash as pathsep +/// +/// @return NULL on failure. +char_u * file_pat_to_reg_pat(const char_u *pat, const char_u *pat_end, char *allow_dirs, + int no_bslash) FUNC_ATTR_NONNULL_ARG(1) { const char_u *endp; - char_u *reg_pat; + char_u *reg_pat; const char_u *p; int nested = 0; int add_dollar = TRUE; - if (allow_dirs != NULL) + if (allow_dirs != NULL) { *allow_dirs = FALSE; - if (pat_end == NULL) + } + if (pat_end == NULL) { pat_end = pat + STRLEN(pat); + } if (pat_end == pat) { return (char_u *)xstrdup("^$"); @@ -5436,12 +5571,12 @@ char_u * file_pat_to_reg_pat( case '{': case '}': case '~': - size += 2; /* extra backslash */ + size += 2; // extra backslash break; #ifdef BACKSLASH_IN_FILENAME case '\\': case '/': - size += 4; /* could become "[\/]" */ + size += 4; // could become "[\/]" break; #endif default: @@ -5453,11 +5588,13 @@ char_u * file_pat_to_reg_pat( size_t i = 0; - if (pat[0] == '*') - while (pat[0] == '*' && pat < pat_end - 1) + if (pat[0] == '*') { + while (pat[0] == '*' && pat < pat_end - 1) { pat++; - else + } + } else { reg_pat[i++] = '^'; + } endp = pat_end - 1; if (endp >= pat && *endp == '*') { while (endp - pat > 0 && *endp == '*') { @@ -5470,8 +5607,9 @@ char_u * file_pat_to_reg_pat( case '*': reg_pat[i++] = '.'; reg_pat[i++] = '*'; - while (p[1] == '*') /* "**" matches like "*" */ + while (p[1] == '*') { // "**" matches like "*" ++p; + } break; case '.': case '~': @@ -5482,8 +5620,9 @@ char_u * file_pat_to_reg_pat( reg_pat[i++] = '.'; break; case '\\': - if (p[1] == NUL) + if (p[1] == NUL) { break; + } #ifdef BACKSLASH_IN_FILENAME if (!no_bslash) { /* translate: @@ -5498,8 +5637,9 @@ char_u * file_pat_to_reg_pat( reg_pat[i++] = '\\'; reg_pat[i++] = '/'; reg_pat[i++] = ']'; - if (allow_dirs != NULL) + if (allow_dirs != NULL) { *allow_dirs = TRUE; + } break; } } @@ -5532,8 +5672,9 @@ char_u * file_pat_to_reg_pat( #ifdef BACKSLASH_IN_FILENAME && (!no_bslash || *p != '\\') #endif - ) + ) { *allow_dirs = TRUE; + } reg_pat[i++] = '\\'; reg_pat[i++] = *p; } @@ -5544,8 +5685,9 @@ char_u * file_pat_to_reg_pat( reg_pat[i++] = '\\'; reg_pat[i++] = '/'; reg_pat[i++] = ']'; - if (allow_dirs != NULL) + if (allow_dirs != NULL) { *allow_dirs = TRUE; + } break; #endif case '{': @@ -5562,8 +5704,9 @@ char_u * file_pat_to_reg_pat( if (nested) { reg_pat[i++] = '\\'; reg_pat[i++] = '|'; - } else + } else { reg_pat[i++] = ','; + } break; default: if (allow_dirs != NULL && vim_ispathsep(*p)) { @@ -5573,8 +5716,9 @@ char_u * file_pat_to_reg_pat( break; } } - if (add_dollar) + if (add_dollar) { reg_pat[i++] = '$'; + } reg_pat[i] = NUL; if (nested != 0) { if (nested < 0) { @@ -5598,8 +5742,9 @@ long read_eintr(int fd, void *buf, size_t bufsize) for (;; ) { ret = read(fd, buf, bufsize); - if (ret >= 0 || errno != EINTR) + if (ret >= 0 || errno != EINTR) { break; + } } return ret; } @@ -5618,10 +5763,12 @@ long write_eintr(int fd, void *buf, size_t bufsize) while (ret < (long)bufsize) { wlen = write(fd, (char *)buf + ret, bufsize - ret); if (wlen < 0) { - if (errno != EINTR) + if (errno != EINTR) { break; - } else + } + } else { ret += wlen; + } } return ret; } diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 5032646d7e..567cf9c8c3 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -7,12 +7,11 @@ * fold.c: code for folding */ -#include <string.h> #include <inttypes.h> +#include <string.h> -#include "nvim/vim.h" #include "nvim/ascii.h" -#include "nvim/fold.h" +#include "nvim/buffer_updates.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -20,26 +19,28 @@ #include "nvim/eval.h" #include "nvim/ex_docmd.h" #include "nvim/ex_session.h" +#include "nvim/extmark.h" +#include "nvim/fold.h" #include "nvim/func_attr.h" +#include "nvim/garray.h" #include "nvim/indent.h" -#include "nvim/buffer_updates.h" -#include "nvim/extmark.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/garray.h" #include "nvim/move.h" +#include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/plines.h" #include "nvim/screen.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/undo.h" -#include "nvim/ops.h" +#include "nvim/vim.h" -/* local declarations. {{{1 */ -/* typedef fold_T {{{2 */ +// local declarations. {{{1 +// typedef fold_T {{{2 /* * The toplevel folds for each window are stored in the w_folds growarray. * Each toplevel fold can contain an array of second level folds in the @@ -57,20 +58,20 @@ typedef struct { // folds too } fold_T; -#define FD_OPEN 0 /* fold is open (nested ones can be closed) */ -#define FD_CLOSED 1 /* fold is closed */ -#define FD_LEVEL 2 /* depends on 'foldlevel' (nested folds too) */ +#define FD_OPEN 0 // fold is open (nested ones can be closed) +#define FD_CLOSED 1 // fold is closed +#define FD_LEVEL 2 // depends on 'foldlevel' (nested folds too) -#define MAX_LEVEL 20 /* maximum fold depth */ +#define MAX_LEVEL 20 // maximum fold depth -/* Define "fline_T", passed to get fold level for a line. {{{2 */ +// Define "fline_T", passed to get fold level for a line. {{{2 typedef struct { - win_T *wp; /* window */ - linenr_T lnum; /* current line number */ - linenr_T off; /* offset between lnum and real line number */ - linenr_T lnum_save; /* line nr used by foldUpdateIEMSRecurse() */ - int lvl; /* current level (-1 for undefined) */ - int lvl_next; /* level used for next line */ + win_T *wp; // window + linenr_T lnum; // current line number + linenr_T off; // offset between lnum and real line number + linenr_T lnum_save; // line nr used by foldUpdateIEMSRecurse() + int lvl; // current level (-1 for undefined) + int lvl_next; // level used for next line int start; /* number of folds that are forced to start at this line. */ int end; /* level of fold that is forced to end below @@ -82,10 +83,10 @@ typedef struct { // Flag is set when redrawing is needed. static bool fold_changed; -/* Function used by foldUpdateIEMSRecurse */ +// Function used by foldUpdateIEMSRecurse typedef void (*LevelGetter)(fline_T *); -/* static functions {{{2 */ +// static functions {{{2 #ifdef INCLUDE_GENERATED_DECLARATIONS # include "fold.c.generated.h" @@ -109,17 +110,17 @@ static linenr_T invalid_bot = (linenr_T)0; static linenr_T prev_lnum = 0; static int prev_lnum_lvl = -1; -/* Flags used for "done" argument of setManualFold. */ +// Flags used for "done" argument of setManualFold. #define DONE_NOTHING 0 -#define DONE_ACTION 1 /* did close or open a fold */ -#define DONE_FOLD 2 /* did find a fold */ +#define DONE_ACTION 1 // did close or open a fold +#define DONE_FOLD 2 // did find a fold static size_t foldstartmarkerlen; static char_u *foldendmarker; static size_t foldendmarkerlen; -/* Exported folding functions. {{{1 */ -/* copyFoldingState() {{{2 */ +// Exported folding functions. {{{1 +// copyFoldingState() {{{2 /* * Copy that folding state from window "wp_from" to window "wp_to". */ @@ -130,18 +131,18 @@ void copyFoldingState(win_T *wp_from, win_T *wp_to) cloneFoldGrowArray(&wp_from->w_folds, &wp_to->w_folds); } -/* hasAnyFolding() {{{2 */ +// hasAnyFolding() {{{2 /* * Return TRUE if there may be folded lines in the current window. */ int hasAnyFolding(win_T *win) { - /* very simple now, but can become more complex later */ + // very simple now, but can become more complex later return !win->w_buffer->terminal && win->w_p_fen && (!foldmethodIsManual(win) || !GA_EMPTY(&win->w_folds)); } -/* hasFolding() {{{2 */ +// hasFolding() {{{2 /* * Return TRUE if line "lnum" in the current window is part of a closed * fold. @@ -162,20 +163,14 @@ bool hasFolding(linenr_T lnum, linenr_T *firstp, linenr_T *lastp) /// @param[out] infop where to store fold info /// /// @return true if range contains folds -bool hasFoldingWin( - win_T *const win, - const linenr_T lnum, - linenr_T *const firstp, - linenr_T *const lastp, - const bool cache, - foldinfo_T *const infop -) +bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp, + linenr_T *const lastp, const bool cache, foldinfo_T *const infop) { bool had_folded = false; linenr_T first = 0; linenr_T last = 0; linenr_T lnum_rel = lnum; - fold_T *fp; + fold_T *fp; int level = 0; bool use_level = false; bool maybe_small = false; @@ -185,8 +180,9 @@ bool hasFoldingWin( // Return quickly when there is no folding at all in this window. if (!hasAnyFolding(win)) { - if (infop != NULL) + if (infop != NULL) { infop->fi_level = 0; + } return false; } @@ -209,21 +205,23 @@ bool hasFoldingWin( */ garray_T *gap = &win->w_folds; for (;; ) { - if (!foldFind(gap, lnum_rel, &fp)) + if (!foldFind(gap, lnum_rel, &fp)) { break; + } - /* Remember lowest level of fold that starts in "lnum". */ - if (lnum_rel == fp->fd_top && low_level == 0) + // Remember lowest level of fold that starts in "lnum". + if (lnum_rel == fp->fd_top && low_level == 0) { low_level = level + 1; + } first += fp->fd_top; last += fp->fd_top; - /* is this fold closed? */ + // is this fold closed? had_folded = check_closed(win, fp, &use_level, level, - &maybe_small, lnum - lnum_rel); + &maybe_small, lnum - lnum_rel); if (had_folded) { - /* Fold closed: Set last and quit loop. */ + // Fold closed: Set last and quit loop. last += fp->fd_len - 1; break; } @@ -248,10 +246,12 @@ bool hasFoldingWin( if (last > win->w_buffer->b_ml.ml_line_count) { last = win->w_buffer->b_ml.ml_line_count; } - if (lastp != NULL) + if (lastp != NULL) { *lastp = last; - if (firstp != NULL) + } + if (firstp != NULL) { *firstp = first; + } if (infop != NULL) { infop->fi_level = level + 1; infop->fi_lnum = first; @@ -260,7 +260,7 @@ bool hasFoldingWin( return true; } -/* foldLevel() {{{2 */ +// foldLevel() {{{2 /* * Return fold level at line number "lnum" in the current window. */ @@ -268,16 +268,18 @@ int foldLevel(linenr_T lnum) { /* While updating the folds lines between invalid_top and invalid_bot have * an undefined fold level. Otherwise update the folds first. */ - if (invalid_top == (linenr_T)0) + if (invalid_top == (linenr_T)0) { checkupdate(curwin); - else if (lnum == prev_lnum && prev_lnum_lvl >= 0) + } else if (lnum == prev_lnum && prev_lnum_lvl >= 0) { return prev_lnum_lvl; - else if (lnum >= invalid_top && lnum <= invalid_bot) + } else if (lnum >= invalid_top && lnum <= invalid_bot) { return -1; + } - /* Return quickly when there is no folding at all in this window. */ - if (!hasAnyFolding(curwin)) + // Return quickly when there is no folding at all in this window. + if (!hasAnyFolding(curwin)) { return 0; + } return foldLevelWin(curwin, lnum); } @@ -315,7 +317,7 @@ foldinfo_T fold_info(win_T *win, linenr_T lnum) return info; } -/* foldmethodIsManual() {{{2 */ +// foldmethodIsManual() {{{2 /* * Return TRUE if 'foldmethod' is "manual" */ @@ -324,7 +326,7 @@ int foldmethodIsManual(win_T *wp) return wp->w_p_fdm[3] == 'u'; } -/* foldmethodIsIndent() {{{2 */ +// foldmethodIsIndent() {{{2 /* * Return TRUE if 'foldmethod' is "indent" */ @@ -333,7 +335,7 @@ int foldmethodIsIndent(win_T *wp) return wp->w_p_fdm[0] == 'i'; } -/* foldmethodIsExpr() {{{2 */ +// foldmethodIsExpr() {{{2 /* * Return TRUE if 'foldmethod' is "expr" */ @@ -342,7 +344,7 @@ int foldmethodIsExpr(win_T *wp) return wp->w_p_fdm[1] == 'x'; } -/* foldmethodIsMarker() {{{2 */ +// foldmethodIsMarker() {{{2 /* * Return TRUE if 'foldmethod' is "marker" */ @@ -351,7 +353,7 @@ int foldmethodIsMarker(win_T *wp) return wp->w_p_fdm[2] == 'r'; } -/* foldmethodIsSyntax() {{{2 */ +// foldmethodIsSyntax() {{{2 /* * Return TRUE if 'foldmethod' is "syntax" */ @@ -360,7 +362,7 @@ int foldmethodIsSyntax(win_T *wp) return wp->w_p_fdm[0] == 's'; } -/* foldmethodIsDiff() {{{2 */ +// foldmethodIsDiff() {{{2 /* * Return TRUE if 'foldmethod' is "diff" */ @@ -377,7 +379,7 @@ void closeFold(pos_T pos, long count) setFoldRepeat(pos, count, false); } -/* closeFoldRecurse() {{{2 */ +// closeFoldRecurse() {{{2 /* * Close fold for current window at line "lnum" recursively. */ @@ -386,19 +388,15 @@ void closeFoldRecurse(pos_T pos) (void)setManualFold(pos, false, true, NULL); } -/* opFoldRange() {{{2 */ -/* - * Open or Close folds for current window in lines "first" to "last". - * Used for "zo", "zO", "zc" and "zC" in Visual mode. - */ -void -opFoldRange( - pos_T firstpos, - pos_T lastpos, - int opening, // TRUE to open, FALSE to close - int recurse, // TRUE to do it recursively - int had_visual // TRUE when Visual selection used -) +// opFoldRange() {{{2 +/// +/// Open or Close folds for current window in lines "first" to "last". +/// Used for "zo", "zO", "zc" and "zC" in Visual mode. +/// +/// @param opening TRUE to open, FALSE to close +/// @param recurse TRUE to do it recursively +/// @param had_visual TRUE when Visual selection used +void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, int had_visual) { int done = DONE_NOTHING; // avoid error messages linenr_T first = firstpos.lnum; @@ -411,8 +409,9 @@ opFoldRange( lnum_next = lnum; /* Opening one level only: next fold to open is after the one going to * be opened. */ - if (opening && !recurse) + if (opening && !recurse) { (void)hasFolding(lnum, NULL, &lnum_next); + } (void)setManualFold(temp, opening, recurse, &done); // Closing one level only: next line to close a fold is after just // closed fold. @@ -420,14 +419,16 @@ opFoldRange( (void)hasFolding(lnum, NULL, &lnum_next); } } - if (done == DONE_NOTHING) + if (done == DONE_NOTHING) { EMSG(_(e_nofold)); - /* Force a redraw to remove the Visual highlighting. */ - if (had_visual) + } + // Force a redraw to remove the Visual highlighting. + if (had_visual) { redraw_curbuf_later(INVERTED); + } } -/* openFold() {{{2 */ +// openFold() {{{2 /* * Open fold for current window at line "lnum". * Repeat "count" times. @@ -437,7 +438,7 @@ void openFold(pos_T pos, long count) setFoldRepeat(pos, count, true); } -/* openFoldRecurse() {{{2 */ +// openFoldRecurse() {{{2 /* * Open fold for current window at line "lnum" recursively. */ @@ -446,7 +447,7 @@ void openFoldRecurse(pos_T pos) (void)setManualFold(pos, true, true, NULL); } -/* foldOpenCursor() {{{2 */ +// foldOpenCursor() {{{2 /* * Open folds until the cursor line is not in a closed fold. */ @@ -455,7 +456,7 @@ void foldOpenCursor(void) int done; checkupdate(curwin); - if (hasAnyFolding(curwin)) + if (hasAnyFolding(curwin)) { for (;; ) { done = DONE_NOTHING; (void)setManualFold(curwin->w_cursor, true, false, &done); @@ -463,9 +464,10 @@ void foldOpenCursor(void) break; } } + } } -/* newFoldLevel() {{{2 */ +// newFoldLevel() {{{2 /* * Set new foldlevel for current window. */ @@ -488,7 +490,7 @@ void newFoldLevel(void) static void newFoldLevelWin(win_T *wp) { - fold_T *fp; + fold_T *fp; checkupdate(wp); if (wp->w_fold_manual) { @@ -496,62 +498,67 @@ static void newFoldLevelWin(win_T *wp) * manual open/close will then change the flags to FD_OPEN or * FD_CLOSED for those folds that don't use 'foldlevel'. */ fp = (fold_T *)wp->w_folds.ga_data; - for (int i = 0; i < wp->w_folds.ga_len; ++i) + for (int i = 0; i < wp->w_folds.ga_len; ++i) { fp[i].fd_flags = FD_LEVEL; + } wp->w_fold_manual = false; } changed_window_setting_win(wp); } -/* foldCheckClose() {{{2 */ +// foldCheckClose() {{{2 /* * Apply 'foldlevel' to all folds that don't contain the cursor. */ void foldCheckClose(void) { - if (*p_fcl != NUL) { /* can only be "all" right now */ + if (*p_fcl != NUL) { // can only be "all" right now checkupdate(curwin); if (checkCloseRec(&curwin->w_folds, curwin->w_cursor.lnum, - (int)curwin->w_p_fdl)) + (int)curwin->w_p_fdl)) { changed_window_setting(); + } } } -/* checkCloseRec() {{{2 */ +// checkCloseRec() {{{2 static int checkCloseRec(garray_T *gap, linenr_T lnum, int level) { - fold_T *fp; + fold_T *fp; int retval = FALSE; fp = (fold_T *)gap->ga_data; for (int i = 0; i < gap->ga_len; ++i) { - /* Only manually opened folds may need to be closed. */ + // Only manually opened folds may need to be closed. if (fp[i].fd_flags == FD_OPEN) { if (level <= 0 && (lnum < fp[i].fd_top || lnum >= fp[i].fd_top + fp[i].fd_len)) { fp[i].fd_flags = FD_LEVEL; retval = TRUE; - } else + } else { retval |= checkCloseRec(&fp[i].fd_nested, lnum - fp[i].fd_top, - level - 1); + level - 1); + } } } return retval; } -/* foldCreateAllowed() {{{2 */ +// foldCreateAllowed() {{{2 /* * Return TRUE if it's allowed to manually create or delete a fold. * Give an error message and return FALSE if not. */ int foldManualAllowed(int create) { - if (foldmethodIsManual(curwin) || foldmethodIsMarker(curwin)) + if (foldmethodIsManual(curwin) || foldmethodIsMarker(curwin)) { return TRUE; - if (create) + } + if (create) { EMSG(_("E350: Cannot create fold with current 'foldmethod'")); - else + } else { EMSG(_("E351: Cannot delete fold with current 'foldmethod'")); + } return FALSE; } @@ -560,8 +567,8 @@ int foldManualAllowed(int create) /// window. void foldCreate(win_T *wp, pos_T start, pos_T end) { - fold_T *fp; - garray_T *gap; + fold_T *fp; + garray_T *gap; garray_T fold_ga; int i; int cont; @@ -658,13 +665,14 @@ void foldCreate(win_T *wp, pos_T start, pos_T end) ((fold_T *)fold_ga.ga_data)[j].fd_top -= start_rel.lnum; } } - /* Move remaining entries to after the new fold. */ - if (i < gap->ga_len) + // Move remaining entries to after the new fold. + if (i < gap->ga_len) { memmove(fp + 1, (fold_T *)gap->ga_data + i, sizeof(fold_T) * (size_t)(gap->ga_len - i)); + } gap->ga_len = gap->ga_len + 1 - cont; - /* insert new fold */ + // insert new fold fp->fd_nested = fold_ga; fp->fd_top = start_rel.lnum; fp->fd_len = end_rel.lnum - start_rel.lnum + 1; @@ -692,16 +700,11 @@ void foldCreate(win_T *wp, pos_T start, pos_T end) /// @param end delete all folds from start to end when not 0 /// @param recursive delete recursively if true /// @param had_visual true when Visual selection used -void deleteFold( - win_T *const wp, - const linenr_T start, - const linenr_T end, - const int recursive, - const bool had_visual // true when Visual selection used -) -{ - fold_T *fp; - fold_T *found_fp = NULL; +void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const int recursive, + const bool had_visual) +{ + fold_T *fp; + fold_T *found_fp = NULL; linenr_T found_off = 0; bool maybe_small = false; int level = 0; @@ -719,9 +722,10 @@ void deleteFold( linenr_T lnum_off = 0; bool use_level = false; for (;; ) { - if (!foldFind(gap, lnum - lnum_off, &fp)) + if (!foldFind(gap, lnum - lnum_off, &fp)) { break; - /* lnum is inside this fold, remember info */ + } + // lnum is inside this fold, remember info found_ga = gap; found_fp = fp; found_off = lnum_off; @@ -732,7 +736,7 @@ void deleteFold( break; } - /* check nested folds */ + // check nested folds gap = &fp->fd_nested; lnum_off += fp->fd_top; ++level; @@ -790,7 +794,7 @@ void deleteFold( } } -/* clearFolding() {{{2 */ +// clearFolding() {{{2 /* * Remove all folding for window "win". */ @@ -800,7 +804,7 @@ void clearFolding(win_T *win) win->w_foldinvalid = false; } -/* foldUpdate() {{{2 */ +// foldUpdate() {{{2 /* * Update folds for changes in the buffer of a window. * Note that inserted/deleted lines must have already been taken care of by @@ -836,7 +840,7 @@ void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) || foldmethodIsSyntax(wp)) { int save_got_int = got_int; - /* reset got_int here, otherwise it won't work */ + // reset got_int here, otherwise it won't work got_int = FALSE; foldUpdateIEMS(wp, top, bot); got_int |= save_got_int; @@ -856,7 +860,7 @@ void foldUpdateAfterInsert(void) foldOpenCursor(); } -/* foldUpdateAll() {{{2 */ +// foldUpdateAll() {{{2 /* * Update all lines in a window for folding. * Used when a fold setting changes or after reloading the buffer. @@ -870,19 +874,17 @@ void foldUpdateAll(win_T *win) } // foldMoveTo() {{{2 -// -// If "updown" is false: Move to the start or end of the fold. -// If "updown" is true: move to fold at the same level. -// If not moved return FAIL. -int foldMoveTo( - const bool updown, - const int dir, // FORWARD or BACKWARD - const long count -) +/// +/// If "updown" is false: Move to the start or end of the fold. +/// If "updown" is true: move to fold at the same level. +/// @return FAIL if not moved. +/// +/// @param dir FORWARD or BACKWARD +int foldMoveTo(const bool updown, const int dir, const long count) { int retval = FAIL; linenr_T lnum; - fold_T *fp; + fold_T *fp; checkupdate(curwin); @@ -909,12 +911,14 @@ int foldMoveTo( /* When moving up, consider a fold above the cursor; when * moving down consider a fold below the cursor. */ if (dir == FORWARD) { - if (fp - (fold_T *)gap->ga_data >= gap->ga_len) + if (fp - (fold_T *)gap->ga_data >= gap->ga_len) { break; + } --fp; } else { - if (fp == (fold_T *)gap->ga_data) + if (fp == (fold_T *)gap->ga_data) { break; + } } /* don't look for contained folds, they will always move * the cursor too far. */ @@ -922,31 +926,34 @@ int foldMoveTo( } if (!last) { - /* Check if this fold is closed. */ + // Check if this fold is closed. if (check_closed(curwin, fp, &use_level, level, &maybe_small, lnum_off)) { last = true; } - /* "[z" and "]z" stop at closed fold */ - if (last && !updown) + // "[z" and "]z" stop at closed fold + if (last && !updown) { break; + } } if (updown) { if (dir == FORWARD) { - /* to start of next fold if there is one */ + // to start of next fold if there is one if (fp + 1 - (fold_T *)gap->ga_data < gap->ga_len) { lnum = fp[1].fd_top + lnum_off; - if (lnum > curwin->w_cursor.lnum) + if (lnum > curwin->w_cursor.lnum) { lnum_found = lnum; + } } } else { - /* to end of previous fold if there is one */ + // to end of previous fold if there is one if (fp > (fold_T *)gap->ga_data) { lnum = fp[-1].fd_top + lnum_off + fp[-1].fd_len - 1; - if (lnum < curwin->w_cursor.lnum) + if (lnum < curwin->w_cursor.lnum) { lnum_found = lnum; + } } } } else { @@ -954,37 +961,42 @@ int foldMoveTo( * nested folds. */ if (dir == FORWARD) { lnum = fp->fd_top + lnum_off + fp->fd_len - 1; - if (lnum > curwin->w_cursor.lnum) + if (lnum > curwin->w_cursor.lnum) { lnum_found = lnum; + } } else { lnum = fp->fd_top + lnum_off; - if (lnum < curwin->w_cursor.lnum) + if (lnum < curwin->w_cursor.lnum) { lnum_found = lnum; + } } } - if (last) + if (last) { break; + } - /* Check nested folds (if any). */ + // Check nested folds (if any). gap = &fp->fd_nested; lnum_off += fp->fd_top; ++level; } if (lnum_found != curwin->w_cursor.lnum) { - if (retval == FAIL) + if (retval == FAIL) { setpcmark(); + } curwin->w_cursor.lnum = lnum_found; curwin->w_cursor.col = 0; retval = OK; - } else + } else { break; + } } return retval; } -/* foldInitWin() {{{2 */ +// foldInitWin() {{{2 /* * Init the fold info in a new window. */ @@ -993,7 +1005,7 @@ void foldInitWin(win_T *new_win) ga_init(&new_win->w_folds, (int)sizeof(fold_T), 10); } -/* find_wl_entry() {{{2 */ +// find_wl_entry() {{{2 /* * Find an entry in the win->w_lines[] array for buffer line "lnum". * Only valid entries are considered (for entries where wl_valid is FALSE the @@ -1004,27 +1016,31 @@ int find_wl_entry(win_T *win, linenr_T lnum) { int i; - for (i = 0; i < win->w_lines_valid; ++i) + for (i = 0; i < win->w_lines_valid; ++i) { if (win->w_lines[i].wl_valid) { - if (lnum < win->w_lines[i].wl_lnum) + if (lnum < win->w_lines[i].wl_lnum) { return -1; - if (lnum <= win->w_lines[i].wl_lastlnum) + } + if (lnum <= win->w_lines[i].wl_lastlnum) { return i; + } } + } return -1; } -/* foldAdjustVisual() {{{2 */ +// foldAdjustVisual() {{{2 /* * Adjust the Visual area to include any fold at the start or end completely. */ void foldAdjustVisual(void) { - pos_T *start, *end; - char_u *ptr; + pos_T *start, *end; + char_u *ptr; - if (!VIsual_active || !hasAnyFolding(curwin)) + if (!VIsual_active || !hasAnyFolding(curwin)) { return; + } if (ltoreq(VIsual, curwin->w_cursor)) { start = &VIsual; @@ -1033,8 +1049,9 @@ void foldAdjustVisual(void) start = &curwin->w_cursor; end = &VIsual; } - if (hasFolding(start->lnum, &start->lnum, NULL)) + if (hasFolding(start->lnum, &start->lnum, NULL)) { start->col = 0; + } if (hasFolding(end->lnum, NULL, &end->lnum)) { ptr = ml_get(end->lnum); end->col = (colnr_T)STRLEN(ptr); @@ -1046,7 +1063,7 @@ void foldAdjustVisual(void) } } -/* cursor_foldstart() {{{2 */ +// cursor_foldstart() {{{2 /* * Move the cursor to the first line of a closed fold. */ @@ -1055,20 +1072,21 @@ void foldAdjustCursor(void) (void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); } -/* Internal functions for "fold_T" {{{1 */ -/* cloneFoldGrowArray() {{{2 */ +// Internal functions for "fold_T" {{{1 +// cloneFoldGrowArray() {{{2 /* * Will "clone" (i.e deep copy) a garray_T of folds. */ void cloneFoldGrowArray(garray_T *from, garray_T *to) { - fold_T *from_p; - fold_T *to_p; + fold_T *from_p; + fold_T *to_p; ga_init(to, from->ga_itemsize, from->ga_growsize); - if (GA_EMPTY(from)) + if (GA_EMPTY(from)) { return; + } ga_grow(to, from->ga_len); @@ -1087,7 +1105,7 @@ void cloneFoldGrowArray(garray_T *from, garray_T *to) } } -/* foldFind() {{{2 */ +// foldFind() {{{2 /* * Search for line "lnum" in folds of growarray "gap". * Set *fpp to the fold struct for the fold that contains "lnum" or @@ -1097,7 +1115,7 @@ void cloneFoldGrowArray(garray_T *from, garray_T *to) static bool foldFind(const garray_T *gap, linenr_T lnum, fold_T **fpp) { linenr_T low, high; - fold_T *fp; + fold_T *fp; if (gap->ga_len == 0) { *fpp = NULL; @@ -1114,14 +1132,14 @@ static bool foldFind(const garray_T *gap, linenr_T lnum, fold_T **fpp) high = gap->ga_len - 1; while (low <= high) { linenr_T i = (low + high) / 2; - if (fp[i].fd_top > lnum) - /* fold below lnum, adjust high */ + if (fp[i].fd_top > lnum) { + // fold below lnum, adjust high high = i - 1; - else if (fp[i].fd_top + fp[i].fd_len <= lnum) - /* fold above lnum, adjust low */ + } else if (fp[i].fd_top + fp[i].fd_len <= lnum) { + // fold above lnum, adjust low low = i + 1; - else { - /* lnum is inside this fold */ + } else { + // lnum is inside this fold *fpp = fp + i; return TRUE; } @@ -1130,23 +1148,24 @@ static bool foldFind(const garray_T *gap, linenr_T lnum, fold_T **fpp) return false; } -/* foldLevelWin() {{{2 */ +// foldLevelWin() {{{2 /* * Return fold level at line number "lnum" in window "wp". */ static int foldLevelWin(win_T *wp, linenr_T lnum) { - fold_T *fp; + fold_T *fp; linenr_T lnum_rel = lnum; int level = 0; - garray_T *gap; + garray_T *gap; - /* Recursively search for a fold that contains "lnum". */ + // Recursively search for a fold that contains "lnum". gap = &wp->w_folds; for (;; ) { - if (!foldFind(gap, lnum_rel, &fp)) + if (!foldFind(gap, lnum_rel, &fp)) { break; - /* Check nested folds. Line number is relative to containing fold. */ + } + // Check nested folds. Line number is relative to containing fold. gap = &fp->fd_nested; lnum_rel -= fp->fd_top; ++level; @@ -1155,19 +1174,19 @@ static int foldLevelWin(win_T *wp, linenr_T lnum) return level; } -/* checkupdate() {{{2 */ +// checkupdate() {{{2 /* * Check if the folds in window "wp" are invalid and update them if needed. */ static void checkupdate(win_T *wp) { if (wp->w_foldinvalid) { - foldUpdate(wp, (linenr_T)1, (linenr_T)MAXLNUM); /* will update all */ + foldUpdate(wp, (linenr_T)1, (linenr_T)MAXLNUM); // will update all wp->w_foldinvalid = false; } } -/* setFoldRepeat() {{{2 */ +// setFoldRepeat() {{{2 /* * Open or close fold for current window at line "lnum". * Repeat "count" times. @@ -1181,26 +1200,23 @@ static void setFoldRepeat(pos_T pos, long count, int do_open) done = DONE_NOTHING; (void)setManualFold(pos, do_open, false, &done); if (!(done & DONE_ACTION)) { - /* Only give an error message when no fold could be opened. */ - if (n == 0 && !(done & DONE_FOLD)) + // Only give an error message when no fold could be opened. + if (n == 0 && !(done & DONE_FOLD)) { EMSG(_(e_nofold)); + } break; } } } -/* setManualFold() {{{2 */ -/* - * Open or close the fold in the current window which contains "lnum". - * Also does this for other windows in diff mode when needed. - */ -static linenr_T -setManualFold( - pos_T pos, - int opening, // TRUE when opening, FALSE when closing - int recurse, // TRUE when closing/opening recursive - int *donep -) +// setManualFold() {{{2 +/// +/// Open or close the fold in the current window which contains "lnum". +/// Also does this for other windows in diff mode when needed. +/// +/// @param opening TRUE when opening, FALSE when closing +/// @param recurse TRUE when closing/opening recursive +static linenr_T setManualFold(pos_T pos, int opening, int recurse, int *donep) { linenr_T lnum = pos.lnum; if (foldmethodIsDiff(curwin) && curwin->w_p_scb) { @@ -1223,33 +1239,28 @@ setManualFold( return setManualFoldWin(curwin, lnum, opening, recurse, donep); } -/* setManualFoldWin() {{{2 */ -/* - * Open or close the fold in window "wp" which contains "lnum". - * "donep", when not NULL, points to flag that is set to DONE_FOLD when some - * fold was found and to DONE_ACTION when some fold was opened or closed. - * When "donep" is NULL give an error message when no fold was found for - * "lnum", but only if "wp" is "curwin". - * Return the line number of the next line that could be closed. - * It's only valid when "opening" is TRUE! - */ -static linenr_T -setManualFoldWin( - win_T *wp, - linenr_T lnum, - int opening, // TRUE when opening, FALSE when closing - int recurse, // TRUE when closing/opening recursive - int *donep -) -{ - fold_T *fp; - fold_T *fp2; - fold_T *found = NULL; +// setManualFoldWin() {{{2 +/// Open or close the fold in window "wp" which contains "lnum". +/// "donep", when not NULL, points to flag that is set to DONE_FOLD when some +/// fold was found and to DONE_ACTION when some fold was opened or closed. +/// When "donep" is NULL give an error message when no fold was found for +/// "lnum", but only if "wp" is "curwin". +/// +/// @param opening TRUE when opening, FALSE when closing +/// @param recurse TRUE when closing/opening recursive +/// +/// @return the line number of the next line that could be closed. +/// It's only valid when "opening" is TRUE! +static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recurse, int *donep) +{ + fold_T *fp; + fold_T *fp2; + fold_T *found = NULL; int j; int level = 0; int use_level = FALSE; int found_fold = FALSE; - garray_T *gap; + garray_T *gap; linenr_T next = MAXLNUM; linenr_T off = 0; int done = 0; @@ -1269,43 +1280,47 @@ setManualFoldWin( break; } - /* lnum is inside this fold */ + // lnum is inside this fold found_fold = TRUE; - /* If there is a following fold, continue there next time. */ - if (fp + 1 < (fold_T *)gap->ga_data + gap->ga_len) + // If there is a following fold, continue there next time. + if (fp + 1 < (fold_T *)gap->ga_data + gap->ga_len) { next = fp[1].fd_top + off; + } - /* Change from level-dependent folding to manual. */ + // Change from level-dependent folding to manual. if (use_level || fp->fd_flags == FD_LEVEL) { use_level = TRUE; - if (level >= wp->w_p_fdl) + if (level >= wp->w_p_fdl) { fp->fd_flags = FD_CLOSED; - else + } else { fp->fd_flags = FD_OPEN; + } fp2 = (fold_T *)fp->fd_nested.ga_data; - for (j = 0; j < fp->fd_nested.ga_len; ++j) + for (j = 0; j < fp->fd_nested.ga_len; ++j) { fp2[j].fd_flags = FD_LEVEL; + } } - /* Simple case: Close recursively means closing the fold. */ + // Simple case: Close recursively means closing the fold. if (!opening && recurse) { if (fp->fd_flags != FD_CLOSED) { done |= DONE_ACTION; fp->fd_flags = FD_CLOSED; } } else if (fp->fd_flags == FD_CLOSED) { - /* When opening, open topmost closed fold. */ + // When opening, open topmost closed fold. if (opening) { fp->fd_flags = FD_OPEN; done |= DONE_ACTION; - if (recurse) + if (recurse) { foldOpenNested(fp); + } } break; } - /* fold is open, check nested folds */ + // fold is open, check nested folds found = fp; gap = &fp->fd_nested; lnum -= fp->fd_top; @@ -1313,31 +1328,34 @@ setManualFoldWin( ++level; } if (found_fold) { - /* When closing and not recurse, close deepest open fold. */ + // When closing and not recurse, close deepest open fold. if (!opening && found != NULL) { found->fd_flags = FD_CLOSED; done |= DONE_ACTION; } wp->w_fold_manual = true; - if (done & DONE_ACTION) + if (done & DONE_ACTION) { changed_window_setting_win(wp); + } done |= DONE_FOLD; - } else if (donep == NULL && wp == curwin) + } else if (donep == NULL && wp == curwin) { EMSG(_(e_nofold)); + } - if (donep != NULL) + if (donep != NULL) { *donep |= done; + } return next; } -/* foldOpenNested() {{{2 */ +// foldOpenNested() {{{2 /* * Open all nested folds in fold "fpr" recursively. */ static void foldOpenNested(fold_T *fpr) { - fold_T *fp; + fold_T *fp; fp = (fold_T *)fpr->fd_nested.ga_data; for (int i = 0; i < fpr->fd_nested.ga_len; ++i) { @@ -1367,15 +1385,16 @@ static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx, int moved = fp->fd_nested.ga_len; ga_grow(gap, moved - 1); { - /* Get "fp" again, the array may have been reallocated. */ + // Get "fp" again, the array may have been reallocated. fp = (fold_T *)gap->ga_data + idx; // adjust fd_top and fd_flags for the moved folds fold_T *nfp = (fold_T *)fp->fd_nested.ga_data; for (int i = 0; i < moved; i++) { nfp[i].fd_top += fp->fd_top; - if (fp->fd_flags == FD_LEVEL) + if (fp->fd_flags == FD_LEVEL) { nfp[i].fd_flags = FD_LEVEL; + } if (fp->fd_small == kNone) { nfp[i].fd_small = kNone; } @@ -1394,7 +1413,7 @@ static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx, } } -/* deleteFoldRecurse() {{{2 */ +// deleteFoldRecurse() {{{2 /* * Delete nested folds in a fold. */ @@ -1404,7 +1423,7 @@ void deleteFoldRecurse(buf_T *bp, garray_T *gap) GA_DEEP_CLEAR(gap, fold_T, DELETE_FOLD_NESTED); } -/* foldMarkAdjust() {{{2 */ +// foldMarkAdjust() {{{2 /* * Update line numbers of folds for inserted/deleted lines. */ @@ -1412,8 +1431,9 @@ void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long { /* If deleting marks from line1 to line2, but not deleting all those * lines, set line2 so that only deleted lines have their folds removed. */ - if (amount == MAXLNUM && line2 >= line1 && line2 - line1 >= -amount_after) + if (amount == MAXLNUM && line2 >= line1 && line2 - line1 >= -amount_after) { line2 = line1 - amount_after - 1; + } /* If appending a line in Insert mode, it should be included in the fold * just above the line. */ if ((State & INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM) { @@ -1423,12 +1443,10 @@ void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long } // foldMarkAdjustRecurse() {{{2 -static void foldMarkAdjustRecurse( - win_T *wp, garray_T *gap, - linenr_T line1, linenr_T line2, long amount, long amount_after -) +static void foldMarkAdjustRecurse(win_T *wp, garray_T *gap, linenr_T line1, linenr_T line2, + long amount, long amount_after) { - fold_T *fp; + fold_T *fp; linenr_T last; linenr_T top; @@ -1438,12 +1456,13 @@ static void foldMarkAdjustRecurse( /* In Insert mode an inserted line at the top of a fold is considered part * of the fold, otherwise it isn't. */ - if ((State & INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM) + if ((State & INSERT) && amount == (linenr_T)1 && line2 == MAXLNUM) { top = line1 + 1; - else + } else { top = line1; + } - /* Find the fold containing or just below "line1". */ + // Find the fold containing or just below "line1". (void)foldFind(gap, line1, &fp); /* @@ -1452,26 +1471,28 @@ static void foldMarkAdjustRecurse( for (int i = (int)(fp - (fold_T *)gap->ga_data); i < gap->ga_len; ++i, ++fp) { /* * Check for these situations: - * 1 2 3 - * 1 2 3 - * line1 2 3 4 5 - * 2 3 4 5 - * 2 3 4 5 - * line2 2 3 4 5 - * 3 5 6 - * 3 5 6 + * 1 2 3 + * 1 2 3 + * line1 2 3 4 5 + * 2 3 4 5 + * 2 3 4 5 + * line2 2 3 4 5 + * 3 5 6 + * 3 5 6 */ - last = fp->fd_top + fp->fd_len - 1; /* last line of fold */ + last = fp->fd_top + fp->fd_len - 1; // last line of fold - /* 1. fold completely above line1: nothing to do */ - if (last < line1) + // 1. fold completely above line1: nothing to do + if (last < line1) { continue; + } - /* 6. fold below line2: only adjust for amount_after */ + // 6. fold below line2: only adjust for amount_after if (fp->fd_top > line2) { - if (amount_after == 0) + if (amount_after == 0) { break; + } fp->fd_top += amount_after; } else { if (fp->fd_top >= top && last <= line2) { @@ -1490,13 +1511,14 @@ static void foldMarkAdjustRecurse( foldMarkAdjustRecurse(wp, &fp->fd_nested, line1 - fp->fd_top, line2 - fp->fd_top, amount, amount_after); if (last <= line2) { - /* 2. fold contains line1, line2 is below fold */ - if (amount == MAXLNUM) + // 2. fold contains line1, line2 is below fold + if (amount == MAXLNUM) { fp->fd_len = line1 - fp->fd_top; - else + } else { fp->fd_len += amount; + } } else { - /* 3. fold contains line1 and line2 */ + // 3. fold contains line1 and line2 fp->fd_len += amount_after; } } else { @@ -1521,7 +1543,7 @@ static void foldMarkAdjustRecurse( } } -/* getDeepestNesting() {{{2 */ +// getDeepestNesting() {{{2 /* * Get the lowest 'foldlevel' value that makes the deepest nested fold in the * current window open. @@ -1536,13 +1558,14 @@ static int getDeepestNestingRecurse(garray_T *gap) { int level; int maxlevel = 0; - fold_T *fp; + fold_T *fp; fp = (fold_T *)gap->ga_data; for (int i = 0; i < gap->ga_len; ++i) { level = getDeepestNestingRecurse(&fp[i].fd_nested) + 1; - if (level > maxlevel) + if (level > maxlevel) { maxlevel = level; + } } return maxlevel; @@ -1557,14 +1580,8 @@ static int getDeepestNestingRecurse(garray_T *gap) /// @param[out] maybe_smallp true: outer this had fd_small == kNone /// @param lnum_off line number offset for fp->fd_top /// @return true if fold is closed -static bool check_closed( - win_T *const wp, - fold_T *const fp, - bool *const use_levelp, - const int level, - bool *const maybe_smallp, - const linenr_T lnum_off -) +static bool check_closed(win_T *const wp, fold_T *const fp, bool *const use_levelp, const int level, + bool *const maybe_smallp, const linenr_T lnum_off) { bool closed = false; @@ -1597,13 +1614,9 @@ static bool check_closed( // checkSmall() {{{2 /// Update fd_small field of fold "fp". -/// @param lnum_off offset for fp->fd_top -static void -checkSmall( - win_T *const wp, - fold_T *const fp, - const linenr_T lnum_off // offset for fp->fd_top -) +/// +/// @param lnum_off offset for fp->fd_top +static void checkSmall(win_T *const wp, fold_T *const fp, const linenr_T lnum_off) { if (fp->fd_small == kNone) { // Mark any nested folds to maybe-small @@ -1635,7 +1648,7 @@ static void setSmallMaybe(garray_T *gap) } } -/* foldCreateMarkers() {{{2 */ +// foldCreateMarkers() {{{2 /* * Create a fold from line "start" to line "end" (inclusive) in the current * window by adding markers. @@ -1664,17 +1677,16 @@ static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end) buf_updates_send_changes(buf, start.lnum, num_changed, num_changed, true); } -/* foldAddMarker() {{{2 */ +// foldAddMarker() {{{2 /* * Add "marker[markerlen]" in 'commentstring' to line "lnum". */ -static void foldAddMarker( - buf_T *buf, pos_T pos, const char_u *marker, size_t markerlen) +static void foldAddMarker(buf_T *buf, pos_T pos, const char_u *marker, size_t markerlen) { - char_u *cms = buf->b_p_cms; - char_u *line; - char_u *newline; - char_u *p = (char_u *)strstr((char *)buf->b_p_cms, "%s"); + char_u *cms = buf->b_p_cms; + char_u *line; + char_u *newline; + char_u *p = (char_u *)strstr((char *)buf->b_p_cms, "%s"); bool line_is_comment = false; linenr_T lnum = pos.lnum; @@ -1706,17 +1718,11 @@ static void foldAddMarker( } } -/* deleteFoldMarkers() {{{2 */ -/* - * Delete the markers for a fold, causing it to be deleted. - */ -static void -deleteFoldMarkers( - win_T *wp, - fold_T *fp, - int recursive, - linenr_T lnum_off // offset for fp->fd_top -) +// deleteFoldMarkers() {{{2 +/// Delete the markers for a fold, causing it to be deleted. +/// +/// @param lnum_off offset for fp->fd_top +static void deleteFoldMarkers(win_T *wp, fold_T *fp, int recursive, linenr_T lnum_off) { if (recursive) { for (int i = 0; i < fp->fd_nested.ga_len; i++) { @@ -1736,13 +1742,11 @@ deleteFoldMarkers( // Delete 'commentstring' if it matches. // If the marker is not found, there is no error message. Could be a missing // close-marker. -static void foldDelMarker( - buf_T *buf, linenr_T lnum, char_u *marker, size_t markerlen -) +static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t markerlen) { - char_u *newline; - char_u *cms = buf->b_p_cms; - char_u *cms2; + char_u *newline; + char_u *cms = buf->b_p_cms; + char_u *cms2; // end marker may be missing and fold extends below the last line if (lnum > buf->b_ml.ml_line_count) { @@ -1753,12 +1757,13 @@ static void foldDelMarker( if (STRNCMP(p, marker, markerlen) != 0) { continue; } - /* Found the marker, include a digit if it's there. */ + // Found the marker, include a digit if it's there. size_t len = markerlen; - if (ascii_isdigit(p[len])) + if (ascii_isdigit(p[len])) { ++len; + } if (*cms != NUL) { - /* Also delete 'commentstring' if it matches. */ + // Also delete 'commentstring' if it matches. cms2 = (char_u *)strstr((char *)cms, "%s"); if (p - line >= cms2 - cms && STRNCMP(p - (cms2 - cms), cms, cms2 - cms) == 0 @@ -1768,7 +1773,7 @@ static void foldDelMarker( } } if (u_save(lnum - 1, lnum + 1) == OK) { - /* Make new line: text-before-marker + text-after-marker */ + // Make new line: text-before-marker + text-after-marker newline = xmalloc(STRLEN(line) - len + 1); assert(p >= line); memcpy(newline, line, (size_t)(p - line)); @@ -1790,34 +1795,35 @@ static void foldDelMarker( /// @return the text for a closed fold /// /// Otherwise the result is in allocated memory. -char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, - foldinfo_T foldinfo, char_u *buf) +char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldinfo, char_u *buf) FUNC_ATTR_NONNULL_ARG(1) { - char_u *text = NULL; - /* an error occurred when evaluating 'fdt' setting */ + char_u *text = NULL; + // an error occurred when evaluating 'fdt' setting static int got_fdt_error = FALSE; int save_did_emsg = did_emsg; - static win_T *last_wp = NULL; + static win_T *last_wp = NULL; static linenr_T last_lnum = 0; - if (last_wp == NULL || last_wp != wp || last_lnum > lnum || last_lnum == 0) - /* window changed, try evaluating foldtext setting once again */ + if (last_wp == NULL || last_wp != wp || last_lnum > lnum || last_lnum == 0) { + // window changed, try evaluating foldtext setting once again got_fdt_error = FALSE; + } - if (!got_fdt_error) - /* a previous error should not abort evaluating 'foldexpr' */ + if (!got_fdt_error) { + // a previous error should not abort evaluating 'foldexpr' did_emsg = FALSE; + } if (*wp->w_p_fdt != NUL) { char dashes[MAX_LEVEL + 2]; - win_T *save_curwin; + win_T *save_curwin; int level; - char_u *p; + char_u *p; // Set "v:foldstart" and "v:foldend". - set_vim_var_nr(VV_FOLDSTART, (varnumber_T) lnum); - set_vim_var_nr(VV_FOLDEND, (varnumber_T) lnume); + set_vim_var_nr(VV_FOLDSTART, (varnumber_T)lnum); + set_vim_var_nr(VV_FOLDEND, (varnumber_T)lnume); // Set "v:folddashes" to a string of "level" dashes. // Set "v:foldlevel" to "level". @@ -1828,22 +1834,22 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, memset(dashes, '-', (size_t)level); dashes[level] = NUL; set_vim_var_string(VV_FOLDDASHES, dashes, -1); - set_vim_var_nr(VV_FOLDLEVEL, (varnumber_T) level); + set_vim_var_nr(VV_FOLDLEVEL, (varnumber_T)level); - /* skip evaluating foldtext on errors */ + // skip evaluating foldtext on errors if (!got_fdt_error) { save_curwin = curwin; curwin = wp; curbuf = wp->w_buffer; emsg_silent++; // handle exceptions, but don't display errors - text = eval_to_string_safe( - wp->w_p_fdt, NULL, - was_set_insecurely(wp, (char_u *)"foldtext", OPT_LOCAL)); + text = eval_to_string_safe(wp->w_p_fdt, NULL, + was_set_insecurely(wp, (char_u *)"foldtext", OPT_LOCAL)); emsg_silent--; - if (text == NULL || did_emsg) + if (text == NULL || did_emsg) { got_fdt_error = TRUE; + } curwin = save_curwin; curbuf = curwin->w_buffer; @@ -1852,8 +1858,9 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, last_wp = wp; set_vim_var_string(VV_FOLDDASHES, NULL, -1); - if (!did_emsg && save_did_emsg) + if (!did_emsg && save_did_emsg) { did_emsg = save_did_emsg; + } if (text != NULL) { /* Replace unprintable characters, if there are any. But @@ -1866,10 +1873,11 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, break; } p += len - 1; - } else if (*p == TAB) + } else if (*p == TAB) { *p = ' '; - else if (ptr2cells(p) > 1) + } else if (ptr2cells(p) > 1) { break; + } } if (*p != NUL) { p = (char_u *)transstr((const char *)text); @@ -1890,35 +1898,37 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, return text; } -/* foldtext_cleanup() {{{2 */ +// foldtext_cleanup() {{{2 /* * Remove 'foldmarker' and 'commentstring' from "str" (in-place). */ void foldtext_cleanup(char_u *str) { - char_u *s; - char_u *p; + char_u *s; + char_u *p; int did1 = FALSE; int did2 = FALSE; - /* Ignore leading and trailing white space in 'commentstring'. */ + // Ignore leading and trailing white space in 'commentstring'. char_u *cms_start = skipwhite(curbuf->b_p_cms); size_t cms_slen = STRLEN(cms_start); - while (cms_slen > 0 && ascii_iswhite(cms_start[cms_slen - 1])) + while (cms_slen > 0 && ascii_iswhite(cms_start[cms_slen - 1])) { --cms_slen; + } - /* locate "%s" in 'commentstring', use the part before and after it. */ + // locate "%s" in 'commentstring', use the part before and after it. char_u *cms_end = (char_u *)strstr((char *)cms_start, "%s"); size_t cms_elen = 0; if (cms_end != NULL) { cms_elen = cms_slen - (size_t)(cms_end - cms_start); cms_slen = (size_t)(cms_end - cms_start); - /* exclude white space before "%s" */ - while (cms_slen > 0 && ascii_iswhite(cms_start[cms_slen - 1])) + // exclude white space before "%s" + while (cms_slen > 0 && ascii_iswhite(cms_start[cms_slen - 1])) { --cms_slen; + } - /* skip "%s" and white space after it */ + // skip "%s" and white space after it s = skipwhite(cms_end + 2); cms_elen -= (size_t)(s - cms_end); cms_end = s; @@ -1927,18 +1937,21 @@ void foldtext_cleanup(char_u *str) for (s = str; *s != NUL; ) { size_t len = 0; - if (STRNCMP(s, curwin->w_p_fmr, foldstartmarkerlen) == 0) + if (STRNCMP(s, curwin->w_p_fmr, foldstartmarkerlen) == 0) { len = foldstartmarkerlen; - else if (STRNCMP(s, foldendmarker, foldendmarkerlen) == 0) + } else if (STRNCMP(s, foldendmarker, foldendmarkerlen) == 0) { len = foldendmarkerlen; + } if (len > 0) { - if (ascii_isdigit(s[len])) + if (ascii_isdigit(s[len])) { ++len; + } /* May remove 'commentstring' start. Useful when it's a double * quote and we already removed a double quote. */ - for (p = s; p > str && ascii_iswhite(p[-1]); --p) + for (p = s; p > str && ascii_iswhite(p[-1]); --p) { ; + } if (p >= str + cms_slen && STRNCMP(p - cms_slen, cms_start, cms_slen) == 0) { len += (size_t)(s - p) + cms_slen; @@ -1955,8 +1968,9 @@ void foldtext_cleanup(char_u *str) } } if (len != 0) { - while (ascii_iswhite(s[len])) + while (ascii_iswhite(s[len])) { ++len; + } STRMOVE(s, s + len); } else { MB_PTR_ADV(s); @@ -1964,10 +1978,10 @@ void foldtext_cleanup(char_u *str) } } -/* Folding by indent, expr, marker and syntax. {{{1 */ -/* Function declarations. {{{2 */ +// Folding by indent, expr, marker and syntax. {{{1 +// Function declarations. {{{2 -/* foldUpdateIEMS() {{{2 */ +// foldUpdateIEMS() {{{2 /* * Update the folding for window "wp", at least from lines "top" to "bot". * IEMS = "Indent Expr Marker Syntax" @@ -1976,28 +1990,30 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) { fline_T fline; LevelGetter getlevel = NULL; - fold_T *fp; + fold_T *fp; - /* Avoid problems when being called recursively. */ - if (invalid_top != (linenr_T)0) + // Avoid problems when being called recursively. + if (invalid_top != (linenr_T)0) { return; + } if (wp->w_foldinvalid) { - /* Need to update all folds. */ + // Need to update all folds. top = 1; bot = wp->w_buffer->b_ml.ml_line_count; wp->w_foldinvalid = false; - /* Mark all folds a maybe-small. */ + // Mark all folds a maybe-small. setSmallMaybe(&wp->w_folds); } - /* add the context for "diff" folding */ + // add the context for "diff" folding if (foldmethodIsDiff(wp)) { - if (top > diff_context) + if (top > diff_context) { top -= diff_context; - else + } else { top = 1; + } bot += diff_context; } @@ -2022,7 +2038,7 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) if (foldmethodIsMarker(wp)) { getlevel = foldlevelMarker; - /* Init marker variables to speed up foldlevelMarker(). */ + // Init marker variables to speed up foldlevelMarker(). parseMarker(wp); /* Need to get the level of the line above top, it is used if there is @@ -2031,7 +2047,7 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) // Get the fold level at top - 1. const int level = foldLevelWin(wp, top - 1); - /* The fold may end just above the top, check for that. */ + // The fold may end just above the top, check for that. fline.lnum = top - 1; fline.lvl = level; getlevel(&fline); @@ -2039,10 +2055,11 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) /* If a fold started here, we already had the level, if it stops * here, we need to use lvl_next. Could also start and end a fold * in the same line. */ - if (fline.lvl > level) + if (fline.lvl > level) { fline.lvl = level - (fline.lvl - fline.lvl_next); - else + } else { fline.lvl = fline.lvl_next; + } } fline.lnum = top; getlevel(&fline); @@ -2052,14 +2069,16 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) getlevel = foldlevelExpr; /* start one line back, because a "<1" may indicate the end of a * fold in the topline */ - if (top > 1) + if (top > 1) { --fline.lnum; - } else if (foldmethodIsSyntax(wp)) + } + } else if (foldmethodIsSyntax(wp)) { getlevel = foldlevelSyntax; - else if (foldmethodIsDiff(wp)) + } else if (foldmethodIsDiff(wp)) { getlevel = foldlevelDiff; - else + } else { getlevel = foldlevelIndent; + } /* Backup to a line for which the fold level is defined. Since it's * always defined for line one, we will stop there. */ @@ -2069,8 +2088,9 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) * the next line, but we search backwards here. */ fline.lvl_next = -1; getlevel(&fline); - if (fline.lvl >= 0) + if (fline.lvl >= 0) { break; + } } } @@ -2083,14 +2103,15 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) */ if (foldlevelSyntax == getlevel) { garray_T *gap = &wp->w_folds; - fold_T *fpn = NULL; + fold_T *fpn = NULL; int current_fdl = 0; linenr_T fold_start_lnum = 0; linenr_T lnum_rel = fline.lnum; while (current_fdl < fline.lvl) { - if (!foldFind(gap, lnum_rel, &fpn)) + if (!foldFind(gap, lnum_rel, &fpn)) { break; + } ++current_fdl; fold_start_lnum += fpn->fd_top; @@ -2100,8 +2121,9 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) if (fpn != NULL && current_fdl == fline.lvl) { linenr_T fold_end_lnum = fold_start_lnum + fpn->fd_len; - if (fold_end_lnum > bot) + if (fold_end_lnum > bot) { bot = fold_end_lnum; + } } } @@ -2114,34 +2136,37 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) while (!got_int) { /* Always stop at the end of the file ("end" can be past the end of * the file). */ - if (fline.lnum > wp->w_buffer->b_ml.ml_line_count) + if (fline.lnum > wp->w_buffer->b_ml.ml_line_count) { break; + } if (fline.lnum > end) { /* For "marker", "expr" and "syntax" methods: If a change caused * a fold to be removed, we need to continue at least until where * it ended. */ if (getlevel != foldlevelMarker && getlevel != foldlevelSyntax - && getlevel != foldlevelExpr) + && getlevel != foldlevelExpr) { break; + } if ((start <= end && foldFind(&wp->w_folds, end, &fp) && fp->fd_top + fp->fd_len - 1 > end) || (fline.lvl == 0 && foldFind(&wp->w_folds, fline.lnum, &fp) - && fp->fd_top < fline.lnum)) + && fp->fd_top < fline.lnum)) { end = fp->fd_top + fp->fd_len - 1; - else if (getlevel == foldlevelSyntax - && foldLevelWin(wp, fline.lnum) != fline.lvl) + } else if (getlevel == foldlevelSyntax + && foldLevelWin(wp, fline.lnum) != fline.lvl) { /* For "syntax" method: Compare the foldlevel that the syntax * tells us to the foldlevel from the existing folds. If they * don't match continue updating folds. */ end = fline.lnum; - else + } else { break; + } } - /* A level 1 fold starts at a line with foldlevel > 0. */ + // A level 1 fold starts at a line with foldlevel > 0. if (fline.lvl > 0) { invalid_top = fline.lnum; invalid_bot = end; @@ -2149,8 +2174,9 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) FD_LEVEL); start = fline.lnum; } else { - if (fline.lnum == wp->w_buffer->b_ml.ml_line_count) + if (fline.lnum == wp->w_buffer->b_ml.ml_line_count) { break; + } ++fline.lnum; fline.lvl = fline.lvl_next; getlevel(&fline); @@ -2160,56 +2186,58 @@ static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) // There can't be any folds from start until end now. foldRemove(wp, &wp->w_folds, start, end); - /* If some fold changed, need to redraw and position cursor. */ - if (fold_changed && wp->w_p_fen) + // If some fold changed, need to redraw and position cursor. + if (fold_changed && wp->w_p_fen) { changed_window_setting_win(wp); + } /* If we updated folds past "bot", need to redraw more lines. Don't do * this in other situations, the changed lines will be redrawn anyway and * this method can cause the whole window to be updated. */ if (end != bot) { - if (wp->w_redraw_top == 0 || wp->w_redraw_top > top) + if (wp->w_redraw_top == 0 || wp->w_redraw_top > top) { wp->w_redraw_top = top; - if (wp->w_redraw_bot < end) + } + if (wp->w_redraw_bot < end) { wp->w_redraw_bot = end; + } } invalid_top = (linenr_T)0; } -/* foldUpdateIEMSRecurse() {{{2 */ -/* - * Update a fold that starts at "flp->lnum". At this line there is always a - * valid foldlevel, and its level >= "level". - * "flp" is valid for "flp->lnum" when called and it's valid when returning. - * "flp->lnum" is set to the lnum just below the fold, if it ends before - * "bot", it's "bot" plus one if the fold continues and it's bigger when using - * the marker method and a text change made following folds to change. - * When returning, "flp->lnum_save" is the line number that was used to get - * the level when the level at "flp->lnum" is invalid. - * Remove any folds from "startlnum" up to here at this level. - * Recursively update nested folds. - * Below line "bot" there are no changes in the text. - * "flp->lnum", "flp->lnum_save" and "bot" are relative to the start of the - * outer fold. - * "flp->off" is the offset to the real line number in the buffer. - * - * All this would be a lot simpler if all folds in the range would be deleted - * and then created again. But we would lose all information about the - * folds, even when making changes that don't affect the folding (e.g. "vj~"). - * - * Returns bot, which may have been increased for lines that also need to be - * updated as a result of a detected change in the fold. - */ -static linenr_T foldUpdateIEMSRecurse( - garray_T *const gap, const int level, const linenr_T startlnum, - fline_T *const flp, LevelGetter getlevel, linenr_T bot, - const char topflags // containing fold flags -) +// foldUpdateIEMSRecurse() {{{2 +/// Update a fold that starts at "flp->lnum". At this line there is always a +/// valid foldlevel, and its level >= "level". +/// +/// "flp" is valid for "flp->lnum" when called and it's valid when returning. +/// "flp->lnum" is set to the lnum just below the fold, if it ends before +/// "bot", it's "bot" plus one if the fold continues and it's bigger when using +/// the marker method and a text change made following folds to change. +/// When returning, "flp->lnum_save" is the line number that was used to get +/// the level when the level at "flp->lnum" is invalid. +/// Remove any folds from "startlnum" up to here at this level. +/// Recursively update nested folds. +/// Below line "bot" there are no changes in the text. +/// "flp->lnum", "flp->lnum_save" and "bot" are relative to the start of the +/// outer fold. +/// "flp->off" is the offset to the real line number in the buffer. +/// +/// All this would be a lot simpler if all folds in the range would be deleted +/// and then created again. But we would lose all information about the +/// folds, even when making changes that don't affect the folding (e.g. "vj~"). +/// +/// @param topflags containing fold flags +/// +/// @return bot, which may have been increased for lines that also need to be +/// updated as a result of a detected change in the fold. +static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level, + const linenr_T startlnum, fline_T *const flp, + LevelGetter getlevel, linenr_T bot, const char topflags) { linenr_T ll; - fold_T *fp = NULL; - fold_T *fp2; + fold_T *fp = NULL; + fold_T *fp2; int lvl = level; linenr_T startlnum2 = startlnum; const linenr_T firstlnum = flp->lnum; // first lnum we got @@ -2227,8 +2255,9 @@ static linenr_T foldUpdateIEMSRecurse( if (getlevel == foldlevelMarker && flp->start <= flp->lvl - level && flp->lvl > 0) { (void)foldFind(gap, startlnum - 1, &fp); - if (fp >= ((fold_T *)gap->ga_data) + gap->ga_len - || fp->fd_top >= startlnum) { + if (fp != NULL + && (fp >= ((fold_T *)gap->ga_data) + gap->ga_len + || fp->fd_top >= startlnum)) { fp = NULL; } } @@ -2246,7 +2275,7 @@ static linenr_T foldUpdateIEMSRecurse( */ flp->lnum_save = flp->lnum; while (!got_int) { - /* Updating folds can be slow, check for CTRL-C. */ + // Updating folds can be slow, check for CTRL-C. line_breakcheck(); /* Set "lvl" to the level of line "flp->lnum". When flp->start is set @@ -2254,11 +2283,13 @@ static linenr_T foldUpdateIEMSRecurse( * force the fold to end. Do the same when had_end is set: Previous * line was marked as end of a fold. */ lvl = flp->lvl; - if (lvl > MAX_LEVEL) + if (lvl > MAX_LEVEL) { lvl = MAX_LEVEL; + } if (flp->lnum > firstlnum - && (level > lvl - flp->start || level >= flp->had_end)) + && (level > lvl - flp->start || level >= flp->had_end)) { lvl = 0; + } if (flp->lnum > bot && !finish && fp != NULL) { /* For "marker" and "syntax" methods: @@ -2269,8 +2300,9 @@ static linenr_T foldUpdateIEMSRecurse( */ if (getlevel != foldlevelMarker && getlevel != foldlevelExpr - && getlevel != foldlevelSyntax) + && getlevel != foldlevelSyntax) { break; + } i = 0; fp2 = fp; if (lvl >= level) { @@ -2311,10 +2343,11 @@ static linenr_T foldUpdateIEMSRecurse( while (!got_int) { /* set concat to 1 if it's allowed to concatenated this fold * with a previous one that touches it. */ - if (flp->start != 0 || flp->had_end <= MAX_LEVEL) + if (flp->start != 0 || flp->had_end <= MAX_LEVEL) { concat = 0; - else + } else { concat = 1; + } /* Find an existing fold to re-use. Preferably one that * includes startlnum, otherwise one that ends just before @@ -2437,10 +2470,12 @@ static linenr_T foldUpdateIEMSRecurse( fp->fd_flags = FD_OPEN; } else if (i <= 0) { fp->fd_flags = topflags; - if (topflags != FD_LEVEL) + if (topflags != FD_LEVEL) { flp->wp->w_fold_manual = true; - } else + } + } else { fp->fd_flags = (fp - 1)->fd_flags; + } fp->fd_small = kNone; // If using the "marker", "expr" or "syntax" method, we // need to continue until the end of the fold is found. @@ -2489,7 +2524,7 @@ static linenr_T foldUpdateIEMSRecurse( bot += fp->fd_top; startlnum2 = flp->lnum; - /* This fold may end at the same line, don't incr. flp->lnum. */ + // This fold may end at the same line, don't incr. flp->lnum. } else { /* * Get the level of the next line, then continue the loop to check @@ -2500,20 +2535,23 @@ static linenr_T foldUpdateIEMSRecurse( flp->lnum = flp->lnum_save; ll = flp->lnum + 1; while (!got_int) { - /* Make the previous level available to foldlevel(). */ + // Make the previous level available to foldlevel(). prev_lnum = flp->lnum; prev_lnum_lvl = flp->lvl; - if (++flp->lnum > linecount) + if (++flp->lnum > linecount) { break; + } flp->lvl = flp->lvl_next; getlevel(flp); - if (flp->lvl >= 0 || flp->had_end <= MAX_LEVEL) + if (flp->lvl >= 0 || flp->had_end <= MAX_LEVEL) { break; + } } prev_lnum = 0; - if (flp->lnum > linecount) + if (flp->lnum > linecount) { break; + } /* leave flp->lnum_save to lnum of the line that was used to get * the level, flp->lnum to the lnum of the next line. */ @@ -2522,8 +2560,9 @@ static linenr_T foldUpdateIEMSRecurse( } } - if (fp == NULL) /* only happens when got_int is set */ + if (fp == NULL) { // only happens when got_int is set return bot; + } /* * Get here when: @@ -2572,12 +2611,13 @@ static linenr_T foldUpdateIEMSRecurse( } } - /* delete following folds that end before the current line */ + // delete following folds that end before the current line for (;; ) { fp2 = fp + 1; if (fp2 >= (fold_T *)gap->ga_data + gap->ga_len - || fp2->fd_top > flp->lnum) + || fp2->fd_top > flp->lnum) { break; + } if (fp2->fd_top + fp2->fd_len > flp->lnum) { if (fp2->fd_top < flp->lnum) { // Make fold that includes lnum start at lnum. @@ -2601,19 +2641,20 @@ static linenr_T foldUpdateIEMSRecurse( /* Need to redraw the lines we inspected, which might be further down than * was asked for. */ - if (bot < flp->lnum - 1) + if (bot < flp->lnum - 1) { bot = flp->lnum - 1; + } return bot; } -/* foldInsert() {{{2 */ +// foldInsert() {{{2 /* * Insert a new fold in "gap" at position "i". */ static void foldInsert(garray_T *gap, int i) { - fold_T *fp; + fold_T *fp; ga_grow(gap, 1); @@ -2625,7 +2666,7 @@ static void foldInsert(garray_T *gap, int i) ga_init(&fp->fd_nested, (int)sizeof(fold_T), 10); } -/* foldSplit() {{{2 */ +// foldSplit() {{{2 /* * Split the "i"th fold in "gap", which starts before "top" and ends below * "bot" in two pieces, one ending above "top" and the other starting below @@ -2633,14 +2674,12 @@ static void foldInsert(garray_T *gap, int i) * The caller must first have taken care of any nested folds from "top" to * "bot"! */ -static void foldSplit(buf_T *buf, garray_T *const gap, - const int i, const linenr_T top, - const linenr_T bot - ) +static void foldSplit(buf_T *buf, garray_T *const gap, const int i, const linenr_T top, + const linenr_T bot) { - fold_T *fp2; + fold_T *fp2; - /* The fold continues below bot, need to split it. */ + // The fold continues below bot, need to split it. foldInsert(gap, i + 1); fold_T *const fp = (fold_T *)gap->ga_data + i; @@ -2673,7 +2712,7 @@ static void foldSplit(buf_T *buf, garray_T *const gap, fold_changed = true; } -/* foldRemove() {{{2 */ +// foldRemove() {{{2 /* * Remove folds within the range "top" to and including "bot". * Check for these situations: @@ -2692,11 +2731,9 @@ static void foldSplit(buf_T *buf, garray_T *const gap, * 5: made to start below "bot". * 6: not changed */ -static void foldRemove( - win_T *const wp, garray_T *gap, linenr_T top, linenr_T bot -) +static void foldRemove(win_T *const wp, garray_T *gap, linenr_T top, linenr_T bot) { - fold_T *fp = NULL; + fold_T *fp = NULL; if (bot < top) { return; // nothing to do @@ -2729,10 +2766,9 @@ static void foldRemove( fold_changed = true; if (fp->fd_top + fp->fd_len - 1 > bot) { // 5: Make fold that includes bot start below bot. - foldMarkAdjustRecurse( - wp, &fp->fd_nested, - (linenr_T)0, (long)(bot - fp->fd_top), - (linenr_T)MAXLNUM, (long)(fp->fd_top - bot - 1)); + foldMarkAdjustRecurse(wp, &fp->fd_nested, + (linenr_T)0, (long)(bot - fp->fd_top), + (linenr_T)MAXLNUM, (long)(fp->fd_top - bot - 1)); fp->fd_len -= bot - fp->fd_top + 1; fp->fd_top = bot + 1; break; @@ -2745,10 +2781,7 @@ static void foldRemove( } // foldReverseOrder() {{{2 -static void foldReverseOrder( - garray_T *gap, - const linenr_T start_arg, - const linenr_T end_arg) +static void foldReverseOrder(garray_T *gap, const linenr_T start_arg, const linenr_T end_arg) { linenr_T start = start_arg; linenr_T end = end_arg; @@ -2803,11 +2836,8 @@ static void truncate_fold(win_T *const wp, fold_T *fp, linenr_T end) #define VALID_FOLD(fp, gap) \ ((gap)->ga_len > 0 && (fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len)) #define FOLD_INDEX(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data))) -void foldMoveRange( - win_T *const wp, garray_T *gap, - const linenr_T line1, const linenr_T line2, - const linenr_T dest -) +void foldMoveRange(win_T *const wp, garray_T *gap, const linenr_T line1, const linenr_T line2, + const linenr_T dest) { fold_T *fp; const linenr_T range_len = line2 - line1 + 1; @@ -2907,7 +2937,7 @@ void foldMoveRange( #undef VALID_FOLD #undef FOLD_INDEX -/* foldMerge() {{{2 */ +// foldMerge() {{{2 /* * Merge two adjacent folds (and the nested ones in them). * This only works correctly when the folds are really adjacent! Thus "fp1" @@ -2917,11 +2947,11 @@ void foldMoveRange( */ static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2) { - fold_T *fp3; - fold_T *fp4; + fold_T *fp3; + fold_T *fp4; int idx; - garray_T *gap1 = &fp1->fd_nested; - garray_T *gap2 = &fp2->fd_nested; + garray_T *gap1 = &fp1->fd_nested; + garray_T *gap2 = &fp2->fd_nested; /* If the last nested fold in fp1 touches the first nested fold in fp2, * merge them recursively. */ @@ -2929,7 +2959,7 @@ static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2) foldMerge(wp, fp3, gap2, fp4); } - /* Move nested folds in fp2 to the end of fp1. */ + // Move nested folds in fp2 to the end of fp1. if (!GA_EMPTY(gap2)) { ga_grow(gap1, gap2->ga_len); for (idx = 0; idx < gap2->ga_len; ++idx) { @@ -2946,7 +2976,7 @@ static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2) fold_changed = true; } -/* foldlevelIndent() {{{2 */ +// foldlevelIndent() {{{2 /* * Low level function to get the foldlevel for the "indent" method. * Doesn't use any caching. @@ -2954,43 +2984,45 @@ static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2) */ static void foldlevelIndent(fline_T *flp) { - char_u *s; - buf_T *buf; + char_u *s; + buf_T *buf; linenr_T lnum = flp->lnum + flp->off; buf = flp->wp->w_buffer; - s = skipwhite(ml_get_buf(buf, lnum, FALSE)); + s = skipwhite(ml_get_buf(buf, lnum, false)); /* empty line or lines starting with a character in 'foldignore': level * depends on surrounding lines */ if (*s == NUL || vim_strchr(flp->wp->w_p_fdi, *s) != NULL) { - /* first and last line can't be undefined, use level 0 */ - if (lnum == 1 || lnum == buf->b_ml.ml_line_count) + // first and last line can't be undefined, use level 0 + if (lnum == 1 || lnum == buf->b_ml.ml_line_count) { flp->lvl = 0; - else + } else { flp->lvl = -1; + } } else { flp->lvl = get_indent_buf(buf, lnum) / get_sw_value(buf); } if (flp->lvl > flp->wp->w_p_fdn) { - flp->lvl = (int) MAX(0, flp->wp->w_p_fdn); + flp->lvl = (int)MAX(0, flp->wp->w_p_fdn); } } -/* foldlevelDiff() {{{2 */ +// foldlevelDiff() {{{2 /* * Low level function to get the foldlevel for the "diff" method. * Doesn't use any caching. */ static void foldlevelDiff(fline_T *flp) { - if (diff_infold(flp->wp, flp->lnum + flp->off)) + if (diff_infold(flp->wp, flp->lnum + flp->off)) { flp->lvl = 1; - else + } else { flp->lvl = 0; + } } -/* foldlevelExpr() {{{2 */ +// foldlevelExpr() {{{2 /* * Low level function to get the foldlevel for the "expr" method. * Doesn't use any caching. @@ -2998,20 +3030,21 @@ static void foldlevelDiff(fline_T *flp) */ static void foldlevelExpr(fline_T *flp) { - win_T *win; + win_T *win; int c; linenr_T lnum = flp->lnum + flp->off; win = curwin; curwin = flp->wp; curbuf = flp->wp->w_buffer; - set_vim_var_nr(VV_LNUM, (varnumber_T) lnum); + set_vim_var_nr(VV_LNUM, (varnumber_T)lnum); flp->start = 0; flp->had_end = flp->end; flp->end = MAX_LEVEL + 1; - if (lnum <= 1) + if (lnum <= 1) { flp->lvl = 0; + } /* KeyTyped may be reset to 0 when calling a function which invokes * do_cmdline(). To make 'foldopen' work correctly restore KeyTyped. */ @@ -3020,46 +3053,54 @@ static void foldlevelExpr(fline_T *flp) KeyTyped = save_keytyped; switch (c) { - /* "a1", "a2", .. : add to the fold level */ - case 'a': if (flp->lvl >= 0) { + // "a1", "a2", .. : add to the fold level + case 'a': + if (flp->lvl >= 0) { flp->lvl += n; flp->lvl_next = flp->lvl; - } + } flp->start = n; break; - /* "s1", "s2", .. : subtract from the fold level */ - case 's': if (flp->lvl >= 0) { - if (n > flp->lvl) + // "s1", "s2", .. : subtract from the fold level + case 's': + if (flp->lvl >= 0) { + if (n > flp->lvl) { flp->lvl_next = 0; - else + } else { flp->lvl_next = flp->lvl - n; + } flp->end = flp->lvl_next + 1; - } + } break; - /* ">1", ">2", .. : start a fold with a certain level */ - case '>': flp->lvl = n; + // ">1", ">2", .. : start a fold with a certain level + case '>': + flp->lvl = n; flp->lvl_next = n; flp->start = 1; break; - /* "<1", "<2", .. : end a fold with a certain level */ - case '<': flp->lvl_next = n - 1; + // "<1", "<2", .. : end a fold with a certain level + case '<': + flp->lvl_next = n - 1; flp->end = n; break; - /* "=": No change in level */ - case '=': flp->lvl_next = flp->lvl; + // "=": No change in level + case '=': + flp->lvl_next = flp->lvl; break; - /* "-1", "0", "1", ..: set fold level */ - default: if (n < 0) + // "-1", "0", "1", ..: set fold level + default: + if (n < 0) { /* Use the current level for the next line, so that "a1" * will work there. */ flp->lvl_next = flp->lvl; - else + } else { flp->lvl_next = n; + } flp->lvl = n; break; } @@ -3071,15 +3112,16 @@ static void foldlevelExpr(fline_T *flp) flp->lvl = 0; flp->lvl_next = 0; } - if (lnum == curbuf->b_ml.ml_line_count) + if (lnum == curbuf->b_ml.ml_line_count) { flp->lvl_next = 0; + } } curwin = win; curbuf = curwin->w_buffer; } -/* parseMarker() {{{2 */ +// parseMarker() {{{2 /* * Parse 'foldmarker' and set "foldendmarker", "foldstartmarkerlen" and * "foldendmarkerlen". @@ -3092,7 +3134,7 @@ static void parseMarker(win_T *wp) foldendmarkerlen = STRLEN(foldendmarker); } -/* foldlevelMarker() {{{2 */ +// foldlevelMarker() {{{2 /* * Low level function to get the foldlevel for the "marker" method. * "foldendmarker", "foldstartmarkerlen" and "foldendmarkerlen" must have been @@ -3104,38 +3146,39 @@ static void parseMarker(win_T *wp) */ static void foldlevelMarker(fline_T *flp) { - char_u *startmarker; + char_u *startmarker; int cstart; int cend; int start_lvl = flp->lvl; - char_u *s; + char_u *s; int n; - /* cache a few values for speed */ + // cache a few values for speed startmarker = flp->wp->w_p_fmr; cstart = *startmarker; ++startmarker; cend = *foldendmarker; - /* Default: no start found, next level is same as current level */ + // Default: no start found, next level is same as current level flp->start = 0; flp->lvl_next = flp->lvl; - s = ml_get_buf(flp->wp->w_buffer, flp->lnum + flp->off, FALSE); + s = ml_get_buf(flp->wp->w_buffer, flp->lnum + flp->off, false); while (*s) { if (*s == cstart && STRNCMP(s + 1, startmarker, foldstartmarkerlen - 1) == 0) { - /* found startmarker: set flp->lvl */ + // found startmarker: set flp->lvl s += foldstartmarkerlen; if (ascii_isdigit(*s)) { n = atoi((char *)s); if (n > 0) { flp->lvl = n; flp->lvl_next = n; - if (n <= start_lvl) + if (n <= start_lvl) { flp->start = 1; - else + } else { flp->start = n - start_lvl; + } } } else { ++flp->lvl; @@ -3144,16 +3187,17 @@ static void foldlevelMarker(fline_T *flp) } } else if (*s == cend && STRNCMP(s + 1, foldendmarker + 1, foldendmarkerlen - 1) == 0) { - /* found endmarker: set flp->lvl_next */ + // found endmarker: set flp->lvl_next s += foldendmarkerlen; if (ascii_isdigit(*s)) { n = atoi((char *)s); if (n > 0) { flp->lvl = n; flp->lvl_next = n - 1; - /* never start a fold with an end marker */ - if (flp->lvl_next > start_lvl) + // never start a fold with an end marker + if (flp->lvl_next > start_lvl) { flp->lvl_next = start_lvl; + } } } else { flp->lvl_next--; @@ -3163,12 +3207,13 @@ static void foldlevelMarker(fline_T *flp) } } - /* The level can't go negative, must be missing a start marker. */ - if (flp->lvl_next < 0) + // The level can't go negative, must be missing a start marker. + if (flp->lvl_next < 0) { flp->lvl_next = 0; + } } -/* foldlevelSyntax() {{{2 */ +// foldlevelSyntax() {{{2 /* * Low level function to get the foldlevel for the "syntax" method. * Doesn't use any caching. @@ -3178,20 +3223,20 @@ static void foldlevelSyntax(fline_T *flp) linenr_T lnum = flp->lnum + flp->off; int n; - /* Use the maximum fold level at the start of this line and the next. */ + // Use the maximum fold level at the start of this line and the next. flp->lvl = syn_get_foldlevel(flp->wp, lnum); flp->start = 0; if (lnum < flp->wp->w_buffer->b_ml.ml_line_count) { n = syn_get_foldlevel(flp->wp, lnum + 1); if (n > flp->lvl) { - flp->start = n - flp->lvl; /* fold(s) start here */ + flp->start = n - flp->lvl; // fold(s) start here flp->lvl = n; } } } -/* functions for storing the fold state in a View {{{1 */ -/* put_folds() {{{2 */ +// functions for storing the fold state in a View {{{1 +// put_folds() {{{2 /* * Write commands to "fd" to restore the manual folds in window "wp". @@ -3207,14 +3252,15 @@ int put_folds(FILE *fd, win_T *wp) } } - /* If some folds are manually opened/closed, need to restore that. */ - if (wp->w_fold_manual) + // If some folds are manually opened/closed, need to restore that. + if (wp->w_fold_manual) { return put_foldopen_recurse(fd, wp, &wp->w_folds, (linenr_T)0); + } return OK; } -/* put_folds_recurse() {{{2 */ +// put_folds_recurse() {{{2 /* * Write commands to "fd" to recreate manually created folds. * Returns FAIL when writing failed. @@ -3223,20 +3269,22 @@ static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off) { fold_T *fp = (fold_T *)gap->ga_data; for (int i = 0; i < gap->ga_len; i++) { - /* Do nested folds first, they will be created closed. */ - if (put_folds_recurse(fd, &fp->fd_nested, off + fp->fd_top) == FAIL) + // Do nested folds first, they will be created closed. + if (put_folds_recurse(fd, &fp->fd_nested, off + fp->fd_top) == FAIL) { return FAIL; + } if (fprintf(fd, "%" PRId64 ",%" PRId64 "fold", (int64_t)(fp->fd_top + off), (int64_t)(fp->fd_top + off + fp->fd_len - 1)) < 0 - || put_eol(fd) == FAIL) + || put_eol(fd) == FAIL) { return FAIL; + } ++fp; } return OK; } -/* put_foldopen_recurse() {{{2 */ +// put_foldopen_recurse() {{{2 /* * Write commands to "fd" to open and close manually opened/closed folds. * Returns FAIL when writing failed. @@ -3249,19 +3297,22 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off for (int i = 0; i < gap->ga_len; i++) { if (fp->fd_flags != FD_LEVEL) { if (!GA_EMPTY(&fp->fd_nested)) { - /* open nested folds while this fold is open */ + // open nested folds while this fold is open if (fprintf(fd, "%" PRId64, (int64_t)(fp->fd_top + off)) < 0 || put_eol(fd) == FAIL - || put_line(fd, "normal! zo") == FAIL) + || put_line(fd, "normal! zo") == FAIL) { return FAIL; + } if (put_foldopen_recurse(fd, wp, &fp->fd_nested, - off + fp->fd_top) - == FAIL) + off + fp->fd_top) + == FAIL) { return FAIL; - /* close the parent when needed */ + } + // close the parent when needed if (fp->fd_flags == FD_CLOSED) { - if (put_fold_open_close(fd, fp, off) == FAIL) + if (put_fold_open_close(fd, fp, off) == FAIL) { return FAIL; + } } } else { /* Open or close the leaf according to the window foldlevel. @@ -3269,9 +3320,11 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off * the parent. */ level = foldLevelWin(wp, off + fp->fd_top); if ((fp->fd_flags == FD_CLOSED && wp->w_p_fdl >= level) - || (fp->fd_flags != FD_CLOSED && wp->w_p_fdl < level)) - if (put_fold_open_close(fd, fp, off) == FAIL) + || (fp->fd_flags != FD_CLOSED && wp->w_p_fdl < level)) { + if (put_fold_open_close(fd, fp, off) == FAIL) { return FAIL; + } + } } } ++fp; @@ -3280,7 +3333,7 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off return OK; } -/* put_fold_open_close() {{{2 */ +// put_fold_open_close() {{{2 /* * Write the open or close command to "fd". * Returns FAIL when writing failed. @@ -3290,11 +3343,12 @@ static int put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off) if (fprintf(fd, "%" PRId64, (int64_t)(fp->fd_top + off)) < 0 || put_eol(fd) == FAIL || fprintf(fd, "normal! z%c", - fp->fd_flags == FD_CLOSED ? 'c' : 'o') < 0 - || put_eol(fd) == FAIL) + fp->fd_flags == FD_CLOSED ? 'c' : 'o') < 0 + || put_eol(fd) == FAIL) { return FAIL; + } return OK; } -/* }}}1 */ +// }}}1 diff --git a/src/nvim/fold.h b/src/nvim/fold.h index 95c4b0c1dc..37fab4da60 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -21,6 +21,8 @@ typedef struct foldinfo { long fi_lines; } foldinfo_T; +#define FOLDINFO_INIT { 0, 0, 0, 0 } + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "fold.h.generated.h" diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index d2a7c16186..99d80cdebc 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -7,7 +7,7 @@ if arg[1] == '--help' then print(' 2: dispatch output file (dispatch_wrappers.generated.h)') print(' 3: functions metadata output file (funcs_metadata.generated.h)') print(' 4: API metadata output file (api_metadata.mpack)') - print(' 5: lua C bindings output file (msgpack_lua_c_bindings.generated.c)') + print(' 5: lua C bindings output file (lua_api_c_bindings.generated.c)') print(' rest: C files where API functions are defined') end assert(#arg >= 4) @@ -247,7 +247,7 @@ for i = 1, #functions do (j - 1)..'].type == kObjectTypeInteger) {') output:write('\n '..converted..' = (Float)args.items['..(j - 1)..'].data.integer;') end - -- accept empty lua tables as empty dictionarys + -- accept empty lua tables as empty dictionaries if rt:match('^Dictionary') then output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeArray && args.items['..(j - 1)..'].data.array.size == 0) {') --luacheck: ignore 631 output:write('\n '..converted..' = (Dictionary)ARRAY_DICT_INIT;') @@ -321,8 +321,6 @@ end output:write([[ void msgpack_rpc_init_method_table(void) { - methods = map_new(String, MsgpackRpcRequestHandler)(); - ]]) for i = 1, #functions do @@ -380,7 +378,7 @@ output:write('\n') local lua_c_functions = {} local function process_function(fn) - local lua_c_function_name = ('nlua_msgpack_%s'):format(fn.name) + local lua_c_function_name = ('nlua_api_%s'):format(fn.name) write_shifted_output(output, string.format([[ static int %s(lua_State *lstate) diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua index 679895421a..945fa5099f 100644 --- a/src/nvim/generators/gen_eval.lua +++ b/src/nvim/generators/gen_eval.lua @@ -42,7 +42,7 @@ gperfpipe:write([[ %language=ANSI-C %global-table %readonly-tables -%define initializer-suffix ,0,0,NULL,NULL +%define initializer-suffix ,0,0,BASE_NONE,NULL,NULL %define word-array-name functions %define hash-function-name hash_internal_func_gperf %define lookup-function-name find_internal_func_gperf @@ -59,9 +59,10 @@ for name, def in pairs(funcs) do elseif #args == 1 then args[2] = 'MAX_FUNC_ARGS' end + local base = def.base or "BASE_NONE" local func = def.func or ('f_' .. name) local data = def.data or "NULL" - gperfpipe:write(('%s, %s, %s, &%s, (FunPtr)%s\n') - :format(name, args[1], args[2], func, data)) + gperfpipe:write(('%s, %s, %s, %s, &%s, (FunPtr)%s\n') + :format(name, args[1], args[2], base, func, data)) end gperfpipe:close() diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index d80a6219eb..0454c54faf 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -69,8 +69,6 @@ local get_flags = function(o) {'alloced'}, {'nodefault'}, {'no_mkrc'}, - {'vi_def'}, - {'vim'}, {'secure'}, {'gettext'}, {'noglob'}, @@ -120,8 +118,11 @@ local get_value = function(v) return '(char_u *) ' .. value_dumpers[type(v)](v) end -local get_defaults = function(d) - return ('{' .. get_value(d.vi) .. ', ' .. get_value(d.vim) .. '}') +local get_defaults = function(d,n) + if d == nil then + error("option '"..n.."' should have a default value") + end + return get_value(d) end local defines = {} @@ -170,11 +171,11 @@ local dump_option = function(i, o) if o.defaults.condition then w(get_cond(o.defaults.condition)) end - w(' .def_val=' .. get_defaults(o.defaults.if_true)) + w(' .def_val=' .. get_defaults(o.defaults.if_true, o.full_name)) if o.defaults.condition then if o.defaults.if_false then w('#else') - w(' .def_val=' .. get_defaults(o.defaults.if_false)) + w(' .def_val=' .. get_defaults(o.defaults.if_false, o.full_name)) end w('#endif') end diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 5c2eed363e..11e3b9bc2d 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -34,6 +34,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" +#include "nvim/plines.h" #include "nvim/keymap.h" #include "nvim/garray.h" #include "nvim/move.h" @@ -51,7 +52,6 @@ #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/fileio.h" -#include "nvim/api/private/handle.h" /// Index in scriptin @@ -840,6 +840,14 @@ static void init_typebuf(void) } } +void init_default_mappings(void) +{ + add_map((char_u *)"Y y$", NORMAL, true); + add_map((char_u *)"<C-L> <Cmd>nohlsearch<Bar>diffupdate<CR><C-L>", NORMAL, true); + add_map((char_u *)"<C-U> <C-G>u<C-U>", INSERT, true); + add_map((char_u *)"<C-W> <C-G>u<C-W>", INSERT, true); +} + // Insert a string in position 'offset' in the typeahead buffer (for "@r" // and ":normal" command, vgetorpeek() and check_termcode()) // @@ -1161,7 +1169,7 @@ void may_sync_undo(void) { if ((!(State & (INSERT + CMDLINE)) || arrow_used) && scriptin[curscript] == NULL) - u_sync(FALSE); + u_sync(false); } /* @@ -1555,8 +1563,8 @@ int vgetc(void) */ may_garbage_collect = false; - // Exec lua callbacks for on_keystroke - nlua_execute_log_keystroke(c); + // Execute Lua on_key callbacks. + nlua_execute_on_key(c); return c; } @@ -2230,20 +2238,22 @@ static int vgetorpeek(bool advance) timedout = true; continue; } - /* When 'insertmode' is set, ESC just beeps in Insert - * mode. Use CTRL-L to make edit() return. - * For the command line only CTRL-C always breaks it. - * For the cmdline window: Alternate between ESC and - * CTRL-C: ESC for most situations and CTRL-C to close the - * cmdline window. */ - if (p_im && (State & INSERT)) + // When 'insertmode' is set, ESC just beeps in Insert + // mode. Use CTRL-L to make edit() return. + // In Ex-mode \n is compatible with original Vim behaviour. + // For the command line only CTRL-C always breaks it. + // For the cmdline window: Alternate between ESC and + // CTRL-C: ESC for most situations and CTRL-C to close the + // cmdline window. + if (p_im && (State & INSERT)) { c = Ctrl_L; - else if ((State & CMDLINE) - || (cmdwin_type > 0 && tc == ESC) - ) + } else if (exmode_active) { + c = '\n'; + } else if ((State & CMDLINE) || (cmdwin_type > 0 && tc == ESC)) { c = Ctrl_C; - else + } else { c = ESC; + } tc = c; break; } @@ -2413,7 +2423,7 @@ static int vgetorpeek(bool advance) * 1. a scriptfile * 2. the keyboard * - * As much characters as we can get (upto 'maxlen') are put in "buf" and + * As much characters as we can get (up to 'maxlen') are put in "buf" and * NUL terminated (buffer length must be 'maxlen' + 1). * Minimum for "maxlen" is 3!!!! * @@ -4354,18 +4364,23 @@ check_map ( } -/* - * Add a mapping "map" for mode "mode". - * Need to put string in allocated memory, because do_map() will modify it. - */ -void add_map(char_u *map, int mode) +/// Add a mapping. Unlike @ref do_map this copies the {map} argument, so +/// static or read-only strings can be used. +/// +/// @param map C-string containing the arguments of the map/abbrev command, +/// i.e. everything except the initial `:[X][nore]map`. +/// @param mode Bitflags representing the mode in which to set the mapping. +/// See @ref get_map_mode. +/// @param nore If true, make a non-recursive mapping. +void add_map(char_u *map, int mode, bool nore) { char_u *s; char_u *cpo_save = p_cpo; p_cpo = (char_u *)""; // Allow <> notation + // Need to put string in allocated memory, because do_map() will modify it. s = vim_strsave(map); - (void)do_map(0, s, mode, FALSE); + (void)do_map(nore ? 2 : 0, s, mode, false); xfree(s); p_cpo = cpo_save; } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 0ce2b586e3..2a72dbcd09 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -138,14 +138,14 @@ EXTERN int mod_mask INIT(= 0x0); // current key modifiers // update_screen(). EXTERN int cmdline_row; -EXTERN int redraw_cmdline INIT(= false); // cmdline must be redrawn -EXTERN int clear_cmdline INIT(= false); // cmdline must be cleared -EXTERN int mode_displayed INIT(= false); // mode is being displayed -EXTERN int cmdline_star INIT(= false); // cmdline is crypted -EXTERN int redrawing_cmdline INIT(= false); // cmdline is being redrawn -EXTERN int cmdline_was_last_drawn INIT(= false); // cmdline was last drawn +EXTERN bool redraw_cmdline INIT(= false); // cmdline must be redrawn +EXTERN bool clear_cmdline INIT(= false); // cmdline must be cleared +EXTERN bool mode_displayed INIT(= false); // mode is being displayed +EXTERN int cmdline_star INIT(= false); // cmdline is encrypted +EXTERN bool redrawing_cmdline INIT(= false); // cmdline is being redrawn +EXTERN bool cmdline_was_last_drawn INIT(= false); // cmdline was last drawn -EXTERN int exec_from_reg INIT(= false); // executing register +EXTERN bool exec_from_reg INIT(= false); // executing register // When '$' is included in 'cpoptions' option set: // When a change command is given that deletes only part of a line, a dollar @@ -165,7 +165,7 @@ EXTERN int compl_interrupted INIT(= false); // Set when doing something for completion that may call edit() recursively, // which is not allowed. Also used to disable folding during completion -EXTERN int compl_busy INIT(= false); +EXTERN bool compl_busy INIT(= false); // List of flags for method of completion. EXTERN int compl_cont_status INIT(= 0); @@ -201,23 +201,23 @@ EXTERN bool msg_did_scroll INIT(= false); EXTERN char_u *keep_msg INIT(= NULL); // msg to be shown after redraw EXTERN int keep_msg_attr INIT(= 0); // highlight attr for keep_msg -EXTERN int keep_msg_more INIT(= false); // keep_msg was set by msgmore() -EXTERN int need_fileinfo INIT(= false); // do fileinfo() after redraw +EXTERN bool keep_msg_more INIT(= false); // keep_msg was set by msgmore() +EXTERN bool need_fileinfo INIT(= false); // do fileinfo() after redraw EXTERN int msg_scroll INIT(= false); // msg_start() will scroll -EXTERN int msg_didout INIT(= false); // msg_outstr() was used in line -EXTERN int msg_didany INIT(= false); // msg_outstr() was used at all -EXTERN int msg_nowait INIT(= false); // don't wait for this msg +EXTERN bool msg_didout INIT(= false); // msg_outstr() was used in line +EXTERN bool msg_didany INIT(= false); // msg_outstr() was used at all +EXTERN bool msg_nowait INIT(= false); // don't wait for this msg EXTERN int emsg_off INIT(= 0); // don't display errors for now, // unless 'debug' is set. -EXTERN int info_message INIT(= false); // printing informative message +EXTERN bool info_message INIT(= false); // printing informative message EXTERN bool msg_hist_off INIT(= false); // don't add messages to history -EXTERN int need_clr_eos INIT(= false); // need to clear text before +EXTERN bool need_clr_eos INIT(= false); // need to clear text before // displaying a message. EXTERN int emsg_skip INIT(= 0); // don't display errors for // expression that is skipped EXTERN bool emsg_severe INIT(= false); // use message of next of several // emsg() calls for throw -EXTERN int did_endif INIT(= false); // just had ":endif" +EXTERN bool did_endif INIT(= false); // just had ":endif" EXTERN dict_T vimvardict; // Dictionary with v: variables EXTERN dict_T globvardict; // Dictionary with g: variables /// g: value @@ -225,25 +225,24 @@ EXTERN dict_T globvardict; // Dictionary with g: variables EXTERN int did_emsg; // set by emsg() when the message // is displayed or thrown EXTERN bool called_vim_beep; // set if vim_beep() is called -EXTERN int did_emsg_syntax; // did_emsg set because of a +EXTERN bool did_emsg_syntax; // did_emsg set because of a // syntax error EXTERN int called_emsg; // always set by emsg() EXTERN int ex_exitval INIT(= 0); // exit value for ex mode EXTERN bool emsg_on_display INIT(= false); // there is an error message -EXTERN int rc_did_emsg INIT(= false); // vim_regcomp() called emsg() +EXTERN bool rc_did_emsg INIT(= false); // vim_regcomp() called emsg() -EXTERN int no_wait_return INIT(= 0); // don't wait for return for now -EXTERN int need_wait_return INIT(= 0); // need to wait for return later -EXTERN int did_wait_return INIT(= false); // wait_return() was used and - // nothing written since then -EXTERN int need_maketitle INIT(= true); // call maketitle() soon +EXTERN int no_wait_return INIT(= 0); // don't wait for return for now +EXTERN bool need_wait_return INIT(= false); // need to wait for return later +EXTERN bool did_wait_return INIT(= false); // wait_return() was used and + // nothing written since then +EXTERN bool need_maketitle INIT(= true); // call maketitle() soon EXTERN int quit_more INIT(= false); // 'q' hit at "--more--" msg -EXTERN int ex_keep_indent INIT(= false); // getexmodeline(): keep indent EXTERN int vgetc_busy INIT(= 0); // when inside vgetc() then > 0 -EXTERN int didset_vim INIT(= false); // did set $VIM ourselves -EXTERN int didset_vimruntime INIT(= false); // idem for $VIMRUNTIME +EXTERN bool didset_vim INIT(= false); // did set $VIM ourselves +EXTERN bool didset_vimruntime INIT(= false); // idem for $VIMRUNTIME /// Lines left before a "more" message. Ex mode needs to be able to reset this /// after you type something. @@ -369,7 +368,7 @@ EXTERN colnr_T search_match_endcol; // col nr of match end EXTERN linenr_T search_first_line INIT(= 0); // for :{FIRST},{last}s/pat EXTERN linenr_T search_last_line INIT(= MAXLNUM); // for :{first},{LAST}s/pat -EXTERN int no_smartcase INIT(= false); // don't use 'smartcase' once +EXTERN bool no_smartcase INIT(= false); // don't use 'smartcase' once EXTERN int need_check_timestamps INIT(= false); // need to check file // timestamps asap @@ -450,7 +449,7 @@ EXTERN frame_T *topframe; // top of the window frame tree EXTERN tabpage_T *first_tabpage; EXTERN tabpage_T *lastused_tabpage; EXTERN tabpage_T *curtab; -EXTERN int redraw_tabline INIT(= false); // need to redraw tabline +EXTERN bool redraw_tabline INIT(= false); // need to redraw tabline // Iterates over all tabs in the tab list # define FOR_ALL_TABS(tp) for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) @@ -502,15 +501,12 @@ EXTERN volatile int full_screen INIT(= false); /// Non-zero when only "safe" commands are allowed, e.g. when sourcing .exrc or /// .vimrc in current directory. -EXTERN int secure INIT(= false); +EXTERN int secure INIT(= 0); /// Non-zero when changing text and jumping to another window/buffer is not /// allowed. EXTERN int textlock INIT(= 0); -/// Non-zero when the current buffer can't be changed. Used for FileChangedRO. -EXTERN int curbuf_lock INIT(= 0); - /// Non-zero when no buffer name can be changed, no buffer can be deleted and /// current directory can't be changed. Used for SwapExists et al. EXTERN int allbuf_lock INIT(= 0); @@ -528,6 +524,8 @@ EXTERN pos_T VIsual; EXTERN int VIsual_active INIT(= false); /// Whether Select mode is active. EXTERN int VIsual_select INIT(= false); +/// Restart Select mode when next cmd finished +EXTERN int restart_VIsual_select INIT(= 0); /// Whether to restart the selection after a Select-mode mapping or menu. EXTERN int VIsual_reselect; /// Type of Visual mode. @@ -559,7 +557,7 @@ EXTERN int end_comment_pending INIT(= NUL); // know that it should not attempt to perform scrollbinding due to the scroll // that was a result of the ":syncbind." (Otherwise, check_scrollbind() will // undo some of the work done by ":syncbind.") -ralston -EXTERN int did_syncbind INIT(= false); +EXTERN bool did_syncbind INIT(= false); // This flag is set when a smart indent has been performed. When the next typed // character is a '{' the inserted tab will be deleted again. @@ -624,8 +622,8 @@ EXTERN long opcount INIT(= 0); // count for pending operator EXTERN int motion_force INIT(=0); // motion force for pending operator // Ex Mode (Q) state -EXTERN int exmode_active INIT(= 0); // Zero, EXMODE_NORMAL or EXMODE_VIM. -EXTERN int ex_no_reprint INIT(=false); // No need to print after z or p. +EXTERN bool exmode_active INIT(= false); // true if Ex mode is active +EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p. EXTERN int reg_recording INIT(= 0); // register for recording or zero EXTERN int reg_executing INIT(= 0); // register being executed or zero @@ -646,7 +644,7 @@ EXTERN int arrow_used; // Normally false, set to true after EXTERN bool ins_at_eol INIT(= false); // put cursor after eol when // restarting edit after CTRL-O -EXTERN int no_abbr INIT(= true); // true when no abbreviations loaded +EXTERN bool no_abbr INIT(= true); // true when no abbreviations loaded EXTERN int mapped_ctrl_c INIT(= 0); // Modes where CTRL-C is mapped. @@ -666,7 +664,7 @@ EXTERN bool cmd_silent INIT(= false); // don't echo the command line EXTERN int swap_exists_action INIT(= SEA_NONE); // For dialog when swap file already // exists. -EXTERN int swap_exists_did_quit INIT(= false); +EXTERN bool swap_exists_did_quit INIT(= false); // Selected "quit" at the dialog. EXTERN char_u IObuff[IOSIZE]; ///< Buffer for sprintf, I/O, etc. @@ -703,14 +701,14 @@ EXTERN bool do_redraw INIT(= false); // extra redraw once EXTERN bool must_redraw_pum INIT(= false); // redraw pum. NB: must_redraw // should also be set. -EXTERN int need_highlight_changed INIT(= true); +EXTERN bool need_highlight_changed INIT(= true); EXTERN FILE *scriptout INIT(= NULL); ///< Stream to write script to. // volatile because it is used in a signal handler. EXTERN volatile int got_int INIT(= false); // set to true when interrupt // signal occurred -EXTERN int bangredo INIT(= false); // set to true with ! command +EXTERN bool bangredo INIT(= false); // set to true with ! command EXTERN int searchcmdlen; // length of previous search cmd EXTERN int reg_do_extmatch INIT(= 0); // Used when compiling regexp: // REX_SET to allow \z\(...\), @@ -720,14 +718,14 @@ EXTERN reg_extmatch_T *re_extmatch_in INIT(= NULL); // Set by vim_regexec() to store \z\(...\) matches EXTERN reg_extmatch_T *re_extmatch_out INIT(= NULL); -EXTERN int did_outofmem_msg INIT(= false); +EXTERN bool did_outofmem_msg INIT(= false); // set after out of memory msg -EXTERN int did_swapwrite_msg INIT(= false); +EXTERN bool did_swapwrite_msg INIT(= false); // set after swap write error msg EXTERN int global_busy INIT(= 0); // set when :global is executing -EXTERN int listcmd_busy INIT(= false); // set when :argdo, :windo or +EXTERN bool listcmd_busy INIT(= false); // set when :argdo, :windo or // :bufdo is executing -EXTERN int need_start_insertmode INIT(= false); +EXTERN bool need_start_insertmode INIT(= false); // start insert mode soon EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":) EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "." @@ -735,16 +733,16 @@ EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline EXTERN char_u *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline EXTERN char_u *autocmd_match INIT(= NULL); // name for <amatch> on cmdline -EXTERN int did_cursorhold INIT(= false); // set when CursorHold t'gerd +EXTERN bool did_cursorhold INIT(= false); // set when CursorHold t'gerd EXTERN int postponed_split INIT(= 0); // for CTRL-W CTRL-] command EXTERN int postponed_split_flags INIT(= 0); // args for win_split() EXTERN int postponed_split_tab INIT(= 0); // cmdmod.tab EXTERN int g_do_tagpreview INIT(= 0); // for tag preview commands: // height of preview window -EXTERN int g_tag_at_cursor INIT(= false); // whether the tag command comes - // from the command line (0) or was - // invoked as a normal command (1) +EXTERN bool g_tag_at_cursor INIT(= false); // whether the tag command comes + // from the command line (0) or was + // invoked as a normal command (1) EXTERN int replace_offset INIT(= 0); // offset for replace_push() @@ -758,7 +756,7 @@ EXTERN int keep_help_flag INIT(= false); // doing :ta from help file // everywhere. EXTERN char_u *empty_option INIT(= (char_u *)""); -EXTERN int redir_off INIT(= false); // no redirection for a moment +EXTERN bool redir_off INIT(= false); // no redirection for a moment EXTERN FILE *redir_fd INIT(= NULL); // message redirection file EXTERN int redir_reg INIT(= 0); // message redirection register EXTERN int redir_vname INIT(= 0); // message redirection variable @@ -792,8 +790,8 @@ extern char_u *compiled_sys; EXTERN char_u *globaldir INIT(= NULL); // Whether 'keymodel' contains "stopsel" and "startsel". -EXTERN int km_stopsel INIT(= false); -EXTERN int km_startsel INIT(= false); +EXTERN bool km_stopsel INIT(= false); +EXTERN bool km_startsel INIT(= false); EXTERN int cedit_key INIT(= -1); ///< key value of 'cedit' option EXTERN int cmdwin_type INIT(= 0); ///< type of cmdline window or 0 @@ -878,6 +876,7 @@ EXTERN char_u e_invexpr2[] INIT(= N_("E15: Invalid expression: %s")); EXTERN char_u e_invrange[] INIT(= N_("E16: Invalid range")); EXTERN char_u e_invcmd[] INIT(= N_("E476: Invalid command")); EXTERN char_u e_isadir2[] INIT(= N_("E17: \"%s\" is a directory")); +EXTERN char_u e_no_spell[] INIT(= N_("E756: Spell checking is not possible")); EXTERN char_u e_invchan[] INIT(= N_("E900: Invalid channel id")); EXTERN char_u e_invchanjob[] INIT(= N_("E900: Invalid channel id: not a job")); EXTERN char_u e_jobtblfull[] INIT(= N_("E901: Job table is full")); @@ -973,6 +972,7 @@ EXTERN char_u e_write[] INIT(= N_("E80: Error while writing")); EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required")); EXTERN char_u e_usingsid[] INIT(= N_( "E81: Using <SID> not in a script context")); +EXTERN char_u e_missingparen[] INIT(= N_("E107: Missing parentheses: %s")); EXTERN char_u e_maxmempat[] INIT(= N_( "E363: pattern uses more memory than 'maxmempattern'")); EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer")); diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index 724363674c..dee096214f 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -86,7 +86,7 @@ struct ScreenGrid { int zindex; // Below is state owned by the compositor. Should generally not be set/read - // outside this module, except for specific compatibilty hacks + // outside this module, except for specific compatibility hacks // position of the grid on the composed screen. int comp_row; diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index abba5425e7..1b1735c991 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -1767,7 +1767,7 @@ static bool prt_open_resource(struct prt_ps_resource_S *resource) break; case PRT_DSC_ENDCOMMENTS_TYPE: - // Wont find title or resource after this comment, stop searching + // Won't find title or resource after this comment, stop searching seen_all = true; break; @@ -2398,8 +2398,16 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) EMSG2(_("E456: Can't open file \"%s\""), resource->filename); return FALSE; } - prt_dsc_resources("BeginResource", prt_resource_types[resource->type], - (char *)resource->title); + switch (resource->type) { + case PRT_RESOURCE_TYPE_PROCSET: + case PRT_RESOURCE_TYPE_ENCODING: + case PRT_RESOURCE_TYPE_CMAP: + prt_dsc_resources("BeginResource", prt_resource_types[resource->type], + (char *)resource->title); + break; + default: + return FALSE; + } prt_dsc_textline("BeginDocument", (char *)resource->filename); diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 79e474fa2e..7341ac9393 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -25,28 +25,22 @@ static bool hlstate_active = false; static kvec_t(HlEntry) attr_entries = KV_INITIAL_VALUE; -static Map(HlEntry, int) *attr_entry_ids; -static Map(int, int) *combine_attr_entries; -static Map(int, int) *blend_attr_entries; -static Map(int, int) *blendthrough_attr_entries; +static Map(HlEntry, int) attr_entry_ids = MAP_INIT; +static Map(int, int) combine_attr_entries = MAP_INIT; +static Map(int, int) blend_attr_entries = MAP_INIT; +static Map(int, int) blendthrough_attr_entries = MAP_INIT; /// highlight entries private to a namespace -static Map(ColorKey, ColorItem) *ns_hl; +static Map(ColorKey, ColorItem) ns_hl; void highlight_init(void) { - attr_entry_ids = map_new(HlEntry, int)(); - combine_attr_entries = map_new(int, int)(); - blend_attr_entries = map_new(int, int)(); - blendthrough_attr_entries = map_new(int, int)(); - ns_hl = map_new(ColorKey, ColorItem)(); - // index 0 is no attribute, add dummy entry: kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown, .id1 = 0, .id2 = 0 })); } -/// @return TRUE if hl table was reset +/// @return true if hl table was reset bool highlight_use_hlstate(void) { if (hlstate_active) { @@ -71,7 +65,7 @@ static int get_attr_entry(HlEntry entry) entry.id2 = 0; } - int id = map_get(HlEntry, int)(attr_entry_ids, entry); + int id = map_get(HlEntry, int)(&attr_entry_ids, entry); if (id > 0) { return id; } @@ -104,7 +98,7 @@ static int get_attr_entry(HlEntry entry) id = (int)next_id; kv_push(attr_entries, entry); - map_put(HlEntry, int)(attr_entry_ids, entry, id); + map_put(HlEntry, int)(&attr_entry_ids, entry, id); Array inspect = hl_inspect(id); @@ -154,7 +148,7 @@ void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id) { DecorProvider *p = get_decor_provider(ns_id, true); if ((attrs.rgb_ae_attr & HL_DEFAULT) - && map_has(ColorKey, ColorItem)(ns_hl, ColorKey(ns_id, hl_id))) { + && map_has(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id))) { return; } int attr_id = link_id > 0 ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs); @@ -162,7 +156,7 @@ void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id) .link_id = link_id, .version = p->hl_valid, .is_default = (attrs.rgb_ae_attr & HL_DEFAULT) }; - map_put(ColorKey, ColorItem)(ns_hl, ColorKey(ns_id, hl_id), it); + map_put(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id), it); } int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault) @@ -177,7 +171,7 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault) } DecorProvider *p = get_decor_provider(ns_id, true); - ColorItem it = map_get(ColorKey, ColorItem)(ns_hl, ColorKey(ns_id, hl_id)); + ColorItem it = map_get(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id)); // TODO(bfredl): map_ref true even this? bool valid_cache = it.version >= p->hl_valid; @@ -220,7 +214,7 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault) it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs); it.version = p->hl_valid-tmp; it.is_default = attrs.rgb_ae_attr & HL_DEFAULT; - map_put(ColorKey, ColorItem)(ns_hl, ColorKey(ns_id, hl_id), it); + map_put(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id), it); } if (it.is_default && nodefault) { @@ -395,28 +389,28 @@ void clear_hl_tables(bool reinit) { if (reinit) { kv_size(attr_entries) = 1; - map_clear(HlEntry, int)(attr_entry_ids); - map_clear(int, int)(combine_attr_entries); - map_clear(int, int)(blend_attr_entries); - map_clear(int, int)(blendthrough_attr_entries); + map_clear(HlEntry, int)(&attr_entry_ids); + map_clear(int, int)(&combine_attr_entries); + map_clear(int, int)(&blend_attr_entries); + map_clear(int, int)(&blendthrough_attr_entries); memset(highlight_attr_last, -1, sizeof(highlight_attr_last)); highlight_attr_set_all(); highlight_changed(); screen_invalidate_highlights(); } else { kv_destroy(attr_entries); - map_free(HlEntry, int)(attr_entry_ids); - map_free(int, int)(combine_attr_entries); - map_free(int, int)(blend_attr_entries); - map_free(int, int)(blendthrough_attr_entries); - map_free(ColorKey, ColorItem)(ns_hl); + map_destroy(HlEntry, int)(&attr_entry_ids); + map_destroy(int, int)(&combine_attr_entries); + map_destroy(int, int)(&blend_attr_entries); + map_destroy(int, int)(&blendthrough_attr_entries); + map_destroy(ColorKey, ColorItem)(&ns_hl); } } void hl_invalidate_blends(void) { - map_clear(int, int)(blend_attr_entries); - map_clear(int, int)(blendthrough_attr_entries); + map_clear(int, int)(&blend_attr_entries); + map_clear(int, int)(&blendthrough_attr_entries); highlight_changed(); update_window_hl(curwin, true); } @@ -437,7 +431,7 @@ int hl_combine_attr(int char_attr, int prim_attr) // TODO(bfredl): could use a struct for clearer intent. int combine_tag = (char_attr << 16) + prim_attr; - int id = map_get(int, int)(combine_attr_entries, combine_tag); + int id = map_get(int, int)(&combine_attr_entries, combine_tag); if (id > 0) { return id; } @@ -494,7 +488,7 @@ int hl_combine_attr(int char_attr, int prim_attr) id = get_attr_entry((HlEntry){ .attr = new_en, .kind = kHlCombine, .id1 = char_attr, .id2 = prim_attr }); if (id > 0) { - map_put(int, int)(combine_attr_entries, combine_tag, id); + map_put(int, int)(&combine_attr_entries, combine_tag, id); } return id; @@ -550,8 +544,8 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through) int combine_tag = (back_attr << 16) + front_attr; Map(int, int) *map = (*through - ? blendthrough_attr_entries - : blend_attr_entries); + ? &blendthrough_attr_entries + : &blend_attr_entries); int id = map_get(int, int)(map, combine_tag); if (id > 0) { return id; diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index ed4aefb577..a0e8bad11f 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -63,7 +63,9 @@ typedef enum { , HLF_M // "--More--" message , HLF_CM // Mode (e.g., "-- INSERT --") , HLF_N // line number for ":number" and ":#" commands - , HLF_CLN // current line number + , HLF_LNA // LineNrAbove + , HLF_LNB // LineNrBelow + , HLF_CLN // current line number when 'cursorline' is set , HLF_R // return to continue message and yes/no questions , HLF_S // status lines , HLF_SNC // status lines of not-current windows @@ -118,6 +120,8 @@ EXTERN const char *hlf_names[] INIT(= { [HLF_M] = "MoreMsg", [HLF_CM] = "ModeMsg", [HLF_N] = "LineNr", + [HLF_LNA] = "LineNrAbove", + [HLF_LNB] = "LineNrBelow", [HLF_CLN] = "CursorLineNr", [HLF_R] = "Question", [HLF_S] = "StatusLine", diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 8fa61515ef..a6df0e97e6 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -19,6 +19,7 @@ #include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/option.h" +#include "nvim/plines.h" #include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/search.h" @@ -62,10 +63,10 @@ int get_indent_buf(buf_T *buf, linenr_T lnum) } -// Count the size (in window cells) of the indent in line "ptr", with -// 'tabstop' at "ts". -// If @param list is TRUE, count only screen size for tabs. -int get_indent_str(const char_u *ptr, int ts, int list) +/// Count the size (in window cells) of the indent in line "ptr", with +/// 'tabstop' at "ts". +/// If @param list is true, count only screen size for tabs. +int get_indent_str(const char_u *ptr, int ts, bool list) FUNC_ATTR_NONNULL_ALL { int count = 0; @@ -91,9 +92,9 @@ int get_indent_str(const char_u *ptr, int ts, int list) return count; } -// Count the size (in window cells) of the indent in line "ptr", using -// variable tabstops. -// if "list" is true, count only screen size for tabs. +/// Count the size (in window cells) of the indent in line "ptr", using +/// variable tabstops. +/// if "list" is true, count only screen size for tabs. int get_indent_str_vtab(const char_u *ptr, long ts, long *vts, bool list) { int count = 0; @@ -432,7 +433,7 @@ int get_number_indent(linenr_T lnum) // Return appropriate space number for breakindent, taking influencing // parameters into account. Window must be specified, since it is not // necessarily always the current one. -int get_breakindent_win(win_T *wp, const char_u *line) +int get_breakindent_win(win_T *wp, char_u *line) FUNC_ATTR_NONNULL_ALL { static int prev_indent = 0; // Cached indent value. @@ -462,12 +463,32 @@ int get_breakindent_win(win_T *wp, const char_u *line) } bri = prev_indent + wp->w_briopt_shift; + // Add offset for number column, if 'n' is in 'cpoptions' + bri += win_col_off2(wp); + + // add additional indent for numbered lists + if (wp->w_briopt_list != 0) { + regmatch_T regmatch = { + .regprog = vim_regcomp(curbuf->b_p_flp, + RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT), + }; + + if (regmatch.regprog != NULL) { + if (vim_regexec(®match, line, 0)) { + if (wp->w_briopt_list > 0) { + bri += wp->w_briopt_list; + } else { + bri = (int)(*regmatch.endp - *regmatch.startp); + } + } + vim_regfree(regmatch.regprog); + } + } + // indent minus the length of the showbreak string if (wp->w_briopt_sbr) { - bri -= vim_strsize(p_sbr); + bri -= vim_strsize(get_showbreak_value(wp)); } - // Add offset for number column, if 'n' is in 'cpoptions' - bri += win_col_off2(wp); // never indent past left window margin if (bri < 0) { diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 6dacace0a4..c6966ff9fa 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -543,7 +543,7 @@ unsigned int trans_special(const char_u **srcp, const size_t src_len, /// Put the character sequence for "key" with "modifiers" into "dst" and return /// the resulting length. -/// When "keycode" is TRUE prefer key code, e.g. K_DEL instead of DEL. +/// When "keycode" is true prefer key code, e.g. K_DEL instead of DEL. /// The sequence is not NUL terminated. /// This is how characters in a string are encoded. unsigned int special_to_buf(int key, int modifiers, bool keycode, char_u *dst) @@ -628,7 +628,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') { bp += 3; // skip t_xx, xx may be '-' or '>' } else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) { - vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0); + vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0, true); + if (l == 0) { + EMSG(_(e_invarg)); + return 0; + } bp += l + 5; break; } @@ -654,7 +658,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, if (STRNICMP(last_dash + 1, "char-", 5) == 0 && ascii_isdigit(last_dash[6])) { // <Char-123> or <Char-033> or <Char-0x33> - vim_str2nr(last_dash + 6, NULL, NULL, STR2NR_ALL, NULL, &n, 0); + vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, true); + if (l == 0) { + EMSG(_(e_invarg)); + return 0; + } key = (int)n; } else { int off = 1; diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index d31196d412..9fc44f6f84 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -9,11 +9,11 @@ * Any special key code sequences are replaced by these codes. */ -/* - * For MSDOS some keys produce codes larger than 0xff. They are split into two - * chars, the first one is K_NUL. - */ -#define K_NUL (0xce) // for MSDOS: special key follows +// +// For MS-DOS some keys produce codes larger than 0xff. They are split into two +// chars, the first one is K_NUL. +// +#define K_NUL (0xce) // for MS-DOS: special key follows /* * K_SPECIAL is the first byte of a special key code and is always followed by diff --git a/src/nvim/log.h b/src/nvim/log.h index 17d754c033..654b682de8 100644 --- a/src/nvim/log.h +++ b/src/nvim/log.h @@ -7,7 +7,7 @@ #include "auto/config.h" #include "nvim/macros.h" -// USDT probes. Example invokation: +// USDT probes. Example invocation: // NVIM_PROBE(nvim_foo_bar, 1, string.data); #if defined(HAVE_SYS_SDT_H) #include <sys/sdt.h> // NOLINT diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index ce8c9b0d06..1a59cd94ae 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -196,8 +196,9 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) { bool ret = true; const int initial_size = lua_gettop(lstate); - kvec_t(TVPopStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); + kvec_withinit_t(TVPopStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + kvi_push(stack, ((TVPopStackItem) { ret_tv, false, false, 0 })); while (ret && kv_size(stack)) { if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3); @@ -234,7 +235,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) tv_list_append_owned_tv(kv_pair, (typval_T) { .v_type = VAR_UNKNOWN, }); - kv_push(stack, cur); + kvi_push(stack, cur); tv_list_append_list(cur.tv->vval.v_list, kv_pair); cur = (TVPopStackItem) { .tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)), @@ -247,7 +248,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { abort(); } - kv_push(stack, cur); + kvi_push(stack, cur); cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; } } else { @@ -265,7 +266,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) { .v_type = VAR_UNKNOWN, }); - kv_push(stack, cur); + kvi_push(stack, cur); // TODO(ZyX-I): Use indexes, here list item *will* be reallocated. cur = (TVPopStackItem) { .tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)), @@ -343,7 +344,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) if (table_props.maxidx != 0) { cur.container = true; cur.idx = lua_gettop(lstate); - kv_push(stack, cur); + kvi_push(stack, cur); } break; } @@ -373,7 +374,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } cur.container = true; cur.idx = lua_gettop(lstate); - kv_push(stack, cur); + kvi_push(stack, cur); lua_pushnil(lstate); } break; @@ -434,7 +435,7 @@ nlua_pop_typval_table_processing_end: lua_pop(lstate, 1); } } - kv_destroy(stack); + kvi_destroy(stack); if (!ret) { tv_clear(ret_tv); *ret_tv = (typval_T) { @@ -1060,15 +1061,16 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) { Object ret = NIL; const int initial_size = lua_gettop(lstate); - kvec_t(ObjPopStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((ObjPopStackItem) { &ret, false })); + kvec_withinit_t(ObjPopStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + kvi_push(stack, ((ObjPopStackItem) { &ret, false })); while (!ERROR_SET(err) && kv_size(stack)) { - if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { - api_set_error(err, kErrorTypeException, "Lua failed to grow stack"); - break; - } ObjPopStackItem cur = kv_pop(stack); if (cur.container) { + if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { + api_set_error(err, kErrorTypeException, "Lua failed to grow stack"); + break; + } if (cur.obj->type == kObjectTypeDictionary) { // stack: …, dict, key if (cur.obj->data.dictionary.size @@ -1095,7 +1097,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) .data = xmemdupz(s, len), .size = len, }; - kv_push(stack, cur); + kvi_push(stack, cur); cur = (ObjPopStackItem) { .obj = &cur.obj->data.dictionary.items[idx].value, .container = false, @@ -1117,7 +1119,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) lua_pop(lstate, 2); continue; } - kv_push(stack, cur); + kvi_push(stack, cur); cur = (ObjPopStackItem) { .obj = &cur.obj->data.array.items[idx], .container = false, @@ -1169,7 +1171,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) sizeof(cur.obj->data.array.items[0])); cur.obj->data.array.capacity = table_props.maxidx; cur.container = true; - kv_push(stack, cur); + kvi_push(stack, cur); } break; } @@ -1185,7 +1187,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) sizeof(cur.obj->data.dictionary.items[0])); cur.obj->data.dictionary.capacity = table_props.string_keys_num; cur.container = true; - kv_push(stack, cur); + kvi_push(stack, cur); lua_pushnil(lstate); } break; @@ -1239,7 +1241,7 @@ type_error: lua_pop(lstate, 1); } } - kv_destroy(stack); + kvi_destroy(stack); if (ERROR_SET(err)) { api_free_object(ret); ret = NIL; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 0a52cc16cb..d071203db1 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -13,7 +13,6 @@ #include "nvim/func_attr.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/private/handle.h" #include "nvim/api/vim.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/vim.h" @@ -34,17 +33,22 @@ #include "nvim/eval/userfunc.h" #include "nvim/event/time.h" #include "nvim/event/loop.h" +#include "mpack/lmpack.h" #include "nvim/os/os.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/lua/treesitter.h" +#include "nvim/lua/xdiff.h" #include "luv/luv.h" static int in_fast_callback = 0; +// Initialized in nlua_init(). +static lua_State *global_lstate = NULL; + typedef struct { Error err; String lua_err_str; @@ -65,7 +69,8 @@ typedef struct { } #if __has_feature(address_sanitizer) - PMap(handle_T) *nlua_ref_markers = NULL; + static PMap(handle_T) nlua_ref_markers = MAP_INIT; + static bool nlua_track_refs = false; # define NLUA_TRACK_REFS #endif @@ -144,12 +149,12 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 1; } -/// convert byte index to UTF-32 and UTF-16 indicies +/// convert byte index to UTF-32 and UTF-16 indices /// /// Expects a string and an optional index. If no index is supplied, the length /// of the string is returned. /// -/// Returns two values: the UTF-32 and UTF-16 indicies. +/// Returns two values: the UTF-32 and UTF-16 indices. static int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { size_t s1_len; @@ -173,7 +178,7 @@ static int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 2; } -/// convert UTF-32 or UTF-16 indicies to byte index. +/// convert UTF-32 or UTF-16 indices to byte index. /// /// Expects up to three args: string, index and use_utf16. /// If use_utf16 is not supplied it defaults to false (use UTF-32) @@ -234,7 +239,7 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, multiqueue_put(main_loop.events, nlua_luv_error_event, 1, xstrdup(error)); - lua_pop(lstate, 1); // error mesage + lua_pop(lstate, 1); // error message retval = -status; } else { // LUA_OK if (nresult == LUA_MULTRET) { @@ -250,7 +255,7 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, static void nlua_schedule_event(void **argv) { LuaRef cb = (LuaRef)(ptrdiff_t)argv[0]; - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; nlua_pushref(lstate, cb); nlua_unref(lstate, cb); if (lua_pcall(lstate, 0, 0, 0)) { @@ -502,6 +507,8 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setfield(lstate, -2, "__tostring"); lua_setmetatable(lstate, -2); nlua_nil_ref = nlua_ref(lstate, -1); + lua_pushvalue(lstate, -1); + lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL"); lua_setfield(lstate, -2, "NIL"); // vim._empty_dict_mt @@ -509,11 +516,30 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_empty_dict_tostring); lua_setfield(lstate, -2, "__tostring"); nlua_empty_dict_ref = nlua_ref(lstate, -1); + lua_pushvalue(lstate, -1); + lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict"); lua_setfield(lstate, -2, "_empty_dict_mt"); + // vim.mpack + luaopen_mpack(lstate); + lua_pushvalue(lstate, -1); + lua_setfield(lstate, -3, "mpack"); + + // package.loaded.mpack = vim.mpack + // otherwise luv will be reinitialized when require'mpack' + lua_getglobal(lstate, "package"); + lua_getfield(lstate, -1, "loaded"); + lua_pushvalue(lstate, -3); + lua_setfield(lstate, -2, "mpack"); + lua_pop(lstate, 3); + // internal vim._treesitter... API nlua_add_treesitter(lstate); + // vim.diff + lua_pushcfunction(lstate, &nlua_xdl_diff); + lua_setfield(lstate, -2, "diff"); + lua_setglobal(lstate, "vim"); { @@ -536,8 +562,17 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL return 1; } // [package, loaded, inspect] - lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded] + + code = (char *)&lua_F_module[0]; + if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/F.lua") + || lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s")); + return 1; + } + // [package, loaded, module] + lua_setfield(lstate, -2, "vim.F"); // [package, loaded] + lua_pop(lstate, 2); // [] } @@ -550,22 +585,34 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL } } + { + lua_getglobal(lstate, "package"); // [package] + lua_getfield(lstate, -1, "loaded"); // [package, loaded] + + const char *code = (char *)&lua_meta_module[0]; + if (luaL_loadbuffer(lstate, code, strlen(code), "@vim/_meta.lua") + || lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s")); + return 1; + } + // [package, loaded, module] + lua_setfield(lstate, -2, "vim._meta"); // [package, loaded] + + lua_pop(lstate, 2); // [] + } + return 0; } -/// Initialize lua interpreter +/// Initialize global lua interpreter /// -/// Crashes Nvim if initialization fails. Should be called once per lua -/// interpreter instance. -/// -/// @return New lua interpreter instance. -static lua_State *nlua_init(void) - FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +/// Crashes Nvim if initialization fails. +void nlua_init(void) { #ifdef NLUA_TRACK_REFS const char *env = os_getenv("NVIM_LUA_NOTRACK"); if (!env || !*env) { - nlua_ref_markers = pmap_new(handle_T)(); + nlua_track_refs = true; } #endif @@ -577,28 +624,9 @@ static lua_State *nlua_init(void) luaL_openlibs(lstate); nlua_state_init(lstate); - return lstate; + global_lstate = lstate; } -// only to be used by nlua_enter and nlua_free_all_mem! -static lua_State *global_lstate = NULL; - -/// Enter lua interpreter -/// -/// Calls nlua_init() if needed. Is responsible for pre-lua call initalization -/// like updating `package.[c]path` with directories derived from &runtimepath. -/// -/// @return Interpreter instance to use. Will either be initialized now or -/// taken from previous initialization. -static lua_State *nlua_enter(void) - FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (global_lstate == NULL) { - global_lstate = nlua_init(); - } - lua_State *const lstate = global_lstate; - return lstate; -} void nlua_free_all_mem(void) { @@ -615,10 +643,10 @@ void nlua_free_all_mem(void) fprintf(stderr, "%d lua references were leaked!", nlua_refcount); } - if (nlua_ref_markers) { + if (nlua_track_refs) { // in case there are leaked luarefs, leak the associated memory // to get LeakSanitizer stacktraces on exit - pmap_free(handle_T)(nlua_ref_markers); + pmap_destroy(handle_T)(&nlua_ref_markers); } #endif @@ -800,13 +828,13 @@ int nlua_call(lua_State *lstate) try_start(); typval_T rettv; - int dummy; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; // call_func() retval is deceptive, ignore it. Instead we set `msg_list` // (TRY_WRAP) to capture abort-causing non-exception errors. - (void)call_func(name, (int)name_len, &rettv, nargs, - vim_args, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, NULL, NULL); + (void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe); if (!try_end(&err)) { nlua_push_typval(lstate, &rettv, false); } @@ -1017,9 +1045,9 @@ LuaRef nlua_ref(lua_State *lstate, int index) if (ref > 0) { nlua_refcount++; #ifdef NLUA_TRACK_REFS - if (nlua_ref_markers) { + if (nlua_track_refs) { // dummy allocation to make LeakSanitizer track our luarefs - pmap_put(handle_T)(nlua_ref_markers, ref, xmalloc(3)); + pmap_put(handle_T)(&nlua_ref_markers, ref, xmalloc(3)); } #endif } @@ -1033,8 +1061,8 @@ void nlua_unref(lua_State *lstate, LuaRef ref) nlua_refcount--; #ifdef NLUA_TRACK_REFS // NB: don't remove entry from map to track double-unref - if (nlua_ref_markers) { - xfree(pmap_get(handle_T)(nlua_ref_markers, ref)); + if (nlua_track_refs) { + xfree(pmap_get(handle_T)(&nlua_ref_markers, ref)); } #endif luaL_unref(lstate, LUA_REGISTRYINDEX, ref); @@ -1043,8 +1071,7 @@ void nlua_unref(lua_State *lstate, LuaRef ref) void api_free_luaref(LuaRef ref) { - lua_State *const lstate = nlua_enter(); - nlua_unref(lstate, ref); + nlua_unref(global_lstate, ref); } /// push a value referenced in the registry @@ -1064,7 +1091,7 @@ LuaRef api_new_luaref(LuaRef original_ref) return LUA_NOREF; } - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; nlua_pushref(lstate, original_ref); LuaRef new_ref = nlua_ref(lstate, -1); lua_pop(lstate, 1); @@ -1143,7 +1170,7 @@ static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, return; } - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; if (luaL_loadbuffer(lstate, lcmd, lcmd_len, name)) { nlua_error(lstate, _("E5107: Error loading lua %.*s")); return; @@ -1161,6 +1188,34 @@ static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, } } +int nlua_source_using_linegetter(LineGetter fgetline, + void *cookie, char *name) +{ + const linenr_T save_sourcing_lnum = sourcing_lnum; + const sctx_T save_current_sctx = current_sctx; + current_sctx.sc_sid = SID_STR; + current_sctx.sc_seq = 0; + current_sctx.sc_lnum = 0; + sourcing_lnum = 0; + + garray_T ga; + char_u *line = NULL; + + ga_init(&ga, (int)sizeof(char_u *), 10); + while ((line = fgetline(0, cookie, 0, false)) != NULL) { + GA_APPEND(char_u *, &ga, line); + } + char *code = (char *)ga_concat_strings_sep(&ga, "\n"); + size_t len = strlen(code); + nlua_typval_exec(code, len, name, NULL, 0, false, NULL); + + sourcing_lnum = save_sourcing_lnum; + current_sctx = save_current_sctx; + ga_clear_strings(&ga); + xfree(code); + return OK; +} + /// Call a LuaCallable given some typvals /// /// Used to call any lua callable passed from Lua into VimL @@ -1205,7 +1260,7 @@ int typval_exec_lua_callable( /// @return Return value of the execution. Object nlua_exec(const String str, const Array args, Error *err) { - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; if (luaL_loadbuffer(lstate, str.data, str.size, "<nvim>")) { size_t len; @@ -1242,7 +1297,7 @@ Object nlua_exec(const String str, const Array args, Error *err) Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Error *err) { - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; nlua_pushref(lstate, ref); int nargs = (int)args.size; if (name != NULL) { @@ -1318,7 +1373,7 @@ void ex_luado(exarg_T *const eap) const char *const cmd = (const char *)eap->arg; const size_t cmd_len = strlen(cmd); - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; #define DOSTART "return function(line, linenr) " #define DOEND " end" @@ -1403,7 +1458,7 @@ void ex_luafile(exarg_T *const eap) bool nlua_exec_file(const char *path) FUNC_ATTR_NONNULL_ALL { - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; if (luaL_loadfile(lstate, path)) { nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s")); @@ -1452,7 +1507,7 @@ int nlua_expand_pat(expand_T *xp, int *num_results, char_u ***results) { - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; int ret = OK; // [ vim ] @@ -1462,7 +1517,7 @@ int nlua_expand_pat(expand_T *xp, lua_getfield(lstate, -1, "_expand_pat"); luaL_checktype(lstate, -1, LUA_TFUNCTION); - // [ vim, vim._log_keystroke, buf ] + // [ vim, vim._on_key, buf ] lua_pushlstring(lstate, (const char *)pat, STRLEN(pat)); if (lua_pcall(lstate, 1, 2, 0) != 0) { @@ -1662,7 +1717,7 @@ int nlua_CFunction_func_call( typval_T *rettv, void *state) { - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; LuaCFunctionState *funcstate = (LuaCFunctionState *)state; return typval_exec_lua_callable(lstate, funcstate->lua_callable, @@ -1671,7 +1726,7 @@ int nlua_CFunction_func_call( void nlua_CFunction_func_free(void *state) { - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; LuaCFunctionState *funcstate = (LuaCFunctionState *)state; nlua_unref(lstate, funcstate->lua_callable.func_ref); @@ -1702,7 +1757,7 @@ char_u *nlua_register_table_as_callable(typval_T *const arg) return NULL; } - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; #ifndef NDEBUG int top = lua_gettop(lstate); @@ -1736,12 +1791,12 @@ char_u *nlua_register_table_as_callable(typval_T *const arg) return name; } -void nlua_execute_log_keystroke(int c) +void nlua_execute_on_key(int c) { char_u buf[NUMBUFLEN]; size_t buf_len = special_to_buf(c, mod_mask, false, buf); - lua_State *const lstate = nlua_enter(); + lua_State *const lstate = global_lstate; #ifndef NDEBUG int top = lua_gettop(lstate); @@ -1750,17 +1805,17 @@ void nlua_execute_log_keystroke(int c) // [ vim ] lua_getglobal(lstate, "vim"); - // [ vim, vim._log_keystroke ] - lua_getfield(lstate, -1, "_log_keystroke"); + // [ vim, vim._on_key] + lua_getfield(lstate, -1, "_on_key"); luaL_checktype(lstate, -1, LUA_TFUNCTION); - // [ vim, vim._log_keystroke, buf ] + // [ vim, vim._on_key, buf ] lua_pushlstring(lstate, (const char *)buf, buf_len); if (lua_pcall(lstate, 1, 0, 0)) { nlua_error( lstate, - _("Error executing vim.log_keystroke lua callback: %.*s")); + _("Error executing vim.on_key Lua callback: %.*s")); } // [ vim ] diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c index c186928ae2..ed475c324f 100644 --- a/src/nvim/lua/treesitter.c +++ b/src/nvim/lua/treesitter.c @@ -18,7 +18,7 @@ #include "tree_sitter/api.h" #include "nvim/lua/treesitter.h" -#include "nvim/api/private/handle.h" +#include "nvim/api/private/helpers.h" #include "nvim/memline.h" #include "nvim/buffer.h" @@ -80,6 +80,10 @@ static struct luaL_Reg node_meta[] = { { "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 }, { NULL, NULL } }; @@ -101,7 +105,7 @@ static struct luaL_Reg treecursor_meta[] = { { NULL, NULL } }; -static PMap(cstr_t) *langs; +static PMap(cstr_t) langs = MAP_INIT; static void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) { @@ -119,8 +123,6 @@ static void build_meta(lua_State *L, const char *tname, const luaL_Reg *meta) /// all global state is stored in the regirstry of the lua_State void tslua_init(lua_State *L) { - langs = pmap_new(cstr_t)(); - // type metatables build_meta(L, TS_META_PARSER, parser_meta); build_meta(L, TS_META_TREE, tree_meta); @@ -133,7 +135,7 @@ void tslua_init(lua_State *L) int tslua_has_language(lua_State *L) { const char *lang_name = luaL_checkstring(L, 1); - lua_pushboolean(L, pmap_has(cstr_t)(langs, lang_name)); + lua_pushboolean(L, pmap_has(cstr_t)(&langs, lang_name)); return 1; } @@ -142,7 +144,7 @@ int tslua_add_language(lua_State *L) const char *path = luaL_checkstring(L, 1); const char *lang_name = luaL_checkstring(L, 2); - if (pmap_has(cstr_t)(langs, lang_name)) { + if (pmap_has(cstr_t)(&langs, lang_name)) { return 0; } @@ -185,7 +187,7 @@ int tslua_add_language(lua_State *L) TREE_SITTER_LANGUAGE_VERSION, lang_version); } - pmap_put(cstr_t)(langs, xstrdup(lang_name), lang); + pmap_put(cstr_t)(&langs, xstrdup(lang_name), lang); lua_pushboolean(L, true); return 1; @@ -195,7 +197,7 @@ int tslua_inspect_lang(lua_State *L) { const char *lang_name = luaL_checkstring(L, 1); - TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); + TSLanguage *lang = pmap_get(cstr_t)(&langs, lang_name); if (!lang) { return luaL_error(L, "no such language: %s", lang_name); } @@ -243,7 +245,7 @@ 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); + TSLanguage *lang = pmap_get(cstr_t)(&langs, lang_name); if (!lang) { return luaL_error(L, "no such language: %s", lang_name); } @@ -384,7 +386,7 @@ static int parser_parse(lua_State *L) // Sometimes parsing fails (timeout, or wrong parser ABI) // In those case, just return an error. if (!new_tree) { - return luaL_error(L, "An error occured when parsing."); + return luaL_error(L, "An error occurred when parsing."); } // The new tree will be pushed to the stack, without copy, owwership is now to @@ -992,6 +994,50 @@ static int node_parent(lua_State *L) return 1; } +static int node_next_sibling(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + TSNode sibling = ts_node_next_sibling(node); + push_node(L, sibling, 1); + return 1; +} + +static int node_prev_sibling(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + TSNode sibling = ts_node_prev_sibling(node); + push_node(L, sibling, 1); + return 1; +} + +static int node_next_named_sibling(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + TSNode sibling = ts_node_next_named_sibling(node); + push_node(L, sibling, 1); + return 1; +} + +static int node_prev_named_sibling(lua_State *L) +{ + TSNode node; + if (!node_check(L, 1, &node)) { + return 0; + } + TSNode sibling = ts_node_prev_named_sibling(node); + push_node(L, sibling, 1); + return 1; +} + /// assumes the match table being on top of the stack static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx) { @@ -1073,6 +1119,11 @@ static int node_rawquery(lua_State *L) // TODO(bfredl): these are expensive allegedly, // use a reuse list later on? TSQueryCursor *cursor = ts_query_cursor_new(); + // TODO(clason): API introduced after tree-sitter release 0.19.5 + // remove guard when minimum ts version is bumped to 0.19.6+ +#ifdef NVIM_TS_HAS_SET_MATCH_LIMIT + ts_query_cursor_set_match_limit(cursor, 32); +#endif ts_query_cursor_exec(cursor, query, node); bool captures = lua_toboolean(L, 3); @@ -1122,7 +1173,7 @@ int tslua_parse_query(lua_State *L) } const char *lang_name = lua_tostring(L, 1); - TSLanguage *lang = pmap_get(cstr_t)(langs, lang_name); + TSLanguage *lang = pmap_get(cstr_t)(&langs, lang_name); if (!lang) { return luaL_error(L, "no such language: %s", lang_name); } diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 5c9c5103a7..c6bbdee7ad 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -4,6 +4,7 @@ -- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the -- `inspect` and `lpeg` modules. -- 2. runtime/lua/vim/shared.lua: Code shared between Nvim and tests. +-- (This will go away if we migrate to nvim as the test-runner.) -- 3. src/nvim/lua/: Compiled-into Nvim itself. -- -- Guideline: "If in doubt, put it in the runtime". @@ -178,8 +179,8 @@ end --- Return a human-readable representation of the given object. --- ---@see https://github.com/kikito/inspect.lua ---@see https://github.com/mpeterv/vinspect +---@see https://github.com/kikito/inspect.lua +---@see https://github.com/mpeterv/vinspect local function inspect(object, options) -- luacheck: no unused error(object, options) -- Stub for gen_vimdoc.py end @@ -203,15 +204,15 @@ do --- end)(vim.paste) --- </pre> --- - --@see |paste| + ---@see |paste| --- - --@param lines |readfile()|-style list of lines to paste. |channel-lines| - --@param phase -1: "non-streaming" paste: the call contains all lines. + ---@param lines |readfile()|-style list of lines to paste. |channel-lines| + ---@param phase -1: "non-streaming" paste: the call contains all lines. --- If paste is "streamed", `phase` indicates the stream state: --- - 1: starts the paste (exactly once) --- - 2: continues the paste (zero or more times) --- - 3: ends the paste (exactly once) - --@returns false if client should cancel the paste. + ---@returns false if client should cancel the paste. function vim.paste(lines, phase) local call = vim.api.nvim_call_function local now = vim.loop.now() @@ -273,13 +274,13 @@ end ---@see |vim.in_fast_event()| function vim.schedule_wrap(cb) return (function (...) - local args = {...} - vim.schedule(function() cb(unpack(args)) end) + local args = vim.F.pack_len(...) + vim.schedule(function() cb(vim.F.unpack_len(args)) end) end) end --- <Docs described in |vim.empty_dict()| > ---@private +---@private function vim.empty_dict() return setmetatable({}, vim._empty_dict_mt) end @@ -338,17 +339,22 @@ end --- Get a table of lines with start, end columns for a region marked by two points --- ---@param bufnr number of buffer ---@param pos1 (line, column) tuple marking beginning of region ---@param pos2 (line, column) tuple marking end of region ---@param regtype type of selection (:help setreg) ---@param inclusive boolean indicating whether the selection is end-inclusive ---@return region lua table of the form {linenr = {startcol,endcol}} +---@param bufnr number of buffer +---@param pos1 (line, column) tuple marking beginning of region +---@param pos2 (line, column) tuple marking end of region +---@param regtype type of selection (:help setreg) +---@param inclusive boolean indicating whether the selection is end-inclusive +---@return region lua table of the form {linenr = {startcol,endcol}} function vim.region(bufnr, pos1, pos2, regtype, inclusive) if not vim.api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end + -- check that region falls within current buffer + local buf_line_count = vim.api.nvim_buf_line_count(bufnr) + pos1[1] = math.min(pos1[1], buf_line_count - 1) + pos2[1] = math.min(pos2[1], buf_line_count - 1) + -- in case of block selection, columns need to be adjusted for non-ASCII characters -- TODO: handle double-width characters local bufline @@ -385,9 +391,9 @@ end --- Use to do a one-shot timer that calls `fn` --- Note: The {fn} is |schedule_wrap|ped automatically, so API functions are --- safe to call. ---@param fn Callback to call once `timeout` expires ---@param timeout Number of milliseconds to wait before calling `fn` ---@return timer luv timer object +---@param fn Callback to call once `timeout` expires +---@param timeout Number of milliseconds to wait before calling `fn` +---@return timer luv timer object function vim.defer_fn(fn, timeout) vim.validate { fn = { fn, 'c', true}; } local timer = vim.loop.new_timer() @@ -403,11 +409,12 @@ end --- Notification provider ---- without a runtime, writes to :Messages --- see :help nvim_notify ---@param msg Content of the notification to show to the user ---@param log_level Optional log level ---@param opts Dictionary with optional options (timeout, etc) +--- +--- Without a runtime, writes to :Messages +---@see :help nvim_notify +---@param msg Content of the notification to show to the user +---@param log_level Optional log level +---@param opts Dictionary with optional options (timeout, etc) function vim.notify(msg, log_level, _opts) if log_level == vim.log.levels.ERROR then @@ -420,26 +427,35 @@ function vim.notify(msg, log_level, _opts) end -local on_keystroke_callbacks = {} +function vim.register_keystroke_callback() + error('vim.register_keystroke_callback is deprecated, instead use: vim.on_key') +end + +local on_key_cbs = {} ---- Register a lua {fn} with an {id} to be run after every keystroke. +--- Adds Lua function {fn} with namespace id {ns_id} as a listener to every, +--- yes every, input key. --- ---@param fn function: Function to call. It should take one argument, which is a string. ---- The string will contain the literal keys typed. ---- See |i_CTRL-V| +--- The Nvim command-line option |-w| is related but does not support callbacks +--- and cannot be toggled dynamically. --- +---@param fn function: Callback function. It should take one string argument. +--- On each key press, Nvim passes the key char to fn(). |i_CTRL-V| --- If {fn} is nil, it removes the callback for the associated {ns_id} ---@param ns_id number? Namespace ID. If not passed or 0, will generate and return a new ---- namespace ID from |nvim_create_namesapce()| +---@param ns_id number? Namespace ID. If nil or 0, generates and returns a new +--- |nvim_create_namesapce()| id. --- ---@return number Namespace ID associated with {fn} +---@return number Namespace id associated with {fn}. Or count of all callbacks +---if on_key() is called without arguments. --- ---@note {fn} will be automatically removed if an error occurs while calling. ---- This is to prevent the annoying situation of every keystroke erroring ---- while trying to remove a broken callback. ---@note {fn} will not be cleared from |nvim_buf_clear_namespace()| ---@note {fn} will receive the keystrokes after mappings have been evaluated -function vim.register_keystroke_callback(fn, ns_id) +---@note {fn} will be removed if an error occurs while calling. +---@note {fn} will not be cleared by |nvim_buf_clear_namespace()| +---@note {fn} will receive the keys after mappings have been evaluated +function vim.on_key(fn, ns_id) + if fn == nil and ns_id == nil then + return #on_key_cbs + end + vim.validate { fn = { fn, 'c', true}, ns_id = { ns_id, 'n', true } @@ -449,20 +465,19 @@ function vim.register_keystroke_callback(fn, ns_id) ns_id = vim.api.nvim_create_namespace('') end - on_keystroke_callbacks[ns_id] = fn + on_key_cbs[ns_id] = fn return ns_id end ---- Function that executes the keystroke callbacks. ---@private -function vim._log_keystroke(char) +--- Executes the on_key callbacks. +---@private +function vim._on_key(char) local failed_ns_ids = {} local failed_messages = {} - for k, v in pairs(on_keystroke_callbacks) do + for k, v in pairs(on_key_cbs) do local ok, err_msg = pcall(v, char) if not ok then - vim.register_keystroke_callback(nil, k) - + vim.on_key(nil, k) table.insert(failed_ns_ids, k) table.insert(failed_messages, err_msg) end @@ -470,7 +485,7 @@ function vim._log_keystroke(char) if failed_ns_ids[1] then error(string.format( - "Error executing 'on_keystroke' with ns_ids of '%s'\n With messages: %s", + "Error executing 'on_key' with ns_ids '%s'\n Messages: %s", table.concat(failed_ns_ids, ", "), table.concat(failed_messages, "\n"))) end @@ -636,6 +651,4 @@ vim._expand_pat_get_parts = function(lua_string) return parts, search_index end -pcall(require, 'vim._meta') - return module diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c new file mode 100644 index 0000000000..ae0ea00765 --- /dev/null +++ b/src/nvim/lua/xdiff.c @@ -0,0 +1,334 @@ +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#include "nvim/vim.h" +#include "xdiff/xdiff.h" +#include "nvim/lua/xdiff.h" +#include "nvim/lua/converter.h" +#include "nvim/lua/executor.h" +#include "nvim/api/private/helpers.h" + +typedef enum { + kNluaXdiffModeUnified = 0, + kNluaXdiffModeOnHunkCB, + kNluaXdiffModeLocations, +} NluaXdiffMode; + +typedef struct { + lua_State *lstate; + Error *err; +} hunkpriv_t; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/xdiff.c.generated.h" +#endif + +static int write_string(void *priv, mmbuffer_t *mb, int nbuf) +{ + luaL_Buffer *buf = (luaL_Buffer *)priv; + for (int i = 0; i < nbuf; i++) { + const long size = mb[i].size; + for (long total = 0; total < size; total += LUAL_BUFFERSIZE) { + const int tocopy = MIN((int)(size - total), LUAL_BUFFERSIZE); + char *p = luaL_prepbuffer(buf); + if (!p) { + return -1; + } + memcpy(p, mb[i].ptr + total, (unsigned)tocopy); + luaL_addsize(buf, (unsigned)tocopy); + } + } + return 0; +} + +// hunk_func callback used when opts.hunk_lines = true +static int hunk_locations_cb(long start_a, long count_a, + long start_b, long count_b, void *cb_data) +{ + // Mimic extra offsets done by xdiff, see: + // src/nvim/xdiff/xemit.c:284 + // src/nvim/xdiff/xutils.c:(356,368) + if (count_a > 0) { + start_a += 1; + } + if (count_b > 0) { + start_b += 1; + } + + lua_State * lstate = (lua_State *)cb_data; + lua_createtable(lstate, 0, 0); + + lua_pushinteger(lstate, start_a); + lua_rawseti(lstate, -2, 1); + lua_pushinteger(lstate, count_a); + lua_rawseti(lstate, -2, 2); + lua_pushinteger(lstate, start_b); + lua_rawseti(lstate, -2, 3); + lua_pushinteger(lstate, count_b); + lua_rawseti(lstate, -2, 4); + + lua_rawseti(lstate, -2, (signed)lua_objlen(lstate, -2)+1); + + return 0; +} + +// hunk_func callback used when opts.on_hunk is given +static int call_on_hunk_cb(long start_a, long count_a, + long start_b, long count_b, void *cb_data) +{ + // Mimic extra offsets done by xdiff, see: + // src/nvim/xdiff/xemit.c:284 + // src/nvim/xdiff/xutils.c:(356,368) + if (count_a > 0) { + start_a += 1; + } + if (count_b > 0) { + start_b += 1; + } + + hunkpriv_t *priv = (hunkpriv_t *)cb_data; + lua_State * lstate = priv->lstate; + Error *err = priv->err; + const int fidx = lua_gettop(lstate); + lua_pushvalue(lstate, fidx); + lua_pushinteger(lstate, start_a); + lua_pushinteger(lstate, count_a); + lua_pushinteger(lstate, start_b); + lua_pushinteger(lstate, count_b); + + if (lua_pcall(lstate, 4, 1, 0) != 0) { + api_set_error(err, kErrorTypeException, + "error running function on_hunk: %s", + lua_tostring(lstate, -1)); + return -1; + } + + int r = 0; + if (lua_isnumber(lstate, -1)) { + r = (int)lua_tonumber(lstate, -1); + } + + lua_pop(lstate, 1); + lua_settop(lstate, fidx); + return r; +} + +static mmfile_t get_string_arg(lua_State *lstate, int idx) +{ + if (lua_type(lstate, idx) != LUA_TSTRING) { + luaL_argerror(lstate, idx, "expected string"); + } + mmfile_t mf; + mf.ptr = (char *)lua_tolstring(lstate, idx, (size_t *)&mf.size); + return mf; +} + +// Helper function for validating option types +static bool check_xdiff_opt(ObjectType actType, ObjectType expType, + const char *name, Error *err) +{ + if (actType != expType) { + const char * type_str = + expType == kObjectTypeString ? "string" : + expType == kObjectTypeInteger ? "integer" : + expType == kObjectTypeBoolean ? "boolean" : + expType == kObjectTypeLuaRef ? "function" : + "NA"; + + api_set_error(err, kErrorTypeValidation, "%s is not a %s", name, + type_str); + return true; + } + + return false; +} + +static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, + xdemitconf_t *cfg, + xpparam_t *params, Error *err) +{ + const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, err); + + NluaXdiffMode mode = kNluaXdiffModeUnified; + + bool had_on_hunk = false; + bool had_result_type_indices = false; + for (size_t i = 0; i < opts.size; i++) { + String k = opts.items[i].key; + Object *v = &opts.items[i].value; + if (strequal("on_hunk", k.data)) { + if (check_xdiff_opt(v->type, kObjectTypeLuaRef, "on_hunk", err)) { + goto exit_1; + } + had_on_hunk = true; + nlua_pushref(lstate, v->data.luaref); + } else if (strequal("result_type", k.data)) { + if (check_xdiff_opt(v->type, kObjectTypeString, "result_type", err)) { + goto exit_1; + } + if (strequal("unified", v->data.string.data)) { + } else if (strequal("indices", v->data.string.data)) { + had_result_type_indices = true; + } else { + api_set_error(err, kErrorTypeValidation, "not a valid result_type"); + goto exit_1; + } + } else if (strequal("algorithm", k.data)) { + if (check_xdiff_opt(v->type, kObjectTypeString, "algorithm", err)) { + goto exit_1; + } + if (strequal("myers", v->data.string.data)) { + // default + } else if (strequal("minimal", v->data.string.data)) { + cfg->flags |= XDF_NEED_MINIMAL; + } else if (strequal("patience", v->data.string.data)) { + cfg->flags |= XDF_PATIENCE_DIFF; + } else if (strequal("histogram", v->data.string.data)) { + cfg->flags |= XDF_HISTOGRAM_DIFF; + } else { + api_set_error(err, kErrorTypeValidation, "not a valid algorithm"); + goto exit_1; + } + } else if (strequal("ctxlen", k.data)) { + if (check_xdiff_opt(v->type, kObjectTypeInteger, "ctxlen", err)) { + goto exit_1; + } + cfg->ctxlen = v->data.integer; + } else if (strequal("interhunkctxlen", k.data)) { + if (check_xdiff_opt(v->type, kObjectTypeInteger, "interhunkctxlen", + err)) { + goto exit_1; + } + cfg->interhunkctxlen = v->data.integer; + } else { + struct { + const char *name; + unsigned long value; + } flags[] = { + { "ignore_whitespace" , XDF_IGNORE_WHITESPACE }, + { "ignore_whitespace_change" , XDF_IGNORE_WHITESPACE_CHANGE }, + { "ignore_whitespace_change_at_eol", XDF_IGNORE_WHITESPACE_AT_EOL }, + { "ignore_cr_at_eol" , XDF_IGNORE_CR_AT_EOL }, + { "ignore_blank_lines" , XDF_IGNORE_BLANK_LINES }, + { "indent_heuristic" , XDF_INDENT_HEURISTIC }, + { NULL , 0 }, + }; + bool key_used = false; + for (size_t j = 0; flags[j].name; j++) { + if (strequal(flags[j].name, k.data)) { + if (check_xdiff_opt(v->type, kObjectTypeBoolean, flags[j].name, + err)) { + goto exit_1; + } + if (v->data.boolean) { + params->flags |= flags[j].value; + } + key_used = true; + break; + } + } + + if (key_used) { + continue; + } + + api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + goto exit_1; + } + } + + if (had_on_hunk) { + mode = kNluaXdiffModeOnHunkCB; + cfg->hunk_func = call_on_hunk_cb; + } else if (had_result_type_indices) { + mode = kNluaXdiffModeLocations; + cfg->hunk_func = hunk_locations_cb; + } + +exit_1: + api_free_dictionary(opts); + return mode; +} + +int nlua_xdl_diff(lua_State *lstate) +{ + if (lua_gettop(lstate) < 2) { + return luaL_error(lstate, "Expected at least 2 arguments"); + } + mmfile_t ma = get_string_arg(lstate, 1); + mmfile_t mb = get_string_arg(lstate, 2); + + Error err = ERROR_INIT; + + xdemitconf_t cfg; + xpparam_t params; + xdemitcb_t ecb; + + memset(&cfg , 0, sizeof(cfg)); + memset(¶ms, 0, sizeof(params)); + memset(&ecb , 0, sizeof(ecb)); + + NluaXdiffMode mode = kNluaXdiffModeUnified; + + if (lua_gettop(lstate) == 3) { + if (lua_type(lstate, 3) != LUA_TTABLE) { + return luaL_argerror(lstate, 3, "expected table"); + } + + mode = process_xdl_diff_opts(lstate, &cfg, ¶ms, &err); + + if (ERROR_SET(&err)) { + goto exit_0; + } + } + + luaL_Buffer buf; + hunkpriv_t *priv = NULL; + switch (mode) { + case kNluaXdiffModeUnified: + luaL_buffinit(lstate, &buf); + ecb.priv = &buf; + ecb.out_line = write_string; + break; + case kNluaXdiffModeOnHunkCB: + priv = xmalloc(sizeof(*priv)); + priv->lstate = lstate; + priv->err = &err; + ecb.priv = priv; + break; + case kNluaXdiffModeLocations: + lua_createtable(lstate, 0, 0); + ecb.priv = lstate; + break; + } + + if (xdl_diff(&ma, &mb, ¶ms, &cfg, &ecb) == -1) { + if (!ERROR_SET(&err)) { + api_set_error(&err, kErrorTypeException, + "Error while performing diff operation"); + } + } + + XFREE_CLEAR(priv); + +exit_0: + if (ERROR_SET(&err)) { + luaL_where(lstate, 1); + lua_pushstring(lstate, err.msg); + api_clear_error(&err); + lua_concat(lstate, 2); + return lua_error(lstate); + } else if (mode == kNluaXdiffModeUnified) { + luaL_pushresult(&buf); + return 1; + } else if (mode == kNluaXdiffModeLocations) { + return 1; + } + return 0; +} diff --git a/src/nvim/lua/xdiff.h b/src/nvim/lua/xdiff.h new file mode 100644 index 0000000000..cae7c98e81 --- /dev/null +++ b/src/nvim/lua/xdiff.h @@ -0,0 +1,12 @@ +#ifndef NVIM_LUA_XDIFF_H +#define NVIM_LUA_XDIFF_H + +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "lua/xdiff.h.generated.h" +#endif + +#endif // NVIM_LUA_XDIFF_H diff --git a/src/nvim/macros.h b/src/nvim/macros.h index eb9357d027..e1aa1b7704 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -34,10 +34,6 @@ /// LINEEMPTY() - return TRUE if the line is empty #define LINEEMPTY(p) (*ml_get(p) == NUL) -/// BUFEMPTY() - return TRUE if the current buffer is empty -#define BUFEMPTY() (curbuf->b_ml.ml_line_count == 1 && *ml_get((linenr_T)1) == \ - NUL) - // toupper() and tolower() that use the current locale. // Careful: Only call TOUPPER_LOC() and TOLOWER_LOC() with a character in the // range 0 - 255. toupper()/tolower() on some systems can't handle others. @@ -133,6 +129,8 @@ /// error. A mechanism to detect many (though not all) of those errors at /// compile time is implemented. It works by the second division producing /// a division by zero in those cases (-Wdiv-by-zero in GCC). +/// +/// -V:ARRAY_SIZE:1063 #define ARRAY_SIZE(arr) \ ((sizeof(arr)/sizeof((arr)[0])) \ / ((size_t)(!(sizeof(arr) % sizeof((arr)[0]))))) @@ -169,7 +167,7 @@ #if NVIM_HAS_ATTRIBUTE(fallthrough) \ && (!defined(__apple_build_version__) || __apple_build_version__ >= 7000000) -# define FALLTHROUGH __attribute__((fallthrough)) +# define FALLTHROUGH {} __attribute__((fallthrough)) #else # define FALLTHROUGH #endif diff --git a/src/nvim/main.c b/src/nvim/main.c index 56cd97f133..716434f32e 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -7,8 +7,6 @@ #include <string.h> #include <stdbool.h> -#include <lua.h> -#include <lauxlib.h> #include <msgpack.h> #include "nvim/ascii.h" @@ -80,7 +78,6 @@ #include "nvim/api/ui.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/private/handle.h" #include "nvim/api/private/dispatch.h" #ifndef WIN32 # include "nvim/os/pty_process_unix.h" @@ -128,8 +125,6 @@ void event_init(void) signal_init(); // finish mspgack-rpc initialization channel_init(); - remote_ui_init(); - api_vim_init(); terminal_init(); ui_init(); } @@ -162,8 +157,6 @@ void early_init(mparm_T *paramp) { env_init(); fs_init(); - handle_init(); - decor_init(); eval_init(); // init global variables init_path(argv0 ? argv0 : "nvim"); init_normal_cmds(); // Init the table of Normal mode commands. @@ -258,6 +251,8 @@ int main(int argc, char **argv) // Check if we have an interactive window. check_and_set_isatty(¶ms); + nlua_init(); + // Process the command line arguments. File names are put in the global // argument list "global_alist". command_line_scan(¶ms); @@ -318,7 +313,8 @@ int main(int argc, char **argv) debug_break_level = params.use_debug_break_level; // Read ex-commands if invoked with "-es". - if (!params.input_isatty && silent_mode && exmode_active == EXMODE_NORMAL) { + if (!params.input_isatty && !params.input_neverscript + && silent_mode && exmode_active) { input_start(STDIN_FILENO); } @@ -338,25 +334,14 @@ int main(int argc, char **argv) // prepare screen now, so external UIs can display messages starting = NO_BUFFERS; screenclear(); - TIME_MSG("initialized screen early for UI"); - } - - - // open terminals when opening files that start with term:// -#define PROTO "term://" - do_cmdline_cmd("augroup nvim_terminal"); - do_cmdline_cmd("autocmd!"); - do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested " - ":if !exists('b:term_title')|call termopen( " - // Capture the command string - "matchstr(expand(\"<amatch>\"), " - "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " - // capture the working directory - "{'cwd': expand(get(matchlist(expand(\"<amatch>\"), " - "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, ''))})" - "|endif"); - do_cmdline_cmd("augroup END"); -#undef PROTO + TIME_MSG("init screen for UI"); + } + + init_default_mappings(); // Default mappings. + TIME_MSG("init default mappings"); + + init_default_autocmds(); + TIME_MSG("init default autocommands"); // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments. // Allows for setting 'loadplugins' there. @@ -765,7 +750,7 @@ static bool edit_stdin(bool explicit, mparm_T *parmp) { bool implicit = !headless_mode && !embedded_mode - && exmode_active != EXMODE_NORMAL // -E/-Es but not -e/-es. + && (!exmode_active || parmp->input_neverscript) && !parmp->input_isatty && scriptin[0] == NULL; // `-s -` was not given. return explicit || implicit; @@ -908,11 +893,12 @@ static void command_line_scan(mparm_T *parmp) break; } case 'e': { // "-e" Ex mode - exmode_active = EXMODE_NORMAL; + exmode_active = true; break; } case 'E': { // "-E" Ex mode - exmode_active = EXMODE_VIM; + exmode_active = true; + parmp->input_neverscript = true; break; } case 'f': { // "-f" GUI: run in foreground. @@ -1101,11 +1087,7 @@ static void command_line_scan(mparm_T *parmp) size_t s_size = STRLEN(a) + 9; char *s = xmalloc(s_size); - if (path_with_extension(a, "lua")) { - snprintf(s, s_size, "luafile %s", a); - } else { - snprintf(s, s_size, "so %s", a); - } + snprintf(s, s_size, "so %s", a); parmp->cmds_tofree[parmp->n_commands] = true; parmp->commands[parmp->n_commands++] = s; } else { @@ -1367,7 +1349,8 @@ static void load_plugins(void) { if (p_lpl) { char_u *rtp_copy = NULL; - char_u *const plugin_pattern = (char_u *)"plugin/**/*.vim"; // NOLINT + char_u *const plugin_pattern_vim = (char_u *)"plugin/**/*.vim"; // NOLINT + char_u *const plugin_pattern_lua = (char_u *)"plugin/**/*.lua"; // NOLINT // First add all package directories to 'runtimepath', so that their // autoload directories can be found. Only if not done already with a @@ -1380,7 +1363,10 @@ static void load_plugins(void) } source_in_path(rtp_copy == NULL ? p_rtp : rtp_copy, - plugin_pattern, + plugin_pattern_vim, + DIP_ALL | DIP_NOAFTER); + source_in_path(rtp_copy == NULL ? p_rtp : rtp_copy, + plugin_pattern_lua, DIP_ALL | DIP_NOAFTER); TIME_MSG("loading plugins"); xfree(rtp_copy); @@ -1392,7 +1378,8 @@ static void load_plugins(void) } TIME_MSG("loading packages"); - source_runtime(plugin_pattern, DIP_ALL | DIP_AFTER); + source_runtime(plugin_pattern_vim, DIP_ALL | DIP_AFTER); + source_runtime(plugin_pattern_lua, DIP_ALL | DIP_AFTER); TIME_MSG("loading after plugins"); } } @@ -1423,7 +1410,7 @@ static void handle_quickfix(mparm_T *paramp) static void handle_tag(char_u *tagname) { if (tagname != NULL) { - swap_exists_did_quit = FALSE; + swap_exists_did_quit = false; vim_snprintf((char *)IObuff, IOSIZE, "ta %s", tagname); do_cmdline_cmd((char *)IObuff); @@ -1441,13 +1428,11 @@ static void read_stdin(void) // When getting the ATTENTION prompt here, use a dialog. swap_exists_action = SEA_DIALOG; no_wait_return = true; - int save_msg_didany = msg_didany; + bool save_msg_didany = msg_didany; set_buflisted(true); - // Create memfile and read from stdin. (void)open_buffer(true, NULL, 0); - - if (BUFEMPTY() && curbuf->b_next != NULL) { + if (buf_is_empty(curbuf) && curbuf->b_next != NULL) { // stdin was empty, go to buffer 2 (e.g. "echo file1 | xargs nvim"). #8561 do_cmdline_cmd("silent! bnext"); // Delete the empty stdin buffer. @@ -1633,7 +1618,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) curwin->w_arg_idx = arg_idx; /* Edit file from arg list, if there is one. When "Quit" selected * at the ATTENTION prompt close the window. */ - swap_exists_did_quit = FALSE; + swap_exists_did_quit = false; (void)do_ecmd(0, arg_idx < GARGCOUNT ? alist_name(&GARGLIST[arg_idx]) : NULL, NULL, NULL, ECMD_LASTL, ECMD_HIDE, curwin); @@ -1810,12 +1795,13 @@ static bool do_user_initialization(void) char_u *init_lua_path = (char_u *)stdpaths_user_conf_subpath("init.lua"); if (os_path_exists(init_lua_path) - && nlua_exec_file((const char *)init_lua_path)) { + && do_source(init_lua_path, true, DOSO_VIMRC)) { os_setenv("MYVIMRC", (const char *)init_lua_path, 1); char_u *vimrc_path = (char_u *)stdpaths_user_conf_subpath("init.vim"); if (os_path_exists(vimrc_path)) { - EMSG3(_("Conflicting configs: \"%s\" \"%s\""), init_lua_path, vimrc_path); + EMSG3(_("E5422: Conflicting configs: \"%s\" \"%s\""), init_lua_path, + vimrc_path); } xfree(vimrc_path); @@ -1883,12 +1869,8 @@ static void source_startup_scripts(const mparm_T *const parmp) || strequal(parmp->use_vimrc, "NORC")) { // Do nothing. } else { - if (path_with_extension(parmp->use_vimrc, "lua")) { - nlua_exec_file(parmp->use_vimrc); - } else { - if (do_source((char_u *)parmp->use_vimrc, false, DOSO_NONE) != OK) { - EMSG2(_("E282: Cannot read from \"%s\""), parmp->use_vimrc); - } + if (do_source((char_u *)parmp->use_vimrc, false, DOSO_NONE) != OK) { + EMSG2(_("E282: Cannot read from \"%s\""), parmp->use_vimrc); } } } else if (!silent_mode) { @@ -2003,10 +1985,10 @@ static void mainerr(const char *errstr, const char *str) /// Prints version information for "nvim -v" or "nvim --version". static void version(void) { - info_message = TRUE; // use mch_msg(), not mch_errmsg() + info_message = true; // use mch_msg(), not mch_errmsg() list_version(); msg_putchar('\n'); - msg_didout = FALSE; + msg_didout = false; } /// Prints help message for "nvim -h" or "nvim --help". diff --git a/src/nvim/main.h b/src/nvim/main.h index 61252f2bce..d387e6d668 100644 --- a/src/nvim/main.h +++ b/src/nvim/main.h @@ -30,6 +30,7 @@ typedef struct { bool input_isatty; // stdin is a terminal bool output_isatty; // stdout is a terminal bool err_isatty; // stderr is a terminal + bool input_neverscript; // never treat stdin as script (-E/-Es) int no_swap_file; // "-n" argument used int use_debug_break_level; int window_count; // number of windows to use diff --git a/src/nvim/map.c b/src/nvim/map.c index 7d97b7f13d..ccd332192e 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -54,56 +54,48 @@ INITIALIZER_DECLARE(T, U, __VA_ARGS__); \ __KHASH_IMPL(T##_##U##_map,, T, U, 1, T##_hash, T##_eq) \ \ - Map(T, U) *map_##T##_##U##_new() \ + void map_##T##_##U##_destroy(Map(T, U) *map) \ { \ - Map(T, U) *rv = xmalloc(sizeof(Map(T, U))); \ - rv->table = kh_init(T##_##U##_map); \ - return rv; \ - } \ - \ - void map_##T##_##U##_free(Map(T, U) *map) \ - { \ - kh_destroy(T##_##U##_map, map->table); \ - xfree(map); \ + kh_dealloc(T##_##U##_map, &map->table); \ } \ \ U map_##T##_##U##_get(Map(T, U) *map, T key) \ { \ khiter_t k; \ \ - if ((k = kh_get(T##_##U##_map, map->table, key)) == kh_end(map->table)) { \ + if ((k = kh_get(T##_##U##_map, &map->table, key)) == kh_end(&map->table)) { \ return INITIALIZER(T, U); \ } \ \ - return kh_val(map->table, k); \ + return kh_val(&map->table, k); \ } \ \ bool map_##T##_##U##_has(Map(T, U) *map, T key) \ { \ - return kh_get(T##_##U##_map, map->table, key) != kh_end(map->table); \ + return kh_get(T##_##U##_map, &map->table, key) != kh_end(&map->table); \ } \ \ T map_##T##_##U##_key(Map(T, U) *map, T key) \ { \ khiter_t k; \ \ - if ((k = kh_get(T##_##U##_map, map->table, key)) == kh_end(map->table)) { \ + if ((k = kh_get(T##_##U##_map, &map->table, key)) == kh_end(&map->table)) { \ abort(); /* Caller must check map_has(). */ \ } \ \ - return kh_key(map->table, k); \ + return kh_key(&map->table, k); \ } \ U map_##T##_##U##_put(Map(T, U) *map, T key, U value) \ { \ int ret; \ U rv = INITIALIZER(T, U); \ - khiter_t k = kh_put(T##_##U##_map, map->table, key, &ret); \ + khiter_t k = kh_put(T##_##U##_map, &map->table, key, &ret); \ \ if (!ret) { \ - rv = kh_val(map->table, k); \ + rv = kh_val(&map->table, k); \ } \ \ - kh_val(map->table, k) = value; \ + kh_val(&map->table, k) = value; \ return rv; \ } \ \ @@ -112,18 +104,18 @@ int ret; \ khiter_t k; \ if (put) { \ - k = kh_put(T##_##U##_map, map->table, key, &ret); \ + k = kh_put(T##_##U##_map, &map->table, key, &ret); \ if (ret) { \ - kh_val(map->table, k) = INITIALIZER(T, U); \ + kh_val(&map->table, k) = INITIALIZER(T, U); \ } \ } else { \ - k = kh_get(T##_##U##_map, map->table, key); \ - if (k == kh_end(map->table)) { \ + k = kh_get(T##_##U##_map, &map->table, key); \ + if (k == kh_end(&map->table)) { \ return NULL; \ } \ } \ \ - return &kh_val(map->table, k); \ + return &kh_val(&map->table, k); \ } \ \ U map_##T##_##U##_del(Map(T, U) *map, T key) \ @@ -131,9 +123,9 @@ U rv = INITIALIZER(T, U); \ khiter_t k; \ \ - if ((k = kh_get(T##_##U##_map, map->table, key)) != kh_end(map->table)) { \ - rv = kh_val(map->table, k); \ - kh_del(T##_##U##_map, map->table, k); \ + if ((k = kh_get(T##_##U##_map, &map->table, key)) != kh_end(&map->table)) { \ + rv = kh_val(&map->table, k); \ + kh_del(T##_##U##_map, &map->table, k); \ } \ \ return rv; \ @@ -141,7 +133,7 @@ \ void map_##T##_##U##_clear(Map(T, U) *map) \ { \ - kh_clear(T##_##U##_map, map->table); \ + kh_clear(T##_##U##_map, &map->table); \ } static inline khint_t String_hash(String s) @@ -194,11 +186,12 @@ static inline bool ColorKey_eq(ColorKey ae1, ColorKey ae2) MAP_IMPL(int, int, DEFAULT_INITIALIZER) MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER) +MAP_IMPL(cstr_t, int, DEFAULT_INITIALIZER) MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ssize_t, SSIZE_INITIALIZER) MAP_IMPL(uint64_t, uint64_t, DEFAULT_INITIALIZER) -#define EXTMARK_NS_INITIALIZER { 0, 0 } +#define EXTMARK_NS_INITIALIZER { { MAP_INIT }, 1 } MAP_IMPL(uint64_t, ExtmarkNs, EXTMARK_NS_INITIALIZER) #define EXTMARK_ITEM_INITIALIZER { 0, 0, NULL } MAP_IMPL(uint64_t, ExtmarkItem, EXTMARK_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 7bd3d31330..d6515878a2 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -18,11 +18,12 @@ KHASH_DECLARE(T##_##U##_map, T, U) \ \ typedef struct { \ - khash_t(T##_##U##_map) *table; \ + khash_t(T##_##U##_map) table; \ } Map(T, U); \ \ Map(T, U) *map_##T##_##U##_new(void); \ void map_##T##_##U##_free(Map(T, U) *map); \ + void map_##T##_##U##_destroy(Map(T, U) *map); \ U map_##T##_##U##_get(Map(T, U) *map, T key); \ bool map_##T##_##U##_has(Map(T, U) *map, T key); \ T map_##T##_##U##_key(Map(T, U) *map, T key); \ @@ -36,6 +37,7 @@ // MAP_DECLS(int, int) MAP_DECLS(cstr_t, ptr_t) +MAP_DECLS(cstr_t, int) MAP_DECLS(ptr_t, ptr_t) MAP_DECLS(uint64_t, ptr_t) MAP_DECLS(uint64_t, ssize_t) @@ -44,7 +46,7 @@ MAP_DECLS(uint64_t, uint64_t) // NB: this is the only way to define a struct both containing and contained // in a map... typedef struct ExtmarkNs { // For namespacing extmarks - Map(uint64_t, uint64_t) *map; // For fast lookup + Map(uint64_t, uint64_t) map[1]; // For fast lookup uint64_t free_id; // For automatically assigning id's } ExtmarkNs; @@ -57,8 +59,10 @@ MAP_DECLS(String, handle_T) MAP_DECLS(ColorKey, ColorItem) -#define map_new(T, U) map_##T##_##U##_new -#define map_free(T, U) map_##T##_##U##_free +#define MAP_INIT { { 0, 0, 0, 0, NULL, NULL, NULL } } +#define map_init(k, v, map) do { *(map) = (Map(k, v))MAP_INIT; } while (false) + +#define map_destroy(T, U) map_##T##_##U##_destroy #define map_get(T, U) map_##T##_##U##_get #define map_has(T, U) map_##T##_##U##_has #define map_key(T, U) map_##T##_##U##_key @@ -67,10 +71,9 @@ MAP_DECLS(ColorKey, ColorItem) #define map_del(T, U) map_##T##_##U##_del #define map_clear(T, U) map_##T##_##U##_clear -#define map_size(map) ((map)->table->size) +#define map_size(map) ((map)->table.size) -#define pmap_new(T) map_new(T, ptr_t) -#define pmap_free(T) map_free(T, ptr_t) +#define pmap_destroy(T) map_destroy(T, ptr_t) #define pmap_get(T) map_get(T, ptr_t) #define pmap_has(T) map_has(T, ptr_t) #define pmap_key(T) map_key(T, ptr_t) @@ -79,12 +82,13 @@ MAP_DECLS(ColorKey, ColorItem) /// @see pmap_del2 #define pmap_del(T) map_del(T, ptr_t) #define pmap_clear(T) map_clear(T, ptr_t) +#define pmap_init(k, map) map_init(k, ptr_t, map) #define map_foreach(map, key, value, block) \ - kh_foreach(map->table, key, value, block) + kh_foreach(&(map)->table, key, value, block) #define map_foreach_value(map, value, block) \ - kh_foreach_value(map->table, value, block) + kh_foreach_value(&(map)->table, value, block) void pmap_del2(PMap(cstr_t) *map, const char *key); diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 73a9c1d1d7..0b14089550 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -108,7 +108,7 @@ int setmark_pos(int c, pos_T *pos, int fnum) return OK; } - // Can't set a mark in a non-existant buffer. + // Can't set a mark in a non-existent buffer. buf_T *buf = buflist_findnr(fnum); if (buf == NULL) { return FAIL; @@ -346,10 +346,10 @@ pos_T *getmark_buf_fnum(buf_T *buf, int c, bool changefile, int *fnum) } else if (c == '{' || c == '}') { // to previous/next paragraph pos_T pos; oparg_T oa; - int slcb = listcmd_busy; + bool slcb = listcmd_busy; pos = curwin->w_cursor; - listcmd_busy = TRUE; /* avoid that '' is changed */ + listcmd_busy = true; // avoid that '' is changed if (findpar(&oa.inclusive, c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE)) { pos_copy = curwin->w_cursor; @@ -359,10 +359,10 @@ pos_T *getmark_buf_fnum(buf_T *buf, int c, bool changefile, int *fnum) listcmd_busy = slcb; } else if (c == '(' || c == ')') { /* to previous/next sentence */ pos_T pos; - int slcb = listcmd_busy; + bool slcb = listcmd_busy; pos = curwin->w_cursor; - listcmd_busy = TRUE; /* avoid that '' is changed */ + listcmd_busy = true; // avoid that '' is changed if (findsent(c == ')' ? FORWARD : BACKWARD, 1L)) { pos_copy = curwin->w_cursor; posp = &pos_copy; diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 34acf64d83..a04f250fc3 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -250,7 +250,6 @@ void marktree_put_key(MarkTree *b, int row, int col, uint64_t id) if (!b->root) { b->root = (mtnode_t *)xcalloc(1, ILEN); - b->id2node = pmap_new(uint64_t)(); b->n_nodes++; } mtnode_t *r, *s; @@ -356,6 +355,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) y = y->level ? y->ptr[0] : NULL; } } + itr->i--; } b->n_keys--; @@ -546,9 +546,9 @@ void marktree_clear(MarkTree *b) marktree_free_node(b->root); b->root = NULL; } - if (b->id2node) { - pmap_free(uint64_t)(b->id2node); - b->id2node = NULL; + if (b->id2node->table.keys) { + pmap_destroy(uint64_t)(b->id2node); + pmap_init(uint64_t, b->id2node); } b->n_keys = 0; b->n_nodes = 0; @@ -904,6 +904,7 @@ continue_same_node: refkey(b, enditr->node, enditr->i); } else { past_right = true; // NOLINT + (void)past_right; break; } } diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h index 8a1c564a6d..7af23765c3 100644 --- a/src/nvim/marktree.h +++ b/src/nvim/marktree.h @@ -49,7 +49,7 @@ struct mtnode_s { int32_t n; int32_t level; // TODO(bfredl): we could consider having a only-sometimes-valid - // index into parent for faster "chached" lookup. + // index into parent for faster "cached" lookup. mtnode_t *parent; mtkey_t key[2 * MT_BRANCH_FACTOR - 1]; mtnode_t *ptr[]; @@ -63,7 +63,7 @@ typedef struct { uint64_t next_id; // TODO(bfredl): the pointer to node could be part of the larger // Map(uint64_t, ExtmarkItem) essentially; - PMap(uint64_t) *id2node; + PMap(uint64_t) id2node[1]; } MarkTree; diff --git a/src/nvim/math.c b/src/nvim/math.c index b51f335ed7..63309b6f7a 100644 --- a/src/nvim/math.c +++ b/src/nvim/math.c @@ -2,6 +2,8 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include <math.h> +#include <stdint.h> +#include <string.h> #include "nvim/math.h" @@ -9,34 +11,26 @@ # include "math.c.generated.h" #endif -#if defined(__clang__) && __clang__ == 1 && __clang_major__ >= 6 -// Workaround glibc + Clang 6+ bug. #8274 -// https://bugzilla.redhat.com/show_bug.cgi?id=1472437 -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wconversion" -#endif int xfpclassify(double d) { -#if defined(__MINGW32__) - // Workaround mingw warning. #7863 - return __fpclassify(d); -#else - return fpclassify(d); -#endif + uint64_t m; + int e; + + memcpy(&m, &d, sizeof(m)); + e = 0x7ff & (m >> 52); + m = 0xfffffffffffffULL & m; + + switch (e) { + default: return FP_NORMAL; + case 0x000: return m ? FP_SUBNORMAL : FP_ZERO; + case 0x7ff: return m ? FP_NAN : FP_INFINITE; + } } int xisinf(double d) { - return isinf(d); + return FP_INFINITE == xfpclassify(d); } int xisnan(double d) { -#if defined(__MINGW32__) - // Workaround mingw warning. #7863 - return _isnan(d); -#else - return isnan(d); -#endif + return FP_NAN == xfpclassify(d); } -#if defined(__clang__) && __clang__ == 1 && __clang_major__ >= 6 -# pragma clang diagnostic pop -#endif diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 73e3ba53a5..fea1ab77a2 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -31,30 +31,30 @@ #include <wchar.h> #include <wctype.h> -#include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/vim.h" #ifdef HAVE_LOCALE_H # include <locale.h> #endif -#include "nvim/eval.h" -#include "nvim/path.h" -#include "nvim/iconv.h" -#include "nvim/mbyte.h" +#include "nvim/arabic.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/eval.h" #include "nvim/fileio.h" #include "nvim/func_attr.h" +#include "nvim/iconv.h" +#include "nvim/mark.h" +#include "nvim/mbyte.h" #include "nvim/memline.h" +#include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/memory.h" #include "nvim/option.h" +#include "nvim/os/os.h" +#include "nvim/path.h" #include "nvim/screen.h" #include "nvim/spell.h" #include "nvim/strings.h" -#include "nvim/os/os.h" -#include "nvim/arabic.h" -#include "nvim/mark.h" typedef struct { int rangeStart; @@ -70,12 +70,10 @@ struct interval { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mbyte.c.generated.h" + # include "unicode_tables.generated.h" #endif -char_u e_loadlib[] = "E370: Could not load library %s"; -char_u e_loadfunc[] = "E448: Could not load library function %s"; - // To speed up BYTELEN(); keep a lookup table to quickly get the length in // bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes // which are illegal when used as the first byte have a 1. The NUL byte has @@ -130,131 +128,131 @@ static struct enc_canon_table[] = { #define IDX_LATIN_1 0 - {"latin1", ENC_8BIT + ENC_LATIN1, 1252}, + { "latin1", ENC_8BIT + ENC_LATIN1, 1252 }, #define IDX_ISO_2 1 - {"iso-8859-2", ENC_8BIT, 0}, + { "iso-8859-2", ENC_8BIT, 0 }, #define IDX_ISO_3 2 - {"iso-8859-3", ENC_8BIT, 0}, + { "iso-8859-3", ENC_8BIT, 0 }, #define IDX_ISO_4 3 - {"iso-8859-4", ENC_8BIT, 0}, + { "iso-8859-4", ENC_8BIT, 0 }, #define IDX_ISO_5 4 - {"iso-8859-5", ENC_8BIT, 0}, + { "iso-8859-5", ENC_8BIT, 0 }, #define IDX_ISO_6 5 - {"iso-8859-6", ENC_8BIT, 0}, + { "iso-8859-6", ENC_8BIT, 0 }, #define IDX_ISO_7 6 - {"iso-8859-7", ENC_8BIT, 0}, + { "iso-8859-7", ENC_8BIT, 0 }, #define IDX_ISO_8 7 - {"iso-8859-8", ENC_8BIT, 0}, + { "iso-8859-8", ENC_8BIT, 0 }, #define IDX_ISO_9 8 - {"iso-8859-9", ENC_8BIT, 0}, + { "iso-8859-9", ENC_8BIT, 0 }, #define IDX_ISO_10 9 - {"iso-8859-10", ENC_8BIT, 0}, + { "iso-8859-10", ENC_8BIT, 0 }, #define IDX_ISO_11 10 - {"iso-8859-11", ENC_8BIT, 0}, + { "iso-8859-11", ENC_8BIT, 0 }, #define IDX_ISO_13 11 - {"iso-8859-13", ENC_8BIT, 0}, + { "iso-8859-13", ENC_8BIT, 0 }, #define IDX_ISO_14 12 - {"iso-8859-14", ENC_8BIT, 0}, + { "iso-8859-14", ENC_8BIT, 0 }, #define IDX_ISO_15 13 - {"iso-8859-15", ENC_8BIT + ENC_LATIN9, 0}, + { "iso-8859-15", ENC_8BIT + ENC_LATIN9, 0 }, #define IDX_KOI8_R 14 - {"koi8-r", ENC_8BIT, 0}, + { "koi8-r", ENC_8BIT, 0 }, #define IDX_KOI8_U 15 - {"koi8-u", ENC_8BIT, 0}, + { "koi8-u", ENC_8BIT, 0 }, #define IDX_UTF8 16 - {"utf-8", ENC_UNICODE, 0}, + { "utf-8", ENC_UNICODE, 0 }, #define IDX_UCS2 17 - {"ucs-2", ENC_UNICODE + ENC_ENDIAN_B + ENC_2BYTE, 0}, + { "ucs-2", ENC_UNICODE + ENC_ENDIAN_B + ENC_2BYTE, 0 }, #define IDX_UCS2LE 18 - {"ucs-2le", ENC_UNICODE + ENC_ENDIAN_L + ENC_2BYTE, 0}, + { "ucs-2le", ENC_UNICODE + ENC_ENDIAN_L + ENC_2BYTE, 0 }, #define IDX_UTF16 19 - {"utf-16", ENC_UNICODE + ENC_ENDIAN_B + ENC_2WORD, 0}, + { "utf-16", ENC_UNICODE + ENC_ENDIAN_B + ENC_2WORD, 0 }, #define IDX_UTF16LE 20 - {"utf-16le", ENC_UNICODE + ENC_ENDIAN_L + ENC_2WORD, 0}, + { "utf-16le", ENC_UNICODE + ENC_ENDIAN_L + ENC_2WORD, 0 }, #define IDX_UCS4 21 - {"ucs-4", ENC_UNICODE + ENC_ENDIAN_B + ENC_4BYTE, 0}, + { "ucs-4", ENC_UNICODE + ENC_ENDIAN_B + ENC_4BYTE, 0 }, #define IDX_UCS4LE 22 - {"ucs-4le", ENC_UNICODE + ENC_ENDIAN_L + ENC_4BYTE, 0}, + { "ucs-4le", ENC_UNICODE + ENC_ENDIAN_L + ENC_4BYTE, 0 }, - /* For debugging DBCS encoding on Unix. */ + // For debugging DBCS encoding on Unix. #define IDX_DEBUG 23 - {"debug", ENC_DBCS, DBCS_DEBUG}, + { "debug", ENC_DBCS, DBCS_DEBUG }, #define IDX_EUC_JP 24 - {"euc-jp", ENC_DBCS, DBCS_JPNU}, + { "euc-jp", ENC_DBCS, DBCS_JPNU }, #define IDX_SJIS 25 - {"sjis", ENC_DBCS, DBCS_JPN}, + { "sjis", ENC_DBCS, DBCS_JPN }, #define IDX_EUC_KR 26 - {"euc-kr", ENC_DBCS, DBCS_KORU}, + { "euc-kr", ENC_DBCS, DBCS_KORU }, #define IDX_EUC_CN 27 - {"euc-cn", ENC_DBCS, DBCS_CHSU}, + { "euc-cn", ENC_DBCS, DBCS_CHSU }, #define IDX_EUC_TW 28 - {"euc-tw", ENC_DBCS, DBCS_CHTU}, + { "euc-tw", ENC_DBCS, DBCS_CHTU }, #define IDX_BIG5 29 - {"big5", ENC_DBCS, DBCS_CHT}, + { "big5", ENC_DBCS, DBCS_CHT }, /* MS-DOS and MS-Windows codepages are included here, so that they can be * used on Unix too. Most of them are similar to ISO-8859 encodings, but * not exactly the same. */ #define IDX_CP437 30 - {"cp437", ENC_8BIT, 437}, /* like iso-8859-1 */ + { "cp437", ENC_8BIT, 437 }, // like iso-8859-1 #define IDX_CP737 31 - {"cp737", ENC_8BIT, 737}, /* like iso-8859-7 */ + { "cp737", ENC_8BIT, 737 }, // like iso-8859-7 #define IDX_CP775 32 - {"cp775", ENC_8BIT, 775}, /* Baltic */ + { "cp775", ENC_8BIT, 775 }, // Baltic #define IDX_CP850 33 - {"cp850", ENC_8BIT, 850}, /* like iso-8859-4 */ + { "cp850", ENC_8BIT, 850 }, // like iso-8859-4 #define IDX_CP852 34 - {"cp852", ENC_8BIT, 852}, /* like iso-8859-1 */ + { "cp852", ENC_8BIT, 852 }, // like iso-8859-1 #define IDX_CP855 35 - {"cp855", ENC_8BIT, 855}, /* like iso-8859-2 */ + { "cp855", ENC_8BIT, 855 }, // like iso-8859-2 #define IDX_CP857 36 - {"cp857", ENC_8BIT, 857}, /* like iso-8859-5 */ + { "cp857", ENC_8BIT, 857 }, // like iso-8859-5 #define IDX_CP860 37 - {"cp860", ENC_8BIT, 860}, /* like iso-8859-9 */ + { "cp860", ENC_8BIT, 860 }, // like iso-8859-9 #define IDX_CP861 38 - {"cp861", ENC_8BIT, 861}, /* like iso-8859-1 */ + { "cp861", ENC_8BIT, 861 }, // like iso-8859-1 #define IDX_CP862 39 - {"cp862", ENC_8BIT, 862}, /* like iso-8859-1 */ + { "cp862", ENC_8BIT, 862 }, // like iso-8859-1 #define IDX_CP863 40 - {"cp863", ENC_8BIT, 863}, /* like iso-8859-8 */ + { "cp863", ENC_8BIT, 863 }, // like iso-8859-8 #define IDX_CP865 41 - {"cp865", ENC_8BIT, 865}, /* like iso-8859-1 */ + { "cp865", ENC_8BIT, 865 }, // like iso-8859-1 #define IDX_CP866 42 - {"cp866", ENC_8BIT, 866}, /* like iso-8859-5 */ + { "cp866", ENC_8BIT, 866 }, // like iso-8859-5 #define IDX_CP869 43 - {"cp869", ENC_8BIT, 869}, /* like iso-8859-7 */ + { "cp869", ENC_8BIT, 869 }, // like iso-8859-7 #define IDX_CP874 44 - {"cp874", ENC_8BIT, 874}, /* Thai */ + { "cp874", ENC_8BIT, 874 }, // Thai #define IDX_CP932 45 - {"cp932", ENC_DBCS, DBCS_JPN}, + { "cp932", ENC_DBCS, DBCS_JPN }, #define IDX_CP936 46 - {"cp936", ENC_DBCS, DBCS_CHS}, + { "cp936", ENC_DBCS, DBCS_CHS }, #define IDX_CP949 47 - {"cp949", ENC_DBCS, DBCS_KOR}, + { "cp949", ENC_DBCS, DBCS_KOR }, #define IDX_CP950 48 - {"cp950", ENC_DBCS, DBCS_CHT}, + { "cp950", ENC_DBCS, DBCS_CHT }, #define IDX_CP1250 49 - {"cp1250", ENC_8BIT, 1250}, /* Czech, Polish, etc. */ + { "cp1250", ENC_8BIT, 1250 }, // Czech, Polish, etc. #define IDX_CP1251 50 - {"cp1251", ENC_8BIT, 1251}, /* Cyrillic */ - /* cp1252 is considered to be equal to latin1 */ + { "cp1251", ENC_8BIT, 1251 }, // Cyrillic + // cp1252 is considered to be equal to latin1 #define IDX_CP1253 51 - {"cp1253", ENC_8BIT, 1253}, /* Greek */ + { "cp1253", ENC_8BIT, 1253 }, // Greek #define IDX_CP1254 52 - {"cp1254", ENC_8BIT, 1254}, /* Turkish */ + { "cp1254", ENC_8BIT, 1254 }, // Turkish #define IDX_CP1255 53 - {"cp1255", ENC_8BIT, 1255}, /* Hebrew */ + { "cp1255", ENC_8BIT, 1255 }, // Hebrew #define IDX_CP1256 54 - {"cp1256", ENC_8BIT, 1256}, /* Arabic */ + { "cp1256", ENC_8BIT, 1256 }, // Arabic #define IDX_CP1257 55 - {"cp1257", ENC_8BIT, 1257}, /* Baltic */ + { "cp1257", ENC_8BIT, 1257 }, // Baltic #define IDX_CP1258 56 - {"cp1258", ENC_8BIT, 1258}, /* Vietnamese */ + { "cp1258", ENC_8BIT, 1258 }, // Vietnamese #define IDX_MACROMAN 57 - {"macroman", ENC_8BIT + ENC_MACROMAN, 0}, /* Mac OS */ + { "macroman", ENC_8BIT + ENC_MACROMAN, 0 }, // Mac OS #define IDX_HPROMAN8 58 - {"hp-roman8", ENC_8BIT, 0}, /* HP Roman8 */ + { "hp-roman8", ENC_8BIT, 0 }, // HP Roman8 #define IDX_COUNT 59 }; @@ -339,9 +337,11 @@ static int enc_canon_search(const char_u *name) { int i; - for (i = 0; i < IDX_COUNT; ++i) - if (STRCMP(name, enc_canon_table[i].name) == 0) + for (i = 0; i < IDX_COUNT; ++i) { + if (STRCMP(name, enc_canon_table[i].name) == 0) { return i; + } + } return -1; } @@ -356,12 +356,13 @@ int enc_canon_props(const char_u *name) int i; i = enc_canon_search(name); - if (i >= 0) + if (i >= 0) { return enc_canon_table[i].prop; - if (STRNCMP(name, "2byte-", 6) == 0) + } else if (STRNCMP(name, "2byte-", 6) == 0) { return ENC_DBCS; - if (STRNCMP(name, "8bit-", 5) == 0 || STRNCMP(name, "iso-8859-", 9) == 0) + } else if (STRNCMP(name, "8bit-", 5) == 0 || STRNCMP(name, "iso-8859-", 9) == 0) { return ENC_8BIT; + } return 0; } @@ -439,21 +440,23 @@ static bool intable(const struct interval *table, size_t n_items, int c) { int mid, bot, top; - /* first quick check for Latin1 etc. characters */ - if (c < table[0].first) + // first quick check for Latin1 etc. characters + if (c < table[0].first) { return false; + } - /* binary search in table */ + // binary search in table bot = 0; top = (int)(n_items - 1); while (top >= bot) { mid = (bot + top) / 2; - if (table[mid].last < c) + if (table[mid].last < c) { bot = mid + 1; - else if (table[mid].first > c) + } else if (table[mid].first > c) { top = mid - 1; - else + } else { return true; + } } return false; } @@ -515,12 +518,14 @@ int utf_ptr2cells(const char_u *p) // Need to convert to a character number. if (*p >= 0x80) { c = utf_ptr2char(p); - /* An illegal byte is displayed as <xx>. */ - if (utf_ptr2len(p) == 1 || c == NUL) + // An illegal byte is displayed as <xx>. + if (utf_ptr2len(p) == 1 || c == NUL) { return 4; - /* If the char is ASCII it must be an overlong sequence. */ - if (c < 0x80) + } + // If the char is ASCII it must be an overlong sequence. + if (c < 0x80) { return char2cells(c); + } return utf_char2cells(c); } return 1; @@ -532,17 +537,20 @@ int utf_ptr2cells_len(const char_u *p, int size) { int c; - /* Need to convert to a wide character. */ + // Need to convert to a wide character. if (size > 0 && *p >= 0x80) { - if (utf_ptr2len_len(p, size) < utf8len_tab[*p]) - return 1; /* truncated */ + if (utf_ptr2len_len(p, size) < utf8len_tab[*p]) { + return 1; // truncated + } c = utf_ptr2char(p); - /* An illegal byte is displayed as <xx>. */ - if (utf_ptr2len(p) == 1 || c == NUL) + // An illegal byte is displayed as <xx>. + if (utf_ptr2len(p) == 1 || c == NUL) { return 4; - /* If the char is ASCII it must be an overlong sequence. */ - if (c < 0x80) + } + // If the char is ASCII it must be an overlong sequence. + if (c < 0x80) { return char2cells(c); + } return utf_char2cells(c); } return 1; @@ -654,13 +662,14 @@ static int utf_safe_read_char_adv(const char_u **s, size_t *n) { int c; - if (*n == 0) /* end of buffer */ + if (*n == 0) { // end of buffer return 0; + } uint8_t k = utf8len_tab_zero[**s]; if (k == 1) { - /* ASCII character or NUL */ + // ASCII character or NUL (*n)--; return *(*s)++; } @@ -677,14 +686,14 @@ static int utf_safe_read_char_adv(const char_u **s, size_t *n) * U+00C3 (UTF-8: 0xC3 0x83), so need to check that special case too. * It's safe even if n=1, else we would have k=2 > n. */ if (c != (int)(**s) || (c == 0xC3 && (*s)[1] == 0x83)) { - /* byte sequence was successfully decoded */ + // byte sequence was successfully decoded *s += k; *n -= k; return c; } } - /* byte sequence is incomplete or illegal */ + // byte sequence is incomplete or illegal return -1; } @@ -724,10 +733,12 @@ bool utf_composinglike(const char_u *p1, const char_u *p2) int c2; c2 = utf_ptr2char(p2); - if (utf_iscomposing(c2)) + if (utf_iscomposing(c2)) { return true; - if (!arabic_maycombine(c2)) + } + if (!arabic_maycombine(c2)) { return false; + } return arabic_combine(utf_ptr2char(p1), c2); } @@ -749,23 +760,26 @@ int utfc_ptr2char(const char_u *p, int *pcc) c = utf_ptr2char(p); len = utf_ptr2len(p); - /* Only accept a composing char when the first char isn't illegal. */ + // Only accept a composing char when the first char isn't illegal. if ((len > 1 || *p < 0x80) && p[len] >= 0x80 && UTF_COMPOSINGLIKE(p, p + len)) { cc = utf_ptr2char(p + len); for (;; ) { pcc[i++] = cc; - if (i == MAX_MCO) + if (i == MAX_MCO) { break; + } len += utf_ptr2len(p + len); - if (p[len] < 0x80 || !utf_iscomposing(cc = utf_ptr2char(p + len))) + if (p[len] < 0x80 || !utf_iscomposing(cc = utf_ptr2char(p + len))) { break; + } } } - if (i < MAX_MCO) /* last composing char must be 0 */ + if (i < MAX_MCO) { // last composing char must be 0 pcc[i] = 0; + } return c; } @@ -858,15 +872,19 @@ int utf_ptr2len_len(const char_u *p, int size) int m; len = utf8len_tab[*p]; - if (len == 1) - return 1; /* NUL, ascii or illegal lead byte */ - if (len > size) - m = size; /* incomplete byte sequence. */ - else + if (len == 1) { + return 1; // NUL, ascii or illegal lead byte + } + if (len > size) { + m = size; // incomplete byte sequence. + } else { m = len; - for (i = 1; i < m; ++i) - if ((p[i] & 0xc0) != 0x80) + } + for (i = 1; i < m; ++i) { + if ((p[i] & 0xc0) != 0x80) { return 1; + } + } return len; } @@ -918,17 +936,20 @@ int utfc_ptr2len_len(const char_u *p, int size) int len; int prevlen; - if (size < 1 || *p == NUL) + if (size < 1 || *p == NUL) { return 0; - if (p[0] < 0x80 && (size == 1 || p[1] < 0x80)) /* be quick for ASCII */ + } + if (p[0] < 0x80 && (size == 1 || p[1] < 0x80)) { // be quick for ASCII return 1; + } - /* Skip over first UTF-8 char, stopping at a NUL byte. */ + // Skip over first UTF-8 char, stopping at a NUL byte. len = utf_ptr2len_len(p, size); - /* Check for illegal byte and incomplete byte sequence. */ - if ((len == 1 && p[0] >= 0x80) || len > size) + // Check for illegal byte and incomplete byte sequence. + if ((len == 1 && p[0] >= 0x80) || len > size) { return 1; + } /* * Check for composing characters. We can handle only the first six, but @@ -938,21 +959,24 @@ int utfc_ptr2len_len(const char_u *p, int size) while (len < size) { int len_next_char; - if (p[len] < 0x80) + if (p[len] < 0x80) { break; + } /* * Next character length should not go beyond size to ensure that * UTF_COMPOSINGLIKE(...) does not read beyond size. */ len_next_char = utf_ptr2len_len(p + len, size - len); - if (len_next_char > size - len) + if (len_next_char > size - len) { break; + } - if (!UTF_COMPOSINGLIKE(p + prevlen, p + len)) + if (!UTF_COMPOSINGLIKE(p + prevlen, p + len)) { break; + } - /* Skip over composing char */ + // Skip over composing char prevlen = len; len += len_next_char; } @@ -1046,9 +1070,9 @@ bool utf_printable(int c) * 0xd800-0xdfff is reserved for UTF-16, actually illegal. */ static struct interval nonprint[] = { - {0x070f, 0x070f}, {0x180b, 0x180e}, {0x200b, 0x200f}, {0x202a, 0x202e}, - {0x206a, 0x206f}, {0xd800, 0xdfff}, {0xfeff, 0xfeff}, {0xfff9, 0xfffb}, - {0xfffe, 0xffff} + { 0x070f, 0x070f }, { 0x180b, 0x180e }, { 0x200b, 0x200f }, { 0x202a, 0x202e }, + { 0x206a, 0x206f }, { 0xd800, 0xdfff }, { 0xfeff, 0xfeff }, { 0xfff9, 0xfffb }, + { 0xfffe, 0xffff } }; return !intable(nonprint, ARRAY_SIZE(nonprint), c); @@ -1068,7 +1092,7 @@ int utf_class(const int c) int utf_class_tab(const int c, const uint64_t *const chartab) { - /* sorted list of non-overlapping intervals */ + // sorted list of non-overlapping intervals static struct clinterval { unsigned int first; unsigned int last; @@ -1150,7 +1174,7 @@ int utf_class_tab(const int c, const uint64_t *const chartab) int top = ARRAY_SIZE(classes) - 1; int mid; - /* First quick check for Latin1 characters, use 'iskeyword'. */ + // First quick check for Latin1 characters, use 'iskeyword'. if (c < 0x100) { if (c == ' ' || c == '\t' || c == NUL || c == 0xa0) { return 0; // blank @@ -1161,15 +1185,16 @@ int utf_class_tab(const int c, const uint64_t *const chartab) return 1; // punctuation } - /* binary search in table */ + // binary search in table while (top >= bot) { mid = (bot + top) / 2; - if (classes[mid].last < (unsigned int)c) + if (classes[mid].last < (unsigned int)c) { bot = mid + 1; - else if (classes[mid].first > (unsigned int)c) + } else if (classes[mid].first > (unsigned int)c) { top = mid - 1; - else + } else { return (int)classes[mid].class; + } } // emoji @@ -1177,7 +1202,7 @@ int utf_class_tab(const int c, const uint64_t *const chartab) return 3; } - /* most other characters are "word" characters */ + // most other characters are "word" characters return 2; } @@ -1194,25 +1219,27 @@ bool utf_ambiguous_width(int c) */ static int utf_convert(int a, const convertStruct *const table, size_t n_items) { - size_t start, mid, end; /* indices into table */ + size_t start, mid, end; // indices into table start = 0; end = n_items; while (start < end) { - /* need to search further */ + // need to search further mid = (end + start) / 2; - if (table[mid].rangeEnd < a) + if (table[mid].rangeEnd < a) { start = mid + 1; - else + } else { end = mid; + } } if (start < n_items && table[start].rangeStart <= a && a <= table[start].rangeEnd - && (a - table[start].rangeStart) % table[start].step == 0) + && (a - table[start].rangeStart) % table[start].step == 0) { return a + table[start].offset; - else + } else { return a; + } } /* @@ -1237,21 +1264,24 @@ int utf_fold(int a) /// simple case folding. int mb_toupper(int a) { - /* If 'casemap' contains "keepascii" use ASCII style toupper(). */ - if (a < 128 && (cmp_flags & CMP_KEEPASCII)) + // If 'casemap' contains "keepascii" use ASCII style toupper(). + if (a < 128 && (cmp_flags & CMP_KEEPASCII)) { return TOUPPER_ASC(a); + } #if defined(__STDC_ISO_10646__) - /* If towupper() is available and handles Unicode, use it. */ - if (!(cmp_flags & CMP_INTERNAL)) + // If towupper() is available and handles Unicode, use it. + if (!(cmp_flags & CMP_INTERNAL)) { return towupper(a); + } #endif - /* For characters below 128 use locale sensitive toupper(). */ - if (a < 128) + // For characters below 128 use locale sensitive toupper(). + if (a < 128) { return TOUPPER_LOC(a); + } - /* For any other characters use the above mapping table. */ + // For any other characters use the above mapping table. return utf_convert(a, toUpper, ARRAY_SIZE(toUpper)); } @@ -1265,21 +1295,24 @@ bool mb_islower(int a) /// simple case folding. int mb_tolower(int a) { - /* If 'casemap' contains "keepascii" use ASCII style tolower(). */ - if (a < 128 && (cmp_flags & CMP_KEEPASCII)) + // If 'casemap' contains "keepascii" use ASCII style tolower(). + if (a < 128 && (cmp_flags & CMP_KEEPASCII)) { return TOLOWER_ASC(a); + } #if defined(__STDC_ISO_10646__) - /* If towlower() is available and handles Unicode, use it. */ - if (!(cmp_flags & CMP_INTERNAL)) + // If towlower() is available and handles Unicode, use it. + if (!(cmp_flags & CMP_INTERNAL)) { return towlower(a); + } #endif - /* For characters below 128 use locale sensitive tolower(). */ - if (a < 128) + // For characters below 128 use locale sensitive tolower(). + if (a < 128) { return TOLOWER_LOC(a); + } - /* For any other characters use the above mapping table. */ + // For any other characters use the above mapping table. return utf_convert(a, toLower, ARRAY_SIZE(toLower)); } @@ -1288,8 +1321,7 @@ bool mb_isupper(int a) return mb_tolower(a) != a; } -static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, - size_t n2) +static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, size_t n2) { int c1, c2, cdiff; char_u buffer[6]; @@ -1298,23 +1330,27 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, c1 = utf_safe_read_char_adv(&s1, &n1); c2 = utf_safe_read_char_adv(&s2, &n2); - if (c1 <= 0 || c2 <= 0) + if (c1 <= 0 || c2 <= 0) { break; + } - if (c1 == c2) + if (c1 == c2) { continue; + } cdiff = utf_fold(c1) - utf_fold(c2); - if (cdiff != 0) + if (cdiff != 0) { return cdiff; + } } - /* some string ended or has an incomplete/illegal character sequence */ + // some string ended or has an incomplete/illegal character sequence if (c1 == 0 || c2 == 0) { - /* some string ended. shorter string is smaller */ - if (c1 == 0 && c2 == 0) + // some string ended. shorter string is smaller + if (c1 == 0 && c2 == 0) { return 0; + } return c1 == 0 ? -1 : 1; } @@ -1335,8 +1371,9 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, while (n1 > 0 && n2 > 0 && *s1 != NUL && *s2 != NUL) { cdiff = (int)(*s1) - (int)(*s2); - if (cdiff != 0) + if (cdiff != 0) { return cdiff; + } s1++; s2++; @@ -1344,19 +1381,22 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, n2--; } - if (n1 > 0 && *s1 == NUL) + if (n1 > 0 && *s1 == NUL) { n1 = 0; - if (n2 > 0 && *s2 == NUL) + } + if (n2 > 0 && *s2 == NUL) { n2 = 0; + } - if (n1 == 0 && n2 == 0) + if (n1 == 0 && n2 == 0) { return 0; + } return n1 == 0 ? -1 : 1; } #ifdef WIN32 #ifndef CP_UTF8 -# define CP_UTF8 65001 /* magic number from winnls.h */ +# define CP_UTF8 65001 // magic number from winnls.h #endif /// Converts string from UTF-8 to UTF-16. @@ -1456,8 +1496,7 @@ int utf16_to_utf8(const wchar_t *utf16, int utf16len, char **utf8) /// @param len maximum length (an earlier NUL terminates) /// @param[out] codepoints incremented with UTF-32 code point size /// @param[out] codeunits incremented with UTF-16 code unit size -void mb_utflen(const char_u *s, size_t len, size_t *codepoints, - size_t *codeunits) +void mb_utflen(const char_u *s, size_t len, size_t *codepoints, size_t *codeunits) FUNC_ATTR_NONNULL_ALL { size_t count = 0, extra = 0; @@ -1476,8 +1515,7 @@ void mb_utflen(const char_u *s, size_t len, size_t *codepoints, *codeunits += count + extra; } -ssize_t mb_utf_index_to_bytes(const char_u *s, size_t len, - size_t index, bool use_utf16_units) +ssize_t mb_utf_index_to_bytes(const char_u *s, size_t len, size_t index, bool use_utf16_units) FUNC_ATTR_NONNULL_ALL { size_t count = 0; @@ -1540,7 +1578,7 @@ void show_utf8(void) { int len; int rlen = 0; - char_u *line; + char_u *line; int clen; int i; @@ -1556,7 +1594,7 @@ void show_utf8(void) clen = 0; for (i = 0; i < len; ++i) { if (clen == 0) { - /* start of (composing) character, get its length */ + // start of (composing) character, get its length if (i > 0) { STRCPY(IObuff + rlen, "+ "); rlen += 2; @@ -1564,11 +1602,12 @@ void show_utf8(void) clen = utf_ptr2len(line + i); } sprintf((char *)IObuff + rlen, "%02x ", - (line[i] == NL) ? NUL : line[i]); /* NUL is stored as NL */ + (line[i] == NL) ? NUL : line[i]); // NUL is stored as NL --clen; rlen += (int)STRLEN(IObuff + rlen); - if (rlen > IOSIZE - 20) + if (rlen > IOSIZE - 20) { break; + } } msg(IObuff); @@ -1582,42 +1621,49 @@ int utf_head_off(const char_u *base, const char_u *p) int c; int len; - if (*p < 0x80) /* be quick for ASCII */ + if (*p < 0x80) { // be quick for ASCII return 0; + } /* Skip backwards over trailing bytes: 10xx.xxxx * Skip backwards again if on a composing char. */ const char_u *q; for (q = p;; --q) { - /* Move s to the last byte of this char. */ + // Move s to the last byte of this char. const char_u *s; for (s = q; (s[1] & 0xc0) == 0x80; ++s) {} - /* Move q to the first byte of this char. */ - while (q > base && (*q & 0xc0) == 0x80) + // Move q to the first byte of this char. + while (q > base && (*q & 0xc0) == 0x80) { --q; + } /* Check for illegal sequence. Do allow an illegal byte after where we * started. */ len = utf8len_tab[*q]; - if (len != (int)(s - q + 1) && len != (int)(p - q + 1)) + if (len != (int)(s - q + 1) && len != (int)(p - q + 1)) { return 0; + } - if (q <= base) + if (q <= base) { break; + } c = utf_ptr2char(q); - if (utf_iscomposing(c)) + if (utf_iscomposing(c)) { continue; + } if (arabic_maycombine(c)) { - /* Advance to get a sneak-peak at the next char */ + // Advance to get a sneak-peak at the next char const char_u *j = q; --j; - /* Move j to the first byte of this char. */ - while (j > base && (*j & 0xc0) == 0x80) + // Move j to the first byte of this char. + while (j > base && (*j & 0xc0) == 0x80) { --j; - if (arabic_combine(utf_ptr2char(j), c)) + } + if (arabic_combine(utf_ptr2char(j), c)) { continue; + } } break; } @@ -1630,12 +1676,12 @@ bool utf_eat_space(int cc) FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { return (cc >= 0x2000 && cc <= 0x206F) // General punctuations - || (cc >= 0x2e00 && cc <= 0x2e7f) // Supplemental punctuations - || (cc >= 0x3000 && cc <= 0x303f) // CJK symbols and punctuations - || (cc >= 0xff01 && cc <= 0xff0f) // Full width ASCII punctuations - || (cc >= 0xff1a && cc <= 0xff20) // .. - || (cc >= 0xff3b && cc <= 0xff40) // .. - || (cc >= 0xff5b && cc <= 0xff65); // .. + || (cc >= 0x2e00 && cc <= 0x2e7f) // Supplemental punctuations + || (cc >= 0x3000 && cc <= 0x303f) // CJK symbols and punctuations + || (cc >= 0xff01 && cc <= 0xff0f) // Full width ASCII punctuations + || (cc >= 0xff1a && cc <= 0xff20) // .. + || (cc >= 0xff3b && cc <= 0xff40) // .. + || (cc >= 0xff5b && cc <= 0xff65); // .. } // Whether line break is allowed before "cc". @@ -1817,8 +1863,9 @@ int mb_tail_off(char_u *base, char_u *p) int i; int j; - if (*p == NUL) + if (*p == NUL) { return 0; + } // Find the last character that is 10xx.xxxx for (i = 0; (p[i + 1] & 0xc0) == 0x80; i++) {} @@ -1842,10 +1889,10 @@ int mb_tail_off(char_u *base, char_u *p) void utf_find_illegal(void) { pos_T pos = curwin->w_cursor; - char_u *p; + char_u *p; int len; vimconv_T vimconv; - char_u *tofree = NULL; + char_u *tofree = NULL; vimconv.vc_type = CONV_NONE; if (enc_canon_props(curbuf->b_p_fenc) & ENC_8BIT) { @@ -1861,8 +1908,9 @@ void utf_find_illegal(void) if (vimconv.vc_type != CONV_NONE) { xfree(tofree); tofree = string_convert(&vimconv, p, NULL); - if (tofree == NULL) + if (tofree == NULL) { break; + } p = tofree; } @@ -1871,10 +1919,10 @@ void utf_find_illegal(void) * utf_ptr2len()) or too many of them (overlong sequence). */ len = utf_ptr2len(p); if (*p >= 0x80 && (len == 1 - || utf_char2len(utf_ptr2char(p)) != len)) { - if (vimconv.vc_type == CONV_NONE) + || utf_char2len(utf_ptr2char(p)) != len)) { + if (vimconv.vc_type == CONV_NONE) { curwin->w_cursor.col += (colnr_T)(p - get_cursor_pos_ptr()); - else { + } else { int l; len = (int)(p - tofree); @@ -1887,13 +1935,14 @@ void utf_find_illegal(void) } p += len; } - if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) + if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) { break; + } ++curwin->w_cursor.lnum; curwin->w_cursor.col = 0; } - /* didn't find it: don't move and beep */ + // didn't find it: don't move and beep curwin->w_cursor = pos; beep_flush(); @@ -1947,13 +1996,10 @@ void mb_check_adjust_col(void *win_) } } -/* - * Return a pointer to the character before "*p", if there is one. - */ -char_u * mb_prevptr( - char_u *line, /* start of the string */ - char_u *p - ) +/// @param line start of the string +/// +/// @return a pointer to the character before "*p", if there is one. +char_u * mb_prevptr(char_u *line, char_u *p) { if (p > line) { MB_PTR_BACK(line, p); @@ -1967,14 +2013,16 @@ char_u * mb_prevptr( */ int mb_charlen(char_u *str) { - char_u *p = str; + char_u *p = str; int count; - if (p == NULL) + if (p == NULL) { return 0; + } - for (count = 0; *p != NUL; count++) + for (count = 0; *p != NUL; count++) { p += (*mb_ptr2len)(p); + } return count; } @@ -1984,11 +2032,12 @@ int mb_charlen(char_u *str) */ int mb_charlen_len(char_u *str, int len) { - char_u *p = str; + char_u *p = str; int count; - for (count = 0; *p != NUL && p < str + len; count++) + for (count = 0; *p != NUL && p < str + len; count++) { p += (*mb_ptr2len)(p); + } return count; } @@ -2052,10 +2101,12 @@ const char *mb_unescape(const char **const pp) */ char_u * enc_skip(char_u *p) { - if (STRNCMP(p, "2byte-", 6) == 0) + if (STRNCMP(p, "2byte-", 6) == 0) { return p + 6; - if (STRNCMP(p, "8bit-", 5) == 0) + } + if (STRNCMP(p, "8bit-", 5) == 0) { return p + 5; + } return p; } @@ -2067,7 +2118,7 @@ char_u * enc_skip(char_u *p) */ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET { - char_u *p, *s; + char_u *p, *s; int i; if (STRCMP(enc, "default") == 0) { @@ -2075,47 +2126,51 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET return vim_strsave(fenc_default); } - /* copy "enc" to allocated memory, with room for two '-' */ + // copy "enc" to allocated memory, with room for two '-' char_u *r = xmalloc(STRLEN(enc) + 3); - /* Make it all lower case and replace '_' with '-'. */ + // Make it all lower case and replace '_' with '-'. p = r; for (s = enc; *s != NUL; ++s) { - if (*s == '_') + if (*s == '_') { *p++ = '-'; - else + } else { *p++ = TOLOWER_ASC(*s); + } } *p = NUL; - /* Skip "2byte-" and "8bit-". */ + // Skip "2byte-" and "8bit-". p = enc_skip(r); - /* Change "microsoft-cp" to "cp". Used in some spell files. */ - if (STRNCMP(p, "microsoft-cp", 12) == 0) + // Change "microsoft-cp" to "cp". Used in some spell files. + if (STRNCMP(p, "microsoft-cp", 12) == 0) { STRMOVE(p, p + 10); + } - /* "iso8859" -> "iso-8859" */ + // "iso8859" -> "iso-8859" if (STRNCMP(p, "iso8859", 7) == 0) { STRMOVE(p + 4, p + 3); p[3] = '-'; } - /* "iso-8859n" -> "iso-8859-n" */ + // "iso-8859n" -> "iso-8859-n" if (STRNCMP(p, "iso-8859", 8) == 0 && p[8] != '-') { STRMOVE(p + 9, p + 8); p[8] = '-'; } - /* "latin-N" -> "latinN" */ - if (STRNCMP(p, "latin-", 6) == 0) + // "latin-N" -> "latinN" + if (STRNCMP(p, "latin-", 6) == 0) { STRMOVE(p + 5, p + 6); + } if (enc_canon_search(p) >= 0) { - /* canonical name can be used unmodified */ - if (p != r) + // canonical name can be used unmodified + if (p != r) { STRMOVE(r, p); + } } else if ((i = enc_alias_search(p)) >= 0) { - /* alias recognized, get canonical name */ + // alias recognized, get canonical name xfree(r); r = vim_strsave((char_u *)enc_canon_table[i].name); } @@ -2130,9 +2185,11 @@ static int enc_alias_search(char_u *name) { int i; - for (i = 0; enc_alias_table[i].name != NULL; ++i) - if (STRCMP(name, enc_alias_table[i].name) == 0) + for (i = 0; enc_alias_table[i].name != NULL; ++i) { + if (STRCMP(name, enc_alias_table[i].name) == 0) { return enc_alias_table[i].canon; + } + } return -1; } @@ -2222,13 +2279,13 @@ void * my_iconv_open(char_u *to, char_u *from) iconv_t fd; #define ICONV_TESTLEN 400 char_u tobuf[ICONV_TESTLEN]; - char *p; + char *p; size_t tolen; static WorkingStatus iconv_working = kUnknown; - if (iconv_working == kBroken) - return (void *)-1; /* detected a broken iconv() previously */ - + if (iconv_working == kBroken) { + return (void *)-1; // detected a broken iconv() previously + } fd = iconv_open((char *)enc_skip(to), (char *)enc_skip(from)); if (fd != (iconv_t)-1 && iconv_working == kUnknown) { @@ -2246,8 +2303,9 @@ void * my_iconv_open(char_u *to, char_u *from) iconv_working = kBroken; iconv_close(fd); fd = (iconv_t)-1; - } else + } else { iconv_working = kWorking; + } } return (void *)fd; @@ -2260,17 +2318,17 @@ void * my_iconv_open(char_u *to, char_u *from) * Returns the converted string in allocated memory. NULL for an error. * If resultlenp is not NULL, sets it to the result length in bytes. */ -static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, - size_t slen, size_t *unconvlenp, size_t *resultlenp) +static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen, + size_t *unconvlenp, size_t *resultlenp) { - const char *from; + const char *from; size_t fromlen; - char *to; + char *to; size_t tolen; size_t len = 0; size_t done = 0; - char_u *result = NULL; - char_u *p; + char_u *result = NULL; + char_u *p; int l; from = (char *)str; @@ -2281,8 +2339,9 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, * increase the buffer size. */ len = len + fromlen * 2 + 40; p = xmalloc(len); - if (done > 0) + if (done > 0) { memmove(p, result, done); + } xfree(result); result = p; } @@ -2330,8 +2389,9 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, done = to - (char *)result; } - if (resultlenp != NULL && result != NULL) + if (resultlenp != NULL && result != NULL) { *resultlenp = (size_t)(to - (char *)result); + } return result; } @@ -2354,12 +2414,10 @@ int convert_setup(vimconv_T *vcp, char_u *from, char_u *to) return convert_setup_ext(vcp, from, true, to, true); } -/* - * As convert_setup(), but only when from_unicode_is_utf8 is TRUE will all - * "from" unicode charsets be considered utf-8. Same for "to". - */ -int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, - char_u *to, bool to_unicode_is_utf8) +/// As convert_setup(), but only when from_unicode_is_utf8 is true will all +/// "from" unicode charsets be considered utf-8. Same for "to". +int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, char_u *to, + bool to_unicode_is_utf8) { int from_prop; int to_prop; @@ -2374,51 +2432,54 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, # endif *vcp = (vimconv_T)MBYTE_NONE_CONV; - /* No conversion when one of the names is empty or they are equal. */ + // No conversion when one of the names is empty or they are equal. if (from == NULL || *from == NUL || to == NULL || *to == NUL - || STRCMP(from, to) == 0) + || STRCMP(from, to) == 0) { return OK; + } from_prop = enc_canon_props(from); to_prop = enc_canon_props(to); - if (from_unicode_is_utf8) + if (from_unicode_is_utf8) { from_is_utf8 = from_prop & ENC_UNICODE; - else + } else { from_is_utf8 = from_prop == ENC_UNICODE; - if (to_unicode_is_utf8) + } + if (to_unicode_is_utf8) { to_is_utf8 = to_prop & ENC_UNICODE; - else + } else { to_is_utf8 = to_prop == ENC_UNICODE; + } if ((from_prop & ENC_LATIN1) && to_is_utf8) { - /* Internal latin1 -> utf-8 conversion. */ + // Internal latin1 -> utf-8 conversion. vcp->vc_type = CONV_TO_UTF8; - vcp->vc_factor = 2; /* up to twice as long */ + vcp->vc_factor = 2; // up to twice as long } else if ((from_prop & ENC_LATIN9) && to_is_utf8) { - /* Internal latin9 -> utf-8 conversion. */ + // Internal latin9 -> utf-8 conversion. vcp->vc_type = CONV_9_TO_UTF8; - vcp->vc_factor = 3; /* up to three as long (euro sign) */ + vcp->vc_factor = 3; // up to three as long (euro sign) } else if (from_is_utf8 && (to_prop & ENC_LATIN1)) { - /* Internal utf-8 -> latin1 conversion. */ + // Internal utf-8 -> latin1 conversion. vcp->vc_type = CONV_TO_LATIN1; } else if (from_is_utf8 && (to_prop & ENC_LATIN9)) { - /* Internal utf-8 -> latin9 conversion. */ + // Internal utf-8 -> latin9 conversion. vcp->vc_type = CONV_TO_LATIN9; } # ifdef HAVE_ICONV else { // NOLINT(readability/braces) // Use iconv() for conversion. - vcp->vc_fd = (iconv_t)my_iconv_open( - to_is_utf8 ? (char_u *)"utf-8" : to, - from_is_utf8 ? (char_u *)"utf-8" : from); + vcp->vc_fd = (iconv_t)my_iconv_open(to_is_utf8 ? (char_u *)"utf-8" : to, + from_is_utf8 ? (char_u *)"utf-8" : from); if (vcp->vc_fd != (iconv_t)-1) { vcp->vc_type = CONV_ICONV; - vcp->vc_factor = 4; /* could be longer too... */ + vcp->vc_factor = 4; // could be longer too... } } # endif - if (vcp->vc_type == CONV_NONE) + if (vcp->vc_type == CONV_NONE) { return FAIL; + } return OK; } @@ -2440,129 +2501,153 @@ char_u *string_convert(const vimconv_T *const vcp, char_u *ptr, size_t *lenp) * an incomplete sequence at the end it is not converted and "*unconvlenp" is * set to the number of remaining bytes. */ -char_u * string_convert_ext(const vimconv_T *const vcp, char_u *ptr, - size_t *lenp, size_t *unconvlenp) +char_u * string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp, + size_t *unconvlenp) { - char_u *retval = NULL; - char_u *d; + char_u *retval = NULL; + char_u *d; int l; int c; size_t len; - if (lenp == NULL) + if (lenp == NULL) { len = STRLEN(ptr); - else + } else { len = *lenp; - if (len == 0) + } + if (len == 0) { return vim_strsave((char_u *)""); + } switch (vcp->vc_type) { - case CONV_TO_UTF8: /* latin1 to utf-8 conversion */ - retval = xmalloc(len * 2 + 1); - d = retval; - for (size_t i = 0; i < len; ++i) { - c = ptr[i]; - if (c < 0x80) - *d++ = c; - else { - *d++ = 0xc0 + ((unsigned)c >> 6); - *d++ = 0x80 + (c & 0x3f); - } + case CONV_TO_UTF8: // latin1 to utf-8 conversion + retval = xmalloc(len * 2 + 1); + d = retval; + for (size_t i = 0; i < len; ++i) { + c = ptr[i]; + if (c < 0x80) { + *d++ = c; + } else { + *d++ = 0xc0 + ((unsigned)c >> 6); + *d++ = 0x80 + (c & 0x3f); } - *d = NUL; - if (lenp != NULL) - *lenp = (size_t)(d - retval); - break; + } + *d = NUL; + if (lenp != NULL) { + *lenp = (size_t)(d - retval); + } + break; - case CONV_9_TO_UTF8: /* latin9 to utf-8 conversion */ - retval = xmalloc(len * 3 + 1); - d = retval; - for (size_t i = 0; i < len; ++i) { - c = ptr[i]; - switch (c) { - case 0xa4: c = 0x20ac; break; /* euro */ - case 0xa6: c = 0x0160; break; /* S hat */ - case 0xa8: c = 0x0161; break; /* S -hat */ - case 0xb4: c = 0x017d; break; /* Z hat */ - case 0xb8: c = 0x017e; break; /* Z -hat */ - case 0xbc: c = 0x0152; break; /* OE */ - case 0xbd: c = 0x0153; break; /* oe */ - case 0xbe: c = 0x0178; break; /* Y */ - } - d += utf_char2bytes(c, d); + case CONV_9_TO_UTF8: // latin9 to utf-8 conversion + retval = xmalloc(len * 3 + 1); + d = retval; + for (size_t i = 0; i < len; ++i) { + c = ptr[i]; + switch (c) { + case 0xa4: + c = 0x20ac; break; // euro + case 0xa6: + c = 0x0160; break; // S hat + case 0xa8: + c = 0x0161; break; // S -hat + case 0xb4: + c = 0x017d; break; // Z hat + case 0xb8: + c = 0x017e; break; // Z -hat + case 0xbc: + c = 0x0152; break; // OE + case 0xbd: + c = 0x0153; break; // oe + case 0xbe: + c = 0x0178; break; // Y } - *d = NUL; - if (lenp != NULL) - *lenp = (size_t)(d - retval); - break; + d += utf_char2bytes(c, d); + } + *d = NUL; + if (lenp != NULL) { + *lenp = (size_t)(d - retval); + } + break; - case CONV_TO_LATIN1: /* utf-8 to latin1 conversion */ - case CONV_TO_LATIN9: /* utf-8 to latin9 conversion */ - retval = xmalloc(len + 1); - d = retval; - for (size_t i = 0; i < len; ++i) { - l = utf_ptr2len_len(ptr + i, len - i); - if (l == 0) - *d++ = NUL; - else if (l == 1) { - uint8_t l_w = utf8len_tab_zero[ptr[i]]; - - if (l_w == 0) { - /* Illegal utf-8 byte cannot be converted */ + case CONV_TO_LATIN1: // utf-8 to latin1 conversion + case CONV_TO_LATIN9: // utf-8 to latin9 conversion + retval = xmalloc(len + 1); + d = retval; + for (size_t i = 0; i < len; ++i) { + l = utf_ptr2len_len(ptr + i, len - i); + if (l == 0) { + *d++ = NUL; + } else if (l == 1) { + uint8_t l_w = utf8len_tab_zero[ptr[i]]; + + if (l_w == 0) { + // Illegal utf-8 byte cannot be converted + xfree(retval); + return NULL; + } + if (unconvlenp != NULL && l_w > len - i) { + // Incomplete sequence at the end. + *unconvlenp = len - i; + break; + } + *d++ = ptr[i]; + } else { + c = utf_ptr2char(ptr + i); + if (vcp->vc_type == CONV_TO_LATIN9) { + switch (c) { + case 0x20ac: + c = 0xa4; break; // euro + case 0x0160: + c = 0xa6; break; // S hat + case 0x0161: + c = 0xa8; break; // S -hat + case 0x017d: + c = 0xb4; break; // Z hat + case 0x017e: + c = 0xb8; break; // Z -hat + case 0x0152: + c = 0xbc; break; // OE + case 0x0153: + c = 0xbd; break; // oe + case 0x0178: + c = 0xbe; break; // Y + case 0xa4: + case 0xa6: + case 0xa8: + case 0xb4: + case 0xb8: + case 0xbc: + case 0xbd: + case 0xbe: + c = 0x100; break; // not in latin9 + } + } + if (!utf_iscomposing(c)) { // skip composing chars + if (c < 0x100) { + *d++ = c; + } else if (vcp->vc_fail) { xfree(retval); return NULL; - } - if (unconvlenp != NULL && l_w > len - i) { - /* Incomplete sequence at the end. */ - *unconvlenp = len - i; - break; - } - *d++ = ptr[i]; - } else { - c = utf_ptr2char(ptr + i); - if (vcp->vc_type == CONV_TO_LATIN9) - switch (c) { - case 0x20ac: c = 0xa4; break; /* euro */ - case 0x0160: c = 0xa6; break; /* S hat */ - case 0x0161: c = 0xa8; break; /* S -hat */ - case 0x017d: c = 0xb4; break; /* Z hat */ - case 0x017e: c = 0xb8; break; /* Z -hat */ - case 0x0152: c = 0xbc; break; /* OE */ - case 0x0153: c = 0xbd; break; /* oe */ - case 0x0178: c = 0xbe; break; /* Y */ - case 0xa4: - case 0xa6: - case 0xa8: - case 0xb4: - case 0xb8: - case 0xbc: - case 0xbd: - case 0xbe: c = 0x100; break; /* not in latin9 */ - } - if (!utf_iscomposing(c)) { /* skip composing chars */ - if (c < 0x100) - *d++ = c; - else if (vcp->vc_fail) { - xfree(retval); - return NULL; - } else { - *d++ = 0xbf; - if (utf_char2cells(c) > 1) - *d++ = '?'; + } else { + *d++ = 0xbf; + if (utf_char2cells(c) > 1) { + *d++ = '?'; } } - i += l - 1; } + i += l - 1; } - *d = NUL; - if (lenp != NULL) - *lenp = (size_t)(d - retval); - break; + } + *d = NUL; + if (lenp != NULL) { + *lenp = (size_t)(d - retval); + } + break; # ifdef HAVE_ICONV - case CONV_ICONV: // conversion with vcp->vc_fd - retval = iconv_string(vcp, ptr, len, unconvlenp, lenp); - break; + case CONV_ICONV: // conversion with vcp->vc_fd + retval = iconv_string(vcp, ptr, len, unconvlenp, lenp); + break; # endif } diff --git a/src/nvim/memfile_defs.h b/src/nvim/memfile_defs.h index 2402d2147d..3eaa7d83e0 100644 --- a/src/nvim/memfile_defs.h +++ b/src/nvim/memfile_defs.h @@ -101,7 +101,7 @@ typedef struct memfile { blocknr_T mf_neg_count; /// number of negative blocks numbers blocknr_T mf_infile_count; /// number of pages in the file unsigned mf_page_size; /// number of bytes in a page - bool mf_dirty; /// TRUE if there are dirty blocks + bool mf_dirty; /// true if there are dirty blocks } memfile_T; #endif // NVIM_MEMFILE_DEFS_H diff --git a/src/nvim/memline.c b/src/nvim/memline.c index e42b138253..230361b997 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -118,15 +118,15 @@ struct pointer_block { * etc. Thus the order of the lines is the opposite of the line number. */ struct data_block { - uint16_t db_id; /* ID for data block: DATA_ID */ - unsigned db_free; /* free space available */ - unsigned db_txt_start; /* byte where text starts */ - unsigned db_txt_end; /* byte just after data block */ - linenr_T db_line_count; /* number of lines in this block */ - unsigned db_index[1]; /* index for start of line (actually bigger) - * followed by empty space upto db_txt_start - * followed by the text in the lines until - * end of page */ + uint16_t db_id; // ID for data block: DATA_ID + unsigned db_free; // free space available + unsigned db_txt_start; // byte where text starts + unsigned db_txt_end; // byte just after data block + linenr_T db_line_count; // number of lines in this block + unsigned db_index[1]; // index for start of line (actually bigger) + // followed by empty space up to db_txt_start + // followed by the text in the lines until + // end of page }; /* @@ -382,7 +382,7 @@ error: */ void ml_setname(buf_T *buf) { - int success = FALSE; + bool success = false; memfile_T *mfp; char_u *fname; char_u *dirp; @@ -418,7 +418,7 @@ void ml_setname(buf_T *buf) /* if the file name is the same we don't have to do anything */ if (fnamecmp(fname, mfp->mf_fname) == 0) { xfree(fname); - success = TRUE; + success = true; break; } /* need to close the swap file before renaming */ @@ -429,7 +429,7 @@ void ml_setname(buf_T *buf) /* try to rename the swap file */ if (vim_rename(mfp->mf_fname, fname) == 0) { - success = TRUE; + success = true; mf_free_fnames(mfp); mf_set_fnames(mfp, fname); ml_upd_block0(buf, UB_SAME_DIR); @@ -758,12 +758,12 @@ void ml_recover(bool checkext) blocknr_T bnum; int page_count; int len; - int directly; + bool directly; linenr_T lnum; char_u *p; int i; long error; - int cannot_open; + bool cannot_open; linenr_T line_count; bool has_error; int idx; @@ -771,7 +771,7 @@ void ml_recover(bool checkext) int txt_start; off_T size; int called_from_main; - int serious_error = TRUE; + bool serious_error = true; long mtime; int attr; int orig_file_status = NOTDONE; @@ -791,10 +791,10 @@ void ml_recover(bool checkext) && vim_strchr((char_u *)"abcdefghijklmnopqrstuvw", TOLOWER_ASC(fname[len - 2])) != NULL && ASCII_ISALPHA(fname[len - 1])) { - directly = TRUE; - fname_used = vim_strsave(fname); /* make a copy for mf_open() */ + directly = true; + fname_used = vim_strsave(fname); // make a copy for mf_open() } else { - directly = FALSE; + directly = false; /* count the number of matching swap files */ len = recover_names(fname, FALSE, 0, NULL); @@ -1018,12 +1018,13 @@ void ml_recover(bool checkext) buf->b_ml.ml_stack = NULL; buf->b_ml.ml_stack_size = 0; /* no stack yet */ - if (curbuf->b_ffname == NULL) - cannot_open = TRUE; - else - cannot_open = FALSE; + if (curbuf->b_ffname == NULL) { + cannot_open = true; + } else { + cannot_open = false; + } - serious_error = FALSE; + serious_error = false; for (; !got_int; line_breakcheck()) { if (hp != NULL) mf_put(mfp, hp, false, false); /* release previous block */ @@ -1207,6 +1208,7 @@ void ml_recover(bool checkext) && !(curbuf->b_ml.ml_flags & ML_EMPTY)) ml_delete(curbuf->b_ml.ml_line_count, false); curbuf->b_flags |= BF_RECOVERED; + check_cursor(); recoverymode = FALSE; if (got_int) @@ -1440,7 +1442,7 @@ recover_names ( * Append the full path to name with path separators made into percent * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"") */ -char *make_percent_swname(const char *dir, char *name) +char *make_percent_swname(const char *dir, const char *name) FUNC_ATTR_NONNULL_ARG(1) { char *d = NULL; @@ -1795,7 +1797,7 @@ theend: */ char_u *ml_get(linenr_T lnum) { - return ml_get_buf(curbuf, lnum, FALSE); + return ml_get_buf(curbuf, lnum, false); } /* @@ -2095,7 +2097,7 @@ static int ml_append_int( int total_moved = 0; /* init to shut up gcc */ DATA_BL *dp_right, *dp_left; int stack_idx; - int in_left; + bool in_left; int lineadd; blocknr_T bnum_left, bnum_right; linenr_T lnum_left, lnum_right; @@ -2112,22 +2114,22 @@ static int ml_append_int( */ if (db_idx < 0) { /* left block is new, right block is existing */ lines_moved = 0; - in_left = TRUE; - /* space_needed does not change */ - } else { /* left block is existing, right block is new */ + in_left = true; + // space_needed does not change + } else { // left block is existing, right block is new lines_moved = line_count - db_idx - 1; - if (lines_moved == 0) - in_left = FALSE; /* put new line in right block */ - /* space_needed does not change */ - else { + if (lines_moved == 0) { + in_left = false; // put new line in right block + // space_needed does not change + } else { data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) - dp->db_txt_start; total_moved = data_moved + lines_moved * INDEX_SIZE; if ((int)dp->db_free + total_moved >= space_needed) { - in_left = TRUE; /* put new line in left block */ + in_left = true; // put new line in left block space_needed = total_moved; } else { - in_left = FALSE; /* put new line in right block */ + in_left = false; // put new line in right block space_needed += total_moved; } } @@ -2760,7 +2762,7 @@ static void ml_flush_line(buf_T *buf) int start; int count; int i; - static int entered = FALSE; + static bool entered = false; if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL) return; /* nothing to do */ @@ -2769,7 +2771,7 @@ static void ml_flush_line(buf_T *buf) /* This code doesn't work recursively. */ if (entered) return; - entered = TRUE; + entered = true; buf->flush_count++; @@ -2832,7 +2834,7 @@ static void ml_flush_line(buf_T *buf) } xfree(new_line); - entered = FALSE; + entered = false; } buf->b_ml.ml_line_lnum = 0; @@ -3574,9 +3576,10 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, } } else { MSG_PUTS("\n"); - if (msg_silent == 0) - /* call wait_return() later */ - need_wait_return = TRUE; + if (msg_silent == 0) { + // call wait_return() later + need_wait_return = true; + } } } diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 112f51fc64..5c07f87bd5 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -955,7 +955,7 @@ char_u *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char_u *arg, after_dot = p + 1; } - // ":popup" only uses menues, not entries + // ":popup" only uses menus, not entries expand_menus = !((*cmd == 't' && cmd[1] == 'e') || *cmd == 'p'); expand_emenu = (*cmd == 'e'); if (expand_menus && ascii_iswhite(*p)) { diff --git a/src/nvim/message.c b/src/nvim/message.c index ec5dabbbc0..bd26b8608f 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -79,12 +79,12 @@ static int verbose_did_open = FALSE; /* * When writing messages to the screen, there are many different situations. * A number of variables is used to remember the current state: - * msg_didany TRUE when messages were written since the last time the + * msg_didany true when messages were written since the last time the * user reacted to a prompt. * Reset: After hitting a key for the hit-return prompt, * hitting <CR> for the command line or input(). * Set: When any message is written to the screen. - * msg_didout TRUE when something was written to the current line. + * msg_didout true when something was written to the current line. * Reset: When advancing to the next line, when the current * text can be overwritten. * Set: When any message is written to the screen. @@ -102,7 +102,7 @@ static int verbose_did_open = FALSE; * work without an extra prompt. * lines_left Number of lines available for messages before the * more-prompt is to be given. -1 when not set. - * need_wait_return TRUE when the hit-return prompt is needed. + * need_wait_return true when the hit-return prompt is needed. * Reset: After giving the hit-return prompt, when the user * has answered some other prompt. * Set: When the ruler or typeahead display is overwritten, @@ -1097,14 +1097,14 @@ void wait_return(int redraw) */ if (vgetc_busy > 0) return; - need_wait_return = TRUE; + need_wait_return = true; if (no_wait_return) { if (!exmode_active) cmdline_row = msg_row; return; } - redir_off = TRUE; /* don't redirect this message */ + redir_off = true; // don't redirect this message oldState = State; if (quit_more) { c = CAR; /* just pretend CR was hit */ @@ -1165,11 +1165,11 @@ void wait_return(int redraw) if (p_more) { if (c == 'b' || c == 'k' || c == 'u' || c == 'g' || c == K_UP || c == K_PAGEUP) { - if (msg_scrolled > Rows) - /* scroll back to show older messages */ + if (msg_scrolled > Rows) { + // scroll back to show older messages do_more_prompt(c); - else { - msg_didout = FALSE; + } else { + msg_didout = false; c = K_IGNORE; msg_col = cmdmsg_rl ? Columns - 1 : @@ -1284,7 +1284,7 @@ void set_keep_msg(char_u *s, int attr) keep_msg = vim_strsave(s); else keep_msg = NULL; - keep_msg_more = FALSE; + keep_msg_more = false; keep_msg_attr = attr; } @@ -1324,9 +1324,8 @@ void msg_start(void) 0; } else if (msg_didout) { // start message on next line msg_putchar('\n'); - did_return = TRUE; - if (exmode_active != EXMODE_NORMAL) - cmdline_row = msg_row; + did_return = true; + cmdline_row = msg_row; } if (!msg_didany || lines_left < 0) msg_starthere(); @@ -1354,7 +1353,7 @@ void msg_start(void) void msg_starthere(void) { lines_left = cmdline_row; - msg_didany = FALSE; + msg_didany = false; } void msg_putchar(int c) @@ -2146,15 +2145,17 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, store_sb_text((char_u **)&sb_str, (char_u *)s, attr, &sb_col, true); } - if (*s == '\n') { /* go to next line */ - msg_didout = FALSE; /* remember that line is empty */ - if (cmdmsg_rl) + if (*s == '\n') { // go to next line + msg_didout = false; // remember that line is empty + if (cmdmsg_rl) { msg_col = Columns - 1; - else + } else { msg_col = 0; - if (++msg_row >= Rows) /* safety check */ + } + if (++msg_row >= Rows) { // safety check msg_row = Rows - 1; - } else if (*s == '\r') { /* go to column 0 */ + } + } else if (*s == '\r') { // go to column 0 msg_col = 0; } else if (*s == '\b') { /* go to previous char */ if (msg_col) @@ -2709,9 +2710,9 @@ static int do_more_prompt(int typed_char) /* Since got_int is set all typeahead will be flushed, but we * want to keep this ':', remember that in a special way. */ typeahead_noflush(':'); - cmdline_row = Rows - 1; /* put ':' on this line */ - skip_redraw = TRUE; /* skip redraw once */ - need_wait_return = FALSE; /* don't wait in main() */ + cmdline_row = Rows - 1; // put ':' on this line + skip_redraw = true; // skip redraw once + need_wait_return = false; // don't wait in main() } FALLTHROUGH; case 'q': // quit @@ -2932,7 +2933,7 @@ void repeat_message(void) /* Avoid drawing the "hit-enter" prompt below the previous one, * overwrite it. Esp. useful when regaining focus and a * FocusGained autocmd exists but didn't draw anything. */ - msg_didout = FALSE; + msg_didout = false; msg_col = 0; msg_clr_eos(); } @@ -3096,8 +3097,8 @@ void msg_check(void) return; } if (msg_row == Rows - 1 && msg_col >= sc_col) { - need_wait_return = TRUE; - redraw_cmdline = TRUE; + need_wait_return = true; + redraw_cmdline = true; } } diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 38d0a7dadf..6d94632687 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -349,178 +349,6 @@ int get_last_leader_offset(char_u *line, char_u **flags) return result; } -/* - * Return the number of window lines occupied by buffer line "lnum". - */ -int plines(const linenr_T lnum) -{ - return plines_win(curwin, lnum, true); -} - -int plines_win( - win_T *const wp, - const linenr_T lnum, - const bool winheight // when true limit to window height -) -{ - /* Check for filler lines above this buffer line. When folded the result - * is one line anyway. */ - return plines_win_nofill(wp, lnum, winheight) + diff_check_fill(wp, lnum); -} - -int plines_nofill(const linenr_T lnum) -{ - return plines_win_nofill(curwin, lnum, true); -} - -int plines_win_nofill( - win_T *const wp, - const linenr_T lnum, - const bool winheight // when true limit to window height -) -{ - if (!wp->w_p_wrap) { - return 1; - } - - if (wp->w_width_inner == 0) { - return 1; - } - - // A folded lines is handled just like an empty line. - if (lineFolded(wp, lnum)) { - return 1; - } - - const int lines = plines_win_nofold(wp, lnum); - if (winheight && lines > wp->w_height_inner) { - return wp->w_height_inner; - } - return lines; -} - -/* - * Return number of window lines physical line "lnum" will occupy in window - * "wp". Does not care about folding, 'wrap' or 'diff'. - */ -int plines_win_nofold(win_T *wp, linenr_T lnum) -{ - char_u *s; - unsigned int col; - int width; - - s = ml_get_buf(wp->w_buffer, lnum, FALSE); - if (*s == NUL) /* empty line */ - return 1; - col = win_linetabsize(wp, s, MAXCOL); - - // If list mode is on, then the '$' at the end of the line may take up one - // extra column. - if (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL) { - col += 1; - } - - /* - * Add column offset for 'number', 'relativenumber' and 'foldcolumn'. - */ - width = wp->w_width_inner - win_col_off(wp); - if (width <= 0 || col > 32000) { - return 32000; // bigger than the number of screen columns - } - if (col <= (unsigned int)width) { - return 1; - } - col -= (unsigned int)width; - width += win_col_off2(wp); - assert(col <= INT_MAX && (int)col < INT_MAX - (width -1)); - return ((int)col + (width - 1)) / width + 1; -} - -/* - * Like plines_win(), but only reports the number of physical screen lines - * used from the start of the line to the given column number. - */ -int plines_win_col(win_T *wp, linenr_T lnum, long column) -{ - // Check for filler lines above this buffer line. When folded the result - // is one line anyway. - int lines = diff_check_fill(wp, lnum); - - if (!wp->w_p_wrap) - return lines + 1; - - if (wp->w_width_inner == 0) { - return lines + 1; - } - - char_u *line = ml_get_buf(wp->w_buffer, lnum, false); - char_u *s = line; - - colnr_T col = 0; - while (*s != NUL && --column >= 0) { - col += win_lbr_chartabsize(wp, line, s, col, NULL); - MB_PTR_ADV(s); - } - - // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in - // INSERT mode, then col must be adjusted so that it represents the last - // screen position of the TAB. This only fixes an error when the TAB wraps - // from one screen line to the next (when 'columns' is not a multiple of - // 'ts') -- webb. - if (*s == TAB && (State & NORMAL) - && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { - col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1; - } - - // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. - int width = wp->w_width_inner - win_col_off(wp); - if (width <= 0) { - return 9999; - } - - lines += 1; - if (col > width) - lines += (col - width) / (width + win_col_off2(wp)) + 1; - return lines; -} - -/// Get the number of screen lines lnum takes up. This takes care of -/// both folds and topfill, and limits to the current window height. -/// -/// @param[in] wp window line is in -/// @param[in] lnum line number -/// @param[out] nextp if not NULL, the line after a fold -/// @param[out] foldedp if not NULL, whether lnum is on a fold -/// @param[in] cache whether to use the window's cache for folds -/// -/// @return the total number of screen lines -int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, - bool *const foldedp, const bool cache) -{ - bool folded = hasFoldingWin(wp, lnum, NULL, nextp, cache, NULL); - if (foldedp) { - *foldedp = folded; - } - if (folded) { - return 1; - } else if (lnum == wp->w_topline) { - return plines_win_nofill(wp, lnum, true) + wp->w_topfill; - } - return plines_win(wp, lnum, true); -} - -int plines_m_win(win_T *wp, linenr_T first, linenr_T last) -{ - int count = 0; - - while (first <= last) { - linenr_T next = first; - count += plines_win_full(wp, first, &next, NULL, false); - first = next + 1; - } - return count; -} - int gchar_pos(pos_T *pos) FUNC_ATTR_NONNULL_ARG(1) { @@ -750,8 +578,8 @@ get_number ( stuffcharReadbuff(':'); if (!exmode_active) cmdline_row = msg_row; - skip_redraw = TRUE; /* skip redraw once */ - do_redraw = FALSE; + skip_redraw = true; // skip redraw once + do_redraw = false; break; } else if (c == Ctrl_C || c == ESC || c == 'q') { n = 0; @@ -849,7 +677,7 @@ void msgmore(long n) } if (msg(msg_buf)) { set_keep_msg(msg_buf, 0); - keep_msg_more = TRUE; + keep_msg_more = true; } } } diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 4c0339e5f4..c4fa269851 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -18,6 +18,7 @@ #include "nvim/diff.h" #include "nvim/move.h" #include "nvim/misc1.h" +#include "nvim/plines.h" #include "nvim/cursor.h" #include "nvim/buffer_defs.h" #include "nvim/memline.h" @@ -227,21 +228,19 @@ retnomove: redraw_curbuf_later(INVERTED); // delete the inversion } - - row -= curwin->w_winrow; - col -= curwin->w_wincol; - // When clicking beyond the end of the window, scroll the screen. // Scroll by however many rows outside the window we are. if (row < 0) { count = 0; for (first = true; curwin->w_topline > 1; ) { - if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) - ++count; - else - count += plines(curwin->w_topline - 1); - if (!first && count > -row) + if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) { + count++; + } else { + count += plines_win(curwin, curwin->w_topline - 1, true); + } + if (!first && count > -row) { break; + } first = false; (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) { @@ -262,7 +261,7 @@ retnomove: if (curwin->w_topfill > 0) { ++count; } else { - count += plines(curwin->w_topline); + count += plines_win(curwin, curwin->w_topline, true); } if (!first && count > row - curwin->w_height_inner + 1) { @@ -522,7 +521,7 @@ static colnr_T scroll_line_len(linenr_T lnum) char_u *line = ml_get(lnum); if (*line != NUL) { for (;;) { - int numchar = chartabsize(line, col); + int numchar = win_chartabsize(curwin, line, col); MB_PTR_ADV(line); if (*line == NUL) { // don't count the last character break; @@ -570,9 +569,8 @@ static linenr_T find_longest_lnum(void) return ret; } -/// -/// Do a horizontal scroll. Return TRUE if the cursor moved, FALSE otherwise. -/// +/// Do a horizontal scroll. +/// @return true if the cursor moved, false otherwise. bool mouse_scroll_horiz(int dir) { if (curwin->w_p_wrap) { @@ -619,10 +617,10 @@ static int mouse_adjust_click(win_T *wp, int row, int col) // scanned *up to* `col`, nudging it left or right when concealed characters // are encountered. // - // chartabsize() is used to keep track of the virtual column position relative - // to the line's bytes. For example: if col == 9 and the line starts with a - // tab that's 8 columns wide, we would want the cursor to be highlighting the - // second byte, not the ninth. + // win_chartabsize() is used to keep track of the virtual column position + // relative to the line's bytes. For example: if col == 9 and the line + // starts with a tab that's 8 columns wide, we would want the cursor to be + // highlighting the second byte, not the ninth. linenr_T lnum = wp->w_cursor.lnum; char_u *line = ml_get(lnum); @@ -646,7 +644,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col) // checked for concealed characters. vcol = 0; while (vcol < offset && *ptr != NUL) { - vcol += chartabsize(ptr, vcol); + vcol += win_chartabsize(curwin, ptr, vcol); ptr += utfc_ptr2len(ptr); } @@ -657,7 +655,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col) vcol = offset; ptr_end = ptr_row_offset; while (vcol < col && *ptr_end != NUL) { - vcol += chartabsize(ptr_end, vcol); + vcol += win_chartabsize(curwin, ptr_end, vcol); ptr_end += utfc_ptr2len(ptr_end); } @@ -672,7 +670,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col) #define decr() nudge--; ptr_end -= utfc_ptr2len(ptr_end) while (ptr < ptr_end && *ptr != NUL) { - cwidth = chartabsize(ptr, vcol); + cwidth = win_chartabsize(curwin, ptr, vcol); vcol += cwidth; if (cwidth > 1 && *ptr == '\t' && nudge > 0) { // A tab will "absorb" any previous adjustments. @@ -721,14 +719,20 @@ int mouse_check_fold(void) int click_row = mouse_row; int click_col = mouse_col; int mouse_char = ' '; + int max_row = Rows; + int max_col = Columns; + int multigrid = ui_has(kUIMultigrid); win_T *wp; wp = mouse_find_win(&click_grid, &click_row, &click_col); + if (wp && multigrid) { + max_row = wp->w_grid_alloc.Rows; + max_col = wp->w_grid_alloc.Columns; + } - if (wp && mouse_row >= 0 && mouse_row < Rows - && mouse_col >= 0 && mouse_col <= Columns) { - int multigrid = ui_has(kUIMultigrid); + if (wp && mouse_row >= 0 && mouse_row < max_row + && mouse_col >= 0 && mouse_col < max_col) { ScreenGrid *gp = multigrid ? &wp->w_grid_alloc : &default_grid; int fdc = win_fdccol_count(wp); int row = multigrid && mouse_grid == 0 ? click_row : mouse_row; diff --git a/src/nvim/move.c b/src/nvim/move.c index 1210a3365a..21cbac4d79 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -17,6 +17,7 @@ #include <stdbool.h> #include "nvim/ascii.h" +#include "nvim/buffer.h" #include "nvim/move.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -26,6 +27,7 @@ #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/misc1.h" +#include "nvim/plines.h" #include "nvim/option.h" #include "nvim/popupmnu.h" #include "nvim/screen.h" @@ -172,7 +174,7 @@ void update_topline(win_T *wp) old_topfill = wp->w_topfill; // If the buffer is empty, always set topline to 1. - if (BUFEMPTY()) { // special case - file is empty + if (buf_is_empty(curbuf)) { // special case - file is empty if (wp->w_topline != 1) { redraw_later(wp, NOT_VALID); } @@ -786,11 +788,12 @@ void curs_columns( wp->w_wcol -= n * width; wp->w_wrow += n; - /* When cursor wraps to first char of next line in Insert - * mode, the 'showbreak' string isn't shown, backup to first - * column */ - if (*p_sbr && *get_cursor_pos_ptr() == NUL - && wp->w_wcol == (int)vim_strsize(p_sbr)) { + // When cursor wraps to first char of next line in Insert + // mode, the 'showbreak' string isn't shown, backup to first + // column + char_u *const sbr = get_showbreak_value(wp); + if (*sbr && *get_cursor_pos_ptr() == NUL + && wp->w_wcol == (int)vim_strsize(sbr)) { wp->w_wcol = 0; } } @@ -1054,8 +1057,9 @@ bool scrolldown(long line_count, int byfold) line_count -= curwin->w_topline - first - 1; curwin->w_botline -= curwin->w_topline - first; curwin->w_topline = first; - } else - done += plines_nofill(curwin->w_topline); + } else { + done += plines_win_nofill(curwin, curwin->w_topline, true); + } } --curwin->w_botline; /* approximate w_botline */ invalidate_botline(); @@ -1089,8 +1093,9 @@ bool scrolldown(long line_count, int byfold) curwin->w_cursor.lnum = 1; else curwin->w_cursor.lnum = first - 1; - } else - wrow -= plines(curwin->w_cursor.lnum--); + } else { + wrow -= plines_win(curwin, curwin->w_cursor.lnum--, true); + } curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL); moved = true; @@ -1193,7 +1198,7 @@ check_topfill ( */ static void max_topfill(void) { - int n = plines_nofill(curwin->w_topline); + int n = plines_win_nofill(curwin, curwin->w_topline, true); if (n >= curwin->w_height_inner) { curwin->w_topfill = 0; } else { @@ -1220,19 +1225,16 @@ void scrolldown_clamp(void) validate_cursor(); /* 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 - * doesn't go past 'scrolloff' lines from the screen end. - */ + // 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 + // doesn't go past 'scrolloff' lines from the screen end. int end_row = curwin->w_wrow; - if (can_fill) - ++end_row; - else - end_row += plines_nofill(curwin->w_topline - 1); - if (curwin->w_p_wrap - && curwin->w_width_inner != 0 - ) { + if (can_fill) { + end_row++; + } else { + 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(); end_row += curwin->w_cline_height - 1 - @@ -1265,16 +1267,13 @@ void scrollup_clamp(void) validate_cursor(); /* 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 - * doesn't go before 'scrolloff' lines from the screen start. - */ - int start_row = curwin->w_wrow - plines_nofill(curwin->w_topline) - - curwin->w_topfill; - if (curwin->w_p_wrap - && curwin->w_width_inner != 0 - ) { + // 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 + // doesn't go before 'scrolloff' lines from the screen start. + int start_row = (curwin->w_wrow + - plines_win_nofill(curwin, curwin->w_topline, true) + - curwin->w_topfill); + if (curwin->w_p_wrap && curwin->w_width_inner != 0) { validate_virtcol(); start_row -= curwin->w_virtcol / curwin->w_width_inner; } @@ -1421,14 +1420,15 @@ void scroll_cursor_top(int min_scroll, int always) while (top > 0) { int i = hasFolding(top, &top, NULL) ? 1 // count one logical line for a sequence of folded lines - : plines_nofill(top); + : plines_win_nofill(curwin, top, true); used += i; if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) { - if (hasFolding(bot, NULL, &bot)) - /* count one logical line for a sequence of folded lines */ - ++used; - else - used += plines(bot); + if (hasFolding(bot, NULL, &bot)) { + // count one logical line for a sequence of folded lines + used++; + } else { + used += plines_win(curwin, bot, true); + } } if (used > curwin->w_height_inner) { break; @@ -1554,12 +1554,12 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) validate_botline(curwin); } - /* The lines of the cursor line itself are always used. */ - used = plines_nofill(cln); + // The lines of the cursor line itself are always used. + used = plines_win_nofill(curwin, cln, true); - /* If the cursor is below botline, we will at least scroll by the height - * of the cursor line. Correct for empty lines, which are really part of - * botline. */ + // If the cursor is below botline, we will at least scroll by the height + // of the cursor line. Correct for empty lines, which are really part of + // botline. if (cln >= curwin->w_botline) { scrolled = used; if (cln == curwin->w_botline) @@ -1703,7 +1703,7 @@ void scroll_cursor_halfway(int atend) loff.lnum = boff.lnum = curwin->w_cursor.lnum; (void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum); - int used = plines_nofill(loff.lnum); + int used = plines_win_nofill(curwin, loff.lnum, true); loff.fill = 0; boff.fill = 0; linenr_T topline = loff.lnum; @@ -1808,17 +1808,19 @@ void cursor_correct(void) int below = curwin->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)) - ++below; - else - below += plines(botline); - --botline; + if (hasFolding(botline, &botline, NULL)) { + below++; + } else { + below += plines_win(curwin, botline, true); + } + botline--; } if (above < above_wanted && (above < below || below >= below_wanted)) { - if (hasFolding(topline, NULL, &topline)) - ++above; - else - above += plines_nofill(topline); + if (hasFolding(topline, NULL, &topline)) { + above++; + } else { + above += plines_win_nofill(curwin, topline, true); + } /* Count filler lines below this line as context. */ if (topline < botline) @@ -2046,10 +2048,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir) { int min_height = curwin->w_height_inner - 2; - if (lp->fill > 0) + if (lp->fill > 0) { lp->height = 1; - else - lp->height = plines_nofill(lp->lnum); + } else { + lp->height = plines_win_nofill(curwin, lp->lnum, true); + } int h1 = lp->height; if (h1 > min_height) return; /* no overlap */ @@ -2119,7 +2122,7 @@ void halfpage(bool flag, linenr_T Prenum) n--; curwin->w_topfill--; } else { - i = plines_nofill(curwin->w_topline); + i = plines_win_nofill(curwin, curwin->w_topline, true); n -= i; if (n < 0 && scrolled > 0) break; @@ -2145,12 +2148,12 @@ void halfpage(bool flag, linenr_T Prenum) else { room += i; do { - i = plines(curwin->w_botline); - if (i > room) + i = plines_win(curwin, curwin->w_botline, true); + if (i > room) { break; - (void)hasFolding(curwin->w_botline, NULL, - &curwin->w_botline); - ++curwin->w_botline; + } + (void)hasFolding(curwin->w_botline, NULL, &curwin->w_botline); + curwin->w_botline++; room -= i; } while (curwin->w_botline <= curbuf->b_ml.ml_line_count); } @@ -2179,7 +2182,7 @@ void halfpage(bool flag, linenr_T Prenum) n--; curwin->w_topfill++; } else { - i = plines_nofill(curwin->w_topline - 1); + i = plines_win_nofill(curwin, curwin->w_topline - 1, true); n -= i; if (n < 0 && scrolled > 0) break; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index a2d8859c68..e5743f345b 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -38,7 +38,7 @@ #define log_server_msg(...) #endif -static PMap(cstr_t) *event_strings = NULL; +static PMap(cstr_t) event_strings = MAP_INIT; static msgpack_sbuffer out_buffer; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -48,7 +48,6 @@ static msgpack_sbuffer out_buffer; void rpc_init(void) { ch_before_blocking_events = multiqueue_new_child(main_loop.events); - event_strings = pmap_new(cstr_t)(); msgpack_sbuffer_init(&out_buffer); } @@ -60,7 +59,6 @@ void rpc_start(Channel *channel) RpcState *rpc = &channel->rpc; rpc->closed = false; rpc->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); - rpc->subscribed_events = pmap_new(cstr_t)(); rpc->next_request_id = 1; rpc->info = (Dictionary)ARRAY_DICT_INIT; kv_init(rpc->call_stack); @@ -183,11 +181,11 @@ void rpc_subscribe(uint64_t id, char *event) abort(); } - char *event_string = pmap_get(cstr_t)(event_strings, event); + char *event_string = pmap_get(cstr_t)(&event_strings, event); if (!event_string) { event_string = xstrdup(event); - pmap_put(cstr_t)(event_strings, event_string, event_string); + pmap_put(cstr_t)(&event_strings, event_string, event_string); } pmap_put(cstr_t)(channel->rpc.subscribed_events, event_string, event_string); @@ -497,7 +495,7 @@ static void broadcast_event(const char *name, Array args) kvec_t(Channel *) subscribed = KV_INITIAL_VALUE; Channel *channel; - map_foreach_value(channels, channel, { + map_foreach_value(&channels, channel, { if (channel->is_rpc && pmap_has(cstr_t)(channel->rpc.subscribed_events, name)) { kv_push(subscribed, channel); @@ -528,7 +526,7 @@ end: static void unsubscribe(Channel *channel, char *event) { - char *event_string = pmap_get(cstr_t)(event_strings, event); + char *event_string = pmap_get(cstr_t)(&event_strings, event); if (!event_string) { WLOG("RPC: ch %" PRIu64 ": tried to unsubscribe unknown event '%s'", channel->id, event); @@ -536,7 +534,7 @@ static void unsubscribe(Channel *channel, char *event) } pmap_del(cstr_t)(channel->rpc.subscribed_events, event_string); - map_foreach_value(channels, channel, { + map_foreach_value(&channels, channel, { if (channel->is_rpc && pmap_has(cstr_t)(channel->rpc.subscribed_events, event_string)) { return; @@ -544,7 +542,7 @@ static void unsubscribe(Channel *channel, char *event) }); // Since the string is no longer used by other channels, release it's memory - pmap_del(cstr_t)(event_strings, event_string); + pmap_del(cstr_t)(&event_strings, event_string); xfree(event_string); } @@ -583,7 +581,7 @@ void rpc_free(Channel *channel) unsubscribe(channel, event_string); }); - pmap_free(cstr_t)(channel->rpc.subscribed_events); + pmap_destroy(cstr_t)(channel->rpc.subscribed_events); kv_destroy(channel->rpc.call_stack); api_free_dictionary(channel->rpc.info); } diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h index 6ef8c027f0..de328af1ce 100644 --- a/src/nvim/msgpack_rpc/channel_defs.h +++ b/src/nvim/msgpack_rpc/channel_defs.h @@ -27,7 +27,7 @@ typedef struct { } RequestEvent; typedef struct { - PMap(cstr_t) *subscribed_events; + PMap(cstr_t) subscribed_events[1]; bool closed; msgpack_unpacker *unpacker; uint32_t next_request_id; diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index a4a36e5ebf..35f126eab1 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -86,8 +86,9 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) FUNC_ATTR_NONNULL_ALL { bool ret = true; - kvec_t(MPToAPIObjectStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((MPToAPIObjectStackItem) { + kvec_withinit_t(MPToAPIObjectStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + kvi_push(stack, ((MPToAPIObjectStackItem) { .mobj = obj, .aobj = arg, .container = false, @@ -155,7 +156,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) const size_t idx = cur.idx; cur.idx++; kv_last(stack) = cur; - kv_push(stack, ((MPToAPIObjectStackItem) { + kvi_push(stack, ((MPToAPIObjectStackItem) { .mobj = &cur.mobj->via.array.ptr[idx], .aobj = &cur.aobj->data.array.items[idx], .container = false, @@ -209,7 +210,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) } } if (ret) { - kv_push(stack, ((MPToAPIObjectStackItem) { + kvi_push(stack, ((MPToAPIObjectStackItem) { .mobj = &cur.mobj->via.map.ptr[idx].val, .aobj = &cur.aobj->data.dictionary.items[idx].value, .container = false, @@ -265,7 +266,7 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) (void)kv_pop(stack); } } - kv_destroy(stack); + kvi_destroy(stack); return ret; } @@ -375,8 +376,9 @@ typedef struct { void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) FUNC_ATTR_NONNULL_ARG(2) { - kvec_t(APIToMPObjectStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 })); + kvec_withinit_t(APIToMPObjectStackItem, 2) stack = KV_INITIAL_VALUE; + kvi_init(stack); + kvi_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 })); while (kv_size(stack)) { APIToMPObjectStackItem cur = kv_last(stack); STATIC_ASSERT(kObjectTypeWindow == kObjectTypeBuffer + 1 @@ -428,7 +430,7 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) const size_t idx = cur.idx; cur.idx++; kv_last(stack) = cur; - kv_push(stack, ((APIToMPObjectStackItem) { + kvi_push(stack, ((APIToMPObjectStackItem) { .aobj = &cur.aobj->data.array.items[idx], .container = false, })); @@ -451,7 +453,7 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) kv_last(stack) = cur; msgpack_rpc_from_string(cur.aobj->data.dictionary.items[idx].key, res); - kv_push(stack, ((APIToMPObjectStackItem) { + kvi_push(stack, ((APIToMPObjectStackItem) { .aobj = &cur.aobj->data.dictionary.items[idx].value, .container = false, })); @@ -468,7 +470,7 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) (void)kv_pop(stack); } } - kv_destroy(stack); + kvi_destroy(stack); } void msgpack_rpc_from_array(Array result, msgpack_packer *res) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 69afe1644e..74aaed87c1 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -9,14 +9,12 @@ #include <assert.h> #include <inttypes.h> -#include <string.h> #include <stdbool.h> #include <stdlib.h> +#include <string.h> -#include "nvim/log.h" -#include "nvim/vim.h" +#include "nvim/api/private/helpers.h" #include "nvim/ascii.h" -#include "nvim/normal.h" #include "nvim/buffer.h" #include "nvim/change.h" #include "nvim/charset.h" @@ -25,6 +23,7 @@ #include "nvim/digraph.h" #include "nvim/edit.h" #include "nvim/eval/userfunc.h" +#include "nvim/event/loop.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" @@ -34,34 +33,35 @@ #include "nvim/getchar.h" #include "nvim/indent.h" #include "nvim/indent_c.h" +#include "nvim/keymap.h" +#include "nvim/log.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/keymap.h" -#include "nvim/move.h" #include "nvim/mouse.h" +#include "nvim/move.h" +#include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/time.h" +#include "nvim/plines.h" #include "nvim/quickfix.h" #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/spell.h" #include "nvim/spellfile.h" +#include "nvim/state.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" #include "nvim/ui.h" -#include "nvim/mouse.h" #include "nvim/undo.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/state.h" -#include "nvim/event/loop.h" -#include "nvim/os/time.h" -#include "nvim/os/input.h" -#include "nvim/api/private/helpers.h" typedef struct normal_state { VimState state; @@ -87,12 +87,10 @@ typedef struct normal_state { /* * The Visual area is remembered for reselection. */ -static int resel_VIsual_mode = NUL; /* 'v', 'V', or Ctrl-V */ -static linenr_T resel_VIsual_line_count; /* number of lines */ -static colnr_T resel_VIsual_vcol; /* nr of cols or end col */ -static int VIsual_mode_orig = NUL; /* saved Visual mode */ - -static int restart_VIsual_select = 0; +static int resel_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V +static linenr_T resel_VIsual_line_count; // number of lines +static colnr_T resel_VIsual_vcol; // nr of cols or end col +static int VIsual_mode_orig = NUL; // saved Visual mode #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -120,18 +118,18 @@ static char *e_noident = N_("E349: No identifier under cursor"); */ typedef void (*nv_func_T)(cmdarg_T *cap); -/* Values for cmd_flags. */ -#define NV_NCH 0x01 /* may need to get a second char */ -#define NV_NCH_NOP (0x02|NV_NCH) /* get second char when no operator pending */ -#define NV_NCH_ALW (0x04|NV_NCH) /* always get a second char */ -#define NV_LANG 0x08 /* second char needs language adjustment */ +// Values for cmd_flags. +#define NV_NCH 0x01 // may need to get a second char +#define NV_NCH_NOP (0x02|NV_NCH) // get second char when no operator pending +#define NV_NCH_ALW (0x04|NV_NCH) // always get a second char +#define NV_LANG 0x08 // second char needs language adjustment -#define NV_SS 0x10 /* may start selection */ -#define NV_SSS 0x20 /* may start selection with shift modifier */ -#define NV_STS 0x40 /* may stop selection without shift modif. */ -#define NV_RL 0x80 /* 'rightleft' modifies command */ -#define NV_KEEPREG 0x100 /* don't clear regname */ -#define NV_NCW 0x200 /* not allowed in command-line window */ +#define NV_SS 0x10 // may start selection +#define NV_SSS 0x20 // may start selection with shift modifier +#define NV_STS 0x40 // may stop selection without shift modif. +#define NV_RL 0x80 // 'rightleft' modifies command +#define NV_KEEPREG 0x100 // don't clear regname +#define NV_NCW 0x200 // not allowed in command-line window /* * Generally speaking, every Normal mode command should either clear any @@ -149,10 +147,10 @@ typedef void (*nv_func_T)(cmdarg_T *cap); * It is faster when all keys from zero to '~' are present. */ static const struct nv_cmd { - int cmd_char; /* (first) command character */ - nv_func_T cmd_func; /* function for this command */ - uint16_t cmd_flags; /* NV_ flags */ - short cmd_arg; /* value for ca.arg */ + int cmd_char; // (first) command character + nv_func_T cmd_func; // function for this command + uint16_t cmd_flags; // NV_ flags + short cmd_arg; // value for ca.arg } nv_cmds[] = { { NUL, nv_error, 0, 0 }, @@ -345,10 +343,10 @@ static const struct nv_cmd { { K_COMMAND, nv_colon, 0, 0 }, }; -/* Number of commands in nv_cmds[]. */ +// Number of commands in nv_cmds[]. #define NV_CMDS_SIZE ARRAY_SIZE(nv_cmds) -/* Sorted index of commands in nv_cmds[]. */ +// Sorted index of commands in nv_cmds[]. static short nv_cmd_idx[NV_CMDS_SIZE]; /* The highest index for which @@ -363,13 +361,15 @@ static int nv_compare(const void *s1, const void *s2) { int c1, c2; - /* The commands are sorted on absolute value. */ + // The commands are sorted on absolute value. c1 = nv_cmds[*(const short *)s1].cmd_char; c2 = nv_cmds[*(const short *)s2].cmd_char; - if (c1 < 0) + if (c1 < 0) { c1 = -c1; - if (c2 < 0) + } + if (c2 < 0) { c2 = -c2; + } return c1 - c2; } @@ -380,15 +380,15 @@ void init_normal_cmds(void) { assert(NV_CMDS_SIZE <= SHRT_MAX); - /* Fill the index table with a one to one relation. */ + // Fill the index table with a one to one relation. for (short int i = 0; i < (short int)NV_CMDS_SIZE; ++i) { nv_cmd_idx[i] = i; } - /* Sort the commands by the command character. */ + // Sort the commands by the command character. qsort(&nv_cmd_idx, NV_CMDS_SIZE, sizeof(short), nv_compare); - /* Find the first entry that can't be indexed by the command character. */ + // Find the first entry that can't be indexed by the command character. short int i; for (i = 0; i < (short int)NV_CMDS_SIZE; ++i) { if (i != nv_cmds[nv_cmd_idx[i]].cmd_char) { @@ -409,38 +409,43 @@ static int find_command(int cmdchar) int top, bot; int c; - /* A multi-byte character is never a command. */ - if (cmdchar >= 0x100) + // A multi-byte character is never a command. + if (cmdchar >= 0x100) { return -1; + } /* We use the absolute value of the character. Special keys have a * negative value, but are sorted on their absolute value. */ - if (cmdchar < 0) + if (cmdchar < 0) { cmdchar = -cmdchar; + } /* If the character is in the first part: The character is the index into * nv_cmd_idx[]. */ assert(nv_max_linear < (int)NV_CMDS_SIZE); - if (cmdchar <= nv_max_linear) + if (cmdchar <= nv_max_linear) { return nv_cmd_idx[cmdchar]; + } - /* Perform a binary search. */ + // Perform a binary search. bot = nv_max_linear + 1; top = NV_CMDS_SIZE - 1; idx = -1; while (bot <= top) { i = (top + bot) / 2; c = nv_cmds[nv_cmd_idx[i]].cmd_char; - if (c < 0) + if (c < 0) { c = -c; + } if (cmdchar == c) { idx = nv_cmd_idx[i]; break; } - if (cmdchar > c) + if (cmdchar > c) { bot = i + 1; - else + } else { top = i - 1; + } } return idx; } @@ -545,59 +550,60 @@ static bool normal_need_additional_char(NormalState *s) int cmdchar = s->ca.cmdchar; // without NV_NCH we never need to check for an additional char return flags & NV_NCH && ( - // NV_NCH_NOP is set and no operator is pending, get a second char - ((flags & NV_NCH_NOP) == NV_NCH_NOP && !pending_op) - // NV_NCH_ALW is set, always get a second char - || (flags & NV_NCH_ALW) == NV_NCH_ALW - // 'q' without a pending operator, recording or executing a register, - // needs to be followed by a second char, examples: - // - qc => record using register c - // - q: => open command-line window - || (cmdchar == 'q' - && !pending_op - && reg_recording == 0 - && reg_executing == 0) - // 'a' or 'i' after an operator is a text object, examples: - // - ciw => change inside word - // - da( => delete parenthesis and everything inside. - // Also, don't do anything when these keys are received in visual mode - // so just get another char. - // - // TODO(tarruda): Visual state needs to be refactored into a - // separate state that "inherits" from normal state. - || ((cmdchar == 'a' || cmdchar == 'i') && (pending_op || VIsual_active))); + // NV_NCH_NOP is set and no operator is pending, get a second char + ((flags & NV_NCH_NOP) == NV_NCH_NOP && !pending_op) + // NV_NCH_ALW is set, always get a second char + || (flags & NV_NCH_ALW) == NV_NCH_ALW + // 'q' without a pending operator, recording or executing a register, + // needs to be followed by a second char, examples: + // - qc => record using register c + // - q: => open command-line window + || (cmdchar == 'q' + && !pending_op + && reg_recording == 0 + && reg_executing == 0) + // 'a' or 'i' after an operator is a text object, examples: + // - ciw => change inside word + // - da( => delete parenthesis and everything inside. + // Also, don't do anything when these keys are received in visual mode + // so just get another char. + // + // TODO(tarruda): Visual state needs to be refactored into a + // separate state that "inherits" from normal state. + || ((cmdchar == 'a' || cmdchar == 'i') && + (pending_op || VIsual_active))); } static bool normal_need_redraw_mode_message(NormalState *s) { return ( - // 'showmode' is set and messages can be printed - ((p_smd && msg_silent == 0 - // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual - // mode - && (restart_edit != 0 || (VIsual_active - && s->old_pos.lnum == curwin->w_cursor.lnum - && s->old_pos.col == curwin->w_cursor.col)) - // command-line must be cleared or redrawn - && (clear_cmdline || redraw_cmdline) - // some message was printed or scrolled - && (msg_didout || (msg_didany && msg_scroll)) - // it is fine to remove the current message - && !msg_nowait - // the command was the result of direct user input and not a mapping - && KeyTyped) - // must restart insert mode, not in visual mode and error message is - // being shown - || (restart_edit != 0 && !VIsual_active && msg_scroll - && emsg_on_display)) - // no register was used - && s->oa.regname == 0 - && !(s->ca.retval & CA_COMMAND_BUSY) - && stuff_empty() - && typebuf_typed() - && emsg_silent == 0 - && !did_wait_return - && s->oa.op_type == OP_NOP); + // 'showmode' is set and messages can be printed + ((p_smd && msg_silent == 0 + // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual + // mode + && (restart_edit != 0 || (VIsual_active + && s->old_pos.lnum == curwin->w_cursor.lnum + && s->old_pos.col == curwin->w_cursor.col)) + // command-line must be cleared or redrawn + && (clear_cmdline || redraw_cmdline) + // some message was printed or scrolled + && (msg_didout || (msg_didany && msg_scroll)) + // it is fine to remove the current message + && !msg_nowait + // the command was the result of direct user input and not a mapping + && KeyTyped) + // must restart insert mode, not in visual mode and error message is + // being shown + || (restart_edit != 0 && !VIsual_active && msg_scroll + && emsg_on_display)) + // no register was used + && s->oa.regname == 0 + && !(s->ca.retval & CA_COMMAND_BUSY) + && stuff_empty() + && typebuf_typed() + && emsg_silent == 0 + && !did_wait_return + && s->oa.op_type == OP_NOP); } static void normal_redraw_mode_message(NormalState *s) @@ -612,7 +618,7 @@ static void normal_redraw_mode_message(NormalState *s) // If need to redraw, and there is a "keep_msg", redraw before the // delay if (must_redraw && keep_msg != NULL && !emsg_on_display) { - char_u *kmsg; + char_u *kmsg; kmsg = keep_msg; keep_msg = NULL; @@ -708,7 +714,7 @@ static void normal_get_additional_char(NormalState *s) if (!lit) { // Typing CTRL-K gets a digraph. if (*cp == Ctrl_K && ((nv_cmds[s->idx].cmd_flags & NV_LANG) - || cp == &s->ca.extra_char) + || cp == &s->ca.extra_char) && vim_strchr(p_cpo, CPO_DIGRAPH) == NULL) { s->c = get_digraph(false); if (s->c > 0) { @@ -736,7 +742,7 @@ static void normal_get_additional_char(NormalState *s) s->ca.nchar = s->ca.extra_char; s->idx = find_command(s->ca.cmdchar); } else if ((s->ca.nchar == 'n' || s->ca.nchar == 'N') - && s->ca.cmdchar == 'g') { + && s->ca.cmdchar == 'g') { s->ca.oap->op_type = get_op_type(*cp, NUL); } else if (*cp == Ctrl_BSL) { long towait = (p_ttm >= 0 ? p_ttm : p_tm); @@ -770,7 +776,7 @@ static void normal_get_additional_char(NormalState *s) && (s->c >= 0x100 || MB_BYTE2LEN(vpeekc()) > 1)) { s->c = plain_vgetc(); if (!utf_iscomposing(s->c)) { - vungetc(s->c); /* it wasn't, put it back */ + vungetc(s->c); // it wasn't, put it back break; } else if (s->ca.ncharC1 == 0) { s->ca.ncharC1 = s->c; @@ -786,16 +792,26 @@ static void normal_get_additional_char(NormalState *s) static void normal_invert_horizontal(NormalState *s) { switch (s->ca.cmdchar) { - case 'l': s->ca.cmdchar = 'h'; break; - case K_RIGHT: s->ca.cmdchar = K_LEFT; break; - case K_S_RIGHT: s->ca.cmdchar = K_S_LEFT; break; - case K_C_RIGHT: s->ca.cmdchar = K_C_LEFT; break; - case 'h': s->ca.cmdchar = 'l'; break; - case K_LEFT: s->ca.cmdchar = K_RIGHT; break; - case K_S_LEFT: s->ca.cmdchar = K_S_RIGHT; break; - case K_C_LEFT: s->ca.cmdchar = K_C_RIGHT; break; - case '>': s->ca.cmdchar = '<'; break; - case '<': s->ca.cmdchar = '>'; break; + case 'l': + s->ca.cmdchar = 'h'; break; + case K_RIGHT: + s->ca.cmdchar = K_LEFT; break; + case K_S_RIGHT: + s->ca.cmdchar = K_S_LEFT; break; + case K_C_RIGHT: + s->ca.cmdchar = K_C_LEFT; break; + case 'h': + s->ca.cmdchar = 'l'; break; + case K_LEFT: + s->ca.cmdchar = K_RIGHT; break; + case K_S_LEFT: + s->ca.cmdchar = K_S_RIGHT; break; + case K_C_LEFT: + s->ca.cmdchar = K_C_RIGHT; break; + case '>': + s->ca.cmdchar = '<'; break; + case '<': + s->ca.cmdchar = '>'; break; } s->idx = find_command(s->ca.cmdchar); } @@ -809,7 +825,7 @@ static bool normal_get_command_count(NormalState *s) // 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'))) { + && (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 ~@% @@ -818,7 +834,7 @@ static bool normal_get_command_count(NormalState *s) } if (s->ca.count0 < 0) { - // got too large! + // overflow s->ca.count0 = 999999999L; } @@ -942,11 +958,11 @@ normal_end: // if still inside a mapping that started in Visual mode). // May switch from Visual to Select mode after CTRL-O command. if (s->oa.op_type == OP_NOP - && ((restart_edit != 0 && !VIsual_active && s->old_mapped_len == 0) - || restart_VIsual_select == 1) - && !(s->ca.retval & CA_COMMAND_BUSY) - && stuff_empty() - && s->oa.regname == 0) { + && ((restart_edit != 0 && !VIsual_active && s->old_mapped_len == 0) + || restart_VIsual_select == 1) + && !(s->ca.retval & CA_COMMAND_BUSY) + && stuff_empty() + && s->oa.regname == 0) { if (restart_VIsual_select == 1) { VIsual_select = true; showmode(); @@ -969,7 +985,7 @@ static int normal_execute(VimState *state, int key) { NormalState *s = (NormalState *)state; s->command_finished = false; - s->ctrl_w = false; /* got CTRL-W command */ + s->ctrl_w = false; // got CTRL-W command s->old_col = curwin->w_curswant; s->c = key; @@ -981,7 +997,7 @@ static int normal_execute(VimState *state, int key) if (restart_edit == 0) { s->old_mapped_len = 0; } else if (s->old_mapped_len || (VIsual_active && s->mapped_len == 0 - && typebuf_maplen() > 0)) { + && typebuf_maplen() > 0)) { s->old_mapped_len = typebuf_maplen(); } @@ -991,7 +1007,7 @@ static int normal_execute(VimState *state, int key) // In Select mode, typed text replaces the selection. if (VIsual_active && VIsual_select && (vim_isprintc(s->c) - || s->c == NL || s->c == CAR || s->c == K_KENTER)) { + || s->c == NL || s->c == CAR || s->c == K_KENTER)) { // Fake a "c"hange command. When "restart_edit" is set (e.g., because // 'insertmode' is set) fake a "d"elete command, Insert mode will // restart automatically. @@ -1009,14 +1025,14 @@ static int normal_execute(VimState *state, int key) s->need_flushbuf = add_to_showcmd(s->c); - while (normal_get_command_count(s)) continue; + while (normal_get_command_count(s)) { continue; } if (s->c == K_EVENT) { // Save the count values so that ca.opcount and ca.count0 are exactly // the same when coming back here after handling K_EVENT. s->oa.prev_opcount = s->ca.opcount; s->oa.prev_count0 = s->ca.count0; - } else if (s->ca.opcount != 0) { + } else if (s->ca.opcount != 0) { // If we're in the middle of an operator (including after entering a // yank buffer with '"') AND we had a count before the operator, then // that count overrides the current value of ca.count0. @@ -1025,10 +1041,14 @@ static int normal_execute(VimState *state, int key) // If you give a count before AND after the operator, they are // multiplied. if (s->ca.count0) { - s->ca.count0 *= s->ca.opcount; + s->ca.count0 = (long)((uint64_t)s->ca.count0 * (uint64_t)s->ca.opcount); } else { s->ca.count0 = s->ca.opcount; } + if (s->ca.count0 < 0) { + // overflow + s->ca.count0 = 999999999L; + } } // Always remember the count. It will be set to zero (on the next call, @@ -1184,7 +1204,7 @@ static void normal_check_interrupt(NormalState *s) && s->previous_got_int) { // Typed two CTRL-C in a row: go back to ex mode as if "Q" was // used and keep "got_int" set, so that it aborts ":g". - exmode_active = EXMODE_NORMAL; + exmode_active = true; State = NORMAL; } else if (!global_busy || !exmode_active) { if (!quit_more) { @@ -1273,6 +1293,15 @@ static void normal_redraw(NormalState *s) redrawWinline(curwin, curwin->w_cursor.lnum); } + // Might need to update for 'cursorline'. + // When 'cursorlineopt' is "screenline" need to redraw always. + if (curwin->w_p_cul + && (curwin->w_last_cursorline != curwin->w_cursor.lnum + || (curwin->w_p_culopt_flags & CULOPT_SCRLINE)) + && !char_avail()) { + redraw_later(curwin, VALID); + } + if (VIsual_active) { update_curbuf(INVERTED); // update inverted part } else if (must_redraw) { @@ -1336,7 +1365,7 @@ static int normal_check(VimState *state) quit_more = false; // If skip redraw is set (for ":" in wait_return()), don't redraw now. - // If there is nothing in the stuff_buffer or do_redraw is TRUE, + // If there is nothing in the stuff_buffer or do_redraw is true, // update cursor and redraw. if (skip_redraw || exmode_active) { skip_redraw = false; @@ -1394,7 +1423,7 @@ static int normal_check(VimState *state) if (s->noexmode) { return 0; } - do_exmode(exmode_active == EXMODE_VIM); + do_exmode(); return -1; } @@ -1415,18 +1444,19 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount) { long count = cap->count0; - /* multiply with cap->opcount the same way as above */ - if (cap->opcount != 0) + // multiply with cap->opcount the same way as above + if (cap->opcount != 0) { count = cap->opcount * (count == 0 ? 1 : count); + } set_vcount(count, count == 0 ? 1 : count, *set_prevcount); - *set_prevcount = false; /* only set v:prevcount once */ + *set_prevcount = false; // only set v:prevcount once } // Handle an operator after Visual mode or when the movement is finished. // "gui_yank" is true when yanking text for the clipboard. void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) { - oparg_T *oap = cap->oap; + oparg_T *oap = cap->oap; pos_T old_cursor; bool empty_region_error; int restart_edit_save; @@ -1496,12 +1526,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) && oap->op_type != OP_FOLDCLOSE && oap->op_type != OP_FOLDCLOSEREC && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC - ) { + && oap->op_type != OP_FOLDDELREC) { prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - if (cap->cmdchar == '/' || cap->cmdchar == '?') { /* was a search */ + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search /* * If 'cpoptions' does not contain 'r', insert the search * pattern to really repeat the same command. @@ -1529,8 +1558,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * redo_VIsual_line_count and redo_VIsual_vcol. */ oap->start = curwin->w_cursor; curwin->w_cursor.lnum += redo_VIsual_line_count - 1; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) + if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + } VIsual_mode = redo_VIsual_mode; if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') { if (VIsual_mode == 'v') { @@ -1538,8 +1568,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) validate_virtcol(); curwin->w_curswant = curwin->w_virtcol + redo_VIsual_vcol - 1; - } else + } else { curwin->w_curswant = redo_VIsual_vcol; + } } else { curwin->w_curswant = MAXCOL; } @@ -1549,7 +1580,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); } else if (VIsual_active) { if (!gui_yank) { - /* Save the current VIsual area for '< and '> marks, and "gv" */ + // Save the current VIsual area for '< and '> marks, and "gv" curbuf->b_visual.vi_start = VIsual; curbuf->b_visual.vi_end = curwin->w_cursor; curbuf->b_visual.vi_mode = VIsual_mode; @@ -1595,10 +1626,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * to the end of the operated text. w_cursor is equal to oap->start. */ if (lt(oap->start, curwin->w_cursor)) { - /* Include folded lines completely. */ + // Include folded lines completely. if (!VIsual_active) { - if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) + if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { oap->start.col = 0; + } if ((curwin->w_cursor.col > 0 || oap->inclusive || oap->motion_type == kMTLineWise) @@ -1633,7 +1665,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) check_pos(curwin->w_buffer, &oap->end); oap->line_count = oap->end.lnum - oap->start.lnum + 1; - /* Set "virtual_op" before resetting VIsual_active. */ + // Set "virtual_op" before resetting VIsual_active. virtual_op = virtual_active(); if (VIsual_active || redo_VIsual_busy) { @@ -1645,19 +1677,22 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * size of the Visual text */ resel_VIsual_mode = VIsual_mode; - if (curwin->w_curswant == MAXCOL) + if (curwin->w_curswant == MAXCOL) { resel_VIsual_vcol = MAXCOL; - else { - if (VIsual_mode != Ctrl_V) + } else { + if (VIsual_mode != Ctrl_V) { getvvcol(curwin, &(oap->end), - NULL, NULL, &oap->end_vcol); + NULL, NULL, &oap->end_vcol); + } if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { - if (VIsual_mode != Ctrl_V) + if (VIsual_mode != Ctrl_V) { getvvcol(curwin, &(oap->start), - &oap->start_vcol, NULL, NULL); + &oap->start_vcol, NULL, NULL); + } resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; - } else + } else { resel_VIsual_vcol = oap->end_vcol; + } } resel_VIsual_line_count = oap->line_count; } @@ -1672,8 +1707,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) && oap->op_type != OP_FOLDCLOSEREC && oap->op_type != OP_FOLDDEL && oap->op_type != OP_FOLDDELREC - && oap->motion_force == NUL - ) { + && oap->motion_force == NUL) { /* Prepare for redoing. Only use the nchar field for "r", * otherwise it might be the second char of the operator. */ if (cap->cmdchar == 'g' && (cap->nchar == 'n' @@ -1713,8 +1747,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else if (VIsual_mode == 'v') { oap->motion_type = kMTCharWise; if (*ml_get_pos(&(oap->end)) == NUL - && (include_line_break || !virtual_op) - ) { + && (include_line_break || !virtual_op)) { oap->inclusive = false; // Try to include the newline, unless it's an operator // that works on lines only. @@ -1816,22 +1849,24 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) oap->inclusive = true; } } - } else + } else { oap->end_adjusted = false; + } switch (oap->op_type) { case OP_LSHIFT: case OP_RSHIFT: op_shift(oap, true, - oap->is_VIsual ? (int)cap->count1 : - 1); + oap->is_VIsual ? (int)cap->count1 : + 1); auto_format(false, true); break; case OP_JOIN_NS: case OP_JOIN: - if (oap->line_count < 2) + if (oap->line_count < 2) { oap->line_count = 2; + } if (curwin->w_cursor.lnum + oap->line_count - 1 > curbuf->b_ml.ml_line_count) { beep_flush(); @@ -1843,7 +1878,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) break; case OP_DELETE: - VIsual_reselect = false; /* don't reselect now */ + VIsual_reselect = false; // don't reselect now if (empty_region_error) { vim_beep(BO_OPER); CancelRedo(); @@ -1866,13 +1901,14 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } } else { curwin->w_p_lbr = lbr_saved; + oap->excl_tr_ws = cap->cmdchar == 'z'; (void)op_yank(oap, !gui_yank, false); } check_cursor_col(); break; case OP_CHANGE: - VIsual_reselect = false; /* don't reselect now */ + VIsual_reselect = false; // don't reselect now if (empty_region_error) { vim_beep(BO_OPER); CancelRedo(); @@ -1881,10 +1917,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * remember it to make 'insertmode' work with mappings for * Visual mode. But do this only once and not when typed and * 'insertmode' isn't set. */ - if (p_im || !KeyTyped) + if (p_im || !KeyTyped) { restart_edit_save = restart_edit; - else + } else { restart_edit_save = 0; + } restart_edit = 0; // Restore linebreak, so that when the user edits it looks as before. @@ -1892,10 +1929,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) // Reset finish_op now, don't want it set inside edit(). finish_op = false; - if (op_change(oap)) /* will call edit() */ + if (op_change(oap)) { // will call edit() cap->retval |= CA_COMMAND_BUSY; - if (restart_edit == 0) + } + if (restart_edit == 0) { restart_edit = restart_edit_save; + } } break; @@ -1919,8 +1958,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) break; } op_reindent(oap, - *curbuf->b_p_inde != NUL ? get_expr_indent : - get_c_indent); + *curbuf->b_p_inde != NUL ? get_expr_indent : + get_c_indent); break; } @@ -1934,8 +1973,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) if (empty_region_error) { vim_beep(BO_OPER); CancelRedo(); - } else + } else { op_tilde(oap); + } check_cursor_col(); break; @@ -1952,7 +1992,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) break; case OP_FORMAT2: - op_format(oap, true); /* use internal function */ + op_format(oap, true); // use internal function break; case OP_FUNCTION: @@ -1964,7 +2004,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) case OP_INSERT: case OP_APPEND: - VIsual_reselect = false; /* don't reselect now */ + VIsual_reselect = false; // don't reselect now if (empty_region_error) { vim_beep(BO_OPER); CancelRedo(); @@ -1996,7 +2036,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) break; case OP_REPLACE: - VIsual_reselect = false; /* don't reselect now */ + VIsual_reselect = false; // don't reselect now if (empty_region_error) { vim_beep(BO_OPER); CancelRedo(); @@ -2128,10 +2168,10 @@ static void op_function(const oparg_T *oap) { const TriState save_virtual_op = virtual_op; - if (*p_opfunc == NUL) + if (*p_opfunc == NUL) { EMSG(_("E774: 'operatorfunc' is empty")); - else { - /* Set '[ and '] marks to text to be operated on. */ + } else { + // Set '[ and '] marks to text to be operated on. curbuf->b_op_start = oap->start; curbuf->b_op_end = oap->end; if (oap->motion_type != kMTLineWise && !oap->inclusive) { @@ -2144,10 +2184,10 @@ static void op_function(const oparg_T *oap) argv[1].v_type = VAR_UNKNOWN; argv[0].vval.v_string = (char_u *)(((const char *const[]) { - [kMTBlockWise] = "block", - [kMTLineWise] = "line", - [kMTCharWise] = "char", - })[oap->motion_type]); + [kMTBlockWise] = "block", + [kMTLineWise] = "line", + [kMTCharWise] = "char", + })[oap->motion_type]); // Reset virtual_op so that 'virtualedit' can be changed in the // function. @@ -2165,73 +2205,69 @@ static void move_tab_to_mouse(void) { int tabnr = tab_page_click_defs[mouse_col].tabnr; if (tabnr <= 0) { - tabpage_move(9999); + tabpage_move(9999); } else if (tabnr < tabpage_index(curtab)) { - tabpage_move(tabnr - 1); + tabpage_move(tabnr - 1); } else { - tabpage_move(tabnr); - } -} - -/* - * Do the appropriate action for the current mouse click in the current mode. - * Not used for Command-line mode. - * - * Normal Mode: - * event modi- position visual change action - * fier cursor window - * left press - yes end yes - * left press C yes end yes "^]" (2) - * left press S yes end yes "*" (2) - * left drag - yes start if moved no - * left relse - yes start if moved no - * middle press - yes if not active no put register - * middle press - yes if active no yank and put - * right press - yes start or extend yes - * right press S yes no change yes "#" (2) - * right drag - yes extend no - * right relse - yes extend no - * - * Insert or Replace Mode: - * event modi- position visual change action - * fier cursor window - * left press - yes (cannot be active) yes - * left press C yes (cannot be active) yes "CTRL-O^]" (2) - * left press S yes (cannot be active) yes "CTRL-O*" (2) - * left drag - yes start or extend (1) no CTRL-O (1) - * left relse - yes start or extend (1) no CTRL-O (1) - * middle press - no (cannot be active) no put register - * right press - yes start or extend yes CTRL-O - * right press S yes (cannot be active) yes "CTRL-O#" (2) - * - * (1) only if mouse pointer moved since press - * (2) only if click is in same buffer - * - * Return true if start_arrow() should be called for edit mode. - */ -bool -do_mouse ( - oparg_T *oap, /* operator argument, can be NULL */ - int c, /* K_LEFTMOUSE, etc */ - int dir, /* Direction to 'put' if necessary */ - long count, - bool fixindent /* PUT_FIXINDENT if fixing indent necessary */ -) -{ - static bool got_click = false; /* got a click some time back */ - - int which_button; /* MOUSE_LEFT, _MIDDLE or _RIGHT */ - bool is_click; /* If false it's a drag or release event */ - bool is_drag; /* If true it's a drag event */ - int jump_flags = 0; /* flags for jump_to_mouse() */ + tabpage_move(tabnr); + } +} + +/// Do the appropriate action for the current mouse click in the current mode. +/// Not used for Command-line mode. +/// +/// Normal Mode: +/// event modi- position visual change action +/// fier cursor window +/// left press - yes end yes +/// left press C yes end yes "^]" (2) +/// left press S yes end yes "*" (2) +/// left drag - yes start if moved no +/// left relse - yes start if moved no +/// middle press - yes if not active no put register +/// middle press - yes if active no yank and put +/// right press - yes start or extend yes +/// right press S yes no change yes "#" (2) +/// right drag - yes extend no +/// right relse - yes extend no +/// +/// Insert or Replace Mode: +/// event modi- position visual change action +/// fier cursor window +/// left press - yes (cannot be active) yes +/// left press C yes (cannot be active) yes "CTRL-O^]" (2) +/// left press S yes (cannot be active) yes "CTRL-O*" (2) +/// left drag - yes start or extend (1) no CTRL-O (1) +/// left relse - yes start or extend (1) no CTRL-O (1) +/// middle press - no (cannot be active) no put register +/// right press - yes start or extend yes CTRL-O +/// right press S yes (cannot be active) yes "CTRL-O#" (2) +/// +/// (1) only if mouse pointer moved since press +/// (2) only if click is in same buffer +/// +/// @param oap operator argument, can be NULL +/// @param c K_LEFTMOUSE, etc +/// @param dir Direction to 'put' if necessary +/// @param fixindent PUT_FIXINDENT if fixing indent necessary +/// +/// @return true if start_arrow() should be called for edit mode. +bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) +{ + static bool got_click = false; // got a click some time back + + int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT + bool is_click; // If false it's a drag or release event + bool is_drag; // If true it's a drag event + int jump_flags = 0; // flags for jump_to_mouse() pos_T start_visual; - bool moved; /* Has cursor moved? */ - bool in_status_line; /* mouse in status line */ - static bool in_tab_line = false; /* mouse clicked in tab line */ - bool in_sep_line; /* mouse in vertical separator line */ + bool moved; // Has cursor moved? + bool in_status_line; // mouse in status line + static bool in_tab_line = false; // mouse clicked in tab line + bool in_sep_line; // mouse in vertical separator line int c1, c2; pos_T save_cursor; - win_T *old_curwin = curwin; + win_T *old_curwin = curwin; static pos_T orig_cursor; colnr_T leftcol, rightcol; pos_T end_visual; @@ -2256,8 +2292,9 @@ do_mouse ( /* Need to get the character, peeking doesn't get the actual * one. */ nc = safe_vgetc(); - if (c == nc) + if (c == nc) { continue; + } vungetc(nc); mouse_grid = save_mouse_grid; mouse_row = save_mouse_row; @@ -2275,12 +2312,13 @@ do_mouse ( /* * Ignore drag and release events if we didn't get a click. */ - if (is_click) + if (is_click) { got_click = true; - else { - if (!got_click) /* didn't get click, ignore */ + } else { + if (!got_click) { // didn't get click, ignore return false; - if (!is_drag) { /* release, reset got_click */ + } + if (!is_drag) { // release, reset got_click got_click = false; if (in_tab_line) { in_tab_line = false; @@ -2294,20 +2332,23 @@ do_mouse ( * CTRL right mouse button does CTRL-T */ if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT) { - if (State & INSERT) + if (State & INSERT) { stuffcharReadbuff(Ctrl_O); - if (count > 1) + } + if (count > 1) { stuffnumReadbuff(count); + } stuffcharReadbuff(Ctrl_T); - got_click = false; /* ignore drag&release now */ + got_click = false; // ignore drag&release now return false; } /* * CTRL only works with left mouse button */ - if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT) + if ((mod_mask & MOD_MASK_CTRL) && which_button != MOUSE_LEFT) { return false; + } /* * When a modifier is down, ignore drag and release events, as well as @@ -2324,22 +2365,24 @@ do_mouse ( && which_button == MOUSE_LEFT) && !((mod_mask & MOD_MASK_ALT) && !mouse_model_popup() - && which_button == MOUSE_RIGHT) - ) + && which_button == MOUSE_RIGHT)) { return false; + } /* * If the button press was used as the movement command for an operator * (eg "d<MOUSE>"), or it is the middle button that is held down, ignore * drag/release events. */ - if (!is_click && which_button == MOUSE_MIDDLE) + if (!is_click && which_button == MOUSE_MIDDLE) { return false; + } - if (oap != NULL) + if (oap != NULL) { regname = oap->regname; - else + } else { regname = 0; + } /* * Middle mouse button does a 'put' of the selected text @@ -2373,8 +2416,9 @@ do_mouse ( /* * The rest is below jump_to_mouse() */ - } else if ((State & INSERT) == 0) + } else if ((State & INSERT) == 0) { return false; + } /* * Middle click in insert mode doesn't move the mouse, just insert the @@ -2396,7 +2440,7 @@ do_mouse ( do_put(regname, NULL, BACKWARD, 1L, (fixindent ? PUT_FIXINDENT : 0) | PUT_CURSEND); - /* Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r */ + // Repeat it with CTRL-R CTRL-O r or CTRL-R CTRL-P r AppendCharToRedobuff(Ctrl_R); AppendCharToRedobuff(fixindent ? Ctrl_P : Ctrl_O); AppendCharToRedobuff(regname == 0 ? '"' : regname); @@ -2406,9 +2450,10 @@ do_mouse ( } } - /* When dragging or button-up stay in the same window. */ - if (!is_click) + // When dragging or button-up stay in the same window. + if (!is_click) { jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE; + } start_visual.lnum = 0; @@ -2421,17 +2466,17 @@ do_mouse ( return false; } - /* click in a tab selects that tab page */ + // click in a tab selects that tab page if (is_click && cmdwin_type == 0 && mouse_col < Columns) { in_tab_line = true; c1 = tab_page_click_defs[mouse_col].tabnr; switch (tab_page_click_defs[mouse_col].type) { - case kStlClickDisabled: { + case kStlClickDisabled: { break; } - case kStlClickTabClose: { + case kStlClickTabClose: { tabpage_T *tp; // Close the current or specified tab page. @@ -2449,7 +2494,7 @@ do_mouse ( } break; } - case kStlClickTabSwitch: { + case kStlClickTabSwitch: { if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { // double click opens new page end_visual_mode(); @@ -2467,13 +2512,13 @@ do_mouse ( } break; } - case kStlClickFuncRun: { + case kStlClickFuncRun: { typval_T argv[] = { { .v_lock = VAR_FIXED, .v_type = VAR_NUMBER, .vval = { - .v_number = (varnumber_T) tab_page_click_defs[mouse_col].tabnr + .v_number = (varnumber_T)tab_page_click_defs[mouse_col].tabnr }, }, { @@ -2495,7 +2540,7 @@ do_mouse ( { .v_lock = VAR_FIXED, .v_type = VAR_STRING, - .vval = { .v_string = (char_u *) (which_button == MOUSE_LEFT + .vval = { .v_string = (char_u *)(which_button == MOUSE_LEFT ? "l" : which_button == MOUSE_RIGHT ? "r" @@ -2508,22 +2553,22 @@ do_mouse ( .v_type = VAR_STRING, .vval = { .v_string = (char_u[]) { - (char_u) (mod_mask & MOD_MASK_SHIFT ? 's' : ' '), - (char_u) (mod_mask & MOD_MASK_CTRL ? 'c' : ' '), - (char_u) (mod_mask & MOD_MASK_ALT ? 'a' : ' '), - (char_u) (mod_mask & MOD_MASK_META ? 'm' : ' '), + (char_u)(mod_mask & MOD_MASK_SHIFT ? 's' : ' '), + (char_u)(mod_mask & MOD_MASK_CTRL ? 'c' : ' '), + (char_u)(mod_mask & MOD_MASK_ALT ? 'a' : ' '), + (char_u)(mod_mask & MOD_MASK_META ? 'm' : ' '), NUL } }, } }; typval_T rettv; - int doesrange; - (void)call_func((char_u *)tab_page_click_defs[mouse_col].func, - -1, - &rettv, ARRAY_SIZE(argv), argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, true, NULL, NULL); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + (void)call_func((char_u *)tab_page_click_defs[mouse_col].func, -1, + &rettv, ARRAY_SIZE(argv), argv, &funcexe); tv_clear(&rettv); break; } @@ -2564,8 +2609,9 @@ do_mouse ( if (is_click) { /* stop Visual mode for a left click in a window, but not when * on a status line */ - if (VIsual_active) + if (VIsual_active) { jump_flags |= MOUSE_MAY_STOP_VIS; + } } else { jump_flags |= MOUSE_MAY_VIS; } @@ -2597,9 +2643,10 @@ do_mouse ( oap->motion_type = kMTCharWise; } - /* When releasing the button let jump_to_mouse() know. */ - if (!is_click && !is_drag) + // When releasing the button let jump_to_mouse() know. + if (!is_click && !is_drag) { jump_flags |= MOUSE_RELEASED; + } /* * JUMP! @@ -2615,8 +2662,9 @@ do_mouse ( /* When jumping to another window, clear a pending operator. That's a bit * friendlier than beeping and not jumping to that window. */ - if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP) + if (curwin != old_curwin && oap != NULL && oap->op_type != OP_NOP) { clearop(oap); + } if (mod_mask == 0 && !is_drag @@ -2646,16 +2694,17 @@ do_mouse ( } } - /* When dragging the mouse above the window, scroll down. */ + // When dragging the mouse above the window, scroll down. if (is_drag && mouse_row < 0 && !in_status_line) { scroll_redraw(false, 1L); mouse_row = 0; } - if (start_visual.lnum) { /* right click in visual mode */ - /* When ALT is pressed make Visual mode blockwise. */ - if (mod_mask & MOD_MASK_ALT) + if (start_visual.lnum) { // right click in visual mode + // When ALT is pressed make Visual mode blockwise. + if (mod_mask & MOD_MASK_ALT) { VIsual_mode = Ctrl_V; + } /* * In Visual-block mode, divide the area in four, pick up the corner @@ -2663,55 +2712,58 @@ do_mouse ( */ if (VIsual_mode == Ctrl_V) { getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol); - if (curwin->w_curswant > (leftcol + rightcol) / 2) + if (curwin->w_curswant > (leftcol + rightcol) / 2) { end_visual.col = leftcol; - else + } else { end_visual.col = rightcol; + } if (curwin->w_cursor.lnum >= (start_visual.lnum + end_visual.lnum) / 2) { end_visual.lnum = start_visual.lnum; } - /* move VIsual to the right column */ - start_visual = curwin->w_cursor; /* save the cursor pos */ + // move VIsual to the right column + start_visual = curwin->w_cursor; // save the cursor pos curwin->w_cursor = end_visual; coladvance(end_visual.col); VIsual = curwin->w_cursor; - curwin->w_cursor = start_visual; /* restore the cursor */ + curwin->w_cursor = start_visual; // restore the cursor } else { /* * If the click is before the start of visual, change the start. * If the click is after the end of visual, change the end. If * the click is inside the visual, change the closest side. */ - if (lt(curwin->w_cursor, start_visual)) + if (lt(curwin->w_cursor, start_visual)) { VIsual = end_visual; - else if (lt(end_visual, curwin->w_cursor)) + } else if (lt(end_visual, curwin->w_cursor)) { VIsual = start_visual; - else { - /* In the same line, compare column number */ + } else { + // In the same line, compare column number if (end_visual.lnum == start_visual.lnum) { if (curwin->w_cursor.col - start_visual.col > - end_visual.col - curwin->w_cursor.col) + end_visual.col - curwin->w_cursor.col) { VIsual = start_visual; - else + } else { VIsual = end_visual; + } } - /* In different lines, compare line number */ + // In different lines, compare line number else { diff = (curwin->w_cursor.lnum - start_visual.lnum) - (end_visual.lnum - curwin->w_cursor.lnum); - if (diff > 0) /* closest to end */ + if (diff > 0) { // closest to end VIsual = start_visual; - else if (diff < 0) /* closest to start */ + } else if (diff < 0) { // closest to start VIsual = end_visual; - else { /* in the middle line */ + } else { // in the middle line if (curwin->w_cursor.col < - (start_visual.col + end_visual.col) / 2) + (start_visual.col + end_visual.col) / 2) { VIsual = end_visual; - else + } else { VIsual = start_visual; + } } } } @@ -2720,8 +2772,9 @@ do_mouse ( /* * If Visual mode started in insert mode, execute "CTRL-O" */ - else if ((State & INSERT) && VIsual_active) + else if ((State & INSERT) && VIsual_active) { stuffcharReadbuff(Ctrl_O); + } /* * Middle mouse click: Put text before cursor. @@ -2731,10 +2784,12 @@ do_mouse ( regname = '*'; } if (yank_register_mline(regname)) { - if (mouse_past_bottom) + if (mouse_past_bottom) { dir = FORWARD; - } else if (mouse_past_eol) + } + } else if (mouse_past_eol) { dir = FORWARD; + } if (fixindent) { c1 = (dir == BACKWARD) ? '[' : ']'; @@ -2749,8 +2804,9 @@ do_mouse ( * Remember where the paste started, so in edit() Insstart can be set * to this position */ - if (restart_edit != 0) + if (restart_edit != 0) { where_paste_started = curwin->w_cursor; + } do_put(regname, NULL, dir, count, (fixindent ? PUT_FIXINDENT : 0)| PUT_CURSEND); } @@ -2776,10 +2832,11 @@ do_mouse ( && (mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK)) { - if (State & INSERT) + if (State & INSERT) { stuffcharReadbuff(Ctrl_O); + } stuffcharReadbuff(Ctrl_RSB); - got_click = false; /* ignore drag&release now */ + got_click = false; // ignore drag&release now } /* * Shift-Mouse click searches for the next occurrence of the word under @@ -2787,15 +2844,16 @@ do_mouse ( */ else if ((mod_mask & MOD_MASK_SHIFT)) { if (State & INSERT - || (VIsual_active && VIsual_select) - ) + || (VIsual_active && VIsual_select)) { stuffcharReadbuff(Ctrl_O); - if (which_button == MOUSE_LEFT) + } + if (which_button == MOUSE_LEFT) { stuffcharReadbuff('*'); - else /* MOUSE_RIGHT */ + } else { // MOUSE_RIGHT stuffcharReadbuff('#'); + } } - /* Handle double clicks, unless on status line */ + // Handle double clicks, unless on status line else if (in_status_line) { } else if (in_sep_line) { } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT))) { @@ -2807,26 +2865,28 @@ do_mouse ( orig_cursor = VIsual; VIsual_active = true; VIsual_reselect = true; - /* start Select mode if 'selectmode' contains "mouse" */ + // start Select mode if 'selectmode' contains "mouse" may_start_select('o'); setmouse(); } if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { - /* Double click with ALT pressed makes it blockwise. */ - if (mod_mask & MOD_MASK_ALT) + // Double click with ALT pressed makes it blockwise. + if (mod_mask & MOD_MASK_ALT) { VIsual_mode = Ctrl_V; - else + } else { VIsual_mode = 'v'; - } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK) + } + } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_3CLICK) { VIsual_mode = 'V'; - else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK) + } else if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_4CLICK) { VIsual_mode = Ctrl_V; + } } /* * A double click selects a word or a block. */ if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) { - pos_T *pos = NULL; + pos_T *pos = NULL; int gc; if (is_click) { @@ -2834,8 +2894,9 @@ do_mouse ( * not a word character, try finding a match and select a (), * {}, [], #if/#endif, etc. block. */ end_visual = curwin->w_cursor; - while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) + while (gc = gchar_pos(&end_visual), ascii_iswhite(gc)) { inc(&end_visual); + } if (oap != NULL) { oap->motion_type = kMTCharWise; } @@ -2865,28 +2926,32 @@ do_mouse ( find_end_of_word(&VIsual); } else { find_start_of_word(&VIsual); - if (*p_sel == 'e' && *get_cursor_pos_ptr() != NUL) + if (*p_sel == 'e' && *get_cursor_pos_ptr() != NUL) { curwin->w_cursor.col += (*mb_ptr2len)(get_cursor_pos_ptr()); + } find_end_of_word(&curwin->w_cursor); } } curwin->w_set_curswant = true; } - if (is_click) - redraw_curbuf_later(INVERTED); /* update the inversion */ + if (is_click) { + redraw_curbuf_later(INVERTED); // update the inversion + } } else if (VIsual_active && !old_active) { - if (mod_mask & MOD_MASK_ALT) + if (mod_mask & MOD_MASK_ALT) { VIsual_mode = Ctrl_V; - else + } else { VIsual_mode = 'v'; + } } - /* If Visual mode changed show it later. */ + // If Visual mode changed show it later. if ((!VIsual_active && old_active && mode_displayed) || (VIsual_active && p_smd && msg_silent == 0 - && (!old_active || VIsual_mode != old_mode))) + && (!old_active || VIsual_mode != old_mode))) { redraw_cmdline = true; + } return moved; } @@ -2896,7 +2961,7 @@ do_mouse ( */ static void find_start_of_word(pos_T *pos) { - char_u *line; + char_u *line; int cclass; int col; @@ -2919,7 +2984,7 @@ static void find_start_of_word(pos_T *pos) */ static void find_end_of_word(pos_T *pos) { - char_u *line; + char_u *line; int cclass; int col; @@ -2932,8 +2997,9 @@ static void find_end_of_word(pos_T *pos) while (line[pos->col] != NUL) { col = pos->col + (*mb_ptr2len)(line + pos->col); if (get_mouse_class(line + col) != cclass) { - if (*p_sel == 'e') + if (*p_sel == 'e') { pos->col = col; + } break; } pos->col = col; @@ -2967,8 +3033,9 @@ static int get_mouse_class(char_u *p) * "->", "/ *", "*=", "+=", "&=", "<=", ">=", "!=" etc. Otherwise, each * character is in its own class. */ - if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL) + if (c != NUL && vim_strchr((char_u *)"-+*/%<>&|^!=", c) != NULL) { return 1; + } return c; } @@ -2979,19 +3046,19 @@ static int get_mouse_class(char_u *p) */ void end_visual_mode(void) { - VIsual_active = false; setmouse(); mouse_dragging = 0; - /* Save the current VIsual area for '< and '> marks, and "gv" */ + // Save the current VIsual area for '< and '> marks, and "gv" curbuf->b_visual.vi_mode = VIsual_mode; curbuf->b_visual.vi_start = VIsual; 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->w_cursor.coladd = 0; + } may_clear_cmdline(); @@ -3005,7 +3072,7 @@ void reset_VIsual_and_resel(void) { if (VIsual_active) { end_visual_mode(); - redraw_curbuf_later(INVERTED); /* delete the inversion later */ + redraw_curbuf_later(INVERTED); // delete the inversion later } VIsual_reselect = false; } @@ -3017,7 +3084,7 @@ void reset_VIsual(void) { if (VIsual_active) { end_visual_mode(); - redraw_curbuf_later(INVERTED); /* delete the inversion later */ + redraw_curbuf_later(INVERTED); // delete the inversion later VIsual_reselect = false; } } @@ -3028,11 +3095,8 @@ void reset_VIsual(void) // "dir" is FORWARD or BACKWARD, the direction of searching. // "*colp" is in/decremented if "ptr[-dir]" should also be included. // "bnp" points to a counter for square brackets. -static bool find_is_eval_item( - const char_u *const ptr, - int *const colp, - int *const bnp, - const int dir) +static bool find_is_eval_item(const char_u *const ptr, int *const colp, int *const bnp, + const int dir) { // Accept everything inside []. if ((*ptr == ']' && dir == BACKWARD) || (*ptr == '[' && dir == FORWARD)) { @@ -3085,17 +3149,12 @@ size_t find_ident_under_cursor(char_u **text, int find_type) curwin->w_cursor.col, text, NULL, find_type); } -/* - * Like find_ident_under_cursor(), but for any window and any position. - * However: Uses 'iskeyword' from the current window!. - */ -size_t find_ident_at_pos( - win_T *wp, - linenr_T lnum, - colnr_T startcol, - char_u **text, - int *textcol, // column where "text" starts, can be NULL - int find_type) +/// Like find_ident_under_cursor(), but for any window and any position. +/// However: Uses 'iskeyword' from the current window!. +/// +/// @param textcol column where "text" starts, can be NULL +size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char_u **text, int *textcol, + int find_type) FUNC_ATTR_NONNULL_ARG(1, 4) { int col = 0; // init to shut up GCC @@ -3201,7 +3260,7 @@ size_t find_ident_at_pos( static void prep_redo_cmd(cmdarg_T *cap) { prep_redo(cap->oap->regname, cap->count0, - NUL, cap->cmdchar, NUL, NUL, cap->nchar); + NUL, cap->cmdchar, NUL, NUL, cap->nchar); } /* @@ -3211,23 +3270,29 @@ static void prep_redo_cmd(cmdarg_T *cap) static void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) { ResetRedobuff(); - if (regname != 0) { /* yank from specified buffer */ + if (regname != 0) { // yank from specified buffer AppendCharToRedobuff('"'); AppendCharToRedobuff(regname); } - if (num) + if (num) { AppendNumberToRedobuff(num); + } - if (cmd1 != NUL) + if (cmd1 != NUL) { AppendCharToRedobuff(cmd1); - if (cmd2 != NUL) + } + if (cmd2 != NUL) { AppendCharToRedobuff(cmd2); - if (cmd3 != NUL) + } + if (cmd3 != NUL) { AppendCharToRedobuff(cmd3); - if (cmd4 != NUL) + } + if (cmd4 != NUL) { AppendCharToRedobuff(cmd4); - if (cmd5 != NUL) + } + if (cmd5 != NUL) { AppendCharToRedobuff(cmd5); + } } /* @@ -3237,8 +3302,9 @@ static void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int c */ static bool checkclearop(oparg_T *oap) { - if (oap->op_type == OP_NOP) + if (oap->op_type == OP_NOP) { return false; + } clearopbeep(oap); return true; } @@ -3251,9 +3317,9 @@ static bool checkclearop(oparg_T *oap) static bool checkclearopq(oparg_T *oap) { if (oap->op_type == OP_NOP - && !VIsual_active - ) + && !VIsual_active) { return false; + } clearopbeep(oap); return true; } @@ -3264,6 +3330,7 @@ static void clearop(oparg_T *oap) oap->regname = 0; oap->motion_force = NUL; oap->use_reg_one = false; + motion_force = NUL; } static void clearopbeep(oparg_T *oap) @@ -3278,12 +3345,18 @@ static void clearopbeep(oparg_T *oap) static void unshift_special(cmdarg_T *cap) { switch (cap->cmdchar) { - case K_S_RIGHT: cap->cmdchar = K_RIGHT; break; - case K_S_LEFT: cap->cmdchar = K_LEFT; break; - case K_S_UP: cap->cmdchar = K_UP; break; - case K_S_DOWN: cap->cmdchar = K_DOWN; break; - case K_S_HOME: cap->cmdchar = K_HOME; break; - case K_S_END: cap->cmdchar = K_END; break; + case K_S_RIGHT: + cap->cmdchar = K_RIGHT; break; + case K_S_LEFT: + cap->cmdchar = K_LEFT; break; + case K_S_UP: + cap->cmdchar = K_UP; break; + case K_S_DOWN: + cap->cmdchar = K_DOWN; break; + case K_S_HOME: + cap->cmdchar = K_HOME; break; + case K_S_END: + cap->cmdchar = K_END; break; } cap->cmdchar = simplify_key(cap->cmdchar, &mod_mask); } @@ -3303,15 +3376,16 @@ static void may_clear_cmdline(void) // Routines for displaying a partly typed command # define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30 static char_u showcmd_buf[SHOWCMD_BUFLEN]; -static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; /* For push_showcmd() */ +static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; // For push_showcmd() static bool showcmd_is_clear = true; static bool showcmd_visual = false; void clear_showcmd(void) { - if (!p_sc) + if (!p_sc) { return; + } if (VIsual_active && !char_avail()) { int cursor_bot = lt(VIsual, curwin->w_cursor); @@ -3319,7 +3393,7 @@ void clear_showcmd(void) colnr_T leftcol, rightcol; linenr_T top, bot; - /* Show the size of the Visual area. */ + // Show the size of the Visual area. if (cursor_bot) { top = VIsual.lnum; bot = curwin->w_cursor.lnum; @@ -3333,18 +3407,21 @@ void clear_showcmd(void) lines = bot - top + 1; if (VIsual_mode == Ctrl_V) { - char_u *saved_sbr = p_sbr; + char_u *const saved_sbr = p_sbr; + char_u *const saved_w_sbr = curwin->w_p_sbr; - /* Make 'sbr' empty for a moment to get the correct size. */ + // Make 'sbr' empty for a moment to get the correct size. p_sbr = empty_option; + curwin->w_p_sbr = empty_option; getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol); p_sbr = saved_sbr; + curwin->w_p_sbr = saved_w_sbr; snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64 "x%" PRId64, (int64_t)lines, (int64_t)rightcol - leftcol + 1); } else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum) { snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64, (int64_t)lines); } else { - char_u *s, *e; + char_u *s, *e; int l; int bytes = 0; int chars = 0; @@ -3361,16 +3438,17 @@ void clear_showcmd(void) if (l == 0) { ++bytes; ++chars; - break; /* end of line */ + break; // end of line } bytes += l; ++chars; s += l; } - if (bytes == chars) + if (bytes == chars) { sprintf((char *)showcmd_buf, "%d", chars); - else + } else { sprintf((char *)showcmd_buf, "%d-%d", chars, bytes); + } } int limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS; showcmd_buf[limit] = NUL; // truncate @@ -3379,9 +3457,10 @@ void clear_showcmd(void) showcmd_buf[0] = NUL; showcmd_visual = false; - /* Don't actually display something if there is nothing to clear. */ - if (showcmd_is_clear) + // Don't actually display something if there is nothing to clear. + if (showcmd_is_clear) { return; + } } display_showcmd(); @@ -3393,7 +3472,7 @@ void clear_showcmd(void) */ bool add_to_showcmd(int c) { - char_u *p; + char_u *p; int i; static int ignore[] = { @@ -3407,23 +3486,28 @@ bool add_to_showcmd(int c) 0 }; - if (!p_sc || msg_silent != 0) + if (!p_sc || msg_silent != 0) { return false; + } if (showcmd_visual) { showcmd_buf[0] = NUL; showcmd_visual = false; } - /* Ignore keys that are scrollbar updates and mouse clicks */ - if (IS_SPECIAL(c)) - for (i = 0; ignore[i] != 0; ++i) - if (ignore[i] == c) + // Ignore keys that are scrollbar updates and mouse clicks + if (IS_SPECIAL(c)) { + for (i = 0; ignore[i] != 0; ++i) { + if (ignore[i] == c) { return false; + } + } + } p = transchar(c); - if (*p == ' ') + if (*p == ' ') { STRCPY(p, "<20>"); + } size_t old_len = STRLEN(showcmd_buf); size_t extra_len = STRLEN(p); size_t limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS; @@ -3433,8 +3517,9 @@ bool add_to_showcmd(int c) } STRCAT(showcmd_buf, p); - if (char_avail()) + if (char_avail()) { return false; + } display_showcmd(); @@ -3454,16 +3539,19 @@ static void del_from_showcmd(int len) { int old_len; - if (!p_sc) + if (!p_sc) { return; + } old_len = (int)STRLEN(showcmd_buf); - if (len > old_len) + if (len > old_len) { len = old_len; + } showcmd_buf[old_len - len] = NUL; - if (!char_avail()) + if (!char_avail()) { display_showcmd(); + } } /* @@ -3472,14 +3560,16 @@ static void del_from_showcmd(int len) */ void push_showcmd(void) { - if (p_sc) + if (p_sc) { STRCPY(old_showcmd_buf, showcmd_buf); + } } void pop_showcmd(void) { - if (!p_sc) + if (!p_sc) { return; + } STRCPY(showcmd_buf, old_showcmd_buf); @@ -3528,18 +3618,18 @@ static void display_showcmd(void) */ void do_check_scrollbind(bool check) { - static win_T *old_curwin = NULL; + static win_T *old_curwin = NULL; static linenr_T old_topline = 0; static int old_topfill = 0; - static buf_T *old_buf = NULL; + static buf_T *old_buf = NULL; static colnr_T old_leftcol = 0; if (check && curwin->w_p_scb) { /* If a ":syncbind" command was just used, don't scroll, only reset * the values. */ - if (did_syncbind) + if (did_syncbind) { did_syncbind = false; - else if (curwin == old_curwin) { + } else if (curwin == old_curwin) { /* * Synchronize other windows, as necessary according to * 'scrollbind'. Don't do this after an ":edit" command, except @@ -3552,9 +3642,9 @@ void do_check_scrollbind(bool check) || curwin->w_topfill != old_topfill || curwin->w_leftcol != old_leftcol)) { check_scrollbind(curwin->w_topline - old_topline, - (long)(curwin->w_leftcol - old_leftcol)); + (long)(curwin->w_leftcol - old_leftcol)); } - } else if (vim_strchr(p_sbo, 'j')) { /* jump flag set in 'scrollopt' */ + } else if (vim_strchr(p_sbo, 'j')) { // jump flag set in 'scrollopt' /* * When switching between windows, make sure that the relative * vertical offset is valid for the new window. The relative @@ -3586,8 +3676,8 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) { bool want_ver; bool want_hor; - win_T *old_curwin = curwin; - buf_T *old_curbuf = curbuf; + win_T *old_curwin = curwin; + buf_T *old_curbuf = curbuf; int old_VIsual_select = VIsual_select; int old_VIsual_active = VIsual_active; colnr_T tgt_leftcol = curwin->w_leftcol; @@ -3608,7 +3698,7 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { curwin = wp; curbuf = curwin->w_buffer; - /* skip original window and windows with 'noscrollbind' */ + // skip original window and windows with 'noscrollbind' if (curwin == old_curwin || !curwin->w_p_scb) { continue; } @@ -3621,16 +3711,19 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) } else { curwin->w_scbind_pos += topline_diff; topline = curwin->w_scbind_pos; - if (topline > curbuf->b_ml.ml_line_count) + if (topline > curbuf->b_ml.ml_line_count) { topline = curbuf->b_ml.ml_line_count; - if (topline < 1) + } + if (topline < 1) { topline = 1; + } y = topline - curwin->w_topline; - if (y > 0) + if (y > 0) { scrollup(y, false); - else + } else { scrolldown(-y, false); + } } redraw_later(curwin, VALID); @@ -3663,7 +3756,7 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff) */ static void nv_ignore(cmdarg_T *cap) { - cap->retval |= CA_COMMAND_BUSY; /* don't call edit() now */ + cap->retval |= CA_COMMAND_BUSY; // don't call edit() now } /* @@ -3687,8 +3780,9 @@ static void nv_error(cmdarg_T *cap) */ static void nv_help(cmdarg_T *cap) { - if (!checkclearopq(cap->oap)) + if (!checkclearopq(cap->oap)) { ex_help(NULL); + } } /* @@ -3717,25 +3811,22 @@ static void nv_page(cmdarg_T *cap) { if (!checkclearop(cap->oap)) { if (mod_mask & MOD_MASK_CTRL) { - /* <C-PageUp>: tab page back; <C-PageDown>: tab page forward */ - if (cap->arg == BACKWARD) + // <C-PageUp>: tab page back; <C-PageDown>: tab page forward + if (cap->arg == BACKWARD) { goto_tabpage(-(int)cap->count1); - else + } else { goto_tabpage((int)cap->count0); - } else + } + } else { (void)onepage(cap->arg, cap->count1); + } } } -/* - * Implementation of "gd" and "gD" command. - */ -static void -nv_gd ( - oparg_T *oap, - int nchar, - int thisblock /* 1 for "1gd" and "1gD" */ -) +/// Implementation of "gd" and "gD" command. +/// +/// @param thisblock 1 for "1gd" and "1gD" +static void nv_gd(oparg_T *oap, int nchar, int thisblock) { size_t len; char_u *ptr; @@ -3780,23 +3871,17 @@ static bool is_ident(char_u *line, int offset) return incomment == false && instring == 0; } -/* - * Search for variable declaration of "ptr[len]". - * When "locally" is true in the current function ("gd"), otherwise in the - * current file ("gD"). - * When "thisblock" is true check the {} block scope. - * Return fail when not found. - */ -bool -find_decl ( - char_u *ptr, - size_t len, - bool locally, - bool thisblock, - int flags_arg // flags passed to searchit() -) -{ - char_u *pat; +/// Search for variable declaration of "ptr[len]". +/// When "locally" is true in the current function ("gd"), otherwise in the +/// current file ("gD"). +/// +/// @param thisblock when true check the {} block scope. +/// @param flags_arg flags passed to searchit() +/// +/// @return fail when not found. +bool find_decl(char_u *ptr, size_t len, bool locally, bool thisblock, int flags_arg) +{ + char_u *pat; pos_T old_pos; pos_T par_pos; pos_T found_pos; @@ -3817,8 +3902,8 @@ find_decl ( old_pos = curwin->w_cursor; save_p_ws = p_ws; save_p_scs = p_scs; - p_ws = false; /* don't wrap around end of file now */ - p_scs = false; /* don't switch ignorecase off now */ + p_ws = false; // don't wrap around end of file now + p_scs = false; // don't switch ignorecase off now /* * With "gD" go to line 1. @@ -3826,18 +3911,19 @@ find_decl ( * back until a blank line. If this fails go to line 1. */ if (!locally || !findpar(&incll, BACKWARD, 1L, '{', false)) { - setpcmark(); /* Set in findpar() otherwise */ + setpcmark(); // Set in findpar() otherwise curwin->w_cursor.lnum = 1; par_pos = curwin->w_cursor; } else { par_pos = curwin->w_cursor; while (curwin->w_cursor.lnum > 1 - && *skipwhite(get_cursor_line_ptr()) != NUL) + && *skipwhite(get_cursor_line_ptr()) != NUL) { --curwin->w_cursor.lnum; + } } curwin->w_cursor.col = 0; - /* Search forward for the identifier, ignore comment lines. */ + // Search forward for the identifier, ignore comment lines. clearpos(&found_pos); for (;; ) { t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD, @@ -3861,7 +3947,7 @@ find_decl ( } if (t == false) { - /* If we previously found a valid position, use it. */ + // If we previously found a valid position, use it. if (found_pos.lnum != 0) { curwin->w_cursor = found_pos; t = true; @@ -3869,7 +3955,7 @@ find_decl ( break; } if (get_leader_len(get_cursor_line_ptr(), NULL, false, true) > 0) { - /* Ignore this line, continue at start of next line. */ + // Ignore this line, continue at start of next line. ++curwin->w_cursor.lnum; curwin->w_cursor.col = 0; continue; @@ -3911,7 +3997,7 @@ find_decl ( curwin->w_cursor = old_pos; } else { curwin->w_set_curswant = true; - /* "n" searches forward now */ + // "n" searches forward now reset_search_dir(); } @@ -3935,10 +4021,10 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) bool retval = true; bool atend = false; int n; - int col_off1; /* margin offset for first screen line */ - int col_off2; /* margin offset for wrapped screen line */ - int width1; /* text width for first screen line */ - int width2; /* test width for wrapped screen line */ + int col_off1; // margin offset for first screen line + int col_off2; // margin offset for wrapped screen line + int width1; // text width for first screen line + int width2; // test width for wrapped screen line oap->motion_type = kMTCharWise; oap->inclusive = (curwin->w_curswant == MAXCOL); @@ -3958,20 +4044,22 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) if (curwin->w_curswant == MAXCOL) { atend = true; validate_virtcol(); - if (width1 <= 0) + if (width1 <= 0) { curwin->w_curswant = 0; - else { + } else { curwin->w_curswant = width1 - 1; - if (curwin->w_virtcol > curwin->w_curswant) + if (curwin->w_virtcol > curwin->w_curswant) { curwin->w_curswant += ((curwin->w_virtcol - curwin->w_curswant - 1) / width2 + 1) * width2; + } } } else { - if (linelen > width1) + if (linelen > width1) { n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1; - else + } else { n = width1; + } if (curwin->w_curswant >= n) { curwin->w_curswant = n - 1; } @@ -4007,11 +4095,12 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) curwin->w_curswant += w; } } - } else { /* dir == FORWARD */ - if (linelen > width1) + } else { // dir == FORWARD + if (linelen > width1) { n = ((linelen - width1 - 1) / width2 + 1) * width2 + width1; - else + } else { n = width1; + } if (curwin->w_curswant + width2 < (colnr_T)n && !hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { // move forward within line @@ -4041,10 +4130,11 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) } } - if (virtual_active() && atend) + if (virtual_active() && atend) { coladvance(MAXCOL); - else + } else { coladvance(curwin->w_curswant); + } if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) { /* @@ -4054,20 +4144,22 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) */ validate_virtcol(); colnr_T virtcol = curwin->w_virtcol; - if (virtcol > (colnr_T)width1 && *p_sbr != NUL) - virtcol -= vim_strsize(p_sbr); + if (virtcol > (colnr_T)width1 && *get_showbreak_value(curwin) != NUL) { + virtcol -= vim_strsize(get_showbreak_value(curwin)); + } if (virtcol > curwin->w_curswant && (curwin->w_curswant < (colnr_T)width1 ? (curwin->w_curswant > (colnr_T)width1 / 2) : ((curwin->w_curswant - width1) % width2 - > (colnr_T)width2 / 2))) + > (colnr_T)width2 / 2))) { --curwin->w_cursor.col; + } } - if (atend) - curwin->w_curswant = MAXCOL; /* stick in the last column */ - + if (atend) { + curwin->w_curswant = MAXCOL; // stick in the last column + } return retval; } @@ -4132,8 +4224,9 @@ static void nv_mouse(cmdarg_T *cap) */ static void nv_scroll_line(cmdarg_T *cap) { - if (!checkclearop(cap->oap)) + if (!checkclearop(cap->oap)) { scroll_redraw(cap->arg, cap->count1); + } } /* @@ -4146,8 +4239,8 @@ void scroll_redraw(int up, long count) linenr_T prev_lnum = curwin->w_cursor.lnum; bool moved = up ? - scrollup(count, true) : - scrolldown(count, true); + scrollup(count, true) : + scrolldown(count, true); if (get_scrolloff_value(curwin)) { // Adjust the cursor position for 'scrolloff'. Mark w_topline as @@ -4160,17 +4253,18 @@ void scroll_redraw(int up, long count) * we get stuck at one position. Don't move the cursor up if the * first line of the buffer is already on the screen */ while (curwin->w_topline == prev_topline - && curwin->w_topfill == prev_topfill - ) { + && curwin->w_topfill == prev_topfill) { if (up) { if (curwin->w_cursor.lnum > prev_lnum - || cursor_down(1L, false) == false) + || cursor_down(1L, false) == false) { break; + } } else { if (curwin->w_cursor.lnum < prev_lnum || prev_topline == 1L - || cursor_up(1L, false) == false) + || cursor_up(1L, false) == false) { break; + } } /* Mark w_topline as valid, otherwise the screen jumps back at the * end of the file. */ @@ -4206,8 +4300,9 @@ static void nv_zet(cmdarg_T *cap) /* * "z123{nchar}": edit the count before obtaining {nchar} */ - if (checkclearop(cap->oap)) + if (checkclearop(cap->oap)) { return; + } n = nchar - '0'; for (;; ) { no_mapping++; @@ -4215,11 +4310,11 @@ static void nv_zet(cmdarg_T *cap) LANGMAP_ADJUST(nchar, true); no_mapping--; (void)add_to_showcmd(nchar); - if (nchar == K_DEL || nchar == K_KDEL) + if (nchar == K_DEL || nchar == K_KDEL) { n /= 10; - else if (ascii_isdigit(nchar)) + } else if (ascii_isdigit(nchar)) { n = n * 10 + (nchar - '0'); - else if (nchar == CAR) { + } else if (nchar == CAR) { win_setheight(n); break; } else if (nchar == 'l' @@ -4256,15 +4351,16 @@ dozet: && cap->count0 && cap->count0 != curwin->w_cursor.lnum) { setpcmark(); - if (cap->count0 > curbuf->b_ml.ml_line_count) + if (cap->count0 > curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - else + } else { curwin->w_cursor.lnum = cap->count0; + } check_cursor_col(); } switch (nchar) { - /* "z+", "z<CR>" and "zt": put cursor at top of screen */ + // "z+", "z<CR>" and "zt": put cursor at top of screen case '+': if (cap->count0 == 0) { // No count given: put cursor at the line below screen @@ -4282,16 +4378,19 @@ dozet: beginline(BL_WHITE | BL_FIX); FALLTHROUGH; - case 't': scroll_cursor_top(0, true); + case 't': + scroll_cursor_top(0, true); redraw_later(curwin, VALID); set_fraction(curwin); break; - /* "z." and "zz": put cursor in middle of screen */ - case '.': beginline(BL_WHITE | BL_FIX); - FALLTHROUGH; + // "z." and "zz": put cursor in middle of screen + case '.': + beginline(BL_WHITE | BL_FIX); + FALLTHROUGH; - case 'z': scroll_cursor_halfway(true); + case 'z': + scroll_cursor_halfway(true); redraw_later(curwin, VALID); set_fraction(curwin); break; @@ -4303,74 +4402,83 @@ dozet: if (cap->count0 != 0) { scroll_cursor_bot(0, true); curwin->w_cursor.lnum = curwin->w_topline; - } else if (curwin->w_topline == 1) + } else if (curwin->w_topline == 1) { curwin->w_cursor.lnum = 1; - else + } else { curwin->w_cursor.lnum = curwin->w_topline - 1; + } FALLTHROUGH; case '-': beginline(BL_WHITE | BL_FIX); FALLTHROUGH; - case 'b': scroll_cursor_bot(0, true); + case 'b': + scroll_cursor_bot(0, true); redraw_later(curwin, VALID); set_fraction(curwin); break; - /* "zH" - scroll screen right half-page */ + // "zH" - scroll screen right half-page case 'H': cap->count1 *= curwin->w_width_inner / 2; FALLTHROUGH; - /* "zh" - scroll screen to the right */ + // "zh" - scroll screen to the right case 'h': case K_LEFT: if (!curwin->w_p_wrap) { - if ((colnr_T)cap->count1 > curwin->w_leftcol) + if ((colnr_T)cap->count1 > curwin->w_leftcol) { curwin->w_leftcol = 0; - else + } else { curwin->w_leftcol -= (colnr_T)cap->count1; + } leftcol_changed(); } break; // "zL" - scroll screen left half-page - case 'L': cap->count1 *= curwin->w_width_inner / 2; + case 'L': + cap->count1 *= curwin->w_width_inner / 2; FALLTHROUGH; - /* "zl" - scroll screen to the left */ + // "zl" - scroll screen to the left case 'l': case K_RIGHT: if (!curwin->w_p_wrap) { - /* scroll the window left */ + // scroll the window left curwin->w_leftcol += (colnr_T)cap->count1; leftcol_changed(); } break; - /* "zs" - scroll screen, cursor at the start */ - case 's': if (!curwin->w_p_wrap) { - if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) - col = 0; /* like the cursor is in col 0 */ - else + // "zs" - scroll screen, cursor at the start + case 's': + if (!curwin->w_p_wrap) { + if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + col = 0; // like the cursor is in col 0 + } else { getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL); - if (col > l_p_siso) + } + if (col > l_p_siso) { col -= l_p_siso; - else + } else { col = 0; + } if (curwin->w_leftcol != col) { curwin->w_leftcol = col; redraw_later(curwin, NOT_VALID); } - } + } break; - /* "ze" - scroll screen, cursor at the end */ - case 'e': if (!curwin->w_p_wrap) { - if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) - col = 0; /* like the cursor is in col 0 */ - else + // "ze" - scroll screen, cursor at the end + case 'e': + if (!curwin->w_p_wrap) { + if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + col = 0; // like the cursor is in col 0 + } else { getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col); + } n = curwin->w_width_inner - curwin_col_off(); if (col + l_p_siso < n) { col = 0; @@ -4381,7 +4489,7 @@ dozet: curwin->w_leftcol = col; redraw_later(curwin, NOT_VALID); } - } + } break; // "zp", "zP" in block mode put without addind trailing spaces @@ -4389,39 +4497,47 @@ dozet: case 'p': nv_put(cap); break; + // "zy" Yank without trailing spaces + case 'y': + nv_operator(cap); + break; - /* "zF": create fold command */ - /* "zf": create fold operator */ + // "zF": create fold command + // "zf": create fold operator case 'F': - case 'f': if (foldManualAllowed(true)) { + case 'f': + if (foldManualAllowed(true)) { cap->nchar = 'f'; nv_operator(cap); curwin->w_p_fen = true; - /* "zF" is like "zfzf" */ + // "zF" is like "zfzf" if (nchar == 'F' && cap->oap->op_type == OP_FOLD) { nv_operator(cap); finish_op = true; } - } else + } else { clearopbeep(cap->oap); + } break; - /* "zd": delete fold at cursor */ - /* "zD": delete fold at cursor recursively */ + // "zd": delete fold at cursor + // "zD": delete fold at cursor recursively case 'd': - case 'D': if (foldManualAllowed(false)) { + case 'D': + if (foldManualAllowed(false)) { if (VIsual_active) { nv_operator(cap); } else { deleteFold(curwin, curwin->w_cursor.lnum, curwin->w_cursor.lnum, nchar == 'D', false); } - } + } break; - /* "zE": erase all folds */ - case 'E': if (foldmethodIsManual(curwin)) { + // "zE": erase all folds + case 'E': + if (foldmethodIsManual(curwin)) { clearFolding(curwin); changed_window_setting(); } else if (foldmethodIsMarker(curwin)) { @@ -4431,20 +4547,24 @@ dozet: } break; - /* "zn": fold none: reset 'foldenable' */ - case 'n': curwin->w_p_fen = false; + // "zn": fold none: reset 'foldenable' + case 'n': + curwin->w_p_fen = false; break; - /* "zN": fold Normal: set 'foldenable' */ - case 'N': curwin->w_p_fen = true; + // "zN": fold Normal: set 'foldenable' + case 'N': + curwin->w_p_fen = true; break; - /* "zi": invert folding: toggle 'foldenable' */ - case 'i': curwin->w_p_fen = !curwin->w_p_fen; + // "zi": invert folding: toggle 'foldenable' + case 'i': + curwin->w_p_fen = !curwin->w_p_fen; break; // "za": open closed fold or close open fold at cursor - case 'a': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + case 'a': + if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { openFold(curwin->w_cursor, cap->count1); } else { closeFold(curwin->w_cursor, cap->count1); @@ -4453,7 +4573,8 @@ dozet: break; // "zA": open fold at cursor recursively - case 'A': if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { + case 'A': + if (hasFolding(curwin->w_cursor.lnum, NULL, NULL)) { openFoldRecurse(curwin->w_cursor); } else { closeFoldRecurse(curwin->w_cursor); @@ -4462,7 +4583,8 @@ dozet: break; // "zo": open fold at cursor or Visual area - case 'o': if (VIsual_active) { + case 'o': + if (VIsual_active) { nv_operator(cap); } else { openFold(curwin->w_cursor, cap->count1); @@ -4470,7 +4592,8 @@ dozet: break; // "zO": open fold recursively - case 'O': if (VIsual_active) { + case 'O': + if (VIsual_active) { nv_operator(cap); } else { openFoldRecurse(curwin->w_cursor); @@ -4478,16 +4601,18 @@ dozet: break; // "zc": close fold at cursor or Visual area - case 'c': if (VIsual_active) { + case 'c': + if (VIsual_active) { nv_operator(cap); - } else { + } else { closeFold(curwin->w_cursor, cap->count1); } curwin->w_p_fen = true; break; // "zC": close fold recursively - case 'C': if (VIsual_active) { + case 'C': + if (VIsual_active) { nv_operator(cap); } else { closeFoldRecurse(curwin->w_cursor); @@ -4495,24 +4620,27 @@ dozet: curwin->w_p_fen = true; break; - /* "zv": open folds at the cursor */ - case 'v': foldOpenCursor(); + // "zv": open folds at the cursor + case 'v': + foldOpenCursor(); break; - /* "zx": re-apply 'foldlevel' and open folds at the cursor */ - case 'x': curwin->w_p_fen = true; - curwin->w_foldinvalid = true; /* recompute folds */ - newFoldLevel(); /* update right now */ + // "zx": re-apply 'foldlevel' and open folds at the cursor + case 'x': + curwin->w_p_fen = true; + curwin->w_foldinvalid = true; // recompute folds + newFoldLevel(); // update right now foldOpenCursor(); break; - /* "zX": undo manual opens/closes, re-apply 'foldlevel' */ - case 'X': curwin->w_p_fen = true; - curwin->w_foldinvalid = true; /* recompute folds */ - old_fdl = -1; /* force an update */ + // "zX": undo manual opens/closes, re-apply 'foldlevel' + case 'X': + curwin->w_p_fen = true; + curwin->w_foldinvalid = true; // recompute folds + old_fdl = -1; // force an update break; - /* "zm": fold more */ + // "zm": fold more case 'm': if (curwin->w_p_fdl > 0) { curwin->w_p_fdl -= cap->count1; @@ -4520,17 +4648,18 @@ dozet: curwin->w_p_fdl = 0; } } - old_fdl = -1; /* force an update */ + old_fdl = -1; // force an update curwin->w_p_fen = true; break; - /* "zM": close all folds */ - case 'M': curwin->w_p_fdl = 0; - old_fdl = -1; /* force an update */ + // "zM": close all folds + case 'M': + curwin->w_p_fdl = 0; + old_fdl = -1; // force an update curwin->w_p_fen = true; break; - /* "zr": reduce folding */ + // "zr": reduce folding case 'r': curwin->w_p_fdl += cap->count1; { @@ -4546,11 +4675,12 @@ dozet: old_fdl = -1; // force an update break; - case 'j': /* "zj" move to next fold downwards */ - case 'k': /* "zk" move to next fold upwards */ + case 'j': // "zj" move to next fold downwards + case 'k': // "zk" move to next fold upwards if (foldMoveTo(true, nchar == 'j' ? FORWARD : BACKWARD, - cap->count1) == false) + cap->count1) == false) { clearopbeep(cap->oap); + } break; @@ -4567,53 +4697,61 @@ dozet: undo = true; FALLTHROUGH; - case 'g': /* "zg": add good word to word list */ - case 'w': /* "zw": add wrong word to word list */ - case 'G': /* "zG": add good word to temp word list */ - case 'W': /* "zW": add wrong word to temp word list */ - { - char_u *ptr = NULL; - size_t len; + case 'g': // "zg": add good word to word list + case 'w': // "zw": add wrong word to word list + case 'G': // "zG": add good word to temp word list + case 'W': // "zW": add wrong word to temp word list + { + char_u *ptr = NULL; + size_t len; - if (checkclearop(cap->oap)) - break; - if (VIsual_active && !get_visual_text(cap, &ptr, &len)) - return; - if (ptr == NULL) { - pos_T pos = curwin->w_cursor; + if (checkclearop(cap->oap)) { + break; + } + if (VIsual_active && !get_visual_text(cap, &ptr, &len)) { + return; + } + if (ptr == NULL) { + pos_T pos = curwin->w_cursor; + + /* Find bad word under the cursor. When 'spell' is + * off this fails and find_ident_under_cursor() is + * used below. */ + emsg_off++; + len = spell_move_to(curwin, FORWARD, true, true, NULL); + emsg_off--; + if (len != 0 && curwin->w_cursor.col <= pos.col) { + ptr = ml_get_pos(&curwin->w_cursor); + } + curwin->w_cursor = pos; + } - /* Find bad word under the cursor. When 'spell' is - * off this fails and find_ident_under_cursor() is - * used below. */ - emsg_off++; - len = spell_move_to(curwin, FORWARD, true, true, NULL); - emsg_off--; - if (len != 0 && curwin->w_cursor.col <= pos.col) - ptr = ml_get_pos(&curwin->w_cursor); - curwin->w_cursor = pos; + if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) { + return; + } + assert(len <= INT_MAX); + spell_add_word(ptr, (int)len, + nchar == 'w' || nchar == 'W' + ? SPELL_ADD_BAD : SPELL_ADD_GOOD, + (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1, + undo); } + break; - if (ptr == NULL && (len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) - return; - assert(len <= INT_MAX); - spell_add_word(ptr, (int)len, nchar == 'w' || nchar == 'W', - (nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1, - undo); - } - break; - - case '=': /* "z=": suggestions for a badly spelled word */ - if (!checkclearop(cap->oap)) + case '=': // "z=": suggestions for a badly spelled word + if (!checkclearop(cap->oap)) { spell_suggest((int)cap->count0); + } break; - default: clearopbeep(cap->oap); + default: + clearopbeep(cap->oap); } - /* Redraw when 'foldenable' changed */ + // Redraw when 'foldenable' changed if (old_fen != curwin->w_p_fen) { if (foldmethodIsDiff(curwin) && curwin->w_p_scb) { - /* Adjust 'foldenable' in diff-synced windows. */ + // Adjust 'foldenable' in diff-synced windows. FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp != curwin && foldmethodIsDiff(wp) && wp->w_p_scb) { wp->w_p_fen = curwin->w_p_fen; @@ -4624,9 +4762,10 @@ dozet: changed_window_setting(); } - /* Redraw when 'foldlevel' changed. */ - if (old_fdl != curwin->w_p_fdl) + // Redraw when 'foldlevel' changed. + if (old_fdl != curwin->w_p_fdl) { newFoldLevel(); + } } @@ -4642,7 +4781,7 @@ static void nv_exmode(cmdarg_T *cap) if (VIsual_active) { vim_beep(BO_EX); } else if (!checkclearop(cap->oap)) { - do_exmode(false); + do_exmode(); } } @@ -4669,9 +4808,10 @@ static void nv_colon(cmdarg_T *cap) } } - /* When typing, don't type below an old message */ - if (KeyTyped) + // When typing, don't type below an old message + if (KeyTyped) { compute_cmdrow(); + } old_p_im = p_im; @@ -4679,26 +4819,28 @@ static void nv_colon(cmdarg_T *cap) cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL, cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0); - /* If 'insertmode' changed, enter or exit Insert mode */ + // If 'insertmode' changed, enter or exit Insert mode if (p_im != old_p_im) { - if (p_im) + if (p_im) { restart_edit = 'i'; - else + } else { restart_edit = 0; + } } - if (cmd_result == false) - /* The Ex command failed, do not execute the operator. */ + if (cmd_result == false) { + // The Ex command failed, do not execute the operator. 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)) - || did_emsg - )) + } 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)) + || did_emsg + )) { /* The start of the operator has become invalid by the Ex command. */ clearopbeep(cap->oap); + } } } @@ -4707,12 +4849,13 @@ static void nv_colon(cmdarg_T *cap) */ static void nv_ctrlg(cmdarg_T *cap) { - if (VIsual_active) { /* toggle Selection/Visual mode */ + if (VIsual_active) { // toggle Selection/Visual mode VIsual_select = !VIsual_select; showmode(); - } else if (!checkclearop(cap->oap)) - /* print full name if count given or :cd used */ + } else if (!checkclearop(cap->oap)) { + // print full name if count given or :cd used fileinfo((int)cap->count0, false, true); + } } /* @@ -4721,10 +4864,11 @@ static void nv_ctrlg(cmdarg_T *cap) static void nv_ctrlh(cmdarg_T *cap) { if (VIsual_active && VIsual_select) { - cap->cmdchar = 'x'; /* BS key behaves like 'x' in Select mode */ + cap->cmdchar = 'x'; // BS key behaves like 'x' in Select mode v_visop(cap); - } else + } else { nv_left(cap); + } } /* @@ -4733,7 +4877,7 @@ static void nv_ctrlh(cmdarg_T *cap) static void nv_clear(cmdarg_T *cap) { if (!checkclearop(cap->oap)) { - /* Clear all syntax states to force resyncing. */ + // Clear all syntax states to force resyncing. syn_stack_free_all(curwin->w_s); FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { wp->w_s->b_syn_slow = false; @@ -4751,7 +4895,7 @@ static void nv_ctrlo(cmdarg_T *cap) if (VIsual_active && VIsual_select) { VIsual_select = false; showmode(); - restart_VIsual_select = 2; /* restart Select mode later */ + restart_VIsual_select = 2; // restart Select mode later } else { cap->count1 = -cap->count1; nv_pcmark(cap); @@ -4762,9 +4906,10 @@ static void nv_ctrlo(cmdarg_T *cap) // not named. static void nv_hat(cmdarg_T *cap) { - if (!checkclearopq(cap->oap)) + if (!checkclearopq(cap->oap)) { (void)buflist_getfile((int)cap->count0, (linenr_T)0, - GETF_SETMARK|GETF_ALT, false); + GETF_SETMARK|GETF_ALT, false); + } } /* @@ -4774,15 +4919,18 @@ static void nv_Zet(cmdarg_T *cap) { if (!checkclearopq(cap->oap)) { switch (cap->nchar) { - /* "ZZ": equivalent to ":x". */ - case 'Z': do_cmdline_cmd("x"); + // "ZZ": equivalent to ":x". + case 'Z': + do_cmdline_cmd("x"); break; - /* "ZQ": equivalent to ":q!" (Elvis compatible). */ - case 'Q': do_cmdline_cmd("q!"); + // "ZQ": equivalent to ":q!" (Elvis compatible). + case 'Q': + do_cmdline_cmd("q!"); break; - default: clearopbeep(cap->oap); + default: + clearopbeep(cap->oap); } } } @@ -4805,23 +4953,23 @@ void do_nv_ident(int c1, int c2) /* * Handle the commands that use the word under the cursor. - * [g] CTRL-] :ta to current identifier - * [g] 'K' run program for current identifier - * [g] '*' / to current identifier or string - * [g] '#' ? to current identifier or string - * g ']' :tselect for current identifier + * [g] CTRL-] :ta to current identifier + * [g] 'K' run program for current identifier + * [g] '*' / to current identifier or string + * [g] '#' ? to current identifier or string + * g ']' :tselect for current identifier */ static void nv_ident(cmdarg_T *cap) { - char_u *ptr = NULL; - char_u *p; - size_t n = 0; /* init for GCC */ + char_u *ptr = NULL; + char_u *p; + size_t n = 0; // init for GCC int cmdchar; - bool g_cmd; /* "g" command */ + bool g_cmd; // "g" command bool tag_cmd = false; - char_u *aux_ptr; + char_u *aux_ptr; - if (cap->cmdchar == 'g') { /* "g*", "g#", "g]" and "gCTRL-]" */ + if (cap->cmdchar == 'g') { // "g*", "g#", "g]" and "gCTRL-]" cmdchar = cap->nchar; g_cmd = true; } else { @@ -4829,17 +4977,20 @@ static void nv_ident(cmdarg_T *cap) g_cmd = false; } - if (cmdchar == POUND) /* the pound sign, '#' for English keyboards */ + if (cmdchar == POUND) { // the pound sign, '#' for English keyboards cmdchar = '#'; + } /* * The "]", "CTRL-]" and "K" commands accept an argument in Visual mode. */ if (cmdchar == ']' || cmdchar == Ctrl_RSB || cmdchar == 'K') { - if (VIsual_active && get_visual_text(cap, &ptr, &n) == false) + if (VIsual_active && get_visual_text(cap, &ptr, &n) == false) { return; - if (checkclearopq(cap->oap)) + } + if (checkclearopq(cap->oap)) { return; + } } if (ptr == NULL && (n = find_ident_under_cursor(&ptr, @@ -4876,11 +5027,12 @@ static void nv_ident(cmdarg_T *cap) * it was. */ setpcmark(); - curwin->w_cursor.col = (colnr_T) (ptr - get_cursor_line_ptr()); + curwin->w_cursor.col = (colnr_T)(ptr - get_cursor_line_ptr()); - if (!g_cmd && vim_iswordp(ptr)) + if (!g_cmd && vim_iswordp(ptr)) { STRCPY(buf, "\\<"); - no_smartcase = true; /* don't use 'smartcase' now */ + } + no_smartcase = true; // don't use 'smartcase' now break; case 'K': @@ -4900,7 +5052,7 @@ static void nv_ident(cmdarg_T *cap) --n; } if (n == 0) { - EMSG(_(e_noident)); /* found dashes only */ + EMSG(_(e_noident)); // found dashes only xfree(buf); return; } @@ -4913,7 +5065,8 @@ static void nv_ident(cmdarg_T *cap) snprintf(buf, buf_size, ".,.+%" PRId64, (int64_t)(cap->count0 - 1)); } - STRCAT(buf, "! "); + do_cmdline_cmd("tabnew"); + STRCAT(buf, "terminal "); if (cap->count0 == 0 && isman_s) { STRCAT(buf, "man"); } else { @@ -4922,7 +5075,7 @@ static void nv_ident(cmdarg_T *cap) STRCAT(buf, " "); if (cap->count0 != 0 && (isman || isman_s)) { snprintf(buf + STRLEN(buf), buf_size - STRLEN(buf), "%" PRId64, - (int64_t)cap->count0); + (int64_t)cap->count0); STRCAT(buf, " "); } } @@ -4930,21 +5083,23 @@ static void nv_ident(cmdarg_T *cap) case ']': tag_cmd = true; - if (p_cst) + if (p_cst) { STRCPY(buf, "cstag "); - else + } else { STRCPY(buf, "ts "); + } break; default: tag_cmd = true; - if (curbuf->b_help) + if (curbuf->b_help) { STRCPY(buf, "he! "); - else { - if (g_cmd) + } else { + if (g_cmd) { STRCPY(buf, "tj "); - else + } else { snprintf(buf, buf_size, "%" PRId64 "ta ", (int64_t)cap->count0); + } } } @@ -4964,24 +5119,27 @@ static void nv_ident(cmdarg_T *cap) STRCAT(buf, p); xfree(p); } else { - if (cmdchar == '*') + if (cmdchar == '*') { aux_ptr = (char_u *)(p_magic ? "/.*~[^$\\" : "/^$\\"); - else if (cmdchar == '#') + } else if (cmdchar == '#') { aux_ptr = (char_u *)(p_magic ? "/?.*~[^$\\" : "/?^$\\"); - else if (tag_cmd) { - if (curbuf->b_help) - /* ":help" handles unescaped argument */ + } else if (tag_cmd) { + if (curbuf->b_help) { + // ":help" handles unescaped argument aux_ptr = (char_u *)""; - else + } else { aux_ptr = (char_u *)"\\|\"\n["; - } else + } + } else { aux_ptr = (char_u *)"\\|\"\n*?["; + } p = (char_u *)buf + STRLEN(buf); while (n-- > 0) { - /* put a backslash before \ and some others */ - if (vim_strchr(aux_ptr, *ptr) != NULL) + // put a backslash before \ and some others + if (vim_strchr(aux_ptr, *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); @@ -5010,27 +5168,37 @@ static void nv_ident(cmdarg_T *cap) g_tag_at_cursor = true; do_cmdline_cmd(buf); g_tag_at_cursor = false; + + if (cmdchar == 'K' && !kp_ex && !kp_help) { + // Start insert mode in terminal buffer + restart_edit = 'i'; + + add_map((char_u *)"<buffer> <esc> <Cmd>call jobstop(&channel)<CR>", TERM_FOCUS, true); + do_cmdline_cmd("autocmd TermClose <buffer> " + " if !v:event.status |" + " exec 'bdelete! ' .. expand('<abuf>') |" + " endif"); + } } xfree(buf); } -/* - * Get visually selected text, within one line only. - * Returns false if more than one line selected. - */ -bool -get_visual_text ( - cmdarg_T *cap, - char_u **pp, /* return: start of selected text */ - size_t *lenp /* return: length of selected text */ -) -{ - if (VIsual_mode != 'V') +/// Get visually selected text, within one line only. +/// +/// @param pp return: start of selected text +/// @param lenp return: length of selected text +/// +/// @return false if more than one line selected. +bool get_visual_text(cmdarg_T *cap, char_u **pp, size_t *lenp) +{ + if (VIsual_mode != 'V') { unadjust_for_sel(); + } if (VIsual.lnum != curwin->w_cursor.lnum) { - if (cap != NULL) + if (cap != NULL) { clearopbeep(cap->oap); + } return false; } if (VIsual_mode == 'V') { @@ -5056,8 +5224,9 @@ get_visual_text ( */ static void nv_tagpop(cmdarg_T *cap) { - if (!checkclearopq(cap->oap)) + if (!checkclearopq(cap->oap)) { do_tag((char_u *)"", DT_POP, (int)cap->count1, false, true); + } } /* @@ -5076,23 +5245,24 @@ static void nv_scroll(cmdarg_T *cap) if (cap->cmdchar == 'L') { validate_botline(curwin); // make sure curwin->w_botline is valid curwin->w_cursor.lnum = curwin->w_botline - 1; - if (cap->count1 - 1 >= curwin->w_cursor.lnum) + if (cap->count1 - 1 >= curwin->w_cursor.lnum) { curwin->w_cursor.lnum = 1; - else { + } else { if (hasAnyFolding(curwin)) { - /* Count a fold for one screen line. */ + // Count a fold for one screen line. for (n = cap->count1 - 1; n > 0 && curwin->w_cursor.lnum > curwin->w_topline; --n) { (void)hasFolding(curwin->w_cursor.lnum, - &curwin->w_cursor.lnum, NULL); + &curwin->w_cursor.lnum, NULL); --curwin->w_cursor.lnum; } - } else + } else { curwin->w_cursor.lnum -= cap->count1 - 1; + } } } else { if (cap->cmdchar == 'M') { - /* Don't count filler lines above the window. */ + // Don't count filler lines above the window. used -= diff_check_fill(curwin, curwin->w_topline) - curwin->w_topfill; validate_botline(curwin); // make sure w_empty_rows is valid @@ -5101,15 +5271,17 @@ static void nv_scroll(cmdarg_T *cap) // Count half he number of filler lines to be "below this // line" and half to be "above the next line". if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline - + n) / 2 >= half) { + + n) / 2 >= half) { --n; break; } - used += plines(curwin->w_topline + n); - if (used >= half) + used += plines_win(curwin, curwin->w_topline + n, true); + if (used >= half) { break; - if (hasFolding(curwin->w_topline + n, NULL, &lnum)) + } + if (hasFolding(curwin->w_topline + n, NULL, &lnum)) { n = lnum - curwin->w_topline; + } } if (n > 0 && used > curwin->w_height_inner) { n--; @@ -5117,7 +5289,7 @@ static void nv_scroll(cmdarg_T *cap) } else { // (cap->cmdchar == 'H') n = cap->count1 - 1; if (hasAnyFolding(curwin)) { - /* Count a fold for one screen line. */ + // Count a fold for one screen line. lnum = curwin->w_topline; while (n-- > 0 && lnum < curwin->w_botline - 1) { (void)hasFolding(lnum, NULL, &lnum); @@ -5127,8 +5299,9 @@ static void nv_scroll(cmdarg_T *cap) } } curwin->w_cursor.lnum = curwin->w_topline + n; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) + if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + } } // Correct for 'so', except when an operator is pending. @@ -5147,9 +5320,10 @@ static void nv_right(cmdarg_T *cap) int PAST_LINE; if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) { - /* <C-Right> and <S-Right> move a word or WORD right */ - if (mod_mask & MOD_MASK_CTRL) + // <C-Right> and <S-Right> move a word or WORD right + if (mod_mask & MOD_MASK_CTRL) { cap->arg = true; + } nv_wordcmd(cap); return; } @@ -5162,13 +5336,14 @@ static void nv_right(cmdarg_T *cap) * In virtual mode, there's no such thing as "PAST_LINE", as lines are * (theoretically) infinitely long. */ - if (virtual_active()) + if (virtual_active()) { PAST_LINE = 0; + } for (n = cap->count1; n > 0; --n) { if ((!PAST_LINE && oneright() == false) - || (PAST_LINE && *get_cursor_pos_ptr() == NUL) - ) { + || (PAST_LINE && + *get_cursor_pos_ptr() == NUL)) { // <Space> wraps to next line if 'whichwrap' has 's'. // 'l' wraps to next line if 'whichwrap' has 'l'. // CURS_RIGHT wraps to next line if 'whichwrap' has '>'. @@ -5213,8 +5388,9 @@ static void nv_right(cmdarg_T *cap) } } if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped - && cap->oap->op_type == OP_NOP) + && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } /* @@ -5227,9 +5403,10 @@ static void nv_left(cmdarg_T *cap) long n; if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) { - /* <C-Left> and <S-Left> move a word or WORD left */ - if (mod_mask & MOD_MASK_CTRL) + // <C-Left> and <S-Left> move a word or WORD left + if (mod_mask & MOD_MASK_CTRL) { cap->arg = 1; + } nv_bck_word(cap); return; } @@ -5239,8 +5416,8 @@ static void nv_left(cmdarg_T *cap) for (n = cap->count1; n > 0; --n) { if (oneleft() == false) { /* <BS> and <Del> wrap to previous line if 'whichwrap' has 'b'. - * 'h' wraps to previous line if 'whichwrap' has 'h'. - * CURS_LEFT wraps to previous line if 'whichwrap' has '<'. + * 'h' wraps to previous line if 'whichwrap' has 'h'. + * CURS_LEFT wraps to previous line if 'whichwrap' has '<'. */ if ((((cap->cmdchar == K_BS || cap->cmdchar == Ctrl_H) && vim_strchr(p_ww, 'b') != NULL) @@ -5266,15 +5443,17 @@ static void nv_left(cmdarg_T *cap) } continue; } - /* Only beep and flush if not moved at all */ - else if (cap->oap->op_type == OP_NOP && n == cap->count1) + // Only beep and flush if not moved at all + else if (cap->oap->op_type == OP_NOP && n == cap->count1) { beep_flush(); + } break; } } if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped - && cap->oap->op_type == OP_NOP) + && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } /* @@ -5284,7 +5463,7 @@ static void nv_left(cmdarg_T *cap) static void nv_up(cmdarg_T *cap) { if (mod_mask & MOD_MASK_SHIFT) { - /* <S-Up> is page up */ + // <S-Up> is page up cap->arg = BACKWARD; nv_page(cap); } else { @@ -5304,7 +5483,7 @@ static void nv_up(cmdarg_T *cap) static void nv_down(cmdarg_T *cap) { if (mod_mask & MOD_MASK_SHIFT) { - /* <S-Down> is page down */ + // <S-Down> is page down cap->arg = FORWARD; nv_page(cap); } else if (bt_quickfix(curbuf) && cap->cmdchar == CAR) { @@ -5337,7 +5516,7 @@ static void nv_down(cmdarg_T *cap) */ static void nv_gotofile(cmdarg_T *cap) { - char_u *ptr; + char_u *ptr; linenr_T lnum = -1; if (text_locked()) { @@ -5366,8 +5545,9 @@ static void nv_gotofile(cmdarg_T *cap) beginline(BL_SOL | BL_FIX); } xfree(ptr); - } else + } else { clearop(cap->oap); + } } /* @@ -5375,10 +5555,10 @@ static void nv_gotofile(cmdarg_T *cap) */ static void nv_end(cmdarg_T *cap) { - if (cap->arg || (mod_mask & MOD_MASK_CTRL)) { /* CTRL-END = goto last line */ + if (cap->arg || (mod_mask & MOD_MASK_CTRL)) { // CTRL-END = goto last line cap->arg = true; nv_goto(cap); - cap->count1 = 1; /* to end of current line */ + cap->count1 = 1; // to end of current line } nv_dollar(cap); } @@ -5394,13 +5574,15 @@ static void nv_dollar(cmdarg_T *cap) * is pending (whew!) keep the cursor where it is. * Otherwise, send it to the end of the line. */ if (!virtual_active() || gchar_cursor() != NUL - || cap->oap->op_type == OP_NOP) - curwin->w_curswant = MAXCOL; /* so we stay at the end */ + || cap->oap->op_type == OP_NOP) { + curwin->w_curswant = MAXCOL; // so we stay at the end + } if (cursor_down(cap->count1 - 1, - cap->oap->op_type == OP_NOP) == false) + cap->oap->op_type == OP_NOP) == false) { clearopbeep(cap->oap); - else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) + } else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } /* @@ -5409,11 +5591,11 @@ static void nv_dollar(cmdarg_T *cap) */ static void nv_search(cmdarg_T *cap) { - oparg_T *oap = cap->oap; + oparg_T *oap = cap->oap; pos_T save_cursor = curwin->w_cursor; if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13) { - /* Translate "g??" to "g?g?" */ + // Translate "g??" to "g?g?" cap->cmdchar = 'g'; cap->nchar = '?'; nv_operator(cap); @@ -5454,18 +5636,13 @@ static void nv_next(cmdarg_T *cap) } } -/* - * Search for "pat" in direction "dir" ('/' or '?', 0 for repeat). - * Uses only cap->count1 and cap->oap from "cap". - * Return 0 for failure, 1 for found, 2 for found and line offset added. - */ -static int normal_search( - cmdarg_T *cap, - int dir, - char_u *pat, - int opt, // extra flags for do_search() - int *wrapped -) +/// Search for "pat" in direction "dir" ('/' or '?', 0 for repeat). +/// Uses only cap->count1 and cap->oap from "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_u *pat, int opt, int *wrapped) { int i; searchit_arg_T sia; @@ -5488,8 +5665,9 @@ static int normal_search( cap->oap->motion_type = kMTLineWise; } curwin->w_cursor.coladd = 0; - if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) + if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) { foldOpenCursor(); + } } /* "/$" will put the cursor after the end of the line, may need to @@ -5508,28 +5686,31 @@ static void nv_csearch(cmdarg_T *cap) { bool t_cmd; - if (cap->cmdchar == 't' || cap->cmdchar == 'T') + if (cap->cmdchar == 't' || cap->cmdchar == 'T') { t_cmd = true; - else + } else { t_cmd = false; + } cap->oap->motion_type = kMTCharWise; if (IS_SPECIAL(cap->nchar) || searchc(cap, t_cmd) == false) { clearopbeep(cap->oap); } else { curwin->w_set_curswant = true; - /* Include a Tab for "tx" and for "dfx". */ + // Include a Tab for "tx" and for "dfx". if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD && (t_cmd || cap->oap->op_type != OP_NOP)) { colnr_T scol, ecol; getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol); curwin->w_cursor.coladd = ecol - scol; - } else + } else { curwin->w_cursor.coladd = 0; + } adjust_for_sel(cap); - if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) + if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } } @@ -5541,8 +5722,8 @@ static void nv_brackets(cmdarg_T *cap) { pos_T new_pos = { 0, 0, 0 }; pos_T prev_pos; - pos_T *pos = NULL; /* init for GCC */ - pos_T old_pos; /* cursor position before command */ + pos_T *pos = NULL; // init for GCC + pos_T old_pos; // cursor position before command int flag; long n; int findc; @@ -5551,32 +5732,32 @@ static void nv_brackets(cmdarg_T *cap) cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; old_pos = curwin->w_cursor; - curwin->w_cursor.coladd = 0; /* TODO: don't do this for an error. */ + curwin->w_cursor.coladd = 0; // TODO: don't do this for an error. /* * "[f" or "]f" : Edit file under the cursor (same as "gf") */ - if (cap->nchar == 'f') + if (cap->nchar == 'f') { nv_gotofile(cap); - else + } else /* * Find the occurrence(s) of the identifier or define under cursor * in current and included files or jump to the first occurrence. * - * search list jump - * fwd bwd fwd bwd fwd bwd - * identifier "]i" "[i" "]I" "[I" "]^I" "[^I" - * define "]d" "[d" "]D" "[D" "]^D" "[^D" + * search list jump + * fwd bwd fwd bwd fwd bwd + * identifier "]i" "[i" "]I" "[I" "]^I" "[^I" + * define "]d" "[d" "]D" "[D" "]^D" "[^D" */ if (vim_strchr((char_u *) - "iI\011dD\004", - cap->nchar) != NULL) { - char_u *ptr; + "iI\011dD\004", + cap->nchar) != NULL) { + char_u *ptr; size_t len; - if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) + if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) { clearop(cap->oap); - else { + } else { find_pattern_in_path(ptr, 0, len, true, cap->count0 == 0 ? !isupper(cap->nchar) : false, (((cap->nchar & 0xf) == ('d' & 0xf)) @@ -5604,14 +5785,16 @@ static void nv_brackets(cmdarg_T *cap) && vim_strchr((char_u *)"{(*/#mM", cap->nchar) != NULL) || (cap->cmdchar == ']' && vim_strchr((char_u *)"})*/#mM", cap->nchar) != NULL)) { - if (cap->nchar == '*') + if (cap->nchar == '*') { cap->nchar = '/'; + } prev_pos.lnum = 0; if (cap->nchar == 'm' || cap->nchar == 'M') { - if (cap->cmdchar == '[') + if (cap->cmdchar == '[') { findc = '{'; - else + } else { findc = '}'; + } n = 9999; } else { findc = cap->nchar; @@ -5619,12 +5802,14 @@ static void nv_brackets(cmdarg_T *cap) } for (; n > 0; --n) { if ((pos = findmatchlimit(cap->oap, findc, - (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL) { - if (new_pos.lnum == 0) { /* nothing found */ - if (cap->nchar != 'm' && cap->nchar != 'M') + (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, 0)) == NULL) { + if (new_pos.lnum == 0) { // nothing found + if (cap->nchar != 'm' && cap->nchar != 'M') { clearopbeep(cap->oap); - } else - pos = &new_pos; /* use last one found */ + } + } else { + pos = &new_pos; // use last one found + } break; } prev_pos = new_pos; @@ -5640,24 +5825,27 @@ static void nv_brackets(cmdarg_T *cap) * Also repeat for the given count. */ if (cap->nchar == 'm' || cap->nchar == 'M') { - /* norm is true for "]M" and "[m" */ + // norm is true for "]M" and "[m" int norm = ((findc == '{') == (cap->nchar == 'm')); n = cap->count1; - /* found a match: we were inside a method */ + // found a match: we were inside a method if (prev_pos.lnum != 0) { pos = &prev_pos; curwin->w_cursor = prev_pos; - if (norm) + if (norm) { --n; - } else + } + } else { pos = NULL; + } while (n > 0) { for (;; ) { if ((findc == '{' ? dec_cursor() : inc_cursor()) < 0) { - /* if not found anything, that's an error */ - if (pos == NULL) + // if not found anything, that's an error + if (pos == NULL) { clearopbeep(cap->oap); + } n = 0; break; } @@ -5676,54 +5864,59 @@ static void nv_brackets(cmdarg_T *cap) new_pos = curwin->w_cursor; pos = &new_pos; } - /* found start/end of other method: go to match */ + // found start/end of other method: go to match else if ((pos = findmatchlimit(cap->oap, findc, - (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, - 0)) == NULL) + (cap->cmdchar == '[') ? FM_BACKWARD : FM_FORWARD, + 0)) == NULL) { n = 0; - else + } else { curwin->w_cursor = *pos; + } break; } } --n; } curwin->w_cursor = old_pos; - if (pos == NULL && new_pos.lnum != 0) + if (pos == NULL && new_pos.lnum != 0) { clearopbeep(cap->oap); + } } if (pos != NULL) { setpcmark(); curwin->w_cursor = *pos; curwin->w_set_curswant = true; if ((fdo_flags & FDO_BLOCK) && KeyTyped - && cap->oap->op_type == OP_NOP) + && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } } /* * "[[", "[]", "]]" and "][": move to start or end of function */ else if (cap->nchar == '[' || cap->nchar == ']') { - if (cap->nchar == cap->cmdchar) /* "]]" or "[[" */ + if (cap->nchar == cap->cmdchar) { // "]]" or "[[" flag = '{'; - else - flag = '}'; /* "][" or "[]" */ - + } else { + flag = '}'; // "][" or "[]" + } curwin->w_set_curswant = true; /* * Imitate strange Vi behaviour: When using "]]" with an operator * we also stop at '}'. */ if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, flag, - (cap->oap->op_type != OP_NOP - && cap->arg == FORWARD && flag == '{'))) + (cap->oap->op_type != OP_NOP + && cap->arg == FORWARD && flag == '{'))) { clearopbeep(cap->oap); - else { - if (cap->oap->op_type == OP_NOP) + } else { + if (cap->oap->op_type == OP_NOP) { beginline(BL_WHITE | BL_FIX); - if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) + } + if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } } else if (cap->nchar == 'p' || cap->nchar == 'P') { // "[p", "[P", "]P" and "]p": put with indent adjustment @@ -5737,12 +5930,14 @@ static void nv_brackets(cmdarg_T *cap) for (n = cap->count1; n > 0; --n) { prev_pos = *pos; pos = getnextmark(pos, cap->cmdchar == '[' ? BACKWARD : FORWARD, - cap->nchar == '\''); - if (pos == NULL) + cap->nchar == '\''); + if (pos == NULL) { break; + } } - if (pos == NULL) + if (pos == NULL) { pos = &prev_pos; + } nv_cursormark(cap, cap->nchar == '\'', pos); } /* @@ -5751,31 +5946,33 @@ static void nv_brackets(cmdarg_T *cap) */ else if (cap->nchar >= K_RIGHTRELEASE && cap->nchar <= K_LEFTMOUSE) { (void)do_mouse(cap->oap, cap->nchar, - (cap->cmdchar == ']') ? FORWARD : BACKWARD, - cap->count1, PUT_FIXINDENT); + (cap->cmdchar == ']') ? FORWARD : BACKWARD, + cap->count1, PUT_FIXINDENT); } /* * "[z" and "]z": move to start or end of open fold. */ else if (cap->nchar == 'z') { if (foldMoveTo(false, cap->cmdchar == ']' ? FORWARD : BACKWARD, - cap->count1) == false) + cap->count1) == false) { clearopbeep(cap->oap); + } } /* * "[c" and "]c": move to next or previous diff-change. */ else if (cap->nchar == 'c') { if (diff_move_to(cap->cmdchar == ']' ? FORWARD : BACKWARD, - cap->count1) == false) + cap->count1) == false) { clearopbeep(cap->oap); + } } /* * "[s", "[S", "]s" and "]S": move to next spell error. */ else if (cap->nchar == 's' || cap->nchar == 'S') { setpcmark(); - for (n = 0; n < cap->count1; ++n) + for (n = 0; n < cap->count1; ++n) { if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD, cap->nchar == 's', false, NULL) == 0) { clearopbeep(cap->oap); @@ -5783,12 +5980,15 @@ static void nv_brackets(cmdarg_T *cap) } else { curwin->w_set_curswant = true; } - if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) + } + if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) { foldOpenCursor(); + } } - /* Not a valid cap->nchar. */ - else + // Not a valid cap->nchar. + else { clearopbeep(cap->oap); + } } /* @@ -5796,7 +5996,7 @@ static void nv_brackets(cmdarg_T *cap) */ static void nv_percent(cmdarg_T *cap) { - pos_T *pos; + pos_T *pos; linenr_T lnum = curwin->w_cursor.lnum; cap->oap->inclusive = true; @@ -5817,6 +6017,9 @@ static void nv_percent(cmdarg_T *cap) curwin->w_cursor.lnum = (curbuf->b_ml.ml_line_count * cap->count0 + 99L) / 100L; } + if (curwin->w_cursor.lnum < 1) { + curwin->w_cursor.lnum = 1; + } if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; } @@ -5825,9 +6028,9 @@ static void nv_percent(cmdarg_T *cap) } else { // "%" : go to matching paren cap->oap->motion_type = kMTCharWise; cap->oap->use_reg_one = true; - if ((pos = findmatch(cap->oap, NUL)) == NULL) + if ((pos = findmatch(cap->oap, NUL)) == NULL) { clearopbeep(cap->oap); - else { + } else { setpcmark(); curwin->w_cursor = *pos; curwin->w_set_curswant = true; @@ -5838,8 +6041,9 @@ static void nv_percent(cmdarg_T *cap) if (cap->oap->op_type == OP_NOP && lnum != curwin->w_cursor.lnum && (fdo_flags & FDO_PERCENT) - && KeyTyped) + && KeyTyped) { foldOpenCursor(); + } } /* @@ -5850,18 +6054,19 @@ static void nv_brace(cmdarg_T *cap) { cap->oap->motion_type = kMTCharWise; cap->oap->use_reg_one = true; - /* The motion used to be inclusive for "(", but that is not what Vi does. */ + // The motion used to be inclusive for "(", but that is not what Vi does. cap->oap->inclusive = false; curwin->w_set_curswant = true; - if (findsent(cap->arg, cap->count1) == false) + if (findsent(cap->arg, cap->count1) == false) { clearopbeep(cap->oap); - else { - /* Don't leave the cursor on the NUL past end of line. */ + } else { + // Don't leave the cursor on the NUL past end of line. adjust_cursor(cap->oap); curwin->w_cursor.coladd = 0; - if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) + if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } } @@ -5871,8 +6076,9 @@ static void nv_brace(cmdarg_T *cap) static void nv_mark(cmdarg_T *cap) { if (!checkclearop(cap->oap)) { - if (setmark(cap->nchar) == false) + if (setmark(cap->nchar) == false) { clearopbeep(cap->oap); + } } } @@ -5886,12 +6092,13 @@ static void nv_findpar(cmdarg_T *cap) cap->oap->inclusive = false; cap->oap->use_reg_one = true; curwin->w_set_curswant = true; - if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, NUL, false)) + if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, NUL, false)) { clearopbeep(cap->oap); - else { + } else { curwin->w_cursor.coladd = 0; - if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) + if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } } @@ -5901,14 +6108,14 @@ static void nv_findpar(cmdarg_T *cap) static void nv_undo(cmdarg_T *cap) { if (cap->oap->op_type == OP_LOWER - || VIsual_active - ) { - /* translate "<Visual>u" to "<Visual>gu" and "guu" to "gugu" */ + || VIsual_active) { + // translate "<Visual>u" to "<Visual>gu" and "guu" to "gugu" cap->cmdchar = 'g'; cap->nchar = 'u'; nv_operator(cap); - } else + } else { nv_kundo(cap); + } } /* @@ -5931,7 +6138,7 @@ static void nv_kundo(cmdarg_T *cap) */ static void nv_replace(cmdarg_T *cap) { - char_u *ptr; + char_u *ptr; int had_ctrl_v; if (checkclearop(cap->oap)) { @@ -5942,26 +6149,29 @@ static void nv_replace(cmdarg_T *cap) return; } - /* get another character */ + // get another character if (cap->nchar == Ctrl_V) { had_ctrl_v = Ctrl_V; cap->nchar = get_literal(); - /* Don't redo a multibyte character with CTRL-V. */ - if (cap->nchar > DEL) + // Don't redo a multibyte character with CTRL-V. + if (cap->nchar > DEL) { had_ctrl_v = NUL; - } else + } + } else { had_ctrl_v = NUL; + } - /* Abort if the character is a special key. */ + // Abort if the character is a special key. if (IS_SPECIAL(cap->nchar)) { clearopbeep(cap->oap); return; } - /* Visual mode "r" */ + // Visual mode "r" if (VIsual_active) { - if (got_int) + if (got_int) { reset_VIsual(); + } if (had_ctrl_v) { // Use a special (negative) number to make a difference between a // literal CR or NL and a line break. @@ -5975,24 +6185,25 @@ static void nv_replace(cmdarg_T *cap) return; } - /* Break tabs, etc. */ + // Break tabs, etc. if (virtual_active()) { - if (u_save_cursor() == false) + if (u_save_cursor() == false) { return; + } if (gchar_cursor() == NUL) { - /* Add extra space and put the cursor on the first one. */ + // Add extra space and put the cursor on the first one. coladvance_force((colnr_T)(getviscol() + cap->count1)); assert(cap->count1 <= INT_MAX); curwin->w_cursor.col -= (colnr_T)cap->count1; - } else if (gchar_cursor() == TAB) + } else if (gchar_cursor() == TAB) { coladvance_force(getviscol()); + } } - /* Abort if not enough characters to replace. */ + // Abort if not enough characters to replace. ptr = get_cursor_pos_ptr(); if (STRLEN(ptr) < (unsigned)cap->count1 - || (mb_charlen(ptr) < cap->count1) - ) { + || (mb_charlen(ptr) < cap->count1)) { clearopbeep(cap->oap); return; } @@ -6009,9 +6220,10 @@ static void nv_replace(cmdarg_T *cap) return; } - /* save line for undo */ - if (u_save_cursor() == false) + // save line for undo + if (u_save_cursor() == false) { return; + } if (had_ctrl_v != Ctrl_V && (cap->nchar == '\r' || cap->nchar == '\n')) { /* @@ -6019,18 +6231,18 @@ static void nv_replace(cmdarg_T *cap) * Strange vi behaviour: Only one newline is inserted. * Delete the characters here. * Insert the newline with an insert command, takes care of - * autoindent. The insert command depends on being on the last + * autoindent. The insert command depends on being on the last * character of a line or not. */ - (void)del_chars(cap->count1, false); /* delete the characters */ + (void)del_chars(cap->count1, false); // delete the characters stuffcharReadbuff('\r'); stuffcharReadbuff(ESC); - /* Give 'r' to edit(), to get the redo command right. */ + // Give 'r' to edit(), to get the redo command right. invoke_edit(cap, true, 'r', false); } else { prep_redo(cap->oap->regname, cap->count1, - NUL, 'r', NUL, had_ctrl_v, cap->nchar); + NUL, 'r', NUL, had_ctrl_v, cap->nchar); curbuf->b_op_start = curwin->w_cursor; const int old_State = State; @@ -6067,7 +6279,7 @@ static void nv_replace(cmdarg_T *cap) ins_char(cap->ncharC2); } } - --curwin->w_cursor.col; /* cursor on the last replaced char */ + --curwin->w_cursor.col; // cursor on the last replaced char /* if the character on the left of the current cursor is a multi-byte * character, move two characters left */ mb_adjust_cursor(); @@ -6099,16 +6311,18 @@ static void v_swap_corners(int cmdchar) curwin->w_curswant = right; /* 'selection "exclusive" and cursor at right-bottom corner: move it * right one column */ - if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e') + if (old_cursor.lnum >= VIsual.lnum && *p_sel == 'e') { ++curwin->w_curswant; + } coladvance(curwin->w_curswant); if (curwin->w_cursor.col == old_cursor.col && (!virtual_active() - || curwin->w_cursor.coladd == old_cursor.coladd) - ) { + || curwin->w_cursor.coladd == + old_cursor.coladd)) { curwin->w_cursor.lnum = VIsual.lnum; - if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e') + if (old_cursor.lnum <= VIsual.lnum && *p_sel == 'e') { ++right; + } coladvance(right); VIsual = curwin->w_cursor; @@ -6129,18 +6343,19 @@ static void v_swap_corners(int cmdchar) */ static void nv_Replace(cmdarg_T *cap) { - if (VIsual_active) { /* "R" is replace lines */ + if (VIsual_active) { // "R" is replace lines cap->cmdchar = 'c'; cap->nchar = NUL; - VIsual_mode_orig = VIsual_mode; /* remember original area for gv */ + VIsual_mode_orig = VIsual_mode; // remember original area for gv VIsual_mode = 'V'; nv_operator(cap); } else if (!checkclearopq(cap->oap)) { if (!MODIFIABLE(curbuf)) { EMSG(_(e_modifiable)); } else { - if (virtual_active()) + if (virtual_active()) { coladvance(getviscol()); + } invoke_edit(cap, false, cap->arg ? 'V' : 'R', false); } } @@ -6154,17 +6369,19 @@ static void nv_vreplace(cmdarg_T *cap) if (VIsual_active) { cap->cmdchar = 'r'; cap->nchar = cap->extra_char; - nv_replace(cap); /* Do same as "r" in Visual mode for now */ + nv_replace(cap); // Do same as "r" in Visual mode for now } else if (!checkclearopq(cap->oap)) { if (!MODIFIABLE(curbuf)) { EMSG(_(e_modifiable)); } else { - if (cap->extra_char == Ctrl_V) /* get another character */ + if (cap->extra_char == Ctrl_V) { // get another character cap->extra_char = get_literal(); + } stuffcharReadbuff(cap->extra_char); stuffcharReadbuff(ESC); - if (virtual_active()) + if (virtual_active()) { coladvance(getviscol()); + } invoke_edit(cap, true, 'v', false); } } @@ -6190,8 +6407,9 @@ static void n_swapchar(cmdarg_T *cap) prep_redo_cmd(cap); - if (u_save_cursor() == false) + if (u_save_cursor() == false) { return; + } startpos = curwin->w_cursor; for (n = cap->count1; n > 0; --n) { @@ -6203,12 +6421,14 @@ static void n_swapchar(cmdarg_T *cap) ++curwin->w_cursor.lnum; curwin->w_cursor.col = 0; if (n > 1) { - if (u_savesub(curwin->w_cursor.lnum) == false) + if (u_savesub(curwin->w_cursor.lnum) == false) { break; + } u_clearline(); } - } else + } else { break; + } } } @@ -6220,8 +6440,9 @@ static void n_swapchar(cmdarg_T *cap) 0L, true); curbuf->b_op_start = startpos; curbuf->b_op_end = curwin->w_cursor; - if (curbuf->b_op_end.col > 0) + if (curbuf->b_op_end.col > 0) { --curbuf->b_op_end.col; + } } } @@ -6230,19 +6451,21 @@ static void n_swapchar(cmdarg_T *cap) */ static void nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos) { - if (check_mark(pos) == false) + if (check_mark(pos) == false) { clearop(cap->oap); - else { + } else { if (cap->cmdchar == '\'' || cap->cmdchar == '`' || cap->cmdchar == '[' - || cap->cmdchar == ']') + || cap->cmdchar == ']') { setpcmark(); + } curwin->w_cursor = *pos; - if (flag) + if (flag) { beginline(BL_WHITE | BL_FIX); - else + } else { check_cursor(); + } } cap->oap->motion_type = flag ? kMTLineWise : kMTCharWise; if (cap->cmdchar == '`') { @@ -6265,8 +6488,9 @@ static void v_visop(cmdarg_T *cap) if (VIsual_mode != Ctrl_V) { VIsual_mode_orig = VIsual_mode; VIsual_mode = 'V'; - } else if (cap->cmdchar == 'C' || cap->cmdchar == 'D') + } else if (cap->cmdchar == 'C' || cap->cmdchar == 'D') { curwin->w_curswant = MAXCOL; + } } cap->cmdchar = *(vim_strchr(trans, cap->cmdchar) + 1); nv_operator(cap); @@ -6288,8 +6512,9 @@ static void nv_subst(cmdarg_T *cap) } cap->cmdchar = 'c'; nv_operator(cap); - } else + } else { nv_optrans(cap); + } } /* @@ -6297,14 +6522,15 @@ static void nv_subst(cmdarg_T *cap) */ static void nv_abbrev(cmdarg_T *cap) { - if (cap->cmdchar == K_DEL || cap->cmdchar == K_KDEL) - cap->cmdchar = 'x'; /* DEL key behaves like 'x' */ - - /* in Visual mode these commands are operators */ - if (VIsual_active) + if (cap->cmdchar == K_DEL || cap->cmdchar == K_KDEL) { + cap->cmdchar = 'x'; // DEL key behaves like 'x' + } + // in Visual mode these commands are operators + if (VIsual_active) { v_visop(cap); - else + } else { nv_optrans(cap); + } } /* @@ -6331,24 +6557,27 @@ static void nv_optrans(cmdarg_T *cap) */ static void nv_gomark(cmdarg_T *cap) { - pos_T *pos; + pos_T *pos; int c; pos_T old_cursor = curwin->w_cursor; const bool old_KeyTyped = KeyTyped; // getting file may reset it - if (cap->cmdchar == 'g') + if (cap->cmdchar == 'g') { c = cap->extra_char; - else + } else { c = cap->nchar; + } pos = getmark(c, (cap->oap->op_type == OP_NOP)); - if (pos == (pos_T *)-1) { /* jumped to other file */ + if (pos == (pos_T *)-1) { // jumped to other file if (cap->arg) { check_cursor_lnum(); beginline(BL_WHITE | BL_FIX); - } else + } else { check_cursor(); - } else + } + } else { nv_cursormark(cap, cap->arg, pos); + } // May need to clear the coladd that a mark includes. if (!virtual_active()) { @@ -6367,7 +6596,7 @@ static void nv_gomark(cmdarg_T *cap) // Handle CTRL-O, CTRL-I, "g;", "g,", and "CTRL-Tab" commands. static void nv_pcmark(cmdarg_T *cap) { - pos_T *pos; + pos_T *pos; linenr_T lnum = curwin->w_cursor.lnum; const bool old_KeyTyped = KeyTyped; // getting file may reset it @@ -6384,22 +6613,25 @@ static void nv_pcmark(cmdarg_T *cap) if (pos == (pos_T *)-1) { // jump to other file curwin->w_set_curswant = true; check_cursor(); - } else if (pos != NULL) /* can jump */ + } else if (pos != NULL) { // can jump nv_cursormark(cap, false, pos); - else if (cap->cmdchar == 'g') { - if (curbuf->b_changelistlen == 0) + } else if (cap->cmdchar == 'g') { + if (curbuf->b_changelistlen == 0) { EMSG(_("E664: changelist is empty")); - else if (cap->count1 < 0) + } else if (cap->count1 < 0) { EMSG(_("E662: At start of changelist")); - else + } else { EMSG(_("E663: At end of changelist")); - } else + } + } else { clearopbeep(cap->oap); + } if (cap->oap->op_type == OP_NOP && (pos == (pos_T *)-1 || lnum != curwin->w_cursor.lnum) && (fdo_flags & FDO_MARK) - && old_KeyTyped) + && old_KeyTyped) { foldOpenCursor(); + } } } @@ -6408,16 +6640,19 @@ static void nv_pcmark(cmdarg_T *cap) */ static void nv_regname(cmdarg_T *cap) { - if (checkclearop(cap->oap)) + if (checkclearop(cap->oap)) { return; - if (cap->nchar == '=') + } + if (cap->nchar == '=') { cap->nchar = get_expr_register(); + } if (cap->nchar != NUL && valid_yank_reg(cap->nchar, false)) { cap->oap->regname = cap->nchar; - cap->opcount = cap->count0; /* remember count before '"' */ + cap->opcount = cap->count0; // remember count before '"' set_reg_var(cap->oap->regname); - } else + } else { clearopbeep(cap->oap); + } } /* @@ -6428,8 +6663,9 @@ static void nv_regname(cmdarg_T *cap) */ static void nv_visual(cmdarg_T *cap) { - if (cap->cmdchar == Ctrl_Q) + if (cap->cmdchar == Ctrl_Q) { cap->cmdchar = Ctrl_V; + } // 'v', 'V' and CTRL-V can be used while an operator is pending to make it // charwise, linewise, or blockwise. @@ -6440,28 +6676,30 @@ static void nv_visual(cmdarg_T *cap) } VIsual_select = cap->arg; - if (VIsual_active) { /* change Visual mode */ - if (VIsual_mode == cap->cmdchar) /* stop visual mode */ + if (VIsual_active) { // change Visual mode + if (VIsual_mode == cap->cmdchar) { // stop visual mode end_visual_mode(); - else { /* toggle char/block mode */ - /* or char/line mode */ + } else { // toggle char/block mode + // or char/line mode VIsual_mode = cap->cmdchar; showmode(); } redraw_curbuf_later(INVERTED); // update the inversion } else { // start Visual mode if (cap->count0 > 0 && resel_VIsual_mode != NUL) { - /* use previously selected part */ + // use previously selected part VIsual = curwin->w_cursor; VIsual_active = true; VIsual_reselect = true; - if (!cap->arg) - /* start Select mode when 'selectmode' contains "cmd" */ + if (!cap->arg) { + // start Select mode when 'selectmode' contains "cmd" may_start_select('c'); + } setmouse(); - if (p_smd && msg_silent == 0) - redraw_cmdline = true; /* show visual mode later */ + if (p_smd && msg_silent == 0) { + redraw_cmdline = true; // show visual mode later + } /* * For V and ^V, we multiply the number of lines even if there * was only one -- webb @@ -6469,8 +6707,9 @@ static void nv_visual(cmdarg_T *cap) if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1) { curwin->w_cursor.lnum += resel_VIsual_line_count * cap->count0 - 1; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) + if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + } } VIsual_mode = resel_VIsual_mode; if (VIsual_mode == 'v') { @@ -6479,8 +6718,9 @@ static void nv_visual(cmdarg_T *cap) assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX); curwin->w_curswant = (curwin->w_virtcol + resel_VIsual_vcol * (int)cap->count0 - 1); - } else + } else { curwin->w_curswant = resel_VIsual_vcol; + } coladvance(curwin->w_curswant); } if (resel_VIsual_vcol == MAXCOL) { @@ -6492,22 +6732,26 @@ static void nv_visual(cmdarg_T *cap) curwin->w_curswant = (curwin->w_virtcol + resel_VIsual_vcol * (int)cap->count0 - 1); coladvance(curwin->w_curswant); - } else + } else { curwin->w_set_curswant = true; - redraw_curbuf_later(INVERTED); /* show the inversion */ + } + redraw_curbuf_later(INVERTED); // show the inversion } else { - if (!cap->arg) - /* start Select mode when 'selectmode' contains "cmd" */ + if (!cap->arg) { + // start Select mode when 'selectmode' contains "cmd" may_start_select('c'); + } n_start_visual_mode(cap->cmdchar); - if (VIsual_mode != 'V' && *p_sel == 'e') - ++cap->count1; /* include one more char */ + if (VIsual_mode != 'V' && *p_sel == 'e') { + ++cap->count1; // include one more char + } if (cap->count0 > 0 && --cap->count1 > 0) { - /* With a count select that many characters or lines. */ - if (VIsual_mode == 'v' || VIsual_mode == Ctrl_V) + // With a count select that many characters or lines. + if (VIsual_mode == 'v' || VIsual_mode == Ctrl_V) { nv_right(cap); - else if (VIsual_mode == 'V') + } else if (VIsual_mode == 'V') { nv_down(cap); + } } } } @@ -6518,7 +6762,7 @@ static void nv_visual(cmdarg_T *cap) */ void start_selection(void) { - /* if 'selectmode' contains "key", start Select mode */ + // if 'selectmode' contains "key", start Select mode may_start_select('k'); n_start_visual_mode('v'); } @@ -6541,9 +6785,9 @@ static void n_start_visual_mode(int c) VIsual_mode = c; VIsual_active = true; VIsual_reselect = true; - /* Corner case: the 0 position in a tab may change when going into - * virtualedit. Recalculate curwin->w_cursor to avoid bad hilighting. - */ + // 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 && (ve_flags & VE_BLOCK) && gchar_cursor() == TAB) { validate_virtcol(); coladvance(curwin->w_virtcol); @@ -6556,9 +6800,9 @@ static void n_start_visual_mode(int c) // Check for redraw after changing the state. conceal_check_cursor_line(); - if (p_smd && msg_silent == 0) - redraw_cmdline = true; /* show visual mode later */ - + if (p_smd && msg_silent == 0) { + redraw_cmdline = true; // show visual mode later + } /* Only need to redraw this line, unless still need to redraw an old * Visual area (when 'lazyredraw' is set). */ if (curwin->w_redr_type < INVERTED) { @@ -6589,8 +6833,9 @@ static void nv_window(cmdarg_T *cap) static void nv_suspend(cmdarg_T *cap) { clearop(cap->oap); - if (VIsual_active) - end_visual_mode(); /* stop Visual mode */ + if (VIsual_active) { + end_visual_mode(); // stop Visual mode + } do_cmdline_cmd("st"); } @@ -6599,7 +6844,7 @@ static void nv_suspend(cmdarg_T *cap) */ static void nv_g_cmd(cmdarg_T *cap) { - oparg_T *oap = cap->oap; + oparg_T *oap = cap->oap; pos_T tpos; int i; bool flag = false; @@ -6634,18 +6879,19 @@ static void nv_g_cmd(cmdarg_T *cap) /* * "gv": Reselect the previous Visual area. If Visual already active, - * exchange previous and current Visual area. + * exchange previous and current Visual area. */ case 'v': - if (checkclearop(oap)) + if (checkclearop(oap)) { break; + } - if ( curbuf->b_visual.vi_start.lnum == 0 - || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count - || curbuf->b_visual.vi_end.lnum == 0) + if (curbuf->b_visual.vi_start.lnum == 0 + || curbuf->b_visual.vi_start.lnum > curbuf->b_ml.ml_line_count + || curbuf->b_visual.vi_end.lnum == 0) { beep_flush(); - else { - /* set w_cursor to the start of the Visual area, tpos to the end */ + } else { + // set w_cursor to the start of the Visual area, tpos to the end if (VIsual_active) { i = VIsual_mode; VIsual_mode = curbuf->b_visual.vi_mode; @@ -6691,7 +6937,7 @@ static void nv_g_cmd(cmdarg_T *cap) break; /* * "gV": Don't reselect the previous Visual area after a Select mode - * mapping of menu. + * mapping of menu. */ case 'V': VIsual_reselect = false; @@ -6719,8 +6965,9 @@ static void nv_g_cmd(cmdarg_T *cap) */ case 'N': case 'n': - if (!current_search(cap->count1, cap->nchar == 'n')) + if (!current_search(cap->count1, cap->nchar == 'n')) { clearopbeep(oap); + } break; /* @@ -6733,10 +6980,12 @@ static void nv_g_cmd(cmdarg_T *cap) if (!curwin->w_p_wrap) { oap->motion_type = kMTLineWise; i = cursor_down(cap->count1, oap->op_type == OP_NOP); - } else + } else { i = nv_screengo(oap, FORWARD, cap->count1); - if (!i) + } + if (!i) { clearopbeep(oap); + } break; case 'k': @@ -6745,10 +6994,12 @@ static void nv_g_cmd(cmdarg_T *cap) if (!curwin->w_p_wrap) { oap->motion_type = kMTLineWise; i = cursor_up(cap->count1, oap->op_type == OP_NOP); - } else + } else { i = nv_screengo(oap, BACKWARD, cap->count1); - if (!i) + } + if (!i) { clearopbeep(oap); + } break; /* @@ -6773,17 +7024,18 @@ static void nv_g_cmd(cmdarg_T *cap) oap->motion_type = kMTCharWise; oap->inclusive = false; if (curwin->w_p_wrap - && curwin->w_width_inner != 0 - ) { + && curwin->w_width_inner != 0) { int width1 = curwin->w_width_inner - curwin_col_off(); int width2 = width1 + curwin_col_off2(); validate_virtcol(); i = 0; - if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) + if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0) { i = (curwin->w_virtcol - width1) / width2 * width2 + width1; - } else + } + } else { i = curwin->w_leftcol; + } /* Go to the middle of the screen line. When 'number' or * 'relativenumber' is on and lines are wrapping the middle can be more * to the left. */ @@ -6802,8 +7054,7 @@ static void nv_g_cmd(cmdarg_T *cap) curwin->w_set_curswant = true; break; - case 'M': - { + case 'M': { const char_u *const ptr = get_cursor_line_ptr(); oap->motion_type = kMTCharWise; @@ -6825,19 +7076,21 @@ static void nv_g_cmd(cmdarg_T *cap) cap->oap->inclusive = true; curwin->w_curswant = MAXCOL; if (cursor_down(cap->count1 - 1, - cap->oap->op_type == OP_NOP) == false) + cap->oap->op_type == OP_NOP) == false) { clearopbeep(cap->oap); - else { - char_u *ptr = get_cursor_line_ptr(); + } else { + char_u *ptr = get_cursor_line_ptr(); - /* In Visual mode we may end up after the line. */ - if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL) + // In Visual mode we may end up after the line. + if (curwin->w_cursor.col > 0 && ptr[curwin->w_cursor.col] == NUL) { --curwin->w_cursor.col; + } - /* Decrease the cursor column until it's on a non-blank. */ + // Decrease the cursor column until it's on a non-blank. while (curwin->w_cursor.col > 0 - && ascii_iswhite(ptr[curwin->w_cursor.col])) + && ascii_iswhite(ptr[curwin->w_cursor.col])) { --curwin->w_cursor.col; + } curwin->w_set_curswant = true; adjust_for_sel(cap); } @@ -6845,57 +7098,58 @@ static void nv_g_cmd(cmdarg_T *cap) case '$': case K_END: - case K_KEND: - { - int col_off = curwin_col_off(); + case K_KEND: { + int col_off = curwin_col_off(); - oap->motion_type = kMTCharWise; - oap->inclusive = true; - if (curwin->w_p_wrap - && curwin->w_width_inner != 0 - ) { - 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(); + oap->motion_type = kMTCharWise; + oap->inclusive = true; + if (curwin->w_p_wrap + && curwin->w_width_inner != 0) { + 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(); - validate_virtcol(); - i = width1 - 1; - if (curwin->w_virtcol >= (colnr_T)width1) - i += ((curwin->w_virtcol - width1) / width2 + 1) - * width2; + validate_virtcol(); + i = width1 - 1; + if (curwin->w_virtcol >= (colnr_T)width1) { + i += ((curwin->w_virtcol - width1) / width2 + 1) + * width2; + } + coladvance((colnr_T)i); + + // Make sure we stick in this column. + validate_virtcol(); + curwin->w_curswant = curwin->w_virtcol; + curwin->w_set_curswant = false; + if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) { + /* + * Check for landing on a character that got split at + * the end of the line. We do not want to advance to + * the next screen line. + */ + if (curwin->w_virtcol > (colnr_T)i) { + --curwin->w_cursor.col; + } + } + } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) { + clearopbeep(oap); + } + } else { + if (cap->count1 > 1) { + // if it fails, let the cursor still move to the last char + (void)cursor_down(cap->count1 - 1, false); + } + i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1; coladvance((colnr_T)i); - /* Make sure we stick in this column. */ + // Make sure we stick in this column. validate_virtcol(); curwin->w_curswant = curwin->w_virtcol; curwin->w_set_curswant = false; - if (curwin->w_cursor.col > 0 && curwin->w_p_wrap) { - /* - * Check for landing on a character that got split at - * the end of the line. We do not want to advance to - * the next screen line. - */ - if (curwin->w_virtcol > (colnr_T)i) - --curwin->w_cursor.col; - } - } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) - clearopbeep(oap); - } else { - if (cap->count1 > 1) { - // if it fails, let the cursor still move to the last char - (void)cursor_down(cap->count1 - 1, false); } - i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1; - coladvance((colnr_T)i); - - // Make sure we stick in this column. - validate_virtcol(); - curwin->w_curswant = curwin->w_virtcol; - curwin->w_set_curswant = false; } - } - break; + break; /* * "g*" and "g#", like "*" and "#" but without using "\<" and "\>" @@ -6903,10 +7157,10 @@ static void nv_g_cmd(cmdarg_T *cap) case '*': case '#': #if POUND != '#' - case POUND: /* pound sign (sometimes equal to '#') */ + case POUND: // pound sign (sometimes equal to '#') #endif - case Ctrl_RSB: /* :tag or :tselect for current identifier */ - case ']': /* :tselect for current identifier */ + case Ctrl_RSB: // :tag or :tselect for current identifier + case ']': // :tselect for current identifier nv_ident(cap); break; @@ -6918,8 +7172,9 @@ static void nv_g_cmd(cmdarg_T *cap) oap->motion_type = kMTCharWise; curwin->w_set_curswant = true; oap->inclusive = true; - if (bckend_word(cap->count1, cap->nchar == 'E', false) == false) + if (bckend_word(cap->count1, cap->nchar == 'E', false) == false) { clearopbeep(oap); + } break; // "g CTRL-G": display info about cursor position @@ -6934,8 +7189,9 @@ static void nv_g_cmd(cmdarg_T *cap) check_cursor_lnum(); i = (int)STRLEN(get_cursor_line_ptr()); if (curwin->w_cursor.col > (colnr_T)i) { - if (virtual_active()) + if (virtual_active()) { curwin->w_cursor.coladd += curwin->w_cursor.col - i; + } curwin->w_cursor.col = i; } } @@ -6948,8 +7204,9 @@ static void nv_g_cmd(cmdarg_T *cap) */ case 'I': beginline(0); - if (!checkclearopq(oap)) + if (!checkclearopq(oap)) { invoke_edit(cap, false, 'g', false); + } break; /* @@ -6961,7 +7218,7 @@ static void nv_g_cmd(cmdarg_T *cap) nv_gotofile(cap); break; - /* "g'm" and "g`m": jump to mark without setting pcmark */ + // "g'm" and "g`m": jump to mark without setting pcmark case '\'': cap->arg = true; FALLTHROUGH; @@ -6978,7 +7235,7 @@ static void nv_g_cmd(cmdarg_T *cap) /* * "ga": Display the ascii value of the character under the - * cursor. It is displayed in decimal, hex, and octal. -- webb + * cursor. It is displayed in decimal, hex, and octal. -- webb */ case 'a': do_ascii(NULL); @@ -6986,14 +7243,15 @@ static void nv_g_cmd(cmdarg_T *cap) /* * "g8": Display the bytes used for the UTF-8 character under the - * cursor. It is displayed in hex. + * cursor. It is displayed in hex. * "8g8" finds illegal byte sequence. */ case '8': - if (cap->count0 == 8) + if (cap->count0 == 8) { utf_find_illegal(); - else + } else { show_utf8(); + } break; // "g<": show scrollback text case '<': @@ -7010,14 +7268,14 @@ static void nv_g_cmd(cmdarg_T *cap) break; /* - * Two-character operators: - * "gq" Format text - * "gw" Format text and keep cursor position - * "g~" Toggle the case of the text. - * "gu" Change text to lower case. - * "gU" Change text to upper case. - * "g?" rot13 encoding - * "g@" call 'operatorfunc' + * Two-character operators: + * "gq" Format text + * "gw" Format text and keep cursor position + * "g~" Toggle the case of the text. + * "gu" Change text to lower case. + * "gU" Change text to upper case. + * "g?" rot13 encoding + * "g@" call 'operatorfunc' */ case 'q': case 'w': @@ -7033,7 +7291,7 @@ static void nv_g_cmd(cmdarg_T *cap) /* * "gd": Find first occurrence of pattern under the cursor in the - * current function + * current function * "gD": idem, but in the current file. */ case 'd': @@ -7075,12 +7333,12 @@ static void nv_g_cmd(cmdarg_T *cap) nv_put(cap); break; - /* "go": goto byte count from start of buffer */ + // "go": goto byte count from start of buffer case 'o': goto_byte(cap->count0); break; - /* "gQ": improved Ex mode */ + // "gQ": improved Ex mode case 'Q': if (text_locked()) { clearopbeep(cap->oap); @@ -7088,8 +7346,9 @@ static void nv_g_cmd(cmdarg_T *cap) break; } - if (!checkclearopq(oap)) - do_exmode(true); + if (!checkclearopq(oap)) { + do_exmode(); + } break; case ',': @@ -7102,12 +7361,14 @@ static void nv_g_cmd(cmdarg_T *cap) break; case 't': - if (!checkclearop(oap)) + if (!checkclearop(oap)) { goto_tabpage((int)cap->count0); + } break; case 'T': - if (!checkclearop(oap)) + if (!checkclearop(oap)) { goto_tabpage(-(int)cap->count1); + } break; case TAB: if (!checkclearop(oap)) { @@ -7116,10 +7377,11 @@ static void nv_g_cmd(cmdarg_T *cap) break; case '+': - case '-': /* "g+" and "g-": undo or redo along the timeline */ - if (!checkclearopq(oap)) + case '-': // "g+" and "g-": undo or redo along the timeline + if (!checkclearopq(oap)) { undo_time(cap->nchar == '-' ? -cap->count1 : cap->count1, - false, false, false); + false, false, false); + } break; default: @@ -7134,19 +7396,20 @@ static void nv_g_cmd(cmdarg_T *cap) static void n_opencmd(cmdarg_T *cap) { if (!checkclearopq(cap->oap)) { - if (cap->cmdchar == 'O') - /* Open above the first line of a folded sequence of lines */ + if (cap->cmdchar == 'O') { + // Open above the first line of a folded sequence of lines (void)hasFolding(curwin->w_cursor.lnum, - &curwin->w_cursor.lnum, NULL); - else - /* Open below the last line of a folded sequence of lines */ + &curwin->w_cursor.lnum, NULL); + } else { + // Open below the last line of a folded sequence of lines (void)hasFolding(curwin->w_cursor.lnum, - NULL, &curwin->w_cursor.lnum); + NULL, &curwin->w_cursor.lnum); + } if (u_save((linenr_T)(curwin->w_cursor.lnum - (cap->cmdchar == 'O' ? 1 : 0)), - (linenr_T)(curwin->w_cursor.lnum + - (cap->cmdchar == 'o' ? 1 : 0)) - ) + (linenr_T)(curwin->w_cursor.lnum + + (cap->cmdchar == 'o' ? 1 : 0)) + ) && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD, has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0, @@ -7171,8 +7434,9 @@ static void nv_dot(cmdarg_T *cap) * instead of the last command (inserting text). This is used for * CTRL-O <.> in insert mode. */ - if (start_redo(cap->count0, restart_edit != 0 && !arrow_used) == false) + if (start_redo(cap->count0, restart_edit != 0 && !arrow_used) == false) { clearopbeep(cap->oap); + } } } @@ -7192,11 +7456,10 @@ static void nv_redo(cmdarg_T *cap) */ static void nv_Undo(cmdarg_T *cap) { - /* In Visual mode and typing "gUU" triggers an operator */ + // In Visual mode and typing "gUU" triggers an operator if (cap->oap->op_type == OP_UPPER - || VIsual_active - ) { - /* translate "gUU" to "gUgU" */ + || VIsual_active) { + // translate "gUU" to "gUgU" cap->cmdchar = 'g'; cap->nchar = 'U'; nv_operator(cap); @@ -7241,9 +7504,9 @@ static void nv_operator(cmdarg_T *cap) return; } - if (op_type == cap->oap->op_type) /* double operator works on lines */ + if (op_type == cap->oap->op_type) { // double operator works on lines nv_lineop(cap); - else if (!checkclearop(cap->oap)) { + } else if (!checkclearop(cap->oap)) { cap->oap->start = curwin->w_cursor; cap->oap->op_type = op_type; set_op_var(op_type); @@ -7261,11 +7524,11 @@ static void set_op_var(int optype) char opchars[3]; int opchar0 = get_op_char(optype); assert(opchar0 >= 0 && opchar0 <= UCHAR_MAX); - opchars[0] = (char) opchar0; + opchars[0] = (char)opchar0; int opchar1 = get_extra_op_char(optype); assert(opchar1 >= 0 && opchar1 <= UCHAR_MAX); - opchars[1] = (char) opchar1; + opchars[1] = (char)opchar1; opchars[2] = NUL; set_vim_var_string(VV_OP, opchars, -1); @@ -7303,10 +7566,10 @@ static void nv_lineop(cmdarg_T *cap) */ static void nv_home(cmdarg_T *cap) { - /* CTRL-HOME is like "gg" */ - if (mod_mask & MOD_MASK_CTRL) + // CTRL-HOME is like "gg" + if (mod_mask & MOD_MASK_CTRL) { nv_goto(cap); - else { + } else { cap->count0 = 1; nv_pipe(cap); } @@ -7325,8 +7588,9 @@ static void nv_pipe(cmdarg_T *cap) if (cap->count0 > 0) { coladvance((colnr_T)(cap->count0 - 1)); curwin->w_curswant = (colnr_T)(cap->count0 - 1); - } else + } else { curwin->w_curswant = 0; + } /* keep curswant at the column where we wanted to go, not where * we ended; differs if line is too short */ curwin->w_set_curswant = false; @@ -7341,10 +7605,11 @@ static void nv_bck_word(cmdarg_T *cap) cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; curwin->w_set_curswant = true; - if (bck_word(cap->count1, cap->arg, false) == false) + if (bck_word(cap->count1, cap->arg, false) == false) { clearopbeep(cap->oap); - else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) + } else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } /* @@ -7361,10 +7626,11 @@ static void nv_wordcmd(cmdarg_T *cap) /* * Set inclusive for the "E" and "e" command. */ - if (cap->cmdchar == 'e' || cap->cmdchar == 'E') + if (cap->cmdchar == 'e' || cap->cmdchar == 'E') { word_end = true; - else + } else { word_end = false; + } cap->oap->inclusive = word_end; /* @@ -7391,22 +7657,25 @@ static void nv_wordcmd(cmdarg_T *cap) cap->oap->motion_type = kMTCharWise; curwin->w_set_curswant = true; - if (word_end) + if (word_end) { n = end_word(cap->count1, cap->arg, flag, false); - else + } else { n = fwd_word(cap->count1, cap->arg, cap->oap->op_type != OP_NOP); + } /* Don't leave the cursor on the NUL past the end of line. Unless we * didn't move it forward. */ - if (lt(startpos, curwin->w_cursor)) + if (lt(startpos, curwin->w_cursor)) { adjust_cursor(cap->oap); + } - if (n == false && cap->oap->op_type == OP_NOP) + if (n == false && cap->oap->op_type == OP_NOP) { clearopbeep(cap->oap); - else { + } else { adjust_for_sel(cap); - if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) + if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } } @@ -7424,8 +7693,8 @@ static void adjust_cursor(oparg_T *oap) */ if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL && (!VIsual_active || *p_sel == 'o') - && !virtual_active() && (ve_flags & VE_ONEMORE) == 0 - ) { + && !virtual_active() && + (ve_flags & VE_ONEMORE) == 0) { curwin->w_cursor.col--; // prevent cursor from moving on the trail byte mb_adjust_cursor(); @@ -7442,8 +7711,9 @@ static void nv_beginline(cmdarg_T *cap) cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; beginline(cap->arg); - if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) + if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } ins_at_eol = false; /* Don't move cursor past eol (only necessary in a one-character line). */ } @@ -7467,13 +7737,14 @@ static void adjust_for_sel(cmdarg_T *cap) */ static bool unadjust_for_sel(void) { - pos_T *pp; + pos_T *pp; if (*p_sel == 'e' && !equalpos(VIsual, curwin->w_cursor)) { - if (lt(VIsual, curwin->w_cursor)) + if (lt(VIsual, curwin->w_cursor)) { pp = &curwin->w_cursor; - else + } else { pp = &VIsual; + } if (pp->coladd > 0) { pp->coladd--; } else if (pp->col > 0) { @@ -7493,10 +7764,10 @@ static bool unadjust_for_sel(void) */ static void nv_select(cmdarg_T *cap) { - if (VIsual_active) + if (VIsual_active) { VIsual_select = true; - else if (VIsual_reselect) { - cap->nchar = 'v'; /* fake "gv" command */ + } else if (VIsual_reselect) { + cap->nchar = 'v'; // fake "gv" command cap->arg = true; nv_g_cmd(cap); } @@ -7511,24 +7782,28 @@ static void nv_goto(cmdarg_T *cap) { linenr_T lnum; - if (cap->arg) + if (cap->arg) { lnum = curbuf->b_ml.ml_line_count; - else + } else { lnum = 1L; + } cap->oap->motion_type = kMTLineWise; setpcmark(); - /* When a count is given, use it instead of the default lnum */ - if (cap->count0 != 0) + // When a count is given, use it instead of the default lnum + if (cap->count0 != 0) { lnum = cap->count0; - if (lnum < 1L) + } + if (lnum < 1L) { lnum = 1L; - else if (lnum > curbuf->b_ml.ml_line_count) + } else if (lnum > curbuf->b_ml.ml_line_count) { lnum = curbuf->b_ml.ml_line_count; + } curwin->w_cursor.lnum = lnum; beginline(BL_SOL | BL_FIX); - if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP) + if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP) { foldOpenCursor(); + } } /* @@ -7538,20 +7813,24 @@ static void nv_normal(cmdarg_T *cap) { if (cap->nchar == Ctrl_N || cap->nchar == Ctrl_G) { clearop(cap->oap); - if (restart_edit != 0 && mode_displayed) - clear_cmdline = true; /* unshow mode later */ + if (restart_edit != 0 && mode_displayed) { + clear_cmdline = true; // unshow mode later + } restart_edit = 0; - if (cmdwin_type != 0) + if (cmdwin_type != 0) { cmdwin_result = Ctrl_C; + } if (VIsual_active) { - end_visual_mode(); /* stop Visual */ + end_visual_mode(); // stop Visual redraw_curbuf_later(INVERTED); } - /* CTRL-\ CTRL-G restarts Insert mode when 'insertmode' is set. */ - if (cap->nchar == Ctrl_G && p_im) + // CTRL-\ CTRL-G restarts Insert mode when 'insertmode' is set. + if (cap->nchar == Ctrl_G && p_im) { restart_edit = 'a'; - } else + } + } else { clearopbeep(cap->oap); + } } /* @@ -7568,7 +7847,7 @@ static void nv_esc(cmdarg_T *cap) && cap->oap->regname == 0 && !p_im); - if (cap->arg) { /* true for CTRL-C */ + if (cap->arg) { // true for CTRL-C if (restart_edit == 0 && cmdwin_type == 0 && !VIsual_active @@ -7583,11 +7862,12 @@ static void nv_esc(cmdarg_T *cap) /* Don't reset "restart_edit" when 'insertmode' is set, it won't be * set again below when halfway through a mapping. */ - if (!p_im) + if (!p_im) { restart_edit = 0; + } if (cmdwin_type != 0) { cmdwin_result = K_IGNORE; - got_int = false; /* don't stop executing autocommands et al. */ + got_int = false; // don't stop executing autocommands et al. return; } } else if (cmdwin_type != 0 && ex_normal_busy) { @@ -7599,8 +7879,8 @@ 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 */ + end_visual_mode(); // stop Visual + check_cursor_col(); // make sure cursor is not beyond EOL curwin->w_set_curswant = true; redraw_curbuf_later(INVERTED); } else if (no_reason) { @@ -7611,9 +7891,9 @@ static void nv_esc(cmdarg_T *cap) /* A CTRL-C is often used at the start of a menu. When 'insertmode' is * set return to Insert mode afterwards. */ if (restart_edit == 0 && goto_im() - && ex_normal_busy == 0 - ) + && ex_normal_busy == 0) { restart_edit = 'a'; + } } // Move the cursor for the "A" command. @@ -7644,7 +7924,7 @@ static void nv_edit(cmdarg_T *cap) // in Visual mode "A" and "I" are an operator if (VIsual_active && (cap->cmdchar == 'A' || cap->cmdchar == 'I')) { v_visop(cap); - // in Visual mode and after an operator "a" and "i" are for text objects + // in Visual mode and after an operator "a" and "i" are for text objects } else if ((cap->cmdchar == 'a' || cap->cmdchar == 'i') && (cap->oap->op_type != OP_NOP || VIsual_active)) { nv_object(cap); @@ -7658,20 +7938,21 @@ static void nv_edit(cmdarg_T *cap) set_cursor_for_append_to_line(); break; - case 'I': /* "I"nsert before the first non-blank */ + case 'I': // "I"nsert before the first non-blank beginline(BL_WHITE); break; - case 'a': /* "a"ppend is like "i"nsert on the next character. */ + 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() && (curwin->w_cursor.coladd > 0 || *get_cursor_pos_ptr() == NUL - || *get_cursor_pos_ptr() == TAB)) + || *get_cursor_pos_ptr() == TAB)) { curwin->w_cursor.coladd++; - else if (*get_cursor_pos_ptr() != NUL) + } else if (*get_cursor_pos_ptr() != NUL) { inc_cursor(); + } break; } @@ -7689,35 +7970,32 @@ static void nv_edit(cmdarg_T *cap) } } -/* - * Invoke edit() and take care of "restart_edit" and the return value. - */ -static void -invoke_edit ( - cmdarg_T *cap, - int repl, /* "r" or "gr" command */ - int cmd, - int startln -) +/// Invoke edit() and take care of "restart_edit" and the return value. +/// +/// @param repl "r" or "gr" command +static void invoke_edit(cmdarg_T *cap, int repl, int cmd, int startln) { int restart_edit_save = 0; /* Complicated: When the user types "a<C-O>a" we don't want to do Insert * mode recursively. But when doing "a<C-O>." or "a<C-O>rx" we do allow * it. */ - if (repl || !stuff_empty()) + if (repl || !stuff_empty()) { restart_edit_save = restart_edit; - else + } else { restart_edit_save = 0; + } - /* Always reset "restart_edit", this is not a restarted edit. */ + // Always reset "restart_edit", this is not a restarted edit. restart_edit = 0; - if (edit(cmd, startln, cap->count1)) + if (edit(cmd, startln, cap->count1)) { cap->retval |= CA_COMMAND_BUSY; + } - if (restart_edit == 0) + if (restart_edit == 0) { restart_edit = restart_edit_save; + } } /* @@ -7727,43 +8005,43 @@ static void nv_object(cmdarg_T *cap) { bool flag; bool include; - char_u *mps_save; + char_u *mps_save; - if (cap->cmdchar == 'i') - include = false; /* "ix" = inner object: exclude white space */ - else - include = true; /* "ax" = an object: include white space */ - - /* Make sure (), [], {} and <> are in 'matchpairs' */ + if (cap->cmdchar == 'i') { + include = false; // "ix" = inner object: exclude white space + } else { + include = true; // "ax" = an object: include white space + } + // Make sure (), [], {} and <> are in 'matchpairs' mps_save = curbuf->b_p_mps; curbuf->b_p_mps = (char_u *)"(:),{:},[:],<:>"; switch (cap->nchar) { - case 'w': /* "aw" = a word */ + case 'w': // "aw" = a word flag = current_word(cap->oap, cap->count1, include, false); break; - case 'W': /* "aW" = a WORD */ + case 'W': // "aW" = a WORD flag = current_word(cap->oap, cap->count1, include, true); break; - case 'b': /* "ab" = a braces block */ + case 'b': // "ab" = a braces block case '(': case ')': flag = current_block(cap->oap, cap->count1, include, '(', ')'); break; - case 'B': /* "aB" = a Brackets block */ + case 'B': // "aB" = a Brackets block case '{': case '}': flag = current_block(cap->oap, cap->count1, include, '{', '}'); break; - case '[': /* "a[" = a [] block */ + case '[': // "a[" = a [] block case ']': flag = current_block(cap->oap, cap->count1, include, '[', ']'); break; - case '<': /* "a<" = a <> block */ + case '<': // "a<" = a <> block case '>': flag = current_block(cap->oap, cap->count1, include, '<', '>'); break; - case 't': /* "at" = a tag block (xml and html) */ + case 't': // "at" = a tag block (xml and html) // Do not adjust oap->end in do_pending_operator() // otherwise there are different results for 'dit' // (note leading whitespace in last line): @@ -7773,17 +8051,17 @@ static void nv_object(cmdarg_T *cap) cap->retval |= CA_NO_ADJ_OP_END; flag = current_tagblock(cap->oap, cap->count1, include); break; - case 'p': /* "ap" = a paragraph */ + case 'p': // "ap" = a paragraph flag = current_par(cap->oap, cap->count1, include, 'p'); break; - case 's': /* "as" = a sentence */ + case 's': // "as" = a sentence flag = current_sent(cap->oap, cap->count1, include); break; - case '"': /* "a"" = a double quoted string */ - case '\'': /* "a'" = a single quoted string */ - case '`': /* "a`" = a backtick quoted string */ + case '"': // "a"" = a double quoted string + case '\'': // "a'" = a single quoted string + case '`': // "a`" = a backtick quoted string flag = current_quote(cap->oap, cap->count1, include, - cap->nchar); + cap->nchar); break; default: flag = false; @@ -7791,8 +8069,9 @@ static void nv_object(cmdarg_T *cap) } curbuf->b_p_mps = mps_save; - if (!flag) + if (!flag) { clearopbeep(cap->oap); + } adjust_cursor_col(); curwin->w_set_curswant = true; } @@ -7804,7 +8083,7 @@ static void nv_object(cmdarg_T *cap) static void nv_record(cmdarg_T *cap) { if (cap->oap->op_type == OP_FORMAT) { - /* "gqq" is the same as "gqgq": format line */ + // "gqq" is the same as "gqgq": format line cap->cmdchar = 'g'; cap->nchar = 'q'; nv_operator(cap); @@ -7827,11 +8106,13 @@ static void nv_record(cmdarg_T *cap) */ static void nv_at(cmdarg_T *cap) { - if (checkclearop(cap->oap)) + if (checkclearop(cap->oap)) { return; + } if (cap->nchar == '=') { - if (get_expr_register() == NUL) + if (get_expr_register() == NUL) { return; + } } while (cap->count1-- && !got_int) { if (do_execreg(cap->nchar, false, false, false) == false) { @@ -7849,10 +8130,11 @@ 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)) + && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)) { clearopbeep(cap->oap); - else if (!checkclearop(cap->oap)) + } else if (!checkclearop(cap->oap)) { halfpage(cap->cmdchar == Ctrl_D, cap->count0); + } } /* @@ -7902,7 +8184,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) int flags = 0; if (cap->oap->op_type != OP_NOP) { - /* "dp" is ":diffput" */ + // "dp" is ":diffput" if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'p') { clearop(cap->oap); assert(cap->opcount >= 0); @@ -7939,7 +8221,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) regname = cap->oap->regname; // '+' and '*' could be the same selection bool clipoverwrite = (regname == '+' || regname == '*') - && (cb_flags & CB_UNNAMEDMASK); + && (cb_flags & CB_UNNAMEDMASK); if (regname == 0 || regname == '"' || clipoverwrite || ascii_isdigit(regname) || regname == '-') { // The delete might overwrite the register we want to put, save it first @@ -7980,11 +8262,12 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) if ((VIsual_mode != 'V' && curwin->w_cursor.col < curbuf->b_op_start.col) || (VIsual_mode == 'V' - && curwin->w_cursor.lnum < curbuf->b_op_start.lnum)) + && curwin->w_cursor.lnum < curbuf->b_op_start.lnum)) { /* cursor is at the end of the line or end of file, put * forward. */ dir = FORWARD; - /* May have been reset in do_put(). */ + } + // May have been reset in do_put(). VIsual_active = true; } do_put(cap->oap->regname, savereg, dir, cap->count1, flags); @@ -8028,7 +8311,7 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) */ static void nv_open(cmdarg_T *cap) { - /* "do" is ":diffget" */ + // "do" is ":diffget" if (cap->oap->op_type == OP_DELETE && cap->cmdchar == 'o') { clearop(cap->oap); assert(cap->opcount >= 0); @@ -8043,12 +8326,10 @@ static void nv_open(cmdarg_T *cap) } } -// Calculate start/end virtual columns for operating in block mode. -static void get_op_vcol( - oparg_T *oap, - colnr_T redo_VIsual_vcol, - bool initial // when true: adjust position for 'selectmode' -) +/// Calculate start/end virtual columns for operating in block mode. +/// +/// @param initial when true: adjust position for 'selectmode' +static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) { colnr_T start; colnr_T end; @@ -8133,10 +8414,8 @@ static void nv_event(cmdarg_T *cap) } } -/* - * Return TRUE when 'mousemodel' is set to "popup" or "popup_setpos". - */ -static int mouse_model_popup(void) +/// @return true when 'mousemodel' is set to "popup" or "popup_setpos". +static bool mouse_model_popup(void) { return p_mousem[0] == 'p'; } diff --git a/src/nvim/normal.h b/src/nvim/normal.h index 51170105ed..8e15e909d4 100644 --- a/src/nvim/normal.h +++ b/src/nvim/normal.h @@ -48,6 +48,8 @@ typedef struct oparg_S { colnr_T end_vcol; // end col for block mode operator long prev_opcount; // ca.opcount saved for K_EVENT long prev_count0; // ca.count0 saved for K_EVENT + bool excl_tr_ws; // exclude trailing whitespace for yank of a + // block } oparg_T; /* diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 2c8c7f0567..77432149ce 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -11,27 +11,27 @@ #include <stdbool.h> #include <string.h> -#include "nvim/vim.h" #include "nvim/ascii.h" -#include "nvim/ops.h" +#include "nvim/assert.h" #include "nvim/buffer.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cursor.h" -#include "nvim/assert.h" #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" +#include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" #include "nvim/indent.h" +#include "nvim/lib/kvec.h" #include "nvim/log.h" +#include "nvim/macros.h" #include "nvim/mark.h" -#include "nvim/extmark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -39,8 +39,12 @@ #include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" +#include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/time.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/state.h" @@ -48,15 +52,12 @@ #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/undo.h" -#include "nvim/macros.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/lib/kvec.h" -#include "nvim/os/input.h" -#include "nvim/os/time.h" static yankreg_T y_regs[NUM_REGISTERS]; -static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */ +static yankreg_T *y_previous = NULL; // ptr to last written yankreg // for behavior between start_batch_changes() and end_batch_changes()) static int batch_change_count = 0; // inside a script @@ -69,20 +70,20 @@ static bool clipboard_didwarn = false; * also op_change, op_shift, op_insert, op_replace - AKelly */ struct block_def { - int startspaces; /* 'extra' cols before first char */ - int endspaces; /* 'extra' cols after last char */ - int textlen; /* chars in block */ - char_u *textstart; /* pointer to 1st char (partially) in block */ - colnr_T textcol; /* index of chars (partially) in block */ - colnr_T start_vcol; /* start col of 1st char wholly inside block */ - colnr_T end_vcol; /* start col of 1st char wholly after block */ - int is_short; /* TRUE if line is too short to fit in block */ - int is_MAX; /* TRUE if curswant==MAXCOL when starting */ - int is_oneChar; /* TRUE if block within one character */ - int pre_whitesp; /* screen cols of ws before block */ - int pre_whitesp_c; /* chars of ws before block */ - colnr_T end_char_vcols; /* number of vcols of post-block char */ - colnr_T start_char_vcols; /* number of vcols of pre-block char */ + int startspaces; // 'extra' cols before first char + int endspaces; // 'extra' cols after last char + int textlen; // chars in block + char_u *textstart; // pointer to 1st char (partially) in block + colnr_T textcol; // index of chars (partially) in block + colnr_T start_vcol; // start col of 1st char wholly inside block + colnr_T end_vcol; // start col of 1st char wholly after block + int is_short; // TRUE if line is too short to fit in block + int is_MAX; // TRUE if curswant==MAXCOL when starting + int is_oneChar; // TRUE if block within one character + int pre_whitesp; // screen cols of ws before block + int pre_whitesp_c; // chars of ws before block + colnr_T end_char_vcols; // number of vcols of post-block char + colnr_T start_char_vcols; // number of vcols of pre-block char }; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -156,6 +157,9 @@ int get_op_type(int char1, int char2) // subtract return OP_NR_SUB; } + if (char1 == 'z' && char2 == 'y') { // OP_YANK + return OP_YANK; + } for (i = 0;; i++) { if (opchars[i][0] == char1 && opchars[i][1] == char2) { break; @@ -179,7 +183,7 @@ int op_on_lines(int op) // Return TRUE if operator "op" changes text. int op_is_change(int op) { - return opchars[op][2] & OPF_CHANGE; + return opchars[op][2] & OPF_CHANGE; } /* @@ -206,12 +210,13 @@ void op_shift(oparg_T *oap, int curs_top, int amount) { long i; int first_char; - char_u *s; + char_u *s; int block_col = 0; if (u_save((linenr_T)(oap->start.lnum - 1), - (linenr_T)(oap->end.lnum + 1)) == FAIL) + (linenr_T)(oap->end.lnum + 1)) == FAIL) { return; + } if (oap->motion_type == kMTBlockWise) { block_col = curwin->w_cursor.col; @@ -234,32 +239,35 @@ void op_shift(oparg_T *oap, int curs_top, int amount) if (oap->motion_type == kMTBlockWise) { curwin->w_cursor.lnum = oap->start.lnum; curwin->w_cursor.col = block_col; - } else if (curs_top) { /* put cursor on first line, for ">>" */ + } else if (curs_top) { // put cursor on first line, for ">>" curwin->w_cursor.lnum = oap->start.lnum; - beginline(BL_SOL | BL_FIX); /* shift_line() may have set cursor.col */ - } else - --curwin->w_cursor.lnum; /* put cursor on last line, for ":>" */ - + beginline(BL_SOL | BL_FIX); // shift_line() may have set cursor.col + } else { + --curwin->w_cursor.lnum; // put cursor on last line, for ":>" + } // The cursor line is not in a closed fold foldOpenCursor(); if (oap->line_count > p_report) { - if (oap->op_type == OP_RSHIFT) + if (oap->op_type == OP_RSHIFT) { s = (char_u *)">"; - else + } else { s = (char_u *)"<"; + } if (oap->line_count == 1) { - if (amount == 1) + if (amount == 1) { sprintf((char *)IObuff, _("1 line %sed 1 time"), s); - else + } else { sprintf((char *)IObuff, _("1 line %sed %d times"), s, amount); + } } else { - if (amount == 1) + if (amount == 1) { sprintf((char *)IObuff, _("%" PRId64 " lines %sed 1 time"), - (int64_t)oap->line_count, s); - else + (int64_t)oap->line_count, s); + } else { sprintf((char *)IObuff, _("%" PRId64 " lines %sed %d times"), - (int64_t)oap->line_count, s, amount); + (int64_t)oap->line_count, s, amount); + } } msg_attr_keep(IObuff, 0, true, false); } @@ -277,14 +285,11 @@ void op_shift(oparg_T *oap, int curs_top, int amount) changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true); } -// Shift the current line one shiftwidth left (if left != 0) or right -// leaves cursor on first blank in the line. -void shift_line( - int left, - int round, - int amount, - int call_changed_bytes // call changed_bytes() -) +/// Shift the current line one shiftwidth left (if left != 0) or right +/// leaves cursor on first blank in the line. +/// +/// @param call_changed_bytes call changed_bytes() +void shift_line(int left, int round, int amount, int call_changed_bytes) { int count; int i, j; @@ -300,18 +305,22 @@ void shift_line( } if (left) { i -= amount; - if (i < 0) + if (i < 0) { i = 0; - } else + } + } else { i += amount; + } count = i * p_sw; } else { // original vi indent if (left) { count -= p_sw * amount; - if (count < 0) + if (count < 0) { count = 0; - } else + } + } else { count += p_sw * amount; + } } // Set new indent @@ -340,7 +349,7 @@ static void shift_block(oparg_T *oap, int amount) int i = 0, j = 0; const int old_p_ri = p_ri; - p_ri = 0; /* don't want revins in indent */ + p_ri = 0; // don't want revins in indent State = INSERT; // don't want REPLACE for State block_prep(oap, &bd, curwin->w_cursor.lnum, true); @@ -404,7 +413,7 @@ static void shift_block(oparg_T *oap, int amount) newlen = i+j; memset(newp + bd.textcol, TAB, (size_t)i); memset(newp + bd.textcol + i, ' ', (size_t)j); - /* the end */ + // the end memmove(newp + bd.textcol + i + j, bd.textstart, (size_t)len); } else { // left colnr_T destination_col; // column to which text in block will @@ -416,7 +425,7 @@ static void shift_block(oparg_T *oap, int amount) size_t fill; // nr of spaces that replace a TAB size_t new_line_len; // the length of the line after the // block shift - char_u *non_white = bd.textstart; + char_u *non_white = bd.textstart; /* * Firstly, let's find the first non-whitespace character that is @@ -458,15 +467,17 @@ static void shift_block(oparg_T *oap, int amount) /* If "bd.startspaces" is set, "bd.textstart" points to the character * preceding the block. We have to subtract its width to obtain its * column number. */ - if (bd.startspaces) + if (bd.startspaces) { verbatim_copy_width -= bd.start_char_vcols; + } while (verbatim_copy_width < destination_col) { char_u *line = verbatim_copy_end; // TODO: is passing verbatim_copy_end for start of the line OK? incr = lbr_chartabsize(line, verbatim_copy_end, verbatim_copy_width); - if (verbatim_copy_width + incr > destination_col) + if (verbatim_copy_width + incr > destination_col) { break; + } verbatim_copy_width += incr; MB_PTR_ADV(verbatim_copy_end); } @@ -515,7 +526,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def int spaces = 0; // non-zero if cutting a TAB colnr_T offset; // pointer along new line size_t s_len = STRLEN(s); - char_u *newp, *oldp; // new, old lines + char_u *newp, *oldp; // new, old lines linenr_T lnum; // loop var int oldstate = State; State = INSERT; // don't want REPLACE for State @@ -531,20 +542,23 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def if (b_insert) { p_ts = bdp->start_char_vcols; spaces = bdp->startspaces; - if (spaces != 0) - count = p_ts - 1; /* we're cutting a TAB */ + if (spaces != 0) { + count = p_ts - 1; // we're cutting a TAB + } offset = bdp->textcol; - } else { /* append */ + } else { // append p_ts = bdp->end_char_vcols; - if (!bdp->is_short) { /* spaces = padding after block */ + if (!bdp->is_short) { // spaces = padding after block spaces = (bdp->endspaces ? p_ts - bdp->endspaces : 0); - if (spaces != 0) - count = p_ts - 1; /* we're cutting a TAB */ + if (spaces != 0) { + count = p_ts - 1; // we're cutting a TAB + } offset = bdp->textcol + bdp->textlen - (spaces != 0); - } else { /* spaces = padding to block edge */ - /* if $ used, just append to EOL (ie spaces==0) */ - if (!bdp->is_MAX) + } else { // spaces = padding to block edge + // if $ used, just append to EOL (ie spaces==0) + if (!bdp->is_MAX) { spaces = (oap->end_vcol - bdp->end_vcol) + 1; + } count = spaces; offset = bdp->textcol + bdp->textlen; } @@ -590,8 +604,9 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def skipped = 1; } - if (spaces > 0) + if (spaces > 0) { offset += count; + } STRMOVE(newp + offset, oldp); ml_replace(lnum, newp, false); @@ -604,7 +619,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def curbuf->b_op_end.lnum = oap->end.lnum; curbuf->b_op_end.col = offset; } - } /* for all lnum */ + } // for all lnum changed_lines(oap->start.lnum + 1, 0, oap->end.lnum + 1, 0L, true); @@ -617,13 +632,13 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def void op_reindent(oparg_T *oap, Indenter how) { long i; - char_u *l; + char_u *l; int amount; linenr_T first_changed = 0; linenr_T last_changed = 0; linenr_T start_lnum = curwin->w_cursor.lnum; - /* Don't even try when 'modifiable' is off. */ + // Don't even try when 'modifiable' is off. if (!MODIFIABLE(curbuf)) { EMSG(_(e_modifiable)); return; @@ -635,8 +650,9 @@ void op_reindent(oparg_T *oap, Indenter how) if (i > 1 && (i % 50 == 0 || i == oap->line_count - 1) - && oap->line_count > p_report) + && oap->line_count > p_report) { smsg(_("%" PRId64 " lines to indent... "), (int64_t)i); + } /* * Be vi-compatible: For lisp indenting the first line is not @@ -645,11 +661,11 @@ void op_reindent(oparg_T *oap, Indenter how) if (i != oap->line_count - 1 || oap->line_count == 1 || how != get_lisp_indent) { l = skipwhite(get_cursor_line_ptr()); - if (*l == NUL) /* empty or blank line */ + if (*l == NUL) { // empty or blank line amount = 0; - else - amount = how(); /* get the indent for this line */ - + } else { + amount = how(); // get the indent for this line + } if (amount >= 0 && set_indent(amount, SIN_UNDO)) { // did change the indent, call changed_lines() later if (first_changed == 0) { @@ -659,10 +675,10 @@ void op_reindent(oparg_T *oap, Indenter how) } } ++curwin->w_cursor.lnum; - curwin->w_cursor.col = 0; /* make sure it's valid */ + curwin->w_cursor.col = 0; // make sure it's valid } - /* put cursor on first non-blank of indented line */ + // put cursor on first non-blank of indented line curwin->w_cursor.lnum = start_lnum; beginline(BL_SOL | BL_FIX); @@ -679,12 +695,13 @@ void op_reindent(oparg_T *oap, Indenter how) if (oap->line_count > p_report) { i = oap->line_count - (i + 1); - if (i == 1) + if (i == 1) { MSG(_("1 line indented ")); - else + } else { smsg(_("%" PRId64 " lines indented "), (int64_t)i); + } } - /* set '[ and '] marks */ + // set '[ and '] marks curbuf->b_op_start = oap->start; curbuf->b_op_end = oap->end; } @@ -692,7 +709,7 @@ void op_reindent(oparg_T *oap, Indenter how) /* * Keep the last expression line here, for repeating. */ -static char_u *expr_line = NULL; +static char_u *expr_line = NULL; /* * Get an expression for the "\"=expr1" or "CTRL-R =expr1" @@ -700,7 +717,7 @@ static char_u *expr_line = NULL; */ int get_expr_register(void) { - char_u *new_line; + char_u *new_line; new_line = getcmdline('=', 0L, 0, true); if (new_line == NULL) { @@ -730,12 +747,13 @@ void set_expr_line(char_u *new_line) */ char_u *get_expr_line(void) { - char_u *expr_copy; - char_u *rv; + char_u *expr_copy; + char_u *rv; static int nested = 0; - if (expr_line == NULL) + if (expr_line == NULL) { return NULL; + } /* Make a copy of the expression, because evaluating it may cause it to be * changed. */ @@ -743,8 +761,9 @@ char_u *get_expr_line(void) /* When we are invoked recursively limit the evaluation to 10 levels. * Then return the string as-is. */ - if (nested >= 10) + if (nested >= 10) { return expr_copy; + } ++nested; rv = eval_to_string(expr_copy, NULL, TRUE); @@ -758,8 +777,9 @@ char_u *get_expr_line(void) */ char_u *get_expr_line_src(void) { - if (expr_line == NULL) + if (expr_line == NULL) { return NULL; + } return vim_strsave(expr_line); } @@ -772,7 +792,7 @@ char_u *get_expr_line_src(void) bool valid_yank_reg(int regname, bool writing) { if ((regname > 0 && ASCII_ISALNUM(regname)) - || (!writing && vim_strchr((char_u *) "/.%:=" , regname) != NULL) + || (!writing && vim_strchr((char_u *)"/.%:=", regname) != NULL) || regname == '#' || regname == '"' || regname == '-' @@ -816,8 +836,8 @@ yankreg_T *get_yank_register(int regname, int mode) // reg is set to clipboard contents. return reg; } else if (mode != YREG_YANK - && (regname == 0 || regname == '"' || regname == '*' || regname == '+') - && y_previous != NULL) { + && (regname == 0 || regname == '"' || regname == '*' || regname == '+') + && y_previous != NULL) { // in case clipboard not available, paste from previous used register return y_previous; } @@ -890,9 +910,9 @@ bool yank_register_mline(int regname) */ int do_record(int c) { - char_u *p; + char_u *p; static int regname; - yankreg_T *old_y_previous; + yankreg_T *old_y_previous; int retval; if (reg_recording == 0) { @@ -906,7 +926,7 @@ int do_record(int c) regname = c; retval = OK; } - } else { /* stop recording */ + } else { // stop recording /* * Get the recorded key hits. K_SPECIAL and CSI will be escaped, this * needs to be removed again to put it in a register. exec_reg then @@ -919,10 +939,10 @@ int do_record(int c) MSG(""); } p = get_recorded(); - if (p == NULL) + if (p == NULL) { retval = FAIL; - else { - /* Remove escaping for CSI and K_SPECIAL in multi-byte chars. */ + } else { + // Remove escaping for CSI and K_SPECIAL in multi-byte chars. vim_unescape_csi(p); /* @@ -951,18 +971,18 @@ static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data) /* * Stuff string "p" into yank register "regname" as a single line (append if - * uppercase). "p" must have been alloced. + * uppercase). "p" must have been allocated. * * return FAIL for failure, OK otherwise */ static int stuff_yank(int regname, char_u *p) { - /* check for read-only register */ + // check for read-only register if (regname != 0 && !valid_yank_reg(regname, true)) { xfree(p); return FAIL; } - if (regname == '_') { /* black hole: don't do anything */ + if (regname == '_') { // black hole: don't do anything xfree(p); return OK; } @@ -992,36 +1012,35 @@ static int execreg_lastc = NUL; /// Execute a yank register: copy it into the stuff buffer /// -/// Return FAIL for failure, OK otherwise -int -do_execreg( - int regname, - int colon, /* insert ':' before each line */ - int addcr, /* always add '\n' to end of line */ - int silent /* set "silent" flag in typeahead buffer */ -) +/// @param colon insert ':' before each line +/// @param addcr always add '\n' to end of line +/// @param silent set "silent" flag in typeahead buffer +/// +/// @return FAIL for failure, OK otherwise +int do_execreg(int regname, int colon, int addcr, int silent) { char_u *p; int retval = OK; - if (regname == '@') { /* repeat previous one */ + if (regname == '@') { // repeat previous one if (execreg_lastc == NUL) { EMSG(_("E748: No previously used register")); return FAIL; } regname = execreg_lastc; } - /* check for valid regname */ + // check for valid regname if (regname == '%' || regname == '#' || !valid_yank_reg(regname, false)) { emsg_invreg(regname); return FAIL; } execreg_lastc = regname; - if (regname == '_') /* black hole: don't stuff anything */ + if (regname == '_') { // black hole: don't stuff anything return OK; + } - if (regname == ':') { /* use last command line */ + if (regname == ':') { // use last command line if (last_cmdline == NULL) { EMSG(_(e_nolastcmd)); return FAIL; @@ -1029,13 +1048,12 @@ do_execreg( // don't keep the cmdline containing @: XFREE_CLEAR(new_last_cmdline); // Escape all control characters with a CTRL-V - p = vim_strsave_escaped_ext( - last_cmdline, - (char_u *)"\001\002\003\004\005\006\007" - "\010\011\012\013\014\015\016\017" - "\020\021\022\023\024\025\026\027" - "\030\031\032\033\034\035\036\037", - Ctrl_V, false); + p = vim_strsave_escaped_ext(last_cmdline, + (char_u *)"\001\002\003\004\005\006\007" + "\010\011\012\013\014\015\016\017" + "\020\021\022\023\024\025\026\027" + "\030\031\032\033\034\035\036\037", + Ctrl_V, false); // When in Visual mode "'<,'>" will be prepended to the command. // Remove it when it's already there. if (VIsual_active && STRNCMP(p, "'<,'>", 5) == 0) { @@ -1046,11 +1064,12 @@ do_execreg( xfree(p); } else if (regname == '=') { p = get_expr_line(); - if (p == NULL) + if (p == NULL) { return FAIL; + } retval = put_in_typebuf(p, true, colon, silent); xfree(p); - } else if (regname == '.') { /* use last inserted text */ + } else if (regname == '.') { // use last inserted text p = get_last_insert_save(); if (p == NULL) { EMSG(_(e_noinstext)); @@ -1060,10 +1079,11 @@ do_execreg( xfree(p); } else { yankreg_T *reg = get_yank_register(regname, YREG_PASTE); - if (reg->y_array == NULL) + if (reg->y_array == NULL) { return FAIL; + } - // Disallow remaping for ":@r". + // Disallow remapping for ":@r". int remap = colon ? REMAP_NONE : REMAP_YES; /* @@ -1117,18 +1137,13 @@ static void put_reedit_in_typebuf(int silent) } } -/* - * Insert register contents "s" into the typeahead buffer, so that it will be - * executed again. - * When "esc" is TRUE it is to be taken literally: Escape CSI characters and - * no remapping. - */ -static int put_in_typebuf( - char_u *s, - bool esc, - bool colon, // add ':' before the line - int silent -) +/// Insert register contents "s" into the typeahead buffer, so that it will be +/// executed again. +/// +/// @param esc when true then it is to be taken literally: Escape CSI +/// characters and no remapping. +/// @param colon add ':' before the line +static int put_in_typebuf(char_u *s, bool esc, bool colon, int silent) { int retval = OK; @@ -1137,7 +1152,7 @@ static int put_in_typebuf( retval = ins_typebuf((char_u *)"\n", REMAP_NONE, 0, true, silent); } if (retval == OK) { - char_u *p; + char_u *p; if (esc) { p = vim_strsave_escape_csi(s); @@ -1159,16 +1174,13 @@ static int put_in_typebuf( return retval; } -/* - * Insert a yank register: copy it into the Read buffer. - * Used by CTRL-R command and middle mouse button in insert mode. - * - * return FAIL for failure, OK otherwise - */ -int insert_reg( - int regname, - bool literally_arg // insert literally, not as if typed -) +/// Insert a yank register: copy it into the Read buffer. +/// Used by CTRL-R command and middle mouse button in insert mode. +/// +/// @param literally_arg insert literally, not as if typed +/// +/// @return FAIL for failure, OK otherwise +int insert_reg(int regname, bool literally_arg) { int retval = OK; bool allocated; @@ -1180,12 +1192,14 @@ int insert_reg( * If you hit CTRL-C, the loop will be broken here. */ os_breakcheck(); - if (got_int) + if (got_int) { return FAIL; + } - /* check for valid regname */ - if (regname != NUL && !valid_yank_reg(regname, false)) + // check for valid regname + if (regname != NUL && !valid_yank_reg(regname, false)) { return FAIL; + } char_u *arg; if (regname == '.') { // Insert last inserted text. @@ -1242,7 +1256,7 @@ static void stuffescaped(const char *arg, int literally) stuffReadbuffLen(start, (long)(arg - start)); } - /* stuff a single special character */ + // stuff a single special character if (*arg != NUL) { const int c = mb_cptr2char_adv((const char_u **)&arg); if (literally && ((c < ' ' && c != TAB) || c == DEL)) { @@ -1253,23 +1267,24 @@ static void stuffescaped(const char *arg, int literally) } } -// If "regname" is a special register, return true and store a pointer to its -// value in "argp". -bool get_spec_reg( - int regname, - char_u **argp, - bool *allocated, // return: true when value was allocated - bool errmsg // give error message when failing -) +/// If "regname" is a special register, return true and store a pointer to its +/// value in "argp". +/// +/// @param allocated return: true when value was allocated +/// @param errmsg give error message when failing +/// +/// @return true if "regname" is a special register, +bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg) { size_t cnt; *argp = NULL; *allocated = false; switch (regname) { - case '%': /* file name */ - if (errmsg) - check_fname(); /* will give emsg if not set */ + case '%': // file name + if (errmsg) { + check_fname(); // will give emsg if not set + } *argp = curbuf->b_fname; return true; @@ -1277,24 +1292,26 @@ bool get_spec_reg( *argp = getaltfname(errmsg); // may give emsg if not set return true; - case '=': /* result of expression */ + case '=': // result of expression *argp = get_expr_line(); *allocated = true; return true; - case ':': /* last command line */ - if (last_cmdline == NULL && errmsg) + case ':': // last command line + if (last_cmdline == NULL && errmsg) { EMSG(_(e_nolastcmd)); + } *argp = last_cmdline; return true; - case '/': /* last search-pattern */ - if (last_search_pat() == NULL && errmsg) + case '/': // last search-pattern + if (last_search_pat() == NULL && errmsg) { EMSG(_(e_noprevre)); + } *argp = last_search_pat(); return true; - case '.': /* last inserted text */ + case '.': // last inserted text *argp = get_last_insert_save(); *allocated = true; if (*argp == NULL && errmsg) { @@ -1307,9 +1324,8 @@ bool get_spec_reg( if (!errmsg) { return false; } - *argp = file_name_at_cursor( - FNAME_MESS | FNAME_HYP | (regname == Ctrl_P ? FNAME_EXP : 0), - 1L, NULL); + *argp = file_name_at_cursor(FNAME_MESS | FNAME_HYP | (regname == Ctrl_P ? FNAME_EXP : 0), + 1L, NULL); *allocated = true; return true; @@ -1333,7 +1349,7 @@ bool get_spec_reg( *argp = ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum, false); return true; - case '_': /* black hole: always empty */ + case '_': // black hole: always empty *argp = (char_u *)""; return true; } @@ -1357,8 +1373,9 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr) const bool literally = literally_arg || is_literal_register(regname); yankreg_T *reg = get_yank_register(regname, YREG_PASTE); - if (reg->y_array == NULL) + if (reg->y_array == NULL) { return FAIL; + } for (size_t i = 0; i < reg->y_size; i++) { cmdline_paste_str(reg->y_array[i], literally); @@ -1371,8 +1388,9 @@ bool cmdline_paste_reg(int regname, bool literally_arg, bool remcr) /* Check for CTRL-C, in case someone tries to paste a few thousand * lines and gets bored. */ os_breakcheck(); - if (got_int) + if (got_int) { return FAIL; + } } return OK; } @@ -1399,8 +1417,8 @@ int op_delete(oparg_T *oap) { int n; linenr_T lnum; - char_u *ptr; - char_u *newp, *oldp; + char_u *ptr; + char_u *newp, *oldp; struct block_def bd = { 0 }; linenr_T old_lcount = curbuf->b_ml.ml_line_count; @@ -1431,8 +1449,9 @@ int op_delete(oparg_T *oap) && oap->motion_force == NUL && oap->op_type == OP_DELETE) { ptr = ml_get(oap->end.lnum) + oap->end.col; - if (*ptr != NUL) + if (*ptr != NUL) { ptr += oap->inclusive; + } ptr = skipwhite(ptr); if (*ptr == NUL && inindent(0)) { oap->motion_type = kMTLineWise; @@ -1505,7 +1524,6 @@ int op_delete(oparg_T *oap) set_clipboard(oap->regname, reg); do_autocmd_textyankpost(oap, reg); } - } /* @@ -1523,7 +1541,7 @@ int op_delete(oparg_T *oap) continue; } - /* Adjust cursor position for tab replaced by spaces and 'lbr'. */ + // Adjust cursor position for tab replaced by spaces and 'lbr'. if (lnum == curwin->w_cursor.lnum) { curwin->w_cursor.col = bd.textcol + bd.startspaces; curwin->w_cursor.coladd = 0; @@ -1563,12 +1581,13 @@ int op_delete(oparg_T *oap) if (oap->line_count > 1) { lnum = curwin->w_cursor.lnum; - ++curwin->w_cursor.lnum; - del_lines(oap->line_count - 1, TRUE); + curwin->w_cursor.lnum++; + del_lines(oap->line_count - 1, true); curwin->w_cursor.lnum = lnum; } - if (u_save_cursor() == FAIL) + if (u_save_cursor() == FAIL) { return FAIL; + } if (curbuf->b_p_ai) { // don't delete indent beginline(BL_WHITE); // cursor on first non-white did_ai = true; // delete the indent when ESC hit @@ -1584,25 +1603,27 @@ int op_delete(oparg_T *oap) (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 + // leave cursor past last char in line if (oap->line_count > 1) { u_clearline(); // "U" command not possible after "2cc" } } else { - del_lines(oap->line_count, TRUE); + del_lines(oap->line_count, true); beginline(BL_WHITE | BL_FIX); - u_clearline(); /* "U" command not possible after "dd" */ + u_clearline(); // "U" command not possible after "dd" } } else { if (virtual_op) { int endcol = 0; - /* For virtualedit: break the tabs that are partly included. */ + // For virtualedit: break the tabs that are partly included. if (gchar_pos(&oap->start) == '\t') { - if (u_save_cursor() == FAIL) /* save first line for undo */ + if (u_save_cursor() == FAIL) { // save first line for undo return FAIL; - if (oap->line_count == 1) + } + if (oap->line_count == 1) { endcol = getviscol2(oap->end.col, oap->end.coladd); + } coladvance_force(getviscol2(oap->start.col, oap->start.coladd)); oap->start = curwin->w_cursor; if (oap->line_count == 1) { @@ -1613,14 +1634,15 @@ int op_delete(oparg_T *oap) } } - /* Break a tab only when it's included in the area. */ + // Break a tab only when it's included in the area. if (gchar_pos(&oap->end) == '\t' && oap->end.coladd == 0 && oap->inclusive) { - /* save last line for undo */ + // save last line for undo if (u_save((linenr_T)(oap->end.lnum - 1), - (linenr_T)(oap->end.lnum + 1)) == FAIL) + (linenr_T)(oap->end.lnum + 1)) == FAIL) { return FAIL; + } curwin->w_cursor = oap->end; coladvance_force(getviscol2(oap->end.col, oap->end.coladd)); oap->end = curwin->w_cursor; @@ -1629,17 +1651,18 @@ int op_delete(oparg_T *oap) mb_adjust_opend(oap); } - if (oap->line_count == 1) { /* delete characters within one line */ - if (u_save_cursor() == FAIL) /* save line for undo */ + if (oap->line_count == 1) { // delete characters within one line + if (u_save_cursor() == FAIL) { // save line for undo return FAIL; + } - /* if 'cpoptions' contains '$', display '$' at end of change */ - if ( vim_strchr(p_cpo, CPO_DOLLAR) != NULL - && oap->op_type == OP_CHANGE - && oap->end.lnum == curwin->w_cursor.lnum - && !oap->is_VIsual - ) + // if 'cpoptions' contains '$', display '$' at end of change + if (vim_strchr(p_cpo, CPO_DOLLAR) != NULL + && oap->op_type == OP_CHANGE + && oap->end.lnum == curwin->w_cursor.lnum + && !oap->is_VIsual) { display_dollar(oap->end.col - !oap->inclusive); + } n = oap->end.col - oap->start.col + 1 - !oap->inclusive; @@ -1647,20 +1670,23 @@ int op_delete(oparg_T *oap) /* fix up things for virtualedit-delete: * break the tabs which are going to get in our way */ - char_u *curline = get_cursor_line_ptr(); + char_u *curline = get_cursor_line_ptr(); int len = (int)STRLEN(curline); if (oap->end.coladd != 0 && (int)oap->end.col >= len - 1 - && !(oap->start.coladd && (int)oap->end.col >= len - 1)) + && !(oap->start.coladd && (int)oap->end.col >= len - 1)) { n++; - /* Delete at least one char (e.g, when on a control char). */ - if (n == 0 && oap->start.coladd != oap->end.coladd) + } + // Delete at least one char (e.g, when on a control char). + if (n == 0 && oap->start.coladd != oap->end.coladd) { n = 1; + } - /* When deleted a char in the line, reset coladd. */ - if (gchar_cursor() != NUL) + // When deleted a char in the line, reset coladd. + if (gchar_cursor() != NUL) { curwin->w_cursor.coladd = 0; + } } (void)del_bytes((colnr_T)n, !virtual_op, @@ -1669,16 +1695,17 @@ int op_delete(oparg_T *oap) // delete characters between lines pos_T curpos; - /* save deleted and changed lines for undo */ + // save deleted and changed lines for undo if (u_save((linenr_T)(curwin->w_cursor.lnum - 1), - (linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL) + (linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL) { return FAIL; + } curbuf_splice_pending++; pos_T startpos = curwin->w_cursor; // start position for delete - bcount_t deleted_bytes = get_region_bytecount( - curbuf, startpos.lnum, oap->end.lnum, startpos.col, - oap->end.col) + oap->inclusive; + bcount_t deleted_bytes = get_region_bytecount(curbuf, startpos.lnum, oap->end.lnum, + startpos.col, + oap->end.col) + oap->inclusive; truncate_line(true); // delete from cursor to end of line curpos = curwin->w_cursor; // remember curwin->w_cursor @@ -1721,7 +1748,7 @@ setmarks: */ static void mb_adjust_opend(oparg_T *oap) { - char_u *p; + char_u *p; if (oap->inclusive) { p = ml_get(oap->end.lnum); @@ -1761,15 +1788,15 @@ int op_replace(oparg_T *oap, int c) { int n, numc; int num_chars; - char_u *newp, *oldp; + char_u *newp, *oldp; colnr_T oldlen; struct block_def bd; - char_u *after_p = NULL; + char_u *after_p = NULL; int had_ctrl_v_cr = false; - if ((curbuf->b_ml.ml_flags & ML_EMPTY ) || oap->empty) - return OK; /* nothing to do */ - + if ((curbuf->b_ml.ml_flags & ML_EMPTY ) || oap->empty) { + return OK; // nothing to do + } if (c == REPLACE_CR_NCHAR) { had_ctrl_v_cr = true; c = CAR; @@ -1781,8 +1808,9 @@ int op_replace(oparg_T *oap, int c) mb_adjust_opend(oap); if (u_save((linenr_T)(oap->start.lnum - 1), - (linenr_T)(oap->end.lnum + 1)) == FAIL) + (linenr_T)(oap->end.lnum + 1)) == FAIL) { return FAIL; + } /* * block mode replace @@ -1809,18 +1837,20 @@ int op_replace(oparg_T *oap, int c) getvpos(&vpos, oap->start_vcol); bd.startspaces += vpos.coladd; n = bd.startspaces; - } else - /* allow for pre spaces */ + } else { + // allow for pre spaces n = (bd.startspaces ? bd.start_char_vcols - 1 : 0); + } - /* allow for post spp */ + // allow for post spp n += (bd.endspaces && !bd.is_oneChar && bd.end_char_vcols > 0) ? bd.end_char_vcols - 1 : 0; - /* Figure out how many characters to replace. */ + // Figure out how many characters to replace. numc = oap->end_vcol - oap->start_vcol + 1; - if (bd.is_short && (!virtual_op || bd.is_MAX)) + if (bd.is_short && (!virtual_op || bd.is_MAX)) { numc -= (oap->end_vcol - bd.end_vcol) + 1; + } /* A double-wide character can be replaced only up to half the * times. */ @@ -1832,7 +1862,7 @@ int op_replace(oparg_T *oap, int c) numc = numc / 2; } - /* Compute bytes needed, move character count to num_chars. */ + // Compute bytes needed, move character count to num_chars. num_chars = numc; numc *= (*mb_char2len)(c); @@ -1860,19 +1890,19 @@ int op_replace(oparg_T *oap, int c) assert(col >= 0); int newrows = 0, newcols = 0; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { - // strlen(newp) at this point - int newp_len = bd.textcol + bd.startspaces; - while (--num_chars >= 0) { - newp_len += utf_char2bytes(c, newp + newp_len); - } - if (!bd.is_short) { - // insert post-spaces - memset(newp + newp_len, ' ', (size_t)bd.endspaces); - newp_len += bd.endspaces; - // copy the part after the changed part - memmove(newp + newp_len, oldp, (size_t)col); - } - newcols = newp_len - bd.textcol; + // strlen(newp) at this point + int newp_len = bd.textcol + bd.startspaces; + while (--num_chars >= 0) { + newp_len += utf_char2bytes(c, newp + newp_len); + } + if (!bd.is_short) { + // insert post-spaces + memset(newp + newp_len, ' ', (size_t)bd.endspaces); + newp_len += bd.endspaces; + // copy the part after the changed part + memmove(newp + newp_len, oldp, (size_t)col); + } + newcols = newp_len - bd.textcol; } else { // Replacing with \r or \n means splitting the line. after_p_len = (size_t)col; @@ -1901,10 +1931,12 @@ int op_replace(oparg_T *oap, int c) oap->start.col = 0; curwin->w_cursor.col = 0; oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (oap->end.col) + if (oap->end.col) { --oap->end.col; - } else if (!oap->inclusive) + } + } else if (!oap->inclusive) { dec(&(oap->end)); + } // TODO(bfredl): we could batch all the splicing // done on the same line, at least @@ -1914,8 +1946,9 @@ int op_replace(oparg_T *oap, int c) if ((*mb_char2len)(c) > 1 || (*mb_char2len)(n) > 1) { /* This is slow, but it handles replacing a single-byte * with a multi-byte and the other way around. */ - if (curwin->w_cursor.lnum == oap->end.lnum) + if (curwin->w_cursor.lnum == oap->end.lnum) { oap->end.col += (*mb_char2len)(c) - (*mb_char2len)(n); + } replace_character(c); } else { if (n == TAB) { @@ -1925,11 +1958,12 @@ int op_replace(oparg_T *oap, int c) /* oap->end has to be recalculated when * the tab breaks */ end_vcol = getviscol2(oap->end.col, - oap->end.coladd); + oap->end.coladd); } coladvance_force(getviscol()); - if (curwin->w_cursor.lnum == oap->end.lnum) + if (curwin->w_cursor.lnum == oap->end.lnum) { getvpos(&oap->end, end_vcol); + } } pbyte(curwin->w_cursor, c); } @@ -1937,8 +1971,9 @@ int op_replace(oparg_T *oap, int c) int virtcols = oap->end.coladd; if (curwin->w_cursor.lnum == oap->start.lnum - && oap->start.col == oap->end.col && oap->start.coladd) + && oap->start.col == oap->end.col && oap->start.coladd) { virtcols -= oap->start.coladd; + } /* oap->end has been trimmed so it's effectively inclusive; * as a result an extra +1 must be counted so we don't @@ -1957,9 +1992,10 @@ int op_replace(oparg_T *oap, int c) } } - /* Advance to next character, stop at the end of the file. */ - if (inc_cursor() == -1) + // Advance to next character, stop at the end of the file. + if (inc_cursor() == -1) { break; + } } } @@ -1967,7 +2003,7 @@ int op_replace(oparg_T *oap, int c) check_cursor(); changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L, true); - /* Set "'[" and "']" marks. */ + // Set "'[" and "']" marks. curbuf->b_op_start = oap->start; curbuf->b_op_end = oap->end; @@ -1985,8 +2021,9 @@ void op_tilde(oparg_T *oap) int did_change = FALSE; if (u_save((linenr_T)(oap->start.lnum - 1), - (linenr_T)(oap->end.lnum + 1)) == FAIL) + (linenr_T)(oap->end.lnum + 1)) == FAIL) { return; + } pos = oap->start; if (oap->motion_type == kMTBlockWise) { // Visual block mode @@ -1997,7 +2034,6 @@ void op_tilde(oparg_T *oap) pos.col = bd.textcol; one_change = swapchars(oap->op_type, &pos, bd.textlen); did_change |= one_change; - } if (did_change) { changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true); @@ -2007,31 +2043,36 @@ void op_tilde(oparg_T *oap) oap->start.col = 0; pos.col = 0; oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (oap->end.col) + if (oap->end.col) { --oap->end.col; - } else if (!oap->inclusive) + } + } else if (!oap->inclusive) { dec(&(oap->end)); + } - if (pos.lnum == oap->end.lnum) + if (pos.lnum == oap->end.lnum) { did_change = swapchars(oap->op_type, &pos, - oap->end.col - pos.col + 1); - else + oap->end.col - pos.col + 1); + } else { for (;; ) { did_change |= swapchars(oap->op_type, &pos, - pos.lnum == oap->end.lnum ? oap->end.col + 1 : - (int)STRLEN(ml_get_pos(&pos))); - if (ltoreq(oap->end, pos) || inc(&pos) == -1) + pos.lnum == oap->end.lnum ? oap->end.col + 1 : + (int)STRLEN(ml_get_pos(&pos))); + if (ltoreq(oap->end, pos) || inc(&pos) == -1) { break; + } } + } if (did_change) { changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L, true); } } - if (!did_change && oap->is_VIsual) - /* No change: need to remove the Visual selection */ + if (!did_change && oap->is_VIsual) { + // No change: need to remove the Visual selection redraw_curbuf_later(INVERTED); + } /* * Set '[ and '] marks. @@ -2040,10 +2081,11 @@ void op_tilde(oparg_T *oap) curbuf->b_op_end = oap->end; if (oap->line_count > p_report) { - if (oap->line_count == 1) + if (oap->line_count == 1) { MSG(_("1 line changed")); - else + } else { smsg(_("%" PRId64 " lines changed"), (int64_t)oap->line_count); + } } } @@ -2067,8 +2109,9 @@ static int swapchars(int op_type, pos_T *pos, int length) todo -= len - 1; } did_change |= swapchar(op_type, pos); - if (inc(pos) == -1) /* at end of file */ + if (inc(pos) == -1) { // at end of file break; + } } return did_change; } @@ -2091,7 +2134,7 @@ bool swapchar(int op_type, pos_T *pos) if (op_type == OP_UPPER && c == 0xdf) { pos_T sp = curwin->w_cursor; - /* Special handling of German sharp s: change to "SS". */ + // Special handling of German sharp s: change to "SS". curwin->w_cursor = *pos; del_char(false); ins_char('S'); @@ -2137,16 +2180,16 @@ bool swapchar(int op_type, pos_T *pos) void op_insert(oparg_T *oap, long count1) { long ins_len, pre_textlen = 0; - char_u *firstline, *ins_text; + char_u *firstline, *ins_text; colnr_T ind_pre = 0; struct block_def bd; int i; pos_T t1; - /* edit() changes this - record it for OP_APPEND */ + // edit() changes this - record it for OP_APPEND bd.is_MAX = (curwin->w_curswant == MAXCOL); - /* vis block is still marked. Get rid of it now. */ + // vis block is still marked. Get rid of it now. curwin->w_cursor.lnum = oap->start.lnum; update_screen(INVERTED); @@ -2159,12 +2202,14 @@ void op_insert(oparg_T *oap, long count1) unsigned old_ve_flags = ve_flags; ve_flags = VE_ALL; - if (u_save_cursor() == FAIL) + if (u_save_cursor() == FAIL) { return; + } coladvance_force(oap->op_type == OP_APPEND ? oap->end_vcol + 1 : getviscol()); - if (oap->op_type == OP_APPEND) + if (oap->op_type == OP_APPEND) { --curwin->w_cursor.col; + } ve_flags = old_ve_flags; } // Get the info about the block before entering the text @@ -2181,18 +2226,19 @@ void op_insert(oparg_T *oap, long count1) if (oap->op_type == OP_APPEND) { if (oap->motion_type == kMTBlockWise - && curwin->w_cursor.coladd == 0 - ) { - /* Move the cursor to the character right of the block. */ + && curwin->w_cursor.coladd == 0) { + // Move the cursor to the character right of the block. curwin->w_set_curswant = TRUE; while (*get_cursor_pos_ptr() != NUL - && (curwin->w_cursor.col < bd.textcol + bd.textlen)) + && (curwin->w_cursor.col < bd.textcol + bd.textlen)) { ++curwin->w_cursor.col; + } if (bd.is_short && !bd.is_MAX) { /* First line was too short, make it longer and adjust the * values in "bd". */ - if (u_save_cursor() == FAIL) + if (u_save_cursor() == FAIL) { return; + } for (i = 0; i < bd.endspaces; i++) { ins_char(' '); } @@ -2224,8 +2270,9 @@ void op_insert(oparg_T *oap, long count1) /* If user has moved off this line, we don't know what to do, so do * nothing. * Also don't repeat the insert when Insert mode ended with CTRL-C. */ - if (curwin->w_cursor.lnum != oap->start.lnum || got_int) + if (curwin->w_cursor.lnum != oap->start.lnum || got_int) { return; + } if (oap->motion_type == kMTBlockWise) { struct block_def bd2; @@ -2261,7 +2308,7 @@ void op_insert(oparg_T *oap, long count1) int t = getviscol2(curbuf->b_op_start_orig.col, curbuf->b_op_start_orig.coladd); oap->start.col = curbuf->b_op_start_orig.col; - /* reset pre_textlen to the value of OP_INSERT */ + // reset pre_textlen to the value of OP_INSERT pre_textlen += bd.textlen; pre_textlen -= t - oap->start_vcol; oap->start_vcol = t; @@ -2278,8 +2325,9 @@ void op_insert(oparg_T *oap, long count1) if (!bd.is_MAX || bd2.textlen < bd.textlen) { if (oap->op_type == OP_APPEND) { pre_textlen += bd2.textlen - bd.textlen; - if (bd2.endspaces) + if (bd2.endspaces) { --bd2.textlen; + } } bd.textcol = bd2.textcol; bd.textlen = bd2.textlen; @@ -2339,18 +2387,20 @@ int op_change(oparg_T *oap) if (oap->motion_type == kMTLineWise) { l = 0; if (!p_paste && curbuf->b_p_si - && !curbuf->b_p_cin - ) + && !curbuf->b_p_cin) { can_si = true; // It's like opening a new line, do si + } } /* First delete the text in the region. In an empty buffer only need to * save for undo */ if (curbuf->b_ml.ml_flags & ML_EMPTY) { - if (u_save_cursor() == FAIL) + if (u_save_cursor() == FAIL) { return FALSE; - } else if (op_delete(oap) == FAIL) + } + } else if (op_delete(oap) == FAIL) { return FALSE; + } if ((l > curwin->w_cursor.col) && !LINEEMPTY(curwin->w_cursor.lnum) && !virtual_op) { @@ -2461,9 +2511,9 @@ void clear_registers(void) #endif - /// Free contents of yankreg `reg`. - /// Called for normal freeing and in case of error. - /// `reg` must not be NULL (but `reg->y_array` might be) +/// Free contents of yankreg `reg`. +/// Called for normal freeing and in case of error. +/// `reg` must not be NULL (but `reg->y_array` might be) void free_register(yankreg_T *reg) FUNC_ATTR_NONNULL_ALL { @@ -2493,7 +2543,7 @@ bool op_yank(oparg_T *oap, bool message, int deleting) return false; } if (oap->regname == '_') { - return true; // black hole: nothing to do + return true; // black hole: nothing to do } yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK); @@ -2555,91 +2605,92 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) // Visual block mode reg->y_width = oap->end_vcol - oap->start_vcol; - if (curwin->w_curswant == MAXCOL && reg->y_width > 0) + if (curwin->w_curswant == MAXCOL && reg->y_width > 0) { reg->y_width--; + } } for (; lnum <= yankendlnum; lnum++, y_idx++) { switch (reg->y_type) { case kMTBlockWise: block_prep(oap, &bd, lnum, false); - yank_copy_line(reg, &bd, y_idx); + yank_copy_line(reg, &bd, y_idx, oap->excl_tr_ws); break; case kMTLineWise: reg->y_array[y_idx] = vim_strsave(ml_get(lnum)); break; - case kMTCharWise: - { - colnr_T startcol = 0, endcol = MAXCOL; - int is_oneChar = false; - colnr_T cs, ce; - p = ml_get(lnum); - bd.startspaces = 0; - bd.endspaces = 0; - - if (lnum == oap->start.lnum) { - startcol = oap->start.col; - if (virtual_op) { - getvcol(curwin, &oap->start, &cs, NULL, &ce); - if (ce != cs && oap->start.coladd > 0) { - /* Part of a tab selected -- but don't - * double-count it. */ - bd.startspaces = (ce - cs + 1) - - oap->start.coladd; - startcol++; + case kMTCharWise: { + colnr_T startcol = 0, endcol = MAXCOL; + int is_oneChar = false; + colnr_T cs, ce; + p = ml_get(lnum); + bd.startspaces = 0; + bd.endspaces = 0; + + if (lnum == oap->start.lnum) { + startcol = oap->start.col; + if (virtual_op) { + getvcol(curwin, &oap->start, &cs, NULL, &ce); + if (ce != cs && oap->start.coladd > 0) { + /* Part of a tab selected -- but don't + * double-count it. */ + bd.startspaces = (ce - cs + 1) + - oap->start.coladd; + startcol++; + } } } - } - if (lnum == oap->end.lnum) { - endcol = oap->end.col; - if (virtual_op) { - getvcol(curwin, &oap->end, &cs, NULL, &ce); - if (p[endcol] == NUL || (cs + oap->end.coladd < ce - // Don't add space for double-wide - // char; endcol will be on last byte - // of multi-byte char. - && utf_head_off(p, p + endcol) == 0)) { - if (oap->start.lnum == oap->end.lnum - && oap->start.col == oap->end.col) { - // Special case: inside a single char - is_oneChar = true; - bd.startspaces = oap->end.coladd - - oap->start.coladd + oap->inclusive; - endcol = startcol; - } else { - bd.endspaces = oap->end.coladd - + oap->inclusive; - endcol -= oap->inclusive; + if (lnum == oap->end.lnum) { + endcol = oap->end.col; + if (virtual_op) { + getvcol(curwin, &oap->end, &cs, NULL, &ce); + if (p[endcol] == NUL || (cs + oap->end.coladd < ce + // Don't add space for double-wide + // char; endcol will be on last byte + // of multi-byte char. + && utf_head_off(p, p + endcol) == 0)) { + if (oap->start.lnum == oap->end.lnum + && oap->start.col == oap->end.col) { + // Special case: inside a single char + is_oneChar = true; + bd.startspaces = oap->end.coladd + - oap->start.coladd + oap->inclusive; + endcol = startcol; + } else { + bd.endspaces = oap->end.coladd + + oap->inclusive; + endcol -= oap->inclusive; + } } } } + if (endcol == MAXCOL) { + endcol = (colnr_T)STRLEN(p); + } + if (startcol > endcol + || is_oneChar) { + bd.textlen = 0; + } else { + bd.textlen = endcol - startcol + oap->inclusive; + } + bd.textstart = p + startcol; + yank_copy_line(reg, &bd, y_idx, false); + break; } - if (endcol == MAXCOL) - endcol = (colnr_T)STRLEN(p); - if (startcol > endcol - || is_oneChar - ) { - bd.textlen = 0; - } else { - bd.textlen = endcol - startcol + oap->inclusive; - } - bd.textstart = p + startcol; - yank_copy_line(reg, &bd, y_idx); - break; - } // NOTREACHED case kMTUnknown: - abort(); + abort(); } } - if (curr != reg) { /* append the new block to the old block */ + if (curr != reg) { // append the new block to the old block new_ptr = xmalloc(sizeof(char_u *) * (curr->y_size + reg->y_size)); - for (j = 0; j < curr->y_size; ++j) + for (j = 0; j < curr->y_size; ++j) { new_ptr[j] = curr->y_array[j]; + } xfree(curr->y_array); curr->y_array = new_ptr; @@ -2660,10 +2711,12 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) xfree(reg->y_array[0]); curr->y_array[j++] = pnew; y_idx = 1; - } else + } else { y_idx = 0; - while (y_idx < reg->y_size) + } + while (y_idx < reg->y_size) { curr->y_array[j++] = reg->y_array[y_idx++]; + } curr->y_size = j; xfree(reg->y_array); } @@ -2714,8 +2767,15 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) return; } -static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx) +// Copy a block range into a register. +// If "exclude_trailing_space" is set, do not copy trailing whitespaces. +static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx, + bool exclude_trailing_space) + FUNC_ATTR_NONNULL_ALL { + if (exclude_trailing_space) { + bd->endspaces = 0; + } int size = bd->startspaces + bd->endspaces + bd->textlen; assert(size >= 0); char_u *pnew = xmallocz((size_t)size); @@ -2726,6 +2786,14 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx) pnew += bd->textlen; memset(pnew, ' ', (size_t)bd->endspaces); pnew += bd->endspaces; + if (exclude_trailing_space) { + int s = bd->textlen + bd->endspaces; + + while (ascii_iswhite(*(bd->textstart + s - 1)) && s > 0) { + s = s - utf_head_off(bd->textstart, bd->textstart + s - 1) - 1; + pnew--; + } + } *pnew = NUL; } @@ -2813,24 +2881,25 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) int delcount; int incr = 0; struct block_def bd; - char_u **y_array = NULL; + char_u **y_array = NULL; long nr_lines = 0; pos_T new_cursor; int indent; - int orig_indent = 0; /* init for gcc */ - int indent_diff = 0; /* init for gcc */ + int orig_indent = 0; // init for gcc + int indent_diff = 0; // init for gcc int first_indent = TRUE; int lendiff = 0; pos_T old_pos; - char_u *insert_string = NULL; + char_u *insert_string = NULL; bool allocated = false; long cnt; - if (flags & PUT_FIXINDENT) + if (flags & PUT_FIXINDENT) { orig_indent = get_indent(); + } - curbuf->b_op_start = curwin->w_cursor; /* default for '[ mark */ - curbuf->b_op_end = curwin->w_cursor; /* default for '] mark */ + curbuf->b_op_start = curwin->w_cursor; // default for '[ mark + curbuf->b_op_end = curwin->w_cursor; // default for '] mark /* * Using inserted text works differently, because the register includes @@ -2841,7 +2910,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // PUT_LINE has special handling below which means we use 'i' to start. char command_start_char = non_linewise_vis ? 'c' : - (flags & PUT_LINE ? 'i' : (dir == FORWARD ? 'a' : 'i')); + (flags & PUT_LINE ? 'i' : (dir == FORWARD ? 'a' : 'i')); // To avoid 'autoindent' on linewise puts, create a new line with `:put _`. if (flags & PUT_LINE) { @@ -2943,27 +3012,30 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) y_size = 0; ptr = insert_string; while (ptr != NULL) { - if (y_array != NULL) + if (y_array != NULL) { y_array[y_size] = ptr; + } ++y_size; ptr = vim_strchr(ptr, '\n'); if (ptr != NULL) { - if (y_array != NULL) + if (y_array != NULL) { *ptr = NUL; + } ++ptr; - /* A trailing '\n' makes the register linewise. */ + // A trailing '\n' makes the register linewise. if (*ptr == NUL) { y_type = kMTLineWise; break; } } } - if (y_array != NULL) + if (y_array != NULL) { break; + } y_array = (char_u **)xmalloc(y_size * sizeof(char_u *)); } } else { - y_size = 1; /* use fake one-line yank register */ + y_size = 1; // use fake one-line yank register y_array = &insert_string; } } else { @@ -3011,12 +3083,12 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) dir = FORWARD; } if (flags & PUT_LINE_FORWARD) { - /* Must be "p" for a Visual block, put lines below the block. */ + // Must be "p" for a Visual block, put lines below the block. curwin->w_cursor = curbuf->b_visual.vi_end; dir = FORWARD; } - curbuf->b_op_start = curwin->w_cursor; /* default for '[ mark */ - curbuf->b_op_end = curwin->w_cursor; /* default for '] mark */ + curbuf->b_op_start = curwin->w_cursor; // default for '[ mark + curbuf->b_op_end = curwin->w_cursor; // default for '] mark } if (flags & PUT_LINE) { // :put command or "p" in Visual line mode. @@ -3025,7 +3097,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if (y_size == 0 || y_array == NULL) { EMSG2(_("E353: Nothing in register %s"), - regname == 0 ? (char_u *)"\"" : transchar(regname)); + regname == 0 ? (char_u *)"\"" : transchar(regname)); goto end; } @@ -3051,7 +3123,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } // In an empty buffer the empty line is going to be replaced, include // it in the saved lines. - if ((BUFEMPTY() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL) { + if ((buf_is_empty(curbuf) ? + u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL) { goto end; } if (dir == FORWARD) { @@ -3094,10 +3167,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) colnr_T endcol2 = 0; if (dir == FORWARD && c != NUL) { - if (ve_flags == VE_ALL) + if (ve_flags == VE_ALL) { getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2); - else + } else { getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col); + } // move to start of next multi-byte character curwin->w_cursor.col += utfc_ptr2len(get_cursor_pos_ptr()); @@ -3117,10 +3191,12 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) curwin->w_cursor.col++; } if (c == TAB) { - if (dir == BACKWARD && curwin->w_cursor.col) + if (dir == BACKWARD && curwin->w_cursor.col) { curwin->w_cursor.col--; - if (dir == FORWARD && col - 1 == endcol2) + } + if (dir == FORWARD && col - 1 == endcol2) { curwin->w_cursor.col++; + } } } curwin->w_cursor.coladd = 0; @@ -3137,7 +3213,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) vcol = 0; delcount = 0; - /* add a new line */ + // add a new line if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { if (ml_append(curbuf->b_ml.ml_line_count, (char_u *)"", (colnr_T)1, false) == FAIL) { @@ -3146,11 +3222,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) nr_lines++; lines_appended = 1; } - /* get the old line and advance to the position to insert at */ + // get the old line and advance to the position to insert at oldp = get_cursor_line_ptr(); oldlen = STRLEN(oldp); for (ptr = oldp; vcol < col && *ptr; ) { - /* Count a tab for what it's worth (if list mode not on) */ + // Count a tab for what it's worth (if list mode not on) incr = lbr_chartabsize_adv(oldp, &ptr, (colnr_T)vcol); vcol += incr; } @@ -3158,9 +3234,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) shortline = (vcol < col) || (vcol == col && !*ptr); - if (vcol < col) /* line too short, padd with spaces */ + if (vcol < col) { // line too short, padd with spaces bd.startspaces = col - vcol; - else if (vcol > col) { + } else if (vcol > col) { bd.endspaces = vcol - col; bd.startspaces = incr - bd.endspaces; --bd.textcol; @@ -3226,18 +3302,19 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) delcount, addcount, kExtmarkUndo); ++curwin->w_cursor.lnum; - if (i == 0) + if (i == 0) { curwin->w_cursor.col += bd.startspaces; + } } changed_lines(lnum, 0, curbuf->b_op_start.lnum + (linenr_T)y_size - - (linenr_T)nr_lines , nr_lines, true); + - (linenr_T)nr_lines, nr_lines, true); - /* Set '[ mark. */ + // Set '[ mark. curbuf->b_op_start = curwin->w_cursor; curbuf->b_op_start.lnum = lnum; - /* adjust '] mark */ + // adjust '] mark curbuf->b_op_end.lnum = curwin->w_cursor.lnum - 1; curbuf->b_op_end.col = bd.textcol + (colnr_T)totlen - 1; curbuf->b_op_end.coladd = 0; @@ -3247,12 +3324,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) curwin->w_cursor = curbuf->b_op_end; curwin->w_cursor.col++; - /* in Insert mode we might be after the NUL, correct for that */ + // in Insert mode we might be after the NUL, correct for that len = (colnr_T)STRLEN(get_cursor_line_ptr()); - if (curwin->w_cursor.col > len) + if (curwin->w_cursor.col > len) { curwin->w_cursor.col = len; - } else + } + } else { curwin->w_cursor.lnum = lnum; + } } else { // Character or Line mode if (y_type == kMTCharWise) { @@ -3273,8 +3352,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) /* * Line mode: BACKWARD is the same as FORWARD on the previous line */ - else if (dir == BACKWARD) + else if (dir == BACKWARD) { --lnum; + } new_cursor = curwin->w_cursor; // simple case: insert into one line at a time @@ -3285,7 +3365,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if (VIsual_active) { end_lnum = curbuf->b_visual.vi_end.lnum; if (end_lnum < curbuf->b_visual.vi_start.lnum) { - end_lnum = curbuf->b_visual.vi_start.lnum; + end_lnum = curbuf->b_visual.vi_start.lnum; } if (end_lnum > start_lnum) { // "col" is valid for the first line, in following lines @@ -3342,7 +3422,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } while (VIsual_active && lnum <= end_lnum); - if (VIsual_active) { /* reset lnum to the last visual line */ + if (VIsual_active) { // reset lnum to the last visual line lnum--; } @@ -3363,7 +3443,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) lnum = new_cursor.lnum; ptr = ml_get(lnum) + col; totlen = STRLEN(y_array[y_size - 1]); - newp = (char_u *) xmalloc((size_t)(STRLEN(ptr) + totlen + 1)); + newp = (char_u *)xmalloc((size_t)(STRLEN(ptr) + totlen + 1)); STRCPY(newp, y_array[y_size - 1]); STRCAT(newp, ptr); // insert second line @@ -3394,23 +3474,26 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) old_pos = curwin->w_cursor; curwin->w_cursor.lnum = lnum; ptr = ml_get(lnum); - if (cnt == count && i == y_size - 1) + if (cnt == count && i == y_size - 1) { lendiff = (int)STRLEN(ptr); - if (*ptr == '#' && preprocs_left()) - indent = 0; /* Leave # lines at start */ - else if (*ptr == NUL) - indent = 0; /* Ignore empty lines */ - else if (first_indent) { + } + if (*ptr == '#' && preprocs_left()) { + indent = 0; // Leave # lines at start + } else if (*ptr == NUL) { + indent = 0; // Ignore empty lines + } else if (first_indent) { indent_diff = orig_indent - get_indent(); indent = orig_indent; first_indent = FALSE; - } else if ((indent = get_indent() + indent_diff) < 0) + } else if ((indent = get_indent() + indent_diff) < 0) { indent = 0; + } (void)set_indent(indent, SIN_NOMARK); curwin->w_cursor = old_pos; - /* remember how many chars were removed */ - if (cnt == count && i == y_size - 1) + // remember how many chars were removed + if (cnt == count && i == y_size - 1) { lendiff -= (int)STRLEN(ml_get(lnum)); + } } } @@ -3429,8 +3512,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) (int)y_size-1, lastsize, totsize, kExtmarkUndo); } else if (y_type == kMTLineWise && flags & PUT_LINE_SPLIT) { - extmark_splice(curbuf, (int)new_cursor.lnum-1, col, 0, 0, 0, - (int)y_size+1, 0, totsize+1, kExtmarkUndo); + // Account for last pasted NL + last NL + extmark_splice(curbuf, (int)new_cursor.lnum-1, col + 1, 0, 0, 0, + (int)y_size+1, 0, totsize+2, kExtmarkUndo); } } @@ -3438,8 +3522,9 @@ error: // Adjust marks. if (y_type == kMTLineWise) { curbuf->b_op_start.col = 0; - if (dir == FORWARD) + if (dir == FORWARD) { curbuf->b_op_start.lnum++; + } } ExtmarkOp kind = (y_type == kMTLineWise && !(flags & PUT_LINE_SPLIT)) @@ -3456,17 +3541,18 @@ error: curbuf->b_op_start.lnum, nr_lines, true); } - /* put '] mark at last inserted character */ + // put '] mark at last inserted character curbuf->b_op_end.lnum = lnum; - /* correct length for change in indent */ + // correct length for change in indent col = (colnr_T)STRLEN(y_array[y_size - 1]) - lendiff; - if (col > 1) + if (col > 1) { curbuf->b_op_end.col = col - 1; - else + } else { curbuf->b_op_end.col = 0; + } if (flags & PUT_CURSLINE) { - /* ":put": put cursor on last inserted line */ + // ":put": put cursor on last inserted line curwin->w_cursor.lnum = lnum; beginline(BL_WHITE | BL_FIX); } else if (flags & PUT_CURSEND) { @@ -3485,11 +3571,13 @@ error: } else if (y_type == kMTLineWise) { // put cursor on first non-blank in first inserted line curwin->w_cursor.col = 0; - if (dir == FORWARD) + if (dir == FORWARD) { ++curwin->w_cursor.lnum; + } beginline(BL_WHITE | BL_FIX); - } else /* put cursor on first inserted character */ + } else { // put cursor on first inserted character curwin->w_cursor = new_cursor; + } } } @@ -3497,14 +3585,16 @@ error: curwin->w_set_curswant = TRUE; end: - if (allocated) + if (allocated) { xfree(insert_string); - if (regname == '=') + } + if (regname == '=') { xfree(y_array); + } VIsual_active = FALSE; - /* If the cursor is past the end of the line put it at the end. */ + // If the cursor is past the end of the line put it at the end. adjust_cursor_eol(); } // NOLINT(readability/fn_size) @@ -3518,13 +3608,13 @@ void adjust_cursor_eol(void) && gchar_cursor() == NUL && (ve_flags & VE_ONEMORE) == 0 && !(restart_edit || (State & INSERT))) { - /* Put the cursor on the last character in the line. */ + // Put the cursor on the last character in the line. dec_cursor(); if (ve_flags == VE_ALL) { colnr_T scol, ecol; - /* Coladd is set to the width of the last character. */ + // Coladd is set to the width of the last character. getvcol(curwin, &curwin->w_cursor, &scol, NULL, &ecol); curwin->w_cursor.coladd = ecol - scol + 1; } @@ -3541,20 +3631,20 @@ int preprocs_left(void) && curbuf->b_ind_hash_comment == 0)); } -/* Return the character name of the register with the given number */ +// Return the character name of the register with the given number int get_register_name(int num) { - if (num == -1) + if (num == -1) { return '"'; - else if (num < 10) + } else if (num < 10) { return num + '0'; - else if (num == DELETION_REGISTER) + } else if (num == DELETION_REGISTER) { return '-'; - else if (num == STAR_REGISTER) + } else if (num == STAR_REGISTER) { return '*'; - else if (num == PLUS_REGISTER) + } else if (num == PLUS_REGISTER) { return '+'; - else { + } else { return num + 'a' - 10; } } @@ -3571,8 +3661,9 @@ void ex_display(exarg_T *eap) int clen; char_u type[2]; - if (arg != NULL && *arg == NUL) + if (arg != NULL && *arg == NUL) { arg = NULL; + } int attr = HL_ATTR(HLF_8); // Highlight title @@ -3580,23 +3671,28 @@ void ex_display(exarg_T *eap) for (int i = -1; i < NUM_REGISTERS && !got_int; i++) { name = get_register_name(i); switch (get_reg_type(name, NULL)) { - case kMTLineWise: type[0] = 'l'; break; - case kMTCharWise: type[0] = 'c'; break; - default: type[0] = 'b'; break; + case kMTLineWise: + type[0] = 'l'; break; + case kMTCharWise: + type[0] = 'c'; break; + default: + type[0] = 'b'; break; } if (arg != NULL && vim_strchr(arg, name) == NULL) { - continue; /* did not ask for this register */ + continue; // did not ask for this register } if (i == -1) { - if (y_previous != NULL) + if (y_previous != NULL) { yb = y_previous; - else + } else { yb = &(y_regs[0]); - } else + } + } else { yb = &(y_regs[i]); + } get_clipboard(name, &yb, true); @@ -3666,7 +3762,7 @@ void ex_display(exarg_T *eap) * display alternate file name */ if ((arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) { - char_u *fname; + char_u *fname; linenr_T dummy; if (buflist_name_nr(0, &fname, &dummy) != FAIL) { @@ -3694,15 +3790,11 @@ void ex_display(exarg_T *eap) } } -/* - * display a string for do_dis() - * truncate at end of screen line - */ -static void -dis_msg( - const char_u *p, - bool skip_esc // if true, ignore trailing ESC -) +/// display a string for do_dis() +/// truncate at end of screen line +/// +/// @param skip_esc if true, ignore trailing ESC +static void dis_msg(const char_u *p, bool skip_esc) FUNC_ATTR_NONNULL_ALL { int n; @@ -3715,8 +3807,9 @@ dis_msg( if ((l = utfc_ptr2len(p)) > 1) { msg_outtrans_len(p, l); p += l; - } else + } else { msg_outtrans_len(p++, 1); + } } os_breakcheck(); } @@ -3732,9 +3825,7 @@ dis_msg( /// @param include_space - whether to skip space following the comment leader /// @param[out] is_comment - whether the current line ends with an unclosed /// comment. -char_u *skip_comment( - char_u *line, bool process, bool include_space, bool *is_comment -) +char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_comment) { char_u *comment_flags = NULL; int lead_len; @@ -3763,8 +3854,9 @@ char_u *skip_comment( lead_len = get_leader_len(line, &comment_flags, false, include_space); - if (lead_len == 0) + if (lead_len == 0) { return line; + } /* Find: * - COM_END, @@ -3798,25 +3890,21 @@ char_u *skip_comment( // to set those marks. // // return FAIL for failure, OK otherwise -int do_join(size_t count, - int insert_space, - int save_undo, - int use_formatoptions, - bool setmark) +int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions, bool setmark) { - char_u *curr = NULL; - char_u *curr_start = NULL; - char_u *cend; - char_u *newp; - char_u *spaces; /* number of spaces inserted before a line */ + char_u *curr = NULL; + char_u *curr_start = NULL; + char_u *cend; + char_u *newp; + char_u *spaces; // number of spaces inserted before a line int endcurr1 = NUL; int endcurr2 = NUL; - int currsize = 0; /* size of the current line */ - int sumsize = 0; /* size of the long new line */ + int currsize = 0; // size of the current line + int sumsize = 0; // size of the long new line linenr_T t; colnr_T col = 0; int ret = OK; - int *comments = NULL; + int *comments = NULL; int remove_comments = (use_formatoptions == TRUE) && has_format_option(FO_REMOVE_COMS); bool prev_was_comment = false; @@ -3866,13 +3954,14 @@ int do_join(size_t count, || (utf_ptr2char(curr) < 0x100 && endcurr1 < 0x100)) && (!has_format_option(FO_MBYTE_JOIN2) || (utf_ptr2char(curr) < 0x100 && !utf_eat_space(endcurr1)) - || (endcurr1 < 0x100 && !utf_eat_space(utf_ptr2char(curr)))) - ) { - /* don't add a space if the line is ending in a space */ - if (endcurr1 == ' ') + || (endcurr1 < 0x100 && + !utf_eat_space(utf_ptr2char(curr))))) { + // don't add a space if the line is ending in a space + if (endcurr1 == ' ') { endcurr1 = endcurr2; - else + } else { ++spaces[t]; + } // Extra space when 'joinspaces' set and line ends in '.', '?', or '!'. if (p_js && (endcurr1 == '.' || endcurr1 == '?' || endcurr1 == '!')) { ++spaces[t]; @@ -3947,10 +4036,12 @@ int do_join(size_t count, } curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t - 1)); - if (remove_comments) + if (remove_comments) { curr += comments[t - 1]; - if (insert_space && t > 1) + } + if (insert_space && t > 1) { curr = skipwhite(curr); + } currsize = (int)STRLEN(curr); } @@ -3977,11 +4068,12 @@ int do_join(size_t count, del_lines((long)count - 1, false); curwin->w_cursor.lnum = t; curbuf_splice_pending--; + curbuf->deleted_bytes2 = 0; /* * Set the cursor column: * Vi compatible: use the column of the first join - * vim: use the column of the last join + * vim: use the column of the last join */ curwin->w_cursor.col = (vim_strchr(p_cpo, CPO_JOINCOL) != NULL ? currsize : col); @@ -3992,8 +4084,9 @@ int do_join(size_t count, theend: xfree(spaces); - if (remove_comments) + if (remove_comments) { xfree(comments); + } return ret; } @@ -4002,15 +4095,17 @@ theend: * the first line. White-space is ignored. Note that the whole of * 'leader1' must match 'leader2_len' characters from 'leader2' -- webb */ -static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, int leader2_len, char_u *leader2_flags) +static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, int leader2_len, + char_u *leader2_flags) { int idx1 = 0, idx2 = 0; - char_u *p; - char_u *line1; - char_u *line2; + char_u *p; + char_u *line1; + char_u *line2; - if (leader1_len == 0) + if (leader1_len == 0) { return leader2_len == 0; + } /* * If first leader has 'f' flag, the lines can be joined only if the @@ -4021,18 +4116,24 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in */ if (leader1_flags != NULL) { for (p = leader1_flags; *p && *p != ':'; ++p) { - if (*p == COM_FIRST) + if (*p == COM_FIRST) { return leader2_len == 0; - if (*p == COM_END) + } + if (*p == COM_END) { return FALSE; + } if (*p == COM_START) { - if (*(ml_get(lnum) + leader1_len) == NUL) + if (*(ml_get(lnum) + leader1_len) == NUL) { return FALSE; - if (leader2_flags == NULL || leader2_len == 0) + } + if (leader2_flags == NULL || leader2_len == 0) { return FALSE; - for (p = leader2_flags; *p && *p != ':'; ++p) - if (*p == COM_MIDDLE) + } + for (p = leader2_flags; *p && *p != ':'; ++p) { + if (*p == COM_MIDDLE) { return TRUE; + } + } return FALSE; } } @@ -4043,30 +4144,30 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in * The first line has to be saved, only one line can be locked at a time. */ line1 = vim_strsave(ml_get(lnum)); - for (idx1 = 0; ascii_iswhite(line1[idx1]); ++idx1) + for (idx1 = 0; ascii_iswhite(line1[idx1]); ++idx1) { ; + } line2 = ml_get(lnum + 1); for (idx2 = 0; idx2 < leader2_len; ++idx2) { if (!ascii_iswhite(line2[idx2])) { - if (line1[idx1++] != line2[idx2]) + if (line1[idx1++] != line2[idx2]) { break; - } else - while (ascii_iswhite(line1[idx1])) + } + } else { + while (ascii_iswhite(line1[idx1])) { ++idx1; + } + } } xfree(line1); return idx2 == leader2_len && idx1 == leader1_len; } -/* - * Implementation of the format operator 'gq'. - */ -void -op_format( - oparg_T *oap, - int keep_cursor /* keep cursor on same text char */ -) +/// Implementation of the format operator 'gq'. +/// +/// @param keep_cursor keep cursor on same text char +void op_format(oparg_T *oap, int keep_cursor) { long old_line_count = curbuf->b_ml.ml_line_count; @@ -4075,21 +4176,24 @@ op_format( curwin->w_cursor = oap->cursor_start; if (u_save((linenr_T)(oap->start.lnum - 1), - (linenr_T)(oap->end.lnum + 1)) == FAIL) + (linenr_T)(oap->end.lnum + 1)) == FAIL) { return; + } curwin->w_cursor = oap->start; - if (oap->is_VIsual) - /* When there is no change: need to remove the Visual selection */ + if (oap->is_VIsual) { + // When there is no change: need to remove the Visual selection redraw_curbuf_later(INVERTED); + } - /* Set '[ mark at the start of the formatted area */ + // Set '[ mark at the start of the formatted area curbuf->b_op_start = oap->start; /* For "gw" remember the cursor position and put it back below (adjusted * for joined and split lines). */ - if (keep_cursor) + if (keep_cursor) { saved_cursor = oap->cursor_start; + } format_lines(oap->line_count, keep_cursor); @@ -4098,13 +4202,14 @@ op_format( * If the cursor was moved one line back (e.g. with "Q}") go to the next * line, so "." will do the next lines. */ - if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) + if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { ++curwin->w_cursor.lnum; + } beginline(BL_WHITE | BL_FIX); old_line_count = curbuf->b_ml.ml_line_count - old_line_count; msgmore(old_line_count); - /* put '] mark on the end of the formatted area */ + // put '] mark on the end of the formatted area curbuf->b_op_end = curwin->w_cursor; if (keep_cursor) { @@ -4132,25 +4237,22 @@ op_format( */ void op_formatexpr(oparg_T *oap) { - if (oap->is_VIsual) - /* When there is no change: need to remove the Visual selection */ + if (oap->is_VIsual) { + // When there is no change: need to remove the Visual selection redraw_curbuf_later(INVERTED); + } - if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0) + if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0) { /* As documented: when 'formatexpr' returns non-zero fall back to * internal formatting. */ op_format(oap, FALSE); + } } -int -fex_format( - linenr_T lnum, - long count, - int c /* character to be inserted */ -) +/// @param c character to be inserted +int fex_format(linenr_T lnum, long count, int c) { - int use_sandbox = was_set_insecurely( - curwin, (char_u *)"formatexpr", OPT_LOCAL); + int use_sandbox = was_set_insecurely(curwin, (char_u *)"formatexpr", OPT_LOCAL); int r; char_u *fex; @@ -4179,17 +4281,13 @@ fex_format( return r; } -/* - * Format "line_count" lines, starting at the cursor position. - * When "line_count" is negative, format until the end of the paragraph. - * Lines after the cursor line are saved for undo, caller must have saved the - * first line. - */ -void -format_lines( - linenr_T line_count, - int avoid_fex /* don't use 'formatexpr' */ -) +/// Format "line_count" lines, starting at the cursor position. +/// When "line_count" is negative, format until the end of the paragraph. +/// Lines after the cursor line are saved for undo, caller must have saved the +/// first line. +/// +/// @param avoid_fex don't use 'formatexpr' +void format_lines(linenr_T line_count, int avoid_fex) { bool is_not_par; // current line not part of parag. bool next_is_not_par; // next line not part of paragraph @@ -4227,11 +4325,12 @@ format_lines( is_not_par = true; } next_is_not_par = fmt_check_par(curwin->w_cursor.lnum - , &next_leader_len, &next_leader_flags, do_comments - ); + , &next_leader_len, &next_leader_flags, do_comments + ); is_end_par = (is_not_par || next_is_not_par); - if (!is_end_par && do_trail_white) + if (!is_end_par && do_trail_white) { is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1); + } curwin->w_cursor.lnum--; for (count = line_count; count != 0 && !got_int; --count) { @@ -4255,23 +4354,26 @@ format_lines( next_leader_flags = NULL; } else { next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1 - , &next_leader_len, &next_leader_flags, do_comments - ); - if (do_number_indent) + , &next_leader_len, &next_leader_flags, do_comments + ); + if (do_number_indent) { next_is_start_par = (get_number_indent(curwin->w_cursor.lnum + 1) > 0); + } } advance = true; is_end_par = (is_not_par || next_is_not_par || next_is_start_par); - if (!is_end_par && do_trail_white) + if (!is_end_par && do_trail_white) { is_end_par = !ends_in_white(curwin->w_cursor.lnum); + } /* * Skip lines that are not in a paragraph. */ if (is_not_par) { - if (line_count < 0) + if (line_count < 0) { break; + } } else { /* * For the first line of a paragraph, check indent of second line. @@ -4283,7 +4385,7 @@ format_lines( && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) { if (leader_len == 0 && next_leader_len == 0) { - /* no comment found */ + // no comment found second_indent = get_indent_lnum(curwin->w_cursor.lnum + 1); } else { @@ -4292,11 +4394,11 @@ format_lines( } } else if (do_number_indent) { if (leader_len == 0 && next_leader_len == 0) { - /* no comment found */ + // no comment found second_indent = get_number_indent(curwin->w_cursor.lnum); } else { - /* get_number_indent() is now "comment aware"... */ + // get_number_indent() is now "comment aware"... second_indent = get_number_indent(curwin->w_cursor.lnum); do_comments_list = 1; @@ -4309,20 +4411,22 @@ format_lines( */ if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count || !same_leader(curwin->w_cursor.lnum, - leader_len, leader_flags, - next_leader_len, next_leader_flags) - ) + leader_len, leader_flags, + next_leader_len, + next_leader_flags)) { is_end_par = true; + } /* * If we have got to the end of a paragraph, or the line is * getting long, format it. */ if (is_end_par || force_format) { - if (need_set_indent) + if (need_set_indent) { /* replace indent in first line with minimal number of * tabs and spaces, according to current options */ (void)set_indent(get_indent(), SIN_CHANGED); + } // put cursor on last non-space State = NORMAL; // don't go past end-of-line @@ -4331,25 +4435,26 @@ format_lines( dec_cursor(); } - /* do the formatting, without 'showmode' */ - State = INSERT; /* for open_line() */ + // do the formatting, without 'showmode' + State = INSERT; // for open_line() smd_save = p_smd; p_smd = FALSE; insertchar(NUL, INSCHAR_FORMAT - + (do_comments ? INSCHAR_DO_COM : 0) - + (do_comments && do_comments_list + + (do_comments ? INSCHAR_DO_COM : 0) + + (do_comments && do_comments_list ? INSCHAR_COM_LIST : 0) - + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent); + + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent); State = old_State; p_smd = smd_save; second_indent = -1; - /* at end of par.: need to set indent of next par. */ + // at end of par.: need to set indent of next par. need_set_indent = is_end_par; if (is_end_par) { /* When called with a negative line count, break at the * end of the paragraph. */ - if (line_count < 0) + if (line_count < 0) { break; + } first_par_line = true; } force_format = false; @@ -4363,8 +4468,9 @@ format_lines( advance = false; curwin->w_cursor.lnum++; curwin->w_cursor.col = 0; - if (line_count < 0 && u_save_cursor() == FAIL) + if (line_count < 0 && u_save_cursor() == FAIL) { break; + } if (next_leader_len > 0) { (void)del_bytes(next_leader_len, false, false); mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L, @@ -4401,11 +4507,12 @@ format_lines( */ static int ends_in_white(linenr_T lnum) { - char_u *s = ml_get(lnum); + char_u *s = ml_get(lnum); size_t l; - if (*s == NUL) + if (*s == NUL) { return FALSE; + } l = STRLEN(s) - 1; return ascii_iswhite(s[l]); } @@ -4420,22 +4527,24 @@ static int ends_in_white(linenr_T lnum) */ static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags, int do_comments) { - char_u *flags = NULL; /* init for GCC */ - char_u *ptr; + char_u *flags = NULL; // init for GCC + char_u *ptr; ptr = ml_get(lnum); - if (do_comments) + if (do_comments) { *leader_len = get_leader_len(ptr, leader_flags, FALSE, TRUE); - else + } else { *leader_len = 0; + } if (*leader_len > 0) { /* * Search for 'e' flag in comment leader flags. */ flags = *leader_flags; - while (*flags && *flags != ':' && *flags != COM_END) + while (*flags && *flags != ':' && *flags != COM_END) { ++flags; + } } return *skipwhite(ptr + *leader_len) == NUL @@ -4455,13 +4564,13 @@ int paragraph_start(linenr_T lnum) int next_leader_len = 0; // leader len of next line char_u *next_leader_flags = NULL; // flags for leader of next line - if (lnum <= 1) - return TRUE; /* start of the file */ - + if (lnum <= 1) { + return TRUE; // start of the file + } p = ml_get(lnum - 1); - if (*p == NUL) - return TRUE; /* after empty line */ - + if (*p == NUL) { + return TRUE; // after empty line + } const bool do_comments = has_format_option(FO_Q_COMS); // format comments if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) { return true; // after non-paragraph line @@ -4471,16 +4580,16 @@ int paragraph_start(linenr_T lnum) return true; // "lnum" is not a paragraph line } - if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1)) - return TRUE; /* missing trailing space in previous line. */ - - if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0)) - return TRUE; /* numbered item starts in "lnum". */ - + if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1)) { + return TRUE; // missing trailing space in previous line. + } + if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0)) { + return TRUE; // numbered item starts in "lnum". + } if (!same_leader(lnum - 1, leader_len, leader_flags, - next_leader_len, next_leader_flags)) - return TRUE; /* change of comment leader. */ - + next_leader_len, next_leader_flags)) { + return TRUE; // change of comment leader. + } return FALSE; } @@ -4497,15 +4606,14 @@ int paragraph_start(linenr_T lnum) * - start/endspaces is the number of columns of the first/last yanked char * that are to be yanked. */ -static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, - bool is_del) +static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool is_del) { int incr = 0; - char_u *pend; - char_u *pstart; - char_u *line; - char_u *prev_pstart; - char_u *prev_pend; + char_u *pend; + char_u *pstart; + char_u *line; + char_u *prev_pstart; + char_u *prev_pend; const int lbr_saved = curwin->w_p_lbr; // Avoid a problem with unwanted linebreaks in block mode. @@ -4526,7 +4634,7 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, pstart = line; prev_pstart = line; while (bdp->start_vcol < oap->start_vcol && *pstart) { - /* Count a tab for what it's worth (if list mode not on) */ + // Count a tab for what it's worth (if list mode not on) incr = lbr_chartabsize(line, pstart, (colnr_T)bdp->start_vcol); bdp->start_vcol += incr; if (ascii_iswhite(*pstart)) { @@ -4540,7 +4648,7 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, MB_PTR_ADV(pstart); } bdp->start_char_vcols = incr; - if (bdp->start_vcol < oap->start_vcol) { /* line too short */ + if (bdp->start_vcol < oap->start_vcol) { // line too short bdp->end_vcol = bdp->start_vcol; bdp->is_short = true; if (!is_del || oap->op_type == OP_APPEND) { @@ -4550,8 +4658,9 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, /* notice: this converts partly selected Multibyte characters to * spaces, too. */ bdp->startspaces = bdp->start_vcol - oap->start_vcol; - if (is_del && bdp->startspaces) + if (is_del && bdp->startspaces) { bdp->startspaces = bdp->start_char_vcols - bdp->startspaces; + } pend = pstart; bdp->end_vcol = bdp->start_vcol; if (bdp->end_vcol > oap->end_vcol) { // it's all in one character @@ -4575,7 +4684,7 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, } else { prev_pend = pend; while (bdp->end_vcol <= oap->end_vcol && *pend != NUL) { - /* Count a tab for what it's worth (if list mode not on) */ + // Count a tab for what it's worth (if list mode not on) prev_pend = pend; incr = lbr_chartabsize_adv(line, &pend, (colnr_T)bdp->end_vcol); bdp->end_vcol += incr; @@ -4597,17 +4706,19 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bdp->endspaces = bdp->end_vcol - oap->end_vcol - 1; if (!is_del && bdp->endspaces) { bdp->endspaces = incr - bdp->endspaces; - if (pend != pstart) + if (pend != pstart) { pend = prev_pend; + } } } } bdp->end_char_vcols = incr; - if (is_del && bdp->startspaces) + if (is_del && bdp->startspaces) { pstart = prev_pstart; + } bdp->textlen = (int)(pend - pstart); } - bdp->textcol = (colnr_T) (pstart - line); + bdp->textcol = (colnr_T)(pstart - line); bdp->textstart = pstart; curwin->w_p_lbr = lbr_saved; } @@ -4722,13 +4833,13 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) { int col; - char_u *buf1 = NULL; + char_u *buf1 = NULL; char_u buf2[NUMBUFLEN]; int pre; // 'X' or 'x': hex; '0': octal; 'B' or 'b': bin static bool hexupper = false; // 0xABC uvarnumber_T n; uvarnumber_T oldn; - char_u *ptr; + char_u *ptr; int c; int todel; int firstdigit; @@ -4784,14 +4895,14 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) && ptr[col - 1] == '0' && !utf_head_off(ptr, ptr + col - 1) && ascii_isxdigit(ptr[col + 1])))) { - // In case of binary/hexadecimal pattern overlap match, rescan + // In case of binary/hexadecimal pattern overlap match, rescan - col = curwin->w_cursor.col; + col = curwin->w_cursor.col; - while (col > 0 && ascii_isdigit(ptr[col])) { - col--; - col -= utf_head_off(ptr, ptr + col); - } + while (col > 0 && ascii_isdigit(ptr[col])) { + col--; + col -= utf_head_off(ptr, ptr + col); + } } if ((do_hex @@ -4807,8 +4918,8 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) && !utf_head_off(ptr, ptr + col - 1) && ascii_isbdigit(ptr[col + 1]))) { // Found hexadecimal or binary number, move to its start. - col--; - col -= utf_head_off(ptr, ptr + col); + col--; + col -= utf_head_off(ptr, ptr + col); } else { // Search forward and then backward to find the start of number. col = pos->col; @@ -4906,7 +5017,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) 0 + (do_bin ? STR2NR_BIN : 0) + (do_oct ? STR2NR_OCT : 0) + (do_hex ? STR2NR_HEX : 0), - NULL, &n, maxlen); + NULL, &n, maxlen, false); // ignore leading '-' for hex, octal and bin numbers if (pre && negative) { @@ -5014,17 +5125,16 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) // leading zeros for (bits = 8 * sizeof(n); bits > 0; bits--) { - if ((n >> (bits - 1)) & 0x1) { - break; - } + if ((n >> (bits - 1)) & 0x1) { + break; + } } while (bits > 0) { - buf2[i++] = ((n >> --bits) & 0x1) ? '1' : '0'; + buf2[i++] = ((n >> --bits) & 0x1) ? '1' : '0'; } buf2[i] = '\0'; - } else if (pre == 0) { vim_snprintf((char *)buf2, ARRAY_SIZE(buf2), "%" PRIu64, (uint64_t)n); } else if (pre == '0') { @@ -5082,18 +5192,18 @@ theend: MotionType get_reg_type(int regname, colnr_T *reg_width) { switch (regname) { - case '%': // file name - case '#': // alternate file name - case '=': // expression - case ':': // last command line - case '/': // last search-pattern - case '.': // last inserted text - case Ctrl_F: // Filename under cursor - case Ctrl_P: // Path under cursor, expand via "path" - case Ctrl_W: // word under cursor - case Ctrl_A: // WORD (mnemonic All) under cursor - case '_': // black hole: always empty - return kMTCharWise; + case '%': // file name + case '#': // alternate file name + case '=': // expression + case ':': // last command line + case '/': // last search-pattern + case '.': // last inserted text + case Ctrl_F: // Filename under cursor + case Ctrl_P: // Path under cursor, expand via "path" + case Ctrl_W: // word under cursor + case Ctrl_A: // WORD (mnemonic All) under cursor + case '_': // black hole: always empty + return kMTCharWise; } if (regname != NUL && !valid_yank_reg(regname, false)) { @@ -5118,26 +5228,25 @@ MotionType get_reg_type(int regname, colnr_T *reg_width) /// @param[out] buf Buffer to store formatted string. The allocated size should /// be at least NUMBUFLEN+2 to always fit the value. /// @param buf_len The allocated size of the buffer. -void format_reg_type(MotionType reg_type, colnr_T reg_width, - char *buf, size_t buf_len) +void format_reg_type(MotionType reg_type, colnr_T reg_width, char *buf, size_t buf_len) FUNC_ATTR_NONNULL_ALL { assert(buf_len > 1); switch (reg_type) { - case kMTLineWise: - buf[0] = 'V'; - buf[1] = NUL; - break; - case kMTCharWise: - buf[0] = 'v'; - buf[1] = NUL; - break; - case kMTBlockWise: - snprintf(buf, buf_len, CTRL_V_STR "%" PRIdCOLNR, reg_width + 1); - break; - case kMTUnknown: - buf[0] = NUL; - break; + case kMTLineWise: + buf[0] = 'V'; + buf[1] = NUL; + break; + case kMTCharWise: + buf[0] = 'v'; + buf[1] = NUL; + break; + case kMTBlockWise: + snprintf(buf, buf_len, CTRL_V_STR "%" PRIdCOLNR, reg_width + 1); + break; + case kMTUnknown: + buf[0] = NUL; + break; } } @@ -5178,12 +5287,14 @@ void *get_reg_contents(int regname, int flags) return get_reg_wrap_one_line(get_expr_line(), flags); } - if (regname == '@') /* "@@" is used for unnamed register */ + if (regname == '@') { // "@@" is used for unnamed register regname = '"'; + } - /* check for valid regname */ - if (regname != NUL && !valid_yank_reg(regname, false)) + // check for valid regname + if (regname != NUL && !valid_yank_reg(regname, false)) { return NULL; + } char_u *retval; bool allocated; @@ -5198,8 +5309,9 @@ void *get_reg_contents(int regname, int flags) } yankreg_T *reg = get_yank_register(regname, YREG_PASTE); - if (reg->y_array == NULL) + if (reg->y_array == NULL) { return NULL; + } if (flags & kGRegList) { list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size); @@ -5260,7 +5372,7 @@ static yankreg_T *init_write_reg(int name, yankreg_T **old_y_previous, bool must yankreg_T *reg = get_yank_register(name, YREG_YANK); if (!is_append_register(name) && !must_append) { - free_register(reg); + free_register(reg); } return reg; } @@ -5279,18 +5391,16 @@ static void finish_write_reg(int name, yankreg_T *reg, yankreg_T *old_y_previous /// write_reg_contents - store `str` in register `name` /// /// @see write_reg_contents_ex -void write_reg_contents(int name, const char_u *str, ssize_t len, - int must_append) +void write_reg_contents(int name, const char_u *str, ssize_t len, int must_append) { write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L); } -void write_reg_contents_lst(int name, char_u **strings, - bool must_append, MotionType yank_type, +void write_reg_contents_lst(int name, char_u **strings, bool must_append, MotionType yank_type, colnr_T block_len) { if (name == '/' || name == '=') { - char_u *s = strings[0]; + char_u *s = strings[0]; if (strings[0] == NULL) { s = (char_u *)""; } else if (strings[1] != NULL) { @@ -5307,7 +5417,7 @@ void write_reg_contents_lst(int name, char_u **strings, return; } - yankreg_T *old_y_previous, *reg; + yankreg_T *old_y_previous, *reg; if (!(reg = init_write_reg(name, &old_y_previous, must_append))) { return; } @@ -5335,18 +5445,14 @@ void write_reg_contents_lst(int name, char_u **strings, /// is an uppercase letter. /// @param yank_type The motion type (kMTUnknown to auto detect) /// @param block_len width of visual block -void write_reg_contents_ex(int name, - const char_u *str, - ssize_t len, - bool must_append, - MotionType yank_type, - colnr_T block_len) +void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_append, + MotionType yank_type, colnr_T block_len) { if (len < 0) { - len = (ssize_t) STRLEN(str); + len = (ssize_t)STRLEN(str); } - /* Special case: '/' search pattern */ + // Special case: '/' search pattern if (name == '/') { set_last_search_pat(str, RE_SEARCH, TRUE, TRUE); return; @@ -5375,7 +5481,7 @@ void write_reg_contents_ex(int name, if (name == '=') { size_t offset = 0; - size_t totlen = (size_t) len; + size_t totlen = (size_t)len; if (must_append && expr_line) { // append has been specified and expr_line already exists, so we'll @@ -5400,7 +5506,7 @@ void write_reg_contents_ex(int name, return; } - yankreg_T *old_y_previous, *reg; + yankreg_T *old_y_previous, *reg; if (!(reg = init_write_reg(name, &old_y_previous, must_append))) { return; } @@ -5418,9 +5524,8 @@ void write_reg_contents_ex(int name, /// @param len length of the string (Ignored when str_list=true.) /// @param blocklen width of visual block, or -1 for "I don't know." /// @param str_list True if str is `char_u **`. -static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, - const char_u *str, size_t len, colnr_T blocklen, - bool str_list) +static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str, size_t len, + colnr_T blocklen, bool str_list) FUNC_ATTR_NONNULL_ALL { if (y_ptr->y_array == NULL) { // NULL means empty register @@ -5439,7 +5544,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, // Count the number of lines within the string if (str_list) { - for (char_u **ss = (char_u **) str; *ss != NULL; ++ss) { + for (char_u **ss = (char_u **)str; *ss != NULL; ++ss) { newlines++; } } else { @@ -5467,7 +5572,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, // Find the end of each line and save it into the array. if (str_list) { - for (char_u **ss = (char_u **) str; *ss != NULL; ++ss, ++lnum) { + for (char_u **ss = (char_u **)str; *ss != NULL; ++ss, ++lnum) { size_t ss_len = STRLEN(*ss); pp[lnum] = xmemdupz(*ss, ss_len); if (ss_len > maxlen) { @@ -5508,7 +5613,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, set_yreg_additional_data(y_ptr, NULL); y_ptr->timestamp = os_time(); if (yank_type == kMTBlockWise) { - y_ptr->y_width = (blocklen == -1 ? (colnr_T) maxlen - 1 : blocklen); + y_ptr->y_width = (blocklen == -1 ? (colnr_T)maxlen - 1 : blocklen); } else { y_ptr->y_width = 0; } @@ -5534,9 +5639,8 @@ void clear_oparg(oparg_T *oap) * case, eol_size will be added to the character count to account for * the size of the EOL character. */ -static varnumber_T line_count_info(char_u *line, varnumber_T *wc, - varnumber_T *cc, varnumber_T limit, - int eol_size) +static varnumber_T line_count_info(char_u *line, varnumber_T *wc, varnumber_T *cc, + varnumber_T limit, int eol_size) { varnumber_T i; varnumber_T words = 0; @@ -5549,17 +5653,19 @@ static varnumber_T line_count_info(char_u *line, varnumber_T *wc, words++; is_word = 0; } - } else if (!ascii_isspace(line[i])) + } else if (!ascii_isspace(line[i])) { is_word = 1; + } ++chars; i += (*mb_ptr2len)(line + i); } - if (is_word) + if (is_word) { words++; + } *wc += words; - /* Add eol_size if the end of line was reached before hitting limit. */ + // Add eol_size if the end of line was reached before hitting limit. if (i < limit && line[i] == NUL) { i += eol_size; chars += eol_size; @@ -5574,7 +5680,7 @@ static varnumber_T line_count_info(char_u *line, varnumber_T *wc, /// When "dict" is not NULL store the info there instead of showing it. void cursor_pos_info(dict_T *dict) { - char_u *p; + char_u *p; char_u buf1[50]; char_u buf2[40]; linenr_T lnum; @@ -5601,10 +5707,11 @@ void cursor_pos_info(dict_T *dict) return; } } else { - if (get_fileformat(curbuf) == EOL_DOS) + if (get_fileformat(curbuf) == EOL_DOS) { eol_size = 2; - else + } else { eol_size = 1; + } if (l_VIsual_active) { if (lt(VIsual, curwin->w_cursor)) { @@ -5614,23 +5721,28 @@ void cursor_pos_info(dict_T *dict) min_pos = curwin->w_cursor; max_pos = VIsual; } - if (*p_sel == 'e' && max_pos.col > 0) + if (*p_sel == 'e' && max_pos.col > 0) { --max_pos.col; + } if (l_VIsual_mode == Ctrl_V) { - char_u * saved_sbr = p_sbr; + char_u *const saved_sbr = p_sbr; + char_u *const saved_w_sbr = curwin->w_p_sbr; - /* Make 'sbr' empty for a moment to get the correct size. */ + // Make 'sbr' empty for a moment to get the correct size. p_sbr = empty_option; + curwin->w_p_sbr = empty_option; oparg.is_VIsual = true; oparg.motion_type = kMTBlockWise; oparg.op_type = OP_NOP; getvcols(curwin, &min_pos, &max_pos, - &oparg.start_vcol, &oparg.end_vcol); + &oparg.start_vcol, &oparg.end_vcol); p_sbr = saved_sbr; - if (curwin->w_curswant == MAXCOL) + curwin->w_p_sbr = saved_w_sbr; + if (curwin->w_curswant == MAXCOL) { oparg.end_vcol = MAXCOL; - /* Swap the start, end vcol if needed */ + } + // Swap the start, end vcol if needed if (oparg.end_vcol < oparg.start_vcol) { oparg.end_vcol += oparg.start_vcol; oparg.start_vcol = oparg.end_vcol - oparg.start_vcol; @@ -5641,18 +5753,19 @@ void cursor_pos_info(dict_T *dict) } for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { - /* Check for a CTRL-C every 100000 characters. */ + // Check for a CTRL-C every 100000 characters. if (byte_count > last_check) { os_breakcheck(); - if (got_int) + if (got_int) { return; + } last_check = byte_count + 100000L; } - /* Do extra processing for VIsual mode. */ + // Do extra processing for VIsual mode. if (l_VIsual_active && lnum >= min_pos.lnum && lnum <= max_pos.lnum) { - char_u *s = NULL; + char_u *s = NULL; long len = 0L; switch (l_VIsual_mode) { @@ -5667,37 +5780,37 @@ void cursor_pos_info(dict_T *dict) s = ml_get(lnum); len = MAXCOL; break; - case 'v': - { - colnr_T start_col = (lnum == min_pos.lnum) + case 'v': { + colnr_T start_col = (lnum == min_pos.lnum) ? min_pos.col : 0; - colnr_T end_col = (lnum == max_pos.lnum) + colnr_T end_col = (lnum == max_pos.lnum) ? max_pos.col - start_col + 1 : MAXCOL; - s = ml_get(lnum) + start_col; - len = end_col; - } - break; + s = ml_get(lnum) + start_col; + len = end_col; + } + break; } if (s != NULL) { byte_count_cursor += line_count_info(s, &word_count_cursor, - &char_count_cursor, len, eol_size); + &char_count_cursor, len, eol_size); if (lnum == curbuf->b_ml.ml_line_count && !curbuf->b_p_eol && (curbuf->b_p_bin || !curbuf->b_p_fixeol) - && (long)STRLEN(s) < len) + && (long)STRLEN(s) < len) { byte_count_cursor -= eol_size; + } } } else { - /* In non-visual mode, check for the line the cursor is on */ + // In non-visual mode, check for the line the cursor is on if (lnum == curwin->w_cursor.lnum) { word_count_cursor += word_count; char_count_cursor += char_count; byte_count_cursor = byte_count - + line_count_info(ml_get(lnum), &word_count_cursor, - &char_count_cursor, - (varnumber_T)curwin->w_cursor.col + 1, - eol_size); + + line_count_info(ml_get(lnum), &word_count_cursor, + &char_count_cursor, + (varnumber_T)curwin->w_cursor.col + 1, + eol_size); } } // Add to the running totals @@ -5895,13 +6008,16 @@ bool prepare_yankreg_from_object(yankreg_T *reg, String regtype, size_t lines) case 0: reg->y_type = kMTUnknown; break; - case 'v': case 'c': + case 'v': + case 'c': reg->y_type = kMTCharWise; break; - case 'V': case 'l': + case 'V': + case 'l': reg->y_type = kMTLineWise; break; - case 'b': case Ctrl_V: + case 'b': + case Ctrl_V: reg->y_type = kMTBlockWise; break; default: @@ -6006,13 +6122,16 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) case 0: reg->y_type = kMTUnknown; break; - case 'v': case 'c': + case 'v': + case 'c': reg->y_type = kMTCharWise; break; - case 'V': case 'l': + case 'V': + case 'l': reg->y_type = kMTLineWise; break; - case 'b': case Ctrl_V: + case 'b': + case Ctrl_V: reg->y_type = kMTBlockWise; break; default: @@ -6036,11 +6155,10 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) { goto err; } - reg->y_array[tv_idx++] = (char_u *)xstrdupnul( - (const char *)TV_LIST_ITEM_TV(li)->vval.v_string); + reg->y_array[tv_idx++] = (char_u *)xstrdupnul((const char *)TV_LIST_ITEM_TV(li)->vval.v_string); }); - if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) { + if (reg->y_size > 0 && strlen((char *)reg->y_array[reg->y_size-1]) == 0) { // a known-to-be charwise yank might have a final linebreak // but otherwise there is no line after the final newline if (reg->y_type != kMTCharWise) { @@ -6095,8 +6213,7 @@ static void set_clipboard(int name, yankreg_T *reg) return; } - list_T *const lines = tv_list_alloc( - (ptrdiff_t)reg->y_size + (reg->y_type != kMTCharWise)); + list_T *const lines = tv_list_alloc((ptrdiff_t)reg->y_size + (reg->y_type != kMTCharWise)); for (size_t i = 0; i < reg->y_size; i++) { tv_list_append_string(lines, (const char *)reg->y_array[i], -1); @@ -6104,21 +6221,21 @@ static void set_clipboard(int name, yankreg_T *reg) char regtype; switch (reg->y_type) { - case kMTLineWise: { + case kMTLineWise: { regtype = 'V'; tv_list_append_string(lines, NULL, 0); break; } - case kMTCharWise: { + case kMTCharWise: { regtype = 'v'; break; } - case kMTBlockWise: { + case kMTBlockWise: { regtype = 'b'; tv_list_append_string(lines, NULL, 0); break; } - case kMTUnknown: { + case kMTUnknown: { abort(); } } @@ -6194,8 +6311,8 @@ static inline bool reg_empty(const yankreg_T *const reg) /// Iterate over global registers. /// /// @see op_register_iter -const void *op_global_reg_iter(const void *const iter, char *const name, - yankreg_T *const reg, bool *is_unnamed) +const void *op_global_reg_iter(const void *const iter, char *const name, yankreg_T *const reg, + bool *is_unnamed) FUNC_ATTR_NONNULL_ARG(2, 3, 4) FUNC_ATTR_WARN_UNUSED_RESULT { return op_reg_iter(iter, y_regs, name, reg, is_unnamed); @@ -6210,9 +6327,8 @@ const void *op_global_reg_iter(const void *const iter, char *const name, /// /// @return Pointer that must be passed to next `op_register_iter` call or /// NULL if iteration is over. -const void *op_reg_iter(const void *const iter, const yankreg_T *const regs, - char *const name, yankreg_T *const reg, - bool *is_unnamed) +const void *op_reg_iter(const void *const iter, const yankreg_T *const regs, char *const name, + yankreg_T *const reg, bool *is_unnamed) FUNC_ATTR_NONNULL_ARG(3, 4, 5) FUNC_ATTR_WARN_UNUSED_RESULT { *name = NUL; @@ -6231,7 +6347,7 @@ const void *op_reg_iter(const void *const iter, const yankreg_T *const regs, *is_unnamed = (iter_reg == y_previous); while (++iter_reg - &(regs[0]) < NUM_SAVED_REGISTERS) { if (!reg_empty(iter_reg)) { - return (void *) iter_reg; + return (void *)iter_reg; } } return NULL; @@ -6306,8 +6422,7 @@ bool op_reg_set_previous(const char name) /// Get the byte count of buffer region. End-exclusive. /// /// @return number of bytes -bcount_t get_region_bytecount(buf_T *buf, linenr_T start_lnum, - linenr_T end_lnum, colnr_T start_col, +bcount_t get_region_bytecount(buf_T *buf, linenr_T start_lnum, linenr_T end_lnum, colnr_T start_col, colnr_T end_col) { linenr_T max_lnum = buf->b_ml.ml_line_count; @@ -6324,8 +6439,7 @@ bcount_t get_region_bytecount(buf_T *buf, linenr_T start_lnum, if (start_lnum + i > max_lnum) { return deleted_bytes; } - deleted_bytes += (bcount_t)STRLEN( - ml_get_buf(buf, start_lnum + i, false)) + 1; + deleted_bytes += (bcount_t)STRLEN(ml_get_buf(buf, start_lnum + i, false)) + 1; } if (end_lnum > max_lnum) { return deleted_bytes; diff --git a/src/nvim/option.c b/src/nvim/option.c index 67fb78bcc8..23b488ea68 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -16,8 +16,6 @@ // - If it's a numeric option, add any necessary bounds checks to // set_num_option(). // - If it's a list of flags, add some code in do_set(), search for WW_ALL. -// - When adding an option with expansion (P_EXPAND), but with a different -// default for Vi and Vim (no P_VI_DEF), add some code at VIMEXP. // - Add documentation! doc/options.txt, and any other related places. // - Add an entry in runtime/optwin.vim. @@ -85,6 +83,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/os/input.h" #include "nvim/os/lang.h" +#include "nvim/quickfix.h" /* * The options that are local to a window or buffer have "indir" set to one of @@ -207,12 +206,10 @@ typedef struct vimoption { // buffer-local option: global value idopt_T indir; // global option: PV_NONE; // local option: indirect option index - char_u *def_val[2]; // default values for variable (vi and vim) + char_u *def_val; // default values for variable (neovim!!) LastSet last_set; // script in which the option was last set } vimoption_T; -#define VI_DEFAULT 0 // def_val[VI_DEFAULT] is Vi default value -#define VIM_DEFAULT 1 // def_val[VIM_DEFAULT] is Vim default value /* * Flags @@ -231,8 +228,6 @@ typedef struct vimoption { // use free() when assigning new value #define P_WAS_SET 0x100U // option has been set/reset #define P_NO_MKRC 0x200U // don't include in :mkvimrc output -#define P_VI_DEF 0x400U // Use Vi default for Vim -#define P_VIM 0x800U // Vim option // when option changed, what to display: #define P_RSTAT 0x1000U ///< redraw status lines @@ -265,15 +260,13 @@ typedef struct vimoption { #define P_MLE 0x80000000U ///< under control of 'modelineexpr' #define HIGHLIGHT_INIT \ - "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \ - "d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr," \ - "N:CursorLineNr,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title," \ - "v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn," \ - "A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal," \ - "B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel," \ - "x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill," \ - "!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine," \ - "0:Whitespace,I:NormalNC" + "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \ + "i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr," \ + "r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \ + "W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn," \ + "-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar," \ + "X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn," \ + "q:QuickFixLine,0:Whitespace,I:NormalNC" /* * options[] is initialized here. @@ -458,7 +451,7 @@ void set_init_1(bool clean_arg) buf[j] = NUL; opt_idx = findoption("cdpath"); if (opt_idx >= 0) { - options[opt_idx].def_val[VI_DEFAULT] = buf; + options[opt_idx].def_val = buf; options[opt_idx].flags |= P_DEF_ALLOCED; } else { xfree(buf); // cannot happen @@ -490,17 +483,17 @@ void set_init_1(bool clean_arg) #endif false); - char *backupdir = stdpaths_user_data_subpath("backup", 0, true); + char *backupdir = stdpaths_user_data_subpath("backup", 2, true); const size_t backupdir_len = strlen(backupdir); backupdir = xrealloc(backupdir, backupdir_len + 3); memmove(backupdir + 2, backupdir, backupdir_len + 1); memmove(backupdir, ".,", 2); - set_string_default("viewdir", stdpaths_user_data_subpath("view", 0, true), - true); set_string_default("backupdir", backupdir, true); + set_string_default("viewdir", stdpaths_user_data_subpath("view", 2, true), + true); set_string_default("directory", stdpaths_user_data_subpath("swap", 2, true), true); - set_string_default("undodir", stdpaths_user_data_subpath("undo", 0, true), + set_string_default("undodir", stdpaths_user_data_subpath("undo", 2, true), true); // Set default for &runtimepath. All necessary expansions are performed in // this function. @@ -526,7 +519,7 @@ void set_init_1(bool clean_arg) check_win_options(curwin); check_options(); - // Set all options to their Vim default + // Set all options to their default value set_options_default(OPT_FREE); // set 'laststatus' @@ -562,15 +555,10 @@ void set_init_1(bool clean_arg) if (p != NULL) { p = xstrdup(p); *(char **)options[opt_idx].var = p; - /* VIMEXP - * Defaults for all expanded options are currently the same for Vi - * and Vim. When this changes, add some code here! Also need to - * split P_DEF_ALLOCED in two. - */ if (options[opt_idx].flags & P_DEF_ALLOCED) { - xfree(options[opt_idx].def_val[VI_DEFAULT]); + xfree(options[opt_idx].def_val); } - options[opt_idx].def_val[VI_DEFAULT] = (char_u *)p; + options[opt_idx].def_val = (char_u *)p; options[opt_idx].flags |= P_DEF_ALLOCED; } } @@ -613,39 +601,34 @@ void set_init_1(bool clean_arg) /// Set an option to its default value. /// This does not take care of side effects! -static void -set_option_default( - int opt_idx, - int opt_flags, // OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL - int compatible // use Vi default value -) +/// +/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL +static void set_option_default(int opt_idx, int opt_flags) { char_u *varp; // pointer to variable for current option - int dvi; // index in def_val[] int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; varp = get_varp_scope(&(options[opt_idx]), both ? OPT_LOCAL : opt_flags); uint32_t flags = options[opt_idx].flags; if (varp != NULL) { // skip hidden option, nothing to do for it - dvi = ((flags & P_VI_DEF) || compatible) ? VI_DEFAULT : VIM_DEFAULT; if (flags & P_STRING) { /* Use set_string_option_direct() for local options to handle * freeing and allocating the value. */ if (options[opt_idx].indir != PV_NONE) { set_string_option_direct(NULL, opt_idx, - options[opt_idx].def_val[dvi], opt_flags, 0); + options[opt_idx].def_val, opt_flags, 0); } else { if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) { free_string_option(*(char_u **)(varp)); } - *(char_u **)varp = options[opt_idx].def_val[dvi]; + *(char_u **)varp = options[opt_idx].def_val; options[opt_idx].flags &= ~P_ALLOCED; } } else if (flags & P_NUM) { if (options[opt_idx].indir == PV_SCROLL) { win_comp_scroll(curwin); } else { - long def_val = (long)options[opt_idx].def_val[dvi]; + long def_val = (long)options[opt_idx].def_val; if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) { // 'scrolloff' and 'sidescrolloff' local values have a @@ -661,7 +644,7 @@ set_option_default( } } } else { // P_BOOL - *(int *)varp = (int)(intptr_t)options[opt_idx].def_val[dvi]; + *(int *)varp = (int)(intptr_t)options[opt_idx].def_val; #ifdef UNIX // 'modeline' defaults to off for root if (options[opt_idx].indir == PV_ML && getuid() == ROOT_UID) { @@ -691,7 +674,7 @@ set_options_default( { for (int i = 0; options[i].fullname; i++) { if (!(options[i].flags & P_NODEFAULT)) { - set_option_default(i, opt_flags, false); + set_option_default(i, opt_flags); } } @@ -715,10 +698,10 @@ static void set_string_default(const char *name, char *val, bool allocated) int opt_idx = findoption(name); if (opt_idx >= 0) { if (options[opt_idx].flags & P_DEF_ALLOCED) { - xfree(options[opt_idx].def_val[VI_DEFAULT]); + xfree(options[opt_idx].def_val); } - options[opt_idx].def_val[VI_DEFAULT] = allocated + options[opt_idx].def_val = allocated ? (char_u *)val : (char_u *)xstrdup(val); options[opt_idx].flags |= P_DEF_ALLOCED; @@ -765,7 +748,7 @@ void set_number_default(char *name, long val) opt_idx = findoption(name); if (opt_idx >= 0) { - options[opt_idx].def_val[VI_DEFAULT] = (char_u *)(intptr_t)val; + options[opt_idx].def_val = (char_u *)(intptr_t)val; } } @@ -780,11 +763,11 @@ void free_all_options(void) free_string_option(*(char_u **)options[i].var); } if (options[i].flags & P_DEF_ALLOCED) { - free_string_option(options[i].def_val[VI_DEFAULT]); + free_string_option(options[i].def_val); } } else if (options[i].var != VAR_WIN && (options[i].flags & P_STRING)) { // buffer-local option: free global value - free_string_option(*(char_u **)options[i].var); + clear_string_option((char_u **)options[i].var); } } } @@ -803,7 +786,7 @@ void set_init_2(bool headless) // which results in the actual value computed from the window height. idx = findoption("scroll"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { - set_option_default(idx, OPT_LOCAL, false); + set_option_default(idx, OPT_LOCAL); } comp_col(); @@ -849,11 +832,11 @@ void set_init_3(void) ) { if (do_sp) { p_sp = (char_u *)"|& tee"; - options[idx_sp].def_val[VI_DEFAULT] = p_sp; + options[idx_sp].def_val = p_sp; } if (do_srr) { p_srr = (char_u *)">&"; - options[idx_srr].def_val[VI_DEFAULT] = p_srr; + options[idx_srr].def_val = p_srr; } } else if (fnamecmp(p, "sh") == 0 || fnamecmp(p, "ksh") == 0 @@ -869,17 +852,17 @@ void set_init_3(void) // Always use POSIX shell style redirection if we reach this if (do_sp) { p_sp = (char_u *)"2>&1| tee"; - options[idx_sp].def_val[VI_DEFAULT] = p_sp; + options[idx_sp].def_val = p_sp; } if (do_srr) { p_srr = (char_u *)">%s 2>&1"; - options[idx_srr].def_val[VI_DEFAULT] = p_srr; + options[idx_srr].def_val = p_srr; } } xfree(p); } - if (BUFEMPTY()) { + if (buf_is_empty(curbuf)) { int idx_ffs = findoption_len(S_LEN("ffs")); // Apply the first entry of 'fileformats' to the initial buffer. @@ -940,12 +923,12 @@ void set_title_defaults(void) */ idx1 = findoption("title"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { - options[idx1].def_val[VI_DEFAULT] = (char_u *)(intptr_t)0; + options[idx1].def_val = (char_u *)(intptr_t)0; p_title = 0; } idx1 = findoption("icon"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { - options[idx1].def_val[VI_DEFAULT] = (char_u *)(intptr_t)0; + options[idx1].def_val = (char_u *)(intptr_t)0; p_icon = 0; } } @@ -985,7 +968,6 @@ int do_set( int adding; // "opt+=arg" int prepending; // "opt^=arg" int removing; // "opt-=arg" - int cp_val = 0; if (*arg == NUL) { showoptions(0, opt_flags); @@ -1156,13 +1138,10 @@ int do_set( if (vim_strchr((char_u *)"?=:!&<", nextchar) != NULL) { arg += len; - cp_val = false; if (nextchar == '&' && arg[1] == 'v' && arg[2] == 'i') { - if (arg[3] == 'm') { // "opt&vim": set to Vim default - cp_val = false; + if (arg[3] == 'm') { // "opt&vim": set to Vim default arg += 3; - } else { // "opt&vi": set to Vi default - cp_val = true; + } else { // "opt&vi": set to Vi default arg += 2; } } @@ -1173,10 +1152,10 @@ int do_set( } } - /* - * allow '=' and ':' as MSDOS command.com allows only one - * '=' character per "set" command line. grrr. (jw) - */ + // + // allow '=' and ':' as MS-DOS command.com allows only one + // '=' character per "set" command line. grrr. (jw) + // if (nextchar == '?' || (prefix == 1 && vim_strchr((char_u *)"=:&<", nextchar) == NULL @@ -1229,9 +1208,7 @@ int do_set( if (nextchar == '!') { value = *(int *)(varp) ^ 1; } else if (nextchar == '&') { - value = (int)(intptr_t)options[opt_idx].def_val[ - ((flags & P_VI_DEF) || cp_val) - ? VI_DEFAULT : VIM_DEFAULT]; + value = (int)(intptr_t)options[opt_idx].def_val; } else if (nextchar == '<') { // For 'autoread' -1 means to use global value. if ((int *)varp == &curbuf->b_p_ar @@ -1276,8 +1253,7 @@ int do_set( // other error arg++; if (nextchar == '&') { - value = (long)(intptr_t)options[opt_idx].def_val[ - ((flags & P_VI_DEF) || cp_val) ? VI_DEFAULT : VIM_DEFAULT]; + value = (long)(intptr_t)options[opt_idx].def_val; } else if (nextchar == '<') { // For 'undolevels' NO_LOCAL_UNDOLEVEL means to // use the global value. @@ -1300,9 +1276,9 @@ int do_set( } } else if (*arg == '-' || ascii_isdigit(*arg)) { // Allow negative, octal and hex numbers. - vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0); - if (arg[i] != NUL && !ascii_iswhite(arg[i])) { - errmsg = e_invarg; + vim_str2nr(arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true); + if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) { + errmsg = (char_u *)N_("E521: Number required after ="); goto skip; } } else { @@ -1354,14 +1330,12 @@ int do_set( origval = oldval; } - if (nextchar == '&') { // set to default val - newval = options[opt_idx].def_val[ - ((flags & P_VI_DEF) || cp_val) - ? VI_DEFAULT : VIM_DEFAULT]; - /* expand environment variables and ~ (since the - * default value was already expanded, only - * required when an environment variable was set - * later */ + if (nextchar == '&') { // set to default val + newval = options[opt_idx].def_val; + // expand environment variables and ~ (since the + // default value was already expanded, only + // required when an environment variable was set + // later new_value_alloced = true; if (newval == NULL) { newval = empty_option; @@ -1432,22 +1406,19 @@ int do_set( *errbuf = NUL; i = getdigits_int(&arg, true, 0); if (i & 1) { - STRCAT(errbuf, "b,"); + STRLCAT(errbuf, "b,", sizeof(errbuf)); } if (i & 2) { - STRCAT(errbuf, "s,"); + STRLCAT(errbuf, "s,", sizeof(errbuf)); } if (i & 4) { - STRCAT(errbuf, "h,l,"); + STRLCAT(errbuf, "h,l,", sizeof(errbuf)); } if (i & 8) { - STRCAT(errbuf, "<,>,"); + STRLCAT(errbuf, "<,>,", sizeof(errbuf)); } if (i & 16) { - STRCAT(errbuf, "[,],"); - } - if (*errbuf != NUL) { // remove trailing , - errbuf[STRLEN(errbuf) - 1] = NUL; + STRLCAT(errbuf, "[,],", sizeof(errbuf)); } save_arg = arg; arg = errbuf; @@ -1986,6 +1957,7 @@ static void didset_options(void) briopt_check(curwin); // initialize the table for 'breakat'. fill_breakat_flags(); + fill_culopt_flags(NULL, curwin); } // More side effects of setting options. @@ -2435,6 +2407,11 @@ did_set_string_option( os_setenv("VIMRUNTIME", "", 1); didset_vimruntime = false; } + } else if (varp == &curwin->w_p_culopt + || gvarp == &curwin->w_allbuf_opt.wo_culopt) { // 'cursorlineopt' + if (**varp == NUL || fill_culopt_flags(*varp, curwin) != OK) { + errmsg = e_invarg; + } } else if (varp == &curwin->w_p_cc) { // 'colorcolumn' errmsg = check_colorcolumn(curwin); } else if (varp == &p_hlg) { // 'helplang' @@ -2718,7 +2695,7 @@ ambw_end: : opt_idx); // Update free_oldval now that we have the opt_idx for 'shada', otherwise // there would be a disconnect between the check for P_ALLOCED at the start - // of the function and the set of P_ALLOCED at the end of the fuction. + // of the function and the set of P_ALLOCED at the end of the function. free_oldval = (options[opt_idx].flags & P_ALLOCED); for (s = p_shada; *s; ) { // Check it's a valid character @@ -2763,10 +2740,11 @@ ambw_end: if (*p_shada && errmsg == NULL && get_shada_parameter('\'') < 0) { errmsg = (char_u *)N_("E528: Must specify a ' value"); } - } else if (varp == &p_sbr) { // 'showbreak' - for (s = p_sbr; *s; ) { + } else if (gvarp == &p_sbr) { // 'showbreak' + for (s = *varp; *s; ) { if (ptr2cells(s) != 1) { - errmsg = (char_u *)N_("E595: contains unprintable or wide character"); + errmsg = (char_u *)N_( + "E595: 'showbreak' contains unprintable or wide character"); } MB_PTR_ADV(s); } @@ -3182,6 +3160,10 @@ ambw_end: } } } + } else if (varp == &p_qftf) { + if (!qf_process_qftf_option()) { + errmsg = e_invarg; + } } else { // Options that are a list of flags. p = NULL; @@ -3832,22 +3814,19 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, // any changes in between. if (curbuf->b_p_udf || p_udf) { char_u hash[UNDO_HASH_SIZE]; - buf_T *save_curbuf = curbuf; FOR_ALL_BUFFERS(bp) { - curbuf = bp; // When 'undofile' is set globally: for every buffer, otherwise // only for the current buffer: Try to read in the undofile, // if one exists, the buffer wasn't changed and the buffer was // loaded - if ((curbuf == save_curbuf + if ((curbuf == bp || (opt_flags & OPT_GLOBAL) || opt_flags == 0) - && !curbufIsChanged() && curbuf->b_ml.ml_mfp != NULL) { - u_compute_hash(hash); - u_read_undo(NULL, hash, curbuf->b_fname); + && !bufIsChanged(bp) && bp->b_ml.ml_mfp != NULL) { + u_compute_hash(bp, hash); + u_read_undo(NULL, hash, bp->b_fname); } } - curbuf = save_curbuf; } } else if ((int *)varp == &curbuf->b_p_ro) { // when 'readonly' is reset globally, also reset readonlymode @@ -5089,20 +5068,17 @@ showoptions( /// Return true if option "p" has its default value. static int optval_default(vimoption_T *p, char_u *varp) { - int dvi; - if (varp == NULL) { return true; // hidden option is always at default } - dvi = (p->flags & P_VI_DEF) ? VI_DEFAULT : VIM_DEFAULT; if (p->flags & P_NUM) { - return *(long *)varp == (long)(intptr_t)p->def_val[dvi]; + return *(long *)varp == (long)(intptr_t)p->def_val; } if (p->flags & P_BOOL) { - return *(int *)varp == (int)(intptr_t)p->def_val[dvi]; + return *(int *)varp == (int)(intptr_t)p->def_val; } // P_STRING - return STRCMP(*(char_u **)varp, p->def_val[dvi]) == 0; + return STRCMP(*(char_u **)varp, p->def_val) == 0; } /// Send update to UIs with values of UI relevant options @@ -5358,7 +5334,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, home_replace(NULL, *valuep, buf, size, false); // If the option value is longer than MAXPATHL, we need to append - // earch comma separated part of the option sperately, so that it + // search comma separated part of the option separately, so that it // can be expanded when read back. if (size >= MAXPATHL && (flags & P_COMMA) != 0 && vim_strchr(*valuep, ',') != NULL) { @@ -5370,7 +5346,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, } p = buf; while (*p != NUL) { - // for each comma seperated option part, append value to + // for each comma separated option part, append value to // the option, :set rtp+=value if (fprintf(fd, "%s %s+=", cmd, name) < 0) { goto fail; @@ -5548,6 +5524,9 @@ void unset_global_local_option(char *name, void *from) case PV_MP: clear_string_option(&buf->b_p_mp); break; + case PV_SBR: + clear_string_option(&((win_T *)from)->w_p_sbr); + break; case PV_STL: clear_string_option(&((win_T *)from)->w_p_stl); break; @@ -5601,6 +5580,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags) case PV_DICT: return (char_u *)&(curbuf->b_p_dict); case PV_TSR: return (char_u *)&(curbuf->b_p_tsr); case PV_TFU: return (char_u *)&(curbuf->b_p_tfu); + case PV_SBR: return (char_u *)&(curwin->w_p_sbr); case PV_STL: return (char_u *)&(curwin->w_p_stl); case PV_UL: return (char_u *)&(curbuf->b_p_ul); case PV_LW: return (char_u *)&(curbuf->b_p_lw); @@ -5660,6 +5640,8 @@ static char_u *get_varp(vimoption_T *p) ? (char_u *)&(curbuf->b_p_gp) : p->var; case PV_MP: return *curbuf->b_p_mp != NUL ? (char_u *)&(curbuf->b_p_mp) : p->var; + case PV_SBR: return *curwin->w_p_sbr != NUL + ? (char_u *)&(curwin->w_p_sbr) : p->var; case PV_STL: return *curwin->w_p_stl != NUL ? (char_u *)&(curwin->w_p_stl) : p->var; case PV_UL: return curbuf->b_p_ul != NO_LOCAL_UNDOLEVEL @@ -5678,6 +5660,7 @@ static char_u *get_varp(vimoption_T *p) case PV_SPELL: return (char_u *)&(curwin->w_p_spell); case PV_CUC: return (char_u *)&(curwin->w_p_cuc); case PV_CUL: return (char_u *)&(curwin->w_p_cul); + case PV_CULOPT: return (char_u *)&(curwin->w_p_culopt); case PV_CC: return (char_u *)&(curwin->w_p_cc); case PV_DIFF: return (char_u *)&(curwin->w_p_diff); case PV_FDC: return (char_u *)&(curwin->w_p_fdc); @@ -5812,6 +5795,7 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_nuw = from->wo_nuw; to->wo_rl = from->wo_rl; to->wo_rlc = vim_strsave(from->wo_rlc); + to->wo_sbr = vim_strsave(from->wo_sbr); to->wo_stl = vim_strsave(from->wo_stl); to->wo_wrap = from->wo_wrap; to->wo_wrap_save = from->wo_wrap_save; @@ -5825,6 +5809,7 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_spell = from->wo_spell; to->wo_cuc = from->wo_cuc; to->wo_cul = from->wo_cul; + to->wo_culopt = vim_strsave(from->wo_culopt); to->wo_cc = vim_strsave(from->wo_cc); to->wo_diff = from->wo_diff; to->wo_diff_saved = from->wo_diff_saved; @@ -5874,7 +5859,9 @@ static void check_winopt(winopt_T *wop) check_string_option(&wop->wo_fmr); check_string_option(&wop->wo_scl); check_string_option(&wop->wo_rlc); + check_string_option(&wop->wo_sbr); check_string_option(&wop->wo_stl); + check_string_option(&wop->wo_culopt); check_string_option(&wop->wo_cc); check_string_option(&wop->wo_cocu); check_string_option(&wop->wo_briopt); @@ -5896,7 +5883,9 @@ void clear_winopt(winopt_T *wop) clear_string_option(&wop->wo_fmr); clear_string_option(&wop->wo_scl); clear_string_option(&wop->wo_rlc); + clear_string_option(&wop->wo_sbr); clear_string_option(&wop->wo_stl); + clear_string_option(&wop->wo_culopt); clear_string_option(&wop->wo_cc); clear_string_option(&wop->wo_cocu); clear_string_option(&wop->wo_briopt); @@ -5909,6 +5898,7 @@ void didset_window_options(win_T *wp) { check_colorcolumn(wp); briopt_check(wp); + fill_culopt_flags(NULL, wp); set_chars_option(wp, &wp->w_p_fcs, true); set_chars_option(wp, &wp->w_p_lcs, true); parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl @@ -6151,7 +6141,7 @@ void reset_modifiable(void) p_ma = false; opt_idx = findoption("ma"); if (opt_idx >= 0) { - options[opt_idx].def_val[VI_DEFAULT] = false; + options[opt_idx].def_val = false; } } @@ -6850,7 +6840,7 @@ static void paste_option_changed(void) /// vimrc_found() - Called when a vimrc or "VIMINIT" has been found. /// -/// Set the values for options that didn't get set yet to the Vim defaults. +/// Set the values for options that didn't get set yet to the defaults. /// When "fname" is not NULL, use it to set $"envname" when it wasn't set yet. void vimrc_found(char_u *fname, char_u *envname) { @@ -6916,6 +6906,49 @@ static void fill_breakat_flags(void) } } +/// fill_culopt_flags() -- called when 'culopt' changes value +static int fill_culopt_flags(char_u *val, win_T *wp) +{ + char_u *p; + char_u culopt_flags_new = 0; + + if (val == NULL) { + p = wp->w_p_culopt; + } else { + p = val; + } + while (*p != NUL) { + if (STRNCMP(p, "line", 4) == 0) { + p += 4; + culopt_flags_new |= CULOPT_LINE; + } else if (STRNCMP(p, "both", 4) == 0) { + p += 4; + culopt_flags_new |= CULOPT_LINE | CULOPT_NBR; + } else if (STRNCMP(p, "number", 6) == 0) { + p += 6; + culopt_flags_new |= CULOPT_NBR; + } else if (STRNCMP(p, "screenline", 10) == 0) { + p += 10; + culopt_flags_new |= CULOPT_SCRLINE; + } + + if (*p != ',' && *p != NUL) { + return FAIL; + } + if (*p == ',') { + p++; + } + } + + // Can't have both "line" and "screenline". + if ((culopt_flags_new & CULOPT_LINE) && (culopt_flags_new & CULOPT_SCRLINE)) { + return FAIL; + } + wp->w_p_culopt_flags = culopt_flags_new; + + return OK; +} + /// Check an option that can be a range of string values. /// /// Return OK for correct value, FAIL otherwise. @@ -7401,6 +7434,7 @@ static bool briopt_check(win_T *wp) int bri_shift = 0; int bri_min = 20; bool bri_sbr = false; + int bri_list = 0; char_u *p = wp->w_p_briopt; while (*p != NUL) @@ -7420,6 +7454,9 @@ static bool briopt_check(win_T *wp) { p += 3; bri_sbr = true; + } else if (STRNCMP(p, "list:", 5) == 0) { + p += 5; + bri_list = (int)getdigits(&p, false, 0); } if (*p != ',' && *p != NUL) { return false; @@ -7432,6 +7469,7 @@ static bool briopt_check(win_T *wp) wp->w_briopt_shift = bri_shift; wp->w_briopt_min = bri_min; wp->w_briopt_sbr = bri_sbr; + wp->w_briopt_list = bri_list; return true; } @@ -7444,6 +7482,22 @@ unsigned int get_bkc_value(buf_T *buf) return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags; } +/// Get the local or global value of 'showbreak'. +/// +/// @param win If not NULL, the window to get the local option from; global +/// otherwise. +char_u *get_showbreak_value(win_T *const win) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (win->w_p_sbr == NULL || *win->w_p_sbr == NUL) { + return p_sbr; + } + if (STRCMP(win->w_p_sbr, "NONE") == 0) { + return empty_option; + } + return win->w_p_sbr; +} + /// Return the current end-of-line type: EOL_DOS, EOL_UNIX or EOL_MAC. int get_fileformat(const buf_T *buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL @@ -7744,8 +7798,7 @@ static Dictionary vimoption2dict(vimoption_T *opt) const char *type; Object def; // TODO(bfredl): do you even nocp? - char_u *def_val = opt->def_val[(opt->flags & P_VI_DEF) - ? VI_DEFAULT : VIM_DEFAULT]; + char_u *def_val = opt->def_val; if (opt->flags & P_STRING) { type = "string"; def = CSTR_TO_OBJ(def_val ? (char *)def_val : ""); @@ -7760,6 +7813,7 @@ static Dictionary vimoption2dict(vimoption_T *opt) } PUT(dict, "type", CSTR_TO_OBJ(type)); PUT(dict, "default", def); + PUT(dict, "allows_duplicates", BOOL(!(opt->flags & P_NODUP))); return dict; } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index beb62a6a0b..e588d3f373 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -276,10 +276,10 @@ enum { }) // flags used for parsed 'wildmode' -#define WIM_FULL 1 -#define WIM_LONGEST 2 -#define WIM_LIST 4 -#define WIM_BUFLASTUSED 8 +#define WIM_FULL 0x01 +#define WIM_LONGEST 0x02 +#define WIM_LIST 0x04 +#define WIM_BUFLASTUSED 0x08 // arguments for can_bs() // each defined char should be unique over all values @@ -291,6 +291,11 @@ enum { #define BS_START 's' // "Start" #define BS_NOSTOP 'p' // "nostoP +// flags for the 'culopt' option +#define CULOPT_LINE 0x01 // Highlight complete line +#define CULOPT_SCRLINE 0x02 // Highlight screen line +#define CULOPT_NBR 0x04 // Highlight Number column + #define LISPWORD_VALUE \ "defun,define,defmacro,set!,lambda,if,case,let,flet,let*,letrec,do,do*,define-syntax,let-syntax,letrec-syntax,destructuring-bind,defpackage,defparameter,defstruct,deftype,defvar,do-all-symbols,do-external-symbols,do-symbols,dolist,dotimes,ecase,etypecase,eval-when,labels,macrolet,multiple-value-bind,multiple-value-call,multiple-value-prog1,multiple-value-setq,prog1,progv,typecase,unless,unwind-protect,when,with-input-from-string,with-open-file,with-open-stream,with-output-to-string,with-package-iterator,define-condition,handler-bind,handler-case,restart-bind,restart-case,with-simple-restart,store-value,use-value,muffle-warning,abort,continue,with-slots,with-slots*,with-accessors,with-accessors*,defclass,defmethod,print-unreadable-object" @@ -459,7 +464,6 @@ EXTERN char_u *p_pmcs; // 'printmbcharset' EXTERN char_u *p_pfn; // 'printfont' EXTERN char_u *p_popt; // 'printoptions' EXTERN char_u *p_header; // 'printheader' -EXTERN int p_prompt; // 'prompt' EXTERN char_u *p_guicursor; // 'guicursor' EXTERN char_u *p_guifont; // 'guifont' EXTERN char_u *p_guifontwide; // 'guifontwide' @@ -740,11 +744,11 @@ EXTERN long p_wd; // 'writedelay' EXTERN int p_force_on; ///< options that cannot be turned off. EXTERN int p_force_off; ///< options that cannot be turned on. -/* - * "indir" values for buffer-local opions. - * These need to be defined globally, so that the BV_COUNT can be used with - * b_p_scriptID[]. - */ +// +// "indir" values for buffer-local options. +// These need to be defined globally, so that the BV_COUNT can be used with +// b_p_scriptID[]. +// enum { BV_AI = 0 , BV_AR @@ -871,7 +875,9 @@ enum { , WV_SPELL , WV_CUC , WV_CUL + , WV_CULOPT , WV_CC + , WV_SBR , WV_STL , WV_WFH , WV_WFW diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 86dec74f56..8b9cdefd57 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -6,11 +6,11 @@ -- type='number', list=nil, scope={'global'}, -- deny_duplicates=nil, -- enable_if=nil, --- defaults={condition=nil, if_true={vi=224, vim=0}, if_false=nil}, +-- defaults={condition=nil, if_true=224, if_false=nil}, -- secure=nil, gettext=nil, noglob=nil, normal_fname_chars=nil, -- pri_mkrc=nil, deny_in_modelines=nil, normal_dname_chars=nil, -- modelineexpr=nil, --- expand=nil, nodefault=nil, no_mkrc=nil, vi_def=true, vim=true, +-- expand=nil, nodefault=nil, no_mkrc=nil, -- alloced=nil, -- save_pv_indir=nil, -- redraw={'curswant'}, @@ -21,7 +21,6 @@ -- scopes: global, buffer, window -- redraw options: statuslines, current_window, curent_window_only, -- current_buffer, all_windows, everything, curswant --- default: {vi=…[, vim=…]} -- defaults: {condition=#if condition, if_true=default, if_false=default} -- #if condition: -- string: #ifdef string @@ -55,125 +54,109 @@ return { full_name='aleph', abbreviation='al', short_desc=N_("ASCII code of the letter Aleph (Hebrew)"), type='number', scope={'global'}, - vi_def=true, redraw={'curswant'}, varname='p_aleph', - defaults={if_true={vi=224}} + defaults={if_true=224} }, { full_name='arabic', abbreviation='arab', short_desc=N_("Arabic as a default second language"), type='bool', scope={'window'}, - vi_def=true, - vim=true, redraw={'curswant'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='arabicshape', abbreviation='arshape', short_desc=N_("do shaping for Arabic characters"), type='bool', scope={'global'}, - vi_def=true, - vim=true, redraw={'all_windows', 'ui_option'}, varname='p_arshape', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='allowrevins', abbreviation='ari', short_desc=N_("allow CTRL-_ in Insert and Command-line mode"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_ari', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='ambiwidth', abbreviation='ambw', short_desc=N_("what to do with Unicode chars of ambiguous width"), type='string', scope={'global'}, - vi_def=true, redraw={'all_windows', 'ui_option'}, varname='p_ambw', - defaults={if_true={vi="single"}} + defaults={if_true="single"} }, { full_name='autochdir', abbreviation='acd', short_desc=N_("change directory to the file in the current window"), type='bool', scope={'global'}, - vi_def=true, varname='p_acd', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='autoindent', abbreviation='ai', short_desc=N_("take indent for new line from previous line"), type='bool', scope={'buffer'}, varname='p_ai', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='autoread', abbreviation='ar', short_desc=N_("autom. read file when changed outside of Vim"), type='bool', scope={'global', 'buffer'}, varname='p_ar', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='autowrite', abbreviation='aw', short_desc=N_("automatically write file if changed"), type='bool', scope={'global'}, - vi_def=true, varname='p_aw', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='autowriteall', abbreviation='awa', short_desc=N_("as 'autowrite', but works with more commands"), type='bool', scope={'global'}, - vi_def=true, varname='p_awa', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='background', abbreviation='bg', short_desc=N_("\"dark\" or \"light\", used for highlight colors"), type='string', scope={'global'}, - vim=true, redraw={'all_windows'}, varname='p_bg', - defaults={if_true={vi="light",vim="dark"}} + defaults={if_true="dark"} }, { full_name='backspace', abbreviation='bs', short_desc=N_("how backspace works at start of line"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vim=true, varname='p_bs', - defaults={if_true={vi="", vim="indent,eol,start"}} + defaults={if_true="indent,eol,start"} }, { full_name='backup', abbreviation='bk', short_desc=N_("keep backup file after overwriting a file"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_bk', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='backupcopy', abbreviation='bkc', short_desc=N_("make backup as a copy, don't rename the file"), type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, - vim=true, varname='p_bkc', defaults={ condition='UNIX', - if_true={vi="yes", vim="auto"}, - if_false={vi="auto", vim="auto"} + if_true="auto", + if_false="auto" }, }, { @@ -182,90 +165,79 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, - vi_def=true, expand='nodefault', varname='p_bdir', - defaults={if_true={vi=''}} + defaults={if_true=''} }, { full_name='backupext', abbreviation='bex', short_desc=N_("extension used for the backup file"), type='string', scope={'global'}, normal_fname_chars=true, - vi_def=true, varname='p_bex', - defaults={if_true={vi="~"}} + defaults={if_true="~"} }, { full_name='backupskip', abbreviation='bsk', short_desc=N_("no backup for files that match these patterns"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_bsk', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='belloff', abbreviation='bo', short_desc=N_("do not ring the bell for these reasons"), type='string', list='comma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_bo', - defaults={if_true={vi="all"}} + defaults={if_true="all"} }, { full_name='binary', abbreviation='bin', short_desc=N_("read/write/edit file in binary mode"), type='bool', scope={'buffer'}, - vi_def=true, redraw={'statuslines'}, varname='p_bin', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='bomb', short_desc=N_("a Byte Order Mark to the file"), type='bool', scope={'buffer'}, no_mkrc=true, - vi_def=true, redraw={'statuslines'}, varname='p_bomb', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='breakat', abbreviation='brk', short_desc=N_("characters that may cause a line break"), type='string', list='flags', scope={'global'}, - vi_def=true, redraw={'all_windows'}, varname='p_breakat', - defaults={if_true={vi=" \t!@*-+;:,./?"}} + defaults={if_true=" \t!@*-+;:,./?"} }, { full_name='breakindent', abbreviation='bri', short_desc=N_("wrapped line repeats indent"), type='bool', scope={'window'}, - vi_def=true, - vim=true, redraw={'current_window'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='breakindentopt', abbreviation='briopt', short_desc=N_("settings for 'breakindent'"), type='string', list='onecomma', scope={'window'}, deny_duplicates=true, - vi_def=true, alloced=true, redraw={'current_buffer'}, - defaults={if_true={vi=""}}, + defaults={if_true=""}, }, { full_name='browsedir', abbreviation='bsdir', short_desc=N_("which directory to start browsing in"), type='string', scope={'global'}, - vi_def=true, enable_if=false, }, { @@ -273,56 +245,51 @@ return { short_desc=N_("what to do when buffer is no longer in window"), type='string', scope={'buffer'}, noglob=true, - vi_def=true, alloced=true, varname='p_bh', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='buflisted', abbreviation='bl', short_desc=N_("whether the buffer shows up in the buffer list"), type='bool', scope={'buffer'}, noglob=true, - vi_def=true, varname='p_bl', - defaults={if_true={vi=1}} + defaults={if_true=1} }, { full_name='buftype', abbreviation='bt', short_desc=N_("special type of buffer"), type='string', scope={'buffer'}, noglob=true, - vi_def=true, alloced=true, varname='p_bt', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='casemap', abbreviation='cmp', short_desc=N_("specifies how case of letters is changed"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_cmp', - defaults={if_true={vi="internal,keepascii"}} + defaults={if_true="internal,keepascii"} }, { full_name='cdpath', abbreviation='cd', short_desc=N_("list of directories searched with \":cd\""), type='string', list='comma', scope={'global'}, deny_duplicates=true, - vi_def=true, expand=true, secure=true, varname='p_cdpath', - defaults={if_true={vi=",,"}} + defaults={if_true=",,"} }, { full_name='cedit', short_desc=N_("used to open the command-line window"), type='string', scope={'global'}, varname='p_cedit', - defaults={if_true={vi="", vim=macros('CTRL_F_STR')}} + defaults={if_true=macros('CTRL_F_STR')} }, { full_name='channel', @@ -331,121 +298,108 @@ return { no_mkrc=true, nodefault=true, varname='p_channel', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='charconvert', abbreviation='ccv', short_desc=N_("expression for character encoding conversion"), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_ccv', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='cindent', abbreviation='cin', short_desc=N_("do C program indenting"), type='bool', scope={'buffer'}, - vi_def=true, - vim=true, varname='p_cin', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='cinkeys', abbreviation='cink', short_desc=N_("keys that trigger indent when 'cindent' is set"), type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, - vi_def=true, alloced=true, varname='p_cink', - defaults={if_true={vi=indentkeys_default}} + defaults={if_true=indentkeys_default} }, { full_name='cinoptions', abbreviation='cino', short_desc=N_("how to do indenting when 'cindent' is set"), type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, - vi_def=true, alloced=true, varname='p_cino', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='cinwords', abbreviation='cinw', short_desc=N_("words where 'si' and 'cin' add an indent"), type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, - vi_def=true, alloced=true, varname='p_cinw', - defaults={if_true={vi="if,else,while,do,for,switch"}} + defaults={if_true="if,else,while,do,for,switch"} }, { full_name='clipboard', abbreviation='cb', short_desc=N_("use the clipboard as the unnamed register"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_cb', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='cmdheight', abbreviation='ch', short_desc=N_("number of lines to use for the command-line"), type='number', scope={'global'}, - vi_def=true, redraw={'all_windows'}, varname='p_ch', - defaults={if_true={vi=1}} + defaults={if_true=1} }, { full_name='cmdwinheight', abbreviation='cwh', short_desc=N_("height of the command-line window"), type='number', scope={'global'}, - vi_def=true, varname='p_cwh', - defaults={if_true={vi=7}} + defaults={if_true=7} }, { full_name='colorcolumn', abbreviation='cc', short_desc=N_("columns to highlight"), type='string', list='onecomma', scope={'window'}, deny_duplicates=true, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='columns', abbreviation='co', short_desc=N_("number of columns in the display"), type='number', scope={'global'}, no_mkrc=true, - vi_def=true, redraw={'everything'}, varname='p_columns', - defaults={if_true={vi=macros('DFLT_COLS')}} + defaults={if_true=macros('DFLT_COLS')} }, { full_name='comments', abbreviation='com', short_desc=N_("patterns that can start a comment line"), type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, - vi_def=true, alloced=true, redraw={'curswant'}, varname='p_com', - defaults={if_true={vi="s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-"}} + defaults={if_true="s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-"} }, { full_name='commentstring', abbreviation='cms', short_desc=N_("template for comments; used for fold marker"), type='string', scope={'buffer'}, - vi_def=true, alloced=true, redraw={'curswant'}, varname='p_cms', - defaults={if_true={vi="/*%s*/"}} + defaults={if_true="/*%s*/"} }, { full_name='compatible', abbreviation='cp', @@ -455,7 +409,7 @@ return { varname='p_force_off', -- pri_mkrc isn't needed here, optval_default() -- always returns TRUE for 'compatible' - defaults={if_true={vi=true, vim=false}} + defaults={if_true=false} }, { full_name='complete', abbreviation='cpt', @@ -464,193 +418,172 @@ return { deny_duplicates=true, alloced=true, varname='p_cpt', - defaults={if_true={vi=".,w,b,u,t,i", vim=".,w,b,u,t"}} + defaults={if_true=".,w,b,u,t"} }, { full_name='concealcursor', abbreviation='cocu', short_desc=N_("whether concealable text is hidden in cursor line"), type='string', scope={'window'}, - vi_def=true, alloced=true, redraw={'current_window'}, - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='conceallevel', abbreviation='cole', short_desc=N_("whether concealable text is shown or hidden"), type='number', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='completefunc', abbreviation='cfu', short_desc=N_("function to be used for Insert mode completion"), type='string', scope={'buffer'}, secure=true, - vi_def=true, alloced=true, varname='p_cfu', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='completeopt', abbreviation='cot', short_desc=N_("options for Insert mode completion"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_cot', - defaults={if_true={vi="menu,preview"}} + defaults={if_true="menu,preview"} }, { full_name='completeslash', abbreviation='csl', type='string', scope={'buffer'}, - vi_def=true, - vim=true, varname='p_csl', enable_if='BACKSLASH_IN_FILENAME', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='confirm', abbreviation='cf', short_desc=N_("ask what to do about unsaved/read-only files"), type='bool', scope={'global'}, - vi_def=true, varname='p_confirm', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='copyindent', abbreviation='ci', short_desc=N_("make 'autoindent' use existing indent structure"), type='bool', scope={'buffer'}, - vi_def=true, - vim=true, varname='p_ci', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='cpoptions', abbreviation='cpo', short_desc=N_("flags for Vi-compatible behavior"), type='string', list='flags', scope={'global'}, - vim=true, redraw={'all_windows'}, varname='p_cpo', - defaults={if_true={vi=macros('CPO_VI'), vim=macros('CPO_VIM')}} + defaults={if_true=macros('CPO_VIM')} }, { full_name='cscopepathcomp', abbreviation='cspc', short_desc=N_("how many components of the path to show"), type='number', scope={'global'}, - vi_def=true, - vim=true, varname='p_cspc', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='cscopeprg', abbreviation='csprg', short_desc=N_("command to execute cscope"), type='string', scope={'global'}, secure=true, - vi_def=true, expand=true, varname='p_csprg', - defaults={if_true={vi="cscope"}} + defaults={if_true="cscope"} }, { full_name='cscopequickfix', abbreviation='csqf', short_desc=N_("use quickfix window for cscope results"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_csqf', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='cscoperelative', abbreviation='csre', short_desc=N_("Use cscope.out path basename as prefix"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_csre', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='cscopetag', abbreviation='cst', short_desc=N_("use cscope for tag commands"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_cst', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='cscopetagorder', abbreviation='csto', short_desc=N_("determines \":cstag\" search order"), type='number', scope={'global'}, - vi_def=true, - vim=true, varname='p_csto', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='cscopeverbose', abbreviation='csverb', short_desc=N_("give messages when adding a cscope database"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_csverbose', - defaults={if_true={vi=1}} + defaults={if_true=1} }, { full_name='cursorbind', abbreviation='crb', short_desc=N_("move cursor in window as it moves in other windows"), type='bool', scope={'window'}, - vi_def=true, pv_name='p_crbind', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='cursorcolumn', abbreviation='cuc', short_desc=N_("highlight the screen column of the cursor"), type='bool', scope={'window'}, - vi_def=true, redraw={'current_window_only'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='cursorline', abbreviation='cul', short_desc=N_("highlight the screen line of the cursor"), type='bool', scope={'window'}, - vi_def=true, redraw={'current_window_only'}, - defaults={if_true={vi=false}} + defaults={if_true=false} + }, + { + full_name='cursorlineopt', abbreviation='culopt', + short_desc=N_("settings for 'cursorline'"), + type='string', list='onecomma', scope={'window'}, + deny_duplicates=true, + redraw={'current_window_only'}, + defaults={if_true="both"} }, { full_name='debug', short_desc=N_("to \"msg\" to see all error messages"), type='string', scope={'global'}, - vi_def=true, varname='p_debug', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='define', abbreviation='def', short_desc=N_("pattern to be used to find a macro definition"), type='string', scope={'global', 'buffer'}, - vi_def=true, alloced=true, redraw={'curswant'}, varname='p_def', - defaults={if_true={vi="^\\s*#\\s*define"}} + defaults={if_true="^\\s*#\\s*define"} }, { full_name='delcombine', abbreviation='deco', short_desc=N_("delete combining characters on their own"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_deco', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='dictionary', abbreviation='dict', @@ -658,49 +591,43 @@ return { type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, normal_dname_chars=true, - vi_def=true, expand=true, varname='p_dict', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='diff', short_desc=N_("diff mode for the current window"), type='bool', scope={'window'}, noglob=true, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='diffexpr', abbreviation='dex', short_desc=N_("expression used to obtain a diff file"), type='string', scope={'global'}, secure=true, - vi_def=true, redraw={'curswant'}, varname='p_dex', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='diffopt', abbreviation='dip', short_desc=N_("options for using diff mode"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, alloced=true, redraw={'current_window'}, varname='p_dip', - defaults={if_true={vi="internal,filler,closeoff"}} + defaults={if_true="internal,filler,closeoff"} }, { full_name='digraph', abbreviation='dg', short_desc=N_("enable the entering of digraphs in Insert mode"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_dg', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='directory', abbreviation='dir', @@ -708,188 +635,167 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, - vi_def=true, expand='nodefault', varname='p_dir', - defaults={if_true={vi=''}} + defaults={if_true=''} }, { full_name='display', abbreviation='dy', short_desc=N_("list of flags for how to display text"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vim=true, redraw={'all_windows'}, varname='p_dy', - defaults={if_true={vi="", vim="lastline,msgsep"}} + defaults={if_true="lastline,msgsep"} }, { full_name='eadirection', abbreviation='ead', short_desc=N_("in which direction 'equalalways' works"), type='string', scope={'global'}, - vi_def=true, varname='p_ead', - defaults={if_true={vi="both"}} + defaults={if_true="both"} }, { full_name='edcompatible', abbreviation='ed', short_desc=N_("No description"), type='bool', scope={'global'}, - vi_def=true, varname='p_force_off', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='emoji', abbreviation='emo', short_desc=N_("No description"), type='bool', scope={'global'}, - vi_def=true, redraw={'all_windows', 'ui_option'}, varname='p_emoji', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='encoding', abbreviation='enc', short_desc=N_("encoding used internally"), type='string', scope={'global'}, deny_in_modelines=true, - vi_def=true, varname='p_enc', - defaults={if_true={vi=macros('ENC_DFLT')}} + defaults={if_true=macros('ENC_DFLT')} }, { full_name='endofline', abbreviation='eol', short_desc=N_("write <EOL> for last line in file"), type='bool', scope={'buffer'}, no_mkrc=true, - vi_def=true, redraw={'statuslines'}, varname='p_eol', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='equalalways', abbreviation='ea', short_desc=N_("windows are automatically made the same size"), type='bool', scope={'global'}, - vi_def=true, redraw={'all_windows'}, varname='p_ea', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='equalprg', abbreviation='ep', short_desc=N_("external program to use for \"=\" command"), type='string', scope={'global', 'buffer'}, secure=true, - vi_def=true, expand=true, varname='p_ep', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='errorbells', abbreviation='eb', short_desc=N_("ring the bell for error messages"), type='bool', scope={'global'}, - vi_def=true, varname='p_eb', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='errorfile', abbreviation='ef', short_desc=N_("name of the errorfile for the QuickFix mode"), type='string', scope={'global'}, secure=true, - vi_def=true, expand=true, varname='p_ef', - defaults={if_true={vi=macros('DFLT_ERRORFILE')}} + defaults={if_true=macros('DFLT_ERRORFILE')} }, { full_name='errorformat', abbreviation='efm', short_desc=N_("description of the lines in the error file"), type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, - vi_def=true, varname='p_efm', - defaults={if_true={vi=macros('DFLT_EFM')}} + defaults={if_true=macros('DFLT_EFM')} }, { full_name='eventignore', abbreviation='ei', short_desc=N_("autocommand events that are ignored"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_ei', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='expandtab', abbreviation='et', short_desc=N_("use spaces when <Tab> is inserted"), type='bool', scope={'buffer'}, - vi_def=true, - vim=true, varname='p_et', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='exrc', abbreviation='ex', short_desc=N_("read .nvimrc and .exrc in the current directory"), type='bool', scope={'global'}, secure=true, - vi_def=true, varname='p_exrc', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='fileencoding', abbreviation='fenc', short_desc=N_("file encoding for multi-byte text"), type='string', scope={'buffer'}, no_mkrc=true, - vi_def=true, alloced=true, redraw={'statuslines', 'current_buffer'}, varname='p_fenc', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='fileencodings', abbreviation='fencs', short_desc=N_("automatically detected character encodings"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_fencs', - defaults={if_true={vi="ucs-bom,utf-8,default,latin1"}} + defaults={if_true="ucs-bom,utf-8,default,latin1"} }, { full_name='fileformat', abbreviation='ff', short_desc=N_("file format used for file I/O"), type='string', scope={'buffer'}, no_mkrc=true, - vi_def=true, alloced=true, redraw={'curswant', 'statuslines'}, varname='p_ff', - defaults={if_true={vi=macros('DFLT_FF')}} + defaults={if_true=macros('DFLT_FF')} }, { full_name='fileformats', abbreviation='ffs', short_desc=N_("automatically detected values for 'fileformat'"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vim=true, varname='p_ffs', - defaults={if_true={vi=macros('DFLT_FFS_VI'), vim=macros('DFLT_FFS_VIM')}} + defaults={if_true=macros('DFLT_FFS_VIM')} }, { full_name='fileignorecase', abbreviation='fic', short_desc=N_("ignore case when using file names"), type='bool', scope={'global'}, - vi_def=true, varname='p_fic', defaults={ condition='CASE_INSENSITIVE_FILENAME', - if_true={vi=true}, - if_false={vi=false}, + if_true=true, + if_false=false, } }, { @@ -898,235 +804,204 @@ return { type='string', scope={'buffer'}, noglob=true, normal_fname_chars=true, - vi_def=true, alloced=true, expand=true, varname='p_ft', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='fillchars', abbreviation='fcs', short_desc=N_("characters to use for displaying special items"), type='string', list='onecomma', scope={'global', 'window'}, deny_duplicates=true, - vi_def=true, alloced=true, redraw={'current_window'}, varname='p_fcs', - defaults={if_true={vi=''}} + defaults={if_true=''} }, { full_name='fixendofline', abbreviation='fixeol', short_desc=N_("make sure last line in file has <EOL>"), type='bool', scope={'buffer'}, - vi_def=true, redraw={'statuslines'}, varname='p_fixeol', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='foldclose', abbreviation='fcl', short_desc=N_("close a fold when the cursor leaves it"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, redraw={'current_window'}, varname='p_fcl', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='foldcolumn', abbreviation='fdc', short_desc=N_("width of the column used to indicate folds"), type='string', scope={'window'}, - vi_def=true, alloced=true, redraw={'current_window'}, - defaults={if_true={vi="0"}} + defaults={if_true="0"} }, { full_name='foldenable', abbreviation='fen', short_desc=N_("set to display all folds open"), type='bool', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='foldexpr', abbreviation='fde', short_desc=N_("expression used when 'foldmethod' is \"expr\""), type='string', scope={'window'}, - vi_def=true, - vim=true, modelineexpr=true, alloced=true, redraw={'current_window'}, - defaults={if_true={vi="0"}} + defaults={if_true="0"} }, { full_name='foldignore', abbreviation='fdi', short_desc=N_("ignore lines when 'foldmethod' is \"indent\""), type='string', scope={'window'}, - vi_def=true, - vim=true, alloced=true, redraw={'current_window'}, - defaults={if_true={vi="#"}} + defaults={if_true="#"} }, { full_name='foldlevel', abbreviation='fdl', short_desc=N_("close folds with a level higher than this"), type='number', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='foldlevelstart', abbreviation='fdls', short_desc=N_("'foldlevel' when starting to edit a file"), type='number', scope={'global'}, - vi_def=true, redraw={'curswant'}, varname='p_fdls', - defaults={if_true={vi=-1}} + defaults={if_true=-1} }, { full_name='foldmarker', abbreviation='fmr', short_desc=N_("markers used when 'foldmethod' is \"marker\""), type='string', list='onecomma', scope={'window'}, deny_duplicates=true, - vi_def=true, - vim=true, alloced=true, redraw={'current_window'}, - defaults={if_true={vi="{{{,}}}"}} + defaults={if_true="{{{,}}}"} }, { full_name='foldmethod', abbreviation='fdm', short_desc=N_("folding type"), type='string', scope={'window'}, - vi_def=true, - vim=true, alloced=true, redraw={'current_window'}, - defaults={if_true={vi="manual"}} + defaults={if_true="manual"} }, { full_name='foldminlines', abbreviation='fml', short_desc=N_("minimum number of lines for a fold to be closed"), type='number', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=1}} + defaults={if_true=1} }, { full_name='foldnestmax', abbreviation='fdn', short_desc=N_("maximum fold depth"), type='number', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=20}} + defaults={if_true=20} }, { full_name='foldopen', abbreviation='fdo', short_desc=N_("for which commands a fold will be opened"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, redraw={'curswant'}, varname='p_fdo', - defaults={if_true={vi="block,hor,mark,percent,quickfix,search,tag,undo"}} + defaults={if_true="block,hor,mark,percent,quickfix,search,tag,undo"} }, { full_name='foldtext', abbreviation='fdt', short_desc=N_("expression used to display for a closed fold"), type='string', scope={'window'}, - vi_def=true, - vim=true, modelineexpr=true, alloced=true, redraw={'current_window'}, - defaults={if_true={vi="foldtext()"}} + defaults={if_true="foldtext()"} }, { full_name='formatexpr', abbreviation='fex', short_desc=N_("expression used with \"gq\" command"), type='string', scope={'buffer'}, - vi_def=true, - vim=true, modelineexpr=true, alloced=true, varname='p_fex', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='formatoptions', abbreviation='fo', short_desc=N_("how automatic formatting is to be done"), type='string', list='flags', scope={'buffer'}, - vim=true, alloced=true, varname='p_fo', - defaults={if_true={vi=macros('DFLT_FO_VI'), vim=macros('DFLT_FO_VIM')}} + defaults={if_true=macros('DFLT_FO_VIM')} }, { full_name='formatlistpat', abbreviation='flp', short_desc=N_("pattern used to recognize a list header"), type='string', scope={'buffer'}, - vi_def=true, alloced=true, varname='p_flp', - defaults={if_true={vi="^\\s*\\d\\+[\\]:.)}\\t ]\\s*"}} + defaults={if_true="^\\s*\\d\\+[\\]:.)}\\t ]\\s*"} }, { full_name='formatprg', abbreviation='fp', short_desc=N_("name of external program used with \"gq\" command"), type='string', scope={'global', 'buffer'}, secure=true, - vi_def=true, expand=true, varname='p_fp', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='fsync', abbreviation='fs', short_desc=N_("whether to invoke fsync() after file write"), type='bool', scope={'global'}, secure=true, - vi_def=true, varname='p_fs', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='gdefault', abbreviation='gd', short_desc=N_("the \":substitute\" flag 'g' is default on"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_gd', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='grepformat', abbreviation='gfm', short_desc=N_("format of 'grepprg' output"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_gefm', - defaults={if_true={vi=macros('DFLT_GREPFORMAT')}} + defaults={if_true=macros('DFLT_GREPFORMAT')} }, { full_name='grepprg', abbreviation='gp', short_desc=N_("program to use for \":grep\""), type='string', scope={'global', 'buffer'}, secure=true, - vi_def=true, expand=true, varname='p_gp', defaults={ condition='WIN32', -- Add an extra file name so that grep will always -- insert a file name in the match line. */ - if_true={vi="findstr /n $* nul"}, - if_false={vi="grep -n $* /dev/null"} + if_true="findstr /n $* nul", + if_false="grep -n $* /dev/null" } }, { @@ -1134,35 +1009,31 @@ return { short_desc=N_("GUI: settings for cursor shape and blinking"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_guicursor', - defaults={if_true={vi="n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20"}} + defaults={if_true="n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20"} }, { full_name='guifont', abbreviation='gfn', short_desc=N_("GUI: Name(s) of font(s) to be used"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_guifont', redraw={'ui_option'}, - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='guifontwide', abbreviation='gfw', short_desc=N_("list of font names for double-wide characters"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, redraw={'ui_option'}, varname='p_guifontwide', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='guioptions', abbreviation='go', short_desc=N_("GUI: Which components and options are used"), type='string', list='flags', scope={'global'}, - vi_def=true, redraw={'all_windows'}, enable_if=false, }, @@ -1170,7 +1041,6 @@ return { full_name='guitablabel', abbreviation='gtl', short_desc=N_("GUI: custom label for a tab page"), type='string', scope={'global'}, - vi_def=true, modelineexpr=true, redraw={'current_window'}, enable_if=false, @@ -1179,7 +1049,6 @@ return { full_name='guitabtooltip', abbreviation='gtt', short_desc=N_("GUI: custom tooltip for a tab page"), type='string', scope={'global'}, - vi_def=true, redraw={'current_window'}, enable_if=false, }, @@ -1188,228 +1057,199 @@ return { short_desc=N_("full path name of the main help file"), type='string', scope={'global'}, secure=true, - vi_def=true, expand=true, varname='p_hf', - defaults={if_true={vi=macros('DFLT_HELPFILE')}} + defaults={if_true=macros('DFLT_HELPFILE')} }, { full_name='helpheight', abbreviation='hh', short_desc=N_("minimum height of a new help window"), type='number', scope={'global'}, - vi_def=true, varname='p_hh', - defaults={if_true={vi=20}} + defaults={if_true=20} }, { full_name='helplang', abbreviation='hlg', short_desc=N_("preferred help languages"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_hlg', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='hidden', abbreviation='hid', short_desc=N_("don't unload buffer when it is |abandon|ed"), type='bool', scope={'global'}, - vi_def=true, varname='p_hid', - defaults={if_true={vi=false}} + defaults={if_true=true} }, { full_name='highlight', abbreviation='hl', short_desc=N_("sets highlighting mode for various occasions"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_hl', - defaults={if_true={vi=macros('HIGHLIGHT_INIT')}} + defaults={if_true=macros('HIGHLIGHT_INIT')} }, { full_name='history', abbreviation='hi', short_desc=N_("number of command-lines that are remembered"), type='number', scope={'global'}, - vim=true, varname='p_hi', - defaults={if_true={vi=0, vim=10000}} + defaults={if_true=10000} }, { full_name='hkmap', abbreviation='hk', short_desc=N_("Hebrew keyboard mapping"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_hkmap', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='hkmapp', abbreviation='hkp', short_desc=N_("phonetic Hebrew keyboard mapping"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_hkmapp', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='hlsearch', abbreviation='hls', short_desc=N_("highlight matches with last search pattern"), type='bool', scope={'global'}, - vim=true, redraw={'all_windows'}, varname='p_hls', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='icon', short_desc=N_("Vim set the text of the window icon"), type='bool', scope={'global'}, - vi_def=true, varname='p_icon', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='iconstring', short_desc=N_("to use for the Vim icon text"), type='string', scope={'global'}, - vi_def=true, modelineexpr=true, varname='p_iconstring', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='ignorecase', abbreviation='ic', short_desc=N_("ignore case in search patterns"), type='bool', scope={'global'}, - vi_def=true, varname='p_ic', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='imcmdline', abbreviation='imc', short_desc=N_("use IM when starting to edit a command line"), type='bool', scope={'global'}, - vi_def=true, enable_if=false, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='imdisable', abbreviation='imd', short_desc=N_("do not use the IM in any mode"), type='bool', scope={'global'}, - vi_def=true, enable_if=false, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='iminsert', abbreviation='imi', short_desc=N_("use :lmap or IM in Insert mode"), type='number', scope={'buffer'}, - vi_def=true, varname='p_iminsert', pv_name='p_imi', defaults={ - if_true={vi=macros('B_IMODE_NONE')}, + if_true=macros('B_IMODE_NONE'), } }, { full_name='imsearch', abbreviation='ims', short_desc=N_("use :lmap or IM when typing a search pattern"), type='number', scope={'buffer'}, - vi_def=true, varname='p_imsearch', pv_name='p_ims', defaults={ - if_true={vi=macros('B_IMODE_USE_INSERT')}, + if_true=macros('B_IMODE_USE_INSERT'), } }, { full_name='inccommand', abbreviation='icm', short_desc=N_("Live preview of substitution"), type='string', scope={'global'}, - vi_def=true, redraw={'all_windows'}, varname='p_icm', - defaults={if_true={vi=""}} + defaults={if_true="nosplit"} }, { full_name='include', abbreviation='inc', short_desc=N_("pattern to be used to find an include file"), type='string', scope={'global', 'buffer'}, - vi_def=true, alloced=true, varname='p_inc', - defaults={if_true={vi="^\\s*#\\s*include"}} + defaults={if_true="^\\s*#\\s*include"} }, { full_name='includeexpr', abbreviation='inex', short_desc=N_("expression used to process an include line"), type='string', scope={'buffer'}, - vi_def=true, modelineexpr=true, alloced=true, varname='p_inex', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='incsearch', abbreviation='is', short_desc=N_("highlight match while typing search pattern"), type='bool', scope={'global'}, - vim=true, varname='p_is', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='indentexpr', abbreviation='inde', short_desc=N_("expression used to obtain the indent of a line"), type='string', scope={'buffer'}, - vi_def=true, - vim=true, modelineexpr=true, alloced=true, varname='p_inde', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='indentkeys', abbreviation='indk', short_desc=N_("keys that trigger indenting with 'indentexpr'"), type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, - vi_def=true, alloced=true, varname='p_indk', - defaults={if_true={vi=indentkeys_default}} + defaults={if_true=indentkeys_default} }, { full_name='infercase', abbreviation='inf', short_desc=N_("adjust case of match for keyword completion"), type='bool', scope={'buffer'}, - vi_def=true, varname='p_inf', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='insertmode', abbreviation='im', short_desc=N_("start the edit of a file in Insert mode"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_im', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='isfname', abbreviation='isf', short_desc=N_("characters included in file names and pathnames"), type='string', list='comma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_isf', defaults={ condition='BACKSLASH_IN_FILENAME', -- Excluded are: & and ^ are special in cmd.exe -- ( and ) are used in text separating fnames */ - if_true={vi="@,48-57,/,\\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,="}, - if_false={vi="@,48-57,/,.,-,_,+,,,#,$,%,~,="} + if_true="@,48-57,/,\\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=", + if_false="@,48-57,/,.,-,_,+,,,#,$,%,~,=" } }, { @@ -1417,12 +1257,11 @@ return { short_desc=N_("characters included in identifiers"), type='string', list='comma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_isi', defaults={ condition='WIN32', - if_true={vi="@,48-57,_,128-167,224-235"}, - if_false={vi="@,48-57,_,192-255"} + if_true="@,48-57,_,128-167,224-235", + if_false="@,48-57,_,192-255" } }, { @@ -1430,30 +1269,26 @@ return { short_desc=N_("characters included in keywords"), type='string', list='comma', scope={'buffer'}, deny_duplicates=true, - vim=true, alloced=true, varname='p_isk', - defaults={if_true={vi="@,48-57,_", vim="@,48-57,_,192-255"}} + defaults={if_true="@,48-57,_,192-255"} }, { full_name='isprint', abbreviation='isp', short_desc=N_("printable characters"), type='string', list='comma', scope={'global'}, deny_duplicates=true, - vi_def=true, redraw={'all_windows'}, varname='p_isp', - defaults={if_true={vi="@,161-255"} + defaults={if_true="@,161-255" } }, { full_name='joinspaces', abbreviation='js', short_desc=N_("two spaces after a period with a join command"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_js', - defaults={if_true={vi=true}} + defaults={if_true=false} }, { full_name='jumpoptions', abbreviation='jop', @@ -1461,8 +1296,7 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, varname='p_jop', - vim=true, - defaults={if_true={vim=''}} + defaults={if_true=''} }, { full_name='keymap', abbreviation='kmp', @@ -1470,31 +1304,28 @@ return { type='string', scope={'buffer'}, normal_fname_chars=true, pri_mkrc=true, - vi_def=true, alloced=true, redraw={'statuslines', 'current_buffer'}, varname='p_keymap', pv_name='p_kmap', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='keymodel', abbreviation='km', short_desc=N_("enable starting/stopping selection with keys"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_km', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='keywordprg', abbreviation='kp', short_desc=N_("program to use for the \"K\" command"), type='string', scope={'global', 'buffer'}, secure=true, - vi_def=true, expand=true, varname='p_kp', defaults={ - if_true={vi=":Man"}, + if_true=":Man", } }, { @@ -1503,324 +1334,289 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, - vi_def=true, varname='p_langmap', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='langmenu', abbreviation='lm', short_desc=N_("language to be used for the menus"), type='string', scope={'global'}, normal_fname_chars=true, - vi_def=true, varname='p_lm', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='langnoremap', abbreviation='lnr', short_desc=N_("do not apply 'langmap' to mapped characters"), type='bool', scope={'global'}, varname='p_lnr', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='langremap', abbreviation='lrm', short_desc=N_('No description'), type='bool', scope={'global'}, varname='p_lrm', - defaults={if_true={vi=true, vim=false}} + defaults={if_true=false} }, { full_name='laststatus', abbreviation='ls', short_desc=N_("tells when last window has status lines"), type='number', scope={'global'}, - vim=true, redraw={'all_windows'}, varname='p_ls', - defaults={if_true={vi=1,vim=2}} + defaults={if_true=2} }, { full_name='lazyredraw', abbreviation='lz', short_desc=N_("don't redraw while executing macros"), type='bool', scope={'global'}, - vi_def=true, varname='p_lz', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='linebreak', abbreviation='lbr', short_desc=N_("wrap long lines at a blank"), type='bool', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='lines', short_desc=N_("of lines in the display"), type='number', scope={'global'}, no_mkrc=true, - vi_def=true, redraw={'everything'}, varname='p_lines', - defaults={if_true={vi=macros('DFLT_ROWS')}} + defaults={if_true=macros('DFLT_ROWS')} }, { full_name='linespace', abbreviation='lsp', short_desc=N_("number of pixel lines to use between characters"), type='number', scope={'global'}, - vi_def=true, redraw={'ui_option'}, varname='p_linespace', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='lisp', short_desc=N_("indenting for Lisp"), type='bool', scope={'buffer'}, - vi_def=true, varname='p_lisp', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='lispwords', abbreviation='lw', short_desc=N_("words that change how lisp indenting works"), type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, - vi_def=true, varname='p_lispwords', pv_name='p_lw', - defaults={if_true={vi=macros('LISPWORD_VALUE')}} + defaults={if_true=macros('LISPWORD_VALUE')} }, { full_name='list', short_desc=N_("<Tab> and <EOL>"), type='bool', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='listchars', abbreviation='lcs', short_desc=N_("characters for displaying in list mode"), type='string', list='onecomma', scope={'global', 'window'}, deny_duplicates=true, - vim=true, alloced=true, redraw={'current_window'}, varname='p_lcs', - defaults={if_true={vi="eol:$", vim="tab:> ,trail:-,nbsp:+"}} + defaults={if_true="tab:> ,trail:-,nbsp:+"} }, { full_name='loadplugins', abbreviation='lpl', short_desc=N_("load plugin scripts when starting up"), type='bool', scope={'global'}, - vi_def=true, varname='p_lpl', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='magic', short_desc=N_("special characters in search patterns"), type='bool', scope={'global'}, - vi_def=true, varname='p_magic', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='makeef', abbreviation='mef', short_desc=N_("name of the errorfile for \":make\""), type='string', scope={'global'}, secure=true, - vi_def=true, expand=true, varname='p_mef', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='makeencoding', abbreviation='menc', short_desc=N_("Converts the output of external commands"), type='string', scope={'global', 'buffer'}, - vi_def=true, varname='p_menc', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='makeprg', abbreviation='mp', short_desc=N_("program to use for the \":make\" command"), type='string', scope={'global', 'buffer'}, secure=true, - vi_def=true, expand=true, varname='p_mp', - defaults={if_true={vi="make"}} + defaults={if_true="make"} }, { full_name='matchpairs', abbreviation='mps', short_desc=N_("pairs of characters that \"%\" can match"), type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, - vi_def=true, alloced=true, varname='p_mps', - defaults={if_true={vi="(:),{:},[:]"}} + defaults={if_true="(:),{:},[:]"} }, { full_name='matchtime', abbreviation='mat', short_desc=N_("tenths of a second to show matching paren"), type='number', scope={'global'}, - vi_def=true, varname='p_mat', - defaults={if_true={vi=5}} + defaults={if_true=5} }, { full_name='maxcombine', abbreviation='mco', short_desc=N_("maximum nr of combining characters displayed"), type='number', scope={'global'}, - vi_def=true, varname='p_mco', - defaults={if_true={vi=6}} + defaults={if_true=6} }, { full_name='maxfuncdepth', abbreviation='mfd', short_desc=N_("maximum recursive depth for user functions"), type='number', scope={'global'}, - vi_def=true, varname='p_mfd', - defaults={if_true={vi=100}} + defaults={if_true=100} }, { full_name='maxmapdepth', abbreviation='mmd', short_desc=N_("maximum recursive depth for mapping"), type='number', scope={'global'}, - vi_def=true, varname='p_mmd', - defaults={if_true={vi=1000}} + defaults={if_true=1000} }, { full_name='maxmempattern', abbreviation='mmp', short_desc=N_("maximum memory (in Kbyte) used for pattern search"), type='number', scope={'global'}, - vi_def=true, varname='p_mmp', - defaults={if_true={vi=1000}} + defaults={if_true=1000} }, { full_name='menuitems', abbreviation='mis', short_desc=N_("maximum number of items in a menu"), type='number', scope={'global'}, - vi_def=true, varname='p_mis', - defaults={if_true={vi=25}} + defaults={if_true=25} }, { full_name='mkspellmem', abbreviation='msm', short_desc=N_("memory used before |:mkspell| compresses the tree"), type='string', scope={'global'}, secure=true, - vi_def=true, expand=true, varname='p_msm', - defaults={if_true={vi="460000,2000,500"}} + defaults={if_true="460000,2000,500"} }, { full_name='modeline', abbreviation='ml', short_desc=N_("recognize modelines at start or end of file"), type='bool', scope={'buffer'}, - vim=true, varname='p_ml', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='modelineexpr', abbreviation='mle', short_desc=N_("allow some options to be set in modeline"), type='bool', scope={'global'}, - vi_def=true, secure=true, varname='p_mle', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='modelines', abbreviation='mls', short_desc=N_("number of lines checked for modelines"), type='number', scope={'global'}, - vi_def=true, varname='p_mls', - defaults={if_true={vi=5}} + defaults={if_true=5} }, { full_name='modifiable', abbreviation='ma', short_desc=N_("changes to the text are not possible"), type='bool', scope={'buffer'}, noglob=true, - vi_def=true, varname='p_ma', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='modified', abbreviation='mod', short_desc=N_("buffer has been modified"), type='bool', scope={'buffer'}, no_mkrc=true, - vi_def=true, redraw={'statuslines'}, varname='p_mod', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='more', short_desc=N_("listings when the whole screen is filled"), type='bool', scope={'global'}, - vim=true, varname='p_more', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='mouse', short_desc=N_("the use of mouse clicks"), type='string', list='flags', scope={'global'}, varname='p_mouse', - defaults={if_true={vi="", vim=""}} + defaults={if_true=""} }, { full_name='mousefocus', abbreviation='mousef', short_desc=N_("keyboard focus follows the mouse"), type='bool', scope={'global'}, - vi_def=true, redraw={'ui_option'}, varname='p_mousef', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='mousehide', abbreviation='mh', short_desc=N_("hide mouse pointer while typing"), type='bool', scope={'global'}, - vi_def=true, enable_if=false, - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='mousemodel', abbreviation='mousem', short_desc=N_("changes meaning of mouse buttons"), type='string', scope={'global'}, - vi_def=true, varname='p_mousem', - defaults={if_true={vi="extend"}} + defaults={if_true="extend"} }, { full_name='mouseshape', abbreviation='mouses', short_desc=N_("shape of the mouse pointer in different modes"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, enable_if=false, }, { full_name='mousetime', abbreviation='mouset', short_desc=N_("max time between mouse double-click"), type='number', scope={'global'}, - vi_def=true, varname='p_mouset', - defaults={if_true={vi=500}} + defaults={if_true=500} }, { full_name='nrformats', abbreviation='nf', @@ -1829,50 +1625,45 @@ return { deny_duplicates=true, alloced=true, varname='p_nf', - defaults={if_true={vi="bin,octal,hex", vim="bin,hex"}} + defaults={if_true="bin,hex"} }, { full_name='number', abbreviation='nu', short_desc=N_("print the line number in front of each line"), type='bool', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='numberwidth', abbreviation='nuw', short_desc=N_("number of columns used for the line number"), type='number', scope={'window'}, - vim=true, redraw={'current_window'}, - defaults={if_true={vi=8, vim=4}} + defaults={if_true=4} }, { full_name='omnifunc', abbreviation='ofu', short_desc=N_("function for filetype-specific completion"), type='string', scope={'buffer'}, secure=true, - vi_def=true, alloced=true, varname='p_ofu', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='opendevice', abbreviation='odev', short_desc=N_("allow reading/writing devices on MS-Windows"), type='bool', scope={'global'}, - vi_def=true, enable_if=false, - defaults={if_true={vi=false, vim=false}} + defaults={if_true=false} }, { full_name='operatorfunc', abbreviation='opfunc', short_desc=N_("function to be called for |g@| operator"), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_opfunc', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='packpath', abbreviation='pp', @@ -1880,320 +1671,280 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, - vi_def=true, expand=true, varname='p_pp', - defaults={if_true={vi=''}} + defaults={if_true=''} }, { full_name='paragraphs', abbreviation='para', short_desc=N_("nroff macros that separate paragraphs"), type='string', scope={'global'}, - vi_def=true, varname='p_para', - defaults={if_true={vi="IPLPPPQPP TPHPLIPpLpItpplpipbp"}} + defaults={if_true="IPLPPPQPP TPHPLIPpLpItpplpipbp"} }, { full_name='paste', short_desc=N_("pasting text"), type='bool', scope={'global'}, pri_mkrc=true, - vi_def=true, varname='p_paste', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='pastetoggle', abbreviation='pt', short_desc=N_("key code that causes 'paste' to toggle"), type='string', scope={'global'}, - vi_def=true, varname='p_pt', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='patchexpr', abbreviation='pex', short_desc=N_("expression used to patch a file"), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_pex', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='patchmode', abbreviation='pm', short_desc=N_("keep the oldest version of a file"), type='string', scope={'global'}, normal_fname_chars=true, - vi_def=true, varname='p_pm', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='path', abbreviation='pa', short_desc=N_("list of directories searched with \"gf\" et.al."), type='string', list='comma', scope={'global', 'buffer'}, deny_duplicates=true, - vi_def=true, expand=true, varname='p_path', - defaults={if_true={vi=".,/usr/include,,"}} + defaults={if_true=".,/usr/include,,"} }, { full_name='preserveindent', abbreviation='pi', short_desc=N_("preserve the indent structure when reindenting"), type='bool', scope={'buffer'}, - vi_def=true, - vim=true, varname='p_pi', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='previewheight', abbreviation='pvh', short_desc=N_("height of the preview window"), type='number', scope={'global'}, - vi_def=true, varname='p_pvh', - defaults={if_true={vi=12}} + defaults={if_true=12} }, { full_name='previewwindow', abbreviation='pvw', short_desc=N_("identifies the preview window"), type='bool', scope={'window'}, noglob=true, - vi_def=true, redraw={'statuslines'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='printdevice', abbreviation='pdev', short_desc=N_("name of the printer to be used for :hardcopy"), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_pdev', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='printencoding', abbreviation='penc', short_desc=N_("encoding to be used for printing"), type='string', scope={'global'}, - vi_def=true, varname='p_penc', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='printexpr', abbreviation='pexpr', short_desc=N_("expression used to print PostScript for :hardcopy"), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_pexpr', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='printfont', abbreviation='pfn', short_desc=N_("name of the font to be used for :hardcopy"), type='string', scope={'global'}, - vi_def=true, varname='p_pfn', - defaults={if_true={vi="courier"}} + defaults={if_true="courier"} }, { full_name='printheader', abbreviation='pheader', short_desc=N_("format of the header used for :hardcopy"), type='string', scope={'global'}, - vi_def=true, varname='p_header', - defaults={if_true={vi="%<%f%h%m%=Page %N"}} + defaults={if_true="%<%f%h%m%=Page %N"} }, { full_name='printmbcharset', abbreviation='pmbcs', short_desc=N_("CJK character set to be used for :hardcopy"), type='string', scope={'global'}, - vi_def=true, varname='p_pmcs', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='printmbfont', abbreviation='pmbfn', short_desc=N_("font names to be used for CJK output of :hardcopy"), type='string', scope={'global'}, - vi_def=true, varname='p_pmfn', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='printoptions', abbreviation='popt', short_desc=N_("controls the format of :hardcopy output"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_popt', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='prompt', short_desc=N_("enable prompt in Ex mode"), type='bool', scope={'global'}, - vi_def=true, - varname='p_prompt', - defaults={if_true={vi=true}} + varname='p_force_on', + defaults={if_true=true} }, { full_name='pumblend', abbreviation='pb', short_desc=N_("Controls transparency level of popup menu"), type='number', scope={'global'}, - vi_def=true, redraw={'ui_option'}, varname='p_pb', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='pumheight', abbreviation='ph', short_desc=N_("maximum height of the popup menu"), type='number', scope={'global'}, - vi_def=true, varname='p_ph', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='pumwidth', abbreviation='pw', short_desc=N_("minimum width of the popup menu"), type='number', scope={'global'}, - vi_def=true, varname='p_pw', - defaults={if_true={vi=15}} + defaults={if_true=15} }, { full_name='pyxversion', abbreviation='pyx', short_desc=N_("selects default python version to use"), type='number', scope={'global'}, secure=true, - vi_def=true, varname='p_pyx', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='quickfixtextfunc', abbreviation='qftf', short_desc=N_("customize the quickfix window"), type='string', scope={'global'}, - vi_def=true, varname='p_qftf', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='quoteescape', abbreviation='qe', short_desc=N_("escape characters used in a string"), type='string', scope={'buffer'}, - vi_def=true, alloced=true, varname='p_qe', - defaults={if_true={vi="\\"}} + defaults={if_true="\\"} }, { full_name='readonly', abbreviation='ro', short_desc=N_("disallow writing the buffer"), type='bool', scope={'buffer'}, noglob=true, - vi_def=true, redraw={'statuslines'}, varname='p_ro', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='redrawdebug', abbreviation='rdb', short_desc=N_("Changes the way redrawing works (debug)"), type='string', list='onecomma', scope={'global'}, - vi_def=true, varname='p_rdb', - defaults={if_true={vi=''}} + defaults={if_true=''} }, { full_name='redrawtime', abbreviation='rdt', short_desc=N_("timeout for 'hlsearch' and |:match| highlighting"), type='number', scope={'global'}, - vi_def=true, varname='p_rdt', - defaults={if_true={vi=2000}} + defaults={if_true=2000} }, { full_name='regexpengine', abbreviation='re', short_desc=N_("default regexp engine to use"), type='number', scope={'global'}, - vi_def=true, varname='p_re', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='relativenumber', abbreviation='rnu', short_desc=N_("show relative line number in front of each line"), type='bool', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='remap', short_desc=N_("mappings to work recursively"), type='bool', scope={'global'}, - vi_def=true, varname='p_remap', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='report', short_desc=N_("for reporting nr. of lines changed"), type='number', scope={'global'}, - vi_def=true, varname='p_report', - defaults={if_true={vi=2}} + defaults={if_true=2} }, { full_name='revins', abbreviation='ri', short_desc=N_("inserting characters will work backwards"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_ri', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='rightleft', abbreviation='rl', short_desc=N_("window is right-to-left oriented"), type='bool', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='rightleftcmd', abbreviation='rlc', short_desc=N_("commands for which editing works right-to-left"), type='string', scope={'window'}, - vi_def=true, alloced=true, redraw={'current_window'}, - defaults={if_true={vi="search"}} + defaults={if_true="search"} }, { full_name='ruler', abbreviation='ru', short_desc=N_("show cursor line and column in the status line"), type='bool', scope={'global'}, - vi_def=true, - vim=true, redraw={'statuslines'}, varname='p_ru', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='rulerformat', abbreviation='ruf', short_desc=N_("custom format for the ruler"), type='string', scope={'global'}, - vi_def=true, alloced=true, modelineexpr=true, redraw={'statuslines'}, varname='p_ruf', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='runtimepath', abbreviation='rtp', @@ -2201,110 +1952,93 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, - vi_def=true, expand='nodefault', varname='p_rtp', - defaults={if_true={vi=''}} + defaults={if_true=''} }, { full_name='scroll', abbreviation='scr', short_desc=N_("lines to scroll with CTRL-U and CTRL-D"), type='number', scope={'window'}, no_mkrc=true, - vi_def=true, pv_name='p_scroll', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='scrollback', abbreviation='scbk', short_desc=N_("lines to scroll with CTRL-U and CTRL-D"), type='number', scope={'buffer'}, - vi_def=true, varname='p_scbk', redraw={'current_buffer'}, - defaults={if_true={vi=-1}} + defaults={if_true=-1} }, { full_name='scrollbind', abbreviation='scb', short_desc=N_("scroll in window as other windows scroll"), type='bool', scope={'window'}, - vi_def=true, pv_name='p_scbind', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='scrolljump', abbreviation='sj', short_desc=N_("minimum number of lines to scroll"), type='number', scope={'global'}, - vi_def=true, - vim=true, varname='p_sj', - defaults={if_true={vi=1}} + defaults={if_true=1} }, { full_name='scrolloff', abbreviation='so', short_desc=N_("minimum nr. of lines above and below cursor"), type='number', scope={'global', 'window'}, - vi_def=true, - vim=true, redraw={'all_windows'}, varname='p_so', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='scrollopt', abbreviation='sbo', short_desc=N_("how 'scrollbind' should behave"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_sbo', - defaults={if_true={vi="ver,jump"}} + defaults={if_true="ver,jump"} }, { full_name='sections', abbreviation='sect', short_desc=N_("nroff macros that separate sections"), type='string', scope={'global'}, - vi_def=true, varname='p_sections', - defaults={if_true={vi="SHNHH HUnhsh"}} + defaults={if_true="SHNHH HUnhsh"} }, { full_name='secure', short_desc=N_("mode for reading .vimrc in current dir"), type='bool', scope={'global'}, secure=true, - vi_def=true, varname='p_secure', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='selection', abbreviation='sel', short_desc=N_("what type of selection to use"), type='string', scope={'global'}, - vi_def=true, varname='p_sel', - defaults={if_true={vi="inclusive"}} + defaults={if_true="inclusive"} }, { full_name='selectmode', abbreviation='slm', short_desc=N_("when to use Select mode instead of Visual mode"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_slm', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='sessionoptions', abbreviation='ssop', short_desc=N_("options for |:mksession|"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vim=true, varname='p_ssop', - defaults={if_true={ - vi="blank,buffers,curdir,folds,help,options,tabpages,winsize", - vim="blank,buffers,curdir,folds,help,tabpages,winsize" - }} + defaults={if_true="blank,buffers,curdir,folds,help,tabpages,winsize"} }, { full_name='shada', abbreviation='sd', @@ -2313,31 +2047,29 @@ return { deny_duplicates=true, secure=true, varname='p_shada', - defaults={if_true={vi="", vim="!,'100,<50,s10,h"}} + defaults={if_true="!,'100,<50,s10,h"} }, { full_name='shadafile', abbreviation='sdf', short_desc=N_("overrides the filename used for shada"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, secure=true, expand=true, varname='p_shadafile', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='shell', abbreviation='sh', short_desc=N_("name of shell to use for external commands"), type='string', scope={'global'}, secure=true, - vi_def=true, expand=true, varname='p_sh', defaults={ condition='WIN32', - if_true={vi="cmd.exe"}, - if_false={vi="sh"} + if_true="cmd.exe", + if_false="sh" } }, { @@ -2345,12 +2077,11 @@ return { short_desc=N_("flag to shell to execute one command"), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_shcf', defaults={ condition='WIN32', - if_true={vi="/s /c"}, - if_false={vi="-c"} + if_true="/s /c", + if_false="-c" } }, { @@ -2358,12 +2089,11 @@ return { short_desc=N_("string to put output of \":make\" in error file"), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_sp', defaults={ condition='WIN32', - if_true={vi=">%s 2>&1"}, - if_false={vi="| tee"}, + if_true=">%s 2>&1", + if_false="| tee", } }, { @@ -2371,50 +2101,46 @@ return { short_desc=N_("quote character(s) for around shell command"), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_shq', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='shellredir', abbreviation='srr', short_desc=N_("string to put output of filter in a temp file"), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_srr', defaults={ condition='WIN32', - if_true={vi=">%s 2>&1"}, - if_false={vi=">"} + if_true=">%s 2>&1", + if_false=">" } }, { full_name='shellslash', abbreviation='ssl', short_desc=N_("use forward slash for shell file names"), type='bool', scope={'global'}, - vi_def=true, varname='p_ssl', enable_if='BACKSLASH_IN_FILENAME', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='shelltemp', abbreviation='stmp', short_desc=N_("whether to use a temp file for shell commands"), type='bool', scope={'global'}, varname='p_stmp', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='shellxquote', abbreviation='sxq', short_desc=N_("like 'shellquote', but include redirection"), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_sxq', defaults={ condition='WIN32', - if_true={vi="\""}, - if_false={vi=""}, + if_true="\"", + if_false="", } }, { @@ -2422,164 +2148,140 @@ return { short_desc=N_("characters to escape when 'shellxquote' is ("), type='string', scope={'global'}, secure=true, - vi_def=true, varname='p_sxe', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='shiftround', abbreviation='sr', short_desc=N_("round indent to multiple of shiftwidth"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_sr', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='shiftwidth', abbreviation='sw', short_desc=N_("number of spaces to use for (auto)indent step"), type='number', scope={'buffer'}, - vi_def=true, varname='p_sw', - defaults={if_true={vi=8}} + defaults={if_true=8} }, { full_name='shortmess', abbreviation='shm', short_desc=N_("list of flags, reduce length of messages"), type='string', list='flags', scope={'global'}, - vim=true, varname='p_shm', - defaults={if_true={vi="S", vim="filnxtToOF"}} + defaults={if_true="filnxtToOF"} }, { full_name='showbreak', abbreviation='sbr', short_desc=N_("string to use at the start of wrapped lines"), - type='string', scope={'global'}, - vi_def=true, + type='string', scope={'global', 'window'}, redraw={'all_windows'}, varname='p_sbr', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='showcmd', abbreviation='sc', short_desc=N_("show (partial) command in status line"), type='bool', scope={'global'}, - vim=true, varname='p_sc', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='showfulltag', abbreviation='sft', short_desc=N_("show full tag pattern when completing tag"), type='bool', scope={'global'}, - vi_def=true, varname='p_sft', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='showmatch', abbreviation='sm', short_desc=N_("briefly jump to matching bracket if insert one"), type='bool', scope={'global'}, - vi_def=true, varname='p_sm', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='showmode', abbreviation='smd', short_desc=N_("message on status line to show current mode"), type='bool', scope={'global'}, - vim=true, varname='p_smd', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='showtabline', abbreviation='stal', short_desc=N_("tells when the tab pages line is displayed"), type='number', scope={'global'}, - vi_def=true, redraw={'all_windows', 'ui_option'}, varname='p_stal', - defaults={if_true={vi=1}} + defaults={if_true=1} }, { full_name='sidescroll', abbreviation='ss', short_desc=N_("minimum number of columns to scroll horizontal"), type='number', scope={'global'}, - vi_def=true, varname='p_ss', - defaults={if_true={vi=1}} + defaults={if_true=1} }, { full_name='sidescrolloff', abbreviation='siso', short_desc=N_("min. nr. of columns to left and right of cursor"), type='number', scope={'global', 'window'}, - vi_def=true, - vim=true, redraw={'all_windows'}, varname='p_siso', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='signcolumn', abbreviation='scl', short_desc=N_("when to display the sign column"), type='string', scope={'window'}, - vi_def=true, alloced=true, redraw={'current_window'}, - defaults={if_true={vi="auto"}} + defaults={if_true="auto"} }, { full_name='smartcase', abbreviation='scs', short_desc=N_("no ignore case when pattern has uppercase"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_scs', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='smartindent', abbreviation='si', short_desc=N_("smart autoindenting for C programs"), type='bool', scope={'buffer'}, - vi_def=true, - vim=true, varname='p_si', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='smarttab', abbreviation='sta', short_desc=N_("use 'shiftwidth' when inserting <Tab>"), type='bool', scope={'global'}, - vim=true, varname='p_sta', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='softtabstop', abbreviation='sts', short_desc=N_("number of spaces that <Tab> uses while editing"), type='number', scope={'buffer'}, - vi_def=true, - vim=true, varname='p_sts', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='spell', short_desc=N_("spell checking"), type='bool', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='spellcapcheck', abbreviation='spc', short_desc=N_("pattern to locate end of a sentence"), type='string', scope={'buffer'}, - vi_def=true, alloced=true, redraw={'current_buffer'}, varname='p_spc', - defaults={if_true={vi="[.?!]\\_[\\])'\" ]\\+"}} + defaults={if_true="[.?!]\\_[\\])'\" ]\\+"} }, { full_name='spellfile', abbreviation='spf', @@ -2587,23 +2289,21 @@ return { type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, secure=true, - vi_def=true, alloced=true, expand=true, varname='p_spf', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='spelllang', abbreviation='spl', short_desc=N_("language(s) to do spell checking for"), type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, - vi_def=true, alloced=true, expand=true, redraw={'current_buffer'}, varname='p_spl', - defaults={if_true={vi="en"}} + defaults={if_true="en"} }, { full_name='spellsuggest', abbreviation='sps', @@ -2611,102 +2311,91 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, - vi_def=true, expand=true, varname='p_sps', - defaults={if_true={vi="best"}} + defaults={if_true="best"} }, { full_name='spelloptions', abbreviation='spo', type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, secure=true, - vi_def=true, expand=true, varname='p_spo', - defaults={if_true={vi="", vim=""}} + defaults={if_true=""} }, { full_name='splitbelow', abbreviation='sb', short_desc=N_("new window from split is below the current one"), type='bool', scope={'global'}, - vi_def=true, varname='p_sb', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='splitright', abbreviation='spr', short_desc=N_("new window is put right of the current one"), type='bool', scope={'global'}, - vi_def=true, varname='p_spr', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='startofline', abbreviation='sol', short_desc=N_("commands move cursor to first non-blank in line"), type='bool', scope={'global'}, - vi_def=true, vim=false, varname='p_sol', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='statusline', abbreviation='stl', short_desc=N_("custom format for the status line"), type='string', scope={'global', 'window'}, - vi_def=true, alloced=true, modelineexpr=true, redraw={'statuslines'}, varname='p_stl', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='suffixes', abbreviation='su', short_desc=N_("suffixes that are ignored with multiple match"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_su', - defaults={if_true={vi=".bak,~,.o,.h,.info,.swp,.obj"}} + defaults={if_true=".bak,~,.o,.h,.info,.swp,.obj"} }, { full_name='suffixesadd', abbreviation='sua', short_desc=N_("suffixes added when searching for a file"), type='string', list='onecomma', scope={'buffer'}, deny_duplicates=true, - vi_def=true, alloced=true, varname='p_sua', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='swapfile', abbreviation='swf', short_desc=N_("whether to use a swapfile for a buffer"), type='bool', scope={'buffer'}, - vi_def=true, redraw={'statuslines'}, varname='p_swf', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='switchbuf', abbreviation='swb', short_desc=N_("sets behavior when switching to another buffer"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_swb', - defaults={if_true={vi=""}} + defaults={if_true="uselast"} }, { full_name='synmaxcol', abbreviation='smc', short_desc=N_("maximum column to find syntax items"), type='number', scope={'buffer'}, - vi_def=true, redraw={'current_buffer'}, varname='p_smc', - defaults={if_true={vi=3000}} + defaults={if_true=3000} }, { full_name='syntax', abbreviation='syn', @@ -2714,146 +2403,127 @@ return { type='string', scope={'buffer'}, noglob=true, normal_fname_chars=true, - vi_def=true, alloced=true, varname='p_syn', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='tagfunc', abbreviation='tfu', short_desc=N_("function used to perform tag searches"), type='string', scope={'buffer'}, - vim=true, - vi_def=true, varname='p_tfu', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='tabline', abbreviation='tal', short_desc=N_("custom format for the console tab pages line"), type='string', scope={'global'}, - vi_def=true, modelineexpr=true, redraw={'all_windows'}, varname='p_tal', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='tabpagemax', abbreviation='tpm', short_desc=N_("maximum number of tab pages for |-p| and \"tab all\""), type='number', scope={'global'}, - vim=true, varname='p_tpm', - defaults={if_true={vi=10, vim=50}} + defaults={if_true=50} }, { full_name='tabstop', abbreviation='ts', short_desc=N_("number of spaces that <Tab> in file uses"), type='number', scope={'buffer'}, - vi_def=true, redraw={'current_buffer'}, varname='p_ts', - defaults={if_true={vi=8}} + defaults={if_true=8} }, { full_name='tagbsearch', abbreviation='tbs', short_desc=N_("use binary searching in tags files"), type='bool', scope={'global'}, - vi_def=true, varname='p_tbs', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='tagcase', abbreviation='tc', short_desc=N_("how to handle case when searching in tags files"), type='string', scope={'global', 'buffer'}, - vim=true, varname='p_tc', - defaults={if_true={vi="followic", vim="followic"}} + defaults={if_true="followic"} }, { full_name='taglength', abbreviation='tl', short_desc=N_("number of significant characters for a tag"), type='number', scope={'global'}, - vi_def=true, varname='p_tl', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='tagrelative', abbreviation='tr', short_desc=N_("file names in tag file are relative"), type='bool', scope={'global'}, - vim=true, varname='p_tr', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='tags', abbreviation='tag', short_desc=N_("list of file names used by the tag command"), type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, - vi_def=true, expand=true, varname='p_tags', - defaults={if_true={vi="./tags;,tags"}} + defaults={if_true="./tags;,tags"} }, { full_name='tagstack', abbreviation='tgst', short_desc=N_("push tags onto the tag stack"), type='bool', scope={'global'}, - vi_def=true, varname='p_tgst', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='termbidi', abbreviation='tbidi', short_desc=N_("terminal takes care of bi-directionality"), type='bool', scope={'global'}, - vi_def=true, varname='p_tbidi', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='termencoding', abbreviation='tenc', short_desc=N_("Terminal encodig"), type='string', scope={'global'}, - vi_def=true, - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='termguicolors', abbreviation='tgc', short_desc=N_("Terminal true color support"), type='bool', scope={'global'}, - vi_def=false, redraw={'ui_option'}, varname='p_tgc', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='termpastefilter', abbreviation='tpf', type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vim=true, varname='p_tpf', - defaults={if_true={vi="", vim="BS,HT,ESC,DEL"}} + defaults={if_true="BS,HT,ESC,DEL"} }, { full_name='terse', short_desc=N_("hides notification of search wrap"), type='bool', scope={'global'}, - vi_def=true, varname='p_terse', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='textwidth', abbreviation='tw', short_desc=N_("maximum width of text that is being inserted"), type='number', scope={'buffer'}, - vi_def=true, - vim=true, redraw={'current_buffer'}, varname='p_tw', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='thesaurus', abbreviation='tsr', @@ -2861,51 +2531,44 @@ return { type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, normal_dname_chars=true, - vi_def=true, expand=true, varname='p_tsr', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='tildeop', abbreviation='top', short_desc=N_("tilde command \"~\" behaves like an operator"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_to', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='timeout', abbreviation='to', short_desc=N_("time out on mappings and key codes"), type='bool', scope={'global'}, - vi_def=true, varname='p_timeout', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='timeoutlen', abbreviation='tm', short_desc=N_("time out time in milliseconds"), type='number', scope={'global'}, - vi_def=true, varname='p_tm', - defaults={if_true={vi=1000}} + defaults={if_true=1000} }, { full_name='title', short_desc=N_("Vim set the title of the window"), type='bool', scope={'global'}, - vi_def=true, varname='p_title', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='titlelen', short_desc=N_("of 'columns' used for window title"), type='number', scope={'global'}, - vi_def=true, varname='p_titlelen', - defaults={if_true={vi=85}} + defaults={if_true=85} }, { full_name='titleold', @@ -2913,46 +2576,40 @@ return { type='string', scope={'global'}, secure=true, no_mkrc=true, - vi_def=true, varname='p_titleold', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='titlestring', short_desc=N_("to use for the Vim window title"), type='string', scope={'global'}, - vi_def=true, modelineexpr=true, varname='p_titlestring', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='ttimeout', short_desc=N_("out on mappings"), type='bool', scope={'global'}, - vi_def=true, - vim=true, redraw={'ui_option'}, varname='p_ttimeout', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='ttimeoutlen', abbreviation='ttm', short_desc=N_("time out time for key codes in milliseconds"), type='number', scope={'global'}, - vi_def=true, redraw={'ui_option'}, varname='p_ttm', - defaults={if_true={vi=50}} + defaults={if_true=50} }, { full_name='ttyfast', abbreviation='tf', short_desc=N_("No description"), type='bool', scope={'global'}, no_mkrc=true, - vi_def=true, varname='p_force_on', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='undodir', abbreviation='udir', @@ -2960,105 +2617,92 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, secure=true, - vi_def=true, expand='nodefault', varname='p_udir', - defaults={if_true={vi=''}} + defaults={if_true=''} }, { full_name='undofile', abbreviation='udf', short_desc=N_("save undo information in a file"), type='bool', scope={'buffer'}, - vi_def=true, - vim=true, varname='p_udf', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='undolevels', abbreviation='ul', short_desc=N_("maximum number of changes that can be undone"), type='number', scope={'global', 'buffer'}, - vi_def=true, varname='p_ul', - defaults={if_true={vi=1000}} + defaults={if_true=1000} }, { full_name='undoreload', abbreviation='ur', short_desc=N_("max nr of lines to save for undo on a buffer reload"), type='number', scope={'global'}, - vi_def=true, varname='p_ur', - defaults={if_true={vi=10000}} + defaults={if_true=10000} }, { full_name='updatecount', abbreviation='uc', short_desc=N_("after this many characters flush swap file"), type='number', scope={'global'}, - vi_def=true, varname='p_uc', - defaults={if_true={vi=200}} + defaults={if_true=200} }, { full_name='updatetime', abbreviation='ut', short_desc=N_("after this many milliseconds flush swap file"), type='number', scope={'global'}, - vi_def=true, varname='p_ut', - defaults={if_true={vi=4000}} + defaults={if_true=4000} }, { full_name='varsofttabstop', abbreviation='vsts', short_desc=N_("list of numbers of spaces that <Tab> uses while editing"), type='string', list='comma', scope={'buffer'}, - vi_def=true, varname='p_vsts', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='vartabstop', abbreviation='vts', short_desc=N_("list of numbers of spaces that <Tab> in file uses"), type='string', list='comma', scope={'buffer'}, - vi_def=true, varname='p_vts', redraw={'current_buffer'}, - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='verbose', abbreviation='vbs', short_desc=N_("give informative messages"), type='number', scope={'global'}, - vi_def=true, varname='p_verbose', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='verbosefile', abbreviation='vfile', short_desc=N_("file to write messages in"), type='string', scope={'global'}, secure=true, - vi_def=true, expand=true, varname='p_vfile', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='viewdir', abbreviation='vdir', short_desc=N_("directory where to store files with :mkview"), type='string', scope={'global'}, secure=true, - vi_def=true, expand='nodefault', varname='p_vdir', - defaults={if_true={vi=''}} + defaults={if_true=''} }, { full_name='viewoptions', abbreviation='vop', short_desc=N_("specifies what to save for :mkview"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_vop', - defaults={if_true={vi="folds,options,cursor,curdir"}} + defaults={if_true="folds,cursor,curdir"} }, { -- Alias for "shada". @@ -3077,232 +2721,202 @@ return { short_desc=N_("when to use virtual editing"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, - vim=true, redraw={'curswant'}, varname='p_ve', - defaults={if_true={vi="", vim=""}} + defaults={if_true=""} }, { full_name='visualbell', abbreviation='vb', short_desc=N_("use visual bell instead of beeping"), type='bool', scope={'global'}, - vi_def=true, varname='p_vb', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='warn', short_desc=N_("for shell command when buffer was changed"), type='bool', scope={'global'}, - vi_def=true, varname='p_warn', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='whichwrap', abbreviation='ww', short_desc=N_("allow specified keys to cross line boundaries"), type='string', list='flagscomma', scope={'global'}, - vim=true, varname='p_ww', - defaults={if_true={vi="", vim="b,s"}} + defaults={if_true="b,s"} }, { full_name='wildchar', abbreviation='wc', short_desc=N_("command-line character for wildcard expansion"), type='number', scope={'global'}, - vim=true, varname='p_wc', - defaults={if_true={vi=imacros('Ctrl_E'), vim=imacros('TAB')}} + defaults={if_true=imacros('TAB')} }, { full_name='wildcharm', abbreviation='wcm', short_desc=N_("like 'wildchar' but also works when mapped"), type='number', scope={'global'}, - vi_def=true, varname='p_wcm', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='wildignore', abbreviation='wig', short_desc=N_("files matching these patterns are not completed"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vi_def=true, varname='p_wig', - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='wildignorecase', abbreviation='wic', short_desc=N_("ignore case when completing file names"), type='bool', scope={'global'}, - vi_def=true, varname='p_wic', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='wildmenu', abbreviation='wmnu', short_desc=N_("use menu for command line completion"), type='bool', scope={'global'}, - vim=true, varname='p_wmnu', - defaults={if_true={vi=false, vim=true}} + defaults={if_true=true} }, { full_name='wildmode', abbreviation='wim', short_desc=N_("mode for 'wildchar' command-line expansion"), type='string', list='onecomma', scope={'global'}, - deny_duplicates=true, - vim=true, + deny_duplicates=false, varname='p_wim', - defaults={if_true={vi="", vim="full"}} + defaults={if_true="full"} }, { full_name='wildoptions', abbreviation='wop', short_desc=N_("specifies how command line completion is done"), type='string', list='onecomma', scope={'global'}, deny_duplicates=true, - vim=true, varname='p_wop', - defaults={if_true={vi='', vim='pum,tagfile'}} + defaults={if_true='pum,tagfile'} }, { full_name='winaltkeys', abbreviation='wak', short_desc=N_("when the windows system handles ALT keys"), type='string', scope={'global'}, - vi_def=true, varname='p_wak', - defaults={if_true={vi="menu"}} + defaults={if_true="menu"} }, { full_name='winblend', abbreviation='winbl', short_desc=N_("Controls transparency level for floating windows"), type='number', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='winhighlight', abbreviation='winhl', short_desc=N_("Setup window-local highlights"); type='string', scope={'window'}, - vi_def=true, alloced=true, redraw={'current_window'}, - defaults={if_true={vi=""}} + defaults={if_true=""} }, { full_name='window', abbreviation='wi', short_desc=N_("nr of lines to scroll for CTRL-F and CTRL-B"), type='number', scope={'global'}, - vi_def=true, varname='p_window', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='winheight', abbreviation='wh', short_desc=N_("minimum number of lines for the current window"), type='number', scope={'global'}, - vi_def=true, varname='p_wh', - defaults={if_true={vi=1}} + defaults={if_true=1} }, { full_name='winfixheight', abbreviation='wfh', short_desc=N_("keep window height when opening/closing windows"), type='bool', scope={'window'}, - vi_def=true, redraw={'statuslines'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='winfixwidth', abbreviation='wfw', short_desc=N_("keep window width when opening/closing windows"), type='bool', scope={'window'}, - vi_def=true, redraw={'statuslines'}, - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='winminheight', abbreviation='wmh', short_desc=N_("minimum number of lines for any window"), type='number', scope={'global'}, - vi_def=true, varname='p_wmh', - defaults={if_true={vi=1}} + defaults={if_true=1} }, { full_name='winminwidth', abbreviation='wmw', short_desc=N_("minimal number of columns for any window"), type='number', scope={'global'}, - vi_def=true, varname='p_wmw', - defaults={if_true={vi=1}} + defaults={if_true=1} }, { full_name='winwidth', abbreviation='wiw', short_desc=N_("minimal number of columns for current window"), type='number', scope={'global'}, - vi_def=true, varname='p_wiw', - defaults={if_true={vi=20}} + defaults={if_true=20} }, { full_name='wrap', short_desc=N_("lines wrap and continue on the next line"), type='bool', scope={'window'}, - vi_def=true, redraw={'current_window'}, - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='wrapmargin', abbreviation='wm', short_desc=N_("chars from the right where wrapping starts"), type='number', scope={'buffer'}, - vi_def=true, varname='p_wm', - defaults={if_true={vi=0}} + defaults={if_true=0} }, { full_name='wrapscan', abbreviation='ws', short_desc=N_("searches wrap around the end of the file"), type='bool', scope={'global'}, - vi_def=true, varname='p_ws', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='write', short_desc=N_("to a file is allowed"), type='bool', scope={'global'}, - vi_def=true, varname='p_write', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='writeany', abbreviation='wa', short_desc=N_("write to file with no need for \"!\" override"), type='bool', scope={'global'}, - vi_def=true, varname='p_wa', - defaults={if_true={vi=false}} + defaults={if_true=false} }, { full_name='writebackup', abbreviation='wb', short_desc=N_("make a backup before overwriting a file"), type='bool', scope={'global'}, - vi_def=true, - vim=true, varname='p_wb', - defaults={if_true={vi=true}} + defaults={if_true=true} }, { full_name='writedelay', abbreviation='wd', short_desc=N_("delay this many msec for each char (for debug)"), type='number', scope={'global'}, - vi_def=true, varname='p_wd', - defaults={if_true={vi=0}} + defaults={if_true=0} }, } } diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 008f5ef63b..92b5e14824 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -35,12 +35,11 @@ // Because `uv_os_getenv` requires allocating, we must manage a map to maintain // the behavior of `os_getenv`. -static PMap(cstr_t) *envmap; +static PMap(cstr_t) envmap = MAP_INIT; static uv_mutex_t mutex; void env_init(void) { - envmap = pmap_new(cstr_t)(); uv_mutex_init(&mutex); } @@ -66,8 +65,8 @@ const char *os_getenv(const char *name) } uv_mutex_lock(&mutex); int r = 0; - if (pmap_has(cstr_t)(envmap, name) - && !!(e = (char *)pmap_get(cstr_t)(envmap, name))) { + if (pmap_has(cstr_t)(&envmap, name) + && !!(e = (char *)pmap_get(cstr_t)(&envmap, name))) { if (e[0] != '\0') { // Found non-empty cached env var. // NOTE: This risks incoherence if an in-process library changes the @@ -75,7 +74,7 @@ const char *os_getenv(const char *name) // that turns out to be a problem, we can just remove this codepath. goto end; } - pmap_del2(envmap, name); + pmap_del2(&envmap, name); } e = xmalloc(size); r = uv_os_getenv(name, e, &size); @@ -88,7 +87,7 @@ const char *os_getenv(const char *name) e = NULL; goto end; } - pmap_put(cstr_t)(envmap, xstrdup(name), e); + pmap_put(cstr_t)(&envmap, xstrdup(name), e); end: // Must do this before ELOG, log.c may call os_setenv. uv_mutex_unlock(&mutex); @@ -157,7 +156,7 @@ int os_setenv(const char *name, const char *value, int overwrite) assert(r != UV_EINVAL); // Destroy the old map item. Do this AFTER uv_os_setenv(), because `value` // could be a previous os_getenv() result. - pmap_del2(envmap, name); + pmap_del2(&envmap, name); // Must do this before ELOG, log.c may call os_setenv. uv_mutex_unlock(&mutex); if (r != 0) { @@ -174,7 +173,7 @@ int os_unsetenv(const char *name) return -1; } uv_mutex_lock(&mutex); - pmap_del2(envmap, name); + pmap_del2(&envmap, name); int r = uv_os_unsetenv(name); // Must do this before ELOG, log.c may call os_setenv. uv_mutex_unlock(&mutex); diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index bb68326a03..fa359fa32e 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -4,7 +4,7 @@ /// @file fileio.c /// /// Buffered reading/writing to a file. Unlike fileio.c this is not dealing with -/// Nvim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite +/// Nvim structures for buffer, with autocommands, etc: just fopen/fread/fwrite /// replacement. #include <assert.h> diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index d0fa74a77f..b8ba2487f3 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -1119,7 +1119,7 @@ uint64_t os_fileinfo_blocksize(const FileInfo *file_info) /// /// @param path Path to the file. /// @param[out] file_info Pointer to a `FileID` to fill in. -/// @return `true` on sucess, `false` for failure. +/// @return `true` on success, `false` for failure. bool os_fileid(const char *path, FileID *file_id) FUNC_ATTR_NONNULL_ALL { diff --git a/src/nvim/os/pty_conpty_win.c b/src/nvim/os/pty_conpty_win.c index 5bcadd6490..775e303f84 100644 --- a/src/nvim/os/pty_conpty_win.c +++ b/src/nvim/os/pty_conpty_win.c @@ -104,7 +104,7 @@ conpty_t *os_conpty_init(char **in_name, char **out_name, HRESULT hr; hr = pCreatePseudoConsole(size, in_read, out_write, 0, &conpty_object->pty); if (FAILED(hr)) { - emsg = "create psudo console failed"; + emsg = "create pseudo console failed"; goto failed; } diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index be4bd9709b..44274e8f1d 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -7,7 +7,6 @@ #include <stdbool.h> #include <string.h> -#include "nvim/api/private/handle.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/os_unix.h" diff --git a/src/nvim/path.c b/src/nvim/path.c index fe50be5ea1..e8d5cd9102 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -180,6 +180,34 @@ const char *path_next_component(const char *fname) return fname; } +/// Returns the length of the path head on the current platform. +/// @return +/// - 3 on windows +/// - 1 otherwise +int path_head_length(void) +{ +#ifdef WIN32 + return 3; +#else + return 1; +#endif +} + +/// Returns true if path begins with characters denoting the head of a path +/// (e.g. '/' on linux and 'D:' on windows). +/// @param path The path to be checked. +/// @return +/// - True if path begins with a path head +/// - False otherwise +bool is_path_head(const char_u *path) +{ +#ifdef WIN32 + return isalpha(path[0]) && path[1] == ':'; +#else + return vim_ispathsep(*path); +#endif +} + /// Get a pointer to one character past the head of a path name. /// Unix: after "/"; Win: after "c:\" /// If there is no head, path is returned. @@ -189,7 +217,7 @@ char_u *get_past_head(const char_u *path) #ifdef WIN32 // May skip "c:" - if (isalpha(path[0]) && path[1] == ':') { + if (is_path_head(path)) { retval = path + 2; } #endif @@ -355,7 +383,7 @@ int path_fnamencmp(const char *const fname1, const char *const fname2, /// /// @param[in] fname1 First fname to append to. /// @param[in] len1 Length of fname1. -/// @param[in] fname2 Secord part of the file name. +/// @param[in] fname2 Second part of the file name. /// @param[in] len2 Length of fname2. /// @param[in] sep If true and fname1 does not end with a path separator, /// add a path separator before fname2. @@ -1991,10 +2019,24 @@ char_u *path_shorten_fname(char_u *full_path, char_u *dir_name) assert(dir_name != NULL); size_t len = strlen((char *)dir_name); + + // If dir_name is a path head, full_path can always be made relative. + if (len == (size_t)path_head_length() && is_path_head(dir_name)) { + return full_path + len; + } + + // If full_path and dir_name do not match, it's impossible to make one + // relative to the other. + if (fnamencmp(dir_name, full_path, len) != 0) { + return NULL; + } + char_u *p = full_path + len; - if (fnamencmp(dir_name, full_path, len) != 0 - || !vim_ispathsep(*p)) { + // If *p is not pointing to a path separator, this means that full_path's + // last directory name is longer than *dir_name's last directory, so they + // don't actually match. + if (!vim_ispathsep(*p)) { return NULL; } diff --git a/src/nvim/plines.c b/src/nvim/plines.c new file mode 100644 index 0000000000..a656686a95 --- /dev/null +++ b/src/nvim/plines.c @@ -0,0 +1,483 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// plines.c: calculate the vertical and horizontal size of text in a window + +#include <assert.h> +#include <inttypes.h> +#include <stdbool.h> +#include <string.h> +#include <limits.h> + +#include "nvim/vim.h" +#include "nvim/ascii.h" +#include "nvim/plines.h" +#include "nvim/charset.h" +#include "nvim/cursor.h" +#include "nvim/diff.h" +#include "nvim/func_attr.h" +#include "nvim/fold.h" +#include "nvim/indent.h" +#include "nvim/main.h" +#include "nvim/mbyte.h" +#include "nvim/memline.h" +#include "nvim/memory.h" +#include "nvim/move.h" +#include "nvim/option.h" +#include "nvim/screen.h" +#include "nvim/strings.h" +#include "nvim/window.h" +#include "nvim/buffer.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "plines.c.generated.h" +#endif + +/// Functions calculating vertical size of text when displayed inside a window. +/// Calls horizontal size functions defined below. + +/// @param winheight when true limit to window height +int plines_win(win_T *wp, linenr_T lnum, bool winheight) +{ + // Check for filler lines above this buffer line. When folded the result + // is one line anyway. + return plines_win_nofill(wp, lnum, winheight) + diff_check_fill(wp, lnum); +} + +/// @param winheight when true limit to window height +int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight) +{ + if (!wp->w_p_wrap) { + return 1; + } + + if (wp->w_width_inner == 0) { + return 1; + } + + // A folded lines is handled just like an empty line. + if (lineFolded(wp, lnum)) { + return 1; + } + + const int lines = plines_win_nofold(wp, lnum); + if (winheight && lines > wp->w_height_inner) { + return wp->w_height_inner; + } + return lines; +} + +/// @Return number of window lines physical line "lnum" will occupy in window +/// "wp". Does not care about folding, 'wrap' or 'diff'. +int plines_win_nofold(win_T *wp, linenr_T lnum) +{ + char_u *s; + unsigned int col; + int width; + + s = ml_get_buf(wp->w_buffer, lnum, false); + if (*s == NUL) { // empty line + return 1; + } + col = win_linetabsize(wp, s, MAXCOL); + + // If list mode is on, then the '$' at the end of the line may take up one + // extra column. + if (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL) { + col += 1; + } + + // Add column offset for 'number', 'relativenumber' and 'foldcolumn'. + width = wp->w_width_inner - win_col_off(wp); + if (width <= 0 || col > 32000) { + return 32000; // bigger than the number of screen columns + } + if (col <= (unsigned int)width) { + return 1; + } + col -= (unsigned int)width; + width += win_col_off2(wp); + assert(col <= INT_MAX && (int)col < INT_MAX - (width -1)); + return ((int)col + (width - 1)) / width + 1; +} + +/// Like plines_win(), but only reports the number of physical screen lines +/// used from the start of the line to the given column number. +int plines_win_col(win_T *wp, linenr_T lnum, long column) +{ + // Check for filler lines above this buffer line. When folded the result + // is one line anyway. + int lines = diff_check_fill(wp, lnum); + + if (!wp->w_p_wrap) { + return lines + 1; + } + + if (wp->w_width_inner == 0) { + return lines + 1; + } + + char_u *line = ml_get_buf(wp->w_buffer, lnum, false); + char_u *s = line; + + colnr_T col = 0; + while (*s != NUL && --column >= 0) { + col += win_lbr_chartabsize(wp, line, s, col, NULL); + MB_PTR_ADV(s); + } + + // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in + // INSERT mode, then col must be adjusted so that it represents the last + // screen position of the TAB. This only fixes an error when the TAB wraps + // from one screen line to the next (when 'columns' is not a multiple of + // 'ts') -- webb. + if (*s == TAB && (State & NORMAL) + && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { + col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1; + } + + // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. + int width = wp->w_width_inner - win_col_off(wp); + if (width <= 0) { + return 9999; + } + + lines += 1; + if (col > width) { + lines += (col - width) / (width + win_col_off2(wp)) + 1; + } + return lines; +} + +/// Get the number of screen lines lnum takes up. This takes care of +/// both folds and topfill, and limits to the current window height. +/// +/// @param[in] wp window line is in +/// @param[in] lnum line number +/// @param[out] nextp if not NULL, the line after a fold +/// @param[out] foldedp if not NULL, whether lnum is on a fold +/// @param[in] cache whether to use the window's cache for folds +/// +/// @return the total number of screen lines +int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, + bool *const foldedp, const bool cache) +{ + bool folded = hasFoldingWin(wp, lnum, NULL, nextp, cache, NULL); + if (foldedp) { + *foldedp = folded; + } + if (folded) { + return 1; + } else if (lnum == wp->w_topline) { + return plines_win_nofill(wp, lnum, true) + wp->w_topfill; + } + return plines_win(wp, lnum, true); +} + +int plines_m_win(win_T *wp, linenr_T first, linenr_T last) +{ + int count = 0; + + while (first <= last) { + linenr_T next = first; + count += plines_win_full(wp, first, &next, NULL, false); + first = next + 1; + } + return count; +} + +/// Functions calculating horizontal size of text, when displayed in a window. + +/// Return the number of characters 'c' will take on the screen, taking +/// into account the size of a tab. +/// Also see getvcol() +/// +/// @param p +/// @param col +/// +/// @return Number of characters. +int win_chartabsize(win_T *wp, char_u *p, colnr_T col) +{ + buf_T *buf = wp->w_buffer; + if (*p == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { + return tabstop_padding(col, buf->b_p_ts, buf->b_p_vts_array); + } else { + return ptr2cells(p); + } +} + +/// Return the number of characters the string 's' will take on the screen, +/// taking into account the size of a tab. +/// +/// @param s +/// +/// @return Number of characters the string will take on the screen. +int linetabsize(char_u *s) +{ + return linetabsize_col(0, s); +} + +/// Like linetabsize(), but starting at column "startcol". +/// +/// @param startcol +/// @param s +/// +/// @return Number of characters the string will take on the screen. +int linetabsize_col(int startcol, char_u *s) +{ + colnr_T col = startcol; + char_u *line = s; // pointer to start of line, for breakindent + + while (*s != NUL) { + col += lbr_chartabsize_adv(line, &s, col); + } + return (int)col; +} + +/// Like linetabsize(), but for a given window instead of the current one. +/// +/// @param wp +/// @param line +/// @param len +/// +/// @return Number of characters the string will take on the screen. +unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len) +{ + colnr_T col = 0; + + for (char_u *s = line; + *s != NUL && (len == MAXCOL || s < line + len); + MB_PTR_ADV(s)) { + col += win_lbr_chartabsize(wp, line, s, col, NULL); + } + + return (unsigned int)col; +} + +/// like win_chartabsize(), but also check for line breaks on the screen +/// +/// @param line +/// @param s +/// @param col +/// +/// @return The number of characters taken up on the screen. +int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col) +{ + if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL + && !curwin->w_p_bri) { + if (curwin->w_p_wrap) { + return win_nolbr_chartabsize(curwin, s, col, NULL); + } + return win_chartabsize(curwin, s, col); + } + return win_lbr_chartabsize(curwin, line == NULL ? s: line, s, col, NULL); +} + +/// Call lbr_chartabsize() and advance the pointer. +/// +/// @param line +/// @param s +/// @param col +/// +/// @return The number of characters take up on the screen. +int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col) +{ + int retval; + + retval = lbr_chartabsize(line, *s, col); + MB_PTR_ADV(*s); + return retval; +} + +/// This function is used very often, keep it fast!!!! +/// +/// If "headp" not NULL, set *headp to the size of what we for 'showbreak' +/// string at start of line. Warning: *headp is only set if it's a non-zero +/// value, init to 0 before calling. +/// +/// @param wp +/// @param line +/// @param s +/// @param col +/// @param headp +/// +/// @return The number of characters taken up on the screen. +int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, + colnr_T col, int *headp) +{ + colnr_T col2; + colnr_T col_adj = 0; // col + screen size of tab + colnr_T colmax; + int added; + int mb_added = 0; + int numberextra; + char_u *ps; + int n; + + // No 'linebreak', 'showbreak' and 'breakindent': return quickly. + if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL) { + if (wp->w_p_wrap) { + return win_nolbr_chartabsize(wp, s, col, headp); + } + return win_chartabsize(wp, s, col); + } + + // First get normal size, without 'linebreak' + int size = win_chartabsize(wp, s, col); + int c = *s; + if (*s == TAB) { + col_adj = size - 1; + } + + // If 'linebreak' set check at a blank before a non-blank if the line + // needs a break here + if (wp->w_p_lbr + && vim_isbreak(c) + && !vim_isbreak((int)s[1]) + && wp->w_p_wrap + && (wp->w_width_inner != 0)) { + // Count all characters from first non-blank after a blank up to next + // non-blank after a blank. + numberextra = win_col_off(wp); + col2 = col; + colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj); + + if (col >= colmax) { + colmax += col_adj; + n = colmax + win_col_off2(wp); + + if (n > 0) { + colmax += (((col - colmax) / n) + 1) * n - col_adj; + } + } + + for (;;) { + ps = s; + MB_PTR_ADV(s); + c = *s; + + if (!(c != NUL + && (vim_isbreak(c) || col2 == col || !vim_isbreak((int)(*ps))))) { + break; + } + + col2 += win_chartabsize(wp, s, col2); + + if (col2 >= colmax) { // doesn't fit + size = colmax - col + col_adj; + break; + } + } + } else if ((size == 2) + && (MB_BYTE2LEN(*s) > 1) + && wp->w_p_wrap + && in_win_border(wp, col)) { + // Count the ">" in the last column. + size++; + mb_added = 1; + } + + // May have to add something for 'breakindent' and/or 'showbreak' + // string at start of line. + // Set *headp to the size of what we add. + added = 0; + + char_u *const sbr = get_showbreak_value(wp); + if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && col != 0) { + colnr_T sbrlen = 0; + int numberwidth = win_col_off(wp); + + numberextra = numberwidth; + col += numberextra + mb_added; + + if (col >= (colnr_T)wp->w_width_inner) { + col -= wp->w_width_inner; + numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp)); + if (col >= numberextra && numberextra > 0) { + col %= numberextra; + } + if (*sbr != NUL) { + sbrlen = (colnr_T)MB_CHARLEN(sbr); + if (col >= sbrlen) { + col -= sbrlen; + } + } + if (col >= numberextra && numberextra > 0) { + col %= numberextra; + } else if (col > 0 && numberextra > 0) { + col += numberwidth - win_col_off2(wp); + } + + numberwidth -= win_col_off2(wp); + } + + if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_width_inner)) { + if (*sbr != NUL) { + if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) { + // Calculate effective window width. + int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth; + int prev_width = col ? ((colnr_T)wp->w_width_inner - (sbrlen + col)) + : 0; + + if (width <= 0) { + width = 1; + } + added += ((size - prev_width) / width) * vim_strsize(sbr); + if ((size - prev_width) % width) { + // Wrapped, add another length of 'sbr'. + added += vim_strsize(sbr); + } + } else { + added += vim_strsize(sbr); + } + } + + if (wp->w_p_bri) { + added += get_breakindent_win(wp, line); + } + + size += added; + if (col != 0) { + added = 0; + } + } + } + + if (headp != NULL) { + *headp = added + mb_added; + } + return size; +} + +/// Like win_lbr_chartabsize(), except that we know 'linebreak' is off and +/// 'wrap' is on. This means we need to check for a double-byte character that +/// doesn't fit at the end of the screen line. +/// +/// @param wp +/// @param s +/// @param col +/// @param headp +/// +/// @return The number of characters take up on the screen. +static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) +{ + int n; + + if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { + return tabstop_padding(col, + wp->w_buffer->b_p_ts, + wp->w_buffer->b_p_vts_array); + } + n = ptr2cells(s); + + // Add one cell for a double-width character in the last column of the + // window, displayed with a ">". + if ((n == 2) && (MB_BYTE2LEN(*s) > 1) && in_win_border(wp, col)) { + if (headp != NULL) { + *headp = 1; + } + return 3; + } + return n; +} + diff --git a/src/nvim/plines.h b/src/nvim/plines.h new file mode 100644 index 0000000000..32778b69f1 --- /dev/null +++ b/src/nvim/plines.h @@ -0,0 +1,9 @@ +#ifndef NVIM_PLINES_H +#define NVIM_PLINES_H + +#include "nvim/vim.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "plines.h.generated.h" +#endif +#endif // NVIM_PLINES_H diff --git a/src/nvim/po/sr.po b/src/nvim/po/sr.po index 1450ab5164..d34c1c3100 100644 --- a/src/nvim/po/sr.po +++ b/src/nvim/po/sr.po @@ -2,7 +2,7 @@ # # 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) 2017 +# Copyright (C) 2021 # This file is distributed under the same license as the Vim package. # FIRST AUTHOR Ivan PeÅ¡ić <ivan.pesic@gmail.com>, 2017. # @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: Vim(Serbian)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-02-14 01:49+0400\n" -"PO-Revision-Date: 2021-02-14 01:54+0400\n" +"POT-Creation-Date: 2021-06-13 13:16+0400\n" +"PO-Revision-Date: 2021-06-13 13:50+0400\n" "Last-Translator: Ivan PeÅ¡ić <ivan.pesic@gmail.com>\n" "Language-Team: Serbian\n" "Language: sr\n" @@ -97,6 +97,9 @@ msgstr "Извршавање %s" msgid "autocommand %s" msgstr "аутокоманда %s" +msgid "E972: Blob value does not have the right number of bytes" +msgstr "E972: Блоб вредноÑÑ‚ нема одговарајући број бајтова" + msgid "E831: bf_key_init() called with empty password" msgstr "E831: bf_key_init() је позвана Ñа празном лозинком" @@ -866,7 +869,7 @@ msgid "E976: using Blob as a String" msgstr "E976: коришћење Blob као String" msgid "E908: using an invalid value as a String" -msgstr "E908: кориÑти Ñе недозвољена вредноÑÑ‚ као String" +msgstr "E908: КориÑти Ñе неважећа вредноÑÑ‚ као Стринг: %s" msgid "E698: variable nested too deep for making a copy" msgstr "E698: променљива је предубоко угњеждена да би Ñе направила копија" @@ -913,7 +916,7 @@ msgid "E928: String required" msgstr "E928: Захтева Ñе String" msgid "E808: Number or Float required" -msgstr "E808: Захтева Ñе Number или Float" +msgstr "E808: Захтева Ñе Број или Покретни" msgid "add() argument" msgstr "add() аргумент" @@ -1130,6 +1133,9 @@ msgstr "" "\n" "# Преградне линије, копиране доÑловно:\n" +msgid "E503: \"%s\" is not a file or writable device" +msgstr "E503: „%s†није фајл или уређај на који може да Ñе упиÑује" + msgid "Save As" msgstr "Сачувај као" @@ -4635,12 +4641,12 @@ 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 "E835: Conflicts with value of 'fillchars'" msgstr "E835: У конфликту Ñа вредношћу 'fillchars'" +msgid "E834: Conflicts with value of 'listchars'" +msgstr "E834: У конфликту Ñа вредношћу 'listchars'" + msgid "E617: Cannot be changed in the GTK+ 2 GUI" msgstr "E617: Ðе може да Ñе промени у GTK+ 2 GUI" @@ -5064,7 +5070,7 @@ msgstr "E554: СинтакÑна грешка у %s{...}" #, c-format msgid "E888: (NFA regexp) cannot repeat %s" -msgstr "E888: (NFA regexp) не може да Ñе понови %s" +msgstr "E888: (ÐКРрегуларни израз) не може да Ñе понови %s" msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " @@ -5130,15 +5136,15 @@ msgid "External submatches:\n" msgstr "Спољна подпоклапања:\n" msgid "E865: (NFA) Regexp end encountered prematurely" -msgstr "E865: Крај (NFA) Regexp израза је доÑтигнут прерано" +msgstr "E865: (ÐКÐ) прерано је доÑтигнут крај регуларног израза" #, c-format msgid "E866: (NFA regexp) Misplaced %c" -msgstr "E866: (NFA regexp) %c је на погрешном меÑту" +msgstr "E866: (ÐКРрегуларни израз) %c је на погрешном меÑту" #, c-format msgid "E877: (NFA regexp) Invalid character class: %d" -msgstr "E877: (NFA regexp) Ðеважећа карактер клаÑа: %d" +msgstr "E877: (ÐКРрегуларни израз) Ðеважећа карактер клаÑа: %d" #, c-format msgid "E867: (NFA) Unknown operator '\\z%c'" @@ -6422,6 +6428,13 @@ msgstr "E853: Име аргумента је дуплирано: %s" msgid "E989: Non-default argument follows default argument" msgstr "E989: Ðеподразумевани аргумент Ñледи иза подразумеваног аргумента" +msgid "E126: Missing :endfunction" +msgstr "E126: ÐедоÑтаје :endfunction" + +#, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: Пронађен текÑÑ‚ након :endfunction: %s" + #, c-format msgid "E451: Expected }: %s" msgstr "E451: Очекује Ñе }: %s" @@ -6497,17 +6510,6 @@ msgstr "E862: Овде не може да Ñе кориÑти g:" msgid "E932: Closure function should not be at top level: %s" msgstr "E932: Затварајућа функција не би требало да буде на највишем нивоу: %s" -msgid "E126: Missing :endfunction" -msgstr "E126: ÐедоÑтаје :endfunction" - -#, c-format -msgid "W1001: Text found after :enddef: %s" -msgstr "W1001: Пронађен је текÑÑ‚ након :enddef: %s" - -#, 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" @@ -6983,8 +6985,8 @@ msgid "E475: Invalid value for argument %s: %s" msgstr "E475: Ðеважећа вредноÑÑ‚ за аргумент %s: %s" #, c-format -msgid "E15: Invalid expression: %s" -msgstr "E15: Ðеважећи израз: %s" +msgid "E15: Invalid expression: \"%s\"" +msgstr "E15: Ðеважећи израз: „%sâ€" msgid "E16: Invalid range" msgstr "E16: Ðеважећи опÑег" diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po index 3db3cbfef0..e1fef00863 100644 --- a/src/nvim/po/tr.po +++ b/src/nvim/po/tr.po @@ -1,16 +1,16 @@ # Turkish translations for Vim # Vim Türkçe çevirileri -# Copyright (C) 2020 Emir SARI <bitigchi@me.com> +# Copyright (C) 2021 Emir SARI <emir_sari@msn.com> # This file is distributed under the same license as the Vim package. -# Emir SARI <bitigchi@me.com>, 2019-2020 +# Emir SARI <emir_sari@msn.com>, 2019-2021 # msgid "" msgstr "" "Project-Id-Version: Vim Turkish Localization Project\n" -"Report-Msgid-Bugs-To: Emir SARI <bitigchi@me.com>\n" -"POT-Creation-Date: 2020-11-29 00:20+0300\n" -"PO-Revision-Date: 2020-11-29 20:00+0300\n" -"Last-Translator: Emir SARI <bitigchi@me.com>\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-07-16 17:56+0300\n" +"PO-Revision-Date: 2021-07-16 20:00+0300\n" +"Last-Translator: Emir SARI <emir_sari@msn.com>\n" "Language-Team: Turkish <https://github.com/bitigchi/vim>\n" "Language: tr\n" "MIME-Version: 1.0\n" @@ -28,7 +28,7 @@ msgid "E165: Cannot go beyond last file" msgstr "E165: Son dosyadan öteye gidilemez" msgid "E610: No argument to delete" -msgstr "E610: Silinecek bir deÄŸiÅŸken yok" +msgstr "E610: Silinecek bir argüman yok" msgid "E249: window layout changed unexpectedly" msgstr "E249: Pencere yerleÅŸimi beklenmedik bir biçimde deÄŸiÅŸti" @@ -94,6 +94,9 @@ msgstr "%s çalıştırılıyor" msgid "autocommand %s" msgstr "%s otokomutu" +msgid "E972: Blob value does not have the right number of bytes" +msgstr "E972: İkili geniÅŸ nesne deÄŸeri doÄŸru bayt sayısına sahip deÄŸil" + msgid "E831: bf_key_init() called with empty password" msgstr "E831: bf_key_init() boÅŸ bir ÅŸifre ile çaÄŸrıldı" @@ -131,6 +134,27 @@ msgstr "E931: Arabellek kaydedilemedi" msgid "E937: Attempt to delete a buffer that is in use: %s" msgstr "E937: Kullanımda olan bir arabellek silinmeye çalışılıyor: %s" +msgid "E90: Cannot unload last buffer" +msgstr "E90: Son arabellek bellekten kaldırılamıyor" + +msgid "E84: No modified buffer found" +msgstr "E84: DeÄŸiÅŸtirilmiÅŸ bir arabellek bulunamadı" + +msgid "E85: There is no listed buffer" +msgstr "E85: ListelenmiÅŸ bir arabellek yok" + +msgid "E87: Cannot go beyond last buffer" +msgstr "E87: Son arabellekten öteye gidilemez" + +msgid "E88: Cannot go before first buffer" +msgstr "E88: İlk arabellekten öncesine gidilemez" + +#, c-format +msgid "E89: No write since last change for buffer %d (add ! to override)" +msgstr "" +"E89: %d numaralı arabellek son deÄŸiÅŸiklikten sonra yazılmadı (geçersiz " +"kılmak için ! ekleyin)" + msgid "E515: No buffers were unloaded" msgstr "E515: Hiçbir arabellek bellekten kaldırılmadı" @@ -158,27 +182,6 @@ msgid_plural "%d buffers wiped out" msgstr[0] "%d arabellek yok edildi" msgstr[1] "%d arabellek yok edildi" -msgid "E90: Cannot unload last buffer" -msgstr "E90: Son arabellek bellekten kaldırılamıyor" - -msgid "E84: No modified buffer found" -msgstr "E84: DeÄŸiÅŸtirilmiÅŸ bir arabellek bulunamadı" - -msgid "E85: There is no listed buffer" -msgstr "E85: ListelenmiÅŸ bir arabellek yok" - -msgid "E87: Cannot go beyond last buffer" -msgstr "E87: Son arabellekten öteye gidilemez" - -msgid "E88: Cannot go before first buffer" -msgstr "E88: İlk arabellekten öncesine gidilemez" - -#, c-format -msgid "E89: No write since last change for buffer %d (add ! to override)" -msgstr "" -"E89: %d numaralı arabellek son deÄŸiÅŸiklikten sonra yazılmadı (geçersiz " -"kılmak için ! ekleyin)" - msgid "E948: Job still running (add ! to end the job)" msgstr "E948: İş hâlâ sürüyor (bitirmek için ! ekleyin)" @@ -424,13 +427,13 @@ msgid "E901: gethostbyname() in channel_open()" msgstr "E901: channel_open() içinde gethostbyname()" msgid "E903: received command with non-string argument" -msgstr "E903: Dizi olmayan deÄŸiÅŸken içeren komut alındı" +msgstr "E903: Dizi olmayan argüman içeren komut alındı" msgid "E904: last argument for expr/call must be a number" -msgstr "E904: İfadenin/çaÄŸrının son deÄŸiÅŸkeni bir sayı olmalıdır" +msgstr "E904: İfadenin/çaÄŸrının son argüman bir sayı olmalıdır" msgid "E904: third argument for call must be a list" -msgstr "E904: ÇaÄŸrının üçüncü deÄŸiÅŸkeni bir liste olmalıdır" +msgstr "E904: ÇaÄŸrının üçüncü argümanı bir liste olmalıdır" #, c-format msgid "E905: received unknown command: %s" @@ -449,7 +452,7 @@ msgstr "E631: %s(): Yazma baÅŸarısız" #, c-format msgid "E917: Cannot use a callback with %s()" -msgstr "E917: %s() ile geri çağırma kullanılamaz" +msgstr "E917: %s() ile geri çağırma kullanılamıyor" msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" msgstr "E912: ch_evalexpr()/ch_sendexpr() raw/nl kanalları ile kullanılamaz" @@ -511,6 +514,11 @@ msgid "Warning: Using a weak encryption method; see :help 'cm'" msgstr "" "Uyarı: Zayıf bir ÅŸifreleme yöntemi kullanılıyor; bilgi için: :help 'cm'" +msgid "" +"Note: Encryption of swapfile not supported, disabling swap- and undofile" +msgstr "Takas dosyası ÅŸifrelemesi desteklenmiyor, takas ve geri al dosyası " +"devre dışı bırakılıyor" + msgid "Enter encryption key: " msgstr "Åžifreleme anahtarı girin: " @@ -570,7 +578,7 @@ msgid "%3d expr %s" msgstr "%3d ifade %s" msgid "extend() argument" -msgstr "extend() deÄŸiÅŸkeni" +msgstr "extend() argümanı" #, c-format msgid "E737: Key already exists: %s" @@ -728,9 +736,6 @@ msgstr "E708: [:] en son gelmelidir" msgid "E709: [:] requires a List or Blob value" msgstr "E709: [:] bir liste veya ikili geniÅŸ nesne deÄŸeri gerektirir" -msgid "E972: Blob value does not have the right number of bytes" -msgstr "E972: İkili geniÅŸ nesne deÄŸeri doÄŸru bayt sayısına sahip deÄŸil" - msgid "E996: Cannot lock a range" msgstr "E996: Erim kilitlenemiyor" @@ -741,7 +746,7 @@ msgid "E260: Missing name after ->" msgstr "E260: -> sonrası ad eksik" msgid "E695: Cannot index a Funcref" -msgstr "E695: Bir Funcref dizinlenemez" +msgstr "E695: Bir Funcref dizinlenemiyor" msgid "Not enough memory to set references, garbage collection aborted!" msgstr "Referansları ayarlamak için yetersiz bellek, atık toplama durduruldu" @@ -759,9 +764,6 @@ msgstr "" "\n" "\tEn son ÅŸuradan ayarlandı: " -msgid "E808: Number or Float required" -msgstr "E808: Sayı veya kayan noktalı deÄŸer gerekiyor" - #, c-format msgid "E158: Invalid buffer name: %s" msgstr "E158: Geçersiz arabellek adı: %s" @@ -780,7 +782,7 @@ msgid "E922: expected a dict" msgstr "E922: Bir sözlük bekleniyordu" msgid "E923: Second argument of function() must be a list or a dict" -msgstr "E923: function() ikinci deÄŸiÅŸkeni bir liste veya sözlük olmalıdır" +msgstr "E923: function() ikinci argümanı bir liste veya sözlük olmalıdır" msgid "" "&OK\n" @@ -882,7 +884,7 @@ msgid "E742: Cannot change value of %s" msgstr "E742: %s deÄŸeri deÄŸiÅŸtirilemiyor" msgid "E921: Invalid callback argument" -msgstr "E921: Geçersiz geri çağırma deÄŸiÅŸkeni" +msgstr "E921: Geçersiz geri çağırma argümanı" #, c-format msgid "<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s" @@ -927,6 +929,10 @@ msgstr "E135: *Süzgeç* otokomutları ÅŸu anki arabelleÄŸi deÄŸiÅŸtirmemelidir" msgid "[No write since last change]\n" msgstr "[Son deÄŸiÅŸiklikten sonra yazılmadı]\n" +#, c-format +msgid "E503: \"%s\" is not a file or writable device" +msgstr "E503: \"%s\", bir dosya veya yazılabilir aygıt deÄŸil" + msgid "Save As" msgstr "Farklı Kaydet" @@ -985,7 +991,7 @@ msgid "E143: Autocommands unexpectedly deleted new buffer %s" msgstr "E143: yeni %s arabelleÄŸini otokomutlar beklenmedik bir biçimde sildi" msgid "E144: non-numeric argument to :z" -msgstr "E144: :z için sayısal olmayan deÄŸiÅŸken" +msgstr "E144: :z için sayısal olmayan argüman" msgid "E145: Shell commands and some functionality not allowed in rvim" msgstr "E145: rvim içinde kabuk komutları ve bazı iÅŸlevselliÄŸe izin verilmez" @@ -1091,9 +1097,6 @@ msgstr "Kaynak alınan dosyanın sonu" msgid "End of function" msgstr "İşlevin sonu" -msgid "E464: Ambiguous use of user-defined command" -msgstr "E464: Kullanıcı tanımlı komutun belirsiz kullanımı" - msgid "E492: Not an editor command" msgstr "E492: Bir düzenleyici komutu deÄŸil" @@ -1178,7 +1181,7 @@ msgid "E187: Unknown" msgstr "E187: Bilinmeyen" msgid "E465: :winsize requires two number arguments" -msgstr "E465: :winsize iki adet sayı deÄŸiÅŸken gerektirir" +msgstr "E465: :winsize iki adet sayı argüman gerektirir" #, c-format msgid "Window position: X %d, Y %d" @@ -1188,7 +1191,7 @@ msgid "E188: Obtaining window position not implemented for this platform" msgstr "E188: Pencere konumunu alma özelliÄŸi bu platformda mevcut deÄŸil" msgid "E466: :winpos requires two number arguments" -msgstr "E466: :winpos iki adet sayı deÄŸiÅŸken gerektirir" +msgstr "E466: :winpos iki adet sayı argüman gerektirir" msgid "E930: Cannot use :redir inside execute()" msgstr "E930: :redir, execute() içinde kullanılamaz" @@ -1340,6 +1343,9 @@ msgstr "E788: Åžu anda baÅŸka bir arabellek düzenlenemez" msgid "E811: Not allowed to change buffer information now" msgstr "E811: Åžu anda arabellek bilgisi deÄŸiÅŸtirilemez" +msgid "[Command Line]" +msgstr "[Komut Satırı]" + msgid "E199: Active window or buffer deleted" msgstr "E199: Etkin pencere veya arabellek silinmiÅŸ" @@ -1530,7 +1536,7 @@ msgid "E655: Too many symbolic links (cycle?)" msgstr "E655: Çok fazla sembolik baÄŸlantı (çevrim?)" msgid "writefile() first argument must be a List or a Blob" -msgstr "writefile() ilk deÄŸiÅŸkeni bir liste veya ikili geniÅŸ nesne olmalıdır" +msgstr "writefile() ilk argümanı bir liste veya ikili geniÅŸ nesne olmalıdır" msgid "Select Directory dialog" msgstr "Dizin Seç iletiÅŸim kutusu" @@ -1581,6 +1587,9 @@ msgstr "E446: İmleç altında bir dosya adı yok" msgid "E447: Can't find file \"%s\" in path" msgstr "E447: \"%s\" dosyası yol içinde bulunamadı" +msgid "E808: Number or Float required" +msgstr "E808: Sayı veya kayan noktalı deÄŸer gerekiyor" + msgid "E490: No fold found" msgstr "E490: Kıvırma bulunamadı" @@ -1805,7 +1814,7 @@ msgstr "E671: Pencere baÅŸlığı \"%s\" bulunamıyor" #, c-format msgid "E243: Argument not supported: \"-%s\"; Use the OLE version." -msgstr "E243: \"-%s\" deÄŸiÅŸkeni desteklenmiyor; OLE sürümünü kullanın." +msgstr "E243: \"-%s\" argümanı desteklenmiyor; OLE sürümünü kullanın." msgid "E988: GUI cannot be used. Cannot execute gvim.exe." msgstr "E988: Grafik arabirim kullanılamaz. gvim.exe çalıştırılamadı." @@ -2042,11 +2051,11 @@ msgstr "E411: Vurgulama grubu bulunamadı: %s" #, c-format msgid "E412: Not enough arguments: \":highlight link %s\"" -msgstr "E412: Yetersiz sayıda deÄŸiÅŸken: \":highlight link %s\"" +msgstr "E412: Yetersiz sayıda argüman: \":highlight link %s\"" #, c-format msgid "E413: Too many arguments: \":highlight link %s\"" -msgstr "E413: Çok fazla deÄŸiÅŸken: \":highlight link %s\"" +msgstr "E413: Çok fazla argüman: \":highlight link %s\"" msgid "E414: group has settings, highlight link ignored" msgstr "E414: Grup ayarları mevcut, vurgulama baÄŸlantısı yok sayıldı" @@ -2061,7 +2070,7 @@ msgstr "E416: Eksik eÅŸittir imi: %s" #, c-format msgid "E417: missing argument: %s" -msgstr "E417: Eksik deÄŸiÅŸkenler: %s" +msgstr "E417: Argüman eksik: %s" #, c-format msgid "E418: Illegal value: %s" @@ -2086,7 +2095,7 @@ msgstr "E422: Uçbirim kodu çok uzun: %s" #, c-format msgid "E423: Illegal argument: %s" -msgstr "E423: İzin verilmeyen deÄŸiÅŸken: %s" +msgstr "E423: İzin verilmeyen argüman: %s" msgid "E424: Too many different highlighting attributes in use" msgstr "E424: Çok fazla deÄŸiÅŸik vurgulama kuralları kullanılıyor" @@ -2295,7 +2304,7 @@ msgid "unknown option" msgstr "bilinmeyen seçenek" msgid "window index is out of range" -msgstr "pencere dizini erimin dışında" +msgstr "pencere sırası erimin dışında" msgid "couldn't open buffer" msgstr "arabellek açılamadı" @@ -2513,9 +2522,6 @@ msgstr " Dahili anahtar sözcük tamamlaması (^N^P)" msgid "Hit end of paragraph" msgstr "Paragrafın sonuna varıldı" -msgid "E839: Completion function changed window" -msgstr "E839: Tamamlama iÅŸlevi pencereyi deÄŸiÅŸtirdi" - msgid "E840: Completion function deleted text" msgstr "E840: Tamamlama iÅŸlevi metni sildi" @@ -2594,23 +2600,23 @@ msgstr "E938: JSON'da yinelenmiÅŸ anahtar: \"%s\"" #, c-format msgid "E899: Argument of %s must be a List or Blob" -msgstr "E899: %s deÄŸiÅŸkeni bir liste veya ikili geniÅŸ nesne olmalıdır" +msgstr "E899: %s argümanı bir liste veya ikili geniÅŸ nesne olmalıdır" msgid "E900: maxdepth must be non-negative number" msgstr "E900: maxdepth negatif olmayan bir sayı olmalı" msgid "flatten() argument" -msgstr "flatten() deÄŸiÅŸkeni" +msgstr "flatten() argümanı" #, c-format msgid "E696: Missing comma in List: %s" msgstr "E696: Listede virgül eksik: %s" msgid "sort() argument" -msgstr "sort() deÄŸiÅŸkeni" +msgstr "sort() argümanı" msgid "uniq() argument" -msgstr "uniq() deÄŸiÅŸkeni" +msgstr "uniq() argümanı" msgid "E702: Sort compare function failed" msgstr "E702: Sıralayıp karşılaÅŸtırma iÅŸlevi baÅŸarısız oldu" @@ -2619,25 +2625,28 @@ msgid "E882: Uniq compare function failed" msgstr "E882: Benzersizlik karşılaÅŸtırma iÅŸlevi baÅŸarısız oldu" msgid "map() argument" -msgstr "map() deÄŸiÅŸkeni" +msgstr "map() argümanı" msgid "mapnew() argument" -msgstr "mapnew() deÄŸiÅŸkeni" +msgstr "mapnew() argümanı" msgid "filter() argument" -msgstr "filter() deÄŸiÅŸkeni" +msgstr "filter() argümanı" msgid "add() argument" -msgstr "add() deÄŸiÅŸkeni" +msgstr "add() argümanı" + +msgid "extendnew() argument" +msgstr "extendnew() argümanı" msgid "insert() argument" -msgstr "insert() deÄŸiÅŸkeni" +msgstr "insert() argümanı" msgid "remove() argument" -msgstr "remove() deÄŸiÅŸkeni" +msgstr "remove() argümanı" msgid "reverse() argument" -msgstr "reverse() deÄŸiÅŸkeni" +msgstr "reverse() argümanı" #, c-format msgid "Current %slanguage: \"%s\"" @@ -2648,22 +2657,22 @@ msgid "E197: Cannot set language to \"%s\"" msgstr "E197: \"%s\" diline ayarlanamıyor" msgid "Unknown option argument" -msgstr "Bilinmeyen seçenek deÄŸiÅŸkeni" +msgstr "Bilinmeyen seçenek argümanı" msgid "Too many edit arguments" -msgstr "Çok fazla düzenleme deÄŸiÅŸkeni" +msgstr "Çok fazla düzenleme argümanı" msgid "Argument missing after" -msgstr "Åžundan sonra deÄŸiÅŸken eksik:" +msgstr "Åžundan sonra argüman eksik:" msgid "Garbage after option argument" -msgstr "Seçenek deÄŸiÅŸkeninden sonra anlamsız veri" +msgstr "Seçenek argümanından sonra anlamsız veri" msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments" -msgstr "Çok fazla \"+komut\", \"-c komut\" veya \"--cmd komut\" deÄŸiÅŸkeni" +msgstr "Çok fazla \"+komut\", \"-c komut\" veya \"--cmd komut\" argümanı" msgid "Invalid argument for" -msgstr "Åžunun için geçersiz deÄŸiÅŸken:" +msgstr "Åžunun için geçersiz argüman:" #, c-format msgid "%d files to edit\n" @@ -2735,7 +2744,7 @@ msgstr "" "Kullanım:" msgid " vim [arguments] " -msgstr " vim [deÄŸiÅŸkenler] " +msgstr " vim [argümanlar] " msgid "" "\n" @@ -2967,21 +2976,21 @@ msgid "" "Arguments recognised by gvim (Motif version):\n" msgstr "" "\n" -"gvim tarafından tanınan deÄŸiÅŸkenler (Motif sürümü):\n" +"gvim tarafından tanınan argümanlar (Motif sürümü):\n" msgid "" "\n" "Arguments recognised by gvim (neXtaw version):\n" msgstr "" "\n" -"gvim tarafından tanınan deÄŸiÅŸkenler (neXtaw sürümü):\n" +"gvim tarafından tanınan argümanlar (neXtaw sürümü):\n" msgid "" "\n" "Arguments recognised by gvim (Athena version):\n" msgstr "" "\n" -"gvim tarafından tanınan deÄŸiÅŸkenler (Athena sürümü):\n" +"gvim tarafından tanınan argümanlar (Athena sürümü):\n" msgid "-display <display>\tRun Vim on <display>" msgstr "-display <ekran>\tVim'i <ekran>'da çalıştır" @@ -3032,7 +3041,7 @@ msgid "" "Arguments recognised by gvim (GTK+ version):\n" msgstr "" "\n" -"gvim tarafından tanınan deÄŸiÅŸkenler (GTK+ sürümü):\n" +"gvim tarafından tanınan argümanlar (GTK+ sürümü):\n" msgid "-display <display>\tRun Vim on <display> (also: --display)" msgstr "-display <ekran>\tVim'i <ekran>'da çalıştır (veya: --display)" @@ -3078,7 +3087,7 @@ msgid "E228: makemap: Illegal mode" msgstr "E228: makemap: İzin verilmeyen kip" msgid "E460: entries missing in mapset() dict argument" -msgstr "E460: mapset() sözlük deÄŸiÅŸkeninde eksik girdiler" +msgstr "E460: mapset() sözlük argümanında eksik girdiler" #, c-format msgid "E357: 'langmap': Matching character missing for %s" @@ -3716,13 +3725,13 @@ msgstr "" "İ&ptal" msgid "E766: Insufficient arguments for printf()" -msgstr "E766: printf() için yetersiz deÄŸiÅŸkenler" +msgstr "E766: printf() için yetersiz argüman" msgid "E807: Expected Float argument for printf()" -msgstr "E807: printf() için kayan noktalı deÄŸer türünde deÄŸiÅŸken bekleniyordu" +msgstr "E807: printf() için kayan noktalı deÄŸer türünde argüman bekleniyordu" msgid "E767: Too many arguments to printf()" -msgstr "E767: printf() için çok fazla deÄŸiÅŸken" +msgstr "E767: printf() için çok fazla argüman" msgid "Type number and <Enter> or click with the mouse (q or empty cancels): " msgstr "" @@ -4014,12 +4023,12 @@ msgstr "E531: Grafik arabirimi baÅŸlatmak için \":gui\" yazın" msgid "E589: 'backupext' and 'patchmode' are equal" msgstr "E589: 'backupext' ve 'patchmode' birbirine eÅŸit" -msgid "E834: Conflicts with value of 'listchars'" -msgstr "E834: 'listchars' deÄŸeriyle çakışmalar var" - msgid "E835: Conflicts with value of 'fillchars'" msgstr "E835: 'fillchars' deÄŸeriyle çakışmalar var" +msgid "E834: Conflicts with value of 'listchars'" +msgstr "E834: 'listchars' deÄŸeriyle çakışmalar var" + msgid "E617: Cannot be changed in the GTK+ 2 GUI" msgstr "E617: GTK+ 2 grafik arabiriminde deÄŸiÅŸtirilemez" @@ -4392,7 +4401,7 @@ msgid "Cannot open file \"%s\"" msgstr "\"%s\" dosyası açılamıyor" msgid "cannot have both a list and a \"what\" argument" -msgstr "ya birinci ya da son deÄŸiÅŸken belirtilmelidir" +msgstr "ya birinci ya da son argüman belirtilmelidir" msgid "E681: Buffer is not loaded" msgstr "E681: Arabellek yüklenemedi" @@ -4725,10 +4734,10 @@ msgid "modeline" msgstr "kip satırı" msgid "--cmd argument" -msgstr "--cmd deÄŸiÅŸkeni" +msgstr "--cmd argümanı" msgid "-c argument" -msgstr "-c deÄŸiÅŸkeni" +msgstr "-c argümanı" msgid "environment variable" msgstr "ortam deÄŸiÅŸkeni" @@ -4736,6 +4745,9 @@ msgstr "ortam deÄŸiÅŸkeni" msgid "error handler" msgstr "hata iÅŸleyicisi" +msgid "changed window size" +msgstr "deÄŸiÅŸtirilen pencere boyutu" + msgid "W15: Warning: Wrong line separator, ^M may be missing" msgstr "W15: Uyarı: Yanlış satır ayırıcısı, ^M eksik olabilir" @@ -5215,6 +5227,9 @@ msgstr "E765: 'spellfile' içinde %d adet girdi yok" msgid "Word '%.*s' removed from %s" msgstr "'%.*s' sözcüğü %s içinden çıkartıldı" +msgid "Seek error in spellfile" +msgstr "Yazım dosyasında arama hatası" + #, c-format msgid "Word '%.*s' added to %s" msgstr "'%.*s' sözcüğü %s dosyasına eklendi" @@ -5242,7 +5257,7 @@ msgstr " < \"%.*s\"" #, c-format msgid "E390: Illegal argument: %s" -msgstr "E390: İzin verilmeyen deÄŸiÅŸken: %s" +msgstr "E390: İzin verilmeyen argüman: %s" msgid "No Syntax items defined for this buffer" msgstr "Bu arabellek için sözdizim ögeleri tanımlanmamış" @@ -5343,7 +5358,7 @@ msgid " line breaks" msgstr " satır sonu" msgid "E395: contains argument not accepted here" -msgstr "E395: Burada kabul edilmeyen bir deÄŸiÅŸken içeriyor" +msgstr "E395: Burada kabul edilmeyen bir argüman içeriyor" msgid "E844: invalid cchar value" msgstr "E844: Geçersiz cchar deÄŸeri" @@ -5375,7 +5390,7 @@ msgstr "E398: '=' eksik: %s" #, c-format msgid "E399: Not enough arguments: syntax region %s" -msgstr "E399: Yetersiz deÄŸiÅŸken: %s sözdizim bölgesi" +msgstr "E399: Yetersiz sayıda argüman: %s sözdizim bölgesi" msgid "E848: Too many syntax clusters" msgstr "E848: Çok fazla sözdizim kümesi" @@ -5396,7 +5411,7 @@ msgstr "E403: Sözdizim eÅŸitlemesi: Satır devamları dizgisi iki kez tanımlan #, c-format msgid "E404: Illegal arguments: %s" -msgstr "E404: İzin verilmeyen deÄŸiÅŸkenler: %s" +msgstr "E404: İzin verilmeyen argümanlar: %s" #, c-format msgid "E405: Missing equal sign: %s" @@ -5404,7 +5419,7 @@ msgstr "E405: EÅŸittir imi eksik: %s" #, c-format msgid "E406: Empty argument: %s" -msgstr "E406: BoÅŸ deÄŸiÅŸken: %s" +msgstr "E406: BoÅŸ argüman: %s" #, c-format msgid "E407: %s not allowed here" @@ -5539,7 +5554,7 @@ msgid "E436: No \"%s\" entry in termcap" msgstr "E436: termcap içinde \"%s\" girdisi yok" msgid "E437: terminal capability \"cm\" required" -msgstr "E437: \"cm\" uçbirim kabiliyeti gerekiyor" +msgstr "E437: \"cm\" uçbirim yeteneÄŸi gerekiyor" msgid "" "\n" @@ -5688,16 +5703,16 @@ msgstr "E914: Bir Kanal, Kayan Noktalı DeÄŸer yerine kullanılıyor" msgid "E975: Using a Blob as a Float" msgstr "E975: Bir İkili GeniÅŸ Nesne, Kayan Noktalı DeÄŸer yerine kullanılıyor" -msgid "E729: using Funcref as a String" +msgid "E729: Using a Funcref as a String" msgstr "E729: Funcref bir Dizi yerine kullanılıyor" -msgid "E730: using List as a String" +msgid "E730: Using a List as a String" msgstr "E730: Liste bir Dizi yerine kullanılıyor" -msgid "E731: using Dictionary as a String" +msgid "E731: Using a Dictionary as a String" msgstr "E731: Sözlük bir Dizi yerine kullanılıyor" -msgid "E976: using Blob as a String" +msgid "E976: Using a Blob as a String" msgstr "E976: İkili GeniÅŸ Nesne bir Dizi yerine kullanılıyor" msgid "E977: Can only compare Blob with Blob" @@ -5892,16 +5907,16 @@ msgid "E180: Invalid complete value: %s" msgstr "E180: Geçersiz tam deÄŸer: %s" msgid "E468: Completion argument only allowed for custom completion" -msgstr "E468: Tamamlama deÄŸiÅŸkenine yalnızca özel tamamlamalarda izin verilir" +msgstr "E468: Tamamlama argümanına yalnızca özel tamamlamalarda izin verilir" msgid "E467: Custom completion requires a function argument" -msgstr "E467: Özel tamamlama bir iÅŸlev deÄŸiÅŸkeni gerektirir" +msgstr "E467: Özel tamamlama bir iÅŸlev argümanı gerektirir" msgid "E175: No attribute specified" msgstr "E175: Bir öznitelik belirtilmemiÅŸ" msgid "E176: Invalid number of arguments" -msgstr "E176: Geçersiz deÄŸiÅŸken sayısı" +msgstr "E176: Geçersiz argüman sayısı" msgid "E177: Count cannot be specified twice" msgstr "E177: Sayım iki defa belirtilemez" @@ -5910,10 +5925,10 @@ msgid "E178: Invalid default value for count" msgstr "E178: Sayım için geçersiz öntanımlı deÄŸer" msgid "E179: argument required for -complete" -msgstr "E179: -complete için deÄŸiÅŸken gerekiyor" +msgstr "E179: -complete için argüman gerekiyor" msgid "E179: argument required for -addr" -msgstr "E179: -addr için deÄŸiÅŸken gerekiyor" +msgstr "E179: -addr için argüman gerekiyor" #, c-format msgid "E174: Command already exists: add ! to replace it: %s" @@ -5948,14 +5963,21 @@ msgstr "E130: Bilinmeyen iÅŸlev: %s" #, c-format msgid "E125: Illegal argument: %s" -msgstr "E125: İzin verilmeyen deÄŸiÅŸken: %s" +msgstr "E125: İzin verilmeyen argüman: %s" #, c-format msgid "E853: Duplicate argument name: %s" -msgstr "E853: Yinelenen deÄŸiÅŸken adı: %s" +msgstr "E853: Yinelenen argüman adı: %s" msgid "E989: Non-default argument follows default argument" -msgstr "E989: Öntanımlı olmayan deÄŸiÅŸken öntanımlı deÄŸiÅŸkenden sonra" +msgstr "E989: Öntanımlı olmayan argüman öntanımlı argümandan sonra" + +msgid "E126: Missing :endfunction" +msgstr "E126: :endfunction eksik" + +#, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: :endfunction sonrası metin bulundu: %s" #, c-format msgid "E451: Expected }: %s" @@ -5963,11 +5985,11 @@ msgstr "E451: } bekleniyordu: %s" #, c-format msgid "E740: Too many arguments for function %s" -msgstr "E740: %s iÅŸlevi için çok fazla deÄŸiÅŸken" +msgstr "E740: %s iÅŸlevi için çok fazla argüman" #, c-format msgid "E116: Invalid arguments for function %s" -msgstr "E116: %s iÅŸlevi için geçersiz deÄŸiÅŸkenler" +msgstr "E116: %s iÅŸlevi için geçersiz argümanlar" msgid "E132: Function call depth is higher than 'maxfuncdepth'" msgstr "E132: İşlevin çağırdığı derinlik 'maxfuncdepth'ten daha yüksek" @@ -5989,7 +6011,7 @@ msgid "%s returning %s" msgstr "%s, %s döndürüyor" msgid "E699: Too many arguments" -msgstr "E699: Çok fazla deÄŸiÅŸken" +msgstr "E699: Çok fazla argüman" #, c-format msgid "E276: Cannot use function as a method: %s" @@ -6032,17 +6054,6 @@ msgstr "E862: g: burada kullanılamaz" msgid "E932: Closure function should not be at top level: %s" msgstr "E932: Kapatma iÅŸlevi en üst düzeyde olmamalıdır: %s" -msgid "E126: Missing :endfunction" -msgstr "E126: :endfunction eksik" - -#, c-format -msgid "W1001: Text found after :enddef: %s" -msgstr "W1001: :enddef sonrası metin bulundu: %s" - -#, c-format -msgid "W22: Text found after :endfunction: %s" -msgstr "W22: :endfunction sonrası metin bulundu: %s" - #, c-format msgid "E707: Function name conflicts with variable: %s" msgstr "E707: İşlev adı ÅŸu deÄŸiÅŸken ile çakışıyor: %s" @@ -6605,6 +6616,55 @@ msgstr "gvimext.dll hatası" msgid "Path length too long!" msgstr "Yol çok uzun!" +msgid "E10: \\ should be followed by /, ? or &" +msgstr "E10: \\ sonrasında /, ? veya & gelmeli" + +msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits" +msgstr "E11: Komut satırı penceresinde geçersiz; <CR> çalıştırır, CTRL-C çıkar" + +msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search" +msgstr "" +"E12: Geçerli dizin veya etiket aramasında exrc veya vimrc'den komutlara izin " +"verilmiyor" + +msgid "E13: File exists (add ! to override)" +msgstr "E13: Dosya mevcut (geçersiz kılmak için ! ekleyin)" + +#, c-format +msgid "E15: Invalid expression: \"%s\"" +msgstr "E15: Geçersiz ifade: \"%s\"" + +msgid "E16: Invalid range" +msgstr "E16: Geçersiz erim" + +#, c-format +msgid "E17: \"%s\" is a directory" +msgstr "E17: \"%s\" bir dizin" + +msgid "E18: Unexpected characters in :let" +msgstr "E18: :let içinde beklenmeyen karakter" + +msgid "E18: Unexpected characters in assignment" +msgstr "E18: Atama içerisinde beklenmedik karakterler" + +msgid "E19: Mark has invalid line number" +msgstr "E19: İm satır numarası geçersiz" + +msgid "E20: Mark not set" +msgstr "E20: İm ayarlanmamış" + +msgid "E21: Cannot make changes, 'modifiable' is off" +msgstr "E21: DeÄŸiÅŸiklik yapılamıyor, 'modifiable' kapalı" + +msgid "E22: Scripts nested too deep" +msgstr "E22: Betikler çok iç içe geçmiÅŸ" + +msgid "E23: No alternate file" +msgstr "E23: BaÅŸka bir dosya yok" + +msgid "E24: No such abbreviation" +msgstr "E24: Böyle bir kısaltma yok" + #, c-format msgid "E121: Undefined variable: %s" msgstr "E121: Tanımlanmamış deÄŸiÅŸken: %s" @@ -6613,6 +6673,9 @@ msgstr "E121: Tanımlanmamış deÄŸiÅŸken: %s" msgid "E121: Undefined variable: %c:%s" msgstr "E121: Tanımlanmamış deÄŸiÅŸken: %c:%s" +msgid "E464: Ambiguous use of user-defined command" +msgstr "E464: Kullanıcı tanımlı komutun belirsiz kullanımı" + msgid "E476: Invalid command" msgstr "E476: Geçersiz komut" @@ -6633,15 +6696,19 @@ msgid "" "E856: \"assert_fails()\" second argument must be a string or a list with one " "or two strings" msgstr "" -"E856: \"assert_fails()\" ikinci deÄŸiÅŸkeni bir dizi veya bir veya iki dizili " +"E856: \"assert_fails()\" ikinci argüman bir dizi veya bir veya iki dizili " "bir liste olmalıdır" +#, c-format +msgid "E908: using an invalid value as a String: %s" +msgstr "E908: Geçersiz bir deÄŸer bir Dizi yerine kullanılıyor: %s" + msgid "E909: Cannot index a special variable" msgstr "E909: Özel bir deÄŸiÅŸken dizinlenemiyor" #, c-format -msgid "E1100: Missing :var: %s" -msgstr "E1100: :var eksik: %s" +msgid "E1100: Command not supported in Vim9 script (missing :var?): %s" +msgstr "E1100: Komut Vim9 betiÄŸinde desteklenmiyor (:var? eksik): %s" #, c-format msgid "E1001: Variable not found: %s" @@ -6655,18 +6722,18 @@ msgid "E1003: Missing return value" msgstr "E1003: Dönüş deÄŸeri eksik" #, c-format -msgid "E1004: White space required before and after '%s'" -msgstr "E1004: '%s' öncesinde ve sonrasında boÅŸluk gerekiyor" +msgid "E1004: White space required before and after '%s' at \"%s\"" +msgstr "E1004: Åžu konumda '%s' öncesinde ve sonrasında boÅŸluk gerekiyor: \"%s\"" msgid "E1005: Too many argument types" -msgstr "E1005: Çok fazla deÄŸiÅŸken türü" +msgstr "E1005: Çok fazla argüman türü" #, c-format msgid "E1006: %s is used as an argument" -msgstr "E1006: %s bir deÄŸiÅŸken olarak kullanılıyor" +msgstr "E1006: %s bir argüman olarak kullanılıyor" msgid "E1007: Mandatory argument after optional argument" -msgstr "E1007: İsteÄŸe baÄŸlı deÄŸiÅŸken sonrasında zorunlu deÄŸiÅŸken" +msgstr "E1007: İsteÄŸe baÄŸlı argüman sonrasında zorunlu argüman" msgid "E1008: Missing <type>" msgstr "E1008: <tür> eksik" @@ -6688,7 +6755,7 @@ msgstr "E1012: Tür uyumsuzluÄŸu, %s bekleniyordu, ancak %s alındı" #, c-format msgid "E1013: Argument %d: type mismatch, expected %s but got %s" -msgstr "E1013: %d deÄŸiÅŸkeni: Tür uyumsuzluÄŸu, %s bekleniyordu, ancak %s alındı" +msgstr "E1013: %d argümanı: Tür uyumsuzluÄŸu, %s bekleniyordu, ancak %s alındı" #, c-format msgid "E1014: Invalid key: %s" @@ -6728,8 +6795,8 @@ msgid "E1022: Type or initialization required" msgstr "E1022: Tür veya ilklendirme gerekiyor" #, c-format -msgid "E1023: Using a Number as a Bool: %d" -msgstr "E1023: Bir Sayı, bir Bool yerine kullanılıyor: %d" +msgid "E1023: Using a Number as a Bool: %lld" +msgstr "E1023: Bir Sayı, bir Boole yerine kullanılıyor: %lld" msgid "E1024: Using a Number as a String" msgstr "E1024: Bir Sayı, bir Dizi yerine kullanılıyor" @@ -6768,11 +6835,11 @@ msgid "E1034: Cannot use reserved name %s" msgstr "E1034: Ayrılmış ad %s kullanılamaz" msgid "E1035: % requires number arguments" -msgstr "E1035: %, sayı deÄŸiÅŸkenler gerektirir" +msgstr "E1035: %, sayı argümanları gerektirir" #, c-format msgid "E1036: %c requires number or float arguments" -msgstr "E1036: %c, sayı veya kayan noktalı deÄŸer deÄŸiÅŸkenler gerektirir" +msgstr "E1036: %c, sayı veya kayan noktalı deÄŸer argümanları gerektirir" #, c-format msgid "E1037: Cannot use \"%s\" with %s" @@ -6798,7 +6865,7 @@ msgid "E1043: Invalid command after :export" msgstr "E1043: :export sonrası geçersiz komut" msgid "E1044: Export with invalid argument" -msgstr "E1044: Geçersiz deÄŸiÅŸkenle dışa aktarım" +msgstr "E1044: Geçersiz argümanla dışa aktarım" msgid "E1045: Missing \"as\" after *" msgstr "E1045: * sonrası \"as\" eksik" @@ -6817,11 +6884,12 @@ msgstr "E1048: Betikte öge bulunamadı: %s" msgid "E1049: Item not exported in script: %s" msgstr "E1049: Betikte öge dışa aktarılmadı: %s" -msgid "E1050: Colon required before a range" -msgstr "E1050: Bir erim öncesi iki nokta gerekiyor" +#, c-format +msgid "E1050: Colon required before a range: %s" +msgstr "E1050: Bir erim öncesi iki nokta gerekiyor: %s" msgid "E1051: Wrong argument type for +" -msgstr "E1051: + için hatalı deÄŸiÅŸken türü" +msgstr "E1051: + için hatalı argüman türü" #, c-format msgid "E1052: Cannot declare an option: %s" @@ -6875,12 +6943,12 @@ msgid "E1067: Separator mismatch: %s" msgstr "E1067: Ayırıcı uyumsuzluÄŸu: %s" #, c-format -msgid "E1068: No white space allowed before '%s'" -msgstr "E1068: '%s' önce boÅŸluÄŸa izin verilmiyor" +msgid "E1068: No white space allowed before '%s': %s" +msgstr "E1068: '%s' önce boÅŸluÄŸa izin verilmiyor: %s" #, c-format -msgid "E1069: White space required after '%s'" -msgstr "E1069: '%s' sonrası boÅŸluk gerekiyor" +msgid "E1069: White space required after '%s': %s" +msgstr "E1069: '%s' sonrası boÅŸluk gerekiyor: %s" msgid "E1070: Missing \"from\"" msgstr "E1070: \"from\" eksik" @@ -6908,7 +6976,7 @@ msgstr "E1076: Bu Vim kayan noktalı deÄŸer desteÄŸi ile derlenmemiÅŸ" #, c-format msgid "E1077: Missing argument type for %s" -msgstr "E1077: %s için deÄŸiÅŸken türü eksik" +msgstr "E1077: %s için argüman türü eksik" #, c-format msgid "E1081: Cannot unlet %s" @@ -6933,7 +7001,7 @@ msgid "E1086: Cannot use :function inside :def" msgstr "E1086: :def içinde :function kullanılamaz" msgid "E1087: Cannot use an index when declaring a variable" -msgstr "E1087: Bir deÄŸiÅŸken tanımlarken indeks kullanılamaz" +msgstr "E1087: Bir deÄŸiÅŸken tanımlarken dizinleme kullanılamaz" #, c-format msgid "E1089: Unknown variable: %s" @@ -6941,7 +7009,7 @@ msgstr "E1089: Bilinmeyen deÄŸiÅŸken: %s" #, c-format msgid "E1090: Cannot assign to argument %s" -msgstr "E1090: %s deÄŸiÅŸkenine atanamıyor" +msgstr "E1090: %s argümanına atanamıyor" #, c-format msgid "E1091: Function is not compiled: %s" @@ -6989,11 +7057,11 @@ msgid "E1105: Cannot convert %s to string" msgstr "E1105: %s bir diziye dönüştürülemiyor" msgid "E1106: One argument too many" -msgstr "E1106: Bir deÄŸiÅŸken fazladan" +msgstr "E1106: Bir argüman fazladan" #, c-format msgid "E1106: %d arguments too many" -msgstr "E1106: %d deÄŸiÅŸken fazladan" +msgstr "E1106: %d argüman fazladan" msgid "E1107: String, List, Dict or Blob required" msgstr "E1107: Dizi, Liste, Sözlük veya İkili Nesne gerekiyor" @@ -7026,10 +7094,10 @@ msgid "E1114: Only values of 0x100 and higher supported" msgstr "E1114: Yalnızca 0x100 ve daha yüksek deÄŸerler destekleniyor" msgid "E1115: \"assert_fails()\" fourth argument must be a number" -msgstr "E1115: \"assert_fails()\" dördüncü deÄŸiÅŸkeni bir sayı olmalıdır" +msgstr "E1115: \"assert_fails()\" dördüncü argüman bir sayı olmalıdır" msgid "E1116: \"assert_fails()\" fifth argument must be a string" -msgstr "E1116: \"assert_fails()\" beÅŸinci deÄŸiÅŸkeni bir dizi olmalıdır" +msgstr "E1116: \"assert_fails()\" beÅŸinci argüman bir dizi olmalıdır" msgid "E1117: Cannot use ! with nested :def" msgstr "E1117: !, iç içe geçmiÅŸ :def ile kullanılamaz" @@ -7080,7 +7148,7 @@ msgid "E1131: Cannot add to null blob" msgstr "E1131: Null ikili geniÅŸ nesnesine ekleme yapılamaz" msgid "E1132: Missing function argument" -msgstr "E1132: İşlev deÄŸiÅŸkeni eksik" +msgstr "E1132: İşlev argümanı eksik" msgid "E1133: Cannot extend a null dict" msgstr "E1133: Bir null sözlük geniÅŸletilemez" @@ -7108,12 +7176,259 @@ msgstr "E1138: Bir Boole, Sayı yerine kullanılıyor" msgid "E1139: Missing matching bracket after dict key" msgstr "E1139: Sözlük anahtarı sonrası eÅŸleÅŸen ayraç eksik" -msgid "E1140: For argument must be a sequence of lists" -msgstr "E1140: For deÄŸiÅŸkeni listelerin bir sıralaması olmalıdır" +msgid "E1140: :for argument must be a sequence of lists" +msgstr "E1140: :for argümanı listelerin bir sıralaması olmalıdır" msgid "E1141: Indexable type required" msgstr "E1141: İndekslenebilir tür gerekiyor" +msgid "E1142: Non-empty string required" +msgstr "E1142: BoÅŸ olmayan dizi gerekiyor" + +#, c-format +msgid "E1143: Empty expression: \"%s\"" +msgstr "E1143: BoÅŸ ifade: \"%s\"" + +#, c-format +msgid "E1144: Command \"%s\" is not followed by white space: %s" +msgstr "E1144: \"%s\" komutu sonrasında boÅŸluk gelmiyor: %s" + +#, c-format +msgid "E1145: Missing heredoc end marker: %s" +msgstr "E1145: Son imleyicisi eksik: %s" + +#, c-format +msgid "E1146: Command not recognized: %s" +msgstr "E1146: Komut tanınamadı: %s" + +msgid "E1147: List not set" +msgstr "E1147: Liste ayarlanmamış" + +#, c-format +msgid "E1148: Cannot index a %s" +msgstr "E1148: Bir %s dizinlenemiyor" + +#, c-format +msgid "E1149: Script variable is invalid after reload in function %s" +msgstr "E1149: %s iÅŸlevindeki yeniden yüklemeden sonra betik deÄŸiÅŸkeni geçersiz" + +msgid "E1150: Script variable type changed" +msgstr "E1150: Betik deÄŸiÅŸkeni türü deÄŸiÅŸtirildi" + +msgid "E1151: Mismatched endfunction" +msgstr "E1151: EÅŸleÅŸmeyen endfunction" + +msgid "E1152: Mismatched enddef" +msgstr "E1152: EÅŸleÅŸmeyen enddef" + +msgid "E1153: Invalid operation for bool" +msgstr "E1153: Boole için geçersiz iÅŸlem" + +msgid "E1154: Divide by zero" +msgstr "E1154: Sıfır ile bölüm" + +msgid "E1155: Cannot define autocommands for ALL events" +msgstr "E1155: Otokomutlar TÜM olaylar için tanımlanamıyor" + +msgid "E1156: Cannot change the argument list recursively" +msgstr "E1156: DeÄŸiÅŸken listesi özyineli olarak deÄŸiÅŸtirilemiyor" + +msgid "E1157: Missing return type" +msgstr "E1157: Dönüş türü eksik" + +msgid "E1158: Cannot use flatten() in Vim9 script" +msgstr "E1158: flatten(), Vim9 betiÄŸinde kullanılamaz" + +msgid "E1159: Cannot split a window when closing the buffer" +msgstr "E1159: Arabellek kapatılırken bir pencere bölünemez" + +msgid "E1160: Cannot use a default for variable arguments" +msgstr "E1160: DeÄŸiÅŸken argümanları için bir öntanımlı kullanılamaz" + +#, c-format +msgid "E1161: Cannot json encode a %s" +msgstr "E1161: Bir %s JSON olarak kodlanamıyor" + +#, c-format +msgid "E1162: Register name must be one character: %s" +msgstr "E1162: Yazmaç adı tek bir karakter olmalıdır: %s" + +#, c-format +msgid "E1163: Variable %d: type mismatch, expected %s but got %s" +msgstr "E1163: %d deÄŸiÅŸkeni: Tür uyumsuzluÄŸu, %s bekleniyordu, ancak %s alındı" + +msgid "E1164: vim9cmd must be followed by a command" +msgstr "E1164: vim9cmd sonrasında bir komut gelmelidir" + +#, c-format +msgid "E1165: Cannot use a range with an assignment: %s" +msgstr "E1165: Bir atama ile bir erim kullanılamıyor: %s" + +msgid "E1166: Cannot use a range with a dictionary" +msgstr "E1166: Bir sözlük ile bir erim kullanılamıyor" + +#, c-format +msgid "E1167: Argument name shadows existing variable: %s" +msgstr "E1167: Argüman adı var olan deÄŸiÅŸkeni gölgeliyor: %s" + +#, c-format +msgid "E1168: Argument already declared in the script: %s" +msgstr "E1168: Betikte argüman halihazırda tanımlanmış: %s" + +msgid "E1169: 'import * as {name}' not supported here" +msgstr "E1169: 'import * as {name} burada desteklenmiyor" + +msgid "E1170: Cannot use #{ to start a comment" +msgstr "E1170: Bir yorum baÅŸlatmak için #{ kullanılamaz" + +msgid "E1171: Missing } after inline function" +msgstr "E1171: Satıriçi iÅŸlevden sonra } eksik" + +msgid "E1172: Cannot use default values in a lambda" +msgstr "E1172: Bir lambda içerisinde öntanımlı deÄŸerler kullanılamıyor" + +#, c-format +msgid "E1173: Text found after enddef: %s" +msgstr "E1173: :enddef sonrası metin bulundu: %s" + +#, c-format +msgid "E1174: String required for argument %d" +msgstr "E1174: %d argümanı için dizi gerekiyor" + +#, c-format +msgid "E1175: Non-empty string required for argument %d" +msgstr "E1175: %d argümanı için boÅŸ olmayan dizi gerekiyor" + +msgid "E1176: Misplaced command modifier" +msgstr "E1176: Yanlış yere konulmuÅŸ komut deÄŸiÅŸtiricisi" + +#, c-format +msgid "E1177: For loop on %s not supported" +msgstr "E1177: %s üzerinde for döngüsü desteklenmiyor" + +msgid "E1178: Cannot lock or unlock a local variable" +msgstr "E1178: Bir yerel deÄŸiÅŸken kilitlenemiyor/kilidi açılamıyor" + +#, c-format +msgid "" +"E1179: Failed to extract PWD from %s, check your shell's config related to " +"OSC 7" +msgstr "" +"E1179: %s içinden PWD çıkarılamadı, kabuÄŸunuzun OSC 7 ile ilgili " +"yapılandırmasını denetleyin" + +#, c-format +msgid "E1180: Variable arguments type must be a list: %s" +msgstr "E1180: DeÄŸiÅŸken argümanları türü bir liste olmalıdır: %s" + +msgid "E1181: Cannot use an underscore here" +msgstr "E1181: Alt çizgi burada kullanılamaz" + +msgid "E1182: Blob required" +msgstr "E1182: İkili geniÅŸ nesne gerekiyor" + +#, c-format +msgid "E1183: Cannot use a range with an assignment operator: %s" +msgstr "E1183: Bir atama iÅŸleci ile bir erim kullanılamıyor: %s" + +msgid "E1184: Blob not set" +msgstr "E1184: İkili geniÅŸ nesne ayarlanmamış" + +msgid "E1185: Cannot nest :redir" +msgstr "E1185: :redir içe geçirilemiyor" + +msgid "E1185: Missing :redir END" +msgstr "E1185: :redir END eksik" + +#, c-format +msgid "E1186: Expression does not result in a value: %s" +msgstr "E1186: İfade bir deÄŸer sonucu vermiyor: %s" + +msgid "E1187: Failed to source defaults.vim" +msgstr "E1187: defaults.vim kaynaklanamadı" + +msgid "E1188: Cannot open a terminal from the command line window" +msgstr "E1188: Komut satırı penceresinden bir uçbirim açılamıyor" + +#, c-format +msgid "E1189: Cannot use :legacy with this command: %s" +msgstr "E1189: :legacy, bu komut ile kullanılamıyor: %s" + +msgid "E1190: One argument too few" +msgstr "E1190: Bir argüman daha gerekiyor" + +#, c-format +msgid "E1190: %d arguments too few" +msgstr "E1190: %d argüman daha gerekiyor" + +#, c-format +msgid "E1191: Call to function that failed to compile: %s" +msgstr "E1191: Derlenemeyen iÅŸlev çaÄŸrısı: %s" + +msgid "E1192: Empty function name" +msgstr "E1192: BoÅŸ iÅŸlev adı" + +msgid "E1193: cryptmethod xchacha20 not built into this Vim" +msgstr "E1193: cryptmethod xchacha20 bu Vim ile kullanılamıyor" + +msgid "E1194: Cannot encrypt header, not enough space" +msgstr "E1194: Üstbilgi ÅŸifrelenemiyor, yetersiz alan" + +msgid "E1195: Cannot encrypt buffer, not enough space" +msgstr "E1195: Arabellek ÅŸifrelenemiyor, yetersiz alan" + +msgid "E1196: Cannot decrypt header, not enough space" +msgstr "E1196: Üstbilgi ÅŸifresi çözülemiyor, yetersiz alan" + +msgid "E1197: Cannot allocate_buffer for encryption" +msgstr "E1197: Åžifreleme için allocate_buffer yapılamıyor" + +msgid "E1198: Decryption failed: Header incomplete!" +msgstr "E1198: Åžifre çözümü baÅŸarısız: Üstbilgi tam deÄŸil!" + +msgid "E1199: Cannot decrypt buffer, not enough space" +msgstr "E1199: Arabellek ÅŸifresi çözülemiyor, yetersiz alan" + +msgid "E1200: Decryption failed!" +msgstr "E1200: Åžifre çözümü baÅŸarısız!" + +msgid "E1201: Decryption failed: pre-mature end of file!" +msgstr "E1201: Åžifre çözümü baÅŸarısız: Beklenmedik dosya sonu!" + +#, c-format +msgid "E1202: No white space allowed after '%s': %s" +msgstr "E1202: '%s' sonrası boÅŸluÄŸa izin verilmiyor: %s" + +#, c-format +msgid "E1203: Dot can only be used on a dictionary: %s" +msgstr "E1203: Nokta yalnızca bir sözlükte kullanılabilir: %s" + +#, c-format +msgid "E1204: No Number allowed after .: '\\%%%c'" +msgstr "E1204: . sonrası Sayıya izin verilmiyor: '\\%%%c'" + +msgid "E1205: No white space allowed between option and" +msgstr "E1205: and seçeneÄŸi arasında boÅŸluÄŸa izin verilmiyor" + +#, c-format +msgid "E1206: Dictionary required for argument %d" +msgstr "E1206: %d argümanı için sözlük gerekiyor" + +#, c-format +msgid "E1207: Expression without an effect: %s" +msgstr "E1207: Bir efekt olmadan ifade: %s" + +msgid "E1208: -complete used without -nargs" +msgstr "E1208: -complete, -nargs olmadan kullanıldı" + +#, c-format +msgid "E1209: Invalid value for a line number: \"%s\"" +msgstr "E1209: Satır numarası için geçersiz deÄŸer: \"%s\"" + +#, c-format +msgid "E1210: Number required for argument %d" +msgstr "E1210: %d argümanı için sayı gerekiyor" + msgid "--No lines in buffer--" msgstr "--Arabellek içinde satır yok--" @@ -7123,17 +7438,6 @@ msgstr "E470: Komut durduruldu" msgid "E471: Argument required" msgstr "E471: DeÄŸiÅŸken gerekiyor" -msgid "E10: \\ should be followed by /, ? or &" -msgstr "E10: \\ sonrasında /, ? veya & gelmeli" - -msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits" -msgstr "E11: Komut satırı penceresinde geçersiz; <CR> çalıştırır, CTRL-C çıkar" - -msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search" -msgstr "" -"E12: Geçerli dizin veya etiket aramasında exrc veya vimrc'den komutlara izin " -"verilmiyor" - msgid "E171: Missing :endif" msgstr "E171: :endif eksik" @@ -7164,9 +7468,6 @@ msgstr "E588: :while olmadan :endwhile" msgid "E588: :endfor without :for" msgstr "E588: :for olmadan :endfor" -msgid "E13: File exists (add ! to override)" -msgstr "E13: Dosya mevcut (geçersiz kılmak için ! ekleyin)" - msgid "E472: Command failed" msgstr "E472: Komut baÅŸarısız oldu" @@ -7193,34 +7494,23 @@ msgid "Interrupted" msgstr "Yarıda kesildi" msgid "E474: Invalid argument" -msgstr "E474: Geçersiz deÄŸiÅŸken" +msgstr "E474: Geçersiz argüman" #, c-format msgid "E475: Invalid argument: %s" -msgstr "E475: Geçersiz deÄŸiÅŸken: %s" +msgstr "E475: Geçersiz argüman: %s" #, c-format msgid "E983: Duplicate argument: %s" -msgstr "E983: Yinelenen deÄŸiÅŸken: %s" +msgstr "E983: Yinelenen argüman: %s" #, c-format msgid "E475: Invalid value for argument %s" -msgstr "E475: %s deÄŸiÅŸkeni için geçersiz deÄŸer" +msgstr "E475: %s argümanı için geçersiz deÄŸer" #, c-format msgid "E475: Invalid value for argument %s: %s" -msgstr "E475: %s deÄŸiÅŸkeni için geçersiz deÄŸer: %s" - -#, c-format -msgid "E15: Invalid expression: %s" -msgstr "E15: Geçersiz ifade: %s" - -msgid "E16: Invalid range" -msgstr "E16: Geçersiz erim" - -#, c-format -msgid "E17: \"%s\" is a directory" -msgstr "E17: \"%s\" bir dizin" +msgstr "E475: %s argümanı için geçersiz deÄŸer: %s" msgid "E756: Spell checking is not possible" msgstr "E756: Yazım denetimi olanaklı deÄŸil" @@ -7236,24 +7526,6 @@ msgstr "E667: Fsync baÅŸarısız oldu" msgid "E448: Could not load library function %s" msgstr "E448: %s kitaplık iÅŸlevi yüklenemedi" -msgid "E19: Mark has invalid line number" -msgstr "E19: İm satır numarası geçersiz" - -msgid "E20: Mark not set" -msgstr "E20: İm ayarlanmamış" - -msgid "E21: Cannot make changes, 'modifiable' is off" -msgstr "E21: DeÄŸiÅŸiklik yapılamıyor, 'modifiable' kapalı" - -msgid "E22: Scripts nested too deep" -msgstr "E22: Betikler çok iç içe geçmiÅŸ" - -msgid "E23: No alternate file" -msgstr "E23: BaÅŸka bir dosya yok" - -msgid "E24: No such abbreviation" -msgstr "E24: Böyle bir kısaltma yok" - msgid "E477: No ! allowed" msgstr "E477: ! imine izin verilmiyor" @@ -7327,7 +7599,7 @@ msgid "E485: Can't read file %s" msgstr "E485: %s dosyası okunamıyor" msgid "E38: Null argument" -msgstr "E38: Anlamsız deÄŸiÅŸken" +msgstr "E38: Anlamsız argüman" msgid "E39: Number expected" msgstr "E39: Sayı bekleniyordu" @@ -7392,6 +7664,9 @@ msgstr "E794: DeÄŸiÅŸken kum havuzunda ayarlanamıyor: \"%s\"" msgid "E928: String required" msgstr "E928: Dizi gerekiyor" +msgid "E889: Number required" +msgstr "E889: Sayı gerekiyor" + msgid "E713: Cannot use empty key for Dictionary" msgstr "E713: Sözlük için boÅŸ anahtar kullanılamaz" @@ -7411,11 +7686,11 @@ msgstr "E978: İkili geniÅŸ nesne için geçersiz iÅŸlem" #, c-format msgid "E118: Too many arguments for function: %s" -msgstr "E118: İşlev için çok fazla deÄŸiÅŸken: %s" +msgstr "E118: İşlev için çok fazla argüman: %s" #, c-format msgid "E119: Not enough arguments for function: %s" -msgstr "E119: Åžu iÅŸlev için yetersiz sayıda deÄŸiÅŸken: %s" +msgstr "E119: Åžu iÅŸlev için yetersiz sayıda argüman: %s" #, c-format msgid "E933: Function was deleted: %s" @@ -7437,18 +7712,15 @@ msgstr "E697: Liste sonunda ']' eksik: %s" #, c-format msgid "E712: Argument of %s must be a List or Dictionary" -msgstr "E712: %s ögesinin deÄŸiÅŸkeni bir liste veya sözlük olmalıdır" +msgstr "E712: %s ögesinin argümanı bir liste veya sözlük olmalıdır" #, c-format msgid "E896: Argument of %s must be a List, Dictionary or Blob" -msgstr "E896: %s deÄŸiÅŸkeni bir liste, sözlük veya ikili geniÅŸ nesne olmalıdır" +msgstr "E896: %s argümanı bir liste, sözlük veya ikili geniÅŸ nesne olmalıdır" msgid "E804: Cannot use '%' with Float" msgstr "E804: Bir kayan noktalı deÄŸer ile '%' kullanılamaz" -msgid "E908: using an invalid value as a String" -msgstr "E908: Geçersiz bir deÄŸer bir Dizi yerine kullanılıyor" - msgid "E996: Cannot lock an option" msgstr "E996: Seçenek kilitlenemiyor" @@ -7456,9 +7728,6 @@ msgstr "E996: Seçenek kilitlenemiyor" msgid "E113: Unknown option: %s" msgstr "E113: Bilinmeyen seçenek: %s" -msgid "E18: Unexpected characters in :let" -msgstr "E18: :let içinde beklenmeyen karakter" - #, c-format msgid "E998: Reduce of an empty %s with no initial value" msgstr "E998: BaÅŸlangıç deÄŸeri olmayan boÅŸ bir %s için reduce() yapılamıyor" @@ -7616,7 +7885,7 @@ msgstr "E957: Geçersiz pencere numarası" #, c-format msgid "E686: Argument of %s must be a List" -msgstr "E686: %s deÄŸiÅŸkeni bir liste olmalı" +msgstr "E686: %s argümanı bir liste olmalı" msgid "E109: Missing ':' after '?'" msgstr "E109: '?' sonrası ':' eksik" @@ -7685,7 +7954,7 @@ msgstr "'%s' anahtarı sözlüğe eklenemedi" #, c-format msgid "index must be int or slice, not %s" -msgstr "dizin bir tamsayı veya dilim olmalıdır, %s olamaz" +msgstr "sıra bir tamsayı veya dilim olmalıdır, %s olamaz" #, c-format msgid "expected str() or unicode() instance, but got %s" @@ -7762,10 +8031,10 @@ msgid "expected sequence element of size 2, but got sequence of size %d" msgstr "2 boyut bir sıralama bekleniyordu, ancak %d boyut bir sıralama geldi" msgid "list constructor does not accept keyword arguments" -msgstr "liste yapıcısı anahtar sözcük deÄŸiÅŸkenleri kabul etmez" +msgstr "liste yapıcısı anahtar sözcük argümanları kabul etmez" msgid "list index out of range" -msgstr "liste dizini erimin dışında" +msgstr "liste sırası erimin dışında" #, c-format msgid "internal error: failed to get Vim list item %d" @@ -8013,8 +8282,7 @@ msgstr "" "düzenleyebilirsiniz." msgid "\" Hit <Enter> on a help line to open a help window on this option." -msgstr "" -"\" Yardım penceresini açmak için seçenek adı üzerinde <Enter>'a basın." +msgstr "\" Yardım penceresini açmak için seçenek adı üzerinde <Enter>'a basın." msgid "\" Hit <Enter> on an index line to jump there." msgstr "" @@ -8056,7 +8324,8 @@ msgid "moving around, searching and patterns" msgstr "dolaÅŸma, arama ve dizgeler" msgid "list of flags specifying which commands wrap to another line" -msgstr "hangi komutların diÄŸer satıra kaydırıldığını belirleyen bayraklar\n" +msgstr "" +"hangi komutların diÄŸer satıra kaydırıldığını belirleyen bayraklar\n" "listesi" msgid "" @@ -8081,6 +8350,9 @@ msgstr ":cd için kullanılan dizin adları listesi" msgid "change to directory of file in buffer" msgstr "arabellekteki dosyanın olduÄŸu dizine deÄŸiÅŸtir" +msgid "change to pwd of shell in terminal buffer" +msgstr "uçbirim arabelleÄŸindeki kabuÄŸun pwd'sine geç" + msgid "search commands wrap around the end of the buffer" msgstr "arama komutları, arabelleÄŸin sonunda kaydırılır" @@ -8320,7 +8592,8 @@ msgid "multiple windows" msgstr "çoklu pencereler" msgid "0, 1 or 2; when to use a status line for the last window" -msgstr "0, 1 veya 2; son pencere için ne zaman bir durum satırı\n" +msgstr "" +"0, 1 veya 2; son pencere için ne zaman bir durum satırı\n" "kullanılacağı" msgid "alternate format to be used for a status line" @@ -8382,7 +8655,8 @@ msgid "this window scrolls together with other bound windows" msgstr "bu pencere, baÄŸlı diÄŸer pencerelerle birlikte kayar" msgid "\"ver\", \"hor\" and/or \"jump\"; list of options for 'scrollbind'" -msgstr "\"ver\", \"hor\" ve/veya \"jump\"; 'scrollbind' için seçenekler listesi" +msgstr "" +"\"ver\", \"hor\" ve/veya \"jump\"; 'scrollbind' için seçenekler listesi" msgid "this window's cursor moves together with other bound windows" msgstr "bu pencerenin imleci baÄŸlı diÄŸer pencerelerle birlikte kayar" @@ -8394,7 +8668,8 @@ msgid "key that precedes Vim commands in a terminal window" msgstr "bir uçbirim penceresinde Vim komutlarından önce gelen düğme" msgid "max number of lines to keep for scrollback in a terminal window" -msgstr "bir uçbirim penceresinde geri kaydırma için kullanılacak\n" +msgstr "" +"bir uçbirim penceresinde geri kaydırma için kullanılacak\n" "en çok satır sayısı" msgid "type of pty to use for a terminal window" @@ -8522,8 +8797,7 @@ msgid "list of flags that specify how the GUI works" msgstr "grafik arabirimin nice çalıştığını belirleyen bayraklar listesi" msgid "\"icons\", \"text\" and/or \"tooltips\"; how to show the toolbar" -msgstr "" -"\"icons\", \"text\" ve/veya \"tooltips\"; araç çubuÄŸu kipleri " +msgstr "\"icons\", \"text\" ve/veya \"tooltips\"; araç çubuÄŸu kipleri " msgid "size of toolbar icons" msgstr "araç çubuÄŸu simgelerinin boyutu" @@ -8679,7 +8953,8 @@ msgid "list of directories for undo files" msgstr "geri al dosyaları için dizinler listesi" msgid "maximum number lines to save for undo on a buffer reload" -msgstr "arabellek yeniden yüklemesinde geri al için kaydedilecek\n" +msgstr "" +"arabellek yeniden yüklemesinde geri al için kaydedilecek\n" "en çok satır sayısı" msgid "changes have been made and not written to a file" @@ -8704,7 +8979,8 @@ msgid "definition of what comment lines look like" msgstr "yorum satırlarının nice görüneceÄŸinin tanımı" msgid "list of flags that tell how automatic formatting works" -msgstr "kendiliÄŸinden biçimlendirmenin nice çalıştığını anlatan\n" +msgstr "" +"kendiliÄŸinden biçimlendirmenin nice çalıştığını anlatan\n" "bayraklar listesi" msgid "pattern to recognize a numbered list" @@ -8714,7 +8990,8 @@ msgid "expression used for \"gq\" to format lines" msgstr "satırları biçimlendirmek için \"gq\" için kullanılan ifade" msgid "specifies how Insert mode completion works for CTRL-N and CTRL-P" -msgstr "Ekleme kipi tamamlamasının CTRL-N ve CTRL-P için nice çalıştığını\n" +msgstr "" +"Ekleme kipi tamamlamasının CTRL-N ve CTRL-P için nice çalıştığını\n" "belirler" msgid "whether to use a popup menu for Insert mode completion" @@ -8739,7 +9016,8 @@ msgid "list of dictionary files for keyword completion" msgstr "anahtar sözcük tamamlaması için sözlük dosyaları listesi" msgid "list of thesaurus files for keyword completion" -msgstr "anahtar sözcük tamamlaması için eÅŸanlamlılar sözlüğü dosyaları\n" +msgstr "" +"anahtar sözcük tamamlaması için eÅŸanlamlılar sözlüğü dosyaları\n" "listesi" msgid "adjust case of a keyword completion match" @@ -8883,7 +9161,8 @@ msgid "markers used when 'foldmethod' is \"marker\"" msgstr "'foldmethod' \"marker\" olduÄŸunda kullanılan imleyiciler" msgid "maximum fold depth for when 'foldmethod' is \"indent\" or \"syntax\"" -msgstr "'foldmethod' \"indent\" veya \"syntax\" olduÄŸunda kullanılan en çok\n" +msgstr "" +"'foldmethod' \"indent\" veya \"syntax\" olduÄŸunda kullanılan en çok\n" "kıvırma derinliÄŸi" msgid "diff mode" @@ -9013,7 +9292,8 @@ msgid "use a swap file for this buffer" msgstr "bu arabellek için bir takas dosyası kullan" msgid "\"sync\", \"fsync\" or empty; how to flush a swap file to disk" -msgstr "\"sync\", \"fsync\", veya boÅŸ; bir takas dosyasının diske\n" +msgstr "" +"\"sync\", \"fsync\", veya boÅŸ; bir takas dosyasının diske\n" "nice floÅŸlanacağı" msgid "number of characters typed to cause a swap file update" @@ -9089,13 +9369,14 @@ msgid "characters to escape when 'shellxquote' is (" msgstr "'shellxquote' ( iken kaçırılacak karakterler" msgid "argument for 'shell' to execute a command" -msgstr "bir komut çalıştırmak için 'shell' için deÄŸiÅŸken" +msgstr "bir komut çalıştırmak için 'shell' için argüman" msgid "used to redirect command output to a file" msgstr "komut çıktısını bir dosyaya yeniden yönlendirmek için kullanılır" msgid "use a temp file for shell commands instead of using a pipe" -msgstr "bir veri yolu kullanımı yerine kabuk komutları için geçici\n" +msgstr "" +"bir veri yolu kullanımı yerine kabuk komutları için geçici\n" "bir dosya kullan" msgid "program used for \"=\" command" @@ -9108,7 +9389,8 @@ msgid "program used for the \"K\" command" msgstr "\"K\" komutu için kullanılan program" msgid "warn when using a shell command and a buffer has changes" -msgstr "bir kabuk komutu kullanılıyorsa ve arabellekte deÄŸiÅŸiklikler\n" +msgstr "" +"bir kabuk komutu kullanılıyorsa ve arabellekte deÄŸiÅŸiklikler\n" "varsa uyar" msgid "running make and jumping to errors (quickfix)" @@ -9124,7 +9406,8 @@ msgid "program used for the \":make\" command" msgstr "\":make\" komutu için kullanılan program" msgid "string used to put the output of \":make\" in the error file" -msgstr "\":make\" komutunun çıktısını hata dosyasına koymak için\n" +msgstr "" +"\":make\" komutunun çıktısını hata dosyasına koymak için\n" "kullanılan dizi" msgid "name of the errorfile for the 'makeprg' command" @@ -9179,7 +9462,8 @@ msgid "insert characters backwards" msgstr "karakterleri geriye doÄŸru ekle" msgid "allow CTRL-_ in Insert and Command-line mode to toggle 'revins'" -msgstr "'revins' açıp kapatmak için Ekleme ve Komut Satırı kipinde\n" +msgstr "" +"'revins' açıp kapatmak için Ekleme ve Komut Satırı kipinde\n" "CTRL-_ izin ver" msgid "the ASCII code for the first letter of the Hebrew alphabet" @@ -9210,8 +9494,9 @@ msgid "apply 'langmap' to mapped characters" msgstr "eÅŸlemlenen karakterlere 'langmap' uygula" msgid "when set never use IM; overrules following IM options" -msgstr "ayarlandığında hiçbir zaman IM kullanma; aÅŸağıdaki IM seçeneklerini " -"geçersiz kılar" +msgstr "" +"ayarlandığında hiçbir zaman IM kullanma; aÅŸağıdaki IM seçeneklerini geçersiz " +"kılar" msgid "in Insert mode: 1: use :lmap; 2: use IM; 0: neither" msgstr "Ekleme kipinde: 1: :lmap kullan; 2; IM kullan; 0: hiçbiri" diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 7d452d6797..1705ea0c12 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -9,6 +9,7 @@ #include <inttypes.h> #include <stdbool.h> +#include "nvim/buffer.h" #include "nvim/vim.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" @@ -440,7 +441,7 @@ void pum_redraw(void) } if (ui_has(kUIMultigrid)) { const char *anchor = pum_above ? "SW" : "NW"; - int row_off = pum_above ? pum_height : 0; + int row_off = pum_above ? -pum_height : 0; ui_call_win_float_pos(pum_grid.handle, -1, cstr_to_string(anchor), pum_anchor_grid, pum_row-row_off, pum_col-col_off, false, pum_grid.zindex); @@ -735,7 +736,7 @@ static int pum_set_selected(int n, int repeat) && (curbuf->b_p_bt[2] == 'f') && (curbuf->b_p_bh[0] == 'w')) { // Already a "wipeout" buffer, make it empty. - while (!BUFEMPTY()) { + while (!buf_is_empty(curbuf)) { ml_delete((linenr_T)1, false); } } else { diff --git a/src/nvim/profile.c b/src/nvim/profile.c index 0a5030edae..f9b0bb0a2b 100644 --- a/src/nvim/profile.c +++ b/src/nvim/profile.c @@ -250,7 +250,7 @@ void time_start(const char *message) return; } - // intialize the global variables + // initialize the global variables g_prev_time = g_start_time = profile_start(); fprintf(time_fd, "\n\ntimes in msec\n"); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 1a9bbe26f0..0cfb7c192f 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -54,20 +54,23 @@ struct dir_stack_T { // For each error the next struct is allocated and linked in a list. typedef struct qfline_S qfline_T; struct qfline_S { - qfline_T *qf_next; ///< pointer to next error in the list - qfline_T *qf_prev; ///< pointer to previous error in the list - linenr_T qf_lnum; ///< line number where the error occurred - int qf_fnum; ///< file number for the line - int qf_col; ///< column where the error occurred - int qf_nr; ///< error number - char_u *qf_module; ///< module name for this error - char_u *qf_pattern; ///< search pattern for the error - char_u *qf_text; ///< description of the error - char_u qf_viscol; ///< set to TRUE if qf_col is screen column - char_u qf_cleared; ///< set to TRUE if line has been deleted - char_u qf_type; ///< type of the error (mostly 'E'); 1 for - // :helpgrep - char_u qf_valid; ///< valid error message detected + qfline_T *qf_next; ///< pointer to next error in the list + qfline_T *qf_prev; ///< pointer to previous error in the list + linenr_T qf_lnum; ///< line number where the error occurred + linenr_T qf_end_lnum; ///< line number when the error has range or zero + + int qf_fnum; ///< file number for the line + int qf_col; ///< column where the error occurred + int qf_end_col; ///< column when the error has range or zero + int qf_nr; ///< error number + char_u *qf_module; ///< module name for this error + char_u *qf_pattern; ///< search pattern for the error + char_u *qf_text; ///< description of the error + char_u qf_viscol; ///< set to TRUE if qf_col and qf_end_col is + // screen column + char_u qf_cleared; ///< set to TRUE if line has been deleted + char_u qf_type; ///< type of the error (mostly 'E'); 1 for :helpgrep + char_u qf_valid; ///< valid error message detected }; // There is a stack of error lists. @@ -100,7 +103,7 @@ typedef struct qf_list_S { char_u *qf_title; ///< title derived from the command that created ///< the error list or set by setqflist typval_T *qf_ctx; ///< context set by setqflist/setloclist - char_u *qf_qftf; ///< 'quickfixtextfunc' setting for this list + Callback qftf_cb; ///< 'quickfixtextfunc' callback function struct dir_stack_T *qf_dir_stack; char_u *qf_directory; @@ -197,7 +200,9 @@ typedef struct { char_u *errmsg; size_t errmsglen; long lnum; + long end_lnum; int col; + int end_col; bool use_viscol; char_u *pattern; int enr; @@ -282,7 +287,9 @@ static int qf_init_process_nextline(qf_list_T *qfl, 0, fields->errmsg, fields->lnum, + fields->end_lnum, fields->col, + fields->end_col, fields->use_viscol, fields->pattern, fields->enr, @@ -541,6 +548,9 @@ static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr, static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls +// callback function for 'quickfixtextfunc' +static Callback qftf_cb; + static void free_efm_list(efm_T **efm_first) { for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) { @@ -1558,7 +1568,9 @@ static int qf_parse_get_fields(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, fields->errmsg[0] = NUL; } fields->lnum = 0; + fields->end_lnum = 0; fields->col = 0; + fields->end_col = 0; fields->use_viscol = false; fields->enr = -1; fields->type = 0; @@ -1796,7 +1808,9 @@ void check_quickfix_busy(void) /// @param bufnum buffer number or zero /// @param mesg message /// @param lnum line number +/// @param end_lnum line number for end /// @param col column +/// @param end_col column for end /// @param vis_col using visual column /// @param pattern search pattern /// @param nr error number @@ -1805,8 +1819,9 @@ void check_quickfix_busy(void) /// /// @returns QF_OK or QF_FAIL. static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, - char_u *module, int bufnum, char_u *mesg, long lnum, - int col, char_u vis_col, char_u *pattern, int nr, + char_u *module, int bufnum, char_u *mesg, + long lnum, long end_lnum, int col, int end_col, + char_u vis_col, char_u *pattern, int nr, char_u type, char_u valid) { qfline_T *qfp = xmalloc(sizeof(qfline_T)); @@ -1825,7 +1840,9 @@ static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, } qfp->qf_text = vim_strsave(mesg); qfp->qf_lnum = lnum; + qfp->qf_end_lnum = end_lnum; qfp->qf_col = col; + qfp->qf_end_col = end_col; qfp->qf_viscol = vis_col; if (pattern == NULL || *pattern == NUL) { qfp->qf_pattern = NULL; @@ -1954,7 +1971,9 @@ static int copy_loclist_entries(const qf_list_T *from_qfl, qf_list_T *to_qfl) 0, from_qfp->qf_text, from_qfp->qf_lnum, + from_qfp->qf_end_lnum, from_qfp->qf_col, + from_qfp->qf_end_col, from_qfp->qf_viscol, from_qfp->qf_pattern, from_qfp->qf_nr, @@ -1978,7 +1997,7 @@ static int copy_loclist_entries(const qf_list_T *from_qfl, qf_list_T *to_qfl) } /// Copy the specified location list 'from_qfl' to 'to_qfl'. -static int copy_loclist(const qf_list_T *from_qfl, qf_list_T *to_qfl) +static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl) FUNC_ATTR_NONNULL_ALL { // Some of the fields are populated by qf_add_entry() @@ -2000,11 +2019,7 @@ static int copy_loclist(const qf_list_T *from_qfl, qf_list_T *to_qfl) } else { to_qfl->qf_ctx = NULL; } - if (from_qfl->qf_qftf != NULL) { - to_qfl->qf_qftf = vim_strsave(from_qfl->qf_qftf); - } else { - to_qfl->qf_qftf = NULL; - } + callback_copy(&to_qfl->qftf_cb, &from_qfl->qftf_cb); if (from_qfl->qf_count) { if (copy_loclist_entries(from_qfl, to_qfl) == FAIL) { @@ -2422,7 +2437,7 @@ static qfline_T *get_nth_entry(qf_list_T *qfl, int errornr, int *new_qfidx) return qf_ptr; } -/// Get a entry specied by 'errornr' and 'dir' from the current +/// Get a entry specified by 'errornr' and 'dir' from the current /// quickfix/location list. 'errornr' specifies the index of the entry and 'dir' /// specifies the direction (FORWARD/BACKWARD/FORWARD_FILE/BACKWARD_FILE). /// Returns a pointer to the entry and the index of the new entry is stored in @@ -2999,6 +3014,7 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, } qfl->qf_index = qf_index; + qfl->qf_ptr = qf_ptr; if (qf_win_pos_update(qi, old_qf_index)) { // No need to print the error message if it's visible in the error // window @@ -3109,11 +3125,8 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) } if (qfp->qf_lnum == 0) { IObuff[0] = NUL; - } else if (qfp->qf_col == 0) { - vim_snprintf((char *)IObuff, IOSIZE, "%" PRIdLINENR, qfp->qf_lnum); } else { - vim_snprintf((char *)IObuff, IOSIZE, "%" PRIdLINENR " col %d", - qfp->qf_lnum, qfp->qf_col); + qf_range_text(qfp, IObuff, IOSIZE); } vim_snprintf((char *)IObuff + STRLEN(IObuff), IOSIZE, "%s", (char *)qf_types(qfp->qf_type, qfp->qf_nr)); @@ -3233,6 +3246,32 @@ static void qf_fmt_text(const char_u *restrict text, char_u *restrict buf, buf[i] = NUL; } +// Range information from lnum, col, end_lnum, and end_col. +// Put the result in "buf[bufsize]". +static void qf_range_text(const qfline_T *qfp, char_u *buf, int bufsize) +{ + vim_snprintf((char *)buf, (size_t)bufsize, "%" PRIdLINENR, qfp->qf_lnum); + int len = (int)STRLEN(buf); + + if (qfp->qf_end_lnum > 0 && qfp->qf_lnum != qfp->qf_end_lnum) { + vim_snprintf((char *)buf + len, (size_t)(bufsize - len), + "-%" PRIdLINENR, qfp->qf_end_lnum); + len += (int)STRLEN(buf + len); + } + if (qfp->qf_col > 0) { + vim_snprintf((char *)buf + len, (size_t)(bufsize - len), + " col %d", qfp->qf_col); + len += (int)STRLEN(buf + len); + if (qfp->qf_end_col > 0 && qfp->qf_col != qfp->qf_end_col) { + vim_snprintf((char *)buf + len, (size_t)(bufsize - len), + "-%d", qfp->qf_end_col); + len += (int)STRLEN(buf + len); + } + } + buf[len] = NUL; +} + + /// Display information (list number, list size and the title) about a /// quickfix/location list. static void qf_msg(qf_info_T *qi, int which, char *lead) @@ -3385,7 +3424,7 @@ static void qf_free(qf_list_T *qfl) XFREE_CLEAR(qfl->qf_title); tv_free(qfl->qf_ctx); qfl->qf_ctx = NULL; - XFREE_CLEAR(qfl->qf_qftf); + callback_free(&qfl->qftf_cb); qfl->qf_id = 0; qfl->qf_changedtick = 0L; } @@ -3860,6 +3899,41 @@ static buf_T *qf_find_buf(qf_info_T *qi) return NULL; } +// Process the 'quickfixtextfunc' option value. +bool qf_process_qftf_option(void) +{ + typval_T *tv; + Callback cb; + + if (p_qftf == NULL || *p_qftf == NUL) { + callback_free(&qftf_cb); + return true; + } + + if (*p_qftf == '{') { + // Lambda expression + tv = eval_expr(p_qftf); + if (tv == NULL) { + return false; + } + } else { + // treat everything else as a function name string + tv = xcalloc(1, sizeof(*tv)); + tv->v_type = VAR_STRING; + tv->vval.v_string = vim_strsave(p_qftf); + } + + if (!callback_from_typval(&cb, tv)) { + tv_free(tv); + return false; + } + + callback_free(&qftf_cb); + qftf_cb = cb; + tv_free(tv); + return true; +} + /// Update the w:quickfix_title variable in the quickfix/location list window in /// all the tab pages. static void qf_update_win_titlevar(qf_info_T *qi) @@ -3891,7 +3965,15 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) int qf_winid = 0; if (IS_LL_STACK(qi)) { - qf_winid = curwin->handle; + if (curwin->w_llist == qi) { + win = curwin; + } else { + win = qf_find_win_with_loclist(qi); + if (win == NULL) { + return; + } + } + qf_winid = (int)win->handle; } if (old_last == NULL) { @@ -3928,7 +4010,9 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, int len; buf_T *errbuf; - if (qftf_str != NULL) { + // If the 'quickfixtextfunc' function returned an non-empty custom string + // for this entry, then use it. + if (qftf_str != NULL && *qftf_str != NUL) { STRLCPY(IObuff, qftf_str, IOSIZE); } else { if (qfp->qf_module != NULL) { @@ -3961,16 +4045,9 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, IObuff[len++] = '|'; } if (qfp->qf_lnum > 0) { - snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), "%" PRId64, - (int64_t)qfp->qf_lnum); + qf_range_text(qfp, IObuff + len, IOSIZE - len); len += (int)STRLEN(IObuff + len); - if (qfp->qf_col > 0) { - snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), " col %d", - qfp->qf_col); - len += (int)STRLEN(IObuff + len); - } - snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), "%s", (char *)qf_types(qfp->qf_type, qfp->qf_nr)); len += (int)STRLEN(IObuff + len); @@ -3997,22 +4074,25 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, return OK; } +// Call the 'quickfixtextfunc' function to get the list of lines to display in +// the quickfix window for the entries 'start_idx' to 'end_idx'. static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long end_idx) { - char_u *qftf = p_qftf; + Callback *cb = &qftf_cb; list_T *qftf_list = NULL; // If 'quickfixtextfunc' is set, then use the user-supplied function to get // the text to display. Use the local value of 'quickfixtextfunc' if it is // set. - if (qfl->qf_qftf != NULL) { - qftf = qfl->qf_qftf; + if (qfl->qftf_cb.type != kCallbackNone) { + cb = &qfl->qftf_cb; } - if (qftf != NULL && *qftf != NUL) { + if (cb != NULL && cb->type != kCallbackNone) { typval_T args[1]; + typval_T rettv; // create the dict argument dict_T *const dict = tv_dict_alloc_lock(VAR_FIXED); @@ -4026,8 +4106,16 @@ static list_T *call_qftf_func(qf_list_T *qfl, args[0].v_type = VAR_DICT; args[0].vval.v_dict = dict; - qftf_list = call_func_retlist(qftf, 1, args); - dict->dv_refcount--; + qftf_list = NULL; + + if (callback_call(cb, 1, args, &rettv)) { + if (rettv.v_type == VAR_LIST) { + qftf_list = rettv.vval.v_list; + tv_list_ref(qftf_list); + } + tv_clear(&rettv); + } + tv_dict_unref(dict); } return qftf_list; @@ -4064,6 +4152,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, if (qfl != NULL) { char_u dirname[MAXPATHL]; int prev_bufnr = -1; + bool invalid_val = false; *dirname = NUL; @@ -4086,10 +4175,15 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, while (lnum < qfl->qf_count) { char_u *qftf_str = NULL; - if (qftf_li != NULL) { - // Use the text supplied by the user defined function - qftf_str = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(qftf_li)); + // Use the text supplied by the user defined function (if any). + // If the returned value is not string, then ignore the rest + // of the returned values and use the default. + if (qftf_li != NULL && !invalid_val) { + qftf_str = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(qftf_li)); + if (qftf_str == NULL) { + invalid_val = true; } + } if (qf_buf_add_line(qfl, buf, lnum, qfp, dirname, qftf_str, prev_bufnr != qfp->qf_fnum) == FAIL) { @@ -4119,7 +4213,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, // Set the 'filetype' to "qf" each time after filling the buffer. This // resembles reading a file into a buffer, it's more logical when using // autocommands. - curbuf_lock++; + curbuf->b_ro_locked++; set_option_value("ft", 0L, "qf", OPT_LOCAL); curbuf->b_p_ma = false; @@ -4129,7 +4223,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, apply_autocmds(EVENT_BUFWINENTER, (char_u *)"quickfix", NULL, false, curbuf); keep_filetype = false; - curbuf_lock--; + curbuf->b_ro_locked--; // make sure it will be redrawn redraw_curbuf_later(NOT_VALID); @@ -4746,7 +4840,7 @@ static qfline_T *qf_find_entry_after_pos( FUNC_ATTR_NONNULL_ALL { if (qf_entry_after_pos(qfp, pos, linewise)) { - // First entry is after postion 'pos' + // First entry is after position 'pos' return qfp; } @@ -5202,7 +5296,9 @@ static bool vgr_match_buflines(qf_list_T *qfl, char_u *fname, buf_T *buf, ml_get_buf(buf, regmatch->startpos[0].lnum + lnum, false), regmatch->startpos[0].lnum + lnum, + regmatch->endpos[0].lnum + lnum, regmatch->startpos[0].col + 1, + regmatch->endpos[0].col + 1, false, // vis_col NULL, // search pattern 0, // nr @@ -5660,7 +5756,7 @@ static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start) // work when got_int is set. enter_cleanup(&cs); - wipe_buffer(buf, FALSE); + wipe_buffer(buf, true); // Restore the error/interrupt/exception state if not discarded by a // new aborting error, interrupt, or uncaught exception. @@ -5683,7 +5779,7 @@ static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start) } } -/// Copy the specified quickfix entry items into a new dict and appened the dict +/// Copy the specified quickfix entry items into a new dict and append the dict /// to 'list'. Returns OK on success. static int get_qfline_items(qfline_T *qfp, list_T *list) { @@ -5704,7 +5800,11 @@ static int get_qfline_items(qfline_T *qfp, list_T *list) if (tv_dict_add_nr(dict, S_LEN("bufnr"), (varnumber_T)bufnum) == FAIL || (tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)qfp->qf_lnum) == FAIL) + || (tv_dict_add_nr(dict, S_LEN("end_lnum"), (varnumber_T)qfp->qf_end_lnum) + == FAIL) || (tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)qfp->qf_col) == FAIL) + || (tv_dict_add_nr(dict, S_LEN("end_col"), (varnumber_T)qfp->qf_end_col) + == FAIL) || (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol) == FAIL) || (tv_dict_add_nr(dict, S_LEN("nr"), (varnumber_T)qfp->qf_nr) == FAIL) @@ -5796,7 +5896,9 @@ enum { QF_GETLIST_SIZE = 0x80, QF_GETLIST_TICK = 0x100, QF_GETLIST_FILEWINID = 0x200, - QF_GETLIST_ALL = 0x3FF, + QF_GETLIST_QFBUFNR = 0x400, + QF_GETLIST_QFTF = 0x800, + QF_GETLIST_ALL = 0xFFF, }; /// Parse text from 'di' and return the quickfix list items. @@ -5894,6 +5996,9 @@ static int qf_getprop_keys2flags(const dict_T *what, bool loclist) if (loclist && tv_dict_find(what, S_LEN("filewinid")) != NULL) { flags |= QF_GETLIST_FILEWINID; } + if (tv_dict_find(what, S_LEN("quickfixtextfunc")) != NULL) { + flags |= QF_GETLIST_QFTF; + } return flags; } @@ -5934,7 +6039,7 @@ static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what) if ((di = tv_dict_find(what, S_LEN("id"))) != NULL) { // Look for a list with the specified id if (di->di_tv.v_type == VAR_NUMBER) { - // For zero, use the current list or the list specifed by 'nr' + // For zero, use the current list or the list specified by 'nr' if (di->di_tv.vval.v_number != 0) { qf_idx = qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number); } @@ -5985,6 +6090,9 @@ static int qf_getprop_defaults(qf_info_T *qi, if ((status == OK) && locstack && (flags & QF_GETLIST_FILEWINID)) { status = tv_dict_add_nr(retdict, S_LEN("filewinid"), 0); } + if ((status == OK) && (flags & QF_GETLIST_QFTF)) { + status = tv_dict_add_str(retdict, S_LEN("quickfixtextfunc"), ""); + } return status; } @@ -6060,6 +6168,26 @@ static int qf_getprop_idx(qf_list_T *qfl, int eidx, dict_T *retdict) return tv_dict_add_nr(retdict, S_LEN("idx"), eidx); } +/// Return the 'quickfixtextfunc' function of a quickfix/location list +/// @return OK or FAIL +static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict) + FUNC_ATTR_NONNULL_ALL +{ + int status; + + if (qfl->qftf_cb.type != kCallbackNone) { + typval_T tv; + + callback_put(&qfl->qftf_cb, &tv); + status = tv_dict_add_tv(retdict, S_LEN("quickfixtextfunc"), &tv); + tv_clear(&tv); + } else { + status = tv_dict_add_str(retdict, S_LEN("quickfixtextfunc"), ""); + } + + return status; +} + /// Return quickfix/location list details (title) as a dictionary. /// 'what' contains the details to return. If 'list_idx' is -1, /// then current list is used. Otherwise the specified list is used. @@ -6133,19 +6261,25 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) if ((status == OK) && (wp != NULL) && (flags & QF_GETLIST_FILEWINID)) { status = qf_getprop_filewinid(wp, qi, retdict); } + if ((status == OK) && (flags & QF_GETLIST_QFTF)) { + status = qf_getprop_qftf(qfl, retdict); + } return status; } /// Set the current index in the specified quickfix list -static int qf_setprop_qftf(qf_info_T *qi, qf_list_T *qfl, - dictitem_T *di) +/// @return OK +static int qf_setprop_qftf(qf_list_T *qfl, dictitem_T *di) + FUNC_ATTR_NONNULL_ALL { - XFREE_CLEAR(qfl->qf_qftf); - if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL) { - qfl->qf_qftf = vim_strsave(di->di_tv.vval.v_string); - } - return OK; + Callback cb; + + callback_free(&qfl->qftf_cb); + if (callback_from_typval(&cb, &di->di_tv)) { + qfl->qftf_cb = cb; + } + return OK; } /// Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the @@ -6168,7 +6302,9 @@ static int qf_add_entry_from_dict( char *const module = tv_dict_get_string(d, "module", true); int bufnum = (int)tv_dict_get_number(d, "bufnr"); const long lnum = (long)tv_dict_get_number(d, "lnum"); + const long end_lnum = (long)tv_dict_get_number(d, "end_lnum"); const int col = (int)tv_dict_get_number(d, "col"); + const int end_col = (int)tv_dict_get_number(d, "end_col"); const char_u vcol = (char_u)tv_dict_get_number(d, "vcol"); const int nr = (int)tv_dict_get_number(d, "nr"); const char *const type = tv_dict_get_string(d, "type", false); @@ -6206,7 +6342,9 @@ static int qf_add_entry_from_dict( bufnum, (char_u *)text, lnum, + end_lnum, col, + end_col, vcol, // vis_col (char_u *)pattern, // search pattern nr, @@ -6514,7 +6652,7 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, retval = qf_setprop_curidx(qi, qfl, di); } if ((di = tv_dict_find(what, S_LEN("quickfixtextfunc"))) != NULL) { - retval = qf_setprop_qftf(qi, qfl, di); + retval = qf_setprop_qftf(qfl, di); } if (newlist || retval == OK) { @@ -6940,7 +7078,10 @@ static void hgr_search_file( 0, line, lnum, + 0, (int)(p_regmatch->startp[0] - line) + 1, // col + (int)(p_regmatch->endp[0] - line) + + 1, // end_col false, // vis_col NULL, // search pattern 0, // nr diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index c2ef217638..98a46cf781 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -63,6 +63,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" +#include "nvim/plines.h" #include "nvim/garray.h" #include "nvim/strings.h" @@ -6725,26 +6726,24 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (expr != NULL) { typval_T argv[2]; - int dummy; typval_T rettv; staticList10_T matchList = TV_LIST_STATIC10_INIT; - rettv.v_type = VAR_STRING; rettv.vval.v_string = NULL; argv[0].v_type = VAR_LIST; argv[0].vval.v_list = &matchList.sl_list; + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.argv_func = fill_submatch_list; + funcexe.evaluate = true; if (expr->v_type == VAR_FUNC) { s = expr->vval.v_string; - call_func(s, -1, &rettv, 1, argv, - fill_submatch_list, 0L, 0L, &dummy, - true, NULL, NULL); + call_func(s, -1, &rettv, 1, argv, &funcexe); } else if (expr->v_type == VAR_PARTIAL) { partial_T *partial = expr->vval.v_partial; s = partial_name(partial); - call_func(s, -1, &rettv, 1, argv, - fill_submatch_list, 0L, 0L, &dummy, - true, partial, NULL); + funcexe.partial = partial; + call_func(s, -1, &rettv, 1, argv, &funcexe); } if (tv_list_len(&matchList.sl_list) > 0) { // fill_submatch_list() was called. diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 35c3285cda..039f9b4675 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1161,8 +1161,6 @@ static int nfa_regatom(void) int emit_range; int negated; int startc = -1; - int endc = -1; - int oldstartc = -1; int save_prev_at_start = prev_at_start; c = getchr(); @@ -1572,7 +1570,7 @@ collection: * Failed to recognize a character class. Use the simple * version that turns [abc] into 'a' OR 'b' OR 'c' */ - startc = endc = oldstartc = -1; + startc = -1; negated = false; if (*regparse == '^') { // negated range negated = true; @@ -1589,7 +1587,7 @@ collection: // Emit the OR branches for each character in the [] emit_range = false; while (regparse < endp) { - oldstartc = startc; + int oldstartc = startc; startc = -1; got_coll_char = false; if (*regparse == '[') { @@ -1729,7 +1727,7 @@ collection: /* Previous char was '-', so this char is end of range. */ if (emit_range) { - endc = startc; + int endc = startc; startc = oldstartc; if (startc > endc) { EMSG_RET_FAIL(_(e_reverse_range)); diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 1fb7e3b434..4da81f29e3 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -245,7 +245,8 @@ int source_in_path(char_u *path, char_u *name, int flags) return do_in_path_and_pp(path, name, flags, source_callback, NULL); } -// Expand wildcards in "pat" and invoke do_source() for each match. +// Expand wildcards in "pat" and invoke do_source()/nlua_exec_file() +// for each match. static void source_all_matches(char_u *pat) { int num_files; @@ -402,40 +403,39 @@ theend: return retval; } -/// Load scripts in "plugin" and "ftdetect" directories of the package. -static int load_pack_plugin(char_u *fname) +/// Load scripts in "plugin" directory of the package. +/// For opt packages, also load scripts in "ftdetect" (start packages already +/// load these from filetype.vim) +static int load_pack_plugin(bool opt, char_u *fname) { - static const char *plugpat = "%s/plugin/**/*.vim"; // NOLINT static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT - int retval = FAIL; char *const ffname = fix_fname((char *)fname); size_t len = strlen(ffname) + STRLEN(ftpat); - char_u *pat = try_malloc(len + 1); - if (pat == NULL) { - goto theend; - } - vim_snprintf((char *)pat, len, plugpat, ffname); + char_u *pat = xmallocz(len); + + vim_snprintf((char *)pat, len, "%s/plugin/**/*.vim", ffname); // NOLINT + source_all_matches(pat); + vim_snprintf((char *)pat, len, "%s/plugin/**/*.lua", ffname); // NOLINT source_all_matches(pat); char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes"); // If runtime/filetype.vim wasn't loaded yet, the scripts will be // found when it loads. - if (eval_to_number(cmd) > 0) { + if (opt && eval_to_number(cmd) > 0) { do_cmdline_cmd("augroup filetypedetect"); vim_snprintf((char *)pat, len, ftpat, ffname); source_all_matches(pat); + vim_snprintf((char *)pat, len, "%s/ftdetect/*.lua", ffname); // NOLINT + source_all_matches(pat); do_cmdline_cmd("augroup END"); } xfree(cmd); xfree(pat); - retval = OK; - -theend: xfree(ffname); - return retval; + return OK; } // used for "cookie" of add_pack_plugin() @@ -443,7 +443,7 @@ static int APP_ADD_DIR; static int APP_LOAD; static int APP_BOTH; -static void add_pack_plugin(char_u *fname, void *cookie) +static void add_pack_plugin(bool opt, char_u *fname, void *cookie) { if (cookie != &APP_LOAD) { char *buf = xmalloc(MAXPATHL); @@ -467,17 +467,27 @@ static void add_pack_plugin(char_u *fname, void *cookie) } if (cookie != &APP_ADD_DIR) { - load_pack_plugin(fname); + load_pack_plugin(opt, fname); } } +static void add_start_pack_plugin(char_u *fname, void *cookie) +{ + add_pack_plugin(false, fname, cookie); +} + +static void add_opt_pack_plugin(char_u *fname, void *cookie) +{ + add_pack_plugin(true, fname, cookie); +} + /// Add all packages in the "start" directory to 'runtimepath'. void add_pack_start_dirs(void) { do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT - add_pack_plugin, &APP_ADD_DIR); + add_start_pack_plugin, &APP_ADD_DIR); do_in_path(p_pp, (char_u *)"start/*", DIP_ALL + DIP_DIR, // NOLINT - add_pack_plugin, &APP_ADD_DIR); + add_start_pack_plugin, &APP_ADD_DIR); } /// Load plugins from all packages in the "start" directory. @@ -485,9 +495,9 @@ void load_start_packages(void) { did_source_packages = true; do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT - add_pack_plugin, &APP_LOAD); + add_start_pack_plugin, &APP_LOAD); do_in_path(p_pp, (char_u *)"start/*", DIP_ALL + DIP_DIR, // NOLINT - add_pack_plugin, &APP_LOAD); + add_start_pack_plugin, &APP_LOAD); } // ":packloadall" @@ -524,7 +534,8 @@ void ex_packadd(exarg_T *eap) res = do_in_path(p_pp, (char_u *)pat, DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0), - add_pack_plugin, eap->forceit ? &APP_ADD_DIR : &APP_BOTH); + round == 1 ? add_start_pack_plugin : add_opt_pack_plugin, + eap->forceit ? &APP_ADD_DIR : &APP_BOTH); xfree(pat); } } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 844104e7d0..6fdcf8a838 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -65,45 +65,50 @@ #include <stdbool.h> #include <string.h> -#include "nvim/log.h" -#include "nvim/vim.h" -#include "nvim/ascii.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" #include "nvim/arabic.h" -#include "nvim/screen.h" +#include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/cursor_shape.h" +#include "nvim/decoration.h" #include "nvim/diff.h" +#include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" -#include "nvim/edit.h" +#include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/indent.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/indent.h" +#include "nvim/lib/kvec.h" +#include "nvim/log.h" +#include "nvim/lua/executor.h" #include "nvim/main.h" #include "nvim/mark.h" -#include "nvim/extmark.h" -#include "nvim/decoration.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/garray.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" +#include "nvim/screen.h" #include "nvim/search.h" #include "nvim/sign.h" #include "nvim/spell.h" @@ -115,12 +120,8 @@ #include "nvim/ui_compositor.h" #include "nvim/undo.h" #include "nvim/version.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/os/time.h" -#include "nvim/api/private/helpers.h" -#include "nvim/api/vim.h" -#include "nvim/lua/executor.h" -#include "nvim/lib/kvec.h" #define MB_FILLER_CHAR '<' /* character used when a double-width character * doesn't fit. */ @@ -134,7 +135,7 @@ static size_t linebuf_size = 0; static schar_T *linebuf_char = NULL; static sattr_T *linebuf_attr = NULL; -static match_T search_hl; /* used for 'hlsearch' highlight matching */ +static match_T search_hl; // used for 'hlsearch' highlight matching StlClickDefinition *tab_page_click_defs = NULL; @@ -165,10 +166,9 @@ static bool resizing = false; #endif #define SEARCH_HL_PRIORITY 0 -static char * provider_first_error = NULL; +static char * provider_err = NULL; -static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, - Array args, bool default_true) +static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args, bool default_true) { Error err = ERROR_INIT; @@ -187,10 +187,10 @@ static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, const char *ns_name = describe_ns(ns_id); ELOG("error in provider %s:%s: %s", ns_name, name, err.msg); bool verbose_errs = true; // TODO(bfredl): - if (verbose_errs && provider_first_error == NULL) { + if (verbose_errs && provider_err == NULL) { static char errbuf[IOSIZE]; snprintf(errbuf, sizeof errbuf, "%s: %s", ns_name, err.msg); - provider_first_error = xstrdup(errbuf); + provider_err = xstrdup(errbuf); } } @@ -207,10 +207,12 @@ void redraw_later(win_T *wp, int type) { if (!exiting && wp->w_redr_type < type) { wp->w_redr_type = type; - if (type >= NOT_VALID) + if (type >= 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; + } } } @@ -269,10 +271,10 @@ void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) 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; + wp->w_redraw_top = firstline; } if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) { - wp->w_redraw_bot = lastline; + wp->w_redraw_bot = lastline; } redraw_later(wp, VALID); } @@ -287,20 +289,16 @@ void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline) * 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 -) +void redrawWinline(win_T *wp, linenr_T lnum) FUNC_ATTR_NONNULL_ALL { 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; + wp->w_redraw_top = lnum; } if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { - wp->w_redraw_bot = lnum; + wp->w_redraw_bot = lnum; } redraw_later(wp, VALID); } @@ -323,8 +321,7 @@ void update_curbuf(int type) /// @param type set to a NOT_VALID to force redraw of entire screen int update_screen(int type) { - static int did_intro = FALSE; - int did_one; + static bool did_intro = false; // Don't do anything if the screen structures are (not yet) valid. // A VimResized autocmd can invoke redrawing in the middle of a resize, @@ -339,8 +336,9 @@ int update_screen(int type) } if (must_redraw) { - if (type < must_redraw) /* use maximal type */ + if (type < must_redraw) { // use maximal type type = must_redraw; + } /* must_redraw is reset here, so that when we run into some weird * reason to redraw while busy redrawing (e.g., asynchronous @@ -349,9 +347,10 @@ int update_screen(int type) must_redraw = 0; } - /* Need to update w_lines[]. */ - if (curwin->w_lines_valid == 0 && type < NOT_VALID) + // Need to update w_lines[]. + if (curwin->w_lines_valid == 0 && type < NOT_VALID) { type = NOT_VALID; + } /* Postpone the redrawing when it's not needed and when being called * recursively. */ @@ -437,8 +436,8 @@ int update_screen(int type) } } } - redraw_cmdline = TRUE; - redraw_tabline = TRUE; + redraw_cmdline = true; + redraw_tabline = true; } msg_scrolled = 0; msg_scrolled_at_flush = 0; @@ -448,12 +447,13 @@ int update_screen(int type) win_ui_flush(); msg_ext_check_clear(); - /* reset cmdline_row now (may have been changed temporarily) */ + // reset cmdline_row now (may have been changed temporarily) compute_cmdrow(); - /* Check for changed highlighting */ - if (need_highlight_changed) + // Check for changed highlighting + if (need_highlight_changed) { highlight_changed(); + } if (type == CLEAR) { // first clear screen screenclear(); // will reset clear_cmdline @@ -503,21 +503,24 @@ int update_screen(int type) redraw_tabline = true; } - if (clear_cmdline) /* going to clear cmdline (done below) */ + if (clear_cmdline) { // going to clear cmdline (done below) check_for_delay(FALSE); + } /* Force redraw when width of 'number' or 'relativenumber' column * changes. */ if (curwin->w_redr_type < NOT_VALID && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu) - ? number_width(curwin) : 0)) + ? number_width(curwin) : 0)) { curwin->w_redr_type = NOT_VALID; + } /* * Only start redrawing if there is really something to do. */ - if (type == INVERTED) + if (type == INVERTED) { update_curswant(); + } if (curwin->w_redr_type < type && !((type == VALID && curwin->w_lines[0].wl_valid @@ -530,8 +533,9 @@ int update_screen(int type) && curwin->w_old_visual_mode == VIsual_mode && (curwin->w_valid & VALID_VIRTCOL) && curwin->w_old_curswant == curwin->w_curswant) - )) + )) { curwin->w_redr_type = type; + } // Redraw the tab pages line if needed. if (redraw_tabline || type >= NOT_VALID) { @@ -577,7 +581,7 @@ int update_screen(int type) * Go from top to bottom through the windows, redrawing the ones that need * it. */ - did_one = FALSE; + bool did_one = false; search_hl.rm.regprog = NULL; @@ -596,13 +600,13 @@ int update_screen(int type) if (wp->w_redr_type != 0) { if (!did_one) { - did_one = TRUE; + did_one = true; start_search_hl(); } win_update(wp, &providers); } - /* redraw status line after the window to minimize cursor movement */ + // redraw status line after the window to minimize cursor movement if (wp->w_redr_status) { win_redr_status(wp); } @@ -631,10 +635,11 @@ int update_screen(int type) showmode(); } - /* May put up an introductory message when not editing a file */ - if (!did_intro) + // May put up an introductory message when not editing a file + if (!did_intro) { maybe_intro_message(); - did_intro = TRUE; + } + did_intro = true; for (size_t i = 0; i < kv_size(providers); i++) { DecorProvider *p = kv_A(providers, i); @@ -704,7 +709,7 @@ bool win_cursorline_standout(const win_T *wp) FUNC_ATTR_NONNULL_ALL { return wp->w_p_cul - || (wp->w_p_cole > 0 && (VIsual_active || !conceal_cursor_line(wp))); + || (wp->w_p_cole > 0 && (VIsual_active || !conceal_cursor_line(wp))); } /* @@ -715,28 +720,28 @@ bool win_cursorline_standout(const win_T *wp) * * How the window is redrawn depends on wp->w_redr_type. Each type also * implies the one below it. - * NOT_VALID redraw the whole window - * SOME_VALID redraw the whole window but do scroll when possible - * REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID - * INVERTED redraw the changed part of the Visual area - * INVERTED_ALL redraw the whole Visual area - * VALID 1. scroll up/down to adjust for a changed w_topline - * 2. update lines at the top when scrolled down - * 3. redraw changed text: - * - if wp->w_buffer->b_mod_set set, update lines between - * b_mod_top and b_mod_bot. - * - if wp->w_redraw_top non-zero, redraw lines between - * wp->w_redraw_top and wp->w_redr_bot. - * - continue redrawing when syntax status is invalid. - * 4. if scrolled up, update lines at the bottom. + * NOT_VALID redraw the whole window + * SOME_VALID redraw the whole window but do scroll when possible + * REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID + * INVERTED redraw the changed part of the Visual area + * INVERTED_ALL redraw the whole Visual area + * VALID 1. scroll up/down to adjust for a changed w_topline + * 2. update lines at the top when scrolled down + * 3. redraw changed text: + * - if wp->w_buffer->b_mod_set set, update lines between + * b_mod_top and b_mod_bot. + * - if wp->w_redraw_top non-zero, redraw lines between + * wp->w_redraw_top and wp->w_redr_bot. + * - continue redrawing when syntax status is invalid. + * 4. if scrolled up, update lines at the bottom. * This results in three areas that may need updating: - * top: from first row to top_end (when scrolled down) + * top: from first row to top_end (when scrolled down) * mid: from mid_start to mid_end (update inversion or changed text) * bot: from bot_start to last row (when scrolled up) */ static void win_update(win_T *wp, Providers *providers) { - buf_T *buf = wp->w_buffer; + buf_T *buf = wp->w_buffer; int type; int top_end = 0; /* Below last row of the top area that needs updating. 0 when no top area updating. */ @@ -746,29 +751,26 @@ static void win_update(win_T *wp, Providers *providers) 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 */ - int scrolled_down = FALSE; /* TRUE when scrolled down when - w_topline got smaller a bit */ + 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 row; /* current window row to display */ - linenr_T lnum; /* current buffer lnum to display */ - int idx; /* current index in w_lines[] */ - int srow; /* starting row of the current line */ + int row; // current window row to display + linenr_T lnum; // current buffer lnum to display + int idx; // current index in w_lines[] + int srow; // starting row of the current line - int eof = FALSE; /* if TRUE, we hit the end of the file */ - int 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 int i; long j; static bool recursive = false; // being called recursively const linenr_T old_botline = wp->w_botline; - const int old_wrow = wp->w_wrow; - const int old_wcol = wp->w_wcol; // Remember what happened to the previous line. #define DID_NONE 1 // didn't update a line #define DID_LINE 2 // updated a normal line #define DID_FOLD 3 // updated a folded line int 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; int save_got_int; @@ -825,10 +827,11 @@ static void win_update(win_T *wp, Providers *providers) * changes. Set mod_bot to the first line after the changes. */ mod_top = wp->w_redraw_top; - if (wp->w_redraw_bot != 0) + if (wp->w_redraw_bot != 0) { mod_bot = wp->w_redraw_bot + 1; - else + } else { mod_bot = 0; + } if (buf->b_mod_set) { if (mod_top == 0 || mod_top > buf->b_mod_top) { mod_top = buf->b_mod_top; @@ -836,12 +839,14 @@ static void win_update(win_T *wp, Providers *providers) * in a pattern match. */ if (syntax_present(wp)) { mod_top -= buf->b_s.b_syn_sync_linebreaks; - if (mod_top < 1) + if (mod_top < 1) { mod_top = 1; + } } } - if (mod_bot == 0 || mod_bot < buf->b_mod_bot) + if (mod_bot == 0 || mod_bot < buf->b_mod_bot) { mod_bot = buf->b_mod_bot; + } // When 'hlsearch' is on and using a multi-line search pattern, a // change in one line may make the Search highlighting in a @@ -880,10 +885,11 @@ static void win_update(win_T *wp, Providers *providers) * to this line. If there is no valid entry, use MAXLNUM. */ lnumt = wp->w_topline; lnumb = MAXLNUM; - for (i = 0; i < wp->w_lines_valid; ++i) + for (i = 0; i < wp->w_lines_valid; ++i) { if (wp->w_lines[i].wl_valid) { - if (wp->w_lines[i].wl_lastlnum < mod_top) + if (wp->w_lines[i].wl_lastlnum < mod_top) { lnumt = wp->w_lines[i].wl_lastlnum + 1; + } if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot) { lnumb = wp->w_lines[i].wl_lnum; // When there is a fold column it might need updating @@ -893,6 +899,7 @@ static void win_update(win_T *wp, Providers *providers) } } } + } (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL); if (mod_top > lnumt) { @@ -913,16 +920,18 @@ static void win_update(win_T *wp, Providers *providers) * If the end of the change is above w_topline: do like no change was * made, but redraw the first line to find changes in syntax. */ if (mod_top != 0 && mod_top < wp->w_topline) { - if (mod_bot > wp->w_topline) + if (mod_bot > wp->w_topline) { mod_top = wp->w_topline; - else if (syntax_present(wp)) + } else if (syntax_present(wp)) { top_end = 1; + } } /* When line numbers are displayed need to redraw all lines below * inserted/deleted lines. */ - if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) + if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) { mod_bot = MAXLNUM; + } } wp->w_redraw_top = 0; // reset for next time wp->w_redraw_bot = 0; @@ -940,12 +949,13 @@ static void win_update(win_T *wp, Providers *providers) break; } } - if (top_end == 0) - /* not found (cannot happen?): redraw everything */ + if (top_end == 0) { + // not found (cannot happen?): redraw everything type = NOT_VALID; - else - /* top area defined, the rest is VALID */ + } else { + // top area defined, the rest is VALID type = VALID; + } } /* @@ -958,8 +968,7 @@ static void win_update(win_T *wp, Providers *providers) */ if ((type == VALID || type == SOME_VALID || type == INVERTED || type == INVERTED_ALL) - && !wp->w_botfill && !wp->w_old_botfill - ) { + && !wp->w_botfill && !wp->w_old_botfill) { if (mod_top != 0 && wp->w_topline == mod_top && (!wp->w_lines[0].wl_valid @@ -987,14 +996,16 @@ static void win_update(win_T *wp, Providers *providers) } (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL); } - } else + } else { j = wp->w_lines[0].wl_lnum - wp->w_topline; + } if (j < wp->w_grid.Rows - 2) { // not too far off i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1); - /* insert extra lines for previously invisible filler lines */ - if (wp->w_lines[0].wl_lnum != wp->w_topline) + // insert extra lines for previously invisible filler lines + if (wp->w_lines[0].wl_lnum != wp->w_topline) { i += diff_check_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill; + } if (i != 0 && i < wp->w_grid.Rows - 2) { // less than a screen off // Try to insert the correct number of lines. // If not the last window, delete the lines at the bottom. @@ -1031,7 +1042,7 @@ static void win_update(win_T *wp, Providers *providers) * needs updating. */ - /* try to find wp->w_topline in wp->w_lines[].wl_lnum */ + // try to find wp->w_topline in wp->w_lines[].wl_lnum j = -1; row = 0; for (i = 0; i < wp->w_lines_valid; i++) { @@ -1053,11 +1064,12 @@ static void win_update(win_T *wp, Providers *providers) */ /* If the topline didn't change, delete old filler lines, * otherwise delete filler lines of the new topline... */ - if (wp->w_lines[0].wl_lnum == wp->w_topline) + if (wp->w_lines[0].wl_lnum == wp->w_topline) { row += wp->w_old_topfill; - else + } else { row += diff_check_fill(wp, wp->w_topline); - /* ... but don't delete new filler lines. */ + } + // ... but don't delete new filler lines. row -= wp->w_topfill; if (row > 0) { win_scroll_lines(wp, 0, -row); @@ -1066,7 +1078,7 @@ static void win_update(win_T *wp, Providers *providers) if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) { /* * Skip the lines (below the deleted lines) that are still - * valid and don't need redrawing. Copy their info + * valid and don't need redrawing. Copy their info * upwards, to compensate for the deleted lines. Set * bot_start to the first row that needs redrawing. */ @@ -1083,7 +1095,7 @@ static void win_update(win_T *wp, Providers *providers) } bot_start += wp->w_lines[idx++].wl_size; - /* stop at the last valid entry in w_lines[].wl_size */ + // stop at the last valid entry in w_lines[].wl_size if (++j >= wp->w_lines_valid) { wp->w_lines_valid = idx; break; @@ -1091,10 +1103,11 @@ static void win_update(win_T *wp, Providers *providers) } /* Correct the first entry for filler lines at the top * when it won't get updated below. */ - if (wp->w_p_diff && bot_start > 0) + if (wp->w_p_diff && bot_start > 0) { wp->w_lines[0].wl_size = plines_win_nofill(wp, wp->w_topline, true) + wp->w_topfill; + } } } } @@ -1104,19 +1117,19 @@ static void win_update(win_T *wp, Providers *providers) mid_end = wp->w_grid.Rows; } } else { - /* Not VALID or INVERTED: redraw all lines. */ + // Not VALID or INVERTED: redraw all lines. mid_start = 0; mid_end = wp->w_grid.Rows; } if (type == SOME_VALID) { - /* SOME_VALID: redraw all lines. */ + // SOME_VALID: redraw all lines. mid_start = 0; mid_end = wp->w_grid.Rows; type = NOT_VALID; } - /* check if we are updating or removing the inverted part */ + // check if we are updating or removing the inverted part if ((VIsual_active && buf == curwin->w_buffer) || (wp->w_old_cursor_lnum != 0 && type != NOT_VALID)) { linenr_T from, to; @@ -1133,15 +1146,19 @@ static void win_update(win_T *wp, Providers *providers) from = VIsual.lnum; to = curwin->w_cursor.lnum; } - /* redraw more when the cursor moved as well */ - if (wp->w_old_cursor_lnum < from) + // redraw more when the cursor moved as well + if (wp->w_old_cursor_lnum < from) { from = wp->w_old_cursor_lnum; - if (wp->w_old_cursor_lnum > to) + } + if (wp->w_old_cursor_lnum > to) { to = wp->w_old_cursor_lnum; - if (wp->w_old_visual_lnum < from) + } + if (wp->w_old_visual_lnum < from) { from = wp->w_old_visual_lnum; - if (wp->w_old_visual_lnum > to) + } + if (wp->w_old_visual_lnum > to) { to = wp->w_old_visual_lnum; + } } else { /* * Find the line numbers that need to be updated: The lines @@ -1154,21 +1171,26 @@ static void win_update(win_T *wp, Providers *providers) } 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) + && wp->w_old_visual_lnum != 0) { from = wp->w_old_visual_lnum; - if (wp->w_old_visual_lnum > to) + } + if (wp->w_old_visual_lnum > to) { to = wp->w_old_visual_lnum; - if (VIsual.lnum < from) + } + if (VIsual.lnum < from) { from = VIsual.lnum; - if (VIsual.lnum > to) + } + if (VIsual.lnum > to) { to = VIsual.lnum; + } } } @@ -1181,27 +1203,33 @@ static void win_update(win_T *wp, Providers *providers) colnr_T fromc, toc; int save_ve_flags = ve_flags; - if (curwin->w_p_lbr) + if (curwin->w_p_lbr) { ve_flags = VE_ALL; + } getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc); ve_flags = save_ve_flags; - ++toc; - if (curwin->w_curswant == MAXCOL) + toc++; + // Highlight to the end of the line, unless 'virtualedit' has + // "block". + if (curwin->w_curswant == MAXCOL && !(ve_flags & VE_BLOCK)) { toc = MAXCOL; + } if (fromc != wp->w_old_cursor_fcol || toc != wp->w_old_cursor_lcol) { - if (from > VIsual.lnum) + if (from > VIsual.lnum) { from = VIsual.lnum; - if (to < VIsual.lnum) + } + if (to < VIsual.lnum) { to = VIsual.lnum; + } } wp->w_old_cursor_fcol = fromc; wp->w_old_cursor_lcol = toc; } } else { - /* Use the line numbers of the old Visual area. */ + // Use the line numbers of the old Visual area. if (wp->w_old_cursor_lnum < wp->w_old_visual_lnum) { from = wp->w_old_cursor_lnum; to = wp->w_old_visual_lnum; @@ -1214,18 +1242,21 @@ static void win_update(win_T *wp, Providers *providers) /* * There is no need to update lines above the top of the window. */ - if (from < wp->w_topline) + if (from < wp->w_topline) { from = wp->w_topline; + } /* * If we know the value of w_botline, use it to restrict the update to * the lines that are visible in the window. */ if (wp->w_valid & VALID_BOTLINE) { - if (from >= wp->w_botline) + if (from >= wp->w_botline) { from = wp->w_botline - 1; - if (to >= wp->w_botline) + } + if (to >= wp->w_botline) { to = wp->w_botline - 1; + } } /* @@ -1241,27 +1272,30 @@ static void win_update(win_T *wp, Providers *providers) lnum = wp->w_topline; idx = 0; srow = 0; - if (scrolled_down) + if (scrolled_down) { mid_start = top_end; - else + } else { mid_start = 0; - while (lnum < from && idx < wp->w_lines_valid) { /* find start */ - if (wp->w_lines[idx].wl_valid) + } + 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) + } else if (!scrolled_down) { srow += wp->w_lines[idx].wl_size; + } ++idx; - if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid) + if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid) { lnum = wp->w_lines[idx].wl_lnum; - else + } else { ++lnum; + } } 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) { - /* Only update until first row of this line */ + // Only update until first row of this line mid_end = srow; break; } @@ -1283,7 +1317,7 @@ static void win_update(win_T *wp, Providers *providers) wp->w_old_visual_col = 0; } - /* reset got_int, otherwise regexp won't work */ + // reset got_int, otherwise regexp won't work save_got_int = got_int; got_int = 0; // Set the time limit to 'redrawtime'. @@ -1293,7 +1327,7 @@ static void win_update(win_T *wp, Providers *providers) /* * Update all the window rows. */ - idx = 0; /* first entry in w_lines[].wl_size */ + idx = 0; // first entry in w_lines[].wl_size row = 0; srow = 0; lnum = wp->w_topline; // first line shown in window @@ -1333,9 +1367,9 @@ static void win_update(win_T *wp, Providers *providers) break; } - /* stop updating when hit the end of the file */ + // stop updating when hit the end of the file if (lnum > buf->b_ml.ml_line_count) { - eof = TRUE; + eof = true; break; } @@ -1368,7 +1402,9 @@ static void win_update(win_T *wp, Providers *providers) // match in fixed position might need redraw // if lines were inserted or deleted || (wp->w_match_head != NULL - && buf->b_mod_xlines != 0)))))) { + && buf->b_mod_xlines != 0))))) + || (wp->w_p_cul && (lnum == wp->w_cursor.lnum + || lnum == wp->w_last_cursorline))) { if (lnum == mod_top) { top_to_mod = false; } @@ -1378,10 +1414,12 @@ static void win_update(win_T *wp, Providers *providers) * up or down to minimize redrawing. * 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)) { + && !(dollar_vcol >= 0 && mod_bot == mod_top + 1) + && row >= top_end) { int old_rows = 0; int new_rows = 0; int xtra_rows; @@ -1394,8 +1432,9 @@ static void win_update(win_T *wp, Providers *providers) /* 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) + && 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) { @@ -1403,8 +1442,9 @@ static void win_update(win_T *wp, Providers *providers) * Add following invalid entries. */ ++i; while (i < wp->w_lines_valid - && !wp->w_lines[i].wl_valid) + && !wp->w_lines[i].wl_valid) { old_rows += wp->w_lines[i++].wl_size; + } break; } } @@ -1467,15 +1507,15 @@ static void win_update(win_T *wp, Providers *providers) if (j < i) { int x = row + new_rows; - /* move entries in w_lines[] upwards */ + // move entries in w_lines[] upwards for (;; ) { - /* stop at last valid entry in w_lines[] */ + // stop at last valid entry in w_lines[] if (i >= wp->w_lines_valid) { wp->w_lines_valid = j; break; } wp->w_lines[j] = wp->w_lines[i]; - /* stop at a line that won't fit */ + // stop at a line that won't fit if (x + (int)wp->w_lines[j].wl_size > wp->w_grid.Rows) { wp->w_lines_valid = j + 1; @@ -1484,10 +1524,11 @@ static void win_update(win_T *wp, Providers *providers) x += wp->w_lines[j++].wl_size; ++i; } - if (bot_start > x) + 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 += j; if (wp->w_lines_valid > wp->w_grid.Rows) { @@ -1523,17 +1564,17 @@ static void win_update(win_T *wp, Providers *providers) && lnum > wp->w_topline && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows - && diff_check_fill(wp, lnum) == 0 - ) { + && diff_check_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, lnum); - /* Let the syntax stuff know we skipped a few lines. */ + // Let the syntax stuff know we skipped a few lines. if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum - && syntax_present(wp)) + && syntax_present(wp)) { syntax_end_parsing(syntax_last_parsed + 1); + } // Display one line row = win_line(wp, lnum, srow, @@ -1588,7 +1629,7 @@ static void win_update(win_T *wp, Providers *providers) } if (lnum > buf->b_ml.ml_line_count) { - eof = TRUE; + eof = true; break; } } @@ -1597,14 +1638,16 @@ static void win_update(win_T *wp, Providers *providers) */ - if (idx > wp->w_lines_valid) + if (idx > wp->w_lines_valid) { wp->w_lines_valid = idx; + } /* * Let the syntax stuff know we stop parsing here. */ - if (syntax_last_parsed != 0 && syntax_present(wp)) + if (syntax_last_parsed != 0 && syntax_present(wp)) { syntax_end_parsing(syntax_last_parsed + 1); + } /* * If we didn't hit the end of the file, and we didn't finish the last @@ -1650,20 +1693,15 @@ static void win_update(win_T *wp, Providers *providers) wp->w_botline = buf->b_ml.ml_line_count + 1; j = diff_check_fill(wp, wp->w_botline); if (j > 0 && !wp->w_botfill) { - // display filler lines at the end of the file - if (char2cells(wp->w_p_fcs_chars.diff) > 1) { - i = '-'; - } else { - i = wp->w_p_fcs_chars.diff; - } - if (row + j > wp->w_grid.Rows) { - j = wp->w_grid.Rows - row; - } - win_draw_end(wp, i, i, true, row, row + (int)j, HLF_DED); - row += j; + // Display filler text below last line. win_line() will check + // for ml_line_count+1 and only draw filler lines + foldinfo_T info = FOLDINFO_INIT; + row = win_line(wp, wp->w_botline, row, wp->w_grid.Rows, + false, false, info, &line_providers); } - } else if (dollar_vcol == -1) + } else if (dollar_vcol == -1) { wp->w_botline = lnum; + } // make sure the rest of the screen is blank // write the 'eob' character to rows that aren't part of the file. @@ -1678,7 +1716,7 @@ static void win_update(win_T *wp, Providers *providers) } syn_set_timeout(NULL); - /* Reset the type of redrawing required, the window has been updated. */ + // Reset the type of redrawing required, the window has been updated. wp->w_redr_type = 0; wp->w_old_topfill = wp->w_topfill; wp->w_old_botfill = wp->w_botfill; @@ -1687,7 +1725,7 @@ static void win_update(win_T *wp, Providers *providers) /* * There is a trick with w_botline. If we invalidate it on each * change that might modify it, this will cause a lot of expensive - * calls to plines() in update_topline() each time. Therefore the + * calls to plines_win() in update_topline() each time. Therefore the * value of w_botline is often approximated, and this value is used to * compute the value of w_topline. If the value of w_botline was * wrong, check that the value of w_topline is correct (cursor is on @@ -1699,58 +1737,26 @@ static void win_update(win_T *wp, Providers *providers) wp->w_valid |= VALID_BOTLINE; wp->w_viewport_invalid = true; if (wp == curwin && wp->w_botline != old_botline && !recursive) { - const linenr_T old_topline = wp->w_topline; - const int new_wcol = wp->w_wcol; recursive = true; curwin->w_valid &= ~VALID_TOPLINE; update_topline(curwin); // may invalidate w_botline again - - if (old_wcol != new_wcol - && (wp->w_valid & (VALID_WCOL|VALID_WROW)) - != (VALID_WCOL|VALID_WROW)) { - // A win_line() call applied a fix to screen cursor column to - // accomodate concealment of cursor line, but in this call to - // update_topline() the cursor's row or column got invalidated. - // If they are left invalid, setcursor() will recompute them - // but there won't be any further win_line() call to re-fix the - // column and the cursor will end up misplaced. So we call - // cursor validation now and reapply the fix again (or call - // win_line() to do it for us). - validate_cursor(); - if (wp->w_wcol == old_wcol - && wp->w_wrow == old_wrow - && old_topline == wp->w_topline) { - wp->w_wcol = new_wcol; - } else { - redrawWinline(wp, wp->w_cursor.lnum); - } - } - // New redraw either due to updated topline or due to wcol fix. - if (wp->w_redr_type != 0) { + if (must_redraw != 0) { // Don't update for changes in buffer again. i = curbuf->b_mod_set; curbuf->b_mod_set = false; - j = curbuf->b_mod_xlines; - curbuf->b_mod_xlines = 0; win_update(curwin, providers); + must_redraw = 0; curbuf->b_mod_set = i; - curbuf->b_mod_xlines = j; - } - // Other windows might have w_redr_type raised in update_topline(). - must_redraw = 0; - FOR_ALL_WINDOWS_IN_TAB(wwp, curtab) { - if (wwp->w_redr_type > must_redraw) { - must_redraw = wwp->w_redr_type; - } } recursive = false; } } - /* restore got_int, unless CTRL-C was hit while redrawing */ - if (!got_int) + // restore got_int, unless CTRL-C was hit while redrawing + if (!got_int) { got_int = save_got_int; + } } // NOLINT(readability/fn_size) /// Returns width of the signcolumn that should be used for the whole window @@ -1770,8 +1776,8 @@ int win_signcol_width(win_T *wp) /// Call grid_fill() with columns adjusted for 'rightleft' if needed. /// Return the new offset. -static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, - int endrow, int attr) +static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, int endrow, + int attr) { int nn = off + width; @@ -1792,8 +1798,7 @@ static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row, /// Clear lines near the end of the window and mark the unused lines with "c1". /// Use "c2" as filler character. /// When "draw_margin" is true, then draw the sign/fold/number columns. -static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, - int endrow, hlf_T hl) +static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endrow, hlf_T hl) { assert(hl >= 0 && hl < HLF_COUNT); int n = 0; @@ -1833,13 +1838,14 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, } -/* - * Advance **color_cols and return TRUE when there are columns to draw. - */ -static int advance_color_col(int vcol, int **color_cols) +/// Advance **color_cols +/// +/// @return true when there are columns to draw. +static bool advance_color_col(int vcol, int **color_cols) { - while (**color_cols >= 0 && vcol > **color_cols) + while (**color_cols >= 0 && vcol > **color_cols) { ++*color_cols; + } return **color_cols >= 0; } @@ -1917,13 +1923,7 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) /// /// Assume monocell characters /// @return number of chars added to \param p -static size_t -fill_foldcolumn( - char_u *p, - win_T *wp, - foldinfo_T foldinfo, - linenr_T lnum -) +static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum) { int i = 0; int level; @@ -1992,24 +1992,23 @@ fill_foldcolumn( /// or explicitly return `false`. /// /// @return the number of last row the line occupies. -static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, - bool nochange, bool number_only, foldinfo_T foldinfo, - Providers *providers) +static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, + bool number_only, foldinfo_T foldinfo, Providers *providers) { int c = 0; // init for GCC long vcol = 0; // virtual column (for tabs) long vcol_sbr = -1; // virtual column after showbreak long vcol_prev = -1; // "vcol" of previous character - char_u *line; // current line - char_u *ptr; // current position in "line" + char_u *line; // current line + char_u *ptr; // current position in "line" int row; // row in the window, excl w_winrow - ScreenGrid *grid = &wp->w_grid; // grid specfic to the window + ScreenGrid *grid = &wp->w_grid; // grid specific to the window char_u extra[57]; // sign, line number and 'fdc' must // fit in here int n_extra = 0; // number of extra chars - char_u *p_extra = NULL; // string of extra chars, plus NUL - char_u *p_extra_free = NULL; // p_extra needs to be freed + char_u *p_extra = NULL; // string of extra chars, plus NUL + char_u *p_extra_free = NULL; // p_extra needs to be freed int c_extra = NUL; // extra chars, all the same int c_final = NUL; // final char, mandatory if set int extra_attr = 0; // attributes when n_extra != 0 @@ -2022,17 +2021,17 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // saved "extra" items for when draw_state becomes WL_LINE (again) int saved_n_extra = 0; - char_u *saved_p_extra = NULL; + char_u *saved_p_extra = NULL; int saved_c_extra = 0; int saved_c_final = 0; int saved_char_attr = 0; - int n_attr = 0; /* chars with special attr */ - int saved_attr2 = 0; /* char_attr saved for n_attr */ - int n_attr3 = 0; /* chars with overruling special attr */ - int saved_attr3 = 0; /* char_attr saved for n_attr3 */ + int n_attr = 0; // chars with special attr + int saved_attr2 = 0; // char_attr saved for n_attr + int n_attr3 = 0; // chars with overruling special attr + int saved_attr3 = 0; // char_attr saved for n_attr3 - int n_skip = 0; /* nr of chars to skip for 'nowrap' */ + int n_skip = 0; // nr of chars to skip for 'nowrap' int fromcol = -10; // start of inverting int tocol = MAXCOL; // end of inverting @@ -2042,29 +2041,29 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, pos_T pos; long v; - int char_attr = 0; /* attributes for next character */ - int attr_pri = FALSE; /* char_attr has priority */ + int char_attr = 0; // attributes for next character + int attr_pri = FALSE; // char_attr has priority int area_highlighting = FALSE; /* Visual or incsearch highlighting in this line */ - int attr = 0; /* attributes for area highlighting */ - int area_attr = 0; /* attributes desired by highlighting */ - int search_attr = 0; /* attributes desired by 'hlsearch' */ - int vcol_save_attr = 0; /* saved attr for 'cursorcolumn' */ - int syntax_attr = 0; /* attributes desired by syntax */ - int has_syntax = FALSE; /* this buffer has syntax highl. */ + int attr = 0; // attributes for area highlighting + int area_attr = 0; // attributes desired by highlighting + int search_attr = 0; // attributes desired by 'hlsearch' + int vcol_save_attr = 0; // saved attr for 'cursorcolumn' + int syntax_attr = 0; // attributes desired by syntax + int has_syntax = FALSE; // this buffer has syntax highl. int save_did_emsg; int eol_hl_off = 0; // 1 if highlighted char after EOL - int draw_color_col = false; // highlight colorcolumn + bool draw_color_col = false; // highlight colorcolumn int *color_cols = NULL; // pointer to according columns array bool has_spell = false; // this buffer has spell checking # define SPWORDLEN 150 - char_u nextline[SPWORDLEN * 2]; /* text with start of the next line */ - int nextlinecol = 0; /* column where nextline[] starts */ + char_u nextline[SPWORDLEN * 2]; // text with start of the next line + int nextlinecol = 0; // column where nextline[] starts int nextline_idx = 0; /* index in nextline[] where next line starts */ - int spell_attr = 0; /* attributes desired by spelling */ - int word_end = 0; /* last byte with same spell_attr */ - static linenr_T checked_lnum = 0; /* line number for "checked_col" */ + int spell_attr = 0; // attributes desired by spelling + int word_end = 0; // last byte with same spell_attr + static linenr_T checked_lnum = 0; // line number for "checked_col" static int checked_col = 0; /* column in "checked_lnum" up to which * there are no spell errors */ static int cap_col = -1; // column to check for Cap word @@ -2087,9 +2086,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, sign_attrs_T sattrs[SIGN_SHOW_MAX]; // attributes for signs int num_signs; // number of signs for line int line_attr = 0; // attribute for the whole line + int line_attr_save; int line_attr_lowprio = 0; // low-priority attribute for the line + int line_attr_lowprio_save; matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match + match_T *shl; // points to search_hl or a match bool shl_flag; // flag to indicate whether search_hl // has been processed or not bool prevcol_hl_flag; // flag to indicate whether prevcol @@ -2100,29 +2101,36 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool search_attr_from_match = false; // if search_attr is from :match bool has_decor = false; // this buffer has decoration - bool do_virttext = false; // draw virtual text for this line int win_col_offset = 0; // offset for window columns char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext bool area_active = false; - /* draw_state: items that are drawn in sequence: */ -#define WL_START 0 /* nothing done yet */ -# define WL_CMDLINE WL_START + 1 /* cmdline window column */ -# define WL_FOLD WL_CMDLINE + 1 /* 'foldcolumn' */ -# define WL_SIGN WL_FOLD + 1 /* column for signs */ -#define WL_NR WL_SIGN + 1 /* line number */ -# define WL_BRI WL_NR + 1 /* 'breakindent' */ -# define WL_SBR WL_BRI + 1 /* 'showbreak' or 'diff' */ -#define WL_LINE WL_SBR + 1 /* text in the line */ - int draw_state = WL_START; /* what to draw next */ + int cul_attr = 0; // set when 'cursorline' active + // 'cursorlineopt' has "screenline" and cursor is in this line + bool cul_screenline = false; + // margin columns for the screen line, needed for when 'cursorlineopt' + // contains "screenline" + int left_curline_col = 0; + int right_curline_col = 0; + + // draw_state: items that are drawn in sequence: +#define WL_START 0 // nothing done yet +# define WL_CMDLINE WL_START + 1 // cmdline window column +# define WL_FOLD WL_CMDLINE + 1 // 'foldcolumn' +# define WL_SIGN WL_FOLD + 1 // column for signs +#define WL_NR WL_SIGN + 1 // line number +# define WL_BRI WL_NR + 1 // 'breakindent' +# define WL_SBR WL_BRI + 1 // 'showbreak' or 'diff' +#define WL_LINE WL_SBR + 1 // text in the line + int draw_state = WL_START; // what to draw next int syntax_flags = 0; int syntax_seqnr = 0; int prev_syntax_id = 0; int conceal_attr = win_hl_attr(wp, HLF_CONCEAL); - int is_concealing = false; + bool is_concealing = false; int boguscols = 0; ///< nonexistent columns added to ///< force wrapping int vcol_off = 0; ///< offset for concealed characters @@ -2140,14 +2148,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, boguscols = 0; \ } - if (startrow > endrow) /* past the end already! */ + if (startrow > endrow) { // past the end already! return startrow; + } row = startrow; - char *err_text = NULL; - buf_T *buf = wp->w_buffer; + bool end_fill = (lnum == buf->b_ml.ml_line_count+1); if (!number_only) { // To speed up the loop below, set extra_check when there is linebreak, @@ -2191,14 +2199,20 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } - if (has_decor) { - extra_check = true; + if (provider_err) { + Decoration err_decor = DECORATION_INIT; + int hl_err = syn_check_group((char_u *)S_LEN("ErrorMsg")); + kv_push(err_decor.virt_text, + ((VirtTextChunk){ .text = provider_err, + .hl_id = hl_err })); + err_decor.virt_text_width = mb_string2cells((char_u *)provider_err); + decor_add_ephemeral(lnum-1, 0, lnum-1, 0, &err_decor); + provider_err = NULL; + has_decor = true; } - if (provider_first_error) { - err_text = provider_first_error; - provider_first_error = NULL; - do_virttext = true; + if (has_decor) { + extra_check = true; } // Check for columns to display for 'colorcolumn'. @@ -2209,6 +2223,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (wp->w_p_spell && !has_fold + && !end_fill && *wp->w_s->b_p_spl != NUL && !GA_EMPTY(&wp->w_s->b_langp) && *(char **)(wp->w_s->b_langp.ga_data) != NULL) { @@ -2308,7 +2323,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, area_highlighting = true; attr = win_hl_attr(wp, HLF_V); } - // handle 'incsearch' and ":s///c" highlighting + // handle 'incsearch' and ":s///c" highlighting } else if (highlight_match && wp == curwin && !has_fold @@ -2337,42 +2352,52 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, filler_lines = diff_check(wp, lnum); if (filler_lines < 0) { if (filler_lines == -1) { - if (diff_find_change(wp, lnum, &change_start, &change_end)) - diff_hlf = HLF_ADD; /* added line */ - else if (change_start == 0) - diff_hlf = HLF_TXD; /* changed text */ - else - diff_hlf = HLF_CHD; /* changed line */ - } else - diff_hlf = HLF_ADD; /* added line */ + if (diff_find_change(wp, lnum, &change_start, &change_end)) { + diff_hlf = HLF_ADD; // added line + } else if (change_start == 0) { + diff_hlf = HLF_TXD; // changed text + } else { + diff_hlf = HLF_CHD; // changed line + } + } else { + diff_hlf = HLF_ADD; // added line + } filler_lines = 0; area_highlighting = TRUE; } - if (lnum == wp->w_topline) + if (lnum == wp->w_topline) { filler_lines = wp->w_topfill; + } filler_todo = filler_lines; // Cursor line highlighting for 'cursorline' in the current window. if (lnum == wp->w_cursor.lnum) { - // Do not show the cursor line when Visual mode is active, because it's - // not clear what is selected then. - if (wp->w_p_cul && !(wp == curwin && VIsual_active)) { - int cul_attr = win_hl_attr(wp, HLF_CUL); - HlAttrs ae = syn_attr2entry(cul_attr); - - // We make a compromise here (#7383): - // * low-priority CursorLine if fg is not set - // * high-priority ("same as Vim" priority) CursorLine if fg is set - if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) { - line_attr_lowprio = cul_attr; - } else { - if (!(State & INSERT) && bt_quickfix(wp->w_buffer) - && qf_current_entry(wp) == lnum) { - line_attr = hl_combine_attr(cul_attr, line_attr); + // Do not show the cursor line in the text when Visual mode is active, + // because it's not clear what is selected then. + if (wp->w_p_cul && !(wp == curwin && VIsual_active) + && wp->w_p_culopt_flags != CULOPT_NBR) { + cul_screenline = (wp->w_p_wrap + && (wp->w_p_culopt_flags & CULOPT_SCRLINE)); + if (!cul_screenline) { + cul_attr = win_hl_attr(wp, HLF_CUL); + HlAttrs ae = syn_attr2entry(cul_attr); + // We make a compromise here (#7383): + // * low-priority CursorLine if fg is not set + // * high-priority ("same as Vim" priority) CursorLine if fg is set + if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) { + line_attr_lowprio = cul_attr; } else { - line_attr = cul_attr; + if (!(State & INSERT) && bt_quickfix(wp->w_buffer) + && qf_current_entry(wp) == lnum) { + line_attr = hl_combine_attr(cul_attr, line_attr); + } else { + line_attr = cul_attr; + } } + } else { + margin_columns_win(wp, &left_curline_col, &right_curline_col); } + area_highlighting = true; } // Update w_last_cursorline even if Visual mode is active. wp->w_last_cursorline = wp->w_cursor.lnum; @@ -2397,7 +2422,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, area_highlighting = true; } - line = ml_get_buf(wp->w_buffer, lnum, FALSE); + if (cul_screenline) { + line_attr_save = line_attr; + line_attr_lowprio_save = line_attr_lowprio; + } + + line = end_fill ? (char_u *)"" : ml_get_buf(wp->w_buffer, lnum, false); ptr = line; if (has_spell && !number_only) { @@ -2410,7 +2440,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, * current line into nextline[]. Above the start of the next line was * copied to nextline[SPWORDLEN]. */ if (nextline[SPWORDLEN] == NUL) { - /* No next line or it is empty. */ + // No next line or it is empty. nextlinecol = MAXCOL; nextline_idx = 0; } else { @@ -2423,7 +2453,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, STRMOVE(nextline + v, nextline + SPWORDLEN); nextline_idx = v + 1; } else { - /* Long line, use only the last SPWORDLEN bytes. */ + // Long line, use only the last SPWORDLEN bytes. nextlinecol = v - SPWORDLEN; memmove(nextline, line + nextlinecol, SPWORDLEN); // -V512 nextline_idx = SPWORDLEN + 1; @@ -2431,7 +2461,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } - if (wp->w_p_list && !has_fold) { + if (wp->w_p_list && !has_fold && !end_fill) { if (wp->w_p_lcs_chars.space || wp->w_p_lcs_chars.trail || wp->w_p_lcs_chars.lead @@ -2444,7 +2474,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) { trailcol--; } - trailcol += (colnr_T) (ptr - line); + trailcol += (colnr_T)(ptr - line); } // find end of leading whitespace if (wp->w_p_lcs_chars.lead) { @@ -2466,12 +2496,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, * 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the * first character to be displayed. */ - if (wp->w_p_wrap) + if (wp->w_p_wrap) { v = wp->w_skipcol; - else + } else { v = wp->w_leftcol; + } if (v > 0 && !number_only) { - char_u *prev_ptr = ptr; + char_u *prev_ptr = ptr; while (vcol < v && *ptr != NUL) { c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL); vcol += c; @@ -2508,10 +2539,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, * Adjust for when the inverted text is before the screen, * and when the start of the inverted text is before the screen. */ - if (tocol <= vcol) + if (tocol <= vcol) { fromcol = 0; - else if (fromcol >= 0 && fromcol < vcol) + } else if (fromcol >= 0 && fromcol < vcol) { fromcol = vcol; + } // When w_skipcol is non-zero, first line needs 'showbreak' if (wp->w_p_wrap) { @@ -2529,8 +2561,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, wp->w_cursor.col = linecol; len = spell_move_to(wp, FORWARD, TRUE, TRUE, &spell_hlf); - /* spell_move_to() may call ml_get() and make "line" invalid */ - line = ml_get_buf(wp->w_buffer, lnum, FALSE); + // spell_move_to() may call ml_get() and make "line" invalid + line = ml_get_buf(wp->w_buffer, lnum, false); ptr = line + linecol; if (len == 0 || (int)wp->w_cursor.col > ptr - line) { @@ -2539,13 +2571,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, spell_hlf = HLF_COUNT; word_end = (int)(spell_to_word_end(ptr, wp) - line + 1); } else { - /* bad word found, use attributes until end of word */ + // bad word found, use attributes until end of word assert(len <= INT_MAX); word_end = wp->w_cursor.col + (int)len + 1; - /* Turn index into actual attributes. */ - if (spell_hlf != HLF_COUNT) + // Turn index into actual attributes. + if (spell_hlf != HLF_COUNT) { spell_attr = highlight_attr[spell_hlf]; + } } wp->w_cursor = pos; @@ -2567,12 +2600,14 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, * cursor */ fromcol_prev = fromcol; fromcol = -1; - } else if ((colnr_T)fromcol < wp->w_virtcol) - /* restart highlighting after the cursor */ + } else if ((colnr_T)fromcol < wp->w_virtcol) { + // restart highlighting after the cursor fromcol_prev = wp->w_virtcol; + } } - if (fromcol >= tocol) + if (fromcol >= tocol) { fromcol = -1; + } } /* @@ -2582,8 +2617,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, cur = wp->w_match_head; shl_flag = false; while ((cur != NULL || !shl_flag) && !number_only - && !has_fold - ) { + && !has_fold && !end_fill) { if (!shl_flag) { shl = &search_hl; shl_flag = true; @@ -2616,18 +2650,18 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, shl->startcol = 0; } if (lnum == shl->lnum + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum) { - shl->endcol = shl->rm.endpos[0].col; + - shl->rm.startpos[0].lnum) { + shl->endcol = shl->rm.endpos[0].col; } else { - shl->endcol = MAXCOL; + shl->endcol = MAXCOL; } // Highlight one character for an empty match. if (shl->startcol == shl->endcol) { - if (line[shl->endcol] != NUL) { - shl->endcol += (*mb_ptr2len)(line + shl->endcol); - } else { - ++shl->endcol; - } + if (line[shl->endcol] != NUL) { + shl->endcol += (*mb_ptr2len)(line + shl->endcol); + } else { + ++shl->endcol; + } } if ((long)shl->startcol < v) { // match at leftcol shl->attr_cur = shl->attr; @@ -2636,8 +2670,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } area_highlighting = true; } - if (shl != &search_hl && cur != NULL) + if (shl != &search_hl && cur != NULL) { cur = cur->next; + } } unsigned off = 0; // Offset relative start of line @@ -2650,7 +2685,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, off += col; } - // wont highlight after TERM_ATTRS_MAX columns + // won't highlight after TERM_ATTRS_MAX columns int term_attrs[TERM_ATTRS_MAX] = { 0 }; if (wp->w_buffer->terminal) { terminal_get_line_attributes(wp->w_buffer->terminal, wp, lnum, term_attrs); @@ -2665,10 +2700,16 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // Skip this quickly when working on the text. if (draw_state != WL_LINE) { + if (cul_screenline) { + cul_attr = 0; + line_attr = line_attr_save; + line_attr_lowprio = line_attr_lowprio_save; + } + if (draw_state == WL_CMDLINE - 1 && n_extra == 0) { draw_state = WL_CMDLINE; if (cmdwin_type != 0 && wp == curwin) { - /* Draw the cmdline character. */ + // Draw the cmdline character. n_extra = 1; c_extra = cmdwin_type; c_final = NUL; @@ -2696,18 +2737,17 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // sign column, this is hit until sign_idx reaches count if (draw_state == WL_SIGN - 1 && n_extra == 0) { - draw_state = WL_SIGN; - /* Show the sign column when there are any signs in this - * buffer or when using Netbeans. */ - int count = win_signcol_count(wp); - if (count > 0) { - get_sign_display_info( - false, wp, sattrs, row, - startrow, filler_lines, filler_todo, count, - &c_extra, &c_final, extra, sizeof(extra), - &p_extra, &n_extra, - &char_attr, &draw_state, &sign_idx); - } + draw_state = WL_SIGN; + /* Show the sign column when there are any signs in this + * buffer or when using Netbeans. */ + int count = win_signcol_count(wp); + if (count > 0) { + get_sign_display_info(false, wp, sattrs, row, + startrow, filler_lines, filler_todo, count, + &c_extra, &c_final, extra, sizeof(extra), + &p_extra, &n_extra, + &char_attr, &draw_state, &sign_idx); + } } if (draw_state == WL_NR - 1 && n_extra == 0) { @@ -2723,12 +2763,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) { int count = win_signcol_count(wp); - get_sign_display_info( - true, wp, sattrs, row, - startrow, filler_lines, filler_todo, count, - &c_extra, &c_final, extra, sizeof(extra), - &p_extra, &n_extra, - &char_attr, &draw_state, &sign_idx); + get_sign_display_info(true, wp, sattrs, row, + startrow, filler_lines, filler_todo, count, + &c_extra, &c_final, extra, sizeof(extra), + &p_extra, &n_extra, + &char_attr, &draw_state, &sign_idx); } else { if (row == startrow + filler_lines) { // Draw the line number (empty space after wrapping). */ @@ -2757,8 +2796,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (wp->w_p_rl) { // reverse line numbers // like rl_mirror(), but keep the space at the end - char_u *p2 = skiptowhite(extra) - 1; - for (char_u *p1 = extra; p1 < p2; p1++, p2--) { + char_u *p2 = skipwhite(extra); + p2 = skiptowhite(p2) - 1; + for (char_u *p1 = skipwhite(extra); p1 < p2; p1++, p2--) { const int t = *p1; *p1 = *p2; *p2 = t; @@ -2774,15 +2814,29 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, n_extra = number_width(wp) + 1; char_attr = win_hl_attr(wp, HLF_N); + if (wp->w_p_rnu && lnum < wp->w_cursor.lnum) { + // Use LineNrAbove + char_attr = win_hl_attr(wp, HLF_LNA); + } + if (wp->w_p_rnu && lnum > wp->w_cursor.lnum) { + // Use LineNrBelow + char_attr = win_hl_attr(wp, HLF_LNB); + } + sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1); if (num_sattr != NULL) { // :sign defined with "numhl" highlight. char_attr = num_sattr->sat_numhl; - } else if ((wp->w_p_cul || wp->w_p_rnu) + } else if (wp->w_p_cul && lnum == wp->w_cursor.lnum + && (wp->w_p_culopt_flags & CULOPT_NBR) + && (row == startrow + || wp->w_p_culopt_flags & CULOPT_LINE) && filler_todo == 0) { // When 'cursorline' is set highlight the line number of // the current line differently. + // When 'cursorlineopt' has "screenline" only highlight + // the line number itself. // TODO(vim): Can we use CursorLine instead of CursorLineNr // when CursorLineNr isn't set? char_attr = win_hl_attr(wp, HLF_CLN); @@ -2796,7 +2850,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (wp->w_briopt_sbr && draw_state == WL_BRI - 1 - && n_extra == 0 && *p_sbr != NUL) { + && n_extra == 0 && *get_showbreak_value(wp) != NUL) { // draw indent after showbreak value draw_state = WL_BRI; } else if (wp->w_briopt_sbr && draw_state == WL_SBR && n_extra == 0) { @@ -2814,9 +2868,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (diff_hlf != (hlf_T)0) { char_attr = win_hl_attr(wp, diff_hlf); - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { - char_attr = hl_combine_attr(char_attr, win_hl_attr(wp, HLF_CUL)); - } } p_extra = NULL; c_extra = ' '; @@ -2858,24 +2909,26 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } char_attr = win_hl_attr(wp, HLF_DED); } - if (*p_sbr != NUL && need_showbreak) { - /* Draw 'showbreak' at the start of each broken line. */ - p_extra = p_sbr; + char_u *const sbr = get_showbreak_value(wp); + if (*sbr != NUL && need_showbreak) { + // Draw 'showbreak' at the start of each broken line. + p_extra = sbr; c_extra = NUL; c_final = NUL; - n_extra = (int)STRLEN(p_sbr); + n_extra = (int)STRLEN(sbr); char_attr = win_hl_attr(wp, HLF_AT); if (wp->w_skipcol == 0 || !wp->w_p_wrap) { need_showbreak = false; } - vcol_sbr = vcol + MB_CHARLEN(p_sbr); - /* Correct end of highlighted area for 'showbreak', - * required when 'linebreak' is also set. */ - if (tocol == vcol) + vcol_sbr = vcol + MB_CHARLEN(sbr); + // Correct end of highlighted area for 'showbreak', + // required when 'linebreak' is also set. + if (tocol == vcol) { tocol += n_extra; + } // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'. - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { - char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUL), char_attr); + if (cul_attr) { + char_attr = hl_combine_attr(cul_attr, char_attr); } } } @@ -2890,7 +2943,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } if (saved_n_extra) { - /* Continue item from end of wrapped line. */ + // Continue item from end of wrapped line. n_extra = saved_n_extra; c_extra = saved_c_extra; c_final = saved_c_final; @@ -2902,6 +2955,23 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } + if (cul_screenline && draw_state == WL_LINE + && vcol >= left_curline_col + && vcol < right_curline_col) { + cul_attr = win_hl_attr(wp, HLF_CUL); + HlAttrs ae = syn_attr2entry(cul_attr); + if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) { + line_attr_lowprio = cul_attr; + } else { + if (!(State & INSERT) && bt_quickfix(wp->w_buffer) + && qf_current_entry(wp) == lnum) { + line_attr = hl_combine_attr(cul_attr, line_attr); + } else { + line_attr = cul_attr; + } + } + } + // When still displaying '$' of change command, stop at cursor if (((dollar_vcol >= 0 && wp == curwin @@ -2927,20 +2997,20 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && vcol == 0 && n_extra == 0 && row == startrow) { - char_attr = win_hl_attr(wp, HLF_FL); + char_attr = win_hl_attr(wp, HLF_FL); - linenr_T lnume = lnum + foldinfo.fi_lines - 1; - memset(buf_fold, ' ', FOLD_TEXT_LEN); - p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold); - n_extra = STRLEN(p_extra); + linenr_T lnume = lnum + foldinfo.fi_lines - 1; + memset(buf_fold, ' ', FOLD_TEXT_LEN); + p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold); + n_extra = STRLEN(p_extra); - if (p_extra != buf_fold) { - xfree(p_extra_free); - p_extra_free = p_extra; - } - c_extra = NUL; - c_final = NUL; - p_extra[n_extra] = NUL; + if (p_extra != buf_fold) { + xfree(p_extra_free); + p_extra_free = p_extra; + } + c_extra = NUL; + c_final = NUL; + p_extra[n_extra] = NUL; } if (draw_state == WL_LINE @@ -2981,7 +3051,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && (colnr_T)vcol == wp->w_virtcol))) { area_attr = 0; // stop highlighting area_active = false; - } + } if (!n_extra) { /* @@ -3006,10 +3076,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (cur != NULL) { cur->pos.cur = 0; } - bool pos_inprogress = true; // mark that a position match search is - // in progress + bool pos_inprogress = true; // mark that a position match search is + // in progress while (shl->rm.regprog != NULL - || (cur != NULL && pos_inprogress)) { + || (cur != NULL && pos_inprogress)) { if (shl->startcol != MAXCOL && v >= (long)shl->startcol && v < (long)shl->endcol) { @@ -3036,17 +3106,18 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, shl == &search_hl ? NULL : cur); pos_inprogress = !(cur == NULL || cur->pos.cur == 0); - /* Need to get the line again, a multi-line regexp - * may have made it invalid. */ - line = ml_get_buf(wp->w_buffer, lnum, FALSE); + // Need to get the line again, a multi-line regexp + // may have made it invalid. + line = ml_get_buf(wp->w_buffer, lnum, false); ptr = line + v; if (shl->lnum == lnum) { shl->startcol = shl->rm.startpos[0].col; - if (shl->rm.endpos[0].lnum == 0) + if (shl->rm.endpos[0].lnum == 0) { shl->endcol = shl->rm.endpos[0].col; - else + } else { shl->endcol = MAXCOL; + } if (shl->startcol == shl->endcol) { // highlight empty match, try again after it @@ -3060,8 +3131,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } break; } - if (shl != &search_hl && cur != NULL) + if (shl != &search_hl && cur != NULL) { cur = cur->next; + } } /* Use attributes from match with highest priority among @@ -3082,8 +3154,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, search_attr = shl->attr_cur; search_attr_from_match = shl != &search_hl; } - if (shl != &search_hl && cur != NULL) + if (shl != &search_hl && cur != NULL) { cur = cur->next; + } } // Only highlight one character after the last column. if (*ptr == NUL @@ -3109,12 +3182,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } line_attr = win_hl_attr(wp, diff_hlf); // Overlay CursorLine onto diff-mode highlight. - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { + if (cul_attr) { line_attr = 0 != line_attr_lowprio // Low-priority CursorLine - ? hl_combine_attr(hl_combine_attr(win_hl_attr(wp, HLF_CUL), - line_attr), + ? hl_combine_attr(hl_combine_attr(cul_attr, line_attr), hl_get_underline()) - : hl_combine_attr(line_attr, win_hl_attr(wp, HLF_CUL)); + : hl_combine_attr(line_attr, cul_attr); } } @@ -3190,6 +3262,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, (void)mb_l; multi_attr = win_hl_attr(wp, HLF_AT); + if (cul_attr) { + multi_attr = 0 != line_attr_lowprio + ? hl_combine_attr(cul_attr, multi_attr) + : hl_combine_attr(multi_attr, cul_attr); + } + // put the pointer back to output the double-width // character at the start of the next line. n_extra++; @@ -3341,16 +3419,23 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (did_emsg) { wp->w_s->b_syn_error = TRUE; has_syntax = FALSE; - } else + } else { did_emsg = save_did_emsg; + } - /* Need to get the line again, a multi-line regexp may - * have made it invalid. */ - line = ml_get_buf(wp->w_buffer, lnum, FALSE); + // Need to get the line again, a multi-line regexp may + // have made it invalid. + line = ml_get_buf(wp->w_buffer, lnum, false); ptr = line + v; if (!attr_pri) { - char_attr = syntax_attr; + if (cul_attr) { + char_attr = 0 != line_attr_lowprio + ? hl_combine_attr(cul_attr, syntax_attr) + : hl_combine_attr(syntax_attr, cul_attr); + } else { + char_attr = syntax_attr; + } } else { char_attr = hl_combine_attr(syntax_attr, char_attr); } @@ -3416,9 +3501,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, checked_col = (int)((p - nextline) + len - nextline_idx); } - /* Turn index into actual attributes. */ - if (spell_hlf != HLF_COUNT) + // Turn index into actual attributes. + if (spell_hlf != HLF_COUNT) { spell_attr = highlight_attr[spell_hlf]; + } if (cap_col > 0) { if (p != prev_ptr @@ -3428,17 +3514,19 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, capcol_lnum = lnum + 1; cap_col = (int)((p - nextline) + cap_col - nextline_idx); - } else - /* Compute the actual column. */ + } else { + // Compute the actual column. cap_col += (int)(prev_ptr - line); + } } } } if (spell_attr != 0) { - if (!attr_pri) + if (!attr_pri) { char_attr = hl_combine_attr(char_attr, spell_attr); - else + } else { char_attr = hl_combine_attr(spell_attr, char_attr); + } } if (wp->w_buffer->terminal) { @@ -3466,6 +3554,16 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, char_u *p = ptr - (mb_off + 1); // TODO: is passing p for start of the line OK? n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1; + + // We have just drawn the showbreak value, no need to add + // space for it again. + if (vcol == vcol_sbr) { + n_extra -= MB_CHARLEN(get_showbreak_value(wp)); + if (n_extra < 0) { + n_extra = 0; + } + } + if (c == TAB && n_extra + col > grid->Columns) { n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts, wp->w_buffer->b_p_vts_array) - 1; @@ -3473,9 +3571,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; c_final = NUL; if (ascii_iswhite(c)) { - if (c == TAB) - /* See "Tab alignment" below. */ + if (c == TAB) { + // See "Tab alignment" below. FIX_FOR_BOGUSCOLS; + } if (!wp->w_p_list) { c = ' '; } @@ -3531,10 +3630,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { int tab_len = 0; long vcol_adjusted = vcol; // removed showbreak length + char_u *const sbr = get_showbreak_value(wp); + // Only adjust the tab_len, when at the first column after the // showbreak value was drawn. - if (*p_sbr != NUL && vcol == vcol_sbr && wp->w_p_wrap) { - vcol_adjusted = vcol - MB_CHARLEN(p_sbr); + if (*sbr != NUL && vcol == vcol_sbr && wp->w_p_wrap) { + vcol_adjusted = vcol - MB_CHARLEN(sbr); } // tab amount depends on current column tab_len = tabstop_padding(vcol_adjusted, @@ -3622,7 +3723,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, ? wp->w_p_lcs_chars.tab3 : wp->w_p_lcs_chars.tab1; if (wp->w_p_lbr) { - c_extra = NUL; /* using p_extra from above */ + c_extra = NUL; // using p_extra from above } else { c_extra = wp->w_p_lcs_chars.tab2; } @@ -3688,10 +3789,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } else if (c != NUL) { p_extra = transchar_buf(wp->w_buffer, c); if (n_extra == 0) { - n_extra = byte2cells(c) - 1; + n_extra = byte2cells(c) - 1; + } + if ((dy_flags & DY_UHEX) && wp->w_p_rl) { + rl_mirror(p_extra); // reverse "<12>" } - if ((dy_flags & DY_UHEX) && wp->w_p_rl) - rl_mirror(p_extra); /* reverse "<12>" */ c_extra = NUL; c_final = NUL; if (wp->w_p_lbr) { @@ -3749,8 +3851,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, prev_syntax_id = syntax_seqnr; - if (n_extra > 0) + if (n_extra > 0) { vcol_off += n_extra; + } vcol += n_extra; if (wp->w_p_wrap && n_extra > 0) { if (wp->w_p_rl) { @@ -3764,7 +3867,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, n_extra = 0; n_attr = 0; } else if (n_skip == 0) { - is_concealing = TRUE; + is_concealing = true; n_skip = 1; } mb_c = c; @@ -3777,7 +3880,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } } else { prev_syntax_id = 0; - is_concealing = FALSE; + is_concealing = false; } if (n_skip > 0 && did_decrement_ptr) { @@ -3876,8 +3979,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int n = 0; if (wp->w_p_rl) { - if (col < 0) + if (col < 0) { n = 1; + } } else { if (col >= grid->Columns) { n = -1; @@ -3917,8 +4021,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } int eol_attr = char_attr; - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { - eol_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUL), eol_attr); + if (cul_attr) { + eol_attr = hl_combine_attr(cul_attr, eol_attr); } linebuf_attr[off] = eol_attr; if (wp->w_p_rl) { @@ -3938,30 +4042,28 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, v = wp->w_leftcol; } - /* check if line ends before left margin */ - if (vcol < v + col - win_col_off(wp)) + // check if line ends before left margin + if (vcol < v + col - win_col_off(wp)) { vcol = v + col - win_col_off(wp); + } // Get rid of the boguscols now, we want to draw until the right // edge for 'cursorcolumn'. col -= boguscols; // boguscols = 0; // Disabled because value never read after this - if (draw_color_col) + if (draw_color_col) { draw_color_col = advance_color_col(VCOL_HLC, &color_cols); + } - VirtText virt_text = KV_INITIAL_VALUE; - bool has_aligned = false; - if (err_text) { - int hl_err = syn_check_group((char_u *)S_LEN("ErrorMsg")); - kv_push(virt_text, ((VirtTextChunk){ .text = err_text, - .hl_id = hl_err })); - do_virttext = true; - } else if (has_decor) { - virt_text = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr, - &has_aligned); - if (kv_size(virt_text)) { - do_virttext = true; - } + bool has_virttext = false; + // Make sure alignment is the same regardless + // if listchars=eol:X is used or not. + int eol_skip = (wp->w_p_lcs_chars.eol == lcs_eol_one && eol_hl_off == 0 + ? 1 : 0); + + if (has_decor) { + has_virttext = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr, + col + eol_skip); } if (((wp->w_p_cuc @@ -3970,20 +4072,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, grid->Columns * (row - startrow + 1) + v && lnum != wp->w_cursor.lnum) || draw_color_col || line_attr_lowprio || line_attr - || diff_hlf != (hlf_T)0 || do_virttext - || has_aligned)) { + || diff_hlf != (hlf_T)0 || has_virttext)) { int rightmost_vcol = 0; int i; - size_t virt_pos = 0; - LineState s = LINE_STATE(""); - int virt_attr = 0; - - // Make sure alignment is the same regardless - // if listchars=eol:X is used or not. - bool delay_virttext = wp->w_p_lcs_chars.eol == lcs_eol_one - && eol_hl_off == 0; - if (wp->w_p_cuc) { rightmost_vcol = wp->w_virtcol; } @@ -4009,37 +4101,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr); - if (base_attr || line_attr || has_aligned) { + if (base_attr || line_attr || has_virttext) { rightmost_vcol = INT_MAX; } int col_stride = wp->w_p_rl ? -1 : 1; while (wp->w_p_rl ? col >= 0 : col < grid->Columns) { - int cells = -1; - if (do_virttext && !delay_virttext) { - if (*s.p == NUL) { - if (virt_pos < virt_text.size) { - s.p = kv_A(virt_text, virt_pos).text; - int hl_id = kv_A(virt_text, virt_pos).hl_id; - virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; - virt_pos++; - } else { - do_virttext = false; - } - } - if (*s.p != NUL) { - cells = line_putchar(&s, &linebuf_char[off], grid->Columns - col, - false); - } - } - delay_virttext = false; - - if (cells == -1) { - schar_from_ascii(linebuf_char[off], ' '); - cells = 1; - } - col += cells * col_stride; + schar_from_ascii(linebuf_char[off], ' '); + col += col_stride; if (draw_color_col) { draw_color_col = advance_color_col(VCOL_HLC, &color_cols); } @@ -4052,24 +4122,16 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, col_attr = mc_attr; } - if (do_virttext) { - col_attr = hl_combine_attr(col_attr, virt_attr); - } - col_attr = hl_combine_attr(col_attr, line_attr); linebuf_attr[off] = col_attr; - if (cells == 2) { - linebuf_attr[off+1] = col_attr; - } - off += cells * col_stride; + off += col_stride; - if (VCOL_HLC >= rightmost_vcol && *s.p == NUL - && virt_pos >= virt_text.size) { + if (VCOL_HLC >= rightmost_vcol) { break; } - vcol += cells; + vcol += 1; } } @@ -4094,7 +4156,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, /* * Update w_cline_height and w_cline_folded if the cursor line was - * updated (saves a call to plines() later). + * updated (saves a call to plines_win() later). */ if (wp == curwin && lnum == curwin->w_cursor.lnum) { curwin->w_cline_row = startrow; @@ -4202,7 +4264,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, tocol++; } if (wp->w_p_rl) { - /* now it's time to backup one cell */ + // now it's time to backup one cell --off; --col; } @@ -4217,8 +4279,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, } else if (wp->w_p_cole > 0 && is_concealing) { --n_skip; ++vcol_off; - if (n_extra > 0) + if (n_extra > 0) { vcol_off += n_extra; + } if (wp->w_p_wrap) { /* * Special voodoo required if 'wrap' is on. @@ -4272,27 +4335,30 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, n_attr = 0; } } - - } else + } else { --n_skip; + } /* Only advance the "vcol" when after the 'number' or 'relativenumber' * column. */ if (draw_state > WL_NR - && filler_todo <= 0 - ) + && filler_todo <= 0) { ++vcol; + } - if (vcol_save_attr >= 0) + if (vcol_save_attr >= 0) { char_attr = vcol_save_attr; + } - /* restore attributes after "predeces" in 'listchars' */ - if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) + // restore attributes after "predeces" in 'listchars' + if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) { char_attr = saved_attr3; + } - /* restore attributes after last 'listchars' or 'number' char */ - if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0) + // restore attributes after last 'listchars' or 'number' char + if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0) { char_attr = saved_attr2; + } /* * At end of screen line and there is more to come: Display the line @@ -4304,15 +4370,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, || filler_todo > 0 || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL && p_extra != at_end_str) - || (n_extra != 0 && (c_extra != NUL || *p_extra != NUL))) - ) { + || (n_extra != 0 && + (c_extra != NUL || *p_extra != NUL)))) { bool wrap = wp->w_p_wrap // Wrapping enabled. - && filler_todo <= 0 // Not drawing diff filler lines. - && lcs_eol_one != -1 // Haven't printed the lcs_eol character. - && row != endrow - 1 // Not the last line being displayed. - && (grid->Columns == Columns // Window spans the width of the screen, - || ui_has(kUIMultigrid)) // or has dedicated grid. - && !wp->w_p_rl; // Not right-to-left. + && filler_todo <= 0 // Not drawing diff filler lines. + && lcs_eol_one != -1 // Haven't printed the lcs_eol character. + && row != endrow - 1 // Not the last line being displayed. + && (grid->Columns == Columns // Window spans the width of the screen, + || ui_has(kUIMultigrid)) // or has dedicated grid. + && !wp->w_p_rl; // Not right-to-left. int draw_col = col - boguscols; draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns); @@ -4337,8 +4403,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, * '$' and highlighting until last column, break here. */ if ((!wp->w_p_wrap && filler_todo <= 0 - ) || lcs_eol_one == -1) + ) || lcs_eol_one == -1) { break; + } // When the window is too narrow draw all "@" lines. if (draw_state != WL_LINE && filler_todo <= 0) { @@ -4346,7 +4413,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, row = endrow; } - /* When line got too long for screen break here. */ + // When line got too long for screen break here. if (row == endrow) { ++row; break; @@ -4359,7 +4426,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, off += col; } - /* reset the drawing state for the start of a wrapped line */ + // reset the drawing state for the start of a wrapped line draw_state = WL_START; saved_n_extra = n_extra; saved_p_extra = p_extra; @@ -4374,21 +4441,19 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, filler_todo--; // When the filler lines are actually below the last line of the // file, don't draw the line itself, break here. - if (filler_todo == 0 && wp->w_botfill) { + if (filler_todo == 0 && (wp->w_botfill || end_fill)) { break; } } + } // for every character in the line - } /* for every character in the line */ - - /* After an empty line check first word for capital. */ + // After an empty line check first word for capital. if (*skipwhite(line) == NUL) { capcol_lnum = lnum + 1; cap_col = 0; } xfree(p_extra_free); - xfree(err_text); return row; } @@ -4396,13 +4461,17 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col) { DecorState *state = &decor_state; int right_pos = max_col; + bool do_eol = state->eol_col > -1; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange *item = &kv_A(state->active, i); if (item->start_row == state->row && kv_size(item->decor.virt_text)) { if (item->win_col == -1) { if (item->decor.virt_text_pos == kVTRightAlign) { - right_pos -= item->decor.col; + right_pos -= item->decor.virt_text_width; item->win_col = right_pos; + } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { + item->win_col = state->eol_col; + state->eol_col += item->decor.virt_text_width; } else if (item->decor.virt_text_pos == kVTWinCol) { item->win_col = MAX(item->decor.col+col_off, 0); } @@ -4420,14 +4489,20 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col) while (col < max_col) { if (!*s.p) { - if (virt_pos == kv_size(vt)) { + if (virt_pos >= kv_size(vt)) { + break; + } + virt_attr = 0; + do { + s.p = kv_A(vt, virt_pos).text; + int hl_id = kv_A(vt, virt_pos).hl_id; + virt_attr = hl_combine_attr(virt_attr, + hl_id > 0 ? syn_id2attr(hl_id) : 0); + virt_pos++; + } while (!s.p && virt_pos < kv_size(vt)); + if (!s.p) { break; } - s.p = kv_A(vt, virt_pos).text; - int hl_id = kv_A(vt, virt_pos).hl_id; - virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; - virt_pos++; - continue; } int attr; bool through = false; @@ -4478,25 +4553,11 @@ void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) // @param count max number of signs // @param[out] n_extrap number of characters from pp_extra to display // @param[in, out] sign_idxp Index of the displayed sign -static void get_sign_display_info( - bool nrcol, - win_T *wp, - sign_attrs_T sattrs[], - int row, - int startrow, - int filler_lines, - int filler_todo, - int count, - int *c_extrap, - int *c_finalp, - char_u *extra, - size_t extra_size, - char_u **pp_extra, - int *n_extrap, - int *char_attrp, - int *draw_statep, - int *sign_idxp -) +static void get_sign_display_info(bool nrcol, win_T *wp, sign_attrs_T sattrs[], int row, + int startrow, int filler_lines, int filler_todo, int count, + int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size, + char_u **pp_extra, int *n_extrap, int *char_attrp, + int *draw_statep, int *sign_idxp) { // Draw cells with the sign value or blank. *c_extrap = ' '; @@ -4534,7 +4595,7 @@ static void get_sign_display_info( assert((size_t)win_signcol_width(wp) >= mb_string2cells(*pp_extra)); // symbol(s) bytes + (filling spaces) (one byte each) *n_extrap = symbol_blen + - (win_signcol_width(wp) - mb_string2cells(*pp_extra)); + (win_signcol_width(wp) - mb_string2cells(*pp_extra)); assert(extra_size > (size_t)symbol_blen); memset(extra, ' ', extra_size); @@ -4564,8 +4625,7 @@ static void get_sign_display_info( * - the character is multi-byte and the next byte is different * - the character is two cells wide and the second cell differs. */ -static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, - int cols) +static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, int cols) { return (cols > 0 && ((schar_cmp(linebuf_char[off_from], grid->chars[off_to]) @@ -4587,9 +4647,8 @@ static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, /// 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. -static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, - int clear_width, int rlflag, win_T *wp, - int bg_attr, bool wrap) +static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int clear_width, + int rlflag, win_T *wp, int bg_attr, bool wrap) { unsigned off_from; unsigned off_to; @@ -4626,12 +4685,11 @@ static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, max_off_to = grid->line_offset[row] + grid->Columns; if (rlflag) { - /* Clear rest first, because it's left of the text. */ + // Clear rest first, because it's left of the text. if (clear_width > 0) { while (col <= endcol && grid->chars[off_to][0] == ' ' && grid->chars[off_to][1] == NUL - && grid->attrs[off_to] == bg_attr - ) { + && grid->attrs[off_to] == bg_attr) { ++off_to; ++col; } @@ -4757,7 +4815,7 @@ static void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, */ void rl_mirror(char_u *str) { - char_u *p1, *p2; + char_u *p1, *p2; int t; for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; ++p1, --p2) { @@ -4772,7 +4830,6 @@ void rl_mirror(char_u *str) */ void status_redraw_all(void) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_status_height) { wp->w_redr_status = true; @@ -4808,8 +4865,9 @@ void redraw_statuslines(void) win_redr_status(wp); } } - if (redraw_tabline) + if (redraw_tabline) { draw_tabline(); + } } /* @@ -4861,9 +4919,10 @@ static int status_match_len(expand_T *xp, char_u *s) int emenu = (xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES); - /* Check for menu separators - replace with '|'. */ - if (emenu && menu_is_separator(s)) + // Check for menu separators - replace with '|'. + if (emenu && menu_is_separator(s)) { return 1; + } while (*s != NUL) { s += skip_status_match_char(xp, s); @@ -4883,78 +4942,76 @@ static int skip_status_match_char(expand_T *xp, char_u *s) if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP) || ((xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES) - && (s[0] == '\t' || (s[0] == '\\' && s[1] != NUL))) - ) { + && (s[0] == '\t' || + (s[0] == '\\' && s[1] != NUL)))) { #ifndef BACKSLASH_IN_FILENAME - if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') + if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') { return 2; + } #endif return 1; } return 0; } -/* - * Show wildchar matches in the status line. - * Show at least the "match" item. - * We start at item 'first_match' in the list and show all matches that fit. - * - * If inversion is possible we use it. Else '=' characters are used. - */ -void -win_redr_status_matches ( - expand_T *xp, - int num_matches, - char_u **matches, /* list of matches */ - int match, - int showtail -) +/// Show wildchar matches in the status line. +/// Show at least the "match" item. +/// We start at item 'first_match' in the list and show all matches that fit. +/// +/// If inversion is possible we use it. Else '=' characters are used. +/// +/// @param matches list of matches +void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, int match, + int showtail) { #define L_MATCH(m) (showtail ? sm_gettail(matches[m], false) : matches[m]) int row; - char_u *buf; + char_u *buf; int len; - int clen; /* length in screen cells */ + int clen; // length in screen cells int fillchar; int attr; int i; - int highlight = TRUE; - char_u *selstart = NULL; + bool highlight = true; + char_u *selstart = NULL; int selstart_col = 0; - char_u *selend = NULL; + char_u *selend = NULL; static int first_match = 0; - int add_left = FALSE; - char_u *s; + bool add_left = false; + char_u *s; int emenu; int l; - if (matches == NULL) /* interrupted completion? */ + if (matches == NULL) { // interrupted completion? return; + } buf = xmalloc(Columns * MB_MAXBYTES + 1); - if (match == -1) { /* don't show match but original text */ + if (match == -1) { // don't show match but original text match = 0; - highlight = FALSE; + highlight = false; } - /* count 1 for the ending ">" */ + // count 1 for the ending ">" clen = status_match_len(xp, L_MATCH(match)) + 3; - if (match == 0) + if (match == 0) { first_match = 0; - else if (match < first_match) { - /* jumping left, as far as we can go */ + } else if (match < first_match) { + // jumping left, as far as we can go first_match = match; - add_left = TRUE; + add_left = true; } else { - /* check if match fits on the screen */ - for (i = first_match; i < match; ++i) + // check if match fits on the screen + for (i = first_match; i < match; ++i) { clen += status_match_len(xp, L_MATCH(i)) + 2; - if (first_match > 0) + } + if (first_match > 0) { clen += 2; + } // jumping right, put match at the left if ((long)clen > Columns) { first_match = match; - /* if showing the last match, we can add some on the left */ + // if showing the last match, we can add some on the left clen = 2; for (i = match; i < num_matches; ++i) { clen += status_match_len(xp, L_MATCH(i)) + 2; @@ -4962,11 +5019,12 @@ win_redr_status_matches ( break; } } - if (i == num_matches) - add_left = TRUE; + if (i == num_matches) { + add_left = true; + } } } - if (add_left) + if (add_left) { while (first_match > 0) { clen += status_match_len(xp, L_MATCH(first_match - 1)) + 2; if ((long)clen >= Columns) { @@ -4974,6 +5032,7 @@ win_redr_status_matches ( } first_match--; } + } fillchar = fillchar_status(&attr, curwin); @@ -4994,7 +5053,7 @@ win_redr_status_matches ( } s = L_MATCH(i); - /* Check for menu separators - replace with '|' */ + // Check for menu separators - replace with '|' emenu = (xp->xp_context == EXPAND_MENUS || xp->xp_context == EXPAND_MENUNAMES); if (emenu && menu_is_separator(s)) { @@ -5002,7 +5061,7 @@ win_redr_status_matches ( l = (int)STRLEN(buf + len); len += l; clen += l; - } else + } else { for (; *s != NUL; ++s) { s += skip_status_match_char(xp, s); clen += ptr2cells(s); @@ -5015,14 +5074,17 @@ win_redr_status_matches ( len += (int)STRLEN(buf + len); } } - if (i == match) + } + if (i == match) { selend = buf + len; + } *(buf + len++) = ' '; *(buf + len++) = ' '; clen += 2; - if (++i == num_matches) + if (++i == num_matches) { break; + } } if (i != num_matches) { @@ -5055,7 +5117,7 @@ win_redr_status_matches ( save_p_wmh = p_wmh; p_ls = 2; p_wmh = 0; - last_status(FALSE); + last_status(false); } wild_menu_showing = WM_SHOWN; } @@ -5086,7 +5148,7 @@ win_redr_status_matches ( static void win_redr_status(win_T *wp) { int row; - char_u *p; + char_u *p; int len; int fillchar; int attr; @@ -5111,7 +5173,7 @@ static void win_redr_status(win_T *wp) // popup menu is visible and may be drawn over it wp->w_redr_status = true; } else if (*p_stl != NUL || *wp->w_p_stl != NUL) { - /* redraw custom status line */ + // redraw custom status line redraw_custom_statusline(wp); } else { fillchar = fillchar_status(&attr, wp); @@ -5176,11 +5238,12 @@ static void win_redr_status(win_T *wp) this_ru_col + wp->w_wincol, fillchar, fillchar, attr); if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL) - && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) + && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) { grid_puts(&default_grid, NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff) - 1), attr); + } - win_redr_ruler(wp, TRUE); + win_redr_ruler(wp, true); } /* @@ -5203,14 +5266,15 @@ static void win_redr_status(win_T *wp) */ static void redraw_custom_statusline(win_T *wp) { - static int entered = false; + static bool entered = false; int saved_did_emsg = did_emsg; /* When called recursively return. This can happen when the statusline * contains an expression that triggers a redraw. */ - if (entered) + if (entered) { return; - entered = TRUE; + } + entered = true; did_emsg = false; win_redr_custom(wp, false); @@ -5226,54 +5290,52 @@ static void redraw_custom_statusline(win_T *wp) entered = false; } -/* - * Return TRUE if the status line of window "wp" is connected to the status - * line of the window right of it. If not, then it's a vertical separator. - * Only call if (wp->w_vsep_width != 0). - */ -int stl_connected(win_T *wp) +/// Only call if (wp->w_vsep_width != 0). +/// +/// @return true if the status line of window "wp" is connected to the status +/// line of the window right of it. If not, then it's a vertical separator. +bool stl_connected(win_T *wp) { - frame_T *fr; + frame_T *fr; fr = wp->w_frame; while (fr->fr_parent != NULL) { if (fr->fr_parent->fr_layout == FR_COL) { - if (fr->fr_next != NULL) + if (fr->fr_next != NULL) { break; + } } else { - if (fr->fr_next != NULL) - return TRUE; + if (fr->fr_next != NULL) { + return true; + } } fr = fr->fr_parent; } - return FALSE; + return false; } -/* - * Get the value to show for the language mappings, active 'keymap'. - */ -int -get_keymap_str ( - win_T *wp, - char_u *fmt, // format string containing one %s item - char_u *buf, // buffer for the result - int len // length of buffer -) +/// Get the value to show for the language mappings, active 'keymap'. +/// +/// @param fmt format string containing one %s item +/// @param buf buffer for the result +/// @param len length of buffer +bool get_keymap_str(win_T *wp, char_u *fmt, char_u *buf, int len) { - char_u *p; + char_u *p; - if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP) - return FALSE; + if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP) { + return false; + } { - buf_T *old_curbuf = curbuf; - win_T *old_curwin = curwin; - char_u *s; + buf_T *old_curbuf = curbuf; + win_T *old_curwin = curwin; + char_u *s; curbuf = wp->w_buffer; curwin = wp; - STRCPY(buf, "b:keymap_name"); /* must be writable */ + STRCPY(buf, "b:keymap_name"); // must be writable ++emsg_skip; s = p = eval_to_string(buf, NULL, FALSE); --emsg_skip; @@ -5298,13 +5360,9 @@ get_keymap_str ( * Redraw the status line or ruler of window "wp". * When "wp" is NULL redraw the tab pages line from 'tabline'. */ -static void -win_redr_custom ( - win_T *wp, - bool draw_ruler -) +static void win_redr_custom(win_T *wp, bool draw_ruler) { - static int entered = FALSE; + static bool entered = false; int attr; int curattr; int row; @@ -5315,12 +5373,12 @@ win_redr_custom ( int len; int fillchar; char_u buf[MAXPATHL]; - char_u *stl; - char_u *p; + char_u *stl; + char_u *p; stl_hlrec_t *hltab; StlClickRecord *tabtab; int use_sandbox = false; - win_T *ewp; + win_T *ewp; int p_crb_save; ScreenGrid *grid = &default_grid; @@ -5328,13 +5386,14 @@ win_redr_custom ( /* There is a tiny chance that this gets called recursively: When * redrawing a status line triggers redrawing the ruler or tabline. * Avoid trouble by not allowing recursion. */ - if (entered) + if (entered) { return; - entered = TRUE; + } + entered = true; - /* setup environment for the task at hand */ + // setup environment for the task at hand if (wp == NULL) { - /* Use 'tabline'. Always at the first line of the screen. */ + // Use 'tabline'. Always at the first line of the screen. stl = p_tal; row = 0; fillchar = ' '; @@ -5348,15 +5407,19 @@ win_redr_custom ( if (draw_ruler) { stl = p_ruf; - /* advance past any leading group spec - implicit in ru_col */ + // advance past any leading group spec - implicit in ru_col if (*stl == '%') { - if (*++stl == '-') + if (*++stl == '-') { stl++; - if (atoi((char *)stl)) - while (ascii_isdigit(*stl)) + } + if (atoi((char *)stl)) { + while (ascii_isdigit(*stl)) { stl++; - if (*stl++ != '(') + } + } + if (*stl++ != '(') { stl = p_ruf; + } } col = ru_col - (Columns - wp->w_width); if (col < (wp->w_width + 1) / 2) { @@ -5373,19 +5436,21 @@ win_redr_custom ( use_sandbox = was_set_insecurely(wp, (char_u *)"rulerformat", 0); } else { - if (*wp->w_p_stl != NUL) + if (*wp->w_p_stl != NUL) { stl = wp->w_p_stl; - else + } else { stl = p_stl; - use_sandbox = was_set_insecurely( - wp, (char_u *)"statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); + } + use_sandbox = was_set_insecurely(wp, (char_u *)"statusline", + *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); } col += wp->w_wincol; } - if (maxwidth <= 0) + if (maxwidth <= 0) { goto theend; + } /* Temporarily reset 'cursorbind', we don't want a side effect from moving * the cursor away and back. */ @@ -5408,7 +5473,7 @@ win_redr_custom ( len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; xfree(p); - /* fill up with "fillchar" */ + // fill up with "fillchar" while (width < maxwidth && len < (int)sizeof(buf) - 1) { len += utf_char2bytes(fillchar, buf + len); width++; @@ -5428,14 +5493,15 @@ win_redr_custom ( col += vim_strnsize(p, textlen); p = hltab[n].start; - if (hltab[n].userhl == 0) + if (hltab[n].userhl == 0) { curattr = attr; - else if (hltab[n].userhl < 0) + } else if (hltab[n].userhl < 0) { curattr = syn_id2attr(-hltab[n].userhl); - else if (wp != NULL && wp != curwin && wp->w_status_height != 0) + } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) { curattr = highlight_stlnc[hltab[n].userhl - 1]; - else + } else { curattr = highlight_user[hltab[n].userhl - 1]; + } } // Make sure to use an empty string instead of p, if p is beyond buf + len. grid_puts(grid, p >= buf + len ? (char_u *)"" : p, row, col, @@ -5452,11 +5518,11 @@ win_redr_custom ( .type = kStlClickDisabled, }; for (n = 0; tabtab[n].start != NULL; n++) { - len += vim_strnsize(p, (int)(tabtab[n].start - (char *) p)); + len += vim_strnsize(p, (int)(tabtab[n].start - (char *)p)); while (col < len) { tab_page_click_defs[col++] = cur_click_def; } - p = (char_u *) tabtab[n].start; + p = (char_u *)tabtab[n].start; cur_click_def = tabtab[n].def; } while (col < Columns) { @@ -5465,7 +5531,7 @@ win_redr_custom ( } theend: - entered = FALSE; + entered = false; } static void win_redr_border(win_T *wp) @@ -5526,7 +5592,7 @@ static void win_redr_border(win_T *wp) } } -// Low-level functions to manipulate invidual character cells on the +// Low-level functions to manipulate individual character cells on the // screen grid. /// Put a ASCII character in a screen cell. @@ -5620,8 +5686,7 @@ void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr) /// get a single character directly from grid.chars into "bytes[]". /// Also return its attribute in *attrp; -void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, - int *attrp) +void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, int *attrp) { unsigned off; @@ -5668,22 +5733,21 @@ void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr) assert(put_dirty_row == row); unsigned int off = grid->line_offset[row] + col; if (grid->attrs[off] != attr || schar_cmp(grid->chars[off], schar)) { - schar_copy(grid->chars[off], schar); - grid->attrs[off] = attr; + schar_copy(grid->chars[off], schar); + grid->attrs[off] = attr; - put_dirty_first = MIN(put_dirty_first, col); - // TODO(bfredl): Y U NO DOUBLEWIDTH? - put_dirty_last = MAX(put_dirty_last, col+1); + put_dirty_first = MIN(put_dirty_first, col); + // TODO(bfredl): Y U NO DOUBLEWIDTH? + put_dirty_last = MAX(put_dirty_last, col+1); } } /// like grid_puts(), but output "text[len]". When "len" is -1 output up to /// a NUL. -void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, - int col, int attr) +void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col, int attr) { unsigned off; - char_u *ptr = text; + char_u *ptr = text; int len = textlen; int c; unsigned max_off; @@ -5692,7 +5756,7 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int u8c = 0; int u8cc[MAX_MCO]; int clear_next_cell = FALSE; - int prev_c = 0; /* previous Arabic character */ + int prev_c = 0; // previous Arabic character int pc, nc, nc1; int pcc[MAX_MCO]; int need_redraw; @@ -5785,7 +5849,7 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, if (clear_next_cell) { clear_next_cell = false; } else if ((len < 0 ? ptr[mbyte_blen] == NUL - : ptr + mbyte_blen >= text + len) + : ptr + mbyte_blen >= text + len) && ((mbyte_cells == 1 && grid_off2cells(grid, off, max_off) > 1) || (mbyte_cells == 2 @@ -5884,10 +5948,11 @@ static void init_search_hl(win_T *wp) matchitem_T *cur = wp->w_match_head; while (cur != NULL) { cur->hl.rm = cur->match; - if (cur->hlg_id == 0) + if (cur->hlg_id == 0) { cur->hl.attr = 0; - else + } else { cur->hl.attr = syn_id2attr(cur->hlg_id); + } cur->hl.buf = wp->w_buffer; cur->hl.lnum = 0; cur->hl.first_lnum = 0; @@ -5910,7 +5975,7 @@ static void prepare_search_hl(win_T *wp, linenr_T lnum) FUNC_ATTR_NONNULL_ALL { matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match + match_T *shl; // points to search_hl or a match bool shl_flag; // flag to indicate whether search_hl // has been processed or not @@ -5941,8 +6006,8 @@ static void prepare_search_hl(win_T *wp, linenr_T lnum) if (cur != NULL) { cur->pos.cur = 0; } - bool pos_inprogress = true; // mark that a position match search is - // in progress + bool pos_inprogress = true; // mark that a position match search is + // in progress int n = 0; while (shl->first_lnum < lnum && (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))) { @@ -5960,27 +6025,24 @@ static void prepare_search_hl(win_T *wp, linenr_T lnum) } } } - if (shl != &search_hl && cur != NULL) + if (shl != &search_hl && cur != NULL) { cur = cur->next; + } } } -/* - * Search for a next 'hlsearch' or match. - * Uses shl->buf. - * Sets shl->lnum and shl->rm contents. - * Note: Assumes a previous match is always before "lnum", unless - * shl->lnum is zero. - * Careful: Any pointers for buffer lines will become invalid. - */ -static void -next_search_hl ( - win_T *win, - match_T *shl, /* points to search_hl or a match */ - linenr_T lnum, - colnr_T mincol, /* minimal column for a match */ - matchitem_T *cur /* to retrieve match positions if any */ -) +/// Search for a next 'hlsearch' or match. +/// Uses shl->buf. +/// Sets shl->lnum and shl->rm contents. +/// Note: Assumes a previous match is always before "lnum", unless +/// shl->lnum is zero. +/// Careful: Any pointers for buffer lines will become invalid. +/// +/// @param shl points to search_hl or a match +/// @param mincol minimal column for a match +/// @param cur to retrieve match positions if any +static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, colnr_T mincol, + matchitem_T *cur) FUNC_ATTR_NONNULL_ARG(2) { linenr_T l; @@ -6000,10 +6062,11 @@ next_search_hl ( // 2. If the previous match includes "mincol", use it. // 3. Continue after the previous match. l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; - if (lnum > l) + if (lnum > l) { shl->lnum = 0; - else if (lnum < l || shl->rm.endpos[0].col > mincol) + } else if (lnum < l || shl->rm.endpos[0].col > mincol) { return; + } } /* @@ -6014,7 +6077,7 @@ next_search_hl ( for (;; ) { // Stop searching after passing the time limit. if (profile_passed_limit(shl->tm)) { - shl->lnum = 0; /* no match found in time */ + shl->lnum = 0; // no match found in time break; } // Three situations: @@ -6027,10 +6090,10 @@ next_search_hl ( } else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL || (shl->rm.endpos[0].lnum == 0 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) { - char_u *ml; + char_u *ml; matchcol = shl->rm.startpos[0].col; - ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol; + ml = ml_get_buf(shl->buf, lnum, false) + matchcol; if (*ml == NUL) { ++matchcol; shl->lnum = 0; @@ -6066,7 +6129,7 @@ next_search_hl ( } shl->rm.regprog = NULL; shl->lnum = 0; - got_int = FALSE; // avoid the "Type :quit to exit Vim" message + got_int = FALSE; // avoid the "Type :quit to exit Vim" message break; } } else if (cur != NULL) { @@ -6089,15 +6152,12 @@ next_search_hl ( } } -/// If there is a match fill "shl" and return one. -/// Return zero otherwise. -static int -next_search_hl_pos( - match_T *shl, // points to a match - linenr_T lnum, - posmatch_T *posmatch, // match positions - colnr_T mincol // minimal column for a match -) +/// @param shl points to a match. Fill on match. +/// @param posmatch match positions +/// @param mincol minimal column for a match +/// +/// @return one on match, otherwise return zero. +static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch, colnr_T mincol) FUNC_ATTR_NONNULL_ALL { int i; @@ -6151,8 +6211,8 @@ next_search_hl_pos( /// Fill the grid from 'start_row' to 'end_row', from 'start_col' to 'end_col' /// with character 'c1' in first column followed by 'c2' in the other columns. /// Use attributes 'attr'. -void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, - int end_col, int c1, int c2, int attr) +void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1, + int c2, int attr) { schar_T sc; @@ -6390,9 +6450,9 @@ retry: msg_grid_invalid = true; } - win_new_shellsize(); /* fit the windows in the new sized shell */ + win_new_shellsize(); // fit the windows in the new sized shell - comp_col(); /* recompute columns for shown command and ruler */ + comp_col(); // recompute columns for shown command and ruler // We're changing the size of the screen. // - Allocate new arrays for default_grid @@ -6405,8 +6465,8 @@ retry: // size is wrong. grid_alloc(&default_grid, Rows, Columns, true, true); - StlClickDefinition *new_tab_page_click_defs = xcalloc( - (size_t)Columns, sizeof(*new_tab_page_click_defs)); + StlClickDefinition *new_tab_page_click_defs = + xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs)); clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); xfree(tab_page_click_defs); @@ -6514,8 +6574,7 @@ void screen_free_all_mem(void) /// /// @param[out] tpcd Table to clear. /// @param[in] tpcd_size Size of the table. -void clear_tab_page_click_defs(StlClickDefinition *const tpcd, - const long tpcd_size) +void clear_tab_page_click_defs(StlClickDefinition *const tpcd, const long tpcd_size) { if (tpcd != NULL) { for (long i = 0; i < tpcd_size; i++) { @@ -6523,7 +6582,7 @@ void clear_tab_page_click_defs(StlClickDefinition *const tpcd, xfree(tpcd[i].func); } } - memset(tpcd, 0, (size_t) tpcd_size * sizeof(tpcd[0])); + memset(tpcd, 0, (size_t)tpcd_size * sizeof(tpcd[0])); } } @@ -6628,8 +6687,8 @@ void setcursor(void) // 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); + - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2 + && vim_isprintc(gchar_cursor())) ? 2 : 1); } screen_adjust_grid(&grid, &row, &col); @@ -6678,8 +6737,7 @@ void win_scroll_lines(win_T *wp, int row, int line_count) /// 'col' is the column from with we start inserting. // /// 'row', 'col' and 'end' are relative to the start of the region. -void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, - int width) +void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width) { int i; int j; @@ -6730,8 +6788,7 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, /// 'end' is the line after the scrolled part. Normally it is Rows. /// When scrolling region used 'off' is the offset from the top for the region. /// 'row' and 'end' are relative to the start of the region. -void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, - int width) +void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width) { int j; int i; @@ -6782,8 +6839,8 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, // Show the current mode and ruler. // -// If clear_cmdline is TRUE, clear the rest of the cmdline. -// If clear_cmdline is FALSE there may be a message there that needs to be +// If clear_cmdline is true, clear the rest of the cmdline. +// If clear_cmdline is false there may be a message there that needs to be // cleared only if a mode is shown. // Return the length of the message (0 if no message). int showmode(void) @@ -6792,7 +6849,6 @@ int showmode(void) int length = 0; int do_mode; int attr; - int nwr_save; int sub_attr; if (ui_has(kUIMessages) && clear_cmdline) { @@ -6814,26 +6870,26 @@ int showmode(void) // Call char_avail() only when we are going to show something, because // it takes a bit of time. if (!redrawing() || (char_avail() && !KeyTyped) || msg_silent != 0) { - redraw_cmdline = TRUE; /* show mode later */ + redraw_cmdline = true; // show mode later return 0; } - nwr_save = need_wait_return; + bool nwr_save = need_wait_return; - /* wait a bit before overwriting an important message */ + // wait a bit before overwriting an important message check_for_delay(FALSE); - /* if the cmdline is more than one line high, erase top lines */ + // if the cmdline is more than one line high, erase top lines need_clear = clear_cmdline; if (clear_cmdline && cmdline_row < Rows - 1) { msg_clr_cmdline(); // will reset clear_cmdline } - /* Position on the last line in the window, column 0 */ + // Position on the last line in the window, column 0 msg_pos_mode(); attr = HL_ATTR(HLF_CM); // Highlight mode - // When the screen is too narrow to show the entire mode messsage, + // When the screen is too narrow to show the entire mode message, // avoid scrolling and truncate instead. msg_no_more = true; int save_lines_left = lines_left; @@ -6855,8 +6911,9 @@ int showmode(void) length -= vim_strsize(edit_submode_extra); } if (length > 0) { - if (edit_submode_pre != NULL) + if (edit_submode_pre != NULL) { length -= vim_strsize(edit_submode_pre); + } if (length - vim_strsize(edit_submode) > 0) { if (edit_submode_pre != NULL) { msg_puts_attr((const char *)edit_submode_pre, attr); @@ -6876,13 +6933,14 @@ int showmode(void) } else { if (State & TERM_FOCUS) { MSG_PUTS_ATTR(_(" TERMINAL"), attr); - } else if (State & VREPLACE_FLAG) + } else if (State & VREPLACE_FLAG) { MSG_PUTS_ATTR(_(" VREPLACE"), attr); - else if (State & REPLACE_FLAG) + } else if (State & REPLACE_FLAG) { MSG_PUTS_ATTR(_(" REPLACE"), attr); - else if (State & INSERT) { - if (p_ri) + } else if (State & INSERT) { + if (p_ri) { MSG_PUTS_ATTR(_(" REVERSE"), attr); + } MSG_PUTS_ATTR(_(" INSERT"), attr); } else if (restart_edit == 'I' || restart_edit == 'i' || restart_edit == 'a') { @@ -6903,8 +6961,9 @@ int showmode(void) MSG_PUTS_ATTR(NameBuff, attr); } } - if ((State & INSERT) && p_paste) + if ((State & INSERT) && p_paste) { MSG_PUTS_ATTR(_(" (paste)"), attr); + } if (VIsual_active) { char *p; @@ -6914,12 +6973,18 @@ int showmode(void) switch ((VIsual_select ? 4 : 0) + (VIsual_mode == Ctrl_V) * 2 + (VIsual_mode == 'V')) { - case 0: p = N_(" VISUAL"); break; - case 1: p = N_(" VISUAL LINE"); break; - case 2: p = N_(" VISUAL BLOCK"); break; - case 4: p = N_(" SELECT"); break; - case 5: p = N_(" SELECT LINE"); break; - default: p = N_(" SELECT BLOCK"); break; + case 0: + p = N_(" VISUAL"); break; + case 1: + p = N_(" VISUAL LINE"); break; + case 2: + p = N_(" VISUAL BLOCK"); break; + case 4: + p = N_(" SELECT"); break; + case 5: + p = N_(" SELECT LINE"); break; + default: + p = N_(" SELECT BLOCK"); break; } MSG_PUTS_ATTR(_(p), attr); } @@ -6935,10 +7000,11 @@ int showmode(void) need_clear = true; } - mode_displayed = TRUE; - if (need_clear || clear_cmdline) + mode_displayed = true; + if (need_clear || clear_cmdline) { 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; @@ -6949,12 +7015,13 @@ int showmode(void) msg_clr_cmdline(); } - // NB: also handles clearing the showmode if it was emtpy or disabled + // NB: also handles clearing the showmode if it was empty or disabled msg_ext_flush_showmode(); - /* In Visual mode the size of the selected area must be redrawn. */ - if (VIsual_active) + // In Visual mode the size of the selected area must be redrawn. + if (VIsual_active) { clear_showcmd(); + } // If the last window has no status line, the ruler is after the mode // message and must be redrawn @@ -7028,15 +7095,15 @@ void draw_tabline(void) int col = 0; int scol = 0; int attr; - win_T *wp; - win_T *cwp; + win_T *wp; + win_T *cwp; int wincount; int modified; int c; int len; int attr_nosel = HL_ATTR(HLF_TP); int attr_fill = HL_ATTR(HLF_TPF); - char_u *p; + char_u *p; int room; int use_sep_chars = (t_colors < 8 ); @@ -7051,15 +7118,16 @@ void draw_tabline(void) return; } - if (tabline_height() < 1) + if (tabline_height() < 1) { return; + } // Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect. assert(Columns == tab_page_click_defs_size); clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size); - /* Use the 'tabline' option if it's set. */ + // Use the 'tabline' option if it's set. if (*p_tal != NUL) { int saved_did_emsg = did_emsg; @@ -7145,7 +7213,7 @@ void draw_tabline(void) room = scol - col + tabwidth - 1; if (room > 0) { - /* Get buffer name in NameBuff[] */ + // Get buffer name in NameBuff[] get_trans_bufname(cwp->w_buffer); (void)shorten_dir(NameBuff); len = vim_strsize(NameBuff); @@ -7175,13 +7243,14 @@ void draw_tabline(void) } } - if (use_sep_chars) + if (use_sep_chars) { c = '_'; - else + } else { c = ' '; + } grid_fill(&default_grid, 0, 1, col, Columns, c, c, attr_fill); - /* Put an "X" for closing the current tab if there are several. */ + // Put an "X" for closing the current tab if there are several. if (first_tabpage->tp_next != NULL) { grid_putchar(&default_grid, 'X', 0, Columns - 1, attr_nosel); tab_page_click_defs[Columns - 1] = (StlClickDefinition) { @@ -7194,7 +7263,7 @@ void draw_tabline(void) /* Reset the flag here again, in case evaluating 'tabline' causes it to be * set. */ - redraw_tabline = FALSE; + redraw_tabline = false; } void ui_ext_tabline_update(void) @@ -7210,7 +7279,24 @@ void ui_ext_tabline_update(void) ADD(tabs, DICTIONARY_OBJ(tab_info)); } - ui_call_tabline_update(curtab->handle, tabs); + + Array buffers = ARRAY_DICT_INIT; + FOR_ALL_BUFFERS(buf) { + // Do not include unlisted buffers + if (!buf->b_p_bl) { + continue; + } + + Dictionary buffer_info = ARRAY_DICT_INIT; + PUT(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); + + get_trans_bufname(buf); + PUT(buffer_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff))); + + ADD(buffers, DICTIONARY_OBJ(buffer_info)); + } + + ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers); } /* @@ -7219,10 +7305,11 @@ void ui_ext_tabline_update(void) */ void get_trans_bufname(buf_T *buf) { - if (buf_spname(buf) != NULL) + if (buf_spname(buf) != NULL) { STRLCPY(NameBuff, buf_spname(buf), MAXPATHL); - else + } else { home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE); + } trans_characters(NameBuff, MAXPATHL); } @@ -7281,14 +7368,14 @@ int messaging(void) return !(p_lz && char_avail() && !KeyTyped); } -/* - * Show current status info in ruler and various other places - * If always is FALSE, only show ruler if position has changed. - */ -void showruler(int always) +/// Show current status info in ruler and various other places +/// +/// @param always if false, only show ruler if position has changed. +void showruler(bool always) { - if (!always && !redrawing()) + if (!always && !redrawing()) { return; + } if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) { redraw_custom_statusline(curwin); } else { @@ -7297,15 +7384,16 @@ void showruler(int always) if (need_maketitle || (p_icon && (stl_syntax & STL_IN_ICON)) - || (p_title && (stl_syntax & STL_IN_TITLE)) - ) + || (p_title && (stl_syntax & STL_IN_TITLE))) { maketitle(); - /* Redraw the tab pages line if needed. */ - if (redraw_tabline) + } + // Redraw the tab pages line if needed. + if (redraw_tabline) { draw_tabline(); + } } -static void win_redr_ruler(win_T *wp, int always) +static void win_redr_ruler(win_T *wp, bool always) { static bool did_show_ext_ruler = false; @@ -7318,8 +7406,9 @@ static void win_redr_ruler(win_T *wp, int always) * Check if cursor.lnum is valid, since win_redr_ruler() may be called * after deleting lines, before cursor.lnum is corrected. */ - if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) + if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) { return; + } // Don't draw the ruler while doing insert-completion, it might overwrite // the (long) mode message. @@ -7347,24 +7436,24 @@ static void win_redr_ruler(win_T *wp, int always) */ int empty_line = FALSE; if (!(State & INSERT) - && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL) + && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL) { empty_line = TRUE; + } /* * Only draw the ruler when something changed. */ validate_virtcol_win(wp); - if ( redraw_cmdline - || always - || wp->w_cursor.lnum != wp->w_ru_cursor.lnum - || wp->w_cursor.col != wp->w_ru_cursor.col - || wp->w_virtcol != wp->w_ru_virtcol - || wp->w_cursor.coladd != wp->w_ru_cursor.coladd - || wp->w_topline != wp->w_ru_topline - || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count - || wp->w_topfill != wp->w_ru_topfill - || empty_line != wp->w_ru_empty) { - + if (redraw_cmdline + || always + || wp->w_cursor.lnum != wp->w_ru_cursor.lnum + || wp->w_cursor.col != wp->w_ru_cursor.col + || wp->w_virtcol != wp->w_ru_virtcol + || wp->w_cursor.coladd != wp->w_ru_cursor.coladd + || wp->w_topline != wp->w_ru_topline + || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count + || wp->w_topfill != wp->w_ru_topfill + || empty_line != wp->w_ru_empty) { int width; int row; int fillchar; @@ -7402,12 +7491,12 @@ static void win_redr_ruler(win_T *wp, int always) * To avoid portability problems we use strlen() here. */ vim_snprintf((char *)buffer, RULER_BUF_LEN, "%" PRId64 ",", - (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? (int64_t)0L - : (int64_t)wp->w_cursor.lnum); + (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? (int64_t)0L + : (int64_t)wp->w_cursor.lnum); size_t len = STRLEN(buffer); col_print(buffer + len, RULER_BUF_LEN - len, - empty_line ? 0 : (int)wp->w_cursor.col + 1, - (int)virtcol + 1); + empty_line ? 0 : (int)wp->w_cursor.col + 1, + (int)virtcol + 1); /* * Add a "50%" if there is room for it. @@ -7495,8 +7584,9 @@ int number_width(win_T *wp) lnum = wp->w_buffer->b_ml.ml_line_count; } - if (lnum == wp->w_nrwidth_line_count) + if (lnum == wp->w_nrwidth_line_count) { return wp->w_nrwidth_width; + } wp->w_nrwidth_line_count = lnum; n = 0; @@ -7521,22 +7611,66 @@ int number_width(win_T *wp) return n; } +/// Used when 'cursorlineopt' contains "screenline": compute the margins between +/// which the highlighting is used. +static void margin_columns_win(win_T *wp, int *left_col, int *right_col) +{ + // cache previous calculations depending on w_virtcol + static int saved_w_virtcol; + static win_T *prev_wp; + static int prev_left_col; + static int prev_right_col; + static int prev_col_off; + + int cur_col_off = win_col_off(wp); + int width1; + int width2; + + if (saved_w_virtcol == wp->w_virtcol && prev_wp == wp + && prev_col_off == cur_col_off) { + *right_col = prev_right_col; + *left_col = prev_left_col; + return; + } + + width1 = wp->w_width - cur_col_off; + width2 = width1 + win_col_off2(wp); + + *left_col = 0; + *right_col = width1; + + if (wp->w_virtcol >= (colnr_T)width1) { + *right_col = width1 + ((wp->w_virtcol - width1) / width2 + 1) * width2; + } + if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) { + *left_col = (wp->w_virtcol - width1) / width2 * width2 + width1; + } + + // cache values + prev_left_col = *left_col; + prev_right_col = *right_col; + prev_wp = wp; + saved_w_virtcol = wp->w_virtcol; + prev_col_off = cur_col_off; +} + /// Set dimensions of the Nvim application "shell". void screen_resize(int width, int height) { - static int busy = FALSE; + static bool recursive = false; // Avoid recursiveness, can happen when setting the window size causes // another window-changed signal. - if (updating_screen || busy) { + if (updating_screen || recursive) { return; } - if (width < 0 || height < 0) /* just checking... */ + if (width < 0 || height < 0) { // just checking... return; + } if (State == HITRETURN || State == SETWSIZE) { - /* postpone the resizing */ + // postpone the resizing State = SETWSIZE; return; } @@ -7545,10 +7679,11 @@ void screen_resize(int width, int height) * buffer has already been closed and removing a scrollbar causes a resize * event. Don't resize then, it will happen after entering another buffer. */ - if (curwin->w_buffer == NULL) + if (curwin->w_buffer == NULL) { return; + } - ++busy; + recursive = true; Rows = height; Columns = width; @@ -7567,7 +7702,7 @@ void screen_resize(int width, int height) /* The window layout used to be adjusted here, but it now happens in * screenalloc() (also invoked from screenclear()). That is because the - * "busy" check above may skip this, but not screenalloc(). */ + * "recursive" check above may skip this, but not screenalloc(). */ if (State != ASKMORE && State != EXTERNCMD && State != CONFIRM) { screenclear(); @@ -7599,8 +7734,9 @@ void screen_resize(int width, int height) ui_comp_set_screen_valid(true); repeat_message(); } else { - if (curwin->w_p_scb) - do_check_scrollbind(TRUE); + if (curwin->w_p_scb) { + do_check_scrollbind(true); + } if (State & CMDLINE) { redraw_popupmenu = false; update_screen(NOT_VALID); @@ -7625,7 +7761,7 @@ void screen_resize(int width, int height) } ui_flush(); } - busy--; + recursive = false; } /// Check if the new Nvim application "shell" dimensions are valid. @@ -7683,4 +7819,3 @@ win_T *get_win_by_grid_handle(handle_T handle) return NULL; } - diff --git a/src/nvim/search.c b/src/nvim/search.c index 82fc0f9d8e..4be2402f1d 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -7,13 +7,11 @@ #include <assert.h> #include <inttypes.h> +#include <limits.h> // for INT_MAX on MSVC #include <stdbool.h> #include <string.h> -#include <limits.h> /* for INT_MAX on MSVC */ #include "nvim/ascii.h" -#include "nvim/vim.h" -#include "nvim/search.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -34,17 +32,19 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/move.h" #include "nvim/mouse.h" +#include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/os/time.h" #include "nvim/path.h" #include "nvim/regexp.h" #include "nvim/screen.h" +#include "nvim/search.h" #include "nvim/strings.h" #include "nvim/ui.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/os/time.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "search.c.generated.h" @@ -79,12 +79,12 @@ static struct spat spats[2] = { // Last used search pattern - [0] = {NULL, true, false, 0, {'/', false, false, 0L}, NULL}, + [0] = { NULL, true, false, 0, { '/', false, false, 0L }, NULL }, // Last used substitute pattern - [1] = {NULL, true, false, 0, {'/', false, false, 0L}, NULL} + [1] = { NULL, true, false, 0, { '/', false, false, 0L }, NULL } }; -static int last_idx = 0; /* index in spats[] for RE_LAST */ +static int last_idx = 0; // index in spats[] for RE_LAST static char_u lastc[2] = { NUL, NUL }; // last character searched for static Direction lastcdir = FORWARD; // last direction of character search @@ -97,100 +97,100 @@ static struct spat saved_spats[2]; static int saved_spats_last_idx = 0; static bool saved_spats_no_hlsearch = false; -static char_u *mr_pattern = NULL; // pattern used by search_regcomp() -static int mr_pattern_alloced = false; // mr_pattern was allocated +static char_u *mr_pattern = NULL; // pattern used by search_regcomp() +static bool mr_pattern_alloced = false; // mr_pattern was allocated /* * Type used by find_pattern_in_path() to remember which included files have * been searched already. */ typedef struct SearchedFile { - FILE *fp; /* File pointer */ - char_u *name; /* Full name of file */ - linenr_T lnum; /* Line we were up to in file */ - int matched; /* Found a match in this file */ + FILE *fp; // File pointer + char_u *name; // Full name of file + linenr_T lnum; // Line we were up to in file + int matched; // Found a match in this file } SearchedFile; -/* - * translate search pattern for vim_regcomp() - * - * pat_save == RE_SEARCH: save pat in spats[RE_SEARCH].pat (normal search cmd) - * pat_save == RE_SUBST: save pat in spats[RE_SUBST].pat (:substitute command) - * pat_save == RE_BOTH: save pat in both patterns (:global command) - * pat_use == RE_SEARCH: use previous search pattern if "pat" is NULL - * pat_use == RE_SUBST: use previous substitute pattern if "pat" is NULL - * pat_use == RE_LAST: use last used pattern if "pat" is NULL - * options & SEARCH_HIS: put search string in history - * options & SEARCH_KEEP: keep previous search pattern - * - * returns FAIL if failed, OK otherwise. - */ -int -search_regcomp( - char_u *pat, - int pat_save, - int pat_use, - int options, - regmmatch_T *regmatch /* return: pattern and ignore-case flag */ -) +/// translate search pattern for vim_regcomp() +/// +/// pat_save == RE_SEARCH: save pat in spats[RE_SEARCH].pat (normal search cmd) +/// pat_save == RE_SUBST: save pat in spats[RE_SUBST].pat (:substitute command) +/// pat_save == RE_BOTH: save pat in both patterns (:global command) +/// pat_use == RE_SEARCH: use previous search pattern if "pat" is NULL +/// pat_use == RE_SUBST: use previous substitute pattern if "pat" is NULL +/// pat_use == RE_LAST: use last used pattern if "pat" is NULL +/// options & SEARCH_HIS: put search string in history +/// options & SEARCH_KEEP: keep previous search pattern +/// +/// @param regmatch return: pattern and ignore-case flag +/// +/// @return FAIL if failed, OK otherwise. +int search_regcomp(char_u *pat, int pat_save, int pat_use, int options, regmmatch_T *regmatch) { int magic; int i; - rc_did_emsg = FALSE; + rc_did_emsg = false; magic = p_magic; /* * If no pattern given, use a previously defined pattern. */ if (pat == NULL || *pat == NUL) { - if (pat_use == RE_LAST) + if (pat_use == RE_LAST) { i = last_idx; - else + } else { i = pat_use; - if (spats[i].pat == NULL) { /* pattern was never defined */ - if (pat_use == RE_SUBST) + } + if (spats[i].pat == NULL) { // pattern was never defined + if (pat_use == RE_SUBST) { EMSG(_(e_nopresub)); - else + } else { EMSG(_(e_noprevre)); - rc_did_emsg = TRUE; + } + rc_did_emsg = true; return FAIL; } pat = spats[i].pat; 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); + } else if (options & SEARCH_HIS) { // put new pattern in history + add_to_history(HIST_SEARCH, pat, true, NUL); + } if (mr_pattern_alloced) { xfree(mr_pattern); - mr_pattern_alloced = FALSE; + mr_pattern_alloced = false; } if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { mr_pattern = reverse_text(pat); - mr_pattern_alloced = TRUE; - } else + mr_pattern_alloced = true; + } else { mr_pattern = pat; + } /* * Save the currently used pattern in the appropriate place, * unless the pattern should not be remembered. */ if (!(options & SEARCH_KEEP) && !cmdmod.keeppatterns) { - /* search or global command */ - if (pat_save == RE_SEARCH || pat_save == RE_BOTH) + // search or global command + if (pat_save == RE_SEARCH || pat_save == RE_BOTH) { save_re_pat(RE_SEARCH, pat, magic); - /* substitute or global command */ - if (pat_save == RE_SUBST || pat_save == RE_BOTH) + } + // substitute or global command + if (pat_save == RE_SUBST || pat_save == RE_BOTH) { save_re_pat(RE_SUBST, pat, magic); + } } regmatch->rmm_ic = ignorecase(pat); regmatch->rmm_maxcol = 0; regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0); - if (regmatch->regprog == NULL) + if (regmatch->regprog == NULL) { return FAIL; + } return OK; } @@ -237,9 +237,10 @@ void save_re_pat(int idx, char_u *pat, int magic) spats[idx].timestamp = os_time(); spats[idx].additional_data = NULL; last_idx = idx; - /* If 'hlsearch' set and search pat changed: need redraw. */ - if (p_hls) + // If 'hlsearch' set and search pat changed: need redraw. + if (p_hls) { redraw_all_later(SOME_VALID); + } set_no_hlsearch(false); } } @@ -254,11 +255,13 @@ void save_search_patterns(void) { if (save_level++ == 0) { saved_spats[0] = spats[0]; - if (spats[0].pat != NULL) + if (spats[0].pat != NULL) { saved_spats[0].pat = vim_strsave(spats[0].pat); + } saved_spats[1] = spats[1]; - if (spats[1].pat != NULL) + if (spats[1].pat != NULL) { saved_spats[1].pat = vim_strsave(spats[1].pat); + } saved_spats_last_idx = last_idx; saved_spats_no_hlsearch = no_hlsearch; } @@ -293,7 +296,7 @@ void free_search_patterns(void) if (mr_pattern_alloced) { xfree(mr_pattern); - mr_pattern_alloced = FALSE; + mr_pattern_alloced = false; mr_pattern = NULL; } } @@ -367,8 +370,8 @@ int ignorecase_opt(char_u *pat, int ic_in, int scs) { int ic = ic_in; if (ic && !no_smartcase && scs - && !(ctrl_x_mode_not_default() && curbuf->b_p_inf) - ) { + && !(ctrl_x_mode_not_default() && + curbuf->b_p_inf)) { ic = !pat_has_uppercase(pat); } no_smartcase = false; @@ -429,10 +432,11 @@ void set_last_csearch(int c, char_u *s, int len) { *lastc = c; lastc_bytelen = len; - if (len) + if (len) { memcpy(lastc_bytes, s, len); - else + } else { memset(lastc_bytes, 0, sizeof(lastc_bytes)); + } } void set_csearch_direction(Direction cdir) @@ -466,34 +470,38 @@ void reset_search_dir(void) void set_last_search_pat(const char_u *s, int idx, int magic, int setlast) { free_spat(&spats[idx]); - /* An empty string means that nothing should be matched. */ - if (*s == NUL) + // An empty string means that nothing should be matched. + if (*s == NUL) { spats[idx].pat = NULL; - else - spats[idx].pat = (char_u *) xstrdup((char *) s); + } else { + spats[idx].pat = (char_u *)xstrdup((char *)s); + } spats[idx].timestamp = os_time(); spats[idx].additional_data = NULL; spats[idx].magic = magic; - spats[idx].no_scs = FALSE; + spats[idx].no_scs = false; spats[idx].off.dir = '/'; set_vv_searchforward(); spats[idx].off.line = FALSE; spats[idx].off.end = FALSE; spats[idx].off.off = 0; - if (setlast) + if (setlast) { last_idx = idx; + } if (save_level) { free_spat(&saved_spats[idx]); saved_spats[idx] = spats[0]; - if (spats[idx].pat == NULL) + if (spats[idx].pat == NULL) { saved_spats[idx].pat = NULL; - else + } else { saved_spats[idx].pat = vim_strsave(spats[idx].pat); + } saved_spats_last_idx = last_idx; } - /* If 'hlsearch' set and search pat changed: need redraw. */ - if (p_hls && idx == last_idx && !no_hlsearch) + // If 'hlsearch' set and search pat changed: need redraw. + if (p_hls && idx == last_idx && !no_hlsearch) { redraw_all_later(SOME_VALID); + } } /* @@ -507,7 +515,7 @@ void last_pat_prog(regmmatch_T *regmatch) regmatch->regprog = NULL; return; } - ++emsg_off; /* So it doesn't beep if bad expr */ + ++emsg_off; // So it doesn't beep if bad expr (void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch); --emsg_off; } @@ -527,27 +535,21 @@ void last_pat_prog(regmmatch_T *regmatch) /// if (options & SEARCH_PEEK) check for typed char, cancel search /// if (options & SEARCH_COL) start at pos->col instead of zero /// -/// @returns FAIL (zero) for failure, non-zero for success. -/// the index of the first matching -/// subpattern plus one; one if there was none. -int searchit( - win_T *win, // window to search in; can be NULL for a - // buffer without a window! - buf_T *buf, - pos_T *pos, - pos_T *end_pos, // set to end of the match, unless NULL - Direction dir, - char_u *pat, - long count, - int options, - int pat_use, // which pattern to use when "pat" is empty - searchit_arg_T *extra_arg // optional extra arguments, can be NULL -) +/// @param win window to search in; can be NULL for a buffer without a window! +/// @param end_pos set to end of the match, unless NULL +/// @param pat_use which pattern to use when "pat" is empty +/// @param extra_arg optional extra arguments, can be NULL +/// +/// @returns FAIL (zero) for failure, non-zero for success. +/// 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_u *pat, + long count, int options, int pat_use, searchit_arg_T *extra_arg) { int found; - linenr_T lnum; /* no init to shut up Apollo cc */ + linenr_T lnum; // no init to shut up Apollo cc regmmatch_T regmatch; - char_u *ptr; + char_u *ptr; colnr_T matchcol; lpos_T endpos; lpos_T matchpos; @@ -556,26 +558,27 @@ int searchit( int at_first_line; int extra_col; int start_char_len; - int match_ok; + bool match_ok; long nmatched; int submatch = 0; bool first_match = true; int save_called_emsg = called_emsg; - int break_loop = false; + bool break_loop = false; linenr_T stop_lnum = 0; // stop after this line number when != 0 proftime_T *tm = NULL; // timeout limit or NULL int *timed_out = NULL; // set when timed out or NULL if (extra_arg != NULL) { - stop_lnum = extra_arg->sa_stop_lnum; - tm = extra_arg->sa_tm; - timed_out = &extra_arg->sa_timed_out; + stop_lnum = extra_arg->sa_stop_lnum; + tm = extra_arg->sa_tm; + timed_out = &extra_arg->sa_timed_out; } if (search_regcomp(pat, RE_SEARCH, pat_use, - (options & (SEARCH_HIS + SEARCH_KEEP)), ®match) == FAIL) { - if ((options & SEARCH_MSG) && !rc_did_emsg) + (options & (SEARCH_HIS + SEARCH_KEEP)), ®match) == FAIL) { + if ((options & SEARCH_MSG) && !rc_did_emsg) { EMSG2(_("E383: Invalid search string: %s"), mr_pattern); + } return FAIL; } @@ -583,7 +586,7 @@ int searchit( * find the string */ called_emsg = FALSE; - do { /* loop for count */ + do { // loop for count // When not accepting a match at the start position set "extra_col" to a // non-zero value. Don't do that when starting at MAXCOL, since MAXCOL + 1 // is zero. @@ -608,13 +611,13 @@ int searchit( extra_col = (options & SEARCH_START) ? start_char_len : 0; } - start_pos = *pos; /* remember start pos for detecting no match */ - found = 0; /* default: not found */ - at_first_line = TRUE; /* default: start in first line */ - if (pos->lnum == 0) { /* correct lnum for when starting in line 0 */ + start_pos = *pos; // remember start pos for detecting no match + found = 0; // default: not found + at_first_line = TRUE; // default: start in first line + if (pos->lnum == 0) { // correct lnum for when starting in line 0 pos->lnum = 1; pos->col = 0; - at_first_line = FALSE; /* not in first line now */ + at_first_line = FALSE; // not in first line now } /* @@ -628,19 +631,22 @@ int searchit( && (options & SEARCH_START) == 0) { lnum = pos->lnum - 1; at_first_line = FALSE; - } else + } else { lnum = pos->lnum; + } - for (loop = 0; loop <= 1; ++loop) { /* loop twice if 'wrapscan' set */ + for (loop = 0; loop <= 1; ++loop) { // loop twice if 'wrapscan' set for (; lnum > 0 && lnum <= buf->b_ml.ml_line_count; lnum += dir, at_first_line = FALSE) { - /* Stop after checking "stop_lnum", if it's set. */ + // Stop after checking "stop_lnum", if it's set. if (stop_lnum != 0 && (dir == FORWARD - ? lnum > stop_lnum : lnum < stop_lnum)) + ? lnum > stop_lnum : lnum < stop_lnum)) { break; - /* Stop after passing the "tm" time limit. */ - if (tm != NULL && profile_passed_limit(*tm)) + } + // Stop after passing the "tm" time limit. + if (tm != NULL && profile_passed_limit(*tm)) { break; + } // Look for a match somewhere in line "lnum". colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0; @@ -655,15 +661,16 @@ int searchit( break; } if (nmatched > 0) { - /* match may actually be in another line when using \zs */ + // match may actually be in another line when using \zs matchpos = regmatch.startpos[0]; endpos = regmatch.endpos[0]; submatch = first_submatch(®match); - /* "lnum" may be past end of buffer for "\n\zs". */ - if (lnum + matchpos.lnum > buf->b_ml.ml_line_count) + // "lnum" may be past end of buffer for "\n\zs". + if (lnum + matchpos.lnum > buf->b_ml.ml_line_count) { ptr = (char_u *)""; - else - ptr = ml_get_buf(buf, lnum + matchpos.lnum, FALSE); + } else { + ptr = ml_get_buf(buf, lnum + matchpos.lnum, false); + } /* * Forward search in the first line: match should be after @@ -671,14 +678,12 @@ int searchit( * match (this is vi compatible) or on the next char. */ if (dir == FORWARD && at_first_line) { - match_ok = TRUE; - /* - * When the match starts in a next line it's certainly - * past the start position. - * When match lands on a NUL the cursor will be put - * one back afterwards, compare with that position, - * otherwise "/$" will get stuck on end of line. - */ + match_ok = true; + // When the match starts in a next line it's certainly + // past the start position. + // When match lands on a NUL the cursor will be put + // one back afterwards, compare with that position, + // otherwise "/$" will get stuck on end of line. while (matchpos.lnum == 0 && (((options & SEARCH_END) && first_match) ? (nmatched == 1 @@ -696,7 +701,7 @@ int searchit( if (nmatched > 1) { /* end is in next line, thus no match in * this line */ - match_ok = FALSE; + match_ok = false; break; } matchcol = endpos.col; @@ -739,8 +744,9 @@ int searchit( // have made it invalid. ptr = ml_get_buf(buf, lnum, false); } - if (!match_ok) + if (!match_ok) { continue; + } } if (dir == BACKWARD) { /* @@ -750,7 +756,7 @@ int searchit( * When putting the new cursor at the end, compare * relative to the end of the match. */ - match_ok = FALSE; + match_ok = false; for (;; ) { /* Remember a position that is before the start * position, we use it if it's the last match in @@ -774,8 +780,9 @@ int searchit( matchpos = regmatch.startpos[0]; endpos = regmatch.endpos[0]; submatch = first_submatch(®match); - } else + } else { break; + } // We found a valid match, now check if there is // another one after it. @@ -803,16 +810,16 @@ int searchit( } } if (ptr[matchcol] == NUL - || (nmatched = vim_regexec_multi( - ®match, win, buf, lnum + matchpos.lnum, matchcol, - tm, timed_out)) == 0) { - // If the search timed out, we did find a match - // but it might be the wrong one, so that's not - // OK. - if (tm != NULL && profile_passed_limit(*tm)) { - match_ok = false; - } - break; + || (nmatched = + vim_regexec_multi(®match, win, buf, lnum + matchpos.lnum, matchcol, + tm, timed_out)) == 0) { + // If the search timed out, we did find a match + // but it might be the wrong one, so that's not + // OK. + if (tm != NULL && profile_passed_limit(*tm)) { + match_ok = false; + } + break; } // vim_regexec_multi() may clear "regprog" if (regmatch.regprog == NULL) { @@ -827,8 +834,9 @@ int searchit( * If there is only a match after the cursor, skip * this match. */ - if (!match_ok) + if (!match_ok) { continue; + } } /* With the SEARCH_END option move to the last character @@ -842,10 +850,9 @@ int searchit( pos->lnum = lnum + endpos.lnum; pos->col = endpos.col; if (endpos.col == 0) { - if (pos->lnum > 1) { /* just in case */ - --pos->lnum; - pos->col = (colnr_T)STRLEN(ml_get_buf(buf, - pos->lnum, FALSE)); + if (pos->lnum > 1) { // just in case + pos->lnum--; + pos->col = (colnr_T)STRLEN(ml_get_buf(buf, pos->lnum, false)); } } else { pos->col--; @@ -873,14 +880,15 @@ int searchit( found = 1; first_match = false; - /* Set variables used for 'incsearch' highlighting. */ + // Set variables used for 'incsearch' highlighting. search_match_lines = endpos.lnum - matchpos.lnum; search_match_endcol = endpos.col; break; } - line_breakcheck(); /* stop if ctrl-C typed */ - if (got_int) + line_breakcheck(); // stop if ctrl-C typed + if (got_int) { break; + } /* Cancel searching if a character was typed. Used for * 'incsearch'. Don't check too often, that would slowdown @@ -888,12 +896,13 @@ int searchit( if ((options & SEARCH_PEEK) && ((lnum - pos->lnum) & 0x3f) == 0 && char_avail()) { - break_loop = TRUE; + break_loop = true; break; } - if (loop && lnum == start_pos.lnum) - break; /* if second loop, stop where started */ + if (loop && lnum == start_pos.lnum) { + break; // if second loop, stop where started + } } at_first_line = FALSE; @@ -933,8 +942,7 @@ int searchit( } if (got_int || called_emsg || (timed_out != NULL && *timed_out) - || break_loop - ) { + || break_loop) { break; } } while (--count > 0 && found); // stop after count matches or no match @@ -943,28 +951,30 @@ int searchit( called_emsg |= save_called_emsg; - if (!found) { /* did not find it */ - if (got_int) + if (!found) { // did not find it + if (got_int) { EMSG(_(e_interr)); - else if ((options & SEARCH_MSG) == SEARCH_MSG) { - if (p_ws) + } else if ((options & SEARCH_MSG) == SEARCH_MSG) { + if (p_ws) { EMSG2(_(e_patnotf2), mr_pattern); - else if (lnum == 0) + } else if (lnum == 0) { EMSG2(_("E384: search hit TOP without match for: %s"), - mr_pattern); - else + mr_pattern); + } else { EMSG2(_("E385: search hit BOTTOM without match for: %s"), - mr_pattern); + mr_pattern); + } } return FAIL; } - /* A pattern like "\n\zs" may go past the last line. */ + // 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, FALSE)); - if (pos->col > 0) - --pos->col; + pos->col = (int)STRLEN(ml_get_buf(buf, pos->lnum, false)); + if (pos->col > 0) { + pos->col--; + } } return submatch + 1; @@ -987,8 +997,9 @@ static int first_submatch(regmmatch_T *rp) int submatch; for (submatch = 1;; ++submatch) { - if (rp->startpos[submatch].lnum >= 0) + if (rp->startpos[submatch].lnum >= 0) { break; + } if (submatch == 9) { submatch = 0; break; @@ -997,47 +1008,44 @@ static int first_submatch(regmmatch_T *rp) return submatch; } -/* - * Highest level string search function. - * Search for the 'count'th occurrence of pattern 'pat' in direction 'dirc' - * If 'dirc' is 0: use previous dir. - * If 'pat' is NULL or empty : use previous string. - * If 'options & SEARCH_REV' : go in reverse of previous dir. - * If 'options & SEARCH_ECHO': echo the search command and handle options - * If 'options & SEARCH_MSG' : may give error message - * If 'options & SEARCH_OPT' : interpret optional flags - * If 'options & SEARCH_HIS' : put search pattern in history - * If 'options & SEARCH_NOOF': don't add offset to position - * If 'options & SEARCH_MARK': set previous context mark - * If 'options & SEARCH_KEEP': keep previous search pattern - * If 'options & SEARCH_START': accept match at curpos itself - * If 'options & SEARCH_PEEK': check for typed char, cancel search - * - * Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this - * makes the movement linewise without moving the match position. - * - * Return 0 for failure, 1 for found, 2 for found and line offset added. - */ -int do_search( - oparg_T *oap, // can be NULL - int dirc, // '/' or '?' - int search_delim, // delimiter for search, e.g. '%' in s%regex%replacement - char_u *pat, - long count, - int options, - searchit_arg_T *sia // optional arguments or NULL -) +/// Highest level string search function. +/// Search for the 'count'th occurrence of pattern 'pat' in direction 'dirc' +/// +/// Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this +/// makes the movement linewise without moving the match position. +/// +/// @param dirc if 0: use previous dir. +/// @param pat NULL or empty : use previous string. +/// @param options if TRUE and +/// SEARCH_REV == TRUE : go in reverse of previous dir. +/// SEARCH_ECHO == TRUE : echo the search command and handle options +/// SEARCH_MSG == TRUE : may give error message +/// SEARCH_OPT == TRUE : interpret optional flags +/// SEARCH_HIS == TRUE : put search pattern in history +/// SEARCH_NOOF == TRUE : don't add offset to position +/// SEARCH_MARK == TRUE : set previous context mark +/// SEARCH_KEEP == TRUE : keep previous search pattern +/// SEARCH_START == TRUE : accept match at curpos itself +/// SEARCH_PEEK == TRUE : check for typed char, cancel search +/// @param oap can be NULL +/// @param dirc '/' or '?' +/// @param search_delim delimiter for search, e.g. '%' in s%regex%replacement +/// @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_u *pat, long count, int options, + searchit_arg_T *sia) { - pos_T pos; /* position of the last match */ - char_u *searchstr; + pos_T pos; // position of the last match + char_u *searchstr; struct soffset old_off; - int retval; /* Return value */ - char_u *p; + int retval; // Return value + char_u *p; long c; - char_u *dircp; - char_u *strcopy = NULL; - char_u *ps; - char_u *msgbuf = NULL; + char_u *dircp; + char_u *strcopy = NULL; + char_u *ps; + char_u *msgbuf = NULL; size_t len; bool has_offset = false; @@ -1055,32 +1063,35 @@ int do_search( */ old_off = spats[0].off; - pos = curwin->w_cursor; /* start searching at the cursor position */ + pos = curwin->w_cursor; // start searching at the cursor position /* * Find out the direction of the search. */ - if (dirc == 0) + if (dirc == 0) { dirc = spats[0].off.dir; - else { + } else { spats[0].off.dir = dirc; set_vv_searchforward(); } if (options & SEARCH_REV) { - if (dirc == '/') + if (dirc == '/') { dirc = '?'; - else + } else { dirc = '/'; + } } /* 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)) - pos.col = MAXCOL - 2; /* avoid overflow when adding 1 */ + if (hasFolding(pos.lnum, NULL, &pos.lnum)) { + pos.col = MAXCOL - 2; // avoid overflow when adding 1 + } } else { - if (hasFolding(pos.lnum, &pos.lnum, NULL)) + if (hasFolding(pos.lnum, &pos.lnum, NULL)) { pos.col = 0; + } } /* @@ -1109,12 +1120,12 @@ int do_search( goto end_do_search; } } else { - /* make search_regcomp() use spats[RE_SEARCH].pat */ + // make search_regcomp() use spats[RE_SEARCH].pat searchstr = (char_u *)""; } } - if (pat != NULL && *pat != NUL) { /* look for (new) offset */ + if (pat != NULL && *pat != NUL) { // look for (new) offset /* * Find end of regular expression. * If there is a matching '/' or '?', toss it. @@ -1122,7 +1133,7 @@ int do_search( ps = strcopy; p = skip_regexp(pat, search_delim, p_magic, &strcopy); if (strcopy != ps) { - /* made a copy of "pat" to change "\?" to "?" */ + // made a copy of "pat" to change "\?" to "?" searchcmdlen += (int)(STRLEN(pat) - STRLEN(strcopy)); pat = strcopy; searchstr = strcopy; @@ -1147,28 +1158,30 @@ int do_search( } p++; } - if (ascii_isdigit(*p) || *p == '+' || *p == '-') { /* got an offset */ - /* 'nr' or '+nr' or '-nr' */ - if (ascii_isdigit(*p) || ascii_isdigit(*(p + 1))) + if (ascii_isdigit(*p) || *p == '+' || *p == '-') { // got an offset + // 'nr' or '+nr' or '-nr' + if (ascii_isdigit(*p) || ascii_isdigit(*(p + 1))) { spats[0].off.off = atol((char *)p); - else if (*p == '-') /* single '-' */ + } else if (*p == '-') { // single '-' spats[0].off.off = -1; - else /* single '+' */ + } else { // single '+' spats[0].off.off = 1; + } ++p; - while (ascii_isdigit(*p)) /* skip number */ + while (ascii_isdigit(*p)) { // skip number ++p; + } } - /* compute length of search command for get_address() */ + // compute length of search command for get_address() searchcmdlen += (int)(p - pat); - pat = p; /* put pat after search command */ + pat = p; // put pat after search command } if ((options & SEARCH_ECHO) && messaging() && !msg_silent && (!cmd_silent || !shortmess(SHM_SEARCHCOUNT))) { - char_u *trunc; + char_u *trunc; char_u off_buf[40]; size_t off_len = 0; @@ -1290,18 +1303,22 @@ int do_search( */ if (!spats[0].off.line && spats[0].off.off && pos.col < MAXCOL - 2) { if (spats[0].off.off > 0) { - for (c = spats[0].off.off; c; --c) - if (decl(&pos) == -1) + for (c = spats[0].off.off; c; --c) { + if (decl(&pos) == -1) { break; - if (c) { /* at start of buffer */ - pos.lnum = 0; /* allow lnum == 0 here */ + } + } + if (c) { // at start of buffer + pos.lnum = 0; // allow lnum == 0 here pos.col = MAXCOL; } } else { - for (c = spats[0].off.off; c; ++c) - if (incl(&pos) == -1) + for (c = spats[0].off.off; c; ++c) { + if (incl(&pos) == -1) { break; - if (c) { /* at end of buffer */ + } + } + if (c) { // at end of buffer pos.lnum = curbuf->b_ml.ml_line_count + 1; pos.col = 0; } @@ -1331,10 +1348,10 @@ int do_search( retval = 0; goto end_do_search; } - if (spats[0].off.end && oap != NULL) - oap->inclusive = true; /* 'e' includes last character */ - - retval = 1; /* pattern found */ + if (spats[0].off.end && oap != NULL) { + oap->inclusive = true; // 'e' includes last character + } + retval = 1; // pattern found /* * Add character and/or line offset @@ -1344,28 +1361,33 @@ int do_search( if (spats[0].off.line) { // Add the offset to the line number. c = pos.lnum + spats[0].off.off; - if (c < 1) + if (c < 1) { pos.lnum = 1; - else if (c > curbuf->b_ml.ml_line_count) + } else if (c > curbuf->b_ml.ml_line_count) { pos.lnum = curbuf->b_ml.ml_line_count; - else + } else { pos.lnum = c; + } pos.col = 0; - retval = 2; /* pattern found, line offset added */ - } else if (pos.col < MAXCOL - 2) { /* just in case */ - /* to the right, check for end of file */ + retval = 2; // pattern found, line offset added + } else if (pos.col < MAXCOL - 2) { // just in case + // to the right, check for end of file c = spats[0].off.off; if (c > 0) { - while (c-- > 0) - if (incl(&pos) == -1) + while (c-- > 0) { + if (incl(&pos) == -1) { break; + } + } } - /* to the left, check for start of file */ + // to the left, check for start of file else { - while (c++ < 0) - if (decl(&pos) == -1) + while (c++ < 0) { + if (decl(&pos) == -1) { break; + } + } } } if (!equalpos(pos, org_pos)) { @@ -1410,14 +1432,16 @@ int do_search( ++pat; } - if (options & SEARCH_MARK) + if (options & SEARCH_MARK) { setpcmark(); + } curwin->w_cursor = pos; curwin->w_set_curswant = TRUE; end_do_search: - if ((options & SEARCH_KEEP) || cmdmod.keeppatterns) + if ((options & SEARCH_KEEP) || cmdmod.keeppatterns) { spats[0].off = old_off; + } xfree(msgbuf); return retval; @@ -1435,18 +1459,20 @@ end_do_search: int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat) { linenr_T start = 0; - char_u *ptr; - char_u *p; + char_u *ptr; + char_u *p; - if (buf->b_ml.ml_line_count == 0) + if (buf->b_ml.ml_line_count == 0) { return FAIL; + } for (;; ) { pos->lnum += dir; if (pos->lnum < 1) { if (p_ws) { pos->lnum = buf->b_ml.ml_line_count; - if (!shortmess(SHM_SEARCH)) + if (!shortmess(SHM_SEARCH)) { give_warning((char_u *)_(top_bot_msg), true); + } } else { pos->lnum = 1; break; @@ -1454,20 +1480,23 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat) } else if (pos->lnum > buf->b_ml.ml_line_count) { if (p_ws) { pos->lnum = 1; - if (!shortmess(SHM_SEARCH)) + if (!shortmess(SHM_SEARCH)) { give_warning((char_u *)_(bot_top_msg), true); + } } else { pos->lnum = 1; break; } } - if (pos->lnum == start) + if (pos->lnum == start) { break; - if (start == 0) + } + if (start == 0) { start = pos->lnum; - ptr = ml_get_buf(buf, pos->lnum, FALSE); + } + ptr = ml_get_buf(buf, pos->lnum, false); p = skipwhite(ptr); - pos->col = (colnr_T) (p - ptr); + pos->col = (colnr_T)(p - ptr); /* when adding lines the matching line may be empty but it is not * ignored because we are interested in the next line -- Acevedo */ @@ -1480,8 +1509,9 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat) // Expanding lines or words. assert(compl_length >= 0); if ((p_ic ? mb_strnicmp(p, pat, (size_t)compl_length) - : STRNCMP(p, pat, compl_length)) == 0) + : STRNCMP(p, pat, compl_length)) == 0) { return OK; + } } } return FAIL; @@ -1501,15 +1531,15 @@ int searchc(cmdarg_T *cap, int t_cmd) FUNC_ATTR_NONNULL_ALL { int c = cap->nchar; // char to search for - Direction dir = cap->arg; // TRUE for searching forward + int dir = cap->arg; // true for searching forward long count = cap->count1; // repeat count int col; - char_u *p; + char_u *p; int len; - int stop = TRUE; + bool stop = true; - if (c != NUL) { /* normal search: remember args for repeat */ - if (!KeyStuffed) { /* don't remember when redoing */ + if (c != NUL) { // normal search: remember args for repeat + if (!KeyStuffed) { // don't remember when redoing *lastc = c; set_csearch_direction(dir); set_csearch_until(t_cmd); @@ -1534,19 +1564,21 @@ int searchc(cmdarg_T *cap, int t_cmd) } t_cmd = last_t_cmd; c = *lastc; - /* For multi-byte re-use last lastc_bytes[] and lastc_bytelen. */ + // For multi-byte re-use last lastc_bytes[] and lastc_bytelen. /* Force a move of at least one char, so ";" and "," will move the * cursor, even if the cursor is right in front of char we are looking * at. */ - if (vim_strchr(p_cpo, CPO_SCOLON) == NULL && count == 1 && t_cmd) - stop = FALSE; + if (vim_strchr(p_cpo, CPO_SCOLON) == NULL && count == 1 && t_cmd) { + stop = false; + } } - if (dir == BACKWARD) + if (dir == BACKWARD) { cap->oap->inclusive = false; - else + } else { cap->oap->inclusive = true; + } p = get_cursor_line_ptr(); col = curwin->w_cursor.col; @@ -1620,7 +1652,7 @@ static bool check_prevcol(char_u *linep, int col, int ch, int *prevcol) if (prevcol) { *prevcol = col; } - return (col >= 0 && linep[col] == ch) ? true : false; + return col >= 0 && linep[col] == ch; } /* @@ -1665,8 +1697,7 @@ static bool find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos) /// If there is a match set "*initc" to the matching character and "*findc" to /// the opposite character. Set "*backwards" to the direction. /// When "switchit" is true swap the direction. -static void find_mps_values(int *initc, int *findc, bool *backwards, - bool switchit) +static void find_mps_values(int *initc, int *findc, bool *backwards, bool switchit) FUNC_ATTR_NONNULL_ALL { char_u *ptr = curbuf->b_p_mps; @@ -1732,7 +1763,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) bool backwards = false; // init for gcc bool raw_string = false; // search for raw string bool inquote = false; // true when inside quotes - char_u *ptr; + char_u *ptr; int hash_dir = 0; // Direction searched for # things int comment_dir = 0; // Direction searched for comments int traveled = 0; // how far we've searched so far @@ -1752,13 +1783,14 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) // don't recognize backslashes bool cpo_bsl = (vim_strchr(p_cpo, CPO_MATCHBSL) != NULL); - /* Direction to search when initc is '/', '*' or '#' */ - if (flags & FM_BACKWARD) + // Direction to search when initc is '/', '*' or '#' + if (flags & FM_BACKWARD) { dir = BACKWARD; - else if (flags & FM_FORWARD) + } else if (flags & FM_FORWARD) { dir = FORWARD; - else + } else { dir = 0; + } /* * if initc given, look in the table for the matching character @@ -1768,8 +1800,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) */ if (initc == '/' || initc == '*' || initc == 'R') { comment_dir = dir; - if (initc == '/') + if (initc == '/') { ignore_cend = true; + } backwards = (dir == FORWARD) ? false : true; raw_string = (initc == 'R'); initc = NUL; @@ -1792,16 +1825,17 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) * Only check for special things when 'cpo' doesn't have '%'. */ if (!cpo_match) { - /* Are we before or at #if, #else etc.? */ + // Are we before or at #if, #else etc.? ptr = skipwhite(linep); if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep)) { ptr = skipwhite(ptr + 1); - if ( STRNCMP(ptr, "if", 2) == 0 - || STRNCMP(ptr, "endif", 5) == 0 - || STRNCMP(ptr, "el", 2) == 0) + if (STRNCMP(ptr, "if", 2) == 0 + || STRNCMP(ptr, "endif", 5) == 0 + || STRNCMP(ptr, "el", 2) == 0) { hash_dir = 1; + } } - /* Are we on a comment? */ + // Are we on a comment? else if (linep[pos.col] == '/') { if (linep[pos.col + 1] == '*') { comment_dir = FORWARD; @@ -1833,12 +1867,14 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) * If beyond the end of the line, use the last character in * the line. */ - if (linep[pos.col] == NUL && pos.col) + if (linep[pos.col] == NUL && pos.col) { --pos.col; + } for (;; ) { initc = PTR2CHAR(linep + pos.col); - if (initc == NUL) + if (initc == NUL) { break; + } find_mps_values(&initc, &findc, &backwards, false); if (findc) { @@ -1847,18 +1883,20 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) pos.col += utfc_ptr2len(linep + pos.col); } if (!findc) { - /* no brace in the line, maybe use " #if" then */ - if (!cpo_match && *skipwhite(linep) == '#') + // no brace in the line, maybe use " #if" then + if (!cpo_match && *skipwhite(linep) == '#') { hash_dir = 1; - else + } else { return NULL; + } } else if (!cpo_bsl) { int col, bslcnt = 0; /* Set "match_escaped" if there are an odd number of * backslashes. */ - for (col = pos.col; check_prevcol(linep, col, '\\', &col); ) + for (col = pos.col; check_prevcol(linep, col, '\\', &col); ) { bslcnt++; + } match_escaped = (bslcnt & 1); } } @@ -1872,49 +1910,58 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) } if (initc != '#') { ptr = skipwhite(skipwhite(linep) + 1); - if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0) + if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0) { hash_dir = 1; - else if (STRNCMP(ptr, "endif", 5) == 0) + } else if (STRNCMP(ptr, "endif", 5) == 0) { hash_dir = -1; - else + } else { return NULL; + } } pos.col = 0; while (!got_int) { if (hash_dir > 0) { - if (pos.lnum == curbuf->b_ml.ml_line_count) + if (pos.lnum == curbuf->b_ml.ml_line_count) { break; - } else if (pos.lnum == 1) + } + } else if (pos.lnum == 1) { break; + } pos.lnum += hash_dir; linep = ml_get(pos.lnum); - line_breakcheck(); /* check for CTRL-C typed */ + line_breakcheck(); // check for CTRL-C typed ptr = skipwhite(linep); - if (*ptr != '#') + if (*ptr != '#') { continue; - pos.col = (colnr_T) (ptr - linep); + } + pos.col = (colnr_T)(ptr - linep); ptr = skipwhite(ptr + 1); if (hash_dir > 0) { - if (STRNCMP(ptr, "if", 2) == 0) + if (STRNCMP(ptr, "if", 2) == 0) { count++; - else if (STRNCMP(ptr, "el", 2) == 0) { - if (count == 0) + } else if (STRNCMP(ptr, "el", 2) == 0) { + if (count == 0) { return &pos; + } } else if (STRNCMP(ptr, "endif", 5) == 0) { - if (count == 0) + if (count == 0) { return &pos; + } count--; } } else { if (STRNCMP(ptr, "if", 2) == 0) { - if (count == 0) + if (count == 0) { return &pos; + } count--; } else if (initc == '#' && STRNCMP(ptr, "el", 2) == 0) { - if (count == 0) + if (count == 0) { return &pos; - } else if (STRNCMP(ptr, "endif", 5) == 0) + } + } else if (STRNCMP(ptr, "endif", 5) == 0) { count++; + } } } return NULL; @@ -1933,11 +1980,11 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) pos_T match_pos; // Where last slash-star was found clearpos(&match_pos); - /* backward search: Check if this line contains a single-line comment */ + // backward search: Check if this line contains a single-line comment if ((backwards && comment_dir) - || lisp - ) + || lisp) { comment_col = check_linecomment(linep); + } if (lisp && comment_col != MAXCOL && pos.col > (colnr_T)comment_col) { lispcomm = true; // find match inside this comment } @@ -1947,58 +1994,63 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) * inc() and dec() here, but that is much slower */ if (backwards) { - /* char to match is inside of comment, don't search outside */ - if (lispcomm && pos.col < (colnr_T)comment_col) + // char to match is inside of comment, don't search outside + if (lispcomm && pos.col < (colnr_T)comment_col) { break; - if (pos.col == 0) { /* at start of line, go to prev. one */ - if (pos.lnum == 1) /* start of file */ + } + if (pos.col == 0) { // at start of line, go to prev. one + if (pos.lnum == 1) { // start of file break; + } --pos.lnum; - if (maxtravel > 0 && ++traveled > maxtravel) + if (maxtravel > 0 && ++traveled > maxtravel) { break; + } linep = ml_get(pos.lnum); - pos.col = (colnr_T)STRLEN(linep); /* pos.col on trailing NUL */ + pos.col = (colnr_T)STRLEN(linep); // pos.col on trailing NUL do_quotes = -1; line_breakcheck(); - /* Check if this line contains a single-line comment */ + // Check if this line contains a single-line comment if (comment_dir - || lisp - ) + || lisp) { comment_col = check_linecomment(linep); - /* skip comment */ - if (lisp && comment_col != MAXCOL) + } + // skip comment + if (lisp && comment_col != MAXCOL) { pos.col = comment_col; + } } else { pos.col--; pos.col -= utf_head_off(linep, linep + pos.col); } - } else { /* forward search */ + } else { // forward search if (linep[pos.col] == NUL - /* at end of line, go to next one */ - /* don't search for match in comment */ + // at end of line, go to next one + // don't search for match in comment || (lisp && comment_col != MAXCOL - && pos.col == (colnr_T)comment_col) - ) { - if (pos.lnum == curbuf->b_ml.ml_line_count /* end of file */ + && pos.col == (colnr_T)comment_col)) { + if (pos.lnum == curbuf->b_ml.ml_line_count // end of file /* line is exhausted and comment with it, * don't search for match in code */ - || lispcomm - ) + || lispcomm) { break; + } ++pos.lnum; - if (maxtravel && traveled++ > maxtravel) + if (maxtravel && traveled++ > maxtravel) { break; + } linep = ml_get(pos.lnum); pos.col = 0; do_quotes = -1; line_breakcheck(); - if (lisp) /* find comment pos in new line */ + if (lisp) { // find comment pos in new line comment_col = check_linecomment(linep); + } } else { pos.col += utfc_ptr2len(linep + pos.col); } @@ -2014,40 +2066,37 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) } if (comment_dir) { - /* Note: comments do not nest, and we ignore quotes in them */ - /* TODO: ignore comment brackets inside strings */ + // Note: comments do not nest, and we ignore quotes in them + // TODO: ignore comment brackets inside strings if (comment_dir == FORWARD) { if (linep[pos.col] == '*' && linep[pos.col + 1] == '/') { pos.col++; return &pos; } - } else { /* Searching backwards */ + } else { // Searching backwards /* * A comment may contain / * or / /, it may also start or end * with / * /. Ignore a / * after / / and after *. */ - if (pos.col == 0) + if (pos.col == 0) { continue; - else if (raw_string) - { + } else if (raw_string) { if (linep[pos.col - 1] == 'R' && linep[pos.col] == '"' - && vim_strchr(linep + pos.col + 1, '(') != NULL) - { + && vim_strchr(linep + pos.col + 1, '(') != NULL) { /* Possible start of raw string. Now that we have the * delimiter we can check if it ends before where we * started searching, or before the previously found * raw string start. */ if (!find_rawstring_end(linep, &pos, - count > 0 ? &match_pos : &curwin->w_cursor)) - { + count > 0 ? &match_pos : &curwin->w_cursor)) { count++; match_pos = pos; match_pos.col--; } - linep = ml_get(pos.lnum); /* may have been released */ + linep = ml_get(pos.lnum); // may have been released } - } else if ( linep[pos.col - 1] == '/' + } else if (linep[pos.col - 1] == '/' && linep[pos.col] == '*' && (pos.col == 1 || linep[pos.col - 2] != '*') && (int)pos.col < comment_col) { @@ -2055,15 +2104,16 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) match_pos = pos; match_pos.col--; } else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/') { - if (count > 0) + if (count > 0) { pos = match_pos; - else if (pos.col > 1 && linep[pos.col - 2] == '/' - && (int)pos.col <= comment_col) + } else if (pos.col > 1 && linep[pos.col - 2] == '/' + && (int)pos.col <= comment_col) { pos.col -= 2; - else if (ignore_cend) + } else if (ignore_cend) { continue; - else + } else { return NULL; + } return &pos; } } @@ -2075,24 +2125,27 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) * of quotes are ignored, but only if there is an even number of * quotes in the line. */ - if (cpo_match) + if (cpo_match) { do_quotes = 0; - else if (do_quotes == -1) { + } else if (do_quotes == -1) { /* * Count the number of quotes in the line, skipping \" and '"'. * Watch out for "\\". */ at_start = do_quotes; for (ptr = linep; *ptr; ++ptr) { - if (ptr == linep + pos.col + backwards) + if (ptr == linep + pos.col + backwards) { at_start = (do_quotes & 1); + } if (*ptr == '"' - && (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\'')) + && (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\'')) { ++do_quotes; - if (*ptr == '\\' && ptr[1] != NUL) + } + if (*ptr == '\\' && ptr[1] != NUL) { ++ptr; + } } - do_quotes &= 1; /* result is 1 with even number of quotes */ + do_quotes &= 1; // result is 1 with even number of quotes /* * If we find an uneven count, check current line and previous @@ -2124,7 +2177,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) } } - /* ml_get() only keeps one line, need to get linep again */ + // ml_get() only keeps one line, need to get linep again linep = ml_get(pos.lnum); } } @@ -2147,7 +2200,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) const int c = PTR2CHAR(linep + pos.col); switch (c) { case NUL: - /* at end of line without trailing backslash, reset inquote */ + // at end of line without trailing backslash, reset inquote if (pos.col == 0 || linep[pos.col - 1] != '\\') { inquote = false; start_in_quotes = kFalse; @@ -2160,9 +2213,11 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) if (do_quotes) { int col; - for (col = pos.col - 1; col >= 0; --col) - if (linep[col] != '\\') + for (col = pos.col - 1; col >= 0; --col) { + if (linep[col] != '\\') { break; + } + } if ((((int)pos.col - 1 - col) & 1) == 0) { inquote = !inquote; start_in_quotes = kFalse; @@ -2212,8 +2267,9 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) && vim_strchr((char_u *)"(){}[]", c) != NULL && pos.col > 1 && check_prevcol(linep, pos.col, '\\', NULL) - && check_prevcol(linep, pos.col - 1, '#', NULL)) + && check_prevcol(linep, pos.col - 1, '#', NULL)) { break; + } /* Check for match outside of quotes, and inside of * quotes when the start is also inside of quotes. */ @@ -2222,17 +2278,19 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) int col, bslcnt = 0; if (!cpo_bsl) { - for (col = pos.col; check_prevcol(linep, col, '\\', &col); ) + for (col = pos.col; check_prevcol(linep, col, '\\', &col); ) { bslcnt++; + } } /* Only accept a match when 'M' is in 'cpo' or when escaping * is what we expect. */ if (cpo_bsl || (bslcnt & 1) == match_escaped) { - if (c == initc) + if (c == initc) { count++; - else { - if (count == 0) + } else { + if (count == 0) { return &pos; + } count--; } } @@ -2244,7 +2302,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) pos = match_pos; return &pos; } - return (pos_T *)NULL; /* never found it */ + return (pos_T *)NULL; // never found it } /* @@ -2257,51 +2315,55 @@ static int check_linecomment(const char_u *line) const char_u *p = line; // scan from start // skip Lispish one-line comments if (curbuf->b_p_lisp) { - if (vim_strchr(p, ';') != NULL) { /* there may be comments */ - int in_str = FALSE; /* inside of string */ + if (vim_strchr(p, ';') != NULL) { // there may be comments + bool in_str = false; // inside of string while ((p = vim_strpbrk(p, (char_u *)"\";")) != NULL) { if (*p == '"') { if (in_str) { - if (*(p - 1) != '\\') /* skip escaped quote */ - in_str = FALSE; + if (*(p - 1) != '\\') { // skip escaped quote + in_str = false; + } } else if (p == line || ((p - line) >= 2 - /* skip #\" form */ - && *(p - 1) != '\\' && *(p - 2) != '#')) - in_str = TRUE; + // skip #\" form + && *(p - 1) != '\\' && *(p - 2) != '#')) { + in_str = true; + } } else if (!in_str && ((p - line) < 2 - || (*(p - 1) != '\\' && *(p - 2) != '#'))) - break; /* found! */ - ++p; + || (*(p - 1) != '\\' && *(p - 2) != '#'))) { + break; // found! + } + p++; } - } else + } else { p = NULL; - } else + } + } else { while ((p = vim_strchr(p, '/')) != NULL) { /* accept a double /, unless it's preceded with * and followed by *, * because * / / * is an end and start of a C comment */ - if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*')) + if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*')) { break; + } ++p; } + } - if (p == NULL) + if (p == NULL) { return MAXCOL; + } return (int)(p - line); } -/* - * Move cursor briefly to character matching the one under the cursor. - * Used for Insert mode and "r" command. - * Show the match only if it is visible on the screen. - * If there isn't a match, then beep. - */ -void -showmatch( - int c // char to show match for -) +/// Move cursor briefly to character matching the one under the cursor. +/// Used for Insert mode and "r" command. +/// Show the match only if it is visible on the screen. +/// If there isn't a match, then beep. +/// +/// @param c char to show match for +void showmatch(int c) { - pos_T *lpos, save_cursor; + pos_T *lpos, save_cursor; pos_T mpos; colnr_T vcol; long *so = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so; @@ -2310,15 +2372,16 @@ showmatch( long save_siso; int save_state; colnr_T save_dollar_vcol; - char_u *p; + char_u *p; /* * Only show match for chars in the 'matchpairs' option. */ - /* 'matchpairs' is "x:y,x:y" */ + // 'matchpairs' is "x:y,x:y" for (p = curbuf->b_p_mps; *p != NUL; ++p) { - if (PTR2CHAR(p) == c && (curwin->w_p_rl ^ p_ri)) + if (PTR2CHAR(p) == c && (curwin->w_p_rl ^ p_ri)) { break; + } p += utfc_ptr2len(p) + 1; if (PTR2CHAR(p) == c && !(curwin->w_p_rl ^ p_ri)) { break; @@ -2335,7 +2398,7 @@ showmatch( if ((lpos = findmatch(NULL, NUL)) == NULL) { // no match, so beep vim_beep(BO_MATCH); } else if (lpos->lnum >= curwin->w_topline - && lpos->lnum < curwin->w_botline) { + && lpos->lnum < curwin->w_botline) { if (!curwin->w_p_wrap) { getvcol(curwin, lpos, NULL, &vcol, NULL); } @@ -2400,10 +2463,11 @@ int findsent(Direction dir, long count) bool noskip = false; // do not skip blanks pos = curwin->w_cursor; - if (dir == FORWARD) + if (dir == FORWARD) { func = incl; - else + } else { func = decl; + } while (count--) { const pos_T prev_pos = pos; @@ -2418,8 +2482,8 @@ int findsent(Direction dir, long count) if (dir == FORWARD) { goto found; } - // if on the start of a paragraph or a section and searching forward, - // go to the next line + // if on the start of a paragraph or a section and searching forward, + // go to the next line } else if (dir == FORWARD && pos.col == 0 && startPS(pos.lnum, NUL, false)) { if (pos.lnum == curbuf->b_ml.ml_line_count) { @@ -2442,11 +2506,11 @@ int findsent(Direction dir, long count) if (found_dot) { break; } - if (vim_strchr((char_u *) ".!?", c) != NULL) { + if (vim_strchr((char_u *)".!?", c) != NULL) { found_dot = true; } - if (vim_strchr((char_u *) ")]\"'", c) != NULL - && vim_strchr((char_u *) ".!?)]\"'", gchar_pos(&tpos)) == NULL) { + if (vim_strchr((char_u *)")]\"'", c) != NULL + && vim_strchr((char_u *)".!?)]\"'", gchar_pos(&tpos)) == NULL) { break; } decl(&pos); @@ -2456,41 +2520,47 @@ int findsent(Direction dir, long count) const int startlnum = pos.lnum; const bool cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL; - for (;; ) { /* find end of sentence */ + for (;; ) { // find end of sentence c = gchar_pos(&pos); if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE))) { - if (dir == BACKWARD && pos.lnum != startlnum) + if (dir == BACKWARD && pos.lnum != startlnum) { ++pos.lnum; + } break; } if (c == '.' || c == '!' || c == '?') { tpos = pos; do - if ((c = inc(&tpos)) == -1) + if ((c = inc(&tpos)) == -1) { break; + } while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos)) != NULL); if (c == -1 || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL || (cpo_J && (c == ' ' && inc(&tpos) >= 0 && gchar_pos(&tpos) == ' '))) { pos = tpos; - if (gchar_pos(&pos) == NUL) /* skip NUL at EOL */ + if (gchar_pos(&pos) == NUL) { // skip NUL at EOL inc(&pos); + } break; } } if ((*func)(&pos) == -1) { - if (count) + if (count) { return FAIL; + } noskip = true; break; } } found: - /* skip white space */ - while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) - if (incl(&pos) == -1) + // skip white space + while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) { + if (incl(&pos) == -1) { break; + } + } if (equalpos(prev_pos, pos)) { // didn't actually move, advance one character and try again @@ -2509,28 +2579,22 @@ found: return OK; } -/* - * Find the next paragraph or section in direction 'dir'. - * Paragraphs are currently supposed to be separated by empty lines. - * If 'what' is NUL we go to the next paragraph. - * If 'what' is '{' or '}' we go to the next section. - * If 'both' is TRUE also stop at '}'. - * Return TRUE if the next paragraph or section was found. - */ -bool -findpar ( - bool *pincl, /* Return: true if last char is to be included */ - int dir, - long count, - int what, - int both -) +/// Find the next paragraph or section in direction 'dir'. +/// Paragraphs are currently supposed to be separated by empty lines. +/// If 'what' is NUL we go to the next paragraph. +/// If 'what' is '{' or '}' we go to the next section. +/// If 'both' is TRUE also stop at '}'. +/// +/// @param pincl Return: true if last char is to be included +/// +/// @return TRUE if the next paragraph or section was found. +bool findpar(bool *pincl, int dir, long count, int what, int both) { linenr_T curr; - bool did_skip; /* true after separating lines have been skipped */ - bool first; /* true on first line */ - linenr_T fold_first; /* first line of a closed fold */ - linenr_T fold_last; /* last line of a closed fold */ + bool did_skip; // true after separating lines have been skipped + bool first; // true on first line + linenr_T fold_first; // first line of a closed fold + linenr_T fold_last; // last line of a closed fold bool fold_skipped; /* true if a closed fold was skipped this iteration */ @@ -2539,32 +2603,37 @@ findpar ( while (count--) { did_skip = false; for (first = true;; first = false) { - if (*ml_get(curr) != NUL) + if (*ml_get(curr) != NUL) { did_skip = true; + } - /* skip folded lines */ + // skip folded lines fold_skipped = false; if (first && hasFolding(curr, &fold_first, &fold_last)) { curr = ((dir > 0) ? fold_last : fold_first) + dir; fold_skipped = true; } - if (!first && did_skip && startPS(curr, what, both)) + if (!first && did_skip && startPS(curr, what, both)) { break; + } - if (fold_skipped) + if (fold_skipped) { curr -= dir; + } if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count) { - if (count) + if (count) { return false; + } curr -= dir; break; } } } setpcmark(); - if (both && *ml_get(curr) == '}') /* include line with '}' */ + if (both && *ml_get(curr) == '}') { // include line with '}' ++curr; + } curwin->w_cursor.lnum = curr; if (curr == curbuf->b_ml.ml_line_count && what != '}') { char_u *line = ml_get(curr); @@ -2576,8 +2645,9 @@ findpar ( curwin->w_cursor.col -= utf_head_off(line, line + curwin->w_cursor.col); *pincl = true; } - } else + } else { curwin->w_cursor.col = 0; + } return true; } @@ -2586,7 +2656,7 @@ findpar ( */ static int inmacro(char_u *opt, char_u *s) { - char_u *macro; + char_u *macro; for (macro = opt; macro[0]; ++macro) { /* Accept two characters in the option being equal to two characters @@ -2597,11 +2667,13 @@ static int inmacro(char_u *opt, char_u *s) && (s[0] == NUL || s[0] == ' '))) && (macro[1] == s[1] || ((macro[1] == NUL || macro[1] == ' ') - && (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) + && (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) { break; + } ++macro; - if (macro[0] == NUL) + if (macro[0] == NUL) { break; + } } return macro[0] != NUL; } @@ -2613,7 +2685,7 @@ static int inmacro(char_u *opt, char_u *s) */ int startPS(linenr_T lnum, int para, int both) { - char_u *s; + char_u *s; s = ml_get(lnum); if (*s == para || *s == '\f' || (both && *s == '}')) { @@ -2642,7 +2714,7 @@ int startPS(linenr_T lnum, int para, int both) * 2 or higher - keyword characters (letters, digits and underscore) */ -static int cls_bigword; /* TRUE for "W", "B" or "E" */ +static int cls_bigword; // TRUE for "W", "B" or "E" /* * cls() - returns the class of character at curwin->w_cursor @@ -2669,20 +2741,15 @@ static int cls(void) return c; } -/* - * fwd_word(count, type, eol) - move forward one word - * - * Returns FAIL if the cursor was already at the end of the file. - * If eol is TRUE, last word stops at end of line (for operators). - */ -int -fwd_word( - long count, - int bigword, /* "W", "E" or "B" */ - int eol -) +/// fwd_word(count, type, eol) - move forward one word +/// +/// @return FAIL if the cursor was already at the end of the file. +/// If eol is TRUE, last word stops at end of line (for operators). +/// +/// @param bigword "W", "E" or "B" +int fwd_word(long count, int bigword, int eol) { - int sclass; /* starting class */ + int sclass; // starting class int i; int last_line; @@ -2702,20 +2769,24 @@ fwd_word( */ last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count); i = inc_cursor(); - if (i == -1 || (i >= 1 && last_line)) /* started at last char in file */ + if (i == -1 || (i >= 1 && last_line)) { // started at last char in file return FAIL; - if (i >= 1 && eol && count == 0) /* started at last char in line */ + } + if (i >= 1 && eol && count == 0) { // started at last char in line return OK; + } /* * Go one char past end of current word (if any) */ - if (sclass != 0) + if (sclass != 0) { while (cls() == sclass) { i = inc_cursor(); - if (i == -1 || (i >= 1 && eol && count == 0)) + if (i == -1 || (i >= 1 && eol && count == 0)) { return OK; + } } + } /* * go to next non-white @@ -2724,12 +2795,14 @@ fwd_word( /* * We'll stop if we land on a blank line */ - if (curwin->w_cursor.col == 0 && *get_cursor_line_ptr() == NUL) + if (curwin->w_cursor.col == 0 && *get_cursor_line_ptr() == NUL) { break; + } i = inc_cursor(); - if (i == -1 || (i >= 1 && eol && count == 0)) + if (i == -1 || (i >= 1 && eol && count == 0)) { return OK; + } } } return OK; @@ -2744,18 +2817,20 @@ fwd_word( */ int bck_word(long count, int bigword, int stop) { - int sclass; /* starting class */ + int sclass; // starting class curwin->w_cursor.coladd = 0; cls_bigword = bigword; 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->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) { curwin->w_cursor.col = 0; + } sclass = cls(); - if (dec_cursor() == -1) /* started at start of file */ + if (dec_cursor() == -1) { // started at start of file return FAIL; + } if (!stop || sclass == cls() || sclass == 0) { /* @@ -2775,11 +2850,12 @@ int bck_word(long count, int bigword, int stop) /* * Move backward to start of this word. */ - if (skip_chars(cls(), BACKWARD)) + if (skip_chars(cls(), BACKWARD)) { return OK; + } } - inc_cursor(); /* overshot - forward one */ + inc_cursor(); // overshot - forward one finished: stop = FALSE; } @@ -2803,7 +2879,7 @@ finished: */ int end_word(long count, int bigword, int stop, int empty) { - int sclass; /* starting class */ + int sclass; // starting class curwin->w_cursor.coladd = 0; cls_bigword = bigword; @@ -2814,8 +2890,9 @@ int end_word(long count, int bigword, int stop, int empty) coladvance(MAXCOL); } sclass = cls(); - if (inc_cursor() == -1) + if (inc_cursor() == -1) { return FAIL; + } /* * If we're in the middle of a word, we just have to move to the end @@ -2825,8 +2902,9 @@ int end_word(long count, int bigword, int stop, int empty) /* * Move forward to end of the current word */ - if (skip_chars(sclass, FORWARD)) + if (skip_chars(sclass, FORWARD)) { return FAIL; + } } else if (!stop || sclass == 0) { /* * We were at the end of a word. Go to the end of the next word. @@ -2845,47 +2923,48 @@ int end_word(long count, int bigword, int stop, int empty) /* * Move forward to the end of this word. */ - if (skip_chars(cls(), FORWARD)) + if (skip_chars(cls(), FORWARD)) { return FAIL; + } } - dec_cursor(); /* overshot - one char backward */ + dec_cursor(); // overshot - one char backward finished: - stop = FALSE; /* we move only one word less */ + stop = FALSE; // we move only one word less } return OK; } -/* - * Move back to the end of the word. - * - * Returns FAIL if start of the file was reached. - */ -int -bckend_word( - long count, - int bigword, /* TRUE for "B" */ - int eol /* TRUE: stop at end of line. */ -) +/// Move back to the end of the word. +/// +/// @param bigword TRUE for "B" +/// @param eol if true, then stop at end of line. +/// +/// @return FAIL if start of the file was reached. +int bckend_word(long count, int bigword, bool eol) { - int sclass; /* starting class */ + int sclass; // starting class int i; curwin->w_cursor.coladd = 0; cls_bigword = bigword; while (--count >= 0) { sclass = cls(); - if ((i = dec_cursor()) == -1) + if ((i = dec_cursor()) == -1) { return FAIL; - if (eol && i == 1) + } + if (eol && i == 1) { return OK; + } /* * Move backward to before the start of this word. */ if (sclass != 0) { - while (cls() == sclass) - if ((i = dec_cursor()) == -1 || (eol && i == 1)) + while (cls() == sclass) { + if ((i = dec_cursor()) == -1 || (eol && i == 1)) { return OK; + } + } } /* @@ -2903,16 +2982,17 @@ bckend_word( return OK; } -/* - * Skip a row of characters of the same class. - * Return TRUE when end-of-file reached, FALSE otherwise. - */ -static int skip_chars(int cclass, int dir) +/// Skip a row of characters of the same class. +/// +/// @return true when end-of-file reached, false otherwise. +static bool skip_chars(int cclass, int dir) { - while (cls() == cclass) - if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1) - return TRUE; - return FALSE; + while (cls() == cclass) { + if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1) { + return true; + } + } + return false; } /* @@ -2920,14 +3000,15 @@ static int skip_chars(int cclass, int dir) */ static void back_in_line(void) { - int sclass; /* starting class */ + int sclass; // starting class sclass = cls(); for (;; ) { - if (curwin->w_cursor.col == 0) /* stop at start of line */ + if (curwin->w_cursor.col == 0) { // stop at start of line break; + } dec_cursor(); - if (cls() != sclass) { /* stop at start of word */ + if (cls() != sclass) { // stop at start of word inc_cursor(); break; } @@ -2947,36 +3028,29 @@ static void find_first_blank(pos_T *posp) } } -/* - * Skip count/2 sentences and count/2 separating white spaces. - */ -static void -findsent_forward( - long count, - int at_start_sent /* cursor is at start of sentence */ -) +/// Skip count/2 sentences and count/2 separating white spaces. +/// +/// @param at_start_sent cursor is at start of sentence +static void findsent_forward(long count, bool at_start_sent) { while (count--) { findsent(FORWARD, 1L); - if (at_start_sent) + if (at_start_sent) { find_first_blank(&curwin->w_cursor); - if (count == 0 || at_start_sent) + } + if (count == 0 || at_start_sent) { decl(&curwin->w_cursor); + } at_start_sent = !at_start_sent; } } -/* - * Find word under cursor, cursor at end. - * Used while an operator is pending, and in Visual mode. - */ -int -current_word( - oparg_T *oap, - long count, - int include, /* TRUE: include word and white space */ - int bigword /* FALSE == word, TRUE == WORD */ -) +/// Find word under cursor, cursor at end. +/// Used while an operator is pending, and in Visual mode. +/// +/// @param include TRUE: include word and white space +/// @param bigword FALSE == word, TRUE == WORD +int current_word(oparg_T *oap, long count, int include, int bigword) { pos_T start_pos; pos_T pos; @@ -2986,9 +3060,10 @@ current_word( cls_bigword = bigword; clearpos(&start_pos); - /* Correct cursor when 'selection' is exclusive */ - if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) + // Correct cursor when 'selection' is exclusive + if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) { dec_cursor(); + } /* * When Visual mode is not active, or when the VIsual area is only one @@ -3007,8 +3082,9 @@ current_word( * not be included ("word"), find end of word. */ if ((cls() == 0) == include) { - if (end_word(1L, bigword, TRUE, TRUE) == FAIL) + if (end_word(1L, bigword, TRUE, TRUE) == FAIL) { return FAIL; + } } else { /* * If the start is not on white space, and white space should be @@ -3018,19 +3094,21 @@ current_word( * word) back up to end of the line. */ fwd_word(1L, bigword, TRUE); - if (curwin->w_cursor.col == 0) + if (curwin->w_cursor.col == 0) { decl(&curwin->w_cursor); - else + } else { oneleft(); + } - if (include) + if (include) { include_white = TRUE; + } } if (VIsual_active) { - /* should do something when inclusive == false ! */ + // should do something when inclusive == false ! VIsual = start_pos; - redraw_curbuf_later(INVERTED); /* update the inversion */ + redraw_curbuf_later(INVERTED); // update the inversion } else { oap->start = start_pos; oap->motion_type = kMTCharWise; @@ -3047,35 +3125,42 @@ current_word( /* * In Visual mode, with cursor at start: move cursor back. */ - if (decl(&curwin->w_cursor) == -1) + if (decl(&curwin->w_cursor) == -1) { return FAIL; + } if (include != (cls() != 0)) { - if (bck_word(1L, bigword, TRUE) == FAIL) + if (bck_word(1L, bigword, TRUE) == FAIL) { return FAIL; + } } else { - if (bckend_word(1L, bigword, TRUE) == FAIL) + if (bckend_word(1L, bigword, true) == FAIL) { return FAIL; + } (void)incl(&curwin->w_cursor); } } else { /* * Move cursor forward one word and/or white area. */ - if (incl(&curwin->w_cursor) == -1) + if (incl(&curwin->w_cursor) == -1) { return FAIL; + } if (include != (cls() == 0)) { - if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1) + if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1) { return FAIL; + } /* * If end is just past a new-line, we don't want to include * the first character on the line. * Put cursor on last char of white. */ - if (oneleft() == FAIL) + if (oneleft() == FAIL) { inclusive = false; + } } else { - if (end_word(1L, bigword, TRUE, TRUE) == FAIL) + if (end_word(1L, bigword, TRUE, TRUE) == FAIL) { return FAIL; + } } } --count; @@ -3091,29 +3176,32 @@ current_word( * (cursor is at start of next line). * But don't delete white space at start of line (indent). */ - pos = curwin->w_cursor; /* save cursor position */ + pos = curwin->w_cursor; // save cursor position curwin->w_cursor = start_pos; if (oneleft() == OK) { back_in_line(); if (cls() == 0 && curwin->w_cursor.col > 0) { - if (VIsual_active) + if (VIsual_active) { VIsual = curwin->w_cursor; - else + } else { oap->start = curwin->w_cursor; + } } } - curwin->w_cursor = pos; /* put cursor back at end */ + curwin->w_cursor = pos; // put cursor back at end } if (VIsual_active) { - if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor)) + if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor)) { inc_cursor(); + } if (VIsual_mode == 'V') { VIsual_mode = 'v'; - redraw_cmdline = TRUE; /* show mode later */ + redraw_cmdline = true; // show mode later } - } else + } else { oap->inclusive = inclusive; + } return OK; } @@ -3126,14 +3214,14 @@ int current_sent(oparg_T *oap, long count, int include) { pos_T start_pos; pos_T pos; - int start_blank; + bool start_blank; int c; - int at_start_sent; + bool at_start_sent; long ncount; start_pos = curwin->w_cursor; pos = start_pos; - findsent(FORWARD, 1L); /* Find start of next sentence. */ + findsent(FORWARD, 1L); // Find start of next sentence. /* * When the Visual area is bigger than one character: Extend it. @@ -3148,32 +3236,36 @@ extend: * - in a sentence or just after it * - at the start of a sentence */ - at_start_sent = TRUE; + at_start_sent = true; decl(&pos); while (lt(pos, curwin->w_cursor)) { c = gchar_pos(&pos); if (!ascii_iswhite(c)) { - at_start_sent = FALSE; + at_start_sent = false; break; } incl(&pos); } if (!at_start_sent) { findsent(BACKWARD, 1L); - if (equalpos(curwin->w_cursor, start_pos)) - at_start_sent = TRUE; /* exactly at start of sentence */ - else - /* inside a sentence, go to its end (start of next) */ + if (equalpos(curwin->w_cursor, start_pos)) { + at_start_sent = true; // exactly at start of sentence + } else { + // inside a sentence, go to its end (start of next) findsent(FORWARD, 1L); + } } - if (include) /* "as" gets twice as much as "is" */ + if (include) { // "as" gets twice as much as "is" count *= 2; + } while (count--) { - if (at_start_sent) + if (at_start_sent) { find_first_blank(&curwin->w_cursor); + } c = gchar_cursor(); - if (!at_start_sent || (!include && !ascii_iswhite(c))) + if (!at_start_sent || (!include && !ascii_iswhite(c))) { findsent(BACKWARD, 1L); + } at_start_sent = !at_start_sent; } } else { @@ -3185,28 +3277,31 @@ extend: * - in a sentence */ incl(&pos); - at_start_sent = TRUE; - if (!equalpos(pos, curwin->w_cursor)) { /* not just before a sentence */ - at_start_sent = FALSE; + at_start_sent = true; + if (!equalpos(pos, curwin->w_cursor)) { // not just before a sentence + at_start_sent = false; while (lt(pos, curwin->w_cursor)) { c = gchar_pos(&pos); if (!ascii_iswhite(c)) { - at_start_sent = TRUE; + at_start_sent = true; break; } incl(&pos); } - if (at_start_sent) /* in the sentence */ + if (at_start_sent) { // in the sentence findsent(BACKWARD, 1L); - else /* in/before white before a sentence */ + } else { // in/before white before a sentence curwin->w_cursor = start_pos; + } } - if (include) /* "as" gets twice as much as "is" */ + if (include) { // "as" gets twice as much as "is" count *= 2; + } findsent_forward(count, at_start_sent); - if (*p_sel == 'e') + if (*p_sel == 'e') { ++curwin->w_cursor.col; + } } return OK; } @@ -3215,27 +3310,30 @@ extend: * If the cursor started on a blank, check if it is just before the start * of the next sentence. */ - while (c = gchar_pos(&pos), ascii_iswhite(c)) + while (c = gchar_pos(&pos), ascii_iswhite(c)) { incl(&pos); + } if (equalpos(pos, curwin->w_cursor)) { - start_blank = TRUE; - find_first_blank(&start_pos); /* go back to first blank */ + start_blank = true; + find_first_blank(&start_pos); // go back to first blank } else { - start_blank = FALSE; + start_blank = false; findsent(BACKWARD, 1L); start_pos = curwin->w_cursor; } - if (include) + if (include) { ncount = count * 2; - else { + } else { ncount = count; - if (start_blank) + if (start_blank) { --ncount; + } } - if (ncount > 0) - findsent_forward(ncount, TRUE); - else + if (ncount > 0) { + findsent_forward(ncount, true); + } else { decl(&curwin->w_cursor); + } if (include) { /* @@ -3246,57 +3344,57 @@ extend: if (start_blank) { find_first_blank(&curwin->w_cursor); c = gchar_pos(&curwin->w_cursor); - if (ascii_iswhite(c)) + if (ascii_iswhite(c)) { decl(&curwin->w_cursor); - } else if (c = gchar_cursor(), !ascii_iswhite(c)) + } + } else if (c = gchar_cursor(), !ascii_iswhite(c)) { find_first_blank(&start_pos); + } } if (VIsual_active) { - /* Avoid getting stuck with "is" on a single space before a sentence. */ - if (equalpos(start_pos, curwin->w_cursor)) + // Avoid getting stuck with "is" on a single space before a sentence. + if (equalpos(start_pos, curwin->w_cursor)) { goto extend; - if (*p_sel == 'e') + } + if (*p_sel == 'e') { ++curwin->w_cursor.col; + } VIsual = start_pos; VIsual_mode = 'v'; redraw_cmdline = true; // show mode later redraw_curbuf_later(INVERTED); // update the inversion } else { - /* include a newline after the sentence, if there is one */ - if (incl(&curwin->w_cursor) == -1) + // include a newline after the sentence, if there is one + if (incl(&curwin->w_cursor) == -1) { oap->inclusive = true; - else + } else { oap->inclusive = false; + } oap->start = start_pos; oap->motion_type = kMTCharWise; } return OK; } -/* - * Find block under the cursor, cursor at end. - * "what" and "other" are two matching parenthesis/brace/etc. - */ -int -current_block( - oparg_T *oap, - long count, - int include, /* TRUE == include white space */ - int what, /* '(', '{', etc. */ - int other /* ')', '}', etc. */ -) +/// Find block under the cursor, cursor at end. +/// "what" and "other" are two matching parenthesis/brace/etc. +/// +/// @param include TRUE == include white space +/// @param what '(', '{', etc. +/// @param other ')', '}', etc. +int current_block(oparg_T *oap, long count, int include, int what, int other) { pos_T old_pos; - pos_T *pos = NULL; + pos_T *pos = NULL; pos_T start_pos; - pos_T *end_pos; + pos_T *end_pos; pos_T old_start, old_end; - char_u *save_cpo; - int sol = FALSE; /* '{' at start of line */ + char_u *save_cpo; + bool sol = false; // '{' at start of line old_pos = curwin->w_cursor; - old_end = curwin->w_cursor; /* remember where we started */ + old_end = curwin->w_cursor; // remember where we started old_start = old_end; /* @@ -3304,18 +3402,23 @@ current_block( */ if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) { setpcmark(); - if (what == '{') /* ignore indent */ - while (inindent(1)) - if (inc_cursor() != 0) + if (what == '{') { // ignore indent + while (inindent(1)) { + if (inc_cursor() != 0) { break; - if (gchar_cursor() == what) - /* cursor on '(' or '{', move cursor just after it */ + } + } + } + if (gchar_cursor() == what) { + // cursor on '(' or '{', move cursor just after it ++curwin->w_cursor.col; + } } else if (lt(VIsual, curwin->w_cursor)) { old_start = VIsual; - curwin->w_cursor = VIsual; /* cursor at low end of Visual */ - } else + curwin->w_cursor = VIsual; // cursor at low end of Visual + } else { old_end = VIsual; + } // Search backwards for unclosed '(', '{', etc.. // Put this position in start_pos. @@ -3351,7 +3454,7 @@ current_block( sol = (curwin->w_cursor.col == 0); decl(&curwin->w_cursor); while (inindent(1)) { - sol = TRUE; + sol = true; if (decl(&curwin->w_cursor) != 0) { break; } @@ -3376,8 +3479,9 @@ current_block( return FAIL; } curwin->w_cursor = *end_pos; - } else + } else { break; + } } if (VIsual_active) { @@ -3389,35 +3493,35 @@ current_block( } VIsual = start_pos; VIsual_mode = 'v'; - redraw_curbuf_later(INVERTED); /* update the inversion */ + redraw_curbuf_later(INVERTED); // update the inversion showmode(); } else { oap->start = start_pos; oap->motion_type = kMTCharWise; oap->inclusive = false; - if (sol) + if (sol) { incl(&curwin->w_cursor); - else if (ltoreq(start_pos, curwin->w_cursor)) - /* Include the character under the cursor. */ + } else if (ltoreq(start_pos, curwin->w_cursor)) { + // Include the character under the cursor. oap->inclusive = true; - else + } else { /* End is before the start (no text in between <>, [], etc.): don't * operate on any text. */ curwin->w_cursor = start_pos; + } } return OK; } -/* - * Return TRUE if the cursor is on a "<aaa>" tag. Ignore "<aaa/>". - * When "end_tag" is TRUE return TRUE if the cursor is on "</aaa>". - */ -static int in_html_tag(int end_tag) +/// @param end_tag when true, return true if the cursor is on "</aaa>". +/// +/// @return true if the cursor is on a "<aaa>" tag. Ignore "<aaa/>". +static bool in_html_tag(bool end_tag) { - char_u *line = get_cursor_line_ptr(); - char_u *p; + char_u *line = get_cursor_line_ptr(); + char_u *p; int c; int lc = NUL; pos_T pos; @@ -3444,39 +3548,37 @@ static int in_html_tag(int end_tag) return *p == '/'; } - /* check that there is no '/' after the '<' */ - if (*p == '/') - return FALSE; + // check that there is no '/' after the '<' + if (*p == '/') { + return false; + } - /* check that the matching '>' is not preceded by '/' */ + // check that the matching '>' is not preceded by '/' for (;; ) { - if (inc(&pos) < 0) - return FALSE; + if (inc(&pos) < 0) { + return false; + } c = *ml_get_pos(&pos); - if (c == '>') + if (c == '>') { break; + } lc = c; } return lc != '/'; } -/* - * Find tag block under the cursor, cursor at end. - */ -int -current_tagblock( - oparg_T *oap, - long count_arg, - bool include // true == include white space -) +/// Find tag block under the cursor, cursor at end. +/// +/// @param include true == include white space +int current_tagblock(oparg_T *oap, long count_arg, bool include) { long count = count_arg; pos_T old_pos; pos_T start_pos; pos_T end_pos; pos_T old_start, old_end; - char_u *p; - char_u *cp; + char_u *p; + char_u *cp; int len; bool do_include = include; bool save_p_ws = p_ws; @@ -3486,40 +3588,47 @@ current_tagblock( p_ws = false; old_pos = curwin->w_cursor; - old_end = curwin->w_cursor; /* remember where we started */ + old_end = curwin->w_cursor; // remember where we started old_start = old_end; - if (!VIsual_active || *p_sel == 'e') - decl(&old_end); /* old_end is inclusive */ - + if (!VIsual_active || *p_sel == 'e') { + decl(&old_end); // old_end is inclusive + } /* * If we start on "<aaa>" select that block. */ if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) { setpcmark(); - /* ignore indent */ - while (inindent(1)) - if (inc_cursor() != 0) + // ignore indent + while (inindent(1)) { + if (inc_cursor() != 0) { break; + } + } - if (in_html_tag(FALSE)) { - /* cursor on start tag, move to its '>' */ - while (*get_cursor_pos_ptr() != '>') - if (inc_cursor() < 0) + if (in_html_tag(false)) { + // cursor on start tag, move to its '>' + while (*get_cursor_pos_ptr() != '>') { + if (inc_cursor() < 0) { break; - } else if (in_html_tag(TRUE)) { - /* cursor on end tag, move to just before it */ - while (*get_cursor_pos_ptr() != '<') - if (dec_cursor() < 0) + } + } + } else if (in_html_tag(true)) { + // cursor on end tag, move to just before it + while (*get_cursor_pos_ptr() != '<') { + if (dec_cursor() < 0) { break; + } + } dec_cursor(); old_end = curwin->w_cursor; } } else if (lt(VIsual, curwin->w_cursor)) { old_start = VIsual; - curwin->w_cursor = VIsual; /* cursor at low end of Visual */ - } else + curwin->w_cursor = VIsual; // cursor at low end of Visual + } else { old_end = VIsual; + } again: /* @@ -3527,11 +3636,10 @@ again: * Put this position in start_pos. */ for (long n = 0; n < count; n++) { - if (do_searchpair( - "<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)", - "", - "</[^>]*>", BACKWARD, NULL, 0, - NULL, (linenr_T)0, 0L) <= 0) { + if (do_searchpair("<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)", + "", + "</[^>]*>", BACKWARD, NULL, 0, + NULL, (linenr_T)0, 0L) <= 0) { curwin->w_cursor = old_pos; goto theend; } @@ -3597,14 +3705,15 @@ again: end_pos = curwin->w_cursor; if (!do_include) { - /* Exclude the start tag. */ + // Exclude the start tag. curwin->w_cursor = start_pos; - while (inc_cursor() >= 0) + while (inc_cursor() >= 0) { if (*get_cursor_pos_ptr() == '>') { inc_cursor(); start_pos = curwin->w_cursor; break; } + } curwin->w_cursor = end_pos; // If we are in Visual mode and now have the same text as before set @@ -3629,7 +3738,7 @@ again: } VIsual = start_pos; VIsual_mode = 'v'; - redraw_curbuf_later(INVERTED); /* update the inversion */ + redraw_curbuf_later(INVERTED); // update the inversion showmode(); } else { oap->start = start_pos; @@ -3650,13 +3759,9 @@ theend: return retval; } -int -current_par( - oparg_T *oap, - long count, - int include, /* TRUE == include white space */ - int type /* 'p' for paragraph, 'S' for section */ -) +/// @param include TRUE == include white space +/// @param type 'p' for paragraph, 'S' for section +int current_par(oparg_T *oap, long count, int include, int type) { linenr_T start_lnum; linenr_T end_lnum; @@ -3669,8 +3774,9 @@ current_par( int t; int i; - if (type == 'S') /* not implemented yet */ + if (type == 'S') { // not implemented yet return FAIL; + } start_lnum = curwin->w_cursor.lnum; @@ -3679,10 +3785,11 @@ current_par( */ if (VIsual_active && start_lnum != VIsual.lnum) { extend: - if (start_lnum < VIsual.lnum) + if (start_lnum < VIsual.lnum) { dir = BACKWARD; - else + } else { dir = FORWARD; + } for (i = count; --i >= 0; ) { if (start_lnum == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) { @@ -3700,20 +3807,24 @@ extend: } for (;; ) { if (start_lnum == (dir == BACKWARD - ? 1 : curbuf->b_ml.ml_line_count)) + ? 1 : curbuf->b_ml.ml_line_count)) { break; + } if (start_is_white != linewhite(start_lnum + dir) || (!start_is_white && startPS(start_lnum + (dir > 0 - ? 1 : 0), 0, 0))) + ? 1 : 0), 0, 0))) { break; + } start_lnum += dir; } - if (!include) + if (!include) { break; + } if (start_lnum == (dir == BACKWARD - ? 1 : curbuf->b_ml.ml_line_count)) + ? 1 : curbuf->b_ml.ml_line_count)) { break; + } prev_start_is_white = start_is_white; } } @@ -3727,12 +3838,14 @@ extend: */ white_in_front = linewhite(start_lnum); while (start_lnum > 1) { - if (white_in_front) { /* stop at first white line */ - if (!linewhite(start_lnum - 1)) + if (white_in_front) { // stop at first white line + if (!linewhite(start_lnum - 1)) { break; - } else { /* stop at first non-white line of start of paragraph */ - if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0)) + } + } else { // stop at first non-white line of start of paragraph + if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0)) { break; + } } --start_lnum; } @@ -3741,19 +3854,23 @@ extend: * Move past the end of any white lines. */ end_lnum = start_lnum; - while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum)) + while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum)) { ++end_lnum; + } --end_lnum; i = count; - if (!include && white_in_front) + if (!include && white_in_front) { --i; + } while (i--) { - if (end_lnum == curbuf->b_ml.ml_line_count) + if (end_lnum == curbuf->b_ml.ml_line_count) { return FAIL; + } - if (!include) + if (!include) { do_white = linewhite(end_lnum + 1); + } if (include || !do_white) { ++end_lnum; @@ -3762,29 +3879,35 @@ extend: */ while (end_lnum < curbuf->b_ml.ml_line_count && !linewhite(end_lnum + 1) - && !startPS(end_lnum + 1, 0, 0)) + && !startPS(end_lnum + 1, 0, 0)) { ++end_lnum; + } } - if (i == 0 && white_in_front && include) + if (i == 0 && white_in_front && include) { break; + } /* * skip to end of white lines after paragraph */ - if (include || do_white) + if (include || do_white) { while (end_lnum < curbuf->b_ml.ml_line_count - && linewhite(end_lnum + 1)) + && linewhite(end_lnum + 1)) { ++end_lnum; + } + } } /* * If there are no empty lines at the end, try to find some empty lines at * the start (unless that has been done already). */ - if (!white_in_front && !linewhite(end_lnum) && include) - while (start_lnum > 1 && linewhite(start_lnum - 1)) + if (!white_in_front && !linewhite(end_lnum) && include) { + while (start_lnum > 1 && linewhite(start_lnum - 1)) { --start_lnum; + } + } if (VIsual_active) { // Problem: when doing "Vipipip" nothing happens in a single white @@ -3793,11 +3916,11 @@ extend: goto extend; } if (VIsual.lnum != start_lnum) { - VIsual.lnum = start_lnum; - VIsual.col = 0; + VIsual.lnum = start_lnum; + VIsual.col = 0; } VIsual_mode = 'V'; - redraw_curbuf_later(INVERTED); /* update the inversion */ + redraw_curbuf_later(INVERTED); // update the inversion showmode(); } else { oap->start.lnum = start_lnum; @@ -3811,19 +3934,14 @@ extend: } -/* - * Search quote char from string line[col]. - * Quote character escaped by one of the characters in "escape" is not counted - * as a quote. - * Returns column number of "quotechar" or -1 when not found. - */ -static int -find_next_quote( - char_u *line, - int col, - int quotechar, - char_u *escape /* escape characters, can be NULL */ -) +/// Search quote char from string line[col]. +/// Quote character escaped by one of the characters in "escape" is not counted +/// as a quote. +/// +/// @param escape escape characters, can be NULL +/// +/// @return column number of "quotechar" or -1 when not found. +static int find_next_quote(char_u *line, int col, int quotechar, char_u *escape) { int c; @@ -3841,19 +3959,14 @@ find_next_quote( return col; } -/* - * Search backwards in "line" from column "col_start" to find "quotechar". - * Quote character escaped by one of the characters in "escape" is not counted - * as a quote. - * Return the found column or zero. - */ -static int -find_prev_quote( - char_u *line, - int col_start, - int quotechar, - char_u *escape /* escape characters, can be NULL */ -) +/// Search backwards in "line" from column "col_start" to find "quotechar". +/// Quote character escaped by one of the characters in "escape" is not counted +/// as a quote. +/// +/// @param escape escape characters, can be NULL +/// +/// @return the found column or zero. +static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *escape) { int n; @@ -3861,29 +3974,32 @@ find_prev_quote( col_start--; col_start -= utf_head_off(line, line + col_start); n = 0; - if (escape != NULL) + if (escape != NULL) { while (col_start - n > 0 && vim_strchr(escape, - line[col_start - n - 1]) != NULL) + line[col_start - n - 1]) != NULL) { ++n; - if (n & 1) - col_start -= n; /* uneven number of escape chars, skip it */ - else if (line[col_start] == quotechar) + } + } + if (n & 1) { + col_start -= n; // uneven number of escape chars, skip it + } else if (line[col_start] == + quotechar) { break; + } } return col_start; } -// Find quote under the cursor, cursor at end. -// Returns true if found, else false. -bool current_quote( - oparg_T *oap, - long count, - bool include, // true == include quote char - int quotechar // Quote character -) +/// Find quote under the cursor, cursor at end. +/// +/// @param include true == include quote char +/// @param quotechar Quote character +/// +/// @return true if found, else false. +bool current_quote(oparg_T *oap, long count, bool include, int quotechar) FUNC_ATTR_NONNULL_ALL { - char_u *line = get_cursor_line_ptr(); + char_u *line = get_cursor_line_ptr(); int col_end; int col_start = curwin->w_cursor.col; bool inclusive = false; @@ -3901,7 +4017,7 @@ bool current_quote( if (VIsual_active) { // this only works within one line if (VIsual.lnum != curwin->w_cursor.lnum) { - return false; + return false; } vis_bef_curs = lt(VIsual, curwin->w_cursor); @@ -3946,12 +4062,13 @@ bool current_quote( col_end = VIsual.col; } - /* Find out if we have a quote in the selection. */ - while (i <= col_end) + // Find out if we have a quote in the selection. + while (i <= col_end) { if (line[i++] == quotechar) { selected_quote = true; break; } + } } if (!vis_empty && line[col_start] == quotechar) { @@ -3965,9 +4082,9 @@ bool current_quote( goto abort_search; } col_end = find_next_quote(line, col_start + 1, quotechar, - curbuf->b_p_qe); + curbuf->b_p_qe); if (col_end < 0) { - /* We were on a starting quote perhaps? */ + // We were on a starting quote perhaps? col_end = col_start; col_start = curwin->w_cursor.col; } @@ -3977,23 +4094,23 @@ bool current_quote( goto abort_search; } col_start = find_prev_quote(line, col_end, quotechar, - curbuf->b_p_qe); + curbuf->b_p_qe); if (line[col_start] != quotechar) { - /* We were on an ending quote perhaps? */ + // We were on an ending quote perhaps? col_start = col_end; col_end = curwin->w_cursor.col; } } } else if (line[col_start] == quotechar - || !vis_empty - ) { + || !vis_empty) { int first_col = col_start; if (!vis_empty) { - if (vis_bef_curs) + if (vis_bef_curs) { first_col = find_next_quote(line, col_start, quotechar, NULL); - else + } else { first_col = find_prev_quote(line, col_start, quotechar, NULL); + } } /* The cursor is on a quote, we don't know if it's the opening or * closing quote. Search from the start of the line to find out. @@ -4001,14 +4118,14 @@ bool current_quote( * in between two strings. */ col_start = 0; for (;; ) { - /* Find open quote character. */ + // Find open quote character. col_start = find_next_quote(line, col_start, quotechar, NULL); if (col_start < 0 || col_start > first_col) { goto abort_search; } // Find close quote character. col_end = find_next_quote(line, col_start + 1, quotechar, - curbuf->b_p_qe); + curbuf->b_p_qe); if (col_end < 0) { goto abort_search; } @@ -4020,17 +4137,17 @@ bool current_quote( col_start = col_end + 1; } } else { - /* Search backward for a starting quote. */ + // Search backward for a starting quote. col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe); if (line[col_start] != quotechar) { - /* No quote before the cursor, look after the cursor. */ + // No quote before the cursor, look after the cursor. col_start = find_next_quote(line, col_start, quotechar, NULL); if (col_start < 0) { goto abort_search; } } - /* Find close quote character. */ + // Find close quote character. col_end = find_next_quote(line, col_start + 1, quotechar, curbuf->b_p_qe); if (col_end < 0) { @@ -4041,20 +4158,23 @@ bool current_quote( // When "include" is true, include spaces after closing quote or before // the starting quote. if (include) { - if (ascii_iswhite(line[col_end + 1])) - while (ascii_iswhite(line[col_end + 1])) + if (ascii_iswhite(line[col_end + 1])) { + while (ascii_iswhite(line[col_end + 1])) { ++col_end; - else - while (col_start > 0 && ascii_iswhite(line[col_start - 1])) + } + } else { + while (col_start > 0 && ascii_iswhite(line[col_start - 1])) { --col_start; + } + } } /* Set start position. After vi" another i" must include the ". * For v2i" include the quotes. */ if (!include && count < 2 - && (vis_empty || !inside_quotes) - ) + && (vis_empty || !inside_quotes)) { ++col_start; + } curwin->w_cursor.col = col_start; if (VIsual_active) { /* Set the start of the Visual area when the Visual area was empty, we @@ -4076,13 +4196,14 @@ bool current_quote( oap->motion_type = kMTCharWise; } - /* Set end position. */ + // Set end position. curwin->w_cursor.col = col_end; if ((include || count > 1 - /* After vi" another i" must include the ". */ + // After vi" another i" must include the ". || (!vis_empty && inside_quotes) - ) && inc_cursor() == 2) + ) && inc_cursor() == 2) { inclusive = true; + } if (VIsual_active) { if (vis_empty || vis_bef_curs) { // decrement cursor when 'selection' is not exclusive @@ -4105,10 +4226,10 @@ bool current_quote( } if (VIsual_mode == 'V') { VIsual_mode = 'v'; - redraw_cmdline = TRUE; /* show mode later */ + redraw_cmdline = true; // show mode later } } else { - /* Set inclusive and other oap's flags. */ + // Set inclusive and other oap's flags. oap->inclusive = inclusive; } @@ -4120,10 +4241,10 @@ abort_search: inc_cursor(); } if (restore_vis_bef) { - pos_T t = curwin->w_cursor; + pos_T t = curwin->w_cursor; - curwin->w_cursor = VIsual; - VIsual = t; + curwin->w_cursor = VIsual; + VIsual = t; } } return false; @@ -4131,22 +4252,19 @@ abort_search: -/* - * Find next search match under cursor, cursor at end. - * Used while an operator is pending, and in Visual mode. - */ -int -current_search( - long count, - bool forward // true for forward, false for backward -) +/// Find next search match under cursor, cursor at end. +/// Used while an operator is pending, and in Visual mode. +/// +/// @param forward true for forward, false for backward +int current_search(long count, bool forward) { bool old_p_ws = p_ws; pos_T save_VIsual = VIsual; - /* Correct cursor when 'selection' is exclusive */ - if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) + // Correct cursor when 'selection' is exclusive + if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) { dec_cursor(); + } pos_T end_pos; // end position of the pattern match pos_T orig_pos; // position of the cursor at beginning @@ -4156,7 +4274,7 @@ current_search( // When searching forward and the cursor is at the start of the Visual // area, skip the first search backward, otherwise it doesn't move. const bool skip_first_backward = forward && VIsual_active - && lt(curwin->w_cursor, VIsual); + && lt(curwin->w_cursor, VIsual); orig_pos = pos = curwin->w_cursor; if (VIsual_active) { @@ -4214,8 +4332,9 @@ current_search( // selection works. if (i == 1 && !result) { // not found, abort */ curwin->w_cursor = orig_pos; - if (VIsual_active) + if (VIsual_active) { VIsual = save_VIsual; + } return FAIL; } else if (i == 0 && !result) { if (forward) { // try again from start of buffer @@ -4223,8 +4342,7 @@ current_search( } 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 = (colnr_T)STRLEN(ml_get(curwin->w_buffer->b_ml.ml_line_count)); } } } @@ -4277,8 +4395,7 @@ current_search( /// else from position "cur". /// "direction" is FORWARD or BACKWARD. /// Returns TRUE, FALSE or -1 for failure. -static int -is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direction) +static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direction) { regmmatch_T regmatch; int nmatched = 0; @@ -4292,8 +4409,9 @@ is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direction) } if (search_regcomp(pattern, RE_SEARCH, RE_SEARCH, - SEARCH_KEEP, ®match) == FAIL) + SEARCH_KEEP, ®match) == FAIL) { return -1; + } // init startcol correctly regmatch.startpos[0].col = -1; @@ -4340,7 +4458,7 @@ is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direction) */ int linewhite(linenr_T lnum) { - char_u *p; + char_u *p; p = skipwhite(ml_get(lnum)); return *p == NUL; @@ -4348,64 +4466,63 @@ int 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_u *msgbuf, - bool recompute, int maxcount, long timeout) +static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool show_top_bot_msg, + char_u *msgbuf, bool recompute, int maxcount, long timeout) { - searchstat_T stat; - - update_search_stat(dirc, pos, cursor_pos, &stat, recompute, maxcount, - timeout); - if (stat.cur > 0) { - char t[SEARCH_STAT_BUF_LEN]; - - if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { - if (stat.incomplete == 1) { - 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); - } else if (stat.cnt > maxcount) { - 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); - } + searchstat_T stat; + + update_search_stat(dirc, pos, cursor_pos, &stat, recompute, maxcount, + timeout); + if (stat.cur > 0) { + char t[SEARCH_STAT_BUF_LEN]; + + if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { + if (stat.incomplete == 1) { + 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); + } else if (stat.cnt > maxcount) { + vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]", + maxcount, stat.cur); } else { - if (stat.incomplete == 1) { - 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); - } else if (stat.cnt > maxcount) { - 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); - } + vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]", + stat.cnt, stat.cur); } - - size_t len = strlen(t); - if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) { - memmove(t + 2, t, len); - t[0] = 'W'; - t[1] = ' '; - len += 2; + } else { + if (stat.incomplete == 1) { + 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); + } else if (stat.cnt > maxcount) { + 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); } + } - memmove(msgbuf + STRLEN(msgbuf) - len, t, len); - if (dirc == '?' && stat.cur == maxcount + 1) { - stat.cur = -1; - } + size_t len = strlen(t); + if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN) { + memmove(t + 2, t, len); + t[0] = 'W'; + t[1] = ' '; + len += 2; + } - // keep the message even after redraw, but don't put in history - msg_hist_off = true; - msg_ext_set_kind("search_count"); - give_warning(msgbuf, false); - msg_hist_off = false; + memmove(msgbuf + STRLEN(msgbuf) - len, t, len); + if (dirc == '?' && stat.cur == maxcount + 1) { + stat.cur = -1; } + + // keep the message even after redraw, but don't put in history + msg_hist_off = true; + msg_ext_set_kind("search_count"); + give_warning(msgbuf, false); + msg_hist_off = false; + } } // Add the search count information to "stat". @@ -4414,260 +4531,255 @@ static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, // dirc == 0: don't find the next/previous match (only set the result to "stat") // dirc == '/': find the next match // dirc == '?': find the previous match -static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, - searchstat_T *stat, bool recompute, int maxcount, - long timeout) +static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchstat_T *stat, + bool recompute, int maxcount, long timeout) { - int save_ws = p_ws; - bool wraparound = false; - pos_T p = (*pos); - static pos_T lastpos = { 0, 0, 0 }; - static int cur = 0; - static int cnt = 0; - static bool exact_match = false; - static int incomplete = 0; - static int last_maxcount = SEARCH_STAT_DEF_MAX_COUNT; - static int chgtick = 0; - static char_u *lastpat = NULL; - static buf_T *lbuf = NULL; - proftime_T start; - - memset(stat, 0, sizeof(searchstat_T)); - - if (dirc == 0 && !recompute && !EMPTY_POS(lastpos)) { - stat->cur = cur; - stat->cnt = cnt; - stat->exact_match = exact_match; - stat->incomplete = incomplete; - stat->last_maxcount = last_maxcount; - return; - } - last_maxcount = maxcount; - wraparound = ((dirc == '?' && lt(lastpos, p)) - || (dirc == '/' && lt(p, lastpos))); - - // If anything relevant changed the count has to be recomputed. - // STRNICMP ignores case, but we should not ignore case. - // Unfortunately, there is no STRNICMP function. - // XXX: above comment should be "no MB_STRCMP function" ? - if (!(chgtick == buf_get_changedtick(curbuf) - && lastpat != NULL // supress clang/NULL passed as nonnull parameter - && STRNICMP(lastpat, spats[last_idx].pat, STRLEN(lastpat)) == 0 - && STRLEN(lastpat) == STRLEN(spats[last_idx].pat) - && equalpos(lastpos, *cursor_pos) - && lbuf == curbuf) - || wraparound || cur < 0 || (maxcount > 0 && cur > maxcount) - || recompute) { - cur = 0; - cnt = 0; - exact_match = false; - incomplete = 0; - clearpos(&lastpos); - lbuf = curbuf; - } - - if (equalpos(lastpos, *cursor_pos) && !wraparound - && (dirc == 0 || dirc == '/' ? cur < cnt : cur > 0)) { - cur += dirc == 0 ? 0 : dirc == '/' ? 1 : -1; - } else { - bool done_search = false; - pos_T endpos = { 0, 0, 0 }; - p_ws = false; - if (timeout > 0) { - start = profile_setlimit(timeout); - } - while (!got_int && searchit(curwin, curbuf, &lastpos, &endpos, - FORWARD, NULL, 1, SEARCH_KEEP, RE_LAST, - NULL) != FAIL) { - done_search = true; - // Stop after passing the time limit. - if (timeout > 0 && profile_passed_limit(start)) { - incomplete = 1; - break; - } - cnt++; - if (ltoreq(lastpos, p)) { - cur = cnt; - if (lt(p, endpos)) { - exact_match = true; - } - } - fast_breakcheck(); - if (maxcount > 0 && cnt > maxcount) { - incomplete = 2; // max count exceeded - break; - } - } - if (got_int) { - cur = -1; // abort - } - if (done_search) { - xfree(lastpat); - lastpat = vim_strsave(spats[last_idx].pat); - chgtick = buf_get_changedtick(curbuf); - lbuf = curbuf; - lastpos = p; - } - } + int save_ws = p_ws; + bool wraparound = false; + pos_T p = (*pos); + static pos_T lastpos = { 0, 0, 0 }; + static int cur = 0; + static int cnt = 0; + static bool exact_match = false; + static int incomplete = 0; + static int last_maxcount = SEARCH_STAT_DEF_MAX_COUNT; + static int chgtick = 0; + static char_u *lastpat = NULL; + static buf_T *lbuf = NULL; + proftime_T start; + + memset(stat, 0, sizeof(searchstat_T)); + + if (dirc == 0 && !recompute && !EMPTY_POS(lastpos)) { stat->cur = cur; stat->cnt = cnt; stat->exact_match = exact_match; stat->incomplete = incomplete; stat->last_maxcount = last_maxcount; - p_ws = save_ws; + return; + } + last_maxcount = maxcount; + wraparound = ((dirc == '?' && lt(lastpos, p)) + || (dirc == '/' && lt(p, lastpos))); + + // If anything relevant changed the count has to be recomputed. + // STRNICMP ignores case, but we should not ignore case. + // Unfortunately, there is no STRNICMP function. + // XXX: above comment should be "no MB_STRCMP function" ? + if (!(chgtick == buf_get_changedtick(curbuf) + && lastpat != NULL // suppress clang/NULL passed as nonnull parameter + && STRNICMP(lastpat, spats[last_idx].pat, STRLEN(lastpat)) == 0 + && STRLEN(lastpat) == STRLEN(spats[last_idx].pat) + && equalpos(lastpos, *cursor_pos) + && lbuf == curbuf) + || wraparound || cur < 0 || (maxcount > 0 && cur > maxcount) + || recompute) { + cur = 0; + cnt = 0; + exact_match = false; + incomplete = 0; + clearpos(&lastpos); + lbuf = curbuf; + } + + if (equalpos(lastpos, *cursor_pos) && !wraparound + && (dirc == 0 || dirc == '/' ? cur < cnt : cur > 0)) { + cur += dirc == 0 ? 0 : dirc == '/' ? 1 : -1; + } else { + bool done_search = false; + pos_T endpos = { 0, 0, 0 }; + p_ws = false; + if (timeout > 0) { + start = profile_setlimit(timeout); + } + while (!got_int && searchit(curwin, curbuf, &lastpos, &endpos, + FORWARD, NULL, 1, SEARCH_KEEP, RE_LAST, + NULL) != FAIL) { + done_search = true; + // Stop after passing the time limit. + if (timeout > 0 && profile_passed_limit(start)) { + incomplete = 1; + break; + } + cnt++; + if (ltoreq(lastpos, p)) { + cur = cnt; + if (lt(p, endpos)) { + exact_match = true; + } + } + fast_breakcheck(); + if (maxcount > 0 && cnt > maxcount) { + incomplete = 2; // max count exceeded + break; + } + } + if (got_int) { + cur = -1; // abort + } + if (done_search) { + xfree(lastpat); + lastpat = vim_strsave(spats[last_idx].pat); + chgtick = buf_get_changedtick(curbuf); + lbuf = curbuf; + lastpos = p; + } + } + stat->cur = cur; + stat->cnt = cnt; + stat->exact_match = exact_match; + stat->incomplete = incomplete; + stat->last_maxcount = last_maxcount; + p_ws = save_ws; } // "searchcount()" function void f_searchcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - pos_T pos = curwin->w_cursor; - char_u *pattern = NULL; - int maxcount = SEARCH_STAT_DEF_MAX_COUNT; - long timeout = SEARCH_STAT_DEF_TIMEOUT; - bool recompute = true; - searchstat_T stat; + pos_T pos = curwin->w_cursor; + char_u *pattern = NULL; + int maxcount = SEARCH_STAT_DEF_MAX_COUNT; + long timeout = SEARCH_STAT_DEF_TIMEOUT; + bool recompute = true; + searchstat_T stat; - tv_dict_alloc_ret(rettv); + tv_dict_alloc_ret(rettv); - if (shortmess(SHM_SEARCHCOUNT)) { // 'shortmess' contains 'S' flag - recompute = true; - } + if (shortmess(SHM_SEARCHCOUNT)) { // 'shortmess' contains 'S' flag + recompute = true; + } - if (argvars[0].v_type != VAR_UNKNOWN) { - dict_T *dict; - dictitem_T *di; - listitem_T *li; - bool error = false; + if (argvars[0].v_type != VAR_UNKNOWN) { + dict_T *dict; + dictitem_T *di; + listitem_T *li; + bool error = false; - if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL) { - EMSG(_(e_dictreq)); + if (argvars[0].v_type != VAR_DICT || argvars[0].vval.v_dict == NULL) { + EMSG(_(e_dictreq)); + return; + } + dict = argvars[0].vval.v_dict; + di = tv_dict_find(dict, (const char *)"timeout", -1); + if (di != NULL) { + timeout = (long)tv_get_number_chk(&di->di_tv, &error); + if (error) { return; } - dict = argvars[0].vval.v_dict; - di = tv_dict_find(dict, (const char *)"timeout", -1); - if (di != NULL) { - timeout = (long)tv_get_number_chk(&di->di_tv, &error); - if (error) { - return; - } + } + di = tv_dict_find(dict, (const char *)"maxcount", -1); + if (di != NULL) { + maxcount = (int)tv_get_number_chk(&di->di_tv, &error); + if (error) { + return; } - di = tv_dict_find(dict, (const char *)"maxcount", -1); - if (di != NULL) { - maxcount = (int)tv_get_number_chk(&di->di_tv, &error); - if (error) { - return; - } + } + di = tv_dict_find(dict, (const char *)"recompute", -1); + if (di != NULL) { + recompute = tv_get_number_chk(&di->di_tv, &error); + if (error) { + return; + } + } + di = tv_dict_find(dict, (const char *)"pattern", -1); + if (di != NULL) { + pattern = (char_u *)tv_get_string_chk(&di->di_tv); + if (pattern == NULL) { + return; } - di = tv_dict_find(dict, (const char *)"recompute", -1); - if (di != NULL) { - recompute = tv_get_number_chk(&di->di_tv, &error); + } + di = tv_dict_find(dict, (const char *)"pos", -1); + if (di != NULL) { + if (di->di_tv.v_type != VAR_LIST) { + EMSG2(_(e_invarg2), "pos"); + return; + } + if (tv_list_len(di->di_tv.vval.v_list) != 3) { + EMSG2(_(e_invarg2), "List format should be [lnum, col, off]"); + return; + } + li = tv_list_find(di->di_tv.vval.v_list, 0L); + if (li != NULL) { + pos.lnum = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); if (error) { return; } } - di = tv_dict_find(dict, (const char *)"pattern", -1); - if (di != NULL) { - pattern = (char_u *)tv_get_string_chk(&di->di_tv); - if (pattern == NULL) { + li = tv_list_find(di->di_tv.vval.v_list, 1L); + if (li != NULL) { + pos.col = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error) - 1; + if (error) { return; } } - di = tv_dict_find(dict, (const char *)"pos", -1); - if (di != NULL) { - if (di->di_tv.v_type != VAR_LIST) { - EMSG2(_(e_invarg2), "pos"); - return; - } - if (tv_list_len(di->di_tv.vval.v_list) != 3) { - EMSG2(_(e_invarg2), "List format should be [lnum, col, off]"); + li = tv_list_find(di->di_tv.vval.v_list, 2L); + if (li != NULL) { + pos.coladd = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); + if (error) { return; } - li = tv_list_find(di->di_tv.vval.v_list, 0L); - if (li != NULL) { - pos.lnum = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); - if (error) { - return; - } - } - li = tv_list_find(di->di_tv.vval.v_list, 1L); - if (li != NULL) { - pos.col = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error) - 1; - if (error) { - return; - } - } - li = tv_list_find(di->di_tv.vval.v_list, 2L); - if (li != NULL) { - pos.coladd = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); - if (error) { - return; - } - } } } + } - save_last_search_pattern(); - if (pattern != NULL) { - if (*pattern == NUL) { - goto the_end; - } - xfree(spats[last_idx].pat); - spats[last_idx].pat = vim_strsave(pattern); - } - if (spats[last_idx].pat == NULL || *spats[last_idx].pat == NUL) { - goto the_end; // the previous pattern was never defined + save_last_search_pattern(); + if (pattern != NULL) { + if (*pattern == NUL) { + goto the_end; } + xfree(spats[last_idx].pat); + spats[last_idx].pat = vim_strsave(pattern); + } + if (spats[last_idx].pat == NULL || *spats[last_idx].pat == NUL) { + goto the_end; // the previous pattern was never defined + } - update_search_stat(0, &pos, &pos, &stat, recompute, maxcount, timeout); + update_search_stat(0, &pos, &pos, &stat, recompute, maxcount, timeout); - tv_dict_add_nr(rettv->vval.v_dict, S_LEN("current"), stat.cur); - tv_dict_add_nr(rettv->vval.v_dict, S_LEN("total"), stat.cnt); - tv_dict_add_nr(rettv->vval.v_dict, S_LEN("exact_match"), stat.exact_match); - tv_dict_add_nr(rettv->vval.v_dict, S_LEN("incomplete"), stat.incomplete); - tv_dict_add_nr(rettv->vval.v_dict, S_LEN("maxcount"), stat.last_maxcount); + tv_dict_add_nr(rettv->vval.v_dict, S_LEN("current"), stat.cur); + tv_dict_add_nr(rettv->vval.v_dict, S_LEN("total"), stat.cnt); + tv_dict_add_nr(rettv->vval.v_dict, S_LEN("exact_match"), stat.exact_match); + tv_dict_add_nr(rettv->vval.v_dict, S_LEN("incomplete"), stat.incomplete); + tv_dict_add_nr(rettv->vval.v_dict, S_LEN("maxcount"), stat.last_maxcount); the_end: - restore_last_search_pattern(); + restore_last_search_pattern(); } -/* - * Find identifiers or defines in included files. - * If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase. - */ -void -find_pattern_in_path( - char_u *ptr, // pointer to search pattern - Direction dir, // direction of expansion - size_t len, // length of search pattern - bool whole, // match whole words only - bool skip_comments, // don't match inside comments - int type, // Type of search; are we looking for a type? - // a macro? - long count, - int action, // What to do when we find it - linenr_T start_lnum, // first line to start searching - linenr_T end_lnum // last line for searching -) +/// Find identifiers or defines in included files. +/// If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase. +/// +/// @param ptr pointer to search pattern +/// @param dir direction of expansion +/// @param len length of search pattern +/// @param whole match whole words only +/// @param skip_comments don't match inside comments +/// @param type Type of search; are we looking for a type? a macro? +/// @param action What to do when we find it +/// @param start_lnum first line to start searching +/// @param end_lnum last line for searching +void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bool skip_comments, + int type, long count, int action, linenr_T start_lnum, linenr_T end_lnum) { - SearchedFile *files; /* Stack of included files */ - SearchedFile *bigger; /* When we need more space */ + SearchedFile *files; // Stack of included files + SearchedFile *bigger; // When we need more space int max_path_depth = 50; long match_count = 1; - char_u *pat; - char_u *new_fname; - char_u *curr_fname = curbuf->b_fname; - char_u *prev_fname = NULL; + char_u *pat; + char_u *new_fname; + char_u *curr_fname = curbuf->b_fname; + char_u *prev_fname = NULL; linenr_T lnum; int depth; - int depth_displayed; /* For type==CHECK_PATH */ + int depth_displayed; // For type==CHECK_PATH int old_files; int already_searched; - char_u *file_line; - char_u *line; - char_u *p; + char_u *file_line; + char_u *line; + char_u *p; char_u save_char; - int define_matched; + bool define_matched; regmatch_T regmatch; regmatch_T incl_regmatch; regmatch_T def_regmatch; @@ -4675,10 +4787,10 @@ find_pattern_in_path( bool did_show = false; bool found = false; int i; - char_u *already = NULL; - char_u *startp = NULL; - char_u *inc_opt = NULL; - win_T *curwin_save = NULL; + char_u *already = NULL; + char_u *startp = NULL; + char_u *inc_opt = NULL; + win_T *curwin_save = NULL; const int l_g_do_tagpreview = g_do_tagpreview; regmatch.regprog = NULL; @@ -4690,41 +4802,45 @@ find_pattern_in_path( if (type != CHECK_PATH && type != FIND_DEFINE /* when CONT_SOL is set compare "ptr" with the beginning of the line * is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo */ - && !(compl_cont_status & CONT_SOL) - ) { + && !(compl_cont_status & CONT_SOL)) { pat = xmalloc(len + 5); assert(len <= INT_MAX); sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", (int)len, ptr); - /* ignore case according to p_ic, p_scs and pat */ + // ignore case according to p_ic, p_scs and pat regmatch.rm_ic = ignorecase(pat); regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); xfree(pat); - if (regmatch.regprog == NULL) + if (regmatch.regprog == NULL) { goto fpip_end; + } } inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc; if (*inc_opt != NUL) { incl_regmatch.regprog = vim_regcomp(inc_opt, p_magic ? RE_MAGIC : 0); - if (incl_regmatch.regprog == NULL) + if (incl_regmatch.regprog == NULL) { goto fpip_end; - incl_regmatch.rm_ic = FALSE; /* don't ignore case in incl. pat. */ + } + 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, p_magic ? RE_MAGIC : 0); - if (def_regmatch.regprog == NULL) + if (def_regmatch.regprog == NULL) { goto fpip_end; - def_regmatch.rm_ic = FALSE; /* don't ignore case in define pat. */ + } + def_regmatch.rm_ic = FALSE; // don't ignore case in define pat. } files = xcalloc(max_path_depth, sizeof(SearchedFile)); old_files = max_path_depth; depth = depth_displayed = -1; lnum = start_lnum; - if (end_lnum > curbuf->b_ml.ml_line_count) + if (end_lnum > curbuf->b_ml.ml_line_count) { end_lnum = curbuf->b_ml.ml_line_count; - if (lnum > end_lnum) /* do at least one line */ + } + if (lnum > end_lnum) { // do at least one line lnum = end_lnum; + } line = ml_get(lnum); for (;; ) { @@ -4733,17 +4849,18 @@ find_pattern_in_path( char_u *p_fname = (curr_fname == curbuf->b_fname) ? curbuf->b_ffname : curr_fname; - if (inc_opt != NULL && strstr((char *)inc_opt, "\\zs") != NULL) - /* Use text from '\zs' to '\ze' (or end) of 'include'. */ + if (inc_opt != NULL && strstr((char *)inc_opt, "\\zs") != NULL) { + // Use text from '\zs' to '\ze' (or end) of 'include'. new_fname = find_file_name_in_path(incl_regmatch.startp[0], (size_t)(incl_regmatch.endp[0] - incl_regmatch.startp[0]), FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname); - else - /* Use text after match with 'include'. */ + } else { + // Use text after match with 'include'. new_fname = file_name_in_line(incl_regmatch.endp[0], 0, - FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname, NULL); + FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname, NULL); + } already_searched = FALSE; if (new_fname != NULL) { // Check whether we have already searched in this file @@ -4788,15 +4905,17 @@ find_pattern_in_path( did_show = true; while (depth_displayed < depth && !got_int) { ++depth_displayed; - for (i = 0; i < depth_displayed; i++) + for (i = 0; i < depth_displayed; i++) { MSG_PUTS(" "); + } msg_home_replace(files[depth_displayed].name); MSG_PUTS(" -->\n"); } if (!got_int) { /* don't display if 'q' typed for "--more--" message */ - for (i = 0; i <= depth_displayed; i++) + for (i = 0; i <= depth_displayed; i++) { MSG_PUTS(" "); + } if (new_fname != NULL) { /* using "new_fname" is more reliable, e.g., when * 'includeexpr' is set. */ @@ -4808,21 +4927,23 @@ find_pattern_in_path( */ if (inc_opt != NULL && strstr((char *)inc_opt, "\\zs") != NULL) { - /* pattern contains \zs, use the match */ + // pattern contains \zs, use the match p = incl_regmatch.startp[0]; i = (int)(incl_regmatch.endp[0] - incl_regmatch.startp[0]); } else { - /* find the file name after the end of the match */ + // find the file name after the end of the match for (p = incl_regmatch.endp[0]; - *p && !vim_isfilec(*p); p++) + *p && !vim_isfilec(*p); p++) { ; - for (i = 0; vim_isfilec(p[i]); i++) + } + for (i = 0; vim_isfilec(p[i]); i++) { ; + } } if (i == 0) { - /* Nothing found, use the rest of the line. */ + // Nothing found, use the rest of the line. p = incl_regmatch.endp[0]; i = (int)STRLEN(p); } @@ -4833,8 +4954,9 @@ find_pattern_in_path( --p; ++i; } - if (p[i] == '"' || p[i] == '>') + if (p[i] == '"' || p[i] == '>') { ++i; + } } save_char = p[i]; p[i] = NUL; @@ -4843,29 +4965,32 @@ find_pattern_in_path( } if (new_fname == NULL && action == ACTION_SHOW_ALL) { - if (already_searched) + if (already_searched) { MSG_PUTS(_(" (Already listed)")); - else + } else { MSG_PUTS(_(" NOT FOUND")); + } } } - ui_flush(); /* output each line directly */ + ui_flush(); // output each line directly } if (new_fname != NULL) { - /* Push the new file onto the file stack */ + // Push the new file onto the file stack if (depth + 1 == old_files) { bigger = xmalloc(max_path_depth * 2 * sizeof(SearchedFile)); - for (i = 0; i <= depth; i++) + for (i = 0; i <= depth; i++) { bigger[i] = files[i]; + } for (i = depth + 1; i < old_files + max_path_depth; i++) { bigger[i].fp = NULL; bigger[i].name = NULL; bigger[i].lnum = 0; bigger[i].matched = FALSE; } - for (i = old_files; i < max_path_depth; i++) + for (i = old_files; i < max_path_depth; i++) { bigger[i + max_path_depth] = files[i]; + } old_files += max_path_depth; max_path_depth *= 2; xfree(files); @@ -4892,10 +5017,9 @@ find_pattern_in_path( } else if (p_verbose >= 5) { verbose_enter(); smsg(_("Searching included file %s"), - (char *)new_fname); + (char *)new_fname); verbose_leave(); } - } } } else { @@ -4904,7 +5028,7 @@ find_pattern_in_path( */ p = line; search_line: - define_matched = FALSE; + define_matched = false; if (def_regmatch.regprog != NULL && vim_regexec(&def_regmatch, line, (colnr_T)0)) { /* @@ -4913,9 +5037,10 @@ search_line: * don't let it match beyond the end of this identifier. */ p = def_regmatch.endp[0]; - while (*p && !vim_iswordc(*p)) + while (*p && !vim_iswordc(*p)) { p++; - define_matched = TRUE; + } + define_matched = true; } /* @@ -4924,18 +5049,18 @@ search_line: */ if (def_regmatch.regprog == NULL || define_matched) { if (define_matched - || (compl_cont_status & CONT_SOL) - ) { - /* compare the first "len" chars from "ptr" */ + || (compl_cont_status & CONT_SOL)) { + // compare the first "len" chars from "ptr" startp = skipwhite(p); if (p_ic) { matched = !mb_strnicmp(startp, ptr, len); - } - else + } else { matched = !STRNCMP(startp, ptr, len); + } if (matched && define_matched && whole - && vim_iswordc(startp[len])) + && vim_iswordc(startp[len])) { matched = false; + } } else if (regmatch.regprog != NULL && vim_regexec(®match, line, (colnr_T)(p - line))) { matched = true; @@ -4958,7 +5083,7 @@ search_line: */ p = skipwhite(line); if (matched - || (p[0] == '/' && p[1] == '*') || p[0] == '*') + || (p[0] == '/' && p[1] == '*') || p[0] == '*') { for (p = line; *p && p < startp; ++p) { if (matched && p[0] == '/' @@ -4975,6 +5100,7 @@ search_line: p++; } } + } } } } @@ -4982,35 +5108,39 @@ search_line: if (matched) { if (action == ACTION_EXPAND) { bool cont_s_ipos = false; - char_u *aux; + char_u *aux; - if (depth == -1 && lnum == curwin->w_cursor.lnum) + if (depth == -1 && lnum == curwin->w_cursor.lnum) { break; + } found = true; aux = p = startp; if (compl_cont_status & CONT_ADDING) { p += compl_length; - if (vim_iswordp(p)) + if (vim_iswordp(p)) { goto exit_matched; + } p = find_word_start(p); } p = find_word_end(p); i = (int)(p - aux); if ((compl_cont_status & CONT_ADDING) && i == compl_length) { - /* IOSIZE > compl_length, so the STRNCPY works */ + // IOSIZE > compl_length, so the STRNCPY works STRNCPY(IObuff, aux, i); /* Get the next line: when "depth" < 0 from the current * buffer, otherwise from the included file. Jump to * exit_matched when past the last line. */ if (depth < 0) { - if (lnum >= end_lnum) + if (lnum >= end_lnum) { goto exit_matched; + } line = ml_get(++lnum); } else if (vim_fgets(line = file_line, - LSIZE, files[depth].fp)) + LSIZE, files[depth].fp)) { goto exit_matched; + } /* we read a line, set "already" to check this "line" later * if depth >= 0 we'll increase files[depth].lnum far @@ -5020,9 +5150,10 @@ search_line: p = find_word_end(p); if (p > aux) { if (*aux != ')' && IObuff[i-1] != TAB) { - if (IObuff[i-1] != ' ') + if (IObuff[i-1] != ' ') { IObuff[i++] = ' '; - /* IObuf =~ "\(\k\|\i\).* ", thus i >= 2*/ + } + // IObuf =~ "\(\k\|\i\).* ", thus i >= 2 if (p_js && (IObuff[i-2] == '.' || IObuff[i-2] == '?' @@ -5030,9 +5161,10 @@ search_line: IObuff[i++] = ' '; } } - /* copy as much as possible of the new word */ - if (p - aux >= IOSIZE - i) + // copy as much as possible of the new word + if (p - aux >= IOSIZE - i) { p = aux + IOSIZE - i - 1; + } STRNCPY(IObuff + i, aux, p - aux); i += (int)(p - aux); cont_s_ipos = true; @@ -5040,13 +5172,14 @@ search_line: IObuff[i] = NUL; aux = IObuff; - if (i == compl_length) + if (i == compl_length) { goto exit_matched; + } } - const int add_r = ins_compl_add_infercase( - aux, i, p_ic, curr_fname == curbuf->b_fname ? NULL : curr_fname, - dir, cont_s_ipos); + const int add_r = ins_compl_add_infercase(aux, i, p_ic, + curr_fname == curbuf->b_fname ? NULL : curr_fname, + dir, cont_s_ipos); if (add_r == OK) { // if dir was BACKWARD then honor it just once dir = FORWARD; @@ -5059,11 +5192,13 @@ search_line: gotocmdline(true); // cursor at status line } if (curr_fname != prev_fname) { - if (did_show) - msg_putchar('\n'); /* cursor below last one */ - if (!got_int) /* don't display if 'q' typed + if (did_show) { + msg_putchar('\n'); // cursor below last one + } + if (!got_int) { /* don't display if 'q' typed at "--more--" message */ msg_home_replace_hl(curr_fname); + } prev_fname = curr_fname; } did_show = true; @@ -5076,8 +5211,9 @@ search_line: /* Set matched flag for this file and all the ones that * include it */ - for (i = 0; i <= depth; ++i) + for (i = 0; i <= depth; ++i) { files[i].matched = TRUE; + } } else if (--count <= 0) { found = true; if (depth == -1 && lnum == curwin->w_cursor.lnum @@ -5089,14 +5225,15 @@ search_line: (depth == -1) ? &lnum : &files[depth].lnum, 1L); did_show = true; } else { - /* ":psearch" uses the preview window */ + // ":psearch" uses the preview window if (l_g_do_tagpreview != 0) { curwin_save = curwin; prepare_tagpreview(true); } if (action == ACTION_SPLIT) { - if (win_split(0, 0) == FAIL) + if (win_split(0, 0) == FAIL) { break; + } RESET_BINDING(curwin); } if (depth == -1) { @@ -5128,7 +5265,7 @@ search_line: if (l_g_do_tagpreview != 0 && curwin != curwin_save && win_valid(curwin_save)) { - /* Return cursor to where we were */ + // Return cursor to where we were validate_cursor(); redraw_later(curwin, VALID); win_enter(curwin_save, true); @@ -5148,10 +5285,12 @@ exit_matched: } } line_breakcheck(); - if (action == ACTION_EXPAND) + if (action == ACTION_EXPAND) { ins_compl_check_keys(30, false); - if (got_int || compl_interrupted) + } + if (got_int || compl_interrupted) { break; + } /* * Read the next line. When reading an included file and encountering @@ -5166,55 +5305,62 @@ exit_matched: files[old_files].matched = files[depth].matched; --depth; curr_fname = (depth == -1) ? curbuf->b_fname - : files[depth].name; - if (depth < depth_displayed) + : files[depth].name; + if (depth < depth_displayed) { depth_displayed = depth; + } } - if (depth >= 0) { /* we could read the line */ + if (depth >= 0) { // we could read the line files[depth].lnum++; - /* Remove any CR and LF from the line. */ + // Remove any CR and LF from the line. i = (int)STRLEN(line); - if (i > 0 && line[i - 1] == '\n') + if (i > 0 && line[i - 1] == '\n') { line[--i] = NUL; - if (i > 0 && line[i - 1] == '\r') + } + if (i > 0 && line[i - 1] == '\r') { line[--i] = NUL; + } } else if (!already) { - if (++lnum > end_lnum) + if (++lnum > end_lnum) { break; + } line = ml_get(lnum); } already = NULL; } - /* End of big for (;;) loop. */ + // End of big for (;;) loop. - /* Close any files that are still open. */ + // Close any files that are still open. for (i = 0; i <= depth; i++) { fclose(files[i].fp); xfree(files[i].name); } - for (i = old_files; i < max_path_depth; i++) + for (i = old_files; i < max_path_depth; i++) { xfree(files[i].name); + } xfree(files); if (type == CHECK_PATH) { if (!did_show) { - if (action != ACTION_SHOW_ALL) + if (action != ACTION_SHOW_ALL) { MSG(_("All included files were found")); - else + } else { MSG(_("No included files")); + } } } else if (!found - && action != ACTION_EXPAND - ) { - if (got_int || compl_interrupted) + && action != ACTION_EXPAND) { + if (got_int || compl_interrupted) { EMSG(_(e_interr)); - else if (type == FIND_DEFINE) + } else if (type == FIND_DEFINE) { EMSG(_("E388: Couldn't find definition")); - else + } else { EMSG(_("E389: Couldn't find pattern")); + } } - if (action == ACTION_SHOW || action == ACTION_SHOW_ALL) + if (action == ACTION_SHOW || action == ACTION_SHOW_ALL) { msg_end(); + } fpip_end: xfree(file_line); @@ -5223,11 +5369,11 @@ fpip_end: vim_regfree(def_regmatch.regprog); } -static void show_pat_in_path(char_u *line, int type, bool did_show, int action, - FILE *fp, linenr_T *lnum, long count) +static void show_pat_in_path(char_u *line, int type, bool did_show, int action, FILE *fp, + linenr_T *lnum, long count) FUNC_ATTR_NONNULL_ARG(1, 6) { - char_u *p; + char_u *p; if (did_show) { msg_putchar('\n'); // cursor below last one @@ -5240,11 +5386,13 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action, for (;; ) { p = line + STRLEN(line) - 1; if (fp != NULL) { - /* We used fgets(), so get rid of newline at end */ - if (p >= line && *p == '\n') + // We used fgets(), so get rid of newline at end + if (p >= line && *p == '\n') { --p; - if (p >= line && *p == '\r') + } + if (p >= line && *p == '\r') { --p; + } *(p + 1) = NUL; } if (action == ACTION_SHOW_ALL) { @@ -5256,19 +5404,22 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action, msg_puts(" "); } msg_prt_line(line, FALSE); - ui_flush(); /* show one line at a time */ + ui_flush(); // show one line at a time - /* Definition continues until line that doesn't end with '\' */ - if (got_int || type != FIND_DEFINE || p < line || *p != '\\') + // Definition continues until line that doesn't end with '\' + if (got_int || type != FIND_DEFINE || p < line || *p != '\\') { break; + } if (fp != NULL) { - if (vim_fgets(line, LSIZE, fp)) /* end of file */ + if (vim_fgets(line, LSIZE, fp)) { // end of file break; + } ++*lnum; } else { - if (++*lnum > curbuf->b_ml.ml_line_count) + if (++*lnum > curbuf->b_ml.ml_line_count) { break; + } line = ml_get(*lnum); } msg_putchar('\n'); diff --git a/src/nvim/search.h b/src/nvim/search.h index 98ddaa5eeb..0dbaf79c37 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -88,7 +88,7 @@ typedef struct searchstat { int cur; // current position of found words int cnt; // total count of found words - int exact_match; // TRUE if matched exactly on specified position + bool exact_match; // true if matched exactly on specified position int incomplete; // 0: search was fully completed // 1: recomputing was timed out // 2: max count exceeded diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 15fd25e096..c6f59b42b8 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -115,10 +115,10 @@ static void sign_group_unref(char_u *groupname) } } -/// Returns TRUE if 'sign' is in 'group'. +/// @return true if 'sign' is in 'group'. /// A sign can either be in the global group (sign->group == NULL) /// or in a named group. If 'group' is '*', then the sign is part of the group. -int sign_in_group(sign_entry_T *sign, const char_u *group) +bool sign_in_group(sign_entry_T *sign, const char_u *group) { return ((group != NULL && STRCMP(group, "*") == 0) || (group == NULL && sign->se_group == NULL) @@ -143,7 +143,7 @@ int sign_group_get_next_signid(buf_T *buf, const char_u *groupname) group = HI2SG(hi); } - // Search for the next usuable sign identifier + // Search for the next usable sign identifier while (!found) { if (group == NULL) { id = next_sign_id++; // global group @@ -194,7 +194,7 @@ static void insert_sign( if (next != NULL) { next->se_prev = newsign; } - buf->b_signcols_max = -1; + buf->b_signcols_valid = false; if (prev == NULL) { // When adding first sign need to redraw the windows to create the @@ -534,7 +534,7 @@ linenr_T buf_delsign( sign_entry_T *next; // the next sign in a b_signlist linenr_T lnum; // line number whose sign was deleted - buf->b_signcols_max = -1; + buf->b_signcols_valid = false; lastp = &buf->b_signlist; lnum = 0; for (sign = buf->b_signlist; sign != NULL; sign = next) { @@ -668,7 +668,7 @@ void buf_delete_signs(buf_T *buf, char_u *group) lastp = &sign->se_next; } } - buf->b_signcols_max = -1; + buf->b_signcols_valid = false; } /// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. @@ -735,7 +735,7 @@ void sign_mark_adjust( int is_fixed = 0; int signcol = win_signcol_configured(curwin, &is_fixed); - curbuf->b_signcols_max = -1; + curbuf->b_signcols_valid = false; lastp = &curbuf->b_signlist; for (sign = curbuf->b_signlist; sign != NULL; sign = next) { diff --git a/src/nvim/spell.c b/src/nvim/spell.c index d1428b0117..610a359141 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -441,7 +441,8 @@ size_t spell_check( MB_PTR_ADV(mi.mi_fend); } - (void)spell_casefold(ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, MAXWLEN + 1); + (void)spell_casefold(wp, ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, + MAXWLEN + 1); mi.mi_fwordlen = (int)STRLEN(mi.mi_fword); if (camel_case) { @@ -869,10 +870,11 @@ static void find_word(matchinf_T *mip, int mode) if (slang->sl_compsylmax < MAXWLEN) { // "fword" is only needed for checking syllables. - if (ptr == mip->mi_word) - (void)spell_casefold(ptr, wlen, fword, MAXWLEN); - else + if (ptr == mip->mi_word) { + (void)spell_casefold(mip->mi_win, ptr, wlen, fword, MAXWLEN); + } else { STRLCPY(fword, ptr, endlen[endidxcnt] + 1); + } } if (!can_compound(slang, fword, mip->mi_compflags)) continue; @@ -1315,9 +1317,9 @@ static int fold_more(matchinf_T *mip) MB_PTR_ADV(mip->mi_fend); } - (void)spell_casefold(p, (int)(mip->mi_fend - p), - mip->mi_fword + mip->mi_fwordlen, - MAXWLEN - mip->mi_fwordlen); + (void)spell_casefold(mip->mi_win, p, (int)(mip->mi_fend - p), + mip->mi_fword + mip->mi_fwordlen, + MAXWLEN - mip->mi_fwordlen); flen = (int)STRLEN(mip->mi_fword + mip->mi_fwordlen); mip->mi_fwordlen += flen; return flen; @@ -1341,7 +1343,7 @@ static bool no_spell_checking(win_T *wp) { if (!wp->w_p_spell || *wp->w_s->b_p_spl == NUL || GA_EMPTY(&wp->w_s->b_langp)) { - EMSG(_("E756: Spell checking is not enabled")); + EMSG(_(e_no_spell)); return true; } return false; @@ -1397,7 +1399,7 @@ spell_move_to ( clearpos(&found_pos); while (!got_int) { - line = ml_get_buf(wp->w_buffer, lnum, FALSE); + line = ml_get_buf(wp->w_buffer, lnum, false); len = STRLEN(line); if (buflen < len + MAXWLEN + 2) { @@ -1423,7 +1425,7 @@ spell_move_to ( // Need to get the line again, may have looked at the previous // one. - line = ml_get_buf(wp->w_buffer, lnum, FALSE); + line = ml_get_buf(wp->w_buffer, lnum, false); } // Copy the line into "buf" and append the start of the next line if @@ -1431,7 +1433,7 @@ spell_move_to ( STRCPY(buf, line); if (lnum < wp->w_buffer->b_ml.ml_line_count) spell_cat_line(buf + STRLEN(buf), - ml_get_buf(wp->w_buffer, lnum + 1, FALSE), + ml_get_buf(wp->w_buffer, lnum + 1, false), MAXWLEN); p = buf + skip; endp = buf + len; @@ -2655,7 +2657,9 @@ static bool spell_iswordp_w(const int *p, const win_T *wp) // Uses the character definitions from the .spl file. // When using a multi-byte 'encoding' the length may change! // Returns FAIL when something wrong. -int spell_casefold(char_u *str, int len, char_u *buf, int buflen) +int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf, + int buflen) + FUNC_ATTR_NONNULL_ALL { if (len >= buflen) { buf[0] = NUL; @@ -2670,8 +2674,22 @@ int spell_casefold(char_u *str, int len, char_u *buf, int buflen) buf[outi] = NUL; return FAIL; } - const int c = mb_cptr2char_adv((const char_u **)&p); - outi += utf_char2bytes(SPELL_TOFOLD(c), buf + outi); + int c = mb_cptr2char_adv((const char_u **)&p); + + // Exception: greek capital sigma 0x03A3 folds to 0x03C3, except + // when it is the last character in a word, then it folds to + // 0x03C2. + if (c == 0x03a3 || c == 0x03c2) { + if (p == str + len || !spell_iswordp(p, wp)) { + c = 0x03c2; + } else { + c = 0x03c3; + } + } else { + c = SPELL_TOFOLD(c); + } + + outi += utf_char2bytes(c, buf + outi); } buf[outi] = NUL; @@ -2753,9 +2771,17 @@ void spell_suggest(int count) int selected = count; int badlen = 0; int msg_scroll_save = msg_scroll; + const int wo_spell_save = curwin->w_p_spell; + + if (!curwin->w_p_spell) { + did_set_spelllang(curwin); + curwin->w_p_spell = true; + } - if (no_spell_checking(curwin)) + if (*curwin->w_s->b_p_spl == NUL) { + EMSG(_(e_no_spell)); return; + } if (VIsual_active) { // Use the Visually selected text as the bad word. But reject @@ -2948,6 +2974,7 @@ void spell_suggest(int count) spell_find_cleanup(&sug); xfree(line); + curwin->w_p_spell = wo_spell_save; } // Check if the word at line "lnum" column "col" is required to start with a @@ -3155,7 +3182,8 @@ spell_find_suggest ( if (su->su_badlen >= MAXWLEN) su->su_badlen = MAXWLEN - 1; // just in case STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1); - (void)spell_casefold(su->su_badptr, su->su_badlen, su->su_fbadword, MAXWLEN); + (void)spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword, + MAXWLEN); // TODO(vim): make this work if the case-folded text is longer than the // original text. Currently an illegal byte causes wrong pointer @@ -3535,7 +3563,7 @@ static void suggest_try_change(suginfo_T *su) STRCPY(fword, su->su_fbadword); n = (int)STRLEN(fword); p = su->su_badptr + su->su_badlen; - (void)spell_casefold(p, (int)STRLEN(p), fword + n, MAXWLEN - n); + (void)spell_casefold(curwin, p, (int)STRLEN(p), fword + n, MAXWLEN - n); for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) { lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); @@ -5087,7 +5115,7 @@ stp_sal_score ( pbad = badsound; else { // soundfold the bad word with more characters following - (void)spell_casefold(su->su_badptr, stp->st_orglen, fword, MAXWLEN); + (void)spell_casefold(curwin, su->su_badptr, stp->st_orglen, fword, MAXWLEN); // When joining two words the sound often changes a lot. E.g., "t he" // sounds like "t h" while "the" sounds like "@". Avoid that by @@ -5742,7 +5770,9 @@ cleanup_suggestions ( xfree(stp[i].st_word); } gap->ga_len = keep; - return stp[keep - 1].st_score; + if (keep >= 1) { + return stp[keep - 1].st_score; + } } } return maxscore; @@ -5800,10 +5830,10 @@ void spell_soundfold(slang_T *slang, char_u *inword, bool folded, char_u *res) spell_soundfold_sofo(slang, inword, res); else { // SAL items used. Requires the word to be case-folded. - if (folded) + if (folded) { word = inword; - else { - (void)spell_casefold(inword, (int)STRLEN(inword), fword, MAXWLEN); + } else { + (void)spell_casefold(curwin, inword, (int)STRLEN(inword), fword, MAXWLEN); word = fword; } @@ -6642,7 +6672,7 @@ void ex_spelldump(exarg_T *eap) set_option_value("spl", dummy, (char *)spl, OPT_LOCAL); xfree(spl); - if (!BUFEMPTY()) { + if (!buf_is_empty(curbuf)) { return; } diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h index e2c9ab7ae8..f07f5673f9 100644 --- a/src/nvim/spell_defs.h +++ b/src/nvim/spell_defs.h @@ -284,4 +284,11 @@ extern int did_set_spelltab; extern char *e_format; +// Values for "what" argument of spell_add_word() +typedef enum { + SPELL_ADD_GOOD = 0, + SPELL_ADD_BAD = 1, + SPELL_ADD_RARE = 2, +} SpellAddType; + #endif // NVIM_SPELL_DEFS_H diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 3c125959a9..8b95178c84 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -2942,9 +2942,9 @@ static void add_fromto(spellinfo_T *spin, garray_T *gap, char_u *from, char_u *t char_u word[MAXWLEN]; fromto_T *ftp = GA_APPEND_VIA_PTR(fromto_T, gap); - (void)spell_casefold(from, (int)STRLEN(from), word, MAXWLEN); + (void)spell_casefold(curwin, from, (int)STRLEN(from), word, MAXWLEN); ftp->ft_from = getroom_save(spin, word); - (void)spell_casefold(to, (int)STRLEN(to), word, MAXWLEN); + (void)spell_casefold(curwin, to, (int)STRLEN(to), word, MAXWLEN); ftp->ft_to = getroom_save(spin, word); } @@ -3764,7 +3764,7 @@ store_word ( char_u *word, int flags, // extra flags, WF_BANNED int region, // supported region(s) - char_u *pfxlist, // list of prefix IDs or NULL + const char_u *pfxlist, // list of prefix IDs or NULL bool need_affix // only store word with affix ID ) { @@ -3772,25 +3772,28 @@ store_word ( int ct = captype(word, word + len); char_u foldword[MAXWLEN]; int res = OK; - char_u *p; - (void)spell_casefold(word, len, foldword, MAXWLEN); - for (p = pfxlist; res == OK; ++p) { - if (!need_affix || (p != NULL && *p != NUL)) + (void)spell_casefold(curwin, word, len, foldword, MAXWLEN); + for (const char_u *p = pfxlist; res == OK; p++) { + if (!need_affix || (p != NULL && *p != NUL)) { res = tree_add_word(spin, foldword, spin->si_foldroot, ct | flags, - region, p == NULL ? 0 : *p); - if (p == NULL || *p == NUL) + region, p == NULL ? 0 : *p); + } + if (p == NULL || *p == NUL) { break; + } } ++spin->si_foldwcount; if (res == OK && (ct == WF_KEEPCAP || (flags & WF_KEEPCAP))) { - for (p = pfxlist; res == OK; ++p) { - if (!need_affix || (p != NULL && *p != NUL)) + for (const char_u *p = pfxlist; res == OK; p++) { + if (!need_affix || (p != NULL && *p != NUL)) { res = tree_add_word(spin, word, spin->si_keeproot, flags, - region, p == NULL ? 0 : *p); - if (p == NULL || *p == NUL) + region, p == NULL ? 0 : *p); + } + if (p == NULL || *p == NUL) { break; + } } ++spin->si_keepwcount; } @@ -3942,7 +3945,7 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int msg_start(); msg_puts(_(msg_compressing)); msg_clr_eos(); - msg_didout = FALSE; + msg_didout = false; msg_col = 0; ui_flush(); } @@ -5032,7 +5035,7 @@ static void sug_write(spellinfo_T *spin, char_u *fname) for (linenr_T lnum = 1; lnum <= wcount; ++lnum) { // <sugline>: <sugnr> ... NUL - char_u *line = ml_get_buf(spin->si_spellbuf, lnum, FALSE); + char_u *line = ml_get_buf(spin->si_spellbuf, lnum, false); size_t len = STRLEN(line) + 1; if (fwrite(line, len, 1, fd) == 0) { EMSG(_(e_write)); @@ -5287,13 +5290,16 @@ static void spell_message(const spellinfo_T *spin, char_u *str) } // ":[count]spellgood {word}" -// ":[count]spellwrong {word}" +// ":[count]spellwrong {word}" // ":[count]spellundo {word}" +// ":[count]spellrare {word}" void ex_spell(exarg_T *eap) { - spell_add_word(eap->arg, (int)STRLEN(eap->arg), eap->cmdidx == CMD_spellwrong, - eap->forceit ? 0 : (int)eap->line2, - eap->cmdidx == CMD_spellundo); + spell_add_word(eap->arg, (int)STRLEN(eap->arg), + eap->cmdidx == CMD_spellwrong ? SPELL_ADD_BAD : + eap->cmdidx == CMD_spellrare ? SPELL_ADD_RARE : SPELL_ADD_GOOD, + eap->forceit ? 0 : (int)eap->line2, + eap->cmdidx == CMD_spellundo); } // Add "word[len]" to 'spellfile' as a good or bad word. @@ -5301,10 +5307,10 @@ void spell_add_word ( char_u *word, int len, - int bad, - int idx, // "zG" and "zW": zero, otherwise index in - // 'spellfile' - bool undo // true for "zug", "zuG", "zuw" and "zuW" + SpellAddType what, // SPELL_ADD_ values + int idx, // "zG" and "zW": zero, otherwise index in + // 'spellfile' + bool undo // true for "zug", "zuG", "zuw" and "zuW" ) { FILE *fd = NULL; @@ -5361,7 +5367,7 @@ spell_add_word ( fname = fnamebuf; } - if (bad || undo) { + if (what == SPELL_ADD_BAD || undo) { // When the word appears as good word we need to remove that one, // since its flags sort before the one with WF_BANNED. fd = os_fopen((char *)fname, "r"); @@ -5419,13 +5425,16 @@ spell_add_word ( } } - if (fd == NULL) + if (fd == NULL) { EMSG2(_(e_notopen), fname); - else { - if (bad) + } else { + if (what == SPELL_ADD_BAD) { fprintf(fd, "%.*s/!\n", len, word); - else + } else if (what == SPELL_ADD_RARE) { + fprintf(fd, "%.*s/?\n", len, word); + } else { fprintf(fd, "%.*s\n", len, word); + } fclose(fd); home_replace(NULL, fname, NameBuff, MAXPATHL, TRUE); diff --git a/src/nvim/state.c b/src/nvim/state.c index a3c74789d1..02d63d8ab1 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -144,6 +144,9 @@ char *get_mode(void) buf[0] = (char)(VIsual_mode + 's' - 'v'); } else { buf[0] = (char)VIsual_mode; + if (restart_VIsual_select) { + buf[1] = 's'; + } } } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE || State == CONFIRM) { @@ -171,12 +174,10 @@ char *get_mode(void) buf[1] = 'x'; } } - } else if ((State & CMDLINE) || exmode_active) { + } else if (State & CMDLINE) { buf[0] = 'c'; - if (exmode_active == EXMODE_VIM) { + if (exmode_active) { buf[1] = 'v'; - } else if (exmode_active == EXMODE_NORMAL) { - buf[1] = 'e'; } } else if (State & TERM_FOCUS) { buf[0] = 't'; diff --git a/src/nvim/strings.c b/src/nvim/strings.c index cb66f7682d..0363afe02d 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -964,7 +964,7 @@ int vim_vsnprintf_typval( break; } } - str_arg_l = precision = (size_t)(p1 - (char_u *)str_arg); + str_arg_l = (size_t)(p1 - (char_u *)str_arg); } } break; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index ed886ab7f9..77d2b21a7a 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -9,13 +9,12 @@ #include <ctype.h> #include <inttypes.h> #include <stdbool.h> -#include <string.h> #include <stdlib.h> +#include <string.h> -#include "nvim/vim.h" -#include "nvim/ascii.h" #include "nvim/api/private/helpers.h" -#include "nvim/syntax.h" +#include "nvim/ascii.h" +#include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor_shape.h" #include "nvim/eval.h" @@ -23,39 +22,40 @@ #include "nvim/ex_docmd.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/garray.h" #include "nvim/hashtab.h" #include "nvim/highlight.h" #include "nvim/indent_c.h" +#include "nvim/keymap.h" +#include "nvim/macros.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/keymap.h" -#include "nvim/garray.h" #include "nvim/option.h" +#include "nvim/os/os.h" +#include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" -#include "nvim/macros.h" #include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/sign.h" #include "nvim/strings.h" +#include "nvim/syntax.h" #include "nvim/syntax_defs.h" #include "nvim/terminal.h" #include "nvim/ui.h" -#include "nvim/os/os.h" -#include "nvim/os/time.h" -#include "nvim/buffer.h" +#include "nvim/vim.h" static bool did_syntax_onoff = false; /// Structure that stores information about a highlight group. /// The ID of a highlight group is also called group ID. It is the index in /// the highlight_ga array PLUS ONE. -struct hl_group { - char_u *sg_name; ///< highlight group name - char_u *sg_name_u; ///< uppercase of sg_name +typedef struct hl_group { + char_u *sg_name; ///< highlight group name + char *sg_name_u; ///< uppercase of sg_name bool sg_cleared; ///< "hi clear" was used int sg_attr; ///< Screen attr @see ATTR_ENTRY int sg_link; ///< link to this highlight group ID @@ -80,7 +80,7 @@ struct hl_group { char *sg_rgb_sp_name; ///< RGB special color name int sg_blend; ///< blend level (0-100 inclusive), -1 if unset -}; +} HlGroup; /// \addtogroup SG_SET /// @{ @@ -91,28 +91,29 @@ struct hl_group { // builtin |highlight-groups| static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; +Map(cstr_t, int) highlight_unames = MAP_INIT; static inline struct hl_group * HL_TABLE(void) { return ((struct hl_group *)((highlight_ga.ga_data))); } -#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */ +#define MAX_HL_ID 20000 // maximum value for a highlight ID. -/* different types of offsets that are possible */ -#define SPO_MS_OFF 0 /* match start offset */ -#define SPO_ME_OFF 1 /* match end offset */ -#define SPO_HS_OFF 2 /* highl. start offset */ -#define SPO_HE_OFF 3 /* highl. end offset */ -#define SPO_RS_OFF 4 /* region start offset */ -#define SPO_RE_OFF 5 /* region end offset */ -#define SPO_LC_OFF 6 /* leading context offset */ +// different types of offsets that are possible +#define SPO_MS_OFF 0 // match start offset +#define SPO_ME_OFF 1 // match end offset +#define SPO_HS_OFF 2 // highl. start offset +#define SPO_HE_OFF 3 // highl. end offset +#define SPO_RS_OFF 4 // region start offset +#define SPO_RE_OFF 5 // region end offset +#define SPO_LC_OFF 6 // leading context offset #define SPO_COUNT 7 -/* Flags to indicate an additional string for highlight name completion. */ -static int include_none = 0; /* when 1 include "nvim/None" */ -static int include_default = 0; /* when 1 include "nvim/default" */ -static int include_link = 0; /* when 2 include "nvim/link" and "clear" */ +// Flags to indicate an additional string for highlight name completion. +static int include_none = 0; // when 1 include "nvim/None" +static int include_default = 0; // when 1 include "nvim/default" +static int include_link = 0; // when 2 include "nvim/link" and "clear" /// The "term", "cterm" and "gui" arguments can be any combination of the /// following names, separated by commas (but no spaces!). @@ -214,12 +215,12 @@ typedef struct { proftime_T slowest; proftime_T average; int id; - char_u *pattern; + char_u *pattern; } time_entry_T; struct name_list { int flag; - char *name; + char *name; }; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -227,40 +228,40 @@ struct name_list { #endif static char *(spo_name_tab[SPO_COUNT]) = -{"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="}; +{ "ms=", "me=", "hs=", "he=", "rs=", "re=", "lc=" }; /* The sp_off_flags are computed like this: * offset from the start of the matched text: (1 << SPO_XX_OFF) - * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT)) + * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT)) * When both are present, only one is used. */ -#define SPTYPE_MATCH 1 /* match keyword with this group ID */ -#define SPTYPE_START 2 /* match a regexp, start of item */ -#define SPTYPE_END 3 /* match a regexp, end of item */ -#define SPTYPE_SKIP 4 /* match a regexp, skip within item */ +#define SPTYPE_MATCH 1 // match keyword with this group ID +#define SPTYPE_START 2 // match a regexp, start of item +#define SPTYPE_END 3 // match a regexp, end of item +#define SPTYPE_SKIP 4 // match a regexp, skip within item #define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data)) -#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */ +#define NONE_IDX -2 // value of sp_sync_idx for "NONE" /* * Flags for b_syn_sync_flags: */ -#define SF_CCOMMENT 0x01 /* sync on a C-style comment */ -#define SF_MATCH 0x02 /* sync by matching a pattern */ +#define SF_CCOMMENT 0x01 // sync on a C-style comment +#define SF_MATCH 0x02 // sync by matching a pattern #define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data)) -#define MAXKEYWLEN 80 /* maximum length of a keyword */ +#define MAXKEYWLEN 80 // maximum length of a keyword /* * The attributes of the syntax item that has been recognized. */ -static int current_attr = 0; /* attr of current syntax word */ -static int current_id = 0; /* ID of current char for syn_get_id() */ -static int current_trans_id = 0; /* idem, transparency removed */ +static int current_attr = 0; // attr of current syntax word +static int current_id = 0; // ID of current char for syn_get_id() +static int current_trans_id = 0; // idem, transparency removed static int current_flags = 0; static int current_seqnr = 0; static int current_sub_char = 0; @@ -268,9 +269,9 @@ static int current_sub_char = 0; /* * Methods of combining two clusters */ -#define CLUSTER_REPLACE 1 /* replace first list with second */ -#define CLUSTER_ADD 2 /* add second list to first */ -#define CLUSTER_SUBTRACT 3 /* subtract second list from first */ +#define CLUSTER_REPLACE 1 // replace first list with second +#define CLUSTER_ADD 2 // add second list to first +#define CLUSTER_SUBTRACT 3 // subtract second list from first #define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data)) @@ -282,12 +283,12 @@ static int current_sub_char = 0; * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added) * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID) */ -#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */ -#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */ -#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */ -#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */ +#define SYNID_ALLBUT MAX_HL_ID // syntax group ID for contains=ALLBUT +#define SYNID_TOP 21000 // syntax group ID for contains=TOP +#define SYNID_CONTAINED 22000 // syntax group ID for contains=CONTAINED +#define SYNID_CLUSTER 23000 // first syntax group ID for clusters -#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */ +#define MAX_SYN_INC_TAG 999 // maximum before the above overflow #define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER) /* @@ -333,7 +334,7 @@ static char msg_no_items[] = N_("No Syntax items defined for this buffer"); // valid of si_cont_list for containing all but contained groups #define ID_LIST_ALL (int16_t *)-1 -static int next_seqnr = 1; /* value to use for si_seqnr */ +static int next_seqnr = 1; // value to use for si_seqnr /* * The next possible match in the current line for any pattern is remembered, @@ -342,15 +343,15 @@ static int next_seqnr = 1; /* value to use for si_seqnr */ * If next_match_col == MAXCOL, no match found in this line. * (All end positions have the column of the char after the end) */ -static int next_match_col; /* column for start of next match */ -static lpos_T next_match_m_endpos; /* position for end of next match */ -static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */ -static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */ -static int next_match_idx; /* index of matched item */ -static long next_match_flags; /* flags for next match */ -static lpos_T next_match_eos_pos; /* end of start pattn (start region) */ -static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */ -static int next_match_end_idx; /* ID of group for end pattn or zero */ +static int next_match_col; // column for start of next match +static lpos_T next_match_m_endpos; // position for end of next match +static lpos_T next_match_h_startpos; // pos. for highl. start of next match +static lpos_T next_match_h_endpos; // pos. for highl. end of next match +static int next_match_idx; // index of matched item +static long next_match_flags; // flags for next match +static lpos_T next_match_eos_pos; // end of start pattn (start region) +static lpos_T next_match_eoe_pos; // pos. for end of end pattern +static int next_match_end_idx; // ID of group for end pattn or zero static reg_extmatch_T *next_match_extmatch = NULL; /* @@ -364,24 +365,24 @@ static reg_extmatch_T *next_match_extmatch = NULL; * The current state (within the line) of the recognition engine. * When current_state.ga_itemsize is 0 the current state is invalid. */ -static win_T *syn_win; // current window for highlighting -static buf_T *syn_buf; // current buffer for highlighting -static synblock_T *syn_block; // current buffer for highlighting -static proftime_T *syn_tm; // timeout limit -static linenr_T current_lnum = 0; // lnum of current state -static colnr_T current_col = 0; // column of current state -static int current_state_stored = 0; // TRUE if stored current state - // after setting current_finished -static int current_finished = 0; // current line has been finished -static garray_T current_state // current stack of state_items +static win_T *syn_win; // current window for highlighting +static buf_T *syn_buf; // current buffer for highlighting +static synblock_T *syn_block; // current buffer for highlighting +static proftime_T *syn_tm; // timeout limit +static linenr_T current_lnum = 0; // lnum of current state +static colnr_T current_col = 0; // column of current state +static bool current_state_stored = false; // true if stored current state + // after setting current_finished +static bool current_finished = false; // current line has been finished +static garray_T current_state // current stack of state_items = GA_EMPTY_INIT_VALUE; -static int16_t *current_next_list = NULL; // when non-zero, nextgroup list -static int current_next_flags = 0; // flags for current_next_list -static int current_line_id = 0; // unique number for current line +static int16_t *current_next_list = NULL; // when non-zero, nextgroup list +static int current_next_flags = 0; // flags for current_next_list +static int current_line_id = 0; // unique number for current line #define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx] -static int syn_time_on = FALSE; +static bool syn_time_on = false; # define IF_SYN_TIME(p) (p) // Set the timeout used for syntax highlighting. @@ -395,19 +396,19 @@ void syn_set_timeout(proftime_T *tm) * Start the syntax recognition for a line. This function is normally called * from the screen updating, once for each displayed line. * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get - * it. Careful: curbuf and curwin are likely to point to another buffer and + * it. Careful: curbuf and curwin are likely to point to another buffer and * window. */ void syntax_start(win_T *wp, linenr_T lnum) { - synstate_T *p; - synstate_T *last_valid = NULL; - synstate_T *last_min_valid = NULL; - synstate_T *sp, *prev = NULL; + synstate_T *p; + synstate_T *last_valid = NULL; + synstate_T *last_min_valid = NULL; + synstate_T *sp, *prev = NULL; linenr_T parsed_lnum; linenr_T first_stored; int dist; - static int changedtick = 0; /* remember the last change ID */ + static int changedtick = 0; // remember the last change ID current_sub_char = NUL; @@ -430,8 +431,9 @@ void syntax_start(win_T *wp, linenr_T lnum) * Allocate syntax stack when needed. */ syn_stack_alloc(); - if (syn_block->b_sst_array == NULL) - return; /* out of memory */ + if (syn_block->b_sst_array == NULL) { + return; // out of memory + } syn_block->b_sst_lasttick = display_tick; /* @@ -451,29 +453,33 @@ void syntax_start(win_T *wp, linenr_T lnum) * state (this happens very often!). Otherwise invalidate * current_state and figure it out below. */ - if (current_lnum != lnum) + if (current_lnum != lnum) { invalidate_current_state(); - } else + } + } else { invalidate_current_state(); + } /* * Try to synchronize from a saved state in b_sst_array[]. * Only do this if lnum is not before and not to far beyond a saved state. */ if (INVALID_STATE(¤t_state) && syn_block->b_sst_array != NULL) { - /* Find last valid saved state before start_lnum. */ + // Find last valid saved state before start_lnum. for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next) { if (p->sst_lnum > lnum) { break; } if (p->sst_change_lnum == 0) { last_valid = p; - if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines) + if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines) { last_min_valid = p; + } } } - if (last_min_valid != NULL) + if (last_min_valid != NULL) { load_current_state(last_min_valid); + } } /* @@ -482,24 +488,27 @@ void syntax_start(win_T *wp, linenr_T lnum) */ if (INVALID_STATE(¤t_state)) { syn_sync(wp, lnum, last_valid); - if (current_lnum == 1) - /* First line is always valid, no matter "minlines". */ + if (current_lnum == 1) { + // First line is always valid, no matter "minlines". first_stored = 1; - else + } else { /* Need to parse "minlines" lines before state can be considered * valid to store. */ first_stored = current_lnum + syn_block->b_syn_sync_minlines; - } else + } + } else { first_stored = current_lnum; + } /* * Advance from the sync point or saved state until the current line. * Save some entries for syncing with later on. */ - if (syn_block->b_sst_len <= Rows) + if (syn_block->b_sst_len <= Rows) { dist = 999999; - else + } else { dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1; + } while (current_lnum < lnum) { syn_start_line(); (void)syn_finish_line(false); @@ -511,26 +520,30 @@ void syntax_start(win_T *wp, linenr_T lnum) /* Check if the saved state entry is for the current line and is * equal to the current state. If so, then validate all saved * states that depended on a change before the parsed line. */ - if (prev == NULL) + if (prev == NULL) { prev = syn_stack_find_entry(current_lnum - 1); - if (prev == NULL) + } + if (prev == NULL) { sp = syn_block->b_sst_first; - else + } else { sp = prev; - while (sp != NULL && sp->sst_lnum < current_lnum) + } + while (sp != NULL && sp->sst_lnum < current_lnum) { sp = sp->sst_next; + } if (sp != NULL && sp->sst_lnum == current_lnum && syn_stack_equal(sp)) { parsed_lnum = current_lnum; prev = sp; while (sp != NULL && sp->sst_change_lnum <= parsed_lnum) { - if (sp->sst_lnum <= lnum) - /* valid state before desired line, use this one */ + if (sp->sst_lnum <= lnum) { + // valid state before desired line, use this one prev = sp; - else if (sp->sst_change_lnum == 0) - /* past saved states depending on change, break here. */ + } else if (sp->sst_change_lnum == 0) { + // past saved states depending on change, break here. break; + } sp->sst_change_lnum = 0; sp = sp->sst_next; } @@ -541,8 +554,9 @@ void syntax_start(win_T *wp, linenr_T lnum) * saved state. But only when parsed at least 'minlines'. */ else if (prev == NULL || current_lnum == lnum - || current_lnum >= prev->sst_lnum + dist) + || current_lnum >= prev->sst_lnum + dist) { prev = store_current_state(); + } } /* This can take a long time: break when CTRL-C pressed. The current @@ -593,8 +607,8 @@ static void clear_current_state(void) */ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) { - buf_T *curbuf_save; - win_T *curwin_save; + buf_T *curbuf_save; + win_T *curwin_save; pos_T cursor_save; int idx; linenr_T lnum; @@ -602,8 +616,8 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) linenr_T break_lnum; bool had_sync_point; stateitem_T *cur_si; - synpat_T *spp; - char_u *line; + synpat_T *spp; + char_u *line; int found_flags = 0; int found_match_idx = 0; linenr_T found_current_lnum = 0; @@ -624,22 +638,25 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) * where N is minlines * 1.5, or minlines * 2 if minlines is small. * Watch out for overflow when minlines is MAXLNUM. */ - if (syn_block->b_syn_sync_minlines > start_lnum) + if (syn_block->b_syn_sync_minlines > start_lnum) { start_lnum = 1; - else { - if (syn_block->b_syn_sync_minlines == 1) + } else { + if (syn_block->b_syn_sync_minlines == 1) { lnum = 1; - else if (syn_block->b_syn_sync_minlines < 10) + } else if (syn_block->b_syn_sync_minlines < 10) { lnum = syn_block->b_syn_sync_minlines * 2; - else + } else { lnum = syn_block->b_syn_sync_minlines * 3 / 2; + } if (syn_block->b_syn_sync_maxlines != 0 - && lnum > syn_block->b_syn_sync_maxlines) + && lnum > syn_block->b_syn_sync_maxlines) { lnum = syn_block->b_syn_sync_maxlines; - if (lnum >= start_lnum) + } + if (lnum >= start_lnum) { start_lnum = 1; - else + } else { start_lnum -= lnum; + } } current_lnum = start_lnum; @@ -659,12 +676,13 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) */ for (; start_lnum > 1; --start_lnum) { line = ml_get(start_lnum - 1); - if (*line == NUL || *(line + STRLEN(line) - 1) != '\\') + if (*line == NUL || *(line + STRLEN(line) - 1) != '\\') { break; + } } current_lnum = start_lnum; - /* set cursor to start of search */ + // set cursor to start of search cursor_save = wp->w_cursor; wp->w_cursor.lnum = start_lnum; wp->w_cursor.col = 0; @@ -675,7 +693,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) * Restrict the search for the end of a comment to b_syn_sync_maxlines. */ if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL) { - for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; ) + for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; ) { if (SYN_ITEMS(syn_block)[idx].sp_syn.id == syn_block->b_syn_sync_id && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START) { @@ -684,9 +702,10 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) update_si_attr(current_state.ga_len - 1); break; } + } } - /* restore cursor and buffer */ + // restore cursor and buffer wp->w_cursor = cursor_save; curwin = curwin_save; curbuf = curbuf_save; @@ -696,17 +715,18 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) */ else if (syn_block->b_syn_sync_flags & SF_MATCH) { if (syn_block->b_syn_sync_maxlines != 0 - && start_lnum > syn_block->b_syn_sync_maxlines) + && start_lnum > syn_block->b_syn_sync_maxlines) { break_lnum = start_lnum - syn_block->b_syn_sync_maxlines; - else + } else { break_lnum = 0; + } found_m_endpos.lnum = 0; found_m_endpos.col = 0; end_lnum = start_lnum; lnum = start_lnum; while (--lnum > break_lnum) { - /* This can take a long time: break when CTRL-C pressed. */ + // This can take a long time: break when CTRL-C pressed. line_breakcheck(); if (got_int) { invalidate_current_state(); @@ -714,7 +734,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) break; } - /* Check if we have run into a valid saved state stack now. */ + // Check if we have run into a valid saved state stack now. if (last_valid != NULL && lnum == last_valid->sst_lnum) { load_current_state(last_valid); break; @@ -723,8 +743,9 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) /* * Check if the previous line has the line-continuation pattern. */ - if (lnum > 1 && syn_match_linecont(lnum - 1)) + if (lnum > 1 && syn_match_linecont(lnum - 1)) { continue; + } /* * Start with nothing on the state stack @@ -740,12 +761,12 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) if (had_sync_point && current_state.ga_len) { cur_si = &CUR_STATE(current_state.ga_len - 1); if (cur_si->si_m_endpos.lnum > start_lnum) { - /* ignore match that goes to after where started */ + // ignore match that goes to after where started current_lnum = end_lnum; break; } if (cur_si->si_idx < 0) { - /* Cannot happen? */ + // Cannot happen? found_flags = 0; found_match_idx = KEYWORD_IDX; } else { @@ -763,23 +784,27 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) if (found_m_endpos.lnum > current_lnum) { current_lnum = found_m_endpos.lnum; current_col = found_m_endpos.col; - if (current_lnum >= end_lnum) + if (current_lnum >= end_lnum) { break; - } else if (found_m_endpos.col > current_col) + } + } else if (found_m_endpos.col > current_col) { current_col = found_m_endpos.col; - else + } else { ++current_col; + } /* syn_current_attr() will have skipped the check for * an item that ends here, need to do that now. Be * careful not to go past the NUL. */ prev_current_col = current_col; - if (syn_getcurline()[current_col] != NUL) + if (syn_getcurline()[current_col] != NUL) { ++current_col; + } check_state_ends(); current_col = prev_current_col; - } else + } else { break; + } } } @@ -809,7 +834,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) cur_si = &CUR_STATE(current_state.ga_len - 1); cur_si->si_h_startpos.lnum = found_current_lnum; cur_si->si_h_startpos.col = found_current_col; - update_si_end(cur_si, (int)current_col, TRUE); + update_si_end(cur_si, (int)current_col, true); check_keepend(); } current_col = found_m_endpos.col; @@ -827,7 +852,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid) invalidate_current_state(); } - /* Ran into start of the file or exceeded maximum number of lines */ + // Ran into start of the file or exceeded maximum number of lines if (lnum <= break_lnum) { invalidate_current_state(); current_lnum = break_lnum + 1; @@ -880,7 +905,7 @@ static int syn_match_linecont(linenr_T lnum) */ static void syn_start_line(void) { - current_finished = FALSE; + current_finished = false; current_col = 0; /* @@ -888,7 +913,7 @@ static void syn_start_line(void) * previous line and regions that have "keepend". */ if (!GA_EMPTY(¤t_state)) { - syn_update_ends(TRUE); + syn_update_ends(true); check_state_ends(); } @@ -897,15 +922,13 @@ static void syn_start_line(void) next_seqnr = 1; } -/* - * Check for items in the stack that need their end updated. - * When "startofline" is TRUE the last item is always updated. - * When "startofline" is FALSE the item with "keepend" is forcefully updated. - */ -static void syn_update_ends(int startofline) +/// Check for items in the stack that need their end updated. +/// +/// @param startofline if true the last item is always updated. +/// if false the item with "keepend" is forcefully updated. +static void syn_update_ends(bool startofline) { stateitem_T *cur_si; - int seen_keepend; if (startofline) { /* Check for a match carried over from a previous line with a @@ -935,25 +958,30 @@ static void syn_update_ends(int startofline) * Then check for items ending in column 0. */ int i = current_state.ga_len - 1; - if (keepend_level >= 0) - for (; i > keepend_level; --i) - if (CUR_STATE(i).si_flags & HL_EXTEND) + if (keepend_level >= 0) { + for (; i > keepend_level; --i) { + if (CUR_STATE(i).si_flags & HL_EXTEND) { break; + } + } + } - seen_keepend = FALSE; - for (; i < current_state.ga_len; ++i) { + bool seen_keepend = false; + for (; i < current_state.ga_len; i++) { cur_si = &CUR_STATE(i); if ((cur_si->si_flags & HL_KEEPEND) || (seen_keepend && !startofline) || (i == current_state.ga_len - 1 && startofline)) { - cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */ + cur_si->si_h_startpos.col = 0; // start highl. in col 0 cur_si->si_h_startpos.lnum = current_lnum; - if (!(cur_si->si_flags & HL_MATCHCONT)) + if (!(cur_si->si_flags & HL_MATCHCONT)) { update_si_end(cur_si, (int)current_col, !startofline); + } - if (!startofline && (cur_si->si_flags & HL_KEEPEND)) - seen_keepend = TRUE; + if (!startofline && (cur_si->si_flags & HL_KEEPEND)) { + seen_keepend = true; + } } } check_keepend(); @@ -997,7 +1025,7 @@ static void syn_update_ends(int startofline) static void syn_stack_free_block(synblock_T *block) { - synstate_T *p; + synstate_T *p; if (block->b_sst_array != NULL) { for (p = block->b_sst_first; p != NULL; p = p->sst_next) { @@ -1016,7 +1044,7 @@ void syn_stack_free_all(synblock_T *block) { syn_stack_free_block(block); - /* When using "syntax" fold method, must update all folds. */ + // When using "syntax" fold method, must update all folds. FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_s == block && foldmethodIsSyntax(wp)) { foldUpdateAll(wp); @@ -1033,31 +1061,35 @@ void syn_stack_free_all(synblock_T *block) static void syn_stack_alloc(void) { long len; - synstate_T *to, *from; - synstate_T *sstp; + synstate_T *to, *from; + synstate_T *sstp; len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2; - if (len < SST_MIN_ENTRIES) + if (len < SST_MIN_ENTRIES) { len = SST_MIN_ENTRIES; - else if (len > SST_MAX_ENTRIES) + } else if (len > SST_MAX_ENTRIES) { len = SST_MAX_ENTRIES; + } if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len) { - /* Allocate 50% too much, to avoid reallocating too often. */ + // Allocate 50% too much, to avoid reallocating too often. len = syn_buf->b_ml.ml_line_count; len = (len + len / 2) / SST_DIST + Rows * 2; - if (len < SST_MIN_ENTRIES) + if (len < SST_MIN_ENTRIES) { len = SST_MIN_ENTRIES; - else if (len > SST_MAX_ENTRIES) + } else if (len > SST_MAX_ENTRIES) { len = SST_MAX_ENTRIES; + } if (syn_block->b_sst_array != NULL) { /* When shrinking the array, cleanup the existing stack. * Make sure that all valid entries fit in the new array. */ while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len - && syn_stack_cleanup()) + && syn_stack_cleanup()) { ; - if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2) + } + if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2) { len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2; + } } assert(len >= 0); @@ -1065,7 +1097,7 @@ static void syn_stack_alloc(void) to = sstp - 1; if (syn_block->b_sst_array != NULL) { - /* Move the states from the old array to the new one. */ + // Move the states from the old array to the new one. for (from = syn_block->b_sst_first; from != NULL; from = from->sst_next) { ++to; @@ -1082,10 +1114,11 @@ static void syn_stack_alloc(void) syn_block->b_sst_freecount = len; } - /* Create the list of free entries. */ + // Create the list of free entries. syn_block->b_sst_firstfree = to + 1; - while (++to < sstp + len) + while (++to < sstp + len) { to->sst_next = to + 1; + } (sstp + len - 1)->sst_next = NULL; xfree(syn_block->b_sst_array); @@ -1113,7 +1146,7 @@ void syn_stack_apply_changes(buf_T *buf) static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf) { - synstate_T *p, *prev, *np; + synstate_T *p, *prev, *np; linenr_T n; prev = NULL; @@ -1121,12 +1154,13 @@ static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf) if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top) { n = p->sst_lnum + buf->b_mod_xlines; if (n <= buf->b_mod_bot) { - /* this state is inside the changed area, remove it */ + // this state is inside the changed area, remove it np = p->sst_next; - if (prev == NULL) + if (prev == NULL) { block->b_sst_first = np; - else + } else { prev->sst_next = np; + } syn_stack_free_entry(block, p); p = np; continue; @@ -1135,14 +1169,16 @@ static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf) * that needs to be parsed before this entry can be made valid * again. */ if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top) { - if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top) + if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top) { p->sst_change_lnum += buf->b_mod_xlines; - else + } else { p->sst_change_lnum = buf->b_mod_top; + } } if (p->sst_change_lnum == 0 - || p->sst_change_lnum < buf->b_mod_bot) + || p->sst_change_lnum < buf->b_mod_bot) { p->sst_change_lnum = buf->b_mod_bot; + } p->sst_lnum = n; } @@ -1151,27 +1187,26 @@ static void syn_stack_apply_changes_block(synblock_T *block, buf_T *buf) } } -/* - * Reduce the number of entries in the state stack for syn_buf. - * Returns TRUE if at least one entry was freed. - */ -static int syn_stack_cleanup(void) +/// Reduce the number of entries in the state stack for syn_buf. +/// +/// @return true if at least one entry was freed. +static bool syn_stack_cleanup(void) { - synstate_T *p, *prev; + synstate_T *p, *prev; disptick_T tick; - int above; int dist; - int retval = FALSE; + bool retval = false; if (syn_block->b_sst_first == NULL) { return retval; } - /* Compute normal distance between non-displayed entries. */ - if (syn_block->b_sst_len <= Rows) + // Compute normal distance between non-displayed entries. + if (syn_block->b_sst_len <= Rows) { dist = 999999; - else + } else { dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1; + } /* * Go through the list to find the "tick" for the oldest entry that can @@ -1179,16 +1214,18 @@ static int syn_stack_cleanup(void) * "b_sst_lasttick" (the display tick wraps around). */ tick = syn_block->b_sst_lasttick; - above = FALSE; + bool above = false; prev = syn_block->b_sst_first; for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next) { if (prev->sst_lnum + dist > p->sst_lnum) { if (p->sst_tick > syn_block->b_sst_lasttick) { - if (!above || p->sst_tick < tick) + if (!above || p->sst_tick < tick) { tick = p->sst_tick; - above = TRUE; - } else if (!above && p->sst_tick < tick) + } + above = true; + } else if (!above && p->sst_tick < tick) { tick = p->sst_tick; + } } } @@ -1199,11 +1236,11 @@ static int syn_stack_cleanup(void) prev = syn_block->b_sst_first; for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next) { if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum) { - /* Move this entry from used list to free list */ + // Move this entry from used list to free list prev->sst_next = p->sst_next; syn_stack_free_entry(syn_block, p); p = prev; - retval = TRUE; + retval = true; } } return retval; @@ -1227,14 +1264,16 @@ static void syn_stack_free_entry(synblock_T *block, synstate_T *p) */ static synstate_T *syn_stack_find_entry(linenr_T lnum) { - synstate_T *p, *prev; + synstate_T *p, *prev; prev = NULL; for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next) { - if (p->sst_lnum == lnum) + if (p->sst_lnum == lnum) { return p; - if (p->sst_lnum > lnum) + } + if (p->sst_lnum > lnum) { break; + } } return prev; } @@ -1246,10 +1285,10 @@ static synstate_T *syn_stack_find_entry(linenr_T lnum) static synstate_T *store_current_state(void) { int i; - synstate_T *p; - bufstate_T *bp; + synstate_T *p; + bufstate_T *bp; stateitem_T *cur_si; - synstate_T *sp = syn_stack_find_entry(current_lnum); + synstate_T *sp = syn_stack_find_entry(current_lnum); /* * If the current state contains a start or end pattern that continues @@ -1261,51 +1300,55 @@ static synstate_T *store_current_state(void) || cur_si->si_m_endpos.lnum >= current_lnum || cur_si->si_h_endpos.lnum >= current_lnum || (cur_si->si_end_idx - && cur_si->si_eoe_pos.lnum >= current_lnum)) + && cur_si->si_eoe_pos.lnum >= current_lnum)) { break; + } } if (i >= 0) { if (sp != NULL) { - /* find "sp" in the list and remove it */ - if (syn_block->b_sst_first == sp) - /* it's the first entry */ + // find "sp" in the list and remove it + if (syn_block->b_sst_first == sp) { + // it's the first entry syn_block->b_sst_first = sp->sst_next; - else { - /* find the entry just before this one to adjust sst_next */ - for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next) - if (p->sst_next == sp) + } else { + // find the entry just before this one to adjust sst_next + for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next) { + if (p->sst_next == sp) { break; - if (p != NULL) /* just in case */ + } + } + if (p != NULL) { // just in case p->sst_next = sp->sst_next; + } } syn_stack_free_entry(syn_block, sp); sp = NULL; } - } else if (sp == NULL || sp->sst_lnum != current_lnum) { + } else if (sp == NULL || sp->sst_lnum != current_lnum) { /* * Add a new entry */ - /* If no free items, cleanup the array first. */ + // If no free items, cleanup the array first. if (syn_block->b_sst_freecount == 0) { (void)syn_stack_cleanup(); - /* "sp" may have been moved to the freelist now */ + // "sp" may have been moved to the freelist now sp = syn_stack_find_entry(current_lnum); } - /* Still no free items? Must be a strange problem... */ - if (syn_block->b_sst_freecount == 0) + // Still no free items? Must be a strange problem... + if (syn_block->b_sst_freecount == 0) { sp = NULL; - else { + } else { /* Take the first item from the free list and put it in the used * list, after *sp */ p = syn_block->b_sst_firstfree; syn_block->b_sst_firstfree = p->sst_next; --syn_block->b_sst_freecount; if (sp == NULL) { - /* Insert in front of the list */ + // Insert in front of the list p->sst_next = syn_block->b_sst_first; syn_block->b_sst_first = p; } else { - /* insert in list after *sp */ + // insert in list after *sp p->sst_next = sp->sst_next; sp->sst_next = p; } @@ -1315,7 +1358,7 @@ static synstate_T *store_current_state(void) } } if (sp != NULL) { - /* When overwriting an existing state stack, clear it first */ + // When overwriting an existing state stack, clear it first clear_syn_state(sp); sp->sst_stacksize = current_state.ga_len; if (current_state.ga_len > SST_FIX_STATES) { @@ -1325,8 +1368,9 @@ static synstate_T *store_current_state(void) ga_grow(&sp->sst_union.sst_ga, current_state.ga_len); sp->sst_union.sst_ga.ga_len = current_state.ga_len; bp = SYN_STATE_P(&(sp->sst_union.sst_ga)); - } else + } else { bp = sp->sst_union.sst_stack; + } for (i = 0; i < sp->sst_stacksize; ++i) { bp[i].bs_idx = CUR_STATE(i).si_idx; bp[i].bs_flags = CUR_STATE(i).si_flags; @@ -1339,7 +1383,7 @@ static synstate_T *store_current_state(void) sp->sst_tick = display_tick; sp->sst_change_lnum = 0; } - current_state_stored = TRUE; + current_state_stored = true; return sp; } @@ -1349,32 +1393,35 @@ static synstate_T *store_current_state(void) static void load_current_state(synstate_T *from) { int i; - bufstate_T *bp; + bufstate_T *bp; clear_current_state(); validate_current_state(); keepend_level = -1; if (from->sst_stacksize) { ga_grow(¤t_state, from->sst_stacksize); - if (from->sst_stacksize > SST_FIX_STATES) + if (from->sst_stacksize > SST_FIX_STATES) { bp = SYN_STATE_P(&(from->sst_union.sst_ga)); - else + } else { bp = from->sst_union.sst_stack; + } for (i = 0; i < from->sst_stacksize; ++i) { CUR_STATE(i).si_idx = bp[i].bs_idx; CUR_STATE(i).si_flags = bp[i].bs_flags; CUR_STATE(i).si_seqnr = bp[i].bs_seqnr; CUR_STATE(i).si_cchar = bp[i].bs_cchar; CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch); - if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND)) + if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND)) { keepend_level = i; + } CUR_STATE(i).si_ends = FALSE; CUR_STATE(i).si_m_lnum = 0; - if (CUR_STATE(i).si_idx >= 0) + if (CUR_STATE(i).si_idx >= 0) { CUR_STATE(i).si_next_list = (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list; - else + } else { CUR_STATE(i).si_next_list = NULL; + } update_si_attr(i); } current_state.ga_len = from->sst_stacksize; @@ -1384,32 +1431,33 @@ static void load_current_state(synstate_T *from) current_lnum = from->sst_lnum; } -/* - * Compare saved state stack "*sp" with the current state. - * Return TRUE when they are equal. - */ -static int syn_stack_equal(synstate_T *sp) +/// Compare saved state stack "*sp" with the current state. +/// +/// @return true when they are equal. +static bool syn_stack_equal(synstate_T *sp) { - bufstate_T *bp; - reg_extmatch_T *six, *bsx; + bufstate_T *bp; + reg_extmatch_T *six, *bsx; - /* First a quick check if the stacks have the same size end nextlist. */ + // First a quick check if the stacks have the same size end nextlist. if (sp->sst_stacksize != current_state.ga_len || sp->sst_next_list != current_next_list) { - return FALSE; + return false; } - /* Need to compare all states on both stacks. */ - if (sp->sst_stacksize > SST_FIX_STATES) + // Need to compare all states on both stacks. + if (sp->sst_stacksize > SST_FIX_STATES) { bp = SYN_STATE_P(&(sp->sst_union.sst_ga)); - else + } else { bp = sp->sst_union.sst_stack; + } int i; for (i = current_state.ga_len; --i >= 0; ) { - /* If the item has another index the state is different. */ - if (bp[i].bs_idx != CUR_STATE(i).si_idx) + // If the item has another index the state is different. + if (bp[i].bs_idx != CUR_STATE(i).si_idx) { break; + } if (bp[i].bs_extmatch == CUR_STATE(i).si_extmatch) { continue; } @@ -1420,8 +1468,9 @@ static int syn_stack_equal(synstate_T *sp) six = CUR_STATE(i).si_extmatch; /* If one of the extmatch pointers is NULL the states are * different. */ - if (bsx == NULL || six == NULL) + if (bsx == NULL || six == NULL) { break; + } int j; for (j = 0; j < NSUBEXP; ++j) { /* Check each referenced match string. They must all be @@ -1440,13 +1489,15 @@ static int syn_stack_equal(synstate_T *sp) } } } - if (j != NSUBEXP) + if (j != NSUBEXP) { break; + } + } + if (i < 0) { + return true; } - if (i < 0) - return TRUE; - return FALSE; + return false; } /* @@ -1454,21 +1505,23 @@ static int syn_stack_equal(synstate_T *sp) * this line depended on a change before it, it now depends on the line below * the last parsed line. * The window looks like this: - * line which changed - * displayed line - * displayed line + * line which changed + * displayed line + * displayed line * lnum -> line below window */ void syntax_end_parsing(linenr_T lnum) { - synstate_T *sp; + synstate_T *sp; sp = syn_stack_find_entry(lnum); - if (sp != NULL && sp->sst_lnum < lnum) + if (sp != NULL && sp->sst_lnum < lnum) { sp = sp->sst_next; + } - if (sp != NULL && sp->sst_change_lnum != 0) + if (sp != NULL && sp->sst_change_lnum != 0) { sp->sst_change_lnum = lnum; + } } /* @@ -1478,7 +1531,7 @@ void syntax_end_parsing(linenr_T lnum) static void invalidate_current_state(void) { clear_current_state(); - current_state.ga_itemsize = 0; /* mark current_state invalid */ + current_state.ga_itemsize = 0; // mark current_state invalid current_next_list = NULL; keepend_level = -1; } @@ -1489,15 +1542,14 @@ static void validate_current_state(void) ga_set_growsize(¤t_state, 3); } -/* - * Return TRUE if the syntax at start of lnum changed since last time. - * This will only be called just after get_syntax_attr() for the previous - * line, to check if the next line needs to be redrawn too. - */ -int syntax_check_changed(linenr_T lnum) +/// This will only be called just after get_syntax_attr() for the previous +/// line, to check if the next line needs to be redrawn too. +/// +/// @return true if the syntax at start of lnum changed since last time. +bool syntax_check_changed(linenr_T lnum) { - int retval = TRUE; - synstate_T *sp; + bool retval = true; + synstate_T *sp; /* * Check the state stack when: @@ -1519,8 +1571,9 @@ int syntax_check_changed(linenr_T lnum) * Compare the current state with the previously saved state of * the line. */ - if (syn_stack_equal(sp)) - retval = FALSE; + if (syn_stack_equal(sp)) { + retval = false; + } /* * Store the current state in b_sst_array[] for later use. @@ -1533,16 +1586,13 @@ int syntax_check_changed(linenr_T lnum) return retval; } -/* - * Finish the current line. - * This doesn't return any attributes, it only gets the state at the end of - * the line. It can start anywhere in the line, as long as the current state - * is valid. - */ -static bool -syn_finish_line( - const bool syncing // called for syncing -) +/// Finish the current line. +/// This doesn't return any attributes, it only gets the state at the end of +/// the line. It can start anywhere in the line, as long as the current state +/// is valid. +/// +/// @param syncing called for syncing +static bool syn_finish_line(const bool syncing) { while (!current_finished) { (void)syn_current_attr(syncing, false, NULL, false); @@ -1572,36 +1622,35 @@ syn_finish_line( return false; } -/* - * Return highlight attributes for next character. - * Must first call syntax_start() once for the line. - * "col" is normally 0 for the first use in a line, and increments by one each - * time. It's allowed to skip characters and to stop before the end of the - * line. But only a "col" after a previously used column is allowed. - * When "can_spell" is not NULL set it to TRUE when spell-checking should be - * done. - */ -int -get_syntax_attr( - const colnr_T col, - bool *const can_spell, - const bool keep_state // keep state of char at "col" -) +/// Gets highlight attributes for next character. +/// Must first call syntax_start() once for the line. +/// "col" is normally 0 for the first use in a line, and increments by one each +/// time. It's allowed to skip characters and to stop before the end of the +/// line. But only a "col" after a previously used column is allowed. +/// When "can_spell" is not NULL set it to TRUE when spell-checking should be +/// done. +/// +/// @param keep_state keep state of char at "col" +/// +/// @return highlight attributes for next character. +int get_syntax_attr(const colnr_T col, bool *const can_spell, const bool keep_state) { int attr = 0; - if (can_spell != NULL) + if (can_spell != NULL) { /* Default: Only do spelling when there is no @Spell cluster or when * ":syn spell toplevel" was used. */ *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT ? (syn_block->b_spell_cluster_id == 0) : (syn_block->b_syn_spell == SYNSPL_TOP); + } - /* check for out of memory situation */ - if (syn_block->b_sst_array == NULL) + // check for out of memory situation + if (syn_block->b_sst_array == NULL) { return 0; + } - /* After 'synmaxcol' the attribute is always zero. */ + // After 'synmaxcol' the attribute is always zero. if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc) { clear_current_state(); current_id = 0; @@ -1611,9 +1660,10 @@ get_syntax_attr( return 0; } - /* Make sure current_state is valid */ - if (INVALID_STATE(¤t_state)) + // Make sure current_state is valid + if (INVALID_STATE(¤t_state)) { validate_current_state(); + } /* * Skip from the current column to "col", get the attributes for "col". @@ -1627,15 +1677,14 @@ get_syntax_attr( return attr; } -/* - * Get syntax attributes for current_lnum, current_col. - */ -static int syn_current_attr( - const bool syncing, // When true: called for syncing - const bool displaying, // result will be displayed - bool *const can_spell, // return: do spell checking - const bool keep_state // keep syntax stack afterwards -) +/// Get syntax attributes for current_lnum, current_col. +/// +/// @param syncing When true: called for syncing +/// @param displaying result will be displayed +/// @param can_spell return: do spell checking +/// @param keep_state keep syntax stack afterwards +static int syn_current_attr(const bool syncing, const bool displaying, bool *const can_spell, + const bool keep_state) { lpos_T endpos; // was: char_u *endp; lpos_T hl_startpos; // was: int hl_startcol; @@ -1655,8 +1704,8 @@ static int syn_current_attr( lpos_T pos; reg_extmatch_T *cur_extmatch = NULL; char_u buf_chartab[32]; // chartab array for syn iskeyword - char_u *line; // current line. NOTE: becomes invalid after - // looking for a pattern match! + char_u *line; // current line. NOTE: becomes invalid after + // looking for a pattern match! // variables for zero-width matches that have a "nextgroup" argument bool keep_next_list; @@ -1677,15 +1726,15 @@ static int syn_current_attr( (void)push_next_match(); } - current_finished = TRUE; - current_state_stored = FALSE; + current_finished = true; + current_state_stored = false; return 0; } - /* if the current or next character is NUL, we will finish the line now */ + // if the current or next character is NUL, we will finish the line now if (line[current_col] == NUL || line[current_col + 1] == NUL) { - current_finished = TRUE; - current_state_stored = FALSE; + current_finished = true; + current_state_stored = false; } /* @@ -1700,8 +1749,8 @@ static int syn_current_attr( // Only check for keywords when not syncing and there are some. const bool do_keywords = !syncing - && (syn_block->b_keywtab.ht_used > 0 - || syn_block->b_keywtab_ic.ht_used > 0); + && (syn_block->b_keywtab.ht_used > 0 + || syn_block->b_keywtab_ic.ht_used > 0); /* Init the list of zero-width matches with a nextlist. This is used to * avoid matching the same item in the same position twice. */ @@ -1727,23 +1776,25 @@ static int syn_current_attr( * Always need to check for contained items if some item has the * "containedin" argument (takes extra time!). */ - if (current_state.ga_len) + if (current_state.ga_len) { cur_si = &CUR_STATE(current_state.ga_len - 1); - else + } else { cur_si = NULL; + } if (syn_block->b_syn_containedin || cur_si == NULL || cur_si->si_cont_list != NULL) { /* * 2. Check for keywords, if on a keyword char after a non-keyword - * char. Don't do this when syncing. + * char. Don't do this when syncing. */ if (do_keywords) { line = syn_getcurline(); const char_u *cur_pos = line + current_col; if (vim_iswordp_buf(cur_pos, syn_buf) - && (current_col == 0 || !vim_iswordp_buf( - cur_pos - 1 - utf_head_off(line, cur_pos - 1), syn_buf))) { + && (current_col == 0 || + !vim_iswordp_buf(cur_pos - 1 - utf_head_off(line, cur_pos - 1), + syn_buf))) { syn_id = check_keyword_id(line, (int)current_col, &endcol, &flags, &next_list, cur_si, &cchar); if (syn_id != 0) { @@ -1752,7 +1803,7 @@ static int syn_current_attr( cur_si = &CUR_STATE(current_state.ga_len - 1); cur_si->si_m_startcol = current_col; cur_si->si_h_startpos.lnum = current_lnum; - cur_si->si_h_startpos.col = 0; /* starts right away */ + cur_si->si_h_startpos.col = 0; // starts right away cur_si->si_m_endpos.lnum = current_lnum; cur_si->si_m_endpos.col = endcol; cur_si->si_h_endpos.lnum = current_lnum; @@ -1762,10 +1813,11 @@ static int syn_current_attr( cur_si->si_flags = flags; cur_si->si_seqnr = next_seqnr++; cur_si->si_cchar = cchar; - if (current_state.ga_len > 1) + if (current_state.ga_len > 1) { cur_si->si_flags |= CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL; + } cur_si->si_id = syn_id; cur_si->si_trans_id = syn_id; if (flags & HL_TRANSP) { @@ -1773,10 +1825,8 @@ static int syn_current_attr( cur_si->si_attr = 0; cur_si->si_trans_id = 0; } else { - cur_si->si_attr = CUR_STATE( - current_state.ga_len - 2).si_attr; - cur_si->si_trans_id = CUR_STATE( - current_state.ga_len - 2).si_trans_id; + cur_si->si_attr = CUR_STATE(current_state.ga_len - 2).si_attr; + cur_si->si_trans_id = CUR_STATE(current_state.ga_len - 2).si_trans_id; } } else { cur_si->si_attr = syn_id2attr(syn_id); @@ -1804,28 +1854,29 @@ static int syn_current_attr( * pattern takes quite a bit of time, thus we want to * avoid doing it when it's not needed. */ - next_match_idx = 0; /* no match in this line yet */ + next_match_idx = 0; // no match in this line yet next_match_col = MAXCOL; for (int idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; ) { synpat_T *const spp = &(SYN_ITEMS(syn_block)[idx]); - if ( spp->sp_syncing == syncing - && (displaying || !(spp->sp_flags & HL_DISPLAY)) - && (spp->sp_type == SPTYPE_MATCH - || spp->sp_type == SPTYPE_START) - && (current_next_list != NULL + if (spp->sp_syncing == syncing + && (displaying || !(spp->sp_flags & HL_DISPLAY)) + && (spp->sp_type == SPTYPE_MATCH + || spp->sp_type == SPTYPE_START) + && (current_next_list != NULL ? in_id_list(NULL, current_next_list, - &spp->sp_syn, 0) + &spp->sp_syn, 0) : (cur_si == NULL ? !(spp->sp_flags & HL_CONTAINED) : in_id_list(cur_si, - cur_si->si_cont_list, &spp->sp_syn, - spp->sp_flags & HL_CONTAINED)))) { + cur_si->si_cont_list, &spp->sp_syn, + spp->sp_flags & HL_CONTAINED)))) { /* If we already tried matching in this line, and * there isn't a match before next_match_col, skip * this item. */ if (spp->sp_line_id == current_line_id - && spp->sp_startcol >= next_match_col) + && spp->sp_startcol >= next_match_col) { continue; + } spp->sp_line_id = current_line_id; colnr_T lc_col = current_col - spp->sp_offsets[SPO_LC_OFF]; @@ -1839,7 +1890,7 @@ static int syn_current_attr( IF_SYN_TIME(&spp->sp_time)); spp->sp_prog = regmatch.regprog; if (!r) { - /* no match in this line, try another one */ + // no match in this line, try another one spp->sp_startcol = MAXCOL; continue; } @@ -1848,7 +1899,7 @@ static int syn_current_attr( * Compute the first column of the match. */ syn_add_start_off(&pos, ®match, - spp, SPO_MS_OFF, -1); + spp, SPO_MS_OFF, -1); if (pos.lnum > current_lnum) { /* must have used end of match in a next line, * we can't handle that */ @@ -1865,8 +1916,9 @@ static int syn_current_attr( * If a previously found match starts at a lower * column number, don't use this one. */ - if (startcol >= next_match_col) + if (startcol >= next_match_col) { continue; + } /* * If we matched this pattern at this position @@ -1881,14 +1933,14 @@ static int syn_current_attr( endpos.lnum = regmatch.endpos[0].lnum; endpos.col = regmatch.endpos[0].col; - /* Compute the highlight start. */ + // Compute the highlight start. syn_add_start_off(&hl_startpos, ®match, - spp, SPO_HS_OFF, -1); + spp, SPO_HS_OFF, -1); - /* Compute the region start. */ - /* Default is to use the end of the match. */ + // Compute the region start. + // Default is to use the end of the match. syn_add_end_off(&eos_pos, ®match, - spp, SPO_RS_OFF, 0); + spp, SPO_RS_OFF, 0); /* * Grab the external submatches before they get @@ -1899,7 +1951,7 @@ static int syn_current_attr( re_extmatch_out = NULL; flags = 0; - eoe_pos.lnum = 0; /* avoid warning */ + eoe_pos.lnum = 0; // avoid warning eoe_pos.col = 0; end_idx = 0; hl_endpos.lnum = 0; @@ -1916,9 +1968,10 @@ static int syn_current_attr( startpos = endpos; find_endpos(idx, &startpos, &endpos, &hl_endpos, - &flags, &eoe_pos, &end_idx, cur_extmatch); - if (endpos.lnum == 0) - continue; /* not found */ + &flags, &eoe_pos, &end_idx, cur_extmatch); + if (endpos.lnum == 0) { + continue; // not found + } } /* * For a "match" the size must be > 0 after the @@ -1927,9 +1980,9 @@ static int syn_current_attr( */ else if (spp->sp_type == SPTYPE_MATCH) { syn_add_end_off(&hl_endpos, ®match, spp, - SPO_HE_OFF, 0); + SPO_HE_OFF, 0); syn_add_end_off(&endpos, ®match, spp, - SPO_ME_OFF, 0); + SPO_ME_OFF, 0); if (endpos.lnum == current_lnum && (int)endpos.col + syncing < startcol) { /* @@ -1949,8 +2002,9 @@ static int syn_current_attr( /* Highlighting must start after startpos and end * before endpos. */ if (hl_startpos.lnum == current_lnum - && (int)hl_startpos.col < startcol) + && (int)hl_startpos.col < startcol) { hl_startpos.col = startcol; + } limit_pos_zero(&hl_endpos, &endpos); next_match_idx = idx; @@ -1973,7 +2027,7 @@ static int syn_current_attr( * If we found a match at the current column, use it. */ if (next_match_idx >= 0 && next_match_col == (int)current_col) { - synpat_T *lspp; + synpat_T *lspp; /* When a zero-width item matched which has a nextgroup, * don't push the item but set nextgroup. */ @@ -2013,8 +2067,9 @@ static int syn_current_attr( if (((current_next_flags & HL_SKIPWHITE) && ascii_iswhite(line[current_col])) || ((current_next_flags & HL_SKIPEMPTY) - && *line == NUL)) + && *line == NUL)) { break; + } } /* @@ -2031,7 +2086,6 @@ static int syn_current_attr( found_match = true; } } - } while (found_match); restore_chartab(buf_chartab); @@ -2076,9 +2130,9 @@ static int syn_current_attr( /* There is no @Spell cluster: Do spelling for items without * @NoSpell cluster. */ if (syn_block->b_nospell_cluster_id == 0 - || current_trans_id == 0) + || current_trans_id == 0) { *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP); - else { + } else { sps.inc_tag = 0; sps.id = syn_block->b_nospell_cluster_id; sps.cont_in_list = NULL; @@ -2089,9 +2143,9 @@ static int syn_current_attr( * the @Spell cluster. But not when @NoSpell is also there. * At the toplevel only spell check when ":syn spell toplevel" * was used. */ - if (current_trans_id == 0) + if (current_trans_id == 0) { *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP); - else { + } else { sps.inc_tag = 0; sps.id = syn_block->b_spell_cluster_id; sps.cont_in_list = NULL; @@ -2099,8 +2153,9 @@ static int syn_current_attr( if (syn_block->b_nospell_cluster_id != 0) { sps.id = syn_block->b_nospell_cluster_id; - if (in_id_list(sip, sip->si_cont_list, &sps, 0)) + if (in_id_list(sip, sip->si_cont_list, &sps, 0)) { *can_spell = false; + } } } } @@ -2124,14 +2179,15 @@ static int syn_current_attr( --current_col; } } - } else if (can_spell != NULL) + } else if (can_spell != NULL) { /* Default: Only do spelling when there is no @Spell cluster or when * ":syn spell toplevel" was used. */ *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT ? (syn_block->b_spell_cluster_id == 0) : (syn_block->b_syn_spell == SYNSPL_TOP); + } - /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */ + // nextgroup ends at end of line, unless "skipnl" or "skipempty" present if (current_next_list != NULL && (line = syn_getcurline())[current_col] != NUL && line[current_col + 1] == NUL @@ -2139,10 +2195,11 @@ static int syn_current_attr( current_next_list = NULL; } - if (!GA_EMPTY(&zero_width_next_ga)) + if (!GA_EMPTY(&zero_width_next_ga)) { ga_clear(&zero_width_next_ga); + } - /* No longer need external matches. But keep next_match_extmatch. */ + // No longer need external matches. But keep next_match_extmatch. unref_extmatch(re_extmatch_out); re_extmatch_out = NULL; unref_extmatch(cur_extmatch); @@ -2151,16 +2208,14 @@ static int syn_current_attr( } -/* - * Check if we already matched pattern "idx" at the current column. - */ -static int did_match_already(int idx, garray_T *gap) +/// @return true if we already matched pattern "idx" at the current column. +static bool did_match_already(int idx, garray_T *gap) { for (int i = current_state.ga_len; --i >= 0; ) { if (CUR_STATE(i).si_m_startcol == (int)current_col && CUR_STATE(i).si_m_lnum == (int)current_lnum && CUR_STATE(i).si_idx == idx) { - return TRUE; + return true; } } @@ -2168,11 +2223,11 @@ static int did_match_already(int idx, garray_T *gap) * stack, and can only be matched once anyway. */ for (int i = gap->ga_len; --i >= 0; ) { if (((int *)(gap->ga_data))[i] == idx) { - return TRUE; + return true; } } - return FALSE; + return false; } /* @@ -2202,14 +2257,15 @@ static stateitem_T *push_next_match(void) cur_si->si_flags = spp->sp_flags; cur_si->si_seqnr = next_seqnr++; cur_si->si_cchar = spp->sp_cchar; - if (current_state.ga_len > 1) + if (current_state.ga_len > 1) { cur_si->si_flags |= CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL; + } cur_si->si_next_list = spp->sp_next_list; cur_si->si_extmatch = ref_extmatch(next_match_extmatch); if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE)) { - /* Try to find the end pattern in the current line */ - update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE); + // Try to find the end pattern in the current line + update_si_end(cur_si, (int)(next_match_m_endpos.col), true); check_keepend(); } else { cur_si->si_m_endpos = next_match_m_endpos; @@ -2219,8 +2275,9 @@ static stateitem_T *push_next_match(void) cur_si->si_eoe_pos = next_match_eoe_pos; cur_si->si_end_idx = next_match_end_idx; } - if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND)) + if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND)) { keepend_level = current_state.ga_len - 1; + } check_keepend(); update_si_attr(current_state.ga_len - 1); @@ -2242,15 +2299,16 @@ static stateitem_T *push_next_match(void) cur_si->si_flags = HL_MATCH; cur_si->si_seqnr = next_seqnr++; cur_si->si_flags |= save_flags; - if (cur_si->si_flags & HL_CONCEALENDS) + if (cur_si->si_flags & HL_CONCEALENDS) { cur_si->si_flags |= HL_CONCEAL; + } cur_si->si_next_list = NULL; check_keepend(); update_si_attr(current_state.ga_len - 1); } } - next_match_idx = -1; /* try other match next time */ + next_match_idx = -1; // try other match next time return cur_si; } @@ -2285,14 +2343,15 @@ static void check_state_ends(void) cur_si->si_h_endpos = cur_si->si_eoe_pos; cur_si->si_flags |= HL_MATCH; cur_si->si_seqnr = next_seqnr++; - if (cur_si->si_flags & HL_CONCEALENDS) + if (cur_si->si_flags & HL_CONCEALENDS) { cur_si->si_flags |= HL_CONCEAL; + } update_si_attr(current_state.ga_len - 1); - /* nextgroup= should not match in the end pattern */ + // nextgroup= should not match in the end pattern current_next_list = NULL; - /* what matches next may be different now, clear it */ + // what matches next may be different now, clear it next_match_idx = 0; next_match_col = MAXCOL; break; @@ -2302,8 +2361,9 @@ static void check_state_ends(void) current_next_list = cur_si->si_next_list; current_next_flags = cur_si->si_flags; if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)) - && syn_getcurline()[current_col] == NUL) + && syn_getcurline()[current_col] == NUL) { current_next_list = NULL; + } /* When the ended item has "extend", another item with * "keepend" now needs to check for its end. */ @@ -2311,13 +2371,15 @@ static void check_state_ends(void) pop_current_state(); - if (GA_EMPTY(¤t_state)) + if (GA_EMPTY(¤t_state)) { break; + } if (had_extend && keepend_level >= 0) { - syn_update_ends(FALSE); - if (GA_EMPTY(¤t_state)) + syn_update_ends(false); + if (GA_EMPTY(¤t_state)) { break; + } } cur_si = &CUR_STATE(current_state.ga_len - 1); @@ -2335,16 +2397,18 @@ static void check_state_ends(void) && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type == SPTYPE_START && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) { - update_si_end(cur_si, (int)current_col, TRUE); + update_si_end(cur_si, (int)current_col, true); check_keepend(); if ((current_next_flags & HL_HAS_EOL) && keepend_level < 0 - && syn_getcurline()[current_col] == NUL) + && syn_getcurline()[current_col] == NUL) { break; + } } } - } else + } else { break; + } } } @@ -2355,23 +2419,26 @@ static void check_state_ends(void) static void update_si_attr(int idx) { stateitem_T *sip = &CUR_STATE(idx); - synpat_T *spp; + synpat_T *spp; - /* This should not happen... */ - if (sip->si_idx < 0) + // This should not happen... + if (sip->si_idx < 0) { return; + } spp = &(SYN_ITEMS(syn_block)[sip->si_idx]); - if (sip->si_flags & HL_MATCH) + if (sip->si_flags & HL_MATCH) { sip->si_id = spp->sp_syn_match_id; - else + } else { sip->si_id = spp->sp_syn.id; + } sip->si_attr = syn_id2attr(sip->si_id); sip->si_trans_id = sip->si_id; - if (sip->si_flags & HL_MATCH) + if (sip->si_flags & HL_MATCH) { sip->si_cont_list = NULL; - else + } else { sip->si_cont_list = spp->sp_cont_list; + } /* * For transparent items, take attr from outer item. @@ -2382,8 +2449,9 @@ static void update_si_attr(int idx) if (idx == 0) { sip->si_attr = 0; sip->si_trans_id = 0; - if (sip->si_cont_list == NULL) + if (sip->si_cont_list == NULL) { sip->si_cont_list = ID_LIST_ALL; + } } else { sip->si_attr = CUR_STATE(idx - 1).si_attr; sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id; @@ -2412,17 +2480,20 @@ static void check_keepend(void) * This check can consume a lot of time; only do it from the level where * there really is a keepend. */ - if (keepend_level < 0) + if (keepend_level < 0) { return; + } /* * Find the last index of an "extend" item. "keepend" items before that * won't do anything. If there is no "extend" item "i" will be * "keepend_level" and all "keepend" items will work normally. */ - for (i = current_state.ga_len - 1; i > keepend_level; --i) - if (CUR_STATE(i).si_flags & HL_EXTEND) + for (i = current_state.ga_len - 1; i > keepend_level; --i) { + if (CUR_STATE(i).si_flags & HL_EXTEND) { break; + } + } maxpos.lnum = 0; maxpos.col = 0; @@ -2440,42 +2511,42 @@ static void check_keepend(void) if (maxpos.lnum == 0 || maxpos.lnum > sip->si_m_endpos.lnum || (maxpos.lnum == sip->si_m_endpos.lnum - && maxpos.col > sip->si_m_endpos.col)) + && maxpos.col > sip->si_m_endpos.col)) { maxpos = sip->si_m_endpos; + } if (maxpos_h.lnum == 0 || maxpos_h.lnum > sip->si_h_endpos.lnum || (maxpos_h.lnum == sip->si_h_endpos.lnum - && maxpos_h.col > sip->si_h_endpos.col)) + && maxpos_h.col > sip->si_h_endpos.col)) { maxpos_h = sip->si_h_endpos; + } } } } -/* - * Update an entry in the current_state stack for a start-skip-end pattern. - * This finds the end of the current item, if it's in the current line. - * - * Return the flags for the matched END. - */ -static void -update_si_end( - stateitem_T *sip, - int startcol, /* where to start searching for the end */ - int force /* when TRUE overrule a previous end */ -) +/// Update an entry in the current_state stack for a start-skip-end pattern. +/// This finds the end of the current item, if it's in the current line. +/// +/// @param startcol where to start searching for the end +/// @param force when true overrule a previous end +/// +/// @return the flags for the matched END. +static void update_si_end(stateitem_T *sip, int startcol, bool force) { lpos_T hl_endpos; lpos_T end_endpos; - /* return quickly for a keyword */ - if (sip->si_idx < 0) + // return quickly for a keyword + if (sip->si_idx < 0) { return; + } /* Don't update when it's already done. Can be a match of an end pattern * that started in a previous line. Watch out: can also be a "keepend" * from a containing item. */ - if (!force && sip->si_m_endpos.lnum >= current_lnum) + if (!force && sip->si_m_endpos.lnum >= current_lnum) { return; + } /* * We need to find the end of the region. It may continue in the next @@ -2488,23 +2559,23 @@ update_si_end( }; lpos_T endpos = { 0 }; find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos, - &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch); + &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch); if (endpos.lnum == 0) { - /* No end pattern matched. */ + // No end pattern matched. if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE) { - /* a "oneline" never continues in the next line */ + // a "oneline" never continues in the next line sip->si_ends = TRUE; sip->si_m_endpos.lnum = current_lnum; sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline()); } else { - /* continues in the next line */ + // continues in the next line sip->si_ends = FALSE; sip->si_m_endpos.lnum = 0; } sip->si_h_endpos = sip->si_m_endpos; } else { - /* match within this line */ + // match within this line sip->si_m_endpos = endpos; sip->si_h_endpos = hl_endpos; sip->si_eoe_pos = end_endpos; @@ -2533,49 +2604,49 @@ static void pop_current_state(void) unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch); --current_state.ga_len; } - /* after the end of a pattern, try matching a keyword or pattern */ + // after the end of a pattern, try matching a keyword or pattern next_match_idx = -1; - /* if first state with "keepend" is popped, reset keepend_level */ - if (keepend_level >= current_state.ga_len) + // if first state with "keepend" is popped, reset keepend_level + if (keepend_level >= current_state.ga_len) { keepend_level = -1; + } } -/* - * Find the end of a start/skip/end syntax region after "startpos". - * Only checks one line. - * Also handles a match item that continued from a previous line. - * If not found, the syntax item continues in the next line. m_endpos->lnum - * will be 0. - * If found, the end of the region and the end of the highlighting is - * computed. - */ -static void -find_endpos( - int idx, // index of the pattern - lpos_T *startpos, // where to start looking for an END match - lpos_T *m_endpos, // return: end of match - lpos_T *hl_endpos, // return: end of highlighting - long *flagsp, // return: flags of matching END - lpos_T *end_endpos, // return: end of end pattern match - int *end_idx, // return: group ID for end pat. match, or 0 - reg_extmatch_T *start_ext // submatches from the start pattern -) +/// Find the end of a start/skip/end syntax region after "startpos". +/// Only checks one line. +/// Also handles a match item that continued from a previous line. +/// If not found, the syntax item continues in the next line. m_endpos->lnum +/// will be 0. +/// If found, the end of the region and the end of the highlighting is +/// computed. +/// +/// @param idx index of the pattern +/// @param startpos where to start looking for an END match +/// @param m_endpos return: end of match +/// @param hl_endpos return: end of highlighting +/// @param flagsp return: flags of matching END +/// @param end_endpos return: end of end pattern match +/// @param end_idx return: group ID for end pat. match, or 0 +/// @param start_ext submatches from the start pattern +static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, + long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext) { colnr_T matchcol; - synpat_T *spp, *spp_skip; + synpat_T *spp, *spp_skip; int start_idx; int best_idx; regmmatch_T regmatch; - regmmatch_T best_regmatch; /* startpos/endpos of best match */ + regmmatch_T best_regmatch; // startpos/endpos of best match lpos_T pos; - char_u *line; - int had_match = false; + char_u *line; + bool had_match = false; char_u buf_chartab[32]; // chartab array for syn option iskeyword - /* just in case we are invoked for a keyword */ - if (idx < 0) + // just in case we are invoked for a keyword + if (idx < 0) { return; + } /* * Check for being called with a START pattern. @@ -2593,21 +2664,23 @@ find_endpos( */ for (;; ) { spp = &(SYN_ITEMS(syn_block)[idx]); - if (spp->sp_type != SPTYPE_START) + if (spp->sp_type != SPTYPE_START) { break; + } ++idx; } /* - * Lookup the SKIP pattern (if present) + * Lookup the SKIP pattern (if present) */ if (spp->sp_type == SPTYPE_SKIP) { spp_skip = spp; ++idx; - } else + } else { spp_skip = NULL; + } - /* Setup external matches for syn_regexec(). */ + // Setup external matches for syn_regexec(). unref_extmatch(re_extmatch_in); re_extmatch_in = ref_extmatch(start_ext); @@ -2627,11 +2700,13 @@ find_endpos( int lc_col = matchcol; spp = &(SYN_ITEMS(syn_block)[idx]); - if (spp->sp_type != SPTYPE_END) /* past last END pattern */ + if (spp->sp_type != SPTYPE_END) { // past last END pattern break; + } lc_col -= spp->sp_offsets[SPO_LC_OFF]; - if (lc_col < 0) + if (lc_col < 0) { lc_col = 0; + } regmatch.rmm_ic = spp->sp_ic; regmatch.regprog = spp->sp_prog; @@ -2652,8 +2727,9 @@ find_endpos( * If all end patterns have been tried, and there is no match, the * item continues until end-of-line. */ - if (best_idx == -1) + if (best_idx == -1) { break; + } /* * If the skip pattern matches before the end pattern, @@ -2662,8 +2738,9 @@ find_endpos( if (spp_skip != NULL) { int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF]; - if (lc_col < 0) + if (lc_col < 0) { lc_col = 0; + } regmatch.rmm_ic = spp_skip->sp_ic; regmatch.regprog = spp_skip->sp_prog; int r = syn_regexec(®match, startpos->lnum, lc_col, @@ -2710,16 +2787,18 @@ find_endpos( */ spp = &(SYN_ITEMS(syn_block)[best_idx]); syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1); - /* can't end before the start */ - if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col) + // can't end before the start + if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col) { m_endpos->col = startpos->col; + } syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1); - /* can't end before the start */ + // can't end before the start if (end_endpos->lnum == startpos->lnum - && end_endpos->col < startpos->col) + && end_endpos->col < startpos->col) { end_endpos->col = startpos->col; - /* can't end after the match */ + } + // can't end after the match limit_pos(end_endpos, m_endpos); /* @@ -2736,10 +2815,11 @@ find_endpos( } hl_endpos->col += spp->sp_offsets[SPO_RE_OFF]; - /* can't end before the start */ + // can't end before the start if (hl_endpos->lnum == startpos->lnum - && hl_endpos->col < startpos->col) + && hl_endpos->col < startpos->col) { hl_endpos->col = startpos->col; + } limit_pos(hl_endpos, m_endpos); /* now the match ends where the highlighting ends, it is turned @@ -2752,17 +2832,18 @@ find_endpos( *flagsp = spp->sp_flags; - had_match = TRUE; + had_match = true; break; } - /* no match for an END pattern in this line */ - if (!had_match) + // no match for an END pattern in this line + if (!had_match) { m_endpos->lnum = 0; + } restore_chartab(buf_chartab); - /* Remove external matches. */ + // Remove external matches. unref_extmatch(re_extmatch_in); re_extmatch_in = NULL; } @@ -2772,10 +2853,11 @@ find_endpos( */ static void limit_pos(lpos_T *pos, lpos_T *limit) { - if (pos->lnum > limit->lnum) + if (pos->lnum > limit->lnum) { *pos = *limit; - else if (pos->lnum == limit->lnum && pos->col > limit->col) + } else if (pos->lnum == limit->lnum && pos->col > limit->col) { pos->col = limit->col; + } } /* @@ -2783,28 +2865,27 @@ static void limit_pos(lpos_T *pos, lpos_T *limit) */ static void limit_pos_zero(lpos_T *pos, lpos_T *limit) { - if (pos->lnum == 0) + if (pos->lnum == 0) { *pos = *limit; - else + } else { limit_pos(pos, limit); + } } -/* - * Add offset to matched text for end of match or highlight. - */ -static void -syn_add_end_off( - lpos_T *result, // returned position - regmmatch_T *regmatch, // start/end of match - synpat_T *spp, // matched pattern - int idx, // index of offset - int extra // extra chars for offset to start -) +/// Add offset to matched text for end of match or highlight. +/// +/// @param result returned position +/// @param regmatch start/end of match +/// @param spp matched pattern +/// @param idx index of offset +/// @param extra extra chars for offset to start +static void syn_add_end_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, + int extra) { int col; int off; - char_u *base; - char_u *p; + char_u *base; + char_u *p; if (spp->sp_off_flags & (1 << idx)) { result->lnum = regmatch->startpos[0].lnum; @@ -2815,12 +2896,12 @@ syn_add_end_off( col = regmatch->endpos[0].col; off = spp->sp_offsets[idx]; } - /* Don't go past the end of the line. Matters for "rs=e+2" when there - * is a matchgroup. Watch out for match with last NL in the buffer. */ - if (result->lnum > syn_buf->b_ml.ml_line_count) + // Don't go past the end of the line. Matters for "rs=e+2" when there + // is a matchgroup. Watch out for match with last NL in the buffer. + if (result->lnum > syn_buf->b_ml.ml_line_count) { col = 0; - else if (off != 0) { - base = ml_get_buf(syn_buf, result->lnum, FALSE); + } else if (off != 0) { + base = ml_get_buf(syn_buf, result->lnum, false); p = base + col; if (off > 0) { while (off-- > 0 && *p != NUL) { @@ -2836,23 +2917,19 @@ syn_add_end_off( result->col = col; } -/* - * Add offset to matched text for start of match or highlight. - * Avoid resulting column to become negative. - */ -static void -syn_add_start_off( - lpos_T *result, // returned position - regmmatch_T *regmatch, // start/end of match - synpat_T *spp, - int idx, - int extra // extra chars for offset to end -) +/// Add offset to matched text for start of match or highlight. +/// Avoid resulting column to become negative. +/// +/// @param result returned position +/// @param regmatch start/end of match +/// @param extra extra chars for offset to end +static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, + int extra) { int col; int off; - char_u *base; - char_u *p; + char_u *base; + char_u *p; if (spp->sp_off_flags & (1 << (idx + SPO_COUNT))) { result->lnum = regmatch->endpos[0].lnum; @@ -2864,12 +2941,12 @@ syn_add_start_off( off = spp->sp_offsets[idx]; } 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 */ + // 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, FALSE)); + col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, false)); } if (off != 0) { - base = ml_get_buf(syn_buf, result->lnum, FALSE); + base = ml_get_buf(syn_buf, result->lnum, false); p = base + col; if (off > 0) { while (off-- && *p != NUL) { @@ -2890,7 +2967,7 @@ syn_add_start_off( */ static char_u *syn_getcurline(void) { - return ml_get_buf(syn_buf, current_lnum, FALSE); + return ml_get_buf(syn_buf, current_lnum, false); } /* @@ -2902,7 +2979,7 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T int r; int timed_out = 0; proftime_T pt; - const int l_syn_time_on = syn_time_on; + const bool l_syn_time_on = syn_time_on; if (l_syn_time_on) { pt = profile_start(); @@ -2926,8 +3003,9 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T st->slowest = pt; } ++st->count; - if (r > 0) + if (r > 0) { ++st->match; + } } if (timed_out && !syn_win->w_s->b_syn_slow) { syn_win->w_s->b_syn_slow = true; @@ -2942,20 +3020,19 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T return FALSE; } -/* - * Check one position in a line for a matching keyword. - * The caller must check if a keyword can start at startcol. - * Return its ID if found, 0 otherwise. - */ -static int check_keyword_id( - char_u *const line, - const int startcol, // position in line to check for keyword - int *const endcolp, // return: character after found keyword - long *const flagsp, // return: flags of matching keyword - int16_t **const next_listp, // return: next_list of matching keyword - stateitem_T *const cur_si, // item at the top of the stack - int *const ccharp // conceal substitution char -) +/// Check one position in a line for a matching keyword. +/// The caller must check if a keyword can start at startcol. +/// Return its ID if found, 0 otherwise. +/// +/// @param startcol position in line to check for keyword +/// @param endcolp return: character after found keyword +/// @param flagsp return: flags of matching keyword +/// @param next_listp return: next_list of matching keyword +/// @param cur_si item at the top of the stack +/// @param ccharp conceal substitution char +static int check_keyword_id(char_u *const line, const int startcol, int *const endcolp, + long *const flagsp, int16_t **const next_listp, + stateitem_T *const cur_si, int *const ccharp) { // Find first character after the keyword. First character was already // checked. @@ -3003,11 +3080,10 @@ static int check_keyword_id( /// When current_next_list is non-zero accept only that group, otherwise: /// Accept a not-contained keyword at toplevel. /// Accept a keyword at other levels only if it is in the contains list. -static keyentry_T *match_keyword(char_u *keyword, hashtab_T *ht, - stateitem_T *cur_si) +static keyentry_T *match_keyword(char_u *keyword, hashtab_T *ht, stateitem_T *cur_si) { hashitem_T *hi = hash_find(ht, keyword); - if (!HASHITEM_EMPTY(hi)) + if (!HASHITEM_EMPTY(hi)) { for (keyentry_T *kp = HI2KE(hi); kp != NULL; kp = kp->ke_next) { if (current_next_list != 0 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0) @@ -3018,6 +3094,7 @@ static keyentry_T *match_keyword(char_u *keyword, hashtab_T *ht, return kp; } } + } return NULL; } @@ -3026,12 +3103,13 @@ static keyentry_T *match_keyword(char_u *keyword, hashtab_T *ht, */ static void syn_cmd_conceal(exarg_T *eap, int syncing) { - char_u *arg = eap->arg; - char_u *next; + char_u *arg = eap->arg; + char_u *next; eap->nextcmd = find_nextcmd(arg); - if (eap->skip) + if (eap->skip) { return; + } next = skiptowhite(arg); if (*arg == NUL) { @@ -3054,12 +3132,13 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing) */ static void syn_cmd_case(exarg_T *eap, int syncing) { - char_u *arg = eap->arg; - char_u *next; + char_u *arg = eap->arg; + char_u *next; eap->nextcmd = find_nextcmd(arg); - if (eap->skip) + if (eap->skip) { return; + } next = skiptowhite(arg); if (*arg == NUL) { @@ -3084,14 +3163,18 @@ static void syn_cmd_foldlevel(exarg_T *eap, int syncing) char_u *arg_end; eap->nextcmd = find_nextcmd(arg); - if (eap->skip) + if (eap->skip) { return; + } if (*arg == NUL) { switch (curwin->w_s->b_syn_foldlevel) { - case SYNFLD_START: MSG(_("syntax foldlevel start")); break; - case SYNFLD_MINIMUM: MSG(_("syntax foldlevel minimum")); break; - default: break; + case SYNFLD_START: + MSG(_("syntax foldlevel start")); break; + case SYNFLD_MINIMUM: + MSG(_("syntax foldlevel minimum")); break; + default: + break; } return; } @@ -3117,12 +3200,13 @@ static void syn_cmd_foldlevel(exarg_T *eap, int syncing) */ static void syn_cmd_spell(exarg_T *eap, int syncing) { - char_u *arg = eap->arg; - char_u *next; + char_u *arg = eap->arg; + char_u *next; eap->nextcmd = find_nextcmd(arg); - if (eap->skip) + if (eap->skip) { return; + } next = skiptowhite(arg); if (*arg == NUL) { @@ -3201,17 +3285,17 @@ void syntax_clear(synblock_T *block) block->b_syn_containedin = false; block->b_syn_conceal = false; - /* free the keywords */ + // free the keywords clear_keywtab(&block->b_keywtab); clear_keywtab(&block->b_keywtab_ic); - /* free the syntax patterns */ + // free the syntax patterns for (int i = block->b_syn_patterns.ga_len; --i >= 0; ) { syn_clear_pattern(block, i); } ga_clear(&block->b_syn_patterns); - /* free the syntax clusters */ + // free the syntax clusters for (int i = block->b_syn_clusters.ga_len; --i >= 0; ) { syn_clear_cluster(block, i); } @@ -3230,11 +3314,11 @@ void syntax_clear(synblock_T *block) block->b_syn_folditems = 0; clear_string_option(&block->b_syn_isk); - /* free the stored states */ + // free the stored states syn_stack_free_all(block); invalidate_current_state(); - /* Reset the counter for ":syn include" */ + // Reset the counter for ":syn include" running_syn_inc_tag = 0; } @@ -3255,7 +3339,7 @@ void reset_synblock(win_T *wp) */ static void syntax_sync_clear(void) { - /* free the syntax patterns */ + // free the syntax patterns for (int i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) { if (SYN_ITEMS(curwin->w_s)[i].sp_syncing) { syn_remove_pattern(curwin->w_s, i); @@ -3272,7 +3356,7 @@ static void syntax_sync_clear(void) XFREE_CLEAR(curwin->w_s->b_syn_linecont_pat); clear_string_option(&curwin->w_s->b_syn_isk); - syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ + syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. } /* @@ -3280,14 +3364,15 @@ static void syntax_sync_clear(void) */ static void syn_remove_pattern(synblock_T *block, int idx) { - synpat_T *spp; + synpat_T *spp; spp = &(SYN_ITEMS(block)[idx]); - if (spp->sp_flags & HL_FOLD) + if (spp->sp_flags & HL_FOLD) { --block->b_syn_folditems; + } syn_clear_pattern(block, idx); memmove(spp, spp + 1, - sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1)); + sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1)); --block->b_syn_patterns.ga_len; } @@ -3299,7 +3384,7 @@ static void syn_clear_pattern(synblock_T *block, int i) { xfree(SYN_ITEMS(block)[i].sp_pattern); vim_regfree(SYN_ITEMS(block)[i].sp_prog); - /* Only free sp_cont_list and sp_next_list of first start pattern */ + // Only free sp_cont_list and sp_next_list of first start pattern if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START) { xfree(SYN_ITEMS(block)[i].sp_cont_list); xfree(SYN_ITEMS(block)[i].sp_next_list); @@ -3322,13 +3407,14 @@ static void syn_clear_cluster(synblock_T *block, int i) */ static void syn_cmd_clear(exarg_T *eap, int syncing) { - char_u *arg = eap->arg; - char_u *arg_end; + char_u *arg = eap->arg; + char_u *arg_end; int id; eap->nextcmd = find_nextcmd(arg); - if (eap->skip) + if (eap->skip) { return; + } /* * We have to disable this within ":syn include @group filename", @@ -3336,16 +3422,17 @@ static void syn_cmd_clear(exarg_T *eap, int syncing) * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn * clear". */ - if (curwin->w_s->b_syn_topgrp != 0) + if (curwin->w_s->b_syn_topgrp != 0) { return; + } if (ends_excmd(*arg)) { /* * No argument: Clear all syntax items. */ - if (syncing) + if (syncing) { syntax_sync_clear(); - else { + } else { syntax_clear(curwin->w_s); if (curwin->w_s == &curwin->w_buffer->b_s) { do_unlet(S_LEN("b:current_syntax"), true); @@ -3372,18 +3459,19 @@ static void syn_cmd_clear(exarg_T *eap, int syncing) XFREE_CLEAR(SYN_CLSTR(curwin->w_s)[scl_id].scl_list); } } else { - id = syn_namen2id(arg, (int)(arg_end - arg)); + id = syn_name2id_len(arg, (int)(arg_end - arg)); if (id == 0) { EMSG2(_(e_nogroup), arg); break; - } else + } else { syn_clear_one(id, syncing); + } } arg = skipwhite(arg_end); } } redraw_curbuf_later(SOME_VALID); - syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ + syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. } /* @@ -3391,19 +3479,20 @@ static void syn_cmd_clear(exarg_T *eap, int syncing) */ static void syn_clear_one(const int id, const bool syncing) { - synpat_T *spp; + synpat_T *spp; - /* Clear keywords only when not ":syn sync clear group-name" */ + // Clear keywords only when not ":syn sync clear group-name" if (!syncing) { syn_clear_keyword(id, &curwin->w_s->b_keywtab); syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic); } - /* clear the patterns for "id" */ + // clear the patterns for "id" for (int idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; ) { spp = &(SYN_ITEMS(curwin->w_s)[idx]); - if (spp->sp_syn.id != id || spp->sp_syncing != syncing) + if (spp->sp_syn.id != id || spp->sp_syncing != syncing) { continue; + } syn_remove_pattern(curwin->w_s, idx); } } @@ -3417,16 +3506,6 @@ static void syn_cmd_on(exarg_T *eap, int syncing) } /* - * Handle ":syntax enable" command. - */ -static void syn_cmd_enable(exarg_T *eap, int syncing) -{ - set_internal_string_var("syntax_cmd", (char_u *)"enable"); - syn_cmd_onoff(eap, "syntax"); - do_unlet(S_LEN("g:syntax_cmd"), true); -} - -/* * Handle ":syntax reset" command. * It actually resets highlighting, not syntax. */ @@ -3434,9 +3513,7 @@ static void syn_cmd_reset(exarg_T *eap, int syncing) { eap->nextcmd = check_nextcmd(eap->arg); if (!eap->skip) { - set_internal_string_var("syntax_cmd", (char_u *)"reset"); - do_cmdline_cmd("runtime! syntax/syncolor.vim"); - do_unlet(S_LEN("g:syntax_cmd"), true); + init_highlight(true, true); } } @@ -3475,25 +3552,22 @@ void syn_maybe_enable(void) exarg_T ea; ea.arg = (char_u *)""; ea.skip = false; - syn_cmd_enable(&ea, false); + syn_cmd_on(&ea, false); } } -/* - * Handle ":syntax [list]" command: list current syntax words. - */ -static void -syn_cmd_list( - exarg_T *eap, - int syncing /* when TRUE: list syncing items */ -) +/// Handle ":syntax [list]" command: list current syntax words. +/// +/// @param syncing when TRUE: list syncing items +static void syn_cmd_list(exarg_T *eap, int syncing) { - char_u *arg = eap->arg; - char_u *arg_end; + char_u *arg = eap->arg; + char_u *arg_end; eap->nextcmd = find_nextcmd(arg); - if (eap->skip) + if (eap->skip) { return; + } if (!syntax_present(curwin)) { MSG(_(msg_no_items)); @@ -3506,7 +3580,7 @@ syn_cmd_list( syn_lines_msg(); syn_match_msg(); return; - } else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH)) { + } else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH)) { if (curwin->w_s->b_syn_sync_minlines == 0) { MSG_PUTS(_("no syncing")); } else { @@ -3529,8 +3603,9 @@ syn_cmd_list( syn_lines_msg(); syn_match_msg(); } - } else + } else { MSG_PUTS_TITLE(_("\n--- Syntax items ---")); + } if (ends_excmd(*arg)) { /* * No argument: List all group IDs and all syntax clusters. @@ -3549,12 +3624,13 @@ syn_cmd_list( arg_end = skiptowhite(arg); if (*arg == '@') { int id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1)); - if (id == 0) + if (id == 0) { EMSG2(_("E392: No such syntax cluster: %s"), arg); - else + } else { syn_list_cluster(id - SYNID_CLUSTER); + } } else { - int id = syn_namen2id(arg, (int)(arg_end - arg)); + int id = syn_name2id_len(arg, (int)(arg_end - arg)); if (id == 0) { EMSG2(_(e_nogroup), arg); } else { @@ -3603,37 +3679,33 @@ static void syn_match_msg(void) static int last_matchgroup; -/* - * List one syntax item, for ":syntax" or "syntax list syntax_name". - */ -static void -syn_list_one( - const int id, - const bool syncing, // when true: list syncing items - const bool link_only // when true; list link-only too -) +/// List one syntax item, for ":syntax" or "syntax list syntax_name". +/// +/// @param syncing when true: list syncing items +/// @param link_only when true; list link-only too +static void syn_list_one(const int id, const bool syncing, const bool link_only) { bool did_header = false; static struct name_list namelist1[] = { - {HL_DISPLAY, "display"}, - {HL_CONTAINED, "contained"}, - {HL_ONELINE, "oneline"}, - {HL_KEEPEND, "keepend"}, - {HL_EXTEND, "extend"}, - {HL_EXCLUDENL, "excludenl"}, - {HL_TRANSP, "transparent"}, - {HL_FOLD, "fold"}, - {HL_CONCEAL, "conceal"}, - {HL_CONCEALENDS, "concealends"}, - {0, NULL} + { HL_DISPLAY, "display" }, + { HL_CONTAINED, "contained" }, + { HL_ONELINE, "oneline" }, + { HL_KEEPEND, "keepend" }, + { HL_EXTEND, "extend" }, + { HL_EXCLUDENL, "excludenl" }, + { HL_TRANSP, "transparent" }, + { HL_FOLD, "fold" }, + { HL_CONCEAL, "conceal" }, + { HL_CONCEALENDS, "concealends" }, + { 0, NULL } }; static struct name_list namelist2[] = { - {HL_SKIPWHITE, "skipwhite"}, - {HL_SKIPNL, "skipnl"}, - {HL_SKIPEMPTY, "skipempty"}, - {0, NULL} + { HL_SKIPWHITE, "skipwhite" }, + { HL_SKIPNL, "skipnl" }, + { HL_SKIPEMPTY, "skipempty" }, + { 0, NULL } }; const int attr = HL_ATTR(HLF_D); // highlight like directories @@ -3660,14 +3732,17 @@ syn_list_one( if (spp->sp_type == SPTYPE_MATCH) { put_pattern("match", ' ', spp, attr); msg_putchar(' '); - } else if (spp->sp_type == SPTYPE_START) { - while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START) + } else if (spp->sp_type == SPTYPE_START) { + while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START) { put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr); - if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP) + } + if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP) { put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr); + } while (idx < curwin->w_s->b_syn_patterns.ga_len - && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END) + && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END) { put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr); + } --idx; msg_putchar(' '); } @@ -3692,16 +3767,17 @@ syn_list_one( msg_puts_attr("groupthere", attr); } msg_putchar(' '); - if (spp->sp_sync_idx >= 0) + if (spp->sp_sync_idx >= 0) { msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s) [spp->sp_sync_idx].sp_syn.id - 1].sg_name); - else + } else { MSG_PUTS("NONE"); + } msg_putchar(' '); } } - /* list the link, if there is one */ + // list the link, if there is one if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int) { (void)syn_list_header(did_header, 0, id, true); msg_puts_attr("links to", attr); @@ -3714,11 +3790,12 @@ static void syn_list_flags(struct name_list *nlist, int flags, int attr) { int i; - for (i = 0; nlist[i].flag != 0; ++i) + for (i = 0; nlist[i].flag != 0; ++i) { if (flags & nlist[i].flag) { msg_puts_attr(nlist[i].name, attr); msg_putchar(' '); } + } } /* @@ -3728,14 +3805,16 @@ static void syn_list_cluster(int id) { int endcol = 15; - /* slight hack: roughly duplicate the guts of syn_list_header() */ + // slight hack: roughly duplicate the guts of syn_list_header() msg_putchar('\n'); msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name); - if (msg_col >= endcol) /* output at least one space */ + if (msg_col >= endcol) { // output at least one space endcol = msg_col + 1; - if (Columns <= endcol) /* avoid hang for tiny window */ + } + if (Columns <= endcol) { // avoid hang for tiny window endcol = Columns - 1; + } msg_advance(endcol); if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL) { @@ -3746,9 +3825,7 @@ static void syn_list_cluster(int id) } } -static void put_id_list(const char *const name, - const int16_t *const list, - const int attr) +static void put_id_list(const char *const name, const int16_t *const list, const int attr) { msg_puts_attr(name, attr); msg_putchar('='); @@ -3759,38 +3836,40 @@ static void put_id_list(const char *const name, } else { msg_puts("ALL"); } - } else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED) { + } else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED) { msg_puts("TOP"); - } else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER) { + } else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER) { msg_puts("CONTAINED"); - } else if (*p >= SYNID_CLUSTER) { + } else if (*p >= SYNID_CLUSTER) { int scl_id = *p - SYNID_CLUSTER; msg_putchar('@'); msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name); - } else + } else { msg_outtrans(HL_TABLE()[*p - 1].sg_name); - if (p[1]) + } + if (p[1]) { msg_putchar(','); + } } msg_putchar(' '); } -static void put_pattern(const char *const s, const int c, - const synpat_T *const spp, const int attr) +static void put_pattern(const char *const s, const int c, const synpat_T *const spp, const int attr) { static const char *const sepchars = "/+=-#@\"|'^&"; int i; - /* May have to write "matchgroup=group" */ + // May have to write "matchgroup=group" if (last_matchgroup != spp->sp_syn_match_id) { last_matchgroup = spp->sp_syn_match_id; msg_puts_attr("matchgroup", attr); msg_putchar('='); - if (last_matchgroup == 0) + if (last_matchgroup == 0) { msg_outtrans((char_u *)"NONE"); - else + } else { msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name); + } msg_putchar(' '); } @@ -3798,12 +3877,13 @@ static void put_pattern(const char *const s, const int c, msg_puts_attr(s, attr); msg_putchar(c); - /* output the pattern, in between a char that is not in the pattern */ - for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; ) + // output the pattern, in between a char that is not in the pattern + for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; ) { if (sepchars[++i] == NUL) { - i = 0; /* no good char found, just use the first one */ + i = 0; // no good char found, just use the first one break; } + } msg_putchar(sepchars[i]); msg_outtrans(spp->sp_pattern); msg_putchar(sepchars[i]); @@ -3821,12 +3901,14 @@ static void put_pattern(const char *const s, const int c, msg_puts(spo_name_tab[i]); const long n = spp->sp_offsets[i]; if (i != SPO_LC_OFF) { - if (spp->sp_off_flags & mask) + if (spp->sp_off_flags & mask) { msg_putchar('s'); - else + } else { msg_putchar('e'); - if (n > 0) + } + if (n > 0) { msg_putchar('+'); + } } if (n || i == SPO_LC_OFF) { msg_outnum(n); @@ -3836,14 +3918,13 @@ static void put_pattern(const char *const s, const int c, msg_putchar(' '); } -// List or clear the keywords for one syntax group. -// Return true if the header has been printed. -static bool syn_list_keywords( - const int id, - const hashtab_T *const ht, - bool did_header, // header has already been printed - const int attr -) +/// List or clear the keywords for one syntax group. +/// +/// @param did_header header has already been printed +/// +/// @return true if the header has been printed. +static bool syn_list_keywords(const int id, const hashtab_T *const ht, bool did_header, + const int attr) { int prev_contained = 0; const int16_t *prev_next_list = NULL; @@ -3870,7 +3951,7 @@ static bool syn_list_keywords( || prev_skipempty != (kp->flags & HL_SKIPEMPTY) || prev_cont_in_list != kp->k_syn.cont_in_list || prev_next_list != kp->next_list) { - force_newline = true; + force_newline = true; } else { outlen = (int)STRLEN(kp->keyword); } @@ -3924,10 +4005,10 @@ static bool syn_list_keywords( static void syn_clear_keyword(int id, hashtab_T *ht) { - hashitem_T *hi; - keyentry_T *kp; - keyentry_T *kp_prev; - keyentry_T *kp_next; + hashitem_T *hi; + keyentry_T *kp; + keyentry_T *kp_prev; + keyentry_T *kp_next; int todo; hash_lock(ht); @@ -3942,12 +4023,14 @@ static void syn_clear_keyword(int id, hashtab_T *ht) if (kp->k_syn.id == id) { kp_next = kp->ke_next; if (kp_prev == NULL) { - if (kp_next == NULL) + if (kp_next == NULL) { hash_remove(ht, hi); - else + } else { hi->hi_key = KE2HIKEY(kp_next); - } else + } + } else { kp_prev->ke_next = kp_next; + } xfree(kp->next_list); xfree(kp->k_syn.cont_in_list); xfree(kp); @@ -3966,10 +4049,10 @@ static void syn_clear_keyword(int id, hashtab_T *ht) */ static void clear_keywtab(hashtab_T *ht) { - hashitem_T *hi; + hashitem_T *hi; int todo; - keyentry_T *kp; - keyentry_T *kp_next; + keyentry_T *kp; + keyentry_T *kp_next; todo = (int)ht->ht_used; for (hi = ht->ht_array; todo > 0; ++hi) { @@ -3994,11 +4077,8 @@ static void clear_keywtab(hashtab_T *ht) /// @param flags flags for this keyword /// @param cont_in_list containedin for this keyword /// @param next_list nextgroup for this keyword -static void add_keyword(char_u *const name, - const int id, - const int flags, - int16_t *const cont_in_list, - int16_t *const next_list, +static void add_keyword(char_u *const name, const int id, const int flags, + int16_t *const cont_in_list, int16_t *const next_list, const int conceal_char) { char_u name_folded[MAXKEYWLEN + 1]; @@ -4040,18 +4120,16 @@ static void add_keyword(char_u *const name, } } -/* - * Get the start and end of the group name argument. - * Return a pointer to the first argument. - * Return NULL if the end of the command was found instead of further args. - */ -static char_u * -get_group_name ( - char_u *arg, /* start of the argument */ - char_u **name_end /* pointer to end of the name */ -) +/// Get the start and end of the group name argument. +/// +/// @param arg start of the argument +/// @param name_end pointer to end of the name +/// +/// @return a pointer to the first argument. +/// Return NULL if the end of the command was found instead of further args. +static char_u *get_group_name(char_u *arg, char_u **name_end) { - char_u *rest; + char_u *rest; *name_end = skiptowhite(arg); rest = skipwhite(*name_end); @@ -4060,62 +4138,62 @@ get_group_name ( * Check if there are enough arguments. The first argument may be a * pattern, where '|' is allowed, so only check for NUL. */ - if (ends_excmd(*arg) || *rest == NUL) + if (ends_excmd(*arg) || *rest == NUL) { return NULL; + } return rest; } -/* - * Check for syntax command option arguments. - * This can be called at any place in the list of arguments, and just picks - * out the arguments that are known. Can be called several times in a row to - * collect all options in between other arguments. - * Return a pointer to the next argument (which isn't an option). - * Return NULL for any error; - */ -static char_u * -get_syn_options( - char_u *arg, // next argument to be checked - syn_opt_arg_T *opt, // various things - int *conceal_char, - int skip // TRUE if skipping over command -) -{ - char_u *gname_start, *gname; +/// Check for syntax command option arguments. +/// This can be called at any place in the list of arguments, and just picks +/// out the arguments that are known. Can be called several times in a row to +/// collect all options in between other arguments. +/// +/// @param arg next argument to be checked +/// @param opt various things +/// @param skip TRUE if skipping over command +/// +/// @return a pointer to the next argument (which isn't an option). +/// Return NULL for any error; +static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_char, int skip) +{ + char_u *gname_start, *gname; int syn_id; int len = 0; - char *p; + char *p; int fidx; static const struct flag { - char *name; + char *name; int argtype; int flags; - } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED}, - {"oOnNeElLiInNeE", 0, HL_ONELINE}, - {"kKeEeEpPeEnNdD", 0, HL_KEEPEND}, - {"eExXtTeEnNdD", 0, HL_EXTEND}, - {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL}, - {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP}, - {"sSkKiIpPnNlL", 0, HL_SKIPNL}, - {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE}, - {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY}, - {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE}, - {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE}, - {"dDiIsSpPlLaAyY", 0, HL_DISPLAY}, - {"fFoOlLdD", 0, HL_FOLD}, - {"cCoOnNcCeEaAlL", 0, HL_CONCEAL}, - {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS}, - {"cCcChHaArR", 11, 0}, - {"cCoOnNtTaAiInNsS", 1, 0}, - {"cCoOnNtTaAiInNeEdDiInN", 2, 0}, - {"nNeExXtTgGrRoOuUpP", 3, 0},}; + } flagtab[] = { { "cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED }, + { "oOnNeElLiInNeE", 0, HL_ONELINE }, + { "kKeEeEpPeEnNdD", 0, HL_KEEPEND }, + { "eExXtTeEnNdD", 0, HL_EXTEND }, + { "eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL }, + { "tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP }, + { "sSkKiIpPnNlL", 0, HL_SKIPNL }, + { "sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE }, + { "sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY }, + { "gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE }, + { "gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE }, + { "dDiIsSpPlLaAyY", 0, HL_DISPLAY }, + { "fFoOlLdD", 0, HL_FOLD }, + { "cCoOnNcCeEaAlL", 0, HL_CONCEAL }, + { "cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS }, + { "cCcChHaArR", 11, 0 }, + { "cCoOnNtTaAiInNsS", 1, 0 }, + { "cCoOnNtTaAiInNeEdDiInN", 2, 0 }, + { "nNeExXtTgGrRoOuUpP", 3, 0 }, }; static const char *const first_letters = "cCoOkKeEtTsSgGdDfFnN"; - if (arg == NULL) /* already detected error */ + if (arg == NULL) { // already detected error return NULL; + } - if (curwin->w_s->b_syn_conceal) + if (curwin->w_s->b_syn_conceal) { opt->flags |= HL_CONCEAL; + } for (;; ) { /* @@ -4123,15 +4201,17 @@ get_syn_options( * Need to skip quickly when no option name is found. * Also avoid tolower(), it's slow. */ - if (strchr(first_letters, *arg) == NULL) + if (strchr(first_letters, *arg) == NULL) { break; + } for (fidx = ARRAY_SIZE(flagtab); --fidx >= 0; ) { p = flagtab[fidx].name; int i; for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) { - if (arg[len] != p[i] && arg[len] != p[i + 1]) + if (arg[len] != p[i] && arg[len] != p[i + 1]) { break; + } } if (p[i] == NUL && (ascii_iswhite(arg[len]) || (flagtab[fidx].argtype > 0 @@ -4140,14 +4220,16 @@ get_syn_options( if (opt->keyword && (flagtab[fidx].flags == HL_DISPLAY || flagtab[fidx].flags == HL_FOLD - || flagtab[fidx].flags == HL_EXTEND)) - /* treat "display", "fold" and "extend" as a keyword */ + || flagtab[fidx].flags == HL_EXTEND)) { + // treat "display", "fold" and "extend" as a keyword fidx = -1; + } break; } } - if (fidx < 0) /* no match found */ + if (fidx < 0) { // no match found break; + } if (flagtab[fidx].argtype == 1) { if (!opt->has_cont_list) { @@ -4157,15 +4239,15 @@ get_syn_options( if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL) { return NULL; } - } else if (flagtab[fidx].argtype == 2) { + } else if (flagtab[fidx].argtype == 2) { if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL) { return NULL; } - } else if (flagtab[fidx].argtype == 3) { + } else if (flagtab[fidx].argtype == 3) { if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL) { return NULL; } - } else if (flagtab[fidx].argtype == 11 && arg[5] == '=') { + } else if (flagtab[fidx].argtype == 11 && arg[5] == '=') { // cchar=? *conceal_char = utf_ptr2char(arg + 6); arg += mb_ptr2len(arg + 6) - 1; @@ -4186,20 +4268,22 @@ get_syn_options( } gname_start = arg; arg = skiptowhite(arg); - if (gname_start == arg) + if (gname_start == arg) { return NULL; + } gname = vim_strnsave(gname_start, arg - gname_start); if (STRCMP(gname, "NONE") == 0) { *opt->sync_idx = NONE_IDX; } else { syn_id = syn_name2id(gname); int i; - for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) + for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; ) { if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START) { *opt->sync_idx = i; break; } + } if (i < 0) { EMSG2(_("E394: Didn't find region item for %s"), gname); xfree(gname); @@ -4210,9 +4294,10 @@ get_syn_options( xfree(gname); arg = skipwhite(arg); } else if (flagtab[fidx].flags == HL_FOLD - && foldmethodIsSyntax(curwin)) - /* Need to update folds later. */ + && foldmethodIsSyntax(curwin)) { + // Need to update folds later. foldUpdateAll(curwin); + } } } @@ -4226,8 +4311,9 @@ get_syn_options( */ static void syn_incl_toplevel(int id, int *flagsp) { - if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0) + if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0) { return; + } *flagsp |= HL_CONTAINED; if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER) { // We have to alloc this, because syn_combine_list() will free it. @@ -4237,7 +4323,7 @@ static void syn_incl_toplevel(int id, int *flagsp) grp_list[0] = id; grp_list[1] = 0; syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list, - CLUSTER_ADD); + CLUSTER_ADD); } } @@ -4246,18 +4332,19 @@ static void syn_incl_toplevel(int id, int *flagsp) */ static void syn_cmd_include(exarg_T *eap, int syncing) { - char_u *arg = eap->arg; + char_u *arg = eap->arg; int sgl_id = 1; - char_u *group_name_end; - char_u *rest; - char_u *errormsg = NULL; + char_u *group_name_end; + char_u *rest; + char_u *errormsg = NULL; int prev_toplvl_grp; int prev_syn_inc_tag; - int source = FALSE; + bool source = false; eap->nextcmd = find_nextcmd(arg); - if (eap->skip) + if (eap->skip) { return; + } if (arg[0] == '@') { ++arg; @@ -4267,9 +4354,10 @@ static void syn_cmd_include(exarg_T *eap, int syncing) return; } sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg)); - if (sgl_id == 0) + if (sgl_id == 0) { return; - /* separate_nextcmd() and expand_filename() depend on this */ + } + // separate_nextcmd() and expand_filename() depend on this eap->arg = rest; } @@ -4285,8 +4373,9 @@ static void syn_cmd_include(exarg_T *eap, int syncing) // ":runtime!" is used. source = true; if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL) { - if (errormsg != NULL) + if (errormsg != NULL) { EMSG(errormsg); + } return; } } @@ -4317,13 +4406,13 @@ static void syn_cmd_include(exarg_T *eap, int syncing) */ static void syn_cmd_keyword(exarg_T *eap, int syncing) { - char_u *arg = eap->arg; - char_u *group_name_end; + char_u *arg = eap->arg; + char_u *group_name_end; int syn_id; - char_u *rest; - char_u *keyword_copy = NULL; - char_u *p; - char_u *kw; + char_u *rest; + char_u *keyword_copy = NULL; + char_u *p; + char_u *kw; syn_opt_arg_T syn_opt_arg; int cnt; int conceal_char = NUL; @@ -4413,39 +4502,36 @@ error: } } - if (rest != NULL) + if (rest != NULL) { eap->nextcmd = check_nextcmd(rest); - else + } else { EMSG2(_(e_invarg2), arg); + } redraw_curbuf_later(SOME_VALID); - syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ + syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. } -/* - * Handle ":syntax match {name} [{options}] {pattern} [{options}]". - * - * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .." - */ -static void -syn_cmd_match( - exarg_T *eap, - int syncing /* TRUE for ":syntax sync match .. " */ -) -{ - char_u *arg = eap->arg; - char_u *group_name_end; - char_u *rest; - synpat_T item; /* the item found in the line */ +/// Handle ":syntax match {name} [{options}] {pattern} [{options}]". +/// +/// Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .." +/// +/// @param syncing TRUE for ":syntax sync match .. " +static void syn_cmd_match(exarg_T *eap, int syncing) +{ + char_u *arg = eap->arg; + char_u *group_name_end; + char_u *rest; + synpat_T item; // the item found in the line int syn_id; syn_opt_arg_T syn_opt_arg; int sync_idx = 0; int conceal_char = NUL; - /* Isolate the group name, check for validity */ + // Isolate the group name, check for validity rest = get_group_name(arg, &group_name_end); - /* Get options before the pattern */ + // Get options before the pattern syn_opt_arg.flags = 0; syn_opt_arg.keyword = false; syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL; @@ -4455,7 +4541,7 @@ syn_cmd_match( syn_opt_arg.next_list = NULL; rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); - /* get the pattern. */ + // get the pattern. init_syn_patterns(); memset(&item, 0, sizeof(item)); rest = get_syn_pattern(rest, &item); @@ -4466,14 +4552,14 @@ syn_cmd_match( // Get options after the pattern rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); - if (rest != NULL) { /* all arguments are valid */ + if (rest != NULL) { // all arguments are valid /* * Check for trailing command and illegal trailing arguments. */ eap->nextcmd = check_nextcmd(rest); - if (!ends_excmd(*rest) || eap->skip) + if (!ends_excmd(*rest) || eap->skip) { rest = NULL; - else { + } else { if ((syn_id = syn_check_group(arg, (int)(group_name_end - arg))) != 0) { syn_incl_toplevel(syn_id, &syn_opt_arg.flags); /* @@ -4491,19 +4577,22 @@ syn_cmd_match( spp->sp_cont_list = syn_opt_arg.cont_list; spp->sp_syn.cont_in_list = syn_opt_arg.cont_in_list; spp->sp_cchar = conceal_char; - if (syn_opt_arg.cont_in_list != NULL) + if (syn_opt_arg.cont_in_list != NULL) { curwin->w_s->b_syn_containedin = TRUE; + } spp->sp_next_list = syn_opt_arg.next_list; - /* remember that we found a match for syncing on */ - if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE)) + // remember that we found a match for syncing on + if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE)) { curwin->w_s->b_syn_sync_flags |= SF_MATCH; - if (syn_opt_arg.flags & HL_FOLD) + } + if (syn_opt_arg.flags & HL_FOLD) { ++curwin->w_s->b_syn_folditems; + } redraw_curbuf_later(SOME_VALID); - syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ - return; /* don't free the progs and patterns now */ + syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. + return; // don't free the progs and patterns now } } } @@ -4517,49 +4606,46 @@ syn_cmd_match( xfree(syn_opt_arg.cont_in_list); xfree(syn_opt_arg.next_list); - if (rest == NULL) + if (rest == NULL) { EMSG2(_(e_invarg2), arg); + } } -/* - * Handle ":syntax region {group-name} [matchgroup={group-name}] - * start {start} .. [skip {skip}] end {end} .. [{options}]". - */ -static void -syn_cmd_region( - exarg_T *eap, - int syncing /* TRUE for ":syntax sync region .." */ -) -{ - char_u *arg = eap->arg; - char_u *group_name_end; - char_u *rest; /* next arg, NULL on error */ - char_u *key_end; - char_u *key = NULL; - char_u *p; +/// Handle ":syntax region {group-name} [matchgroup={group-name}] +/// start {start} .. [skip {skip}] end {end} .. [{options}]". +/// +/// @param syncing TRUE for ":syntax sync region .." +static void syn_cmd_region(exarg_T *eap, int syncing) +{ + char_u *arg = eap->arg; + char_u *group_name_end; + char_u *rest; // next arg, NULL on error + char_u *key_end; + char_u *key = NULL; + char_u *p; int item; #define ITEM_START 0 #define ITEM_SKIP 1 #define ITEM_END 2 #define ITEM_MATCHGROUP 3 struct pat_ptr { - synpat_T *pp_synp; /* pointer to syn_pattern */ - int pp_matchgroup_id; /* matchgroup ID */ - struct pat_ptr *pp_next; /* pointer to next pat_ptr */ + synpat_T *pp_synp; // pointer to syn_pattern + int pp_matchgroup_id; // matchgroup ID + struct pat_ptr *pp_next; // pointer to next pat_ptr } *(pat_ptrs[3]); - /* patterns found in the line */ - struct pat_ptr *ppp; - struct pat_ptr *ppp_next; - int pat_count = 0; /* nr of syn_patterns found */ + // patterns found in the line + struct pat_ptr *ppp; + struct pat_ptr *ppp_next; + int pat_count = 0; // nr of syn_patterns found int syn_id; int matchgroup_id = 0; - int not_enough = FALSE; /* not enough arguments */ - int illegal = FALSE; /* illegal arguments */ - int success = FALSE; + bool not_enough = false; // not enough arguments + bool illegal = false; // illegal arguments + bool success = false; syn_opt_arg_T syn_opt_arg; int conceal_char = NUL; - /* Isolate the group name, check for validity */ + // Isolate the group name, check for validity rest = get_group_name(arg, &group_name_end); pat_ptrs[0] = NULL; @@ -4584,10 +4670,11 @@ syn_cmd_region( break; } - /* must be a pattern or matchgroup then */ + // must be a pattern or matchgroup then key_end = rest; - while (*key_end && !ascii_iswhite(*key_end) && *key_end != '=') + while (*key_end && !ascii_iswhite(*key_end) && *key_end != '=') { ++key_end; + } xfree(key); key = vim_strnsave_up(rest, key_end - rest); if (STRCMP(key, "MATCHGROUP") == 0) { @@ -4613,18 +4700,18 @@ syn_cmd_region( } rest = skipwhite(rest + 1); if (*rest == NUL) { - not_enough = TRUE; + not_enough = true; break; } if (item == ITEM_MATCHGROUP) { p = skiptowhite(rest); - if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip) + if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip) { matchgroup_id = 0; - else { + } else { matchgroup_id = syn_check_group(rest, (int)(p - rest)); if (matchgroup_id == 0) { - illegal = TRUE; + illegal = true; break; } } @@ -4660,8 +4747,9 @@ syn_cmd_region( } } xfree(key); - if (illegal || not_enough) + if (illegal || not_enough) { rest = NULL; + } // Must have a "start" and "end" pattern. if (rest != NULL && (pat_ptrs[ITEM_START] == NULL @@ -4676,9 +4764,9 @@ syn_cmd_region( * If OK, add the item. */ eap->nextcmd = check_nextcmd(rest); - if (!ends_excmd(*rest) || eap->skip) + if (!ends_excmd(*rest) || eap->skip) { rest = NULL; - else { + } else { ga_grow(&(curwin->w_s->b_syn_patterns), pat_count); if ((syn_id = syn_check_group(arg, (int)(group_name_end - arg))) != 0) { syn_incl_toplevel(syn_id, &syn_opt_arg.flags); @@ -4705,21 +4793,23 @@ syn_cmd_region( syn_opt_arg.cont_list; SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list = syn_opt_arg.cont_in_list; - if (syn_opt_arg.cont_in_list != NULL) + if (syn_opt_arg.cont_in_list != NULL) { curwin->w_s->b_syn_containedin = TRUE; + } SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list; } ++curwin->w_s->b_syn_patterns.ga_len; ++idx; - if (syn_opt_arg.flags & HL_FOLD) + if (syn_opt_arg.flags & HL_FOLD) { ++curwin->w_s->b_syn_folditems; + } } } redraw_curbuf_later(SOME_VALID); - syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ - success = TRUE; /* don't free the progs and patterns now */ + syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. + success = true; // don't free the progs and patterns now } } } @@ -4727,7 +4817,7 @@ syn_cmd_region( /* * Free the allocated memory. */ - for (item = ITEM_START; item <= ITEM_END; ++item) + for (item = ITEM_START; item <= ITEM_END; ++item) { for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next) { if (!success && ppp->pp_synp != NULL) { vim_regfree(ppp->pp_synp->sp_prog); @@ -4737,15 +4827,17 @@ syn_cmd_region( ppp_next = ppp->pp_next; xfree(ppp); } + } if (!success) { xfree(syn_opt_arg.cont_list); xfree(syn_opt_arg.cont_in_list); xfree(syn_opt_arg.next_list); - if (not_enough) + if (not_enough) { EMSG2(_("E399: Not enough arguments: syntax region %s"), arg); - else if (illegal || rest == NULL) + } else if (illegal || rest == NULL) { EMSG2(_(e_invarg2), arg); + } } } @@ -4760,8 +4852,7 @@ static int syn_compare_stub(const void *const v1, const void *const v2) // Combines lists of syntax clusters. // *clstr1 and *clstr2 must both be allocated memory; they will be consumed. -static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, - const int list_op) +static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, const int list_op) { size_t count1 = 0; size_t count2 = 0; @@ -4772,15 +4863,18 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, /* * Handle degenerate cases. */ - if (*clstr2 == NULL) + if (*clstr2 == NULL) { return; + } if (*clstr1 == NULL || list_op == CLUSTER_REPLACE) { - if (list_op == CLUSTER_REPLACE) + if (list_op == CLUSTER_REPLACE) { xfree(*clstr1); - if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD) + } + if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD) { *clstr1 = *clstr2; - else + } else { xfree(*clstr2); + } return; } @@ -4812,8 +4906,9 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, * We always want to add from the first list. */ if (*g1 < *g2) { - if (round == 2) + if (round == 2) { clstr[count] = *g1; + } count++; g1++; continue; @@ -4823,12 +4918,14 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, * lists. */ if (list_op == CLUSTER_ADD) { - if (round == 2) + if (round == 2) { clstr[count] = *g2; + } count++; } - if (*g1 == *g2) + if (*g1 == *g2) { g1++; + } g2++; } @@ -4837,13 +4934,18 @@ static void syn_combine_list(int16_t **const clstr1, int16_t **const clstr2, * first. As before, we only want to add from the second list if * we're adding the lists. */ - for (; *g1; g1++, count++) - if (round == 2) + for (; *g1; g1++, count++) { + if (round == 2) { clstr[count] = *g1; - if (list_op == CLUSTER_ADD) - for (; *g2; g2++, count++) - if (round == 2) + } + } + if (list_op == CLUSTER_ADD) { + for (; *g2; g2++, count++) { + if (round == 2) { clstr[count] = *g2; + } + } + } if (round == 1) { /* @@ -4903,15 +5005,16 @@ static int syn_scl_namen2id(char_u *linep, int len) static int syn_check_cluster(char_u *pp, int len) { int id; - char_u *name; + char_u *name; name = vim_strnsave(pp, len); id = syn_scl_name2id(name); - if (id == 0) /* doesn't exist yet */ + if (id == 0) { // doesn't exist yet id = syn_add_cluster(name); - else + } else { xfree(name); + } return id; } @@ -4942,30 +5045,33 @@ static int syn_add_cluster(char_u *name) scp->scl_name_u = vim_strsave_up(name); scp->scl_list = NULL; - if (STRICMP(name, "Spell") == 0) + if (STRICMP(name, "Spell") == 0) { curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER; - if (STRICMP(name, "NoSpell") == 0) + } + if (STRICMP(name, "NoSpell") == 0) { curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER; + } return len + SYNID_CLUSTER; } /* * Handle ":syntax cluster {cluster-name} [contains={groupname},..] - * [add={groupname},..] [remove={groupname},..]". + * [add={groupname},..] [remove={groupname},..]". */ static void syn_cmd_cluster(exarg_T *eap, int syncing) { - char_u *arg = eap->arg; - char_u *group_name_end; - char_u *rest; + char_u *arg = eap->arg; + char_u *group_name_end; + char_u *rest; bool got_clstr = false; int opt_len; int list_op; eap->nextcmd = find_nextcmd(arg); - if (eap->skip) + if (eap->skip) { return; + } rest = get_group_name(arg, &group_name_end); @@ -4989,8 +5095,9 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing) && (ascii_iswhite(rest[8]) || rest[8] == '=')) { opt_len = 8; list_op = CLUSTER_REPLACE; - } else + } else { break; + } int16_t *clstr_list = NULL; if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL) { @@ -5008,14 +5115,16 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing) if (got_clstr) { redraw_curbuf_later(SOME_VALID); - syn_stack_free_all(curwin->w_s); /* Need to recompute all. */ + syn_stack_free_all(curwin->w_s); // Need to recompute all. } } - if (!got_clstr) + if (!got_clstr) { EMSG(_("E400: No cluster specified")); - if (rest == NULL || !ends_excmd(*rest)) + } + if (rest == NULL || !ends_excmd(*rest)) { EMSG2(_(e_invarg2), arg); + } } /* @@ -5034,10 +5143,10 @@ static void init_syn_patterns(void) */ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) { - char_u *end; - int *p; + char_u *end; + int *p; int idx; - char_u *cpo_save; + char_u *cpo_save; // need at least three chars if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL) { @@ -5045,21 +5154,22 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) } end = skip_regexp(arg + 1, *arg, TRUE, NULL); - if (*end != *arg) { /* end delimiter not found */ + if (*end != *arg) { // end delimiter not found EMSG2(_("E401: Pattern delimiter not found: %s"), arg); return NULL; } // store the pattern and compiled regexp program ci->sp_pattern = vim_strnsave(arg + 1, end - arg - 1); - /* Make 'cpoptions' empty, to avoid the 'l' flag */ + // Make 'cpoptions' empty, to avoid the 'l' flag cpo_save = p_cpo; p_cpo = (char_u *)""; ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC); p_cpo = cpo_save; - if (ci->sp_prog == NULL) + if (ci->sp_prog == NULL) { return NULL; + } ci->sp_ic = curwin->w_s->b_syn_ic; syn_clear_time(&ci->sp_time); @@ -5068,41 +5178,49 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) */ ++end; do { - for (idx = SPO_COUNT; --idx >= 0; ) - if (STRNCMP(end, spo_name_tab[idx], 3) == 0) + for (idx = SPO_COUNT; --idx >= 0; ) { + if (STRNCMP(end, spo_name_tab[idx], 3) == 0) { break; + } + } if (idx >= 0) { p = &(ci->sp_offsets[idx]); - if (idx != SPO_LC_OFF) + if (idx != SPO_LC_OFF) { switch (end[3]) { - case 's': break; - case 'b': break; - case 'e': idx += SPO_COUNT; break; - default: idx = -1; break; + case 's': + break; + case 'b': + break; + case 'e': + idx += SPO_COUNT; break; + default: + idx = -1; break; } + } if (idx >= 0) { ci->sp_off_flags |= (1 << idx); - if (idx == SPO_LC_OFF) { /* lc=99 */ + if (idx == SPO_LC_OFF) { // lc=99 end += 3; *p = getdigits_int(&end, true, 0); - /* "lc=" offset automatically sets "ms=" offset */ + // "lc=" offset automatically sets "ms=" offset if (!(ci->sp_off_flags & (1 << SPO_MS_OFF))) { ci->sp_off_flags |= (1 << SPO_MS_OFF); ci->sp_offsets[SPO_MS_OFF] = *p; } - } else { /* yy=x+99 */ + } else { // yy=x+99 end += 4; if (*end == '+') { end++; *p = getdigits_int(&end, true, 0); // positive offset - } else if (*end == '-') { + } else if (*end == '-') { end++; *p = -getdigits_int(&end, true, 0); // negative offset } } - if (*end != ',') + if (*end != ',') { break; + } ++end; } } @@ -5120,14 +5238,14 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci) */ static void syn_cmd_sync(exarg_T *eap, int syncing) { - char_u *arg_start = eap->arg; - char_u *arg_end; - char_u *key = NULL; - char_u *next_arg; + char_u *arg_start = eap->arg; + char_u *arg_end; + char_u *key = NULL; + char_u *next_arg; int illegal = FALSE; int finished = FALSE; long n; - char_u *cpo_save; + char_u *cpo_save; if (ends_excmd(*arg_start)) { syn_cmd_list(eap, TRUE); @@ -5140,45 +5258,50 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) xfree(key); key = vim_strnsave_up(arg_start, arg_end - arg_start); if (STRCMP(key, "CCOMMENT") == 0) { - if (!eap->skip) + if (!eap->skip) { curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT; + } if (!ends_excmd(*next_arg)) { arg_end = skiptowhite(next_arg); - if (!eap->skip) + if (!eap->skip) { curwin->w_s->b_syn_sync_id = syn_check_group(next_arg, - (int)(arg_end - next_arg)); + (int)(arg_end - next_arg)); + } next_arg = skipwhite(arg_end); - } else if (!eap->skip) + } else if (!eap->skip) { curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment"); - } else if ( STRNCMP(key, "LINES", 5) == 0 - || STRNCMP(key, "MINLINES", 8) == 0 - || STRNCMP(key, "MAXLINES", 8) == 0 - || STRNCMP(key, "LINEBREAKS", 10) == 0) { - if (key[4] == 'S') + } + } else if (STRNCMP(key, "LINES", 5) == 0 + || STRNCMP(key, "MINLINES", 8) == 0 + || STRNCMP(key, "MAXLINES", 8) == 0 + || STRNCMP(key, "LINEBREAKS", 10) == 0) { + if (key[4] == 'S') { arg_end = key + 6; - else if (key[0] == 'L') + } else if (key[0] == 'L') { arg_end = key + 11; - else + } else { arg_end = key + 9; + } if (arg_end[-1] != '=' || !ascii_isdigit(*arg_end)) { illegal = TRUE; break; } n = getdigits_long(&arg_end, false, 0); if (!eap->skip) { - if (key[4] == 'B') + if (key[4] == 'B') { curwin->w_s->b_syn_sync_linebreaks = n; - else if (key[1] == 'A') + } else if (key[1] == 'A') { curwin->w_s->b_syn_sync_maxlines = n; - else + } else { curwin->w_s->b_syn_sync_minlines = n; + } } - } else if (STRCMP(key, "FROMSTART") == 0) { + } else if (STRCMP(key, "FROMSTART") == 0) { if (!eap->skip) { curwin->w_s->b_syn_sync_minlines = MAXLNUM; curwin->w_s->b_syn_sync_maxlines = 0; } - } else if (STRCMP(key, "LINECONT") == 0) { + } else if (STRCMP(key, "LINECONT") == 0) { if (*next_arg == NUL) { // missing pattern illegal = true; break; @@ -5189,18 +5312,18 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) break; } arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL); - if (*arg_end != *next_arg) { /* end delimiter not found */ + if (*arg_end != *next_arg) { // end delimiter not found illegal = TRUE; break; } if (!eap->skip) { - /* store the pattern and compiled regexp program */ + // store the pattern and compiled regexp program curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1, arg_end - next_arg - 1); curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic; - /* Make 'cpoptions' empty, to avoid the 'l' flag */ + // Make 'cpoptions' empty, to avoid the 'l' flag cpo_save = p_cpo; p_cpo = (char_u *)""; curwin->w_s->b_syn_linecont_prog = @@ -5217,47 +5340,43 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) next_arg = skipwhite(arg_end + 1); } else { eap->arg = next_arg; - if (STRCMP(key, "MATCH") == 0) + if (STRCMP(key, "MATCH") == 0) { syn_cmd_match(eap, TRUE); - else if (STRCMP(key, "REGION") == 0) + } else if (STRCMP(key, "REGION") == 0) { syn_cmd_region(eap, TRUE); - else if (STRCMP(key, "CLEAR") == 0) + } else if (STRCMP(key, "CLEAR") == 0) { syn_cmd_clear(eap, TRUE); - else + } else { illegal = TRUE; + } finished = TRUE; break; } arg_start = next_arg; } xfree(key); - if (illegal) + if (illegal) { EMSG2(_("E404: Illegal arguments: %s"), arg_start); - else if (!finished) { + } else if (!finished) { eap->nextcmd = check_nextcmd(arg_start); redraw_curbuf_later(SOME_VALID); - syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */ + syn_stack_free_all(curwin->w_s); // Need to recompute all syntax. } } -/* - * Convert a line of highlight group names into a list of group ID numbers. - * "arg" should point to the "contains" or "nextgroup" keyword. - * "arg" is advanced to after the last group name. - * Careful: the argument is modified (NULs added). - * returns FAIL for some error, OK for success. - */ -static int -get_id_list( - char_u **const arg, - const int keylen, // length of keyword - int16_t **const list, // where to store the resulting list, if not - // NULL, the list is silently skipped! - const bool skip -) -{ - char_u *p = NULL; - char_u *end; +/// Convert a line of highlight group names into a list of group ID numbers. +/// "arg" should point to the "contains" or "nextgroup" keyword. +/// "arg" is advanced to after the last group name. +/// Careful: the argument is modified (NULs added). +/// +/// @param keylen length of keyword +/// @param list where to store the resulting list, if not NULL, the list is silently skipped! +/// +/// @return FAIL for some error, OK for success. +static int get_id_list(char_u **const arg, const int keylen, int16_t **const list, const bool skip) +{ + char_u *p = NULL; + char_u *end; int total_count = 0; int16_t *retval = NULL; regmatch_T regmatch; @@ -5289,10 +5408,10 @@ get_id_list( } char_u *const name = xmalloc((int)(end - p + 3)); // leave room for "^$" STRLCPY(name + 1, p, end - p + 1); - if ( STRCMP(name + 1, "ALLBUT") == 0 - || STRCMP(name + 1, "ALL") == 0 - || STRCMP(name + 1, "TOP") == 0 - || STRCMP(name + 1, "CONTAINED") == 0) { + if (STRCMP(name + 1, "ALLBUT") == 0 + || STRCMP(name + 1, "ALL") == 0 + || STRCMP(name + 1, "TOP") == 0 + || STRCMP(name + 1, "CONTAINED") == 0) { if (TOUPPER_ASC(**arg) != 'C') { EMSG2(_("E407: %s not allowed here"), name + 1); failed = true; @@ -5317,7 +5436,7 @@ get_id_list( } else { id = SYNID_CONTAINED + current_syn_inc_tag; } - } else if (name[1] == '@') { + } else if (name[1] == '@') { if (skip) { id = -1; } else { @@ -5382,12 +5501,14 @@ get_id_list( ++count; } p = skipwhite(end); - if (*p != ',') + if (*p != ',') { break; - p = skipwhite(p + 1); /* skip comma in between arguments */ + } + p = skipwhite(p + 1); // skip comma in between arguments } while (!ends_excmd(*p)); - if (failed) + if (failed) { break; + } if (round == 1) { retval = xmalloc((count + 1) * sizeof(*retval)); retval[count] = 0; // zero means end of the list @@ -5401,11 +5522,11 @@ get_id_list( return FAIL; } - if (*list == NULL) + if (*list == NULL) { *list = retval; - else - xfree(retval); /* list already found, don't overwrite it */ - + } else { + xfree(retval); // list already found, don't overwrite it + } return OK; } @@ -5428,20 +5549,17 @@ static int16_t *copy_id_list(const int16_t *const list) return retval; } -/* - * Check if syntax group "ssp" is in the ID list "list" of "cur_si". - * "cur_si" can be NULL if not checking the "containedin" list. - * Used to check if a syntax item is in the "contains" or "nextgroup" list of - * the current item. - * This function is called very often, keep it fast!! - */ -static int -in_id_list( - stateitem_T *cur_si, // current item or NULL - int16_t *list, // id list - struct sp_syn *ssp, // group id and ":syn include" tag of group - int contained // group id is contained -) +/// Check if syntax group "ssp" is in the ID list "list" of "cur_si". +/// "cur_si" can be NULL if not checking the "containedin" list. +/// Used to check if a syntax item is in the "contains" or "nextgroup" list of +/// the current item. +/// This function is called very often, keep it fast!! +/// +/// @param cur_si current item or NULL +/// @param list id list +/// @param ssp group id and ":syn include" tag of group +/// @param contained group id is contained +static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, int contained) { int retval; int16_t *scl_list; @@ -5450,30 +5568,35 @@ in_id_list( static int depth = 0; int r; - /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */ + // If ssp has a "containedin" list and "cur_si" is in it, return TRUE. if (cur_si != NULL && ssp->cont_in_list != NULL && !(cur_si->si_flags & HL_MATCH)) { /* Ignore transparent items without a contains argument. Double check * that we don't go back past the first one. */ while ((cur_si->si_flags & HL_TRANS_CONT) - && cur_si > (stateitem_T *)(current_state.ga_data)) + && cur_si > (stateitem_T *)(current_state.ga_data)) { --cur_si; - /* cur_si->si_idx is -1 for keywords, these never contain anything. */ + } + // cur_si->si_idx is -1 for keywords, these never contain anything. if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list, - &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn), - SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED)) + &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn), + SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & + HL_CONTAINED)) { return TRUE; + } } - if (list == NULL) + if (list == NULL) { return FALSE; + } /* * If list is ID_LIST_ALL, we are in a transparent item that isn't * inside anything. Only allow not-contained groups. */ - if (list == ID_LIST_ALL) + if (list == ID_LIST_ALL) { return !contained; + } /* * If the first item is "ALLBUT", return TRUE if "id" is NOT in the @@ -5483,29 +5606,34 @@ in_id_list( item = *list; if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER) { if (item < SYNID_TOP) { - /* ALL or ALLBUT: accept all groups in the same file */ - if (item - SYNID_ALLBUT != ssp->inc_tag) + // ALL or ALLBUT: accept all groups in the same file + if (item - SYNID_ALLBUT != ssp->inc_tag) { return FALSE; - } else if (item < SYNID_CONTAINED) { - /* TOP: accept all not-contained groups in the same file */ - if (item - SYNID_TOP != ssp->inc_tag || contained) + } + } else if (item < SYNID_CONTAINED) { + // TOP: accept all not-contained groups in the same file + if (item - SYNID_TOP != ssp->inc_tag || contained) { return FALSE; + } } else { - /* CONTAINED: accept all contained groups in the same file */ - if (item - SYNID_CONTAINED != ssp->inc_tag || !contained) + // CONTAINED: accept all contained groups in the same file + if (item - SYNID_CONTAINED != ssp->inc_tag || !contained) { return FALSE; + } } item = *++list; retval = FALSE; - } else + } else { retval = TRUE; + } /* * Return "retval" if id is in the contains list. */ while (item != 0) { - if (item == id) + if (item == id) { return retval; + } if (item >= SYNID_CLUSTER) { scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list; /* restrict recursiveness to 30 to avoid an endless loop for a @@ -5514,8 +5642,9 @@ in_id_list( ++depth; r = in_id_list(NULL, scl_list, ssp, contained); --depth; - if (r) + if (r) { return retval; + } } } item = *++list; @@ -5524,8 +5653,8 @@ in_id_list( } struct subcommand { - char *name; /* subcommand name */ - void (*func)(exarg_T *, int); /* function to call */ + char *name; // subcommand name + void (*func)(exarg_T *, int); // function to call }; static struct subcommand subcommands[] = @@ -5534,7 +5663,7 @@ static struct subcommand subcommands[] = { "clear", syn_cmd_clear }, { "cluster", syn_cmd_cluster }, { "conceal", syn_cmd_conceal }, - { "enable", syn_cmd_enable }, + { "enable", syn_cmd_on }, { "foldlevel", syn_cmd_foldlevel }, { "include", syn_cmd_include }, { "iskeyword", syn_cmd_iskeyword }, @@ -5559,8 +5688,8 @@ static struct subcommand subcommands[] = */ void ex_syntax(exarg_T *eap) { - char_u *arg = eap->arg; - char_u *subcmd_end; + char_u *arg = eap->arg; + char_u *subcmd_end; syn_cmdlinep = eap->cmdlinep; @@ -5583,14 +5712,15 @@ void ex_syntax(exarg_T *eap) } } xfree(subcmd_name); - if (eap->skip) + if (eap->skip) { --emsg_skip; + } } void ex_ownsyntax(exarg_T *eap) { - char_u *old_value; - char_u *new_value; + char_u *old_value; + char_u *new_value; if (curwin->w_s == &curwin->w_buffer->b_s) { curwin->w_s = xmalloc(sizeof(synblock_T)); @@ -5680,7 +5810,7 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg) include_link = 0; include_default = 0; - /* (part of) subcommand already typed */ + // (part of) subcommand already typed if (*arg != NUL) { const char *p = (const char *)skiptowhite((const char_u *)arg); if (*p != NUL) { // Past first word. @@ -5712,47 +5842,44 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg) char_u *get_syntax_name(expand_T *xp, int idx) { switch (expand_what) { - case EXP_SUBCMD: - return (char_u *)subcommands[idx].name; - case EXP_CASE: { - static char *case_args[] = { "match", "ignore", NULL }; - return (char_u *)case_args[idx]; + case EXP_SUBCMD: + return (char_u *)subcommands[idx].name; + case EXP_CASE: { + static char *case_args[] = { "match", "ignore", NULL }; + return (char_u *)case_args[idx]; } - case EXP_SPELL: { - static char *spell_args[] = - { "toplevel", "notoplevel", "default", NULL }; - return (char_u *)spell_args[idx]; + case EXP_SPELL: { + static char *spell_args[] = + { "toplevel", "notoplevel", "default", NULL }; + return (char_u *)spell_args[idx]; } - case EXP_SYNC: { - static char *sync_args[] = - { "ccomment", "clear", "fromstart", - "linebreaks=", "linecont", "lines=", "match", - "maxlines=", "minlines=", "region", NULL }; - return (char_u *)sync_args[idx]; + case EXP_SYNC: { + static char *sync_args[] = + { "ccomment", "clear", "fromstart", + "linebreaks=", "linecont", "lines=", "match", + "maxlines=", "minlines=", "region", NULL }; + return (char_u *)sync_args[idx]; } } return NULL; } -// Function called for expression evaluation: get syntax ID at file position. -int syn_get_id( - win_T *wp, - long lnum, - colnr_T col, - int trans, // remove transparency - bool *spellp, // return: can do spell checking - int keep_state // keep state of char at "col" -) +/// Function called for expression evaluation: get syntax ID at file position. +/// +/// @param trans remove transparency +/// @param spellp return: can do spell checking +/// @param keep_state keep state of char at "col" +int syn_get_id(win_T *wp, long lnum, colnr_T col, int trans, bool *spellp, int keep_state) { // When the position is not after the current position and in the same // line of the same buffer, need to restart parsing. if (wp->w_buffer != syn_buf || lnum != current_lnum || col < current_col) { syntax_start(wp, lnum); } else if (col > current_col) { - // next_match may not be correct when moving around, e.g. with the - // "skip" expression in searchpair() - next_match_idx = -1; + // next_match may not be correct when moving around, e.g. with the + // "skip" expression in searchpair() + next_match_idx = -1; } (void)get_syntax_attr(col, spellp, keep_state); @@ -5860,8 +5987,9 @@ int syn_get_foldlevel(win_T *wp, long lnum) } if (level > wp->w_p_fdn) { level = wp->w_p_fdn; - if (level < 0) + if (level < 0) { level = 0; + } } return level; } @@ -5871,16 +5999,17 @@ int syn_get_foldlevel(win_T *wp, long lnum) */ void ex_syntime(exarg_T *eap) { - if (STRCMP(eap->arg, "on") == 0) - syn_time_on = TRUE; - else if (STRCMP(eap->arg, "off") == 0) - syn_time_on = FALSE; - else if (STRCMP(eap->arg, "clear") == 0) + if (STRCMP(eap->arg, "on") == 0) { + syn_time_on = true; + } else if (STRCMP(eap->arg, "off") == 0) { + syn_time_on = false; + } else if (STRCMP(eap->arg, "clear") == 0) { syntime_clear(); - else if (STRCMP(eap->arg, "report") == 0) + } else if (STRCMP(eap->arg, "report") == 0) { syntime_report(); - else + } else { EMSG2(_(e_invarg2), eap->arg); + } } static void syn_clear_time(syn_time_T *st) @@ -5896,7 +6025,7 @@ static void syn_clear_time(syn_time_T *st) */ static void syntime_clear(void) { - synpat_T *spp; + synpat_T *spp; if (!syntax_present(curwin)) { MSG(_(msg_no_items)); @@ -5915,18 +6044,22 @@ static void syntime_clear(void) char_u *get_syntime_arg(expand_T *xp, int idx) { switch (idx) { - case 0: return (char_u *)"on"; - case 1: return (char_u *)"off"; - case 2: return (char_u *)"clear"; - case 3: return (char_u *)"report"; + case 0: + return (char_u *)"on"; + case 1: + return (char_u *)"off"; + case 2: + return (char_u *)"clear"; + case 3: + return (char_u *)"report"; } return NULL; } static int syn_compare_syntime(const void *v1, const void *v2) { - const time_entry_T *s1 = v1; - const time_entry_T *s2 = v2; + const time_entry_T *s1 = v1; + const time_entry_T *s2 = v2; return profile_cmp(s1->total, s2->total); } @@ -5971,14 +6104,13 @@ static void syntime_report(void) syn_compare_syntime); } - MSG_PUTS_TITLE(_( - " TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN")); + MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN")); MSG_PUTS("\n"); for (int idx = 0; idx < ga.ga_len && !got_int; ++idx) { p = ((time_entry_T *)ga.ga_data) + idx; MSG_PUTS(profile_msg(p->total)); - MSG_PUTS(" "); /* make sure there is always a separating space */ + MSG_PUTS(" "); // make sure there is always a separating space msg_advance(13); msg_outnum(p->count); MSG_PUTS(" "); @@ -5997,12 +6129,14 @@ static void syntime_report(void) msg_advance(69); int len; - if (Columns < 80) - len = 20; /* will wrap anyway */ - else + if (Columns < 80) { + len = 20; // will wrap anyway + } else { len = Columns - 70; - if (len > (int)STRLEN(p->pattern)) + } + if (len > (int)STRLEN(p->pattern)) { len = (int)STRLEN(p->pattern); + } msg_outtrans_len(p->pattern, len); MSG_PUTS("\n"); } @@ -6017,7 +6151,7 @@ static void syntime_report(void) } /************************************** -* Highlighting stuff * +* Highlighting stuff * **************************************/ // The default highlight groups. These are compiled-in for fast startup and @@ -6026,8 +6160,7 @@ static void syntime_report(void) // When making changes here, also change runtime/colors/default.vim! static const char *highlight_init_both[] = { - "Conceal " - "ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey", + "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey", "Cursor guibg=fg guifg=bg", "lCursor guibg=fg guifg=bg", "DiffText cterm=bold ctermbg=Red gui=bold guibg=Red", @@ -6045,6 +6178,8 @@ static const char *highlight_init_both[] = { "VertSplit cterm=reverse gui=reverse", "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", "default link EndOfBuffer NonText", + "default link LineNrAbove LineNr", + "default link LineNrBelow LineNr", "default link QuickFixLine Search", "default link Substitute Search", "default link Whitespace NonText", @@ -6057,6 +6192,32 @@ static const char *highlight_init_both[] = { "RedrawDebugClear ctermbg=Yellow guibg=Yellow", "RedrawDebugComposed ctermbg=Green guibg=Green", "RedrawDebugRecompose ctermbg=Red guibg=Red", + "Error term=reverse cterm=NONE ctermfg=White ctermbg=Red gui=NONE guifg=White guibg=Red", + "Todo term=standout cterm=NONE ctermfg=Black ctermbg=Yellow gui=NONE guifg=Blue guibg=Yellow", + "default link String Constant", + "default link Character Constant", + "default link Number Constant", + "default link Boolean Constant", + "default link Float Number", + "default link Function Identifier", + "default link Conditional Statement", + "default link Repeat Statement", + "default link Label Statement", + "default link Operator Statement", + "default link Keyword Statement", + "default link Exception Statement", + "default link Include PreProc", + "default link Define PreProc", + "default link Macro PreProc", + "default link PreCondit PreProc", + "default link StorageClass Type", + "default link Structure Type", + "default link Typedef Type", + "default link Tag Special", + "default link SpecialChar Special", + "default link Delimiter Special", + "default link SpecialComment Special", + "default link Debug Special", NULL }; @@ -6065,7 +6226,7 @@ static const char *highlight_init_light[] = { "ColorColumn ctermbg=LightRed guibg=LightRed", "CursorColumn ctermbg=LightGrey guibg=Grey90", "CursorLine cterm=underline guibg=Grey90", - "CursorLineNr ctermfg=Brown gui=bold guifg=Brown", + "CursorLineNr cterm=underline ctermfg=Brown gui=bold guifg=Brown", "DiffAdd ctermbg=LightBlue guibg=LightBlue", "DiffChange ctermbg=LightMagenta guibg=LightMagenta", "DiffDelete ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan", @@ -6090,6 +6251,15 @@ static const char *highlight_init_light[] = { "Title ctermfg=DarkMagenta gui=bold guifg=Magenta", "Visual guibg=LightGrey", "WarningMsg ctermfg=DarkRed guifg=Red", + "Comment term=bold cterm=NONE ctermfg=DarkBlue ctermbg=NONE gui=NONE guifg=Blue guibg=NONE", + "Constant term=underline cterm=NONE ctermfg=DarkRed ctermbg=NONE gui=NONE guifg=Magenta guibg=NONE", + "Special term=bold cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a5acd guibg=NONE", + "Identifier term=underline cterm=NONE ctermfg=DarkCyan ctermbg=NONE gui=NONE guifg=DarkCyan guibg=NONE", + "Statement term=bold cterm=NONE ctermfg=Brown ctermbg=NONE gui=bold guifg=Brown guibg=NONE", + "PreProc term=underline cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a0dad guibg=NONE", + "Type term=underline cterm=NONE ctermfg=DarkGreen ctermbg=NONE gui=bold guifg=SeaGreen guibg=NONE", + "Underlined term=underline cterm=underline ctermfg=DarkMagenta gui=underline guifg=SlateBlue", + "Ignore term=NONE cterm=NONE ctermfg=white ctermbg=NONE gui=NONE guifg=bg guibg=NONE", NULL }; @@ -6098,7 +6268,7 @@ static const char *highlight_init_dark[] = { "ColorColumn ctermbg=DarkRed guibg=DarkRed", "CursorColumn ctermbg=DarkGrey guibg=Grey40", "CursorLine cterm=underline guibg=Grey40", - "CursorLineNr ctermfg=Yellow gui=bold guifg=Yellow", + "CursorLineNr cterm=underline ctermfg=Yellow gui=bold guifg=Yellow", "DiffAdd ctermbg=DarkBlue guibg=DarkBlue", "DiffChange ctermbg=DarkMagenta guibg=DarkMagenta", "DiffDelete ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan", @@ -6123,11 +6293,20 @@ static const char *highlight_init_dark[] = { "Title ctermfg=LightMagenta gui=bold guifg=Magenta", "Visual guibg=DarkGrey", "WarningMsg ctermfg=LightRed guifg=Red", + "Comment term=bold cterm=NONE ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#80a0ff guibg=NONE", + "Constant term=underline cterm=NONE ctermfg=Magenta ctermbg=NONE gui=NONE guifg=#ffa0a0 guibg=NONE", + "Special term=bold cterm=NONE ctermfg=LightRed ctermbg=NONE gui=NONE guifg=Orange guibg=NONE", + "Identifier term=underline cterm=bold ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#40ffff guibg=NONE", + "Statement term=bold cterm=NONE ctermfg=Yellow ctermbg=NONE gui=bold guifg=#ffff60 guibg=NONE", + "PreProc term=underline cterm=NONE ctermfg=LightBlue ctermbg=NONE gui=NONE guifg=#ff80ff guibg=NONE", + "Type term=underline cterm=NONE ctermfg=LightGreen ctermbg=NONE gui=bold guifg=#60ff60 guibg=NONE", + "Underlined term=underline cterm=underline ctermfg=LightBlue gui=underline guifg=#80a0ff", + "Ignore term=NONE cterm=NONE ctermfg=black ctermbg=NONE gui=NONE guifg=bg guibg=NONE", NULL }; const char *const highlight_init_cmdline[] = { - // XXX When modifying a list modify it in both valid and invalid halfs. + // XXX When modifying a list modify it in both valid and invalid halves. // TODO(ZyX-I): merge valid and invalid groups via a macros. // NvimInternalError should appear only when highlighter has a bug. @@ -6229,12 +6408,9 @@ const char *const highlight_init_cmdline[] = { "default link NvimInvalidAssignment NvimInvalid", "default link NvimInvalidPlainAssignment NvimInvalidAssignment", "default link NvimInvalidAugmentedAssignment NvimInvalidAssignment", - "default link NvimInvalidAssignmentWithAddition " - "NvimInvalidAugmentedAssignment", - "default link NvimInvalidAssignmentWithSubtraction " - "NvimInvalidAugmentedAssignment", - "default link NvimInvalidAssignmentWithConcatenation " - "NvimInvalidAugmentedAssignment", + "default link NvimInvalidAssignmentWithAddition NvimInvalidAugmentedAssignment", + "default link NvimInvalidAssignmentWithSubtraction NvimInvalidAugmentedAssignment", + "default link NvimInvalidAssignmentWithConcatenation NvimInvalidAugmentedAssignment", "default link NvimInvalidOperator NvimInvalid", @@ -6296,7 +6472,7 @@ const char *const highlight_init_cmdline[] = { "default link NvimInvalidOptionName NvimInvalidIdentifier", "default link NvimInvalidOptionScope NvimInvalidIdentifierScope", "default link NvimInvalidOptionScopeDelimiter " - "NvimInvalidIdentifierScopeDelimiter", + "NvimInvalidIdentifierScopeDelimiter", "default link NvimInvalidEnvironmentSigil NvimInvalidOptionSigil", "default link NvimInvalidEnvironmentName NvimInvalidIdentifier", @@ -6354,7 +6530,7 @@ void init_highlight(bool both, bool reset) bool okay = load_colors(copy_p); xfree(copy_p); if (okay) { - return; + return; } } @@ -6368,7 +6544,7 @@ void init_highlight(bool both, bool reset) do_highlight(pp[i], reset, true); } } else if (!had_both) { - // Don't do anything before the call with both == TRUE from main(). + // Don't do anything before the call with both == true from main(). // Not everything has been setup then, and that call will overrule // everything anyway. return; @@ -6387,8 +6563,7 @@ void init_highlight(bool both, bool reset) * to avoid Statement highlighted text disappears. * Clear the attributes, needed when changing the t_Co value. */ if (t_colors > 8) { - do_highlight( - (*p_bg == 'l' + do_highlight((*p_bg == 'l' ? "Visual cterm=NONE ctermbg=LightGrey" : "Visual cterm=NONE ctermbg=DarkGrey"), false, true); } else { @@ -6398,20 +6573,6 @@ void init_highlight(bool both, bool reset) } } - /* - * If syntax highlighting is enabled load the highlighting for it. - */ - if (get_var_value("g:syntax_on") != NULL) { - static int recursive = 0; - - if (recursive >= 5) { - EMSG(_("E679: recursive loop loading syncolor.vim")); - } else { - recursive++; - (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL); - recursive--; - } - } syn_init_cmdline_highlight(false, false); } @@ -6421,9 +6582,9 @@ void init_highlight(bool both, bool reset) */ int load_colors(char_u *name) { - char_u *buf; + char_u *buf; int retval = FAIL; - static int recursive = false; + static bool recursive = false; // When being called recursively, this is probably because setting // 'background' caused the highlighting to be reloaded. This means it is @@ -6438,6 +6599,10 @@ int load_colors(char_u *name) apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf); snprintf((char *)buf, buflen, "colors/%s.vim", name); retval = source_runtime(buf, DIP_START + DIP_OPT); + if (retval == FAIL) { + snprintf((char *)buf, buflen, "colors/%s.lua", name); + retval = source_runtime(buf, DIP_START + DIP_OPT); + } xfree(buf); apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf); @@ -6453,46 +6618,47 @@ static char *(color_names[28]) = { "DarkGray", "DarkGrey", "Blue", "LightBlue", "Green", "LightGreen", "Cyan", "LightCyan", "Red", "LightRed", "Magenta", - "LightMagenta", "Yellow", "LightYellow", "White", "NONE" }; - // indices: - // 0, 1, 2, 3, - // 4, 5, 6, 7, - // 8, 9, 10, 11, - // 12, 13, - // 14, 15, 16, 17, - // 18, 19, 20, 21, 22, - // 23, 24, 25, 26, 27 + "LightMagenta", "Yellow", "LightYellow", "White", "NONE" +}; +// indices: +// 0, 1, 2, 3, +// 4, 5, 6, 7, +// 8, 9, 10, 11, +// 12, 13, +// 14, 15, 16, 17, +// 18, 19, 20, 21, 22, +// 23, 24, 25, 26, 27 static int color_numbers_16[28] = { 0, 1, 2, 3, - 4, 5, 6, 6, - 7, 7, 7, 7, - 8, 8, - 9, 9, 10, 10, - 11, 11, 12, 12, 13, - 13, 14, 14, 15, -1 }; + 4, 5, 6, 6, + 7, 7, 7, 7, + 8, 8, + 9, 9, 10, 10, + 11, 11, 12, 12, 13, + 13, 14, 14, 15, -1 }; // for xterm with 88 colors... static int color_numbers_88[28] = { 0, 4, 2, 6, - 1, 5, 32, 72, - 84, 84, 7, 7, - 82, 82, - 12, 43, 10, 61, - 14, 63, 9, 74, 13, - 75, 11, 78, 15, -1 }; + 1, 5, 32, 72, + 84, 84, 7, 7, + 82, 82, + 12, 43, 10, 61, + 14, 63, 9, 74, 13, + 75, 11, 78, 15, -1 }; // for xterm with 256 colors... static int color_numbers_256[28] = { 0, 4, 2, 6, - 1, 5, 130, 3, - 248, 248, 7, 7, - 242, 242, - 12, 81, 10, 121, - 14, 159, 9, 224, 13, - 225, 11, 229, 15, -1 }; + 1, 5, 130, 3, + 248, 248, 7, 7, + 242, 242, + 12, 81, 10, 121, + 14, 159, 9, 224, 13, + 225, 11, 229, 15, -1 }; // for terminals with less than 16 colors... static int color_numbers_8[28] = { 0, 4, 2, 6, - 1, 5, 3, 3, - 7, 7, 7, 7, - 0+8, 0+8, - 4+8, 4+8, 2+8, 2+8, - 6+8, 6+8, 1+8, 1+8, 5+8, - 5+8, 3+8, 3+8, 7+8, -1 }; + 1, 5, 3, 3, + 7, 7, 7, 7, + 0+8, 0+8, + 4+8, 4+8, 2+8, 2+8, + 6+8, 6+8, 1+8, 1+8, 5+8, + 5+8, 3+8, 3+8, 7+8, -1 }; // Lookup the "cterm" value to be used for color with index "idx" in // color_names[]. @@ -6593,7 +6759,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) // ":highlight {group-name}": list highlighting for one group. if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) { - id = syn_namen2id((const char_u *)line, (int)(name_end - line)); + id = syn_name2id_len((const char_u *)line, (int)(name_end - line)); if (id == 0) { emsgf(_("E411: highlight group not found: %s"), line); } else { @@ -6810,7 +6976,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (error) { break; } - if (*key == 'C') { + if (*key == 'C') { if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { if (!init) { HL_TABLE()[idx].sg_set |= SG_CTERM; @@ -6826,7 +6992,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) HL_TABLE()[idx].sg_gui = attr; } } - } else if (STRCMP(key, "FONT") == 0) { + } else if (STRCMP(key, "FONT") == 0) { // in non-GUI fonts are simply ignored } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { @@ -6851,10 +7017,10 @@ void do_highlight(const char *line, const bool forceit, const bool init) error = true; break; } - } else if (STRICMP(arg, "bg") == 0) { - if (cterm_normal_bg_color > 0) + } else if (STRICMP(arg, "bg") == 0) { + if (cterm_normal_bg_color > 0) { color = cterm_normal_bg_color - 1; - else { + } else { EMSG(_("E420: BG color unknown")); error = true; break; @@ -6921,7 +7087,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) } } } - } else if (strcmp(key, "GUIFG") == 0) { + } else if (strcmp(key, "GUIFG") == 0) { char **namep = &HL_TABLE()[idx].sg_rgb_fg_name; if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { @@ -6945,7 +7111,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (is_normal_group) { normal_fg = HL_TABLE()[idx].sg_rgb_fg; } - } else if (STRCMP(key, "GUIBG") == 0) { + } else if (STRCMP(key, "GUIBG") == 0) { char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { @@ -6969,7 +7135,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (is_normal_group) { normal_bg = HL_TABLE()[idx].sg_rgb_bg; } - } else if (strcmp(key, "GUISP") == 0) { + } else if (strcmp(key, "GUISP") == 0) { char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { @@ -6993,9 +7159,9 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (is_normal_group) { normal_sp = HL_TABLE()[idx].sg_rgb_sp; } - } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) { + } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) { // Ignored for now - } else if (strcmp(key, "BLEND") == 0) { + } else if (strcmp(key, "BLEND") == 0) { if (strcmp(arg, "NONE") != 0) { HL_TABLE()[idx].sg_blend = strtol(arg, NULL, 10); } else { @@ -7071,6 +7237,7 @@ void free_highlight(void) xfree(HL_TABLE()[i].sg_name_u); } ga_clear(&highlight_ga); + map_destroy(cstr_t, int)(&highlight_unames); } #endif @@ -7088,20 +7255,19 @@ void restore_cterm_colors(void) cterm_normal_bg_color = 0; } -/* - * Return TRUE if highlight group "idx" has any settings. - * When "check_link" is TRUE also check for an existing link. - */ -static int hl_has_settings(int idx, int check_link) +/// @param check_link if true also check for an existing link. +/// +/// @return TRUE if highlight group "idx" has any settings. +static int hl_has_settings(int idx, bool check_link) { return HL_TABLE()[idx].sg_cleared == 0 - && (HL_TABLE()[idx].sg_attr != 0 - || HL_TABLE()[idx].sg_cterm_fg != 0 - || HL_TABLE()[idx].sg_cterm_bg != 0 - || HL_TABLE()[idx].sg_rgb_fg_name != NULL - || HL_TABLE()[idx].sg_rgb_bg_name != NULL - || HL_TABLE()[idx].sg_rgb_sp_name != NULL - || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); + && (HL_TABLE()[idx].sg_attr != 0 + || HL_TABLE()[idx].sg_cterm_fg != 0 + || HL_TABLE()[idx].sg_cterm_bg != 0 + || HL_TABLE()[idx].sg_rgb_fg_name != NULL + || HL_TABLE()[idx].sg_rgb_bg_name != NULL + || HL_TABLE()[idx].sg_rgb_sp_name != NULL + || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); } /* @@ -7149,18 +7315,18 @@ static void highlight_list_one(const int id) } didh = highlight_list_arg(id, didh, LIST_ATTR, - sgp->sg_cterm, NULL, "cterm"); + sgp->sg_cterm, NULL, "cterm"); didh = highlight_list_arg(id, didh, LIST_INT, - sgp->sg_cterm_fg, NULL, "ctermfg"); + sgp->sg_cterm_fg, NULL, "ctermfg"); didh = highlight_list_arg(id, didh, LIST_INT, - sgp->sg_cterm_bg, NULL, "ctermbg"); + sgp->sg_cterm_bg, NULL, "ctermbg"); didh = highlight_list_arg(id, didh, LIST_ATTR, - sgp->sg_gui, NULL, "gui"); + sgp->sg_gui, NULL, "gui"); didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_fg_name, "guifg"); + 0, sgp->sg_rgb_fg_name, "guifg"); didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_bg_name, "guibg"); + 0, sgp->sg_rgb_bg_name, "guibg"); didh = highlight_list_arg(id, didh, LIST_STRING, 0, sgp->sg_rgb_sp_name, "guisp"); @@ -7206,9 +7372,8 @@ Dictionary get_global_hl_defs(void) /// @param type one of \ref LIST_XXX /// @param iarg integer argument used if \p type == LIST_INT /// @param sarg string used if \p type == LIST_STRING -static bool highlight_list_arg( - const int id, bool didh, const int type, int iarg, - char *const sarg, const char *const name) +static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, char *const sarg, + const char *const name) { char buf[100]; @@ -7225,10 +7390,11 @@ static bool highlight_list_arg( buf[0] = NUL; for (int i = 0; hl_attr_table[i] != 0; i++) { if (iarg & hl_attr_table[i]) { - if (buf[0] != NUL) + if (buf[0] != NUL) { xstrlcat((char *)buf, ",", 100); + } xstrlcat((char *)buf, hl_name_table[i], 100); - iarg &= ~hl_attr_table[i]; /* don't want "inverse" */ + iarg &= ~hl_attr_table[i]; // don't want "inverse" } } } @@ -7281,8 +7447,7 @@ const char *highlight_has_attr(const int id, const int flag, const int modec) /// /// @return color name, possibly in a static buffer. Buffer will be overwritten /// on next highlight_color() call. May return NULL. -const char *highlight_color(const int id, const char *const what, - const int modec) +const char *highlight_color(const int id, const char *const what, const int modec) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { static char name[20]; @@ -7308,11 +7473,11 @@ const char *highlight_color(const int id, const char *const what, if (modec == 'g') { if (what[2] == '#' && ui_rgb_attached()) { if (fg) { - n = HL_TABLE()[id - 1].sg_rgb_fg; + n = HL_TABLE()[id - 1].sg_rgb_fg; } else if (sp) { - n = HL_TABLE()[id - 1].sg_rgb_sp; + n = HL_TABLE()[id - 1].sg_rgb_sp; } else { - n = HL_TABLE()[id - 1].sg_rgb_bg; + n = HL_TABLE()[id - 1].sg_rgb_bg; } if (n < 0 || n > 0xffffff) { return NULL; @@ -7354,8 +7519,8 @@ const char *highlight_color(const int id, const char *const what, /// @param id highlight group id /// @param force_newline always start a new line /// @return true when started a new line. -static bool syn_list_header(const bool did_header, const int outlen, - const int id, bool force_newline) +static bool syn_list_header(const bool did_header, const int outlen, const int id, + bool force_newline) { int endcol = 19; bool newline = true; @@ -7371,7 +7536,7 @@ static bool syn_list_header(const bool did_header, const int outlen, } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) { msg_putchar(' '); adjust = false; - } else if (msg_col + outlen + 1 >= Columns || force_newline) { + } else if (msg_col + outlen + 1 >= Columns || force_newline) { msg_putchar('\n'); if (got_int) { return true; @@ -7391,7 +7556,7 @@ static bool syn_list_header(const bool did_header, const int outlen, msg_advance(endcol); } - /* Show "xxx" with the attributes. */ + // Show "xxx" with the attributes. if (!did_header) { msg_puts_attr("xxx", syn_id2attr(id)); msg_putchar(' '); @@ -7406,7 +7571,7 @@ static bool syn_list_header(const bool did_header, const int outlen, static void set_hl_attr(int idx) { HlAttrs at_en = HLATTRS_INIT; - struct hl_group *sgp = HL_TABLE() + idx; + struct hl_group *sgp = HL_TABLE() + idx; at_en.cterm_ae_attr = sgp->sg_cterm; at_en.cterm_fg_color = sgp->sg_cterm_fg; @@ -7428,26 +7593,35 @@ static void set_hl_attr(int idx) } } +int syn_name2id(const char_u *name) + FUNC_ATTR_NONNULL_ALL +{ + return syn_name2id_len(name, STRLEN(name)); +} + /// Lookup a highlight group name and return its ID. /// /// @param highlight name e.g. 'Cursor', 'Normal' /// @return the highlight id, else 0 if \p name does not exist -int syn_name2id(const char_u *name) +int syn_name2id_len(const char_u *name, size_t len) FUNC_ATTR_NONNULL_ALL { - int i; - char_u name_u[200]; - - /* Avoid using stricmp() too much, it's slow on some systems */ - /* Avoid alloc()/free(), these are slow too. ID names over 200 chars - * don't deserve to be found! */ - STRLCPY(name_u, name, 200); - vim_strup(name_u); - for (i = highlight_ga.ga_len; --i >= 0; ) - if (HL_TABLE()[i].sg_name_u != NULL - && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0) - break; - return i + 1; + char name_u[201]; + + if (len == 0 || len > 200) { + // ID names over 200 chars don't deserve to be found! + return 0; + } + + // Avoid using stricmp() too much, it's slow on some systems */ + // Avoid alloc()/free(), these are slow too. + memcpy(name_u, name, len); + name_u[len] = '\0'; + vim_strup((char_u *)name_u); + + // map_get(..., int) returns 0 when no key is present, which is + // the expected value for missing highlight group. + return map_get(cstr_t, int)(&highlight_unames, name_u); } /// Lookup a highlight group name and return its attributes. @@ -7477,22 +7651,12 @@ int highlight_exists(const char_u *name) */ char_u *syn_id2name(int id) { - if (id <= 0 || id > highlight_ga.ga_len) + if (id <= 0 || id > highlight_ga.ga_len) { return (char_u *)""; + } return HL_TABLE()[id - 1].sg_name; } -/* - * Like syn_name2id(), but take a pointer + length argument. - */ -int syn_namen2id(const char_u *linep, int len) -{ - char_u *name = vim_strnsave(linep, len); - int id = syn_name2id(name); - xfree(name); - - return id; -} /// Find highlight group name in the table and return its ID. /// If it doesn't exist yet, a new entry is created. @@ -7501,14 +7665,11 @@ int syn_namen2id(const char_u *linep, int len) /// @param len length of \p pp /// /// @return 0 for failure else the id of the group -int syn_check_group(const char_u *pp, int len) +int syn_check_group(const char_u *name, int len) { - char_u *name = vim_strnsave(pp, len); - int id = syn_name2id(name); + int id = syn_name2id_len(name, len); if (id == 0) { // doesn't exist yet - id = syn_add_group(name); - } else { - xfree(name); + return syn_add_group(vim_strnsave(name, len)); } return id; } @@ -7520,15 +7681,15 @@ int syn_check_group(const char_u *pp, int len) /// @see syn_check_group syn_unadd_group static int syn_add_group(char_u *name) { - char_u *p; + char_u *p; - /* Check that the name is ASCII letters, digits and underscore. */ + // Check that the name is ASCII letters, digits and underscore. for (p = name; *p != NUL; ++p) { if (!vim_isprintc(*p)) { EMSG(_("E669: Unprintable character in group name")); xfree(name); return 0; - } else if (!ASCII_ISALNUM(*p) && *p != '_') { + } else if (!ASCII_ISALNUM(*p) && *p != '_') { /* This is an error, but since there previously was no check only * give a warning. */ msg_source(HL_ATTR(HLF_W)); @@ -7551,10 +7712,10 @@ static int syn_add_group(char_u *name) return 0; } - char_u *const name_up = vim_strsave_up(name); + char *const name_up = (char *)vim_strsave_up(name); // Append another syntax_highlight entry. - struct hl_group* hlgp = GA_APPEND_VIA_PTR(struct hl_group, &highlight_ga); + struct hl_group * hlgp = GA_APPEND_VIA_PTR(struct hl_group, &highlight_ga); memset(hlgp, 0, sizeof(*hlgp)); hlgp->sg_name = name; hlgp->sg_rgb_bg = -1; @@ -7563,7 +7724,11 @@ static int syn_add_group(char_u *name) hlgp->sg_blend = -1; hlgp->sg_name_u = name_up; - return highlight_ga.ga_len; /* ID is index plus one */ + int id = highlight_ga.ga_len; // ID is index plus one + + map_put(cstr_t, int)(&highlight_unames, name_up, id); + + return id; } /// When, just after calling syn_add_group(), an error is discovered, this @@ -7571,8 +7736,10 @@ static int syn_add_group(char_u *name) static void syn_unadd_group(void) { highlight_ga.ga_len--; - xfree(HL_TABLE()[highlight_ga.ga_len].sg_name); - xfree(HL_TABLE()[highlight_ga.ga_len].sg_name_u); + HlGroup *item = &HL_TABLE()[highlight_ga.ga_len]; + map_del(cstr_t, int)(&highlight_unames, item->sg_name_u); + xfree(item->sg_name); + xfree(item->sg_name_u); } @@ -7600,9 +7767,9 @@ int syn_get_final_id(int hl_id) { int count; - if (hl_id > highlight_ga.ga_len || hl_id < 1) - return 0; /* Can be called from eval!! */ - + if (hl_id > highlight_ga.ga_len || hl_id < 1) { + return 0; // Can be called from eval!! + } /* * Follow links until there is no more. * Look out for loops! Break after 100 links. @@ -7650,8 +7817,7 @@ void highlight_attr_set_all(void) } // Apply difference between User[1-9] and HLF_S to HLF_SNC. -static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, - int hlf, int *table) +static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int hlf, int *table) FUNC_ATTR_NONNULL_ALL { struct hl_group *const hlt = HL_TABLE(); @@ -7699,7 +7865,7 @@ void highlight_changed(void) int id_SNC = 0; int hlcnt; - need_highlight_changed = FALSE; + need_highlight_changed = false; /// Translate builtin highlight groups into attributes for quick lookup. for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) { @@ -7768,7 +7934,7 @@ void set_context_in_highlight_cmd(expand_T *xp, const char *arg) include_link = 2; include_default = 1; - /* (part of) subcommand already typed */ + // (part of) subcommand already typed if (*arg != NUL) { const char *p = (const char *)skiptowhite((const char_u *)arg); if (*p != NUL) { // Past "default" or group name. @@ -7778,7 +7944,7 @@ void set_context_in_highlight_cmd(expand_T *xp, const char *arg) xp->xp_pattern = (char_u *)arg; p = (const char *)skiptowhite((const char_u *)arg); } - if (*p != NUL) { /* past group name */ + if (*p != NUL) { // past group name include_link = 0; if (arg[1] == 'i' && arg[0] == 'N') { highlight_list(); @@ -7833,8 +7999,9 @@ const char *get_highlight_name(expand_T *const xp, int idx) /// Obtain a highlight group name. -/// When "skip_cleared" is TRUE don't return a cleared entry. -const char *get_highlight_name_ext(expand_T *xp, int idx, int skip_cleared) +/// +/// @param skip_cleared if true don't return a cleared entry. +const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared) FUNC_ATTR_WARN_UNUSED_RESULT { if (idx < 0) { @@ -8556,7 +8723,6 @@ color_name_table_T color_name_table[] = { /// return the hex value or -1 if could not find a correct value RgbValue name_to_color(const char *name) { - if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5]) && isxdigit(name[6]) && name[7] == NUL) { @@ -8579,5 +8745,5 @@ RgbValue name_to_color(const char *name) /************************************** -* End of Highlighting stuff * +* End of Highlighting stuff * **************************************/ diff --git a/src/nvim/tag.c b/src/nvim/tag.c index ab35c936ca..61d48eb4bf 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1371,12 +1371,12 @@ find_tags( tagname_T tn; /* info for get_tagfname() */ int first_file; /* trying first tag file */ tagptrs_T tagp; - int did_open = FALSE; /* did open a tag file */ - int stop_searching = FALSE; /* stop when match found or error */ - int retval = FAIL; /* return value */ - int is_static; /* current tag line is static */ - int is_current; /* file name matches */ - int eof = FALSE; /* found end-of-file */ + bool did_open = false; // did open a tag file + bool stop_searching = false; // stop when match found or error + int retval = FAIL; // return value + int is_static; // current tag line is static + int is_current; // file name matches + bool eof = false; // found end-of-file char_u *p; char_u *s; int i; @@ -1429,12 +1429,12 @@ find_tags( vimconv_T vimconv; int findall = (mincount == MAXCOL || mincount == TAG_MANY); - /* find all matching tags */ - int sort_error = FALSE; /* tags file not sorted */ - int linear; /* do a linear search */ - int sortic = FALSE; /* tag file sorted in nocase */ - int line_error = FALSE; /* syntax error */ - int has_re = (flags & TAG_REGEXP); /* regexp used */ + // find all matching tags + bool sort_error = false; // tags file not sorted + int linear; // do a linear search + bool sortic = false; // tag file sorted in nocase + bool line_error = false; // syntax error + int has_re = (flags & TAG_REGEXP); // regexp used int help_only = (flags & TAG_HELP); int name_only = (flags & TAG_NAMES); int noic = (flags & TAG_NOIC); @@ -1621,7 +1621,7 @@ find_tags( verbose_leave(); } } - did_open = TRUE; /* remember that we found at least one file */ + did_open = true; // remember that we found at least one file state = TS_START; /* we're at the start of the file */ @@ -1638,13 +1638,13 @@ find_tags( if ((flags & TAG_INS_COMP)) /* Double brackets for gcc */ ins_compl_check_keys(30, false); if (got_int || compl_interrupted) { - stop_searching = TRUE; + stop_searching = true; break; } /* When mincount is TAG_MANY, stop when enough matches have been * found (for completion). */ if (mincount == TAG_MANY && match_count >= TAG_MANY) { - stop_searching = TRUE; + stop_searching = true; retval = OK; break; } @@ -1795,7 +1795,7 @@ line_read_in: state = TS_BINARY; else if (tag_file_sorted == '2') { state = TS_BINARY; - sortic = TRUE; + sortic = true; orgpat.regmatch.rm_ic = (p_ic || !noic); } else state = TS_LINEAR; @@ -1878,8 +1878,9 @@ parse_line: i = (int)tagp.tagname[0]; if (sortic) i = TOUPPER_ASC(tagp.tagname[0]); - if (i < search_info.low_char || i > search_info.high_char) - sort_error = TRUE; + if (i < search_info.low_char || i > search_info.high_char) { + sort_error = true; + } /* * Compare the current tag with the searched tag. @@ -1970,7 +1971,7 @@ parse_line: i = parse_tag_line(lbuf, &tagp); if (i == FAIL) { - line_error = TRUE; + line_error = true; break; } @@ -2175,7 +2176,7 @@ parse_line: tag_file_sorted = NUL; if (sort_error) { EMSG2(_("E432: Tags file not sorted: %s"), tag_fname); - sort_error = FALSE; + sort_error = false; } /* @@ -2183,7 +2184,7 @@ parse_line: */ if (match_count >= mincount) { retval = OK; - stop_searching = TRUE; + stop_searching = true; } if (stop_searching || use_cscope) @@ -2611,7 +2612,6 @@ static int jumpto_tag( int keep_help // keep help flag (FALSE for cscope) ) { - int save_secure; int save_magic; bool save_p_ws; int save_p_scs, save_p_ic; @@ -2766,9 +2766,6 @@ static int jumpto_tag( curwin->w_set_curswant = true; postponed_split = 0; - save_secure = secure; - secure = 1; - ++sandbox; save_magic = p_magic; p_magic = false; // always execute with 'nomagic' // Save value of no_hlsearch, jumping to a tag is not a real search @@ -2866,21 +2863,26 @@ static int jumpto_tag( * of the line. May need to correct that here. */ check_cursor(); } else { - curwin->w_cursor.lnum = 1; /* start command in line 1 */ + const int save_secure = secure; + + // Setup the sandbox for executing the command from the tags file. + secure = 1; + sandbox++; + curwin->w_cursor.lnum = 1; // start command in line 1 do_cmdline_cmd((char *)pbuf); retval = OK; + + // When the command has done something that is not allowed make sure + // the error message can be seen. + if (secure == 2) { + wait_return(true); + } + secure = save_secure; + sandbox--; } - /* - * When the command has done something that is not allowed make sure - * the error message can be seen. - */ - if (secure == 2) - wait_return(TRUE); - secure = save_secure; p_magic = save_magic; - --sandbox; - /* restore no_hlsearch when keeping the old search pattern */ + // restore no_hlsearch when keeping the old search pattern if (search_options) { set_no_hlsearch(save_no_hlsearch); } @@ -3059,24 +3061,26 @@ expand_tags ( ) { int i; - int c; - int tagnmflag; - char_u tagnm[100]; + int extra_flag; + char_u *name_buf; + size_t name_buf_size = 100; tagptrs_T t_p; int ret; - if (tagnames) - tagnmflag = TAG_NAMES; - else - tagnmflag = 0; + name_buf = xmalloc(name_buf_size); + + if (tagnames) { + extra_flag = TAG_NAMES; + } else { + extra_flag = 0; + } if (pat[0] == '/') { ret = find_tags(pat + 1, num_file, file, - TAG_REGEXP | tagnmflag | TAG_VERBOSE | TAG_NO_TAGFUNC, + TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC, TAG_MANY, curbuf->b_ffname); } else { ret = find_tags(pat, num_file, file, - TAG_REGEXP | tagnmflag | TAG_VERBOSE - | TAG_NO_TAGFUNC | TAG_NOIC, + TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC | TAG_NOIC, TAG_MANY, curbuf->b_ffname); } if (ret == OK && !tagnames) { @@ -3084,18 +3088,29 @@ expand_tags ( * "<tagname>\0<kind>\0<filename>\0" */ for (i = 0; i < *num_file; i++) { + size_t len; + parse_match((*file)[i], &t_p); - c = (int)(t_p.tagname_end - t_p.tagname); - memmove(tagnm, t_p.tagname, (size_t)c); - tagnm[c++] = 0; - tagnm[c++] = (t_p.tagkind != NULL && *t_p.tagkind) - ? *t_p.tagkind : 'f'; - tagnm[c++] = 0; - memmove((*file)[i] + c, t_p.fname, t_p.fname_end - t_p.fname); - (*file)[i][c + (t_p.fname_end - t_p.fname)] = 0; - memmove((*file)[i], tagnm, (size_t)c); + len = t_p.tagname_end - t_p.tagname; + if (len > name_buf_size - 3) { + char_u *buf; + + name_buf_size = len + 3; + buf = xrealloc(name_buf, name_buf_size); + name_buf = buf; + } + + memmove(name_buf, t_p.tagname, len); + name_buf[len++] = 0; + name_buf[len++] = (t_p.tagkind != NULL && *t_p.tagkind) + ? *t_p.tagkind : 'f'; + name_buf[len++] = 0; + memmove((*file)[i] + len, t_p.fname, t_p.fname_end - t_p.fname); + (*file)[i][len + (t_p.fname_end - t_p.fname)] = 0; + memmove((*file)[i], name_buf, len); } } + xfree(name_buf); return ret; } @@ -3291,7 +3306,7 @@ static void tagstack_clear(win_T *wp) } // Remove the oldest entry from the tag stack and shift the rest of -// the entires to free up the top of the stack. +// the entries to free up the top of the stack. static void tagstack_shift(win_T *wp) { taggy_T *tagstack = wp->w_tagstack; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index c07a956dde..3335fa500a 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -76,7 +76,6 @@ #include "nvim/event/time.h" #include "nvim/os/input.h" #include "nvim/api/private/helpers.h" -#include "nvim/api/private/handle.h" typedef struct terminal_state { VimState state; @@ -153,11 +152,10 @@ static VTermScreenCallbacks vterm_screen_callbacks = { .sb_popline = term_sb_pop, }; -static PMap(ptr_t) *invalidated_terminals; +static PMap(ptr_t) invalidated_terminals = MAP_INIT; void terminal_init(void) { - invalidated_terminals = pmap_new(ptr_t)(); time_watcher_init(&main_loop, &refresh_timer, NULL); // refresh_timer_cb will redraw the screen which can call vimscript refresh_timer.events = multiqueue_new_child(main_loop.events); @@ -168,8 +166,10 @@ void terminal_teardown(void) time_watcher_stop(&refresh_timer); multiqueue_free(refresh_timer.events); time_watcher_close(&refresh_timer, NULL); - pmap_free(ptr_t)(invalidated_terminals); - invalidated_terminals = NULL; + pmap_destroy(ptr_t)(&invalidated_terminals); + // terminal_destroy might be called after terminal_teardown is invoked + // make sure it is in an empty, valid state + pmap_init(ptr_t, &invalidated_terminals); } // public API {{{ @@ -260,7 +260,7 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts) return rv; } -void terminal_close(Terminal *term, char *msg) +void terminal_close(Terminal *term, int status) { if (term->closed) { return; @@ -278,8 +278,8 @@ void terminal_close(Terminal *term, char *msg) buf_T *buf = handle_get_buffer(term->buf_handle); term->closed = true; - if (!msg || exiting) { - // If no msg was given, this was called by close_buffer(buffer.c). Or if + if (status == -1 || exiting) { + // If status is -1, this was called by close_buffer(buffer.c). Or if // exiting, we must inform the buffer the terminal no longer exists so that // close_buffer() doesn't call this again. term->buf_handle = 0; @@ -291,11 +291,16 @@ void terminal_close(Terminal *term, char *msg) term->opts.close_cb(term->opts.data); } } else { + char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; + snprintf(msg, sizeof msg, "\r\n[Process exited %d]", status); terminal_receive(term, msg, strlen(msg)); } - if (buf) { + if (buf && !is_autocmd_blocked()) { + dict_T *dict = get_vim_var_dict(VV_EVENT); + tv_dict_add_nr(dict, S_LEN("status"), status); apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf); + tv_dict_clear(dict); } } @@ -521,14 +526,12 @@ void terminal_destroy(Terminal *term) } if (!term->refcount) { - // might be destroyed after terminal_teardown is invoked - if (invalidated_terminals - && pmap_has(ptr_t)(invalidated_terminals, term)) { + if (pmap_has(ptr_t)(&invalidated_terminals, term)) { // flush any pending changes to the buffer block_autocmds(); refresh_terminal(term); unblock_autocmds(); - pmap_del(ptr_t)(invalidated_terminals, term); + pmap_del(ptr_t)(&invalidated_terminals, term); } for (size_t i = 0; i < term->sb_current; i++) { xfree(term->sb_buffer[i]); @@ -865,7 +868,7 @@ static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) } memcpy(sbrow->cells, cells, sizeof(cells[0]) * c); - pmap_put(ptr_t)(invalidated_terminals, term, NULL); + pmap_put(ptr_t)(&invalidated_terminals, term, NULL); return 1; } @@ -906,7 +909,7 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) } xfree(sbrow); - pmap_put(ptr_t)(invalidated_terminals, term, NULL); + pmap_put(ptr_t)(&invalidated_terminals, term, NULL); return 1; } @@ -1208,7 +1211,7 @@ static void invalidate_terminal(Terminal *term, int start_row, int end_row) term->invalid_end = MAX(term->invalid_end, end_row); } - pmap_put(ptr_t)(invalidated_terminals, term, NULL); + pmap_put(ptr_t)(&invalidated_terminals, term, NULL); if (!refresh_pending) { time_watcher_start(&refresh_timer, refresh_timer_cb, REFRESH_DELAY, 0); refresh_pending = true; @@ -1250,10 +1253,10 @@ static void refresh_timer_cb(TimeWatcher *watcher, void *data) void *stub; (void)(stub); // don't process autocommands while updating terminal buffers block_autocmds(); - map_foreach(invalidated_terminals, term, stub, { + map_foreach(&invalidated_terminals, term, stub, { refresh_terminal(term); }); - pmap_clear(ptr_t)(invalidated_terminals); + pmap_clear(ptr_t)(&invalidated_terminals); unblock_autocmds(); } diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim index 7b06e53dd5..14bab33a2f 100644 --- a/src/nvim/testdir/check.vim +++ b/src/nvim/testdir/check.vim @@ -9,6 +9,17 @@ func CheckFeature(name) endif endfunc +" Command to check for the absence of a feature. +command -nargs=1 CheckNotFeature call CheckNotFeature(<f-args>) +func CheckNotFeature(name) + if !has(a:name, 1) + throw 'Checking for non-existent feature ' .. a:name + endif + if has(a:name) + throw 'Skipped: ' .. a:name .. ' feature present' + endif +endfunc + " Command to check for the presence of a working option. command -nargs=1 CheckOption call CheckOption(<f-args>) func CheckOption(name) diff --git a/src/nvim/testdir/samples/memfile_test.c b/src/nvim/testdir/samples/memfile_test.c index c71a5c8f40..7023064637 100644 --- a/src/nvim/testdir/samples/memfile_test.c +++ b/src/nvim/testdir/samples/memfile_test.c @@ -37,8 +37,8 @@ test_mf_hash(void) mf_hashtab_T ht; mf_hashitem_T *item; blocknr_T key; - long_u i; - long_u num_buckets; + size_t i; + size_t num_buckets; mf_hash_init(&ht); diff --git a/src/nvim/testdir/sautest/autoload/foo.vim b/src/nvim/testdir/sautest/autoload/foo.vim index d7dcd5ce3d..298e7275d8 100644 --- a/src/nvim/testdir/sautest/autoload/foo.vim +++ b/src/nvim/testdir/sautest/autoload/foo.vim @@ -5,3 +5,7 @@ let foo#bar = {} func foo#bar.echo() let g:called_foo_bar_echo += 1 endfunc + +func foo#addFoo(head) + return a:head .. 'foo' +endfunc diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index fd9cfb54be..b3df8c63e6 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -12,6 +12,7 @@ set directory^=. set fillchars=vert:\|,fold:- set laststatus=1 set listchars=eol:$ +set joinspaces set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd set nrformats+=octal set shortmess-=F @@ -20,7 +21,15 @@ set tags=./tags,tags set undodir^=. set wildoptions= set startofline -set sessionoptions&vi +set sessionoptions+=options +set viewoptions+=options +set switchbuf= + +" Unmap Nvim default mappings. +unmap Y +unmap <C-L> +iunmap <C-U> +iunmap <C-W> " Prevent Nvim log from writing to stderr. let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' diff --git a/src/nvim/testdir/test42.in b/src/nvim/testdir/test42.in Binary files differindex d9057e72fb..456f9ddb07 100644 --- a/src/nvim/testdir/test42.in +++ b/src/nvim/testdir/test42.in diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index b5c50b5894..cc767a9bcf 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -7,6 +7,7 @@ source test_cd.vim source test_changedtick.vim source test_compiler.vim source test_cursor_func.vim +source test_cursorline.vim source test_ex_equal.vim source test_ex_undo.vim source test_ex_z.vim diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index a1ef8325ec..01d8f32893 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -90,8 +90,8 @@ func Test_argadd_empty_curbuf() call assert_equal('', bufname('%')) call assert_equal(1, line('$')) rew - call assert_notequal(curbuf, bufnr('%')) - call assert_equal('Xargadd', bufname('%')) + call assert_notequal(curbuf, '%'->bufnr()) + call assert_equal('Xargadd', '%'->bufname()) call assert_equal(2, line('$')) %argd diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim index 1d114221dc..52f243aaea 100644 --- a/src/nvim/testdir/test_assert.vim +++ b/src/nvim/testdir/test_assert.vim @@ -7,7 +7,7 @@ func Test_assert_equalfile() let goodtext = ["one", "two", "three"] call writefile(goodtext, 'Xone') - call assert_equal(1, assert_equalfile('Xone', 'xyzxyz')) + call assert_equal(1, 'Xone'->assert_equalfile('xyzxyz')) call assert_match("E485: Can't read file xyzxyz", v:errors[0]) call remove(v:errors, 0) diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index bb84fa498e..015979e1be 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -108,19 +108,19 @@ func Test_bufunload() autocmd BufWipeout * call add(s:li, "bufwipeout") augroup END - let s:li=[] + let s:li = [] new setlocal bufhidden= bunload call assert_equal(["bufunload", "bufdelete"], s:li) - let s:li=[] + let s:li = [] new setlocal bufhidden=delete bunload call assert_equal(["bufunload", "bufdelete"], s:li) - let s:li=[] + let s:li = [] new setlocal bufhidden=unload bwipeout @@ -196,6 +196,29 @@ func Test_autocmd_bufunload_avoiding_SEGV_02() bwipe! a.txt endfunc +func Test_autocmd_dummy_wipeout() + " prepare files + call writefile([''], 'Xdummywipetest1.txt') + call writefile([''], 'Xdummywipetest2.txt') + augroup test_bufunload_group + autocmd! + autocmd BufUnload * call add(s:li, "bufunload") + autocmd BufDelete * call add(s:li, "bufdelete") + autocmd BufWipeout * call add(s:li, "bufwipeout") + augroup END + + let s:li = [] + split Xdummywipetest1.txt + silent! vimgrep /notmatched/ Xdummywipetest* + call assert_equal(["bufunload", "bufwipeout"], s:li) + + bwipeout + call delete('Xdummywipetest1.txt') + call delete('Xdummywipetest2.txt') + au! test_bufunload_group + augroup! test_bufunload_group +endfunc + func Test_win_tab_autocmd() let g:record = [] @@ -428,7 +451,7 @@ func Test_autocmd_bufwipe_in_SessLoadPost() let content =<< trim [CODE] set nocp noswapfile - let v:swapchoice="e" + let v:swapchoice = "e" augroup test_autocmd_sessionload autocmd! autocmd SessionLoadPost * exe bufnr("Xsomething") . "bw!" @@ -537,92 +560,92 @@ func Test_OptionSet() au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>")) " 1: Setting number option" - let g:options=[['number', 0, 1, 'global']] + let g:options = [['number', 0, 1, 'global']] set nu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 2: Setting local number option" - let g:options=[['number', 1, 0, 'local']] + let g:options = [['number', 1, 0, 'local']] setlocal nonu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 3: Setting global number option" - let g:options=[['number', 1, 0, 'global']] + let g:options = [['number', 1, 0, 'global']] setglobal nonu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 4: Setting local autoindent option" - let g:options=[['autoindent', 0, 1, 'local']] + let g:options = [['autoindent', 0, 1, 'local']] setlocal ai call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 5: Setting global autoindent option" - let g:options=[['autoindent', 0, 1, 'global']] + let g:options = [['autoindent', 0, 1, 'global']] setglobal ai call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 6: Setting global autoindent option" - let g:options=[['autoindent', 1, 0, 'global']] + let g:options = [['autoindent', 1, 0, 'global']] set ai! call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " Should not print anything, use :noa " 7: don't trigger OptionSet" - let g:options=[['invalid', 1, 1, 'invalid']] + let g:options = [['invalid', 1, 1, 'invalid']] noa set nonu call assert_equal([['invalid', 1, 1, 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) " 8: Setting several global list and number option" - let g:options=[['list', 0, 1, 'global'], ['number', 0, 1, 'global']] + let g:options = [['list', 0, 1, 'global'], ['number', 0, 1, 'global']] set list nu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 9: don't trigger OptionSet" - let g:options=[['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']] + let g:options = [['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']] noa set nolist nonu call assert_equal([['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) " 10: Setting global acd" - let g:options=[['autochdir', 0, 1, 'local']] + let g:options = [['autochdir', 0, 1, 'local']] setlocal acd call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 11: Setting global autoread (also sets local value)" - let g:options=[['autoread', 0, 1, 'global']] + let g:options = [['autoread', 0, 1, 'global']] set ar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 12: Setting local autoread" - let g:options=[['autoread', 1, 1, 'local']] + let g:options = [['autoread', 1, 1, 'local']] setlocal ar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 13: Setting global autoread" - let g:options=[['autoread', 1, 0, 'global']] + let g:options = [['autoread', 1, 0, 'global']] setglobal invar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 14: Setting option backspace through :let" - let g:options=[['backspace', '', 'eol,indent,start', 'global']] - let &bs="eol,indent,start" + let g:options = [['backspace', '', 'eol,indent,start', 'global']] + let &bs = "eol,indent,start" call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 15: Setting option backspace through setbufvar()" - let g:options=[['backup', 0, 1, 'local']] + let g:options = [['backup', 0, 1, 'local']] " try twice, first time, shouldn't trigger because option name is invalid, " second time, it should trigger call assert_fails("call setbufvar(1, '&l:bk', 1)", "E355") @@ -632,13 +655,13 @@ func Test_OptionSet() call assert_equal(g:opt[0], g:opt[1]) " 16: Setting number option using setwinvar" - let g:options=[['number', 0, 1, 'local']] + let g:options = [['number', 0, 1, 'local']] call setwinvar(0, '&number', 1) call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 17: Setting key option, shouldn't trigger" - let g:options=[['key', 'invalid', 'invalid1', 'invalid']] + let g:options = [['key', 'invalid', 'invalid1', 'invalid']] setlocal key=blah setlocal key= call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options) @@ -646,13 +669,13 @@ func Test_OptionSet() " 18: Setting string option" let oldval = &tags - let g:options=[['tags', oldval, 'tagpath', 'global']] + let g:options = [['tags', oldval, 'tagpath', 'global']] set tags=tagpath call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 1l: Resetting string option" - let g:options=[['tags', 'tagpath', oldval, 'global']] + let g:options = [['tags', 'tagpath', oldval, 'global']] set tags& call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) @@ -672,7 +695,7 @@ func Test_OptionSet_diffmode() call test_override('starting', 1) " 18: Changing an option when entering diff mode new - au OptionSet diff :let &l:cul=v:option_new + au OptionSet diff :let &l:cul = v:option_new call setline(1, ['buffer 1', 'line2', 'line3', 'line4']) call assert_equal(0, &l:cul) @@ -975,6 +998,7 @@ func Test_bufunload_all() endfunc au BufUnload * call UnloadAllBufs() au VimLeave * call writefile(['Test Finished'], 'Xout') + set nohidden edit Xxx1 split Xxx2 q @@ -1754,7 +1778,7 @@ func Test_autocmd_CmdWinEnter() autocmd CmdWinEnter * quit let winnr = winnr('$') END - let filename='XCmdWinEnter' + let filename = 'XCmdWinEnter' call writefile(lines, filename) let buf = RunVimInTerminal('-S '.filename, #{rows: 6}) @@ -1932,7 +1956,7 @@ func Test_autocmd_sigusr1() let g:sigusr1_passed = 0 au Signal SIGUSR1 let g:sigusr1_passed = 1 - call system('/bin/kill -s usr1 ' . getpid()) + call system('kill -s usr1 ' . getpid()) call WaitForAssert({-> assert_true(g:sigusr1_passed)}) au! Signal diff --git a/src/nvim/testdir/test_autoload.vim b/src/nvim/testdir/test_autoload.vim index 7396c227c9..b8c4fa251f 100644 --- a/src/nvim/testdir/test_autoload.vim +++ b/src/nvim/testdir/test_autoload.vim @@ -8,6 +8,8 @@ func Test_autoload_dict_func() call g:foo#bar.echo() call assert_equal(1, g:loaded_foo_vim) call assert_equal(1, g:called_foo_bar_echo) + + eval 'bar'->g:foo#addFoo()->assert_equal('barfoo') endfunc func Test_source_autoload() diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index ff5029b889..97b570e64f 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -16,6 +16,10 @@ func s:screen_lines(lnum, width) abort return ScreenLines([a:lnum, a:lnum + 2], a:width) endfunc +func s:screen_lines2(lnums, lnume, width) abort + return ScreenLines([a:lnums, a:lnume], a:width) +endfunc + func! s:compare_lines(expect, actual) call assert_equal(join(a:expect, "\n"), join(a:actual, "\n")) endfunc @@ -63,7 +67,8 @@ endfunc func Test_breakindent02() " simple breakindent test with showbreak set - call s:test_windows('setl briopt=min:0 sbr=>>') + set sbr=>> + call s:test_windows('setl briopt=min:0 sbr=') let lines = s:screen_lines(line('.'),8) let expect = [ \ " abcd", @@ -123,7 +128,8 @@ endfunc func Test_breakindent04() " breakindent set with min width 18 - call s:test_windows('setl sbr= briopt=min:18') + set sbr=<<< + call s:test_windows('setl sbr=NONE briopt=min:18') let lines = s:screen_lines(line('.'),8) let expect = [ \ " abcd", @@ -133,6 +139,7 @@ func Test_breakindent04() call s:compare_lines(expect, lines) " clean up call s:close_windows('set sbr=') + set sbr= endfunc func Test_breakindent04_vartabs() @@ -745,4 +752,141 @@ func Test_breakindent20_cpo_n_nextpage() call s:close_windows('set breakindent& briopt& cpo& number&') endfunc +func Test_breakindent20_list() + call s:test_windows('setl breakindent breakindentopt= linebreak') + " default: + call setline(1, [' 1. Congress shall make no law', + \ ' 2.) Congress shall make no law', + \ ' 3.] Congress shall make no law']) + norm! 1gg + redraw! + let lines = s:screen_lines2(1, 6, 20) + let expect = [ + \ " 1. Congress ", + \ "shall make no law ", + \ " 2.) Congress ", + \ "shall make no law ", + \ " 3.] Congress ", + \ "shall make no law ", + \ ] + call s:compare_lines(expect, lines) + " set mininum indent + setl briopt=min:5 + redraw! + let lines = s:screen_lines2(1, 6, 20) + let expect = [ + \ " 1. Congress ", + \ " shall make no law ", + \ " 2.) Congress ", + \ " shall make no law ", + \ " 3.] Congress ", + \ " shall make no law ", + \ ] + call s:compare_lines(expect, lines) + " set additional handing indent + setl briopt+=list:4 + redraw! + let expect = [ + \ " 1. Congress ", + \ " shall make no ", + \ " law ", + \ " 2.) Congress ", + \ " shall make no ", + \ " law ", + \ " 3.] Congress ", + \ " shall make no ", + \ " law ", + \ ] + let lines = s:screen_lines2(1, 9, 20) + call s:compare_lines(expect, lines) + + " reset linebreak option + " Note: it indents by one additional + " space, because of the leading space. + setl linebreak&vim list listchars=eol:$,space:_ + redraw! + let expect = [ + \ "__1.__Congress_shall", + \ " _make_no_law$ ", + \ "__2.)_Congress_shall", + \ " _make_no_law$ ", + \ "__3.]_Congress_shall", + \ " _make_no_law$ ", + \ ] + let lines = s:screen_lines2(1, 6, 20) + call s:compare_lines(expect, lines) + + " check formatlistpat indent + setl briopt=min:5,list:-1 + setl linebreak list&vim listchars&vim + let &l:flp = '^\s*\d\+\.\?[\]:)}\t ]\s*' + redraw! + let expect = [ + \ " 1. Congress ", + \ " shall make no ", + \ " law ", + \ " 2.) Congress ", + \ " shall make no ", + \ " law ", + \ " 3.] Congress ", + \ " shall make no ", + \ " law ", + \ ] + let lines = s:screen_lines2(1, 9, 20) + call s:compare_lines(expect, lines) + " check formatlistpat indent with different list levels + let &l:flp = '^\s*\*\+\s\+' + redraw! + %delete _ + call setline(1, ['* Congress shall make no law', + \ '*** Congress shall make no law', + \ '**** Congress shall make no law']) + norm! 1gg + let expect = [ + \ "* Congress shall ", + \ " make no law ", + \ "*** Congress shall ", + \ " make no law ", + \ "**** Congress shall ", + \ " make no law ", + \ ] + let lines = s:screen_lines2(1, 6, 20) + call s:compare_lines(expect, lines) + + " check formatlistpat indent with different list level + " showbreak and sbr + setl briopt=min:5,sbr,list:-1,shift:2 + setl showbreak=> + redraw! + let expect = [ + \ "* Congress shall ", + \ "> make no law ", + \ "*** Congress shall ", + \ "> make no law ", + \ "**** Congress shall ", + \ "> make no law ", + \ ] + let lines = s:screen_lines2(1, 6, 20) + call s:compare_lines(expect, lines) + call s:close_windows('set breakindent& briopt& linebreak& list& listchars& showbreak&') +endfunc + +" The following used to crash Vim. This is fixed by 8.2.3391. +" This is a regression introduced by 8.2.2903. +func Test_window_resize_with_linebreak() + new + 53vnew + set linebreak + set showbreak=>> + set breakindent + set breakindentopt=shift:4 + call setline(1, "\naaaaaaaaa\n\na\naaaaa\n¯aaaaaaaaaa\naaaaaaaaaaaa\naaa\n\"a:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaa\"\naaaaaaaa\n\"a") + redraw! + call assert_equal([" >>aa^@\"a: "], ScreenLines(2, 14)) + vertical resize 52 + redraw! + call assert_equal([" >>aaa^@\"a:"], ScreenLines(2, 14)) + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim index e038bce08e..b4e8a0bc71 100644 --- a/src/nvim/testdir/test_bufline.vim +++ b/src/nvim/testdir/test_bufline.vim @@ -102,7 +102,7 @@ func Test_deletebufline() call assert_equal(0, deletebufline(b, 2, 8)) call assert_equal(['aaa'], getbufline(b, 1, 2)) exe "bd!" b - call assert_equal(1, deletebufline(b, 1)) + call assert_equal(1, b->deletebufline(1)) split Xtest call setline(1, ['a', 'b', 'c']) @@ -131,11 +131,11 @@ func Test_appendbufline_redraw() endif let lines =<< trim END new foo - let winnr=bufwinnr('foo') - let buf=bufnr('foo') + let winnr = 'foo'->bufwinnr() + let buf = bufnr('foo') wincmd p call appendbufline(buf, '$', range(1,200)) - exe winnr. 'wincmd w' + exe winnr .. 'wincmd w' norm! G wincmd p call deletebufline(buf, 1, '$') diff --git a/src/nvim/testdir/test_bufwintabinfo.vim b/src/nvim/testdir/test_bufwintabinfo.vim index cb7ab44798..4b5b55e6bf 100644 --- a/src/nvim/testdir/test_bufwintabinfo.vim +++ b/src/nvim/testdir/test_bufwintabinfo.vim @@ -18,7 +18,7 @@ function Test_getbufwintabinfo() let l = getbufinfo('%') call assert_equal(bufnr('%'), l[0].bufnr) call assert_equal('vim', l[0].variables.editor) - call assert_notequal(-1, index(l[0].windows, bufwinid('%'))) + call assert_notequal(-1, index(l[0].windows, '%'->bufwinid())) " Test for getbufinfo() with 'bufmodified' call assert_equal(0, len(getbufinfo({'bufmodified' : 1}))) diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim index 770ed55b8d..02a23bf82f 100644 --- a/src/nvim/testdir/test_cd.vim +++ b/src/nvim/testdir/test_cd.vim @@ -1,4 +1,7 @@ -" Test for :cd +" Test for :cd and chdir() + +source shared.vim +source check.vim func Test_cd_large_path() " This used to crash with a heap write overflow. @@ -65,3 +68,18 @@ func Test_cd_with_cpo_chdir() set cpo& bw! endfunc + +func Test_cd_from_non_existing_dir() + CheckNotMSWindows + + let saveddir = getcwd() + call mkdir('Xdeleted_dir') + cd Xdeleted_dir + call delete(saveddir .. '/Xdeleted_dir', 'd') + + " Expect E187 as the current directory was deleted. + call assert_fails('pwd', 'E187:') + call assert_equal('', getcwd()) + cd - + call assert_equal(saveddir, getcwd()) +endfunc diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index b6c2d1467e..562867f548 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -118,6 +118,16 @@ b = something(); bw! endfunc +func Test_cindent_func() + new + setlocal cindent + call setline(1, ['int main(void)', '{', 'return 0;', '}']) + call assert_equal(-1, cindent(0)) + call assert_equal(&sw, 3->cindent()) + call assert_equal(-1, cindent(line('$')+1)) + bwipe! +endfunc + " this was going beyond the end of the line. func Test_cindent_case() new diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim index 55b230373f..c7dddf4164 100644 --- a/src/nvim/testdir/test_command_count.vim +++ b/src/nvim/testdir/test_command_count.vim @@ -33,7 +33,7 @@ func Test_command_count_0() delcommand RangeBuffers delcommand RangeBuffersAll - set hidden& + set nohidden set swapfile& endfunc diff --git a/src/nvim/testdir/test_cursorline.vim b/src/nvim/testdir/test_cursorline.vim new file mode 100644 index 0000000000..39d8b901ed --- /dev/null +++ b/src/nvim/testdir/test_cursorline.vim @@ -0,0 +1,271 @@ +" Test for cursorline and cursorlineopt + +source check.vim +source screendump.vim + +function! s:screen_attr(lnum) abort + return map(range(1, 8), 'screenattr(a:lnum, v:val)') +endfunction + +function! s:test_windows(h, w) abort + call NewWindow(a:h, a:w) +endfunction + +function! s:close_windows() abort + call CloseWindow() +endfunction + +function! s:new_hi() abort + redir => save_hi + silent! hi CursorLineNr + redir END + let save_hi = join(split(substitute(save_hi, '\s*xxx\s*', ' ', ''), "\n"), '') + exe 'hi' save_hi 'ctermbg=0 guibg=Black' + return save_hi +endfunction + +func Test_cursorline_highlight1() + let save_hi = s:new_hi() + try + call s:test_windows(10, 20) + call setline(1, repeat(['aaaa'], 10)) + redraw + let attr01 = s:screen_attr(1) + call assert_equal(repeat([attr01[0]], 8), attr01) + + setl number numberwidth=4 + redraw + let attr11 = s:screen_attr(1) + call assert_equal(repeat([attr11[0]], 4), attr11[0:3]) + call assert_equal(repeat([attr11[4]], 4), attr11[4:7]) + call assert_notequal(attr11[0], attr11[4]) + + setl cursorline + redraw + let attr21 = s:screen_attr(1) + let attr22 = s:screen_attr(2) + call assert_equal(repeat([attr21[0]], 4), attr21[0:3]) + call assert_equal(repeat([attr21[4]], 4), attr21[4:7]) + call assert_equal(attr11, attr22) + call assert_notequal(attr22, attr21) + + setl nocursorline relativenumber + redraw + let attr31 = s:screen_attr(1) + call assert_equal(attr22[0:3], attr31[0:3]) + call assert_equal(attr11[4:7], attr31[4:7]) + + call s:close_windows() + finally + exe 'hi' save_hi + endtry +endfunc + +func Test_cursorline_highlight2() + CheckOption cursorlineopt + + let save_hi = s:new_hi() + try + call s:test_windows(10, 20) + call setline(1, repeat(['aaaa'], 10)) + redraw + let attr0 = s:screen_attr(1) + call assert_equal(repeat([attr0[0]], 8), attr0) + + setl number + redraw + let attr1 = s:screen_attr(1) + call assert_notequal(attr0[0:3], attr1[0:3]) + call assert_equal(attr0[0:3], attr1[4:7]) + + setl cursorline cursorlineopt=both + redraw + let attr2 = s:screen_attr(1) + call assert_notequal(attr1[0:3], attr2[0:3]) + call assert_notequal(attr1[4:7], attr2[4:7]) + + setl cursorlineopt=line + redraw + let attr3 = s:screen_attr(1) + call assert_equal(attr1[0:3], attr3[0:3]) + call assert_equal(attr2[4:7], attr3[4:7]) + + setl cursorlineopt=number + redraw + let attr4 = s:screen_attr(1) + call assert_equal(attr2[0:3], attr4[0:3]) + call assert_equal(attr1[4:7], attr4[4:7]) + + setl nonumber + redraw + let attr5 = s:screen_attr(1) + call assert_equal(attr0, attr5) + + call s:close_windows() + finally + exe 'hi' save_hi + endtry +endfunc + +func Test_cursorline_screenline() + CheckScreendump + CheckOption cursorlineopt + + let filename='Xcursorline' + let lines = [] + + let file_content =<< trim END + 1 foooooooo ar einsâ€zwei drei vier fünf sechs sieben acht un zehn elf zwöfl dreizehn v ierzehn fünfzehn + 2 foooooooo bar eins zwei drei vier fünf sechs sieben + 3 foooooooo bar eins zwei drei vier fünf sechs sieben + 4 foooooooo bar eins zwei drei vier fünf sechs sieben + END + let lines1 =<< trim END1 + set nocp + set display=lastline + set cursorlineopt=screenline cursorline nu wrap sbr=> + hi CursorLineNr ctermfg=blue + 25vsp + END1 + let lines2 =<< trim END2 + call cursor(1,1) + END2 + call extend(lines, lines1) + call extend(lines, ["call append(0, ".. string(file_content).. ')']) + call extend(lines, lines2) + call writefile(lines, filename) + " basic test + let buf = RunVimInTerminal('-S '. filename, #{rows: 20}) + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_1', {}) + call term_sendkeys(buf, "fagj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_2', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_3', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_4', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_5', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_6', {}) + " test with set list and cursorlineopt containing number + call term_sendkeys(buf, "gg0") + call term_sendkeys(buf, ":set list cursorlineopt+=number listchars=space:-\<cr>") + call VerifyScreenDump(buf, 'Test_'. filename. '_7', {}) + call term_sendkeys(buf, "fagj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_8', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_9', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_10', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_11', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_12', {}) + if exists("+foldcolumn") && exists("+signcolumn") && exists("+breakindent") + " test with set foldcolumn signcoloumn and breakindent + call term_sendkeys(buf, "gg0") + call term_sendkeys(buf, ":set breakindent foldcolumn=2 signcolumn=yes\<cr>") + call VerifyScreenDump(buf, 'Test_'. filename. '_13', {}) + call term_sendkeys(buf, "fagj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_14', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_15', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_16', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_17', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_18', {}) + call term_sendkeys(buf, ":set breakindent& foldcolumn& signcolumn&\<cr>") + endif + " showbreak should not be highlighted with CursorLine when 'number' is off + call term_sendkeys(buf, "gg0") + call term_sendkeys(buf, ":set list cursorlineopt=screenline listchars=space:-\<cr>") + call term_sendkeys(buf, ":set nonumber\<cr>") + call VerifyScreenDump(buf, 'Test_'. filename. '_19', {}) + call term_sendkeys(buf, "fagj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_20', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_21', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_22', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_23', {}) + call term_sendkeys(buf, "gj") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_'. filename. '_24', {}) + call term_sendkeys(buf, ":set list& cursorlineopt& listchars&\<cr>") + + call StopVimInTerminal(buf) + call delete(filename) +endfunc + +func Test_cursorline_redraw() + CheckScreendump + CheckOption cursorlineopt + + let textlines =<< END + When the option is a list of flags, {value} must be + exactly as they appear in the option. Remove flags + one by one to avoid problems. + Also see |:set-args| above. + +The {option} arguments to ":set" may be repeated. For example: > + :set ai nosi sw=3 ts=3 +If you make an error in one of the arguments, an error message will be given +and the following arguments will be ignored. + + *:set-verbose* +When 'verbose' is non-zero, displaying an option value will also tell where it +was last set. Example: > + :verbose set shiftwidth cindent? +< shiftwidth=4 ~ + Last set from modeline line 1 ~ + cindent ~ + Last set from /usr/local/share/vim/vim60/ftplugin/c.vim line 30 ~ +This is only done when specific option values are requested, not for ":verbose +set all" or ":verbose set" without an argument. +When the option was set by hand there is no "Last set" message. +When the option was set while executing a function, user command or +END + call writefile(textlines, 'Xtextfile') + + let script =<< trim END + set cursorline scrolloff=2 + normal 12G + END + call writefile(script, 'Xscript') + + let buf = RunVimInTerminal('-S Xscript Xtextfile', #{rows: 20, cols: 40}) + call VerifyScreenDump(buf, 'Test_cursorline_redraw_1', {}) + call term_sendkeys(buf, "zt") + call TermWait(buf) + call term_sendkeys(buf, "\<C-U>") + call VerifyScreenDump(buf, 'Test_cursorline_redraw_2', {}) + + call StopVimInTerminal(buf) + call delete('Xscript') + call delete('Xtextfile') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 8592f48af7..efa7f552e0 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -540,7 +540,7 @@ func Test_diffopt_hiddenoff() bwipe! bwipe! - set hidden& diffopt& + set nohidden diffopt& endfunc func Test_diffoff_hidden() @@ -577,7 +577,7 @@ func Test_diffoff_hidden() bwipe! bwipe! - set hidden& diffopt& + set nohidden diffopt& endfunc func Test_setting_cursor() @@ -725,7 +725,7 @@ func Test_diff_filler() diffthis redraw - call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'diff_filler(v:val)')) + call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'v:val->diff_filler()')) wincmd w call assert_equal([0, 0, 0, 0, 2, 0, 0, 0], map(range(-1, 6), 'diff_filler(v:val)')) @@ -741,16 +741,16 @@ func Test_diff_hlID() diffthis redraw - call assert_equal(synIDattr(diff_hlID(-1, 1), "name"), "") + call diff_hlID(-1, 1)->synIDattr("name")->assert_equal("") call assert_equal(diff_hlID(1, 1), hlID("DiffChange")) - call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange") + call diff_hlID(1, 1)->synIDattr("name")->assert_equal("DiffChange") call assert_equal(diff_hlID(1, 2), hlID("DiffText")) - call assert_equal(synIDattr(diff_hlID(1, 2), "name"), "DiffText") - call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "") + call diff_hlID(1, 2)->synIDattr("name")->assert_equal("DiffText") + call diff_hlID(2, 1)->synIDattr("name")->assert_equal("") call assert_equal(diff_hlID(3, 1), hlID("DiffAdd")) - call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "DiffAdd") - call assert_equal(synIDattr(diff_hlID(4, 1), "name"), "") + call diff_hlID(3, 1)->synIDattr("name")->assert_equal("DiffAdd") + call diff_hlID(4, 1)->synIDattr("name")->assert_equal("") wincmd w call assert_equal(diff_hlID(1, 1), hlID("DiffChange")) @@ -990,6 +990,37 @@ func Test_diff_with_cursorline() call delete('Xtest_diff_cursorline') endfunc +func Test_diff_with_cursorline_breakindent() + CheckScreendump + + call writefile([ + \ 'hi CursorLine ctermbg=red ctermfg=white', + \ 'set noequalalways wrap diffopt=followwrap cursorline breakindent', + \ '50vnew', + \ 'call setline(1, [" "," "," "," "])', + \ 'exe "norm 20Afoo\<Esc>j20Afoo\<Esc>j20Afoo\<Esc>j20Abar\<Esc>"', + \ 'vnew', + \ 'call setline(1, [" "," "," "," "])', + \ 'exe "norm 20Abee\<Esc>j20Afoo\<Esc>j20Afoo\<Esc>j20Abaz\<Esc>"', + \ 'windo diffthis', + \ '2wincmd w', + \ ], 'Xtest_diff_cursorline_breakindent') + let buf = RunVimInTerminal('-S Xtest_diff_cursorline_breakindent', {}) + + call term_sendkeys(buf, "gg0") + call VerifyScreenDump(buf, 'Test_diff_with_cul_bri_01', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_with_cul_bri_02', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_with_cul_bri_03', {}) + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_diff_with_cul_bri_04', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_diff_cursorline_breakindent') +endfunc + func Test_diff_with_syntax() CheckScreendump diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim index c702b44b88..12327f34d6 100644 --- a/src/nvim/testdir/test_display.vim +++ b/src/nvim/testdir/test_display.vim @@ -262,3 +262,21 @@ func Test_display_scroll_at_topline() call StopVimInTerminal(buf) endfunc + +func Test_display_linebreak_breakat() + new + vert resize 25 + let _breakat = &breakat + setl signcolumn=yes linebreak breakat=) showbreak=+\ + call setline(1, repeat('x', winwidth(0) - 2) .. ')abc') + let lines = ScreenLines([1, 2], 25) + let expected = [ + \ ' xxxxxxxxxxxxxxxxxxxxxxx', + \ ' + )abc ' + \ ] + call assert_equal(expected, lines) + %bw! + let &breakat=_breakat +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index 4870b9a60a..084c856ba0 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -33,6 +33,24 @@ func Test_for_invalid() redraw endfunc +func Test_readfile_binary() + new + call setline(1, ['one', 'two', 'three']) + setlocal ff=dos + silent write XReadfile + let lines = readfile('XReadfile') + call assert_equal(['one', 'two', 'three'], lines) + let lines = readfile('XReadfile', '', 2) + call assert_equal(['one', 'two'], lines) + let lines = readfile('XReadfile', 'b') + call assert_equal(["one\r", "two\r", "three\r", ""], lines) + let lines = readfile('XReadfile', 'b', 2) + call assert_equal(["one\r", "two\r"], lines) + + bwipe! + call delete('XReadfile') +endfunc + func Test_mkdir_p() call mkdir('Xmkdir/nested', 'p') call assert_true(isdirectory('Xmkdir/nested')) @@ -90,6 +108,15 @@ func Test_string_concatenation() call assert_equal('ab', a) endfunc +" Test fix for issue #4507 +func Test_skip_after_throw() + try + throw 'something' + let x = wincol() || &ts + catch /something/ + endtry +endfunc + func Test_nocatch_restore_silent_emsg() silent! try throw 1 @@ -111,15 +138,6 @@ func Test_let_errmsg() let v:errmsg = '' endfunc -" Test fix for issue #4507 -func Test_skip_after_throw() - try - throw 'something' - let x = wincol() || &ts - catch /something/ - endtry -endfunc - func Test_number_max_min_size() " This will fail on systems without 64 bit number support or when not " configured correctly. diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim index f70cb261e0..1c645ad0f8 100644 --- a/src/nvim/testdir/test_ex_mode.vim +++ b/src/nvim/testdir/test_ex_mode.vim @@ -1,5 +1,8 @@ " Test editing line in Ex mode (see :help Q and :help gQ). +source check.vim +source shared.vim + " Helper function to test editing line in Q Ex mode func Ex_Q(cmd) " Is there a simpler way to test editing Ex line? @@ -79,4 +82,20 @@ func Test_ex_mode_errors() quit endfunc +func Test_ex_mode_count_overflow() + " this used to cause a crash + let lines =<< trim END + call feedkeys("\<Esc>Q\<CR>") + v9|9silent! vi|333333233333y32333333%O + call writefile(['done'], 'Xdidexmode') + qall! + END + call writefile(lines, 'Xexmodescript') + call assert_equal(1, RunVim([], [], '-e -s -S Xexmodescript -c qa')) + call assert_equal(['done'], readfile('Xdidexmode')) + + call delete('Xdidexmode') + call delete('Xexmodescript') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 0b41a1127a..c49285621a 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -502,6 +502,17 @@ func Test_empty_concatenate() call assert_equal('b', 'b' . 'a'[4:0]) endfunc +func Test_broken_number() + let X = 'bad' + call assert_fails('echo 1X', 'E15:') + call assert_fails('echo 0b1X', 'E15:') + call assert_fails('echo 0b12', 'E15:') + call assert_fails('echo 0x1X', 'E15:') + call assert_fails('echo 011X', 'E15:') + call assert_equal(2, str2nr('2a')) + call assert_fails('inoremap <Char-0b1z> b', 'E474:') +endfunc + func Test_eval_after_if() let s:val = '' func SetVal(x) diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 09fdbf4e20..5326d3460f 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -151,7 +151,6 @@ let s:filename_checks = { \ 'dosini': ['.editorconfig', '/etc/pacman.conf', '/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', 'any/etc/pacman.conf', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap'], \ 'dot': ['file.dot', 'file.gv'], \ 'dracula': ['file.drac', 'file.drc', 'filelvs', 'filelpe', 'drac.file', 'lpe', 'lvs', 'some-lpe', 'some-lvs'], - \ 'dsl': ['file.dsl'], \ 'dtd': ['file.dtd'], \ 'dts': ['file.dts', 'file.dtsi'], \ 'dune': ['jbuild', 'dune', 'dune-project', 'dune-workspace'], @@ -161,6 +160,8 @@ let s:filename_checks = { \ 'ecd': ['file.ecd'], \ 'edif': ['file.edf', 'file.edif', 'file.edo'], \ 'elinks': ['elinks.conf'], + \ 'elixir': ['file.ex', 'file.exs', 'mix.lock'], + \ 'eelixir': ['file.eex', 'file.leex'], \ 'elm': ['file.elm'], \ 'elmfilt': ['filter-rules'], \ 'epuppet': ['file.epp'], @@ -190,6 +191,7 @@ let s:filename_checks = { \ 'gdb': ['.gdbinit'], \ 'gdmo': ['file.mo', 'file.gdmo'], \ 'gedcom': ['file.ged', 'lltxxxxx.txt', '/tmp/lltmp', '/tmp/lltmp-file', 'any/tmp/lltmp', 'any/tmp/lltmp-file'], + \ 'gemtext': ['file.gmi', 'file.gemini'], \ 'gift': ['file.gift'], \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG'], \ 'gitconfig': ['file.git/config', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'], @@ -257,8 +259,10 @@ let s:filename_checks = { \ 'jgraph': ['file.jgr'], \ 'jovial': ['file.jov', 'file.j73', 'file.jovial'], \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'], - \ 'json': ['file.json', 'file.jsonp', 'file.webmanifest', 'Pipfile.lock'], + \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb'], + \ 'jsonc': ['file.jsonc'], \ 'jsp': ['file.jsp'], + \ 'julia': ['file.jl'], \ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file'], \ 'kivy': ['file.kv'], \ 'kix': ['file.kix'], @@ -291,6 +295,7 @@ let s:filename_checks = { \ 'lss': ['file.lss'], \ 'lua': ['file.lua', 'file.rockspec', 'file.nse'], \ 'lynx': ['lynx.cfg'], + \ 'matlab': ['file.m'], \ 'm3build': ['m3makefile', 'm3overrides'], \ 'm3quake': ['file.quake', 'cm3.cfg'], \ 'm4': ['file.at'], @@ -346,6 +351,7 @@ let s:filename_checks = { \ 'obj': ['file.obj'], \ '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'], \ 'omnimark': ['file.xom', 'file.xin'], \ 'opam': ['opam', 'file.opam', 'file.opam.template'], \ 'openroad': ['file.or'], @@ -422,11 +428,12 @@ let s:filename_checks = { \ 'sass': ['file.sass'], \ 'sather': ['file.sa'], \ 'sbt': ['file.sbt'], - \ 'scala': ['file.scala'], + \ 'scala': ['file.scala', 'file.sc'], \ 'scheme': ['file.scm', 'file.ss', 'file.rkt'], \ 'scilab': ['file.sci', 'file.sce'], \ 'screen': ['.screenrc', 'screenrc'], \ 'sexplib': ['file.sexp'], + \ 'scdoc': ['file.scd'], \ 'scss': ['file.scss'], \ 'sd': ['file.sd'], \ 'sdc': ['file.sdc'], @@ -749,6 +756,7 @@ func Test_pp_file() split Xfile.pp call assert_equal('pascal', &filetype) bwipe! + unlet g:filetype_pp " Test dist#ft#FTpp() call writefile(['{ pascal comment'], 'Xfile.pp') @@ -765,5 +773,150 @@ func Test_pp_file() filetype off endfunc +func Test_ex_file() + filetype on + + call writefile(['arbitrary content'], 'Xfile.ex') + split Xfile.ex + call assert_equal('elixir', &filetype) + bwipe! + let g:filetype_euphoria = 'euphoria4' + split Xfile.ex + call assert_equal('euphoria4', &filetype) + bwipe! + unlet g:filetype_euphoria + + call writefile(['-- filetype euphoria comment'], 'Xfile.ex') + split Xfile.ex + call assert_equal('euphoria3', &filetype) + bwipe! + + call writefile(['--filetype euphoria comment'], 'Xfile.ex') + split Xfile.ex + call assert_equal('euphoria3', &filetype) + bwipe! + + call writefile(['ifdef '], 'Xfile.ex') + split Xfile.ex + call assert_equal('euphoria3', &filetype) + bwipe! + + call writefile(['include '], 'Xfile.ex') + split Xfile.ex + call assert_equal('euphoria3', &filetype) + bwipe! + + call delete('Xfile.ex') + filetype off +endfunc + +func Test_dsl_file() + filetype on + + call writefile([' <!doctype dsssl-spec ['], 'dslfile.dsl') + split dslfile.dsl + call assert_equal('dsl', &filetype) + bwipe! + + call writefile(['workspace {'], 'dslfile.dsl') + split dslfile.dsl + call assert_equal('structurizr', &filetype) + bwipe! + + call delete('dslfile.dsl') + filetype off +endfunc + +func Test_m_file() + filetype on + + call writefile(['looks like Matlab'], 'Xfile.m') + split Xfile.m + call assert_equal('matlab', &filetype) + bwipe! + + let g:filetype_m = 'octave' + split Xfile.m + call assert_equal('octave', &filetype) + bwipe! + unlet g:filetype_m + + " Test dist#ft#FTm() + + " Objective-C + call writefile(['// Objective-C line comment'], 'Xfile.m') + split Xfile.m + call assert_equal('objc', &filetype) + bwipe! + + call writefile(['/* Objective-C block comment */'], 'Xfile.m') + split Xfile.m + call assert_equal('objc', &filetype) + bwipe! + + call writefile(['#import "test.m"'], 'Xfile.m') + split Xfile.m + call assert_equal('objc', &filetype) + bwipe! + + " Octave + + call writefile(['# Octave line comment'], 'Xfile.m') + split Xfile.m + call assert_equal('octave', &filetype) + bwipe! + + call writefile(['#{', 'Octave block comment', '#}'], 'Xfile.m') + split Xfile.m + call assert_equal('octave', &filetype) + bwipe! + + call writefile(['%{', 'Octave block comment', '%}'], 'Xfile.m') + split Xfile.m + call assert_equal('octave', &filetype) + bwipe! + + call writefile(['%!test "Octave test"'], 'Xfile.m') + split Xfile.m + call assert_equal('octave', &filetype) + bwipe! + + call writefile(['unwind_protect'], 'Xfile.m') + split Xfile.m + call assert_equal('octave', &filetype) + bwipe! + + call writefile(['function test(); 42; endfunction'], 'Xfile.m') + split Xfile.m + call assert_equal('octave', &filetype) + bwipe! + + " Mathematica + + call writefile(['(* Mathematica comment'], 'Xfile.m') + split Xfile.m + call assert_equal('mma', &filetype) + bwipe! + + " Murphi + + call writefile(['-- Murphi comment'], 'Xfile.m') + split Xfile.m + call assert_equal('murphi', &filetype) + bwipe! + + call writefile(['/* Murphi block comment */', 'Type'], 'Xfile.m') + split Xfile.m + call assert_equal('murphi', &filetype) + bwipe! + + call writefile(['Type'], 'Xfile.m') + split Xfile.m + call assert_equal('murphi', &filetype) + bwipe! + + call delete('Xfile.m') + filetype off +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_float_func.vim b/src/nvim/testdir/test_float_func.vim index 154ef570e0..78675d7016 100644 --- a/src/nvim/testdir/test_float_func.vim +++ b/src/nvim/testdir/test_float_func.vim @@ -7,6 +7,8 @@ end func Test_abs() call assert_equal('1.23', string(abs(1.23))) call assert_equal('1.23', string(abs(-1.23))) + eval -1.23->abs()->string()->assert_equal('1.23') + call assert_equal('0.0', string(abs(0.0))) call assert_equal('0.0', string(abs(1.0/(1.0/0.0)))) call assert_equal('0.0', string(abs(-1.0/(1.0/0.0)))) @@ -22,6 +24,7 @@ endfunc func Test_sqrt() call assert_equal('0.0', string(sqrt(0.0))) call assert_equal('1.414214', string(sqrt(2.0))) + eval 2.0->sqrt()->string()->assert_equal('1.414214') call assert_equal("str2float('inf')", string(sqrt(1.0/0.0))) call assert_equal("str2float('nan')", string(sqrt(-1.0))) call assert_equal("str2float('nan')", string(sqrt(0.0/0.0))) @@ -31,6 +34,7 @@ endfunc func Test_log() call assert_equal('0.0', string(log(1.0))) call assert_equal('-0.693147', string(log(0.5))) + eval 0.5->log()->string()->assert_equal('-0.693147') call assert_equal("-str2float('inf')", string(log(0.0))) call assert_equal("str2float('nan')", string(log(-1.0))) call assert_equal("str2float('inf')", string(log(1.0/0.0))) @@ -42,6 +46,7 @@ func Test_log10() call assert_equal('0.0', string(log10(1.0))) call assert_equal('2.0', string(log10(100.0))) call assert_equal('2.079181', string(log10(120.0))) + eval 120.0->log10()->string()->assert_equal('2.079181') call assert_equal("-str2float('inf')", string(log10(0.0))) call assert_equal("str2float('nan')", string(log10(-1.0))) call assert_equal("str2float('inf')", string(log10(1.0/0.0))) @@ -53,6 +58,7 @@ func Test_exp() call assert_equal('1.0', string(exp(0.0))) call assert_equal('7.389056', string(exp(2.0))) call assert_equal('0.367879', string(exp(-1.0))) + eval -1.0->exp()->string()->assert_equal('0.367879') call assert_equal("str2float('inf')", string(exp(1.0/0.0))) call assert_equal('0.0', string(exp(-1.0/0.0))) call assert_equal("str2float('nan')", string(exp(0.0/0.0))) @@ -63,6 +69,7 @@ func Test_sin() call assert_equal('0.0', string(sin(0.0))) call assert_equal('0.841471', string(sin(1.0))) call assert_equal('-0.479426', string(sin(-0.5))) + eval -0.5->sin()->string()->assert_equal('-0.479426') call assert_equal("str2float('nan')", string(sin(0.0/0.0))) call assert_equal("str2float('nan')", string(sin(1.0/0.0))) call assert_equal('0.0', string(sin(1.0/(1.0/0.0)))) @@ -73,6 +80,8 @@ endfunc func Test_asin() call assert_equal('0.0', string(asin(0.0))) call assert_equal('1.570796', string(asin(1.0))) + eval 1.0->asin()->string()->assert_equal('1.570796') + call assert_equal('-0.523599', string(asin(-0.5))) call assert_equal("str2float('nan')", string(asin(1.1))) call assert_equal("str2float('nan')", string(asin(1.0/0.0))) @@ -84,6 +93,7 @@ func Test_sinh() call assert_equal('0.0', string(sinh(0.0))) call assert_equal('0.521095', string(sinh(0.5))) call assert_equal('-1.026517', string(sinh(-0.9))) + eval -0.9->sinh()->string()->assert_equal('-1.026517') call assert_equal("str2float('inf')", string(sinh(1.0/0.0))) call assert_equal("-str2float('inf')", string(sinh(-1.0/0.0))) call assert_equal("str2float('nan')", string(sinh(0.0/0.0))) @@ -94,6 +104,7 @@ func Test_cos() call assert_equal('1.0', string(cos(0.0))) call assert_equal('0.540302', string(cos(1.0))) call assert_equal('0.877583', string(cos(-0.5))) + eval -0.5->cos()->string()->assert_equal('0.877583') call assert_equal("str2float('nan')", string(cos(0.0/0.0))) call assert_equal("str2float('nan')", string(cos(1.0/0.0))) call assert_fails('call cos("")', 'E808:') @@ -103,6 +114,7 @@ func Test_acos() call assert_equal('1.570796', string(acos(0.0))) call assert_equal('0.0', string(acos(1.0))) call assert_equal('3.141593', string(acos(-1.0))) + eval -1.0->acos()->string()->assert_equal('3.141593') call assert_equal('2.094395', string(acos(-0.5))) call assert_equal("str2float('nan')", string(acos(1.1))) call assert_equal("str2float('nan')", string(acos(1.0/0.0))) @@ -113,6 +125,7 @@ endfunc func Test_cosh() call assert_equal('1.0', string(cosh(0.0))) call assert_equal('1.127626', string(cosh(0.5))) + eval 0.5->cosh()->string()->assert_equal('1.127626') call assert_equal("str2float('inf')", string(cosh(1.0/0.0))) call assert_equal("str2float('inf')", string(cosh(-1.0/0.0))) call assert_equal("str2float('nan')", string(cosh(0.0/0.0))) @@ -123,6 +136,7 @@ func Test_tan() call assert_equal('0.0', string(tan(0.0))) call assert_equal('0.546302', string(tan(0.5))) call assert_equal('-0.546302', string(tan(-0.5))) + eval -0.5->tan()->string()->assert_equal('-0.546302') call assert_equal("str2float('nan')", string(tan(1.0/0.0))) call assert_equal("str2float('nan')", string(cos(0.0/0.0))) call assert_equal('0.0', string(tan(1.0/(1.0/0.0)))) @@ -134,6 +148,7 @@ func Test_atan() call assert_equal('0.0', string(atan(0.0))) call assert_equal('0.463648', string(atan(0.5))) call assert_equal('-0.785398', string(atan(-1.0))) + eval -1.0->atan()->string()->assert_equal('-0.785398') call assert_equal('1.570796', string(atan(1.0/0.0))) call assert_equal('-1.570796', string(atan(-1.0/0.0))) call assert_equal("str2float('nan')", string(atan(0.0/0.0))) @@ -144,6 +159,7 @@ func Test_atan2() call assert_equal('-2.356194', string(atan2(-1, -1))) call assert_equal('2.356194', string(atan2(1, -1))) call assert_equal('0.0', string(atan2(1.0, 1.0/0.0))) + eval 1.0->atan2(1.0/0.0)->string()->assert_equal('0.0') call assert_equal('1.570796', string(atan2(1.0/0.0, 1.0))) call assert_equal("str2float('nan')", string(atan2(0.0/0.0, 1.0))) call assert_fails('call atan2("", -1)', 'E808:') @@ -154,6 +170,7 @@ func Test_tanh() call assert_equal('0.0', string(tanh(0.0))) call assert_equal('0.462117', string(tanh(0.5))) call assert_equal('-0.761594', string(tanh(-1.0))) + eval -1.0->tanh()->string()->assert_equal('-0.761594') call assert_equal('1.0', string(tanh(1.0/0.0))) call assert_equal('-1.0', string(tanh(-1.0/0.0))) call assert_equal("str2float('nan')", string(tanh(0.0/0.0))) @@ -164,6 +181,7 @@ func Test_fmod() call assert_equal('0.13', string(fmod(12.33, 1.22))) call assert_equal('-0.13', string(fmod(-12.33, 1.22))) call assert_equal("str2float('nan')", string(fmod(1.0/0.0, 1.0))) + eval (1.0/0.0)->fmod(1.0)->string()->assert_equal("str2float('nan')") " On Windows we get "nan" instead of 1.0, accept both. let res = string(fmod(1.0, 1.0/0.0)) if res != "str2float('nan')" @@ -177,6 +195,7 @@ endfunc func Test_pow() call assert_equal('1.0', string(pow(0.0, 0.0))) call assert_equal('8.0', string(pow(2.0, 3.0))) + eval 2.0->pow(3.0)->string()->assert_equal('8.0') call assert_equal("str2float('nan')", string(pow(2.0, 0.0/0.0))) call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0))) call assert_equal("str2float('nan')", string(pow(0.0/0.0, 3.0))) @@ -192,6 +211,7 @@ func Test_str2float() call assert_equal('1.0', string(str2float(' 1.0 '))) call assert_equal('1.23', string(str2float('1.23'))) call assert_equal('1.23', string(str2float('1.23abc'))) + eval '1.23abc'->str2float()->string()->assert_equal('1.23') call assert_equal('1.0e40', string(str2float('1e40'))) call assert_equal('-1.23', string(str2float('-1.23'))) call assert_equal('1.23', string(str2float(' + 1.23 '))) @@ -228,6 +248,7 @@ func Test_float2nr() call assert_equal(1, float2nr(1.234)) call assert_equal(123, float2nr(1.234e2)) call assert_equal(12, float2nr(123.4e-1)) + eval 123.4e-1->float2nr()->assert_equal(12) let max_number = 1/0 let min_number = -max_number call assert_equal(max_number/2+1, float2nr(pow(2, 62))) @@ -242,6 +263,7 @@ func Test_floor() call assert_equal('2.0', string(floor(2.0))) call assert_equal('2.0', string(floor(2.11))) call assert_equal('2.0', string(floor(2.99))) + eval 2.99->floor()->string()->assert_equal('2.0') call assert_equal('-3.0', string(floor(-2.11))) call assert_equal('-3.0', string(floor(-2.99))) call assert_equal("str2float('nan')", string(floor(0.0/0.0))) @@ -255,6 +277,7 @@ func Test_ceil() call assert_equal('3.0', string(ceil(2.11))) call assert_equal('3.0', string(ceil(2.99))) call assert_equal('-2.0', string(ceil(-2.11))) + eval -2.11->ceil()->string()->assert_equal('-2.0') call assert_equal('-2.0', string(ceil(-2.99))) call assert_equal("str2float('nan')", string(ceil(0.0/0.0))) call assert_equal("str2float('inf')", string(ceil(1.0/0.0))) @@ -266,6 +289,7 @@ func Test_round() call assert_equal('2.0', string(round(2.1))) call assert_equal('3.0', string(round(2.5))) call assert_equal('3.0', string(round(2.9))) + eval 2.9->round()->string()->assert_equal('3.0') call assert_equal('-2.0', string(round(-2.1))) call assert_equal('-3.0', string(round(-2.5))) call assert_equal('-3.0', string(round(-2.9))) @@ -279,6 +303,7 @@ func Test_trunc() call assert_equal('2.0', string(trunc(2.1))) call assert_equal('2.0', string(trunc(2.5))) call assert_equal('2.0', string(trunc(2.9))) + eval 2.9->trunc()->string()->assert_equal('2.0') call assert_equal('-2.0', string(trunc(-2.1))) call assert_equal('-2.0', string(trunc(-2.5))) call assert_equal('-2.0', string(trunc(-2.9))) @@ -291,6 +316,7 @@ endfunc func Test_isinf() call assert_equal(1, isinf(1.0/0.0)) call assert_equal(-1, isinf(-1.0/0.0)) + eval (-1.0/0.0)->isinf()->assert_equal(-1) call assert_false(isinf(1.0)) call assert_false(isinf(0.0/0.0)) call assert_false(isinf('a')) @@ -302,6 +328,7 @@ func Test_isnan() call assert_true(isnan(0.0/0.0)) call assert_false(isnan(1.0)) call assert_false(isnan(1.0/0.0)) + eval (1.0/0.0)->isnan()->assert_false() call assert_false(isnan(-1.0/0.0)) call assert_false(isnan('a')) call assert_false(isnan([])) diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index fcdf888b96..2cc5b47cb0 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -796,6 +796,26 @@ func Test_fold_delete_first_line() set foldmethod& endfunc +func Test_undo_fold_deletion() + new + set fdm=marker + let lines =<< trim END + " {{{ + " }}}1 + " {{{ + END + call setline(1, lines) + 3d + g/"/d + undo + redo + " eval getline(1, '$')->assert_equal(['']) + eval assert_equal(getline(1, '$'), ['']) + + set fdm&vim + bwipe! +endfunc + " this was crashing func Test_move_no_folds() new diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 224ca257ab..ed46730cbb 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -152,6 +152,10 @@ func Test_str2nr() call assert_equal(65, str2nr('0101', 8)) call assert_equal(-65, str2nr('-101', 8)) call assert_equal(-65, str2nr('-0101', 8)) + call assert_equal(65, str2nr('0o101', 8)) + call assert_equal(65, str2nr('0O0101', 8)) + call assert_equal(-65, str2nr('-0O101', 8)) + call assert_equal(-65, str2nr('-0o0101', 8)) call assert_equal(11259375, str2nr('abcdef', 16)) call assert_equal(11259375, str2nr('ABCDEF', 16)) @@ -161,8 +165,16 @@ func Test_str2nr() call assert_equal(11259375, str2nr('0XABCDEF', 16)) call assert_equal(-11259375, str2nr('-0xABCDEF', 16)) + call assert_equal(1, str2nr("1'000'000", 10, 0)) + call assert_equal(256, str2nr("1'0000'0000", 2, 1)) + call assert_equal(262144, str2nr("1'000'000", 8, 1)) + call assert_equal(1000000, str2nr("1'000'000", 10, 1)) + call assert_equal(1000, str2nr("1'000''000", 10, 1)) + call assert_equal(65536, str2nr("1'00'00", 16, 1)) + call assert_equal(0, str2nr('0x10')) call assert_equal(0, str2nr('0b10')) + call assert_equal(0, str2nr('0o10')) call assert_equal(1, str2nr('12', 2)) call assert_equal(1, str2nr('18', 8)) call assert_equal(1, str2nr('1g', 16)) @@ -534,6 +546,7 @@ func Test_mode() set complete=. inoremap <F2> <C-R>=Save_mode()<CR> + xnoremap <F2> <Cmd>call Save_mode()<CR> normal! 3G exe "normal i\<F2>\<Esc>" @@ -645,6 +658,14 @@ func Test_mode() call assert_equal("\<C-S>", mode(1)) call feedkeys("\<Esc>", 'xt') + " v_CTRL-O + exe "normal gh\<C-O>\<F2>\<Esc>" + call assert_equal("v-vs", g:current_modes) + exe "normal gH\<C-O>\<F2>\<Esc>" + call assert_equal("V-Vs", g:current_modes) + exe "normal g\<C-H>\<C-O>\<F2>\<Esc>" + call assert_equal("\<C-V>-\<C-V>s", g:current_modes) + call feedkeys(":echo \<C-R>=Save_mode()\<C-U>\<CR>", 'xt') call assert_equal('c-c', g:current_modes) call feedkeys("gQecho \<C-R>=Save_mode()\<CR>\<CR>vi\<CR>", 'xt') @@ -653,6 +674,7 @@ func Test_mode() bwipe! iunmap <F2> + xunmap <F2> set complete& endfunc @@ -842,7 +864,7 @@ func Test_byte2line_line2byte() set fileformat=mac call assert_equal([-1, -1, 1, 1, 2, 2, 2, 3, 3, -1], - \ map(range(-1, 8), 'byte2line(v:val)')) + \ map(range(-1, 8), 'v:val->byte2line()')) call assert_equal([-1, -1, 1, 3, 6, 8, -1], \ map(range(-1, 5), 'line2byte(v:val)')) @@ -865,6 +887,34 @@ func Test_byte2line_line2byte() bw! endfunc +func Test_byteidx() + let a = '.é.' " one char of two bytes + call assert_equal(0, byteidx(a, 0)) + call assert_equal(0, byteidxcomp(a, 0)) + call assert_equal(1, byteidx(a, 1)) + call assert_equal(1, byteidxcomp(a, 1)) + call assert_equal(3, byteidx(a, 2)) + call assert_equal(3, byteidxcomp(a, 2)) + call assert_equal(4, byteidx(a, 3)) + call assert_equal(4, byteidxcomp(a, 3)) + call assert_equal(-1, byteidx(a, 4)) + call assert_equal(-1, byteidxcomp(a, 4)) + + let b = '.eÌ.' " normal e with composing char + call assert_equal(0, b->byteidx(0)) + call assert_equal(1, b->byteidx(1)) + call assert_equal(4, b->byteidx(2)) + call assert_equal(5, b->byteidx(3)) + call assert_equal(-1, b->byteidx(4)) + + call assert_equal(0, b->byteidxcomp(0)) + call assert_equal(1, b->byteidxcomp(1)) + call assert_equal(2, b->byteidxcomp(2)) + call assert_equal(4, b->byteidxcomp(3)) + call assert_equal(5, b->byteidxcomp(4)) + call assert_equal(-1, b->byteidxcomp(5)) +endfunc + " Test for charidx() func Test_charidx() let a = 'xaÌbÌy' @@ -996,6 +1046,9 @@ func Test_Executable() if catcmd =~ '\<sbin\>' && result =~ '\<bin\>' call assert_equal('/' .. substitute(catcmd, '\<sbin\>', 'bin', ''), result) else + " /bin/cat and /usr/bin/cat may be hard linked, we could get either + let result = substitute(result, '/usr/bin/cat', '/bin/cat', '') + let catcmd = substitute(catcmd, 'usr/bin/cat', 'bin/cat', '') call assert_equal('/' .. catcmd, result) endif bwipe @@ -1052,7 +1105,7 @@ func Test_col() call assert_equal(7, col('$')) call assert_equal(4, col("'x")) call assert_equal(6, col("'Y")) - call assert_equal(2, col([1, 2])) + call assert_equal(2, [1, 2]->col()) call assert_equal(7, col([1, '$'])) call assert_equal(0, col('')) @@ -1315,7 +1368,15 @@ endfunc func Test_getchar() call feedkeys('a', '') call assert_equal(char2nr('a'), getchar()) + call assert_equal(0, getchar(0)) + call assert_equal(0, getchar(1)) + + call feedkeys('a', '') + call assert_equal('a', getcharstr()) + call assert_equal('', getcharstr(0)) + call assert_equal('', getcharstr(1)) + call setline(1, 'xxxx') " call test_setmouse(1, 3) " let v:mouse_win = 9 " let v:mouse_winid = 9 @@ -1328,6 +1389,7 @@ func Test_getchar() call assert_equal(win_getid(1), v:mouse_winid) call assert_equal(1, v:mouse_lnum) call assert_equal(3, v:mouse_col) + enew! endfunc func Test_libcall_libcallnr() @@ -1391,13 +1453,13 @@ func Test_bufadd_bufload() call assert_equal([''], getbufline(buf, 1, '$')) let curbuf = bufnr('') - call writefile(['some', 'text'], 'otherName') - let buf = bufadd('otherName') + call writefile(['some', 'text'], 'XotherName') + let buf = 'XotherName'->bufadd() call assert_notequal(0, buf) - call assert_equal(1, bufexists('otherName')) + eval 'XotherName'->bufexists()->assert_equal(1) call assert_equal(0, getbufvar(buf, '&buflisted')) call assert_equal(0, bufloaded(buf)) - call bufload(buf) + eval buf->bufload() call assert_equal(1, bufloaded(buf)) call assert_equal(['some', 'text'], getbufline(buf, 1, '$')) call assert_equal(curbuf, bufnr('')) @@ -1417,8 +1479,9 @@ func Test_bufadd_bufload() call assert_equal(0, bufexists(buf2)) bwipe someName - bwipe otherName + bwipe XotherName call assert_equal(0, bufexists('someName')) + call delete('XotherName') endfunc func Test_readdir() @@ -1451,6 +1514,20 @@ func Test_readdir() call delete('Xdir', 'rf') endfunc +func Test_call() + call assert_equal(3, call('len', [123])) + call assert_equal(3, 'len'->call([123])) + call assert_fails("call call('len', 123)", 'E714:') + call assert_equal(0, call('', [])) + + function Mylen() dict + return len(self.data) + endfunction + let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")} + eval mydict.len->call([], mydict)->assert_equal(4) + call assert_fails("call call('Mylen', [], 0)", 'E715:') +endfunc + " Test for the eval() function func Test_eval() call assert_fails("call eval('5 a')", 'E488:') diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim index ee548037ba..43efd6248e 100644 --- a/src/nvim/testdir/test_gf.vim +++ b/src/nvim/testdir/test_gf.vim @@ -145,7 +145,7 @@ func Test_gf_visual() bwipe! call delete('Xtest_gf_visual') - set hidden& + set nohidden endfunc func Test_gf_error() diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim index 2de2c412de..8edc9c2608 100644 --- a/src/nvim/testdir/test_global.vim +++ b/src/nvim/testdir/test_global.vim @@ -36,4 +36,8 @@ func Test_global_error() call assert_fails('g/\(/y', 'E476:') endfunc +func Test_wrong_delimiter() + call assert_fails('g x^bxd', 'E146:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_hide.vim b/src/nvim/testdir/test_hide.vim index 128b8ff945..41b1a4ad7c 100644 --- a/src/nvim/testdir/test_hide.vim +++ b/src/nvim/testdir/test_hide.vim @@ -37,7 +37,7 @@ function Test_hide() " :hide as a command hide call assert_equal([orig_bname, orig_winnr], [bufname(''), winnr('$')]) - call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')]) + call assert_equal([1, 1], ['Xf1'->buflisted(), 'Xf1'->bufloaded()]) bwipeout! Xf1 new Xf1 diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index 24c9c3580e..6fd9477ce9 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -426,6 +426,7 @@ func Test_highlight_eol_with_cursorline_breakindent() let [hiCursorLine, hi_ul, hi_bg] = HiCursorLine() call NewWindow('topleft 5', 10) + set showbreak=xxx setlocal breakindent breakindentopt=min:0,shift:1 showbreak=> call setline(1, ' ' . repeat('a', 9) . 'bcd') call matchadd('Search', '\n') @@ -483,6 +484,7 @@ func Test_highlight_eol_with_cursorline_breakindent() call CloseWindow() set showbreak= + setlocal showbreak= exe hiCursorLine endfunc diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 3da3648fec..ce75799551 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -95,7 +95,7 @@ func Test_ins_complete() call delete('Xtest11.one') call delete('Xtest11.two') call delete('Xtestdata') - set cpt& cot& def& tags& tagbsearch& hidden& + set cpt& cot& def& tags& tagbsearch& nohidden cd .. call delete('Xdir', 'rf') endfunc @@ -497,6 +497,133 @@ func Test_ins_compl_tag_sft() %bwipe! endfunc +" Test for completing special characters +func Test_complete_special_chars() + new + call setline(1, 'int .*[-\^$ func float') + call feedkeys("oin\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>", 'xt') + call assert_equal('int .*[-\^$ func float', getline(2)) + close! +endfunc + +" Test for completion when text is wrapped across lines. +func Test_complete_across_line() + new + call setline(1, ['red green blue', 'one two three']) + setlocal textwidth=20 + exe "normal 2G$a re\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>" + call assert_equal(['one two three red', 'green blue one'], getline(2, '$')) + close! +endfunc + +" Test for using CTRL-L to add one character when completing matching +func Test_complete_add_onechar() + new + call setline(1, ['wool', 'woodwork']) + call feedkeys("Gowoo\<C-P>\<C-P>\<C-P>\<C-L>f", 'xt') + call assert_equal('woof', getline(3)) + + " use 'ignorecase' and backspace to erase characters from the prefix string + " and then add letters using CTRL-L + %d + set ignorecase backspace=2 + setlocal complete=. + call setline(1, ['workhorse', 'workload']) + normal Go + exe "normal aWOR\<C-P>\<bs>\<bs>\<bs>\<bs>\<bs>\<bs>\<C-L>r\<C-L>\<C-L>" + call assert_equal('workh', getline(3)) + set ignorecase& backspace& + close! +endfunc + +" Test insert completion with 'cindent' (adjust the indent) +func Test_complete_with_cindent() + new + setlocal cindent + call setline(1, ['if (i == 1)', " j = 2;"]) + exe "normal Go{\<CR>i\<C-X>\<C-L>\<C-X>\<C-L>\<CR>}" + call assert_equal(['{', "\tif (i == 1)", "\t\tj = 2;", '}'], getline(3, '$')) + + %d + call setline(1, ['when while', '{', '']) + setlocal cinkeys+==while + exe "normal Giwh\<C-P> " + call assert_equal("\twhile ", getline('$')) + close! +endfunc + +" Test for <CTRL-X> <CTRL-V> completion. Complete commands and functions +func Test_complete_cmdline() + new + exe "normal icaddb\<C-X>\<C-V>" + call assert_equal('caddbuffer', getline(1)) + exe "normal ocall getqf\<C-X>\<C-V>" + call assert_equal('call getqflist(', getline(2)) + exe "normal oabcxyz(\<C-X>\<C-V>" + call assert_equal('abcxyz(', getline(3)) + com! -buffer TestCommand1 echo 'TestCommand1' + com! -buffer TestCommand2 echo 'TestCommand2' + write TestCommand1Test + write TestCommand2Test + " Test repeating <CTRL-X> <CTRL-V> and switching to another CTRL-X mode + exe "normal oT\<C-X>\<C-V>\<C-X>\<C-V>\<C-X>\<C-F>\<Esc>" + call assert_equal('TestCommand2Test', getline(4)) + call delete('TestCommand1Test') + call delete('TestCommand2Test') + delcom TestCommand1 + delcom TestCommand2 + close! +endfunc + +" Test for <CTRL-X> <CTRL-Z> stopping completion without changing the match +func Test_complete_stop() + new + func Save_mode1() + let g:mode1 = mode(1) + return '' + endfunc + func Save_mode2() + let g:mode2 = mode(1) + return '' + endfunc + inoremap <F1> <C-R>=Save_mode1()<CR> + inoremap <F2> <C-R>=Save_mode2()<CR> + call setline(1, ['aaa bbb ccc ']) + exe "normal A\<C-N>\<C-P>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>" + call assert_equal('ic', g:mode1) + call assert_equal('i', g:mode2) + call assert_equal('aaa bbb ccc ', getline(1)) + exe "normal A\<C-N>\<Down>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>" + call assert_equal('ic', g:mode1) + call assert_equal('i', g:mode2) + call assert_equal('aaa bbb ccc aaa', getline(1)) + set completeopt+=noselect + exe "normal A \<C-N>\<Down>\<Down>\<C-L>\<C-L>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>" + call assert_equal('ic', g:mode1) + call assert_equal('i', g:mode2) + call assert_equal('aaa bbb ccc aaa bb', getline(1)) + set completeopt& + exe "normal A d\<C-N>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>" + call assert_equal('ic', g:mode1) + call assert_equal('i', g:mode2) + call assert_equal('aaa bbb ccc aaa bb d', getline(1)) + com! -buffer TestCommand1 echo 'TestCommand1' + com! -buffer TestCommand2 echo 'TestCommand2' + exe "normal oT\<C-X>\<C-V>\<C-X>\<C-V>\<F1>\<C-X>\<C-Z>\<F2>\<Esc>" + call assert_equal('ic', g:mode1) + call assert_equal('i', g:mode2) + call assert_equal('TestCommand2', getline(2)) + delcom TestCommand1 + delcom TestCommand2 + unlet g:mode1 + unlet g:mode2 + iunmap <F1> + iunmap <F2> + delfunc Save_mode1 + delfunc Save_mode2 + close! +endfunc + " Test to ensure 'Scanning...' messages are not recorded in messages history func Test_z1_complete_no_history() new diff --git a/src/nvim/testdir/test_join.vim b/src/nvim/testdir/test_join.vim index ac6ef8f29f..ecb969d10a 100644 --- a/src/nvim/testdir/test_join.vim +++ b/src/nvim/testdir/test_join.vim @@ -51,7 +51,7 @@ func Test_join_marks() /^This line/;'}-join call assert_equal([0, 4, 11, 0], getpos("'[")) - call assert_equal([0, 4, 67, 0], getpos("']")) + call assert_equal([0, 4, 66, 0], getpos("']")) enew! endfunc diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index f026c8a55f..63bb4ae1ef 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -61,7 +61,7 @@ endfunction function Test_lambda_fails() call assert_equal(3, {a, b -> a + b}(1, 2)) - call assert_fails('echo {a, a -> a + a}(1, 2)', 'E15:') + call assert_fails('echo {a, a -> a + a}(1, 2)', 'E853:') call assert_fails('echo {a, b -> a + b)}(1, 2)', 'E15:') endfunc diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim index a5cbd8f6a6..6cb736a38a 100644 --- a/src/nvim/testdir/test_let.vim +++ b/src/nvim/testdir/test_let.vim @@ -126,11 +126,16 @@ endfunction func s:set_varg7(...) abort let b = a:000 - call add(b, 1) + let b += [1] endfunction func s:set_varg8(...) abort let b = a:000 + call add(b, 1) +endfunction + +func s:set_varg9(...) abort + let b = a:000 let b[0][0] = 1 endfunction @@ -142,7 +147,8 @@ func Test_let_varg_fail() call s:set_varg5([0]) call assert_fails('call s:set_varg6(1)', 'E742:') call assert_fails('call s:set_varg7(1)', 'E742:') - call s:set_varg8([0]) + call assert_fails('call s:set_varg8(1)', 'E742:') + call s:set_varg9([0]) endfunction func Test_let_utf8_environment() diff --git a/src/nvim/testdir/test_match.vim b/src/nvim/testdir/test_match.vim index 09448ca71b..505a052a19 100644 --- a/src/nvim/testdir/test_match.vim +++ b/src/nvim/testdir/test_match.vim @@ -157,7 +157,10 @@ func Test_match_error() endfunc func Test_matchadd_error() - call assert_fails("call matchadd('GroupDoesNotExist', 'X')", 'E28:') + call clearmatches() + " Nvim: not an error anymore: + call matchadd('GroupDoesNotExist', 'X') + call assert_equal([{'group': 'GroupDoesNotExist', 'pattern': 'X', 'priority': 10, 'id': 13}], getmatches()) call assert_fails("call matchadd('Search', '\\(')", 'E475:') call assert_fails("call matchadd('Search', 'XXX', 1, 123, 1)", 'E715:') call assert_fails("call matchadd('Error', 'XXX', 1, 3)", 'E798:') @@ -239,7 +242,7 @@ func Test_matchaddpos_otherwin() \] call assert_equal(expect, savematches) - call clearmatches(winid) + eval winid->clearmatches() call assert_equal([], getmatches(winid)) call setmatches(savematches, winid) diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim new file mode 100644 index 0000000000..7a6e6aa19d --- /dev/null +++ b/src/nvim/testdir/test_method.vim @@ -0,0 +1,159 @@ +" Tests for ->method() + +func Test_list_method() + let l = [1, 2, 3] + call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) + eval l->assert_equal(l) + eval l->assert_equal(l, 'wrong') + eval l->assert_notequal([3, 2, 1]) + eval l->assert_notequal([3, 2, 1], 'wrong') + call assert_equal(l, l->copy()) + call assert_equal(l, l->deepcopy()) + call assert_equal(1, l->count(2)) + call assert_false(l->empty()) + call assert_true([]->empty()) + call assert_equal(579, ['123', '+', '456']->join()->eval()) + call assert_equal([1, 2, 3, 4, 5], [1, 2, 3]->extend([4, 5])) + call assert_equal([1, 3], [1, 2, 3]->filter('v:val != 2')) + call assert_equal(2, l->get(1)) + call assert_equal(1, l->index(2)) + call assert_equal([0, 1, 2, 3], [1, 2, 3]->insert(0)) + call assert_fails('eval l->items()', 'E715:') + call assert_equal('1 2 3', l->join()) + call assert_fails('eval l->keys()', 'E715:') + call assert_equal(3, l->len()) + call assert_equal([2, 3, 4], [1, 2, 3]->map('v:val + 1')) + call assert_equal(3, l->max()) + call assert_equal(1, l->min()) + call assert_equal(2, [1, 2, 3]->remove(1)) + call assert_equal([1, 2, 3, 1, 2, 3], l->repeat(2)) + call assert_equal([3, 2, 1], [1, 2, 3]->reverse()) + call assert_equal([1, 2, 3, 4], [4, 2, 3, 1]->sort()) + call assert_equal('[1, 2, 3]', l->string()) + call assert_equal(v:t_list, l->type()) + call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq()) + call assert_fails('eval l->values()', 'E715:') +endfunc + +func Test_dict_method() + let d = #{one: 1, two: 2, three: 3} + + call assert_equal(d, d->copy()) + call assert_equal(d, d->deepcopy()) + call assert_equal(1, d->count(2)) + call assert_false(d->empty()) + call assert_true({}->empty()) + call assert_equal(#{one: 1, two: 2, three: 3, four: 4}, d->extend(#{four: 4})) + call assert_equal(#{one: 1, two: 2, three: 3}, d->filter('v:val != 4')) + call assert_equal(2, d->get('two')) + " Nvim doesn't support Blobs yet; expect a different emsg + " call assert_fails("let x = d->index(2)", 'E897:') + " call assert_fails("let x = d->insert(0)", 'E899:') + call assert_fails("let x = d->index(2)", 'E714:') + call assert_fails("let x = d->insert(0)", 'E686:') + call assert_true(d->has_key('two')) + call assert_equal([['one', 1], ['two', 2], ['three', 3]], d->items()) + call assert_fails("let x = d->join()", 'E714:') + call assert_equal(['one', 'two', 'three'], d->keys()) + call assert_equal(3, d->len()) + call assert_equal(#{one: 2, two: 3, three: 4}, d->map('v:val + 1')) + call assert_equal(#{one: 1, two: 2, three: 3}, d->map('v:val - 1')) + call assert_equal(3, d->max()) + call assert_equal(1, d->min()) + call assert_equal(2, d->remove("two")) + let d.two = 2 + call assert_fails('let x = d->repeat(2)', 'E731:') + " Nvim doesn't support Blobs yet; expect a different emsg + " call assert_fails('let x = d->reverse()', 'E899:') + call assert_fails('let x = d->reverse()', 'E686:') + call assert_fails('let x = d->sort()', 'E686:') + call assert_equal("{'one': 1, 'two': 2, 'three': 3}", d->string()) + call assert_equal(v:t_dict, d->type()) + call assert_fails('let x = d->uniq()', 'E686:') + call assert_equal([1, 2, 3], d->values()) +endfunc + +func Test_string_method() + eval '1 2 3'->split()->assert_equal(['1', '2', '3']) + eval '1 2 3'->split()->map({i, v -> str2nr(v)})->assert_equal([1, 2, 3]) + eval 'ABC'->str2list()->assert_equal([65, 66, 67]) + eval 'ABC'->strlen()->assert_equal(3) + eval "a\rb\ec"->strtrans()->assert_equal('a^Mb^[c') + eval "aã‚b"->strwidth()->assert_equal(4) + eval 'abc'->substitute('b', 'x', '')->assert_equal('axc') + + eval 'abc'->printf('the %s arg')->assert_equal('the abc arg') +endfunc + +func Test_method_append() + new + eval ['one', 'two', 'three']->append(1) + call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) + + %del + let bnr = bufnr('') + wincmd w + eval ['one', 'two', 'three']->appendbufline(bnr, 1) + call assert_equal(['', 'one', 'two', 'three'], getbufline(bnr, 1, '$')) + + exe 'bwipe! ' .. bnr +endfunc + +func Test_method_funcref() + func Concat(one, two, three) + return a:one .. a:two .. a:three + endfunc + let FuncRef = function('Concat') + eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail') + + " not enough arguments + call assert_fails("eval 'foo'->FuncRef('bar')", 'E119:') + " too many arguments + call assert_fails("eval 'foo'->FuncRef('bar', 'tail', 'four')", 'E118:') + + let Partial = function('Concat', ['two']) + eval 'one'->Partial('three')->assert_equal('onetwothree') + + " not enough arguments + call assert_fails("eval 'one'->Partial()", 'E119:') + " too many arguments + call assert_fails("eval 'one'->Partial('three', 'four')", 'E118:') + + delfunc Concat +endfunc + +func Test_method_float() + eval 1.234->string()->assert_equal('1.234') + eval -1.234->string()->assert_equal('-1.234') +endfunc + +func Test_method_syntax() + eval [1, 2, 3] ->sort( ) + eval [1, 2, 3] + \ ->sort( + \ ) + call assert_fails('eval [1, 2, 3]-> sort()', 'E260:') + call assert_fails('eval [1, 2, 3]->sort ()', 'E274:') + call assert_fails('eval [1, 2, 3]-> sort ()', 'E260:') +endfunc + +func Test_method_lambda() + eval "text"->{x -> x .. " extended"}()->assert_equal('text extended') + eval "text"->{x, y -> x .. " extended " .. y}('more')->assert_equal('text extended more') + + call assert_fails('eval "text"->{x -> x .. " extended"} ()', 'E274:') + + " todo: lambda accepts more arguments than it consumes + " call assert_fails('eval "text"->{x -> x .. " extended"}("more")', 'E99:') + + " Nvim doesn't include test_refcount(). + " let l = [1, 2, 3] + " eval l->{x -> x}() + " call assert_equal(1, test_refcount(l)) +endfunc + +func Test_method_not_supported() + call assert_fails('eval 123->changenr()', 'E276:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 4e46dbac16..c96c6a9678 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -170,7 +170,7 @@ func Test_mksession_rtp() return endif new - set sessionoptions&vi + set sessionoptions+=options let _rtp=&rtp " Make a real long (invalid) runtimepath value, " that should exceed PATH_MAX (hopefully) @@ -287,10 +287,33 @@ func Test_mksession_blank_windows() call delete('Xtest_mks.out') endfunc +func Test_mksession_buffer_count() + set hidden + + " Edit exactly three files in the current session. + %bwipe! + e Xfoo | tabe Xbar | tabe Xbaz + tabdo write + mksession! Xtest_mks.out + + " Verify that loading the session does not create additional buffers. + %bwipe! + source Xtest_mks.out + call assert_equal(3, len(getbufinfo())) + + " Clean up. + call delete('Xfoo') + call delete('Xbar') + call delete('Xbaz') + call delete('Xtest_mks.out') + %bwipe! + set nohidden +endfunc + if has('extra_search') func Test_mksession_hlsearch() - set sessionoptions&vi + set sessionoptions+=options set hlsearch mksession! Xtest_mks.out nohlsearch @@ -630,7 +653,7 @@ endfunc " Test for mksession with a named scratch buffer func Test_mksession_scratch() - set sessionoptions&vi + set sessionoptions+=options enew | only file Xscratch set buftype=nofile diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 4a00999c45..aff22f5d01 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1111,161 +1111,6 @@ func Test_normal18_z_fold() bw! endfunc -func Test_normal19_z_spell() - if !has("spell") || !has('syntax') - return - endif - new - call append(0, ['1 good', '2 goood', '3 goood']) - set spell spellfile=./Xspellfile.add spelllang=en - let oldlang=v:lang - lang C - - " Test for zg - 1 - norm! ]s - call assert_equal('2 goood', getline('.')) - norm! zg - 1 - let a=execute('unsilent :norm! ]s') - call assert_equal('1 good', getline('.')) - call assert_equal('search hit BOTTOM, continuing at TOP', a[1:]) - let cnt=readfile('./Xspellfile.add') - call assert_equal('goood', cnt[0]) - - " Test for zw - 2 - norm! $zw - 1 - norm! ]s - call assert_equal('2 goood', getline('.')) - let cnt=readfile('./Xspellfile.add') - call assert_equal('#oood', cnt[0]) - call assert_equal('goood/!', cnt[1]) - - " Test for zg in visual mode - let a=execute('unsilent :norm! V$zg') - call assert_equal("Word '2 goood' added to ./Xspellfile.add", a[1:]) - 1 - norm! ]s - call assert_equal('3 goood', getline('.')) - let cnt=readfile('./Xspellfile.add') - call assert_equal('2 goood', cnt[2]) - " Remove "2 good" from spellfile - 2 - let a=execute('unsilent norm! V$zw') - call assert_equal("Word '2 goood' added to ./Xspellfile.add", a[1:]) - let cnt=readfile('./Xspellfile.add') - call assert_equal('2 goood/!', cnt[3]) - - " Test for zG - let a=execute('unsilent norm! V$zG') - call assert_match("Word '2 goood' added to .*", a) - let fname=matchstr(a, 'to\s\+\zs\f\+$') - let fname=Fix_truncated_tmpfile(fname) - let cnt=readfile(fname) - call assert_equal('2 goood', cnt[0]) - - " Test for zW - let a=execute('unsilent norm! V$zW') - call assert_match("Word '2 goood' added to .*", a) - let cnt=readfile(fname) - call assert_equal('# goood', cnt[0]) - call assert_equal('2 goood/!', cnt[1]) - - " Test for zuW - let a=execute('unsilent norm! V$zuW') - call assert_match("Word '2 goood' removed from .*", a) - let cnt=readfile(fname) - call assert_equal('# goood', cnt[0]) - call assert_equal('# goood/!', cnt[1]) - - " Test for zuG - let a=execute('unsilent norm! $zG') - call assert_match("Word 'goood' added to .*", a) - let cnt=readfile(fname) - call assert_equal('# goood', cnt[0]) - call assert_equal('# goood/!', cnt[1]) - call assert_equal('goood', cnt[2]) - let a=execute('unsilent norm! $zuG') - let cnt=readfile(fname) - call assert_match("Word 'goood' removed from .*", a) - call assert_equal('# goood', cnt[0]) - call assert_equal('# goood/!', cnt[1]) - call assert_equal('#oood', cnt[2]) - " word not found in wordlist - let a=execute('unsilent norm! V$zuG') - let cnt=readfile(fname) - call assert_match("", a) - call assert_equal('# goood', cnt[0]) - call assert_equal('# goood/!', cnt[1]) - call assert_equal('#oood', cnt[2]) - - " Test for zug - call delete('./Xspellfile.add') - 2 - let a=execute('unsilent norm! $zg') - let cnt=readfile('./Xspellfile.add') - call assert_equal('goood', cnt[0]) - let a=execute('unsilent norm! $zug') - call assert_match("Word 'goood' removed from \./Xspellfile.add", a) - let cnt=readfile('./Xspellfile.add') - call assert_equal('#oood', cnt[0]) - " word not in wordlist - let a=execute('unsilent norm! V$zug') - call assert_match('', a) - let cnt=readfile('./Xspellfile.add') - call assert_equal('#oood', cnt[0]) - - " Test for zuw - call delete('./Xspellfile.add') - 2 - let a=execute('unsilent norm! Vzw') - let cnt=readfile('./Xspellfile.add') - call assert_equal('2 goood/!', cnt[0]) - let a=execute('unsilent norm! Vzuw') - call assert_match("Word '2 goood' removed from \./Xspellfile.add", a) - let cnt=readfile('./Xspellfile.add') - call assert_equal('# goood/!', cnt[0]) - " word not in wordlist - let a=execute('unsilent norm! $zug') - call assert_match('', a) - let cnt=readfile('./Xspellfile.add') - call assert_equal('# goood/!', cnt[0]) - - " add second entry to spellfile setting - set spellfile=./Xspellfile.add,./Xspellfile2.add - call delete('./Xspellfile.add') - 2 - let a=execute('unsilent norm! $2zg') - let cnt=readfile('./Xspellfile2.add') - call assert_match("Word 'goood' added to ./Xspellfile2.add", a) - call assert_equal('goood', cnt[0]) - - " Test for :spellgood! - let temp = execute(':spe!0/0') - call assert_match('Invalid region', temp) - let spellfile = matchstr(temp, 'Invalid region nr in \zs.*\ze line \d: 0') - call assert_equal(['# goood', '# goood/!', '#oood', '0/0'], readfile(spellfile)) - call delete(spellfile) - - " clean up - exe "lang" oldlang - call delete("./Xspellfile.add") - call delete("./Xspellfile2.add") - call delete("./Xspellfile.add.spl") - call delete("./Xspellfile2.add.spl") - - " zux -> no-op - 2 - norm! $zux - call assert_equal([], glob('Xspellfile.add',0,1)) - call assert_equal([], glob('Xspellfile2.add',0,1)) - - set spellfile= - bw! -endfunc - func Test_normal20_exmode() if !has("unix") " Reading from redirected file doesn't work on MS-Windows @@ -1382,7 +1227,7 @@ func Test_normal23_K() set iskeyword-=\| " Only expect "man" to work on Unix - if !has("unix") + if !has("unix") || has('nvim') " Nvim K uses :terminal. #15398 let &keywordprg = k bw! return diff --git a/src/nvim/testdir/test_number.vim b/src/nvim/testdir/test_number.vim index 81326bce14..92a1bf3c9a 100644 --- a/src/nvim/testdir/test_number.vim +++ b/src/nvim/testdir/test_number.vim @@ -1,7 +1,10 @@ " Test for 'number' and 'relativenumber' +source check.vim source view_util.vim +source screendump.vim + func s:screen_lines(start, end) abort return ScreenLines([a:start, a:end], 8) endfunc @@ -263,3 +266,58 @@ func Test_relativenumber_uninitialised() redraw bwipe! endfunc + +func Test_relativenumber_colors() + CheckScreendump + + let lines =<< trim [CODE] + call setline(1, range(200)) + 111 + set number relativenumber + hi LineNr ctermfg=red + [CODE] + call writefile(lines, 'XTest_relnr') + + " Check that the balloon shows up after a mouse move + let buf = RunVimInTerminal('-S XTest_relnr', {'rows': 10, 'cols': 50}) + call term_wait(buf, 100) + " Default colors + call VerifyScreenDump(buf, 'Test_relnr_colors_1', {}) + + call term_sendkeys(buf, ":hi LineNrAbove ctermfg=blue\<CR>") + call VerifyScreenDump(buf, 'Test_relnr_colors_2', {}) + + call term_sendkeys(buf, ":hi LineNrBelow ctermfg=green\<CR>") + call VerifyScreenDump(buf, 'Test_relnr_colors_3', {}) + + call term_sendkeys(buf, ":hi clear LineNrAbove\<CR>") + call VerifyScreenDump(buf, 'Test_relnr_colors_4', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_relnr') +endfunc + +" Test for displaying line numbers with 'rightleft' +func Test_number_rightleft() + CheckFeature rightleft + new + setlocal number + setlocal rightleft + call setline(1, range(1, 1000)) + normal! 9Gzt + redraw! + call assert_match('^\s\+9 9$', Screenline(1)) + normal! 10Gzt + redraw! + call assert_match('^\s\+01 10$', Screenline(1)) + normal! 100Gzt + redraw! + call assert_match('^\s\+001 100$', Screenline(1)) + normal! 1000Gzt + redraw! + call assert_match('^\s\+0001 1000$', Screenline(1)) + bw! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 8796af7a20..72c151142d 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -539,7 +539,7 @@ func Test_copy_winopt() call assert_equal(4,&numberwidth) bw! - set hidden& + set nohidden endfunc func Test_shortmess_F() diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 06bdb1236a..710450293c 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -250,7 +250,7 @@ endfunc func Test_noinsert_complete() func! s:complTest1() abort - call complete(1, ['source', 'soundfold']) + eval ['source', 'soundfold']->complete(1) return '' endfunc @@ -403,7 +403,7 @@ func DummyCompleteFour(findstart, base) return 0 else call complete_add('four1') - call complete_add('four2') + eval 'four2'->complete_add() call complete_check() call complete_add('four3') call complete_add('four4') @@ -989,7 +989,7 @@ func GetCompleteInfo() if empty(g:compl_what) let g:compl_info = complete_info() else - let g:compl_info = complete_info(g:compl_what) + let g:compl_info = g:compl_what->complete_info() endif return '' endfunc diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index bf15f7f52b..283e7bbafe 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -134,6 +134,21 @@ func XlistTests(cchar) call assert_equal([' 2 Xtestfile1:1 col 3: Line1', \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l) + " Ranged entries + call g:Xsetlist([{'lnum':10,'text':'Line1'}, + \ {'lnum':20,'col':10,'text':'Line2'}, + \ {'lnum':30,'col':15,'end_col':20,'text':'Line3'}, + \ {'lnum':40,'end_lnum':45,'text':'Line4'}, + \ {'lnum':50,'end_lnum':55,'col':15,'text':'Line5'}, + \ {'lnum':60,'end_lnum':65,'col':25,'end_col':35,'text':'Line6'}]) + let l = split(execute('Xlist', ""), "\n") + call assert_equal([' 1:10: Line1', + \ ' 2:20 col 10: Line2', + \ ' 3:30 col 15-20: Line3', + \ ' 4:40-45: Line4', + \ ' 5:50-55 col 15: Line5', + \ ' 6:60-65 col 25-35: Line6'], l) + " Different types of errors call g:Xsetlist([{'lnum':10,'col':5,'type':'W', 'text':'Warning','nr':11}, \ {'lnum':20,'col':10,'type':'e','text':'Error','nr':22}, @@ -599,6 +614,7 @@ func s:test_xhelpgrep(cchar) call assert_true(&buftype == 'help') call assert_true(winnr() == 1) call assert_true(winnr('$') == 2) + call assert_match('|\d\+ col \d\+-\d\+|', getbufline(winbufnr(2), 1)[0]) " This wipes out the buffer, make sure that doesn't cause trouble. Xclose @@ -891,7 +907,7 @@ func Test_efm1() Xtestfile:9: parse error before `asd' make: *** [vim] Error 1 in file "Xtestfile" linenr 10: there is an error - + 2 returned "Xtestfile", line 11 col 1; this is an error "Xtestfile", line 12 col 2; this is another error @@ -914,7 +930,7 @@ func Test_efm1() x should be a dot xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 20 ^ - + Does anyone know what is the problem and how to correction it? "Xtestfile", line 21 col 9: What is the title of the quickfix window? "Xtestfile", line 22 col 9: What is the title of the quickfix window? @@ -1437,10 +1453,13 @@ func SetXlistTests(cchar, bnum) call s:setup_commands(a:cchar) call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 1}, - \ {'bufnr': a:bnum, 'lnum': 2}]) + \ {'bufnr': a:bnum, 'lnum': 2, 'end_lnum': 3, 'col': 4, 'end_col': 5}]) let l = g:Xgetlist() call assert_equal(2, len(l)) call assert_equal(2, l[1].lnum) + call assert_equal(3, l[1].end_lnum) + call assert_equal(4, l[1].col) + call assert_equal(5, l[1].end_col) Xnext call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 3}], 'a') @@ -1892,7 +1911,7 @@ func Test_switchbuf() enew | only set switchbuf=useopen cexpr "Xqftestfile1:1:10" - call assert_equal('', &switchbuf) + call assert_equal('uselast', &switchbuf) call delete('Xqftestfile1') call delete('Xqftestfile2') @@ -2692,6 +2711,28 @@ func Test_cwindow_jump() set efm&vim endfunc +func Test_cwindow_highlight() + CheckScreendump + + let lines =<< trim END + call setline(1, ['some', 'text', 'with', 'matches']) + write XCwindow + vimgrep e XCwindow + redraw + cwindow 4 + END + call writefile(lines, 'XtestCwindow') + let buf = RunVimInTerminal('-S XtestCwindow', #{rows: 12}) + call VerifyScreenDump(buf, 'Test_quickfix_cwindow_1', {}) + call term_sendkeys(buf, ":cnext\<CR>") + call VerifyScreenDump(buf, 'Test_quickfix_cwindow_2', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XtestCwindow') + call delete('XCwindow') +endfunc + func XvimgrepTests(cchar) call s:setup_commands(a:cchar) @@ -2721,7 +2762,9 @@ func XvimgrepTests(cchar) let l = g:Xgetlist() call assert_equal(2, len(l)) call assert_equal(8, l[0].col) + call assert_equal(11, l[0].end_col) call assert_equal(12, l[1].col) + call assert_equal(15, l[1].end_col) 1Xvimgrep ?Editor? Xtestfile* let l = g:Xgetlist() @@ -2943,7 +2986,7 @@ func Test_cclose_in_autocmd() " call test_override('starting', 0) endfunc -" Check that ":file" without an argument is possible even when "curbuf_lock" +" Check that ":file" without an argument is possible even when curbuf is locked " is set. func Test_file_from_copen() " Works without argument. @@ -3461,12 +3504,13 @@ func Xgetlist_empty_tests(cchar) if a:cchar == 'c' call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, \ 'items' : [], 'nr' : 0, 'size' : 0, - \ 'title' : '', 'winid' : 0, 'changedtick': 0}, - \ g:Xgetlist({'all' : 0})) + \ 'title' : '', 'winid' : 0, 'changedtick': 0, + \ 'quickfixtextfunc' : ''}, g:Xgetlist({'all' : 0})) else call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, \ 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', - \ 'winid' : 0, 'changedtick': 0, 'filewinid' : 0}, + \ 'winid' : 0, 'changedtick': 0, 'filewinid' : 0, + \ 'quickfixtextfunc' : ''}, \ g:Xgetlist({'all' : 0})) endif @@ -3504,11 +3548,13 @@ func Xgetlist_empty_tests(cchar) if a:cchar == 'c' call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, + \ 'quickfixtextfunc' : '', \ 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0})) else call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, - \ 'changedtick' : 0, 'filewinid' : 0}, + \ 'changedtick' : 0, 'filewinid' : 0, + \ 'quickfixtextfunc' : ''}, \ g:Xgetlist({'id' : qfid, 'all' : 0})) endif @@ -3525,12 +3571,13 @@ func Xgetlist_empty_tests(cchar) if a:cchar == 'c' call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, - \ 'changedtick' : 0}, g:Xgetlist({'nr' : 5, 'all' : 0})) + \ 'changedtick' : 0, + \ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0})) else call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, - \ 'changedtick' : 0, 'filewinid' : 0}, - \ g:Xgetlist({'nr' : 5, 'all' : 0})) + \ 'changedtick' : 0, 'filewinid' : 0, + \ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0})) endif endfunc @@ -4824,7 +4871,42 @@ func Test_add_invalid_entry_with_qf_window() call setqflist(['bb'], 'a') call assert_equal(1, line('$')) call assert_equal(['Xfile1|10| aa'], getline(1, '$')) - call assert_equal([{'lnum': 10, 'bufnr': bufnr('Xfile1'), 'col': 0, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'module': '', 'text': 'aa'}], getqflist()) + call assert_equal([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10 col 666| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10 col 666| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10 col 666-222| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': 6 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10-6 col 666-222| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': 6 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) cclose endfunc @@ -4972,15 +5054,24 @@ func Xtest_qftextfunc(cchar) set efm=%f:%l:%c:%m set quickfixtextfunc=Tqfexpr - Xexpr ['F1:10:2:green', 'F1:20:4:blue'] + call assert_equal('Tqfexpr', &quickfixtextfunc) + call assert_equal('', + \ g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc) + call g:Xsetlist([ + \ { 'filename': 'F1', 'lnum': 10, 'col': 2, + \ 'end_col': 7, 'text': 'green'}, + \ { 'filename': 'F1', 'lnum': 20, 'end_lnum': 25, 'col': 4, + \ 'end_col': 8, 'text': 'blue'}, + \ ]) + Xwindow call assert_equal('F1-L10C2-green', getline(1)) call assert_equal('F1-L20C4-blue', getline(2)) Xclose set quickfixtextfunc&vim Xwindow - call assert_equal('F1|10 col 2| green', getline(1)) - call assert_equal('F1|20 col 4| blue', getline(2)) + call assert_equal('F1|10 col 2-7| green', getline(1)) + call assert_equal('F1|20-25 col 4-8| blue', getline(2)) Xclose set efm& set quickfixtextfunc& @@ -5008,12 +5099,15 @@ func Xtest_qftextfunc(cchar) call assert_equal('Line 10, Col 2', getline(1)) call assert_equal('Line 20, Col 4', getline(2)) Xclose + call assert_equal(function('PerQfText'), + \ g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc) " Add entries to the list when the quickfix buffer is hidden Xaddexpr ['F1:30:6:red'] Xwindow call assert_equal('Line 30, Col 6', getline(3)) Xclose call g:Xsetlist([], 'r', {'quickfixtextfunc' : ''}) + call assert_equal('', g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc) set quickfixtextfunc& delfunc PerQfText @@ -5052,12 +5146,53 @@ func Xtest_qftextfunc(cchar) " \ 'E730:') Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red'] call assert_fails('Xwindow', 'E730:') - call assert_equal(['one', 'F1|20 col 4| blue', 'two'], getline(1, '$')) + call assert_equal(['one', 'F1|20 col 4| blue', 'F1|30 col 6| red'], + \ getline(1, '$')) Xclose set quickfixtextfunc& delfunc Xqftext delfunc Xqftext2 + + " set the global option to a lambda function + set quickfixtextfunc={d\ ->\ map(g:Xgetlist({'id'\ :\ d.id,\ 'items'\ :\ 1}).items[d.start_idx-1:d.end_idx-1],\ 'v:val.text')} + Xexpr ['F1:10:2:green', 'F1:20:4:blue'] + Xwindow + call assert_equal(['green', 'blue'], getline(1, '$')) + Xclose + call assert_equal("{d -> map(g:Xgetlist({'id' : d.id, 'items' : 1}).items[d.start_idx-1:d.end_idx-1], 'v:val.text')}", &quickfixtextfunc) + set quickfixtextfunc& + + " use a lambda function that returns an empty list + set quickfixtextfunc={d\ ->\ []} + Xexpr ['F1:10:2:green', 'F1:20:4:blue'] + Xwindow + call assert_equal(['F1|10 col 2| green', 'F1|20 col 4| blue'], + \ getline(1, '$')) + Xclose + set quickfixtextfunc& + + " use a lambda function that returns a list with empty strings + set quickfixtextfunc={d\ ->\ ['',\ '']} + Xexpr ['F1:10:2:green', 'F1:20:4:blue'] + Xwindow + call assert_equal(['F1|10 col 2| green', 'F1|20 col 4| blue'], + \ getline(1, '$')) + Xclose + set quickfixtextfunc& + + " set the per-quickfix list text function to a lambda function + call g:Xsetlist([], ' ', + \ {'quickfixtextfunc' : + \ {d -> map(g:Xgetlist({'id' : d.id, 'items' : 1}).items[d.start_idx-1:d.end_idx-1], + \ "'Line ' .. v:val.lnum .. ', Col ' .. v:val.col")}}) + Xaddexpr ['F1:10:2:green', 'F1:20:4:blue'] + Xwindow + call assert_equal('Line 10, Col 2', getline(1)) + call assert_equal('Line 20, Col 4', getline(2)) + Xclose + call assert_match("function('<lambda>\\d\\+')", string(g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc)) + call g:Xsetlist([], 'f') endfunc func Test_qftextfunc() @@ -5065,4 +5200,112 @@ func Test_qftextfunc() call Xtest_qftextfunc('l') endfunc +" Test for updating a location list for some other window and check that +" 'qftextfunc' uses the correct location list. +func Test_qftextfunc_other_loclist() + %bw! + call setloclist(0, [], 'f') + + " create a window and a location list for it and open the location list + " window + lexpr ['F1:10:12:one', 'F1:20:14:two'] + let w1_id = win_getid() + call setloclist(0, [], ' ', + \ {'lines': ['F1:10:12:one', 'F1:20:14:two'], + \ 'quickfixtextfunc': + \ {d -> map(getloclist(d.winid, {'id' : d.id, + \ 'items' : 1}).items[d.start_idx-1:d.end_idx-1], + \ "'Line ' .. v:val.lnum .. ', Col ' .. v:val.col")}}) + lwindow + let w2_id = win_getid() + + " create another window and a location list for it and open the location + " list window + topleft new + let w3_id = win_getid() + call setloclist(0, [], ' ', + \ {'lines': ['F2:30:32:eleven', 'F2:40:34:twelve'], + \ 'quickfixtextfunc': + \ {d -> map(getloclist(d.winid, {'id' : d.id, + \ 'items' : 1}).items[d.start_idx-1:d.end_idx-1], + \ "'Ligne ' .. v:val.lnum .. ', Colonne ' .. v:val.col")}}) + lwindow + let w4_id = win_getid() + + topleft new + lexpr ['F3:50:52:green', 'F3:60:54:blue'] + let w5_id = win_getid() + + " change the location list for some other window + call setloclist(0, [], 'r', {'lines': ['F3:55:56:aaa', 'F3:57:58:bbb']}) + call setloclist(w1_id, [], 'r', {'lines': ['F1:62:63:bbb', 'F1:64:65:ccc']}) + call setloclist(w3_id, [], 'r', {'lines': ['F2:76:77:ddd', 'F2:78:79:eee']}) + call assert_equal(['Line 62, Col 63', 'Line 64, Col 65'], + \ getbufline(winbufnr(w2_id), 1, '$')) + call assert_equal(['Ligne 76, Colonne 77', 'Ligne 78, Colonne 79'], + \ getbufline(winbufnr(w4_id), 1, '$')) + call setloclist(w2_id, [], 'r', {'lines': ['F1:32:33:fff', 'F1:34:35:ggg']}) + call setloclist(w4_id, [], 'r', {'lines': ['F2:46:47:hhh', 'F2:48:49:jjj']}) + call assert_equal(['Line 32, Col 33', 'Line 34, Col 35'], + \ getbufline(winbufnr(w2_id), 1, '$')) + call assert_equal(['Ligne 46, Colonne 47', 'Ligne 48, Colonne 49'], + \ getbufline(winbufnr(w4_id), 1, '$')) + + call win_gotoid(w5_id) + lwindow + call assert_equal(['F3|55 col 56| aaa', 'F3|57 col 58| bbb'], + \ getline(1, '$')) + %bw! +endfunc + +func Test_locationlist_open_in_newtab() + call s:create_test_file('Xqftestfile1') + call s:create_test_file('Xqftestfile2') + call s:create_test_file('Xqftestfile3') + + %bwipe! + + lgetexpr ['Xqftestfile1:5:Line5', + \ 'Xqftestfile2:10:Line10', + \ 'Xqftestfile3:16:Line16'] + + silent! llast + call assert_equal(1, tabpagenr('$')) + call assert_equal('Xqftestfile3', bufname()) + + set switchbuf=newtab + + silent! lfirst + call assert_equal(2, tabpagenr('$')) + call assert_equal('Xqftestfile1', bufname()) + + silent! lnext + call assert_equal(3, tabpagenr('$')) + call assert_equal('Xqftestfile2', bufname()) + + call delete('Xqftestfile1') + call delete('Xqftestfile2') + call delete('Xqftestfile3') + set switchbuf&vim + + %bwipe! +endfunc + +" Test for win_gettype() in quickfix and location list windows +func Test_win_gettype() + copen + call assert_equal("quickfix", win_gettype()) + let wid = win_getid() + wincmd p + call assert_equal("quickfix", win_gettype(wid)) + cclose + lexpr '' + lopen + call assert_equal("loclist", win_gettype()) + let wid = win_getid() + wincmd p + call assert_equal("loclist", win_gettype(wid)) + lclose +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index ab8a998bb8..e525d06ea2 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -106,11 +106,14 @@ foobar/? set spelllang=Xwords.spl call assert_equal(['foobar', 'rare'], spellbadword('foo foobar')) - " Typo should not be detected without the 'spell' option. + " Typo should be detected even without the 'spell' option. set spelllang=en_gb nospell call assert_equal(['', ''], spellbadword('centre')) - call assert_equal(['', ''], spellbadword('My bycycle.')) - call assert_equal(['', ''], spellbadword('A sentence. another sentence')) + call assert_equal(['bycycle', 'bad'], spellbadword('My bycycle.')) + call assert_equal(['another', 'caps'], spellbadword('A sentence. another sentence')) + + set spelllang= + call assert_fails("call spellbadword('maxch')", 'E756:') call delete('Xwords.spl') call delete('Xwords') @@ -172,6 +175,183 @@ func Test_spellreall() bwipe! endfunc +" Test spellsuggest({word} [, {max} [, {capital}]]) +func Test_spellsuggest() + " Verify suggestions are given even when spell checking is not enabled. + set nospell + call assert_equal(['march', 'March'], spellsuggest('marrch', 2)) + + set spell + + " With 1 argument. + call assert_equal(['march', 'March'], spellsuggest('marrch')[0:1]) + + " With 2 arguments. + call assert_equal(['march', 'March'], spellsuggest('marrch', 2)) + + " With 3 arguments. + call assert_equal(['march'], spellsuggest('marrch', 1, 0)) + call assert_equal(['March'], spellsuggest('marrch', 1, 1)) + + " Test with digits and hyphen. + call assert_equal('Carbon-14', spellsuggest('Carbon-15')[0]) + + " Comment taken from spellsuggest.c explains the following test cases: + " + " If there are more UPPER than lower case letters suggest an + " ALLCAP word. Otherwise, if the first letter is UPPER then + " suggest ONECAP. Exception: "ALl" most likely should be "All", + " require three upper case letters. + call assert_equal(['THIRD', 'third'], spellsuggest('thIRD', 2)) + call assert_equal(['third', 'THIRD'], spellsuggest('tHIrd', 2)) + call assert_equal(['Third'], spellsuggest('THird', 1)) + call assert_equal(['All'], spellsuggest('ALl', 1)) + + call assert_fails("call spellsuggest('maxch', [])", 'E745:') + call assert_fails("call spellsuggest('maxch', 2, [])", 'E745:') + + set spelllang= + call assert_fails("call spellsuggest('maxch')", 'E756:') + set spelllang& + + set spell& +endfunc + +" Test 'spellsuggest' option with methods fast, best and double. +func Test_spellsuggest_option_methods() + set spell + + for e in ['utf-8'] + exe 'set encoding=' .. e + + set spellsuggest=fast + call assert_equal(['Stick', 'Stitch'], spellsuggest('Stich', 2), e) + + " With best or double option, "Stitch" should become the top suggestion + " because of better phonetic matching. + set spellsuggest=best + call assert_equal(['Stitch', 'Stick'], spellsuggest('Stich', 2), e) + + set spellsuggest=double + call assert_equal(['Stitch', 'Stick'], spellsuggest('Stich', 2), e) + endfor + + set spell& spellsuggest& encoding& +endfunc + +" Test 'spellsuggest' option with value file:{filename} +func Test_spellsuggest_option_file() + set spell spellsuggest=file:Xspellsuggest + call writefile(['emacs/vim', + \ 'theribal/terrible', + \ 'teribal/terrrible', + \ 'terribal'], + \ 'Xspellsuggest') + + call assert_equal(['vim'], spellsuggest('emacs', 2)) + call assert_equal(['terrible'], spellsuggest('theribal',2)) + + " If the suggestion is misspelled (*terrrible* with 3 r), + " it should not be proposed. + " The entry for "terribal" should be ignored because of missing slash. + call assert_equal([], spellsuggest('teribal', 2)) + call assert_equal([], spellsuggest('terribal', 2)) + + set spell spellsuggest=best,file:Xspellsuggest + call assert_equal(['vim', 'Emacs'], spellsuggest('emacs', 2)) + call assert_equal(['terrible', 'tribal'], spellsuggest('theribal', 2)) + call assert_equal(['tribal'], spellsuggest('teribal', 1)) + call assert_equal(['tribal'], spellsuggest('terribal', 1)) + + call delete('Xspellsuggest') + call assert_fails("call spellsuggest('vim')", "E484: Can't open file Xspellsuggest") + + set spellsuggest& spell& +endfunc + +" Test 'spellsuggest' option with value {number} +" to limit the number of suggestions +func Test_spellsuggest_option_number() + set spell spellsuggest=2,best + new + + " We limited the number of suggestions to 2, so selecting + " the 1st and 2nd suggestion should correct the word, but + " selecting a 3rd suggestion should do nothing. + call setline(1, 'A baord') + norm $1z= + call assert_equal('A board', getline(1)) + + call setline(1, 'A baord') + norm $2z= + call assert_equal('A bard', getline(1)) + + call setline(1, 'A baord') + norm $3z= + call assert_equal('A baord', getline(1)) + + let a = execute('norm $z=') + call assert_equal( + \ "\n" + \ .. "Change \"baord\" to:\n" + \ .. " 1 \"board\"\n" + \ .. " 2 \"bard\"\n" + \ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a) + + set spell spellsuggest=0 + call assert_equal("\nSorry, no suggestions", execute('norm $z=')) + + " Unlike z=, function spellsuggest(...) should not be affected by the + " max number of suggestions (2) set by the 'spellsuggest' option. + call assert_equal(['board', 'bard', 'broad'], spellsuggest('baord', 3)) + + set spellsuggest& spell& + bwipe! +endfunc + +" Test 'spellsuggest' option with value expr:{expr} +func Test_spellsuggest_option_expr() + " A silly 'spellsuggest' function which makes suggestions all uppercase + " and makes the score of each suggestion the length of the suggested word. + " So shorter suggestions are preferred. + func MySuggest() + let spellsuggest_save = &spellsuggest + set spellsuggest=3,best + let result = map(spellsuggest(v:val, 3), "[toupper(v:val), len(v:val)]") + let &spellsuggest = spellsuggest_save + return result + endfunc + + set spell spellsuggest=expr:MySuggest() + call assert_equal(['BARD', 'BOARD', 'BROAD'], spellsuggest('baord', 3)) + + new + call setline(1, 'baord') + let a = execute('norm z=') + call assert_equal( + \ "\n" + \ .. "Change \"baord\" to:\n" + \ .. " 1 \"BARD\"\n" + \ .. " 2 \"BOARD\"\n" + \ .. " 3 \"BROAD\"\n" + \ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a) + + " With verbose, z= should show the score i.e. word length with + " our SpellSuggest() function. + set verbose=1 + let a = execute('norm z=') + call assert_equal( + \ "\n" + \ .. "Change \"baord\" to:\n" + \ .. " 1 \"BARD\" (4 - 0)\n" + \ .. " 2 \"BOARD\" (5 - 0)\n" + \ .. " 3 \"BROAD\" (5 - 0)\n" + \ .. "Type number and <Enter> or click with the mouse (q or empty cancels): ", a) + + set spell& spellsuggest& verbose& + bwipe! +endfunc + func Test_spellinfo() throw 'skipped: Nvim does not support enc=latin1' new @@ -227,7 +407,7 @@ func Test_zz_basic() \ ) call assert_equal("gebletegek", soundfold('goobledygoook')) - call assert_equal("kepereneven", soundfold('kóopërÿnôven')) + call assert_equal("kepereneven", soundfold('kóopërÿnôven')) call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale')) endfunc @@ -408,7 +588,7 @@ func Test_zz_sal_and_addition() mkspell! Xtest Xtest set spl=Xtest.latin1.spl spell call assert_equal('kbltykk', soundfold('goobledygoook')) - call assert_equal('kprnfn', soundfold('kóopërÿnôven')) + call assert_equal('kprnfn', soundfold('kóopërÿnôven')) call assert_equal('*fls kswts tl', soundfold('oeverloos gezwets edale')) "also use an addition file @@ -461,6 +641,34 @@ func Test_zeq_crash() bwipe! endfunc +" Check that z= works even when 'nospell' is set. This test uses one of the +" tests in Test_spellsuggest_option_number() just to verify that z= basically +" works and that "E756: Spell checking is not enabled" is not generated. +func Test_zeq_nospell() + new + set nospell spellsuggest=1,best + call setline(1, 'A baord') + try + norm $1z= + call assert_equal('A board', getline(1)) + catch + call assert_report("Caught exception: " . v:exception) + endtry + set spell& spellsuggest& + bwipe! +endfunc + +" Check that "E756: Spell checking is not possible" is reported when z= is +" executed and 'spelllang' is empty. +func Test_zeq_no_spelllang() + new + set spelllang= spellsuggest=1,best + call setline(1, 'A baord') + call assert_fails('normal $1z=', 'E756:') + set spelllang& spellsuggest& + bwipe! +endfunc + " Check handling a word longer than MAXWLEN. func Test_spell_long_word() set enc=utf-8 diff --git a/src/nvim/testdir/test_spell_utf8.vim b/src/nvim/testdir/test_spell_utf8.vim new file mode 100644 index 0000000000..cafdb97f28 --- /dev/null +++ b/src/nvim/testdir/test_spell_utf8.vim @@ -0,0 +1,773 @@ +" Test for spell checking with 'encoding' set to utf-8 + +source check.vim +CheckFeature spell + +scriptencoding utf-8 + +func TearDown() + set nospell + call delete('Xtest.aff') + call delete('Xtest.dic') + call delete('Xtest.utf-8.add') + call delete('Xtest.utf-8.add.spl') + call delete('Xtest.utf-8.spl') + call delete('Xtest.utf-8.sug') +endfunc + +let g:test_data_aff1 = [ + \"SET ISO8859-1", + \"TRY esianrtolcdugmphbyfvkwjkqxz-ëéèêïîäà âöüû'ESIANRTOLCDUGMPHBYFVKWJKQXZ", + \"", + \"FOL à áâãäåæçèéêëìÃîïðñòóôõöøùúûüýþßÿ", + \"LOW à áâãäåæçèéêëìÃîïðñòóôõöøùúûüýþßÿ", + \"UPP ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖØÙÚÛÜÃÞßÿ", + \"", + \"SOFOFROM abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xBF", + \"SOFOTO ebctefghejklnnepkrstevvkesebctefghejklnnepkrstevvkeseeeeeeeceeeeeeeedneeeeeeeeeeepseeeeeeeeceeeeeeeedneeeeeeeeeeep?", + \"", + \"MIDWORD\t'-", + \"", + \"KEP =", + \"RAR ?", + \"BAD !", + \"", + \"PFX I N 1", + \"PFX I 0 in .", + \"", + \"PFX O Y 1", + \"PFX O 0 out .", + \"", + \"SFX S Y 2", + \"SFX S 0 s [^s]", + \"SFX S 0 es s", + \"", + \"SFX N N 3", + \"SFX N 0 en [^n]", + \"SFX N 0 nen n", + \"SFX N 0 n .", + \"", + \"REP 3", + \"REP g ch", + \"REP ch g", + \"REP svp s.v.p.", + \"", + \"MAP 9", + \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5", + \"MAP e\xE8\xE9\xEA\xEB", + \"MAP i\xEC\xED\xEE\xEF", + \"MAP o\xF2\xF3\xF4\xF5\xF6", + \"MAP u\xF9\xFA\xFB\xFC", + \"MAP n\xF1", + \"MAP c\xE7", + \"MAP y\xFF\xFD", + \"MAP s\xDF" + \ ] +let g:test_data_dic1 = [ + \"123456", + \"test/NO", + \"# comment", + \"wrong", + \"Comment", + \"OK", + \"uk", + \"put/ISO", + \"the end", + \"deol", + \"d\xE9\xF4r", + \ ] +let g:test_data_aff2 = [ + \"SET ISO8859-1", + \"", + \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF", + \"", + \"PFXPOSTPONE", + \"", + \"MIDWORD\t'-", + \"", + \"KEP =", + \"RAR ?", + \"BAD !", + \"", + \"PFX I N 1", + \"PFX I 0 in .", + \"", + \"PFX O Y 1", + \"PFX O 0 out [a-z]", + \"", + \"SFX S Y 2", + \"SFX S 0 s [^s]", + \"SFX S 0 es s", + \"", + \"SFX N N 3", + \"SFX N 0 en [^n]", + \"SFX N 0 nen n", + \"SFX N 0 n .", + \"", + \"REP 3", + \"REP g ch", + \"REP ch g", + \"REP svp s.v.p.", + \"", + \"MAP 9", + \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5", + \"MAP e\xE8\xE9\xEA\xEB", + \"MAP i\xEC\xED\xEE\xEF", + \"MAP o\xF2\xF3\xF4\xF5\xF6", + \"MAP u\xF9\xFA\xFB\xFC", + \"MAP n\xF1", + \"MAP c\xE7", + \"MAP y\xFF\xFD", + \"MAP s\xDF", + \ ] +let g:test_data_aff3 = [ + \"SET ISO8859-1", + \"", + \"COMPOUNDMIN 3", + \"COMPOUNDRULE m*", + \"NEEDCOMPOUND x", + \ ] +let g:test_data_dic3 = [ + \"1234", + \"foo/m", + \"bar/mx", + \"m\xEF/m", + \"la/mx", + \ ] +let g:test_data_aff4 = [ + \"SET ISO8859-1", + \"", + \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF", + \"", + \"COMPOUNDRULE m+", + \"COMPOUNDRULE sm*e", + \"COMPOUNDRULE sm+", + \"COMPOUNDMIN 3", + \"COMPOUNDWORDMAX 3", + \"COMPOUNDFORBIDFLAG t", + \"", + \"COMPOUNDSYLMAX 5", + \"SYLLABLE a\xE1e\xE9i\xEDo\xF3\xF6\xF5u\xFA\xFC\xFBy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui", + \"", + \"MAP 9", + \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5", + \"MAP e\xE8\xE9\xEA\xEB", + \"MAP i\xEC\xED\xEE\xEF", + \"MAP o\xF2\xF3\xF4\xF5\xF6", + \"MAP u\xF9\xFA\xFB\xFC", + \"MAP n\xF1", + \"MAP c\xE7", + \"MAP y\xFF\xFD", + \"MAP s\xDF", + \"", + \"NEEDAFFIX x", + \"", + \"PFXPOSTPONE", + \"", + \"MIDWORD '-", + \"", + \"SFX q N 1", + \"SFX q 0 -ok .", + \"", + \"SFX a Y 2", + \"SFX a 0 s .", + \"SFX a 0 ize/t .", + \"", + \"PFX p N 1", + \"PFX p 0 pre .", + \"", + \"PFX P N 1", + \"PFX P 0 nou .", + \ ] +let g:test_data_dic4 = [ + \"1234", + \"word/mP", + \"util/am", + \"pro/xq", + \"tomato/m", + \"bork/mp", + \"start/s", + \"end/e", + \ ] +let g:test_data_aff5 = [ + \"SET ISO8859-1", + \"", + \"FLAG long", + \"", + \"NEEDAFFIX !!", + \"", + \"COMPOUNDRULE ssmm*ee", + \"", + \"NEEDCOMPOUND xx", + \"COMPOUNDPERMITFLAG pp", + \"", + \"SFX 13 Y 1", + \"SFX 13 0 bork .", + \"", + \"SFX a1 Y 1", + \"SFX a1 0 a1 .", + \"", + \"SFX a\xE9 Y 1", + \"SFX a\xE9 0 a\xE9 .", + \"", + \"PFX zz Y 1", + \"PFX zz 0 pre/pp .", + \"", + \"PFX yy Y 1", + \"PFX yy 0 nou .", + \ ] +let g:test_data_dic5 = [ + \"1234", + \"foo/a1a\xE9!!", + \"bar/zz13ee", + \"start/ss", + \"end/eeyy", + \"middle/mmxx", + \ ] +let g:test_data_aff6 = [ + \"SET ISO8859-1", + \"", + \"FLAG caplong", + \"", + \"NEEDAFFIX A!", + \"", + \"COMPOUNDRULE sMm*Ee", + \"", + \"NEEDCOMPOUND Xx", + \"", + \"COMPOUNDPERMITFLAG p", + \"", + \"SFX N3 Y 1", + \"SFX N3 0 bork .", + \"", + \"SFX A1 Y 1", + \"SFX A1 0 a1 .", + \"", + \"SFX A\xE9 Y 1", + \"SFX A\xE9 0 a\xE9 .", + \"", + \"PFX Zz Y 1", + \"PFX Zz 0 pre/p .", + \ ] +let g:test_data_dic6 = [ + \"1234", + \"mee/A1A\xE9A!", + \"bar/ZzN3Ee", + \"lead/s", + \"end/Ee", + \"middle/MmXx", + \ ] +let g:test_data_aff7 = [ + \"SET ISO8859-1", + \"", + \"FLAG num", + \"", + \"NEEDAFFIX 9999", + \"", + \"COMPOUNDRULE 2,77*123", + \"", + \"NEEDCOMPOUND 1", + \"COMPOUNDPERMITFLAG 432", + \"", + \"SFX 61003 Y 1", + \"SFX 61003 0 meat .", + \"", + \"SFX 0 Y 1", + \"SFX 0 0 zero .", + \"", + \"SFX 391 Y 1", + \"SFX 391 0 a1 .", + \"", + \"SFX 111 Y 1", + \"SFX 111 0 a\xE9 .", + \"", + \"PFX 17 Y 1", + \"PFX 17 0 pre/432 .", + \ ] +let g:test_data_dic7 = [ + \"1234", + \"mee/0,391,111,9999", + \"bar/17,61003,123", + \"lead/2", + \"tail/123", + \"middle/77,1", + \ ] +let g:test_data_aff8 = [ + \"SET ISO8859-1", + \"", + \"NOSPLITSUGS", + \ ] +let g:test_data_dic8 = [ + \"1234", + \"foo", + \"bar", + \"faabar", + \ ] +let g:test_data_aff9 = [ + \ ] +let g:test_data_dic9 = [ + \"1234", + \"foo", + \"bar", + \ ] +let g:test_data_aff10 = [ + \"COMPOUNDRULE se", + \"COMPOUNDPERMITFLAG p", + \"", + \"SFX A Y 1", + \"SFX A 0 able/Mp .", + \"", + \"SFX M Y 1", + \"SFX M 0 s .", + \ ] +let g:test_data_dic10 = [ + \"1234", + \"drink/As", + \"table/e", + \ ] +let g:test_data_aff_sal = [ + \"SET ISO8859-1", + \"TRY esianrtolcdugmphbyfvkwjkqxz-\xEB\xE9\xE8\xEA\xEF\xEE\xE4\xE0\xE2\xF6\xFC\xFB'ESIANRTOLCDUGMPHBYFVKWJKQXZ", + \"", + \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF", + \"", + \"MIDWORD\t'-", + \"", + \"KEP =", + \"RAR ?", + \"BAD !", + \"", + \"PFX I N 1", + \"PFX I 0 in .", + \"", + \"PFX O Y 1", + \"PFX O 0 out .", + \"", + \"SFX S Y 2", + \"SFX S 0 s [^s]", + \"SFX S 0 es s", + \"", + \"SFX N N 3", + \"SFX N 0 en [^n]", + \"SFX N 0 nen n", + \"SFX N 0 n .", + \"", + \"REP 3", + \"REP g ch", + \"REP ch g", + \"REP svp s.v.p.", + \"", + \"MAP 9", + \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5", + \"MAP e\xE8\xE9\xEA\xEB", + \"MAP i\xEC\xED\xEE\xEF", + \"MAP o\xF2\xF3\xF4\xF5\xF6", + \"MAP u\xF9\xFA\xFB\xFC", + \"MAP n\xF1", + \"MAP c\xE7", + \"MAP y\xFF\xFD", + \"MAP s\xDF", + \"", + \"SAL AH(AEIOUY)-^ *H", + \"SAL AR(AEIOUY)-^ *R", + \"SAL A(HR)^ *", + \"SAL A^ *", + \"SAL AH(AEIOUY)- H", + \"SAL AR(AEIOUY)- R", + \"SAL A(HR) _", + \"SAL \xC0^ *", + \"SAL \xC5^ *", + \"SAL BB- _", + \"SAL B B", + \"SAL CQ- _", + \"SAL CIA X", + \"SAL CH X", + \"SAL C(EIY)- S", + \"SAL CK K", + \"SAL COUGH^ KF", + \"SAL CC< C", + \"SAL C K", + \"SAL DG(EIY) K", + \"SAL DD- _", + \"SAL D T", + \"SAL \xC9< E", + \"SAL EH(AEIOUY)-^ *H", + \"SAL ER(AEIOUY)-^ *R", + \"SAL E(HR)^ *", + \"SAL ENOUGH^$ *NF", + \"SAL E^ *", + \"SAL EH(AEIOUY)- H", + \"SAL ER(AEIOUY)- R", + \"SAL E(HR) _", + \"SAL FF- _", + \"SAL F F", + \"SAL GN^ N", + \"SAL GN$ N", + \"SAL GNS$ NS", + \"SAL GNED$ N", + \"SAL GH(AEIOUY)- K", + \"SAL GH _", + \"SAL GG9 K", + \"SAL G K", + \"SAL H H", + \"SAL IH(AEIOUY)-^ *H", + \"SAL IR(AEIOUY)-^ *R", + \"SAL I(HR)^ *", + \"SAL I^ *", + \"SAL ING6 N", + \"SAL IH(AEIOUY)- H", + \"SAL IR(AEIOUY)- R", + \"SAL I(HR) _", + \"SAL J K", + \"SAL KN^ N", + \"SAL KK- _", + \"SAL K K", + \"SAL LAUGH^ LF", + \"SAL LL- _", + \"SAL L L", + \"SAL MB$ M", + \"SAL MM M", + \"SAL M M", + \"SAL NN- _", + \"SAL N N", + \"SAL OH(AEIOUY)-^ *H", + \"SAL OR(AEIOUY)-^ *R", + \"SAL O(HR)^ *", + \"SAL O^ *", + \"SAL OH(AEIOUY)- H", + \"SAL OR(AEIOUY)- R", + \"SAL O(HR) _", + \"SAL PH F", + \"SAL PN^ N", + \"SAL PP- _", + \"SAL P P", + \"SAL Q K", + \"SAL RH^ R", + \"SAL ROUGH^ RF", + \"SAL RR- _", + \"SAL R R", + \"SAL SCH(EOU)- SK", + \"SAL SC(IEY)- S", + \"SAL SH X", + \"SAL SI(AO)- X", + \"SAL SS- _", + \"SAL S S", + \"SAL TI(AO)- X", + \"SAL TH @", + \"SAL TCH-- _", + \"SAL TOUGH^ TF", + \"SAL TT- _", + \"SAL T T", + \"SAL UH(AEIOUY)-^ *H", + \"SAL UR(AEIOUY)-^ *R", + \"SAL U(HR)^ *", + \"SAL U^ *", + \"SAL UH(AEIOUY)- H", + \"SAL UR(AEIOUY)- R", + \"SAL U(HR) _", + \"SAL V^ W", + \"SAL V F", + \"SAL WR^ R", + \"SAL WH^ W", + \"SAL W(AEIOU)- W", + \"SAL X^ S", + \"SAL X KS", + \"SAL Y(AEIOU)- Y", + \"SAL ZZ- _", + \"SAL Z S", + \ ] + +func LoadAffAndDic(aff_contents, dic_contents) + set enc=utf-8 + set spellfile= + call writefile(a:aff_contents, "Xtest.aff") + call writefile(a:dic_contents, "Xtest.dic") + " Generate a .spl file from a .dic and .aff file. + mkspell! Xtest Xtest + " use that spell file + set spl=Xtest.utf-8.spl spell +endfunc + +func ListWords() + spelldump + %yank + quit + return split(@", "\n") +endfunc + +func TestGoodBadBase() + exe '1;/^good:' + normal 0f:]s + let prevbad = '' + let result = [] + while 1 + let [bad, a] = spellbadword() + if bad == '' || bad == prevbad || bad == 'badend' + break + endif + let prevbad = bad + " let lst = bad->spellsuggest(3) + let lst = spellsuggest(bad, 3) + normal mm + + call add(result, [bad, lst]) + normal `m]s + endwhile + return result +endfunc + +func RunGoodBad(good, bad, expected_words, expected_bad_words) + %bwipe! + call setline(1, ['', "good: ", a:good, a:bad, " badend "]) + let words = ListWords() + call assert_equal(a:expected_words, words[1:-1]) + let bad_words = TestGoodBadBase() + call assert_equal(a:expected_bad_words, bad_words) + %bwipe! +endfunc + +func Test_spell_basic() + call LoadAffAndDic(g:test_data_aff1, g:test_data_dic1) + call RunGoodBad("wrong OK puts. Test the end", + \ "bad: inputs comment ok Ok. test d\u00E9\u00F4l end the", + \["Comment", "deol", "d\u00E9\u00F4r", "input", "OK", "output", "outputs", "outtest", "put", "puts", + \ "test", "testen", "testn", "the end", "uk", "wrong"], + \[ + \ ["bad", ["put", "uk", "OK"]], + \ ["inputs", ["input", "puts", "outputs"]], + \ ["comment", ["Comment", "outtest", "the end"]], + \ ["ok", ["OK", "uk", "put"]], + \ ["Ok", ["OK", "Uk", "Put"]], + \ ["test", ["Test", "testn", "testen"]], + \ ["d\u00E9\u00F4l", ["deol", "d\u00E9\u00F4r", "test"]], + \ ["end", ["put", "uk", "test"]], + \ ["the", ["put", "uk", "test"]], + \ ] + \ ) + + call assert_equal("gebletegek", soundfold('goobledygoook')) + " call assert_equal("kepereneven", 'kóopërÿnôven'->soundfold()) + call assert_equal("kepereneven", soundfold('kóopërÿnôven')) + call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale')) +endfunc + +" Postponed prefixes +func Test_spell_prefixes() + call LoadAffAndDic(g:test_data_aff2, g:test_data_dic1) + call RunGoodBad("puts", + \ "bad: inputs comment ok Ok end the. test d\u00E9\u00F4l", + \ ["Comment", "deol", "d\u00E9\u00F4r", "OK", "put", "input", "output", "puts", "outputs", "test", "outtest", "testen", "testn", "the end", "uk", "wrong"], + \ [ + \ ["bad", ["put", "uk", "OK"]], + \ ["inputs", ["input", "puts", "outputs"]], + \ ["comment", ["Comment"]], + \ ["ok", ["OK", "uk", "put"]], + \ ["Ok", ["OK", "Uk", "Put"]], + \ ["end", ["put", "uk", "deol"]], + \ ["the", ["put", "uk", "test"]], + \ ["test", ["Test", "testn", "testen"]], + \ ["d\u00E9\u00F4l", ["deol", "d\u00E9\u00F4r", "test"]], + \ ]) +endfunc + +"Compound words +func Test_spell_compound() + throw 'skipped: TODO: ' + call LoadAffAndDic(g:test_data_aff3, g:test_data_dic3) + call RunGoodBad("foo m\u00EF foobar foofoobar barfoo barbarfoo", + \ "bad: bar la foom\u00EF barm\u00EF m\u00EFfoo m\u00EFbar m\u00EFm\u00EF lala m\u00EFla lam\u00EF foola labar", + \ ["foo", "m\u00EF"], + \ [ + \ ["bad", ["foo", "m\u00EF"]], + \ ["bar", ["barfoo", "foobar", "foo"]], + \ ["la", ["m\u00EF", "foo"]], + \ ["foom\u00EF", ["foo m\u00EF", "foo", "foofoo"]], + \ ["barm\u00EF", ["barfoo", "m\u00EF", "barbar"]], + \ ["m\u00EFfoo", ["m\u00EF foo", "foo", "foofoo"]], + \ ["m\u00EFbar", ["foobar", "barbar", "m\u00EF"]], + \ ["m\u00EFm\u00EF", ["m\u00EF m\u00EF", "m\u00EF"]], + \ ["lala", []], + \ ["m\u00EFla", ["m\u00EF", "m\u00EF m\u00EF"]], + \ ["lam\u00EF", ["m\u00EF", "m\u00EF m\u00EF"]], + \ ["foola", ["foo", "foobar", "foofoo"]], + \ ["labar", ["barbar", "foobar"]], + \ ]) + + call LoadAffAndDic(g:test_data_aff4, g:test_data_dic4) + call RunGoodBad("word util bork prebork start end wordutil wordutils pro-ok bork borkbork borkborkbork borkborkborkbork borkborkborkborkbork tomato tomatotomato startend startword startwordword startwordend startwordwordend startwordwordwordend prebork preborkbork preborkborkbork nouword", + \ "bad: wordutilize pro borkborkborkborkborkbork tomatotomatotomato endstart endend startstart wordend wordstart preborkprebork preborkpreborkbork startwordwordwordwordend borkpreborkpreborkbork utilsbork startnouword", + \ ["bork", "prebork", "end", "pro-ok", "start", "tomato", "util", "utilize", "utils", "word", "nouword"], + \ [ + \ ["bad", ["end", "bork", "word"]], + \ ["wordutilize", ["word utilize", "wordutils", "wordutil"]], + \ ["pro", ["bork", "word", "end"]], + \ ["borkborkborkborkborkbork", ["bork borkborkborkborkbork", "borkbork borkborkborkbork", "borkborkbork borkborkbork"]], + \ ["tomatotomatotomato", ["tomato tomatotomato", "tomatotomato tomato", "tomato tomato tomato"]], + \ ["endstart", ["end start", "start"]], + \ ["endend", ["end end", "end"]], + \ ["startstart", ["start start"]], + \ ["wordend", ["word end", "word", "wordword"]], + \ ["wordstart", ["word start", "bork start"]], + \ ["preborkprebork", ["prebork prebork", "preborkbork", "preborkborkbork"]], + \ ["preborkpreborkbork", ["prebork preborkbork", "preborkborkbork", "preborkborkborkbork"]], + \ ["startwordwordwordwordend", ["startwordwordwordword end", "startwordwordwordword", "start wordwordwordword end"]], + \ ["borkpreborkpreborkbork", ["bork preborkpreborkbork", "bork prebork preborkbork", "bork preborkprebork bork"]], + \ ["utilsbork", ["utilbork", "utils bork", "util bork"]], + \ ["startnouword", ["start nouword", "startword", "startborkword"]], + \ ]) + +endfunc + +" Test affix flags with two characters +func Test_spell_affix() + throw 'skipped: TODO: ' + call LoadAffAndDic(g:test_data_aff5, g:test_data_dic5) + call RunGoodBad("fooa1 fooa\u00E9 bar prebar barbork prebarbork startprebar start end startend startmiddleend nouend", + \ "bad: foo fooa2 prabar probarbirk middle startmiddle middleend endstart startprobar startnouend", + \ ["bar", "barbork", "end", "fooa1", "fooa\u00E9", "nouend", "prebar", "prebarbork", "start"], + \ [ + \ ["bad", ["bar", "end", "fooa1"]], + \ ["foo", ["fooa1", "fooa\u00E9", "bar"]], + \ ["fooa2", ["fooa1", "fooa\u00E9", "bar"]], + \ ["prabar", ["prebar", "bar", "bar bar"]], + \ ["probarbirk", ["prebarbork"]], + \ ["middle", []], + \ ["startmiddle", ["startmiddleend", "startmiddlebar"]], + \ ["middleend", []], + \ ["endstart", ["end start", "start"]], + \ ["startprobar", ["startprebar", "start prebar", "startbar"]], + \ ["startnouend", ["start nouend", "startend"]], + \ ]) + + call LoadAffAndDic(g:test_data_aff6, g:test_data_dic6) + call RunGoodBad("meea1 meea\u00E9 bar prebar barbork prebarbork leadprebar lead end leadend leadmiddleend", + \ "bad: mee meea2 prabar probarbirk middle leadmiddle middleend endlead leadprobar", + \ ["bar", "barbork", "end", "lead", "meea1", "meea\u00E9", "prebar", "prebarbork"], + \ [ + \ ["bad", ["bar", "end", "lead"]], + \ ["mee", ["meea1", "meea\u00E9", "bar"]], + \ ["meea2", ["meea1", "meea\u00E9", "lead"]], + \ ["prabar", ["prebar", "bar", "leadbar"]], + \ ["probarbirk", ["prebarbork"]], + \ ["middle", []], + \ ["leadmiddle", ["leadmiddleend", "leadmiddlebar"]], + \ ["middleend", []], + \ ["endlead", ["end lead", "lead", "end end"]], + \ ["leadprobar", ["leadprebar", "lead prebar", "leadbar"]], + \ ]) + + call LoadAffAndDic(g:test_data_aff7, g:test_data_dic7) + call RunGoodBad("meea1 meezero meea\u00E9 bar prebar barmeat prebarmeat leadprebar lead tail leadtail leadmiddletail", + \ "bad: mee meea2 prabar probarmaat middle leadmiddle middletail taillead leadprobar", + \ ["bar", "barmeat", "lead", "meea1", "meea\u00E9", "meezero", "prebar", "prebarmeat", "tail"], + \ [ + \ ["bad", ["bar", "lead", "tail"]], + \ ["mee", ["meea1", "meea\u00E9", "bar"]], + \ ["meea2", ["meea1", "meea\u00E9", "lead"]], + \ ["prabar", ["prebar", "bar", "leadbar"]], + \ ["probarmaat", ["prebarmeat"]], + \ ["middle", []], + \ ["leadmiddle", ["leadmiddlebar"]], + \ ["middletail", []], + \ ["taillead", ["tail lead", "tail"]], + \ ["leadprobar", ["leadprebar", "lead prebar", "leadbar"]], + \ ]) +endfunc + +func Test_spell_NOSLITSUGS() + call LoadAffAndDic(g:test_data_aff8, g:test_data_dic8) + call RunGoodBad("foo bar faabar", "bad: foobar barfoo", + \ ["bar", "faabar", "foo"], + \ [ + \ ["bad", ["bar", "foo"]], + \ ["foobar", ["faabar", "foo bar", "bar"]], + \ ["barfoo", ["bar foo", "bar", "foo"]], + \ ]) +endfunc + +" Numbers +func Test_spell_Numbers() + call LoadAffAndDic(g:test_data_aff9, g:test_data_dic9) + call RunGoodBad("0b1011 0777 1234 0x01ff", "", + \ ["bar", "foo"], + \ [ + \ ]) +endfunc + +" Affix flags +func Test_spell_affix_flags() + throw 'skipped: TODO: ' + call LoadAffAndDic(g:test_data_aff10, g:test_data_dic10) + call RunGoodBad("drink drinkable drinkables drinktable drinkabletable", + \ "bad: drinks drinkstable drinkablestable", + \ ["drink", "drinkable", "drinkables", "table"], + \ [['bad', []], + \ ['drinks', ['drink']], + \ ['drinkstable', ['drinktable', 'drinkable', 'drink table']], + \ ['drinkablestable', ['drinkabletable', 'drinkables table', 'drinkable table']], + \ ]) +endfunc + +function FirstSpellWord() + call feedkeys("/^start:\n", 'tx') + normal ]smm + let [str, a] = spellbadword() + return str +endfunc + +function SecondSpellWord() + normal `m]s + let [str, a] = spellbadword() + return str +endfunc + +" Test with SAL instead of SOFO items; test automatic reloading +func Test_spell_sal_and_addition() + set spellfile= + call writefile(g:test_data_dic1, "Xtest.dic") + call writefile(g:test_data_aff_sal, "Xtest.aff") + mkspell! Xtest Xtest + set spl=Xtest.utf-8.spl spell + call assert_equal('kbltykk', soundfold('goobledygoook')) + call assert_equal('kprnfn', soundfold('kóopërÿnôven')) + call assert_equal('*fls kswts tl', soundfold('oeverloos gezwets edale')) + + "also use an addition file + call writefile(["/regions=usgbnz", "elequint/2", "elekwint/3"], "Xtest.utf-8.add") + mkspell! Xtest.utf-8.add.spl Xtest.utf-8.add + + bwipe! + call setline(1, ["start: elequint test elekwint test elekwent asdf"]) + + set spellfile=Xtest.utf-8.add + call assert_equal("elekwent", FirstSpellWord()) + + set spl=Xtest_us.utf-8.spl + call assert_equal("elequint", FirstSpellWord()) + call assert_equal("elekwint", SecondSpellWord()) + + set spl=Xtest_gb.utf-8.spl + call assert_equal("elekwint", FirstSpellWord()) + call assert_equal("elekwent", SecondSpellWord()) + + set spl=Xtest_nz.utf-8.spl + call assert_equal("elequint", FirstSpellWord()) + call assert_equal("elekwent", SecondSpellWord()) + + set spl=Xtest_ca.utf-8.spl + call assert_equal("elequint", FirstSpellWord()) + call assert_equal("elekwint", SecondSpellWord()) +endfunc + +func Test_spellfile_value() + set spellfile=Xdir/Xtest.utf-8.add + set spellfile=Xdir/Xtest.utf-8.add,Xtest_other.add +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_spellfile.vim b/src/nvim/testdir/test_spellfile.vim new file mode 100644 index 0000000000..0f48ab8f6f --- /dev/null +++ b/src/nvim/testdir/test_spellfile.vim @@ -0,0 +1,286 @@ +" Test for commands that operate on the spellfile. + +source shared.vim +source check.vim + +CheckFeature spell +CheckFeature syntax + +func Test_spell_normal() + new + call append(0, ['1 good', '2 goood', '3 goood']) + set spell spellfile=./Xspellfile.add spelllang=en + let oldlang=v:lang + lang C + + " Test for zg + 1 + norm! ]s + call assert_equal('2 goood', getline('.')) + norm! zg + 1 + let a=execute('unsilent :norm! ]s') + call assert_equal('1 good', getline('.')) + call assert_equal('search hit BOTTOM, continuing at TOP', a[1:]) + let cnt=readfile('./Xspellfile.add') + call assert_equal('goood', cnt[0]) + + " Test for zw + 2 + norm! $zw + 1 + norm! ]s + call assert_equal('2 goood', getline('.')) + let cnt=readfile('./Xspellfile.add') + call assert_equal('#oood', cnt[0]) + call assert_equal('goood/!', cnt[1]) + + " Test for :spellrare + spellrare rare + let cnt=readfile('./Xspellfile.add') + call assert_equal(['#oood', 'goood/!', 'rare/?'], cnt) + + " Make sure :spellundo works for rare words. + spellundo rare + let cnt=readfile('./Xspellfile.add') + call assert_equal(['#oood', 'goood/!', '#are/?'], cnt) + + " Test for zg in visual mode + let a=execute('unsilent :norm! V$zg') + call assert_equal("Word '2 goood' added to ./Xspellfile.add", a[1:]) + 1 + norm! ]s + call assert_equal('3 goood', getline('.')) + let cnt=readfile('./Xspellfile.add') + call assert_equal('2 goood', cnt[3]) + " Remove "2 good" from spellfile + 2 + let a=execute('unsilent norm! V$zw') + call assert_equal("Word '2 goood' added to ./Xspellfile.add", a[1:]) + let cnt=readfile('./Xspellfile.add') + call assert_equal('2 goood/!', cnt[4]) + + " Test for zG + let a=execute('unsilent norm! V$zG') + call assert_match("Word '2 goood' added to .*", a) + let fname=matchstr(a, 'to\s\+\zs\f\+$') + let cnt=readfile(fname) + call assert_equal('2 goood', cnt[0]) + + " Test for zW + let a=execute('unsilent norm! V$zW') + call assert_match("Word '2 goood' added to .*", a) + let cnt=readfile(fname) + call assert_equal('# goood', cnt[0]) + call assert_equal('2 goood/!', cnt[1]) + + " Test for zuW + let a=execute('unsilent norm! V$zuW') + call assert_match("Word '2 goood' removed from .*", a) + let cnt=readfile(fname) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + + " Test for zuG + let a=execute('unsilent norm! $zG') + call assert_match("Word 'goood' added to .*", a) + let cnt=readfile(fname) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + call assert_equal('goood', cnt[2]) + let a=execute('unsilent norm! $zuG') + let cnt=readfile(fname) + call assert_match("Word 'goood' removed from .*", a) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + call assert_equal('#oood', cnt[2]) + " word not found in wordlist + let a=execute('unsilent norm! V$zuG') + let cnt=readfile(fname) + call assert_match("", a) + call assert_equal('# goood', cnt[0]) + call assert_equal('# goood/!', cnt[1]) + call assert_equal('#oood', cnt[2]) + + " Test for zug + call delete('./Xspellfile.add') + 2 + let a=execute('unsilent norm! $zg') + let cnt=readfile('./Xspellfile.add') + call assert_equal('goood', cnt[0]) + let a=execute('unsilent norm! $zug') + call assert_match("Word 'goood' removed from \./Xspellfile.add", a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('#oood', cnt[0]) + " word not in wordlist + let a=execute('unsilent norm! V$zug') + call assert_match('', a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('#oood', cnt[0]) + + " Test for zuw + call delete('./Xspellfile.add') + 2 + let a=execute('unsilent norm! Vzw') + let cnt=readfile('./Xspellfile.add') + call assert_equal('2 goood/!', cnt[0]) + let a=execute('unsilent norm! Vzuw') + call assert_match("Word '2 goood' removed from \./Xspellfile.add", a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('# goood/!', cnt[0]) + " word not in wordlist + let a=execute('unsilent norm! $zug') + call assert_match('', a) + let cnt=readfile('./Xspellfile.add') + call assert_equal('# goood/!', cnt[0]) + + " add second entry to spellfile setting + set spellfile=./Xspellfile.add,./Xspellfile2.add + call delete('./Xspellfile.add') + 2 + let a=execute('unsilent norm! $2zg') + let cnt=readfile('./Xspellfile2.add') + call assert_match("Word 'goood' added to ./Xspellfile2.add", a) + call assert_equal('goood', cnt[0]) + + " Test for :spellgood! + let temp = execute(':spe!0/0') + call assert_match('Invalid region', temp) + let spellfile = matchstr(temp, 'Invalid region nr in \zs.*\ze line \d: 0') + call assert_equal(['# goood', '# goood/!', '#oood', '0/0'], readfile(spellfile)) + + " Test for :spellrare! + :spellrare! raare + call assert_equal(['# goood', '# goood/!', '#oood', '0/0', 'raare/?'], readfile(spellfile)) + call delete(spellfile) + + " clean up + exe "lang" oldlang + call delete("./Xspellfile.add") + call delete("./Xspellfile2.add") + call delete("./Xspellfile.add.spl") + call delete("./Xspellfile2.add.spl") + + " zux -> no-op + 2 + norm! $zux + call assert_equal([], glob('Xspellfile.add',0,1)) + call assert_equal([], glob('Xspellfile2.add',0,1)) + + set spellfile= + bw! +endfunc + +" Test CHECKCOMPOUNDPATTERN (see :help spell-CHECKCOMPOUNDPATTERN) +func Test_spellfile_CHECKCOMPOUNDPATTERN() + call writefile(['4', + \ 'one/c', + \ 'two/c', + \ 'three/c', + \ 'four'], 'XtestCHECKCOMPOUNDPATTERN.dic') + " Forbid compound words where first word ends with 'wo' and second starts with 'on'. + call writefile(['CHECKCOMPOUNDPATTERN 1', + \ 'CHECKCOMPOUNDPATTERN wo on', + \ 'COMPOUNDFLAG c'], 'XtestCHECKCOMPOUNDPATTERN.aff') + + let output = execute('mkspell! XtestCHECKCOMPOUNDPATTERN-utf8.spl XtestCHECKCOMPOUNDPATTERN') + set spell spelllang=XtestCHECKCOMPOUNDPATTERN-utf8.spl + + " Check valid words with and without valid compounds. + for goodword in ['one', 'two', 'three', 'four', + \ 'oneone', 'onetwo', 'onethree', + \ 'twotwo', 'twothree', + \ 'threeone', 'threetwo', 'threethree', + \ 'onetwothree', 'onethreetwo', 'twothreeone', 'oneoneone'] + call assert_equal(['', ''], spellbadword(goodword), goodword) + endfor + + " Compounds 'twoone' or 'threetwoone' should be forbidden by CHECKCOMPOUNPATTERN. + " 'four' does not have the 'c' flag in *.aff file so no compound. + " 'five' is not in the *.dic file. + for badword in ['five', 'onetwox', + \ 'twoone', 'threetwoone', + \ 'fourone', 'onefour'] + call assert_equal([badword, 'bad'], spellbadword(badword)) + endfor + + set spell& spelllang& + call delete('XtestCHECKCOMPOUNDPATTERN.dic') + call delete('XtestCHECKCOMPOUNDPATTERN.aff') + call delete('XtestCHECKCOMPOUNDPATTERN-utf8.spl') +endfunc + +" Test NOCOMPOUNDSUGS (see :help spell-NOCOMPOUNDSUGS) +func Test_spellfile_NOCOMPOUNDSUGS() + call writefile(['3', + \ 'one/c', + \ 'two/c', + \ 'three/c'], 'XtestNOCOMPOUNDSUGS.dic') + + " pass 0 tests without NOCOMPOUNDSUGS, pass 1 tests with NOCOMPOUNDSUGS + for pass in [0, 1] + if pass == 0 + call writefile(['COMPOUNDFLAG c'], 'XtestNOCOMPOUNDSUGS.aff') + else + call writefile(['NOCOMPOUNDSUGS', + \ 'COMPOUNDFLAG c'], 'XtestNOCOMPOUNDSUGS.aff') + endif + + mkspell! XtestNOCOMPOUNDSUGS-utf8.spl XtestNOCOMPOUNDSUGS + set spell spelllang=XtestNOCOMPOUNDSUGS-utf8.spl + + for goodword in ['one', 'two', 'three', + \ 'oneone', 'onetwo', 'onethree', + \ 'twoone', 'twotwo', 'twothree', + \ 'threeone', 'threetwo', 'threethree', + \ 'onetwothree', 'onethreetwo', 'twothreeone', 'oneoneone'] + call assert_equal(['', ''], spellbadword(goodword), goodword) + endfor + + for badword in ['four', 'onetwox', 'onexone'] + call assert_equal([badword, 'bad'], spellbadword(badword)) + endfor + + if pass == 0 + call assert_equal(['one', 'oneone'], spellsuggest('onne', 2)) + call assert_equal(['onethree', 'one three'], spellsuggest('onethre', 2)) + else + call assert_equal(['one', 'one one'], spellsuggest('onne', 2)) + call assert_equal(['one three'], spellsuggest('onethre', 2)) + endif + endfor + + set spell& spelllang& + call delete('XtestNOCOMPOUNDSUGS.dic') + call delete('XtestNOCOMPOUNDSUGS.aff') + call delete('XtestNOCOMPOUNDSUGS-utf8.spl') +endfunc + +" Test COMMON (better suggestions with common words, see :help spell-COMMON) +func Test_spellfile_COMMON() + call writefile(['7', + \ 'and', + \ 'ant', + \ 'end', + \ 'any', + \ 'tee', + \ 'the', + \ 'ted'], 'XtestCOMMON.dic') + call writefile(['COMMON the and'], 'XtestCOMMON.aff') + + let output = execute('mkspell! XtestCOMMON-utf8.spl XtestCOMMON') + set spell spelllang=XtestCOMMON-utf8.spl + + " COMMON words 'and' and 'the' should be the top suggestions. + call assert_equal(['and', 'ant'], spellsuggest('anr', 2)) + call assert_equal(['and', 'end'], spellsuggest('ond', 2)) + call assert_equal(['the', 'ted'], spellsuggest('tha', 2)) + call assert_equal(['the', 'tee'], spellsuggest('dhe', 2)) + + set spell& spelllang& + call delete('XtestCOMMON.dic') + call delete('XtestCOMMON.aff') + call delete('XtestCOMMON-utf8.spl') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_startup_utf8.vim b/src/nvim/testdir/test_startup_utf8.vim index 1b3d2184a0..bb4304396e 100644 --- a/src/nvim/testdir/test_startup_utf8.vim +++ b/src/nvim/testdir/test_startup_utf8.vim @@ -1,5 +1,6 @@ " Tests for startup using utf-8. +source check.vim source shared.vim source screendump.vim @@ -73,7 +74,7 @@ func Test_detect_ambiwidth() \ 'call test_option_not_set("ambiwidth")', \ 'redraw', \ ], 'Xscript') - let buf = RunVimInTerminal('-S Xscript', {}) + let buf = RunVimInTerminal('-S Xscript', #{keep_t_u7: 1}) call term_wait(buf) call term_sendkeys(buf, "S\<C-R>=&ambiwidth\<CR>\<Esc>") call WaitForAssert({-> assert_match('single', term_getline(buf, 1))}) diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index f27920d20f..02bc297de1 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -319,6 +319,7 @@ func Test_swap_prompt_splitwin() let buf = RunVimInTerminal('', {'rows': 20}) call term_sendkeys(buf, ":set nomore\n") call term_sendkeys(buf, ":set noruler\n") + call term_sendkeys(buf, ":split Xfile1\n") call term_wait(buf) call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: $', term_getline(buf, 20))}) @@ -330,8 +331,19 @@ func Test_swap_prompt_splitwin() call term_wait(buf) call WaitForAssert({-> assert_match('^1$', term_getline(buf, 20))}) call StopVimInTerminal(buf) + + " This caused Vim to crash when typing "q". + " TODO: it does not actually reproduce the crash. + call writefile(['au BufAdd * set virtualedit=all'], 'Xvimrc') + + let buf = RunVimInTerminal('-u Xvimrc Xfile1', {'rows': 20, 'wait_for_ruler': 0}) + call TermWait(buf) + call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort:', term_getline(buf, 20))}) + call term_sendkeys(buf, "q") + %bwipe! call delete('Xfile1') + call delete('Xvimrc') endfunc func Test_swap_symlink() @@ -364,4 +376,8 @@ func Test_swap_symlink() call delete('Xswapdir', 'rf') endfunc +func Test_no_swap_file() + call assert_equal("\nNo swap file", execute('swapname')) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 875e23894f..914d9c2782 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -116,7 +116,7 @@ func Test_syntime() let a = execute('syntime report') call assert_equal("\nNo Syntax items defined for this buffer", a) - view ../memfile_test.c + view samples/memfile_test.c setfiletype cpp redraw let a = execute('syntime report') @@ -546,8 +546,8 @@ func Test_synstack_synIDtrans() call assert_equal([], synstack(1, 1)) norm f/ - call assert_equal(['cComment', 'cCommentStart'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) - call assert_equal(['Comment', 'Comment'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")')) + eval synstack(line("."), col("."))->map('synIDattr(v:val, "name")')->assert_equal(['cComment', 'cCommentStart']) + eval synstack(line("."), col("."))->map('synIDattr(synIDtrans(v:val), "name")')->assert_equal(['Comment', 'Comment']) norm fA call assert_equal(['cComment'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 6bbe714d19..7b8ee778cc 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -7,10 +7,10 @@ func Test_System() if !executable('echo') || !executable('cat') || !executable('wc') return endif - let out = system('echo 123') + let out = 'echo 123'->system() call assert_equal("123\n", out) - let out = systemlist('echo 123') + let out = 'echo 123'->systemlist() if &shell =~# 'cmd.exe$' call assert_equal(["123\r"], out) else diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 9f02af7d8e..0fa7f85f0d 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -170,7 +170,7 @@ func Test_tag_symbolic() call assert_equal('Xtest.c', expand('%:t')) call assert_equal(2, col('.')) - set hidden& + set nohidden set tags& enew! call delete('Xtags') @@ -548,6 +548,16 @@ func Test_tag_line_toolong() call assert_equal('Xsomewhere', expand('%')) call assert_equal(3, getcurpos()[1]) + " expansion on command line works with long lines when &wildoptions contains + " 'tagfile' + set wildoptions=tagfile + call writefile([ + \ 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa file /^pattern$/;" f' + \ ], 'Xtags') + call feedkeys(":tag \<Tab>", 'tx') + " Should not crash + call assert_true(v:true) + call delete('Xtags') call delete('Xsomewhere') set tags& @@ -771,15 +781,16 @@ func Test_ltag() ltag third call assert_equal('Xfoo', bufname('')) call assert_equal(3, line('.')) - call assert_equal([{'lnum': 3, 'bufnr': bufnr('Xfoo'), 'col': 0, - \ 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': 0, 'type': '', - \ 'module': '', 'text': 'third'}], getloclist(0)) + call assert_equal([{'lnum': 3, 'end_lnum': 0, 'bufnr': bufnr('Xfoo'), + \ 'col': 0, 'end_col': 0, 'pattern': '', 'valid': 1, 'vcol': 0, + \ 'nr': 0, 'type': '', 'module': '', 'text': 'third'}], getloclist(0)) ltag second call assert_equal(2, line('.')) - call assert_equal([{'lnum': 0, 'bufnr': bufnr('Xfoo'), 'col': 0, - \ 'pattern': '^\Vint second() {}\$', 'valid': 1, 'vcol': 0, 'nr': 0, - \ 'type': '', 'module': '', 'text': 'second'}], getloclist(0)) + call assert_equal([{'lnum': 0, 'end_lnum': 0, 'bufnr': bufnr('Xfoo'), + \ 'col': 0, 'end_col': 0, 'pattern': '^\Vint second() {}\$', + \ 'valid': 1, 'vcol': 0, 'nr': 0, 'type': '', 'module': '', + \ 'text': 'second'}], getloclist(0)) call delete('Xtags') call delete('Xfoo') diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim index 7dcd92a54b..5231ef7b4f 100644 --- a/src/nvim/testdir/test_user_func.vim +++ b/src/nvim/testdir/test_user_func.vim @@ -47,7 +47,7 @@ func FuncWithRef(a) endfunc func Test_user_func() - let g:FuncRef=function("FuncWithRef") + let g:FuncRef = function("FuncWithRef") let g:counter = 0 inoremap <expr> ( ListItem() inoremap <expr> [ ListReset() @@ -62,6 +62,14 @@ func Test_user_func() call assert_equal(9, g:retval) call assert_equal(333, g:FuncRef(333)) + let g:retval = "nop" + call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf")) + call assert_equal('fail', 45->Compute(0, "retval")) + call assert_equal('nop', g:retval) + call assert_equal('ok', 45->Compute(5, "retval")) + call assert_equal(9, g:retval) + " call assert_equal(333, 333->g:FuncRef()) + enew normal oXX+-XX @@ -150,6 +158,14 @@ func Test_default_arg() \ execute('func Args2')) endfunc +func s:addFoo(lead) + return a:lead .. 'foo' +endfunc + +func Test_user_method() + eval 'bar'->s:addFoo()->assert_equal('barfoo') +endfunc + func Test_failed_call_in_try() try | call UnknownFunc() | catch | endtry endfunc diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 4621207d19..29e578ac6d 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -238,6 +238,8 @@ func Test_CmdErrors() call assert_fails('com! -complete=custom DoCmd :', 'E467:') call assert_fails('com! -complete=customlist DoCmd :', 'E467:') call assert_fails('com! -complete=behave,CustomComplete DoCmd :', 'E468:') + call assert_fails('com! -complete=file DoCmd :', 'E1208:') + call assert_fails('com! -nargs=0 -complete=file DoCmd :', 'E1208:') call assert_fails('com! -nargs=x DoCmd :', 'E176:') call assert_fails('com! -count=1 -count=2 DoCmd :', 'E177:') call assert_fails('com! -count=x DoCmd :', 'E178:') @@ -306,27 +308,33 @@ func Test_CmdCompletion() call feedkeys(":com DoC\<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"com DoC', @:) - com! -complete=behave DoCmd : + com! -nargs=1 -complete=behave DoCmd : call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"DoCmd mswin xterm', @:) - " This does not work. Why? - "call feedkeys(":DoCmd x\<C-A>\<C-B>\"\<CR>", 'tx') - "call assert_equal('"DoCmd xterm', @:) - - com! -complete=custom,CustomComplete DoCmd : + com! -nargs=* -complete=custom,CustomComplete DoCmd : call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"DoCmd January February Mars', @:) - com! -complete=customlist,CustomCompleteList DoCmd : + com! -nargs=? -complete=customlist,CustomCompleteList DoCmd : call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') call assert_equal('"DoCmd Monday Tuesday Wednesday', @:) - com! -complete=custom,CustomCompleteList DoCmd : + com! -nargs=+ -complete=custom,CustomCompleteList DoCmd : call assert_fails("call feedkeys(':DoCmd \<C-D>', 'tx')", 'E730:') - com! -complete=customlist,CustomComp DoCmd : + com! -nargs=+ -complete=customlist,CustomComp DoCmd : call assert_fails("call feedkeys(':DoCmd \<C-D>', 'tx')", 'E117:') + + " custom completion without a function + com! -nargs=? -complete=custom, DoCmd + call assert_beeps("call feedkeys(':DoCmd \t', 'tx')") + + " custom completion failure with the wrong function + com! -nargs=? -complete=custom,min DoCmd + call assert_fails("call feedkeys(':DoCmd \t', 'tx')", 'E118:') + + delcom DoCmd endfunc func CallExecute(A, L, P) @@ -459,21 +467,21 @@ func Test_command_list() \ execute('command DoCmd')) " Test with various -complete= argument values (non-exhaustive list) - command! -complete=arglist DoCmd : + command! -nargs=1 -complete=arglist DoCmd : call assert_equal("\n Name Args Address Complete Definition" - \ .. "\n DoCmd 0 arglist :", + \ .. "\n DoCmd 1 arglist :", \ execute('command DoCmd')) - command! -complete=augroup DoCmd : + command! -nargs=* -complete=augroup DoCmd : call assert_equal("\n Name Args Address Complete Definition" - \ .. "\n DoCmd 0 augroup :", + \ .. "\n DoCmd * augroup :", \ execute('command DoCmd')) - command! -complete=custom,CustomComplete DoCmd : + command! -nargs=? -complete=custom,CustomComplete DoCmd : call assert_equal("\n Name Args Address Complete Definition" - \ .. "\n DoCmd 0 custom :", + \ .. "\n DoCmd ? custom :", \ execute('command DoCmd')) - command! -complete=customlist,CustomComplete DoCmd : + command! -nargs=+ -complete=customlist,CustomComplete DoCmd : call assert_equal("\n Name Args Address Complete Definition" - \ .. "\n DoCmd 0 customlist :", + \ .. "\n DoCmd + customlist :", \ execute('command DoCmd')) " Test with various -narg= argument values. diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 5922660ef9..d5837e88c9 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1372,6 +1372,7 @@ func Test_bitwise_functions() " and call assert_equal(127, and(127, 127)) call assert_equal(16, and(127, 16)) + eval 127->and(16)->assert_equal(16) call assert_equal(0, and(127, 128)) call assert_fails("call and(1.0, 1)", 'E805:') call assert_fails("call and([], 1)", 'E745:') @@ -1382,6 +1383,7 @@ func Test_bitwise_functions() " or call assert_equal(23, or(16, 7)) call assert_equal(15, or(8, 7)) + eval 8->or(7)->assert_equal(15) call assert_equal(123, or(0, 123)) call assert_fails("call or(1.0, 1)", 'E805:') call assert_fails("call or([], 1)", 'E745:') @@ -1392,6 +1394,7 @@ func Test_bitwise_functions() " xor call assert_equal(0, xor(127, 127)) call assert_equal(111, xor(127, 16)) + eval 127->xor(16)->assert_equal(111) call assert_equal(255, xor(127, 128)) call assert_fails("call xor(1.0, 1)", 'E805:') call assert_fails("call xor([], 1)", 'E745:') @@ -1401,6 +1404,7 @@ func Test_bitwise_functions() call assert_fails("call xor(1, {})", 'E728:') " invert call assert_equal(65408, and(invert(127), 65535)) + eval 127->invert()->and(65535)->assert_equal(65408) call assert_equal(65519, and(invert(16), 65535)) call assert_equal(65407, and(invert(128), 65535)) call assert_fails("call invert(1.0)", 'E805:') diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 21fd57b791..dbabdcf427 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1,5 +1,9 @@ " Tests for various Visual modes. +source shared.vim +source check.vim +source screendump.vim + func Test_block_shift_multibyte() " Uses double-wide character. split @@ -857,6 +861,15 @@ func Test_visual_block_mode() set tabstop& shiftwidth& endfunc +func Test_visual_force_motion_feedkeys() + onoremap <expr> i- execute('let g:mode = mode(1)') + call feedkeys('dvi-', 'x') + call assert_equal('nov', g:mode) + call feedkeys('di-', 'x') + call assert_equal('no', g:mode) + ounmap i- +endfunc + " Test block-insert using cursor keys for movement func Test_visual_block_insert_cursor_keys() new @@ -1006,4 +1019,98 @@ func Test_visual_put_in_block_using_zp() bwipe! endfunc +func Test_visual_put_in_block_using_zy_and_zp() + new + + " Test 1) Paste using zp - after the cursor without trailing spaces + call setline(1, ['/path;text', '/path;text', '/path;text', '', + \ 'texttext /subdir columntext', + \ 'texttext /longsubdir columntext', + \ 'texttext /longlongsubdir columntext']) + exe "normal! 5G0f/\<c-v>2jezy" + norm! 1G0f;hzp + call assert_equal(['/path/subdir;text', '/path/longsubdir;text', '/path/longlongsubdir;text'], getline(1, 3)) + + " Test 2) Paste using zP - in front of the cursor without trailing spaces + %d + call setline(1, ['/path;text', '/path;text', '/path;text', '', + \ 'texttext /subdir columntext', + \ 'texttext /longsubdir columntext', + \ 'texttext /longlongsubdir columntext']) + exe "normal! 5G0f/\<c-v>2jezy" + norm! 1G0f;zP + call assert_equal(['/path/subdir;text', '/path/longsubdir;text', '/path/longlongsubdir;text'], getline(1, 3)) + + " Test 3) Paste using p - with trailing spaces + %d + call setline(1, ['/path;text', '/path;text', '/path;text', '', + \ 'texttext /subdir columntext', + \ 'texttext /longsubdir columntext', + \ 'texttext /longlongsubdir columntext']) + exe "normal! 5G0f/\<c-v>2jezy" + norm! 1G0f;hp + call assert_equal(['/path/subdir ;text', '/path/longsubdir ;text', '/path/longlongsubdir;text'], getline(1, 3)) + + " Test 4) Paste using P - with trailing spaces + %d + call setline(1, ['/path;text', '/path;text', '/path;text', '', + \ 'texttext /subdir columntext', + \ 'texttext /longsubdir columntext', + \ 'texttext /longlongsubdir columntext']) + exe "normal! 5G0f/\<c-v>2jezy" + norm! 1G0f;P + call assert_equal(['/path/subdir ;text', '/path/longsubdir ;text', '/path/longlongsubdir;text'], getline(1, 3)) + + " Test 5) Yank with spaces inside the block + %d + call setline(1, ['/path;text', '/path;text', '/path;text', '', + \ 'texttext /sub dir/ columntext', + \ 'texttext /lon gsubdir/ columntext', + \ 'texttext /lon glongsubdir/ columntext']) + exe "normal! 5G0f/\<c-v>2jf/zy" + norm! 1G0f;zP + call assert_equal(['/path/sub dir/;text', '/path/lon gsubdir/;text', '/path/lon glongsubdir/;text'], getline(1, 3)) + bwipe! +endfunc + +func Test_visual_put_blockedit_zy_and_zp() + new + + call setline(1, ['aa', 'bbbbb', 'ccc', '', 'XX', 'GGHHJ', 'RTZU']) + exe "normal! gg0\<c-v>2j$zy" + norm! 5gg0zP + call assert_equal(['aa', 'bbbbb', 'ccc', '', 'aaXX', 'bbbbbGGHHJ', 'cccRTZU'], getline(1, 7)) + " + " now with blockmode editing + sil %d + :set ve=block + call setline(1, ['aa', 'bbbbb', 'ccc', '', 'XX', 'GGHHJ', 'RTZU']) + exe "normal! gg0\<c-v>2j$zy" + norm! 5gg0zP + call assert_equal(['aa', 'bbbbb', 'ccc', '', 'aaXX', 'bbbbbGGHHJ', 'cccRTZU'], getline(1, 7)) + set ve&vim + bw! +endfunc + +func Test_visual_block_with_virtualedit() + CheckScreendump + + let lines =<< trim END + call setline(1, ['aaaaaa', 'bbbb', 'cc']) + set virtualedit=block + normal G + END + call writefile(lines, 'XTest_block') + + let buf = RunVimInTerminal('-S XTest_block', {'rows': 8, 'cols': 50}) + call term_sendkeys(buf, "\<C-V>gg$") + call VerifyScreenDump(buf, 'Test_visual_block_with_virtualedit', {}) + + " clean up + call term_sendkeys(buf, "\<Esc>") + call StopVimInTerminal(buf) + call delete('XTest_beval') +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index a522705238..039de0c623 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -608,7 +608,7 @@ func Test_window_prevwin() " reset q call delete('tmp.txt') - set hidden&vim autoread&vim + set nohidden autoread&vim delfunc Fun_RenewFile endfunc diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index fd83681aed..dcc086a0cf 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -52,21 +52,18 @@ #define OUTBUF_SIZE 0xffff #define TOO_MANY_EVENTS 1000000 -#define STARTS_WITH(str, prefix) (strlen(str) >= (sizeof(prefix) - 1) \ - && 0 == memcmp((str), (prefix), sizeof(prefix) - 1)) -#define TMUX_WRAP(is_tmux, seq) ((is_tmux) \ - ? DCS_STR "tmux;\x1b" seq STERM_STR : seq) -#define SCREEN_TMUX_WRAP(is_screen, is_tmux, seq) \ - ((is_screen) \ - ? DCS_STR seq STERM_STR : (is_tmux) \ - ? DCS_STR "tmux;\x1b" seq STERM_STR : seq) +#define STARTS_WITH(str, prefix) \ + (strlen(str) >= (sizeof(prefix) - 1) \ + && 0 == memcmp((str), (prefix), sizeof(prefix) - 1)) +#define TMUX_WRAP(is_tmux, seq) \ + ((is_tmux) ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) #define LINUXSET0C "\x1b[?0c" #define LINUXSET1C "\x1b[?1c" #ifdef NVIM_UNIBI_HAS_VAR_FROM #define UNIBI_SET_NUM_VAR(var, num) \ do { \ - (var) = unibi_var_from_num((num)); \ + (var) = unibi_var_from_num((num)); \ } while (0) #else #define UNIBI_SET_NUM_VAR(var, num) (var).i = (num); @@ -301,12 +298,6 @@ static void terminfo_start(UI *ui) data->invis, sizeof data->invis); // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); - // Ask the terminal to send us the background color. - // If get_bg is sent at the same time after enter_ca_mode, tmux will not send - // get_bg to the host terminal. To avoid this, send get_bg before - // enter_ca_mode. - data->input.waiting_for_bg_response = 5; - unibi_out_ext(ui, data->unibi_ext.get_bg); // Enter alternate screen, save title, and clear. // NOTE: Do this *before* changing terminal settings. #6433 unibi_out(ui, unibi_enter_ca_mode); @@ -314,6 +305,9 @@ static void terminfo_start(UI *ui) unibi_out_ext(ui, data->unibi_ext.save_title); unibi_out(ui, unibi_keypad_xmit); unibi_out(ui, unibi_clear_screen); + // Ask the terminal to send us the background color. + data->input.waiting_for_bg_response = 5; + unibi_out_ext(ui, data->unibi_ext.get_bg); // Enable bracketed paste unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste); @@ -335,7 +329,6 @@ static void terminfo_start(UI *ui) uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0); uv_pipe_open(&data->output_handle.pipe, data->out_fd); } - flush_buf(ui); } @@ -1032,7 +1025,7 @@ static void tui_mouse_on(UI *ui) if (!data->mouse_enabled) { #ifdef WIN32 // Windows versions with vtp(ENABLE_VIRTUAL_TERMINAL_PROCESSING) and - // no vti(ENABLE_VIRTUAL_TERMINAL_INPUT) will need to use mouse traking of + // no vti(ENABLE_VIRTUAL_TERMINAL_INPUT) will need to use mouse tracking of // libuv. For this reason, vtp (vterm) state of libuv is temporarily // disabled because the control sequence needs to be processed by libuv // instead of Windows vtp. @@ -1055,7 +1048,7 @@ static void tui_mouse_off(UI *ui) if (data->mouse_enabled) { #ifdef WIN32 // Windows versions with vtp(ENABLE_VIRTUAL_TERMINAL_PROCESSING) and - // no vti(ENABLE_VIRTUAL_TERMINAL_INPUT) will need to use mouse traking of + // no vti(ENABLE_VIRTUAL_TERMINAL_INPUT) will need to use mouse tracking of // libuv. For this reason, vtp (vterm) state of libuv is temporarily // disabled because the control sequence needs to be processed by libuv // instead of Windows vtp. @@ -1130,8 +1123,8 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx) data->showing_mode = (ModeShape)mode_idx; } -static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow, - Integer startcol, Integer endcol, +static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, // -V751 + Integer endrow, Integer startcol, Integer endcol, Integer rows, Integer cols FUNC_ATTR_UNUSED) { TUIData *data = ui->data; @@ -1780,10 +1773,8 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, #define XTERM_SETAB_16 \ "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e39%;m" - data->unibi_ext.get_bg = - (int)unibi_add_ext_str(ut, "ext.get_bg", - SCREEN_TMUX_WRAP((screen && !tmux), tmux, - "\x1b]11;?\x07")); + data->unibi_ext.get_bg = (int)unibi_add_ext_str(ut, "ext.get_bg", + "\x1b]11;?\x07"); // Terminals with 256-colour SGR support despite what terminfo says. if (unibi_get_num(ut, unibi_max_colors) < 256) { diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 1ec5189795..9c9aec1cf5 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -214,7 +214,7 @@ void ui_comp_remove_grid(ScreenGrid *grid) grid->comp_index = 0; // recompose the area under the grid - // inefficent when being overlapped: only draw up to grid->comp_index + // inefficient when being overlapped: only draw up to grid->comp_index ui_comp_compose_grid(grid); } @@ -594,7 +594,7 @@ static void ui_comp_msg_set_pos(UI *ui, Integer grid, Integer row, int first_row = MAX((int)row-(scrolled?1:0), 0); compose_area(first_row, Rows-delta, 0, Columns); } else { - // scroll separator togheter with message text + // scroll separator together with message text int first_row = MAX((int)row-(msg_was_scrolled?1:0), 0); ui_composed_call_grid_scroll(1, first_row, Rows, 0, Columns, delta, 0); if (scrolled && !msg_was_scrolled && row > 0) { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index ffd613cec2..fb96d7e6ff 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -234,7 +234,7 @@ int u_save(linenr_T top, linenr_T bot) if (top + 2 == bot) u_saveline((linenr_T)(top + 1)); - return u_savecommon(top, bot, (linenr_T)0, FALSE); + return u_savecommon(curbuf, top, bot, (linenr_T)0, false); } /* @@ -245,7 +245,7 @@ int u_save(linenr_T top, linenr_T bot) */ int u_savesub(linenr_T lnum) { - return u_savecommon(lnum - 1, lnum + 1, lnum + 1, false); + return u_savecommon(curbuf, lnum - 1, lnum + 1, lnum + 1, false); } /* @@ -256,7 +256,7 @@ int u_savesub(linenr_T lnum) */ int u_inssub(linenr_T lnum) { - return u_savecommon(lnum - 1, lnum, lnum + 1, false); + return u_savecommon(curbuf, lnum - 1, lnum, lnum + 1, false); } /* @@ -268,18 +268,19 @@ int u_inssub(linenr_T lnum) */ int u_savedel(linenr_T lnum, long nlines) { - return u_savecommon(lnum - 1, lnum + nlines, - nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, FALSE); + return u_savecommon( + curbuf, lnum - 1, lnum + nlines, + nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, false); } /// Return true when undo is allowed. Otherwise print an error message and /// return false. /// /// @return true if undo is allowed. -bool undo_allowed(void) +bool undo_allowed(buf_T *buf) { - /* Don't allow changes when 'modifiable' is off. */ - if (!MODIFIABLE(curbuf)) { + // Don't allow changes when 'modifiable' is off. + if (!MODIFIABLE(buf)) { EMSG(_(e_modifiable)); return false; } @@ -301,12 +302,12 @@ bool undo_allowed(void) } /// Get the 'undolevels' value for the current buffer. -static long get_undolevel(void) +static long get_undolevel(buf_T *buf) { - if (curbuf->b_p_ul == NO_LOCAL_UNDOLEVEL) { + if (buf->b_p_ul == NO_LOCAL_UNDOLEVEL) { return p_ul; } - return curbuf->b_p_ul; + return buf->b_p_ul; } static inline void zero_fmark_additional_data(fmark_T *fmarks) @@ -326,7 +327,9 @@ static inline void zero_fmark_additional_data(fmark_T *fmarks) * Careful: may trigger autocommands that reload the buffer. * Returns FAIL when lines could not be saved, OK otherwise. */ -int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) +int u_savecommon(buf_T *buf, + linenr_T top, linenr_T bot, + linenr_T newbot, int reload) { linenr_T lnum; long i; @@ -337,22 +340,23 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) long size; if (!reload) { - /* When making changes is not allowed return FAIL. It's a crude way - * to make all change commands fail. */ - if (!undo_allowed()) + // When making changes is not allowed return FAIL. It's a crude way + // to make all change commands fail. + if (!undo_allowed(buf)) { return FAIL; + } + // Saving text for undo means we are going to make a change. Give a + // warning for a read-only file before making the change, so that the + // FileChangedRO event can replace the buffer with a read-write version + // (e.g., obtained from a source control system). + if (buf == curbuf) { + change_warning(buf, 0); + } - /* - * Saving text for undo means we are going to make a change. Give a - * warning for a read-only file before making the change, so that the - * FileChangedRO event can replace the buffer with a read-write version - * (e.g., obtained from a source control system). - */ - change_warning(0); - if (bot > curbuf->b_ml.ml_line_count + 1) { - /* This happens when the FileChangedRO autocommand changes the - * file in a way it becomes shorter. */ + if (bot > buf->b_ml.ml_line_count + 1) { + // This happens when the FileChangedRO autocommand changes the + // file in a way it becomes shorter. EMSG(_("E881: Line count changed unexpectedly")); return FAIL; } @@ -364,18 +368,14 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) size = bot - top - 1; - /* - * If curbuf->b_u_synced == true make a new header. - */ - if (curbuf->b_u_synced) { - /* Need to create new entry in b_changelist. */ - curbuf->b_new_change = true; - - if (get_undolevel() >= 0) { - /* - * Make a new header entry. Do this first so that we don't mess - * up the undo info when out of memory. - */ + // If curbuf->b_u_synced == true make a new header. + if (buf->b_u_synced) { + // Need to create new entry in b_changelist. + buf->b_new_change = true; + + if (get_undolevel(buf) >= 0) { + // Make a new header entry. Do this first so that we don't mess + // up the undo info when out of memory. uhp = xmalloc(sizeof(u_header_T)); kv_init(uhp->uh_extmark); #ifdef U_DEBUG @@ -388,63 +388,73 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) * If we undid more than we redid, move the entry lists before and * including curbuf->b_u_curhead to an alternate branch. */ - old_curhead = curbuf->b_u_curhead; + old_curhead = buf->b_u_curhead; if (old_curhead != NULL) { - curbuf->b_u_newhead = old_curhead->uh_next.ptr; - curbuf->b_u_curhead = NULL; + buf->b_u_newhead = old_curhead->uh_next.ptr; + buf->b_u_curhead = NULL; } /* * free headers to keep the size right */ - while (curbuf->b_u_numhead > get_undolevel() - && curbuf->b_u_oldhead != NULL) { - u_header_T *uhfree = curbuf->b_u_oldhead; - - if (uhfree == old_curhead) - /* Can't reconnect the branch, delete all of it. */ - u_freebranch(curbuf, uhfree, &old_curhead); - else if (uhfree->uh_alt_next.ptr == NULL) - /* There is no branch, only free one header. */ - u_freeheader(curbuf, uhfree, &old_curhead); - else { - /* Free the oldest alternate branch as a whole. */ - while (uhfree->uh_alt_next.ptr != NULL) + while (buf->b_u_numhead > get_undolevel(buf) + && buf->b_u_oldhead != NULL) { + u_header_T *uhfree = buf->b_u_oldhead; + + if (uhfree == old_curhead) { + // Can't reconnect the branch, delete all of it. + u_freebranch(buf, uhfree, &old_curhead); + } else if (uhfree->uh_alt_next.ptr == NULL) { + // There is no branch, only free one header. + u_freeheader(buf, uhfree, &old_curhead); + } else { + // Free the oldest alternate branch as a whole. + while (uhfree->uh_alt_next.ptr != NULL) { uhfree = uhfree->uh_alt_next.ptr; - u_freebranch(curbuf, uhfree, &old_curhead); + } + u_freebranch(buf, uhfree, &old_curhead); } #ifdef U_DEBUG u_check(TRUE); #endif } - if (uhp == NULL) { /* no undo at all */ - if (old_curhead != NULL) - u_freebranch(curbuf, old_curhead, NULL); - curbuf->b_u_synced = false; + if (uhp == NULL) { // no undo at all + if (old_curhead != NULL) { + u_freebranch(buf, old_curhead, NULL); + } + buf->b_u_synced = false; return OK; } uhp->uh_prev.ptr = NULL; - uhp->uh_next.ptr = curbuf->b_u_newhead; + uhp->uh_next.ptr = buf->b_u_newhead; uhp->uh_alt_next.ptr = old_curhead; if (old_curhead != NULL) { uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr; - if (uhp->uh_alt_prev.ptr != NULL) + + if (uhp->uh_alt_prev.ptr != NULL) { uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp; + } + old_curhead->uh_alt_prev.ptr = uhp; - if (curbuf->b_u_oldhead == old_curhead) - curbuf->b_u_oldhead = uhp; - } else + + if (buf->b_u_oldhead == old_curhead) { + buf->b_u_oldhead = uhp; + } + } else { uhp->uh_alt_prev.ptr = NULL; - if (curbuf->b_u_newhead != NULL) - curbuf->b_u_newhead->uh_prev.ptr = uhp; + } + + if (buf->b_u_newhead != NULL) { + buf->b_u_newhead->uh_prev.ptr = uhp; + } - uhp->uh_seq = ++curbuf->b_u_seq_last; - curbuf->b_u_seq_cur = uhp->uh_seq; + uhp->uh_seq = ++buf->b_u_seq_last; + buf->b_u_seq_cur = uhp->uh_seq; uhp->uh_time = time(NULL); uhp->uh_save_nr = 0; - curbuf->b_u_time_cur = uhp->uh_time + 1; + buf->b_u_time_cur = uhp->uh_time + 1; uhp->uh_walk = 0; uhp->uh_entry = NULL; @@ -455,23 +465,26 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) else uhp->uh_cursor_vcol = -1; - /* save changed and buffer empty flag for undo */ - uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) + - ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0); + // save changed and buffer empty flag for undo + uhp->uh_flags = (buf->b_changed ? UH_CHANGED : 0) + + ((buf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0); - /* save named marks and Visual marks for undo */ - zero_fmark_additional_data(curbuf->b_namedm); - memmove(uhp->uh_namedm, curbuf->b_namedm, - sizeof(curbuf->b_namedm[0]) * NMARKS); - uhp->uh_visual = curbuf->b_visual; + // save named marks and Visual marks for undo + zero_fmark_additional_data(buf->b_namedm); + memmove(uhp->uh_namedm, buf->b_namedm, + sizeof(buf->b_namedm[0]) * NMARKS); + uhp->uh_visual = buf->b_visual; - curbuf->b_u_newhead = uhp; - if (curbuf->b_u_oldhead == NULL) - curbuf->b_u_oldhead = uhp; - ++curbuf->b_u_numhead; + buf->b_u_newhead = uhp; + + if (buf->b_u_oldhead == NULL) { + buf->b_u_oldhead = uhp; + } + buf->b_u_numhead++; } else { - if (get_undolevel() < 0) /* no undo at all */ + if (get_undolevel(buf) < 0) { // no undo at all return OK; + } /* * When saving a single line, and it has been saved just before, it @@ -483,7 +496,7 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) * long. */ if (size == 1) { - uep = u_get_headentry(); + uep = u_get_headentry(buf); prev_uep = NULL; for (i = 0; i < 10; ++i) { if (uep == NULL) @@ -491,16 +504,17 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) /* If lines have been inserted/deleted we give up. * Also when the line was included in a multi-line save. */ - if ((curbuf->b_u_newhead->uh_getbot_entry != uep + if ((buf->b_u_newhead->uh_getbot_entry != uep ? (uep->ue_top + uep->ue_size + 1 != (uep->ue_bot == 0 - ? curbuf->b_ml.ml_line_count + 1 + ? buf->b_ml.ml_line_count + 1 : uep->ue_bot)) - : uep->ue_lcount != curbuf->b_ml.ml_line_count) + : uep->ue_lcount != buf->b_ml.ml_line_count) || (uep->ue_size > 1 && top >= uep->ue_top - && top + 2 <= uep->ue_top + uep->ue_size + 1)) + && top + 2 <= uep->ue_top + uep->ue_size + 1)) { break; + } /* If it's the same line we can skip saving it again. */ if (uep->ue_size == 1 && uep->ue_top == top) { @@ -508,8 +522,8 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) /* It's not the last entry: get ue_bot for the last * entry now. Following deleted/inserted lines go to * the re-used entry. */ - u_getbot(); - curbuf->b_u_synced = false; + u_getbot(buf); + buf->b_u_synced = false; /* Move the found entry to become the last entry. The * order of undo/redo doesn't matter for the entries @@ -518,18 +532,18 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) * for the found entry if the line count is changed by * the executed command. */ prev_uep->ue_next = uep->ue_next; - uep->ue_next = curbuf->b_u_newhead->uh_entry; - curbuf->b_u_newhead->uh_entry = uep; + uep->ue_next = buf->b_u_newhead->uh_entry; + buf->b_u_newhead->uh_entry = uep; } - /* The executed command may change the line count. */ - if (newbot != 0) + // The executed command may change the line count. + if (newbot != 0) { uep->ue_bot = newbot; - else if (bot > curbuf->b_ml.ml_line_count) + } else if (bot > buf->b_ml.ml_line_count) { uep->ue_bot = 0; - else { - uep->ue_lcount = curbuf->b_ml.ml_line_count; - curbuf->b_u_newhead->uh_getbot_entry = uep; + } else { + uep->ue_lcount = buf->b_ml.ml_line_count; + buf->b_u_newhead->uh_getbot_entry = uep; } return OK; } @@ -538,8 +552,8 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) } } - /* find line number for ue_bot for previous u_save() */ - u_getbot(); + // find line number for ue_bot for previous u_save() + u_getbot(buf); } /* @@ -553,17 +567,15 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) uep->ue_size = size; uep->ue_top = top; - if (newbot != 0) + if (newbot != 0) { uep->ue_bot = newbot; - /* - * Use 0 for ue_bot if bot is below last line. - * Otherwise we have to compute ue_bot later. - */ - else if (bot > curbuf->b_ml.ml_line_count) + // Use 0 for ue_bot if bot is below last line. + // Otherwise we have to compute ue_bot later. + } else if (bot > buf->b_ml.ml_line_count) { uep->ue_bot = 0; - else { - uep->ue_lcount = curbuf->b_ml.ml_line_count; - curbuf->b_u_newhead->uh_getbot_entry = uep; + } else { + uep->ue_lcount = buf->b_ml.ml_line_count; + buf->b_u_newhead->uh_getbot_entry = uep; } if (size > 0) { @@ -574,17 +586,19 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) u_freeentry(uep, i); return FAIL; } - uep->ue_array[i] = u_save_line(lnum++); + uep->ue_array[i] = u_save_line_buf(buf, lnum++); } - } else + } else { uep->ue_array = NULL; - uep->ue_next = curbuf->b_u_newhead->uh_entry; - curbuf->b_u_newhead->uh_entry = uep; + } + + uep->ue_next = buf->b_u_newhead->uh_entry; + buf->b_u_newhead->uh_entry = uep; if (reload) { // buffer was reloaded, notify text change subscribers curbuf->b_u_newhead->uh_flags |= UH_RELOAD; } - curbuf->b_u_synced = false; + buf->b_u_synced = false; undo_undoes = false; #ifdef U_DEBUG @@ -617,18 +631,20 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s"); -/* - * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE]. - */ -void u_compute_hash(char_u *hash) +/// Compute the hash for a buffer text into hash[UNDO_HASH_SIZE]. +/// +/// @param[in] buf The buffer used to compute the hash +/// @param[in] hash Array of size UNDO_HASH_SIZE in which to store the value of +/// the hash +void u_compute_hash(buf_T *buf, char_u *hash) { context_sha256_T ctx; linenr_T lnum; char_u *p; sha256_start(&ctx); - for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { - p = ml_get(lnum); + for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + p = ml_get_buf(buf, lnum, false); sha256_update(&ctx, p, (uint32_t)(STRLEN(p) + 1)); } sha256_finish(&ctx, hash); @@ -656,6 +672,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) #ifdef HAVE_READLINK char fname_buf[MAXPATHL]; #endif + char *p; if (ffname == NULL) { return NULL; @@ -688,6 +705,13 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) memmove(tail + tail_len + 1, ".un~", sizeof(".un~")); } else { dir_name[dir_len] = NUL; + + // Remove trailing pathseps from directory name + p = &dir_name[dir_len - 1]; + while (vim_ispathsep(*p)) { + *p-- = NUL; + } + bool has_directory = os_isdir((char_u *)dir_name); if (!has_directory && *dirp == NUL && !reading) { // Last directory in the list does not exist, create it. @@ -704,7 +728,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) if (has_directory) { if (munged_name == NULL) { munged_name = xstrdup(ffname); - for (char *p = munged_name; *p != NUL; MB_PTR_ADV(p)) { + for (p = munged_name; *p != NUL; MB_PTR_ADV(p)) { if (vim_ispathsep(*p)) { *p = '%'; } @@ -1002,14 +1026,14 @@ static ExtmarkUndoObject *unserialize_extmark(bufinfo_T *bi, bool *error, extup->type = type; if (type == kExtmarkSplice) { n_elems = (size_t)sizeof(ExtmarkSplice) / sizeof(uint8_t); - buf = xcalloc(sizeof(uint8_t), n_elems); + buf = xcalloc(n_elems, sizeof(uint8_t)); if (!undo_read(bi, buf, n_elems)) { goto error; } extup->data.splice = *(ExtmarkSplice *)buf; } else if (type == kExtmarkMove) { n_elems = (size_t)sizeof(ExtmarkMove) / sizeof(uint8_t); - buf = xcalloc(sizeof(uint8_t), n_elems); + buf = xcalloc(n_elems, sizeof(uint8_t)); if (!undo_read(bi, buf, n_elems)) { goto error; } @@ -1281,8 +1305,8 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, goto theend; } - /* Undo must be synced. */ - u_sync(TRUE); + // Undo must be synced. + u_sync(true); /* * Write the header. @@ -1767,7 +1791,7 @@ void u_undo(int count) * be compatible. */ if (curbuf->b_u_synced == false) { - u_sync(TRUE); + u_sync(true); count = 1; } @@ -1846,8 +1870,9 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event) { int count = startcount; - if (!undo_allowed()) + if (!undo_allowed(curbuf)) { return; + } u_newcount = 0; u_oldcount = 0; @@ -1858,15 +1883,16 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event) * needed. This may cause the file to be reloaded, that must happen * before we do anything, because it may change curbuf->b_u_curhead * and more. */ - change_warning(0); + change_warning(curbuf, 0); if (undo_undoes) { - if (curbuf->b_u_curhead == NULL) /* first undo */ + if (curbuf->b_u_curhead == NULL) { // first undo curbuf->b_u_curhead = curbuf->b_u_newhead; - else if (get_undolevel() > 0) /* multi level undo */ - /* get next undo */ + } else if (get_undolevel(curbuf) > 0) { // multi level undo + // get next undo curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr; - /* nothing to undo */ + } + // nothing to undo if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL) { /* stick curbuf->b_u_curhead at end */ curbuf->b_u_curhead = curbuf->b_u_oldhead; @@ -1880,8 +1906,8 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event) u_undoredo(true, do_buf_event); } else { - if (curbuf->b_u_curhead == NULL || get_undolevel() <= 0) { - beep_flush(); /* nothing to redo */ + if (curbuf->b_u_curhead == NULL || get_undolevel(curbuf) <= 0) { + beep_flush(); // nothing to redo if (count == startcount - 1) { MSG(_("Already at newest change")); return; @@ -1925,9 +1951,10 @@ void undo_time(long step, bool sec, bool file, bool absolute) bool above = false; bool did_undo = true; - /* First make sure the current undoable change is synced. */ - if (curbuf->b_u_synced == false) - u_sync(TRUE); + // First make sure the current undoable change is synced. + if (curbuf->b_u_synced == false) { + u_sync(true); + } u_newcount = 0; u_oldcount = 0; @@ -2122,8 +2149,8 @@ target_zero: if (uhp != NULL || target == 0) { // First go up the tree as much as needed. while (!got_int) { - /* Do the change warning now, for the same reason as above. */ - change_warning(0); + // Do the change warning now, for the same reason as above. + change_warning(curbuf, 0); uhp = curbuf->b_u_curhead; if (uhp == NULL) @@ -2147,7 +2174,7 @@ target_zero: // And now go down the tree (redo), branching off where needed. while (!got_int) { // Do the change warning now, for the same reason as above. - change_warning(0); + change_warning(curbuf, 0); uhp = curbuf->b_u_curhead; if (uhp == NULL) { @@ -2414,7 +2441,7 @@ static void u_undoredo(int undo, bool do_buf_event) curhead->uh_entry = newlist; curhead->uh_flags = new_flags; - if ((old_flags & UH_EMPTYBUF) && BUFEMPTY()) { + if ((old_flags & UH_EMPTYBUF) && buf_is_empty(curbuf)) { curbuf->b_ml.ml_flags |= ML_EMPTY; } if (old_flags & UH_CHANGED) { @@ -2583,21 +2610,20 @@ static void u_undo_end( msgbuf); } -/* - * u_sync: stop adding to the current entry list - */ -void -u_sync( - int force // Also sync when no_u_sync is set. -) +/// u_sync: stop adding to the current entry list +/// +/// @param force if true, also sync when no_u_sync is set. +void u_sync(bool force) { - /* Skip it when already synced or syncing is disabled. */ - if (curbuf->b_u_synced || (!force && no_u_sync > 0)) + // Skip it when already synced or syncing is disabled. + if (curbuf->b_u_synced || (!force && no_u_sync > 0)) { return; - if (get_undolevel() < 0) - curbuf->b_u_synced = true; /* no entries, nothing to do */ - else { - u_getbot(); /* compute ue_bot of previous u_save */ + } + + if (get_undolevel(curbuf) < 0) { + curbuf->b_u_synced = true; // no entries, nothing to do + } else { + u_getbot(curbuf); // compute ue_bot of previous u_save curbuf->b_u_curhead = NULL; } } @@ -2708,7 +2734,7 @@ void ex_undojoin(exarg_T *eap) if (!curbuf->b_u_synced) { return; // already unsynced } - if (get_undolevel() < 0) { + if (get_undolevel(curbuf) < 0) { return; // no entries, nothing to do } else { curbuf->b_u_synced = false; // Append next change to last entry @@ -2744,13 +2770,13 @@ void u_find_first_changed(void) return; for (lnum = 1; lnum < curbuf->b_ml.ml_line_count - && lnum <= uep->ue_size; ++lnum) - if (STRCMP(ml_get_buf(curbuf, lnum, FALSE), - uep->ue_array[lnum - 1]) != 0) { + && lnum <= uep->ue_size; lnum++) { + if (STRCMP(ml_get_buf(curbuf, lnum, false), uep->ue_array[lnum - 1]) != 0) { clearpos(&(uhp->uh_cursor)); uhp->uh_cursor.lnum = lnum; return; } + } if (curbuf->b_ml.ml_line_count != uep->ue_size) { /* lines added or deleted at the end, put the cursor there */ clearpos(&(uhp->uh_cursor)); @@ -2792,38 +2818,39 @@ static void u_unch_branch(u_header_T *uhp) * Get pointer to last added entry. * If it's not valid, give an error message and return NULL. */ -static u_entry_T *u_get_headentry(void) +static u_entry_T *u_get_headentry(buf_T *buf) { - if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL) { + if (buf->b_u_newhead == NULL || buf->b_u_newhead->uh_entry == NULL) { IEMSG(_("E439: undo list corrupt")); return NULL; } - return curbuf->b_u_newhead->uh_entry; + return buf->b_u_newhead->uh_entry; } /* * u_getbot(): compute the line number of the previous u_save * It is called only when b_u_synced is false. */ -static void u_getbot(void) +static void u_getbot(buf_T *buf) { u_entry_T *uep; linenr_T extra; - uep = u_get_headentry(); /* check for corrupt undo list */ - if (uep == NULL) + uep = u_get_headentry(buf); // check for corrupt undo list + if (uep == NULL) { return; + } - uep = curbuf->b_u_newhead->uh_getbot_entry; + uep = buf->b_u_newhead->uh_getbot_entry; if (uep != NULL) { /* * the new ue_bot is computed from the number of lines that has been * inserted (0 - deleted) since calling u_save. This is equal to the * old line count subtracted from the current line count. */ - extra = curbuf->b_ml.ml_line_count - uep->ue_lcount; + extra = buf->b_ml.ml_line_count - uep->ue_lcount; uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra; - if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count) { + if (uep->ue_bot < 1 || uep->ue_bot > buf->b_ml.ml_line_count) { IEMSG(_("E440: undo line missing")); uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will // get all the old lines back @@ -2831,10 +2858,10 @@ static void u_getbot(void) // ones } - curbuf->b_u_newhead->uh_getbot_entry = NULL; + buf->b_u_newhead->uh_getbot_entry = NULL; } - curbuf->b_u_synced = true; + buf->b_u_synced = true; } /* @@ -3014,10 +3041,12 @@ void u_undoline(void) return; } - /* first save the line for the 'u' command */ - if (u_savecommon(curbuf->b_u_line_lnum - 1, - curbuf->b_u_line_lnum + 1, (linenr_T)0, FALSE) == FAIL) + // first save the line for the 'u' command + if (u_savecommon(curbuf, curbuf->b_u_line_lnum - 1, + curbuf->b_u_line_lnum + 1, (linenr_T)0, false) == FAIL) { return; + } + oldp = u_save_line(curbuf->b_u_line_lnum); ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, true); changed_bytes(curbuf->b_u_line_lnum, 0); @@ -3048,12 +3077,21 @@ void u_blockfree(buf_T *buf) xfree(buf->b_u_line_ptr); } -/* - * u_save_line(): allocate memory and copy line 'lnum' into it. - */ +/// Allocate memory and copy curbuf line into it. +/// +/// @param lnum the line to copy static char_u *u_save_line(linenr_T lnum) { - return vim_strsave(ml_get(lnum)); + return u_save_line_buf(curbuf, lnum); +} + +/// Allocate memory and copy line into it +/// +/// @param lnum line to copy +/// @param buf buffer to copy from +static char_u *u_save_line_buf(buf_T *buf, linenr_T lnum) +{ + return vim_strsave(ml_get_buf(buf, lnum, false)); } /// Check if the 'modified' flag is set, or 'ff' has changed (only need to @@ -3143,18 +3181,16 @@ u_header_T *u_force_get_undo_header(buf_T *buf) if (!uhp) { // Undo is normally invoked in change code, which already has swapped // curbuf. - buf_T *save_curbuf = curbuf; - curbuf = buf; // Args are tricky: this means replace empty range by empty range.. - u_savecommon(0, 1, 1, true); + u_savecommon(curbuf, 0, 1, 1, true); + uhp = buf->b_u_curhead; if (!uhp) { uhp = buf->b_u_newhead; - if (get_undolevel() > 0 && !uhp) { + if (get_undolevel(curbuf) > 0 && !uhp) { abort(); } } - curbuf = save_curbuf; } return uhp; } diff --git a/src/nvim/version.c b/src/nvim/version.c index f3a30630f8..7c197f1b7f 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -13,6 +13,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/buffer.h" #include "nvim/iconv.h" #include "nvim/version.h" #include "nvim/charset.h" @@ -38,7 +39,10 @@ NVIM_VERSION_PRERELEASE char *Version = VIM_VERSION_SHORT; char *longVersion = NVIM_VERSION_LONG; char *version_buildtype = "Build type: " NVIM_VERSION_BUILD_TYPE; +// Reproducible builds: omit compile info in Release builds. #15424 +#ifndef NDEBUG char *version_cflags = "Compilation: " NVIM_VERSION_CFLAGS; +#endif #ifdef INCLUDE_GENERATED_DECLARATIONS # include "version.c.generated.h" @@ -2142,7 +2146,9 @@ void list_version(void) MSG(longVersion); MSG(version_buildtype); list_lua_version(); +#ifndef NDEBUG MSG(version_cflags); +#endif #ifdef HAVE_PATHDEF @@ -2190,7 +2196,7 @@ void list_version(void) /// Show the intro message when not editing a file. void maybe_intro_message(void) { - if (BUFEMPTY() + if (buf_is_empty(curbuf) && (curbuf->b_fname == NULL) && (firstwin->w_next == NULL) && (vim_strchr(p_shm, SHM_INTRO) == NULL)) { diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 0245c472ef..c719c064e2 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -270,7 +270,7 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() /// On some systems case in a file name does not matter, on others it does. /// /// @note Does not account for maximum name lengths and things like "../dir", -/// thus it is not 100% accurate. OS may also use different algorythm for +/// thus it is not 100% accurate. OS may also use different algorithm for /// case-insensitive comparison. /// /// @param[in] x First file name to compare. @@ -313,6 +313,7 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() #define DIP_NORTP 0x20 // do not use 'runtimepath' #define DIP_NOAFTER 0x40 // skip "after" directories #define DIP_AFTER 0x80 // only use "after" directories +#define DIP_LUA 0x100 // also use ".lua" files // Lowest number used for window ID. Cannot have this many windows per tab. #define LOWEST_WIN_ID 1000 diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index e9d82ca87d..f5bd5479c4 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -351,7 +351,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) } if (exp_start) { vim_str2nr(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part, - (int)(ret.len - exp_start)); + (int)(ret.len - exp_start), false); } if (exp_negative) { exp_part += frac_size; @@ -369,7 +369,7 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) int len; int prep; vim_str2nr(pline.data, &prep, &len, STR2NR_ALL, NULL, - &ret.data.num.val.integer, (int)pline.size); + &ret.data.num.val.integer, (int)pline.size, false); ret.len = (size_t)len; const uint8_t bases[] = { [0] = 10, diff --git a/src/nvim/window.c b/src/nvim/window.c index acdc40dc74..55a7882401 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5,11 +5,8 @@ #include <inttypes.h> #include <stdbool.h> -#include "nvim/api/private/handle.h" #include "nvim/api/private/helpers.h" -#include "nvim/vim.h" #include "nvim/ascii.h" -#include "nvim/window.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -21,8 +18,10 @@ #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" +#include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/main.h" @@ -31,14 +30,14 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/file_search.h" -#include "nvim/garray.h" -#include "nvim/move.h" #include "nvim/mouse.h" +#include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/os/os.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/screen.h" @@ -47,10 +46,11 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/terminal.h" -#include "nvim/undo.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" -#include "nvim/os/os.h" +#include "nvim/undo.h" +#include "nvim/vim.h" +#include "nvim/window.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -60,26 +60,21 @@ -#define NOWIN (win_T *)-1 /* non-existing window */ +#define NOWIN (win_T *)-1 // non-existing window # define ROWS_AVAIL (Rows - p_ch - tabline_height()) static char *m_onlyone = N_("Already only one window"); -/* - * all CTRL-W window commands are handled here, called from normal_cmd(). - */ -void -do_window( - int nchar, - long Prenum, - int xchar /* extra char from ":wincmd gx" or NUL */ -) +/// all CTRL-W window commands are handled here, called from normal_cmd(). +/// +/// @param xchar extra char from ":wincmd gx" or NUL +void do_window(int nchar, long Prenum, int xchar) { long Prenum1; - win_T *wp; - char_u *ptr; + win_T *wp; + char_u *ptr; linenr_T lnum = -1; int type = FIND_DEFINE; size_t len; @@ -96,7 +91,7 @@ do_window( } while (0) switch (nchar) { - /* split current window in two parts, horizontally */ + // split current window in two parts, horizontally case 'S': case Ctrl_S: case 's': @@ -110,7 +105,7 @@ do_window( (void)win_split((int)Prenum, 0); break; - /* split current window in two parts, vertically */ + // split current window in two parts, vertically case Ctrl_V: case 'v': CHECK_CMDWIN; @@ -123,7 +118,7 @@ do_window( (void)win_split((int)Prenum, WSP_VERT); break; - /* split current window and edit alternate file */ + // split current window and edit alternate file case Ctrl_HAT: case '^': CHECK_CMDWIN; @@ -144,17 +139,18 @@ do_window( } break; - /* open new window */ + // open new window case Ctrl_N: case 'n': CHECK_CMDWIN; reset_VIsual_and_resel(); // stop Visual mode newwindow: - if (Prenum) - /* window height */ + if (Prenum) { + // window height vim_snprintf(cbuf, sizeof(cbuf) - 5, "%" PRId64, (int64_t)Prenum); - else + } else { cbuf[0] = NUL; + } if (nchar == 'v' || nchar == Ctrl_V) { xstrlcat(cbuf, "v", sizeof(cbuf)); } @@ -162,23 +158,23 @@ newwindow: do_cmdline_cmd(cbuf); break; - /* quit current window */ + // quit current window case Ctrl_Q: case 'q': - reset_VIsual_and_resel(); /* stop Visual mode */ + reset_VIsual_and_resel(); // stop Visual mode cmd_with_count("quit", (char_u *)cbuf, sizeof(cbuf), Prenum); do_cmdline_cmd(cbuf); break; - /* close current window */ + // close current window case Ctrl_C: case 'c': - reset_VIsual_and_resel(); /* stop Visual mode */ + reset_VIsual_and_resel(); // stop Visual mode cmd_with_count("close", (char_u *)cbuf, sizeof(cbuf), Prenum); do_cmdline_cmd(cbuf); break; - /* close preview window */ + // close preview window case Ctrl_Z: case 'z': CHECK_CMDWIN; @@ -186,7 +182,7 @@ newwindow: do_cmdline_cmd("pclose"); break; - /* cursor to preview window */ + // cursor to preview window case 'P': wp = NULL; FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) { @@ -202,7 +198,7 @@ newwindow: } break; - /* close all but current window */ + // close all but current window case Ctrl_O: case 'o': CHECK_CMDWIN; @@ -211,10 +207,10 @@ newwindow: do_cmdline_cmd(cbuf); break; - /* cursor to next window with wrap around */ + // cursor to next window with wrap around case Ctrl_W: case 'w': - /* cursor to previous window with wrap around */ + // cursor to previous window with wrap around case 'W': CHECK_CMDWIN; if (ONE_WINDOW && Prenum != 1) { // just one window @@ -222,10 +218,11 @@ newwindow: } else { if (Prenum) { // go to specified window for (wp = firstwin; --Prenum > 0; ) { - if (wp->w_next == NULL) + if (wp->w_next == NULL) { break; - else + } else { wp = wp->w_next; + } } } else { if (nchar == 'W') { // go to previous window @@ -252,7 +249,7 @@ newwindow: } break; - /* cursor to window below */ + // cursor to window below case 'j': case K_DOWN: case Ctrl_J: @@ -260,7 +257,7 @@ newwindow: win_goto_ver(false, Prenum1); break; - /* cursor to window above */ + // cursor to window above case 'k': case K_UP: case Ctrl_K: @@ -268,7 +265,7 @@ newwindow: win_goto_ver(true, Prenum1); break; - /* cursor to left window */ + // cursor to left window case 'h': case K_LEFT: case Ctrl_H: @@ -277,7 +274,7 @@ newwindow: win_goto_hor(true, Prenum1); break; - /* cursor to right window */ + // cursor to right window case 'l': case K_RIGHT: case Ctrl_L: @@ -285,13 +282,13 @@ newwindow: win_goto_hor(false, Prenum1); break; - /* move window to new tab page */ + // move window to new tab page case 'T': - if (one_window()) + if (one_window()) { MSG(_(m_onlyone)); - else { - tabpage_T *oldtab = curtab; - tabpage_T *newtab; + } else { + tabpage_T *oldtab = curtab; + tabpage_T *newtab; /* First create a new tab with the window, then go back to * the old tab and close the window there. */ @@ -311,19 +308,19 @@ newwindow: } break; - /* cursor to top-left window */ + // cursor to top-left window case 't': case Ctrl_T: win_goto(firstwin); break; - /* cursor to bottom-right window */ + // cursor to bottom-right window case 'b': case Ctrl_B: win_goto(lastwin_nofloating()); break; - /* cursor to last accessed (previous) window */ + // cursor to last accessed (previous) window case 'p': case Ctrl_P: if (!win_valid(prevwin)) { @@ -333,14 +330,14 @@ newwindow: } break; - /* exchange current and next window */ + // exchange current and next window case 'x': case Ctrl_X: CHECK_CMDWIN; win_exchange(Prenum); break; - /* rotate windows downwards */ + // rotate windows downwards case Ctrl_R: case 'r': CHECK_CMDWIN; @@ -348,14 +345,14 @@ newwindow: win_rotate(false, (int)Prenum1); // downwards break; - /* rotate windows upwards */ + // rotate windows upwards case 'R': CHECK_CMDWIN; reset_VIsual_and_resel(); // stop Visual mode win_rotate(true, (int)Prenum1); // upwards break; - /* move window to the very top/bottom/left/right */ + // move window to the very top/bottom/left/right case 'K': case 'J': case 'H': @@ -366,43 +363,43 @@ newwindow: | ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT)); break; - /* make all windows the same height */ + // make all windows the same height case '=': win_equal(NULL, false, 'b'); break; - /* increase current window height */ + // increase current window height case '+': win_setheight(curwin->w_height + (int)Prenum1); break; - /* decrease current window height */ + // decrease current window height case '-': win_setheight(curwin->w_height - (int)Prenum1); break; - /* set current window height */ + // set current window height case Ctrl__: case '_': win_setheight(Prenum ? (int)Prenum : Rows-1); break; - /* increase current window width */ + // increase current window width case '>': win_setwidth(curwin->w_width + (int)Prenum1); break; - /* decrease current window width */ + // decrease current window width case '<': win_setwidth(curwin->w_width - (int)Prenum1); break; - /* set current window width */ + // set current window width case '|': win_setwidth(Prenum != 0 ? (int)Prenum : Columns); break; - /* jump to tag and split window if tag exists (in preview window) */ + // jump to tag and split window if tag exists (in preview window) case '}': CHECK_CMDWIN; if (Prenum) { @@ -415,10 +412,11 @@ newwindow: case Ctrl_RSB: CHECK_CMDWIN; // Keep visual mode, can select words to use as a tag. - if (Prenum) + if (Prenum) { postponed_split = Prenum; - else + } else { postponed_split = -1; + } if (nchar != '}') { g_do_tagpreview = 0; @@ -429,7 +427,7 @@ newwindow: do_nv_ident(Ctrl_RSB, NUL); break; - /* edit file name under cursor in a new window */ + // edit file name under cursor in a new window case 'f': case 'F': case Ctrl_F: @@ -460,7 +458,7 @@ wingotofile: /* Go to the first occurrence of the identifier under cursor along path in a * new window -- webb */ - case 'i': /* Go to any match */ + case 'i': // Go to any match case Ctrl_I: type = FIND_ANY; FALLTHROUGH; @@ -484,7 +482,7 @@ wingotofile: break; - /* CTRL-W g extended commands */ + // CTRL-W g extended commands case 'g': case Ctrl_G: CHECK_CMDWIN; @@ -498,18 +496,20 @@ wingotofile: switch (xchar) { case '}': xchar = Ctrl_RSB; - if (Prenum) + if (Prenum) { g_do_tagpreview = Prenum; - else + } else { g_do_tagpreview = p_pvh; + } FALLTHROUGH; case ']': case Ctrl_RSB: // Keep visual mode, can select words to use as a tag. - if (Prenum) + if (Prenum) { postponed_split = Prenum; - else + } else { postponed_split = -1; + } /* Execute the command right here, required when * "wincmd g}" was used in a function. */ @@ -520,8 +520,8 @@ wingotofile: goto_tabpage_lastused(); break; - case 'f': /* CTRL-W gf: "gf" in a new tab page */ - case 'F': /* CTRL-W gF: "gF" in a new tab page */ + case 'f': // CTRL-W gf: "gf" in a new tab page + case 'F': // CTRL-W gF: "gF" in a new tab page cmdmod.tab = tabpage_index(curtab) + 1; nchar = xchar; goto wingotofile; @@ -555,13 +555,13 @@ wingotofile: } break; - default: beep_flush(); + default: + beep_flush(); break; } } -static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, - int64_t Prenum) +static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, int64_t Prenum) { size_t len = xstrlcpy((char *)bufp, cmd, bufsize); @@ -676,7 +676,7 @@ void win_set_minimal_style(win_T *wp) } // signcolumn: use 'auto' - if (wp->w_p_scl[0] != 'a') { + if (wp->w_p_scl[0] != 'a' || STRLEN(wp->w_p_scl) >= 8) { xfree(wp->w_p_scl); wp->w_p_scl = (char_u *)xstrdup("auto"); } @@ -866,21 +866,21 @@ void ui_ext_win_position(win_T *wp) } else { ui_call_win_external_pos(wp->w_grid_alloc.handle, wp->handle); } - } void ui_ext_win_viewport(win_T *wp) { if ((wp == curwin || ui_has(kUIMultigrid)) && wp->w_viewport_invalid) { int botline = wp->w_botline; - if (botline == wp->w_buffer->b_ml.ml_line_count+1 - && wp->w_empty_rows == 0) { + int line_count = wp->w_buffer->b_ml.ml_line_count; + if (botline == line_count+1 && wp->w_empty_rows == 0) { // TODO(bfredl): The might be more cases to consider, like how does this // interact with incomplete final line? Diff filler lines? botline = wp->w_buffer->b_ml.ml_line_count; } ui_call_win_viewport(wp->w_grid_alloc.handle, wp->handle, wp->w_topline-1, - botline, wp->w_cursor.lnum-1, wp->w_cursor.col); + botline, wp->w_cursor.lnum-1, wp->w_cursor.col, + line_count); wp->w_viewport_invalid = false; } } @@ -902,11 +902,12 @@ void ui_ext_win_viewport(win_T *wp) */ int win_split(int size, int flags) { - /* When the ":tab" modifier was used open a new tab page instead. */ - if (may_open_tabpage() == OK) + // When the ":tab" modifier was used open a new tab page instead. + if (may_open_tabpage() == OK) { return OK; + } - /* Add flags from ":vertical", ":topleft" and ":botright". */ + // Add flags from ":vertical", ":topleft" and ":botright". flags |= cmdmod.split; if ((flags & WSP_TOP) && (flags & WSP_BOT)) { EMSG(_("E442: Can't split topleft and botright at the same time")); @@ -915,10 +916,11 @@ int win_split(int size, int flags) /* When creating the help window make a snapshot of the window layout. * Otherwise clear the snapshot, it's now invalid. */ - if (flags & WSP_HELP) + if (flags & WSP_HELP) { make_snapshot(SNAP_HELP_IDX); - else + } else { clear_snapshot(curtab, SNAP_HELP_IDX); + } return win_split_ins(size, flags, NULL, 0); } @@ -931,17 +933,17 @@ int win_split(int size, int flags) */ int win_split_ins(int size, int flags, win_T *new_wp, int dir) { - win_T *wp = new_wp; - win_T *oldwin; + win_T *wp = new_wp; + win_T *oldwin; int new_size = size; int i; int need_status = 0; - int do_equal = FALSE; + bool do_equal = false; int needed; int available; int oldwin_height = 0; int layout; - frame_T *frp, *curfrp, *frp2, *prevfrp; + frame_T *frp, *curfrp, *frp2, *prevfrp; int before; int minheight; int wmh1; @@ -1013,8 +1015,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) EMSG(_(e_noroom)); return FAIL; } - if (new_size == 0) + if (new_size == 0) { new_size = oldwin->w_width / 2; + } if (new_size > available - minwidth - 1) { new_size = available - minwidth - 1; } @@ -1022,9 +1025,10 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) new_size = wmw1; } - /* if it doesn't fit in the current window, need win_equal() */ - if (oldwin->w_width - new_size - 1 < p_wmw) - do_equal = TRUE; + // if it doesn't fit in the current window, need win_equal() + if (oldwin->w_width - new_size - 1 < p_wmw) { + do_equal = true; + } // We don't like to take lines for the new window from a // 'winfixwidth' window. Take them from a window to the left or right @@ -1041,9 +1045,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) while (frp != NULL) { if (frp->fr_win != oldwin && frp->fr_win != NULL && (frp->fr_win->w_width > new_size - || frp->fr_win->w_width > oldwin->w_width - - new_size - 1)) { - do_equal = TRUE; + || frp->fr_win->w_width > (oldwin->w_width + - new_size - 1))) { + do_equal = true; break; } frp = frp->fr_next; @@ -1096,8 +1100,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) oldwin->w_status_height = STATUS_HEIGHT; oldwin_height -= STATUS_HEIGHT; } - if (new_size == 0) + if (new_size == 0) { new_size = oldwin_height / 2; + } if (new_size > available - minheight - STATUS_HEIGHT) { new_size = available - minheight - STATUS_HEIGHT; @@ -1106,9 +1111,10 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) new_size = wmh1; } - /* if it doesn't fit in the current window, need win_equal() */ - if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh) - do_equal = TRUE; + // if it doesn't fit in the current window, need win_equal() + if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh) { + do_equal = true; + } /* We don't like to take lines for the new window from a * 'winfixheight' window. Take them from a window above or below @@ -1120,10 +1126,11 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) did_set_fraction = true; win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT, - oldwin); + oldwin); oldwin_height = oldwin->w_height; - if (need_status) + if (need_status) { oldwin_height -= STATUS_HEIGHT; + } } /* Only make all windows the same height if one of them (except oldwin) @@ -1137,7 +1144,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) && (frp->fr_win->w_height > new_size || frp->fr_win->w_height > oldwin_height - new_size - STATUS_HEIGHT)) { - do_equal = TRUE; + do_equal = true; break; } frp = frp->fr_next; @@ -1152,28 +1159,29 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) && ((flags & WSP_BOT) || (flags & WSP_BELOW) || (!(flags & WSP_ABOVE) - && ( - (flags & WSP_VERT) ? p_spr : - p_sb)))) { - /* new window below/right of current one */ - if (new_wp == NULL) - wp = win_alloc(oldwin, FALSE); - else + && ((flags & WSP_VERT) ? p_spr : p_sb)))) { + // new window below/right of current one + if (new_wp == NULL) { + wp = win_alloc(oldwin, false); + } else { win_append(oldwin, wp); + } } else { - if (new_wp == NULL) - wp = win_alloc(oldwin->w_prev, FALSE); - else + if (new_wp == NULL) { + wp = win_alloc(oldwin->w_prev, false); + } else { win_append(oldwin->w_prev, wp); + } } if (new_wp == NULL) { - if (wp == NULL) + if (wp == NULL) { return FAIL; + } new_frame(wp); - /* make the contents of the new window the same as the current one */ + // 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); @@ -1189,25 +1197,29 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0) || (topframe->fr_layout == FR_ROW && (flags & WSP_VERT) != 0)) { curfrp = topframe->fr_child; - if (flags & WSP_BOT) - while (curfrp->fr_next != NULL) + if (flags & WSP_BOT) { + while (curfrp->fr_next != NULL) { curfrp = curfrp->fr_next; - } else + } + } + } else { curfrp = topframe; + } before = (flags & WSP_TOP); } else { curfrp = oldwin->w_frame; - if (flags & WSP_BELOW) + if (flags & WSP_BELOW) { before = FALSE; - else if (flags & WSP_ABOVE) + } else if (flags & WSP_ABOVE) { before = TRUE; - else if (flags & WSP_VERT) + } else if (flags & WSP_VERT) { before = !p_spr; - else + } else { before = !p_sb; + } } if (curfrp->fr_parent == NULL || curfrp->fr_parent->fr_layout != layout) { - /* Need to create a new frame in the tree to make a branch. */ + // Need to create a new frame in the tree to make a branch. frp = xcalloc(1, sizeof(frame_T)); *frp = *curfrp; curfrp->fr_layout = layout; @@ -1226,17 +1238,19 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } } - if (new_wp == NULL) + if (new_wp == NULL) { frp = wp->w_frame; - else + } else { frp = new_wp->w_frame; + } frp->fr_parent = curfrp->fr_parent; - /* Insert the new frame at the right place in the frame list. */ - if (before) + // Insert the new frame at the right place in the frame list. + if (before) { frame_insert(curfrp, frp); - else + } else { frame_append(curfrp, frp); + } /* Set w_fraction now so that the cursor keeps the same relative * vertical position. */ @@ -1253,12 +1267,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) oldwin->w_status_height = need_status; } if (flags & (WSP_TOP | WSP_BOT)) { - /* set height and row of new window to full height */ + // set height and row of new window to full height wp->w_winrow = tabline_height(); win_new_height(wp, curfrp->fr_height - (p_ls > 0)); wp->w_status_height = (p_ls > 0); } else { - /* height and row of new window is same as current window */ + // height and row of new window is same as current window wp->w_winrow = oldwin->w_winrow; win_new_height(wp, oldwin->w_height); wp->w_status_height = oldwin->w_status_height; @@ -1268,30 +1282,33 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) /* "new_size" of the current window goes to the new window, use * one column for the vertical separator */ win_new_width(wp, new_size); - if (before) + if (before) { wp->w_vsep_width = 1; - else { + } else { wp->w_vsep_width = oldwin->w_vsep_width; oldwin->w_vsep_width = 1; } if (flags & (WSP_TOP | WSP_BOT)) { - if (flags & WSP_BOT) + if (flags & WSP_BOT) { frame_add_vsep(curfrp); - /* Set width of neighbor frame */ + } + // Set width of neighbor frame frame_new_width(curfrp, curfrp->fr_width - - (new_size + ((flags & WSP_TOP) != 0)), flags & WSP_TOP, - FALSE); - } else + - (new_size + ((flags & WSP_TOP) != 0)), flags & WSP_TOP, + false); + } else { win_new_width(oldwin, oldwin->w_width - (new_size + 1)); - if (before) { /* new window left of current one */ + } + if (before) { // new window left of current one wp->w_wincol = oldwin->w_wincol; oldwin->w_wincol += new_size + 1; - } else /* new window right of current one */ + } else { // new window right of current one wp->w_wincol = oldwin->w_wincol + oldwin->w_width + 1; + } frame_fix_width(oldwin); frame_fix_width(wp); } else { - /* width and column of new window is same as current window */ + // width and column of new window is same as current window if (flags & (WSP_TOP | WSP_BOT)) { wp->w_wincol = 0; win_new_width(wp, Columns); @@ -1327,14 +1344,16 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) oldwin->w_status_height = STATUS_HEIGHT; } } - if (flags & WSP_BOT) + if (flags & WSP_BOT) { frame_add_statusline(curfrp); + } frame_fix_height(wp); frame_fix_height(oldwin); } - if (flags & (WSP_TOP | WSP_BOT)) + if (flags & (WSP_TOP | WSP_BOT)) { (void)win_comp_pos(); + } // Both windows need redrawing. Update all status lines, in case they // show something related to the window count or position. @@ -1345,32 +1364,34 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (need_status) { msg_row = Rows - 1; msg_col = sc_col; - msg_clr_eos_force(); /* Old command/ruler may still be there */ + msg_clr_eos_force(); // Old command/ruler may still be there comp_col(); msg_row = Rows - 1; - msg_col = 0; /* put position back at start of line */ + msg_col = 0; // put position back at start of line } /* * equalize the window sizes. */ - if (do_equal || dir != 0) + if (do_equal || dir != 0) { win_equal(wp, true, - (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') - : dir == 'h' ? 'b' : - 'v'); + (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') + : dir == 'h' ? 'b' : + 'v'); + } /* Don't change the window height/width to 'winheight' / 'winwidth' if a * size was given. */ if (flags & WSP_VERT) { i = p_wiw; - if (size != 0) + if (size != 0) { p_wiw = size; - + } } else { i = p_wh; - if (size != 0) + if (size != 0) { p_wh = size; + } } // Keep same changelist position in new window. @@ -1426,7 +1447,7 @@ static void win_init(win_T *newp, win_T *oldp, int flags) newp->w_prev_fraction_row = oldp->w_prev_fraction_row; copy_jumplist(oldp, newp); if (flags & WSP_NEWLOC) { - /* Don't copy the location list. */ + // Don't copy the location list. newp->w_llist = NULL; newp->w_llist_ref = NULL; } else { @@ -1435,7 +1456,7 @@ static void win_init(win_T *newp, win_T *oldp, int flags) newp->w_localdir = (oldp->w_localdir == NULL) ? NULL : vim_strsave(oldp->w_localdir); - /* copy tagstack and folds */ + // copy tagstack and folds for (i = 0; i < oldp->w_tagstacklen; i++) { taggy_T *tag = &newp->w_tagstack[i]; *tag = oldp->w_tagstack[i]; @@ -1461,16 +1482,16 @@ static void win_init(win_T *newp, win_T *oldp, int flags) */ static void win_init_some(win_T *newp, win_T *oldp) { - /* Use the same argument list. */ + // Use the same argument list. newp->w_alist = oldp->w_alist; ++newp->w_alist->al_refcount; newp->w_arg_idx = oldp->w_arg_idx; - /* copy options from existing window */ + // copy options from existing window win_copy_options(oldp, newp); } -/// Return TRUE if "win" is floating window in the current tab page. +/// Return true if "win" is floating window in the current tab page. /// /// @param win window to check bool win_valid_floating(const win_T *win) @@ -1548,17 +1569,14 @@ int win_count(void) return count; } -/* - * Make "count" windows on the screen. - * Return actual number of windows on the screen. - * Must be called when there is just one window, filling the whole screen - * (excluding the command line). - */ -int -make_windows ( - int count, - int vertical /* split windows vertically if TRUE */ -) +/// Make "count" windows on the screen. +/// Must be called when there is just one window, filling the whole screen +/// (excluding the command line). +/// +/// @param vertical split windows vertically if true +/// +/// @return actual number of windows on the screen. +int make_windows(int count, bool vertical) { int maxcount; int todo; @@ -1575,16 +1593,19 @@ make_windows ( - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT); } - if (maxcount < 2) + if (maxcount < 2) { maxcount = 2; - if (count > maxcount) + } + if (count > maxcount) { count = maxcount; + } /* * add status line now, otherwise first window will be too big */ - if (count > 1) - last_status(TRUE); + if (count > 1) { + last_status(true); + } /* * Don't execute autocommands while creating the windows. Must do that @@ -1592,22 +1613,25 @@ make_windows ( */ block_autocmds(); - /* todo is number of windows left to create */ - for (todo = count - 1; todo > 0; --todo) + // todo is number of windows left to create + for (todo = count - 1; todo > 0; --todo) { if (vertical) { if (win_split(curwin->w_width - (curwin->w_width - todo) - / (todo + 1) - 1, WSP_VERT | WSP_ABOVE) == FAIL) + / (todo + 1) - 1, WSP_VERT | WSP_ABOVE) == FAIL) { break; + } } else { if (win_split(curwin->w_height - (curwin->w_height - todo * STATUS_HEIGHT) / (todo + 1) - - STATUS_HEIGHT, WSP_ABOVE) == FAIL) + - STATUS_HEIGHT, WSP_ABOVE) == FAIL) { break; + } } + } unblock_autocmds(); - /* return actual number of windows */ + // return actual number of windows return count - todo; } @@ -1616,10 +1640,10 @@ make_windows ( */ static void win_exchange(long Prenum) { - frame_T *frp; - frame_T *frp2; - win_T *wp; - win_T *wp2; + frame_T *frp; + frame_T *frp2; + win_T *wp; + win_T *wp2; int temp; if (curwin->w_floating) { @@ -1639,17 +1663,20 @@ static void win_exchange(long Prenum) */ if (Prenum) { frp = curwin->w_frame->fr_parent->fr_child; - while (frp != NULL && --Prenum > 0) + while (frp != NULL && --Prenum > 0) { frp = frp->fr_next; - } else if (curwin->w_frame->fr_next != NULL) /* Swap with next */ + } + } else if (curwin->w_frame->fr_next != NULL) { // Swap with next frp = curwin->w_frame->fr_next; - else /* Swap last window in row/col with previous */ + } else { // Swap last window in row/col with previous frp = curwin->w_frame->fr_prev; + } /* We can only exchange a window with another window, not with a frame * containing windows. */ - if (frp == NULL || frp->fr_win == NULL || frp->fr_win == curwin) + if (frp == NULL || frp->fr_win == NULL || frp->fr_win == curwin) { return; + } wp = frp->fr_win; /* @@ -1672,10 +1699,11 @@ static void win_exchange(long Prenum) win_remove(wp, NULL); frame_remove(wp->w_frame); win_append(wp2, wp); - if (frp2 == NULL) + if (frp2 == NULL) { frame_insert(wp->w_frame->fr_parent->fr_child, wp->w_frame); - else + } else { frame_append(frp2, wp->w_frame); + } } temp = curwin->w_status_height; curwin->w_status_height = wp->w_status_height; @@ -1700,7 +1728,7 @@ static void win_exchange(long Prenum) frame_fix_width(wp); } - (void)win_comp_pos(); /* recompute window positions */ + (void)win_comp_pos(); // recompute window positions win_enter(wp, true); redraw_later(curwin, NOT_VALID); @@ -1711,9 +1739,9 @@ static void win_exchange(long Prenum) // if upwards false the first window becomes the second one static void win_rotate(bool upwards, int count) { - win_T *wp1; - win_T *wp2; - frame_T *frp; + win_T *wp1; + win_T *wp2; + frame_T *frp; int n; if (curwin->w_floating) { @@ -1736,8 +1764,8 @@ static void win_rotate(bool upwards, int count) } while (count--) { - if (upwards) { /* first window becomes last window */ - /* remove first window/frame from the list */ + if (upwards) { // first window becomes last window + // remove first window/frame from the list frp = curwin->w_frame->fr_parent->fr_child; assert(frp != NULL); wp1 = frp->fr_win; @@ -1745,30 +1773,32 @@ static void win_rotate(bool upwards, int count) frame_remove(frp); assert(frp->fr_parent->fr_child); - /* find last frame and append removed window/frame after it */ - for (; frp->fr_next != NULL; frp = frp->fr_next) + // 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); frame_append(frp, wp1->w_frame); - wp2 = frp->fr_win; /* previously last window */ - } else { /* last window becomes first window */ - /* find last window/frame in the list and remove it */ + wp2 = frp->fr_win; // previously last window + } else { // last window becomes first window + // find last window/frame in the list and remove it for (frp = curwin->w_frame; frp->fr_next != NULL; - frp = frp->fr_next) + frp = frp->fr_next) { ; + } wp1 = frp->fr_win; - wp2 = wp1->w_prev; /* will become last window */ + wp2 = wp1->w_prev; // will become last window win_remove(wp1, NULL); frame_remove(frp); assert(frp->fr_parent->fr_child); - /* append the removed window/frame before the first in the list */ + // append the removed window/frame before the first in the list win_append(frp->fr_parent->fr_child->fr_win->w_prev, wp1); frame_insert(frp->fr_parent->fr_child, frp); } - /* exchange status height and vsep width of old and new last window */ + // exchange status height and vsep width of old and new last window n = wp2->w_status_height; wp2->w_status_height = wp1->w_status_height; wp1->w_status_height = n; @@ -1780,7 +1810,7 @@ static void win_rotate(bool upwards, int count) frame_fix_width(wp1); frame_fix_width(wp2); - /* recompute w_winrow and w_wincol for all windows */ + // recompute w_winrow and w_wincol for all windows (void)win_comp_pos(); } @@ -1817,17 +1847,17 @@ static void win_totop(int size, int flags) (void)winframe_remove(curwin, &dir, NULL); } win_remove(curwin, NULL); - last_status(FALSE); /* may need to remove last status line */ - (void)win_comp_pos(); /* recompute window positions */ + last_status(false); // may need to remove last status line + (void)win_comp_pos(); // recompute window positions - /* Split a window on the desired side and put the window there. */ + // Split a window on the desired side and put the window there. (void)win_split_ins(size, flags, curwin, dir); if (!(flags & WSP_VERT)) { win_setheight(height); - if (p_ea) + if (p_ea) { win_equal(curwin, true, 'v'); + } } - } /* @@ -1838,11 +1868,12 @@ void win_move_after(win_T *win1, win_T *win2) { int height; - /* check if the arguments are reasonable */ - if (win1 == win2) + // check if the arguments are reasonable + if (win1 == win2) { return; + } - /* check if there is something to do */ + // check if there is something to do if (win2->w_next != win1) { /* may need move the status line/vertical separator of the last window * */ @@ -1885,58 +1916,54 @@ void win_move_after(win_T *win1, win_T *win2) win2->w_pos_changed = true; } -/* - * Make all windows the same height. - * 'next_curwin' will soon be the current window, make sure it has enough - * rows. - */ -void win_equal( - win_T *next_curwin, // pointer to current window to be or NULL - bool current, // do only frame with current window - int dir // 'v' for vertically, 'h' for horizontally, - // 'b' for both, 0 for using p_ead -) -{ - if (dir == 0) +/// Make all windows the same height. +///'next_curwin' will soon be the current window, make sure it has enough rows. +/// +/// @param next_curwin pointer to current window to be or NULL +/// @param current do only frame with current window +/// @param dir 'v' for vertically, 'h' for horizontally, 'b' for both, 0 for using p_ead +void win_equal(win_T *next_curwin, bool current, int dir) +{ + if (dir == 0) { dir = *p_ead; + } win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current, topframe, dir, 0, tabline_height(), Columns, topframe->fr_height); } -/* - * Set a frame to a new position and height, spreading the available room - * equally over contained frames. - * The window "next_curwin" (if not NULL) should at least get the size from - * 'winheight' and 'winwidth' if possible. - */ -static void win_equal_rec( - win_T *next_curwin, /* pointer to current window to be or NULL */ - bool current, /* do only frame with current window */ - frame_T *topfr, /* frame to set size off */ - int dir, /* 'v', 'h' or 'b', see win_equal() */ - int col, /* horizontal position for frame */ - int row, /* vertical position for frame */ - int width, /* new width of frame */ - int height /* new height of frame */ -) +/// Set a frame to a new position and height, spreading the available room +/// equally over contained frames. +/// The window "next_curwin" (if not NULL) should at least get the size from +/// 'winheight' and 'winwidth' if possible. +/// +/// @param next_curwin pointer to current window to be or NULL +/// @param current do only frame with current window +/// @param topfr frame to set size off +/// @param dir 'v', 'h' or 'b', see win_equal() +/// @param col horizontal position for frame +/// @param row vertical position for frame +/// @param width new width of frame +/// @param height new height of frame +static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int dir, int col, + int row, int width, int height) { int n, m; int extra_sep = 0; int wincount, totwincount = 0; - frame_T *fr; + frame_T *fr; int next_curwin_size = 0; int room = 0; int new_size; int has_next_curwin = 0; - int hnc; + bool hnc; if (topfr->fr_layout == FR_LEAF) { /* Set the width/height of this frame. * Redraw when size or position changes */ if (topfr->fr_height != height || topfr->fr_win->w_winrow != row - || topfr->fr_width != width || topfr->fr_win->w_wincol != col - ) { + || topfr->fr_width != width || + topfr->fr_win->w_wincol != col) { topfr->fr_win->w_winrow = row; frame_new_height(topfr, height, false, false); topfr->fr_win->w_wincol = col; @@ -1947,15 +1974,16 @@ static void win_equal_rec( topfr->fr_width = width; topfr->fr_height = height; - if (dir != 'v') { /* equalize frame widths */ + if (dir != 'v') { // equalize frame widths /* Compute the maximum number of windows horizontally in this * frame. */ n = frame_minwidth(topfr, NOWIN); - /* add one for the rightmost window, it doesn't have a separator */ - if (col + width == Columns) + // add one for the rightmost window, it doesn't have a separator + if (col + width == Columns) { extra_sep = 1; - else + } else { extra_sep = 0; + } totwincount = (n + extra_sep) / (p_wmw + 1); has_next_curwin = frame_has_win(topfr, next_curwin); @@ -1983,12 +2011,14 @@ static void win_equal_rec( if (frame_has_win(fr, next_curwin)) { room += p_wiw - p_wmw; next_curwin_size = 0; - if (new_size < p_wiw) + if (new_size < p_wiw) { new_size = p_wiw; - } else - /* These windows don't use up room. */ + } + } else { + // These windows don't use up room. totwincount -= (n + (fr->fr_next == NULL ? extra_sep : 0)) / (p_wmw + 1); + } room -= new_size - n; if (room < 0) { new_size += room; @@ -1997,58 +2027,64 @@ static void win_equal_rec( fr->fr_newwidth = new_size; } if (next_curwin_size == -1) { - if (!has_next_curwin) + if (!has_next_curwin) { next_curwin_size = 0; - else if (totwincount > 1 - && (room + (totwincount - 2)) - / (totwincount - 1) > p_wiw) { + } else if (totwincount > 1 + && (room + (totwincount - 2)) + / (totwincount - 1) > p_wiw) { /* Can make all windows wider than 'winwidth', spread * the room equally. */ next_curwin_size = (room + p_wiw + (totwincount - 1) * p_wmw + (totwincount - 1)) / totwincount; room -= next_curwin_size - p_wiw; - } else + } else { next_curwin_size = p_wiw; + } } } - if (has_next_curwin) - --totwincount; /* don't count curwin */ + if (has_next_curwin) { + --totwincount; // don't count curwin + } } FOR_ALL_FRAMES(fr, topfr->fr_child) { wincount = 1; - if (fr->fr_next == NULL) - /* last frame gets all that remains (avoid roundoff error) */ + if (fr->fr_next == NULL) { + // last frame gets all that remains (avoid roundoff error) new_size = width; - else if (dir == 'v') + } else if (dir == 'v') { new_size = fr->fr_width; - else if (frame_fixed_width(fr)) { + } else if (frame_fixed_width(fr)) { new_size = fr->fr_newwidth; - wincount = 0; /* doesn't count as a sizeable window */ + wincount = 0; // doesn't count as a sizeable window } else { - /* Compute the maximum number of windows horiz. in "fr". */ + // Compute the maximum number of windows horiz. in "fr". n = frame_minwidth(fr, NOWIN); wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) / (p_wmw + 1); m = frame_minwidth(fr, next_curwin); - if (has_next_curwin) + if (has_next_curwin) { hnc = frame_has_win(fr, next_curwin); - else - hnc = FALSE; - if (hnc) /* don't count next_curwin */ - --wincount; - if (totwincount == 0) + } else { + hnc = false; + } + if (hnc) { // don't count next_curwin + wincount--; + } + if (totwincount == 0) { new_size = room; - else + } else { new_size = (wincount * room + (totwincount / 2)) / totwincount; - if (hnc) { /* add next_curwin size */ + } + if (hnc) { // add next_curwin size next_curwin_size -= p_wiw - (m - n); new_size += next_curwin_size; room -= new_size - next_curwin_size; - } else + } else { room -= new_size; + } new_size += n; } @@ -2056,25 +2092,27 @@ static void win_equal_rec( * window, unless equalizing all frames. */ if (!current || dir != 'v' || topfr->fr_parent != NULL || (new_size != fr->fr_width) - || frame_has_win(fr, next_curwin)) + || frame_has_win(fr, next_curwin)) { win_equal_rec(next_curwin, current, fr, dir, col, row, - new_size, height); + new_size, height); + } col += new_size; width -= new_size; totwincount -= wincount; } - } else { /* topfr->fr_layout == FR_COL */ + } else { // topfr->fr_layout == FR_COL topfr->fr_width = width; topfr->fr_height = height; - if (dir != 'h') { /* equalize frame heights */ - /* Compute maximum number of windows vertically in this frame. */ + if (dir != 'h') { // equalize frame heights + // Compute maximum number of windows vertically in this frame. n = frame_minheight(topfr, NOWIN); - /* add one for the bottom window if it doesn't have a statusline */ - if (row + height == cmdline_row && p_ls == 0) + // add one for the bottom window if it doesn't have a statusline + if (row + height == cmdline_row && p_ls == 0) { extra_sep = 1; - else + } else { extra_sep = 0; + } totwincount = (n + extra_sep) / (p_wmh + 1); has_next_curwin = frame_has_win(topfr, next_curwin); @@ -2104,12 +2142,14 @@ static void win_equal_rec( if (frame_has_win(fr, next_curwin)) { room += p_wh - p_wmh; next_curwin_size = 0; - if (new_size < p_wh) + if (new_size < p_wh) { new_size = p_wh; - } else - /* These windows don't use up room. */ + } + } else { + // These windows don't use up room. totwincount -= (n + (fr->fr_next == NULL ? extra_sep : 0)) / (p_wmh + 1); + } room -= new_size - n; if (room < 0) { new_size += room; @@ -2118,67 +2158,74 @@ static void win_equal_rec( fr->fr_newheight = new_size; } if (next_curwin_size == -1) { - if (!has_next_curwin) + if (!has_next_curwin) { next_curwin_size = 0; - else if (totwincount > 1 - && (room + (totwincount - 2)) - / (totwincount - 1) > p_wh) { + } else if (totwincount > 1 + && (room + (totwincount - 2)) + / (totwincount - 1) > p_wh) { /* can make all windows higher than 'winheight', * spread the room equally. */ next_curwin_size = (room + p_wh + (totwincount - 1) * p_wmh + (totwincount - 1)) / totwincount; room -= next_curwin_size - p_wh; - } else + } else { next_curwin_size = p_wh; + } } } - if (has_next_curwin) - --totwincount; /* don't count curwin */ + if (has_next_curwin) { + --totwincount; // don't count curwin + } } FOR_ALL_FRAMES(fr, topfr->fr_child) { wincount = 1; - if (fr->fr_next == NULL) - /* last frame gets all that remains (avoid roundoff error) */ + if (fr->fr_next == NULL) { + // last frame gets all that remains (avoid roundoff error) new_size = height; - else if (dir == 'h') + } else if (dir == 'h') { new_size = fr->fr_height; - else if (frame_fixed_height(fr)) { + } else if (frame_fixed_height(fr)) { new_size = fr->fr_newheight; - wincount = 0; /* doesn't count as a sizeable window */ + wincount = 0; // doesn't count as a sizeable window } else { - /* Compute the maximum number of windows vert. in "fr". */ + // Compute the maximum number of windows vert. in "fr". n = frame_minheight(fr, NOWIN); wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) / (p_wmh + 1); m = frame_minheight(fr, next_curwin); - if (has_next_curwin) + if (has_next_curwin) { hnc = frame_has_win(fr, next_curwin); - else - hnc = FALSE; - if (hnc) /* don't count next_curwin */ - --wincount; - if (totwincount == 0) + } else { + hnc = false; + } + if (hnc) { // don't count next_curwin + wincount--; + } + if (totwincount == 0) { new_size = room; - else + } else { new_size = (wincount * room + (totwincount / 2)) / totwincount; - if (hnc) { /* add next_curwin size */ + } + if (hnc) { // add next_curwin size next_curwin_size -= p_wh - (m - n); new_size += next_curwin_size; room -= new_size - next_curwin_size; - } else + } else { room -= new_size; + } new_size += n; } /* Skip frame that is full width when splitting or closing a * window, unless equalizing all frames. */ if (!current || dir != 'h' || topfr->fr_parent != NULL || (new_size != fr->fr_height) - || frame_has_win(fr, next_curwin)) + || frame_has_win(fr, next_curwin)) { win_equal_rec(next_curwin, current, fr, dir, col, row, - width, new_size); + width, new_size); + } row += new_size; height -= new_size; totwincount -= wincount; @@ -2191,7 +2238,7 @@ static void win_equal_rec( /// @param keep_curwin don't close `curwin` void close_windows(buf_T *buf, int keep_curwin) { - tabpage_T *tp, *nexttp; + tabpage_T *tp, *nexttp; int h = tabline_height(); ++RedrawingDisabled; @@ -2204,13 +2251,14 @@ void close_windows(buf_T *buf, int keep_curwin) break; } - /* Start all over, autocommands may change the window layout. */ + // Start all over, autocommands may change the window layout. wp = firstwin; - } else + } else { wp = wp->w_next; + } } - /* Also check windows in other tab pages. */ + // Also check windows in other tab pages. for (tp = first_tabpage; tp != NULL; tp = nexttp) { nexttp = tp->tp_next; if (tp != curtab) { @@ -2284,14 +2332,13 @@ bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT /// last window in the tabpage /// /// @return true when the window was closed already. -static bool close_last_window_tabpage(win_T *win, bool free_buf, - tabpage_T *prev_curtab) +static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev_curtab) FUNC_ATTR_NONNULL_ARG(1) { if (!ONE_WINDOW) { return false; } - buf_T *old_curbuf = curbuf; + buf_T *old_curbuf = curbuf; Terminal *term = win->w_buffer ? win->w_buffer->terminal : NULL; if (term) { @@ -2307,8 +2354,8 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, * Don't trigger autocommands yet, they may use wrong values, so do * that below. */ - goto_tabpage_tp(alt_tabpage(), FALSE, TRUE); - redraw_tabline = TRUE; + goto_tabpage_tp(alt_tabpage(), false, true); + redraw_tabline = true; // save index for tabclosed event char_u prev_idx[NUMBUFLEN]; @@ -2320,8 +2367,9 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, int h = tabline_height(); win_close_othertab(win, free_buf, prev_curtab); - if (h != tabline_height()) + if (h != tabline_height()) { shell_new_rows(); + } } // Since goto_tabpage_tp above did not trigger *Enter autocommands, do @@ -2341,12 +2389,12 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, // Returns FAIL when the window was not closed. int win_close(win_T *win, bool free_buf) { - win_T *wp; - int other_buffer = FALSE; - int close_curwin = FALSE; + win_T *wp; + bool other_buffer = false; + bool close_curwin = false; int dir; bool help_window = false; - tabpage_T *prev_curtab = curtab; + tabpage_T *prev_curtab = curtab; frame_T *win_frame = win->w_floating ? NULL : win->w_frame->fr_parent; const bool had_diffmode = win->w_p_diff; @@ -2377,8 +2425,9 @@ int win_close(win_T *win, bool free_buf) /* When closing the last window in a tab page first go to another tab page * and then close the window and the tab page to avoid that curwin and * curtab are invalid while we are freeing memory. */ - if (close_last_window_tabpage(win, free_buf, prev_curtab)) + if (close_last_window_tabpage(win, free_buf, prev_curtab)) { return FAIL; + } /* When closing the help window, try restoring a snapshot after closing * the window. Otherwise clear the snapshot, it's now invalid. */ @@ -2408,14 +2457,16 @@ int win_close(win_T *win, bool free_buf) * to be the last one left, return now. */ if (wp->w_buffer != curbuf) { - other_buffer = TRUE; + other_buffer = true; win->w_closing = true; apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); - if (!win_valid(win)) + if (!win_valid(win)) { return FAIL; + } win->w_closing = false; - if (last_window()) + if (last_window()) { return FAIL; + } } win->w_closing = true; apply_autocmds(EVENT_WINLEAVE, NULL, NULL, false, curbuf); @@ -2423,11 +2474,13 @@ int win_close(win_T *win, bool free_buf) return FAIL; } win->w_closing = false; - if (last_window()) + if (last_window()) { return FAIL; - /* autocmds may abort script processing */ - if (aborting()) + } + // autocmds may abort script processing + if (aborting()) { return FAIL; + } } bool was_floating = win->w_floating; @@ -2534,12 +2587,14 @@ int win_close(win_T *win, bool free_buf) * finding another window to go to. */ for (;; ) { - if (wp->w_next == NULL) + if (wp->w_next == NULL) { wp = firstwin; - else + } else { wp = wp->w_next; - if (wp == curwin) + } + if (wp == curwin) { break; + } if (!wp->w_p_pvw && !bt_quickfix(wp->w_buffer)) { curwin = wp; break; @@ -2547,7 +2602,7 @@ int win_close(win_T *win, bool free_buf) } } curbuf = curwin->w_buffer; - close_curwin = TRUE; + close_curwin = true; // The cursor position may be invalid if the buffer changed after last // using the window. @@ -2576,12 +2631,13 @@ int win_close(win_T *win, bool free_buf) * If last window has a status line now and we don't want one, * remove the status line. */ - last_status(FALSE); + last_status(false); /* After closing the help window, try restoring the window layout from * before it was opened. */ - if (help_window) + if (help_window) { restore_snapshot(SNAP_HELP_IDX, close_curwin); + } // If the window had 'diff' set and now there is only one window left in // the tab page with 'diff' set, and "closeoff" is in 'diffopt', then @@ -2628,8 +2684,8 @@ static void do_autocmd_winclosed(win_T *win) void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) { int dir; - tabpage_T *ptp = NULL; - int free_tp = FALSE; + tabpage_T *ptp = NULL; + bool free_tp = false; // Get here with win->w_buffer == NULL when win_close() detects the tab page // changed. @@ -2652,26 +2708,28 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) /* Careful: Autocommands may have closed the tab page or made it the * current tab page. */ - for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next) + for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next) { ; - if (ptp == NULL || tp == curtab) + } + if (ptp == NULL || tp == curtab) { return; + } - /* Autocommands may have closed the window already. */ + // Autocommands may have closed the window already. { bool found_window = false; FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (wp == win) { - found_window = true; - break; + found_window = true; + break; } } if (!found_window) { - return; + return; } } - /* When closing the last window in a tab page remove the tab page. */ + // When closing the last window in a tab page remove the tab page. if (tp->tp_firstwin == tp->tp_lastwin) { char_u prev_idx[NUMBUFLEN]; if (has_event(EVENT_TABCLOSED)) { @@ -2698,23 +2756,24 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) } } - /* Free the memory used for the window. */ + // Free the memory used for the window. win_free_mem(win, &dir, tp); - if (free_tp) + if (free_tp) { free_tabpage(tp); + } } -// Free the memory used for a window. -// Returns a pointer to the window that got the freed up space. -static win_T *win_free_mem( - win_T *win, - int *dirp, // set to 'v' or 'h' for direction if 'ea' - tabpage_T *tp // tab page "win" is in, NULL for current -) +/// Free the memory used for a window. +/// +/// @param dirp set to 'v' or 'h' for direction if 'ea' +/// @param tp tab page "win" is in, NULL for current +/// +/// @return a pointer to the window that got the freed up space. +static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp) { - frame_T *frp; - win_T *wp; + frame_T *frp; + win_T *wp; if (!win->w_floating) { // Remove the window and its frame from the tree of frames. @@ -2734,7 +2793,11 @@ static win_T *win_free_mem( // When deleting the current window of another tab page select a new // current window. if (tp != NULL && win == tp->tp_curwin) { - tp->tp_curwin = wp; + if (win_valid(tp->tp_prevwin) && tp->tp_prevwin != win) { + tp->tp_curwin = tp->tp_prevwin; + } else { + tp->tp_curwin = tp->tp_firstwin; + } } return wp; @@ -2745,8 +2808,9 @@ void win_free_all(void) { int dummy; - while (first_tabpage->tp_next != NULL) + while (first_tabpage->tp_next != NULL) { tabpage_close(TRUE); + } while (lastwin != NULL && lastwin->w_floating) { win_T *wp = lastwin; @@ -2762,8 +2826,9 @@ void win_free_all(void) aucmd_win = NULL; } - while (firstwin != NULL) + while (firstwin != NULL) { (void)win_free_mem(firstwin, &dummy, NULL); + } // No window should be used after this. Set curwin to NULL to crash // instead of using freed memory. @@ -2772,26 +2837,24 @@ void win_free_all(void) #endif -/* - * Remove a window and its frame from the tree of frames. - * Returns a pointer to the window that got the freed up space. - */ -win_T * -winframe_remove ( - win_T *win, - int *dirp, /* set to 'v' or 'h' for direction if 'ea' */ - tabpage_T *tp /* tab page "win" is in, NULL for current */ -) +/// Remove a window and its frame from the tree of frames. +/// +/// @param dirp set to 'v' or 'h' for direction if 'ea' +/// @param tp tab page "win" is in, NULL for current +/// +/// @return a pointer to the window that got the freed up space. +win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp) { - frame_T *frp, *frp2, *frp3; - frame_T *frp_close = win->w_frame; - win_T *wp; + frame_T *frp, *frp2, *frp3; + frame_T *frp_close = win->w_frame; + win_T *wp; /* * If there is only one window there is nothing to remove. */ - if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) + if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) { return NULL; + } /* * Remove the window from its frame. @@ -2799,7 +2862,7 @@ winframe_remove ( frp2 = win_altframe(win, tp); wp = frame2win(frp2); - /* Remove this frame from the list of frames. */ + // Remove this frame from the list of frames. frame_remove(frp_close); if (frp_close->fr_parent->fr_layout == FR_COL) { @@ -2829,7 +2892,7 @@ winframe_remove ( } } frame_new_height(frp2, frp2->fr_height + frp_close->fr_height, - frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE); + frp2 == frp_close->fr_next, false); *dirp = 'v'; } else { /* When 'winfixwidth' is set, try to find another frame in the column @@ -2858,7 +2921,7 @@ winframe_remove ( } } frame_new_width(frp2, frp2->fr_width + frp_close->fr_width, - frp2 == frp_close->fr_next ? TRUE : FALSE, FALSE); + frp2 == frp_close->fr_next, false); *dirp = 'h'; } @@ -2880,8 +2943,9 @@ winframe_remove ( frp->fr_parent = frp2->fr_parent; } frp2->fr_parent->fr_win = frp2->fr_win; - if (frp2->fr_win != NULL) + if (frp2->fr_win != NULL) { frp2->fr_win->w_frame = frp2->fr_parent; + } frp = frp2->fr_parent; if (topframe->fr_child == frp2) { topframe->fr_child = frp; @@ -2892,18 +2956,21 @@ winframe_remove ( 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) + 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) + if (frp->fr_prev != NULL) { frp->fr_prev->fr_next = frp->fr_child; + } 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) + if (frp->fr_next != NULL) { frp->fr_next->fr_prev = frp3; + } break; } } @@ -2917,21 +2984,19 @@ winframe_remove ( return wp; } -// Return a pointer to the frame that will receive the empty screen space that -// is left over after "win" is closed. -// -// If 'splitbelow' or 'splitright' is set, the space goes above or to the left -// by default. Otherwise, the free space goes below or to the right. The -// result is that opening a window and then immediately closing it will -// preserve the initial window layout. The 'wfh' and 'wfw' settings are -// respected when possible. -static frame_T * -win_altframe ( - win_T *win, - tabpage_T *tp /* tab page "win" is in, NULL for current */ -) -{ - frame_T *frp; +/// If 'splitbelow' or 'splitright' is set, the space goes above or to the left +/// by default. Otherwise, the free space goes below or to the right. The +/// result is that opening a window and then immediately closing it will +/// preserve the initial window layout. The 'wfh' and 'wfw' settings are +/// respected when possible. +/// +/// @param tp tab page "win" is in, NULL for current +/// +/// @return a pointer to the frame that will receive the empty screen space that +/// is left over after "win" is closed. +static frame_T *win_altframe(win_T *win, tabpage_T *tp) +{ + frame_T *frp; if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) { return alt_tabpage()->tp_curwin->w_frame; @@ -2973,15 +3038,17 @@ win_altframe ( */ static tabpage_T *alt_tabpage(void) { - tabpage_T *tp; + tabpage_T *tp; - /* Use the next tab page if possible. */ - if (curtab->tp_next != NULL) + // Use the next tab page if possible. + if (curtab->tp_next != NULL) { return curtab->tp_next; + } - /* Find the last but one tab page. */ - for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) + // Find the last but one tab page. + for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) { ; + } return tp; } @@ -2990,8 +3057,9 @@ static tabpage_T *alt_tabpage(void) */ static win_T *frame2win(frame_T *frp) { - while (frp->fr_win == NULL) + while (frp->fr_win == NULL) { frp = frp->fr_child; + } return frp->fr_win; } @@ -3014,21 +3082,16 @@ static bool frame_has_win(const frame_T *frp, const win_T *wp) return false; } -/* - * Set a new height for a frame. Recursively sets the height for contained - * frames and windows. Caller must take care of positions. - */ -static void -frame_new_height ( - frame_T *topfrp, - int height, - int topfirst, /* resize topmost contained frame first */ - int wfh /* obey 'winfixheight' when there is a choice; - may cause the height not to be set */ -) +/// Set a new height for a frame. Recursively sets the height for contained +/// frames and windows. Caller must take care of positions. +/// +/// @param topfirst resize topmost contained frame first. +/// @param wfh obey 'winfixheight' when there is a choice; +/// may cause the height not to be set. +static void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh) FUNC_ATTR_NONNULL_ALL { - frame_T *frp; + frame_T *frp; int extra_lines; int h; @@ -3042,37 +3105,42 @@ frame_new_height ( FOR_ALL_FRAMES(frp, topfrp->fr_child) { frame_new_height(frp, height, topfirst, wfh); if (frp->fr_height > height) { - /* Could not fit the windows, make the whole row higher. */ + // Could not fit the windows, make the whole row higher. height = frp->fr_height; break; } } } while (frp != NULL); - } else { /* fr_layout == FR_COL */ + } else { // fr_layout == FR_COL /* Complicated case: Resize a column of frames. Resize the bottom * frame first, frames above that when needed. */ frp = topfrp->fr_child; - if (wfh) - /* Advance past frames with one window with 'wfh' set. */ + if (wfh) { + // Advance past frames with one window with 'wfh' set. while (frame_fixed_height(frp)) { frp = frp->fr_next; - if (frp == NULL) - return; /* no frame without 'wfh', give up */ + if (frp == NULL) { + return; // no frame without 'wfh', give up + } } + } if (!topfirst) { - /* Find the bottom frame of this column */ - while (frp->fr_next != NULL) + // Find the bottom frame of this column + while (frp->fr_next != NULL) { frp = frp->fr_next; - if (wfh) - /* Advance back for frames with one window with 'wfh' set. */ - while (frame_fixed_height(frp)) + } + if (wfh) { + // Advance back for frames with one window with 'wfh' set. + while (frame_fixed_height(frp)) { frp = frp->fr_prev; + } + } } extra_lines = height - topfrp->fr_height; if (extra_lines < 0) { - /* reduce height of contained frames, bottom or top frame first */ + // reduce height of contained frames, bottom or top frame first while (frp != NULL) { h = frame_minheight(frp, NULL); if (frp->fr_height + extra_lines < h) { @@ -3080,7 +3148,7 @@ frame_new_height ( frame_new_height(frp, h, topfirst, wfh); } else { frame_new_height(frp, frp->fr_height + extra_lines, - topfirst, wfh); + topfirst, wfh); break; } if (topfirst) { @@ -3092,12 +3160,13 @@ frame_new_height ( frp = frp->fr_prev; while (wfh && frp != NULL && frame_fixed_height(frp)); } - /* Increase "height" if we could not reduce enough frames. */ - if (frp == NULL) + // Increase "height" if we could not reduce enough frames. + if (frp == NULL) { height -= extra_lines; + } } } else if (extra_lines > 0) { - /* increase height of bottom or top frame */ + // increase height of bottom or top frame frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh); } } @@ -3178,13 +3247,14 @@ static bool frame_fixed_width(frame_T *frp) */ static void frame_add_statusline(frame_T *frp) { - win_T *wp; + win_T *wp; if (frp->fr_layout == FR_LEAF) { wp = frp->fr_win; if (wp->w_status_height == 0) { - if (wp->w_height > 0) /* don't make it negative */ + if (wp->w_height > 0) { // don't make it negative --wp->w_height; + } wp->w_status_height = STATUS_HEIGHT; } } else if (frp->fr_layout == FR_ROW) { @@ -3201,33 +3271,31 @@ static void frame_add_statusline(frame_T *frp) } } -/* - * Set width of a frame. Handles recursively going through contained frames. - * May remove separator line for windows at the right side (for win_close()). - */ -static void -frame_new_width ( - frame_T *topfrp, - int width, - int leftfirst, /* resize leftmost contained frame first */ - int wfw /* obey 'winfixwidth' when there is a choice; - may cause the width not to be set */ -) -{ - frame_T *frp; +/// Set width of a frame. Handles recursively going through contained frames. +/// May remove separator line for windows at the right side (for win_close()). +/// +/// @param leftfirst resize leftmost contained frame first. +/// @param wfw obey 'winfixwidth' when there is a choice; +/// may cause the width not to be set. +static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw) +{ + frame_T *frp; int extra_cols; int w; - win_T *wp; + win_T *wp; if (topfrp->fr_layout == FR_LEAF) { - /* Simple case: just one window. */ + // Simple case: just one window. wp = topfrp->fr_win; - /* Find out if there are any windows right of this one. */ - for (frp = topfrp; frp->fr_parent != NULL; frp = frp->fr_parent) - if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_next != NULL) + // Find out if there are any windows right of this one. + for (frp = topfrp; frp->fr_parent != NULL; frp = frp->fr_parent) { + if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_next != NULL) { break; - if (frp->fr_parent == NULL) + } + } + if (frp->fr_parent == NULL) { wp->w_vsep_width = 0; + } win_new_width(wp, width - wp->w_vsep_width); } else if (topfrp->fr_layout == FR_COL) { do { @@ -3235,37 +3303,42 @@ frame_new_width ( FOR_ALL_FRAMES(frp, topfrp->fr_child) { frame_new_width(frp, width, leftfirst, wfw); if (frp->fr_width > width) { - /* Could not fit the windows, make whole column wider. */ + // Could not fit the windows, make whole column wider. width = frp->fr_width; break; } } } while (frp != NULL); - } else { /* fr_layout == FR_ROW */ + } else { // fr_layout == FR_ROW /* Complicated case: Resize a row of frames. Resize the rightmost * frame first, frames left of it when needed. */ frp = topfrp->fr_child; - if (wfw) - /* Advance past frames with one window with 'wfw' set. */ + if (wfw) { + // Advance past frames with one window with 'wfw' set. while (frame_fixed_width(frp)) { frp = frp->fr_next; - if (frp == NULL) - return; /* no frame without 'wfw', give up */ + if (frp == NULL) { + return; // no frame without 'wfw', give up + } } + } if (!leftfirst) { - /* Find the rightmost frame of this row */ - while (frp->fr_next != NULL) + // Find the rightmost frame of this row + while (frp->fr_next != NULL) { frp = frp->fr_next; - if (wfw) - /* Advance back for frames with one window with 'wfw' set. */ - while (frame_fixed_width(frp)) + } + if (wfw) { + // Advance back for frames with one window with 'wfw' set. + while (frame_fixed_width(frp)) { frp = frp->fr_prev; + } + } } extra_cols = width - topfrp->fr_width; if (extra_cols < 0) { - /* reduce frame width, rightmost frame first */ + // reduce frame width, rightmost frame first while (frp != NULL) { w = frame_minwidth(frp, NULL); if (frp->fr_width + extra_cols < w) { @@ -3273,7 +3346,7 @@ frame_new_width ( frame_new_width(frp, w, leftfirst, wfw); } else { frame_new_width(frp, frp->fr_width + extra_cols, - leftfirst, wfw); + leftfirst, wfw); break; } if (leftfirst) { @@ -3285,12 +3358,13 @@ frame_new_width ( frp = frp->fr_prev; while (wfw && frp != NULL && frame_fixed_width(frp)); } - /* Increase "width" if we could not reduce enough frames. */ - if (frp == NULL) + // Increase "width" if we could not reduce enough frames. + if (frp == NULL) { width -= extra_cols; + } } } else if (extra_cols > 0) { - /* increase width of rightmost frame */ + // increase width of rightmost frame frame_new_width(frp, frp->fr_width + extra_cols, leftfirst, wfw); } } @@ -3304,13 +3378,14 @@ frame_new_width ( static void frame_add_vsep(const frame_T *frp) FUNC_ATTR_NONNULL_ARG(1) { - win_T *wp; + win_T *wp; if (frp->fr_layout == FR_LEAF) { wp = frp->fr_win; if (wp->w_vsep_width == 0) { - if (wp->w_width > 0) /* don't make it negative */ + if (wp->w_width > 0) { // don't make it negative --wp->w_width; + } wp->w_vsep_width = 1; } } else if (frp->fr_layout == FR_COL) { @@ -3322,8 +3397,9 @@ static void frame_add_vsep(const frame_T *frp) assert(frp->fr_layout == FR_ROW); // Only need to handle the last frame in the row. frp = frp->fr_child; - while (frp->fr_next != NULL) + while (frp->fr_next != NULL) { frp = frp->fr_next; + } frame_add_vsep(frp); } } @@ -3354,7 +3430,7 @@ static void frame_fix_height(win_T *wp) */ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) { - frame_T *frp; + frame_T *frp; int m; int n; @@ -3372,15 +3448,16 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) } } } else if (topfrp->fr_layout == FR_ROW) { - /* get the minimal height from each frame in this row */ + // get the minimal height from each frame in this row m = 0; FOR_ALL_FRAMES(frp, topfrp->fr_child) { n = frame_minheight(frp, next_curwin); - if (n > m) + if (n > m) { m = n; + } } } else { - /* Add up the minimal heights for all frames in this column. */ + // Add up the minimal heights for all frames in this column. m = 0; FOR_ALL_FRAMES(frp, topfrp->fr_child) { m += frame_minheight(frp, next_curwin); @@ -3390,41 +3467,39 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) return m; } -/* - * Compute the minimal width for frame "topfrp". - * When "next_curwin" isn't NULL, use p_wiw for this window. - * When "next_curwin" is NOWIN, don't use at least one column for the current - * window. - */ -static int -frame_minwidth ( - frame_T *topfrp, - win_T *next_curwin /* use p_wh and p_wiw for next_curwin */ -) +/// Compute the minimal width for frame "topfrp". +/// When "next_curwin" isn't NULL, use p_wiw for this window. +/// When "next_curwin" is NOWIN, don't use at least one column for the current +/// window. +/// +/// @param next_curwin use p_wh and p_wiw for next_curwin +static int frame_minwidth(frame_T *topfrp, win_T *next_curwin) { - frame_T *frp; + frame_T *frp; int m, n; if (topfrp->fr_win != NULL) { - if (topfrp->fr_win == next_curwin) + if (topfrp->fr_win == next_curwin) { m = p_wiw + topfrp->fr_win->w_vsep_width; - else { - /* window: minimal width of the window plus separator column */ + } else { + // window: minimal width of the window plus separator column m = p_wmw + topfrp->fr_win->w_vsep_width; - /* Current window is minimal one column wide */ - if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL) + // Current window is minimal one column wide + if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL) { ++m; + } } } else if (topfrp->fr_layout == FR_COL) { - /* get the minimal width from each frame in this column */ + // get the minimal width from each frame in this column m = 0; FOR_ALL_FRAMES(frp, topfrp->fr_child) { n = frame_minwidth(frp, next_curwin); - if (n > m) + if (n > m) { m = n; + } } } else { - /* Add up the minimal widths for all frames in this row. */ + // Add up the minimal widths for all frames in this row. m = 0; FOR_ALL_FRAMES(frp, topfrp->fr_child) { m += frame_minwidth(frp, next_curwin); @@ -3435,21 +3510,17 @@ frame_minwidth ( } -/* - * Try to close all windows except current one. - * Buffers in the other windows become hidden if 'hidden' is set, or '!' is - * used and the buffer was modified. - * - * Used by ":bdel" and ":only". - */ -void -close_others ( - int message, - int forceit /* always hide all other windows */ -) -{ - win_T *wp; - win_T *nextwp; +/// Try to close all windows except current one. +/// Buffers in the other windows become hidden if 'hidden' is set, or '!' is +/// used and the buffer was modified. +/// +/// Used by ":bdel" and ":only". +/// +/// @param forceit always hide all other windows +void close_others(int message, int forceit) +{ + win_T *wp; + win_T *nextwp; int r; if (curwin->w_floating) { @@ -3461,23 +3532,22 @@ close_others ( if (one_window() && !lastwin->w_floating) { if (message - && !autocmd_busy - ) { + && !autocmd_busy) { MSG(_(m_onlyone)); } return; } - /* Be very careful here: autocommands may change the window layout. */ + // Be very careful here: autocommands may change the window layout. for (wp = firstwin; win_valid(wp); wp = nextwp) { nextwp = wp->w_next; - if (wp == curwin) { /* don't close current window */ + if (wp == curwin) { // don't close current window continue; } - /* Check if it's allowed to abandon this window */ + // Check if it's allowed to abandon this window r = can_abandon(wp->w_buffer, forceit); - if (!win_valid(wp)) { /* autocommands messed wp up */ + if (!win_valid(wp)) { // autocommands messed wp up nextwp = firstwin; continue; } @@ -3489,14 +3559,16 @@ close_others ( continue; } } - if (bufIsChanged(wp->w_buffer)) + if (bufIsChanged(wp->w_buffer)) { continue; + } } win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer)); } - if (message && !ONE_WINDOW) + if (message && !ONE_WINDOW) { EMSG(_("E445: Other window contains changes")); + } } @@ -3516,7 +3588,7 @@ void win_init_empty(win_T *wp) wp->w_cursor.lnum = 1; wp->w_curswant = wp->w_cursor.col = 0; wp->w_cursor.coladd = 0; - wp->w_pcmark.lnum = 1; /* pcmark not cleared but set to line 1 */ + wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1 wp->w_pcmark.col = 0; wp->w_prev_pcmark.lnum = 0; wp->w_prev_pcmark.col = 0; @@ -3534,8 +3606,9 @@ void win_init_empty(win_T *wp) */ int win_alloc_first(void) { - if (win_alloc_firstwin(NULL) == FAIL) + if (win_alloc_firstwin(NULL) == FAIL) { return FAIL; + } first_tabpage = alloc_tabpage(); first_tabpage->tp_topframe = topframe; @@ -3569,7 +3642,7 @@ void win_alloc_aucmd_win(void) */ static int win_alloc_firstwin(win_T *oldwin) { - curwin = win_alloc(NULL, FALSE); + curwin = win_alloc(NULL, false); if (oldwin == NULL) { /* Very first window, need to create an empty buffer for it and * initialize from scratch. */ @@ -3579,14 +3652,14 @@ static int win_alloc_firstwin(win_T *oldwin) } curwin->w_buffer = curbuf; curwin->w_s = &(curbuf->b_s); - curbuf->b_nwindows = 1; /* there is one window */ + curbuf->b_nwindows = 1; // there is one window curwin->w_alist = &global_alist; - curwin_init(); /* init current window */ + curwin_init(); // init current window } else { - /* First window in new tab page, initialize it from "oldwin". */ + // First window in new tab page, initialize it from "oldwin". win_init(curwin, oldwin, 0); - /* We don't want cursor- and scroll-binding in the first window. */ + // We don't want cursor- and scroll-binding in the first window. RESET_BINDING(curwin); } @@ -3633,7 +3706,7 @@ static tabpage_T *alloc_tabpage(void) static int last_tp_handle = 0; tabpage_T *tp = xcalloc(1, sizeof(tabpage_T)); tp->handle = ++last_tp_handle; - handle_register_tabpage(tp); + pmap_put(handle_T)(&tabpage_handles, tp->handle, tp); // Init t: variables. tp->tp_vars = tv_dict_alloc(); @@ -3648,11 +3721,12 @@ void free_tabpage(tabpage_T *tp) { int idx; - handle_unregister_tabpage(tp); + pmap_del(handle_T)(&tabpage_handles, tp->handle); diff_clear(tp); - for (idx = 0; idx < SNAP_COUNT; ++idx) + for (idx = 0; idx < SNAP_COUNT; ++idx) { clear_snapshot(tp, idx); - vars_clear(&tp->tp_vars->dv_hashtab); /* free all t: variables */ + } + vars_clear(&tp->tp_vars->dv_hashtab); // free all t: variables hash_init(&tp->tp_vars->dv_hashtab); unref_var_dict(tp->tp_vars); @@ -3674,8 +3748,8 @@ void free_tabpage(tabpage_T *tp) /// @return Was the new tabpage created successfully? FAIL or OK. int win_new_tabpage(int after, char_u *filename) { - tabpage_T *old_curtab = curtab; - tabpage_T *newtp; + tabpage_T *old_curtab = curtab; + tabpage_T *newtp; int n; newtp = alloc_tabpage(); @@ -3699,14 +3773,15 @@ int win_new_tabpage(int after, char_u *filename) newtp->tp_next = first_tabpage; first_tabpage = newtp; } else { - tabpage_T *tp = old_curtab; + tabpage_T *tp = old_curtab; if (after > 0) { // Put new tab page before tab page "after". n = 2; for (tp = first_tabpage; tp->tp_next != NULL - && n < after; tp = tp->tp_next) + && n < after; tp = tp->tp_next) { ++n; + } } newtp->tp_next = tp->tp_next; tp->tp_next = newtp; @@ -3749,7 +3824,7 @@ int may_open_tabpage(void) int n = (cmdmod.tab == 0) ? postponed_split_tab : cmdmod.tab; if (n != 0) { - cmdmod.tab = 0; /* reset it to avoid doing it twice */ + cmdmod.tab = 0; // reset it to avoid doing it twice postponed_split_tab = 0; return win_new_tabpage(n, NULL); } @@ -3765,9 +3840,10 @@ int make_tabpages(int maxcount) int count = maxcount; int todo; - /* Limit to 'tabpagemax' tabs. */ - if (count > p_tpm) + // Limit to 'tabpagemax' tabs. + if (count > p_tpm) { count = p_tpm; + } /* * Don't execute autocommands while creating the tab pages. Must do that @@ -3783,7 +3859,7 @@ int make_tabpages(int maxcount) unblock_autocmds(); - /* return actual number of tab pages */ + // return actual number of tab pages return count - todo; } @@ -3844,11 +3920,12 @@ void close_tabpage(tabpage_T *tab) */ tabpage_T *find_tabpage(int n) { - tabpage_T *tp; + tabpage_T *tp; int i = 1; - for (tp = first_tabpage; tp != NULL && i != n; tp = tp->tp_next) + for (tp = first_tabpage; tp != NULL && i != n; tp = tp->tp_next) { ++i; + } return tp; } @@ -3859,41 +3936,42 @@ tabpage_T *find_tabpage(int n) int tabpage_index(tabpage_T *ftp) { int i = 1; - tabpage_T *tp; + tabpage_T *tp; - for (tp = first_tabpage; tp != NULL && tp != ftp; tp = tp->tp_next) + for (tp = first_tabpage; tp != NULL && tp != ftp; tp = tp->tp_next) { ++i; + } return i; } -/* - * Prepare for leaving the current tab page. - * When autocommands change "curtab" we don't leave the tab page and return - * FAIL. - * Careful: When OK is returned need to get a new tab page very very soon! - */ -static int -leave_tabpage ( - buf_T *new_curbuf, /* what is going to be the new curbuf, - NULL if unknown */ - int trigger_leave_autocmds -) +/// Prepare for leaving the current tab page. +/// When autocommands change "curtab" we don't leave the tab page and return +/// FAIL. +/// Careful: When OK is returned need to get a new tab page very very soon! +/// +/// @param new_curbuf what is going to be the new curbuf, +/// NULL if unknown. +/// @param trigger_leave_autocmds when true trigger *Leave autocommands. +static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds) { - tabpage_T *tp = curtab; + tabpage_T *tp = curtab; - reset_VIsual_and_resel(); /* stop Visual mode */ + reset_VIsual_and_resel(); // stop Visual mode if (trigger_leave_autocmds) { if (new_curbuf != curbuf) { apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); - if (curtab != tp) + if (curtab != tp) { return FAIL; + } } apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf); - if (curtab != tp) + if (curtab != tp) { return FAIL; + } apply_autocmds(EVENT_TABLEAVE, NULL, NULL, FALSE, curbuf); - if (curtab != tp) + if (curtab != tp) { return FAIL; + } } tp->tp_curwin = curwin; tp->tp_prevwin = prevwin; @@ -3906,16 +3984,16 @@ leave_tabpage ( return OK; } -/* - * Start using tab page "tp". - * Only to be used after leave_tabpage() or freeing the current tab page. - * Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE. - * Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE. - */ -static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_autocmds, int trigger_leave_autocmds) +/// Start using tab page "tp". +/// Only to be used after leave_tabpage() or freeing the current tab page. +/// +/// @param trigger_enter_autocmds when true trigger *Enter autocommands. +/// @param trigger_leave_autocmds when true trigger *Leave autocommands. +static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_autocmds, + bool trigger_leave_autocmds) { int old_off = tp->tp_firstwin->w_winrow; - win_T *next_prevwin = tp->tp_prevwin; + win_T *next_prevwin = tp->tp_prevwin; tabpage_T *old_curtab = curtab; curtab = tp; @@ -3924,7 +4002,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au topframe = tp->tp_topframe; if (old_curtab != curtab) { - tabpage_check_windows(old_curtab); + tabpage_check_windows(old_curtab); } /* We would like doing the TabEnter event first, but we don't have a @@ -3967,8 +4045,9 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au * 'columns' have been set correctly. */ if (trigger_enter_autocmds) { apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf); - if (old_curbuf != curbuf) + if (old_curbuf != curbuf) { apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); + } } redraw_all_later(NOT_VALID); @@ -4009,8 +4088,8 @@ static void tabpage_check_windows(tabpage_T *old_curtab) */ void goto_tabpage(int n) { - tabpage_T *tp = NULL; // shut up compiler - tabpage_T *ttp; + tabpage_T *tp = NULL; // shut up compiler + tabpage_T *ttp; int i; if (text_locked()) { @@ -4019,35 +4098,39 @@ void goto_tabpage(int n) return; } - /* If there is only one it can't work. */ + // If there is only one it can't work. if (first_tabpage->tp_next == NULL) { - if (n > 1) + if (n > 1) { beep_flush(); + } return; } if (n == 0) { - /* No count, go to next tab page, wrap around end. */ - if (curtab->tp_next == NULL) + // No count, go to next tab page, wrap around end. + if (curtab->tp_next == NULL) { tp = first_tabpage; - else + } else { tp = curtab->tp_next; + } } else if (n < 0) { /* "gT": go to previous tab page, wrap around end. "N gT" repeats * this N times. */ ttp = curtab; for (i = n; i < 0; ++i) { for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL; - tp = tp->tp_next) + tp = tp->tp_next) { ; + } ttp = tp; } } else if (n == 9999) { - /* Go to last tab page. */ - for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next) + // Go to last tab page. + for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next) { ; + } } else { - /* Go to tab page "n". */ + // Go to tab page "n". tp = find_tabpage(n); if (tp == NULL) { beep_flush(); @@ -4055,29 +4138,28 @@ void goto_tabpage(int n) } } - goto_tabpage_tp(tp, TRUE, TRUE); - + goto_tabpage_tp(tp, true, true); } -/* - * Go to tabpage "tp". - * Only trigger *Enter autocommands when trigger_enter_autocmds is TRUE. - * Only trigger *Leave autocommands when trigger_leave_autocmds is TRUE. - * Note: doesn't update the GUI tab. - */ -void goto_tabpage_tp(tabpage_T *tp, int trigger_enter_autocmds, int trigger_leave_autocmds) +/// Go to tabpage "tp". +/// Note: doesn't update the GUI tab. +/// +/// @param trigger_enter_autocmds when true trigger *Enter autocommands. +/// @param trigger_leave_autocmds when true trigger *Leave autocommands. +void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_leave_autocmds) { - /* Don't repeat a message in another tab page. */ + // Don't repeat a message in another tab page. set_keep_msg(NULL, 0); if (tp != curtab && leave_tabpage(tp->tp_curwin->w_buffer, - trigger_leave_autocmds) == OK) { - if (valid_tabpage(tp)) + trigger_leave_autocmds) == OK) { + if (valid_tabpage(tp)) { enter_tabpage(tp, curbuf, trigger_enter_autocmds, - trigger_leave_autocmds); - else + trigger_leave_autocmds); + } else { enter_tabpage(curtab, curbuf, trigger_enter_autocmds, - trigger_leave_autocmds); + trigger_leave_autocmds); + } } } @@ -4096,7 +4178,7 @@ void goto_tabpage_lastused(void) */ void goto_tabpage_win(tabpage_T *tp, win_T *wp) { - goto_tabpage_tp(tp, TRUE, TRUE); + goto_tabpage_tp(tp, true, true); if (curtab == tp && win_valid(wp)) { win_enter(wp, true); } @@ -4152,8 +4234,8 @@ void tabpage_move(int nr) tp_dst->tp_next = curtab; } - /* Need to redraw the tabline. Tab page contents doesn't change. */ - redraw_tabline = TRUE; + // Need to redraw the tabline. Tab page contents doesn't change. + redraw_tabline = true; } @@ -4166,20 +4248,22 @@ void tabpage_move(int nr) */ void win_goto(win_T *wp) { - win_T *owp = curwin; + win_T *owp = curwin; if (text_locked()) { beep_flush(); text_locked_msg(); return; } - if (curbuf_locked()) + if (curbuf_locked()) { return; + } - if (wp->w_buffer != curbuf) + if (wp->w_buffer != curbuf) { reset_VIsual_and_resel(); - else if (VIsual_active) + } else if (VIsual_active) { wp->w_cursor = curwin->w_cursor; + } win_enter(wp, true); @@ -4217,9 +4301,9 @@ tabpage_T *win_find_tabpage(win_T *win) /// @return found window win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count) { - frame_T *fr; - frame_T *nfr; - frame_T *foundfr; + frame_T *fr; + frame_T *nfr; + frame_T *foundfr; foundfr = wp->w_frame; @@ -4265,9 +4349,11 @@ win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count) fr = fr->fr_next; } } - if (nfr->fr_layout == FR_COL && up) - while (fr->fr_next != NULL) + if (nfr->fr_layout == FR_COL && up) { + while (fr->fr_next != NULL) { fr = fr->fr_next; + } + } nfr = fr; } } @@ -4298,9 +4384,9 @@ static void win_goto_ver(bool up, long count) /// @return found window win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count) { - frame_T *fr; - frame_T *nfr; - frame_T *foundfr; + frame_T *fr; + frame_T *nfr; + frame_T *foundfr; foundfr = wp->w_frame; @@ -4339,15 +4425,18 @@ win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count) } fr = nfr->fr_child; if (nfr->fr_layout == FR_COL) { - /* Find the frame at the cursor row. */ + // Find the frame at the cursor row. while (fr->fr_next != NULL && frame2win(fr)->w_winrow + fr->fr_height - <= wp->w_winrow + wp->w_wrow) + <= wp->w_winrow + wp->w_wrow) { fr = fr->fr_next; + } } - if (nfr->fr_layout == FR_ROW && left) - while (fr->fr_next != NULL) + if (nfr->fr_layout == FR_ROW && left) { + while (fr->fr_next != NULL) { fr = fr->fr_next; + } + } nfr = fr; } } @@ -4376,19 +4465,18 @@ void win_enter(win_T *wp, bool undo_sync) win_enter_ext(wp, undo_sync, false, false, true, true); } -/* - * Make window wp the current window. - * Can be called with "curwin_invalid" TRUE, which means that curwin has just - * been closed and isn't valid. - */ -static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, - int trigger_new_autocmds, int trigger_enter_autocmds, - int trigger_leave_autocmds) +/// Make window wp the current window. +/// +/// @param curwin_invalid curwin has just been closed and +/// isn't valid when true. +static void win_enter_ext(win_T *wp, bool undo_sync, bool curwin_invalid, bool trigger_new_autocmds, + bool trigger_enter_autocmds, bool trigger_leave_autocmds) { int other_buffer = FALSE; - if (wp == curwin && !curwin_invalid) /* nothing to do */ + if (wp == curwin && !curwin_invalid) { // nothing to do return; + } if (!curwin_invalid && trigger_leave_autocmds) { /* @@ -4397,20 +4485,23 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, if (wp->w_buffer != curbuf) { apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); other_buffer = TRUE; - if (!win_valid(wp)) + if (!win_valid(wp)) { return; + } } apply_autocmds(EVENT_WINLEAVE, NULL, NULL, FALSE, curbuf); - if (!win_valid(wp)) + if (!win_valid(wp)) { return; - /* autocmds may abort script processing */ - if (aborting()) + } + // autocmds may abort script processing + if (aborting()) { return; + } } // sync undo before leaving the current buffer if (undo_sync && curbuf != wp->w_buffer) { - u_sync(FALSE); + u_sync(false); } // Might need to scroll the old window before switching, e.g., when the @@ -4422,16 +4513,17 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, buf_copy_options(wp->w_buffer, BCO_ENTER | BCO_NOHELP); } if (!curwin_invalid) { - prevwin = curwin; /* remember for CTRL-W p */ + prevwin = curwin; // remember for CTRL-W p curwin->w_redr_status = TRUE; } curwin = wp; curbuf = wp->w_buffer; check_cursor(); - if (!virtual_active()) + if (!virtual_active()) { curwin->w_cursor.coladd = 0; - changed_line_abv_curs(); /* assume cursor position needs updating */ + } + changed_line_abv_curs(); // assume cursor position needs updating // New directory is either the local directory of the window, tab or NULL. char *new_dir = (char *)(curwin->w_localdir @@ -4506,9 +4598,9 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, win_setwidth((int)p_wiw); } - setmouse(); /* in case jumped to/from help buffer */ + setmouse(); // in case jumped to/from help buffer - /* Change directories when the 'acd' option is set. */ + // Change directories when the 'acd' option is set. do_autochdir(); } @@ -4537,12 +4629,12 @@ win_T *buf_jump_open_win(buf_T *buf) /// @return the found window, or NULL. win_T *buf_jump_open_tab(buf_T *buf) { - // First try the current tab page. { win_T *wp = buf_jump_open_win(buf); - if (wp != NULL) + if (wp != NULL) { return wp; + } } FOR_ALL_TABS(tp) { @@ -4570,11 +4662,9 @@ win_T *buf_jump_open_tab(buf_T *buf) return NULL; } -/* - * Allocate a window structure and link it in the window list when "hidden" is - * FALSE. - */ -static win_T *win_alloc(win_T *after, int hidden) +/// @param hidden allocate a window structure and link it in the window if +// false. +static win_T *win_alloc(win_T *after, bool hidden) { static int last_win_id = LOWEST_WIN_ID - 1; @@ -4582,7 +4672,7 @@ static win_T *win_alloc(win_T *after, int hidden) win_T *new_wp = xcalloc(1, sizeof(win_T)); new_wp->handle = ++last_win_id; - handle_register_window(new_wp); + pmap_put(handle_T)(&window_handles, new_wp->handle, new_wp); grid_assign_handle(&new_wp->w_grid_alloc); @@ -4597,13 +4687,14 @@ static win_T *win_alloc(win_T *after, int hidden) /* * link the window in the window list */ - if (!hidden) + if (!hidden) { win_append(after, new_wp); + } new_wp->w_wincol = 0; new_wp->w_width = Columns; - /* position the display and the cursor at the top of the file. */ + // position the display and the cursor at the top of the file. new_wp->w_topline = 1; new_wp->w_topfill = 0; new_wp->w_botline = 2; @@ -4617,7 +4708,7 @@ static win_T *win_alloc(win_T *after, int hidden) new_wp->w_p_so = -1; new_wp->w_p_siso = -1; - /* We won't calculate w_fraction until resizing the window */ + // We won't calculate w_fraction until resizing the window new_wp->w_fraction = 0; new_wp->w_prev_fraction_row = -1; @@ -4629,22 +4720,29 @@ static win_T *win_alloc(win_T *after, int hidden) } -/* - * Remove window 'wp' from the window list and free the structure. - */ -static void -win_free ( - win_T *wp, - tabpage_T *tp /* tab page "win" is in, NULL for current */ -) +// Free one wininfo_T. +void free_wininfo(wininfo_T *wip, buf_T *bp) +{ + if (wip->wi_optset) { + clear_winopt(&wip->wi_opt); + deleteFoldRecurse(bp, &wip->wi_folds); + } + xfree(wip); +} + + +/// Remove window 'wp' from the window list and free the structure. +/// +/// @param tp tab page "win" is in, NULL for current +static void win_free(win_T *wp, tabpage_T *tp) { int i; - wininfo_T *wip; + wininfo_T *wip; - handle_unregister_window(wp); + pmap_del(handle_T)(&window_handles, wp->handle); clearFolding(wp); - /* reduce the reference count to the argument list. */ + // reduce the reference count to the argument list. alist_unlink(wp->w_alist); /* Don't execute autocommands while the window is halfway being deleted. @@ -4654,7 +4752,7 @@ win_free ( clear_winopt(&wp->w_onebuf_opt); clear_winopt(&wp->w_allbuf_opt); - vars_clear(&wp->w_vars->dv_hashtab); /* free all w: variables */ + vars_clear(&wp->w_vars->dv_hashtab); // free all w: variables hash_init(&wp->w_vars->dv_hashtab); unref_var_dict(wp->w_vars); @@ -4679,9 +4777,32 @@ win_free ( /* Remove the window from the b_wininfo lists, it may happen that the * freed memory is re-used for another window. */ FOR_ALL_BUFFERS(buf) { - for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) - if (wip->wi_win == wp) + for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) { + if (wip->wi_win == wp) { + wininfo_T *wip2; + + // If there already is an entry with "wi_win" set to NULL it + // must be removed, it would never be used. + // Skip "wip" itself, otherwise Coverity complains. + for (wip2 = buf->b_wininfo; wip2 != NULL; wip2 = wip2->wi_next) { + // `wip2 != wip` to satisfy Coverity. #14884 + if (wip2 != wip && wip2->wi_win == NULL) { + if (wip2->wi_next != NULL) { + wip2->wi_next->wi_prev = wip2->wi_prev; + } + if (wip2->wi_prev == NULL) { + buf->b_wininfo = wip2->wi_next; + } else { + wip2->wi_prev->wi_next = wip2->wi_next; + } + free_wininfo(wip2, buf); + break; + } + } + wip->wi_win = NULL; + } + } } clear_matches(wp); @@ -4694,8 +4815,9 @@ win_free ( win_free_grid(wp, false); - if (wp != aucmd_win) + if (wp != aucmd_win) { win_remove(wp, tp); + } if (autocmd_busy) { wp->w_next = au_pending_free_win; au_pending_free_win = wp; @@ -4724,33 +4846,32 @@ void win_free_grid(win_T *wp, bool reinit) */ void win_append(win_T *after, win_T *wp) { - win_T *before; + win_T *before; - if (after == NULL) /* after NULL is in front of the first */ + if (after == NULL) { // after NULL is in front of the first before = firstwin; - else + } else { before = after->w_next; + } wp->w_next = before; wp->w_prev = after; - if (after == NULL) + if (after == NULL) { firstwin = wp; - else + } else { after->w_next = wp; - if (before == NULL) + } + if (before == NULL) { lastwin = wp; - else + } else { before->w_prev = wp; + } } -/* - * Remove a window from the window list. - */ -void -win_remove ( - win_T *wp, - tabpage_T *tp /* tab page "win" is in, NULL for current */ -) +/// Remove a window from the window list. +/// +/// @param tp tab page "win" is in, NULL for current +void win_remove(win_T *wp, tabpage_T *tp) { if (wp->w_prev != NULL) { wp->w_prev->w_next = wp->w_next; @@ -4775,8 +4896,9 @@ static void frame_append(frame_T *after, frame_T *frp) { frp->fr_next = after->fr_next; after->fr_next = frp; - if (frp->fr_next != NULL) + if (frp->fr_next != NULL) { frp->fr_next->fr_prev = frp; + } frp->fr_prev = after; } @@ -4788,10 +4910,11 @@ static void frame_insert(frame_T *before, frame_T *frp) frp->fr_next = before; frp->fr_prev = before->fr_prev; before->fr_prev = frp; - if (frp->fr_prev != NULL) + if (frp->fr_prev != NULL) { frp->fr_prev->fr_next = frp; - else + } else { frp->fr_parent->fr_child = frp; + } } /* @@ -4822,22 +4945,24 @@ void shell_new_rows(void) { int h = (int)ROWS_AVAIL; - if (firstwin == NULL) /* not initialized yet */ + if (firstwin == NULL) { // not initialized yet return; - if (h < frame_minheight(topframe, NULL)) + } + if (h < frame_minheight(topframe, NULL)) { h = frame_minheight(topframe, NULL); + } /* First try setting the heights of windows with 'winfixheight'. If * that doesn't result in the right height, forget about that option. */ - frame_new_height(topframe, h, FALSE, TRUE); - if (!frame_check_height(topframe, h)) - frame_new_height(topframe, h, FALSE, FALSE); + frame_new_height(topframe, h, false, true); + if (!frame_check_height(topframe, h)) { + frame_new_height(topframe, h, false, false); + } (void)win_comp_pos(); // recompute w_winrow and w_wincol win_reconfig_floats(); // The size of floats might change compute_cmdrow(); curtab->tp_ch_used = p_ch; - } /* @@ -4845,8 +4970,9 @@ void shell_new_rows(void) */ void shell_new_columns(void) { - if (firstwin == NULL) /* not initialized yet */ + if (firstwin == NULL) { // not initialized yet return; + } /* First try setting the widths of windows with 'winfixwidth'. If that * doesn't result in the right width, forget about that option. */ @@ -4919,7 +5045,7 @@ void win_size_restore(garray_T *gap) } } } - /* recompute the window positions */ + // recompute the window positions (void)win_comp_pos(); } } @@ -4961,17 +5087,16 @@ void win_reconfig_floats(void) */ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) { - win_T *wp; - frame_T *frp; + win_T *wp; + frame_T *frp; int startcol; int startrow; wp = topfrp->fr_win; if (wp != NULL) { if (wp->w_winrow != *row - || wp->w_wincol != *col - ) { - /* position changed, redraw */ + || wp->w_wincol != *col) { + // position changed, redraw wp->w_winrow = *row; wp->w_wincol = *col; redraw_later(wp, NOT_VALID); @@ -5042,7 +5167,6 @@ void win_setheight_win(int height, win_T *win) msg_col = 0; redraw_all_later(NOT_VALID); } - } @@ -5061,30 +5185,34 @@ void win_setheight_win(int height, win_T *win) */ static void frame_setheight(frame_T *curfrp, int height) { - int room; /* total number of lines available */ - int take; /* number of lines taken from other windows */ - int room_cmdline; /* lines available from cmdline */ + int room; // total number of lines available + int take; // number of lines taken from other windows + int room_cmdline; // lines available from cmdline int run; - frame_T *frp; + frame_T *frp; int h; int room_reserved; - /* If the height already is the desired value, nothing to do. */ - if (curfrp->fr_height == height) + // If the height already is the desired value, nothing to do. + if (curfrp->fr_height == height) { return; + } if (curfrp->fr_parent == NULL) { - /* topframe: can only change the command line */ - if (height > ROWS_AVAIL) + // topframe: can only change the command line + if (height > ROWS_AVAIL) { height = ROWS_AVAIL; - if (height > 0) - frame_new_height(curfrp, height, FALSE, FALSE); + } + if (height > 0) { + frame_new_height(curfrp, height, false, false); + } } else if (curfrp->fr_parent->fr_layout == FR_ROW) { /* Row of frames: Also need to resize frames left and right of this * one. First check for the minimal height of these. */ h = frame_minheight(curfrp->fr_parent, NULL); - if (height < h) + if (height < h) { height = h; + } frame_setheight(curfrp->fr_parent, height); } else { /* @@ -5116,7 +5244,7 @@ static void frame_setheight(frame_T *curfrp, int height) } else { win_T *wp = lastwin_nofloating(); room_cmdline = Rows - p_ch - - (wp->w_winrow + wp->w_height + wp->w_status_height); + - (wp->w_winrow + wp->w_height + wp->w_status_height); if (room_cmdline < 0) { room_cmdline = 0; } @@ -5130,8 +5258,8 @@ static void frame_setheight(frame_T *curfrp, int height) break; } frame_setheight(curfrp->fr_parent, height - + frame_minheight(curfrp->fr_parent, NOWIN) - (int)p_wmh - 1); - /*NOTREACHED*/ + + frame_minheight(curfrp->fr_parent, NOWIN) - (int)p_wmh - 1); + //NOTREACHED } /* @@ -5142,17 +5270,20 @@ static void frame_setheight(frame_T *curfrp, int height) /* If there is not enough room, also reduce the height of a window * with 'winfixheight' set. */ - if (height > room + room_cmdline - room_reserved) + if (height > room + room_cmdline - room_reserved) { room_reserved = room + room_cmdline - height; + } /* If there is only a 'winfixheight' window and making the * window smaller, need to make the other window taller. */ - if (take < 0 && room - curfrp->fr_height < room_reserved) + if (take < 0 && room - curfrp->fr_height < room_reserved) { room_reserved = 0; + } if (take > 0 && room_cmdline > 0) { - /* use lines from cmdline first */ - if (take < room_cmdline) + // use lines from cmdline first + if (take < room_cmdline) { room_cmdline = take; + } take -= room_cmdline; topframe->fr_height += room_cmdline; } @@ -5160,7 +5291,7 @@ static void frame_setheight(frame_T *curfrp, int height) /* * set the current frame to the new height */ - frame_new_height(curfrp, height, FALSE, FALSE); + frame_new_height(curfrp, height, false, false); /* * First take lines from the frames after the current frame. If @@ -5168,38 +5299,40 @@ static void frame_setheight(frame_T *curfrp, int height) * frame. */ for (run = 0; run < 2; ++run) { - if (run == 0) - frp = curfrp->fr_next; /* 1st run: start with next window */ - else - frp = curfrp->fr_prev; /* 2nd run: start with prev window */ + if (run == 0) { + frp = curfrp->fr_next; // 1st run: start with next window + } else { + frp = curfrp->fr_prev; // 2nd run: start with prev window + } while (frp != NULL && take != 0) { h = frame_minheight(frp, NULL); if (room_reserved > 0 && frp->fr_win != NULL && frp->fr_win->w_p_wfh) { - if (room_reserved >= frp->fr_height) + if (room_reserved >= frp->fr_height) { room_reserved -= frp->fr_height; - else { - if (frp->fr_height - room_reserved > take) + } else { + if (frp->fr_height - room_reserved > take) { room_reserved = frp->fr_height - take; + } take -= frp->fr_height - room_reserved; - frame_new_height(frp, room_reserved, FALSE, FALSE); + frame_new_height(frp, room_reserved, false, false); room_reserved = 0; } } else { if (frp->fr_height - take < h) { take -= frp->fr_height - h; - frame_new_height(frp, h, FALSE, FALSE); + frame_new_height(frp, h, false, false); } else { - frame_new_height(frp, frp->fr_height - take, - FALSE, FALSE); + frame_new_height(frp, frp->fr_height - take, false, false); take = 0; } } - if (run == 0) + if (run == 0) { frp = frp->fr_next; - else + } else { frp = frp->fr_prev; + } } } } @@ -5219,10 +5352,12 @@ void win_setwidth_win(int width, win_T *wp) /* Always keep current window at least one column wide, even when * 'winminwidth' is zero. */ if (wp == curwin) { - if (width < p_wmw) + if (width < p_wmw) { width = p_wmw; - if (width == 0) + } + if (width == 0) { width = 1; + } } else if (width < 0) { width = 0; } @@ -5237,7 +5372,6 @@ void win_setwidth_win(int width, win_T *wp) (void)win_comp_pos(); redraw_all_later(NOT_VALID); } - } /* @@ -5249,27 +5383,30 @@ void win_setwidth_win(int width, win_T *wp) */ static void frame_setwidth(frame_T *curfrp, int width) { - int room; /* total number of lines available */ - int take; /* number of lines taken from other windows */ + int room; // total number of lines available + int take; // number of lines taken from other windows int run; - frame_T *frp; + frame_T *frp; int w; int room_reserved; - /* If the width already is the desired value, nothing to do. */ - if (curfrp->fr_width == width) + // If the width already is the desired value, nothing to do. + if (curfrp->fr_width == width) { return; + } - if (curfrp->fr_parent == NULL) - /* topframe: can't change width */ + if (curfrp->fr_parent == NULL) { + // topframe: can't change width return; + } if (curfrp->fr_parent->fr_layout == FR_COL) { /* Column of frames: Also need to resize frames above and below of * this one. First check for the minimal width of these. */ w = frame_minwidth(curfrp->fr_parent, NULL); - if (width < w) + if (width < w) { width = w; + } frame_setwidth(curfrp->fr_parent, width); } else { /* @@ -5295,14 +5432,15 @@ static void frame_setwidth(frame_T *curfrp, int width) } } - if (width <= room) + if (width <= room) { break; + } if (run == 2 || curfrp->fr_height >= ROWS_AVAIL) { width = room; break; } frame_setwidth(curfrp->fr_parent, width - + frame_minwidth(curfrp->fr_parent, NOWIN) - (int)p_wmw - 1); + + frame_minwidth(curfrp->fr_parent, NOWIN) - (int)p_wmw - 1); } /* @@ -5313,17 +5451,19 @@ static void frame_setwidth(frame_T *curfrp, int width) /* If there is not enough room, also reduce the width of a window * with 'winfixwidth' set. */ - if (width > room - room_reserved) + if (width > room - room_reserved) { room_reserved = room - width; + } /* If there is only a 'winfixwidth' window and making the * window smaller, need to make the other window narrower. */ - if (take < 0 && room - curfrp->fr_width < room_reserved) + if (take < 0 && room - curfrp->fr_width < room_reserved) { room_reserved = 0; + } /* * set the current frame to the new width */ - frame_new_width(curfrp, width, FALSE, FALSE); + frame_new_width(curfrp, width, false, false); /* * First take lines from the frames right of the current frame. If @@ -5331,38 +5471,40 @@ static void frame_setwidth(frame_T *curfrp, int width) * frame. */ for (run = 0; run < 2; ++run) { - if (run == 0) - frp = curfrp->fr_next; /* 1st run: start with next window */ - else - frp = curfrp->fr_prev; /* 2nd run: start with prev window */ + if (run == 0) { + frp = curfrp->fr_next; // 1st run: start with next window + } else { + frp = curfrp->fr_prev; // 2nd run: start with prev window + } while (frp != NULL && take != 0) { w = frame_minwidth(frp, NULL); if (room_reserved > 0 && frp->fr_win != NULL && frp->fr_win->w_p_wfw) { - if (room_reserved >= frp->fr_width) + if (room_reserved >= frp->fr_width) { room_reserved -= frp->fr_width; - else { - if (frp->fr_width - room_reserved > take) + } else { + if (frp->fr_width - room_reserved > take) { room_reserved = frp->fr_width - take; + } take -= frp->fr_width - room_reserved; - frame_new_width(frp, room_reserved, FALSE, FALSE); + frame_new_width(frp, room_reserved, false, false); room_reserved = 0; } } else { if (frp->fr_width - take < w) { take -= frp->fr_width - w; - frame_new_width(frp, w, FALSE, FALSE); + frame_new_width(frp, w, false, false); } else { - frame_new_width(frp, frp->fr_width - take, - FALSE, FALSE); + frame_new_width(frp, frp->fr_width - take, false, false); take = 0; } } - if (run == 0) + if (run == 0) { frp = frp->fr_next; - else + } else { frp = frp->fr_prev; + } } } } @@ -5408,69 +5550,71 @@ void win_setminwidth(void) } } -/* - * Status line of dragwin is dragged "offset" lines down (negative is up). - */ +/// Status line of dragwin is dragged "offset" lines down (negative is up). void win_drag_status_line(win_T *dragwin, int offset) { - frame_T *curfr; - frame_T *fr; + frame_T *curfr; + frame_T *fr; int room; int row; - int up; /* if TRUE, drag status line up, otherwise down */ + bool up; // if true, drag status line up, otherwise down int n; fr = dragwin->w_frame; curfr = fr; - if (fr != topframe) { /* more than one window */ + if (fr != topframe) { // more than one window fr = fr->fr_parent; /* When the parent frame is not a column of frames, its parent should * be. */ if (fr->fr_layout != FR_COL) { curfr = fr; - if (fr != topframe) /* only a row of windows, may drag statusline */ + if (fr != topframe) { // only a row of windows, may drag statusline fr = fr->fr_parent; + } } } /* If this is the last frame in a column, may want to resize the parent * frame instead (go two up to skip a row of frames). */ while (curfr != topframe && curfr->fr_next == NULL) { - if (fr != topframe) + if (fr != topframe) { fr = fr->fr_parent; + } curfr = fr; - if (fr != topframe) + if (fr != topframe) { fr = fr->fr_parent; + } } - if (offset < 0) { /* drag up */ - up = TRUE; + if (offset < 0) { // drag up + up = true; offset = -offset; - /* sum up the room of the current frame and above it */ + // sum up the room of the current frame and above it if (fr == curfr) { - /* only one window */ + // only one window room = fr->fr_height - frame_minheight(fr, NULL); } else { room = 0; for (fr = fr->fr_child;; fr = fr->fr_next) { room += fr->fr_height - frame_minheight(fr, NULL); - if (fr == curfr) + if (fr == curfr) { break; + } } } - fr = curfr->fr_next; /* put fr at frame that grows */ - } else { /* drag down */ - up = FALSE; - /* - * Only dragging the last status line can reduce p_ch. - */ + fr = curfr->fr_next; // put fr at frame that grows + } else { // drag down + up = false; + // Only dragging the last status line can reduce p_ch. room = Rows - cmdline_row; - if (curfr->fr_next == NULL) + if (curfr->fr_next == NULL) { room -= 1; - else + } else { room -= p_ch; - if (room < 0) + } + if (room < 0) { room = 0; + } // sum up the room of frames below of the current one FOR_ALL_FRAMES(fr, curfr->fr_next) { room += fr->fr_height - frame_minheight(fr, NULL); @@ -5478,23 +5622,26 @@ void win_drag_status_line(win_T *dragwin, int offset) fr = curfr; // put fr at window that grows } - if (room < offset) /* Not enough room */ - offset = room; /* Move as far as we can */ - if (offset <= 0) + if (room < offset) { // Not enough room + offset = room; // Move as far as we can + } + if (offset <= 0) { return; + } /* * Grow frame fr by "offset" lines. * Doesn't happen when dragging the last status line up. */ - if (fr != NULL) - frame_new_height(fr, fr->fr_height + offset, up, FALSE); - - if (up) - fr = curfr; /* current frame gets smaller */ - else - fr = curfr->fr_next; /* next frame gets smaller */ + if (fr != NULL) { + frame_new_height(fr, fr->fr_height + offset, up, false); + } + if (up) { + fr = curfr; // current frame gets smaller + } else { + fr = curfr->fr_next; // next frame gets smaller + } /* * Now make the other frames smaller. */ @@ -5502,15 +5649,16 @@ void win_drag_status_line(win_T *dragwin, int offset) n = frame_minheight(fr, NULL); if (fr->fr_height - offset <= n) { offset -= fr->fr_height - n; - frame_new_height(fr, n, !up, FALSE); + frame_new_height(fr, n, !up, false); } else { - frame_new_height(fr, fr->fr_height - offset, !up, FALSE); + frame_new_height(fr, fr->fr_height - offset, !up, false); break; } - if (up) + if (up) { fr = fr->fr_prev; - else + } else { fr = fr->fr_next; + } } row = win_comp_pos(); grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0); @@ -5519,8 +5667,9 @@ void win_drag_status_line(win_T *dragwin, int offset) } cmdline_row = row; p_ch = Rows - cmdline_row; - if (p_ch < 1) + if (p_ch < 1) { p_ch = 1; + } curtab->tp_ch_used = p_ch; redraw_all_later(SOME_VALID); showmode(); @@ -5531,21 +5680,23 @@ void win_drag_status_line(win_T *dragwin, int offset) */ void win_drag_vsep_line(win_T *dragwin, int offset) { - frame_T *curfr; - frame_T *fr; + frame_T *curfr; + frame_T *fr; int room; - int left; /* if TRUE, drag separator line left, otherwise right */ + bool left; // if true, drag separator line left, otherwise right int n; fr = dragwin->w_frame; - if (fr == topframe) /* only one window (cannot happen?) */ + if (fr == topframe) { // only one window (cannot happen?) return; + } curfr = fr; fr = fr->fr_parent; - /* When the parent frame is not a row of frames, its parent should be. */ + // When the parent frame is not a row of frames, its parent should be. if (fr->fr_layout != FR_ROW) { - if (fr == topframe) /* only a column of windows (cannot happen?) */ + if (fr == topframe) { // only a column of windows (cannot happen?) return; + } curfr = fr; fr = fr->fr_parent; } @@ -5553,8 +5704,9 @@ void win_drag_vsep_line(win_T *dragwin, int offset) /* If this is the last frame in a row, may want to resize a parent * frame instead. */ while (curfr->fr_next == NULL) { - if (fr == topframe) + if (fr == topframe) { break; + } curfr = fr; fr = fr->fr_parent; if (fr != topframe) { @@ -5563,20 +5715,21 @@ void win_drag_vsep_line(win_T *dragwin, int offset) } } - if (offset < 0) { /* drag left */ - left = TRUE; + if (offset < 0) { // drag left + left = true; offset = -offset; - /* sum up the room of the current frame and left of it */ + // sum up the room of the current frame and left of it room = 0; for (fr = fr->fr_child;; fr = fr->fr_next) { room += fr->fr_width - frame_minwidth(fr, NULL); - if (fr == curfr) + if (fr == curfr) { break; + } } - fr = curfr->fr_next; /* put fr at frame that grows */ - } else { /* drag right */ - left = FALSE; - /* sum up the room of frames right of the current one */ + fr = curfr->fr_next; // put fr at frame that grows + } else { // drag right + left = false; + // sum up the room of frames right of the current one room = 0; FOR_ALL_FRAMES(fr, curfr->fr_next) { room += fr->fr_width - frame_minwidth(fr, NULL); @@ -5599,28 +5752,29 @@ void win_drag_vsep_line(win_T *dragwin, int offset) return; // Safety check, should not happen. } - /* grow frame fr by offset lines */ - frame_new_width(fr, fr->fr_width + offset, left, FALSE); - - /* shrink other frames: current and at the left or at the right */ - if (left) - fr = curfr; /* current frame gets smaller */ - else - fr = curfr->fr_next; /* next frame gets smaller */ + // grow frame fr by offset lines + frame_new_width(fr, fr->fr_width + offset, left, false); + // shrink other frames: current and at the left or at the right + if (left) { + fr = curfr; // current frame gets smaller + } else { + fr = curfr->fr_next; // next frame gets smaller + } while (fr != NULL && offset > 0) { n = frame_minwidth(fr, NULL); if (fr->fr_width - offset <= n) { offset -= fr->fr_width - n; - frame_new_width(fr, n, !left, FALSE); + frame_new_width(fr, n, !left, false); } else { - frame_new_width(fr, fr->fr_width - offset, !left, FALSE); + frame_new_width(fr, fr->fr_width - offset, !left, false); break; } - if (left) + if (left) { fr = fr->fr_prev; - else + } else { fr = fr->fr_next; + } } (void)win_comp_pos(); redraw_all_later(NOT_VALID); @@ -5638,7 +5792,7 @@ void set_fraction(win_T *wp) // it's halfway that line. Thus with two lines it is 25%, with three // lines 17%, etc. Similarly for the last line: 75%, 83%, etc. wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + FRACTION_MULT / 2) - / (long)wp->w_height_inner; + / (long)wp->w_height_inner; } } @@ -5664,9 +5818,9 @@ void win_new_height(win_T *wp, int height) void scroll_to_fraction(win_T *wp, int prev_height) { - linenr_T lnum; - int sline, line_size; - int height = wp->w_height_inner; + linenr_T lnum; + int sline, line_size; + int height = wp->w_height_inner; // Don't change w_topline in any of these cases: // - window height is 0 @@ -5675,15 +5829,16 @@ void scroll_to_fraction(win_T *wp, int prev_height) // is visible. if (height > 0 && (!wp->w_p_scb || wp == curwin) - && (height < wp->w_buffer->b_ml.ml_line_count || wp->w_topline > 1) - ) { + && (height < wp->w_buffer->b_ml.ml_line_count || + wp->w_topline > 1)) { /* * Find a value for w_topline that shows the cursor at the same * relative position in the window as before (more or less). */ lnum = wp->w_cursor.lnum; - if (lnum < 1) /* can happen when starting up */ + if (lnum < 1) { // can happen when starting up lnum = 1; + } wp->w_wrow = ((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT; line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1; sline = wp->w_wrow - line_size; @@ -5719,7 +5874,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) while (sline > 0 && lnum > 1) { (void)hasFoldingWin(wp, lnum, &lnum, NULL, true, NULL); if (lnum == 1) { - /* first line in buffer is folded */ + // first line in buffer is folded line_size = 1; --sline; break; @@ -5858,7 +6013,7 @@ void win_comp_scroll(win_T *wp) void command_height(void) { int h; - frame_T *frp; + frame_T *frp; int old_p_ch = curtab->tp_ch_used; /* Use the value of p_ch that we remembered. This is needed for when the @@ -5872,15 +6027,16 @@ void command_height(void) frp = frp->fr_parent; } - /* Avoid changing the height of a window with 'winfixheight' set. */ + // Avoid changing the height of a window with 'winfixheight' set. while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF - && frp->fr_win->w_p_wfh) + && frp->fr_win->w_p_wfh) { frp = frp->fr_prev; + } if (starting != NO_SCREEN) { cmdline_row = Rows - p_ch; - if (p_ch > old_p_ch) { /* p_ch got bigger */ + if (p_ch > old_p_ch) { // p_ch got bigger while (p_ch > old_p_ch) { if (frp == NULL) { EMSG(_(e_noroom)); @@ -5890,14 +6046,15 @@ void command_height(void) break; } h = frp->fr_height - frame_minheight(frp, NULL); - if (h > p_ch - old_p_ch) + if (h > p_ch - old_p_ch) { h = p_ch - old_p_ch; + } old_p_ch += h; frame_add_height(frp, -h); frp = frp->fr_prev; } - /* Recompute window positions. */ + // Recompute window positions. (void)win_comp_pos(); // clear the lines added to cmdline @@ -5905,19 +6062,21 @@ void command_height(void) grid_fill(&default_grid, cmdline_row, Rows, 0, Columns, ' ', ' ', 0); } msg_row = cmdline_row; - redraw_cmdline = TRUE; + redraw_cmdline = true; return; } - if (msg_row < cmdline_row) + if (msg_row < cmdline_row) { msg_row = cmdline_row; - redraw_cmdline = TRUE; + } + redraw_cmdline = true; } frame_add_height(frp, (int)(old_p_ch - p_ch)); - /* Recompute window positions. */ - if (frp != lastwin->w_frame) + // Recompute window positions. + if (frp != lastwin->w_frame) { (void)win_comp_pos(); + } } /* @@ -5926,11 +6085,12 @@ void command_height(void) */ static void frame_add_height(frame_T *frp, int n) { - frame_new_height(frp, frp->fr_height + n, FALSE, FALSE); + frame_new_height(frp, frp->fr_height + n, false, false); for (;; ) { frp = frp->fr_parent; - if (frp == NULL) + if (frp == NULL) { break; + } frp->fr_height += n; } } @@ -5945,9 +6105,10 @@ char_u *grab_file_name(long count, linenr_T *file_lnum) int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC; if (VIsual_active) { size_t len; - char_u *ptr; - if (get_visual_text(NULL, &ptr, &len) == FAIL) + char_u *ptr; + if (get_visual_text(NULL, &ptr, &len) == FAIL) { return NULL; + } // Only recognize ":123" here if (file_lnum != NULL && ptr[len] == ':' && isdigit(ptr[len + 1])) { char_u *p = ptr + len + 1; @@ -5967,33 +6128,26 @@ char_u *grab_file_name(long count, linenr_T *file_lnum) * NULL is returned if the file name or file is not found. * * options: - * FNAME_MESS give error messages - * FNAME_EXP expand to path - * FNAME_HYP check for hypertext link - * FNAME_INCL apply "includeexpr" + * FNAME_MESS give error messages + * FNAME_EXP expand to path + * FNAME_HYP check for hypertext link + * FNAME_INCL apply "includeexpr" */ char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum) { return file_name_in_line(get_cursor_line_ptr(), - curwin->w_cursor.col, options, count, curbuf->b_ffname, - file_lnum); + curwin->w_cursor.col, options, count, curbuf->b_ffname, + file_lnum); } -/* - * Return the name of the file under or after ptr[col]. - * Otherwise like file_name_at_cursor(). - */ -char_u * -file_name_in_line ( - char_u *line, - int col, - int options, - long count, - char_u *rel_fname, /* file we are searching relative to */ - linenr_T *file_lnum /* line number after the file name */ -) -{ - char_u *ptr; +/// @param rel_fname file we are searching relative to +/// @param file_lnum line number after the file name +/// +/// @return the name of the file under or after ptr[col]. Otherwise like file_name_at_cursor(). +char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u *rel_fname, + linenr_T *file_lnum) +{ + char_u *ptr; size_t len; bool in_type = true; bool is_url = false; @@ -6058,8 +6212,9 @@ file_name_in_line ( * But don't remove "..", could be a directory name. */ if (len > 2 && vim_strchr((char_u *)".,:;!", ptr[len - 1]) != NULL - && ptr[len - 2] != '.') + && ptr[len - 2] != '.') { --len; + } if (file_lnum != NULL) { char_u *p; @@ -6091,34 +6246,31 @@ file_name_in_line ( return find_file_name_in_path(ptr, len, options, count, rel_fname); } -/* - * Add or remove a status line for the bottom window(s), according to the - * value of 'laststatus'. - */ -void -last_status ( - int morewin /* pretend there are two or more windows */ -) +/// Add or remove a status line for the bottom window(s), according to the +/// value of 'laststatus'. +/// +/// @param morewin pretend there are two or more windows if true. +void last_status(bool morewin) { - /* Don't make a difference between horizontal or vertical split. */ + // Don't make a difference between horizontal or vertical split. last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_window())))); } -static void last_status_rec(frame_T *fr, int statusline) +static void last_status_rec(frame_T *fr, bool statusline) { - frame_T *fp; - win_T *wp; + frame_T *fp; + win_T *wp; if (fr->fr_layout == FR_LEAF) { wp = fr->fr_win; if (wp->w_status_height != 0 && !statusline) { - /* remove status line */ + // remove status line win_new_height(wp, wp->w_height + 1); wp->w_status_height = 0; comp_col(); } else if (wp->w_status_height == 0 && statusline) { - /* Find a frame to take a line from. */ + // Find a frame to take a line from. fp = fr; while (fp->fr_height <= frame_minheight(fp, NULL)) { if (fp == topframe) { @@ -6127,18 +6279,20 @@ static void last_status_rec(frame_T *fr, int statusline) } /* In a column of frames: go to frame above. If already at * the top or in a row of frames: go to parent. */ - if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) + if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) { fp = fp->fr_prev; - else + } else { fp = fp->fr_parent; + } } wp->w_status_height = 1; if (fp != fr) { - frame_new_height(fp, fp->fr_height - 1, FALSE, FALSE); + frame_new_height(fp, fp->fr_height - 1, false, false); frame_fix_height(wp); (void)win_comp_pos(); - } else + } else { win_new_height(wp, wp->w_height - 1); + } comp_col(); redraw_all_later(SOME_VALID); } @@ -6148,9 +6302,10 @@ static void last_status_rec(frame_T *fr, int statusline) last_status_rec(fp, statusline); } } else { - /* horizontally split window, set status line for last one */ - for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) + // horizontally split window, set status line for last one + for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) { ; + } last_status_rec(fp, statusline); } } @@ -6165,8 +6320,10 @@ int tabline_height(void) } assert(first_tabpage); switch (p_stal) { - case 0: return 0; - case 1: return (first_tabpage->tp_next == NULL) ? 0 : 1; + case 0: + return 0; + case 1: + return (first_tabpage->tp_next == NULL) ? 0 : 1; } return 1; } @@ -6177,8 +6334,9 @@ int tabline_height(void) */ int min_rows(void) { - if (firstwin == NULL) /* not initialized yet */ + if (firstwin == NULL) { // not initialized yet return MIN_LINES; + } int total = 0; FOR_ALL_TABS(tp) { @@ -6188,7 +6346,7 @@ int min_rows(void) } } total += tabline_height(); - total += 1; /* count the room for the command line */ + total += 1; // count the room for the command line return total; } @@ -6213,12 +6371,11 @@ bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return count <= 1; } -/* - * Correct the cursor line number in other windows. Used after changing the - * current buffer, and before applying autocommands. - * When "do_curwin" is TRUE, also check current window. - */ -void check_lnums(int do_curwin) +/// Correct the cursor line number in other windows. Used after changing the +/// current buffer, and before applying autocommands. +/// +/// @param do_curwin when true, also check current window. +void check_lnums(bool do_curwin) { FOR_ALL_TAB_WINDOWS(tp, wp) { if ((do_curwin || wp != curwin) && wp->w_buffer == curbuf) { @@ -6284,12 +6441,15 @@ static void make_snapshot_rec(frame_T *fr, frame_T **frp) (*frp)->fr_layout = fr->fr_layout; (*frp)->fr_width = fr->fr_width; (*frp)->fr_height = fr->fr_height; - if (fr->fr_next != NULL) + if (fr->fr_next != NULL) { make_snapshot_rec(fr->fr_next, &((*frp)->fr_next)); - if (fr->fr_child != NULL) + } + if (fr->fr_child != NULL) { make_snapshot_rec(fr->fr_child, &((*frp)->fr_child)); - if (fr->fr_layout == FR_LEAF && fr->fr_win == curwin) + } + if (fr->fr_layout == FR_LEAF && fr->fr_win == curwin) { (*frp)->fr_win = curwin; + } } /* @@ -6310,18 +6470,14 @@ static void clear_snapshot_rec(frame_T *fr) } } -/* - * Restore a previously created snapshot, if there is any. - * This is only done if the screen size didn't change and the window layout is - * still the same. - */ -void -restore_snapshot ( - int idx, - int close_curwin /* closing current window */ -) +/// Restore a previously created snapshot, if there is any. +/// This is only done if the screen size didn't change and the window layout is +/// still the same. +/// +/// @param close_curwin closing current window +void restore_snapshot(int idx, int close_curwin) { - win_T *wp; + win_T *wp; if (curtab->tp_snapshot[idx] != NULL && curtab->tp_snapshot[idx]->fr_width == topframe->fr_width @@ -6348,8 +6504,9 @@ static int check_snapshot_rec(frame_T *sn, frame_T *fr) && check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL) || (sn->fr_child != NULL && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL) - || (sn->fr_win != NULL && !win_valid(sn->fr_win))) + || (sn->fr_win != NULL && !win_valid(sn->fr_win))) { return FAIL; + } return OK; } @@ -6360,25 +6517,27 @@ static int check_snapshot_rec(frame_T *sn, frame_T *fr) */ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr) { - win_T *wp = NULL; - win_T *wp2; + win_T *wp = NULL; + win_T *wp2; fr->fr_height = sn->fr_height; fr->fr_width = sn->fr_width; if (fr->fr_layout == FR_LEAF) { - frame_new_height(fr, fr->fr_height, FALSE, FALSE); - frame_new_width(fr, fr->fr_width, FALSE, FALSE); + frame_new_height(fr, fr->fr_height, false, false); + frame_new_width(fr, fr->fr_width, false, false); wp = sn->fr_win; } if (sn->fr_next != NULL) { wp2 = restore_snapshot_rec(sn->fr_next, fr->fr_next); - if (wp2 != NULL) + if (wp2 != NULL) { wp = wp2; + } } if (sn->fr_child != NULL) { wp2 = restore_snapshot_rec(sn->fr_child, fr->fr_child); - if (wp2 != NULL) + if (wp2 != NULL) { wp = wp2; + } } return wp; } @@ -6405,23 +6564,24 @@ static win_T *get_snapshot_focus(int idx) return win_valid(sn->fr_win) ? sn->fr_win : NULL; } -/* - * Set "win" to be the curwin and "tp" to be the current tab page. - * restore_win() MUST be called to undo, also when FAIL is returned. - * No autocommands will be executed until restore_win() is called. - * When "no_display" is TRUE the display won't be affected, no redraw is - * triggered, another tabpage access is limited. - * Returns FAIL if switching to "win" failed. - */ -int switch_win(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp, int no_display) +/// Set "win" to be the curwin and "tp" to be the current tab page. +/// restore_win() MUST be called to undo, also when FAIL is returned. +/// No autocommands will be executed until restore_win() is called. +/// +/// @param no_display if true the display won't be affected, no redraw is +/// triggered, another tabpage access is limited. +/// +/// @return FAIL if switching to "win" failed. +int switch_win(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp, + bool no_display) { block_autocmds(); return switch_win_noblock(save_curwin, save_curtab, win, tp, no_display); } // As switch_win() but without blocking autocommands. -int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, - win_T *win, tabpage_T *tp, int no_display) +int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp, + bool no_display) { *save_curwin = curwin; if (tp != NULL) { @@ -6432,8 +6592,9 @@ int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, curtab = tp; firstwin = curtab->tp_firstwin; lastwin = curtab->tp_lastwin; - } else - goto_tabpage_tp(tp, FALSE, FALSE); + } else { + goto_tabpage_tp(tp, false, false); + } } if (!win_valid(win)) { return FAIL; @@ -6453,8 +6614,7 @@ void restore_win(win_T *save_curwin, tabpage_T *save_curtab, bool no_display) } // As restore_win() but without unblocking autocommands. -void restore_win_noblock(win_T *save_curwin, tabpage_T *save_curtab, - bool no_display) +void restore_win_noblock(win_T *save_curwin, tabpage_T *save_curtab, bool no_display) { if (save_curtab != NULL && valid_tabpage(save_curtab)) { if (no_display) { @@ -6463,8 +6623,9 @@ void restore_win_noblock(win_T *save_curwin, tabpage_T *save_curtab, curtab = save_curtab; firstwin = curtab->tp_firstwin; lastwin = curtab->tp_lastwin; - } else - goto_tabpage_tp(save_curtab, FALSE, FALSE); + } else { + goto_tabpage_tp(save_curtab, false, false); + } } if (win_valid(save_curwin)) { curwin = save_curwin; @@ -6509,16 +6670,15 @@ void restore_buffer(bufref_T *save_curbuf) /// particular ID is desired /// @param[in] conceal_char pointer to conceal replacement char /// @return ID of added match, -1 on failure. -int match_add(win_T *wp, const char *const grp, const char *const pat, - int prio, int id, list_T *pos_list, - const char *const conceal_char) +int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, + list_T *pos_list, const char *const conceal_char) FUNC_ATTR_NONNULL_ARG(1, 2) { matchitem_T *cur; matchitem_T *prev; matchitem_T *m; int hlg_id; - regprog_T *regprog = NULL; + regprog_T *regprog = NULL; int rtype = SOME_VALID; if (*grp == NUL || (pat != NULL && *pat == NUL)) { @@ -6540,8 +6700,7 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, cur = cur->next; } } - if ((hlg_id = syn_name2id((const char_u *)grp)) == 0) { - EMSG2(_(e_nogroup), grp); + if ((hlg_id = syn_check_group((const char_u *)grp, strlen(grp))) == 0) { return -1; } if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { @@ -6552,10 +6711,12 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, // Find available match ID. while (id == -1) { cur = wp->w_match_head; - while (cur != NULL && cur->id != wp->w_next_match_id) + while (cur != NULL && cur->id != wp->w_next_match_id) { cur = cur->next; - if (cur == NULL) + } + if (cur == NULL) { id = wp->w_next_match_id; + } wp->w_next_match_id++; } @@ -6647,8 +6808,8 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, } }); - // Calculate top and bottom lines for redrawing area - if (toplnum != 0){ + // Calculate top and bottom lines for redrawing area + if (toplnum != 0) { if (wp->w_buffer->b_mod_set) { if (wp->w_buffer->b_mod_top > toplnum) { wp->w_buffer->b_mod_top = toplnum; @@ -6676,10 +6837,11 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, prev = cur; cur = cur->next; } - if (cur == prev) + if (cur == prev) { wp->w_match_head = m; - else + } else { prev->next = m; + } m->next = cur; redraw_later(wp, rtype); @@ -6692,8 +6854,9 @@ fail: /// Delete match with ID 'id' in the match list of window 'wp'. -/// Print error messages if 'perr' is TRUE. -int match_delete(win_T *wp, int id, int perr) +/// +/// @param perr print error messages if true. +int match_delete(win_T *wp, int id, bool perr) { matchitem_T *cur = wp->w_match_head; matchitem_T *prev = cur; @@ -6717,10 +6880,11 @@ int match_delete(win_T *wp, int id, int perr) } return -1; } - if (cur == prev) + if (cur == prev) { wp->w_match_head = cur->next; - else + } else { prev->next = cur->next; + } vim_regfree(cur->match.regprog); xfree(cur->pattern); if (cur->pos.toplnum != 0) { @@ -6769,8 +6933,9 @@ matchitem_T *get_match(win_T *wp, int id) { matchitem_T *cur = wp->w_match_head; - while (cur != NULL && cur->id != id) + while (cur != NULL && cur->id != id) { cur = cur->next; + } return cur; } diff --git a/src/nvim/xdiff/COPYING b/src/xdiff/COPYING index f3f1b3b65e..f3f1b3b65e 100644 --- a/src/nvim/xdiff/COPYING +++ b/src/xdiff/COPYING diff --git a/src/nvim/xdiff/README.txt b/src/xdiff/README.txt index 1afe74095b..95b2242b87 100644 --- a/src/nvim/xdiff/README.txt +++ b/src/xdiff/README.txt @@ -1,6 +1,6 @@ The files in this directory come from the xdiff implementation in git. You can find it here: https://github.com/git/git/tree/master/xdiff -The files were last updated 2018 September 10. +The files were last updated August 31, 2021 from git release v.2.33.0 This is originally based on libxdiff, which can be found here: http://www.xmailserver.org/xdiff-lib.html diff --git a/src/nvim/xdiff/xdiff.h b/src/xdiff/xdiff.h index 8ff4a05bfb..49985a0e9a 100644 --- a/src/nvim/xdiff/xdiff.h +++ b/src/xdiff/xdiff.h @@ -25,9 +25,9 @@ #ifdef __cplusplus extern "C" { -#endif // #ifdef __cplusplus +#endif /* #ifdef __cplusplus */ -// xpparm_t.flags +/* xpparm_t.flags */ #define XDF_NEED_MINIMAL (1 << 0) #define XDF_IGNORE_WHITESPACE (1 << 1) @@ -48,22 +48,23 @@ extern "C" { #define XDF_INDENT_HEURISTIC (1 << 23) -// xdemitconf_t.flags +/* xdemitconf_t.flags */ #define XDL_EMIT_FUNCNAMES (1 << 0) +#define XDL_EMIT_NO_HUNK_HDR (1 << 1) #define XDL_EMIT_FUNCCONTEXT (1 << 2) -// merge simplification levels +/* merge simplification levels */ #define XDL_MERGE_MINIMAL 0 #define XDL_MERGE_EAGER 1 #define XDL_MERGE_ZEALOUS 2 #define XDL_MERGE_ZEALOUS_ALNUM 3 -// merge favor modes +/* merge favor modes */ #define XDL_MERGE_FAVOR_OURS 1 #define XDL_MERGE_FAVOR_THEIRS 2 #define XDL_MERGE_FAVOR_UNION 3 -// merge output styles +/* merge output styles */ #define XDL_MERGE_DIFF3 1 typedef struct s_mmfile { @@ -79,14 +80,24 @@ typedef struct s_mmbuffer { typedef struct s_xpparam { unsigned long flags; - // See Documentation/diff-options.txt. + /* -I<regex> */ + #if 0 // unused by Vim + regex_t **ignore_regex; + size_t ignore_regex_nr; +#endif + + /* See Documentation/diff-options.txt. */ char **anchors; size_t anchors_nr; } xpparam_t; typedef struct s_xdemitcb { void *priv; - int (*outf)(void *, mmbuffer_t *, int); + int (*out_hunk)(void *, + long old_begin, long old_nr, + long new_begin, long new_nr, + const char *func, long funclen); + int (*out_line)(void *, mmbuffer_t *, int); } xdemitcb_t; typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv); @@ -108,7 +119,7 @@ typedef struct s_bdiffparam { long bsize; } bdiffparam_t; -#include "../memory.h" +#include "../nvim/memory.h" #define xdl_malloc(x) xmalloc((x)) #define xdl_free(ptr) xfree(ptr) @@ -126,9 +137,9 @@ typedef struct s_xmparam { int level; int favor; int style; - const char *ancestor; // label for orig - const char *file1; // label for mf1 - const char *file2; // label for mf2 + const char *ancestor; /* label for orig */ + const char *file1; /* label for mf1 */ + const char *file2; /* label for mf2 */ } xmparam_t; #define DEFAULT_CONFLICT_MARKER_SIZE 7 @@ -138,6 +149,6 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2, #ifdef __cplusplus } -#endif // #ifdef __cplusplus +#endif /* #ifdef __cplusplus */ -#endif // #if !defined(XDIFF_H) +#endif /* #if !defined(XDIFF_H) */ diff --git a/src/nvim/xdiff/xdiffi.c b/src/xdiff/xdiffi.c index 3806903986..cfcbb5d982 100644 --- a/src/nvim/xdiff/xdiffi.c +++ b/src/xdiff/xdiffi.c @@ -38,9 +38,9 @@ typedef struct s_xdpsplit { * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both * the forward diagonal starting from (off1, off2) and the backward diagonal * starting from (lim1, lim2). If the K values on the same diagonal crosses - * returns the furthest point of reach. We might end up having to expensive - * cases using this algorithm is full, so a little bit of heuristic is needed - * to cut the search and to return a suboptimal point. + * returns the furthest point of reach. We might encounter expensive edge cases + * using this algorithm, so a little bit of heuristic is needed to cut the + * search and to return a suboptimal point. */ static long xdl_split(unsigned long const *ha1, long off1, long lim1, unsigned long const *ha2, long off2, long lim2, @@ -63,11 +63,13 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, int got_snake = 0; /* - * We need to extent the diagonal "domain" by one. If the next + * We need to extend the diagonal "domain" by one. If the next * values exits the box boundaries we need to change it in the - * opposite direction because (max - min) must be a power of two. + * opposite direction because (max - min) must be a power of + * two. + * * Also we initialize the external K value to -1 so that we can - * avoid extra conditions check inside the core loop. + * avoid extra conditions in the check inside the core loop. */ if (fmin > dmin) kvdf[--fmin - 1] = -1; @@ -98,11 +100,13 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, } /* - * We need to extent the diagonal "domain" by one. If the next + * We need to extend the diagonal "domain" by one. If the next * values exits the box boundaries we need to change it in the - * opposite direction because (max - min) must be a power of two. + * opposite direction because (max - min) must be a power of + * two. + * * Also we initialize the external K value to -1 so that we can - * avoid extra conditions check inside the core loop. + * avoid extra conditions in the check inside the core loop. */ if (bmin > dmin) kvdb[--bmin - 1] = XDL_LINE_MAX; @@ -138,7 +142,7 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, /* * If the edit cost is above the heuristic trigger and if * we got a good snake, we sample current diagonals to see - * if some of the, have reached an "interesting" path. Our + * if some of them have reached an "interesting" path. Our * measure is a function of the distance from the diagonal * corner (i1 + i2) penalized with the distance from the * mid diagonal itself. If this value is above the current @@ -196,8 +200,9 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, } /* - * Enough is enough. We spent too much time here and now we collect - * the furthest reaching path using the (i1 + i2) measure. + * Enough is enough. We spent too much time here and now we + * collect the furthest reaching path using the (i1 + i2) + * measure. */ if (ec >= xenv->mxcost) { long fbest, fbest1, bbest, bbest1; @@ -244,9 +249,9 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1, /* - * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling - * the box splitting function. Note that the real job (marking changed lines) - * is done in the two boundary reaching checks. + * Rule: "Divide et Impera" (divide & conquer). Recursively split the box in + * sub-boxes by calling the box splitting function. Note that the real job + * (marking changed lines) is done in the two boundary reaching checks. */ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, diffdata_t *dd2, long off2, long lim2, @@ -323,7 +328,9 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, } /* - * Allocate and setup K vectors to be used by the differential algorithm. + * Allocate and setup K vectors to be used by the differential + * algorithm. + * * One is to store the forward path and one to store the backward path. */ ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3; @@ -418,13 +425,13 @@ static int xget_indent(xrecord_t *rec) ret += 1; else if (c == '\t') ret += 8 - ret % 8; - // ignore other whitespace characters + /* ignore other whitespace characters */ if (ret >= MAX_INDENT) return MAX_INDENT; } - // The line contains only whitespace. + /* The line contains only whitespace. */ return -1; } @@ -435,7 +442,7 @@ static int xget_indent(xrecord_t *rec) */ #define MAX_BLANKS 20 -// Characteristics measured about a hypothetical split position. +/* Characteristics measured about a hypothetical split position. */ struct split_measurement { /* * Is the split at the end of the file (aside from any blank lines)? @@ -443,8 +450,8 @@ struct split_measurement { int end_of_file; /* - * How much is the line immediately following the split indented (or -1 if - * the line is blank): + * How much is the line immediately following the split indented (or -1 + * if the line is blank): */ int indent; @@ -454,8 +461,8 @@ struct split_measurement { int pre_blank; /* - * How much is the nearest non-blank line above the split indented (or -1 - * if there is no such line)? + * How much is the nearest non-blank line above the split indented (or + * -1 if there is no such line)? */ int pre_indent; @@ -472,10 +479,10 @@ struct split_measurement { }; struct split_score { - // The effective indent of this split (smaller is preferred). + /* The effective indent of this split (smaller is preferred). */ int effective_indent; - // Penalty for this split (smaller is preferred). + /* Penalty for this split (smaller is preferred). */ int penalty; }; @@ -534,16 +541,16 @@ static void measure_split(const xdfile_t *xdf, long split, * integer math. */ -// Penalty if there are no non-blank lines before the split +/* Penalty if there are no non-blank lines before the split */ #define START_OF_FILE_PENALTY 1 -// Penalty if there are no non-blank lines after the split +/* Penalty if there are no non-blank lines after the split */ #define END_OF_FILE_PENALTY 21 -// Multiplier for the number of blank lines around the split +/* Multiplier for the number of blank lines around the split */ #define TOTAL_BLANK_WEIGHT (-30) -// Multiplier for the number of blank lines after the split +/* Multiplier for the number of blank lines after the split */ #define POST_BLANK_WEIGHT 6 /* @@ -581,13 +588,13 @@ static void measure_split(const xdfile_t *xdf, long split, /* * Compute a badness score for the hypothetical split whose measurements are - * stored in m. The weight factors were determined empirically using the tools and - * corpus described in + * stored in m. The weight factors were determined empirically using the tools + * and corpus described in * * https://github.com/mhagger/diff-slider-tools * - * Also see that project if you want to improve the weights based on, for example, - * a larger or more diverse corpus. + * Also see that project if you want to improve the weights based on, for + * example, a larger or more diverse corpus. */ static void score_add_split(const struct split_measurement *m, struct split_score *s) { @@ -610,7 +617,7 @@ static void score_add_split(const struct split_measurement *m, struct split_scor post_blank = (m->indent == -1) ? 1 + m->post_blank : 0; total_blank = m->pre_blank + post_blank; - // Penalties based on nearby blank lines: + /* Penalties based on nearby blank lines: */ s->penalty += TOTAL_BLANK_WEIGHT * total_blank; s->penalty += POST_BLANK_WEIGHT * post_blank; @@ -621,13 +628,13 @@ static void score_add_split(const struct split_measurement *m, struct split_scor any_blanks = (total_blank != 0); - // Note that the effective indent is -1 at the end of the file: + /* Note that the effective indent is -1 at the end of the file: */ s->effective_indent += indent; if (indent == -1) { - // No additional adjustments needed. + /* No additional adjustments needed. */ } else if (m->pre_indent == -1) { - // No additional adjustments needed. + /* No additional adjustments needed. */ } else if (indent > m->pre_indent) { /* * The line is indented more than its predecessor. @@ -669,7 +676,7 @@ static void score_add_split(const struct split_measurement *m, struct split_scor static int score_cmp(struct split_score *s1, struct split_score *s2) { - // -1 if s1.effective_indent < s2->effective_indent, etc. + /* -1 if s1.effective_indent < s2->effective_indent, etc. */ int cmp_indents = ((s1->effective_indent > s2->effective_indent) - (s1->effective_indent < s2->effective_indent)); @@ -809,13 +816,16 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { group_init(xdfo, &go); while (1) { - // If the group is empty in the to-be-compacted file, skip it: + /* + * If the group is empty in the to-be-compacted file, skip it: + */ if (g.end == g.start) goto next; /* * Now shift the change up and then down as far as possible in - * each direction. If it bumps into any other changes, merge them. + * each direction. If it bumps into any other changes, merge + * them. */ do { groupsize = g.end - g.start; @@ -828,7 +838,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { */ end_matching_other = -1; - // Shift the group backward as much as possible: + /* Shift the group backward as much as possible: */ while (!group_slide_up(xdf, &g, flags)) if (group_previous(xdfo, &go)) xdl_bug("group sync broken sliding up"); @@ -842,7 +852,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { if (go.end > go.start) end_matching_other = g.end; - // Now shift the group forward as far as possible: + /* Now shift the group forward as far as possible: */ while (1) { if (group_slide_down(xdf, &g, flags)) break; @@ -858,17 +868,17 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { * If the group can be shifted, then we can possibly use this * freedom to produce a more intuitive diff. * - * The group is currently shifted as far down as possible, so the - * heuristics below only have to handle upwards shifts. + * The group is currently shifted as far down as possible, so + * the heuristics below only have to handle upwards shifts. */ if (g.end == earliest_end) { - // no shifting was possible + /* no shifting was possible */ } else if (end_matching_other != -1) { /* - * Move the possibly merged group of changes back to line - * up with the last group of changes from the other file - * that it can align with. + * Move the possibly merged group of changes back to + * line up with the last group of changes from the + * other file that it can align with. */ while (go.end == go.start) { if (group_slide_up(xdf, &g, flags)) @@ -879,14 +889,15 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { } else if (flags & XDF_INDENT_HEURISTIC) { /* * Indent heuristic: a group of pure add/delete lines - * implies two splits, one between the end of the "before" - * context and the start of the group, and another between - * the end of the group and the beginning of the "after" - * context. Some splits are aesthetically better and some - * are worse. We compute a badness "score" for each split, - * and add the scores for the two splits to define a - * "score" for each position that the group can be shifted - * to. Then we pick the shift with the lowest score. + * implies two splits, one between the end of the + * "before" context and the start of the group, and + * another between the end of the group and the + * beginning of the "after" context. Some splits are + * aesthetically better and some are worse. We compute + * a badness "score" for each split, and add the scores + * for the two splits to define a "score" for each + * position that the group can be shifted to. Then we + * pick the shift with the lowest score. */ long shift, best_shift = -1; struct split_score best_score; @@ -921,7 +932,7 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { } next: - // Move past the just-processed group: + /* Move past the just-processed group: */ if (group_next(xdf, &g)) break; if (group_next(xdfo, &go)) @@ -987,7 +998,7 @@ static int xdl_call_hunk_func(xdfenv_t *xe UNUSED, xdchange_t *xscr, xdemitcb_t return 0; } -static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags) +static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags) { xdchange_t *xch; @@ -1008,6 +1019,48 @@ static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags) } } +#if 0 // unused by Vim +static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) { + regmatch_t regmatch; + int i; + + for (i = 0; i < xpp->ignore_regex_nr; i++) + if (!regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1, + ®match, 0)) + return 1; + + return 0; +} + +static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe, + xpparam_t const *xpp) +{ + xdchange_t *xch; + + for (xch = xscr; xch; xch = xch->next) { + xrecord_t **rec; + int ignore = 1; + long i; + + /* + * Do not override --ignore-blank-lines. + */ + if (xch->ignore) + continue; + + rec = &xe->xdf1.recs[xch->i1]; + for (i = 0; i < xch->chg1 && ignore; i++) + ignore = record_matches_regex(rec[i], xpp); + + rec = &xe->xdf2.recs[xch->i2]; + for (i = 0; i < xch->chg2 && ignore; i++) + ignore = record_matches_regex(rec[i], xpp); + + xch->ignore = ignore; + } +} +#endif + int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb) { xdchange_t *xscr; @@ -1027,7 +1080,12 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, } if (xscr) { if (xpp->flags & XDF_IGNORE_BLANK_LINES) - xdl_mark_ignorable(xscr, &xe, xpp->flags); + xdl_mark_ignorable_lines(xscr, &xe, xpp->flags); + +#if 0 + if (xpp->ignore_regex) + xdl_mark_ignorable_regex(xscr, &xe, xpp); +#endif if (ef(&xe, xscr, ecb, xecfg) < 0) { diff --git a/src/nvim/xdiff/xdiffi.h b/src/xdiff/xdiffi.h index 467a1e85cd..8f1c7c8b04 100644 --- a/src/nvim/xdiff/xdiffi.h +++ b/src/xdiff/xdiffi.h @@ -61,4 +61,4 @@ int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdfenv_t *env); -#endif // #if !defined(XDIFFI_H) +#endif /* #if !defined(XDIFFI_H) */ diff --git a/src/nvim/xdiff/xemit.c b/src/xdiff/xemit.c index f1a45139cc..a0078f928c 100644 --- a/src/nvim/xdiff/xemit.c +++ b/src/xdiff/xemit.c @@ -31,7 +31,7 @@ static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) { static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) { - long size, psize = (long)strlen(pre); + long size, psize = strlen(pre); char const *rec; size = xdl_get_rec(xdf, ri, &rec); @@ -54,9 +54,9 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) xdchange_t *xch, *xchp, *lxch; long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen; long max_ignorable = xecfg->ctxlen; - unsigned long ignored = 0; // number of ignored blank lines + unsigned long ignored = 0; /* number of ignored blank lines */ - // remove ignorable changes that are too far before other changes + /* remove ignorable changes that are too far before other changes */ for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) { xch = xchp->next; @@ -99,9 +99,9 @@ xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) static long def_ff(const char *rec, long len, char *buf, long sz, void *priv UNUSED) { if (len > 0 && - (isalpha((unsigned char)*rec) || // identifier? - *rec == '_' || // also identifier? - *rec == '$')) { // identifiers from VMS and other esoterico + (isalpha((unsigned char)*rec) || /* identifier? */ + *rec == '_' || /* also identifier? */ + *rec == '$')) { /* identifiers from VMS and other esoterico */ if (len > sz) len = sz; while (0 < len && isspace((unsigned char)rec[len - 1])) @@ -197,7 +197,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) { long fs1, i1 = xch->i1; - // Appended chunk? + /* Appended chunk? */ if (i1 >= xe->xdf1.nrec) { long i2 = xch->i2; @@ -225,8 +225,23 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, if (fs1 < 0) fs1 = 0; if (fs1 < s1) { - s2 -= s1 - fs1; + s2 = XDL_MAX(s2 - (s1 - fs1), 0); s1 = fs1; + + /* + * Did we extend context upwards into an + * ignored change? + */ + while (xchp != xch && + xchp->i1 + xchp->chg1 <= s1 && + xchp->i2 + xchp->chg2 <= s2) + xchp = xchp->next; + + /* If so, show it after all. */ + if (xchp != xch) { + xch = xchp; + goto pre_context_calculation; + } } } @@ -249,7 +264,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, if (fe1 < 0) fe1 = xe->xdf1.nrec; if (fe1 > e1) { - e2 += fe1 - e1; + e2 = XDL_MIN(e2 + (fe1 - e1), xe->xdf2.nrec); e1 = fe1; } @@ -281,7 +296,8 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, funclineprev = s1 - 1; } #endif - if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2, + if (!(xecfg->flags & XDL_EMIT_NO_HUNK_HDR) && + xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2, func_line.buf, func_line.len, ecb) < 0) return -1; diff --git a/src/nvim/xdiff/xemit.h b/src/xdiff/xemit.h index 3ce7e3dd50..1b9887e670 100644 --- a/src/nvim/xdiff/xemit.h +++ b/src/xdiff/xemit.h @@ -33,4 +33,4 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, -#endif // #if !defined(XEMIT_H) +#endif /* #if !defined(XEMIT_H) */ diff --git a/src/nvim/xdiff/xhistogram.c b/src/xdiff/xhistogram.c index 28cf8258e5..8598a8550d 100644 --- a/src/nvim/xdiff/xhistogram.c +++ b/src/xdiff/xhistogram.c @@ -42,8 +42,6 @@ */ #include "xinclude.h" -#include "xtypes.h" -#include "xdiff.h" #define MAX_PTR INT_MAX #define MAX_CNT INT_MAX @@ -55,8 +53,8 @@ struct histindex { struct record { unsigned int ptr, cnt; struct record *next; - } **records, // an occurrence - **line_map; // map of line to record chain + } **records, /* an occurrence */ + **line_map; /* map of line to record chain */ chastore_t rcha; unsigned int *next_ptrs; unsigned int table_bits, @@ -128,7 +126,7 @@ static int scanA(struct histindex *index, int line1, int count1) */ NEXT_PTR(index, ptr) = rec->ptr; rec->ptr = ptr; - // cap rec->cnt at MAX_CNT + /* cap rec->cnt at MAX_CNT */ rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1); LINE_MAP(index, ptr) = rec; goto continue_scan; @@ -154,7 +152,7 @@ static int scanA(struct histindex *index, int line1, int count1) LINE_MAP(index, ptr) = rec; continue_scan: - ; // no op + ; /* no op */ } return 0; @@ -237,6 +235,8 @@ static int fall_back_to_classic_diff(xpparam_t const *xpp, xdfenv_t *env, int line1, int count1, int line2, int count2) { xpparam_t xpparam; + + memset(&xpparam, 0, sizeof(xpparam)); xpparam.flags = xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; return xdl_fall_back_diff(env, &xpparam, @@ -266,7 +266,7 @@ static int find_lcs(xpparam_t const *xpp, xdfenv_t *env, index.records = NULL; index.line_map = NULL; - // in case of early xdl_cha_free() + /* in case of early xdl_cha_free() */ index.rcha.head = NULL; index.table_bits = xdl_hashbits(count1); @@ -288,7 +288,7 @@ static int find_lcs(xpparam_t const *xpp, xdfenv_t *env, goto cleanup; memset(index.next_ptrs, 0, sz); - // lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() + /* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */ if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0) goto cleanup; diff --git a/src/nvim/xdiff/xinclude.h b/src/xdiff/xinclude.h index 5a359d1431..a412404bfe 100644 --- a/src/nvim/xdiff/xinclude.h +++ b/src/xdiff/xinclude.h @@ -40,6 +40,7 @@ #if !defined(XINCLUDE_H) #define XINCLUDE_H +// This effectively re-verts b46054b3746271d23feab0 from git #include <ctype.h> #include <stdio.h> #include <stdlib.h> @@ -48,7 +49,10 @@ #endif #include <string.h> #include <limits.h> - +// This include comes from git, so uncomment it +#if 0 +#include "git-compat-util.h" +#endif #include "xmacros.h" #include "xdiff.h" #include "xtypes.h" @@ -58,4 +62,4 @@ #include "xemit.h" -#endif // #if !defined(XINCLUDE_H) +#endif /* #if !defined(XINCLUDE_H) */ diff --git a/src/nvim/xdiff/xmacros.h b/src/xdiff/xmacros.h index 1167ebbb05..2809a28ca9 100644 --- a/src/nvim/xdiff/xmacros.h +++ b/src/xdiff/xmacros.h @@ -51,4 +51,4 @@ do { \ } while (0) -#endif // #if !defined(XMACROS_H) +#endif /* #if !defined(XMACROS_H) */ diff --git a/src/nvim/xdiff/xpatience.c b/src/xdiff/xpatience.c index f6c84c67d8..f78c897ad8 100644 --- a/src/nvim/xdiff/xpatience.c +++ b/src/xdiff/xpatience.c @@ -20,8 +20,6 @@ * */ #include "xinclude.h" -#include "xtypes.h" -#include "xdiff.h" /* * The basic idea of patience diff is to find lines that are unique in @@ -69,7 +67,7 @@ struct hashmap { */ unsigned anchor : 1; } *entries, *first, *last; - // were common records found? + /* were common records found? */ unsigned long has_matches; mmfile_t *file1, *file2; xdfenv_t *env; @@ -78,21 +76,21 @@ struct hashmap { static int is_anchor(xpparam_t const *xpp, const char *line) { - size_t i; - for (i = 0; i < xpp->anchors_nr; i++) { + int i; + for (i = 0; i < (int)xpp->anchors_nr; i++) { if (!strncmp(line, xpp->anchors[i], strlen(xpp->anchors[i]))) return 1; } return 0; } -// The argument "pass" is 1 for the first file, 2 for the second. +/* The argument "pass" is 1 for the first file, 2 for the second. */ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map, int pass) { xrecord_t **records = pass == 1 ? map->env->xdf1.recs : map->env->xdf2.recs; - xrecord_t *record = records[line - 1], *other; + xrecord_t *record = records[line - 1]; /* * After xdl_prepare_env() (or more precisely, due to * xdl_classify_record()), the "ha" member of the records (AKA lines) @@ -106,11 +104,7 @@ static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map, int index = (int)((record->ha << 1) % map->alloc); while (map->entries[index].line1) { - other = map->env->xdf1.recs[map->entries[index].line1 - 1]; - if (map->entries[index].hash != record->ha || - !xdl_recmatch(record->ptr, record->size, - other->ptr, other->size, - map->xpp->flags)) { + if (map->entries[index].hash != record->ha) { if (++index >= map->alloc) index = 0; continue; @@ -155,7 +149,7 @@ static int fill_hashmap(mmfile_t *file1, mmfile_t *file2, result->xpp = xpp; result->env = env; - // We know exactly how large we want the hash map + /* We know exactly how large we want the hash map */ result->alloc = count1 * 2; result->entries = (struct entry *) xdl_malloc(result->alloc * sizeof(struct entry)); @@ -163,11 +157,11 @@ static int fill_hashmap(mmfile_t *file1, mmfile_t *file2, return -1; memset(result->entries, 0, result->alloc * sizeof(struct entry)); - // First, fill with entries from the first file + /* First, fill with entries from the first file */ while (count1--) insert_record(xpp, line1++, result, 1); - // Then search for matches in the second file + /* Then search for matches in the second file */ while (count2--) insert_record(xpp, line2++, result, 2); @@ -185,13 +179,13 @@ static int binary_search(struct entry **sequence, int longest, while (left + 1 < right) { int middle = left + (right - left) / 2; - // by construction, no two entries can be equal + /* by construction, no two entries can be equal */ if (sequence[middle]->line2 > entry->line2) right = middle; else left = middle; } - // return the index in "sequence", _not_ the sequence length + /* return the index in "sequence", _not_ the sequence length */ return left; } @@ -206,9 +200,10 @@ static int binary_search(struct entry **sequence, int longest, */ static struct entry *find_longest_common_sequence(struct hashmap *map) { - struct entry **sequence = (struct entry **)xdl_malloc(map->nr * sizeof(struct entry *)); + struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *)); int longest = 0, i; struct entry *entry; + /* * If not -1, this entry in sequence must never be overridden. * Therefore, overriding entries before this has no effect, so @@ -237,13 +232,13 @@ static struct entry *find_longest_common_sequence(struct hashmap *map) } } - // No common unique lines were found + /* No common unique lines were found */ if (!longest) { xdl_free(sequence); return NULL; } - // Iterate starting at the last element, adjusting the "next" members + /* Iterate starting at the last element, adjusting the "next" members */ entry = sequence[longest - 1]; entry->next = NULL; while (entry->previous) { @@ -258,8 +253,7 @@ static int match(struct hashmap *map, int line1, int line2) { xrecord_t *record1 = map->env->xdf1.recs[line1 - 1]; xrecord_t *record2 = map->env->xdf2.recs[line2 - 1]; - return xdl_recmatch(record1->ptr, record1->size, - record2->ptr, record2->size, map->xpp->flags); + return record1->ha == record2->ha; } static int patience_diff(mmfile_t *file1, mmfile_t *file2, @@ -273,7 +267,7 @@ static int walk_common_sequence(struct hashmap *map, struct entry *first, int next1, next2; for (;;) { - // Try to grow the line ranges of common lines + /* Try to grow the line ranges of common lines */ if (first) { next1 = first->line1; next2 = first->line2; @@ -292,11 +286,8 @@ static int walk_common_sequence(struct hashmap *map, struct entry *first, line2++; } - // Recurse + /* Recurse */ if (next1 > line1 || next2 > line2) { - struct hashmap submap; - - memset(&submap, 0, sizeof(submap)); if (patience_diff(map->file1, map->file2, map->xpp, map->env, line1, next1 - line1, @@ -323,6 +314,8 @@ static int fall_back_to_classic_diff(struct hashmap *map, int line1, int count1, int line2, int count2) { xpparam_t xpp; + + memset(&xpp, 0, sizeof(xpp)); xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; return xdl_fall_back_diff(map->env, &xpp, @@ -343,7 +336,7 @@ static int patience_diff(mmfile_t *file1, mmfile_t *file2, struct entry *first; int result = 0; - // trivial case: one side is empty + /* trivial case: one side is empty */ if (!count1) { while(count2--) env->xdf2.rchg[line2++ - 1] = 1; @@ -359,7 +352,7 @@ static int patience_diff(mmfile_t *file1, mmfile_t *file2, line1, count1, line2, count2)) return -1; - // are there any matching lines at all? + /* are there any matching lines at all? */ if (!map.has_matches) { while(count1--) env->xdf1.rchg[line1++ - 1] = 1; @@ -387,7 +380,7 @@ int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2, if (xdl_prepare_env(file1, file2, xpp, env) < 0) return -1; - // environment is cleaned up in xdl_diff() + /* environment is cleaned up in xdl_diff() */ return patience_diff(file1, file2, xpp, env, 1, env->xdf1.nrec, 1, env->xdf2.nrec); } diff --git a/src/nvim/xdiff/xprepare.c b/src/xdiff/xprepare.c index abeb8fb84e..abeb8fb84e 100644 --- a/src/nvim/xdiff/xprepare.c +++ b/src/xdiff/xprepare.c diff --git a/src/nvim/xdiff/xprepare.h b/src/xdiff/xprepare.h index b67b3b25ab..947d9fc1bb 100644 --- a/src/nvim/xdiff/xprepare.h +++ b/src/xdiff/xprepare.h @@ -31,4 +31,4 @@ void xdl_free_env(xdfenv_t *xe); -#endif // #if !defined(XPREPARE_H) +#endif /* #if !defined(XPREPARE_H) */ diff --git a/src/nvim/xdiff/xtypes.h b/src/xdiff/xtypes.h index 026999c1bf..8442bd436e 100644 --- a/src/nvim/xdiff/xtypes.h +++ b/src/xdiff/xtypes.h @@ -64,4 +64,4 @@ typedef struct s_xdfenv { -#endif // #if !defined(XTYPES_H) +#endif /* #if !defined(XTYPES_H) */ diff --git a/src/nvim/xdiff/xutils.c b/src/xdiff/xutils.c index e8c7d2f884..5ef519155d 100644 --- a/src/nvim/xdiff/xutils.c +++ b/src/xdiff/xutils.c @@ -20,13 +20,9 @@ * */ -#include <limits.h> -#include <assert.h> #include "xinclude.h" - - long xdl_bogosqrt(long n) { long i; @@ -51,10 +47,10 @@ int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, mb[1].size = size; if (size > 0 && rec[size - 1] != '\n') { mb[2].ptr = (char *) "\n\\ No newline at end of file\n"; - mb[2].size = (long)strlen(mb[2].ptr); + mb[2].size = strlen(mb[2].ptr); i++; } - if (ecb->outf(ecb->priv, mb, i) < 0) { + if (ecb->out_line(ecb->priv, mb, i) < 0) { return -1; } @@ -168,7 +164,7 @@ static int ends_with_optional_cr(const char *l, long s, long i) s--; if (s == i) return 1; - // do not ignore CR at the end of an incomplete line + /* do not ignore CR at the end of an incomplete line */ if (complete && s == i + 1 && l[i] == '\r') return 1; return 0; @@ -208,7 +204,7 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) { while (i1 < s1 && i2 < s2) { if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) { - // Skip matching spaces and try again + /* Skip matching spaces and try again */ while (i1 < s1 && XDL_ISSPACE(l1[i1])) i1++; while (i2 < s2 && XDL_ISSPACE(l2[i2])) @@ -224,7 +220,7 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) i2++; } } else if (flags & XDF_IGNORE_CR_AT_EOL) { - // Find the first difference and see how the line ends + /* Find the first difference and see how the line ends */ while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) { i1++; i2++; @@ -261,7 +257,7 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data, for (; ptr < top && *ptr != '\n'; ptr++) { if (cr_at_eol_only) { - // do not ignore CR at the end of an incomplete line + /* do not ignore CR at the end of an incomplete line */ if (*ptr == '\r' && (ptr + 1 < top && ptr[1] == '\n')) continue; @@ -274,7 +270,7 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data, ptr++; at_eol = (top <= ptr + 1 || ptr[1] == '\n'); if (flags & XDF_IGNORE_WHITESPACE) - ; // already handled + ; /* already handled */ else if (flags & XDF_IGNORE_WHITESPACE_CHANGE && !at_eol) { ha += (ha << 5); @@ -344,8 +340,9 @@ int xdl_num_out(char *out, long val) { return str - out; } -int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, - const char *func, long funclen, xdemitcb_t *ecb) { +static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2, + const char *func, long funclen, + xdemitcb_t *ecb) { int nb = 0; mmbuffer_t mb; char buf[128]; @@ -387,9 +384,21 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, mb.ptr = buf; mb.size = nb; - if (ecb->outf(ecb->priv, &mb, 1) < 0) + if (ecb->out_line(ecb->priv, &mb, 1) < 0) return -1; + return 0; +} +int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, + const char *func, long funclen, + xdemitcb_t *ecb) { + if (!ecb->out_hunk) + return xdl_format_hunk_hdr(s1, c1, s2, c2, func, funclen, ecb); + if (ecb->out_hunk(ecb->priv, + c1 ? s1 : s1 - 1, c1, + c2 ? s2 : s2 - 1, c2, + func, funclen) < 0) + return -1; return 0; } diff --git a/src/nvim/xdiff/xutils.h b/src/xdiff/xutils.h index 0bebd93022..fba7bae03c 100644 --- a/src/nvim/xdiff/xutils.h +++ b/src/xdiff/xutils.h @@ -44,4 +44,4 @@ int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp, -#endif // #if !defined(XUTILS_H) +#endif /* #if !defined(XUTILS_H) */ diff --git a/test/README.md b/test/README.md index a6e9080a40..8669ab6f3e 100644 --- a/test/README.md +++ b/test/README.md @@ -258,6 +258,9 @@ Number; !must be defined to function properly): - `TEST_SKIP_FRAGILE` (F) (D): makes test suite skip some fragile tests. +- `TEST_TIMEOUT` (FU) (I): specifies maximum time, in seconds, before the test + suite run is killed + - `NVIM_PROG`, `NVIM_PRG` (F) (S): override path to Neovim executable (default to `build/bin/nvim`). diff --git a/test/busted/outputHandlers/nvim.lua b/test/busted/outputHandlers/nvim.lua index 5456e9ca98..191387e1b9 100644 --- a/test/busted/outputHandlers/nvim.lua +++ b/test/busted/outputHandlers/nvim.lua @@ -1,13 +1,9 @@ local pretty = require 'pl.pretty' local global_helpers = require('test.helpers') -local colors - -local isWindows = package.config:sub(1,1) == '\\' - -if isWindows then - colors = setmetatable({}, {__index = function() return function(s) return s end end}) -else +-- Colors are disabled by default. #15610 +local colors = setmetatable({}, {__index = function() return function(s) return s end end}) +if os.getenv "NVIM_COLORS" then colors = require 'term.colors' end diff --git a/test/functional/api/buffer_updates_spec.lua b/test/functional/api/buffer_updates_spec.lua index 05ca0d5f4d..c9c9be5406 100644 --- a/test/functional/api/buffer_updates_spec.lua +++ b/test/functional/api/buffer_updates_spec.lua @@ -159,9 +159,8 @@ describe('API: buffer events:', function() tick = tick + 1 expectn('nvim_buf_lines_event', {b, tick, 29, 29, firstfour, false}) - -- create a new empty buffer and wipe out the old one ... this will - -- turn off buffer events - command('enew!') + -- delete the current buffer to turn off buffer events + command('bdelete!') expectn('nvim_buf_detach_event', {b}) -- add a line at the start of an empty file @@ -269,7 +268,7 @@ describe('API: buffer events:', function() 'original foo'}, false}) -- type text into the first line of a blank file, one character at a time - command('enew!') + command('bdelete!') tick = 2 expectn('nvim_buf_detach_event', {b}) local bnew = nvim('get_current_buf') @@ -666,7 +665,8 @@ describe('API: buffer events:', function() tick = tick + 1 expectn('nvim_buf_changedtick_event', {b, tick}) - -- close our buffer by creating a new one + -- close our buffer and create a new one + command('bdelete') command('enew') expectn('nvim_buf_detach_event', {b}) diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index e6a9e11fd9..37331d11c7 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -53,7 +53,7 @@ describe('nvim_get_commands', function() end) it('gets various command attributes', function() - local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='0', range='10', register=false, script_id=0, } + local cmd0 = { addr='arguments', bang=false, bar=false, complete='dir', complete_arg=NIL, count='10', definition='pwd <args>', name='TestCmd', nargs='1', range='10', register=false, script_id=0, } local cmd1 = { addr=NIL, bang=false, bar=false, complete='custom', complete_arg='ListUsers', count=NIL, definition='!finger <args>', name='Finger', nargs='+', range=NIL, register=false, script_id=1, } local cmd2 = { addr=NIL, bang=true, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R2_foo(<q-args>)', name='Cmd2', nargs='*', range=NIL, register=false, script_id=2, } local cmd3 = { addr=NIL, bang=false, bar=true, complete=NIL, complete_arg=NIL, count=NIL, definition='call \128\253R3_ohyeah()', name='Cmd3', nargs='0', range=NIL, register=false, script_id=3, } @@ -62,7 +62,7 @@ describe('nvim_get_commands', function() command -complete=custom,ListUsers -nargs=+ Finger !finger <args> ]]) eq({Finger=cmd1}, meths.get_commands({builtin=false})) - command('command -complete=dir -addr=arguments -count=10 TestCmd pwd <args>') + command('command -nargs=1 -complete=dir -addr=arguments -count=10 TestCmd pwd <args>') eq({Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false})) source([[ diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index d2b555ee5b..50b4b85d2a 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -12,6 +12,7 @@ local feed = helpers.feed local clear = helpers.clear local command = helpers.command local meths = helpers.meths +local assert_alive = helpers.assert_alive local function expect(contents) return eq(contents, helpers.curbuf_contents()) @@ -1381,13 +1382,13 @@ describe('API/extmarks', function() end) it('does not crash with append/delete/undo seqence', function() - meths.exec([[ + meths.exec([[ let ns = nvim_create_namespace('myplugin') call nvim_buf_set_extmark(0, ns, 0, 0, {}) call append(0, '') %delete undo]],false) - eq(2, meths.eval('1+1')) -- did not crash + assert_alive() end) it('works with left and right gravity', function() diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 9ee2570798..6367cc5caa 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -5,6 +5,8 @@ local eq, clear, eval, command, nvim, next_msg = local meths = helpers.meths local exec_lua = helpers.exec_lua local retry = helpers.retry +local isCI = helpers.isCI +local assert_alive = helpers.assert_alive describe('notify', function() local channel @@ -72,18 +74,23 @@ describe('notify', function() nvim('subscribe', 'event1') nvim('unsubscribe', 'doesnotexist') nvim('unsubscribe', 'event1') - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) it('cancels stale events on channel close', function() + if isCI() then + pending('hangs on CI #14083 #15251') + return + end if helpers.pending_win32(pending) then return end local catchan = eval("jobstart(['cat'], {'rpc': v:true})") - eq({id=catchan, stream='job', mode='rpc', client = {}}, exec_lua ([[ + local catpath = eval('exepath("cat")') + eq({id=catchan, argv={catpath}, stream='job', mode='rpc', client = {}}, exec_lua ([[ vim.rpcnotify(..., "nvim_call_function", 'chanclose', {..., 'rpc'}) vim.rpcnotify(..., "nvim_subscribe", "daily_rant") return vim.api.nvim_get_chan_info(...) ]], catchan)) - eq(2, eval('1+1')) -- Still alive? + assert_alive() eq({false, 'Invalid channel: '..catchan}, exec_lua ([[ return {pcall(vim.rpcrequest, ..., 'nvim_eval', '1+1')}]], catchan)) retry(nil, 3000, function() eq({}, meths.get_chan_info(catchan)) end) -- cat be dead :( diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index 237a4b01e4..e408890906 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -11,6 +11,7 @@ local meths = helpers.meths 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 describe('server -> client', function() local cid @@ -33,7 +34,7 @@ describe('server -> client', function() call jobstop(ch1) ]]) - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) describe('simple call', function() @@ -158,7 +159,7 @@ describe('server -> client', function() -- do some busywork, so the first request will return -- before this one for _ = 1, 5 do - eq(2, eval("1+1")) + assert_alive() end eq(1, eval('rpcnotify('..cid..', "nested_done")')) return 'done!' diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 0c0f610401..9aad8a1319 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1,6 +1,8 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local fmt = string.format +local assert_alive = helpers.assert_alive local NIL = helpers.NIL local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq local command = helpers.command @@ -57,7 +59,7 @@ describe('API', function() eq({'notification', 'nvim_error_event', {error_types.Exception.id, 'Invalid method: nvim_bogus'}}, next_msg()) -- error didn't close channel. - eq(2, eval('1+1')) + assert_alive() end) it('failed async request emits nvim_error_event', function() @@ -67,7 +69,7 @@ describe('API', function() {error_types.Exception.id, 'Vim:E492: Not an editor command: bogus'}}, next_msg()) -- error didn't close channel. - eq(2, eval('1+1')) + assert_alive() end) it('does not set CA_COMMAND_BUSY #7254', function() @@ -1326,10 +1328,10 @@ describe('API', function() end) end) - describe('nvim_list_chans and nvim_get_chan_info', function() + describe('nvim_list_chans, nvim_get_chan_info', function() before_each(function() - command('autocmd ChanOpen * let g:opened_event = copy(v:event)') - command('autocmd ChanInfo * let g:info_event = copy(v:event)') + command('autocmd ChanOpen * let g:opened_event = deepcopy(v:event)') + command('autocmd ChanInfo * let g:info_event = deepcopy(v:event)') end) local testinfo = { stream = 'stdio', @@ -1350,7 +1352,7 @@ describe('API', function() eq({}, meths.get_chan_info(10)) end) - it('works for stdio channel', function() + it('stream=stdio channel', function() eq({[1]=testinfo,[2]=stderr}, meths.list_chans()) eq(testinfo, meths.get_chan_info(1)) eq(stderr, meths.get_chan_info(2)) @@ -1377,11 +1379,13 @@ describe('API', function() eq(info, meths.get_chan_info(1)) end) - it('works for job channel', function() + it('stream=job channel', function() eq(3, eval("jobstart(['cat'], {'rpc': v:true})")) + local catpath = eval('exepath("cat")') local info = { stream='job', id=3, + argv={ catpath }, mode='rpc', client={}, } @@ -1394,6 +1398,7 @@ describe('API', function() info = { stream='job', id=3, + argv={ catpath }, mode='rpc', client = { name='amazing-cat', @@ -1410,14 +1415,15 @@ describe('API', function() pcall_err(eval, 'rpcrequest(3, "nvim_set_current_buf", -1)')) end) - it('works for :terminal channel', function() - command(":terminal") + it('stream=job :terminal channel', function() + command(':terminal') eq({id=1}, meths.get_current_buf()) - eq(3, meths.buf_get_option(1, "channel")) + eq(3, meths.buf_get_option(1, 'channel')) local info = { stream='job', id=3, + argv={ eval('exepath(&shell)') }, mode='terminal', buffer = 1, pty='?', @@ -1431,6 +1437,38 @@ describe('API', function() info.buffer = {id=1} eq({[1]=testinfo,[2]=stderr,[3]=info}, meths.list_chans()) eq(info, meths.get_chan_info(3)) + + -- :terminal with args + running process. + command(':exe "terminal" shellescape(v:progpath) "-u NONE -i NONE"') + eq(-1, eval('jobwait([&channel], 0)[0]')) -- Running? + local expected2 = { + stream = 'job', + id = 4, + argv = ( + iswin() and { + eval('&shell'), + '/s', + '/c', + fmt('"%s -u NONE -i NONE"', eval('shellescape(v:progpath)')), + } or { + eval('&shell'), + eval('&shellcmdflag'), + fmt('%s -u NONE -i NONE', eval('shellescape(v:progpath)')), + } + ), + mode = 'terminal', + buffer = 2, + pty = '?', + } + local actual2 = eval('nvim_get_chan_info(&channel)') + expected2.pty = actual2.pty + eq(expected2, actual2) + + -- :terminal with args + stopped process. + eq(1, eval('jobstop(&channel)')) + eval('jobwait([&channel], 1000)') -- Wait. + expected2.pty = (iswin() and '?' or '') -- pty stream was closed. + eq(expected2, eval('nvim_get_chan_info(&channel)')) end) end) @@ -2003,6 +2041,7 @@ describe('API', function() it('should have information about window options', function() eq({ + allows_duplicates = true, commalist = false; default = ""; flaglist = false; @@ -2020,6 +2059,7 @@ describe('API', function() it('should have information about buffer options', function() eq({ + allows_duplicates = true, commalist = false, default = "", flaglist = false, @@ -2041,6 +2081,7 @@ describe('API', function() eq(false, meths.get_option'showcmd') eq({ + allows_duplicates = true, commalist = false, default = true, flaglist = false, diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index bb72b63b6c..c49d6405f4 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -311,7 +311,8 @@ describe('API/win', function() eq({newwin}, meths.list_wins()) end) - it('handles changed buffer', function() + it("handles changed buffer when 'hidden' is unset", function() + command('set nohidden') local oldwin = meths.get_current_win() insert('text') command('new') @@ -346,6 +347,21 @@ describe('API/win', function() eq(2, #meths.list_wins()) eq('', funcs.getcmdwintype()) end) + + it('closing current (float) window of another tabpage #15313', function() + command('tabedit') + eq(2, eval('tabpagenr()')) + local win = meths.open_win(0, true, { + relative='editor', row=10, col=10, width=50, height=10 + }) + local tabpage = eval('tabpagenr()') + command('tabprevious') + eq(1, eval('tabpagenr()')) + meths.win_close(win, false) + + eq(1001, meths.tabpage_get_win(tabpage).id) + helpers.assert_alive() + end) end) describe('hide', function() diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua index e62d3bb66b..93d71a9e45 100644 --- a/test/functional/autocmd/autocmd_spec.lua +++ b/test/functional/autocmd/autocmd_spec.lua @@ -101,6 +101,17 @@ describe('autocmd', function() }, eval('g:evs')) end) + it('WinClosed from root directory', function() + command('cd /') + command('let g:evs = []') + command('autocmd WinClosed * :call add(g:evs, ["WinClosed", expand("<afile>")])') + command('new') + command('close') + eq({ + {'WinClosed', '1001'}, + }, eval('g:evs')) + end) + it('v:vim_did_enter is 1 after VimEnter', function() eq(1, eval('v:vim_did_enter')) end) diff --git a/test/functional/autocmd/termxx_spec.lua b/test/functional/autocmd/termxx_spec.lua index b12c24b58d..1e8f981437 100644 --- a/test/functional/autocmd/termxx_spec.lua +++ b/test/functional/autocmd/termxx_spec.lua @@ -13,7 +13,7 @@ describe('autocmd TermClose', function() before_each(function() clear() nvim('set_option', 'shell', nvim_dir .. '/shell-test') - nvim('set_option', 'shellcmdflag', 'EXE') + command('set shellcmdflag=EXE shellredir= shellpipe= shellquote= shellxquote=') end) it('triggers when fast-exiting terminal job stops', function() @@ -90,6 +90,17 @@ describe('autocmd TermClose', function() retry(nil, nil, function() eq('3', eval('g:abuf')) end) feed('<c-c>:qa!<cr>') end) + + it('exposes v:event.status', function() + command('set shellcmdflag=EXIT') + command('autocmd TermClose * let g:status = v:event.status') + + command('terminal 0') + retry(nil, nil, function() eq(0, eval('g:status')) end) + + command('terminal 42') + retry(nil, nil, function() eq(42, eval('g:status')) end) + end) end) it('autocmd TermEnter, TermLeave', function() diff --git a/test/functional/core/exit_spec.lua b/test/functional/core/exit_spec.lua index 230b7f8e01..5e569a01d6 100644 --- a/test/functional/core/exit_spec.lua +++ b/test/functional/core/exit_spec.lua @@ -1,5 +1,6 @@ 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 eval = helpers.eval @@ -53,7 +54,7 @@ describe(':cquit', function() if redir_msg then eq('\n' .. redir_msg, redir_exec(cmdline)) poke_eventloop() - eq(2, eval("1+1")) -- Still alive? + assert_alive() else funcs.system({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '--cmd', cmdline}) eq(exit_code, eval('v:shell_error')) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 34ab90d760..5e127bce26 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -5,6 +5,7 @@ local clear, eq, eval, exc_exec, feed_command, feed, insert, neq, next_msg, nvim helpers.insert, helpers.neq, helpers.next_msg, helpers.nvim, helpers.nvim_dir, helpers.ok, helpers.source, helpers.write_file, helpers.mkdir, helpers.rmdir +local assert_alive = helpers.assert_alive local command = helpers.command local funcs = helpers.funcs local os_kill = helpers.os_kill @@ -348,6 +349,12 @@ describe('jobs', function() eq(false, pcall(function() nvim('command', 'call jobsend(j, ["some data"])') end)) + + command("let g:job_opts.stdin = 'null'") + nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") + eq(false, pcall(function() + nvim('command', 'call jobsend(j, ["some data"])') + end)) end) it('disallows jobsend on a non-existent job', function() @@ -864,7 +871,7 @@ describe('jobs', function() -- loop tick. This is also prevented by try-block, so feed must be used. feed_command("call DoIt()") feed('<cr>') -- press RETURN - eq(2,eval('1+1')) + assert_alive() end) it('jobstop() kills entire process tree #6530', function() diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index d5f03db03a..7cddc72561 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive local clear = helpers.clear local command = helpers.command local ok = helpers.ok @@ -11,6 +12,7 @@ local exec_lua = helpers.exec_lua local feed = helpers.feed local funcs = helpers.funcs 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 @@ -230,7 +232,7 @@ describe('startup', function() it('does not crash if --embed is given twice', function() clear{args={'--embed'}} - eq(2, eval('1+1')) + assert_alive() end) it('does not crash when expanding cdpath during early_init', function() @@ -246,9 +248,9 @@ describe('startup', function() [[" -u NONE -i NONE --cmd "set noruler" --cmd "let g:foo = g:bar"')]]) screen:expect([[ ^ | + | Error detected while processing pre-vimrc command line: | E121: Undefined variable: g:bar | - E15: Invalid expression: g:bar | Press ENTER or type command to continue | | ]]) @@ -442,10 +444,7 @@ describe('user config init', function() before_each(function() rmdir(xhome) - -- TODO, make mkdir_p helper - mkdir(xhome) - mkdir(xconfig) - mkdir(xconfig .. pathsep .. 'nvim') + mkdir_p(xconfig .. pathsep .. 'nvim') write_file(init_lua_path, [[ vim.g.lua_rc = 1 @@ -489,11 +488,80 @@ describe('user config init', function() clear{ args_rm={'-u'}, env={ XDG_CONFIG_HOME=xconfig }} feed('<cr>') -- TODO check this, test execution is blocked without it eq(1, eval('g:lua_rc')) - matches('Conflicting configs', meths.exec('messages', true)) + matches('^E5422: Conflicting configs', meths.exec('messages', true)) end) end) end) +describe('runtime:', function() + local xhome = 'Xhome' + local pathsep = helpers.get_pathsep() + local xconfig = xhome .. pathsep .. 'Xconfig' + + setup(function() + mkdir_p(xconfig .. pathsep .. 'nvim') + end) + + teardown(function() + rmdir(xhome) + end) + + it('loads plugin/*.lua from XDG config home', function() + local plugin_folder_path = table.concat({xconfig, 'nvim', 'plugin'}, pathsep) + local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, pathsep) + mkdir_p(plugin_folder_path) + write_file(plugin_file_path, [[ vim.g.lua_plugin = 1 ]]) + + clear{ args_rm={'-u'}, env={ XDG_CONFIG_HOME=xconfig }} + + eq(1, eval('g:lua_plugin')) + rmdir(plugin_folder_path) + end) + + it('loads plugin/*.lua from start plugins', function() + local plugin_path = table.concat({xconfig, 'nvim', 'pack', 'catagory', + 'start', 'test_plugin'}, pathsep) + local plugin_folder_path = table.concat({plugin_path, 'plugin'}, pathsep) + local plugin_file_path = table.concat({plugin_folder_path, 'plugin.lua'}, + pathsep) + local profiler_file = 'test_startuptime.log' + + mkdir_p(plugin_folder_path) + write_file(plugin_file_path, [[vim.g.lua_plugin = 2]]) + + clear{ args_rm={'-u'}, args={'--startuptime', profiler_file}, env={ XDG_CONFIG_HOME=xconfig }} + + eq(2, eval('g:lua_plugin')) + -- Check if plugin_file_path is listed in :scriptname + local scripts = meths.exec(':scriptnames', true) + assert.Truthy(scripts:find(plugin_file_path)) + + -- Check if plugin_file_path is listed in startup profile + local profile_reader = io.open(profiler_file, 'r') + local profile_log = profile_reader:read('*a') + profile_reader:close() + assert.Truthy(profile_log :find(plugin_file_path)) + + os.remove(profiler_file) + rmdir(plugin_path) + end) + + it('loads ftdetect/*.lua', function() + local ftdetect_folder = table.concat({xconfig, 'nvim', 'ftdetect'}, pathsep) + local ftdetect_file = table.concat({ftdetect_folder , 'new-ft.lua'}, pathsep) + mkdir_p(ftdetect_folder) + write_file(ftdetect_file , [[vim.g.lua_ftdetect = 1]]) + + -- TODO(shadmansaleh): Figure out why this test fails without + -- setting VIMRUNTIME + clear{ args_rm={'-u'}, env={XDG_CONFIG_HOME=xconfig, + VIMRUNTIME='runtime/'}} + + eq(1, eval('g:lua_ftdetect')) + rmdir(ftdetect_folder) + end) +end) + describe('user session', function() local xhome = 'Xhome' local pathsep = helpers.get_pathsep() diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua index 06841a4521..e957e5f5af 100644 --- a/test/functional/eval/buf_functions_spec.lua +++ b/test/functional/eval/buf_functions_spec.lua @@ -221,9 +221,9 @@ describe('getbufvar() function', function() eq(0, funcs.getbufvar(1, '&l:autoindent')) eq(0, funcs.getbufvar(1, '&g:autoindent')) -- Also works with global-only options - eq(0, funcs.getbufvar(1, '&hidden')) - eq(0, funcs.getbufvar(1, '&l:hidden')) - eq(0, funcs.getbufvar(1, '&g:hidden')) + eq(1, funcs.getbufvar(1, '&hidden')) + eq(1, funcs.getbufvar(1, '&l:hidden')) + eq(1, funcs.getbufvar(1, '&g:hidden')) -- Also works with window-local options eq(0, funcs.getbufvar(1, '&number')) eq(0, funcs.getbufvar(1, '&l:number')) @@ -279,9 +279,9 @@ describe('setbufvar() function', function() eq(false, winmeths.get_option(windows[3], 'number')) eq(false, winmeths.get_option(meths.get_current_win(), 'number')) - eq(false, meths.get_option('hidden')) - funcs.setbufvar(1, '&hidden', true) eq(true, meths.get_option('hidden')) + funcs.setbufvar(1, '&hidden', 0) + eq(false, meths.get_option('hidden')) eq(false, bufmeths.get_option(buf1, 'autoindent')) funcs.setbufvar(1, '&autoindent', true) diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua index f399ef47d3..9f168c913a 100644 --- a/test/functional/eval/match_functions_spec.lua +++ b/test/functional/eval/match_functions_spec.lua @@ -6,7 +6,6 @@ local clear = helpers.clear local funcs = helpers.funcs local command = helpers.command local exc_exec = helpers.exc_exec -local pcall_err = helpers.pcall_err before_each(clear) @@ -40,13 +39,13 @@ describe('setmatches()', function() }}, funcs.getmatches()) end) - it('fails with -1 if highlight group is not defined', function() - eq('Vim:E28: No such highlight group name: 1', - pcall_err(funcs.setmatches, {{group=1, pattern=2, id=3, priority=4}})) - eq({}, funcs.getmatches()) - eq('Vim:E28: No such highlight group name: 1', - pcall_err(funcs.setmatches, {{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}})) - eq({}, funcs.getmatches()) + it('does not fail if highlight group is not defined', function() + eq(0, funcs.setmatches{{group=1, pattern=2, id=3, priority=4}}) + eq({{group='1', pattern='2', id=3, priority=4}}, + funcs.getmatches()) + eq(0, funcs.setmatches{{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}}) + eq({{group='1', pos1={2}, pos2={6}, id=3, priority=4, conceal='5'}}, + funcs.getmatches()) end) end) diff --git a/test/functional/eval/modeline_spec.lua b/test/functional/eval/modeline_spec.lua index c5bb798f4a..b2346079a1 100644 --- a/test/functional/eval/modeline_spec.lua +++ b/test/functional/eval/modeline_spec.lua @@ -1,6 +1,6 @@ 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 eq, eval = helpers.eq, helpers.eval describe("modeline", function() local tempfile = helpers.tmpname() @@ -14,6 +14,6 @@ describe("modeline", function() write_file(tempfile, 'vim100000000000000000000000') command('e! ' .. tempfile) - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index b1ceff9115..f866aca3ed 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -53,7 +53,7 @@ describe('NULL', function() -- Correct behaviour null_expr_test('can be indexed with error message for empty list', 'L[0]', - 'E684: list index out of range: 0\nE15: Invalid expression: L[0]', nil) + 'E684: list index out of range: 0', nil) null_expr_test('can be splice-indexed', 'L[:]', 0, {}) null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) @@ -68,7 +68,7 @@ describe('NULL', function() null_expr_test('can be copied', 'copy(L)', 0, {}) null_expr_test('can be deepcopied', 'deepcopy(L)', 0, {}) null_expr_test('does not crash when indexed', 'L[1]', - 'E684: list index out of range: 1\nE15: Invalid expression: L[1]', nil) + 'E684: list index out of range: 1', nil) null_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0) null_expr_test('does not crash col()', 'col(L)', 0, 0) null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) @@ -135,7 +135,7 @@ describe('NULL', function() end) describe('dict', function() it('does not crash when indexing NULL dict', function() - eq('\nE716: Key not present in Dictionary: "test"\nE15: Invalid expression: v:_null_dict.test', + eq('\nE716: Key not present in Dictionary: "test"', redir_exec('echo v:_null_dict.test')) end) null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0) diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 8b18eff451..24a1f05390 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local nvim_dir = helpers.nvim_dir local eq, call, clear, eval, feed_command, feed, nvim = helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.feed_command, @@ -174,6 +175,21 @@ describe('system()', function() end) end + it('works with powershell w/ UTF-8 text (#13713)', function() + if not helpers.has_powershell() then + pending("not tested; powershell was not found", function() end) + return + end + -- Should work with recommended config used in helper + helpers.set_shell_powershell() + eq('ã‚ã‚\n', eval([[system('Write-Output "ã‚ã‚"')]])) + -- Sanity test w/ default encoding + -- * on Windows, expected to default to Western European enc + -- * on Linux, expected to default to UTF8 + command([[let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ']]) + eq(iswin() and '??\n' or 'ã‚ã‚\n', eval([[system('Write-Output "ã‚ã‚"')]])) + end) + it('`echo` and waits for its return', function() feed(':call system("echo")<cr>') screen:expect([[ @@ -287,7 +303,7 @@ describe('system()', function() if v_errnum then eq("E5677:", v_errnum) end - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) end) @@ -302,11 +318,11 @@ describe('system()', function() if v_errnum then eq("E5677:", v_errnum) end - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) it('works with an empty string', function() eq("test\n", eval('system("echo test", "")')) - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) end) @@ -554,4 +570,20 @@ describe('systemlist()', function() assert(out[1]:sub(0, 5) == 'pid: ', out) os_kill(out[1]:match("%d+")) end) + + it('works with powershell w/ UTF-8 text (#13713)', function() + if not helpers.has_powershell() then + pending("not tested; powershell was not found", function() end) + return + end + -- Should work with recommended config used in helper + helpers.set_shell_powershell() + eq({iswin() and 'ã‚\r' or 'ã‚'}, eval([[systemlist('Write-Output ã‚')]])) + -- Sanity test w/ default encoding + -- * on Windows, expected to default to Western European enc + -- * on Linux, expected to default to UTF8 + command([[let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ']]) + eq({iswin() and '?\r' or 'ã‚'}, eval([[systemlist('Write-Output ã‚')]])) + end) + end) diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index e5c9a20db3..21adcf37da 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source local insert = helpers.insert local eq, next_msg = helpers.eq, helpers.next_msg @@ -325,7 +326,7 @@ describe('VimL dictionary notifications', function() ]]) command('call MakeWatch()') - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) end) @@ -354,7 +355,7 @@ describe('VimL dictionary notifications', function() command([[call dictwatcherdel(b:, 'changedtick', 'OnTickChanged')]]) insert('t'); - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) it('does not cause use-after-free when unletting from callback', function() diff --git a/test/functional/ex_cmds/drop_spec.lua b/test/functional/ex_cmds/drop_spec.lua index ef53fe75e3..9d84a2d4f6 100644 --- a/test/functional/ex_cmds/drop_spec.lua +++ b/test/functional/ex_cmds/drop_spec.lua @@ -55,6 +55,7 @@ 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") diff --git a/test/functional/ex_cmds/make_spec.lua b/test/functional/ex_cmds/make_spec.lua new file mode 100644 index 0000000000..3b4d22ab38 --- /dev/null +++ b/test/functional/ex_cmds/make_spec.lua @@ -0,0 +1,44 @@ +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 nvim = helpers.nvim +local nvim_dir = helpers.nvim_dir + +describe(':make', function() + clear() + before_each(function () + clear() + end) + + describe('with powershell', function() + if not has_powershell() then + pending("not tested; powershell was not found", function() end) + return + end + before_each(function () + helpers.set_shell_powershell() + end) + + it('captures stderr & non zero exit code #14349', function () + nvim('set_option', 'makeprg', nvim_dir..'/shell-test foo') + local out = eval('execute("make")') + -- Make program exit code correctly captured + matches('\nshell returned 3', out) + -- Error message is captured in the file and printed in the footer + matches('\n.*%: Unknown first argument%: foo', out) + end) + + it('captures stderr & zero exit code #14349', function () + nvim('set_option', 'makeprg', nvim_dir..'/shell-test') + local out = eval('execute("make")') + -- Ensure there are no "shell returned X" messages between + -- command and last line (indicating zero exit) + matches('LastExitCode%s+[(]', out) + matches('\n.*%: ready [$]', out) + end) + + end) + +end) diff --git a/test/functional/ex_cmds/mksession_spec.lua b/test/functional/ex_cmds/mksession_spec.lua index 949724bb53..09eaa36686 100644 --- a/test/functional/ex_cmds/mksession_spec.lua +++ b/test/functional/ex_cmds/mksession_spec.lua @@ -43,7 +43,7 @@ describe(':mksession', function() -- Restore session. command('source '..session_file) - eq({3,3,2}, + eq({2,2,4}, {funcs.winbufnr(1), funcs.winbufnr(2), funcs.winbufnr(3)}) end) @@ -91,7 +91,12 @@ describe(':mksession', function() command('tabnext 1') eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '1', funcs.expand('%:p')) command('tabnext 2') - eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '2', funcs.expand('%:p')) + -- :mksession stores paths using unix slashes, but Nvim doesn't adjust these + -- for absolute paths in all cases yet. Absolute paths are used in the + -- session file after :tcd, so we need to expect unix slashes here for now + -- eq(cwd_dir .. get_pathsep() .. tmpfile_base .. '2', funcs.expand('%:p')) + eq(cwd_dir:gsub([[\]], '/') .. '/' .. tmpfile_base .. '2', + funcs.expand('%:p')) end) it('restores CWD for :terminal buffers #11288', function() diff --git a/test/functional/ex_cmds/quickfix_commands_spec.lua b/test/functional/ex_cmds/quickfix_commands_spec.lua index 3392a90270..c956a2df2d 100644 --- a/test/functional/ex_cmds/quickfix_commands_spec.lua +++ b/test/functional/ex_cmds/quickfix_commands_spec.lua @@ -37,9 +37,9 @@ for _, c in ipairs({'l', 'c'}) do -- Second line of each entry (i.e. `nr=-1, …`) was obtained from actual -- results. First line (i.e. `{lnum=…`) was obtained from legacy test. local list = { - {lnum=700, col=10, text='Line 700', module='', + {lnum=700, end_lnum=0, col=10, end_col=0, text='Line 700', module='', nr=-1, bufnr=2, valid=1, pattern='', vcol=0, ['type']=''}, - {lnum=800, col=15, text='Line 800', module='', + {lnum=800, end_lnum=0, col=15, end_col=0, text='Line 800', module='', nr=-1, bufnr=3, valid=1, pattern='', vcol=0, ['type']=''}, } eq(list, getlist()) @@ -47,6 +47,7 @@ for _, c in ipairs({'l', 'c'}) do eq(('%s-2.res'):format(file), funcs.bufname(list[2].bufnr)) -- Run cfile/lfile from a modified buffer + command('set nohidden') command('enew!') curbufmeths.set_lines(1, 1, true, {'Quickfix'}) eq(('Vim(%s):E37: No write since last change (add ! to override)'):format( @@ -58,7 +59,7 @@ for _, c in ipairs({'l', 'c'}) do ]]):format(file)) command(('%s %s'):format(addfcmd, file)) list[#list + 1] = { - lnum=900, col=30, text='Line 900', module='', + lnum=900, end_lnum=0, col=30, end_col=0, text='Line 900', module='', nr=-1, bufnr=5, valid=1, pattern='', vcol=0, ['type']='', } eq(list, getlist()) @@ -71,9 +72,9 @@ for _, c in ipairs({'l', 'c'}) do command('enew!') command(('%s %s'):format(getfcmd, file)) list = { - {lnum=222, col=77, text='Line 222', module='', + {lnum=222, end_lnum=0, col=77, end_col=0, text='Line 222', module='', nr=-1, bufnr=2, valid=1, pattern='', vcol=0, ['type']=''}, - {lnum=333, col=88, text='Line 333', module='', + {lnum=333, end_lnum=0, col=88, end_col=0, text='Line 333', module='', nr=-1, bufnr=3, valid=1, pattern='', vcol=0, ['type']=''}, } eq(list, getlist()) diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua index 16d0dfb6a1..37c97f519a 100644 --- a/test/functional/ex_cmds/source_spec.lua +++ b/test/functional/ex_cmds/source_spec.lua @@ -6,6 +6,11 @@ local clear = helpers.clear local meths = helpers.meths local feed = helpers.feed local feed_command = helpers.feed_command +local write_file = helpers.write_file +local exec = helpers.exec +local eval = helpers.eval +local exec_capture = helpers.exec_capture +local neq = helpers.neq describe(':source', function() before_each(function() @@ -44,4 +49,70 @@ describe(':source', function() command('source') eq('4', meths.exec('echo luaeval("y")', true)) end) + + it('can source lua files', function() + local test_file = 'test.lua' + write_file (test_file, [[vim.g.sourced_lua = 1]]) + + exec('source ' .. test_file) + + eq(1, eval('g:sourced_lua')) + os.remove(test_file) + end) + + it('can source selected region in lua file', function() + local test_file = 'test.lua' + + write_file (test_file, [[ + vim.g.b = 5 + vim.g.b = 6 + vim.g.b = 7 + ]]) + + command('edit '..test_file) + feed('ggjV') + feed_command(':source') + + eq(6, eval('g:b')) + os.remove(test_file) + end) + + it('can source current lua buffer without argument', function() + local test_file = 'test.lua' + + write_file (test_file, [[ + vim.g.c = 10 + vim.g.c = 11 + vim.g.c = 12 + ]]) + + command('edit '..test_file) + feed_command(':source') + + eq(12, eval('g:c')) + os.remove(test_file) + end) + + it("doesn't throw E484 for lua parsing/runtime errors", function() + local test_file = 'test.lua' + + -- Does throw E484 for unreadable files + local ok, result = pcall(exec_capture, ":source "..test_file ..'noexisting') + eq(false, ok) + neq(nil, result:find("E484")) + + -- Doesn't throw for parsing error + write_file (test_file, "vim.g.c = ") + ok, result = pcall(exec_capture, ":source "..test_file) + eq(false, ok) + eq(nil, result:find("E484")) + os.remove(test_file) + + -- Doesn't throw for runtime error + write_file (test_file, "error('Cause error anyway :D')") + ok, result = pcall(exec_capture, ":source "..test_file) + eq(false, ok) + eq(nil, result:find("E484")) + os.remove(test_file) + end) end) diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua index f2a381869e..d91feb4bc1 100644 --- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local lfs = require('lfs') local eq, eval, expect, source = helpers.eq, helpers.eval, helpers.expect, helpers.source +local assert_alive = helpers.assert_alive local clear = helpers.clear local command = helpers.command local feed = helpers.feed @@ -26,7 +27,7 @@ describe(':recover', function() -- Also check filename ending with ".swp". #9504 eq('Vim(recover):E306: Cannot open '..swapname2, pcall_err(command, 'recover '..swapname2)) -- Should not segfault. #2117 - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/fixtures/api_level_7.mpack b/test/functional/fixtures/api_level_7.mpack Binary files differnew file mode 100644 index 0000000000..8aaca91410 --- /dev/null +++ b/test/functional/fixtures/api_level_7.mpack diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index bcd5e22492..9579525502 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -126,6 +126,89 @@ function tests.check_workspace_configuration() } end +function tests.prepare_rename_nil() + skeleton { + on_init = function() + return { capabilities = { + renameProvider = true, + } } + end; + body = function() + notify('start') + expect_request('textDocument/prepareRename', function() + return nil, nil + end) + notify('shutdown') + end; + } +end + +function tests.prepare_rename_placeholder() + skeleton { + on_init = function() + return { capabilities = { + renameProvider = true, + } } + end; + body = function() + notify('start') + expect_request('textDocument/prepareRename', function() + return nil, {placeholder = 'placeholder'} + end) + expect_request('textDocument/rename', function(params) + assert_eq(params.newName, 'renameto') + return nil, nil + end) + notify('shutdown') + end; + } +end + +function tests.prepare_rename_range() + skeleton { + on_init = function() + return { capabilities = { + renameProvider = true, + } } + end; + body = function() + notify('start') + expect_request('textDocument/prepareRename', function() + return nil, { + start = { line = 1, character = 8 }, + ['end'] = { line = 1, character = 12 }, + } + end) + expect_request('textDocument/rename', function(params) + assert_eq(params.newName, 'renameto') + return nil, nil + end) + notify('shutdown') + end; + } +end + +function tests.prepare_rename_error() + skeleton { + on_init = function() + return { capabilities = { + renameProvider = true, + } } + end; + body = function() + notify('start') + expect_request('textDocument/prepareRename', function() + return {}, nil + end) + expect_request('textDocument/rename', function(params) + assert_eq(params.newName, 'renameto') + return nil, nil + end) + notify('shutdown') + end; + } +end + function tests.basic_check_capabilities() skeleton { on_init = function(params) @@ -464,6 +547,23 @@ function tests.invalid_header() io.stdout:write("Content-length: \r\n") end +function tests.decode_nil() + skeleton { + on_init = function(_) + return { capabilities = {} } + end; + body = function() + notify('start') + notify("workspace/executeCommand", { + arguments = { "EXTRACT_METHOD", {metadata = {field = vim.NIL}}, 3, 0, 6123, vim.NIL }, + command = "refactor.perform", + title = "EXTRACT_METHOD" + }) + notify('finish') + end; + } +end + -- Tests will be indexed by TEST_NAME local kill_timer = vim.loop.new_timer() diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c index b95e563932..4196716799 100644 --- a/test/functional/fixtures/shell-test.c +++ b/test/functional/fixtures/shell-test.c @@ -19,7 +19,7 @@ static void flush_wait(void) static void help(void) { - puts("A simple implementation of a shell for testing termopen()."); + puts("Fake shell"); puts(""); puts("Usage:"); puts(" shell-test --help"); @@ -42,6 +42,8 @@ static void help(void) puts(" 96: foo bar"); puts(" shell-test INTERACT"); puts(" Prints \"interact $ \" to stderr, and waits for \"exit\" input."); + puts(" shell-test EXIT {code}"); + puts(" Exits immediately with exit code \"{code}\"."); } int main(int argc, char **argv) @@ -103,7 +105,6 @@ int main(int argc, char **argv) char input[256]; char cmd[100]; int arg; - int input_argc; while (1) { fprintf(stderr, "interact $ "); @@ -112,8 +113,7 @@ int main(int argc, char **argv) break; // EOF } - input_argc = sscanf(input, "%99s %d", cmd, &arg); - if(1 == input_argc) { + if(1 == sscanf(input, "%99s %d", cmd, &arg)) { arg = 0; } if (strcmp(cmd, "exit") == 0) { @@ -122,6 +122,15 @@ int main(int argc, char **argv) fprintf(stderr, "command not found: %s\n", cmd); } } + } else if (strcmp(argv[1], "EXIT") == 0) { + int code = 1; + if (argc >= 3) { + if (sscanf(argv[2], "%d", &code) != 1) { + fprintf(stderr, "Invalid exit code: %s\n", argv[2]); + return 2; + } + } + return code; } else { fprintf(stderr, "Unknown first argument: %s\n", argv[1]); return 3; diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 4acb1a7d8d..92d802d62d 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -38,10 +38,16 @@ module.nvim_prog = ( module.nvim_set = ( 'set shortmess+=IS background=light noswapfile noautoindent startofline' ..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.' - ..' belloff= wildoptions-=pum noshowcmd noruler nomore redrawdebug=invalid') + ..' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid') module.nvim_argv = { module.nvim_prog, '-u', 'NONE', '-i', 'NONE', - '--cmd', module.nvim_set, '--embed'} + '--cmd', module.nvim_set, + '--cmd', 'unmap Y', + '--cmd', 'unmap <C-L>', + '--cmd', 'iunmap <C-U>', + '--cmd', 'iunmap <C-W>', + '--embed'} + -- Directory containing nvim. module.nvim_dir = module.nvim_prog:gsub("[/\\][^/\\]+$", "") if module.nvim_dir == module.nvim_prog then @@ -416,7 +422,7 @@ end -- Builds an argument list for use in clear(). -- ---@see clear() for parameters. +---@see clear() for parameters. function module.new_argv(...) local args = {unpack(module.nvim_argv)} table.insert(args, '--headless') @@ -513,13 +519,15 @@ end function module.set_shell_powershell() local shell = iswin() and 'powershell' or 'pwsh' assert(module.has_powershell()) - local cmd = 'Remove-Item -Force '..table.concat(iswin() + local set_encoding = '[Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;' + local cmd = set_encoding..'Remove-Item -Force '..table.concat(iswin() and {'alias:cat', 'alias:echo', 'alias:sleep'} or {'alias:echo'}, ',')..';' module.source([[ let &shell = ']]..shell..[[' - set shellquote= shellpipe=\| shellxquote= - let &shellredir = '| Out-File -Encoding UTF8' + set shellquote= shellxquote= + let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' + let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode' let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ]]..cmd..[[' ]]) end @@ -565,7 +573,7 @@ function module.buf_lines(bufnr) return module.exec_lua("return vim.api.nvim_buf_get_lines((...), 0, -1, false)", bufnr) end ---@see buf_lines() +---@see buf_lines() function module.curbuf_contents() module.poke_eventloop() -- Before inspecting the buffer, do whatever. return table.concat(module.curbuf('get_lines', 0, -1, true), '\n') @@ -878,6 +886,13 @@ function module.os_kill(pid) or 'kill -9 '..pid..' > /dev/null')) end +-- Create folder with non existing parents +function module.mkdir_p(path) + return os.execute((iswin() + and 'mkdir '..path + or 'mkdir -p '..path)) +end + module = global_helpers.tbl_extend('error', module, global_helpers) return function(after_each) diff --git a/test/functional/insert/ctrl_o_spec.lua b/test/functional/insert/ctrl_o_spec.lua index 543d0a7d68..950ab24219 100644 --- a/test/functional/insert/ctrl_o_spec.lua +++ b/test/functional/insert/ctrl_o_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local clear = helpers.clear local eq = helpers.eq local eval = helpers.eval @@ -45,7 +46,7 @@ describe('insert-mode Ctrl-O', function() it("doesn't cancel Ctrl-O mode when processing event", function() feed('iHello World<c-o>') eq({mode='niI', blocking=false}, meths.get_mode()) -- fast event - eq(2, eval('1+1')) -- causes K_EVENT key + assert_alive() -- causes K_EVENT key eq({mode='niI', blocking=false}, meths.get_mode()) -- still in ctrl-o mode feed('dd') eq({mode='i', blocking=false}, meths.get_mode()) -- left ctrl-o mode diff --git a/test/functional/legacy/007_ball_buffer_list_spec.lua b/test/functional/legacy/007_ball_buffer_list_spec.lua index a180e73301..d4e4547c43 100644 --- a/test/functional/legacy/007_ball_buffer_list_spec.lua +++ b/test/functional/legacy/007_ball_buffer_list_spec.lua @@ -8,6 +8,9 @@ describe(':ball', function() setup(clear) it('is working', function() + -- Must disable 'hidden' so that the BufReadPost autocmd is triggered + -- when Xxx2 is reloaded + feed_command('set nohidden') insert([[ start of test file Xxx this is a test @@ -18,7 +21,7 @@ describe(':ball', function() feed('gg') -- Write test file Xxx1 - feed('A1:.,/end of/w! Xxx1<cr>') + feed('A1<esc>:.,/end of/w! Xxx1<cr>') feed_command('sp Xxx1') feed_command('close') diff --git a/test/functional/legacy/008_autocommands_spec.lua b/test/functional/legacy/008_autocommands_spec.lua index 939404cb5e..002f037d09 100644 --- a/test/functional/legacy/008_autocommands_spec.lua +++ b/test/functional/legacy/008_autocommands_spec.lua @@ -71,6 +71,9 @@ describe('autocommands that delete and unload buffers:', function() au BufUnload * call CloseAll() au VimLeave * call WriteToOut() ]]) + -- Must disable 'hidden' so that the BufUnload autocmd is triggered between + -- each :edit + command('set nohidden') command('silent! edit Xxx2') command('silent! edit Xxx1') command('silent! edit Makefile') -- an existing file diff --git a/test/functional/legacy/011_autocommands_spec.lua b/test/functional/legacy/011_autocommands_spec.lua index 7cc31dc787..0fa9290f3c 100644 --- a/test/functional/legacy/011_autocommands_spec.lua +++ b/test/functional/legacy/011_autocommands_spec.lua @@ -17,6 +17,7 @@ local lfs = require('lfs') 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 iswin = helpers.iswin local read_file = helpers.read_file @@ -28,7 +29,7 @@ end local function prepare_gz_file(name, text) write_file(name, text..'\n') -- Compress the file with gzip. - os.execute('gzip --force '..name) + command([[call system(['gzip', '--force', ']]..name..[['])]]) -- This should create the .gz file and delete the original. neq(nil, lfs.attributes(name..'.gz')) eq(nil, lfs.attributes(name)) @@ -54,7 +55,9 @@ describe('file reading, writing and bufnew and filter autocommands', function() */ ]]) end) - before_each(clear) + before_each(function () + clear({env={GZIP=nil}}) + end) teardown(function() os.remove('Xtestfile.gz') os.remove('Xtest.c') @@ -67,7 +70,6 @@ describe('file reading, writing and bufnew and filter autocommands', function() it('FileReadPost (using gzip)', function() prepare_gz_file('Xtestfile', text1) - feed_command('let $GZIP = ""') --execute('au FileChangedShell * echo "caught FileChangedShell"') feed_command('set bin') feed_command("au FileReadPost *.gz '[,']!gzip -d") @@ -79,7 +81,6 @@ describe('file reading, writing and bufnew and filter autocommands', function() it('BufReadPre, BufReadPost (using gzip)', function() prepare_gz_file('Xtestfile', text1) local gzip_data = read_file('Xtestfile.gz') - feed_command('let $GZIP = ""') -- Setup autocommands to decompress before reading and re-compress afterwards. feed_command("au BufReadPre *.gz exe '!gzip -d ' . shellescape(expand('<afile>'))") feed_command("au BufReadPre *.gz call rename(expand('<afile>:r'), expand('<afile>'))") @@ -128,13 +129,11 @@ describe('file reading, writing and bufnew and filter autocommands', function() -- Will load Xtest.c. feed_command('e! foo.c') feed_command("au FileAppendPre *.out '[,']s/new/NEW/") - feed_command('au FileAppendPost *.out !cat Xtest.c >>test.out') + feed_command('au FileAppendPost *.out !cat Xtest.c >test.out') -- Append it to the output file. feed_command('w>>test.out') -- Discard all prompts and messages. feed('<C-L>') - -- Expect the decompressed file in the buffer. - feed_command('e test.out') expect([[ /* diff --git a/test/functional/legacy/012_directory_spec.lua b/test/functional/legacy/012_directory_spec.lua index 48dd24db9e..f666e51469 100644 --- a/test/functional/legacy/012_directory_spec.lua +++ b/test/functional/legacy/012_directory_spec.lua @@ -80,6 +80,7 @@ describe("'directory' option", function() eq({ "Xtest1.swp", "Xtest3" }, ls_dir_sorted("Xtest2")) meths.set_option('directory', 'Xtest.je') + command('bdelete') command('edit Xtest2/Xtest3') eq(true, curbufmeths.get_option('swapfile')) poke_eventloop() diff --git a/test/functional/legacy/arglist_spec.lua b/test/functional/legacy/arglist_spec.lua index 67c5750033..6a2e86ccb4 100644 --- a/test/functional/legacy/arglist_spec.lua +++ b/test/functional/legacy/arglist_spec.lua @@ -156,10 +156,12 @@ describe('argument list commands', function() eq({'a', 'b', 'a', 'c'}, eval('argv()')) command('0argedit x') eq({'x', 'a', 'b', 'a', 'c'}, eval('argv()')) + command('set nohidden') command('enew! | set modified') assert_fails('argedit y', 'E37:') command('argedit! y') eq({'x', 'y', 'y', 'a', 'b', 'a', 'c'}, eval('argv()')) + command('set hidden') command('%argd') command('argedit a b') eq({'a', 'b'}, eval('argv()')) diff --git a/test/functional/legacy/assert_spec.lua b/test/functional/legacy/assert_spec.lua index 515d6d91b8..c2b22472bf 100644 --- a/test/functional/legacy/assert_spec.lua +++ b/test/functional/legacy/assert_spec.lua @@ -26,6 +26,14 @@ describe('assert function:', function() call('assert_beeps', 'normal 0') expected_errors({'command did not beep: normal 0'}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(0, 'normal h'->assert_beeps()) + call assert_equal(1, 'normal 0'->assert_beeps()) + ]] + expected_errors({tmpname .. ' line 2: command did not beep: normal 0'}) + end) end) -- assert_equal({expected}, {actual}, [, {msg}]) @@ -133,6 +141,14 @@ describe('assert function:', function() call('assert_false', {}) expected_errors({'Expected False but got []'}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(0, v:false->assert_false()) + call assert_equal(1, 123->assert_false()) + ]] + expected_errors({tmpname .. ' line 2: Expected False but got 123'}) + end) end) -- assert_true({actual}, [, {msg}]) @@ -148,6 +164,14 @@ describe('assert function:', function() eq(1, call('assert_true', 1.5)) expected_errors({'Expected True but got 1.5'}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(0, v:true->assert_true()) + call assert_equal(1, 0->assert_true()) + ]] + expected_errors({tmpname .. ' line 2: Expected True but got 0'}) + end) end) describe('v:errors', function() @@ -223,6 +247,15 @@ describe('assert function:', function() call('assert_match', 'bar.*foo', 'foobar', 'wrong') expected_errors({"wrong: Pattern 'bar.*foo' does not match 'foobar'"}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(1, 'foobar'->assert_match('bar.*foo', 'wrong')) + ]] + expected_errors({ + tmpname .. " line 1: wrong: Pattern 'bar.*foo' does not match 'foobar'" + }) + end) end) -- assert_notmatch({pat}, {text}[, {msg}]) @@ -237,6 +270,13 @@ describe('assert function:', function() call('assert_notmatch', 'foo', 'foobar') expected_errors({"Pattern 'foo' does match 'foobar'"}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(1, 'foobar'->assert_notmatch('foo')) + ]] + expected_errors({tmpname .. " line 1: Pattern 'foo' does match 'foobar'"}) + end) end) -- assert_fails({cmd}, [, {error}]) @@ -267,6 +307,15 @@ describe('assert function:', function() eq(1, eval([[assert_fails('echo', '', 'echo command')]])) expected_errors({'command did not fail: echo command'}) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(1, 'echo'->assert_fails('', 'echo command')) + ]] + expected_errors({ + tmpname .. ' line 1: command did not fail: echo command' + }) + end) end) -- assert_inrange({lower}, {upper}, {actual}[, {msg}]) @@ -292,6 +341,15 @@ describe('assert function:', function() eq('Vim(call):E119: Not enough arguments for function: assert_inrange', exc_exec("call assert_inrange(1, 1)")) end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(0, 5->assert_inrange(5, 7)) + call assert_equal(0, 7->assert_inrange(5, 7)) + call assert_equal(1, 8->assert_inrange(5, 7)) + ]] + expected_errors({tmpname .. ' line 3: Expected range 5 - 7, but got 8'}) + end) end) -- assert_report({msg}) @@ -302,6 +360,13 @@ describe('assert function:', function() command('call remove(v:errors, 0)') expected_empty() end) + + it('can be used as a method', function() + local tmpname = source [[ + call assert_equal(1, 'also wrong'->assert_report()) + ]] + expected_errors({tmpname .. ' line 1: also wrong'}) + end) end) -- assert_exception({cmd}, [, {error}]) diff --git a/test/functional/legacy/cdo_spec.lua b/test/functional/legacy/cdo_spec.lua index 5e46431cc1..8b3216cbfd 100644 --- a/test/functional/legacy/cdo_spec.lua +++ b/test/functional/legacy/cdo_spec.lua @@ -91,7 +91,8 @@ describe('cdo', function() exe "silent! 4,5" . XdoCmd call assert_equal([], l) - " Run commands from an unsaved buffer + " Run commands from an unsaved buffer when 'hidden' is unset + set nohidden let v:errmsg='' let l = [] enew @@ -108,6 +109,7 @@ describe('cdo', function() if subst_count != 1 || getline('.') != 'xLine1' call add(v:errors, 'Abort command on error test failed') endif + set hidden let l = [] exe "2,2" . Xdo . "! call add(l, GetRuler())" diff --git a/test/functional/legacy/eval_spec.lua b/test/functional/legacy/eval_spec.lua index ee9bd29fc4..3b407ce5f5 100644 --- a/test/functional/legacy/eval_spec.lua +++ b/test/functional/legacy/eval_spec.lua @@ -1,6 +1,7 @@ -- 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 @@ -506,7 +507,7 @@ describe('eval', function() command("call setreg('0',x)") -- nvim didn't crash and "0 was emptied - eq(2, eval("1+1")) + assert_alive() eq({}, eval("getreg('0',1,1)")) -- x is a mutable list diff --git a/test/functional/legacy/fixeol_spec.lua b/test/functional/legacy/fixeol_spec.lua index 50236e8617..d3ff86d349 100644 --- a/test/functional/legacy/fixeol_spec.lua +++ b/test/functional/legacy/fixeol_spec.lua @@ -23,8 +23,6 @@ describe('fixeol', function() it('is working', function() -- First write two test files – with and without trailing EOL. - -- Use Unix fileformat for consistency. - feed_command('set ff=unix') feed_command('enew!') feed('awith eol<esc>:w! XXEol<cr>') feed_command('enew!') @@ -40,7 +38,7 @@ describe('fixeol', function() feed_command('e! XXNoEol') feed('ostays without<esc>:set nofixeol<cr>') feed_command('w! XXTestNoEol') - feed_command('bwipe XXEol XXNoEol XXTestEol XXTestNoEol') + feed_command('bwipe! XXEol XXNoEol XXTestEol XXTestNoEol') feed_command('set fixeol') -- Append "END" to each file so that we can see what the last written char was. diff --git a/test/functional/legacy/listchars_spec.lua b/test/functional/legacy/listchars_spec.lua index cffb9fd376..dc6ccd3628 100644 --- a/test/functional/legacy/listchars_spec.lua +++ b/test/functional/legacy/listchars_spec.lua @@ -8,7 +8,7 @@ local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers describe("'listchars'", function() before_each(function() clear() - feed_command('set listchars&vi') + feed_command('set listchars=eol:$') end) -- luacheck: ignore 613 (Trailing whitespace in a string) diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index 97ac96804e..d86caca0e9 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -166,4 +166,39 @@ describe('memory usage', function() check_result({before=before, after=after, last=last}, pcall(ok, last.last < upper)) end) + + it('releases memory when closing windows when folds exist', function() + if helpers.is_os('mac') then + pending('macOS memory compression causes flakiness') + end + local pid = eval('getpid()') + source([[ + new + " Insert lines + call nvim_buf_set_lines(0, 0, 0, v:false, repeat([''], 999)) + " Create folds + normal! gg + for _ in range(500) + normal! zfjj + endfor + ]]) + poke_eventloop() + local before = monitor_memory_usage(pid) + source([[ + " Split and close window multiple times + for _ in range(1000) + split + close + endfor + ]]) + poke_eventloop() + local after = monitor_memory_usage(pid) + source('bwipe!') + poke_eventloop() + -- Allow for an increase of 5% in memory usage, which accommodates minor fluctuation, + -- but is small enough that if memory were not released (prior to PR #14884), the test + -- would fail. + local upper = before.last * 1.05 + check_result({before=before, after=after}, pcall(ok, after.last <= upper)) + end) end) diff --git a/test/functional/legacy/mksession_spec.lua b/test/functional/legacy/mksession_spec.lua index a2af891107..bca9cd833c 100644 --- a/test/functional/legacy/mksession_spec.lua +++ b/test/functional/legacy/mksession_spec.lua @@ -12,7 +12,7 @@ describe('mksession', function() end) it('supports "skiprtp" value', function() - command('set sessionoptions&vi') + command('set sessionoptions+=options') command('set rtp+=$HOME') command('set pp+=$HOME') command('mksession! Xtest_mks.out') diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua index 486a1fe471..48cd3ef9f8 100644 --- a/test/functional/legacy/packadd_spec.lua +++ b/test/functional/legacy/packadd_spec.lua @@ -101,9 +101,14 @@ describe('packadd', function() call setline(1, 'let g:plugin_works = 24') wq + exe 'split ' . plugdir . '/plugin/test.lua' + call setline(1, 'vim.g.plugin_lua_works = 24') + wq + packadd other call assert_equal(24, g:plugin_works) + call assert_equal(24, g:plugin_lua_works) call assert_true(len(&rtp) > len(rtp)) call assert_match(Escape(plugdir) . '\($\|,\)', &rtp) endfunc @@ -117,13 +122,18 @@ describe('packadd', function() exe 'split ' . s:plugdir . '/plugin/test.vim' call setline(1, 'let g:plugin_works = 42') wq + exe 'split ' . s:plugdir . '/plugin/test.lua' + call setline(1, 'let g:plugin_lua_works = 42') + wq let g:plugin_works = 0 + let g:plugin_lua_works = 0 packadd! mytest call assert_true(len(&rtp) > len(rtp)) call assert_match(Escape(s:plugdir) . '\($\|,\)', &rtp) call assert_equal(0, g:plugin_works) + call assert_equal(0, g:plugin_lua_works) " check the path is not added twice let new_rtp = &rtp diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 1b2c21783e..073927bf22 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -978,6 +978,22 @@ describe('lua: nvim_buf_attach on_bytes', function() } end) + it("visual paste", function() + local check_events= setup_eventcheck(verify, { "aaa {", "b", "}" }) + -- Setting up + feed[[jdd]] + check_events { + { "test1", "bytes", 1, 3, 1, 0, 6, 1, 0, 2, 0, 0, 0 }; + } + + -- Actually testing + feed[[v%p]] + check_events { + { "test1", "bytes", 1, 8, 0, 4, 4, 1, 1, 3, 0, 0, 0 }; + { "test1", "bytes", 1, 8, 0, 4, 4, 0, 0, 0, 2, 0, 3 }; + } + end) + it("nvim_buf_set_lines", function() local check_events = setup_eventcheck(verify, {"AAA", "BBB"}) @@ -1001,6 +1017,39 @@ describe('lua: nvim_buf_attach on_bytes', function() } end) + it("flushes delbytes on substitute", function() + local check_events = setup_eventcheck(verify, {"AAA", "BBB", "CCC"}) + + feed("gg0") + command("s/AAA/GGG/") + + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 0, 3, 3, 0, 3, 3 }; + } + + -- check that byte updates for :delete (which uses curbuf->deleted_bytes2) + -- are correct + command("delete") + check_events { + { "test1", "bytes", 1, 4, 0, 0, 0, 1, 0, 4, 0, 0, 0 }; + } + end) + + it("flushes delbytes on join", function() + local check_events = setup_eventcheck(verify, {"AAA", "BBB", "CCC"}) + + feed("gg0J") + + check_events { + { "test1", "bytes", 1, 3, 0, 3, 3, 1, 0, 1, 0, 1, 1 }; + } + + command("delete") + check_events { + { "test1", "bytes", 1, 5, 0, 0, 0, 1, 0, 8, 0, 0, 0 }; + } + end) + teardown(function() os.remove "Xtest-reload" os.remove "Xtest-undofile" diff --git a/test/functional/lua/highlight_spec.lua b/test/functional/lua/highlight_spec.lua new file mode 100644 index 0000000000..50eecb5d09 --- /dev/null +++ b/test/functional/lua/highlight_spec.lua @@ -0,0 +1,25 @@ +local helpers = require('test.functional.helpers')(after_each) +local exec_lua = helpers.exec_lua +local eq = helpers.eq +local eval = helpers.eval +local command = helpers.command +local clear = helpers.clear + +describe('vim.highlight.on_yank', function() + + before_each(function() + clear() + end) + + it('does not show errors even if buffer is wiped before timeout', function() + command('new') + exec_lua[[ + vim.highlight.on_yank({timeout = 10, on_macro = true, event = {operator = "y", regtype = "v"}}) + vim.cmd('bwipeout!') + ]] + helpers.sleep(10) + helpers.feed('<cr>') -- avoid hang if error message exists + eq('', eval('v:errmsg')) + end) + +end) diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua index 2ec48777fd..8ef77faa0f 100644 --- a/test/functional/lua/luaeval_spec.lua +++ b/test/functional/lua/luaeval_spec.lua @@ -481,6 +481,21 @@ describe('v:lua', function() pcall_err(eval, 'v:lua.mymod.crashy()')) end) + it('works when called as a method', function() + eq(123, eval('110->v:lua.foo(13)')) + eq(true, exec_lua([[return _G.val == nil]])) + + eq(321, eval('300->v:lua.foo(21, "boop")')) + eq("boop", exec_lua([[return _G.val]])) + + eq(NIL, eval('"there"->v:lua.mymod.noisy()')) + eq("hey there", meths.get_current_line()) + eq({5, 10, 15, 20}, eval('[[1], [2, 3], [4]]->v:lua.vim.tbl_flatten()->map({_, v -> v * 5})')) + + eq("Vim:E5108: Error executing lua [string \"<nvim>\"]:0: attempt to call global 'nonexistent' (a nil value)", + pcall_err(eval, '"huh?"->v:lua.mymod.crashy()')) + end) + it('works in :call', function() command(":call v:lua.mymod.noisy('command')") eq("hey command", meths.get_current_line()) @@ -518,8 +533,15 @@ describe('v:lua', function() eq("Vim:E15: Invalid expression: v:['lua'].foo()", pcall_err(eval, "v:['lua'].foo()")) eq("Vim(call):E461: Illegal variable name: v:['lua']", pcall_err(command, "call v:['lua'].baar()")) + eq("Vim:E117: Unknown function: v:lua", pcall_err(eval, "v:lua()")) eq("Vim(let):E46: Cannot change read-only variable \"v:['lua']\"", pcall_err(command, "let v:['lua'] = 'xx'")) eq("Vim(let):E46: Cannot change read-only variable \"v:lua\"", pcall_err(command, "let v:lua = 'xx'")) + + eq("Vim:E107: Missing parentheses: v:lua.func", pcall_err(eval, "'bad'->v:lua.func")) + eq("Vim:E274: No white space allowed before parenthesis", pcall_err(eval, "'bad'->v:lua.func ()")) + eq("Vim:E107: Missing parentheses: v:lua", pcall_err(eval, "'bad'->v:lua")) + eq("Vim:E117: Unknown function: v:lua", pcall_err(eval, "'bad'->v:lua()")) + eq("Vim:E15: Invalid expression: v:lua.()", pcall_err(eval, "'bad'->v:lua.()")) end) end) diff --git a/test/functional/lua/mpack_spec.lua b/test/functional/lua/mpack_spec.lua new file mode 100644 index 0000000000..ef693f01f3 --- /dev/null +++ b/test/functional/lua/mpack_spec.lua @@ -0,0 +1,23 @@ +-- Test suite for testing interactions with API bindings +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local exec_lua = helpers.exec_lua + +describe('lua vim.mpack', function() + before_each(clear) + it('can pack vim.NIL', function() + eq({true, true, true, true}, exec_lua [[ + local var = vim.mpack.unpack(vim.mpack.pack({33, vim.NIL, 77})) + return {var[1]==33, var[2]==vim.NIL, var[3]==77, var[4]==nil} + ]]) + end) + + it('can pack vim.empty_dict()', function() + eq({{{}, "foo", {}}, true, false}, exec_lua [[ + local var = vim.mpack.unpack(vim.mpack.pack({{}, "foo", vim.empty_dict()})) + return {var, vim.tbl_islist(var[1]), vim.tbl_islist(var[3])} + ]]) + end) +end) diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua new file mode 100644 index 0000000000..e9c34c9228 --- /dev/null +++ b/test/functional/lua/runtime_spec.lua @@ -0,0 +1,141 @@ +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 mkdir_p = helpers.mkdir_p +local rmdir = helpers.rmdir +local write_file = helpers.write_file + +describe('runtime:', function() + local plug_dir = 'Test_Plugin' + local sep = helpers.get_pathsep() + local init = 'dummy_init.lua' + + setup(function() + io.open(init, 'w'):close() -- touch init file + clear{args = {'-u', init}} + exec('set rtp+=' .. plug_dir) + end) + + teardown(function() + os.remove(init) + end) + + before_each(function() + mkdir_p(plug_dir) + end) + + after_each(function() + rmdir(plug_dir) + end) + + describe('colors', function() + local colorscheme_folder = plug_dir .. sep .. 'colors' + + it('loads lua colorscheme', function() + local colorscheme_file = colorscheme_folder .. sep .. 'new_colorscheme.lua' + mkdir_p(colorscheme_folder) + write_file(colorscheme_file, [[vim.g.lua_colorscheme = 1]]) + + exec('colorscheme new_colorscheme') + + eq(1, eval('g:lua_colorscheme')) + rmdir(colorscheme_folder) + end) + + it('loads vim colorscheme when both lua and vim version exist', function() + local colorscheme_file = colorscheme_folder .. sep .. 'new_colorscheme' + mkdir_p(colorscheme_folder) + write_file(colorscheme_file..'.vim', [[let g:colorscheme = 'vim']]) + write_file(colorscheme_file..'.lua', [[vim.g.colorscheme = 'lua']]) + + exec('colorscheme new_colorscheme') + + eq('vim', eval('g:colorscheme')) + rmdir(colorscheme_folder) + end) + end) + + describe('compiler', function() + local compiler_folder = plug_dir .. sep .. 'compiler' + + it('loads lua compilers', function() + local compiler_file = compiler_folder .. sep .. 'new_compiler.lua' + mkdir_p(compiler_folder) + write_file(compiler_file, [[vim.g.lua_compiler = 1]]) + + exec('compiler new_compiler') + + eq(1, eval('g:lua_compiler')) + rmdir(compiler_folder) + end) + + it('loads vim compilers when both lua and vim version exist', function() + local compiler_file = compiler_folder .. sep .. 'new_compiler' + mkdir_p(compiler_folder) + write_file(compiler_file..'.vim', [[let g:compiler = 'vim']]) + write_file(compiler_file..'.lua', [[vim.g.compiler = 'lua']]) + + exec('compiler new_compiler') + + eq('vim', eval('g:compiler')) + rmdir(compiler_folder) + end) + end) + + describe('ftplugin', function() + local ftplugin_folder = table.concat({plug_dir, 'ftplugin'}, sep) + + it('loads lua ftplugins', function() + local ftplugin_file = table.concat({ftplugin_folder , 'new-ft.lua'}, sep) + mkdir_p(ftplugin_folder) + write_file(ftplugin_file , [[vim.g.lua_ftplugin = 1]]) + + exec [[set filetype=new-ft]] + eq(1, eval('g:lua_ftplugin')) + rmdir(ftplugin_folder) + end) + end) + + describe('indent', function() + local indent_folder = table.concat({plug_dir, 'indent'}, sep) + + it('loads lua indents', function() + local indent_file = table.concat({indent_folder , 'new-ft.lua'}, sep) + mkdir_p(indent_folder) + write_file(indent_file , [[vim.g.lua_indent = 1]]) + + exec [[set filetype=new-ft]] + eq(1, eval('g:lua_indent')) + rmdir(indent_folder) + end) + end) + + describe('syntax', function() + local syntax_folder = table.concat({plug_dir, 'syntax'}, sep) + + it('loads lua syntaxes on filetype change', function() + local syntax_file = table.concat({syntax_folder , 'my-lang.lua'}, sep) + mkdir_p(syntax_folder) + write_file(syntax_file , [[vim.g.lua_syntax = 1]]) + + exec('set filetype=my-lang') + eq(1, eval('g:lua_syntax')) + rmdir(syntax_folder) + end) + + it('loads lua syntaxes on syntax change', function() + local syntax_file = table.concat({syntax_folder , 'my-lang.lua'}, sep) + mkdir_p(syntax_folder) + write_file(syntax_file , [[vim.g.lua_syntax = 5]]) + + exec('set syntax=my-lang') + eq(5, eval('g:lua_syntax')) + rmdir(syntax_folder) + end) + end) + +end) + diff --git a/test/functional/lua/uri_spec.lua b/test/functional/lua/uri_spec.lua index f782769935..052a8a1ecd 100644 --- a/test/functional/lua/uri_spec.lua +++ b/test/functional/lua/uri_spec.lua @@ -53,12 +53,18 @@ describe('URI methods', function() describe('uri to filepath', function() describe('decode Unix file path', function() - it('file path includes only ascii charactors', function() + it('file path includes only ascii characters', function() exec_lua("uri = 'file:///Foo/Bar/Baz.txt'") eq('/Foo/Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)")) end) + it('local file path without hostname', function() + exec_lua("uri = 'file:/Foo/Bar/Baz.txt'") + + eq('/Foo/Bar/Baz.txt', exec_lua("return vim.uri_to_fname(uri)")) + end) + it('file path including white space', function() exec_lua("uri = 'file:///Foo%20/Bar/Baz.txt'") @@ -85,6 +91,15 @@ describe('URI methods', function() eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case)) end) + it('local file path without hostname', function() + local test_case = [[ + local uri = 'file:/C:/Foo/Bar/Baz.txt' + return vim.uri_to_fname(uri) + ]] + + eq('C:\\Foo\\Bar\\Baz.txt', exec_lua(test_case)) + end) + it('file path includes only ascii charactors with encoded colon character', function() local test_case = [[ local uri = 'file:///C%3A/Foo/Bar/Baz.txt' @@ -125,6 +140,12 @@ describe('URI methods', function() return vim.uri_to_fname('JDT://content/%5C/') ]]) end) + + it('uri_to_fname returns non-file scheme URI without authority unchanged', function() + eq('zipfile:/path/to/archive.zip%3A%3Afilename.txt', exec_lua [[ + return vim.uri_to_fname('zipfile:/path/to/archive.zip%3A%3Afilename.txt') + ]]) + end) end) describe('decode URI without scheme', function() @@ -146,5 +167,14 @@ describe('URI methods', function() ]], uri) eq(uri, exec_lua(test_case)) end) + + it('uri_to_bufnr & uri_from_bufnr returns original uri for non-file uris without authority', function() + local uri = 'zipfile:/path/to/archive.zip%3A%3Afilename.txt' + local test_case = string.format([[ + local uri = '%s' + return vim.uri_from_bufnr(vim.uri_to_bufnr(uri)) + ]], uri) + eq(uri, exec_lua(test_case)) + end) end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 836f514433..2bedbd1453 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -6,6 +6,7 @@ local funcs = helpers.funcs local meths = helpers.meths local dedent = helpers.dedent local command = helpers.command +local insert = helpers.insert local clear = helpers.clear local eq = helpers.eq local ok = helpers.ok @@ -17,6 +18,7 @@ local matches = helpers.matches local source = helpers.source local NIL = helpers.NIL local retry = helpers.retry +local next_msg = helpers.next_msg before_each(clear) @@ -603,6 +605,31 @@ describe('lua stdlib', function() return vim.tbl_islist(c) and count == 0 ]])) + eq(exec_lua([[ + local a = { a = { b = 1 } } + local b = { a = {} } + return vim.tbl_deep_extend("force", a, b) + ]]), {a = {b = 1}}) + + eq(exec_lua([[ + local a = { a = 123 } + local b = { a = { b = 1} } + return vim.tbl_deep_extend("force", a, b) + ]]), {a = {b = 1}}) + + ok(exec_lua([[ + local a = { a = {[2] = 3} } + local b = { a = {[3] = 3} } + local c = vim.tbl_deep_extend("force", a, b) + return vim.deep_equal(c, {a = {[3] = 3}}) + ]])) + + eq(exec_lua([[ + local a = { a = { b = 1} } + local b = { a = 123 } + return vim.tbl_deep_extend("force", a, b) + ]]), {a = 123 }) + eq('Error executing lua: vim/shared.lua:0: invalid "behavior": nil', pcall_err(exec_lua, [[ return vim.tbl_deep_extend() @@ -1228,6 +1255,21 @@ describe('lua stdlib', function() eq("only-local", result[8]) end) + it('should allow you to retrieve window opts even if they have not been set', function() + local result = exec_lua [[ + local result = {} + table.insert(result, vim.opt.number:get()) + table.insert(result, vim.opt_local.number:get()) + + vim.opt_local.number = true + table.insert(result, vim.opt.number:get()) + table.insert(result, vim.opt_local.number:get()) + + return result + ]] + eq({false, false, true, true}, result) + end) + it('should allow all sorts of string manipulation', function() eq({'hello', 'hello world', 'start hello world'}, exec_lua [[ local results = {} @@ -1299,14 +1341,30 @@ describe('lua stdlib', function() eq("*.c", wildignore[1]) end) + it('should work for options that are both commalist and flaglist', function() + local result = exec_lua [[ + vim.opt.whichwrap = "b,s" + return vim.opt.whichwrap:get() + ]] + + eq({b = true, s = true}, result) + + result = exec_lua [[ + vim.opt.whichwrap = { b = true, s = false, h = true } + return vim.opt.whichwrap:get() + ]] + + eq({b = true, h = true}, result) + end) + it('should work for key-value pair options', function() local listchars = exec_lua [[ - vim.opt.listchars = "tab:>~,space:_" + vim.opt.listchars = "tab:> ,space:_" return vim.opt.listchars:get() ]] eq({ - tab = ">~", + tab = "> ", space = "_", }, listchars) end) @@ -1569,7 +1627,222 @@ describe('lua stdlib', function() eq(wildignore, 'super_first,first,foo') end) - end) + it('should not remove duplicates from wildmode: #14708', function() + local wildmode = exec_lua [[ + vim.opt.wildmode = {"full", "list", "full"} + return vim.o.wildmode + ]] + + eq(wildmode, 'full,list,full') + end) + + describe('option types', function() + it('should allow to set option with numeric value', function() + eq(4, exec_lua [[ + vim.opt.tabstop = 4 + return vim.bo.tabstop + ]]) + + matches("Invalid option type 'string' for 'tabstop'", pcall_err(exec_lua, [[ + vim.opt.tabstop = '4' + ]])) + matches("Invalid option type 'boolean' for 'tabstop'", pcall_err(exec_lua, [[ + vim.opt.tabstop = true + ]])) + matches("Invalid option type 'table' for 'tabstop'", pcall_err(exec_lua, [[ + vim.opt.tabstop = {4, 2} + ]])) + matches("Invalid option type 'function' for 'tabstop'", pcall_err(exec_lua, [[ + vim.opt.tabstop = function() + return 4 + end + ]])) + end) + + it('should allow to set option with boolean value', function() + eq(true, exec_lua [[ + vim.opt.undofile = true + return vim.bo.undofile + ]]) + + matches("Invalid option type 'number' for 'undofile'", pcall_err(exec_lua, [[ + vim.opt.undofile = 0 + ]])) + matches("Invalid option type 'table' for 'undofile'", pcall_err(exec_lua, [[ + vim.opt.undofile = {true} + ]])) + matches("Invalid option type 'string' for 'undofile'", pcall_err(exec_lua, [[ + vim.opt.undofile = 'true' + ]])) + matches("Invalid option type 'function' for 'undofile'", pcall_err(exec_lua, [[ + vim.opt.undofile = function() + return true + end + ]])) + end) + + it('should allow to set option with array or string value', function() + eq('indent,eol,start', exec_lua [[ + vim.opt.backspace = {'indent','eol','start'} + return vim.go.backspace + ]]) + eq('indent,eol,start', exec_lua [[ + vim.opt.backspace = 'indent,eol,start' + return vim.go.backspace + ]]) + + matches("Invalid option type 'boolean' for 'backspace'", pcall_err(exec_lua, [[ + vim.opt.backspace = true + ]])) + matches("Invalid option type 'number' for 'backspace'", pcall_err(exec_lua, [[ + vim.opt.backspace = 2 + ]])) + matches("Invalid option type 'function' for 'backspace'", pcall_err(exec_lua, [[ + vim.opt.backspace = function() + return 'indent,eol,start' + end + ]])) + end) + + it('should allow set option with map or string value', function() + eq("eol:~,space:.", exec_lua [[ + vim.opt.listchars = { + eol = "~", + space = ".", + } + return vim.o.listchars + ]]) + eq("eol:~,space:.,tab:>~", exec_lua [[ + vim.opt.listchars = "eol:~,space:.,tab:>~" + return vim.o.listchars + ]]) + + matches("Invalid option type 'boolean' for 'listchars'", pcall_err(exec_lua, [[ + vim.opt.listchars = true + ]])) + matches("Invalid option type 'number' for 'listchars'", pcall_err(exec_lua, [[ + vim.opt.listchars = 2 + ]])) + matches("Invalid option type 'function' for 'listchars'", pcall_err(exec_lua, [[ + vim.opt.listchars = function() + return "eol:~,space:.,tab:>~" + end + ]])) + end) + + it('should allow set option with set or string value', function() + local ww = exec_lua [[ + vim.opt.whichwrap = { + b = true, + s = 1, + } + return vim.go.whichwrap + ]] + + eq(ww, "b,s") + eq("b,s,<,>,[,]", exec_lua [[ + vim.opt.whichwrap = "b,s,<,>,[,]" + return vim.go.whichwrap + ]]) + + matches("Invalid option type 'boolean' for 'whichwrap'", pcall_err(exec_lua, [[ + vim.opt.whichwrap = true + ]])) + matches("Invalid option type 'number' for 'whichwrap'", pcall_err(exec_lua, [[ + vim.opt.whichwrap = 2 + ]])) + matches("Invalid option type 'function' for 'whichwrap'", pcall_err(exec_lua, [[ + vim.opt.whichwrap = function() + return "b,s,<,>,[,]" + end + ]])) + end) + end) + + -- isfname=a,b,c,,,d,e,f + it('can handle isfname ,,,', function() + local result = exec_lua [[ + vim.opt.isfname = "a,b,,,c" + return { vim.opt.isfname:get(), vim.api.nvim_get_option('isfname') } + ]] + + eq({{",", "a", "b", "c"}, "a,b,,,c"}, result) + end) + + -- isfname=a,b,c,^,,def + it('can handle isfname ,^,,', function() + local result = exec_lua [[ + vim.opt.isfname = "a,b,^,,c" + return { vim.opt.isfname:get(), vim.api.nvim_get_option('isfname') } + ]] + + eq({{"^,", "a", "b", "c"}, "a,b,^,,c"}, result) + end) + + + + describe('https://github.com/neovim/neovim/issues/14828', function() + it('gives empty list when item is empty:array', function() + eq({}, exec_lua [[ + vim.cmd("set wildignore=") + return vim.opt.wildignore:get() + ]]) + + eq({}, exec_lua [[ + vim.opt.wildignore = {} + return vim.opt.wildignore:get() + ]]) + end) + + it('gives empty list when item is empty:set', function() + eq({}, exec_lua [[ + vim.cmd("set formatoptions=") + return vim.opt.formatoptions:get() + ]]) + + eq({}, exec_lua [[ + vim.opt.formatoptions = {} + return vim.opt.formatoptions:get() + ]]) + end) + + it('does not append to empty item', function() + eq({"*.foo", "*.bar"}, exec_lua [[ + vim.opt.wildignore = {} + vim.opt.wildignore:append { "*.foo", "*.bar" } + + return vim.opt.wildignore:get() + ]]) + end) + + it('does not prepend to empty item', function() + eq({"*.foo", "*.bar"}, exec_lua [[ + vim.opt.wildignore = {} + vim.opt.wildignore:prepend { "*.foo", "*.bar" } + + return vim.opt.wildignore:get() + ]]) + end) + + it('append to empty set', function() + eq({ t = true }, exec_lua [[ + vim.opt.formatoptions = {} + vim.opt.formatoptions:append("t") + + return vim.opt.formatoptions:get() + ]]) + end) + + it('prepend to empty set', function() + eq({ t = true }, exec_lua [[ + vim.opt.formatoptions = {} + vim.opt.formatoptions:prepend("t") + + return vim.opt.formatoptions:get() + ]]) + end) + end) + end) -- vim.opt it('vim.cmd', function() exec_lua [[ @@ -1609,7 +1882,7 @@ describe('lua stdlib', function() end) it('vim.region', function() - helpers.insert(helpers.dedent( [[ + insert(helpers.dedent( [[ text tααt tααt text text tαxt txtα tex text tαxt tαxt @@ -1617,65 +1890,67 @@ describe('lua stdlib', function() eq({5,15}, exec_lua[[ return vim.region(0,{1,5},{1,14},'v',true)[1] ]]) end) - describe('vim.execute_on_keystroke', function() - it('should keep track of keystrokes', function() - helpers.insert([[hello world ]]) + describe('vim.on_key', function() + it('tracks keystrokes', function() + insert([[hello world ]]) exec_lua [[ - KeysPressed = {} + keys = {} - vim.register_keystroke_callback(function(buf) + vim.on_key(function(buf) if buf:byte() == 27 then buf = "<ESC>" end - table.insert(KeysPressed, buf) + table.insert(keys, buf) end) ]] - helpers.insert([[next 🤦 lines Ã¥ ]]) + insert([[next 🤦 lines Ã¥ ]]) -- It has escape in the keys pressed - eq('inext 🤦 lines Ã¥ <ESC>', exec_lua [[return table.concat(KeysPressed, '')]]) + eq('inext 🤦 lines Ã¥ <ESC>', exec_lua [[return table.concat(keys, '')]]) end) - it('should allow removing trackers.', function() - helpers.insert([[hello world]]) + it('allows removing on_key listeners', function() + insert([[hello world]]) exec_lua [[ - KeysPressed = {} + keys = {} - return vim.register_keystroke_callback(function(buf) + return vim.on_key(function(buf) if buf:byte() == 27 then buf = "<ESC>" end - table.insert(KeysPressed, buf) + table.insert(keys, buf) end, vim.api.nvim_create_namespace("logger")) ]] - helpers.insert([[next lines]]) + insert([[next lines]]) - exec_lua("vim.register_keystroke_callback(nil, vim.api.nvim_create_namespace('logger'))") + eq(1, exec_lua('return vim.on_key()')) + exec_lua("vim.on_key(nil, vim.api.nvim_create_namespace('logger'))") + eq(0, exec_lua('return vim.on_key()')) - helpers.insert([[more lines]]) + insert([[more lines]]) -- It has escape in the keys pressed - eq('inext lines<ESC>', exec_lua [[return table.concat(KeysPressed, '')]]) + eq('inext lines<ESC>', exec_lua [[return table.concat(keys, '')]]) end) - it('should not call functions that error again.', function() - helpers.insert([[hello world]]) + it('skips any function that caused an error', function() + insert([[hello world]]) exec_lua [[ - KeysPressed = {} + keys = {} - return vim.register_keystroke_callback(function(buf) + return vim.on_key(function(buf) if buf:byte() == 27 then buf = "<ESC>" end - table.insert(KeysPressed, buf) + table.insert(keys, buf) if buf == 'l' then error("Dumb Error") @@ -1683,35 +1958,30 @@ describe('lua stdlib', function() end) ]] - helpers.insert([[next lines]]) - helpers.insert([[more lines]]) + insert([[next lines]]) + insert([[more lines]]) -- Only the first letter gets added. After that we remove the callback - eq('inext l', exec_lua [[ return table.concat(KeysPressed, '') ]]) + eq('inext l', exec_lua [[ return table.concat(keys, '') ]]) end) - it('should process mapped keys, not unmapped keys', function() + it('processes mapped keys, not unmapped keys', function() exec_lua [[ - KeysPressed = {} + keys = {} vim.cmd("inoremap hello world") - vim.register_keystroke_callback(function(buf) + vim.on_key(function(buf) if buf:byte() == 27 then buf = "<ESC>" end - table.insert(KeysPressed, buf) + table.insert(keys, buf) end) ]] + insert("hello") - helpers.insert("hello") - - local next_status = exec_lua [[ - return table.concat(KeysPressed, '') - ]] - - eq("iworld<ESC>", next_status) + eq('iworld<ESC>', exec_lua[[return table.concat(keys, '')]]) end) end) @@ -1907,6 +2177,24 @@ describe('lua stdlib', function() end) end) + describe('vim.schedule_wrap', function() + it('preserves argument lists', function() + exec_lua [[ + local fun = vim.schedule_wrap(function(kling, klang, klonk) + vim.rpcnotify(1, 'mayday_mayday', {a=kling, b=klang, c=klonk}) + end) + fun("BOB", nil, "MIKE") + ]] + eq({'notification', 'mayday_mayday', {{a='BOB', c='MIKE'}}}, next_msg()) + + -- let's gooooo + exec_lua [[ + vim.schedule_wrap(function(...) vim.rpcnotify(1, 'boogalo', select('#', ...)) end)(nil,nil,nil,nil) + ]] + eq({'notification', 'boogalo', {4}}, next_msg()) + end) + end) + describe('vim.api.nvim_buf_call', function() it('can access buf options', function() local buf1 = meths.get_current_buf() diff --git a/test/functional/lua/xdiff_spec.lua b/test/functional/lua/xdiff_spec.lua new file mode 100644 index 0000000000..4f28f84c01 --- /dev/null +++ b/test/functional/lua/xdiff_spec.lua @@ -0,0 +1,112 @@ +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 + +describe('xdiff bindings', function() + before_each(function() + clear() + end) + + describe('can diff text', function() + before_each(function() + exec_lua[[ + a1 = 'Hello\n' + b1 = 'Helli\n' + + a2 = 'Hello\nbye\nfoo\n' + b2 = 'Helli\nbye\nbar\nbaz\n' + ]] + end) + + it('with no callback', function() + + eq( + table.concat({ + '@@ -1 +1 @@', + '-Hello', + '+Helli', + '' + }, '\n'), + exec_lua("return vim.diff(a1, b1)") + ) + + eq( + table.concat({ + '@@ -1 +1 @@', + '-Hello', + '+Helli', + '@@ -3 +3,2 @@', + '-foo', + '+bar', + '+baz', + '' + }, '\n'), + exec_lua("return vim.diff(a2, b2)") + ) + + end) + + it('with callback', function() + exec_lua([[on_hunk = function(sa, ca, sb, cb) + exp[#exp+1] = {sa, ca, sb, cb} + end]]) + + eq({{1, 1, 1, 1}}, exec_lua[[ + exp = {} + assert(vim.diff(a1, b1, {on_hunk = on_hunk}) == nil) + return exp + ]]) + + eq({{1, 1, 1, 1}, {3, 1, 3, 2}}, exec_lua[[ + exp = {} + assert(vim.diff(a2, b2, {on_hunk = on_hunk}) == nil) + return exp + ]]) + + -- gives higher precedence to on_hunk over result_type + eq({{1, 1, 1, 1}, {3, 1, 3, 2}}, exec_lua[[ + exp = {} + assert(vim.diff(a2, b2, {on_hunk = on_hunk, result_type='indices'}) == nil) + return exp + ]]) + end) + + it('with error callback', function() + exec_lua([[on_hunk = function(sa, ca, sb, cb) + error('ERROR1') + end]]) + + eq([[Error executing lua: [string "<nvim>"]:0: error running function on_hunk: [string "<nvim>"]:0: ERROR1]], + pcall_err(exec_lua, [[vim.diff(a1, b1, {on_hunk = on_hunk})]])) + end) + + it('with hunk_lines', function() + eq({{1, 1, 1, 1}}, + exec_lua([[return vim.diff(a1, b1, {result_type = 'indices'})]])) + + eq({{1, 1, 1, 1}, {3, 1, 3, 2}}, + exec_lua([[return vim.diff(a2, b2, {result_type = 'indices'})]])) + end) + + end) + + it('can handle bad args', function() + eq([[Error executing lua: [string "<nvim>"]:0: Expected at least 2 arguments]], + pcall_err(exec_lua, [[vim.diff('a')]])) + + eq([[Error executing lua: [string "<nvim>"]:0: bad argument #1 to 'diff' (expected string)]], + pcall_err(exec_lua, [[vim.diff(1, 2)]])) + + eq([[Error executing lua: [string "<nvim>"]:0: bad argument #3 to 'diff' (expected table)]], + pcall_err(exec_lua, [[vim.diff('a', 'b', true)]])) + + eq([[Error executing lua: [string "<nvim>"]:0: unexpected key: bad_key]], + pcall_err(exec_lua, [[vim.diff('a', 'b', { bad_key = true })]])) + + eq([[Error executing lua: [string "<nvim>"]:0: on_hunk is not a function]], + pcall_err(exec_lua, [[vim.diff('a', 'b', { on_hunk = true })]])) + + end) +end) diff --git a/test/functional/normal/K_spec.lua b/test/functional/normal/K_spec.lua index 174313d80e..40f36491e4 100644 --- a/test/functional/normal/K_spec.lua +++ b/test/functional/normal/K_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) -local eq, clear, eval, feed = - helpers.eq, helpers.clear, helpers.eval, helpers.feed +local eq, clear, eval, feed, retry = + helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.retry describe('K', function() local test_file = 'K_spec_out' @@ -31,7 +31,7 @@ describe('K', function() -- K on the text "K_spec_out" resolves to `!echo fnord >> K_spec_out`. feed('i'..test_file..'<ESC>K') - feed('<CR>') -- Press ENTER + retry(nil, nil, function() eq(1, eval('filereadable("'..test_file..'")')) end) eq({'fnord'}, eval("readfile('"..test_file.."')")) end) diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index eb5e284385..6620c9acef 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive local meths = helpers.meths local command = helpers.command local clear = helpers.clear @@ -354,13 +355,13 @@ describe('XDG-based defaults', function() .. ',' .. root_path .. ('/a'):rep(2048) .. '/nvim/after' .. ',' .. root_path .. ('/x'):rep(4096) .. '/nvim/after' ):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/')) - eq('.,' .. root_path .. ('/X'):rep(4096).. '/' .. data_dir .. '/backup', + eq('.,' .. root_path .. ('/X'):rep(4096).. '/' .. data_dir .. '/backup//', (meths.get_option('backupdir'):gsub('\\', '/'))) eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/swap//', (meths.get_option('directory')):gsub('\\', '/')) - eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/undo', + eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/undo//', (meths.get_option('undodir')):gsub('\\', '/')) - eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/view', + eq(root_path .. ('/X'):rep(4096) .. '/' .. data_dir .. '/view//', (meths.get_option('viewdir')):gsub('\\', '/')) end) end) @@ -404,13 +405,13 @@ describe('XDG-based defaults', function() .. ',$XDG_DATA_DIRS/nvim/after' .. ',$XDG_DATA_HOME/nvim/after' ):gsub('\\', '/')), (meths.get_option('runtimepath')):gsub('\\', '/')) - eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup'), + eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup//'), meths.get_option('backupdir'):gsub('\\', '/')) eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/swap//'), meths.get_option('directory'):gsub('\\', '/')) - eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo'), + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo//'), meths.get_option('undodir'):gsub('\\', '/')) - eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view'), + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view//'), meths.get_option('viewdir'):gsub('\\', '/')) meths.command('set all&') eq(('$XDG_DATA_HOME/nvim' @@ -424,13 +425,13 @@ describe('XDG-based defaults', function() .. ',$XDG_DATA_DIRS/nvim/after' .. ',$XDG_DATA_HOME/nvim/after' ):gsub('\\', '/'), (meths.get_option('runtimepath')):gsub('\\', '/')) - eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup'), + eq(('.,$XDG_CONFIG_HOME/' .. data_dir .. '/backup//'), meths.get_option('backupdir'):gsub('\\', '/')) eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/swap//'), meths.get_option('directory'):gsub('\\', '/')) - eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo'), + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/undo//'), meths.get_option('undodir'):gsub('\\', '/')) - eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view'), + eq(('$XDG_CONFIG_HOME/' .. data_dir .. '/view//'), meths.get_option('viewdir'):gsub('\\', '/')) end) end) @@ -483,13 +484,13 @@ describe('XDG-based defaults', function() .. ',\\,-\\,-\\,' .. path_sep ..'nvim' .. path_sep ..'after' .. ',\\, \\, \\,' .. path_sep ..'nvim' .. path_sep ..'after' ), meths.get_option('runtimepath')) - eq('.,\\,=\\,=\\,' .. path_sep .. data_dir .. '' .. path_sep ..'backup', + eq('.,\\,=\\,=\\,' .. path_sep .. data_dir .. '' .. path_sep ..'backup' .. (path_sep):rep(2), meths.get_option('backupdir')) eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'swap' .. (path_sep):rep(2), meths.get_option('directory')) - eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'undo', + eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'undo' .. (path_sep):rep(2), meths.get_option('undodir')) - eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'view', + eq('\\,=\\,=\\,' .. path_sep ..'' .. data_dir .. '' .. path_sep ..'view' .. (path_sep):rep(2), meths.get_option('viewdir')) end) end) @@ -510,8 +511,7 @@ describe('stdpath()', function() eq(datadir, funcs.fnamemodify(funcs.stdpath('data'), ':t')) eq('table', type(funcs.stdpath('config_dirs'))) eq('table', type(funcs.stdpath('data_dirs'))) - -- Check for crash. #8393 - eq(2, eval('1+1')) + assert_alive() -- Check for crash. #8393 end) context('returns a String', function() diff --git a/test/functional/options/tabstop_spec.lua b/test/functional/options/tabstop_spec.lua index dc3ba38438..e34f678650 100644 --- a/test/functional/options/tabstop_spec.lua +++ b/test/functional/options/tabstop_spec.lua @@ -1,9 +1,8 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local clear = helpers.clear local feed = helpers.feed -local eq = helpers.eq -local eval = helpers.eval describe("'tabstop' option", function() before_each(function() @@ -18,6 +17,6 @@ describe("'tabstop' option", function() -- Set 'tabstop' to a very high value. -- Use feed(), not command(), to provoke crash. feed(':set tabstop=3000000000<CR>') - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua new file mode 100644 index 0000000000..e48a0ad260 --- /dev/null +++ b/test/functional/plugin/lsp/codelens_spec.lua @@ -0,0 +1,62 @@ +local helpers = require('test.functional.helpers')(after_each) + +local exec_lua = helpers.exec_lua +local eq = helpers.eq + +describe('vim.lsp.codelens', function() + before_each(function() + helpers.clear() + exec_lua('require("vim.lsp")') + end) + after_each(helpers.clear) + + it('on_codelens_stores_and_displays_lenses', function() + local fake_uri = "file:///fake/uri" + local bufnr = exec_lua([[ + fake_uri = ... + local bufnr = vim.uri_to_bufnr(fake_uri) + local lines = {'So', 'many', 'lines'} + vim.fn.bufload(bufnr) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + return bufnr + ]], fake_uri) + + exec_lua([[ + local bufnr = ... + local lenses = { + { + range = { + start = { line = 0, character = 0, }, + ['end'] = { line = 0, character = 0 } + }, + command = { title = 'Lens1', command = 'Dummy' } + }, + } + vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr}) + ]], bufnr) + + local stored_lenses = exec_lua('return vim.lsp.codelens.get(...)', bufnr) + local expected = { + { + range = { + start = { line = 0, character = 0 }, + ['end'] = { line = 0, character = 0 } + }, + command = { + title = 'Lens1', + command = 'Dummy', + }, + }, + } + eq(expected, stored_lenses) + + local virtual_text_chunks = exec_lua([[ + local bufnr = ... + local ns = vim.lsp.codelens.__namespaces[1] + local extmarks = vim.api.nvim_buf_get_extmarks(bufnr, ns, 0, -1, {}) + return vim.api.nvim_buf_get_extmark_by_id(bufnr, ns, extmarks[1][1], { details = true })[3].virt_text + ]], bufnr) + + eq({[1] = {'Lens1', 'LspCodeLens'}}, virtual_text_chunks) + end) +end) diff --git a/test/functional/plugin/lsp/diagnostic_spec.lua b/test/functional/plugin/lsp/diagnostic_spec.lua index 962028e7e1..e4fe1c1992 100644 --- a/test/functional/plugin/lsp/diagnostic_spec.lua +++ b/test/functional/plugin/lsp/diagnostic_spec.lua @@ -49,7 +49,7 @@ describe('vim.lsp.diagnostic', function() end ]] - fake_uri = "file://fake/uri" + fake_uri = "file:///fake/uri" exec_lua([[ fake_uri = ... @@ -205,8 +205,8 @@ describe('vim.lsp.diagnostic', function() make_warning("Warning 1", 2, 1, 2, 5), } - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_1_diags }, 1) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_2_diags }, 2) + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_1_diags }, {client_id=1}) + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_2_diags }, {client_id=2}) return { vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1), vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2), @@ -241,6 +241,38 @@ describe('vim.lsp.diagnostic', function() ]])) end) + it('should not display diagnostics when disabled', function() + eq({0, 2}, exec_lua [[ + local server_1_diags = { + make_error("Error 1", 1, 1, 1, 5), + make_warning("Warning on Server 1", 2, 1, 2, 5), + } + local server_2_diags = { + make_warning("Warning 1", 2, 1, 2, 5), + } + + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_1_diags }, {client_id=1}) + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_2_diags }, {client_id=2}) + + vim.lsp.diagnostic.disable(diagnostic_bufnr, 1) + + return { + count_of_extmarks_for_client(diagnostic_bufnr, 1), + count_of_extmarks_for_client(diagnostic_bufnr, 2), + } + ]]) + + eq({4, 0}, exec_lua [[ + vim.lsp.diagnostic.enable(diagnostic_bufnr, 1) + vim.lsp.diagnostic.disable(diagnostic_bufnr, 2) + + return { + count_of_extmarks_for_client(diagnostic_bufnr, 1), + count_of_extmarks_for_client(diagnostic_bufnr, 2), + } + ]]) + end) + describe('reset', function() it('diagnostic count is 0 and displayed diagnostics are 0 after call', function() -- 1 Error (1) @@ -258,8 +290,8 @@ describe('vim.lsp.diagnostic', function() make_warning("Warning 1", 2, 1, 2, 5), } - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_1_diags }, 1) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { uri = fake_uri, diagnostics = server_2_diags }, 2) + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_1_diags }, {client_id=1}) + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = server_2_diags }, {client_id=2}) return { vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Error", 1), vim.lsp.diagnostic.get_count(diagnostic_bufnr, "Warning", 2), @@ -435,14 +467,14 @@ describe('vim.lsp.diagnostic', function() it('should return all diagnostics when no severity is supplied', function() eq(2, exec_lua [[ - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = { make_error("Error 1", 1, 1, 1, 5), make_warning("Warning on Server 1", 1, 1, 2, 5), make_error("Error On Other Line", 2, 1, 1, 5), } - }, 1) + }, {client_id=1}) return #vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1) ]]) @@ -450,7 +482,7 @@ describe('vim.lsp.diagnostic', function() it('should return only requested diagnostics when severity_limit is supplied', function() eq(2, exec_lua [[ - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = { make_error("Error 1", 1, 1, 1, 5), @@ -458,7 +490,7 @@ describe('vim.lsp.diagnostic', function() make_information("Ignored information", 1, 1, 2, 5), make_error("Error On Other Line", 2, 1, 1, 5), } - }, 1) + }, {client_id=1}) return #vim.lsp.diagnostic.get_line_diagnostics(diagnostic_bufnr, 1, { severity_limit = "Warning" }) ]]) @@ -470,12 +502,12 @@ describe('vim.lsp.diagnostic', function() exec_lua [[ vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { virtual_text = function() return true end, - })(nil, nil, { + })(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -487,12 +519,12 @@ describe('vim.lsp.diagnostic', function() exec_lua [[ vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { virtual_text = function() return false end, - })(nil, nil, { + })(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -509,12 +541,12 @@ describe('vim.lsp.diagnostic', function() exec_lua [[ vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { update_in_insert = false, - })(nil, nil, { + })(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -551,12 +583,12 @@ describe('vim.lsp.diagnostic', function() return SetVirtualTextOriginal(...) end - PublishDiagnostics(nil, nil, { + PublishDiagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -605,12 +637,12 @@ describe('vim.lsp.diagnostic', function() return SetVirtualTextOriginal(...) end - PublishDiagnostics(nil, nil, { + PublishDiagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -647,12 +679,12 @@ describe('vim.lsp.diagnostic', function() exec_lua [[ vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { update_in_insert = true, - })(nil, nil, { + })(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) ]] @@ -677,12 +709,12 @@ describe('vim.lsp.diagnostic', function() }, }) - PublishDiagnostics(nil, nil, { + PublishDiagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) return vim.api.nvim_buf_get_extmarks( @@ -714,12 +746,12 @@ describe('vim.lsp.diagnostic', function() end, }) - PublishDiagnostics(nil, nil, { + PublishDiagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) return vim.api.nvim_buf_get_extmarks( @@ -747,12 +779,12 @@ describe('vim.lsp.diagnostic', function() }, }) - PublishDiagnostics(nil, nil, { + PublishDiagnostics(nil, { uri = fake_uri, diagnostics = { make_warning('Delayed Diagnostic', 4, 4, 4, 4), } - }, 1 + }, {client_id=1} ) return count_of_extmarks_for_client(diagnostic_bufnr, 1) @@ -838,10 +870,10 @@ describe('vim.lsp.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = diagnostics - }, 1 + }, {client_id=1} ) vim.lsp.diagnostic.set_signs(diagnostics, diagnostic_bufnr, 1) @@ -863,13 +895,13 @@ describe('vim.lsp.diagnostic', function() local loc_list = exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Farther Diagnostic', 4, 4, 4, 4), make_error('Lower Diagnostic', 1, 1, 1, 1), } - }, 1 + }, {client_id=1} ) vim.lsp.diagnostic.set_loclist() @@ -884,20 +916,20 @@ describe('vim.lsp.diagnostic', function() local loc_list = exec_lua [[ vim.api.nvim_win_set_buf(0, diagnostic_bufnr) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = { make_error('Lower Diagnostic', 1, 1, 1, 1), } - }, 1 + }, {client_id=1} ) - vim.lsp.diagnostic.on_publish_diagnostics(nil, nil, { + vim.lsp.diagnostic.on_publish_diagnostics(nil, { uri = fake_uri, diagnostics = { make_warning('Farther Diagnostic', 4, 4, 4, 4), } - }, 2 + }, {client_id=2} ) vim.lsp.diagnostic.set_loclist() diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 663271deab..a9ea26343d 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -6,6 +6,7 @@ local buf_lines = helpers.buf_lines local dedent = helpers.dedent local exec_lua = helpers.exec_lua local eq = helpers.eq +local matches = helpers.matches local pcall_err = helpers.pcall_err local pesc = helpers.pesc local insert = helpers.insert @@ -14,6 +15,7 @@ local retry = helpers.retry local NIL = helpers.NIL local read_file = require('test.helpers').read_file local write_file = require('test.helpers').write_file +local isCI = helpers.isCI -- Use these to get access to a coroutine so that I can run async tests and use -- yield. @@ -105,8 +107,8 @@ local function test_rpc_server(config) return NIL end if method == 'handler' then - if config.on_callback then - config.on_callback(unpack(args)) + if config.on_handler then + config.on_handler(unpack(args)) end end return NIL @@ -215,8 +217,8 @@ describe('LSP', function() end) it('should run correctly', function() - local expected_callbacks = { - {NIL, "test", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="test", client_id=1}}; } test_rpc_server { test_name = "basic_init"; @@ -232,16 +234,16 @@ describe('LSP', function() eq(0, signal, "exit signal", fake_lsp_logfile) end; -- Note that NIL must be used here. - -- on_callback(err, method, result, client_id) - on_callback = function(...) - eq(table.remove(expected_callbacks), {...}) + -- on_handler(err, method, result, client_id) + on_handler = function(...) + eq(table.remove(expected_handlers), {...}) end; } end) it('should fail', function() - local expected_callbacks = { - {NIL, "test", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="test", client_id=1}}; } test_rpc_server { test_name = "basic_init"; @@ -255,20 +257,22 @@ describe('LSP', function() assert_log(pesc([[assert_eq failed: left == "\"shutdown\"", right == "\"test\""]]), fake_lsp_logfile) end; - on_callback = function(...) - eq(table.remove(expected_callbacks), {...}, "expected callback") + on_handler = function(...) + eq(table.remove(expected_handlers), {...}, "expected handler") end; } end) it('should succeed with manual shutdown', function() - if 'openbsd' == helpers.uname() then - pending('hangs the build on openbsd #14028, re-enable with freeze timeout #14204') + if isCI() then + pending('hangs the build on CI #14028, re-enable with freeze timeout #14204') + return + elseif helpers.skip_fragile(pending) then return end - local expected_callbacks = { - {NIL, "shutdown", {}, 1, NIL}; - {NIL, "test", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="test", client_id=1}}; } test_rpc_server { test_name = "basic_init"; @@ -282,20 +286,20 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(...) - eq(table.remove(expected_callbacks), {...}, "expected callback") + on_handler = function(...) + eq(table.remove(expected_handlers), {...}, "expected handler") end; } end) it('client should return settings via workspace/configuration handler', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "workspace/configuration", { items = { + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, { items = { { section = "testSetting1" }; { section = "testSetting2" }; - }}, 1}; - {NIL, "start", {}, 1}; + }}, { method="workspace/configuration", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -307,9 +311,9 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(err, method, params, client_id) - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") - if method == 'start' then + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'start' then exec_lua([=[ local client = vim.lsp.get_client_by_id(TEST_RPC_CLIENT_ID) client.config.settings = { @@ -317,13 +321,13 @@ describe('LSP', function() testSetting2 = false; }]=]) end - if method == 'workspace/configuration' then - local result = exec_lua([=[ + if ctx.method == 'workspace/configuration' then + local server_result = exec_lua([=[ local method, params = ... - return require'vim.lsp.handlers'['workspace/configuration'](err, method, params, TEST_RPC_CLIENT_ID)]=], method, params) - client.notify('workspace/configuration', result) + return require'vim.lsp.handlers'['workspace/configuration'](err, params, {method=method, client_id=TEST_RPC_CLIENT_ID})]=], ctx.method, result) + client.notify('workspace/configuration', server_result) end - if method == 'shutdown' then + if ctx.method == 'shutdown' then client.stop() end end; @@ -333,19 +337,19 @@ describe('LSP', function() clear_notrace() fake_lsp_server_setup('workspace/configuration no settings') eq({ NIL, NIL, }, exec_lua [[ - local params = { + local result = { items = { {section = 'foo'}, {section = 'bar'}, } } - return vim.lsp.handlers['workspace/configuration'](nil, nil, params, TEST_RPC_CLIENT_ID) + return vim.lsp.handlers['workspace/configuration'](nil, result, {client_id=TEST_RPC_CLIENT_ID}) ]]) end) it('should verify capabilities sent', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; } test_rpc_server { test_name = "basic_check_capabilities"; @@ -361,15 +365,15 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(...) - eq(table.remove(expected_callbacks), {...}, "expected callback") + on_handler = function(...) + eq(table.remove(expected_handlers), {...}, "expected handler") end; } end) it('client.supports_methods() should validate capabilities', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; } test_rpc_server { test_name = "capabilities_for_client_supports_method"; @@ -395,15 +399,15 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(...) - eq(table.remove(expected_callbacks), {...}, "expected callback") + on_handler = function(...) + eq(table.remove(expected_handlers), {...}, "expected handler") end; } end) it('should call unsupported_method when trying to call an unsupported method', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; } test_rpc_server { test_name = "capabilities_for_client_supports_method"; @@ -411,8 +415,9 @@ describe('LSP', function() exec_lua([=[ BUFFER = vim.api.nvim_get_current_buf() lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID) - vim.lsp.handlers['textDocument/typeDefinition'] = function(err, method) - vim.lsp._last_lsp_callback = { err = err; method = method } + vim.lsp.handlers['textDocument/typeDefinition'] = function(err, result, ctx) + local method = ctx.method + vim.lsp._last_lsp_handler = { err = err; method = method } end vim.lsp._unsupported_method = function(method) vim.lsp._last_unsupported_method = method @@ -425,7 +430,7 @@ describe('LSP', function() client.stop() local method = exec_lua("return vim.lsp._last_unsupported_method") eq("textDocument/typeDefinition", method) - local lsp_cb_call = exec_lua("return vim.lsp._last_lsp_callback") + local lsp_cb_call = exec_lua("return vim.lsp._last_lsp_handler") eq("fake-error", lsp_cb_call.err) eq("textDocument/typeDefinition", lsp_cb_call.method) exec_lua [[ @@ -436,22 +441,22 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(...) - eq(table.remove(expected_callbacks), {...}, "expected callback") + on_handler = function(...) + eq(table.remove(expected_handlers), {...}, "expected handler") end; } end) it('shouldn\'t call unsupported_method when no client and trying to call an unsupported method', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; } test_rpc_server { test_name = "capabilities_for_client_supports_method"; on_setup = function() exec_lua([=[ vim.lsp.handlers['textDocument/typeDefinition'] = function(err, method) - vim.lsp._last_lsp_callback = { err = err; method = method } + vim.lsp._last_lsp_handler = { err = err; method = method } end vim.lsp._unsupported_method = function(method) vim.lsp._last_unsupported_method = method @@ -463,22 +468,22 @@ describe('LSP', function() on_init = function(client) client.stop() eq(NIL, exec_lua("return vim.lsp._last_unsupported_method")) - eq(NIL, exec_lua("return vim.lsp._last_lsp_callback")) + eq(NIL, exec_lua("return vim.lsp._last_lsp_handler")) end; on_exit = function(code, signal) eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(...) - eq(table.remove(expected_callbacks), {...}, "expected callback") + on_handler = function(...) + eq(table.remove(expected_handlers), {...}, "expected handler") end; } end) it('should not send didOpen if the buffer closes before init', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; } local client test_rpc_server { @@ -509,9 +514,9 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(err, method, params, client_id) - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") - if method == 'finish' then + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -519,10 +524,10 @@ describe('LSP', function() end) it('should check the body sent attaching before init', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -552,12 +557,12 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then client.notify('finish') end - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -565,10 +570,10 @@ describe('LSP', function() end) it('should check the body sent attaching after init', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -595,12 +600,12 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then client.notify('finish') end - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -608,10 +613,10 @@ describe('LSP', function() end) it('should check the body and didChange full', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -638,8 +643,8 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then exec_lua [[ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { "boop"; @@ -647,8 +652,8 @@ describe('LSP', function() ]] client.notify('finish') end - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -656,10 +661,10 @@ describe('LSP', function() end) it('should check the body and didChange full with noeol', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -687,8 +692,8 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then exec_lua [[ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { "boop"; @@ -696,8 +701,8 @@ describe('LSP', function() ]] client.notify('finish') end - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -705,10 +710,10 @@ describe('LSP', function() end) it('should check the body and didChange incremental', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -736,8 +741,8 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then exec_lua [[ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { "123boop"; @@ -745,8 +750,8 @@ describe('LSP', function() ]] client.notify('finish') end - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -755,10 +760,10 @@ describe('LSP', function() -- TODO(askhan) we don't support full for now, so we can disable these tests. pending('should check the body and didChange incremental normal mode editing', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -785,13 +790,13 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then helpers.command("normal! 1Go") client.notify('finish') end - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -799,10 +804,10 @@ describe('LSP', function() end) it('should check the body and didChange full with 2 changes', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -829,8 +834,8 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result, ctx) + if ctx.method == 'start' then exec_lua [[ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { "321"; @@ -841,8 +846,8 @@ describe('LSP', function() ]] client.notify('finish') end - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -850,10 +855,10 @@ describe('LSP', function() end) it('should check the body and didChange full lifecycle', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -880,8 +885,8 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_callback = function(err, method, params, client_id) - if method == 'start' then + on_handler = function(err, result,ctx) + if ctx.method == 'start' then exec_lua [[ vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, { "321"; @@ -893,8 +898,8 @@ describe('LSP', function() ]] client.notify('finish') end - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback") - if method == 'finish' then + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then client.stop() end end; @@ -904,10 +909,10 @@ describe('LSP', function() describe("parsing tests", function() it('should handle invalid content-length correctly', function() - local expected_callbacks = { - {NIL, "shutdown", {}, 1}; - {NIL, "finish", {}, 1}; - {NIL, "start", {}, 1}; + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; } local client test_rpc_server { @@ -922,8 +927,50 @@ describe('LSP', function() eq(0, code, "exit code", fake_lsp_logfile) eq(0, signal, "exit signal", fake_lsp_logfile) end; - on_handler = function(err, method, params, client_id) - eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected handler") + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + end; + } + end) + + it('should not trim vim.NIL from the end of a list', function() + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="finish", client_id=1}}; + {NIL,{ + arguments = { "EXTRACT_METHOD", {metadata = {}}, 3, 0, 6123, NIL }, + command = "refactor.perform", + title = "EXTRACT_METHOD" + }, {method="workspace/executeCommand", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; + } + local client + test_rpc_server { + test_name = "decode_nil"; + on_setup = function() + exec_lua [[ + BUFFER = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, { + "testing"; + "123"; + }) + ]] + end; + on_init = function(_client) + client = _client + exec_lua [[ + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)) + ]] + end; + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end; + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'finish' then + client.stop() + end end; } end) @@ -1105,14 +1152,14 @@ describe('LSP', function() make_edit(0, 0, 0, 3, "First ↥ 🤦 🦄") }, textDocument = { - uri = "file://fake/uri"; + uri = "file:///fake/uri"; version = editVersion } } end before_each(function() target_bufnr = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") + local bufnr = vim.uri_to_bufnr("file:///fake/uri") local lines = {"1st line of text", "2nd line of è¯text"} vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) return bufnr @@ -1179,7 +1226,7 @@ describe('LSP', function() label = nil; edit = {}; } - return vim.lsp.handlers['workspace/applyEdit'](nil, nil, apply_edit) + return vim.lsp.handlers['workspace/applyEdit'](nil, apply_edit) ]]) end) end) @@ -1192,7 +1239,7 @@ describe('LSP', function() make_edit(row, 0, row, 1000, new_line) }, textDocument = { - uri = "file://fake/uri"; + uri = "file:///fake/uri"; version = editVersion } } @@ -1210,7 +1257,7 @@ describe('LSP', function() before_each(function() local ret = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") + local bufnr = vim.uri_to_bufnr("file:///fake/uri") local lines = { "Original Line #1", "Original Line #2" @@ -1490,19 +1537,19 @@ describe('LSP', function() it('Convert Location[] to items', function() local expected = { { - filename = 'fake/uri', + filename = '/fake/uri', lnum = 1, col = 3, text = 'testing' }, } local actual = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") + local bufnr = vim.uri_to_bufnr("file:///fake/uri") local lines = {"testing", "123"} vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) local locations = { { - uri = 'file://fake/uri', + uri = 'file:///fake/uri', range = { start = { line = 0, character = 2 }, ['end'] = { line = 0, character = 3 }, @@ -1516,14 +1563,14 @@ describe('LSP', function() it('Convert LocationLink[] to items', function() local expected = { { - filename = 'fake/uri', + filename = '/fake/uri', lnum = 1, col = 3, text = 'testing' }, } local actual = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") + local bufnr = vim.uri_to_bufnr("file:///fake/uri") local lines = {"testing", "123"} vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) local locations = { @@ -1737,14 +1784,14 @@ describe('LSP', function() local expected = { { col = 1, - filename = 'test_a', + filename = '/test_a', kind = 'File', lnum = 2, text = '[File] TestA' }, { col = 1, - filename = 'test_b', + filename = '/test_b', kind = 'Module', lnum = 4, text = '[Module] TestB' @@ -1767,7 +1814,7 @@ describe('LSP', function() line = 2 } }, - uri = "file://test_a" + uri = "file:///test_a" }, contanerName = "TestAContainer" }, @@ -1786,7 +1833,7 @@ describe('LSP', function() line = 4 } }, - uri = "file://test_b" + uri = "file:///test_b" }, contanerName = "TestBContainer" } @@ -1825,7 +1872,7 @@ describe('LSP', function() before_each(function() target_bufnr = exec_lua [[ - local bufnr = vim.uri_to_bufnr("file://fake/uri") + local bufnr = vim.uri_to_bufnr("file:///fake/uri") local lines = {"1st line of text", "aÌŠ Ã¥ ɧ æ±‰è¯ â†¥ 🤦 🦄"} vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines) return bufnr @@ -1834,7 +1881,7 @@ describe('LSP', function() local location = function(start_line, start_char, end_line, end_char) return { - uri = "file://fake/uri", + uri = "file:///fake/uri", range = { start = { line = start_line, character = start_char }, ["end"] = { line = end_line, character = end_char }, @@ -1859,7 +1906,7 @@ describe('LSP', function() it('jumps to a LocationLink', function() local pos = jump({ - targetUri = "file://fake/uri", + targetUri = "file:///fake/uri", targetSelectionRange = { start = { line = 0, character = 4 }, ["end"] = { line = 0, character = 4 }, @@ -1893,6 +1940,83 @@ describe('LSP', function() end) end) + describe('lsp.util.make_floating_popup_options', function() + before_each(function() + exec_lua [[ + local bufnr = vim.uri_to_bufnr("file:///fake/uri") + local winheight = vim.fn.winheight(0) + for i = 1, winheight do + vim.api.nvim_buf_set_lines(bufnr, 0, 0, false, {''}) + end + vim.api.nvim_win_set_buf(0, bufnr) + vim.api.nvim_win_set_cursor(0, {winheight, 0}) + ]] + end) + + local function popup_row(opts) + return exec_lua([[ + return vim.lsp.util.make_floating_popup_options(...).row + ]], 2, 2, opts) + end + + local err_pattern = "^Error executing lua: %.%.%./util%.lua:0: invalid floating preview border: .*%. :help vim%.api%.nvim_open_win%(%)$" + + it('calculates default border height correctly', function() + eq(0, popup_row()) + end) + + it('calculates string border height correctly', function() + eq(0, popup_row({border = 'none'})) + eq(-2, popup_row({border = 'single'})) + eq(-2, popup_row({border = 'double'})) + eq(-2, popup_row({border = 'rounded'})) + eq(-2, popup_row({border = 'solid'})) + eq(-1, popup_row({border = 'shadow'})) + end) + + it('error on invalid string border', function() + matches(err_pattern, pcall_err(popup_row, {border = ''})) + matches(err_pattern, pcall_err(popup_row, {border = 'invalid'})) + end) + + it('error on invalid array border length', function() + matches(err_pattern, pcall_err(popup_row, {border = {}})) + matches(err_pattern, pcall_err(popup_row, {border = {'', '', ''}})) + matches(err_pattern, pcall_err(popup_row, {border = {'', '', '', '', ''}})) + end) + + it('error on invalid array border member type', function() + matches(err_pattern, pcall_err(popup_row, {border = {0}})) + end) + + it('calculates 8-array border height correctly', function() + eq(0, popup_row({border = {'', '', '', '', '', '', '', ''}})) + eq(-2, popup_row({border = {'', '~', '', '~', '', '~', '', '~'}})) + eq(-1, popup_row({border = {'', '', '', '~', '', '~', '', ''}})) + eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}}})) + eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}, '', ''}})) + end) + + it('calculates 4-array border height correctly', function() + eq(0, popup_row({border = {'', '', '', ''}})) + eq(-2, popup_row({border = {'', '~', '', '~'}})) + eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}}})) + eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', ''}})) + end) + + it('calculates 2-array border height correctly', function() + eq(0, popup_row({border = {'', ''}})) + eq(-2, popup_row({border = {'', '~'}})) + eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}}})) + end) + + it('calculates 1-array border height correctly', function() + eq(0, popup_row({border = {''}})) + eq(-2, popup_row({border = {'~'}})) + eq(-2, popup_row({border = {{'~', 'NormalFloat'}}})) + end) + end) + describe('lsp.util._make_floating_popup_size', function() before_each(function() exec_lua [[ contents = @@ -1911,6 +2035,12 @@ describe('LSP', function() end) end) + describe('lsp.util.trim.trim_empty_lines', function() + it('properly trims empty lines', function() + eq({{"foo", "bar"}}, exec_lua[[ return vim.lsp.util.trim_empty_lines({{ "foo", "bar" }, nil}) ]]) + end) + end) + describe('lsp.util.get_effective_tabstop', function() local function test_tabstop(tabsize, softtabstop) exec_lua(string.format([[ @@ -1929,7 +2059,7 @@ describe('LSP', function() describe('vim.lsp.buf.outgoing_calls', function() it('does nothing for an empty response', function() local qflist_count = exec_lua([=[ - require'vim.lsp.handlers'['callHierarchy/outgoingCalls']() + require'vim.lsp.handlers'['callHierarchy/outgoingCalls'](nil, nil, {}, nil) return #vim.fn.getqflist() ]=]) eq(0, qflist_count) @@ -1976,14 +2106,16 @@ describe('LSP', function() } } } local handler = require'vim.lsp.handlers'['callHierarchy/outgoingCalls'] - handler(nil, nil, rust_analyzer_response) + handler(nil, rust_analyzer_response, {}) return vim.fn.getqflist() ]=]) local expected = { { bufnr = 2, col = 5, + end_col = 0, lnum = 4, + end_lnum = 0, module = "", nr = 0, pattern = "", @@ -2000,7 +2132,7 @@ describe('LSP', function() describe('vim.lsp.buf.incoming_calls', function() it('does nothing for an empty response', function() local qflist_count = exec_lua([=[ - require'vim.lsp.handlers'['callHierarchy/incomingCalls']() + require'vim.lsp.handlers'['callHierarchy/incomingCalls'](nil, nil, {}) return #vim.fn.getqflist() ]=]) eq(0, qflist_count) @@ -2048,14 +2180,16 @@ describe('LSP', function() } } local handler = require'vim.lsp.handlers'['callHierarchy/incomingCalls'] - handler(nil, nil, rust_analyzer_response) + handler(nil, rust_analyzer_response, {}) return vim.fn.getqflist() ]=]) local expected = { { bufnr = 2, col = 5, + end_col = 0, lnum = 4, + end_lnum = 0, module = "", nr = 0, pattern = "", @@ -2068,4 +2202,90 @@ describe('LSP', function() eq(expected, qflist) end) end) + + describe('vim.lsp.buf.rename', function() + for _, test in ipairs({ + { + it = "does not attempt to rename on nil response", + name = "prepare_rename_nil", + expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; + }, + }, + { + it = "handles prepareRename placeholder response", + name = "prepare_rename_placeholder", + expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, NIL, {method="textDocument/rename", client_id=1, bufnr=1}}; + {NIL, {}, {method="start", client_id=1}}; + }, + expected_text = "placeholder", -- see fake lsp response + }, + { + it = "handles range response", + name = "prepare_rename_range", + expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, NIL, {method="textDocument/rename", client_id=1, bufnr=1}}; + {NIL, {}, {method="start", client_id=1}}; + }, + expected_text = "line", -- see test case and fake lsp response + }, + { + it = "handles error", + name = "prepare_rename_error", + expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, NIL, {method="textDocument/rename", client_id=1, bufnr=1}}; + {NIL, {}, {method="start", client_id=1}}; + }, + expected_text = "two", -- see test case + }, + }) do + it(test.it, function() + local client + test_rpc_server { + test_name = test.name; + on_init = function(_client) + client = _client + eq(true, client.resolved_capabilities().rename) + end; + on_setup = function() + exec_lua([=[ + local bufnr = vim.api.nvim_get_current_buf() + lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID) + vim.lsp._stubs = {} + vim.fn.input = function(prompt, text) + vim.lsp._stubs.input_prompt = prompt + vim.lsp._stubs.input_text = text + return 'renameto' -- expect this value in fake lsp + end + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'', 'this is line two'}) + vim.fn.cursor(2, 13) -- the space between "line" and "two" + ]=]) + end; + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end; + on_handler = function(err, result, ctx) + eq(table.remove(test.expected_handlers), {err, result, ctx}, "expected handler") + if ctx.method == 'start' then + exec_lua("vim.lsp.buf.rename()") + end + if ctx.method == 'shutdown' then + if test.expected_text then + eq("New Name: ", exec_lua("return vim.lsp._stubs.input_prompt")) + eq(test.expected_text, exec_lua("return vim.lsp._stubs.input_text")) + end + client.stop() + end + end; + } + end) + end + end) + end) diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index 5663f248bf..a4d78682ad 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -2161,6 +2161,12 @@ describe('plugin/shada.vim', function() reset() wshada('\004\000\009\147\000\196\002ab\196\001a') wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') + + -- Need to set nohidden so that the buffer containing 'fname' is not unloaded + -- after loading 'fname_tmp', otherwise the '++opt not supported' test below + -- won't work since the BufReadCmd autocmd won't be triggered. + nvim_command('set nohidden') + nvim_command('edit ' .. fname) eq({ 'History entry with timestamp ' .. epoch .. ':', diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua index 2c681eb9d8..986db96a18 100644 --- a/test/functional/provider/clipboard_spec.lua +++ b/test/functional/provider/clipboard_spec.lua @@ -506,6 +506,20 @@ describe('clipboard (with fake clipboard.vim)', function() feed('p') eq('textstar', meths.get_current_line()) end) + + it('Block paste works currectly', function() + insert([[ + aabbcc + ddeeff + ]]) + feed('gg^<C-v>') -- Goto start of top line enter visual block mode + feed('3ljy^k') -- yank 4x2 block & goto initial location + feed('P') -- Paste it infront + expect([[ + aabbaabbcc + ddeeddeeff + ]]) + end) end) describe('clipboard=unnamedplus', function() @@ -639,14 +653,12 @@ describe('clipboard (with fake clipboard.vim)', function() '', '', 'E121: Undefined variable: doesnotexist', - 'E15: Invalid expression: doesnotexist', }, 'v'}, eval("g:test_clip['*']")) feed_command(':echo "Howdy!"') eq({{ '', '', 'E121: Undefined variable: doesnotexist', - 'E15: Invalid expression: doesnotexist', '', 'Howdy!', }, 'v'}, eval("g:test_clip['*']")) diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua index d254edc7d5..d100db8de2 100644 --- a/test/functional/provider/python3_spec.lua +++ b/test/functional/provider/python3_spec.lua @@ -1,4 +1,5 @@ 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 @@ -116,6 +117,6 @@ describe('python3 provider', function() feed_command("exe 'split' tempname()") feed_command("bwipeout!") feed_command('help help') - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua index 2729d8dfa2..fba96100fc 100644 --- a/test/functional/provider/ruby_spec.lua +++ b/test/functional/provider/ruby_spec.lua @@ -1,10 +1,10 @@ local helpers = require('test.functional.helpers')(after_each) +local assert_alive = helpers.assert_alive local clear = helpers.clear local command = helpers.command local curbufmeths = helpers.curbufmeths local eq = helpers.eq -local eval = helpers.eval local exc_exec = helpers.exc_exec local expect = helpers.expect local feed = helpers.feed @@ -107,7 +107,7 @@ describe('ruby provider', function() helpers.add_builddir_to_rtp() command([=[autocmd BufDelete * ruby VIM::evaluate('expand("<afile>")')]=]) feed_command('help help') - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) end) diff --git a/test/functional/shada/history_spec.lua b/test/functional/shada/history_spec.lua index 9291f5e100..84cc34c7cc 100644 --- a/test/functional/shada/history_spec.lua +++ b/test/functional/shada/history_spec.lua @@ -2,7 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local nvim_command, funcs, meths, nvim_feed, eq = helpers.command, helpers.funcs, helpers.meths, helpers.feed, helpers.eq -local eval = helpers.eval +local assert_alive = helpers.assert_alive local shada_helpers = require('test.functional.shada.helpers') local reset, clear = shada_helpers.reset, shada_helpers.clear @@ -244,7 +244,7 @@ describe('ShaDa support code', function() nvim_command('wshada') nvim_command('set shada=\'10,:0') nvim_command('wshada') - eq(2, eval('1+1')) -- check nvim still running + assert_alive() end) end) diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index c61bf108cb..103ae59b8e 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local thelpers = require('test.functional.terminal.helpers') +local assert_alive = helpers.assert_alive local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim local poke_eventloop = helpers.poke_eventloop local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source @@ -7,6 +8,7 @@ local eq, neq = helpers.eq, helpers.neq local write_file = helpers.write_file local command= helpers.command local exc_exec = helpers.exc_exec +local matches = helpers.matches describe(':terminal buffer', function() local screen @@ -255,8 +257,23 @@ describe(':terminal buffer', function() command('bdelete!') end) - it('handles wqall', function() + it('requires bang (!) to close a running job #15402', function() eq('Vim(wqall):E948: Job still running', exc_exec('wqall')) + for _, cmd in ipairs({ 'bdelete', '%bdelete', 'bwipeout', 'bunload' }) do + matches('^Vim%('..cmd:gsub('%%', '')..'%):E89: term://.*tty%-test.* will be killed %(add %! to override%)$', + exc_exec(cmd)) + end + command('call jobstop(&channel)') + assert(0 >= eval('jobwait([&channel], 1000)[0]')) + command('bdelete') + end) + + it('stops running jobs with :quit', function() + -- Open in a new window to avoid terminating the nvim instance + command('split') + command('terminal') + command('set nohidden') + command('quit') end) it('does not segfault when pasting empty buffer #13955', function() @@ -284,7 +301,7 @@ describe('No heap-buffer-overflow when using', function() feed('$') -- Let termopen() modify the buffer feed_command('call termopen("echo")') - eq(2, eval('1+1')) -- check nvim still running + assert_alive() feed_command('bdelete!') end) end) @@ -294,6 +311,6 @@ describe('No heap-buffer-overflow when', function() feed_command('set nowrap') feed_command('autocmd TermOpen * startinsert') feed_command('call feedkeys("4000ai\\<esc>:terminal!\\<cr>")') - eq(2, eval('1+1')) + assert_alive() end) end) diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 4b512605e1..707c355069 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive local clear, poke_eventloop, nvim = helpers.clear, helpers.poke_eventloop, helpers.nvim local nvim_dir, source, eq = helpers.nvim_dir, helpers.source, helpers.eq local feed = helpers.feed @@ -215,7 +216,7 @@ describe(':terminal (with fake shell)', function() -- handler), :terminal cleanup is pending on the main-loop. -- This write should be ignored (not crash, #5445). feed('iiYYYYYYY') - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) it('works with findfile()', function() diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 8a5dd7ef18..f7520b14d4 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -8,12 +8,12 @@ local helpers = require('test.functional.helpers')(after_each) local uname = helpers.uname local thelpers = require('test.functional.terminal.helpers') local Screen = require('test.functional.ui.screen') +local assert_alive = helpers.assert_alive local eq = helpers.eq local feed_command = helpers.feed_command local feed_data = thelpers.feed_data local clear = helpers.clear local command = helpers.command -local eval = helpers.eval local nvim_dir = helpers.nvim_dir local retry = helpers.retry local nvim_prog = helpers.nvim_prog @@ -82,7 +82,7 @@ describe('TUI', function() command('call jobresize(b:terminal_job_id, 1, 4)') screen:try_resize(57, 17) command('call jobresize(b:terminal_job_id, 57, 17)') - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) it('accepts resize while pager is active', function() diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua index 03bd336aec..188afa1e84 100644 --- a/test/functional/terminal/window_split_tab_spec.lua +++ b/test/functional/terminal/window_split_tab_spec.lua @@ -1,5 +1,6 @@ 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, nvim = helpers.feed, helpers.nvim local feed_command = helpers.feed_command @@ -33,7 +34,7 @@ describe(':terminal', function() command('vsplit foo') eq(3, eval("winnr('$')")) feed('ZQ') -- Close split, should not crash. #7538 - eq(2, eval("1+1")) -- Still alive? + assert_alive() end) it('does not change size on WinEnter', function() diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua index 05e0c5fe2c..175525b3f2 100644 --- a/test/functional/treesitter/highlight_spec.lua +++ b/test/functional/treesitter/highlight_spec.lua @@ -570,4 +570,47 @@ describe('treesitter highlighting', function() ]]} screen:expect{ unchanged=true } end) + + it("supports highlighting with priority", function() + if pending_c_parser(pending) then return end + + insert([[ + int x = INT_MAX; + #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) + #define foo void main() { \ + return 42; \ + } + ]]) + + exec_lua [[ + local parser = vim.treesitter.get_parser(0, "c") + test_hl = vim.treesitter.highlighter.new(parser, {queries = {c = hl_query..'\n((translation_unit) @Error (set! "priority" 101))\n'}}) + ]] + -- expect everything to have Error highlight + screen:expect{grid=[[ + {12:int}{8: x = INT_MAX;} | + {8:#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))}| + {8:#define foo void main() { \} | + {8: return 42; \} | + {8: }} | + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]], attr_ids={ + [1] = {bold = true, foreground = Screen.colors.Blue1}; + [8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}; + -- bold will not be overwritten at the moment + [12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Grey100}; + }} + end) end) diff --git a/test/functional/treesitter/node_spec.lua b/test/functional/treesitter/node_spec.lua new file mode 100644 index 0000000000..21c287644e --- /dev/null +++ b/test/functional/treesitter/node_spec.lua @@ -0,0 +1,62 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local eq = helpers.eq +local exec_lua = helpers.exec_lua +local insert = helpers.insert +local pending_c_parser = helpers.pending_c_parser + +before_each(clear) + +local function lua_eval(lua_expr) + return exec_lua("return " .. lua_expr) +end + +describe('treesitter node API', function() + clear() + + if pending_c_parser(pending) then + return + end + + it('can move between siblings', function() + insert([[ + int main(int x, int y, int z) { + return x + y * z + } + ]]) + + exec_lua([[ + query = require"vim.treesitter.query" + parser = vim.treesitter.get_parser(0, "c") + tree = parser:parse()[1] + root = tree:root() + lang = vim.treesitter.inspect_language('c') + + function node_text(node) + return query.get_node_text(node, 0) + end + ]]) + + exec_lua 'node = root:descendant_for_range(0, 11, 0, 16)' + eq('int x', lua_eval('node_text(node)')) + + exec_lua 'node = node:next_sibling()' + eq(',', lua_eval('node_text(node)')) + + exec_lua 'node = node:next_sibling()' + eq('int y', lua_eval('node_text(node)')) + + exec_lua 'node = node:prev_sibling()' + eq(',', lua_eval('node_text(node)')) + + exec_lua 'node = node:prev_sibling()' + eq('int x', lua_eval('node_text(node)')) + + exec_lua 'node = node:next_named_sibling()' + eq('int y', lua_eval('node_text(node)')) + + exec_lua 'node = node:prev_named_sibling()' + eq('int x', lua_eval('node_text(node)')) + end) +end) diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index d2f9148e8f..ffaa4141c4 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -10,9 +10,11 @@ local pending_c_parser = helpers.pending_c_parser before_each(clear) describe('treesitter parser API', function() + clear() + if pending_c_parser(pending) then return end it('parses buffer', function() - if helpers.pending_win32(pending) or pending_c_parser(pending) then return end + if helpers.pending_win32(pending) then return end insert([[ int main() { @@ -103,8 +105,6 @@ void ui_refresh(void) }]] it('allows to iterate over nodes children', function() - if pending_c_parser(pending) then return end - insert(test_text); local res = exec_lua([[ @@ -127,8 +127,6 @@ void ui_refresh(void) end) it('allows to get a child by field', function() - if pending_c_parser(pending) then return end - insert(test_text); local res = exec_lua([[ @@ -162,8 +160,6 @@ void ui_refresh(void) ]] it("supports runtime queries", function() - if pending_c_parser(pending) then return end - local ret = exec_lua [[ return require"vim.treesitter.query".get_query("c", "highlights").captures[1] ]] @@ -172,8 +168,6 @@ void ui_refresh(void) end) it('support query and iter by capture', function() - if pending_c_parser(pending) then return end - insert(test_text) local res = exec_lua([[ @@ -203,8 +197,6 @@ void ui_refresh(void) end) it('support query and iter by match', function() - if pending_c_parser(pending) then return end - insert(test_text) local res = exec_lua([[ @@ -236,8 +228,6 @@ void ui_refresh(void) end) it('can match special regex characters like \\ * + ( with `vim-match?`', function() - if pending_c_parser(pending) then return end - insert('char* astring = "\\n"; (1 + 1) * 2 != 2;') local res = exec_lua([[ @@ -271,8 +261,6 @@ void ui_refresh(void) end) it('supports builtin query predicate any-of?', function() - if pending_c_parser(pending) then return end - insert([[ #include <stdio.h> @@ -330,8 +318,6 @@ void ui_refresh(void) end) it('allow loading query with escaped quotes and capture them with `lua-match?` and `vim-match?`', function() - if pending_c_parser(pending) then return end - insert('char* astring = "Hello World!";') local res = exec_lua([[ @@ -407,8 +393,6 @@ void ui_refresh(void) it('allows to set simple ranges', function() - if pending_c_parser(pending) then return end - insert(test_text) local res = exec_lua [[ @@ -450,8 +434,6 @@ void ui_refresh(void) eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } }) end) it("allows to set complex ranges", function() - if pending_c_parser() then return end - insert(test_text) local res = exec_lua [[ @@ -646,6 +628,19 @@ int x = INT_MAX; {2, 29, 2, 68} -- READ_STRING_OK(x, y) (char_u *)read_string((x), (size_t)(y)) }, get_ranges()) end) + it("should list all directives", function() + local res_list = exec_lua[[ + local query = require'vim.treesitter.query' + + local list = query.list_directives() + + table.sort(list) + + return list + ]] + + eq({ 'offset!', 'set!' }, res_list) + end) end) end) diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua index af709cd521..16ed3b9486 100644 --- a/test/functional/ui/bufhl_spec.lua +++ b/test/functional/ui/bufhl_spec.lua @@ -858,8 +858,8 @@ describe('Buffer highlighting', function() it('works with cursorline', function() command("set cursorline") - screen:expect([[ - {14:^1 + 2 }{15:=}{16: 3}{14: }| + screen:expect{grid=[[ + {14:^1 + 2 }{3:=}{2: 3}{14: }| 3 + {11:ERROR:} invalid syntax | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s| @@ -867,32 +867,32 @@ describe('Buffer highlighting', function() {1:~ }| {1:~ }| | - ]]) + ]]} feed('j') - screen:expect([[ + screen:expect{grid=[[ 1 + 2 {3:=}{2: 3} | - {14:^3 + }{11:ERROR:}{14: invalid syntax }| + {14:^3 + }{11:ERROR:} invalid syntax{14: }| 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s| x = 4 | {1:~ }| {1:~ }| | - ]]) + ]]} feed('j') - screen:expect([[ + screen:expect{grid=[[ 1 + 2 {3:=}{2: 3} | 3 + {11:ERROR:} invalid syntax | {14:^5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}| - {14:, 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s}| + {14:, 5, 5, 5, 5, 5, 5, }Lorem ipsum dolor s| x = 4 | {1:~ }| {1:~ }| | - ]]) + ]]} end) it('works with color column', function() @@ -910,11 +910,11 @@ describe('Buffer highlighting', function() command("set colorcolumn=9") screen:expect{grid=[[ - ^1 + 2 {3:=}{2: }{17:3} | + ^1 + 2 {3:=}{2: 3} | 3 + {11:ERROR:} invalid syntax | 5, 5, 5,{18: }5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s| - x = 4 {12:æš—}{19:x}{12:事} | + x = 4 {12:æš—x事} | {1:~ }| {1:~ }| | diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index 0ea8bab957..ad23402ff9 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -14,6 +14,8 @@ local function new_screen(opt) [3] = {bold = true, reverse = true}, [4] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, [5] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [6] = {foreground = Screen.colors.Magenta}, + [7] = {bold = true, foreground = Screen.colors.Brown}, }) return screen end @@ -267,7 +269,7 @@ local function test_cmdline(linegrid) special = {'"', true}, }, { firstc = "=", - content = {{"1"}, {"+"}, {"2"}}, + content = {{"1", 6}, {"+", 7}, {"2", 6}}, pos = 3, }} diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index f75f700fb5..9c035c728b 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -212,10 +212,10 @@ describe('ui/cursor', function() if m.blinkwait then m.blinkwait = 700 end end if m.hl_id then - m.hl_id = 56 + m.hl_id = 58 m.attr = {background = Screen.colors.DarkGray} end - if m.id_lm then m.id_lm = 57 end + if m.id_lm then m.id_lm = 59 end end -- Assert the new expectation. diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 98aafd8757..4373d17890 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -562,7 +562,7 @@ end]] {5:l}{8:blen}{7:dy}{10:e}{7:text}{10:h}{7:-}{10:_}{7:here}ell, count = unpack(item) | {5:i}{12:c}{11:ombining color} {13:nil} {5:then} | {11:replacing color}d_cell | - {5:e}{8:bl}{14:endy}{15:i}{14:text}{15:o}{14:-}{15:o}{14:h}{7:ere} | + {5:e}{8:bl}{7:endy}{10: }{7:text}{10: }{7:-}{10: }{7:here} | {5:f}{12:co}{11:mbini}{16:n}{11:g color}t {5:or} {13:1}) {5:do} | {11:replacing color} line[colpos] | cell.text = text | @@ -697,4 +697,96 @@ end]] | ]]} end) + + it('can have virtual text which combines foreground and backround groups', function() + screen:set_default_attr_ids { + [1] = {bold=true, foreground=Screen.colors.Blue}; + [2] = {background = tonumber('0x123456'), foreground = tonumber('0xbbbbbb')}; + [3] = {background = tonumber('0x123456'), foreground = tonumber('0xcccccc')}; + [4] = {background = tonumber('0x234567'), foreground = tonumber('0xbbbbbb')}; + [5] = {background = tonumber('0x234567'), foreground = tonumber('0xcccccc')}; + [6] = {bold = true, foreground = tonumber('0xcccccc'), background = tonumber('0x234567')}; + } + + exec [[ + hi BgOne guibg=#123456 + hi BgTwo guibg=#234567 + hi FgEin guifg=#bbbbbb + hi FgZwei guifg=#cccccc + hi VeryBold gui=bold + ]] + + meths.buf_set_extmark(0, ns, 0, 0, { virt_text={ + {'a', {'BgOne', 'FgEin'}}; + {'b', {'BgOne', 'FgZwei'}}; + {'c', {'BgTwo', 'FgEin'}}; + {'d', {'BgTwo', 'FgZwei'}}; + {'X', {'BgTwo', 'FgZwei', 'VeryBold'}}; + }}) + + screen:expect{grid=[[ + ^ {2:a}{3:b}{4:c}{5:d}{6:X} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('does not crash when deleting a cleared buffer #15212', function() + exec_lua [[ + ns = vim.api.nvim_create_namespace("myplugin") + vim.api.nvim_buf_set_extmark(0, ns, 0, 0, {virt_text = {{"a"}}, end_col = 0}) + ]] + screen:expect{grid=[[ + ^ a | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + exec_lua [[ + vim.api.nvim_buf_clear_namespace(0, ns, 0, -1) + vim.cmd("bdelete") + ]] + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + helpers.assert_alive() + end) end) diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index a8d9fb02fc..6c6735a4cf 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -1057,7 +1057,7 @@ it('diff updates line numbers below filler lines', function() vnew call setline(1, ['a', 'a', 'a', 'x', 'x', 'x', 'b', 'b', 'b', 'b', 'b']) windo diffthis - setlocal number rnu foldcolumn=0 + setlocal number rnu cursorline cursorlineopt=number foldcolumn=0 ]]) screen:expect([[ {1: }a {3:│}{10:1 }^a | @@ -1109,7 +1109,7 @@ it('diff updates line numbers below filler lines', function() {3:[No Name] [+] }{7:[No Name] [+] }| | ]]) - command("set signcolumn number tgc cursorline") + command("set signcolumn number tgc cursorline cursorlineopt=number,line") command("hi CursorLineNr guibg=red") screen:expect{grid=[[ {1: }a {3:│}{11: 2 }a | diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 16feb3e56b..ccf5f963d1 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -74,7 +74,8 @@ describe('float window', function() funcs.win_execute(win, 'bwipe!') end) - it('win_execute() call commands that not allowed' , function() + it("win_execute() call commands that are not allowed when 'hidden' is not set" , function() + command('set nohidden') local buf = meths.create_buf(false, false) meths.buf_set_lines(buf, 0, -1, true, {'the floatwin'}) local win = meths.open_win(buf, true, {relative='win', width=16, height=1, row=0, col=10}) @@ -524,8 +525,8 @@ describe('float window', function() it('return their configuration', function() local buf = meths.create_buf(false, false) - local win = meths.open_win(buf, false, {relative='editor', width=20, height=2, row=3, col=5}) - local expected = {anchor='NW', col=5, external=false, focusable=true, height=2, relative='editor', row=3, width=20} + local win = meths.open_win(buf, false, {relative='editor', width=20, height=2, row=3, col=5, zindex=60}) + local expected = {anchor='NW', col=5, external=false, focusable=true, height=2, relative='editor', row=3, width=20, zindex=60} eq(expected, meths.win_get_config(win)) eq({relative='', external=false, focusable=true}, meths.win_get_config(0)) @@ -746,6 +747,134 @@ describe('float window', function() end end) + it("would not break 'minimal' style with signcolumn=auto:[min]-[max]", function() + command('set number') + command('set signcolumn=auto:1-3') + command('set colorcolumn=1') + command('set cursorline') + command('set foldcolumn=1') + command('hi NormalFloat guibg=#333333') + feed('ix<cr>y<cr><esc>gg') + local win = meths.open_win(0, false, {relative='editor', width=20, height=4, row=4, col=10, style='minimal'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {15:x }| + {15:y }| + {15: }| + {15: }| + ]], float_pos={[4] = {{id = 1001}, "NW", 1, 4, 10, true}}} + else + screen:expect{grid=[[ + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } {15:x } | + {0:~ }{15:y }{0: }| + {0:~ }{15: }{0: }| + {0:~ }{15: }{0: }| + | + ]]} + end + + command('sign define piet1 text=ðŒ¢Ì€Ì̂̃̅̄ðŒ¢Ì€Ì̂̃̅̄ texthl=Search') + command('sign place 1 line=1 name=piet1 buffer=1') + -- signcolumn=auto:1-3 still works if there actually are signs + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + {19: }{17:ðŒ¢Ì€Ì̂̃̅̄ðŒ¢Ì€Ì̂̃̅̄}{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {17:ðŒ¢Ì€Ì̂̃̅̄ðŒ¢Ì€Ì̂̃̅̄}{15:x }| + {19: }{15:y }| + {19: }{15: }| + {15: }| + ]], float_pos={[4] = {{id = 1001}, "NW", 1, 4, 10, true}}} + + else + screen:expect([[ + {19: }{17:ðŒ¢Ì€Ì̂̃̅̄ðŒ¢Ì€Ì̂̃̅̄}{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } {17:ðŒ¢Ì€Ì̂̃̅̄ðŒ¢Ì€Ì̂̃̅̄}{15:x } | + {0:~ }{19: }{15:y }{0: }| + {0:~ }{19: }{15: }{0: }| + {0:~ }{15: }{0: }| + | + ]]) + end + command('sign unplace 1 buffer=1') + + local buf = meths.create_buf(false, true) + meths.win_set_buf(win, buf) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + {15: }| + {15: }| + {15: }| + {15: }| + ]], float_pos={[4] = {{id = 1001}, "NW", 1, 4, 10, true}}} + else + screen:expect([[ + {19: }{20: 1 }{22:^x}{21: }| + {19: }{14: 2 }{22:y} | + {19: }{14: 3 }{22: } {15: } | + {0:~ }{15: }{0: }| + {0:~ }{15: }{0: }| + {0:~ }{15: }{0: }| + | + ]]) + end + end) + it('can have border', function() local buf = meths.create_buf(false, false) meths.buf_set_lines(buf, 0, -1, true, {' halloj! ', @@ -779,8 +908,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -822,8 +951,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -837,6 +966,49 @@ describe('float window', function() ]]} end + meths.win_set_config(win, {border="rounded"}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {5:â•─────────╮}| + {5:│}{1: halloj! }{5:│}| + {5:│}{1: BORDAA }{5:│}| + {5:╰─────────╯}| + ]], float_pos={ + [5] = { { id = 1002 }, "NW", 1, 2, 5, true } + }, win_viewport={ + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; + }} + else + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }{5:â•─────────╮}{0: }| + {0:~ }{5:│}{1: halloj! }{5:│}{0: }| + {0:~ }{5:│}{1: BORDAA }{5:│}{0: }| + {0:~ }{5:╰─────────╯}{0: }| + | + ]]} + end + meths.win_set_config(win, {border="solid"}) if multigrid then screen:expect{grid=[[ @@ -865,8 +1037,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -909,8 +1081,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -950,8 +1122,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -991,8 +1163,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -1041,8 +1213,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 2, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 6, curline = 5, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 6, curline = 5, curcol = 0, linecount = 6}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 2}; }} else screen:expect{grid=[[ @@ -1092,8 +1264,8 @@ describe('float window', function() ]], float_pos={ [4] = { { id = 1001 }, "NW", 1, 0, 0, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; }} else screen:expect{grid=[[ @@ -1150,8 +1322,8 @@ describe('float window', function() ]], float_pos={ [5] = { { id = 1002 }, "NW", 1, 0, 5, true } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 0, linecount = 3}; }} else screen:expect{grid=[[ @@ -1209,8 +1381,8 @@ describe('float window', function() [5] = { { id = 1002 }, "NW", 1, 0, 5, true, 50 }, [6] = { { id = -1 }, "NW", 5, 4, 0, false, 100 } }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 3}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount=1}; + [5] = {win = {id = 1002}, topline = 0, botline = 3, curline = 2, curcol = 3, linecount=3}; }} else screen:expect{grid=[[ @@ -1659,6 +1831,7 @@ describe('float window', function() botline = 3, curline = 0, curcol = 3, + linecount = 2, win = { id = 1000 } }, [4] = { @@ -1666,6 +1839,7 @@ describe('float window', function() botline = 3, curline = 0, curcol = 3, + linecount = 2, win = { id = 1001 } }, [5] = { @@ -1673,6 +1847,7 @@ describe('float window', function() botline = 2, curline = 0, curcol = 0, + linecount = 1, win = { id = 1002 } } }} @@ -1757,7 +1932,7 @@ describe('float window', function() ]]} end eq({relative='win', width=12, height=1, bufpos={1,32}, anchor='NW', - external=false, col=0, row=1, win=firstwin, focusable=true}, meths.win_get_config(win)) + external=false, col=0, row=1, win=firstwin, focusable=true, zindex=50}, meths.win_get_config(win)) feed('<c-e>') if multigrid then @@ -3027,6 +3202,53 @@ describe('float window', function() end end) + it('command menu rendered above cursor (pum_above)', function() + command('set wildmenu wildmode=longest:full wildoptions=pum') + feed(':sign u<tab>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [3:----------------------------------------]| + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + :sign un^ | + ## grid 4 + {7: }| + {12:~ }| + {12:~ }| + {12:~ }| + ## grid 5 + {1: undefine }| + {1: unplace }| + ]], float_pos={ + [5] = {{id = -1}, "SW", 1, 6, 5, false, 250}; + [4] = {{id = 1001}, "NW", 1, 2, 5, true, 50}; + }} + else + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }{7: }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{1: undefine }{0: }| + {0:~ }{1: unplace }{0: }| + :sign un^ | + ]]} + end + end) + it('with ext_popupmenu', function() screen:set_option('ext_popupmenu', true) feed('ix ') @@ -4943,7 +5165,7 @@ describe('float window', function() ]]) end - eq(2, eval('1+1')) + assert_alive() end) it("o (:only) non-float", function() @@ -6246,8 +6468,8 @@ describe('float window', function() ]], float_pos={ [4] = { { id = 1001 }, "NW", 1, 2, 5, true }; }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; }} else screen:expect{grid=[[ @@ -6303,10 +6525,10 @@ describe('float window', function() [5] = { { id = 1002 }, "NW", 1, 3, 8, true }; [6] = { { id = 1003 }, "NW", 1, 4, 10, true }; }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount=1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount=1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount=1}; + [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount=1}; }} else screen:expect{grid=[[ @@ -6351,8 +6573,8 @@ describe('float window', function() ]], float_pos={ [4] = { { id = 1001 }, "NW", 1, 2, 5, true }; }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; }} else screen:expect{grid=[[ @@ -6408,10 +6630,10 @@ describe('float window', function() [5] = { { id = 1002 }, "NW", 1, 4, 10, true }; [6] = { { id = 1003 }, "NW", 1, 3, 8, true }; }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; }} else screen:expect{grid=[[ @@ -6471,10 +6693,10 @@ describe('float window', function() [5] = {{id = 1002}, "NW", 1, 2, 6, true, 50}; [6] = {{id = 1003}, "NW", 1, 3, 7, true, 40}; }, win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0}; - [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0}; + [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [4] = {win = {id = 1001}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [5] = {win = {id = 1002}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; + [6] = {win = {id = 1003}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1}; }} else screen:expect{grid=[[ diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 8883ad8270..249686234c 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -85,7 +85,7 @@ describe("folded lines", function() end) it("highlighting with relative line numbers", function() - command("set relativenumber foldmethod=marker") + command("set relativenumber cursorline cursorlineopt=number foldmethod=marker") feed_command("set foldcolumn=2") funcs.setline(1, '{{{1') funcs.setline(2, 'line 1') diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 8992ee27ce..c00d30fe32 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -789,7 +789,7 @@ describe("'listchars' highlight", function() [0] = {bold=true, foreground=Screen.colors.Blue}, [1] = {background=Screen.colors.Grey90}, [2] = {foreground=Screen.colors.Red}, - [3] = {foreground=Screen.colors.Green1}, + [3] = {foreground=Screen.colors.X11Green, background=Screen.colors.Red1}, }) feed_command('highlight clear ModeMsg') feed_command('highlight Whitespace guifg=#FF0000') @@ -912,6 +912,97 @@ describe('CursorLine highlight', function() ]]) 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}; + }) + screen:attach() + + feed_command('set wrap cursorline cursorlineopt=screenline') + feed_command('set showbreak=>>>') + feed_command('highlight clear NonText') + feed_command('highlight clear CursorLine') + feed_command('highlight NonText guifg=Yellow gui=NONE') + feed_command('highlight LineNr guifg=Red guibg=Green gui=NONE') + feed_command('highlight CursorLine guifg=Black guibg=White gui=NONE') + feed_command('highlight CursorLineNr guifg=Green guibg=Red gui=NONE') + + feed('30iø<esc>o<esc>30ia<esc>') + + -- CursorLine should not apply to 'showbreak' when 'cursorlineopt' contains "screenline" + screen:expect([[ + øøøøøøøøøøøøøøøøøøøø| + {2:>>>}øøøøøøøøøø | + aaaaaaaaaaaaaaaaaaaa| + {2:>>>}{1:aaaaaaaaa^a }| + | + ]]) + feed('gk') + screen:expect([[ + øøøøøøøøøøøøøøøøøøøø| + {2:>>>}øøøøøøøøøø | + {1:aaaaaaaaaaaa^aaaaaaaa}| + {2:>>>}aaaaaaaaaa | + | + ]]) + feed('k') + screen:expect([[ + {1:øøøøøøøøøøøø^øøøøøøøø}| + {2:>>>}øøøøøøøøøø | + aaaaaaaaaaaaaaaaaaaa| + {2:>>>}aaaaaaaaaa | + | + ]]) + + -- CursorLineNr should not apply to line number when 'cursorlineopt' does not contain "number" + feed_command('set relativenumber numberwidth=2') + screen:expect([[ + {3:0 }{1:øøøøøøøøøøøø^øøøøøø}| + {3: }{2:>>>}øøøøøøøøøøøø | + {3:1 }aaaaaaaaaaaaaaaaaa| + {3: }{2:>>>}aaaaaaaaaaaa | + | + ]]) + + -- CursorLineNr should apply to line number when 'cursorlineopt' contains "number" + feed_command('set cursorlineopt+=number') + screen:expect([[ + {4:0 }{1:øøøøøøøøøøøø^øøøøøø}| + {3: }{2:>>>}øøøøøøøøøøøø | + {3:1 }aaaaaaaaaaaaaaaaaa| + {3: }{2:>>>}aaaaaaaaaaaa | + | + ]]) + feed('gj') + screen:expect([[ + {4:0 }øøøøøøøøøøøøøøøøøø| + {3: }{2:>>>}{1:øøøøøøøøø^øøø }| + {3:1 }aaaaaaaaaaaaaaaaaa| + {3: }{2:>>>}aaaaaaaaaaaa | + | + ]]) + feed('gj') + screen:expect([[ + {3:1 }øøøøøøøøøøøøøøøøøø| + {3: }{2:>>>}øøøøøøøøøøøø | + {4:0 }{1:aaaaaaaaaaaa^aaaaaa}| + {3: }{2:>>>}aaaaaaaaaaaa | + | + ]]) + feed('gj') + screen:expect([[ + {3:1 }øøøøøøøøøøøøøøøøøø| + {3: }{2:>>>}øøøøøøøøøøøø | + {4:0 }aaaaaaaaaaaaaaaaaa| + {3: }{2:>>>}{1:aaaaaaaaa^aaa }| + | + ]]) + end) + it('always updated. vim-patch:8.1.0849', function() local screen = Screen.new(50,5) screen:set_default_attr_ids({ @@ -1201,6 +1292,75 @@ describe("MsgSeparator highlight and msgsep fillchar", function() end) end) +describe("'number' and 'relativenumber' highlight", function() + before_each(clear) + + it('LineNr, LineNrAbove and LineNrBelow', function() + local screen = Screen.new(20,10) + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.Red}, + [2] = {foreground = Screen.colors.Blue}, + [3] = {foreground = Screen.colors.Green}, + }) + screen:attach() + command('set number relativenumber') + command('call setline(1, range(50))') + command('highlight LineNr guifg=Red') + feed('4j') + screen:expect([[ + {1: 4 }0 | + {1: 3 }1 | + {1: 2 }2 | + {1: 1 }3 | + {1:5 }^4 | + {1: 1 }5 | + {1: 2 }6 | + {1: 3 }7 | + {1: 4 }8 | + | + ]]) + command('highlight LineNrAbove guifg=Blue') + screen:expect([[ + {2: 4 }0 | + {2: 3 }1 | + {2: 2 }2 | + {2: 1 }3 | + {1:5 }^4 | + {1: 1 }5 | + {1: 2 }6 | + {1: 3 }7 | + {1: 4 }8 | + | + ]]) + command('highlight LineNrBelow guifg=Green') + screen:expect([[ + {2: 4 }0 | + {2: 3 }1 | + {2: 2 }2 | + {2: 1 }3 | + {1:5 }^4 | + {3: 1 }5 | + {3: 2 }6 | + {3: 3 }7 | + {3: 4 }8 | + | + ]]) + feed('3j') + screen:expect([[ + {2: 7 }0 | + {2: 6 }1 | + {2: 5 }2 | + {2: 4 }3 | + {2: 3 }4 | + {2: 2 }5 | + {2: 1 }6 | + {1:8 }^7 | + {3: 1 }8 | + | + ]]) + end) +end) + describe("'winhighlight' highlight", function() local screen diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 72468392ee..c238898244 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -811,7 +811,7 @@ describe('ui/ext_messages', function() {1:~ }| {1:^~ }| ]], messages={ - {content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Helli"\nType number and <Enter> or click with the mouse (q or empty cancels): ' } }, kind = ""} + {content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\nType number and <Enter> or click with the mouse (q or empty cancels): ' } }, kind = ""} }} feed('1') @@ -822,7 +822,7 @@ describe('ui/ext_messages', function() {1:~ }| {1:^~ }| ]], messages={ - {content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Helli"\nType number and <Enter> or click with the mouse (q or empty cancels): ' } }, kind = ""}, + {content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Hullo"\nType number and <Enter> or click with the mouse (q or empty cancels): ' } }, kind = ""}, { content = { { "1" } }, kind = "" } }} diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua index 719e2ee82a..4e5e9c3a71 100644 --- a/test/functional/ui/multigrid_spec.lua +++ b/test/functional/ui/multigrid_spec.lua @@ -34,6 +34,7 @@ describe('ext_multigrid', function() [17] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta}, [18] = {bold = true, foreground = Screen.colors.Magenta}, [19] = {foreground = Screen.colors.Brown}, + [20] = {background = Screen.colors.LightGrey}, }) end) @@ -2034,6 +2035,66 @@ describe('ext_multigrid', function() ]]} end) + it('supports mouse drag with mouse=a', function() + command('set mouse=a') + command('vsplit') + command('wincmd l') + command('split') + command('enew') + feed('ifoo\nbar<esc>') + + meths.input_mouse('left', 'press', '', 5, 0, 0) + poke_eventloop() + meths.input_mouse('left', 'drag', '', 5, 1, 2) + + screen:expect{grid=[[ + ## grid 1 + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}[5:--------------------------]| + [4:--------------------------]{12:│}{11:[No Name] [+] }| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + [4:--------------------------]{12:│}[2:--------------------------]| + {12:[No Name] [No Name] }| + [3:-----------------------------------------------------]| + ## grid 2 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ## grid 4 + | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 5 + {20:foo} | + {20:ba}^r | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + + end) + it('has viewport information', function() screen:try_resize(48, 8) screen:expect{grid=[[ @@ -2056,7 +2117,7 @@ describe('ext_multigrid', function() ## grid 3 | ]], win_viewport={ - [2] = {win = { id = 1000 }, topline = 0, botline = 2, curline = 0, curcol = 0} + [2] = {win = { id = 1000 }, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1} }} insert([[ Lorem ipsum dolor sit amet, consectetur @@ -2091,7 +2152,7 @@ describe('ext_multigrid', function() ## grid 3 | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 5, botline = 11, curline = 10, curcol = 7}, + [2] = {win = {id = 1000}, topline = 5, botline = 11, curline = 10, curcol = 7, linecount = 11}, }} @@ -2116,7 +2177,7 @@ describe('ext_multigrid', function() ## grid 3 | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 2, botline = 9, curline = 7, curcol = 0}, + [2] = {win = {id = 1000}, topline = 2, botline = 9, curline = 7, curcol = 0, linecount = 11}, }} command("split") @@ -2140,8 +2201,8 @@ describe('ext_multigrid', function() reprehenderit in voluptate velit esse cillum | ^dolore eu fugiat nulla pariatur. Excepteur sint | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0}, - [4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 7, curcol = 0}, + [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0, linecount = 11}, + [4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 7, curcol = 0, linecount = 11}, }} feed("b") @@ -2165,8 +2226,8 @@ describe('ext_multigrid', function() reprehenderit in voluptate velit esse ^cillum | dolore eu fugiat nulla pariatur. Excepteur sint | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0}, - [4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 6, curcol = 38}, + [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0, linecount = 11}, + [4] = {win = {id = 1001}, topline = 5, botline = 9, curline = 6, curcol = 38, linecount = 11}, }} feed("2k") @@ -2190,8 +2251,8 @@ describe('ext_multigrid', function() ea commodo consequat. Duis aute irure dolor in | reprehenderit in voluptate velit esse cillum | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0}, - [4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38}, + [2] = {win = {id = 1000}, topline = 6, botline = 9, curline = 7, curcol = 0, linecount = 11}, + [4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38, linecount = 11}, }} -- handles non-current window @@ -2216,8 +2277,96 @@ describe('ext_multigrid', function() ea commodo consequat. Duis aute irure dolor in | reprehenderit in voluptate velit esse cillum | ]], win_viewport={ - [2] = {win = {id = 1000}, topline = 0, botline = 3, curline = 0, curcol = 10}, - [4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38}, + [2] = {win = {id = 1000}, topline = 0, botline = 3, curline = 0, curcol = 10, linecount = 11}, + [4] = {win = {id = 1001}, topline = 4, botline = 8, curline = 4, curcol = 38, linecount = 11}, + }} + end) + + it('does not crash when dragging mouse across grid boundary', function() + screen:try_resize(48, 8) + screen:expect{grid=[[ + ## grid 1 + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + {11:[No Name] }| + [3:------------------------------------------------]| + ## grid 2 + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ]], win_viewport={ + [2] = {win = { id = 1000 }, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1} + }} + insert([[ + Lorem ipsum dolor sit amet, consectetur + adipisicing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum + dolore eu fugiat nulla pariatur. Excepteur sint + occaecat cupidatat non proident, sunt in culpa + qui officia deserunt mollit anim id est + laborum.]]) + + screen:expect{grid=[[ + ## grid 1 + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + {11:[No Name] [+] }| + [3:------------------------------------------------]| + ## grid 2 + ea commodo consequat. Duis aute irure dolor in | + reprehenderit in voluptate velit esse cillum | + dolore eu fugiat nulla pariatur. Excepteur sint | + occaecat cupidatat non proident, sunt in culpa | + qui officia deserunt mollit anim id est | + laborum^. | + ## grid 3 + | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 5, botline = 11, curline = 10, curcol = 7, linecount = 11}, + }} + + meths.input_mouse('left', 'press', '', 1,5, 1) + poke_eventloop() + meths.input_mouse('left', 'drag', '', 1, 6, 1) + + screen:expect{grid=[[ + ## grid 1 + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + [2:------------------------------------------------]| + {11:[No Name] [+] }| + [3:------------------------------------------------]| + ## grid 2 + reprehenderit in voluptate velit esse cillum | + dolore eu fugiat nulla pariatur. Excepteur sint | + occaecat cupidatat non proident, sunt in culpa | + qui officia deserunt mollit anim id est | + l^aborum. | + {1:~ }| + ## grid 3 + {7:-- VISUAL --} | + ]], win_viewport={ + [2] = {win = {id = 1000}, topline = 6, botline = 12, curline = 10, curcol = 1, linecount = 11}, }} end) end) diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua index 3826707743..50e5dfac84 100644 --- a/test/functional/ui/output_spec.lua +++ b/test/functional/ui/output_spec.lua @@ -1,9 +1,9 @@ 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 eval = helpers.eval local feed = helpers.feed local feed_command = helpers.feed_command local iswin = helpers.iswin @@ -86,12 +86,12 @@ describe("shell command :!", function() it("cat a binary file #4142", function() feed(":exe 'silent !cat '.shellescape(v:progpath)<CR>") - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) it([[display \x08 char #4142]], function() feed(":silent !echo \08<CR>") - eq(2, eval('1+1')) -- Still alive? + assert_alive() end) it('handles control codes', function() diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 0944bfc21a..aeba049557 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) 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 @@ -9,7 +10,6 @@ local funcs = helpers.funcs local get_pathsep = helpers.get_pathsep local eq = helpers.eq local pcall_err = helpers.pcall_err -local eval = helpers.eval describe('ui/ext_popupmenu', function() local screen @@ -2211,6 +2211,6 @@ describe('builtin popupmenu', function() feed('$i') funcs.complete(col - max_len, items) feed('<c-y>') - eq(2, eval('1+1')) + assert_alive() end) end) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index f73d051857..61f19c3794 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -773,13 +773,14 @@ function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height) self.float_pos[grid] = nil end -function Screen:_handle_win_viewport(grid, win, topline, botline, curline, curcol) +function Screen:_handle_win_viewport(grid, win, topline, botline, curline, curcol, linecount) self.win_viewport[grid] = { win = win, topline = topline, botline = botline, curline = curline, - curcol = curcol + curcol = curcol, + linecount = linecount } end @@ -1306,7 +1307,7 @@ local function fmt_ext_state(name, state) for k,v in pairs(state) do str = (str.." ["..k.."] = {win = {id = "..v.win.id.."}, topline = " ..v.topline..", botline = "..v.botline..", curline = "..v.curline - ..", curcol = "..v.curcol.."};\n") + ..", curcol = "..v.curcol..", linecount = "..v.linecount.."};\n") end return str .. "}" elseif name == "float_pos" then diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index 06c92a4b10..741b93043d 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -176,8 +176,8 @@ describe('Signs', function() command('sign place 5 line=3 name=pietWarn buffer=1') command('sign place 3 line=3 name=pietError buffer=1') screen:expect([[ - {1:>>}XX{6: 1 }a | - XX{1:>>}{6: 2 }b | + {1:>>}{8:XX}{6: 1 }a | + {8:XX}{1:>>}{6: 2 }b | {1:>>}WW{6: 3 }c | {2: }{6: 4 }^ | {0:~ }| @@ -194,7 +194,7 @@ describe('Signs', function() -- With the default setting, we get the sign with the top id. command('set signcolumn=yes:1') screen:expect([[ - XX{6: 1 }a | + {8:XX}{6: 1 }a | {1:>>}{6: 2 }b | WW{6: 3 }c | {2: }{6: 4 }^ | @@ -212,9 +212,9 @@ describe('Signs', function() -- "auto:3" accommodates all the signs we defined so far. command('set signcolumn=auto:3') screen:expect([[ - {1:>>}XX{2: }{6: 1 }a | - XX{1:>>}{2: }{6: 2 }b | - XX{1:>>}WW{6: 3 }c | + {1:>>}{8:XX}{2: }{6: 1 }a | + {8:XX}{1:>>}{2: }{6: 2 }b | + {8:XX}{1:>>}WW{6: 3 }c | {2: }{6: 4 }^ | {0:~ }| {0:~ }| @@ -230,9 +230,9 @@ describe('Signs', function() -- Check "yes:9". command('set signcolumn=yes:9') screen:expect([[ - {1:>>}XX{2: }{6: 1 }a | - XX{1:>>}{2: }{6: 2 }b | - XX{1:>>}WW{2: }{6: 3 }c | + {1:>>}{8:XX}{2: }{6: 1 }a | + {8:XX}{1:>>}{2: }{6: 2 }b | + {8:XX}{1:>>}WW{2: }{6: 3 }c | {2: }{6: 4 }^ | {0:~ }| {0:~ }| @@ -249,9 +249,9 @@ describe('Signs', function() -- a single line (same result as "auto:3"). command('set signcolumn=auto:4') screen:expect{grid=[[ - {1:>>}XX{2: }{6: 1 }a | - XX{1:>>}{2: }{6: 2 }b | - XX{1:>>}WW{6: 3 }c | + {1:>>}{8:XX}{2: }{6: 1 }a | + {8:XX}{1:>>}{2: }{6: 2 }b | + {8:XX}{1:>>}WW{6: 3 }c | {2: }{6: 4 }^ | {0:~ }| {0:~ }| @@ -267,8 +267,8 @@ describe('Signs', function() -- line deletion deletes signs. command('2d') screen:expect([[ - {1:>>}XX{2: }{6: 1 }a | - XX{1:>>}WW{6: 2 }^c | + {1:>>}{8:XX}{2: }{6: 1 }a | + {8:XX}{1:>>}WW{6: 2 }^c | {2: }{6: 3 } | {0:~ }| {0:~ }| diff --git a/test/functional/ui/spell_spec.lua b/test/functional/ui/spell_spec.lua index 2c6e586665..de77100cc0 100644 --- a/test/functional/ui/spell_spec.lua +++ b/test/functional/ui/spell_spec.lua @@ -36,7 +36,7 @@ describe("'spell'", function() feed('ggJJJJJJ0') screen:expect([[ {1:^Lorem} {1:ipsum} dolor sit {1:amet}, {1:consectetur} {1:adipiscing} {1:elit}, {1:sed} do {1:eiusmod} {1:tempor} {1:i}| - {1:ncididunt} {1:ut} {1:labore} {1:et} {1:dolore} {1:magna} {1:aliqua}. {1:Ut} {1:enim} ad minim {1:veniam}, {1:quis} {1:nostru}| + {1:ncididunt} {1:ut} {1:labore} et {1:dolore} {1:magna} {1:aliqua}. {1:Ut} {1:enim} ad minim {1:veniam}, {1:quis} {1:nostru}| {1:d} {1:exercitation} {1:ullamco} {1:laboris} {1:nisi} {1:ut} {1:aliquip} ex ea {1:commodo} {1:consequat}. {1:Duis} {1:aut}| {1:e} {1:irure} dolor in {1:reprehenderit} in {1:voluptate} {1:velit} {1:esse} {1:cillum} {1:dolore} {1:eu} {1:fugiat} {1:n}| {1:ulla} {1:pariatur}. {1:Excepteur} {1:sint} {1:occaecat} {1:cupidatat} non {1:proident}, {1:sunt} in culpa {1:qui}| @@ -44,6 +44,7 @@ describe("'spell'", function() {0:~ }| | ]]) + end) it('has correct highlight at start of line', function() diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua index d1af0e955c..4e1852162f 100644 --- a/test/functional/ui/syntax_conceal_spec.lua +++ b/test/functional/ui/syntax_conceal_spec.lua @@ -913,4 +913,46 @@ describe('Screen', function() ]]} eq(grid_lines, {{2, 0, {{'c', 0, 3}}}}) end) + + -- Copy of Test_cursor_column_in_concealed_line_after_window_scroll in + -- test/functional/ui/syntax_conceal_spec.lua. + describe('concealed line after window scroll', function() + after_each(function() + command(':qall!') + os.remove('Xcolesearch') + end) + + it('has the correct cursor column', function() + insert([[ + 3split + let m = matchadd('Conceal', '=') + setl conceallevel=2 concealcursor=nc + normal gg + "==expr== + ]]) + + command('write Xcolesearch') + feed(":so %<CR>") + + -- Jump to something that is beyond the bottom of the window, + -- so there's a scroll down. + feed("/expr<CR>") + + -- Are the concealed parts of the current line really hidden? + -- Is the window's cursor column properly updated for hidden + -- parts of the current line? + screen:expect{grid=[[ + setl conceallevel2 concealcursornc | + normal gg | + "{5:^expr} | + {2:Xcolesearch }| + normal gg | + "=={5:expr}== | + | + {0:~ }| + {3:Xcolesearch }| + /expr | + ]]} + end) + end) end) diff --git a/test/functional/ui/tabline_spec.lua b/test/functional/ui/tabline_spec.lua index 23aae81745..809486d4db 100644 --- a/test/functional/ui/tabline_spec.lua +++ b/test/functional/ui/tabline_spec.lua @@ -4,14 +4,17 @@ local clear, command, eq = helpers.clear, helpers.command, helpers.eq describe('ui/ext_tabline', function() local screen - local event_tabs, event_curtab + local event_tabs, event_curtab, event_curbuf, event_buffers before_each(function() clear() screen = Screen.new(25, 5) screen:attach({rgb=true, ext_tabline=true}) - function screen:_handle_tabline_update(curtab, tabs) - event_curtab, event_tabs = curtab, tabs + function screen:_handle_tabline_update(curtab, tabs, curbuf, buffers) + event_curtab = curtab + event_tabs = tabs + event_curbuf = curbuf + event_buffers = buffers end end) @@ -45,4 +48,39 @@ describe('ui/ext_tabline', function() eq(expected_tabs, event_tabs) end} end) + + it('buffer UI events', function() + local expected_buffers_initial= { + {buffer = { id = 1 }, name = '[No Name]'}, + } + + screen:expect{grid=[[ + ^ | + ~ | + ~ | + ~ | + | + ]], condition=function() + eq({ id = 1}, event_curbuf) + eq(expected_buffers_initial, event_buffers) + end} + + command("badd another-buffer") + command("bnext") + + local expected_buffers = { + {buffer = { id = 1 }, name = '[No Name]'}, + {buffer = { id = 2 }, name = 'another-buffer'}, + } + screen:expect{grid=[[ + ^ | + ~ | + ~ | + ~ | + | + ]], condition=function() + eq({ id = 2 }, event_curbuf) + eq(expected_buffers, event_buffers) + end} + end) end) diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index a4241fe5aa..befad29922 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -1,5 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) 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 @@ -870,7 +871,7 @@ describe('completion', function() {3:-- Keyword completion (^N^P) }{4:match 1 of 2} | ]]) - eval('1 + 1') + assert_alive() -- popupmenu still visible screen:expect{grid=[[ foobar fooegg | diff --git a/test/helpers.lua b/test/helpers.lua index 12d9f19187..469aee53f0 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -58,9 +58,9 @@ local check_logs_useless_lines = { --- Invokes `fn` and includes the tail of `logfile` in the error message if it --- fails. --- ---@param logfile Log file, defaults to $NVIM_LOG_FILE or '.nvimlog' ---@param fn Function to invoke ---@param ... Function arguments +---@param logfile Log file, defaults to $NVIM_LOG_FILE or '.nvimlog' +---@param fn Function to invoke +---@param ... Function arguments local function dumplog(logfile, fn, ...) -- module.validate({ -- logfile={logfile,'s',true}, @@ -102,8 +102,8 @@ end --- Asserts that `pat` matches one or more lines in the tail of $NVIM_LOG_FILE. --- ---@param pat (string) Lua pattern to search for in the log file. ---@param logfile (string, default=$NVIM_LOG_FILE) full path to log file. +---@param pat (string) Lua pattern to search for in the log file. +---@param logfile (string, default=$NVIM_LOG_FILE) full path to log file. function module.assert_log(pat, logfile) logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog' local nrlines = 10 diff --git a/test/unit/charset/vim_str2nr_spec.lua b/test/unit/charset/vim_str2nr_spec.lua index 891e6def09..5fc3b83a13 100644 --- a/test/unit/charset/vim_str2nr_spec.lua +++ b/test/unit/charset/vim_str2nr_spec.lua @@ -43,7 +43,8 @@ local function argreset(arg, args) end end -local function test_vim_str2nr(s, what, exp, maxlen) +local function test_vim_str2nr(s, what, exp, maxlen, strict) + if strict == nil then strict = true end local bits = {} for k, _ in pairs(exp) do bits[#bits + 1] = k @@ -62,11 +63,11 @@ local function test_vim_str2nr(s, what, exp, maxlen) cv[k] = args[k] end end - lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen) + lib.vim_str2nr(s, cv.pre, cv.len, what, cv.num, cv.unum, maxlen, strict) for cck, ccv in pairs(cv) do if exp[cck] ~= tonumber(ccv[0]) then - error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d): %d'):format( - cck, exp[cck], s, tonumber(what), maxlen, tonumber(ccv[0]) + error(('Failed check (%s = %d) in test (s=%s, w=%u, m=%d, strict=%s): %d'):format( + cck, exp[cck], s, tonumber(what), maxlen, tostring(strict), tonumber(ccv[0]) )) end end @@ -85,10 +86,13 @@ describe('vim_str2nr()', function() test_vim_str2nr('', lib.STR2NR_ALL, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_BIN, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_OCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_OOCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_HEX, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_DEC, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_BIN, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OOCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) + test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_OCT + lib.STR2NR_OOCT, {len = 0, num = 0, unum = 0, pre = 0}, 0) test_vim_str2nr('', lib.STR2NR_FORCE + lib.STR2NR_HEX, {len = 0, num = 0, unum = 0, pre = 0}, 0) end) itp('works with decimal numbers', function() @@ -97,31 +101,39 @@ describe('vim_str2nr()', function() lib.STR2NR_BIN, lib.STR2NR_OCT, lib.STR2NR_HEX, + lib.STR2NR_OOCT, lib.STR2NR_BIN + lib.STR2NR_OCT, lib.STR2NR_BIN + lib.STR2NR_HEX, lib.STR2NR_OCT + lib.STR2NR_HEX, + lib.STR2NR_OOCT + lib.STR2NR_HEX, lib.STR2NR_ALL, lib.STR2NR_FORCE + lib.STR2NR_DEC, }) do -- Check that all digits are recognized test_vim_str2nr( '12345', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0) test_vim_str2nr( '67890', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0) - test_vim_str2nr( '12345A', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0) - test_vim_str2nr( '67890A', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0) + test_vim_str2nr( '12345A', flags, {len = 0}, 0) + test_vim_str2nr( '67890A', flags, {len = 0}, 0) + test_vim_str2nr( '12345A', flags, {len = 5, num = 12345, unum = 12345, pre = 0}, 0, false) + test_vim_str2nr( '67890A', flags, {len = 5, num = 67890, unum = 67890, pre = 0}, 0, false) test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0) test_vim_str2nr( '42', flags, {len = 1, num = 4, unum = 4, pre = 0}, 1) test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 2) test_vim_str2nr( '42', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3) -- includes NUL byte in maxlen - test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0) - test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3) + test_vim_str2nr( '42x', flags, {len = 0}, 0) + test_vim_str2nr( '42x', flags, {len = 0}, 3) + test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 0, false) + test_vim_str2nr( '42x', flags, {len = 2, num = 42, unum = 42, pre = 0}, 3, false) test_vim_str2nr('-42', flags, {len = 3, num = -42, unum = 42, pre = 0}, 3) test_vim_str2nr('-42', flags, {len = 1, num = 0, unum = 0, pre = 0}, 1) - test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 0) - test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 4) + test_vim_str2nr('-42x', flags, {len = 0}, 0) + test_vim_str2nr('-42x', flags, {len = 0}, 4) + test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 0, false) + test_vim_str2nr('-42x', flags, {len = 3, num = -42, unum = 42, pre = 0}, 4, false) end end) itp('works with binary numbers', function() @@ -144,62 +156,77 @@ describe('vim_str2nr()', function() test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0) test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) - test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0b101', flags, {len = 0}, 2) + test_vim_str2nr( '0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr( '0b101', flags, {len = 3, num = 1, unum = 1, pre = bin}, 3) test_vim_str2nr( '0b101', flags, {len = 4, num = 2, unum = 2, pre = bin}, 4) test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 5) test_vim_str2nr( '0b101', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6) - test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0) - test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6) + test_vim_str2nr( '0b1012', flags, {len = 0}, 0) + test_vim_str2nr( '0b1012', flags, {len = 0}, 6) + test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 0, false) + test_vim_str2nr( '0b1012', flags, {len = 5, num = 5, unum = 5, pre = bin}, 6, false) test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0) test_vim_str2nr('-0b101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) - test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0b101', flags, {len = 0}, 3) + test_vim_str2nr('-0b101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0b101', flags, {len = 4, num = -1, unum = 1, pre = bin}, 4) test_vim_str2nr('-0b101', flags, {len = 5, num = -2, unum = 2, pre = bin}, 5) test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 6) test_vim_str2nr('-0b101', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7) - test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0) - test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7) + test_vim_str2nr('-0b1012', flags, {len = 0}, 0) + test_vim_str2nr('-0b1012', flags, {len = 0}, 7) + test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 0, false) + test_vim_str2nr('-0b1012', flags, {len = 6, num = -5, unum = 5, pre = bin}, 7, false) test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0) test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) - test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0B101', flags, {len = 0}, 2) + test_vim_str2nr( '0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr( '0B101', flags, {len = 3, num = 1, unum = 1, pre = BIN}, 3) test_vim_str2nr( '0B101', flags, {len = 4, num = 2, unum = 2, pre = BIN}, 4) test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 5) test_vim_str2nr( '0B101', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6) - test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0) - test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6) + test_vim_str2nr( '0B1012', flags, {len = 0}, 0) + test_vim_str2nr( '0B1012', flags, {len = 0}, 6) + test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 0, false) + test_vim_str2nr( '0B1012', flags, {len = 5, num = 5, unum = 5, pre = BIN}, 6, false) test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0) test_vim_str2nr('-0B101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) - test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0B101', flags, {len = 0}, 3) + test_vim_str2nr('-0B101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0B101', flags, {len = 4, num = -1, unum = 1, pre = BIN}, 4) test_vim_str2nr('-0B101', flags, {len = 5, num = -2, unum = 2, pre = BIN}, 5) test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 6) test_vim_str2nr('-0B101', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7) - test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0) - test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7) + test_vim_str2nr('-0B1012', flags, {len = 0}, 0) + test_vim_str2nr('-0B1012', flags, {len = 0}, 7) + test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 0, false) + test_vim_str2nr('-0B1012', flags, {len = 6, num = -5, unum = 5, pre = BIN}, 7, false) if flags > lib.STR2NR_FORCE then test_vim_str2nr('-101', flags, {len = 4, num = -5, unum = 5, pre = 0}, 0) end end end) - itp('works with octal numbers', function() + itp('works with octal numbers (0 prefix)', function() for _, flags in ipairs({ lib.STR2NR_OCT, lib.STR2NR_OCT + lib.STR2NR_BIN, lib.STR2NR_OCT + lib.STR2NR_HEX, + lib.STR2NR_OCT + lib.STR2NR_OOCT, lib.STR2NR_ALL, lib.STR2NR_FORCE + lib.STR2NR_OCT, + lib.STR2NR_FORCE + lib.STR2NR_OOCT, + lib.STR2NR_FORCE + lib.STR2NR_OCT + lib.STR2NR_OOCT, }) do local oct if flags > lib.STR2NR_FORCE then @@ -218,8 +245,10 @@ describe('vim_str2nr()', function() test_vim_str2nr( '0548', flags, {len = 3, num = 44, unum = 44, pre = oct}, 3) test_vim_str2nr( '054', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4) - test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4) - test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 0) + test_vim_str2nr( '054x', flags, {len = 0}, 4) + test_vim_str2nr( '054x', flags, {len = 0}, 0) + test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 4, false) + test_vim_str2nr( '054x', flags, {len = 3, num = 44, unum = 44, pre = oct}, 0, false) test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0) test_vim_str2nr('-054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) @@ -229,13 +258,110 @@ describe('vim_str2nr()', function() test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = oct}, 4) test_vim_str2nr('-054', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5) - test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5) - test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0) + test_vim_str2nr('-054x', flags, {len = 0}, 5) + test_vim_str2nr('-054x', flags, {len = 0}, 0) + test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 5, false) + test_vim_str2nr('-054x', flags, {len = 4, num = -44, unum = 44, pre = oct}, 0, false) if flags > lib.STR2NR_FORCE then test_vim_str2nr('-54', flags, {len = 3, num = -44, unum = 44, pre = 0}, 0) - test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 5) - test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 0) + test_vim_str2nr('-0548', flags, {len = 0}, 5) + test_vim_str2nr('-0548', flags, {len = 0}, 0) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 5, false) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 0, false) + else + test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 5) + test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 0) + end + end + end) + itp('works with octal numbers (0o or 0O prefix)', function() + for _, flags in ipairs({ + lib.STR2NR_OOCT, + lib.STR2NR_OOCT + lib.STR2NR_BIN, + lib.STR2NR_OOCT + lib.STR2NR_HEX, + lib.STR2NR_OCT + lib.STR2NR_OOCT, + lib.STR2NR_OCT + lib.STR2NR_OOCT + lib.STR2NR_BIN, + lib.STR2NR_OCT + lib.STR2NR_OOCT + lib.STR2NR_HEX, + lib.STR2NR_ALL, + lib.STR2NR_FORCE + lib.STR2NR_OCT, + lib.STR2NR_FORCE + lib.STR2NR_OOCT, + lib.STR2NR_FORCE + lib.STR2NR_OCT + lib.STR2NR_OOCT, + }) do + local oct + local OCT + if flags > lib.STR2NR_FORCE then + oct = 0 + OCT = 0 + else + oct = ('o'):byte() + OCT = ('O'):byte() + end + + test_vim_str2nr( '0o054', flags, {len = 5, num = 44, unum = 44, pre = oct}, 0) + test_vim_str2nr( '0o054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr( '0o054', flags, {len = 0}, 2) + test_vim_str2nr( '0o054', flags, {len = 3, num = 0, unum = 0, pre = oct}, 3) + test_vim_str2nr( '0o054', flags, {len = 4, num = 5, unum = 5, pre = oct}, 4) + test_vim_str2nr( '0o054', flags, {len = 5, num = 44, unum = 44, pre = oct}, 5) + test_vim_str2nr( '0o0548', flags, {len = 5, num = 44, unum = 44, pre = oct}, 5) + test_vim_str2nr( '0o054', flags, {len = 5, num = 44, unum = 44, pre = oct}, 6) + + test_vim_str2nr( '0o054x', flags, {len = 0}, 6) + test_vim_str2nr( '0o054x', flags, {len = 0}, 0) + test_vim_str2nr( '0o054x', flags, {len = 5, num = 44, unum = 44, pre = oct}, 6, false) + test_vim_str2nr( '0o054x', flags, {len = 5, num = 44, unum = 44, pre = oct}, 0, false) + + test_vim_str2nr('-0o054', flags, {len = 6, num = -44, unum = 44, pre = oct}, 0) + test_vim_str2nr('-0o054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr('-0o054', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr('-0o054', flags, {len = 0}, 3) + test_vim_str2nr('-0o054', flags, {len = 4, num = 0, unum = 0, pre = oct}, 4) + test_vim_str2nr('-0o054', flags, {len = 5, num = -5, unum = 5, pre = oct}, 5) + test_vim_str2nr('-0o054', flags, {len = 6, num = -44, unum = 44, pre = oct}, 6) + test_vim_str2nr('-0o0548', flags, {len = 6, num = -44, unum = 44, pre = oct}, 6) + test_vim_str2nr('-0o054', flags, {len = 6, num = -44, unum = 44, pre = oct}, 7) + + test_vim_str2nr('-0o054x', flags, {len = 0}, 7) + test_vim_str2nr('-0o054x', flags, {len = 0}, 0) + test_vim_str2nr('-0o054x', flags, {len = 6, num = -44, unum = 44, pre = oct}, 7, false) + test_vim_str2nr('-0o054x', flags, {len = 6, num = -44, unum = 44, pre = oct}, 0, false) + + test_vim_str2nr( '0O054', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 0) + test_vim_str2nr( '0O054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr( '0O054', flags, {len = 0}, 2) + test_vim_str2nr( '0O054', flags, {len = 3, num = 0, unum = 0, pre = OCT}, 3) + test_vim_str2nr( '0O054', flags, {len = 4, num = 5, unum = 5, pre = OCT}, 4) + test_vim_str2nr( '0O054', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 5) + test_vim_str2nr( '0O0548', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 5) + test_vim_str2nr( '0O054', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 6) + + test_vim_str2nr( '0O054x', flags, {len = 0}, 6) + test_vim_str2nr( '0O054x', flags, {len = 0}, 0) + test_vim_str2nr( '0O054x', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 6, false) + test_vim_str2nr( '0O054x', flags, {len = 5, num = 44, unum = 44, pre = OCT}, 0, false) + + test_vim_str2nr('-0O054', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 0) + test_vim_str2nr('-0O054', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) + test_vim_str2nr('-0O054', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr('-0O054', flags, {len = 0}, 3) + test_vim_str2nr('-0O054', flags, {len = 4, num = 0, unum = 0, pre = OCT}, 4) + test_vim_str2nr('-0O054', flags, {len = 5, num = -5, unum = 5, pre = OCT}, 5) + test_vim_str2nr('-0O054', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 6) + test_vim_str2nr('-0O0548', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 6) + test_vim_str2nr('-0O054', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 7) + + test_vim_str2nr('-0O054x', flags, {len = 0}, 7) + test_vim_str2nr('-0O054x', flags, {len = 0}, 0) + test_vim_str2nr('-0O054x', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 7, false) + test_vim_str2nr('-0O054x', flags, {len = 6, num = -44, unum = 44, pre = OCT}, 0, false) + + if flags > lib.STR2NR_FORCE then + test_vim_str2nr('-0548', flags, {len = 0}, 5) + test_vim_str2nr('-0548', flags, {len = 0}, 0) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 5, false) + test_vim_str2nr('-0548', flags, {len = 4, num = -44, unum = 44, pre = 0}, 0, false) + test_vim_str2nr('-055', flags, {len = 4, num = -45, unum = 45, pre = 0}, 0) else test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 5) test_vim_str2nr('-0548', flags, {len = 5, num = -548, unum = 548, pre = 0}, 0) @@ -268,53 +394,85 @@ describe('vim_str2nr()', function() test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 0) test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) - test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0x101', flags, {len = 0}, 2) + test_vim_str2nr( '0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr( '0x101', flags, {len = 3, num = 1, unum = 1, pre = hex}, 3) test_vim_str2nr( '0x101', flags, {len = 4, num = 16, unum = 16, pre = hex}, 4) test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 5) test_vim_str2nr( '0x101', flags, {len = 5, num = 257, unum =257, pre = hex}, 6) - test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 0) - test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 6) + test_vim_str2nr( '0x101G', flags, {len = 0}, 0) + test_vim_str2nr( '0x101G', flags, {len = 0}, 6) + test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 0, false) + test_vim_str2nr( '0x101G', flags, {len = 5, num = 257, unum =257, pre = hex}, 6, false) test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 0) test_vim_str2nr('-0x101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) - test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0x101', flags, {len = 0}, 3) + test_vim_str2nr('-0x101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0x101', flags, {len = 4, num = -1, unum = 1, pre = hex}, 4) test_vim_str2nr('-0x101', flags, {len = 5, num = -16, unum = 16, pre = hex}, 5) test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 6) test_vim_str2nr('-0x101', flags, {len = 6, num =-257, unum =257, pre = hex}, 7) - test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 0) - test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 7) + test_vim_str2nr('-0x101G', flags, {len = 0}, 0) + test_vim_str2nr('-0x101G', flags, {len = 0}, 7) + test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 0, false) + test_vim_str2nr('-0x101G', flags, {len = 6, num =-257, unum =257, pre = hex}, 7, false) test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0) test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) - test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2) + test_vim_str2nr( '0X101', flags, {len = 0}, 2) + test_vim_str2nr( '0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 2, false) test_vim_str2nr( '0X101', flags, {len = 3, num = 1, unum = 1, pre = HEX}, 3) test_vim_str2nr( '0X101', flags, {len = 4, num = 16, unum = 16, pre = HEX}, 4) test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 5) test_vim_str2nr( '0X101', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6) - test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0) - test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6) + test_vim_str2nr( '0X101G', flags, {len = 0}, 0) + test_vim_str2nr( '0X101G', flags, {len = 0}, 6) + test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 0, false) + test_vim_str2nr( '0X101G', flags, {len = 5, num = 257, unum =257, pre = HEX}, 6, false) test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0) test_vim_str2nr('-0X101', flags, {len = 1, num = 0, unum = 0, pre = 0 }, 1) test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 2) - test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3) + test_vim_str2nr('-0X101', flags, {len = 0}, 3) + test_vim_str2nr('-0X101', flags, {len = 2, num = 0, unum = 0, pre = 0 }, 3, false) test_vim_str2nr('-0X101', flags, {len = 4, num = -1, unum = 1, pre = HEX}, 4) test_vim_str2nr('-0X101', flags, {len = 5, num = -16, unum = 16, pre = HEX}, 5) test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 6) test_vim_str2nr('-0X101', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7) - test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0) - test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7) + test_vim_str2nr('-0X101G', flags, {len = 0}, 0) + test_vim_str2nr('-0X101G', flags, {len = 0}, 7) + test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 0, false) + test_vim_str2nr('-0X101G', flags, {len = 6, num =-257, unum =257, pre = HEX}, 7, false) if flags > lib.STR2NR_FORCE then test_vim_str2nr('-101', flags, {len = 4, num = -257, unum = 257, pre = 0}, 0) end end end) + -- Test_str2nr() in test_functions.vim already tests normal usage + itp('works with weirdly quoted numbers', function() + local flags = lib.STR2NR_DEC + lib.STR2NR_QUOTE + test_vim_str2nr("'027", flags, {len = 0}, 0) + test_vim_str2nr("'027", flags, {len = 0}, 0, false) + test_vim_str2nr("1'2'3'4", flags, {len = 7, num = 1234, unum = 1234, pre = 0}, 0) + + -- counter-intuitive, but like Vim, strict=true should partially accept + -- these: (' and - are not alpha-numeric) + test_vim_str2nr("7''331", flags, {len = 1, num = 7, unum = 7, pre = 0}, 0) + test_vim_str2nr("123'x4", flags, {len = 3, num = 123, unum = 123, pre = 0}, 0) + test_vim_str2nr("1337'", flags, {len = 4, num = 1337, unum = 1337, pre = 0}, 0) + test_vim_str2nr("-'", flags, {len = 1, num = 0, unum = 0, pre = 0}, 0) + + flags = lib.STR2NR_HEX + lib.STR2NR_QUOTE + local hex = ('x'):byte() + test_vim_str2nr("0x'abcd", flags, {len = 0}, 0) + test_vim_str2nr("0x'abcd", flags, {len = 1, num = 0, unum = 0, pre = 0}, 0, false) + test_vim_str2nr("0xab''cd", flags, {len = 4, num = 171, unum = 171, pre = hex}, 0) + end) end) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 7c03005529..d81e272877 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2623,7 +2623,7 @@ describe('typval.c', function() describe('check_lock()', function() local function tv_check_lock(lock, name, name_len, emsg) return check_emsg(function() - return lib.tv_check_lock(lock, name, name_len) + return lib.var_check_lock(lock, name, name_len) end, emsg) end itp('works', function() diff --git a/test/unit/marktree_spec.lua b/test/unit/marktree_spec.lua index 56acc0f93e..cd9c7bef13 100644 --- a/test/unit/marktree_spec.lua +++ b/test/unit/marktree_spec.lua @@ -186,5 +186,20 @@ describe('marktree', function() lib.marktree_check(tree) shadoworder(tree, shadow, iter2) end + + -- Check iterator validity for 2 specific edge cases: + -- https://github.com/neovim/neovim/pull/14719 + lib.marktree_clear(tree) + for i = 1,20 do + lib.marktree_put(tree, i, i, false) + end + + lib.marktree_itr_get(tree, 10, 10, iter) + lib.marktree_del_itr(tree, iter, false) + eq(11, iter[0].node.key[iter[0].i].pos.col) + + lib.marktree_itr_get(tree, 11, 11, iter) + lib.marktree_del_itr(tree, iter, false) + eq(12, iter[0].node.key[iter[0].i].pos.col) end) end) diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index 3786bc2122..1073855a7d 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -89,10 +89,6 @@ local Gcc = { get_defines_extra_flags = {'-std=c99', '-dM', '-E'}, get_declarations_extra_flags = {'-std=c99', '-P', '-E'}, } -if ffi.abi("32bit") then - table.insert(Gcc.get_defines_extra_flags, '-m32') - table.insert(Gcc.get_declarations_extra_flags, '-m32') -end function Gcc:define(name, args, val) local define = '-D' .. name diff --git a/test/unit/undo_spec.lua b/test/unit/undo_spec.lua index 616c6fbe3d..f7f8d26d58 100644 --- a/test/unit/undo_spec.lua +++ b/test/unit/undo_spec.lua @@ -38,7 +38,7 @@ child_call_once(function() -- -- compute a hash for this undofile buffer_hash = ffi.new('char_u[32]') - undo.u_compute_hash(buffer_hash) + undo.u_compute_hash(file_buffer, buffer_hash) end) diff --git a/test/unit/viml/expressions/parser_spec.lua b/test/unit/viml/expressions/parser_spec.lua index 032baf6578..8342044b5e 100644 --- a/test/unit/viml/expressions/parser_spec.lua +++ b/test/unit/viml/expressions/parser_spec.lua @@ -52,6 +52,32 @@ local predefined_hl_defs = { QuickFixLine=true, Substitute=true, Whitespace=true, + Error=true, + Todo=true, + String=true, + Character=true, + Number=true, + Boolean=true, + Float=true, + Function=true, + Conditional=true, + Repeat=true, + Label=true, + Operator=true, + Keyword=true, + Exception=true, + Include=true, + Define=true, + Macro=true, + PreCondit=true, + StorageClass=true, + Structure=true, + Typedef=true, + Tag=true, + SpecialChar=true, + Delimiter=true, + SpecialComment=true, + Debug=true, -- From highlight_init_(dark|light) ColorColumn=true, @@ -83,8 +109,6 @@ local predefined_hl_defs = { Visual=true, WarningMsg=true, Normal=true, - - -- From syncolor.vim, if &background Comment=true, Constant=true, Special=true, @@ -94,36 +118,6 @@ local predefined_hl_defs = { Type=true, Underlined=true, Ignore=true, - - -- From syncolor.vim, below if &background - Error=true, - Todo=true, - - -- From syncolor.vim, links at the bottom - String=true, - Character=true, - Number=true, - Boolean=true, - Float=true, - Function=true, - Conditional=true, - Repeat=true, - Label=true, - Operator=true, - Keyword=true, - Exception=true, - Include=true, - Define=true, - Macro=true, - PreCondit=true, - StorageClass=true, - Structure=true, - Typedef=true, - Tag=true, - SpecialChar=true, - Delimiter=true, - SpecialComment=true, - Debug=true, } local nvim_hl_defs = {} diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index e248232909..72c4b72a21 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -21,7 +21,17 @@ if(HAS_OG_FLAG) set(DEFAULT_MAKE_CFLAGS CFLAGS+=-Og ${DEFAULT_MAKE_CFLAGS}) endif() -set(DEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/usr" CACHE PATH "Dependencies install directory.") +if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD") + # pkg-config 29.2 has a bug on OpenBSD which causes it to drop any paths that + # *contain* system include paths. To avoid this, we prefix what would be + # "/usr/include" as "/_usr/include". + # This check is also performed in the root CMakeLists.txt + # https://github.com/neovim/neovim/pull/14745#issuecomment-860201794 + set(DEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/_usr" CACHE PATH "Dependencies install directory.") +else() + set(DEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/usr" CACHE PATH "Dependencies install directory.") +endif() + set(DEPS_BIN_DIR "${DEPS_INSTALL_DIR}/bin" CACHE PATH "Dependencies binary install directory.") set(DEPS_LIB_DIR "${DEPS_INSTALL_DIR}/lib" CACHE PATH "Dependencies library install directory.") set(DEPS_BUILD_DIR "${CMAKE_BINARY_DIR}/build" CACHE PATH "Dependencies build directory.") @@ -165,19 +175,19 @@ set(LIBTERMKEY_SHA256 6945bd3c4aaa83da83d80a045c5563da4edd7d0374c62c0d35aec09eb3 set(LIBVTERM_URL http://www.leonerd.org.uk/code/libvterm/libvterm-0.1.4.tar.gz) set(LIBVTERM_SHA256 bc70349e95559c667672fc8c55b9527d9db9ada0fb80a3beda533418d782d3dd) -set(LUV_VERSION 1.30.1-1) +set(LUV_VERSION 1.40.0-0) set(LUV_URL https://github.com/luvit/luv/archive/${LUV_VERSION}.tar.gz) -set(LUV_SHA256 2b17921e2e18094df6221e3cd291c82d4569e50d8c081518d3e775dceae267cf) +set(LUV_SHA256 23167a3d5dbc1e30df1f106ffae0a4c5bd50993da2066dc1f7e1842bb9fb6cd0) -set(LUA_COMPAT53_URL https://github.com/keplerproject/lua-compat-5.3/archive/v0.7.tar.gz) -set(LUA_COMPAT53_SHA256 bec3a23114a3d9b3218038309657f0f506ad10dfbc03bb54e91da7e5ffdba0a2) +set(LUA_COMPAT53_URL https://github.com/keplerproject/lua-compat-5.3/archive/v0.9.tar.gz) +set(LUA_COMPAT53_SHA256 ad05540d2d96a48725bb79a1def35cf6652a4e2ec26376e2617c8ce2baa6f416) set(GPERF_URL https://github.com/neovim/deps/raw/ff5b4b18a87397a8564016071ae64f64bcd8c635/opt/gperf-3.1.tar.gz) set(GPERF_SHA256 588546b945bba4b70b6a3a616e80b4ab466e3f33024a352fc2198112cdbb3ae2) # cat.exe curl.exe curl-ca-bundle.crt diff.exe tee.exe xxd.exe -set(WINTOOLS_URL https://github.com/neovim/deps/raw/9efd42511dcab26995fa3490f2319b270949159e/opt/win32tools.zip) -set(WINTOOLS_SHA256 378069d88a34e7f7283622213569020a2aba7a54f0b431fba28690f7eae3af9c) +set(WINTOOLS_URL https://github.com/neovim/deps/raw/d66e306abf5b846484b4f2adffd896bce7e065d2/opt/win32tools.zip) +set(WINTOOLS_SHA256 2fb2f8d69070b3f16e029913fb95008e6be33893d77fc358012396c275a0fdb7) set(WINGUI_URL https://github.com/equalsraf/neovim-qt/releases/download/v0.2.16.1/neovim-qt.zip) set(WINGUI_SHA256 ddb4492db03da407703fb0ab271c4eb060250d1a7d71200e2b3b981cb0de59de) @@ -199,9 +209,8 @@ set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc891 set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/5aa0bbb.tar.gz) set(TREESITTER_C_SHA256 a5dcb37460d83002dfae7f9a208170ddbc9a047f231b9d6b75da7d36d707db2f) -# This is 0.19.4+2, fixes some segfaults https://github.com/tree-sitter/tree-sitter/commit/89e1157a299596f3ce2155ba9fd69d5e2c03d3e6 -set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/89e1157a299596f3ce2155ba9fd69d5e2c03d3e6.zip) -set(TREESITTER_SHA256 d5be9fd92cbf783680f921b2adccbd721b9aa8b2c445114a216b6544330f252c) +set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.20.0.tar.gz) +set(TREESITTER_SHA256 4a8070b9de17c3b8096181fe8530320ab3e8cca685d8bee6a3e8d164b5fb47da) if(USE_BUNDLED_UNIBILIUM) include(BuildUnibilium) diff --git a/third-party/cmake/BuildLuarocks.cmake b/third-party/cmake/BuildLuarocks.cmake index c5595bf840..a961ec69c7 100644 --- a/third-party/cmake/BuildLuarocks.cmake +++ b/third-party/cmake/BuildLuarocks.cmake @@ -203,7 +203,7 @@ if(USE_BUNDLED_BUSTED) if(USE_BUNDLED_LIBUV) list(APPEND LUV_ARGS LIBUV_DIR=${HOSTDEPS_INSTALL_DIR}) endif() - SET(LUV_PRIVATE_ARGS LUA_COMPAT53_INCDIR=${DEPS_BUILD_DIR}/src/lua-compat-5.3) + SET(LUV_PRIVATE_ARGS LUA_COMPAT53_INCDIR=${DEPS_BUILD_DIR}/src/lua-compat-5.3/c-api) add_custom_command(OUTPUT ${ROCKS_DIR}/luv COMMAND ${LUAROCKS_BINARY} ARGS make ${LUAROCKS_BUILDARGS} ${LUV_ARGS} ${LUV_PRIVATE_ARGS} diff --git a/third-party/cmake/BuildLuv.cmake b/third-party/cmake/BuildLuv.cmake index ac4196f910..f5d45c7ff7 100644 --- a/third-party/cmake/BuildLuv.cmake +++ b/third-party/cmake/BuildLuv.cmake @@ -71,6 +71,7 @@ set(LUV_CONFIGURE_COMMAND_COMMON -DLUA_COMPAT53_DIR=${DEPS_BUILD_DIR}/src/lua-compat-5.3 -DWITH_SHARED_LIBUV=ON -DBUILD_SHARED_LIBS=OFF + -DBUILD_STATIC_LIBS=ON -DBUILD_MODULE=OFF) if(USE_BUNDLED_LUAJIT) diff --git a/third-party/cmake/DownloadAndExtractFile.cmake b/third-party/cmake/DownloadAndExtractFile.cmake index e008fa8a8a..abb1ddc81a 100644 --- a/third-party/cmake/DownloadAndExtractFile.cmake +++ b/third-party/cmake/DownloadAndExtractFile.cmake @@ -59,41 +59,48 @@ string(REPLACE ";" "-" fname "${fname}") set(file ${DOWNLOAD_DIR}/${fname}) message(STATUS "file: ${file}") -message(STATUS "downloading... - src='${URL}' - dst='${file}' - timeout='${timeout_msg}'") - -file(DOWNLOAD ${URL} ${file} - ${timeout_args} - ${hash_args} - STATUS status - LOG log) - -list(GET status 0 status_code) -list(GET status 1 status_string) - -if(NOT status_code EQUAL 0) - # Retry on certain errors, e.g. CURLE_COULDNT_RESOLVE_HOST, which is often - # seen with libtermkey (www.leonerd.org.uk). - if((status_code EQUAL 6) # "Couldn't resolve host name" - OR (status_code EQUAL 7)) # "Couldn't connect to server" - message(STATUS "warning: retrying '${URL}' (${status_string}, status ${status_code})") - execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 10) - file(DOWNLOAD ${URL} ${file} - ${timeout_args} - ${hash_args} - STATUS status - LOG log) - list(GET status 0 status_code) - list(GET status 1 status_string) - endif() +set(EXISTING_SHA256 "") +if(EXISTS ${file}) + file(SHA256 ${file} EXISTING_SHA256) +endif() + +if(NOT EXISTING_SHA256 STREQUAL ${EXPECTED_SHA256}) + message(STATUS "downloading... + src='${URL}' + dst='${file}' + timeout='${timeout_msg}'") + + file(DOWNLOAD ${URL} ${file} + ${timeout_args} + ${hash_args} + STATUS status + LOG log) + + list(GET status 0 status_code) + list(GET status 1 status_string) + if(NOT status_code EQUAL 0) - message(FATAL_ERROR "error: downloading '${URL}' failed - status_code: ${status_code} - status_string: ${status_string} - log: ${log} -") + # Retry on certain errors, e.g. CURLE_COULDNT_RESOLVE_HOST, which is often + # seen with libtermkey (www.leonerd.org.uk). + if((status_code EQUAL 6) # "Couldn't resolve host name" + OR (status_code EQUAL 7)) # "Couldn't connect to server" + message(STATUS "warning: retrying '${URL}' (${status_string}, status ${status_code})") + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 10) + file(DOWNLOAD ${URL} ${file} + ${timeout_args} + ${hash_args} + STATUS status + LOG log) + list(GET status 0 status_code) + list(GET status 1 status_string) + endif() + if(NOT status_code EQUAL 0) + message(FATAL_ERROR "error: downloading '${URL}' failed + status_code: ${status_code} + status_string: ${status_string} + log: ${log} + ") + endif() endif() endif() |