Merge remote-tracking branch 'upstream/master' into libcall

This commit is contained in:
James McCoy 2019-12-15 21:17:16 -05:00
commit 6566251d14
No known key found for this signature in database
GPG Key ID: DFE691AE331BA3DB
409 changed files with 35808 additions and 8971 deletions

44
.builds/freebsd.yml Normal file
View File

@ -0,0 +1,44 @@
image: freebsd/12.x
packages:
- cmake
- gmake
- ninja
- libtool
- sha
- automake
- pkgconf
- unzip
- wget
- gettext
- python
- libffi
sources:
- https://github.com/neovim/neovim
environment:
SOURCEHUT: 1
LANG: en_US.UTF-8
CMAKE_EXTRA_FLAGS: -DTRAVIS_CI_BUILD=ON -DMIN_LOG_LEVEL=3
tasks:
- build-deps: |
cd neovim
gmake deps
- build: |
cd neovim
gmake CMAKE_BUILD_TYPE=Release CMAKE_EXTRA_FLAGS="${CMAKE_EXTRA_FLAGS}" nvim
- functionaltest: |
cd neovim
gmake functionaltest
- unittest: |
cd neovim
gmake unittest
# Unfortunately, oldtest is tanking hard on sourcehut's FreeBSD instance
# and not producing any logs as a result. So don't do this task for now.
# Ref: https://github.com/neovim/neovim/pull/11477#discussion_r352095005.
# - test-oldtest: |
# cd neovim
# gmake oldtest

View File

@ -16,26 +16,28 @@ packages:
sources:
- https://github.com/neovim/neovim
environment:
SOURCEHUT: 1
LC_CTYPE: en_US.UTF-8
CMAKE_EXTRA_FLAGS: -DTRAVIS_CI_BUILD=ON -DMIN_LOG_LEVEL=3
tasks:
- build: |
- build-deps: |
export AUTOCONF_VERSION=2.69
export AUTOMAKE_VERSION=1.15
cd neovim
mkdir .deps
cd .deps
mkdir neovim/.deps
cd neovim/.deps
cmake -G Ninja ../third-party/
cmake --build . --config Debug
cd ..
mkdir build
cd build
cmake -G Ninja -DMIN_LOG_LEVEL=3 ..
- build: |
mkdir neovim/build
cd neovim/build
cmake -G Ninja $CMAKE_EXTRA_FLAGS ..
cmake --build . --config Debug
./bin/nvim --version
- test: |
export LC_CTYPE=en_US.UTF-8
# functional tests
- functionaltest: |
cd neovim/build
# cmake --build . --config Debug --target functionaltest
# oldtests
cd ..
cmake --build . --config Debug --target functionaltest
- oldtest: |
cd neovim
gmake oldtest

1
.gitattributes vendored
View File

@ -1 +1,2 @@
*.h linguist-language=C
src/nvim/testdir/test42.in diff

3
.gitignore vendored
View File

@ -10,7 +10,9 @@ compile_commands.json
/dist/
/.deps/
/tmp/
/.clangd/
.DS_Store
*.mo
.*.sw?
*~
@ -42,6 +44,7 @@ tags
/src/nvim/testdir/valgrind.*
/src/nvim/testdir/.gdbinit
/runtime/indent/testdir/*.out
+runtime/indent/testdir/*.fail
# Generated by src/nvim/testdir/runnvim.sh.
/src/nvim/testdir/*.tlog

View File

@ -2,6 +2,8 @@
-- Ignore W211 (unused variable) with preload files.
files["**/preload.lua"] = {ignore = { "211" }}
-- Allow vim module to modify itself, but only here.
files["src/nvim/lua/vim.lua"] = {ignore = { "122/vim" }}
-- Don't report unused self arguments of methods.
self = false

22
.luacov Normal file
View File

@ -0,0 +1,22 @@
-- Configuration file for LuaCov
local source = require("lfs").currentdir()
local function pesc(s)
assert(type(s) == 'string', s)
return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
end
return {
include = {
-- Absolute paths (starting with source dir, not hidden (i.e. .deps)).
pesc(source) .. "[/\\][^.].+",
-- Relative (non-hidden) paths.
'^[^/\\.]',
},
modules = {
['vim'] = 'runtime/lua/vim/shared.lua'
},
}
-- vim: ft=lua tw=80 sw=2 et

View File

@ -4,6 +4,19 @@ language: c
env:
global:
# Encrypted environment variables, see
# http://docs.travis-ci.com/user/encryption-keys/
#
# SNAP_SECRET_KEY: generated by:
# travis encrypt SNAP_SECRET_KEY=xx --add
# https://github.com/neovim/neovim/pull/11428
# snapcraft key expires after 1 year. Steps to refresh it:
# 1. snapcraft enable-ci travis --refresh
# 2. mv .snapcraft/travis_snapcraft.cfg ci/snap/travis_snapcraft.cfg
# 3. Copy after_success command to ci/snap/deploy.sh from .travis.yml
# 4. Undo changes to .travis.yml
- secure: hd0qn2u8ABbJg5Bx4pBRcUQbKYFmcSHoecyHIPTCnGJT+NI41Bvm/IkN/N5DhBF+LbD3Q2nmR/dzI5H/dqS7RxMFvEx1DuFLendFHHX3MYf0AuKpXYY3gwgMTmqx8p/v6srlU7RBGWNGzHCWqksAem+EIWCe3I7WvfdKo1/DV/Y=
# Set "false" to force rebuild of third-party dependencies.
- CACHE_ENABLE=true
# Build directory for Neovim.
@ -24,10 +37,6 @@ env:
-DDEPS_PREFIX=$DEPS_BUILD_DIR/usr
-DMIN_LOG_LEVEL=3"
- DEPS_CMAKE_FLAGS="-DUSE_BUNDLED_GPERF=OFF"
# Additional CMake flags for 32-bit builds.
- CMAKE_FLAGS_32BIT="-DCMAKE_SYSTEM_LIBRARY_PATH=/lib32:/usr/lib32:/usr/local/lib32
-DCMAKE_IGNORE_PATH=/lib:/usr/lib:/usr/local/lib
-DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/cmake/i386-linux-gnu.toolchain.cmake"
# Environment variables for Clang sanitizers.
- ASAN_OPTIONS="detect_leaks=1:check_initialization_order=1:log_path=$LOG_DIR/asan"
- TSAN_OPTIONS="log_path=$LOG_DIR/tsan"
@ -99,6 +108,8 @@ jobs:
- GCOV=gcov-9
- CMAKE_FLAGS="$CMAKE_FLAGS -DUSE_GCOV=ON"
- GCOV_ERROR_FILE="/tmp/libgcov-errors.log"
- USE_LUACOV=1
- BUSTED_ARGS="--coverage"
- *common-job-env
addons:
apt:
@ -136,6 +147,8 @@ jobs:
compiler: gcc
env:
- BUILD_32BIT=ON
- CMAKE_FLAGS="$CMAKE_FLAGS -m32 -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/cmake/i386-linux-gnu.toolchain.cmake"
- DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -m32 -DCMAKE_TOOLCHAIN_FILE=$TRAVIS_BUILD_DIR/cmake/i386-linux-gnu.toolchain.cmake"
# Minimum required CMake.
- CMAKE_URL=https://cmake.org/files/v2.8/cmake-2.8.12-Linux-i386.sh
- *common-job-env
@ -145,6 +158,42 @@ jobs:
env:
- CLANG_SANITIZER=TSAN
- *common-job-env
- if: type != pull_request
name: snap
os: linux
env:
- LC_ALL: C.UTF-8
- LANG: C.UTF-8
- SNAPCRAFT_ENABLE_SILENT_REPORT: y
- SNAPCRAFT_ENABLE_DEVELOPER_DEBUG: y
addons:
snaps:
- name: snapcraft
channel: stable
classic: true
- name: http
- name: transfer
- name: lxd
channel: stable
# Override default before_install, before_cache.
before_install: /bin/true
before_cache: /bin/true
install: ci/snap/install.sh
before_script: echo "Building snap..."
script: ci/snap/script.sh
after_success: ci/snap/after_success.sh
deploy:
skip_cleanup: true
provider: script
script: ci/snap/deploy.sh
on:
branch: master
allow_failures:
- env:
- LC_ALL: C.UTF-8
- LANG: C.UTF-8
- SNAPCRAFT_ENABLE_SILENT_REPORT: y
- SNAPCRAFT_ENABLE_DEVELOPER_DEBUG: y
fast_finish: true
before_install: ci/before_install.sh

View File

@ -6,6 +6,13 @@
cmake_minimum_required(VERSION 2.8.12)
project(nvim C)
if(POLICY CMP0065)
cmake_policy(SET CMP0065 NEW)
endif()
if(POLICY CMP0060)
cmake_policy(SET CMP0060 NEW)
endif()
# Point CMake at any custom modules we may ship
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
@ -113,47 +120,32 @@ 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 4)
set(NVIM_VERSION_MINOR 5)
set(NVIM_VERSION_PATCH 0)
set(NVIM_VERSION_PRERELEASE "") # for package maintainers
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
# API level
set(NVIM_API_LEVEL 6) # Bump this after any API change.
set(NVIM_API_LEVEL 7) # Bump this after any API change.
set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change.
set(NVIM_API_PRERELEASE false)
file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR)
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC NVIM_VERSION_COMMIT)
if(NVIM_VERSION_COMMIT) # is a git repo
git_describe(NVIM_VERSION_MEDIUM)
# `git describe` annotates the most recent tagged release; for pre-release
# builds we must replace that with the unreleased version.
string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.[0-9]+"
"v${NVIM_VERSION_MAJOR}.${NVIM_VERSION_MINOR}.${NVIM_VERSION_PATCH}"
NVIM_VERSION_MEDIUM
${NVIM_VERSION_MEDIUM})
endif()
set(NVIM_API_PRERELEASE true)
set(NVIM_VERSION_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
# NVIM_VERSION_CFLAGS set further below.
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Minimize logging for release-type builds.
if(NOT CMAKE_C_FLAGS_RELEASE MATCHES DMIN_LOG_LEVEL)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DMIN_LOG_LEVEL=3")
endif()
if(NOT CMAKE_C_FLAGS_MINSIZEREL MATCHES DMIN_LOG_LEVEL)
set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -DMIN_LOG_LEVEL=3")
endif()
if(NOT CMAKE_C_FLAGS_RELWITHDEBINFO MATCHES DMIN_LOG_LEVEL)
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DMIN_LOG_LEVEL=3")
endif()
# Log level (MIN_LOG_LEVEL in log.h)
if("${MIN_LOG_LEVEL}" MATCHES "^$")
message(STATUS "MIN_LOG_LEVEL not specified, default is 1 (INFO)")
# Minimize logging for release-type builds.
if(CMAKE_BUILD_TYPE STREQUAL "Release"
OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"
OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
message(STATUS "MIN_LOG_LEVEL not specified, default is 3 (ERROR) for release builds")
set(MIN_LOG_LEVEL 3)
else()
message(STATUS "MIN_LOG_LEVEL not specified, default is 1 (INFO)")
set(MIN_LOG_LEVEL 1)
endif()
else()
if(NOT MIN_LOG_LEVEL MATCHES "^[0-3]$")
message(FATAL_ERROR "invalid MIN_LOG_LEVEL: " ${MIN_LOG_LEVEL})
@ -309,8 +301,10 @@ if(UNIX)
if(HAS_FSTACK_PROTECTOR_STRONG_FLAG)
add_compile_options(-fstack-protector-strong)
link_libraries(-fstack-protector-strong)
elseif(HAS_FSTACK_PROTECTOR_FLAG)
add_compile_options(-fstack-protector --param ssp-buffer-size=4)
link_libraries(-fstack-protector --param ssp-buffer-size=4)
endif()
endif()
@ -323,10 +317,10 @@ if(HAS_DIAG_COLOR_FLAG)
endif()
endif()
option(TRAVIS_CI_BUILD "Travis/QuickBuild CI, extra flags will be set" OFF)
option(TRAVIS_CI_BUILD "Travis/sourcehut CI, extra flags will be set" OFF)
if(TRAVIS_CI_BUILD)
message(STATUS "Travis/QuickBuild CI build enabled")
message(STATUS "Travis/sourcehut CI build enabled")
add_compile_options(-Werror)
if(DEFINED ENV{BUILD_32BIT})
# Get some test coverage for unsigned char
@ -375,6 +369,13 @@ include_directories(SYSTEM ${MSGPACK_INCLUDE_DIRS})
find_package(LibLUV 1.30.0 REQUIRED)
include_directories(SYSTEM ${LIBLUV_INCLUDE_DIRS})
find_package(UTF8PROC REQUIRED)
include_directories(SYSTEM ${UTF8PROC_INCLUDE_DIRS})
if(WIN32)
add_definitions(-DUTF8PROC_STATIC)
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)
@ -486,18 +487,19 @@ include(LuaHelpers)
set(LUA_DEPENDENCIES lpeg mpack bit)
if(NOT LUA_PRG)
foreach(CURRENT_LUA_PRG luajit lua5.1 lua5.2 lua)
# If LUA_PRG is set find_program() will not search
unset(LUA_PRG CACHE)
unset(_CHECK_LUA_PRG CACHE)
unset(LUA_PRG_WORKS)
find_program(LUA_PRG ${CURRENT_LUA_PRG})
find_program(_CHECK_LUA_PRG ${CURRENT_LUA_PRG})
if(LUA_PRG)
check_lua_deps(${LUA_PRG} "${LUA_DEPENDENCIES}" LUA_PRG_WORKS)
if(_CHECK_LUA_PRG)
check_lua_deps(${_CHECK_LUA_PRG} "${LUA_DEPENDENCIES}" LUA_PRG_WORKS)
if(LUA_PRG_WORKS)
set(LUA_PRG "${_CHECK_LUA_PRG}" CACHE FILEPATH "Path to a program.")
break()
endif()
endif()
endforeach()
unset(_CHECK_LUA_PRG CACHE)
else()
check_lua_deps(${LUA_PRG} "${LUA_DEPENDENCIES}" LUA_PRG_WORKS)
endif()
@ -560,10 +562,7 @@ if(BUSTED_PRG)
endif()
set(UNITTEST_PREREQS nvim-test unittest-headers)
set(FUNCTIONALTEST_PREREQS nvim printargs-test shell-test streams-test ${GENERATED_HELP_TAGS})
if(NOT WIN32)
list(APPEND FUNCTIONALTEST_PREREQS tty-test)
endif()
set(FUNCTIONALTEST_PREREQS nvim printenv-test printargs-test shell-test streams-test tty-test ${GENERATED_HELP_TAGS})
set(BENCHMARK_PREREQS nvim tty-test)
# Useful for automated build systems, if they want to manually run the tests.

View File

@ -85,7 +85,7 @@ the VCS/git logs more valuable.
### Automated builds (CI)
Each pull request must pass the automated builds on [Travis CI], [QuickBuild]
Each pull request must pass the automated builds on [Travis CI], [sourcehut]
and [AppVeyor].
- CI builds are compiled with [`-Werror`][gcc-warnings], so compiler warnings
@ -100,14 +100,19 @@ and [AppVeyor].
- The [lint](#lint) build checks modified lines _and their immediate
neighbors_, to encourage incrementally updating the legacy style to meet our
[style](#style). (See [#3174][3174] for background.)
- [How to investigate QuickBuild failures](https://github.com/neovim/neovim/pull/4718#issuecomment-217631350)
- QuickBuild uses this invocation:
```
mkdir -p build/${params.get("buildType")} \
&& cd build/${params.get("buildType")} \
&& cmake -G "Unix Makefiles" -DBUSTED_OUTPUT_TYPE=TAP -DCMAKE_BUILD_TYPE=${params.get("buildType")}
-DTRAVIS_CI_BUILD=ON ../.. && ${node.getAttribute("make", "make")}
VERBOSE=1 nvim unittest-prereqs functionaltest-prereqs
- CI for freebsd and openbsd runs on [sourcehut].
- To get a backtrace on freebsd (after connecting via ssh):
```sh
sudo pkg install tmux # If you want tmux.
lldb build/bin/nvim -c nvim.core
# To get a full backtrace:
# 1. Rebuild with debug info.
rm -rf nvim.core build
gmake CMAKE_BUILD_TYPE=RelWithDebInfo CMAKE_EXTRA_FLAGS="-DTRAVIS_CI_BUILD=ON -DMIN_LOG_LEVEL=3" nvim
# 2. Run the failing test to generate a new core file.
TEST_FILE=test/functional/foo.lua gmake functionaltest
lldb build/bin/nvim -c nvim.core
```
### Clang scan-build
@ -223,7 +228,7 @@ as context, use the `-W` argument as well.
[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
[QuickBuild]: http://neovim-qb.szakmeister.net/dashboard
[sourcehut]: https://builds.sr.ht/~jmk
[AppVeyor]: https://ci.appveyor.com/project/neovim/neovim
[Merge a Vim patch]: https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-Vim
[Clang report]: https://neovim.io/doc/reports/clang/

View File

@ -119,8 +119,13 @@ oldtest: | nvim build/runtime/doc/tags
ifeq ($(strip $(TEST_FILE)),)
+$(SINGLE_MAKE) -C src/nvim/testdir NVIM_PRG="$(realpath build/bin/nvim)" $(MAKEOVERRIDES)
else
+$(SINGLE_MAKE) -C src/nvim/testdir NVIM_PRG="$(realpath build/bin/nvim)" NEW_TESTS=$(TEST_FILE) SCRIPTS= $(MAKEOVERRIDES)
@# Handle TEST_FILE=test_foo{,.res,.vim}.
+$(SINGLE_MAKE) -C src/nvim/testdir NVIM_PRG="$(realpath build/bin/nvim)" SCRIPTS= $(MAKEOVERRIDES) $(patsubst %.vim,%,$(patsubst %.res,%,$(TEST_FILE)))
endif
# Build oldtest by specifying the relative .vim filename.
.PHONY: phony_force
src/nvim/testdir/%.vim: phony_force
+$(SINGLE_MAKE) -C src/nvim/testdir NVIM_PRG="$(realpath build/bin/nvim)" SCRIPTS= $(MAKEOVERRIDES) $(patsubst src/nvim/testdir/%.vim,%,$@)
build/runtime/doc/tags helptags: | nvim
+$(BUILD_CMD) -C build runtime/doc/tags
@ -138,6 +143,14 @@ functionaltest-lua: | nvim
lualint: | build/.ran-cmake deps
$(BUILD_CMD) -C build lualint
shlint:
@shellcheck --version | head -n 2
shellcheck scripts/vim-patch.sh
_opt_shlint:
@command -v shellcheck && { $(MAKE) shlint; exit $$?; } \
|| echo "SKIP: shlint (shellcheck not found)"
pylint:
flake8 contrib/ scripts/ src/ test/
@ -158,6 +171,7 @@ clean:
+test -d build && $(BUILD_CMD) -C build clean || true
$(MAKE) -C src/nvim/testdir clean
$(MAKE) -C runtime/doc clean
$(MAKE) -C runtime/indent clean
distclean:
rm -rf $(DEPS_BUILD_DIR) build
@ -187,16 +201,16 @@ appimage:
appimage-%:
bash scripts/genappimage.sh $*
lint: check-single-includes clint lualint _opt_pylint
lint: check-single-includes clint lualint _opt_pylint _opt_shlint
# Generic pattern rules, allowing for `make build/bin/nvim` etc.
# Does not work with "Unix Makefiles".
ifeq ($(BUILD_TYPE),Ninja)
build/%:
build/%: phony_force
$(BUILD_CMD) -C build $(patsubst build/%,%,$@)
$(DEPS_BUILD_DIR)/%:
$(DEPS_BUILD_DIR)/%: phony_force
$(BUILD_CMD) -C $(DEPS_BUILD_DIR) $(patsubst $(DEPS_BUILD_DIR)/%,%,$@)
endif
.PHONY: test lualint pylint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage checkprefix
.PHONY: test lualint pylint shlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage checkprefix

View File

@ -67,7 +67,7 @@ To skip bundled (`third-party/*`) dependencies:
1. Install the dependencies using a package manager.
```
sudo apt install gperf luajit luarocks libuv1-dev libluajit-5.1-dev libunibilium-dev libmsgpack-dev libtermkey-dev libvterm-dev
sudo apt install gperf luajit luarocks libuv1-dev libluajit-5.1-dev libunibilium-dev libmsgpack-dev libtermkey-dev libvterm-dev libutf8proc-dev
sudo luarocks build mpack
sudo luarocks build lpeg
sudo luarocks build inspect

View File

@ -24,7 +24,7 @@ matrix:
fast_finish: true
install: []
before_build:
- ps: Install-Product node 8
- ps: Install-Product node 10
build_script:
- powershell ci\build.ps1
after_build:
@ -40,6 +40,3 @@ cache:
artifacts:
- path: build/Neovim.zip
- path: build/bin/nvim.exe
branches:
only:
- master

View File

@ -3,10 +3,6 @@
set -e
set -o pipefail
if [[ "${CI_TARGET}" == lint ]]; then
exit
fi
echo 'Python info:'
(
set -x
@ -47,12 +43,12 @@ if [[ "${TRAVIS_OS_NAME}" == osx ]] || [ ! -f ~/.nvm/nvm.sh ]; then
fi
source ~/.nvm/nvm.sh
nvm install --lts
nvm use --lts
nvm install 10
nvm use 10
if [[ -n "$CMAKE_URL" ]]; then
echo "Installing custom CMake: $CMAKE_URL"
curl --retry 5 --silent --fail -o /tmp/cmake-installer.sh "$CMAKE_URL"
curl --retry 5 --silent --show-error --fail -o /tmp/cmake-installer.sh "$CMAKE_URL"
mkdir -p "$HOME/.local/bin" /opt/cmake-custom
bash /tmp/cmake-installer.sh --prefix=/opt/cmake-custom --skip-license
ln -sfn /opt/cmake-custom/bin/cmake "$HOME/.local/bin/cmake"

View File

@ -35,5 +35,10 @@ fi
# Compile dependencies.
build_deps
# Install cluacov for Lua coverage.
if [[ "$USE_LUACOV" == 1 ]]; then
"${DEPS_BUILD_DIR}/usr/bin/luarocks" install cluacov
fi
rm -rf "${LOG_DIR}"
mkdir -p "${LOG_DIR}"

View File

@ -1,10 +1,11 @@
$ErrorActionPreference = 'stop'
Set-PSDebug -Strict -Trace 1
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
$isPullRequest = ($env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT -ne $null)
$env:CONFIGURATION -match '^(?<compiler>\w+)_(?<bits>32|64)(?:-(?<option>\w+))?$'
$compiler = $Matches.compiler
$compileOption = $Matches.option
$compileOption = if ($Matches -contains 'option') {$Matches.option} else {''}
$bits = $Matches.bits
$cmakeBuildType = $(if ($env:CMAKE_BUILD_TYPE -ne $null) {$env:CMAKE_BUILD_TYPE} else {'RelWithDebInfo'});
$buildDir = [System.IO.Path]::GetFullPath("$(pwd)")
@ -23,7 +24,6 @@ $uploadToCodeCov = $false
function exitIfFailed() {
if ($LastExitCode -ne 0) {
Set-PSDebug -Off
exit $LastExitCode
}
}
@ -46,6 +46,10 @@ if ($compiler -eq 'MINGW') {
$nvimCmakeVars['USE_GCOV'] = 'ON'
$uploadToCodecov = $true
$env:GCOV = "C:\msys64\mingw$bits\bin\gcov"
# Setup/build Lua coverage.
$env:USE_LUACOV = 1
$env:BUSTED_ARGS = "--coverage"
}
# These are native MinGW builds, but they use the toolchain inside
# MSYS2, this allows using all the dependencies and tools available
@ -94,6 +98,27 @@ npm.cmd install -g neovim
Get-Command -CommandType Application neovim-node-host.cmd
npm.cmd link neovim
$env:TREE_SITTER_DIR = $env:USERPROFILE + "\tree-sitter-build"
mkdir "$env:TREE_SITTER_DIR\bin"
$xbits = if ($bits -eq '32') {'x86'} else {'x64'}
Invoke-WebRequest -UseBasicParsing -Uri "https://github.com/tree-sitter/tree-sitter/releases/download/0.15.9/tree-sitter-windows-$xbits.gz" -OutFile tree-sitter.exe.gz
C:\msys64\usr\bin\gzip -d tree-sitter.exe.gz
Invoke-WebRequest -UseBasicParsing -Uri "https://codeload.github.com/tree-sitter/tree-sitter-c/zip/v0.15.2" -OutFile tree_sitter_c.zip
Expand-Archive .\tree_sitter_c.zip -DestinationPath .
cd tree-sitter-c-0.15.2
..\tree-sitter.exe test
if (-Not (Test-Path -PathType Leaf "$env:TREE_SITTER_DIR\bin\c.dll")) {
exit 1
}
if ($compiler -eq 'MSVC') {
# Required for LuaRocks (https://github.com/luarocks/luarocks/issues/1039#issuecomment-507296940).
$env:VCINSTALLDIR = "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.16.27023/"
}
function convertToCmakeArgs($vars) {
return $vars.GetEnumerator() | foreach { "-D$($_.Key)=$($_.Value)" }
}
@ -113,26 +138,26 @@ cmake --build . --config $cmakeBuildType -- $cmakeGeneratorArgs ; exitIfFailed
# Ensure that the "win32" feature is set.
.\bin\nvim -u NONE --headless -c 'exe !has(\"win32\").\"cq\"' ; exitIfFailed
if ($env:USE_LUACOV -eq 1) {
& $env:DEPS_PREFIX\luarocks\luarocks.bat install cluacov
}
# Functional tests
# The $LastExitCode from MSBuild can't be trusted
$failed = $false
# Temporarily turn off tracing to reduce log file output
Set-PSDebug -Off
cmake --build . --config $cmakeBuildType --target functionaltest -- $cmakeGeneratorArgs 2>&1 |
foreach { $failed = $failed -or
$_ -match 'functional tests failed with error'; $_ }
if ($failed) {
if ($uploadToCodecov) {
bash -l /c/projects/neovim/ci/common/submit_coverage.sh functionaltest
}
exit $LastExitCode
}
Set-PSDebug -Strict -Trace 1
if ($uploadToCodecov) {
if ($env:USE_LUACOV -eq 1) {
& $env:DEPS_PREFIX\bin\luacov.bat
}
bash -l /c/projects/neovim/ci/common/submit_coverage.sh functionaltest
}
if ($failed) {
exit $LastExitCode
}
# Old tests
# Add MSYS to path, required for e.g. `find` used in test scripts.

View File

@ -18,9 +18,6 @@ build_make() {
}
build_deps() {
if test "${BUILD_32BIT}" = ON ; then
DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} ${CMAKE_FLAGS_32BIT}"
fi
if test "${FUNCTIONALTEST}" = "functionaltest-lua" \
|| test "${CLANG_SANITIZER}" = "ASAN_UBSAN" ; then
DEPS_CMAKE_FLAGS="${DEPS_CMAKE_FLAGS} -DUSE_BUNDLED_LUA=ON"
@ -53,9 +50,6 @@ prepare_build() {
if test -n "${CLANG_SANITIZER}" ; then
CMAKE_FLAGS="${CMAKE_FLAGS} -DCLANG_${CLANG_SANITIZER}=ON"
fi
if test "${BUILD_32BIT}" = ON ; then
CMAKE_FLAGS="${CMAKE_FLAGS} ${CMAKE_FLAGS_32BIT}"
fi
mkdir -p "${BUILD_DIR}"
cd "${BUILD_DIR}"

View File

@ -43,3 +43,14 @@ fi
# Cleanup always, especially collected data.
find . \( -name '*.gcov' -o -name '*.gcda' \) -ls -delete | wc -l
rm -f coverage.xml
# Upload Lua coverage (generated manually on AppVeyor/Windows).
if [ "$USE_LUACOV" = 1 ] && [ "$1" != "oldtest" ]; then
if [ -x "${DEPS_BUILD_DIR}/usr/bin/luacov" ]; then
"${DEPS_BUILD_DIR}/usr/bin/luacov"
fi
if ! "$codecov_sh" -f luacov.report.out -X gcov -X fix -Z -F "lua,${codecov_flags}"; then
echo "codecov upload failed."
fi
rm luacov.stats.out
fi

View File

@ -4,7 +4,7 @@ set -e
set -o pipefail
if [[ "${CI_TARGET}" == lint ]]; then
python -m pip -q install --user --upgrade flake8
python3 -m pip -q install --user --upgrade flake8
exit
fi
@ -24,3 +24,27 @@ gem install --no-document --version ">= 0.8.0" neovim
echo "Install neovim npm package"
npm install -g neovim
npm link neovim
echo "Install tree-sitter npm package"
# FIXME
# https://github.com/tree-sitter/tree-sitter/commit/e14e285a1087264a8c74a7c62fcaecc49db9d904
# If queries added to tree-sitter-c, we can use latest tree-sitter-cli
npm install -g tree-sitter-cli@v0.15.9
echo "Install tree-sitter c parser"
curl "https://codeload.github.com/tree-sitter/tree-sitter-c/tar.gz/v0.15.2" -o tree_sitter_c.tar.gz
tar xf tree_sitter_c.tar.gz
cd tree-sitter-c-0.15.2
export TREE_SITTER_DIR=$HOME/tree-sitter-build/
mkdir -p "$TREE_SITTER_DIR/bin"
if [[ "$BUILD_32BIT" != "ON" ]]; then
# builds c parser in $HOME/tree-sitter-build/bin/c.(so|dylib)
tree-sitter test
else
# no tree-sitter binary for 32bit linux, so fake it (no tree-sitter unit tests)
cd src/
gcc -m32 -o "$TREE_SITTER_DIR/bin/c.so" -shared parser.c -I.
fi
test -f "$TREE_SITTER_DIR/bin/c.so"

View File

@ -20,6 +20,10 @@ enter_suite 'pylint'
run_test 'make pylint' pylint
exit_suite --continue
enter_suite 'shlint'
run_test 'make shlint' shlint
exit_suite --continue
enter_suite single-includes
CLICOLOR_FORCE=1 run_test_wd \
--allow-hang \

View File

@ -19,6 +19,8 @@ exit_suite --continue
enter_suite tests
export TREE_SITTER_DIR=$HOME/tree-sitter-build/
if test "$CLANG_SANITIZER" != "TSAN" ; then
# Additional threads are only created when the builtin UI starts, which
# doesn't happen in the unit/functional tests

14
ci/snap/after_success.sh Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -e
set -o pipefail
RESULT_SNAP=$(find ./ -name "*.snap")
sudo snap install "$RESULT_SNAP" --dangerous --classic
/snap/bin/nvim --version
SHA256=$(sha256sum "$RESULT_SNAP")
echo "SHA256: ${SHA256} ."

21
ci/snap/deploy.sh Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -e
set -o pipefail
# not a tagged release, abort
# [[ "$TRAVIS_TAG" != "$TRAVIS_BRANCH" ]] && exit 0
mkdir -p .snapcraft
# shellcheck disable=SC2154
openssl aes-256-cbc -K "$encrypted_ece1c4844832_key" -iv "$encrypted_ece1c4844832_iv" \
-in ci/snap/travis_snapcraft.cfg -out .snapcraft/snapcraft.cfg -d
SNAP=$(find ./ -name "*.snap")
# TODO(justinmk): This always does `edge` until we enable tagged builds.
if [[ "$SNAP" =~ "dirty" || "$SNAP" =~ "nightly" ]]; then
snapcraft push "$SNAP" --release edge
else
snapcraft push "$SNAP" --release candidate
fi

10
ci/snap/install.sh Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -e
set -o pipefail
sudo apt update
sudo /snap/bin/lxd.migrate -yes
sudo /snap/bin/lxd waitready
sudo /snap/bin/lxd init --auto

8
ci/snap/script.sh Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -e
set -o pipefail
mkdir -p "$TRAVIS_BUILD_DIR/snaps-cache"
sudo snapcraft --use-lxd

Binary file not shown.

16
cmake/FindUTF8PROC.cmake Normal file
View File

@ -0,0 +1,16 @@
# - Try to find utf8proc
# Once done this will define
# UTF8PROC_FOUND - System has utf8proc
# UTF8PROC_INCLUDE_DIRS - The utf8proc include directories
# UTF8PROC_LIBRARIES - The libraries needed to use utf8proc
include(LibFindMacros)
set(UTF8PROC_NAMES utf8proc)
if(MSVC)
# "utf8proc_static" is used for MSVC (when built statically from third-party).
# https://github.com/JuliaStrings/utf8proc/commit/0975bf9b6.
list(APPEND UTF8PROC_NAMES utf8proc_static)
endif()
libfind_pkg_detect(UTF8PROC utf8proc FIND_PATH utf8proc.h FIND_LIBRARY ${UTF8PROC_NAMES})
libfind_process(UTF8PROC REQUIRED)

View File

@ -3,6 +3,13 @@ function(get_compile_flags _compile_flags)
set(compile_flags "<CMAKE_C_COMPILER> <CFLAGS> <BUILD_TYPE_CFLAGS> <COMPILE_OPTIONS><COMPILE_DEFINITIONS> <INCLUDES>")
# Get C compiler.
if(CMAKE_C_COMPILER_ARG1)
string(REPLACE
"<CMAKE_C_COMPILER>"
"<CMAKE_C_COMPILER> ${CMAKE_C_COMPILER_ARG1}"
compile_flags
"${compile_flags}")
endif()
string(REPLACE
"<CMAKE_C_COMPILER>"
"${CMAKE_C_COMPILER}"

View File

@ -1,180 +0,0 @@
# https://github.com/rpavlik/cmake-modules
#
# - Returns a version string from Git
#
# These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system.
#
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
#
# Returns the refspec and sha hash of the current head revision
#
# git_describe(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs.
#
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact
# matching tag.
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
# We must run the following at "include" time, not at function call time,
# to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
function(get_git_dir _gitdir)
# check FORCED_GIT_DIR first
if(FORCED_GIT_DIR)
set(${_gitdir} ${FORCED_GIT_DIR} PARENT_SCOPE)
return()
endif()
# check GIT_DIR in environment
set(GIT_DIR $ENV{GIT_DIR})
if(NOT GIT_DIR)
set(GIT_PARENT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(GIT_DIR ${GIT_PARENT_DIR}/.git)
endif()
# .git dir not found, search parent directories
while(NOT EXISTS ${GIT_DIR})
set(GIT_PREVIOUS_PARENT ${GIT_PARENT_DIR})
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
return()
endif()
set(GIT_DIR ${GIT_PARENT_DIR}/.git)
endwhile()
# check if this is a submodule
if(NOT IS_DIRECTORY ${GIT_DIR})
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
endif()
set(${_gitdir} ${GIT_DIR} PARENT_SCOPE)
endfunction()
function(get_git_head_revision _refspecvar _hashvar)
get_git_dir(GIT_DIR)
if(NOT GIT_DIR)
return()
endif()
set(GIT_DATA ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data)
if(NOT EXISTS ${GIT_DATA})
file(MAKE_DIRECTORY ${GIT_DATA})
endif()
if(NOT EXISTS ${GIT_DIR}/HEAD)
return()
endif()
set(HEAD_FILE ${GIT_DATA}/HEAD)
configure_file(${GIT_DIR}/HEAD ${HEAD_FILE} COPYONLY)
configure_file(${_gitdescmoddir}/GetGitRevisionDescription.cmake.in
${GIT_DATA}/grabRef.cmake
@ONLY)
include(${GIT_DATA}/grabRef.cmake)
set(${_refspecvar} ${HEAD_REF} PARENT_SCOPE)
set(${_hashvar} ${HEAD_HASH} PARENT_SCOPE)
endfunction()
function(git_describe _var)
get_git_dir(GIT_DIR)
if(NOT GIT_DIR)
return()
endif()
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
get_git_head_revision(refspec hash)
if(NOT hash)
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
return()
endif()
execute_process(COMMAND
${GIT_EXECUTABLE}
describe
${hash}
${ARGN}
WORKING_DIRECTORY
${GIT_DIR}
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var} ${out} PARENT_SCOPE)
endfunction()
function(git_timestamp _var)
get_git_dir(GIT_DIR)
if(NOT GIT_DIR)
return()
endif()
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
return()
endif()
get_git_head_revision(refspec hash)
if(NOT hash)
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
return()
endif()
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --format="%ci" ${hash} ${ARGN}
WORKING_DIRECTORY ${GIT_DIR}
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(res EQUAL 0)
string(REGEX REPLACE "[-\" :]" "" out ${out})
string(SUBSTRING ${out} 0 12 out)
else()
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var} ${out} PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var} ${out} PARENT_SCOPE)
endfunction()

View File

@ -1,38 +0,0 @@
#
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
set(HEAD_HASH "${HEAD_REF}")
endif()
else()
# detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()

View File

@ -25,3 +25,6 @@ coverage:
changes: no
comment: off
ignore:
- "src/tree_sitter"

View File

@ -5,7 +5,11 @@
#define NVIM_VERSION_MINOR @NVIM_VERSION_MINOR@
#define NVIM_VERSION_PATCH @NVIM_VERSION_PATCH@
#define NVIM_VERSION_PRERELEASE "@NVIM_VERSION_PRERELEASE@"
#cmakedefine NVIM_VERSION_MEDIUM "@NVIM_VERSION_MEDIUM@"
#ifndef NVIM_VERSION_MEDIUM
# include "auto/versiondef_git.h"
#endif
#define NVIM_API_LEVEL @NVIM_API_LEVEL@
#define NVIM_API_LEVEL_COMPAT @NVIM_API_LEVEL_COMPAT@

View File

@ -25,6 +25,12 @@
#
# CMAKE_BUILD_TYPE := Debug
# With non-Debug builds interprocedural optimization (IPO) (which includes
# link-time optimization (LTO)) is enabled by default, which causes the link
# step to take a significant amout of time, which is relevant when building
# often. You can disable it explicitly:
# CMAKE_EXTRA_FLAGS += -DENABLE_LTO=OFF
# Log levels: 0 (DEBUG), 1 (INFO), 2 (WARNING), 3 (ERROR)
# Default is 1 (INFO) unless CMAKE_BUILD_TYPE is Release or RelWithDebInfo.
# CMAKE_EXTRA_FLAGS += -DMIN_LOG_LEVEL=1
@ -42,6 +48,7 @@
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LUAROCKS=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_MSGPACK=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_UNIBILIUM=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_UTF8PROC=OFF
#
# Or disable all bundled dependencies at once.
#

View File

@ -38,9 +38,10 @@ endfunction
" Handler for s:system() function.
function! s:system_handler(jobid, data, event) dict abort
if a:event ==# 'stderr'
let self.stderr .= join(a:data, '')
if !self.ignore_stderr
if self.add_stderr_to_output
let self.output .= join(a:data, '')
else
let self.stderr .= join(a:data, '')
endif
elseif a:event ==# 'stdout'
let self.output .= join(a:data, '')
@ -64,7 +65,7 @@ function! s:system(cmd, ...) abort
let stdin = a:0 ? a:1 : ''
let ignore_error = a:0 > 2 ? a:3 : 0
let opts = {
\ 'ignore_stderr': a:0 > 1 ? a:2 : 0,
\ 'add_stderr_to_output': a:0 > 1 ? a:2 : 0,
\ 'output': '',
\ 'stderr': '',
\ 'on_stdout': function('s:system_handler'),
@ -89,8 +90,15 @@ function! s:system(cmd, ...) abort
call health#report_error(printf('Command timed out: %s', s:shellify(a:cmd)))
call jobstop(jobid)
elseif s:shell_error != 0 && !ignore_error
call health#report_error(printf("Command error (job=%d, exit code %d): `%s` (in %s)\nOutput: %s\nStderr: %s",
\ jobid, s:shell_error, s:shellify(a:cmd), string(getcwd()), opts.output, opts.stderr))
let emsg = printf("Command error (job=%d, exit code %d): `%s` (in %s)",
\ jobid, s:shell_error, s:shellify(a:cmd), string(getcwd()))
if !empty(opts.output)
let emsg .= "\noutput: " . opts.output
end
if !empty(opts.stderr)
let emsg .= "\nstderr: " . opts.stderr
end
call health#report_error(emsg)
endif
return opts.output
@ -194,7 +202,8 @@ function! s:version_info(python) abort
let nvim_path = s:trim(s:system([
\ a:python, '-c',
\ 'import sys; sys.path.remove(""); ' .
\ 'import sys; ' .
\ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' .
\ 'import neovim; print(neovim.__file__)']))
if s:shell_error || empty(nvim_path)
return [python_version, 'unable to load neovim Python module', pypi_version,
@ -257,6 +266,22 @@ function! s:check_bin(bin) abort
return 1
endfunction
" Check "loaded" var for given a:provider.
" Returns 1 if the caller should return (skip checks).
function! s:disabled_via_loaded_var(provider) abort
let loaded_var = 'g:loaded_'.a:provider.'_provider'
if exists(loaded_var) && !exists('*provider#'.a:provider.'#Call')
let v = eval(loaded_var)
if 0 is v
call health#report_info('Disabled ('.loaded_var.'='.v.').')
return 1
else
call health#report_info('Disabled ('.loaded_var.'='.v.'). This might be due to some previous error.')
endif
endif
return 0
endfunction
function! s:check_python(version) abort
call health#report_start('Python ' . a:version . ' provider (optional)')
@ -264,11 +289,10 @@ function! s:check_python(version) abort
let python_exe = ''
let venv = exists('$VIRTUAL_ENV') ? resolve($VIRTUAL_ENV) : ''
let host_prog_var = pyname.'_host_prog'
let loaded_var = 'g:loaded_'.pyname.'_provider'
let python_multiple = []
if exists(loaded_var) && !exists('*provider#'.pyname.'#Call')
call health#report_info('Disabled ('.loaded_var.'='.eval(loaded_var).'). This might be due to some previous error.')
if s:disabled_via_loaded_var(pyname)
return
endif
let [pyenv, pyenv_root] = s:check_for_pyenv()
@ -286,7 +310,7 @@ function! s:check_python(version) abort
let python_exe = pyname
endif
" No Python executable could `import neovim`.
" No Python executable could `import neovim`, or host_prog_var was used.
if !empty(pythonx_errors)
call health#report_error('Python provider error:', pythonx_errors)
@ -476,9 +500,7 @@ endfunction
function! s:check_ruby() abort
call health#report_start('Ruby provider (optional)')
let loaded_var = 'g:loaded_ruby_provider'
if exists(loaded_var) && !exists('*provider#ruby#Call')
call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var))
if s:disabled_via_loaded_var('ruby')
return
endif
@ -532,9 +554,7 @@ endfunction
function! s:check_node() abort
call health#report_start('Node.js provider (optional)')
let loaded_var = 'g:loaded_node_provider'
if exists(loaded_var) && !exists('*provider#node#Call')
call health#report_info('Disabled. '.loaded_var.'='.eval(loaded_var))
if s:disabled_via_loaded_var('node')
return
endif

View File

@ -1,4 +1,4 @@
" Maintainer: Anmol Sethi <anmol@aubble.com>
" Maintainer: Anmol Sethi <hi@nhooyr.io>
if exists('s:loaded_man')
finish
@ -64,33 +64,20 @@ function! man#open_page(count, count1, mods, ...) abort
return
endtry
call s:push_tag()
let bufname = 'man://'.name.(empty(sect)?'':'('.sect.')')
let [l:buf, l:save_tfu] = [bufnr(), &tagfunc]
try
set eventignore+=BufReadCmd
set tagfunc=man#goto_tag
let l:target = l:name . '(' . l:sect . ')'
if a:mods !~# 'tab' && s:find_man()
execute 'silent keepalt edit' fnameescape(bufname)
execute 'silent keepalt tag' l:target
else
execute 'silent keepalt' a:mods 'split' fnameescape(bufname)
execute 'silent keepalt' a:mods 'stag' l:target
endif
finally
set eventignore-=BufReadCmd
endtry
try
let page = s:get_page(path)
catch
if a:mods =~# 'tab' || !s:find_man()
" a new window was opened
close
endif
call s:error(v:exception)
return
call setbufvar(l:buf, '&tagfunc', l:save_tfu)
endtry
let b:man_sect = sect
call s:put_page(page)
endfunction
function! man#read_page(ref) abort
@ -152,7 +139,7 @@ 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/….
" Hard-wrap: driven by `man`.
let manwidth = !get(g:,'man_hardwrap') ? 999 : (empty($MANWIDTH) ? winwidth(0) : $MANWIDTH)
let manwidth = !get(g:,'man_hardwrap', 1) ? 999 : (empty($MANWIDTH) ? winwidth(0) : $MANWIDTH)
" Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db).
" http://comments.gmane.org/gmane.editors.vim.devel/29085
" Set MAN_KEEP_FORMATTING so Debian man doesn't discard backspaces.
@ -163,6 +150,9 @@ endfunction
function! s:put_page(page) abort
setlocal modifiable
setlocal noreadonly
setlocal noswapfile
" git-ls-files(1) is all one keyword/tag-target
setlocal iskeyword+=(,)
silent keepjumps %delete _
silent put =a:page
while getline(1) =~# '^\s*$'
@ -254,24 +244,6 @@ function! s:verify_exists(sect, name) abort
return s:extract_sect_and_name_path(path) + [path]
endfunction
let s:tag_stack = []
function! s:push_tag() abort
let s:tag_stack += [{
\ 'buf': bufnr('%'),
\ 'lnum': line('.'),
\ 'col': col('.'),
\ }]
endfunction
function! man#pop_tag() abort
if !empty(s:tag_stack)
let tag = remove(s:tag_stack, -1)
execute 'silent' tag['buf'].'buffer'
call cursor(tag['lnum'], tag['col'])
endif
endfunction
" extracts the name and sect out of 'path/name.sect'
function! s:extract_sect_and_name_path(path) abort
let tail = fnamemodify(a:path, ':t')
@ -284,20 +256,16 @@ function! s:extract_sect_and_name_path(path) abort
endfunction
function! s:find_man() abort
if &filetype ==# 'man'
return 1
elseif winnr('$') ==# 1
return 0
endif
let thiswin = winnr()
while 1
wincmd w
if &filetype ==# 'man'
let l:win = 1
while l:win <= winnr('$')
let l:buf = winbufnr(l:win)
if getbufvar(l:buf, '&filetype', '') ==# 'man'
execute l:win.'wincmd w'
return 1
elseif thiswin ==# winnr()
return 0
endif
let l:win += 1
endwhile
return 0
endfunction
function! s:error(msg) abort
@ -360,14 +328,18 @@ function! man#complete(arg_lead, cmd_line, cursor_pos) abort
return s:complete(sect, sect, name)
endfunction
function! s:complete(sect, psect, name) abort
function! s:get_paths(sect, name) abort
try
let mandirs = join(split(s:system(['man', s:find_arg]), ':\|\n'), ',')
catch
call s:error(v:exception)
return
endtry
let pages = globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1)
return globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1)
endfunction
function! s:complete(sect, psect, name) abort
let pages = s:get_paths(a:sect, a:name)
" We remove duplicates in case the same manpage in different languages was found.
return uniq(sort(map(pages, 's:format_candidate(v:val, a:psect)'), 'i'))
endfunction
@ -387,6 +359,10 @@ function! s:format_candidate(path, psect) abort
endfunction
function! man#init_pager() abort
" https://github.com/neovim/neovim/issues/6828
let og_modifiable = &modifiable
setlocal modifiable
if getline(1) =~# '^\s*$'
silent keepjumps 1delete _
else
@ -404,6 +380,31 @@ function! man#init_pager() abort
if -1 == match(bufname('%'), 'man:\/\/') " Avoid duplicate buffers, E95.
execute 'silent file man://'.tolower(fnameescape(ref))
endif
let &l:modifiable = og_modifiable
endfunction
function! man#goto_tag(pattern, flags, info) abort
let [l:sect, l:name] = man#extract_sect_and_name_ref(a:pattern)
let l:paths = s:get_paths(l:sect, l:name)
let l:structured = []
for l:path in l:paths
let l:n = s:extract_sect_and_name_path(l:path)[1]
let l:structured += [{ 'name': l:n, 'path': l:path }]
endfor
" sort by relevance - exact matches first, then the previous order
call sort(l:structured, { a, b -> a.name ==? l:name ? -1 : b.name ==? l:name ? 1 : 0 })
return map(l:structured, {
\ _, entry -> {
\ 'name': entry.name,
\ 'filename': 'man://' . entry.path,
\ 'cmd': '1'
\ }
\ })
endfunction
call s:init()

View File

@ -688,10 +688,6 @@ fun! netrw#Explore(indx,dosplit,style,...)
endif
" save registers
if has("clipboard")
sil! let keepregstar = @*
sil! let keepregplus = @+
endif
sil! let keepregslash= @/
" if dosplit
@ -915,10 +911,6 @@ fun! netrw#Explore(indx,dosplit,style,...)
" call Decho("..case Nexplore with starpat=".starpat.": (indx=".indx.")",'~'.expand("<slnum>"))
if !exists("w:netrw_explore_list") " sanity check
NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Nexplore or <s-down> improperly; see help for netrw-starstar",40)
if has("clipboard")
sil! let @* = keepregstar
sil! let @+ = keepregplus
endif
sil! let @/ = keepregslash
" call Dret("netrw#Explore")
return
@ -940,10 +932,6 @@ fun! netrw#Explore(indx,dosplit,style,...)
" call Decho("case Pexplore with starpat=".starpat.": (indx=".indx.")",'~'.expand("<slnum>"))
if !exists("w:netrw_explore_list") " sanity check
NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Pexplore or <s-up> improperly; see help for netrw-starstar",41)
if has("clipboard")
sil! let @* = keepregstar
sil! let @+ = keepregplus
endif
sil! let @/ = keepregslash
" call Dret("netrw#Explore")
return
@ -995,10 +983,6 @@ fun! netrw#Explore(indx,dosplit,style,...)
catch /^Vim\%((\a\+)\)\=:E480/
keepalt call netrw#ErrorMsg(s:WARNING,'no files matched pattern<'.pattern.'>',45)
if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif
if has("clipboard")
sil! let @* = keepregstar
sil! let @+ = keepregplus
endif
sil! let @/ = keepregslash
" call Dret("netrw#Explore : no files matched pattern")
return
@ -1031,10 +1015,6 @@ fun! netrw#Explore(indx,dosplit,style,...)
if w:netrw_explore_listlen == 0 || (w:netrw_explore_listlen == 1 && w:netrw_explore_list[0] =~ '\*\*\/')
keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no files matched",42)
if has("clipboard")
sil! let @* = keepregstar
sil! let @+ = keepregplus
endif
sil! let @/ = keepregslash
" call Dret("netrw#Explore : no files matched")
return
@ -1079,10 +1059,6 @@ fun! netrw#Explore(indx,dosplit,style,...)
if !exists("g:netrw_quiet")
keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your vim needs the +path_extra feature for Exploring with **!",44)
endif
if has("clipboard")
sil! let @* = keepregstar
sil! let @+ = keepregplus
endif
sil! let @/ = keepregslash
" call Dret("netrw#Explore : missing +path_extra")
return
@ -1152,10 +1128,6 @@ fun! netrw#Explore(indx,dosplit,style,...)
" there's no danger of a late FocusGained event on initialization.
" Consequently, set s:netrw_events to 2.
let s:netrw_events= 2
if has("clipboard")
sil! let @* = keepregstar
sil! let @+ = keepregplus
endif
sil! let @/ = keepregslash
" call Dret("netrw#Explore : @/<".@/.">")
endfun
@ -9559,10 +9531,6 @@ fun! s:NetrwWideListing()
let newcolstart = w:netrw_bannercnt + fpc
let newcolend = newcolstart + fpc - 1
" call Decho("bannercnt=".w:netrw_bannercnt." fpl=".w:netrw_fpl." fpc=".fpc." newcol[".newcolstart.",".newcolend."]",'~'.expand("<slnum>"))
if has("clipboard")
sil! let keepregstar = @*
sil! let keepregplus = @+
endif
while line("$") >= newcolstart
if newcolend > line("$") | let newcolend= line("$") | endif
let newcolqty= newcolend - newcolstart
@ -9575,10 +9543,6 @@ fun! s:NetrwWideListing()
exe "sil! NetrwKeepj ".newcolstart.','.newcolend.'d _'
exe 'sil! NetrwKeepj '.w:netrw_bannercnt
endwhile
if has("clipboard")
sil! let @*= keepregstar
sil! let @+= keepregplus
endif
exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/\s\+$//e'
NetrwKeepj call histdel("/",-1)
exe 'nno <buffer> <silent> w :call search(''^.\\|\s\s\zs\S'',''W'')'."\<cr>"

View File

@ -10,7 +10,8 @@ function! provider#pythonx#Require(host) abort
" Python host arguments
let prog = (ver == '2' ? provider#python#Prog() : provider#python3#Prog())
let args = [prog, '-c', 'import sys; sys.path.remove(""); import neovim; neovim.start_host()']
let args = [prog, '-c', 'import sys; sys.path = list(filter(lambda x: x != "", sys.path)); import neovim; neovim.start_host()']
" Collect registered Python plugins into args
let python_plugins = remote#host#PluginsForHost(a:host.name)
@ -28,8 +29,8 @@ endfunction
function! s:get_python_candidates(major_version) abort
return {
\ 2: ['python2', 'python2.7', 'python2.6', 'python'],
\ 3: ['python3', 'python3.7', 'python3.6', 'python3.5', 'python3.4', 'python3.3',
\ 'python']
\ 3: ['python3', 'python3.8', 'python3.7', 'python3.6', 'python3.5',
\ 'python3.4', 'python3.3', 'python']
\ }[a:major_version]
endfunction
@ -43,7 +44,7 @@ function! provider#pythonx#DetectByModule(module, major_version) abort
let python_exe = s:get_python_executable_from_host_var(a:major_version)
if !empty(python_exe)
return [python_exe, '']
return [exepath(expand(python_exe)), '']
endif
let candidates = s:get_python_candidates(a:major_version)
@ -66,7 +67,7 @@ endfunction
function! s:import_module(prog, module) abort
let prog_version = system([a:prog, '-c' , printf(
\ 'import sys; ' .
\ 'sys.path.remove(""); ' .
\ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' .
\ 'sys.stdout.write(str(sys.version_info[0]) + "." + str(sys.version_info[1])); ' .
\ 'import pkgutil; ' .
\ 'exit(2*int(pkgutil.get_loader("%s") is None))',

View File

@ -13,6 +13,13 @@ let s:spellfile_URL = '' " Start with nothing so that s:donedict is reset.
" This function is used for the spellfile plugin.
function! spellfile#LoadFile(lang)
" Check for sandbox/modeline. #11359
try
:!
catch /\<E12\>/
throw 'Cannot download spellfile in sandbox/modeline. Try ":set spell" from the cmdline.'
endtry
" If the netrw plugin isn't loaded we silently skip everything.
if !exists(":Nread")
if &verbose

View File

@ -19,6 +19,7 @@ API Usage *api-rpc* *RPC* *rpc*
*msgpack-rpc*
RPC is the typical way to control Nvim programmatically. Nvim implements the
MessagePack-RPC protocol:
https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
https://github.com/msgpack/msgpack/blob/0b8f5ac/spec.md
Many clients use the API: user interfaces (GUIs), remote plugins, scripts like
@ -438,9 +439,69 @@ Example: create a float with scratch buffer: >
call nvim_win_set_option(win, 'winhl', 'Normal:MyHighlight')
>
==============================================================================
Extended marks *api-extended-marks*
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.
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 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: >
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): >
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()|.
Namespaces allow your 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
and deleting extmarks is not a buffer change, thus new undo states are not
created for extmark changes.
==============================================================================
Global Functions *api-global*
nvim_exec({src}, {output}) *nvim_exec()*
Executes Vimscript (multiline block of Ex-commands), like
anonymous |:source|.
Unlike |nvim_command()| this function supports heredocs,
script-scope (s:), etc.
On execution error: fails with VimL error, does not update
v:errmsg.
Parameters: ~
{src} Vimscript code
{output} Capture and return all (non-error, non-shell
|:!|) output
Return: ~
Output (non-error, non-shell |:!|) if `output` is true,
else empty string.
See also: ~
|execute()|
|nvim_command()|
nvim_command({command}) *nvim_command()*
Executes an ex-command.
@ -450,6 +511,9 @@ nvim_command({command}) *nvim_command()*
Parameters: ~
{command} Ex-command string
See also: ~
|nvim_exec()|
nvim_get_hl_by_name({name}, {rgb}) *nvim_get_hl_by_name()*
Gets a highlight definition by name.
@ -571,19 +635,9 @@ nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
replace_termcodes
cpoptions
nvim_command_output({command}) *nvim_command_output()*
Executes an ex-command and returns its (non-error) output.
Shell |:!| output is not captured.
On execution error: fails with VimL error, does not update
v:errmsg.
Parameters: ~
{command} Ex-command string
nvim_eval({expr}) *nvim_eval()*
Evaluates a VimL expression (:help expression). Dictionaries
and Lists are recursively expanded.
Evaluates a VimL |expression|. Dictionaries and Lists are
recursively expanded.
On execution error: fails with VimL error, does not update
v:errmsg.
@ -594,7 +648,7 @@ nvim_eval({expr}) *nvim_eval()*
Return: ~
Evaluation result or expanded object
nvim_execute_lua({code}, {args}) *nvim_execute_lua()*
nvim_exec_lua({code}, {args}) *nvim_exec_lua()*
Execute Lua code. Parameters (if any) are available as `...`
inside the chunk. The chunk can return a value.
@ -850,10 +904,10 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
{enter} Enter the window (make it the current window)
{config} Map defining the window configuration. Keys:
• `relative` : Sets the window layout to "floating", placed
at (row,col) coordinates relative to one of:
at (row,col) coordinates relative to:
• "editor" The global editor grid
• "win" Window given by the `win` field, or
current window by default.
current window.
• "cursor" Cursor position in current window.
• `win` : |window-ID| for relative="win".
@ -896,10 +950,11 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
'number', 'relativenumber', 'cursorline',
'cursorcolumn', 'foldcolumn', 'spell' and
'list' options. 'signcolumn' is changed to
`auto` . The end-of-buffer region is hidden
by setting `eob` flag of 'fillchars' to a
space char, and clearing the |EndOfBuffer|
region in 'winhighlight'.
`auto` and 'colorcolumn' is cleared. The
end-of-buffer region is hidden by setting
`eob` flag of 'fillchars' to a space char,
and clearing the |EndOfBuffer| region in
'winhighlight'.
Return: ~
Window handle, or 0 on error
@ -984,7 +1039,7 @@ nvim_put({lines}, {type}, {after}, {follow}) *nvim_put()*
{type} Edit behavior: any |getregtype()| result, or:
• "b" |blockwise-visual| mode (may include
width, e.g. "b3")
• "c" |characterwise| mode
• "c" |charwise| mode
• "l" |linewise| mode
• "" guess by contents, see |setreg()|
{after} Insert after cursor (like |p|), or before (like
@ -1476,45 +1531,73 @@ nvim_buf_line_count({buffer}) *nvim_buf_line_count()*
Line count, or 0 for unloaded buffer. |api-buffer|
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
Activates buffer-update events on a channel, or as lua
Activates buffer-update events on a channel, or as Lua
callbacks.
Example (Lua): capture buffer updates in a global `events` variable (use "print(vim.inspect(events))" to see its
contents): >
events = {}
vim.api.nvim_buf_attach(0, false, {
on_lines=function(...) table.insert(events, {...}) end})
<
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{send_buffer} Set to true if the initial notification
should contain the whole buffer. If so, the
first notification will be a
`nvim_buf_lines_event` . Otherwise, the
first notification will be a
`nvim_buf_changedtick_event` . Not used for
lua callbacks.
{send_buffer} True if the initial notification should
contain the whole buffer: first
notification will be `nvim_buf_lines_event`
. Else the first notification will be
`nvim_buf_changedtick_event` . Not for Lua
callbacks.
{opts} Optional parameters.
• `on_lines` : lua callback received on
change.
• `on_changedtick` : lua callback received
on changedtick increment without text
change.
• `utf_sizes` : include UTF-32 and UTF-16
size of the replaced region. See
|api-buffer-updates-lua| for more
information
• on_lines: Lua callback invoked on change.
Return `true` to detach. Args:
• buffer handle
• b:changedtick
• first line that changed (zero-indexed)
• last line that was changed
• last line in the updated range
• byte count of previous contents
• deleted_codepoints (if `utf_sizes` is
true)
• deleted_codeunits (if `utf_sizes` is
true)
• on_changedtick: Lua callback invoked on
changedtick increment without text
change. Args:
• buffer handle
• b:changedtick
• on_detach: Lua callback invoked on
detach. Args:
• buffer handle
• utf_sizes: include UTF-32 and UTF-16 size
of the replaced region, as args to
`on_lines` .
Return: ~
False when updates couldn't be enabled because the buffer
isn't loaded or `opts` contained an invalid key; otherwise
True. TODO: LUA_API_NO_EVAL
False if attach failed (invalid parameter, or buffer isn't
loaded); otherwise True. TODO: LUA_API_NO_EVAL
See also: ~
|nvim_buf_detach()|
|api-buffer-updates-lua|
nvim_buf_detach({buffer}) *nvim_buf_detach()*
Deactivates buffer-update events on the channel.
For Lua callbacks see |api-lua-detach|.
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
Return: ~
False when updates couldn't be disabled because the buffer
isn't loaded; otherwise True.
False if detach failed (because the buffer isn't loaded);
otherwise True.
See also: ~
|nvim_buf_attach()|
|api-lua-detach| for detaching Lua callbacks
*nvim_buf_get_lines()*
nvim_buf_get_lines({buffer}, {start}, {end}, {strict_indexing})
@ -1726,6 +1809,99 @@ nvim_buf_get_mark({buffer}, {name}) *nvim_buf_get_mark()*
Return: ~
(row, col) tuple
*nvim_buf_get_extmark_by_id()*
nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id})
Returns position for a given extmark id
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{ns_id} Namespace id from |nvim_create_namespace()|
{id} Extmark id
Return: ~
(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})
Gets extmarks in "traversal order" from a |charwise| region
defined by buffer positions (inclusive, 0-indexed
|api-indexing|).
Region can be given as (row,col) tuples, or valid extmark ids
(whose positions define the bounds). 0 and -1 are understood
as (0,0) and (-1,-1) respectively, thus the following are
equivalent:
>
nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
nvim_buf_get_extmarks(0, my_ns, [0,0], [-1,-1], {})
<
If `end` is less than `start` , traversal works backwards.
(Useful with `limit` , to get the first marks prior to a given
position.)
Example:
>
local a = vim.api
local pos = a.nvim_win_get_cursor(0)
local ns = a.nvim_create_namespace('my-plugin')
-- Create new extmark at line 1, column 1.
local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {})
-- Create new extmark at line 3, column 1.
local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {})
-- Get extmarks only from line 3.
local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {})
-- Get all marks in this buffer + namespace.
local all = a.nvim_buf_get_extmarks(0, ns, 0, -1, {})
print(vim.inspect(ms))
<
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)
{opts} Optional parameters. Keys:
• limit: Maximum number of marks to return
Return: ~
List of [extmark_id, row, col] tuples in "traversal
order".
*nvim_buf_set_extmark()*
nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts})
Creates or updates an extmark.
To create a new extmark, pass id=0. The extmark id will be
returned. It is also allowed to create a new mark by passing
in a previously unused id, but the caller must then keep track
of existing and unused ids itself. (Useful over RPC, to avoid
waiting for the return value.)
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{ns_id} Namespace id from |nvim_create_namespace()|
{id} Extmark id, or 0 to create new
{line} Line number where to place the mark
{col} Column where to place the mark
{opts} Optional parameters. Currently not used.
Return: ~
Id of the created/updated extmark
nvim_buf_del_extmark({buffer}, {ns_id}, {id}) *nvim_buf_del_extmark()*
Removes an extmark.
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{ns_id} Namespace id from |nvim_create_namespace()|
{id} Extmark id
Return: ~
true if the extmark was found, else false
*nvim_buf_add_highlight()*
nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line},
{col_start}, {col_end})
@ -1769,8 +1945,8 @@ nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line},
*nvim_buf_clear_namespace()*
nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end})
Clears namespaced objects, highlights and virtual text, from a
line range
Clears namespaced objects (highlights, extmarks, virtual text)
from a region.
Lines are 0-indexed. |api-indexing| To clear the namespace in
the entire buffer, specify line_start=0 and line_end=-1.
@ -1821,6 +1997,27 @@ nvim_buf_set_virtual_text({buffer}, {ns_id}, {line}, {chunks}, {opts})
Return: ~
The ns_id that was used
nvim_buf_get_virtual_text({buffer}, {lnum}) *nvim_buf_get_virtual_text()*
Get the virtual text (annotation) for a buffer line.
The virtual text is returned as list of lists, whereas the
inner lists have either one or two elements. The first element
is the actual text, the optional second element is the
highlight group.
The format is exactly the same as given to
nvim_buf_set_virtual_text().
If there is no virtual text associated with the given line, an
empty list is returned.
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{line} Line to get the virtual text from (zero-indexed)
Return: ~
List of virtual text chunks
nvim__buf_stats({buffer}) *nvim__buf_stats()*
TODO: Documentation

View File

@ -559,16 +559,14 @@ CmdlineLeave Before leaving the command-line (including
*CmdwinEnter*
CmdwinEnter After entering the command-line window.
Useful for setting options specifically for
this special type of window. This is
triggered _instead_ of BufEnter and WinEnter.
this special type of window.
<afile> is set to a single character,
indicating the type of command-line.
|cmdwin-char|
*CmdwinLeave*
CmdwinLeave Before leaving the command-line window.
Useful to clean up any global setting done
with CmdwinEnter. This is triggered _instead_
of BufLeave and WinLeave.
with CmdwinEnter.
<afile> is set to a single character,
indicating the type of command-line.
|cmdwin-char|

View File

@ -90,7 +90,7 @@ start and end of the motion are not in the same line, and there are only
blanks before the start and there are no non-blanks after the end of the
motion, the delete becomes linewise. This means that the delete also removes
the line of blanks that you might expect to remain. Use the |o_v| operator to
force the motion to be characterwise.
force the motion to be charwise.
Trying to delete an empty region of text (e.g., "d0" in the first column)
is an error when 'cpoptions' includes the 'E' flag.
@ -1074,7 +1074,7 @@ also use these commands to move text from one file to another, because Vim
preserves all registers when changing buffers (the CTRL-^ command is a quick
way to toggle between two files).
*linewise-register* *characterwise-register*
*linewise-register* *charwise-register*
You can repeat the put commands with "." (except for :put) and undo them. If
the command that was used to get the text into the register was |linewise|,
Vim inserts the text below ("p") or above ("P") the line where the cursor is.
@ -1116,10 +1116,9 @@ this happen. However, if the width of the block is not a multiple of a <Tab>
width and the text after the inserted block contains <Tab>s, that text may be
misaligned.
Note that after a characterwise yank command, Vim leaves the cursor on the
first yanked character that is closest to the start of the buffer. This means
that "yl" doesn't move the cursor, but "yh" moves the cursor one character
left.
Note that after a charwise yank command, Vim leaves the cursor on the first
yanked character that is closest to the start of the buffer. This means that
"yl" doesn't move the cursor, but "yh" moves the cursor one character left.
Rationale: In Vi the "y" command followed by a backwards motion would
sometimes not move the cursor to the first yanked character,
because redisplaying was skipped. In Vim it always moves to

View File

@ -1122,11 +1122,9 @@ edited as described in |cmdwin-char|.
AUTOCOMMANDS
Two autocommand events are used: |CmdwinEnter| and |CmdwinLeave|. Since this
window is of a special type, the WinEnter, WinLeave, BufEnter and BufLeave
events are not triggered. You can use the Cmdwin events to do settings
specifically for the command-line window. Be careful not to cause side
effects!
Two autocommand events are used: |CmdwinEnter| and |CmdwinLeave|. You can use
the Cmdwin events to do settings specifically for the command-line window.
Be careful not to cause side effects!
Example: >
:au CmdwinEnter : let b:cpt_save = &cpt | set cpt=.
:au CmdwinLeave : let &cpt = b:cpt_save

View File

@ -14,6 +14,8 @@ updated.
API ~
*nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead.
*nvim_command_output()* Use |nvim_exec()| instead.
*nvim_execute_lua()* Use |nvim_exec_lua()| instead.
Commands ~
*:rv*

View File

@ -143,6 +143,87 @@ DOCUMENTATION *dev-doc*
/// @param dirname The path fragment before `pend`
<
C docstrings ~
Nvim API documentation lives in the source code, as docstrings (Doxygen
comments) on the function definitions. The |api| :help is generated
from the docstrings defined in src/nvim/api/*.c.
Docstring format:
- Lines start with `///`
- Special tokens start with `@` followed by the token name:
`@note`, `@param`, `@returns`
- Limited markdown is supported.
- List-items start with `-` (useful to nest or "indent")
- Use `<pre>` for code samples.
Example: the help for |nvim_open_win()| is generated from a docstring defined
in src/nvim/api/vim.c like this: >
/// Opens a new window.
/// ...
///
/// Example (Lua): window-relative float
/// <pre>
/// vim.api.nvim_open_win(0, false,
/// {relative='win', row=3, col=3, width=12, height=3})
/// </pre>
///
/// @param buffer Buffer to display
/// @param enter Enter the window
/// @param config Map defining the window configuration. Keys:
/// - relative: Sets the window layout, relative to:
/// - "editor" The global editor grid.
/// - "win" Window given by the `win` field.
/// - "cursor" Cursor position in current window.
/// ...
/// @param[out] err Error details, if any
///
/// @return Window handle, or 0 on error
Lua docstrings ~
*dev-lua-doc*
Lua documentation lives in the source code, as docstrings on the function
definitions. The |lua-vim| :help is generated from the docstrings.
Docstring format:
- Lines in the main description start with `---`
- 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.
Example: the help for |vim.paste()| is generated from a docstring decorating
vim.paste in src/nvim/lua/vim.lua like this: >
--- Paste handler, invoked by |nvim_paste()| when a conforming UI
--- (such as the |TUI|) pastes text into the editor.
---
--- Example: To remove ANSI color codes when pasting:
--- <pre>
--- vim.paste = (function()
--- local overridden = vim.paste
--- ...
--- end)()
--- </pre>
---
--@see |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.
API *dev-api*
Use this template to name new API functions:
@ -155,10 +236,11 @@ with a {thing} that groups functions under a common concept).
Use existing common {action} names if possible:
add Append to, or insert into, a collection
get Get a thing (or group of things by query)
set Set a thing (or group of things)
del Delete a thing (or group of things)
exec Execute code
get Get a thing (or group of things by query)
list Get all things
set Set a thing (or group of things)
Use consistent names for {thing} in all API functions. E.g. a buffer is called
"buf" everywhere, not "buffer" in some places and "buf" in others.
@ -268,8 +350,8 @@ External UIs are expected to implement these common features:
chords (<C-,> <C-Enter> <C-S-x> <D-x>) and patterns ("shift shift") that do
not potentially conflict with Nvim defaults, plugins, etc.
- Consider the "option_set" |ui-global| event as a hint for other GUI
behaviors. UI-related options ('guifont', 'ambiwidth', …) are published in
this event.
behaviors. Various UI-related options ('guifont', 'ambiwidth', …) are
published in this event. See also "mouse_on", "mouse_off".
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -20,7 +20,9 @@ An alternative is using the 'keymap' option.
1. Defining digraphs *digraphs-define*
*:dig* *:digraphs*
:dig[raphs] show currently defined digraphs.
:dig[raphs][!] Show currently defined digraphs.
With [!] headers are used to make it a bit easier to
find a specific character.
*E104* *E39*
:dig[raphs] {char1}{char2} {number} ...
Add digraph {char1}{char2} to the list. {number} is

View File

@ -1217,7 +1217,7 @@ lambda expression *expr-lambda* *lambda*
{args -> expr1} lambda expression
A lambda expression creates a new unnamed function which returns the result of
evaluating |expr1|. Lambda expressions differ from |user-functions| in
evaluating |expr1|. Lambda expressions differ from |user-function|s in
the following ways:
1. The body of the lambda expression is an |expr1| and not a sequence of |Ex|
@ -1547,10 +1547,12 @@ v:errmsg Last given error message.
:if v:errmsg != ""
: ... handle error
<
*v:errors* *errors-variable*
*v:errors* *errors-variable* *assert-return*
v:errors Errors found by assert functions, such as |assert_true()|.
This is a list of strings.
The assert functions append an item when an assert fails.
The return value indicates this: a one is returned if an item
was added to v:errors, otherwise zero is returned.
To remove old results make it empty: >
:let v:errors = []
< If v:errors is set to anything but a list it is made an empty
@ -1735,6 +1737,10 @@ v:lnum Line number for the 'foldexpr' |fold-expr|, 'formatexpr' and
expressions is being evaluated. Read-only when in the
|sandbox|.
*v:lua* *lua-variable*
v:lua Prefix for calling Lua functions from expressions.
See |v:lua-call| for more information.
*v:mouse_win* *mouse_win-variable*
v:mouse_win Window number for a mouse click obtained with |getchar()|.
First window has number 1, like with |winnr()|. The value is
@ -1984,9 +1990,12 @@ v:windowid Application-specific window "handle" which may be set by any
|window-ID|.
==============================================================================
4. Builtin Functions *functions*
4. Builtin Functions *vim-function* *functions*
See |function-list| for a list grouped by what the function is used for.
The Vimscript subsystem (referred to as "eval" internally) provides the
following builtin functions. Scripts can also define |user-function|s.
See |function-list| to browse functions by topic.
(Use CTRL-] on the function name to jump to the full explanation.)
@ -2004,24 +2013,26 @@ argidx() Number current index in the argument list
arglistid([{winnr} [, {tabnr}]]) Number argument list id
argv({nr} [, {winid}]) String {nr} entry of the argument list
argv([-1, {winid}]) List the argument list
assert_beeps({cmd}) none assert {cmd} causes a beep
assert_beeps({cmd}) Number assert {cmd} causes a beep
assert_equal({exp}, {act} [, {msg}])
none assert {exp} is equal to {act}
Number assert {exp} is equal to {act}
assert_equalfile({fname-one}, {fname-two})
Number assert file contents is equal
assert_exception({error} [, {msg}])
none assert {error} is in v:exception
assert_fails({cmd} [, {error}]) none assert {cmd} fails
Number assert {error} is in v:exception
assert_fails({cmd} [, {error}]) Number assert {cmd} fails
assert_false({actual} [, {msg}])
none assert {actual} is false
Number assert {actual} is false
assert_inrange({lower}, {upper}, {actual} [, {msg}])
none assert {actual} is inside the range
Number assert {actual} is inside the range
assert_match({pat}, {text} [, {msg}])
none assert {pat} matches {text}
Number assert {pat} matches {text}
assert_notequal({exp}, {act} [, {msg}])
none assert {exp} is not equal {act}
Number assert {exp} is not equal {act}
assert_notmatch({pat}, {text} [, {msg}])
none assert {pat} not matches {text}
assert_report({msg}) none report a test failure
assert_true({actual} [, {msg}]) none assert {actual} is true
Number assert {pat} not matches {text}
assert_report({msg}) Number report a test failure
assert_true({actual} [, {msg}]) Number assert {actual} is true
asin({expr}) Float arc sine of {expr}
atan({expr}) Float arc tangent of {expr}
atan2({expr}, {expr}) Float arc tangent of {expr1} / {expr2}
@ -2580,12 +2591,13 @@ argv([{nr} [, {winid}])
assert_beeps({cmd}) *assert_beeps()*
Run {cmd} and add an error message to |v:errors| if it does
NOT produce a beep or visual bell.
Also see |assert_fails()|.
Also see |assert_fails()| and |assert-return|.
*assert_equal()*
assert_equal({expected}, {actual}, [, {msg}])
When {expected} and {actual} are not equal an error message is
added to |v:errors|.
added to |v:errors| and 1 is returned. Otherwise zero is
returned |assert-return|.
There is no automatic conversion, the String "4" is different
from the Number 4. And the number 4 is different from the
Float 4.0. The value of 'ignorecase' is not used here, case
@ -2597,9 +2609,17 @@ 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()*
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|.
Also see |assert-return|.
When {fname-one} or {fname-two} does not exist the error will
mention that.
assert_exception({error} [, {msg}]) *assert_exception()*
When v:exception does not contain the string {error} an error
message is added to |v:errors|.
message is added to |v:errors|. Also see |assert-return|.
This can be used to assert that a command throws an exception.
Using the error number, followed by a colon, avoids problems
with translations: >
@ -2612,7 +2632,7 @@ assert_exception({error} [, {msg}]) *assert_exception()*
assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()*
Run {cmd} and add an error message to |v:errors| if it does
NOT produce an error.
NOT produce an error. Also see |assert-return|.
When {error} is given it must match in |v:errmsg|.
Note that beeping is not considered an error, and some failing
commands only beep. Use |assert_beeps()| for those.
@ -2620,6 +2640,7 @@ assert_fails({cmd} [, {error} [, {msg}]]) *assert_fails()*
assert_false({actual} [, {msg}]) *assert_false()*
When {actual} is not false an error message is added to
|v:errors|, like with |assert_equal()|.
Also see |assert-return|.
A value is false when it is zero or |v:false|. When "{actual}"
is not a number or |v:false| the assert fails.
When {msg} is omitted an error in the form
@ -2636,7 +2657,7 @@ assert_inrange({lower}, {upper}, {actual} [, {msg}]) *assert_inrange()*
*assert_match()*
assert_match({pattern}, {actual} [, {msg}])
When {pattern} does not match {actual} an error message is
added to |v:errors|.
added to |v:errors|. Also see |assert-return|.
{pattern} is used as with |=~|: The matching is always done
like 'magic' was set and 'cpoptions' is empty, no matter what
@ -2657,18 +2678,22 @@ assert_match({pattern}, {actual} [, {msg}])
assert_notequal({expected}, {actual} [, {msg}])
The opposite of `assert_equal()`: add an error message to
|v:errors| when {expected} and {actual} are equal.
Also see |assert-return|.
*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|.
assert_report({msg}) *assert_report()*
Report a test failure directly, using {msg}.
Always returns one.
assert_true({actual} [, {msg}]) *assert_true()*
When {actual} is not true an error message is added to
|v:errors|, like with |assert_equal()|.
Also see |assert-return|.
A value is |TRUE| when it is a non-zero number or |v:true|.
When {actual} is not a number or |v:true| the assert fails.
When {msg} is omitted an error in the form "Expected True but
@ -3525,7 +3550,7 @@ exists({expr}) The result is a Number, which is |TRUE| if {expr} is
string)
*funcname built-in function (see |functions|)
or user defined function (see
|user-functions|). Also works for a
|user-function|). Also works for a
variable that is a Funcref.
varname internal variable (see
|internal-variables|). Also works
@ -4479,8 +4504,7 @@ getftype({fname}) *getftype()*
systems that support it. On some systems only "dir" and
"file" are returned.
*getjumplist()*
getjumplist([{winnr} [, {tabnr}]])
getjumplist([{winnr} [, {tabnr}]]) *getjumplist()*
Returns the |jumplist| for the specified window.
Without arguments use the current window.
@ -4536,6 +4560,10 @@ getloclist({nr},[, {what}]) *getloclist()*
If the optional {what} dictionary argument is supplied, then
returns the items listed in {what} as a dictionary. Refer to
|getqflist()| for the supported items in {what}.
If {what} contains 'filewinid', then returns the id of the
window used to display files from the location list. This
field is applicable only when called from a location list
window.
getmatches() *getmatches()*
Returns a |List| with all matches previously defined for the
@ -4699,7 +4727,7 @@ getreg([{regname} [, 1 [, {list}]]]) *getreg()*
getregtype([{regname}]) *getregtype()*
The result is a String, which is type of register {regname}.
The value will be one of:
"v" for |characterwise| text
"v" for |charwise| text
"V" for |linewise| text
"<CTRL-V>{width}" for |blockwise-visual| text
"" for an empty or unknown register
@ -4941,9 +4969,11 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The
< *feature-list*
List of supported pseudo-feature names:
acl |ACL| support
bsd BSD system (not macOS, use "mac" for that).
iconv Can use |iconv()| for conversion.
+shellslash Can use backslashes in filenames (Windows)
clipboard |clipboard| provider is available.
mac MacOS system.
nvim This is Nvim.
python2 Legacy Vim |python2| interface. |has-python|
python3 Legacy Vim |python3| interface. |has-python|
@ -4953,6 +4983,7 @@ has({feature}) Returns 1 if {feature} is supported, 0 otherwise. The
unix Unix system.
*vim_starting* True during |startup|.
win32 Windows system (32 or 64 bit).
win64 Windows system (64 bit).
wsl WSL (Windows Subsystem for Linux) system
*has-patch*
@ -5420,6 +5451,9 @@ jobstart({cmd}[, {opts}]) *jobstart()*
|on_exit| : exit event handler (function name or |Funcref|)
cwd : Working directory of the job; defaults to
|current-directory|.
env : A dict of strings to append (or replace see
|clear_env|) to the current environment.
clear_env: If set, use the exact values passed in |env|
rpc : If set, |msgpack-rpc| will be used to communicate
with the job over stdin and stdout. "on_stdout" is
then ignored, but "on_stderr" can still be used.
@ -6103,7 +6137,7 @@ mode([expr]) Return a string that indicates the current mode.
n Normal
no Operator-pending
nov Operator-pending (forced characterwise |o_v|)
nov Operator-pending (forced charwise |o_v|)
noV Operator-pending (forced linewise |o_V|)
noCTRL-V Operator-pending (forced blockwise |o_CTRL-V|)
niI Normal using |i_CTRL-O| in |Insert-mode|
@ -6640,7 +6674,7 @@ remote_expr({server}, {string} [, {idvar} [, {timeout}]])
between (not at the end), like with join(expr, "\n").
If {idvar} is present and not empty, it is taken as the name
of a variable and a {serverid} for later use with
remote_read() is stored there.
|remote_read()| is stored there.
If {timeout} is given the read times out after this many
seconds. Otherwise a timeout of 600 seconds is used.
See also |clientserver| |RemoteReply|.
@ -7413,7 +7447,7 @@ setreg({regname}, {value} [, {options}])
If {options} contains "a" or {regname} is upper case,
then the value is appended.
{options} can also contain a register type specification:
"c" or "v" |characterwise| mode
"c" or "v" |charwise| mode
"l" or "V" |linewise| mode
"b" or "<CTRL-V>" |blockwise-visual| mode
If a number immediately follows "b" or "<CTRL-V>" then this is
@ -9222,7 +9256,7 @@ Don't forget that "^" will only match at the first character of the String and
"\n".
==============================================================================
5. Defining functions *user-functions*
5. Defining functions *user-function*
New functions can be defined. These can be called just like builtin
functions. The function executes a sequence of Ex commands. Normal mode
@ -9680,7 +9714,7 @@ This does NOT work: >
register, "@/" for the search pattern.
If the result of {expr1} ends in a <CR> or <NL>, the
register will be linewise, otherwise it will be set to
characterwise.
charwise.
This can be used to clear the last search pattern: >
:let @/ = ""
< This is different from searching for an empty string,
@ -9762,6 +9796,54 @@ This does NOT work: >
Like above, but append/add/subtract the value for each
|List| item.
*:let=<<* *:let-heredoc*
*E990* *E991* *E172* *E221*
:let {var-name} =<< [trim] {marker}
text...
text...
{marker}
Set internal variable {var-name} to a List containing
the lines of text bounded by the string {marker}.
{marker} cannot start with a lower case character.
The last line should end only with the {marker} string
without any other character. Watch out for white
space after {marker}!
Without "trim" any white space characters in the lines
of text are preserved. If "trim" is specified before
{marker}, then indentation is stripped so you can do: >
let text =<< trim END
if ok
echo 'done'
endif
END
< Results in: ["if ok", " echo 'done'", "endif"]
The marker must line up with "let" and the indentation
of the first line is removed from all the text lines.
Specifically: all the leading indentation exactly
matching the leading indentation of the first
non-empty text line is stripped from the input lines.
All leading indentation exactly matching the leading
indentation before `let` is stripped from the line
containing {marker}. Note that the difference between
space and tab matters here.
If {var-name} didn't exist yet, it is created.
Cannot be followed by another command, but can be
followed by a comment.
Examples: >
let var1 =<< END
Sample text 1
Sample text 2
Sample text 3
END
let data =<< trim DATA
1 2 3 4
5 6 7 8
DATA
<
*E121*
:let {var-name} .. List the value of variable {var-name}. Multiple
variable names may be given. Special names recognized

View File

@ -549,7 +549,9 @@ Variables:
*b:man_default_sects* Comma-separated, ordered list of preferred sections.
For example in C one usually wants section 3 or 2: >
:let b:man_default_sections = '3,2'
*g:man_hardwrap* Hard-wrap to $MANWIDTH. May improve layout.
*g:man_hardwrap* Hard-wrap to $MANWIDTH or window width if $MANWIDTH is
empty. Enabled by default. Set |FALSE| to enable soft
wrapping.
To use Nvim as a manpager: >
export MANPAGER='nvim +Man!'
@ -558,10 +560,13 @@ Note that when running `man` from the shell and with that `MANPAGER` in your
environment, `man` will pre-format the manpage using `groff`. Thus, Neovim
will inevitably display the manual page as it was passed to it from stdin. One
of the caveats of this is that the width will _always_ be hard-wrapped and not
soft wrapped as with `:Man`. You can set in your environment: >
soft wrapped as with `g:man_hardwrap=0`. You can set in your environment: >
export MANWIDTH=999
So `groff`'s pre-formatting output will be the same as with `:Man` i.e soft-wrapped.
So `groff`'s pre-formatting output will be the same as with `g:man_hardwrap=0` i.e soft-wrapped.
To disable bold highlighting: >
:highlight link manBold Normal
PDF *ft-pdf-plugin*

View File

@ -129,6 +129,7 @@ Advanced editing ~
|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
Special issues ~
|print.txt| printing
@ -157,7 +158,6 @@ GUI ~
Interfaces ~
|if_cscop.txt| using Cscope with Vim
|if_lua.txt| Lua interface
|if_pyth.txt| Python interface
|if_ruby.txt| Ruby interface
|sign.txt| debugging signs

View File

@ -1,668 +1,8 @@
*if_lua.txt* Nvim
NVIM REFERENCE MANUAL
NVIM REFERENCE MANUAL
Lua engine *lua* *Lua*
Type |gO| to see the table of contents.
Moved to |lua.txt|
==============================================================================
Introduction *lua-intro*
The Lua 5.1 language is builtin and always available. Try this command to get
an idea of what lurks beneath: >
:lua print(vim.inspect(package.loaded))
Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the
"editor stdlib" (|functions| and Ex commands) and the |API|, all of which can
be used from Lua code.
Module conflicts are resolved by "last wins". For example if both of these
are on 'runtimepath':
runtime/lua/foo.lua
~/.config/nvim/lua/foo.lua
then `require('foo')` loads "~/.config/nvim/lua/foo.lua", and
"runtime/lua/foo.lua" is not used. See |lua-require| to understand how Nvim
finds and loads Lua modules. The conventions are similar to VimL plugins,
with some extra features. See |lua-require-example| for a walkthrough.
==============================================================================
Importing Lua modules *lua-require*
Nvim automatically adjusts `package.path` and `package.cpath` according to
effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is
changed. `package.path` is adjusted by simply appending `/lua/?.lua` and
`/lua/?/init.lua` to each directory from 'runtimepath' (`/` is actually the
first character of `package.config`).
Similarly to `package.path`, modified directories from 'runtimepath' are also
added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and
`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of
the existing `package.cpath` are used. Example:
1. Given that
- 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`;
- initial (defined at compile-time or derived from
`$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains
`./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`.
2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in
order: parts of the path starting from the first path component containing
question mark and preceding path separator.
3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as its the same
as the suffix of the first path from `package.path` (i.e. `./?.so`). Which
leaves `/?.so` and `/a?d/j/g.elf`, in this order.
4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The
second one contains semicolon which is a paths separator so it is out,
leaving only `/foo/bar` and `/abc`, in order.
5. The cartesian product of paths from 4. and suffixes from 3. is taken,
giving four variants. In each variant `/lua` path segment is inserted
between path and suffix, leaving
- `/foo/bar/lua/?.so`
- `/foo/bar/lua/a?d/j/g.elf`
- `/abc/lua/?.so`
- `/abc/lua/a?d/j/g.elf`
6. New paths are prepended to the original `package.cpath`.
The result will look like this:
`/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath')
× `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`)
= `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`
Note:
- To track 'runtimepath' updates, paths added at previous update are
remembered and removed at the next update, while all paths derived from the
new 'runtimepath' are prepended as described above. This allows removing
paths when path is removed from 'runtimepath', adding paths when they are
added and reordering `package.path`/`package.cpath` content if 'runtimepath'
was reordered.
- Although adjustments happen automatically, Nvim does not track current
values of `package.path` or `package.cpath`. If you happen to delete some
paths from there you can set 'runtimepath' to trigger an update: >
let &runtimepath = &runtimepath
- Skipping paths from 'runtimepath' which contain semicolons applies both to
`package.path` and `package.cpath`. Given that there are some badly written
plugins using shell which will not work with paths containing semicolons it
is better to not have them in 'runtimepath' at all.
------------------------------------------------------------------------------
LUA PLUGIN EXAMPLE *lua-require-example*
The following example plugin adds a command `:MakeCharBlob` which transforms
current buffer into a long `unsigned char` array. Lua contains transformation
function in a module `lua/charblob.lua` which is imported in
`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed
to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in
this case `lua/charblob.lua` means `~/.config/nvim/lua/charblob.lua`).
autoload/charblob.vim: >
function charblob#encode_buffer()
call setline(1, luaeval(
\ 'require("charblob").encode(unpack(_A))',
\ [getline(1, '$'), &textwidth, ' ']))
endfunction
plugin/charblob.vim: >
if exists('g:charblob_loaded')
finish
endif
let g:charblob_loaded = 1
command MakeCharBlob :call charblob#encode_buffer()
lua/charblob.lua: >
local function charblob_bytes_iter(lines)
local init_s = {
next_line_idx = 1,
next_byte_idx = 1,
lines = lines,
}
local function next(s, _)
if lines[s.next_line_idx] == nil then
return nil
end
if s.next_byte_idx > #(lines[s.next_line_idx]) then
s.next_line_idx = s.next_line_idx + 1
s.next_byte_idx = 1
return ('\n'):byte()
end
local ret = lines[s.next_line_idx]:byte(s.next_byte_idx)
if ret == ('\n'):byte() then
ret = 0 -- See :h NL-used-for-NUL.
end
s.next_byte_idx = s.next_byte_idx + 1
return ret
end
return next, init_s, nil
end
local function charblob_encode(lines, textwidth, indent)
local ret = {
'const unsigned char blob[] = {',
indent,
}
for byte in charblob_bytes_iter(lines) do
-- .- space + number (width 3) + comma
if #(ret[#ret]) + 5 > textwidth then
ret[#ret + 1] = indent
else
ret[#ret] = ret[#ret] .. ' '
end
ret[#ret] = ret[#ret] .. (('%3u,'):format(byte))
end
ret[#ret + 1] = '};'
return ret
end
return {
bytes_iter = charblob_bytes_iter,
encode = charblob_encode,
}
==============================================================================
Commands *lua-commands*
*:lua*
:[range]lua {chunk}
Execute Lua chunk {chunk}.
Examples:
>
:lua vim.api.nvim_command('echo "Hello, Nvim!"')
<
To see the Lua version: >
:lua print(_VERSION)
To see the LuaJIT version: >
:lua print(jit.version)
<
:[range]lua << [endmarker]
{script}
{endmarker}
Execute Lua script {script}. Useful for including Lua
code in Vim scripts.
The {endmarker} must NOT be preceded by any white space.
If [endmarker] is omitted from after the "<<", a dot '.' must be used after
{script}, like for the |:append| and |:insert| commands.
Example:
>
function! CurrentLineInfo()
lua << EOF
local linenr = vim.api.nvim_win_get_cursor(0)[1]
local curline = vim.api.nvim_buf_get_lines(
0, linenr, linenr + 1, false)[1]
print(string.format("Current line [%d] has %d bytes",
linenr, #curline))
EOF
endfunction
Note that the `local` variables will disappear when block finishes. This is
not the case for globals.
*:luado*
:[range]luado {body} Execute Lua function "function (line, linenr) {body}
end" for each line in the [range], with the function
argument being set to the text of each line in turn,
without a trailing <EOL>, and the current line number.
If the value returned by the function is a string it
becomes the text of the line in the current turn. The
default for [range] is the whole file: "1,$".
Examples:
>
:luado return string.format("%s\t%d", line:reverse(), #line)
:lua require"lpeg"
:lua -- balanced parenthesis grammar:
:lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" }
:luado if bp:match(line) then return "-->\t" .. line end
<
*:luafile*
:[range]luafile {file}
Execute Lua script in {file}.
The whole argument is used as a single file name.
Examples:
>
:luafile script.lua
:luafile %
<
All these commands execute a Lua chunk from either the command line (:lua and
:luado) or a file (:luafile) with the given line [range]. Similarly to the Lua
interpreter, each chunk has its own scope and so only global variables are
shared between command calls. All Lua default libraries are available. In
addition, Lua "print" function has its output redirected to the Nvim message
area, with arguments separated by a white space instead of a tab.
Lua uses the "vim" module (see |lua-vim|) to issue commands to Nvim. However,
procedures that alter buffer content, open new buffers, and change cursor
position are restricted when the command is executed in the |sandbox|.
==============================================================================
luaeval() *lua-eval* *luaeval()*
The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is
"luaeval". "luaeval" takes an expression string and an optional argument used
for _A inside expression and returns the result of the expression. It is
semantically equivalent in Lua to:
>
local chunkheader = "local _A = select(1, ...) return "
function luaeval (expstr, arg)
local chunk = assert(loadstring(chunkheader .. expstr, "luaeval"))
return chunk(arg) -- return typval
end
Lua nils, numbers, strings, tables and booleans are converted to their
respective VimL types. An error is thrown if conversion of any other Lua types
is attempted.
The magic global "_A" contains the second argument to luaeval().
Example: >
:echo luaeval('_A[1] + _A[2]', [40, 2])
42
:echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123')
foo
Lua tables are used as both dictionaries and lists, so it is impossible to
determine whether empty table is meant to be empty list or empty dictionary.
Additionally lua does not have integer numbers. To distinguish between these
cases there is the following agreement:
0. Empty table is empty list.
1. Table with N incrementally growing integral numbers, starting from 1 and
ending with N is considered to be a list.
2. Table with string keys, none of which contains NUL byte, is considered to
be a dictionary.
3. Table with string keys, at least one of which contains NUL byte, is also
considered to be a dictionary, but this time it is converted to
a |msgpack-special-map|.
*lua-special-tbl*
4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point
value:
- `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to
a floating-point 1.0. Note that by default integral lua numbers are
converted to |Number|s, non-integral are converted to |Float|s. This
variant allows integral |Float|s.
- `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty
dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is
converted to a dictionary `{'a': 42}`: non-string keys are ignored.
Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3.
are errors.
- `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well
as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not
form a 1-step sequence from 1 to N are ignored, as well as all
non-integral keys.
Examples: >
:echo luaeval('math.pi')
:function Rand(x,y) " random uniform between x and y
: return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y})
: endfunction
:echo Rand(1,10)
Note that currently second argument to `luaeval` undergoes VimL to lua
conversion, so changing containers in lua do not affect values in VimL. Return
value is also always converted. When converting, |msgpack-special-dict|s are
treated specially.
==============================================================================
Lua standard modules *lua-stdlib*
The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes
various functions and sub-modules. It is always loaded, thus require("vim")
is unnecessary.
You can peek at the module properties: >
:lua print(vim.inspect(vim))
Result is something like this: >
{
_os_proc_children = <function 1>,
_os_proc_info = <function 2>,
...
api = {
nvim__id = <function 5>,
nvim__id_array = <function 6>,
...
},
deepcopy = <function 106>,
gsplit = <function 107>,
...
}
To find documentation on e.g. the "deepcopy" function: >
:help vim.deepcopy
Note that underscore-prefixed functions (e.g. "_os_proc_children") are
internal/private and must not be used by plugins.
------------------------------------------------------------------------------
VIM.API *lua-api*
`vim.api` exposes the full Nvim |API| as a table of Lua functions.
Example: to use the "nvim_get_current_line()" API function, call
"vim.api.nvim_get_current_line()": >
print(tostring(vim.api.nvim_get_current_line()))
------------------------------------------------------------------------------
VIM.LOOP *lua-loop*
`vim.loop` exposes all features of the Nvim event-loop. This is a low-level
API that provides functionality for networking, filesystem, and process
management. Try this command to see available functions: >
:lua print(vim.inspect(vim.loop))
Reference: http://docs.libuv.org
Examples: https://github.com/luvit/luv/tree/master/examples
*E5560* *lua-loop-callbacks*
It is an error to directly invoke `vim.api` functions (except |api-fast|) in
`vim.loop` callbacks. For example, this is an error: >
local timer = vim.loop.new_timer()
timer:start(1000, 0, function()
vim.api.nvim_command('echomsg "test"')
end)
To avoid the error use |vim.schedule_wrap()| to defer the callback: >
local timer = vim.loop.new_timer()
timer:start(1000, 0, vim.schedule_wrap(function()
vim.api.nvim_command('echomsg "test"')
end))
Example: repeating timer
1. Save this code to a file.
2. Execute it with ":luafile %". >
-- Create a timer handle (implementation detail: uv_timer_t).
local timer = vim.loop.new_timer()
local i = 0
-- Waits 1000ms, then repeats every 750ms until timer:close().
timer:start(1000, 750, function()
print('timer invoked! i='..tostring(i))
if i > 4 then
timer:close() -- Always close handles to avoid leaks.
end
i = i + 1
end)
print('sleeping');
Example: TCP echo-server *tcp-server*
1. Save this code to a file.
2. Execute it with ":luafile %".
3. Note the port number.
4. Connect from any TCP client (e.g. "nc 0.0.0.0 36795"): >
local function create_server(host, port, on_connection)
local server = vim.loop.new_tcp()
server:bind(host, port)
server:listen(128, function(err)
assert(not err, err) -- Check for errors.
local sock = vim.loop.new_tcp()
server:accept(sock) -- Accept client connection.
on_connection(sock) -- Start reading messages.
end)
return server
end
local server = create_server('0.0.0.0', 0, function(sock)
sock:read_start(function(err, chunk)
assert(not err, err) -- Check for errors.
if chunk then
sock:write(chunk) -- Echo received messages to the channel.
else -- EOF (stream closed).
sock:close() -- Always close handles to avoid leaks.
end
end)
end)
print('TCP echo-server listening on port: '..server:getsockname().port)
------------------------------------------------------------------------------
VIM *lua-util*
vim.in_fast_event() *vim.in_fast_event()*
Returns true if the code is executing as part of a "fast" event
handler, where most of the API is disabled. These are low-level events
(e.g. |lua-loop-callbacks|) which can be invoked whenever Nvim polls
for input. When this is `false` most API functions are callable (but
may be subject to other restrictions such as |textlock|).
vim.stricmp({a}, {b}) *vim.stricmp()*
Compares strings case-insensitively. Returns 0, 1 or -1 if strings
are equal, {a} is greater than {b} or {a} is lesser than {b},
respectively.
vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()*
Convert byte index to UTF-32 and UTF-16 indicies. If {index} is not
supplied, the length of the string is used. All indicies are zero-based.
Returns two values: the UTF-32 and UTF-16 indicies respectively.
Embedded NUL bytes are treated as terminating the string. Invalid
UTF-8 bytes, and embedded surrogates are counted as one code
point each. An {index} in the middle of a UTF-8 sequence is rounded
upwards to the end of that sequence.
vim.str_byteindex({str}, {index}[, {use_utf16}]) *vim.str_byteindex()*
Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not
supplied, it defaults to false (use UTF-32). Returns the byte index.
Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. An {index}
in the middle of a UTF-16 sequence is rounded upwards to the end of that
sequence.
vim.schedule({callback}) *vim.schedule()*
Schedules {callback} to be invoked soon by the main event-loop. Useful
to avoid |textlock| or other temporary restrictions.
vim.type_idx *vim.type_idx*
Type index for use in |lua-special-tbl|. Specifying one of the
values from |vim.types| allows typing the empty table (it is
unclear whether empty lua table represents empty list or empty array)
and forcing integral numbers to be |Float|. See |lua-special-tbl| for
more details.
vim.val_idx *vim.val_idx*
Value index for tables representing |Float|s. A table representing
floating-point value 1.0 looks like this: >
{
[vim.type_idx] = vim.types.float,
[vim.val_idx] = 1.0,
}
< See also |vim.type_idx| and |lua-special-tbl|.
vim.types *vim.types*
Table with possible values for |vim.type_idx|. Contains two sets
of key-value pairs: first maps possible values for |vim.type_idx|
to human-readable strings, second maps human-readable type names to
values for |vim.type_idx|. Currently contains pairs for `float`,
`array` and `dictionary` types.
Note: one must expect that values corresponding to `vim.types.float`,
`vim.types.array` and `vim.types.dictionary` fall under only two
following assumptions:
1. Value may serve both as a key and as a value in a table. Given the
properties of lua tables this basically means “value is not `nil`”.
2. For each value in `vim.types` table `vim.types[vim.types[value]]`
is the same as `value`.
No other restrictions are put on types, and it is not guaranteed that
values corresponding to `vim.types.float`, `vim.types.array` and
`vim.types.dictionary` will not change or that `vim.types` table will
only contain values for these three types.
==============================================================================
Lua module: vim *lua-vim*
inspect({object}, {options}) *vim.inspect()*
Return a human-readable representation of the given object.
See also: ~
https://github.com/kikito/inspect.lua
https://github.com/mpeterv/vinspect
paste({lines}, {phase}) *vim.paste()*
Paste handler, invoked by |nvim_paste()| when a conforming UI
(such as the |TUI|) pastes text into the editor.
Parameters: ~
{lines} |readfile()|-style list of lines to paste.
|channel-lines|
{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)
Return: ~
false if client should cancel the paste.
See also: ~
|paste|
schedule_wrap({cb}) *vim.schedule_wrap()*
Defers callback `cb` until the Nvim API is safe to call.
See also: ~
|lua-loop-callbacks|
|vim.schedule()|
|vim.in_fast_event()|
deepcopy({orig}) *vim.deepcopy()*
Returns a deep copy of the given object. Non-table objects are
copied as in a typical Lua assignment, whereas table objects
are copied recursively.
Parameters: ~
{orig} Table to copy
Return: ~
New table of copied keys and (nested) values.
gsplit({s}, {sep}, {plain}) *vim.gsplit()*
Splits a string at each instance of a separator.
Parameters: ~
{s} String to split
{sep} Separator string or pattern
{plain} If `true` use `sep` literally (passed to
String.find)
Return: ~
Iterator over the split components
See also: ~
|vim.split()|
https://www.lua.org/pil/20.2.html
http://lua-users.org/wiki/StringLibraryTutorial
split({s}, {sep}, {plain}) *vim.split()*
Splits a string at each instance of a separator.
Examples: >
split(":aa::b:", ":") --> {'','aa','','bb',''}
split("axaby", "ab?") --> {'','x','y'}
split(x*yz*o, "*", true) --> {'x','yz','o'}
<
Parameters: ~
{s} String to split
{sep} Separator string or pattern
{plain} If `true` use `sep` literally (passed to
String.find)
Return: ~
List-like table of the split components.
See also: ~
|vim.gsplit()|
tbl_contains({t}, {value}) *vim.tbl_contains()*
Checks if a list-like (vector) table contains `value` .
Parameters: ~
{t} Table to check
{value} Value to compare
Return: ~
true if `t` contains `value`
tbl_extend({behavior}, {...}) *vim.tbl_extend()*
Merges two or more map-like tables.
Parameters: ~
{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
{...} Two or more map-like tables.
See also: ~
|extend()|
tbl_flatten({t}) *vim.tbl_flatten()*
Creates a copy of a list-like table such that any nested
tables are "unrolled" and appended to the result.
Parameters: ~
{t} List-like table
Return: ~
Flattened copy of the given list-like table.
trim({s}) *vim.trim()*
Trim whitespace (Lua pattern "%s") from both sides of a
string.
Parameters: ~
{s} String to trim
Return: ~
String with whitespace removed from its beginning and end
See also: ~
https://www.lua.org/pil/20.2.html
pesc({s}) *vim.pesc()*
Escapes magic chars in a Lua pattern string.
Parameters: ~
{s} String to escape
Return: ~
%-escaped pattern string
See also: ~
https://github.com/rxi/lume
vim:tw=78:ts=8:ft=help:norl:
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -404,7 +404,7 @@ tag char note action in Normal mode ~
|t| t{char} 1 cursor till before Nth occurrence of {char}
to the right
|u| u 2 undo changes
|v| v start characterwise Visual mode
|v| v start charwise Visual mode
|w| w 1 cursor N words forward
|x| ["x]x 2 delete N characters under and after the
cursor [into register x]
@ -767,6 +767,7 @@ tag char note action in Normal mode ~
|gn| gn 1,2 find the next match with the last used
search pattern and Visually select it
|gm| gm 1 go to character at middle of the screenline
|gM| gM 1 go to character at middle of the text line
|go| go 1 cursor to byte N in the buffer
|gp| ["x]gp 2 put the text [from register x] after the
cursor N times, leave the cursor after it
@ -865,7 +866,7 @@ These can be used after an operator, but before a {motion} has been entered.
tag char action in Operator-pending mode ~
-----------------------------------------------------------------------
|o_v| v force operator to work characterwise
|o_v| v force operator to work charwise
|o_V| V force operator to work linewise
|o_CTRL-V| CTRL-V force operator to work blockwise
@ -977,7 +978,7 @@ tag command note action in Visual mode ~
|v_r| r 2 replace highlighted area with a character
|v_s| s 2 delete highlighted area and start insert
|v_u| u 2 make highlighted area lowercase
|v_v| v make Visual mode characterwise or stop
|v_v| v make Visual mode charwise or stop
Visual mode
|v_x| x 2 delete the highlighted area
|v_y| y yank the highlighted area
@ -1163,11 +1164,13 @@ tag command action ~
|:cNfile| :cNf[ile] go to last error in previous file
|:cabbrev| :ca[bbrev] like ":abbreviate" but for Command-line mode
|:cabclear| :cabc[lear] clear all abbreviations for Command-line mode
|:cabove| :cabo[ve] go to error above current line
|:caddbuffer| :cad[dbuffer] add errors from buffer
|:caddexpr| :cadde[xpr] add errors from expr
|:caddfile| :caddf[ile] add error message to current quickfix list
|:call| :cal[l] call a function
|:catch| :cat[ch] part of a :try command
|:cbelow| :cbe[low] go to error below current line
|:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window
|:cbuffer| :cb[uffer] parse error messages and jump to first error
|:cc| :cc go to specific error
@ -1324,12 +1327,14 @@ tag command action ~
|:lNext| :lN[ext] go to previous entry in location list
|:lNfile| :lNf[ile] go to last entry in previous file
|:list| :l[ist] print lines
|:labove| :lab[ove] go to location above current line
|:laddexpr| :lad[dexpr] add locations from expr
|:laddbuffer| :laddb[uffer] add locations from buffer
|:laddfile| :laddf[ile] add locations to current location list
|:last| :la[st] go to the last file in the argument list
|:language| :lan[guage] set the language (locale)
|:later| :lat[er] go to newer change, redo
|:lbelow| :lbe[low] go to location below current line
|:lbottom| :lbo[ttom] scroll to the bottom of the location window
|:lbuffer| :lb[uffer] parse locations and jump to first location
|:lcd| :lc[d] change directory locally

View File

@ -271,7 +271,7 @@ and <> are part of what you type, the context should make this clear.
operator is pending.
- Ex commands can be used to move the cursor. This can be
used to call a function that does some complicated motion.
The motion is always characterwise exclusive, no matter
The motion is always charwise exclusive, no matter
what ":" command is used. This means it's impossible to
include the last character of a line without the line break
(unless 'virtualedit' is set).
@ -378,11 +378,11 @@ notation meaning equivalent decimal value(s) ~
<kEqual> keypad = *keypad-equal*
<kEnter> keypad Enter *keypad-enter*
<k0> - <k9> keypad 0 to 9 *keypad-0* *keypad-9*
<S-...> shift-key *shift* *<S-*
<C-...> control-key *control* *ctrl* *<C-*
<M-...> alt-key or meta-key *META* *ALT* *<M-*
<A-...> same as <M-...> *<A-*
<D-...> command-key or "super" key *<D-*
<S-> shift-key *shift* *<S-*
<C-> control-key *control* *ctrl* *<C-*
<M-> alt-key or meta-key *META* *ALT* *<M-*
<A-…> same as <M-…> *<A-*
<D-> command-key or "super" key *<D-*
-----------------------------------------------------------------------
Note: The shifted cursor keys, the help key, and the undo key are only

575
runtime/doc/lsp.txt Normal file
View File

@ -0,0 +1,575 @@
*lsp.txt* Nvim LSP API
NVIM REFERENCE MANUAL
Nvim Language Server Protocol (LSP) API *lsp*
Nvim is a client to the Language Server Protocol:
https://microsoft.github.io/language-server-protocol/
Type |gO| to see the table of contents.
================================================================================
LANGUAGE SERVER PROTOCOL (LSP) CLIENT *lsp-intro*
The `vim.lsp` Lua module provides a flexible API for consuming LSP servers.
To use LSP in practice, a language server must be installed.
https://microsoft.github.io/language-server-protocol/implementors/servers/
After installing a language server to your machine, you must tell Nvim how to
start and interact with that language server.
- Easy way: use the configs provided here by the nvim-lsp plugin.
https://github.com/neovim/nvim-lsp
- Low-level way: use |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()|
directly. Useful if you want to build advanced LSP plugins based on the
Nvim LSP module. |lsp-advanced-js-example|
*lsp-config*
Nvim LSP client will automatically provide inline diagnostics when available.
|lsp-callbacks| But you probably want to use other features too, such as
go-to-definition, "hover", etc. Example config: >
nnoremap <silent> gd <cmd>lua vim.lsp.buf.declaration()<CR>
nnoremap <silent> <c-]> <cmd>lua vim.lsp.buf.definition()<CR>
nnoremap <silent> K <cmd>lua vim.lsp.buf.hover()<CR>
nnoremap <silent> gD <cmd>lua vim.lsp.buf.implementation()<CR>
nnoremap <silent> <c-k> <cmd>lua vim.lsp.buf.signature_help()<CR>
nnoremap <silent> 1gD <cmd>lua vim.lsp.buf.type_definition()<CR>
<
*vim.lsp.omnifunc()*
Nvim provides the vim.lsp.omnifunc 'omnifunc' handler which allows
|i_CTRL-X_CTRL-O| to consume LSP completion features. Example config (note the
use of |v:lua| to call Lua from Vimscript): >
" Use LSP omni-completion in Python files.
autocmd Filetype python setlocal omnifunc=v:lua.vim.lsp.omnifunc
FAQ ~
> How to force-reload LSP?
Stop all clients, then reload the buffer. >
:lua vim.lsp.stop_all_clients()
:edit
> Why isn't completion working?
In the buffer where you want to use LSP, check that 'omnifunc' is set to
"v:lua.vim.lsp.omnifunc": >
:verbose set omnifunc?
Some other plugin may be overriding the option. To avoid that, you could set
the option in an |after-directory| ftplugin, e.g. "after/ftplugin/python.vim".
================================================================================
*lsp-core-api*
These are the core api functions for working with clients. You will mainly be
using |vim.lsp.start_client()| and |vim.lsp.buf_attach_client()| for operations
and |vim.lsp.get_client_by_id()| to retrieve a client by its id after it has
initialized (or {config.on_init}. see below)
*vim.lsp.start_client()*
vim.lsp.start_client({config})
The main function used for starting clients.
Start a client and initialize it.
Its arguments are passed via a configuration object {config}.
Mandatory parameters:~
`root_dir`
{string} specifying the directory where the LSP server will base
as its rootUri on initialization.
`cmd`
{string} or {list} which is the base command to execute for the LSP. A
string will be run using |'shell'| and a list will be interpreted as a
bare command with arguments passed. This is the same as |jobstart()|.
Optional parameters:~
`cmd_cwd`
{string} specifying the directory to launch the `cmd` process. This is not
related to `root_dir`.
By default, |getcwd()| is used.
`cmd_env`
{table} specifying the environment flags to pass to the LSP on spawn.
This can be specified using keys like a map or as a list with `k=v` pairs
or both. Non-string values are coerced to a string.
For example:
`{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }`
`capabilities`
A {table} which will be used instead of
`vim.lsp.protocol.make_client_capabilities()` which contains Nvim's
default capabilities and passed to the language server on initialization.
You'll probably want to use make_client_capabilities() and modify the
result.
NOTE:
To send an empty dictionary, you should use
`{[vim.type_idx]=vim.types.dictionary}` Otherwise, it will be encoded as
an array.
`callbacks`
A {table} of whose keys are language server method names and the values
are `function(err, method, params, client_id)` See |lsp-callbacks| for
more. This will be combined with |lsp-default-callbacks| to resolve
the callbacks for a client as a fallback.
`init_options`
A {table} of values to pass in the initialization request as
`initializationOptions`. See the `initialize` in the LSP spec.
`name`
A {string} used in log messages. Defaults to {client_id}
`offset_encoding`
One of "utf-8", "utf-16", or "utf-32" which is the encoding that the LSP
server expects.
The default encoding for Language Server Protocol is UTF-16, but there are
language servers which may use other encodings.
By default, it is "utf-16" as specified in the LSP specification. The
client does not verify this is correct.
`on_error(code, ...)`
A function for handling errors thrown by client operation. {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.
`vim.lsp.client_errors[code]` can be used to retrieve a human
understandable string.
`before_init(initialize_params, config)`
A function which is called *before* the request `initialize` is completed.
`initialize_params` contains the parameters we are sending to the server
and `config` is the config that was passed to `start_client()` for
convenience. You can use this to modify parameters before they are sent.
`on_init(client, initialize_result)`
A function which is called after the request `initialize` is completed.
`initialize_result` contains `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.
`on_attach(client, bufnr)`
A function which is called after the client is attached to a buffer.
`on_exit(code, signal, client_id)`
A function which is called after the client has exited. code is the exit
code of the process, and signal is a number describing the signal used to
terminate (if any).
`trace`
"off" | "messages" | "verbose" | nil passed directly to the language
server in the initialize request.
Invalid/empty values will default to "off"
Returns:~
{client_id}
You can use |vim.lsp.get_client_by_id()| to get the actual client object.
See |lsp-client| for what the client structure will be.
NOTE: The client is only available *after* it has been initialized, which
may happen after a small delay (or never if there is an error). For this
reason, you may want to use `on_init` to do any actions once the client has
been initialized.
*lsp-client*
The client object has some methods and members related to using the client.
Methods:~
`request(method, params, [callback])`
Send a request to the server. If callback is not specified, it will use
{client.callbacks} to try to find a callback. If one is not found there,
then an error will occur.
This is a thin wrapper around {client.rpc.request} with some additional
checking.
Returns a boolean to indicate if the notification was successful. If it
is false, then it will always be false (the client has shutdown).
If it was successful, then it will return the request id as the second
result. You can use this with `notify("$/cancel", { id = request_id })`
to cancel the request. This helper is made automatically with
|vim.lsp.buf_request()|
Returns: status, [client_id]
`notify(method, params)`
This is just {client.rpc.notify}()
Returns a boolean to indicate if the notification was successful. If it
is false, then it will always be false (the client has shutdown).
Returns: status
`cancel_request(id)`
This is just {client.rpc.notify}("$/cancelRequest", { id = id })
Returns the same as `notify()`.
`stop([force])`
Stop 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.
`is_stopped()`
Returns true if the client is fully stopped.
Members: ~
`id` (number)
The id allocated to the client.
`name` (string)
If a name is specified on creation, that will be used. Otherwise it is
just the client id. This is used for logs and messages.
`offset_encoding` (string)
The encoding used for communicating with the server. You can modify this
in the `on_init` method before text is sent to the server.
`callbacks` (table)
The callbacks used by the client as described in |lsp-callbacks|.
`config` (table)
A copy of the table that was passed by the user to
|vim.lsp.start_client()|.
`server_capabilities` (table)
The response from the server sent on `initialize` describing the
server's capabilities.
`resolved_capabilities` (table)
A normalized table of capabilities that we have detected based on the
initialize response from the server in `server_capabilities`.
*vim.lsp.buf_attach_client()*
vim.lsp.buf_attach_client({bufnr}, {client_id})
Implements the `textDocument/did*` notifications required to track a buffer
for any language server.
Without calling this, the server won't be notified of changes to a buffer.
*vim.lsp.get_client_by_id()*
vim.lsp.get_client_by_id({client_id})
Look up an active client by its id, returns nil if it is not yet initialized
or is not a valid id. Returns |lsp-client|
*vim.lsp.stop_client()*
vim.lsp.stop_client({client_id}, [{force}])
Stop 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.
You can also use `client.stop()` if you have access to the client.
*vim.lsp.stop_all_clients()*
vim.lsp.stop_all_clients([{force}])
|vim.lsp.stop_client()|, but for all active clients.
*vim.lsp.get_active_clients()*
vim.lsp.get_active_clients()
Return a list of all of the active clients. See |lsp-client| for a
description of what a client looks like.
*vim.lsp.rpc_response_error()*
vim.lsp.rpc_response_error({code}, [{message}], [{data}])
Helper function to create an RPC response object/table. This is an alias for
|vim.lsp.rpc.rpc_response_error|. Code must be an RPC error code as
described in `vim.lsp.protocol.ErrorCodes`.
You can describe an optional {message} string or arbitrary {data} to send to
the server.
================================================================================
LSP CALLBACKS *lsp-callbacks*
DEFAULT CALLBACKS ~
*vim.lsp.callbacks*
The `vim.lsp.callbacks` table defines default callbacks used when
creating a new client. Keys are LSP method names: >
:lua print(vim.inspect(vim.tbl_keys(vim.lsp.callbacks)))
These LSP requests/notifications are defined by default:
textDocument/publishDiagnostics
window/logMessage
window/showMessage
You can check these via `vim.tbl_keys(vim.lsp.callbacks)`.
These will be used preferrentially in `vim.lsp.buf` methods when handling
requests. They will also be used when responding to server requests and
notifications.
Use cases:
- Users can modify this to customize to their preferences.
- UI plugins can modify this by assigning to
`vim.lsp.callbacks[method]` so as to provide more specialized
handling, allowing you to leverage the UI capabilities available. UIs should
try to be conscientious of any existing changes the user may have set
already by checking for existing values.
Any callbacks passed directly to `request` methods on a server client will
have the highest precedence, followed by the `callbacks`.
You can override the default handlers,
- globally: by modifying the `vim.lsp.callbacks` table
- per-client: by passing the {callbacks} table parameter to
|vim.lsp.start_client|
Each handler has this signature: >
function(err, method, params, client_id)
Callbacks are functions which are called in a variety of situations by the
client. Their signature is `function(err, method, params, client_id)` They can
be set by the {callbacks} parameter for |vim.lsp.start_client| or via the
|vim.lsp.callbacks|.
Handlers are called for:
- Notifications from the server (`err` is always `nil`).
- Requests initiated by the server (`err` is always `nil`).
The handler can respond by returning two values: `result, err`
where `err` must be shaped like an RPC error:
`{ code, message, data? }`
You can use |vim.lsp.rpc_response_error()| to create this object.
- Handling requests initiated by the client if the request doesn't explicitly
specify a callback (such as in |vim.lsp.buf_request|).
================================================================================
VIM.LSP.PROTOCOL *vim.lsp.protocol*
The `vim.lsp.protocol` module provides constants defined in the LSP
specification, and helper functions for creating protocol-related objects.
https://github.com/microsoft/language-server-protocol/raw/gh-pages/_specifications/specification-3-14.md
Useful examples are `vim.lsp.protocol.ErrorCodes`. These objects allow reverse
lookup by either the number or string name.
e.g. vim.lsp.protocol.TextDocumentSyncKind.Full == 1
vim.lsp.protocol.TextDocumentSyncKind[1] == "Full"
Utility functions used internally are:
`vim.lsp.protocol.make_client_capabilities()`
Make a ClientCapabilities object. These are the builtin
capabilities.
`vim.lsp.protocol.resolve_capabilities(server_capabilites)`
Creates a normalized object describing capabilities from the server
capabilities.
================================================================================
*vim.lsp.util*
TODO: Describe the utils here for handling/applying things from LSP.
================================================================================
*lsp-buf-methods*
There are methods which operate on the buffer level for all of the active
clients attached to the buffer.
*vim.lsp.buf_request()*
vim.lsp.buf_request({bufnr}, {method}, {params}, [{callback}])
Send a async request for all the clients active and attached to the buffer.
Parameters: ~
{bufnr}: The buffer handle or 0 for the current buffer.
{method}: The LSP method name.
{params}: The parameters to send.
{callback}: An optional `function(err, method, params, client_id)` which
will be called for this request. If you do not specify it, then it will
use the client's callback in {client.callbacks}. See |lsp-callbacks| for
more information.
Returns:~
A table from client id to the request id for all of the successful
requests.
The second result is a function which can be used to cancel all the
requests. You can do this individually with `client.cancel_request()`
*vim.lsp.buf_request_sync()*
vim.lsp.buf_request_sync({bufnr}, {method}, {params}, [{timeout_ms}])
Calls |vim.lsp.buf_request()|, but it will wait for the result and block Vim
in the process.
The parameters are the same as |vim.lsp.buf_request()|, but the return
result is different.
It will wait maximum of {timeout_ms} which defaults to 100ms.
Returns:~
If the timeout is exceeded or a cancel is sent or an error, it will cancel
the request and return `nil, err` where `err` is a string that describes
the reason why it failed.
If it is successful, it will return a table from client id to result id.
*vim.lsp.buf_notify()*
vim.lsp.buf_notify({bufnr}, {method}, {params})
Send a notification to all servers on the buffer.
Parameters: ~
{bufnr}: The buffer handle or 0 for the current buffer.
{method}: The LSP method name.
{params}: The parameters to send.
================================================================================
*lsp-logging*
*vim.lsp.set_log_level()*
vim.lsp.set_log_level({level})
You can set the log level for language server client logging.
Possible values: "trace", "debug", "info", "warn", "error"
Default: "warn"
Example: `lua vim.lsp.set_log_level("debug")`
*vim.lsp.get_log_path()*
vim.lsp.get_log_path()
Returns the path that LSP logs are written.
*vim.lsp.log_levels*
vim.lsp.log_levels
Log level dictionary with reverse lookup as well.
Can be used to lookup the number from the name or vice-versa.
Levels: "trace" (0), "debug" (1), "info" (2), "warn" (3), "error" (4)
================================================================================
LSP EXAMPLE *lsp-advanced-js-example*
For more advanced configurations where just filtering by filetype isn't
sufficient, you can use the `vim.lsp.start_client()` and
`vim.lsp.buf_attach_client()` commands to easily customize the configuration
however you please. For example, if you want to do your own filtering, or
start a new LSP client based on the root directory for if you plan to work
with multiple projects in a single session. Below is a fully working Lua
example which can do exactly that.
The example will:
1. Check for each new buffer whether or not we want to start an LSP client.
2. Try to find a root directory by ascending from the buffer's path.
3. Create a new LSP for that root directory if one doesn't exist.
4. Attach the buffer to the client for that root directory.
>
-- Some path manipulation utilities
local function is_dir(filename)
local stat = vim.loop.fs_stat(filename)
return stat and stat.type == 'directory' or false
end
local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/"
-- Asumes filepath is a file.
local function dirname(filepath)
local is_changed = false
local result = filepath:gsub(path_sep.."([^"..path_sep.."]+)$", function()
is_changed = true
return ""
end)
return result, is_changed
end
local function path_join(...)
return table.concat(vim.tbl_flatten {...}, path_sep)
end
-- Ascend the buffer's path until we find the rootdir.
-- is_root_path is a function which returns bool
local function buffer_find_root_dir(bufnr, is_root_path)
local bufname = vim.api.nvim_buf_get_name(bufnr)
if vim.fn.filereadable(bufname) == 0 then
return nil
end
local dir = bufname
-- Just in case our algo is buggy, don't infinite loop.
for _ = 1, 100 do
local did_change
dir, did_change = dirname(dir)
if is_root_path(dir, bufname) then
return dir, bufname
end
-- If we can't ascend further, then stop looking.
if not did_change then
return nil
end
end
end
-- A table to store our root_dir to client_id lookup. We want one LSP per
-- root directory, and this is how we assert that.
local javascript_lsps = {}
-- Which filetypes we want to consider.
local javascript_filetypes = {
["javascript.jsx"] = true;
["javascript"] = true;
["typescript"] = true;
["typescript.jsx"] = true;
}
-- Create a template configuration for a server to start, minus the root_dir
-- which we will specify later.
local javascript_lsp_config = {
name = "javascript";
cmd = { path_join(os.getenv("JAVASCRIPT_LANGUAGE_SERVER_DIRECTORY"), "lib", "language-server-stdio.js") };
}
-- This needs to be global so that we can call it from the autocmd.
function check_start_javascript_lsp()
local bufnr = vim.api.nvim_get_current_buf()
-- Filter which files we are considering.
if not javascript_filetypes[vim.api.nvim_buf_get_option(bufnr, 'filetype')] then
return
end
-- Try to find our root directory. We will define this as a directory which contains
-- node_modules. Another choice would be to check for `package.json`, or for `.git`.
local root_dir = buffer_find_root_dir(bufnr, function(dir)
return is_dir(path_join(dir, 'node_modules'))
-- return vim.fn.filereadable(path_join(dir, 'package.json')) == 1
-- return is_dir(path_join(dir, '.git'))
end)
-- We couldn't find a root directory, so ignore this file.
if not root_dir then return end
-- Check if we have a client alredy or start and store it.
local client_id = javascript_lsps[root_dir]
if not client_id then
local new_config = vim.tbl_extend("error", javascript_lsp_config, {
root_dir = root_dir;
})
client_id = vim.lsp.start_client(new_config)
javascript_lsps[root_dir] = client_id
end
-- Finally, attach to the buffer to track changes. This will do nothing if we
-- are already attached.
vim.lsp.buf_attach_client(bufnr, client_id)
end
vim.api.nvim_command [[autocmd BufReadPost * lua check_start_javascript_lsp()]]
<
vim:tw=78:ts=8:ft=help:norl:

1001
runtime/doc/lua.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -786,7 +786,7 @@ g@{motion} Call the function set by the 'operatorfunc' option.
character of the text.
The function is called with one String argument:
"line" {motion} was |linewise|
"char" {motion} was |characterwise|
"char" {motion} was |charwise|
"block" {motion} was |blockwise-visual|
Although "block" would rarely appear, since it can
only result from Visual mode where "g@" is not useful.

View File

@ -556,7 +556,8 @@ allowed for the command that was used.
Vim was not able to create a swap file. You can still edit the file, but if
Vim unexpectedly exits the changes will be lost. And Vim may consume a lot of
memory when editing a big file. You may want to change the 'directory' option
to avoid this error. See |swap-file|.
to avoid this error. This error is not given when 'directory' is empty. See
|swap-file|.
*E140* >
Use ! to write partial buffer
@ -678,8 +679,8 @@ no argument has been specified.
Invalid argument: {arg}
Duplicate argument: {arg}
An Ex command or function has been executed, but an invalid argument has been
specified.
Ex command or function has been executed, but an invalid argument was
specified. Or a non-executable command was given to |system()|.
*E488* >
Trailing characters

View File

@ -60,11 +60,11 @@ 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"
moves the cursor leftwards to the "e" where the yank started.
*linewise* *characterwise*
*linewise* *charwise* *characterwise*
The operator either affects whole lines, or the characters between the start
and end position. Generally, motions that move between lines affect lines
(are linewise), and motions that move within a line affect characters (are
characterwise). However, there are some exceptions.
charwise). However, there are some exceptions.
*exclusive* *inclusive*
Character motion is either inclusive or exclusive. When inclusive, the
@ -106,10 +106,10 @@ This cannot be repeated: >
d:if 1<CR>
call search("f")<CR>
endif<CR>
Note that when using ":" any motion becomes characterwise exclusive.
Note that when using ":" any motion becomes charwise exclusive.
*forced-motion*
FORCING A MOTION TO BE LINEWISE, CHARACTERWISE OR BLOCKWISE
FORCING A MOTION TO BE LINEWISE, CHARWISE OR BLOCKWISE
When a motion is not of the type you would like to use, you can force another
type by using "v", "V" or CTRL-V just after the operator.
@ -121,22 +121,22 @@ deletes from the cursor position until the character below the cursor >
d<C-V>j
deletes the character under the cursor and the character below the cursor. >
Be careful with forcing a linewise movement to be used characterwise or
blockwise, the column may not always be defined.
Be careful with forcing a linewise movement to be used charwise or blockwise,
the column may not always be defined.
*o_v*
v When used after an operator, before the motion command: Force
the operator to work characterwise, also when the motion is
the operator to work charwise, also when the motion is
linewise. If the motion was linewise, it will become
|exclusive|.
If the motion already was characterwise, toggle
If the motion already was charwise, toggle
inclusive/exclusive. This can be used to make an exclusive
motion inclusive and an inclusive motion exclusive.
*o_V*
V When used after an operator, before the motion command: Force
the operator to work linewise, also when the motion is
characterwise.
charwise.
*o_CTRL-V*
CTRL-V When used after an operator, before the motion command: Force
@ -219,6 +219,12 @@ g^ When lines wrap ('wrap' on): To the first non-blank
gm Like "g0", but half a screenwidth to the right (or as
much as possible).
*gM*
gM Like "g0", but to halfway the text of the line.
With a count: to this percentage of text in the line.
Thus "10gM" is near the start of the text and "90gM"
is near the end of the text.
*g$* *g<End>*
g$ or g<End> When lines wrap ('wrap' on): To the last character of
the screen line and [count - 1] screen lines downward
@ -412,35 +418,35 @@ between Vi and Vim.
5. Text object motions *object-motions*
*(*
( [count] sentences backward. |exclusive| motion.
( [count] |sentence|s backward. |exclusive| motion.
*)*
) [count] sentences forward. |exclusive| motion.
) [count] |sentence|s forward. |exclusive| motion.
*{*
{ [count] paragraphs backward. |exclusive| motion.
{ [count] |paragraph|s backward. |exclusive| motion.
*}*
} [count] paragraphs forward. |exclusive| motion.
} [count] |paragraph|s forward. |exclusive| motion.
*]]*
]] [count] sections forward or to the next '{' in the
]] [count] |section|s forward or to the next '{' in the
first column. When used after an operator, then also
stops below a '}' in the first column. |exclusive|
Note that |exclusive-linewise| often applies.
*][*
][ [count] sections forward or to the next '}' in the
][ [count] |section|s forward or to the next '}' in the
first column. |exclusive|
Note that |exclusive-linewise| often applies.
*[[*
[[ [count] sections backward or to the previous '{' in
[[ [count] |section|s backward or to the previous '{' in
the first column. |exclusive|
Note that |exclusive-linewise| often applies.
*[]*
[] [count] sections backward or to the previous '}' in
[] [count] |section|s backward or to the previous '}' in
the first column. |exclusive|
Note that |exclusive-linewise| often applies.
@ -502,36 +508,36 @@ aw "a word", select [count] words (see |word|).
Leading or trailing white space is included, but not
counted.
When used in Visual linewise mode "aw" switches to
Visual characterwise mode.
Visual charwise mode.
*v_iw* *iw*
iw "inner word", select [count] words (see |word|).
White space between words is counted too.
When used in Visual linewise mode "iw" switches to
Visual characterwise mode.
Visual charwise mode.
*v_aW* *aW*
aW "a WORD", select [count] WORDs (see |WORD|).
Leading or trailing white space is included, but not
counted.
When used in Visual linewise mode "aW" switches to
Visual characterwise mode.
Visual charwise mode.
*v_iW* *iW*
iW "inner WORD", select [count] WORDs (see |WORD|).
White space between words is counted too.
When used in Visual linewise mode "iW" switches to
Visual characterwise mode.
Visual charwise mode.
*v_as* *as*
as "a sentence", select [count] sentences (see
|sentence|).
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
*v_is* *is*
is "inner sentence", select [count] sentences (see
|sentence|).
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
*v_ap* *ap*
ap "a paragraph", select [count] paragraphs (see
@ -552,14 +558,14 @@ a[ "a [] block", select [count] '[' ']' blocks. This
goes backwards to the [count] unclosed '[', and finds
the matching ']'. The enclosed text is selected,
including the '[' and ']'.
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
i] *v_i]* *v_i[* *i]* *i[*
i[ "inner [] block", select [count] '[' ']' blocks. This
goes backwards to the [count] unclosed '[', and finds
the matching ']'. The enclosed text is selected,
excluding the '[' and ']'.
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
a) *v_a)* *a)* *a(*
a( *vab* *v_ab* *v_a(* *ab*
@ -567,54 +573,54 @@ ab "a block", select [count] blocks, from "[count] [(" to
the matching ')', including the '(' and ')' (see
|[(|). Does not include white space outside of the
parenthesis.
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
i) *v_i)* *i)* *i(*
i( *vib* *v_ib* *v_i(* *ib*
ib "inner block", select [count] blocks, from "[count] [("
to the matching ')', excluding the '(' and ')' (see
|[(|).
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
a> *v_a>* *v_a<* *a>* *a<*
a< "a <> block", select [count] <> blocks, from the
[count]'th unmatched '<' backwards to the matching
'>', including the '<' and '>'.
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
i> *v_i>* *v_i<* *i>* *i<*
i< "inner <> block", select [count] <> blocks, from
the [count]'th unmatched '<' backwards to the matching
'>', excluding the '<' and '>'.
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
*v_at* *at*
at "a tag block", select [count] tag blocks, from the
[count]'th unmatched "<aaa>" backwards to the matching
"</aaa>", including the "<aaa>" and "</aaa>".
See |tag-blocks| about the details.
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
*v_it* *it*
it "inner tag block", select [count] tag blocks, from the
[count]'th unmatched "<aaa>" backwards to the matching
"</aaa>", excluding the "<aaa>" and "</aaa>".
See |tag-blocks| about the details.
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
a} *v_a}* *a}* *a{*
a{ *v_aB* *v_a{* *aB*
aB "a Block", select [count] Blocks, from "[count] [{" to
the matching '}', including the '{' and '}' (see
|[{|).
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
i} *v_i}* *i}* *i{*
i{ *v_iB* *v_i{* *iB*
iB "inner Block", select [count] Blocks, from "[count] [{"
to the matching '}', excluding the '{' and '}' (see
|[{|).
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
a" *v_aquote* *aquote*
a' *v_a'* *a'*
@ -628,7 +634,7 @@ a` *v_a`* *a`*
start of the line.
Any trailing white space is included, unless there is
none, then leading white space is included.
When used in Visual mode it is made characterwise.
When used in Visual mode it is made charwise.
Repeating this object in Visual mode another string is
included. A count is currently not used.
@ -1077,6 +1083,60 @@ When you split a window, the jumplist will be copied to the new window.
If you have included the ' item in the 'shada' option the jumplist will be
stored in the ShaDa file and restored when starting Vim.
*jumplist-stack*
When jumpoptions includes "stack", the jumplist behaves like the history in a
web browser and like the tag stack. When jumping to a new location from the
middle of the jumplist, the locations after the current position will be
discarded.
This behavior corresponds to the following situation in a web browser.
Navigate to first.com, second.com, third.com, fourth.com and then fifth.com.
Then navigate backwards twice so that third.com is displayed. At that point,
the history is:
- first.com
- second.com
- third.com <--
- fourth.com
- fifth.com
Finally, navigate to a different webpage, new.com. The history is
- first.com
- second.com
- third.com
- new.com <--
When the jumpoptions includes "stack", this is the behavior of neovim as well.
That is, given a jumplist like the following in which CTRL-O has been used to
move back three times to location X
jump line col file/text
2 1260 8 src/nvim/mark.c <-- location X-2
1 685 0 src/nvim/option_defs.h <-- location X-1
> 0 462 36 src/nvim/option_defs.h <-- location X
1 479 39 src/nvim/option_defs.h
2 213 2 src/nvim/mark.c
3 181 0 src/nvim/mark.c
jumping to location Y results in the locations after the current locations being
removed:
jump line col file/text
3 1260 8 src/nvim/mark.c
2 685 0 src/nvim/option_defs.h
1 462 36 src/nvim/option_defs.h <-- location X
>
Then, when yet another location Z is jumped to, the new location Y appears
directly after location X in the jumplist and location X remains in the same
position relative to the locations (X-1, X-2, etc., ...) that had been before it
prior to the original jump from X to Y:
jump line col file/text
4 1260 8 src/nvim/mark.c <-- location X-2
3 685 0 src/nvim/option_defs.h <-- location X-1
2 462 36 src/nvim/option_defs.h <-- location X
1 100 0 src/nvim/option_defs.h <-- location Y
>
CHANGE LIST JUMPS *changelist* *change-list-jumps* *E664*

View File

@ -1,7 +1,8 @@
NVIM REFERENCE MANUAL by Thiago de Arruda
NVIM REFERENCE MANUAL
This document was merged into |api.txt| and |develop.txt|.
==============================================================================
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -843,6 +843,14 @@ A jump table for the options with a short description can be found at |Q_op|.
name, precede it with a backslash.
- To include a comma in a directory name precede it with a backslash.
- A directory name may end in an '/'.
- For Unix and Win32, if a directory ends in two path separators "//",
the swap file name will be built from the complete path to the file
with all path separators changed to percent '%' signs. This will
ensure file name uniqueness in the backup directory.
On Win32, it is also possible to end with "\\". However, When a
separating comma is following, you must use "//", since "\\" will
include the comma in the file name. Therefore it is recommended to
use '//', instead of '\\'.
- Environment variables are expanded |:set_env|.
- Careful with '\' characters, type one before a space, type two to
get one in the option (see |option-backslash|), for example: >
@ -1875,7 +1883,7 @@ A jump table for the options with a short description can be found at |Q_op|.
security reasons.
*'dip'* *'diffopt'*
'diffopt' 'dip' string (default "internal,filler")
'diffopt' 'dip' string (default "internal,filler,closeoff")
global
Option settings for diff mode. It can consist of the following items.
All are optional. Items must be separated by a comma.
@ -1932,6 +1940,12 @@ A jump table for the options with a short description can be found at |Q_op|.
vertical Start diff mode with vertical splits (unless
explicitly specified otherwise).
closeoff When a window is closed where 'diff' is set
and there is only one window remaining in the
same tab page with 'diff' set, execute
`:diffoff` in that window. This undoes a
`:diffsplit` command.
hiddenoff Do not use diff mode for a buffer when it
becomes hidden.
@ -1978,7 +1992,7 @@ A jump table for the options with a short description can be found at |Q_op|.
possible. If it is not possible in any directory, but last
directory listed in the option does not exist, it is created.
- Empty means that no swap file will be used (recovery is
impossible!).
impossible!) and no |E303| error will be given.
- A directory "." means to put the swap file in the same directory as
the edited file. On Unix, a dot is prepended to the file name, so
it doesn't show in a directory listing. On MS-Windows the "hidden"
@ -1986,12 +2000,14 @@ A jump table for the options with a short description can be found at |Q_op|.
- A directory starting with "./" (or ".\" for Windows) means to
put the swap file relative to where the edited file is. The leading
"." is replaced with the path name of the edited file.
- For Unix and Win32, if a directory ends in two path separators "//"
or "\\", the swap file name will be built from the complete path to
the file with all path separators substituted to percent '%' signs.
This will ensure file name uniqueness in the preserve directory.
On Win32, when a separating comma is following, you must use "//",
since "\\" will include the comma in the file name.
- For Unix and Win32, if a directory ends in two path separators "//",
the swap file name will be built from the complete path to the file
with all path separators substituted to percent '%' signs. This will
ensure file name uniqueness in the preserve directory.
On Win32, it is also possible to end with "\\". However, When a
separating comma is following, you must use "//", since "\\" will
include the comma in the file name. Therefore it is recommended to
use '//', instead of '\\'.
- Spaces after the comma are ignored, other spaces are considered part
of the directory name. To have a space at the start of a directory
name, precede it with a backslash.
@ -2242,8 +2258,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'fileformat'* *'ff'*
'fileformat' 'ff' string (Windows default: "dos",
Unix default: "unix",
Macintosh default: "mac")
Unix default: "unix")
local to buffer
This gives the <EOL> of the current buffer, which is used for
reading/writing the buffer from/to a file:
@ -2265,7 +2280,6 @@ A jump table for the options with a short description can be found at |Q_op|.
'fileformats' 'ffs' string (default:
Vim+Vi Win32: "dos,unix",
Vim Unix: "unix,dos",
Vim Mac: "mac,unix,dos",
Vi others: "")
global
This gives the end-of-line (<EOL>) formats that will be tried when
@ -2348,7 +2362,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'fillchars'* *'fcs'*
'fillchars' 'fcs' string (default "")
local to window
global or local to window |global-local|
Characters to fill the statuslines and vertical separators.
It is a comma separated list of items:
@ -3443,6 +3457,17 @@ A jump table for the options with a short description can be found at |Q_op|.
Unprintable and zero-width Unicode characters are displayed as <xxxx>.
There is no option to specify these characters.
*'jumpoptions'* *'jop'*
'jumpoptions' 'jop' string (default "")
global
List of words that change the behavior of the |jumplist|.
stack Make the jumplist behave like the tagstack or like a
web browser. Relative location of entries in the
jumplist is preserved at the cost of discarding
subsequent entries when navigating backwards in the
jumplist and then jumping to a location.
|jumplist-stack|
*'joinspaces'* *'js'* *'nojoinspaces'* *'nojs'*
'joinspaces' 'js' boolean (default on)
global
@ -3657,7 +3682,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'listchars'* *'lcs'*
'listchars' 'lcs' string (default: "tab:> ,trail:-,nbsp:+"
Vi default: "eol:$")
local to window
global or local to window |global-local|
Strings to use in 'list' mode and for the |:list| command. It is a
comma separated list of string settings.
@ -3698,9 +3723,9 @@ A jump table for the options with a short description can be found at |Q_op|.
off and the line continues beyond the right of the
screen.
*lcs-precedes*
precedes:c Character to show in the first column, when 'wrap'
is off and there is text preceding the character
visible in the first column.
precedes:c Character to show in the first visible column of the
physical line, when there is text preceding the
character visible in the first column.
*lcs-conceal*
conceal:c Character to show in place of concealed text, when
'conceallevel' is set to 1. A space when omitted.
@ -4583,6 +4608,16 @@ A jump table for the options with a short description can be found at |Q_op|.
RedrawDebugRecompose guibg=Red redraw generated by the
compositor itself, due to a
grid being moved or deleted.
nothrottle Turn off throttling of the message grid. This is an
optimization that joins many small scrolls to one
larger scroll when drawing the message area (with
'display' msgsep flag active).
invalid Enable stricter checking (abort) of inconsistencies
of the internal screen state. This is mostly
useful when running nvim inside a debugger (and
the test suite).
nodelta Send all internally redrawn cells to the UI, even if
they are unchanged from the already displayed state.
*'redrawtime'* *'rdt'*
'redrawtime' 'rdt' number (default 2000)
@ -5159,7 +5194,7 @@ A jump table for the options with a short description can be found at |Q_op|.
unescaping, so to keep yourself sane use |:let-&| like shown above.
*shell-powershell*
To use powershell (on Windows): >
set shell=powershell shellquote=( shellpipe=\| shellxquote=
set shell=powershell shellquote= shellpipe=\| shellxquote=
set shellcmdflag=-NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command
set shellredir=\|\ Out-File\ -Encoding\ UTF8
@ -5722,7 +5757,7 @@ A jump table for the options with a short description can be found at |Q_op|.
current one. |:vsplit|
*'startofline'* *'sol'* *'nostartofline'* *'nosol'*
'startofline' 'sol' boolean (default on)
'startofline' 'sol' boolean (default off)
global
When "on" the commands listed below move the cursor to the first
non-blank of the line. When off the cursor is kept in the same column
@ -5990,6 +6025,8 @@ A jump table for the options with a short description can be found at |Q_op|.
vsplit Just like "split" but split vertically.
newtab Like "split", but open a new tab page. Overrules
"split" when both are present.
uselast If included, jump to the previously used window when
jumping to errors with |quickfix| commands.
*'synmaxcol'* *'smc'*
'synmaxcol' 'smc' number (default 3000)
@ -6149,6 +6186,14 @@ A jump table for the options with a short description can be found at |Q_op|.
match Match case
smart Ignore case unless an upper case letter is used
*'tagfunc'* *'tfu'*
'tagfunc' 'tfu' string (default: empty)
local to buffer
This option specifies a function to be used to perform tag searches.
The function gets the tag pattern and should return a List of matching
tags. See |tag-function| for an explanation of how to write the
function and an example.
*'taglength'* *'tl'*
'taglength' 'tl' number (default 0)
global
@ -6620,22 +6665,18 @@ A jump table for the options with a short description can be found at |Q_op|.
*'wildmenu'* *'wmnu'* *'nowildmenu'* *'nowmnu'*
'wildmenu' 'wmnu' boolean (default on)
global
When 'wildmenu' is on, command-line completion operates in an enhanced
mode. On pressing 'wildchar' (usually <Tab>) to invoke completion,
the possible matches are shown just above the command line, with the
first match highlighted (overwriting the status line, if there is
one). Keys that show the previous/next match, such as <Tab> or
CTRL-P/CTRL-N, cause the highlight to move to the appropriate match.
When 'wildmode' is used, "wildmenu" mode is used where "full" is
specified. "longest" and "list" do not start "wildmenu" mode.
You can check the current mode with |wildmenumode()|.
If there are more matches than can fit in the line, a ">" is shown on
the right and/or a "<" is shown on the left. The status line scrolls
as needed.
The "wildmenu" mode is abandoned when a key is hit that is not used
for selecting a completion.
While the "wildmenu" is active the following keys have special
meanings:
Enables "enhanced mode" of command-line completion. When user hits
<Tab> (or 'wildchar') to invoke completion, the possible matches are
shown in a menu just above the command-line (see 'wildoptions'), with
the first match highlighted (overwriting the statusline). Keys that
show the previous/next match (<Tab>/CTRL-P/CTRL-N) highlight the
match.
'wildmode' must specify "full": "longest" and "list" do not start
'wildmenu' mode. You can check the current mode with |wildmenumode()|.
The menu is canceled when a key is hit that is not used for selecting
a completion.
While the menu is active these keys have special meanings:
<Left> <Right> - select previous/next match (like CTRL-P/CTRL-N)
<Down> - in filename/menu name completion: move into a
@ -6645,15 +6686,12 @@ A jump table for the options with a short description can be found at |Q_op|.
<Up> - in filename/menu name completion: move up into
parent directory or parent menu.
This makes the menus accessible from the console |console-menus|.
If you prefer the <Left> and <Right> keys to move the cursor instead
of selecting a different match, use this: >
If you want <Left> and <Right> to move the cursor instead of selecting
a different match, use this: >
:cnoremap <Left> <Space><BS><Left>
:cnoremap <Right> <Space><BS><Right>
<
The "WildMenu" highlighting is used for displaying the current match
|hl-WildMenu|.
|hl-WildMenu| highlights the current match.
*'wildmode'* *'wim'*
'wildmode' 'wim' string (default: "full")
@ -6908,7 +6946,6 @@ A jump table for the options with a short description can be found at |Q_op|.
global
The number of milliseconds to wait for each character sent to the
screen. When positive, characters are sent to the UI one by one.
When negative, all redrawn characters cause a delay, even if the
character already was displayed by the UI. For debugging purposes.
See 'redrawdebug' for more options. For debugging purposes.
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -109,6 +109,36 @@ processing a quickfix or location list command, it will be aborted.
list for the current window is used instead of the
quickfix list.
*:cabo* *:cabove*
:[count]cabo[ve] Go to the [count] error above the current line in the
current buffer. If [count] is omitted, then 1 is
used. If there are no errors, then an error message
is displayed. Assumes that the entries in a quickfix
list are sorted by their buffer number and line
number. If there are multiple errors on the same line,
then only the first entry is used. If [count] exceeds
the number of entries above the current line, then the
first error in the file is selected.
*:lab* *:labove*
:[count]lab[ove] Same as ":cabove", except the location list for the
current window is used instead of the quickfix list.
*:cbe* *:cbelow*
:[count]cbe[low] Go to the [count] error below the current line in the
current buffer. If [count] is omitted, then 1 is
used. If there are no errors, then an error message
is displayed. Assumes that the entries in a quickfix
list are sorted by their buffer number and line
number. If there are multiple errors on the same
line, then only the first entry is used. If [count]
exceeds the number of entries below the current line,
then the last error in the file is selected.
*:lbe* *:lbelow*
:[count]lbe[low] Same as ":cbelow", except the location list for the
current window is used instead of the quickfix list.
*:cnf* *:cnfile*
:[count]cnf[ile][!] Display the first error in the [count] next file in
the list that includes a file name. If there are no

View File

@ -47,6 +47,7 @@ N is used to indicate an optional count that can be given before the command.
|g$| N g$ to last character in screen line (differs from "$"
when lines wrap)
|gm| gm to middle of the screen line
|gM| gM to middle of the line
|bar| N | to column N (default: 1)
|f| N f{char} to the Nth occurrence of {char} to the right
|F| N F{char} to the Nth occurrence of {char} to the left
@ -742,6 +743,7 @@ Short explanation of each option: *option-list*
'iskeyword' 'isk' characters included in keywords
'isprint' 'isp' printable characters
'joinspaces' 'js' two spaces after a period with a join command
'jumpoptions' 'jop' specifies how jumping is done
'keymap' 'kmp' name of a keyboard mapping
'keymodel' 'km' enable starting/stopping selection with keys
'keywordprg' 'kp' program to use for the "K" command

View File

@ -1270,7 +1270,7 @@ exactly four MessagePack objects:
Key Type Def Description ~
rt UInteger 0 Register type:
No Description ~
0 |characterwise-register|
0 |charwise-register|
1 |linewise-register|
2 |blockwise-register|
rw UInteger 0 Register width. Only valid

View File

@ -4720,18 +4720,19 @@ the same syntax file on all UIs.
*bold* *underline* *undercurl*
*inverse* *italic* *standout*
*strikethrough*
*nocombine* *strikethrough*
cterm={attr-list} *attr-list* *highlight-cterm* *E418*
attr-list is a comma separated list (without spaces) of the
following items (in any order):
bold
underline
undercurl curly underline
strikethrough
reverse
inverse same as reverse
italic
standout
strikethrough
nocombine override attributes instead of combining them
NONE no attributes used (used to reset it)
Note that "bold" can be used here and by using a bold font. They

View File

@ -838,4 +838,70 @@ Common arguments for the commands above:
< For a ":djump", ":dsplit", ":dlist" and ":dsearch" command the pattern
is used as a literal string, not as a search pattern.
==============================================================================
7. Using 'tagfunc' *tag-function*
It is possible to provide Vim with a function which will generate a list of
tags used for commands like |:tag|, |:tselect| and Normal mode tag commands
like |CTRL-]|.
The function used for generating the taglist is specified by setting the
'tagfunc' option. The function will be called with three arguments:
a:pattern The tag identifier used during the tag search.
a:flags List of flags to control the function behavior.
a:info Dict containing the following entries:
buf_ffname Full filename which can be used for priority.
user_data Custom data String, if stored in the tag
stack previously by tagfunc.
Currently two flags may be passed to the tag function:
'c' The function was invoked by a normal command being processed
(mnemonic: the tag function may use the context around the
cursor to perform a better job of generating the tag list.)
'i' In Insert mode, the user was completing a tag (with
|i_CTRL-X_CTRL-]|).
Note that when 'tagfunc' is set, the priority of the tags described in
|tag-priority| does not apply. Instead, the priority is exactly as the
ordering of the elements in the list returned by the function.
*E987*
The function should return a List of Dict entries. Each Dict must at least
include the following entries and each value must be a string:
name Name of the tag.
filename Name of the file where the tag is defined. It is
either relative to the current directory or a full path.
cmd Ex command used to locate the tag in the file. This
can be either an Ex search pattern or a line number.
Note that the format is similar to that of |taglist()|, which makes it possible
to use its output to generate the result.
The following fields are optional:
kind Type of the tag.
user_data String of custom data stored in the tag stack which
can be used to disambiguate tags between operations.
If the function returns |v:null| instead of a List, a standard tag lookup will
be performed instead.
It is not allowed to change the tagstack from inside 'tagfunc'. *E986*
The following is a hypothetical example of a function used for 'tagfunc'. It
uses the output of |taglist()| to generate the result: a list of tags in the
inverse order of file names.
>
function! TagFunc(pattern, flags, info)
function! CompareFilenames(item1, item2)
let f1 = a:item1['filename']
let f2 = a:item2['filename']
return f1 >=# f2 ?
\ -1 : f1 <=# f2 ? 1 : 0
endfunction
let result = taglist(a:pattern)
call sort(result, "CompareFilenames")
return result
endfunc
set tagfunc=TagFunc
<
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -201,8 +201,8 @@ the editor.
sent from Nvim, like for |ui-cmdline|.
["mode_change", mode, mode_idx]
The mode changed. The first parameter `mode` is a string representing
the current mode. `mode_idx` is an index into the array received in
Editor mode changed. The `mode` parameter is a string representing
the current mode. `mode_idx` is an index into the array emitted in
the `mode_info_set` event. UIs should change the cursor style
according to the properties specified in the corresponding item. The
set of modes reported will change in new versions of Nvim, for
@ -211,11 +211,11 @@ the editor.
["mouse_on"]
["mouse_off"]
Tells the client whether mouse support, as determined by |'mouse'|
option, is considered to be active in the current mode. This is mostly
useful for a terminal frontend, or other situations where Nvim mouse
would conflict with other usages of the mouse. It is safe for a client
to ignore this and always send mouse events.
|'mouse'| was enabled/disabled in the current editor mode. Useful for
a terminal UI, or other situations where Nvim mouse would conflict
with other usages of the mouse. UIs may ignore this and always send
mouse input, because 'mouse' decides the behavior of |nvim_input()|
implicitly.
["busy_start"]
["busy_stop"]

View File

@ -346,12 +346,13 @@ scroll:
g0 to first visible character in this line
g^ to first non-blank visible character in this line
gm to middle of this line
gm to middle of screen line
gM to middle of the text in this line
g$ to last visible character in this line
|<-- window -->|
some long text, part of which is visible ~
g0 g^ gm g$
|<-- window -->|
some long text, part of which is visible in one line ~
g0 g^ gm gM g$
BREAKING AT WORDS *edit-no-break*

View File

@ -55,6 +55,7 @@ the differences.
- 'showcmd' is enabled
- 'sidescroll' defaults to 1
- 'smarttab' is enabled
- 'startofline' is disabled
- 'tabpagemax' defaults to 50
- 'tags' defaults to "./tags;,tags"
- 'ttimeoutlen' defaults to 50
@ -168,6 +169,7 @@ Functions:
|system()|, |systemlist()| can run {cmd} directly (without 'shell')
Highlight groups:
|highlight-blend| controls blend level for a highlight group
|expr-highlight| highlight groups (prefixed with "Nvim")
|hl-NormalFloat| highlights floating window
|hl-NormalNC| highlights non-current windows
@ -206,6 +208,7 @@ Options:
'statusline' supports unlimited alignment sections
'tabline' %@Func@foo%X can call any function on mouse-click
'wildoptions' `pum` flag to use popupmenu for wildmode completion
'winblend' pseudo-transparency in floating windows |api-floatwin|
'winhighlight' window-local highlights
Signs:
@ -296,7 +299,7 @@ coerced to strings. See |id()| for more details, currently it uses
|c_CTRL-R| pasting a non-special register into |cmdline| omits the last <CR>.
Lua interface (|if_lua.txt|):
Lua interface (|lua.txt|):
- `:lua print("a\0b")` will print `a^@b`, like with `:echomsg "a\nb"` . In Vim
that prints `a` and `b` on separate lines, exactly like
@ -307,15 +310,15 @@ Lua interface (|if_lua.txt|):
- Lua package.path and package.cpath are automatically updated according to
'runtimepath': |lua-require|.
|input()| and |inputdialog()| support for each others features (return on
cancel and completion respectively) via dictionary argument (replaces all
other arguments if used).
|input()| and |inputdialog()| support user-defined cmdline highlighting.
Commands:
|:doautocmd| does not warn about "No matching autocommands".
Functions:
|input()| and |inputdialog()| support for each others features (return on
cancel and completion respectively) via dictionary argument (replaces all
other arguments if used).
|input()| and |inputdialog()| support user-defined cmdline highlighting.
Highlight groups:
|hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other
groups
@ -333,6 +336,11 @@ Macro/|recording| behavior
Motion:
The |jumplist| avoids useless/phantom jumps.
When the new option |jumpoptions| includes 'stack', the jumplist behaves
like the tagstack or history in a web browser--jumping from the middle of
the jumplist discards the locations after the jumped-from position
(|jumplist-stack|).
Normal commands:
|Q| is the same as |gQ|
@ -399,10 +407,10 @@ VimL (Vim script) compatibility:
Some legacy Vim features are not implemented:
- |if_py|: *python-bindeval* *python-Function* are not supported
- |if_lua|: the `vim` object is missing some legacy methods
- *if_perl*
- |if_lua|: Nvim Lua API is not compatible with Vim's "if_lua"
- *if_mzscheme*
- *if_perl*
- |if_py|: *python-bindeval* *python-Function* are not supported
- *if_tcl*
==============================================================================
@ -524,4 +532,4 @@ TUI:
always uses 7-bit control sequences.
==============================================================================
vim:tw=78:ts=8:sw=2:noet:ft=help:norl:
vim:tw=78:ts=8:sw=2:et:ft=help:norl:

View File

@ -48,7 +48,7 @@ position.
==============================================================================
2. Starting and stopping Visual mode *visual-start*
*v* *characterwise-visual*
*v* *charwise-visual*
[count]v Start Visual mode per character.
With [count] select the same number of characters or
lines as used for the last Visual operation, but at
@ -74,7 +74,7 @@ position.
If you use <Esc>, click the left mouse button or use any command that
does a jump to another buffer while in Visual mode, the highlighting stops
and no text is affected. Also when you hit "v" in characterwise Visual mode,
and no text is affected. Also when you hit "v" in charwise Visual mode,
"CTRL-V" in blockwise Visual mode or "V" in linewise Visual mode. If you hit
CTRL-Z the highlighting stops and the editor is suspended or a new shell is
started |CTRL-Z|.
@ -477,7 +477,7 @@ Commands in Select mode:
Otherwise, typed characters are handled as in Visual mode.
When using an operator in Select mode, and the selection is linewise, the
selected lines are operated upon, but like in characterwise selection. For
selected lines are operated upon, but like in charwise selection. For
example, when a whole line is deleted, it can later be pasted in the middle of
a line.
@ -510,7 +510,7 @@ gV Avoid the automatic reselection of the Visual area
selection.
*gh*
gh Start Select mode, characterwise. This is like "v",
gh Start Select mode, charwise. This is like "v",
but starts Select mode instead of Visual mode.
Mnemonic: "get highlighted".

View File

@ -201,9 +201,11 @@ CTRL-W CTRL_N *CTRL-W_CTRL-N*
|:find|. Doesn't split if {file} is not found.
CTRL-W CTRL-^ *CTRL-W_CTRL-^* *CTRL-W_^*
CTRL-W ^ Does ":split #", split window in two and edit alternate file.
When a count is given, it becomes ":split #N", split window
and edit buffer N.
CTRL-W ^ Split the current window in two and edit the alternate file.
When a count N is given, split the current window and edit
buffer N. Similar to ":sp #" and ":sp #N", but it allows the
other buffer to be unnamed. This command matches the behavior
of |CTRL-^|, except that it splits a window first.
CTRL-W ge *CTRL-W_ge*
Detach the current window as an external window.

View File

@ -1,7 +1,7 @@
" Vim support file to detect file types
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
" Last Change: 2019 Aug 26
" Last Change: 2019 Nov 26
" Listen very carefully, I will say this only once
if exists("did_load_filetypes")
@ -421,6 +421,9 @@ au BufNewFile,BufRead *.csp,*.fdr setf csp
au BufNewFile,BufRead *.pld setf cupl
au BufNewFile,BufRead *.si setf cuplsim
" Dart
au BufRead,BufNewfile *.dart,*.drt setf dart
" Debian Control
au BufNewFile,BufRead */debian/control setf debcontrol
au BufNewFile,BufRead control
@ -793,8 +796,8 @@ au BufNewFile,BufRead *.java,*.jav setf java
" JavaCC
au BufNewFile,BufRead *.jj,*.jjt setf javacc
" JavaScript, ECMAScript
au BufNewFile,BufRead *.js,*.javascript,*.es,*.mjs setf javascript
" JavaScript, ECMAScript, ES module script, CommonJS script
au BufNewFile,BufRead *.js,*.javascript,*.es,*.mjs,*.cjs setf javascript
" JavaScript with React
au BufNewFile,BufRead *.jsx setf javascriptreact
@ -975,6 +978,9 @@ au BufNewFile,BufRead hg-editor-*.txt setf hgcommit
" Mercurial config (looks like generic config file)
au BufNewFile,BufRead *.hgrc,*hgrc setf cfg
" Meson Build system config
au BufNewFile,BufRead meson.build,meson_options.txt setf meson
" Messages (logs mostly)
au BufNewFile,BufRead */log/{auth,cron,daemon,debug,kern,lpr,mail,messages,news/news,syslog,user}{,.log,.err,.info,.warn,.crit,.notice}{,.[0-9]*,-[0-9]*} setf messages

View File

@ -1,4 +1,4 @@
" Maintainer: Anmol Sethi <anmol@aubble.com>
" Maintainer: Anmol Sethi <hi@nhooyr.io>
" Previous Maintainer: SungHyun Nam <goweol@gmail.com>
if exists('b:did_ftplugin') || &filetype !=# 'man'
@ -20,13 +20,12 @@ setlocal wrap breakindent linebreak
setlocal nonumber norelativenumber
setlocal foldcolumn=0 colorcolumn=0 nolist nofoldenable
setlocal tagfunc=man#goto_tag
if !exists('g:no_plugin_maps') && !exists('g:no_man_maps')
nnoremap <silent> <buffer> j gj
nnoremap <silent> <buffer> k gk
nnoremap <silent> <buffer> gO :call man#show_toc()<CR>
nnoremap <silent> <buffer> <C-]> :Man<CR>
nnoremap <silent> <buffer> K :Man<CR>
nnoremap <silent> <buffer> <C-T> :call man#pop_tag()<CR>
if 1 == bufnr('%') || s:pager
nnoremap <silent> <buffer> <nowait> q :lclose<CR>:q<CR>
else

14
runtime/indent/Makefile Normal file
View File

@ -0,0 +1,14 @@
# Portable Makefile for running indent tests.
VIM = vim
VIMRUNTIME = ..
# Run the tests that didn't run yet or failed previously.
# If a test succeeds a testdir/*.out file will be written.
# If a test fails a testdir/*.fail file will be written.
test:
VIMRUNTIME=$(VIMRUNTIME) $(VIM) --clean --not-a-term -u testdir/runtest.vim
clean:
rm -f testdir/*.fail testdir/*.out

View File

@ -43,3 +43,5 @@ running. Add a test if the function exists and use ":finish", like this:
The user may have several options set unlike you, try to write the file such
that it works with any option settings. Also be aware of certain features not
being compiled in.
To test the indent file, see testdir/README.txt.

View File

@ -0,0 +1,97 @@
TESTING INDENT SCRIPTS
We'll use FILETYPE for the filetype name here.
FORMAT OF THE FILETYPE.IN FILE
First of all, create a FILETYPE.in file. It should contain:
- A modeline setting the 'filetype' and any other option values.
This must work like a comment for FILETYPE. E.g. for vim:
" vim: set ft=vim sw=4 :
- At least one block of lines to indent, prefixed with START_INDENT and
followed by END_INDENT. These lines must also look like a comment for your
FILETYPE. You would normally leave out all indent, so that the effect of
the indent command results in adding indent. Example:
" START_INDENT
func Some()
let x = 1
endfunc
" END_INDENT
If you just want to test normal indenting with default options, you can make
this a large number of lines. Just add all kinds of language constructs,
nested statements, etc. with valid syntax.
- Optionally, add lines with INDENT_EXE after START_INDENT, followed by a Vim
command. This will be executed before indenting the lines. Example:
" START_INDENT
" INDENT_EXE let g:vim_indent_cont = 6
let cmd =
\ 'some '
\ 'string'
" END_INDENT
Note that the command is not undone, you may need to reverse the effect for
the next block of lines.
- Alternatively to indenting all the lines between START_INDENT and
END_INDENT, use an INDENT_AT line, which specifies a pattern to find the
line to indent. Example:
" START_INDENT
" INDENT_AT this-line
func Some()
let f = x " this-line
endfunc
" END_INDENT
Alternatively you can use INDENT_NEXT to indent the line below the matching
pattern. Keep in mind that quite often it will indent relative to the
matching line:
" START_INDENT
" INDENT_NEXT next-line
func Some()
" next-line
let f = x
endfunc
" END_INDENT
Or use INDENT_PREV to indent the line above the matching pattern:
" START_INDENT
" INDENT_PREV prev-line
func Some()
let f = x
" prev-line
endfunc
" END_INDENT
It's best to keep the whole file valid for FILETYPE, so that syntax
highlighting works normally, and any indenting that depends on the syntax
highlighting also works.
RUNNING THE TEST
Before running the test, create a FILETYPE.ok file. You can leave it empty at
first.
Now run "make test" from the parent directory. After Vim has done the
indenting you will see a FILETYPE.fail file. This contains the actual result
of indenting, and it's different from the FILETYPE.ok file.
Check the contents of the FILETYPE.fail file. If it is perfectly OK, then
rename it to overwrite the FILETYPE.ok file. If you now run "make test" again,
the test will pass and create a FILETYPE.out file, which is identical to the
FILETYPE.ok file. The FILETYPE.fail file will be deleted.
If you try to run "make test" again you will notice that nothing happens,
because the FILETYPE.out file already exists. Delete it, or do "make clean",
so that the text runs again. If you edit the FILETYPE.in file, so that it's
newer than the FILETYPE.out file, the test will also run.

View File

@ -0,0 +1,132 @@
" Runs all the indent tests for which there is no .out file.
"
" Current directory must be runtime/indent.
" Only do this with the +eval feature
if 1
set nocp
filetype indent on
syn on
set nowrapscan
set report=9999
au! SwapExists * call HandleSwapExists()
func HandleSwapExists()
" Ignore finding a swap file for the test input and output, the user might be
" editing them and that's OK.
if expand('<afile>') =~ '.*\.\(in\|out\|fail\|ok\)'
let v:swapchoice = 'e'
endif
endfunc
let failed_count = 0
for fname in glob('testdir/*.in', 1, 1)
let root = substitute(fname, '\.in', '', '')
" Execute the test if the .out file does not exist of when the .in file is
" newer.
let in_time = getftime(fname)
let out_time = getftime(root . '.out')
if out_time < 0 || in_time > out_time
call delete(root . '.fail')
call delete(root . '.out')
set sw& ts& filetype=
exe 'split ' . fname
let did_some = 0
let failed = 0
let end = 1
while 1
" Indent all the lines between "START_INDENT" and "END_INDENT"
exe end
let start = search('\<START_INDENT\>')
let end = search('\<END_INDENT\>')
if start <= 0 || end <= 0 || end <= start
if did_some == 0
call append(0, 'ERROR: START_INDENT and/or END_INDENT not found')
let failed = 1
endif
break
else
let did_some = 1
" Execute all commands marked with INDENT_EXE and find any pattern.
let lnum = start
let pattern = ''
let at = ''
while 1
exe lnum + 1
let lnum_exe = search('\<INDENT_EXE\>')
exe lnum + 1
let indent_at = search('\<INDENT_\(AT\|NEXT\|PREV\)\>')
if lnum_exe > 0 && lnum_exe < end && (indent_at <= 0 || lnum_exe < indent_at)
exe substitute(getline(lnum_exe), '.*INDENT_EXE', '', '')
let lnum = lnum_exe
let start = lnum
elseif indent_at > 0 && indent_at < end
if pattern != ''
call append(indent_at, 'ERROR: duplicate pattern')
let failed = 1
break
endif
let text = getline(indent_at)
let pattern = substitute(text, '.*INDENT_\S*\s*', '', '')
let at = substitute(text, '.*INDENT_\(\S*\).*', '\1', '')
let lnum = indent_at
let start = lnum
else
break
endif
endwhile
exe start + 1
if pattern == ''
exe 'normal =' . (end - 1) . 'G'
else
let lnum = search(pattern)
if lnum <= 0
call append(indent_at, 'ERROR: pattern not found: ' . pattern)
let failed = 1
break
endif
if at == 'AT'
exe lnum
elseif at == 'NEXT'
exe lnum + 1
else
exe lnum - 1
endif
normal ==
endif
endif
endwhile
if !failed
" Check the resulting text equals the .ok file.
if getline(1, '$') != readfile(root . '.ok')
let failed = 1
endif
endif
if failed
let failed_count += 1
exe 'write ' . root . '.fail'
echoerr 'Test ' . fname . ' FAILED!'
else
exe 'write ' . root . '.out'
endif
quit! " close the indented file
endif
endfor
" Matching "if 1" at the start.
endif
if failed_count > 0
" have make report an error
cquit
endif
qall!

View File

@ -0,0 +1,46 @@
" vim: set ft=vim sw=4 :
" START_INDENT
func Some()
let x = 1
endfunc
let cmd =
\ 'some '
\ 'string'
" END_INDENT
" START_INDENT
" INDENT_EXE let g:vim_indent_cont = 6
let cmd =
\ 'some '
\ 'string'
" END_INDENT
" START_INDENT
" INDENT_EXE unlet g:vim_indent_cont
" INDENT_AT this-line
func Some()
let f = x " this-line
endfunc
" END_INDENT
" START_INDENT
" INDENT_NEXT next-line
func Some()
" next-line
let f = x
endfunc
" END_INDENT
" START_INDENT
" INDENT_PREV prev-line
func Some()
let f = x
" prev-line
endfunc
" END_INDENT

View File

@ -0,0 +1,46 @@
" vim: set ft=vim sw=4 :
" START_INDENT
func Some()
let x = 1
endfunc
let cmd =
\ 'some '
\ 'string'
" END_INDENT
" START_INDENT
" INDENT_EXE let g:vim_indent_cont = 6
let cmd =
\ 'some '
\ 'string'
" END_INDENT
" START_INDENT
" INDENT_EXE unlet g:vim_indent_cont
" INDENT_AT this-line
func Some()
let f = x " this-line
endfunc
" END_INDENT
" START_INDENT
" INDENT_NEXT next-line
func Some()
" next-line
let f = x
endfunc
" END_INDENT
" START_INDENT
" INDENT_PREV prev-line
func Some()
let f = x
" prev-line
endfunc
" END_INDENT

View File

@ -0,0 +1,112 @@
" Vim Keymap file for russian characters, layout 'jcuken', MS Windows
" Typewriter variant (slightly incompatible with XFree86 'ru' keymap -
" makes use of NUMERO SIGN)
" Useful mainly with utf-8 but may work with other encodings
" Derived from russian-jcuken.vim by Artem Chuprina <ran@ran.pp.ru>
" Typewriter variant of standart russian layout
" Maintainer: Denis Proskurin <danwerspb@gmail.com>
" Last Changed: 2015 May 15
" All characters are given literally, conversion to another encoding (e.g.,
" UTF-8) should work.
scriptencoding utf-8
let b:keymap_name = "ru"
loadkeymap
F А CYRILLIC CAPITAL LETTER A
< Б CYRILLIC CAPITAL LETTER BE
D В CYRILLIC CAPITAL LETTER VE
U Г CYRILLIC CAPITAL LETTER GHE
L Д CYRILLIC CAPITAL LETTER DE
T Е CYRILLIC CAPITAL LETTER IE
? Ё CYRILLIC CAPITAL LETTER IO
: Ж CYRILLIC CAPITAL LETTER ZHE
P З CYRILLIC CAPITAL LETTER ZE
B И CYRILLIC CAPITAL LETTER I
Q Й CYRILLIC CAPITAL LETTER SHORT I
R К CYRILLIC CAPITAL LETTER KA
K Л CYRILLIC CAPITAL LETTER EL
V М CYRILLIC CAPITAL LETTER EM
Y Н CYRILLIC CAPITAL LETTER EN
J О CYRILLIC CAPITAL LETTER O
G П CYRILLIC CAPITAL LETTER PE
H Р CYRILLIC CAPITAL LETTER ER
C С CYRILLIC CAPITAL LETTER ES
N Т CYRILLIC CAPITAL LETTER TE
E У CYRILLIC CAPITAL LETTER U
A Ф CYRILLIC CAPITAL LETTER EF
{ Х CYRILLIC CAPITAL LETTER HA
W Ц CYRILLIC CAPITAL LETTER TSE
X Ч CYRILLIC CAPITAL LETTER CHE
I Ш CYRILLIC CAPITAL LETTER SHA
O Щ CYRILLIC CAPITAL LETTER SHCHA
} Ъ CYRILLIC CAPITAL LETTER HARD SIGN
S Ы CYRILLIC CAPITAL LETTER YERU
M Ь CYRILLIC CAPITAL LETTER SOFT SIGN
\" Э CYRILLIC CAPITAL LETTER E
> Ю CYRILLIC CAPITAL LETTER YU
Z Я CYRILLIC CAPITAL LETTER YA
f а CYRILLIC SMALL LETTER A
, б CYRILLIC SMALL LETTER BE
d в CYRILLIC SMALL LETTER VE
u г CYRILLIC SMALL LETTER GHE
l д CYRILLIC SMALL LETTER DE
t е CYRILLIC SMALL LETTER IE
/ ё CYRILLIC SMALL LETTER IO
; ж CYRILLIC SMALL LETTER ZHE
p з CYRILLIC SMALL LETTER ZE
b и CYRILLIC SMALL LETTER I
q й CYRILLIC SMALL LETTER SHORT I
r к CYRILLIC SMALL LETTER KA
k л CYRILLIC SMALL LETTER EL
v м CYRILLIC SMALL LETTER EM
y н CYRILLIC SMALL LETTER EN
j о CYRILLIC SMALL LETTER O
g п CYRILLIC SMALL LETTER PE
h р CYRILLIC SMALL LETTER ER
c с CYRILLIC SMALL LETTER ES
n т CYRILLIC SMALL LETTER TE
e у CYRILLIC SMALL LETTER U
a ф CYRILLIC SMALL LETTER EF
[ х CYRILLIC SMALL LETTER HA
w ц CYRILLIC SMALL LETTER TSE
x ч CYRILLIC SMALL LETTER CHE
i ш CYRILLIC SMALL LETTER SHA
o щ CYRILLIC SMALL LETTER SHCHA
] ъ CYRILLIC SMALL LETTER HARD SIGN
s ы CYRILLIC SMALL LETTER YERU
m ь CYRILLIC SMALL LETTER SOFT SIGN
' э CYRILLIC SMALL LETTER E
. ю CYRILLIC SMALL LETTER YU
z я CYRILLIC SMALL LETTER YA
` |
1
2 -
3 /
4 "
5 :
6 ,
7 .
8 _
9 ?
0 %
- !
= ;
~ +
! 1
@ 2
# 3
$ 4
% 5
^ 6
& 7
* 8
( 9
) 0
_ =
+ \\
\\ )
\| (

View File

@ -289,7 +289,7 @@ function Inspector:putValue(v)
if tv == 'string' then
self:puts(smartQuote(escape(v)))
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or
tv == 'cdata' or tv == 'ctype' then
tv == 'cdata' or tv == 'ctype' or (vim and v == vim.NIL) then
self:puts(tostring(v))
elseif tv == 'table' then
self:putTable(v)

958
runtime/lua/vim/lsp.lua Normal file
View File

@ -0,0 +1,958 @@
local default_callbacks = require 'vim.lsp.callbacks'
local log = require 'vim.lsp.log'
local lsp_rpc = require 'vim.lsp.rpc'
local protocol = require 'vim.lsp.protocol'
local util = require 'vim.lsp.util'
local vim = vim
local nvim_err_writeln, nvim_buf_get_lines, nvim_command, nvim_buf_get_option
= vim.api.nvim_err_writeln, vim.api.nvim_buf_get_lines, vim.api.nvim_command, vim.api.nvim_buf_get_option
local uv = vim.loop
local tbl_isempty, tbl_extend = vim.tbl_isempty, vim.tbl_extend
local validate = vim.validate
local lsp = {
protocol = protocol;
callbacks = default_callbacks;
buf = require'vim.lsp.buf';
util = util;
-- Allow raw RPC access.
rpc = lsp_rpc;
-- Export these directly from rpc.
rpc_response_error = lsp_rpc.rpc_response_error;
-- You probably won't need this directly, since __tostring is set for errors
-- by the RPC.
-- format_rpc_error = lsp_rpc.format_rpc_error;
}
-- TODO improve handling of scratch buffers with LSP attached.
local function err_message(...)
nvim_err_writeln(table.concat(vim.tbl_flatten{...}))
nvim_command("redraw")
end
local function resolve_bufnr(bufnr)
validate { bufnr = { bufnr, 'n', true } }
if bufnr == nil or bufnr == 0 then
return vim.api.nvim_get_current_buf()
end
return bufnr
end
local function is_dir(filename)
validate{filename={filename,'s'}}
local stat = uv.fs_stat(filename)
return stat and stat.type == 'directory' or false
end
-- TODO Use vim.wait when that is available, but provide an alternative for now.
local wait = vim.wait or function(timeout_ms, condition, interval)
validate {
timeout_ms = { timeout_ms, 'n' };
condition = { condition, 'f' };
interval = { interval, 'n', true };
}
assert(timeout_ms > 0, "timeout_ms must be > 0")
local _ = log.debug() and log.debug("wait.fallback", timeout_ms)
interval = interval or 200
local interval_cmd = "sleep "..interval.."m"
local timeout = timeout_ms + uv.now()
-- TODO is there a better way to sync this?
while true do
uv.update_time()
if condition() then
return 0
end
if uv.now() >= timeout then
return -1
end
nvim_command(interval_cmd)
-- vim.loop.sleep(10)
end
end
local wait_result_reason = { [-1] = "timeout"; [-2] = "interrupted"; [-3] = "error" }
local valid_encodings = {
["utf-8"] = 'utf-8'; ["utf-16"] = 'utf-16'; ["utf-32"] = 'utf-32';
["utf8"] = 'utf-8'; ["utf16"] = 'utf-16'; ["utf32"] = 'utf-32';
UTF8 = 'utf-8'; UTF16 = 'utf-16'; UTF32 = 'utf-32';
}
local client_index = 0
local function next_client_id()
client_index = client_index + 1
return client_index
end
-- Tracks all clients created via lsp.start_client
local active_clients = {}
local all_buffer_active_clients = {}
local uninitialized_clients = {}
local function for_each_buffer_client(bufnr, callback)
validate {
callback = { callback, 'f' };
}
bufnr = resolve_bufnr(bufnr)
local client_ids = all_buffer_active_clients[bufnr]
if not client_ids or tbl_isempty(client_ids) then
return
end
for client_id in pairs(client_ids) do
local client = active_clients[client_id]
if client then
callback(client, client_id)
end
end
end
-- Error codes to be used with `on_error` from |vim.lsp.start_client|.
-- Can be used to look up the string from a the number or the number
-- from the string.
lsp.client_errors = tbl_extend("error", lsp_rpc.client_errors, vim.tbl_add_reverse_lookup {
ON_INIT_CALLBACK_ERROR = table.maxn(lsp_rpc.client_errors) + 1;
})
local function validate_encoding(encoding)
validate {
encoding = { encoding, 's' };
}
return valid_encodings[encoding:lower()]
or error(string.format("Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding))
end
local function validate_command(input)
local cmd, cmd_args
if type(input) == 'string' then
-- Use a shell to execute the command if it is a string.
cmd = vim.api.nvim_get_option('shell')
cmd_args = {vim.api.nvim_get_option('shellcmdflag'), input}
elseif vim.tbl_islist(input) then
cmd = input[1]
cmd_args = {}
-- Don't mutate our input.
for i, v in ipairs(input) do
assert(type(v) == 'string', "input arguments must be strings")
if i > 1 then
table.insert(cmd_args, v)
end
end
else
error("cmd type must be string or list.")
end
return cmd, cmd_args
end
local function optional_validator(fn)
return function(v)
return v == nil or fn(v)
end
end
local function validate_client_config(config)
validate {
config = { config, 't' };
}
validate {
root_dir = { config.root_dir, is_dir, "directory" };
callbacks = { config.callbacks, "t", true };
capabilities = { config.capabilities, "t", true };
cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), "directory" };
cmd_env = { config.cmd_env, "f", true };
name = { config.name, 's', true };
on_error = { config.on_error, "f", true };
on_exit = { config.on_exit, "f", true };
on_init = { config.on_init, "f", true };
before_init = { config.before_init, "f", true };
offset_encoding = { config.offset_encoding, "s", true };
}
local cmd, cmd_args = validate_command(config.cmd)
local offset_encoding = valid_encodings.UTF16
if config.offset_encoding then
offset_encoding = validate_encoding(config.offset_encoding)
end
return {
cmd = cmd; cmd_args = cmd_args;
offset_encoding = offset_encoding;
}
end
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
text = text .. '\n'
end
return text
end
local function text_document_did_open_handler(bufnr, client)
if not client.resolved_capabilities.text_document_open_close then
return
end
if not vim.api.nvim_buf_is_loaded(bufnr) then
return
end
local params = {
textDocument = {
version = 0;
uri = vim.uri_from_bufnr(bufnr);
-- TODO make sure our filetypes are compatible with languageId names.
languageId = nvim_buf_get_option(bufnr, 'filetype');
text = buf_get_full_text(bufnr);
}
}
client.notify('textDocument/didOpen', params)
end
--- Start a client and initialize it.
-- Its arguments are passed via a configuration object.
--
-- Mandatory parameters:
--
-- root_dir: {string} specifying the directory where the LSP server will base
-- as its rootUri on initialization.
--
-- cmd: {string} or {list} which is the base command to execute for the LSP. A
-- string will be run using |'shell'| and a list will be interpreted as a bare
-- command with arguments passed. This is the same as |jobstart()|.
--
-- Optional parameters:
-- cmd_cwd: {string} specifying the directory to launch the `cmd` process. This
-- is not related to `root_dir`. By default, |getcwd()| is used.
--
-- cmd_env: {table} specifying the environment flags to pass to the LSP on
-- spawn. This can be specified using keys like a map or as a list with `k=v`
-- pairs or both. Non-string values are coerced to a string.
-- For example: `{ "PRODUCTION=true"; "TEST=123"; PORT = 8080; HOST = "0.0.0.0"; }`.
--
-- capabilities: A {table} which will be used instead of
-- `vim.lsp.protocol.make_client_capabilities()` which contains neovim's
-- default capabilities and passed to the language server on initialization.
-- You'll probably want to use make_client_capabilities() and modify the
-- result.
-- NOTE:
-- To send an empty dictionary, you should use
-- `{[vim.type_idx]=vim.types.dictionary}` Otherwise, it will be encoded as
-- an array.
--
-- callbacks: A {table} of whose keys are language server method names and the
-- values are `function(err, method, params, client_id)`.
-- This will be called for:
-- - notifications from the server, where `err` will always be `nil`
-- - requests initiated by the server. For these, you can respond by returning
-- two values: `result, err`. The err must be in the format of an RPC error,
-- which is `{ code, message, data? }`. You can use |vim.lsp.rpc_response_error()|
-- to help with this.
-- - as a callback for requests initiated by the client if the request doesn't
-- explicitly specify a callback.
--
-- init_options: A {table} of values to pass in the initialization request
-- as `initializationOptions`. See the `initialize` in the LSP spec.
--
-- name: A {string} used in log messages. Defaults to {client_id}
--
-- offset_encoding: One of 'utf-8', 'utf-16', or 'utf-32' which is the
-- encoding that the LSP server expects. By default, it is 'utf-16' as
-- specified in the LSP specification. The client does not verify this
-- is correct.
--
-- on_error(code, ...): A function for handling errors thrown by client
-- operation. {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. `vim.lsp.client_errors[code]` can be used to retrieve a
-- human understandable string.
--
-- before_init(initialize_params, config): A function which is called *before*
-- the request `initialize` is completed. `initialize_params` contains
-- the parameters we are sending to the server and `config` is the config that
-- was passed to `start_client()` for convenience. You can use this to modify
-- parameters before they are sent.
--
-- on_init(client, initialize_result): A function which is called after the
-- request `initialize` is completed. `initialize_result` contains
-- `capabilities` and anything else the server may send. For example, `clangd`
-- sends `result.offsetEncoding` if `capabilities.offsetEncoding` was sent to
-- it.
--
-- on_exit(code, signal, client_id): A function which is called after the
-- client has exited. code is the exit code of the process, and signal is a
-- number describing the signal used to terminate (if any).
--
-- on_attach(client, bufnr): A function which is called after the client is
-- attached 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'
--
-- @returns client_id You can use |vim.lsp.get_client_by_id()| to get the
-- actual client.
--
-- NOTE: The client is only available *after* it has been initialized, which
-- may happen after a small delay (or never if there is an error).
-- For this reason, you may want to use `on_init` to do any actions once the
-- client has been initialized.
function lsp.start_client(config)
local cleaned_config = validate_client_config(config)
local cmd, cmd_args, offset_encoding = cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding
local client_id = next_client_id()
local callbacks = config.callbacks or {}
local name = config.name or tostring(client_id)
local log_prefix = string.format("LSP[%s]", name)
local handlers = {}
local function resolve_callback(method)
return callbacks[method] or default_callbacks[method]
end
function handlers.notification(method, params)
local _ = log.debug() and log.debug('notification', method, params)
local callback = resolve_callback(method)
if callback then
-- Method name is provided here for convenience.
callback(nil, method, params, client_id)
end
end
function handlers.server_request(method, params)
local _ = log.debug() and log.debug('server_request', method, params)
local callback = resolve_callback(method)
if callback then
local _ = log.debug() and log.debug("server_request: found callback for", method)
return callback(nil, method, params, client_id)
end
local _ = log.debug() and log.debug("server_request: no callback found for", method)
return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound)
end
function handlers.on_error(code, err)
local _ = log.error() and log.error(log_prefix, "on_error", { code = lsp.client_errors[code], err = err })
err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err))
if config.on_error then
local status, usererr = pcall(config.on_error, code, err)
if not status then
local _ = log.error() and log.error(log_prefix, "user on_error failed", { err = usererr })
err_message(log_prefix, ' user on_error failed: ', tostring(usererr))
end
end
end
function handlers.on_exit(code, signal)
active_clients[client_id] = nil
uninitialized_clients[client_id] = nil
local active_buffers = {}
for bufnr, client_ids in pairs(all_buffer_active_clients) do
if client_ids[client_id] then
table.insert(active_buffers, bufnr)
end
client_ids[client_id] = nil
end
-- Buffer level cleanup
vim.schedule(function()
for _, bufnr in ipairs(active_buffers) do
util.buf_clear_diagnostics(bufnr)
end
end)
if config.on_exit then
pcall(config.on_exit, code, signal, client_id)
end
end
-- Start the RPC client.
local rpc = lsp_rpc.start(cmd, cmd_args, handlers, {
cwd = config.cmd_cwd;
env = config.cmd_env;
})
local client = {
id = client_id;
name = name;
rpc = rpc;
offset_encoding = offset_encoding;
callbacks = callbacks;
config = config;
}
-- Store the uninitialized_clients for cleanup in case we exit before
-- initialize finishes.
uninitialized_clients[client_id] = client;
local function initialize()
local valid_traces = {
off = 'off'; messages = 'messages'; verbose = 'verbose';
}
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
-- process is not alive then the server should exit (see exit notification)
-- its process.
processId = uv.getpid();
-- The rootPath of the workspace. Is null if no folder is open.
--
-- @deprecated in favour of rootUri.
rootPath = nil;
-- 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);
-- User provided initialization options.
initializationOptions = config.init_options;
-- The capabilities provided by the client (editor or tool)
capabilities = config.capabilities or protocol.make_client_capabilities();
-- The initial trace setting. If omitted trace is disabled ('off').
-- trace = 'off' | 'messages' | 'verbose';
trace = valid_traces[config.trace] or 'off';
-- The workspace folders configured in the client when the server starts.
-- This property is only available if the client supports workspace folders.
-- It can be `null` if the client supports workspace folders but none are
-- configured.
--
-- Since 3.6.0
-- workspaceFolders?: WorkspaceFolder[] | null;
-- export interface WorkspaceFolder {
-- -- The associated URI for this workspace folder.
-- uri
-- -- The name of the workspace folder. Used to refer to this
-- -- workspace folder in the user interface.
-- name
-- }
workspaceFolders = nil;
}
if config.before_init then
-- TODO(ashkan) handle errors here.
pcall(config.before_init, initialize_params, config)
end
local _ = log.debug() and log.debug(log_prefix, "initialize_params", initialize_params)
rpc.request('initialize', initialize_params, function(init_err, result)
assert(not init_err, tostring(init_err))
assert(result, "server sent empty result")
rpc.notify('initialized', {[vim.type_idx]=vim.types.dictionary})
client.initialized = true
uninitialized_clients[client_id] = nil
client.server_capabilities = assert(result.capabilities, "initialize result doesn't contain capabilities")
-- These are the cleaned up capabilities we use for dynamically deciding
-- when to send certain events to clients.
client.resolved_capabilities = protocol.resolve_capabilities(client.server_capabilities)
if config.on_init then
local status, err = pcall(config.on_init, client, result)
if not status then
pcall(handlers.on_error, lsp.client_errors.ON_INIT_CALLBACK_ERROR, err)
end
end
local _ = log.debug() and log.debug(log_prefix, "server_capabilities", client.server_capabilities)
local _ = log.info() and log.info(log_prefix, "initialized", { resolved_capabilities = client.resolved_capabilities })
-- Only assign after initialized.
active_clients[client_id] = client
-- If we had been registered before we start, then send didOpen This can
-- happen if we attach to buffers before initialize finishes or if
-- someone restarts a client.
for bufnr, client_ids in pairs(all_buffer_active_clients) do
if client_ids[client_id] then
client._on_attach(bufnr)
end
end
end)
end
local function unsupported_method(method)
local msg = "server doesn't support "..method
local _ = log.warn() and log.warn(msg)
err_message(msg)
return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg)
end
--- Checks capabilities before rpc.request-ing.
function client.request(method, params, callback)
if not callback then
callback = resolve_callback(method)
or error("not found: request callback for client "..client.name)
end
local _ = log.debug() and log.debug(log_prefix, "client.request", client_id, method, params, callback)
-- TODO keep these checks or just let it go anyway?
if (not client.resolved_capabilities.hover and method == 'textDocument/hover')
or (not client.resolved_capabilities.signature_help and method == 'textDocument/signatureHelp')
or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition')
or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation')
then
callback(unsupported_method(method), method, nil, client_id)
return
end
return rpc.request(method, params, function(err, result)
callback(err, method, result, client_id)
end)
end
function client.notify(...)
return rpc.notify(...)
end
function client.cancel_request(id)
validate{id = {id, 'n'}}
return rpc.notify("$/cancelRequest", { id = id })
end
-- Track this so that we can escalate automatically if we've alredy tried a
-- graceful shutdown
local tried_graceful_shutdown = false
function client.stop(force)
local handle = rpc.handle
if handle:is_closing() then
return
end
if force or (not client.initialized) or tried_graceful_shutdown then
handle:kill(15)
return
end
tried_graceful_shutdown = true
-- Sending a signal after a process has exited is acceptable.
rpc.request('shutdown', nil, function(err, _)
if err == nil then
rpc.notify('exit')
else
-- If there was an error in the shutdown request, then term to be safe.
handle:kill(15)
end
end)
end
function client.is_stopped()
return rpc.handle:is_closing()
end
function client._on_attach(bufnr)
text_document_did_open_handler(bufnr, client)
if config.on_attach then
-- TODO(ashkan) handle errors.
pcall(config.on_attach, client, bufnr)
end
end
initialize()
return client_id
end
local function once(fn)
local value
return function(...)
if not value then value = fn(...) end
return value
end
end
local text_document_did_change_handler
do
local encoding_index = { ["utf-8"] = 1; ["utf-16"] = 2; ["utf-32"] = 3; }
text_document_did_change_handler = function(_, bufnr, changedtick,
firstline, lastline, new_lastline, old_byte_size, old_utf32_size,
old_utf16_size)
local _ = log.debug() and log.debug("on_lines", bufnr, changedtick, firstline,
lastline, new_lastline, old_byte_size, old_utf32_size, old_utf16_size, nvim_buf_get_lines(bufnr, firstline, new_lastline, true))
if old_byte_size == 0 then
return
end
-- Don't do anything if there are no clients attached.
if tbl_isempty(all_buffer_active_clients[bufnr] or {}) then
return
end
-- Lazy initialize these because clients may not even need them.
local incremental_changes = once(function(client)
local size_index = encoding_index[client.offset_encoding]
local length = select(size_index, old_byte_size, old_utf16_size, old_utf32_size)
local lines = nvim_buf_get_lines(bufnr, firstline, new_lastline, true)
-- This is necessary because we are specifying the full line including the
-- newline in range. Therefore, we must replace the newline as well.
if #lines > 0 then
table.insert(lines, '')
end
return {
range = {
start = { line = firstline, character = 0 };
["end"] = { line = lastline, character = 0 };
};
rangeLength = length;
text = table.concat(lines, '\n');
};
end)
local full_changes = once(function()
return {
text = buf_get_full_text(bufnr);
};
end)
local uri = vim.uri_from_bufnr(bufnr)
for_each_buffer_client(bufnr, function(client, _client_id)
local text_document_did_change = client.resolved_capabilities.text_document_did_change
local changes
if text_document_did_change == protocol.TextDocumentSyncKind.None then
return
--[=[ TODO(ashkan) there seem to be problems with the byte_sizes sent by
-- neovim right now so only send the full content for now. In general, we
-- can assume that servers *will* support both versions anyway, as there
-- is no way to specify the sync capability by the client.
-- See https://github.com/palantir/python-language-server/commit/cfd6675bc10d5e8dbc50fc50f90e4a37b7178821#diff-f68667852a14e9f761f6ebf07ba02fc8 for an example of pyls handling both.
--]=]
elseif true or text_document_did_change == protocol.TextDocumentSyncKind.Full then
changes = full_changes(client)
elseif text_document_did_change == protocol.TextDocumentSyncKind.Incremental then
changes = incremental_changes(client)
end
client.notify("textDocument/didChange", {
textDocument = {
uri = uri;
version = changedtick;
};
contentChanges = { changes; }
})
end)
end
end
-- Buffer lifecycle handler for textDocument/didSave
function lsp._text_document_did_save_handler(bufnr)
bufnr = resolve_bufnr(bufnr)
local uri = vim.uri_from_bufnr(bufnr)
local text = once(function()
return table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), '\n')
end)
for_each_buffer_client(bufnr, function(client, _client_id)
if client.resolved_capabilities.text_document_save then
local included_text
if client.resolved_capabilities.text_document_save_include_text then
included_text = text()
end
client.notify('textDocument/didSave', {
textDocument = {
uri = uri;
text = included_text;
}
})
end
end)
end
-- Implements the textDocument/did* notifications required to track a buffer
-- for any language server.
-- @param bufnr [number] buffer handle or 0 for current
-- @param client_id [number] the client id
function lsp.buf_attach_client(bufnr, client_id)
validate {
bufnr = {bufnr, 'n', true};
client_id = {client_id, 'n'};
}
bufnr = resolve_bufnr(bufnr)
local buffer_client_ids = all_buffer_active_clients[bufnr]
-- This is our first time attaching to this buffer.
if not buffer_client_ids then
buffer_client_ids = {}
all_buffer_active_clients[bufnr] = buffer_client_ids
local uri = vim.uri_from_bufnr(bufnr)
nvim_command(string.format("autocmd BufWritePost <buffer=%d> lua vim.lsp._text_document_did_save_handler(0)", bufnr))
-- First time, so attach and set up stuff.
vim.api.nvim_buf_attach(bufnr, false, {
on_lines = text_document_did_change_handler;
on_detach = function()
local params = { textDocument = { uri = uri; } }
for_each_buffer_client(bufnr, function(client, _client_id)
if client.resolved_capabilities.text_document_open_close then
client.notify('textDocument/didClose', params)
end
end)
all_buffer_active_clients[bufnr] = nil
end;
-- TODO if we know all of the potential clients ahead of time, then we
-- could conditionally set this.
-- utf_sizes = size_index > 1;
utf_sizes = true;
})
end
if buffer_client_ids[client_id] then return end
-- This is our first time attaching this client to this buffer.
buffer_client_ids[client_id] = true
local client = active_clients[client_id]
-- Send didOpen for the client if it is initialized. If it isn't initialized
-- then it will send didOpen on initialize.
if client then
client._on_attach(bufnr)
end
return true
end
-- Check if a buffer is attached for a particular client.
-- @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
end
-- Look up an active client by its id, returns nil if it is not yet initialized
-- or is not a valid id.
-- @param client_id number the client id.
function lsp.get_client_by_id(client_id)
return active_clients[client_id]
end
-- Stop a client by its id, optionally with force.
-- You can also use the `stop()` function on a client if you already have
-- access to it.
-- 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 force shutdown.
-- @param client_id number the client id.
-- @param force boolean (optional) whether to use force or request shutdown
function lsp.stop_client(client_id, force)
local client
client = active_clients[client_id]
if client then
client.stop(force)
return
end
client = uninitialized_clients[client_id]
if client then
client.stop(true)
end
end
-- Returns a list of all the active clients.
function lsp.get_active_clients()
return vim.tbl_values(active_clients)
end
-- Stop all the clients, optionally with force.
-- You can also use the `stop()` function on a client if you already have
-- access to it.
-- 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 force shutdown.
-- @param force boolean (optional) whether to use force or request shutdown
function lsp.stop_all_clients(force)
for _, client in pairs(uninitialized_clients) do
client.stop(true)
end
for _, client in pairs(active_clients) do
client.stop(force)
end
end
function lsp._vim_exit_handler()
log.info("exit_handler", active_clients)
for _, client in pairs(uninitialized_clients) do
client.stop(true)
end
-- TODO handle v:dying differently?
if tbl_isempty(active_clients) then
return
end
for _, client in pairs(active_clients) do
client.stop()
end
local wait_result = wait(500, function() return tbl_isempty(active_clients) end, 50)
if wait_result ~= 0 then
for _, client in pairs(active_clients) do
client.stop(true)
end
end
end
nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()")
---
--- Buffer level client functions.
---
--- Send a request to a server and return the response
-- @param bufnr [number] Buffer handle or 0 for current.
-- @param method [string] Request method name
-- @param params [table|nil] Parameters to send to the server
-- @param callback [function|nil] Request callback (or uses the client's callbacks)
--
-- @returns: client_request_ids, cancel_all_requests
function lsp.buf_request(bufnr, method, params, callback)
validate {
bufnr = { bufnr, 'n', true };
method = { method, 's' };
callback = { callback, 'f', true };
}
local client_request_ids = {}
for_each_buffer_client(bufnr, function(client, client_id)
local request_success, request_id = client.request(method, params, callback)
-- This could only fail if the client shut down in the time since we looked
-- it up and we did the request, which should be rare.
if request_success then
client_request_ids[client_id] = request_id
end
end)
local function cancel_all_requests()
for client_id, request_id in pairs(client_request_ids) do
local client = active_clients[client_id]
client.cancel_request(request_id)
end
end
return client_request_ids, cancel_all_requests
end
--- Send a request to a server and wait for the response.
-- @param bufnr [number] Buffer handle or 0 for current.
-- @param method [string] Request method name
-- @param params [string] Parameters to send to the server
-- @param timeout_ms [number|100] Maximum ms to wait for a result
--
-- @returns: The table of {[client_id] = request_result}
function lsp.buf_request_sync(bufnr, method, params, timeout_ms)
local request_results = {}
local result_count = 0
local function callback(err, _method, result, client_id)
request_results[client_id] = { error = err, result = result }
result_count = result_count + 1
end
local client_request_ids, cancel = lsp.buf_request(bufnr, method, params, callback)
local expected_result_count = 0
for _ in pairs(client_request_ids) do
expected_result_count = expected_result_count + 1
end
local wait_result = wait(timeout_ms or 100, function()
return result_count >= expected_result_count
end, 10)
if wait_result ~= 0 then
cancel()
return nil, wait_result_reason[wait_result]
end
return request_results
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
--
-- @returns nil
function lsp.buf_notify(bufnr, method, params)
validate {
bufnr = { bufnr, 'n', true };
method = { method, 's' };
}
for_each_buffer_client(bufnr, function(client, _client_id)
client.rpc.notify(method, params)
end)
end
--- Function which can be called to generate omnifunc compatible completion.
function lsp.omnifunc(findstart, base)
local _ = log.debug() and log.debug("omnifunc.findstart", { findstart = findstart, base = base })
local bufnr = resolve_bufnr()
local has_buffer_clients = not tbl_isempty(all_buffer_active_clients[bufnr] or {})
if not has_buffer_clients then
if findstart == 1 then
return -1
else
return {}
end
end
if findstart == 1 then
return vim.fn.col('.')
else
local pos = vim.api.nvim_win_get_cursor(0)
local line = assert(nvim_buf_get_lines(bufnr, pos[1]-1, pos[1], false)[1])
local _ = log.trace() and log.trace("omnifunc.line", pos, line)
local line_to_cursor = line:sub(1, pos[2]+1)
local _ = log.trace() and log.trace("omnifunc.line_to_cursor", line_to_cursor)
local params = {
textDocument = {
uri = vim.uri_from_bufnr(bufnr);
};
position = {
-- 0-indexed for both line and character
line = pos[1] - 1,
character = vim.str_utfindex(line, pos[2]),
};
-- The completion context. This is only available if the client specifies
-- to send this using `ClientCapabilities.textDocument.completion.contextSupport === true`
-- context = nil or {
-- triggerKind = protocol.CompletionTriggerKind.Invoked;
-- triggerCharacter = nil or "";
-- };
}
-- TODO handle timeout error differently? Like via an error?
local client_responses = lsp.buf_request_sync(bufnr, 'textDocument/completion', params) or {}
local matches = {}
for _, response in pairs(client_responses) do
-- TODO how to handle errors?
if not response.error then
local data = response.result
local completion_items = util.text_document_completion_list_to_complete_items(data or {}, line_to_cursor)
local _ = log.trace() and log.trace("omnifunc.completion_items", completion_items)
vim.list_extend(matches, completion_items)
end
end
return matches
end
end
function lsp.client_is_stopped(client_id)
return active_clients[client_id] == nil
end
---
--- Miscellaneous utilities.
---
-- Retrieve a map from client_id to client of all active buffer clients.
-- @param bufnr [number] (optional): buffer handle or 0 for current
function lsp.buf_get_clients(bufnr)
bufnr = resolve_bufnr(bufnr)
local result = {}
for_each_buffer_client(bufnr, function(client, client_id)
result[client_id] = client
end)
return result
end
-- Print some debug information about the current buffer clients.
-- The output of this function should not be relied upon and may change.
function lsp.buf_print_debug_info(bufnr)
print(vim.inspect(lsp.buf_get_clients(bufnr)))
end
-- Print some debug information about all LSP related things.
-- The output of this function should not be relied upon and may change.
function lsp.print_debug_info()
print(vim.inspect({ clients = active_clients }))
end
-- Log level dictionary with reverse lookup as well.
--
-- Can be used to lookup the number from the name or the
-- name from the number.
-- Levels by name: 'trace', 'debug', 'info', 'warn', 'error'
-- Level numbers begin with 'trace' at 0
lsp.log_levels = log.levels
-- Set the log level for lsp logging.
-- Levels by name: 'trace', 'debug', 'info', 'warn', 'error'
-- Level numbers begin with 'trace' at 0
-- @param level [number|string] the case insensitive level name or number @see |vim.lsp.log_levels|
function lsp.set_log_level(level)
if type(level) == 'string' or type(level) == 'number' then
log.set_level(level)
else
error(string.format("Invalid log level: %q", level))
end
end
-- Return the path of the logfile used by the LSP client.
function lsp.get_log_path()
return log.get_filename()
end
return lsp
-- vim:sw=2 ts=2 et

136
runtime/lua/vim/lsp/buf.lua Normal file
View File

@ -0,0 +1,136 @@
local vim = vim
local validate = vim.validate
local api = vim.api
local vfn = vim.fn
local util = require 'vim.lsp.util'
local list_extend = vim.list_extend
local M = {}
local function ok_or_nil(status, ...)
if not status then return end
return ...
end
local function npcall(fn, ...)
return ok_or_nil(pcall(fn, ...))
end
local function request(method, params, callback)
validate {
method = {method, 's'};
callback = {callback, 'f', true};
}
return vim.lsp.buf_request(0, method, params, callback)
end
function M.hover()
local params = util.make_position_params()
request('textDocument/hover', params)
end
function M.peek_definition()
local params = util.make_position_params()
request('textDocument/peekDefinition', params)
end
function M.declaration()
local params = util.make_position_params()
request('textDocument/declaration', params)
end
function M.definition()
local params = util.make_position_params()
request('textDocument/definition', params)
end
function M.type_definition()
local params = util.make_position_params()
request('textDocument/typeDefinition', params)
end
function M.implementation()
local params = util.make_position_params()
request('textDocument/implementation', params)
end
function M.signature_help()
local params = util.make_position_params()
request('textDocument/signatureHelp', params)
end
-- TODO(ashkan) ?
function M.completion(context)
local params = util.make_position_params()
params.context = context
return request('textDocument/completion', params)
end
function M.formatting(options)
validate { options = {options, 't', true} }
options = vim.tbl_extend('keep', options or {}, {
tabSize = vim.bo.tabstop;
insertSpaces = vim.bo.expandtab;
})
local params = {
textDocument = { uri = vim.uri_from_bufnr(0) };
options = options;
}
return request('textDocument/formatting', params)
end
function M.range_formatting(options, start_pos, end_pos)
validate {
options = {options, 't', true};
start_pos = {start_pos, 't', true};
end_pos = {end_pos, 't', true};
}
options = vim.tbl_extend('keep', options or {}, {
tabSize = vim.bo.tabstop;
insertSpaces = vim.bo.expandtab;
})
local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<'))
local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>'))
-- convert to 0-index
A[1] = A[1] - 1
B[1] = B[1] - 1
-- account for encoding.
if A[2] > 0 then
A = {A[1], util.character_offset(0, A[1], A[2])}
end
if B[2] > 0 then
B = {B[1], util.character_offset(0, B[1], B[2])}
end
local params = {
textDocument = { uri = vim.uri_from_bufnr(0) };
range = {
start = { line = A[1]; character = A[2]; };
["end"] = { line = B[1]; character = B[2]; };
};
options = options;
}
return request('textDocument/rangeFormatting', params)
end
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: ")
if not (new_name and #new_name > 0) then return end
params.newName = new_name
request('textDocument/rename', params)
end
function M.references(context)
validate { context = { context, 't', true } }
local params = util.make_position_params()
params.context = context or {
includeDeclaration = true;
}
params[vim.type_idx] = vim.types.dictionary
request('textDocument/references', params)
end
return M
-- vim:sw=2 ts=2 et

View File

@ -0,0 +1,223 @@
local log = require 'vim.lsp.log'
local protocol = require 'vim.lsp.protocol'
local util = require 'vim.lsp.util'
local vim = vim
local api = vim.api
local M = {}
local function err_message(...)
api.nvim_err_writeln(table.concat(vim.tbl_flatten{...}))
api.nvim_command("redraw")
end
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
print("Workspace edit", workspace_edit.label)
end
util.apply_workspace_edit(workspace_edit.edit)
end
M['textDocument/publishDiagnostics'] = function(_, _, result)
if not result then return end
local uri = result.uri
local bufnr = vim.uri_to_bufnr(uri)
if not bufnr then
err_message("LSP.publishDiagnostics: Couldn't find buffer for ", uri)
return
end
util.buf_clear_diagnostics(bufnr)
util.buf_diagnostics_save_positions(bufnr, result.diagnostics)
util.buf_diagnostics_underline(bufnr, result.diagnostics)
util.buf_diagnostics_virtual_text(bufnr, result.diagnostics)
-- util.set_loclist(result.diagnostics)
end
M['textDocument/references'] = function(_, _, result)
if not result then return end
util.set_qflist(result)
api.nvim_command("copen")
api.nvim_command("wincmd p")
end
M['textDocument/rename'] = function(_, _, result)
if not result then return end
util.apply_workspace_edit(result)
end
M['textDocument/rangeFormatting'] = function(_, _, result)
if not result then return end
util.apply_text_edits(result)
end
M['textDocument/formatting'] = function(_, _, result)
if not result then return end
util.apply_text_edits(result)
end
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])
local line_to_cursor = line:sub(col+1)
local matches = util.text_document_completion_list_to_complete_items(result, line_to_cursor)
vim.fn.complete(col, matches)
end
M['textDocument/hover'] = function(_, method, result)
util.focusable_preview(method, function()
if not (result and result.contents) then
return { 'No information available' }
end
local markdown_lines = util.convert_input_to_markdown_lines(result.contents)
markdown_lines = util.trim_empty_lines(markdown_lines)
if vim.tbl_isempty(markdown_lines) then
return { 'No information available' }
end
return markdown_lines, util.try_trim_markdown_code_blocks(markdown_lines)
end)
end
local function location_callback(_, method, result)
if result == nil or vim.tbl_isempty(result) then
local _ = log.info() and log.info(method, 'No location found')
return nil
end
util.jump_to_location(result[1])
if #result > 1 then
util.set_qflist(result)
api.nvim_command("copen")
api.nvim_command("wincmd p")
end
end
M['textDocument/declaration'] = location_callback
M['textDocument/definition'] = location_callback
M['textDocument/typeDefinition'] = location_callback
M['textDocument/implementation'] = location_callback
--- Convert SignatureHelp response to preview contents.
-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_signatureHelp
local function signature_help_to_preview_contents(input)
if not input.signatures then
return
end
--The active signature. If omitted or the value lies outside the range of
--`signatures` the value defaults to zero or is ignored if `signatures.length
--=== 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_signature = input.activeSignature or 0
-- If the activeSignature is not inside the valid range, then clip it.
if active_signature >= #input.signatures then
active_signature = 0
end
local signature = input.signatures[active_signature + 1]
if not signature then
return
end
vim.list_extend(contents, vim.split(signature.label, '\n', true))
if signature.documentation then
util.convert_input_to_markdown_lines(signature.documentation, contents)
end
if input.parameters then
local active_parameter = input.activeParameter or 0
-- If the activeParameter is not inside the valid range, then clip it.
if active_parameter >= #input.parameters then
active_parameter = 0
end
local parameter = signature.parameters and signature.parameters[active_parameter]
if parameter then
--[=[
--Represents a parameter of a callable-signature. A parameter can
--have a label and a doc-comment.
interface ParameterInformation {
--The label of this parameter information.
--
--Either a string or an inclusive start and exclusive end offsets within its containing
--signature label. (see SignatureInformation.label). The offsets are based on a UTF-16
--string representation as `Position` and `Range` does.
--
--*Note*: a label of type string should be a substring of its containing signature label.
--Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`.
label: string | [number, number];
--The human-readable doc-comment of this parameter. Will be shown
--in the UI but can be omitted.
documentation?: string | MarkupContent;
}
--]=]
-- TODO highlight parameter
if parameter.documentation then
util.convert_input_to_markdown_lines(parameter.documentation, contents)
end
end
end
return contents
end
M['textDocument/signatureHelp'] = function(_, method, result)
util.focusable_preview(method, function()
if not (result and result.signatures and result.signatures[1]) then
return { 'No signature available' }
end
-- TODO show popup when signatures is empty?
local lines = signature_help_to_preview_contents(result)
lines = util.trim_empty_lines(lines)
if vim.tbl_isempty(lines) then
return { 'No signature available' }
end
return lines, util.try_trim_markdown_code_blocks(lines)
end)
end
M['textDocument/peekDefinition'] = function(_, _, result, _)
if not (result and result[1]) then return end
local loc = result[1]
local bufnr = vim.uri_to_bufnr(loc.uri) or error("not found: "..tostring(loc.uri))
local start = loc.range.start
local finish = loc.range["end"]
util.open_floating_peek_preview(bufnr, start, finish, { offset_x = 1 })
local headbuf = util.open_floating_preview({"Peek:"}, nil, {
offset_y = -(finish.line - start.line);
width = finish.character - start.character + 2;
})
-- TODO(ashkan) change highlight group?
api.nvim_buf_add_highlight(headbuf, -1, 'Keyword', 0, -1)
end
local function log_message(_, _, result, client_id)
local message_type = result.type
local message = result.message
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
if message_type == protocol.MessageType.Error then
err_message("LSP[", client_name, "] ", message)
else
local message_type_name = protocol.MessageType[message_type]
api.nvim_out_write(string.format("LSP[%s][%s] %s\n", client_name, message_type_name, message))
end
return result
end
M['window/showMessage'] = log_message
M['window/logMessage'] = log_message
-- 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)
local _ = log.debug() and log.debug('default_callback', method, { params = params, client_id = client_id, err = err })
if err then
error(tostring(err))
end
return fn(err, method, params, client_id)
end
end
return M
-- vim:sw=2 ts=2 et

View File

@ -0,0 +1,95 @@
-- Logger for language client plugin.
local log = {}
-- Log level dictionary with reverse lookup as well.
--
-- Can be used to lookup the number from the name or the name from the number.
-- Levels by name: 'trace', 'debug', 'info', 'warn', 'error'
-- Level numbers begin with 'trace' at 0
log.levels = {
TRACE = 0;
DEBUG = 1;
INFO = 2;
WARN = 3;
ERROR = 4;
-- FATAL = 4;
}
-- Default log level is warn.
local current_log_level = log.levels.WARN
local log_date_format = "%FT%H:%M:%SZ%z"
do
local path_sep = vim.loop.os_uname().sysname == "Windows" and "\\" or "/"
local function path_join(...)
return table.concat(vim.tbl_flatten{...}, path_sep)
end
local logfilename = path_join(vim.fn.stdpath('data'), 'vim-lsp.log')
--- Return the log filename.
function log.get_filename()
return logfilename
end
vim.fn.mkdir(vim.fn.stdpath('data'), "p")
local logfile = assert(io.open(logfilename, "a+"))
for level, levelnr in pairs(log.levels) do
-- Also export the log level on the root object.
log[level] = levelnr
-- Set the lowercase name as the main use function.
-- If called without arguments, it will check whether the log level is
-- greater than or equal to this one. When called with arguments, it will
-- log at that level (if applicable, it is checked either way).
--
-- Recommended usage:
-- ```
-- local _ = log.warn() and log.warn("123")
-- ```
--
-- This way you can avoid string allocations if the log level isn't high enough.
log[level:lower()] = function(...)
local argc = select("#", ...)
if levelnr < current_log_level then return false end
if argc == 0 then return true end
local info = debug.getinfo(2, "Sl")
local fileinfo = string.format("%s:%s", info.short_src, info.currentline)
local parts = { table.concat({"[", level, "]", os.date(log_date_format), "]", fileinfo, "]"}, " ") }
for i = 1, argc do
local arg = select(i, ...)
if arg == nil then
table.insert(parts, "nil")
else
table.insert(parts, vim.inspect(arg, {newline=''}))
end
end
logfile:write(table.concat(parts, '\t'), "\n")
logfile:flush()
end
end
-- Add some space to make it easier to distinguish different neovim runs.
logfile:write("\n")
end
-- This is put here on purpose after the loop above so that it doesn't
-- interfere with iterating the levels
vim.tbl_add_reverse_lookup(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))
else
assert(type(level) == 'number', "level must be a number or string")
assert(log.levels[level], string.format("Invalid log level: %d", level))
current_log_level = level
end
end
-- Return whether the level is sufficient for logging.
-- @param level number log level
function log.should_log(level)
return level >= current_log_level
end
return log
-- vim:sw=2 ts=2 et

View File

@ -0,0 +1,922 @@
-- Protocol for the Microsoft Language Server Protocol (mslsp)
local protocol = {}
local function ifnil(a, b)
if a == nil then return b end
return a
end
--[=[
-- 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
function transform_schema_to_table()
transform_schema_comments()
nvim.command [[silent! '<,'>s/: \S\+//]]
nvim.command [[silent! '<,'>s/export const //]]
nvim.command [[silent! '<,'>s/export namespace \(\S*\)\s*{/protocol.\1 = {/]]
nvim.command [[silent! '<,'>s/namespace \(\S*\)\s*{/protocol.\1 = {/]]
end
--]=]
local constants = {
DiagnosticSeverity = {
-- Reports an error.
Error = 1;
-- Reports a warning.
Warning = 2;
-- Reports an information.
Information = 3;
-- Reports a hint.
Hint = 4;
};
MessageType = {
-- An error message.
Error = 1;
-- A warning message.
Warning = 2;
-- An information message.
Info = 3;
-- A log message.
Log = 4;
};
-- The file event type.
FileChangeType = {
-- The file got created.
Created = 1;
-- The file got changed.
Changed = 2;
-- The file got deleted.
Deleted = 3;
};
-- The kind of a completion entry.
CompletionItemKind = {
Text = 1;
Method = 2;
Function = 3;
Constructor = 4;
Field = 5;
Variable = 6;
Class = 7;
Interface = 8;
Module = 9;
Property = 10;
Unit = 11;
Value = 12;
Enum = 13;
Keyword = 14;
Snippet = 15;
Color = 16;
File = 17;
Reference = 18;
Folder = 19;
EnumMember = 20;
Constant = 21;
Struct = 22;
Event = 23;
Operator = 24;
TypeParameter = 25;
};
-- How a completion was triggered
CompletionTriggerKind = {
-- Completion was triggered by typing an identifier (24x7 code
-- complete), manual invocation (e.g Ctrl+Space) or via API.
Invoked = 1;
-- Completion was triggered by a trigger character specified by
-- the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
TriggerCharacter = 2;
-- Completion was re-triggered as the current completion list is incomplete.
TriggerForIncompleteCompletions = 3;
};
-- A document highlight kind.
DocumentHighlightKind = {
-- A textual occurrence.
Text = 1;
-- Read-access of a symbol, like reading a variable.
Read = 2;
-- Write-access of a symbol, like writing to a variable.
Write = 3;
};
-- A symbol kind.
SymbolKind = {
File = 1;
Module = 2;
Namespace = 3;
Package = 4;
Class = 5;
Method = 6;
Property = 7;
Field = 8;
Constructor = 9;
Enum = 10;
Interface = 11;
Function = 12;
Variable = 13;
Constant = 14;
String = 15;
Number = 16;
Boolean = 17;
Array = 18;
Object = 19;
Key = 20;
Null = 21;
EnumMember = 22;
Struct = 23;
Event = 24;
Operator = 25;
TypeParameter = 26;
};
-- Represents reasons why a text document is saved.
TextDocumentSaveReason = {
-- Manually triggered, e.g. by the user pressing save, by starting debugging,
-- or by an API call.
Manual = 1;
-- Automatic after a delay.
AfterDelay = 2;
-- When the editor lost focus.
FocusOut = 3;
};
ErrorCodes = {
-- Defined by JSON RPC
ParseError = -32700;
InvalidRequest = -32600;
MethodNotFound = -32601;
InvalidParams = -32602;
InternalError = -32603;
serverErrorStart = -32099;
serverErrorEnd = -32000;
ServerNotInitialized = -32002;
UnknownErrorCode = -32001;
-- Defined by the protocol.
RequestCancelled = -32800;
ContentModified = -32801;
};
-- Describes the content type that a client supports in various
-- result literals like `Hover`, `ParameterInfo` or `CompletionItem`.
--
-- Please note that `MarkupKinds` must not start with a `$`. This kinds
-- are reserved for internal usage.
MarkupKind = {
-- Plain text is supported as a content format
PlainText = 'plaintext';
-- Markdown is supported as a content format
Markdown = 'markdown';
};
ResourceOperationKind = {
-- Supports creating new files and folders.
Create = 'create';
-- Supports renaming existing files and folders.
Rename = 'rename';
-- Supports deleting existing files and folders.
Delete = 'delete';
};
FailureHandlingKind = {
-- Applying the workspace change is simply aborted if one of the changes provided
-- fails. All operations executed before the failing operation stay executed.
Abort = 'abort';
-- All operations are executed transactionally. That means they either all
-- succeed or no changes at all are applied to the workspace.
Transactional = 'transactional';
-- If the workspace edit contains only textual file changes they are executed transactionally.
-- If resource changes (create, rename or delete file) are part of the change the failure
-- handling strategy is abort.
TextOnlyTransactional = 'textOnlyTransactional';
-- The client tries to undo the operations already executed. But there is no
-- guarantee that this succeeds.
Undo = 'undo';
};
-- Known error codes for an `InitializeError`;
InitializeError = {
-- If the protocol version provided by the client can't be handled by the server.
-- @deprecated This initialize error got replaced by client capabilities. There is
-- no version handshake in version 3.0x
unknownProtocolVersion = 1;
};
-- Defines how the host (editor) should sync document changes to the language server.
TextDocumentSyncKind = {
-- Documents should not be synced at all.
None = 0;
-- Documents are synced by always sending the full content
-- of the document.
Full = 1;
-- Documents are synced by sending the full content on open.
-- After that only incremental updates to the document are
-- send.
Incremental = 2;
};
WatchKind = {
-- Interested in create events.
Create = 1;
-- Interested in change events
Change = 2;
-- Interested in delete events
Delete = 4;
};
-- Defines whether the insert text in a completion item should be interpreted as
-- plain text or a snippet.
InsertTextFormat = {
-- The primary text to be inserted is treated as a plain string.
PlainText = 1;
-- The primary text to be inserted is treated as a snippet.
--
-- A snippet can define tab stops and placeholders with `$1`, `$2`
-- and `${3:foo};`. `$0` defines the final tab stop, it defaults to
-- the end of the snippet. Placeholders with equal identifiers are linked,
-- that is typing in one will update others too.
Snippet = 2;
};
-- A set of predefined code action kinds
CodeActionKind = {
-- Empty kind.
Empty = '';
-- Base kind for quickfix actions
QuickFix = 'quickfix';
-- Base kind for refactoring actions
Refactor = 'refactor';
-- Base kind for refactoring extraction actions
--
-- Example extract actions:
--
-- - Extract method
-- - Extract function
-- - Extract variable
-- - Extract interface from class
-- - ...
RefactorExtract = 'refactor.extract';
-- Base kind for refactoring inline actions
--
-- Example inline actions:
--
-- - Inline function
-- - Inline variable
-- - Inline constant
-- - ...
RefactorInline = 'refactor.inline';
-- Base kind for refactoring rewrite actions
--
-- Example rewrite actions:
--
-- - Convert JavaScript function to class
-- - Add or remove parameter
-- - Encapsulate field
-- - Make method static
-- - Move method to base class
-- - ...
RefactorRewrite = 'refactor.rewrite';
-- Base kind for source actions
--
-- Source code actions apply to the entire file.
Source = 'source';
-- Base kind for an organize imports source action
SourceOrganizeImports = 'source.organizeImports';
};
}
for k, v in pairs(constants) do
vim.tbl_add_reverse_lookup(v)
protocol[k] = v
end
--[=[
--Text document specific client capabilities.
export interface TextDocumentClientCapabilities {
synchronization?: {
--Whether text document synchronization supports dynamic registration.
dynamicRegistration?: boolean;
--The client supports sending will save notifications.
willSave?: boolean;
--The client supports sending a will save request and
--waits for a response providing text edits which will
--be applied to the document before it is saved.
willSaveWaitUntil?: boolean;
--The client supports did save notifications.
didSave?: boolean;
}
--Capabilities specific to the `textDocument/completion`
completion?: {
--Whether completion supports dynamic registration.
dynamicRegistration?: boolean;
--The client supports the following `CompletionItem` specific
--capabilities.
completionItem?: {
--The client supports snippets as insert text.
--
--A snippet can define tab stops and placeholders with `$1`, `$2`
--and `${3:foo}`. `$0` defines the final tab stop, it defaults to
--the end of the snippet. Placeholders with equal identifiers are linked,
--that is typing in one will update others too.
snippetSupport?: boolean;
--The client supports commit characters on a completion item.
commitCharactersSupport?: boolean
--The client supports the following content formats for the documentation
--property. The order describes the preferred format of the client.
documentationFormat?: MarkupKind[];
--The client supports the deprecated property on a completion item.
deprecatedSupport?: boolean;
--The client supports the preselect property on a completion item.
preselectSupport?: boolean;
}
completionItemKind?: {
--The completion item kind values the client supports. When this
--property exists the client also guarantees that it will
--handle values outside its set gracefully and falls back
--to a default value when unknown.
--
--If this property is not present the client only supports
--the completion items kinds from `Text` to `Reference` as defined in
--the initial version of the protocol.
valueSet?: CompletionItemKind[];
},
--The client supports to send additional context information for a
--`textDocument/completion` request.
contextSupport?: boolean;
};
--Capabilities specific to the `textDocument/hover`
hover?: {
--Whether hover supports dynamic registration.
dynamicRegistration?: boolean;
--The client supports the follow content formats for the content
--property. The order describes the preferred format of the client.
contentFormat?: MarkupKind[];
};
--Capabilities specific to the `textDocument/signatureHelp`
signatureHelp?: {
--Whether signature help supports dynamic registration.
dynamicRegistration?: boolean;
--The client supports the following `SignatureInformation`
--specific properties.
signatureInformation?: {
--The client supports the follow content formats for the documentation
--property. The order describes the preferred format of the client.
documentationFormat?: MarkupKind[];
--Client capabilities specific to parameter information.
parameterInformation?: {
--The client supports processing label offsets instead of a
--simple label string.
--
--Since 3.14.0
labelOffsetSupport?: boolean;
}
};
};
--Capabilities specific to the `textDocument/references`
references?: {
--Whether references supports dynamic registration.
dynamicRegistration?: boolean;
};
--Capabilities specific to the `textDocument/documentHighlight`
documentHighlight?: {
--Whether document highlight supports dynamic registration.
dynamicRegistration?: boolean;
};
--Capabilities specific to the `textDocument/documentSymbol`
documentSymbol?: {
--Whether document symbol supports dynamic registration.
dynamicRegistration?: boolean;
--Specific capabilities for the `SymbolKind`.
symbolKind?: {
--The symbol kind values the client supports. When this
--property exists the client also guarantees that it will
--handle values outside its set gracefully and falls back
--to a default value when unknown.
--
--If this property is not present the client only supports
--the symbol kinds from `File` to `Array` as defined in
--the initial version of the protocol.
valueSet?: SymbolKind[];
}
--The client supports hierarchical document symbols.
hierarchicalDocumentSymbolSupport?: boolean;
};
--Capabilities specific to the `textDocument/formatting`
formatting?: {
--Whether formatting supports dynamic registration.
dynamicRegistration?: boolean;
};
--Capabilities specific to the `textDocument/rangeFormatting`
rangeFormatting?: {
--Whether range formatting supports dynamic registration.
dynamicRegistration?: boolean;
};
--Capabilities specific to the `textDocument/onTypeFormatting`
onTypeFormatting?: {
--Whether on type formatting supports dynamic registration.
dynamicRegistration?: boolean;
};
--Capabilities specific to the `textDocument/declaration`
declaration?: {
--Whether declaration supports dynamic registration. If this is set to `true`
--the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
--return value for the corresponding server capability as well.
dynamicRegistration?: boolean;
--The client supports additional metadata in the form of declaration links.
--
--Since 3.14.0
linkSupport?: boolean;
};
--Capabilities specific to the `textDocument/definition`.
--
--Since 3.14.0
definition?: {
--Whether definition supports dynamic registration.
dynamicRegistration?: boolean;
--The client supports additional metadata in the form of definition links.
linkSupport?: boolean;
};
--Capabilities specific to the `textDocument/typeDefinition`
--
--Since 3.6.0
typeDefinition?: {
--Whether typeDefinition supports dynamic registration. If this is set to `true`
--the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
--return value for the corresponding server capability as well.
dynamicRegistration?: boolean;
--The client supports additional metadata in the form of definition links.
--
--Since 3.14.0
linkSupport?: boolean;
};
--Capabilities specific to the `textDocument/implementation`.
--
--Since 3.6.0
implementation?: {
--Whether implementation supports dynamic registration. If this is set to `true`
--the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
--return value for the corresponding server capability as well.
dynamicRegistration?: boolean;
--The client supports additional metadata in the form of definition links.
--
--Since 3.14.0
linkSupport?: boolean;
};
--Capabilities specific to the `textDocument/codeAction`
codeAction?: {
--Whether code action supports dynamic registration.
dynamicRegistration?: boolean;
--The client support code action literals as a valid
--response of the `textDocument/codeAction` request.
--
--Since 3.8.0
codeActionLiteralSupport?: {
--The code action kind is support with the following value
--set.
codeActionKind: {
--The code action kind values the client supports. When this
--property exists the client also guarantees that it will
--handle values outside its set gracefully and falls back
--to a default value when unknown.
valueSet: CodeActionKind[];
};
};
};
--Capabilities specific to the `textDocument/codeLens`
codeLens?: {
--Whether code lens supports dynamic registration.
dynamicRegistration?: boolean;
};
--Capabilities specific to the `textDocument/documentLink`
documentLink?: {
--Whether document link supports dynamic registration.
dynamicRegistration?: boolean;
};
--Capabilities specific to the `textDocument/documentColor` and the
--`textDocument/colorPresentation` request.
--
--Since 3.6.0
colorProvider?: {
--Whether colorProvider supports dynamic registration. If this is set to `true`
--the client supports the new `(ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)`
--return value for the corresponding server capability as well.
dynamicRegistration?: boolean;
}
--Capabilities specific to the `textDocument/rename`
rename?: {
--Whether rename supports dynamic registration.
dynamicRegistration?: boolean;
--The client supports testing for validity of rename operations
--before execution.
prepareSupport?: boolean;
};
--Capabilities specific to `textDocument/publishDiagnostics`.
publishDiagnostics?: {
--Whether the clients accepts diagnostics with related information.
relatedInformation?: boolean;
};
--Capabilities specific to `textDocument/foldingRange` requests.
--
--Since 3.10.0
foldingRange?: {
--Whether implementation supports dynamic registration for folding range providers. If this is set to `true`
--the client supports the new `(FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)`
--return value for the corresponding server capability as well.
dynamicRegistration?: boolean;
--The maximum number of folding ranges that the client prefers to receive per document. The value serves as a
--hint, servers are free to follow the limit.
rangeLimit?: number;
--If set, the client signals that it only supports folding complete lines. If set, client will
--ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange.
lineFoldingOnly?: boolean;
};
}
--]=]
--[=[
--Workspace specific client capabilities.
export interface WorkspaceClientCapabilities {
--The client supports applying batch edits to the workspace by supporting
--the request 'workspace/applyEdit'
applyEdit?: boolean;
--Capabilities specific to `WorkspaceEdit`s
workspaceEdit?: {
--The client supports versioned document changes in `WorkspaceEdit`s
documentChanges?: boolean;
--The resource operations the client supports. Clients should at least
--support 'create', 'rename' and 'delete' files and folders.
resourceOperations?: ResourceOperationKind[];
--The failure handling strategy of a client if applying the workspace edit
--fails.
failureHandling?: FailureHandlingKind;
};
--Capabilities specific to the `workspace/didChangeConfiguration` notification.
didChangeConfiguration?: {
--Did change configuration notification supports dynamic registration.
dynamicRegistration?: boolean;
};
--Capabilities specific to the `workspace/didChangeWatchedFiles` notification.
didChangeWatchedFiles?: {
--Did change watched files notification supports dynamic registration. Please note
--that the current protocol doesn't support static configuration for file changes
--from the server side.
dynamicRegistration?: boolean;
};
--Capabilities specific to the `workspace/symbol` request.
symbol?: {
--Symbol request supports dynamic registration.
dynamicRegistration?: boolean;
--Specific capabilities for the `SymbolKind` in the `workspace/symbol` request.
symbolKind?: {
--The symbol kind values the client supports. When this
--property exists the client also guarantees that it will
--handle values outside its set gracefully and falls back
--to a default value when unknown.
--
--If this property is not present the client only supports
--the symbol kinds from `File` to `Array` as defined in
--the initial version of the protocol.
valueSet?: SymbolKind[];
}
};
--Capabilities specific to the `workspace/executeCommand` request.
executeCommand?: {
--Execute command supports dynamic registration.
dynamicRegistration?: boolean;
};
--The client has support for workspace folders.
--
--Since 3.6.0
workspaceFolders?: boolean;
--The client supports `workspace/configuration` requests.
--
--Since 3.6.0
configuration?: boolean;
}
--]=]
function protocol.make_client_capabilities()
return {
textDocument = {
synchronization = {
dynamicRegistration = false;
-- TODO(ashkan) Send textDocument/willSave before saving (BufWritePre)
willSave = false;
-- TODO(ashkan) Implement textDocument/willSaveWaitUntil
willSaveWaitUntil = false;
-- Send textDocument/didSave after saving (BufWritePost)
didSave = true;
};
completion = {
dynamicRegistration = false;
completionItem = {
-- TODO(tjdevries): Is it possible to implement this in plain lua?
snippetSupport = false;
commitCharactersSupport = false;
preselectSupport = false;
deprecatedSupport = false;
documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
};
completionItemKind = {
valueSet = (function()
local res = {}
for k in pairs(protocol.CompletionItemKind) do
if type(k) == 'number' then table.insert(res, k) end
end
return res
end)();
};
-- TODO(tjdevries): Implement this
contextSupport = false;
};
hover = {
dynamicRegistration = false;
contentFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
};
signatureHelp = {
dynamicRegistration = false;
signatureInformation = {
documentationFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
-- parameterInformation = {
-- labelOffsetSupport = false;
-- };
};
};
references = {
dynamicRegistration = false;
};
documentHighlight = {
dynamicRegistration = false
};
-- documentSymbol = {
-- dynamicRegistration = false;
-- symbolKind = {
-- valueSet = (function()
-- local res = {}
-- for k in pairs(protocol.SymbolKind) do
-- if type(k) == 'string' then table.insert(res, k) end
-- end
-- return res
-- end)();
-- };
-- hierarchicalDocumentSymbolSupport = false;
-- };
};
workspace = nil;
experimental = nil;
}
end
--[=[
export interface DocumentFilter {
--A language id, like `typescript`.
language?: string;
--A Uri [scheme](#Uri.scheme), like `file` or `untitled`.
scheme?: string;
--A glob pattern, like `*.{ts,js}`.
--
--Glob patterns can have the following syntax:
--- `*` to match one or more characters in a path segment
--- `?` to match on one character in a path segment
--- `**` to match any number of path segments, including none
--- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files)
--- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
--- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
pattern?: string;
}
--]=]
--[[
--Static registration options to be returned in the initialize request.
interface StaticRegistrationOptions {
--The id used to register the request. The id can be used to deregister
--the request again. See also Registration#id.
id?: string;
}
export interface DocumentFilter {
--A language id, like `typescript`.
language?: string;
--A Uri [scheme](#Uri.scheme), like `file` or `untitled`.
scheme?: string;
--A glob pattern, like `*.{ts,js}`.
--
--Glob patterns can have the following syntax:
--- `*` to match one or more characters in a path segment
--- `?` to match on one character in a path segment
--- `**` to match any number of path segments, including none
--- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files)
--- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
--- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
pattern?: string;
}
export type DocumentSelector = DocumentFilter[];
export interface TextDocumentRegistrationOptions {
--A document selector to identify the scope of the registration. If set to null
--the document selector provided on the client side will be used.
documentSelector: DocumentSelector | null;
}
--Code Action options.
export interface CodeActionOptions {
--CodeActionKinds that this server may return.
--
--The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server
--may list out every specific kind they provide.
codeActionKinds?: CodeActionKind[];
}
interface ServerCapabilities {
--Defines how text documents are synced. Is either a detailed structure defining each notification or
--for backwards compatibility the TextDocumentSyncKind number. If omitted it defaults to `TextDocumentSyncKind.None`.
textDocumentSync?: TextDocumentSyncOptions | number;
--The server provides hover support.
hoverProvider?: boolean;
--The server provides completion support.
completionProvider?: CompletionOptions;
--The server provides signature help support.
signatureHelpProvider?: SignatureHelpOptions;
--The server provides goto definition support.
definitionProvider?: boolean;
--The server provides Goto Type Definition support.
--
--Since 3.6.0
typeDefinitionProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions);
--The server provides Goto Implementation support.
--
--Since 3.6.0
implementationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions);
--The server provides find references support.
referencesProvider?: boolean;
--The server provides document highlight support.
documentHighlightProvider?: boolean;
--The server provides document symbol support.
documentSymbolProvider?: boolean;
--The server provides workspace symbol support.
workspaceSymbolProvider?: boolean;
--The server provides code actions. The `CodeActionOptions` return type is only
--valid if the client signals code action literal support via the property
--`textDocument.codeAction.codeActionLiteralSupport`.
codeActionProvider?: boolean | CodeActionOptions;
--The server provides code lens.
codeLensProvider?: CodeLensOptions;
--The server provides document formatting.
documentFormattingProvider?: boolean;
--The server provides document range formatting.
documentRangeFormattingProvider?: boolean;
--The server provides document formatting on typing.
documentOnTypeFormattingProvider?: DocumentOnTypeFormattingOptions;
--The server provides rename support. RenameOptions may only be
--specified if the client states that it supports
--`prepareSupport` in its initial `initialize` request.
renameProvider?: boolean | RenameOptions;
--The server provides document link support.
documentLinkProvider?: DocumentLinkOptions;
--The server provides color provider support.
--
--Since 3.6.0
colorProvider?: boolean | ColorProviderOptions | (ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions);
--The server provides folding provider support.
--
--Since 3.10.0
foldingRangeProvider?: boolean | FoldingRangeProviderOptions | (FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions);
--The server provides go to declaration support.
--
--Since 3.14.0
declarationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions);
--The server provides execute command support.
executeCommandProvider?: ExecuteCommandOptions;
--Workspace specific server capabilities
workspace?: {
--The server supports workspace folder.
--
--Since 3.6.0
workspaceFolders?: {
* The server has support for workspace folders
supported?: boolean;
* Whether the server wants to receive workspace folder
* change notifications.
*
* If a strings is provided the string is treated as a ID
* under which the notification is registered on the client
* side. The ID can be used to unregister for these events
* using the `client/unregisterCapability` request.
changeNotifications?: string | boolean;
}
}
--Experimental server capabilities.
experimental?: any;
}
--]]
function protocol.resolve_capabilities(server_capabilities)
local general_properties = {}
local text_document_sync_properties
do
local TextDocumentSyncKind = protocol.TextDocumentSyncKind
local textDocumentSync = server_capabilities.textDocumentSync
if textDocumentSync == nil then
-- Defaults if omitted.
text_document_sync_properties = {
text_document_open_close = false;
text_document_did_change = TextDocumentSyncKind.None;
-- text_document_did_change = false;
text_document_will_save = false;
text_document_will_save_wait_until = false;
text_document_save = false;
text_document_save_include_text = false;
}
elseif type(textDocumentSync) == 'number' then
-- Backwards compatibility
if not TextDocumentSyncKind[textDocumentSync] then
return nil, "Invalid server TextDocumentSyncKind for textDocumentSync"
end
text_document_sync_properties = {
text_document_open_close = true;
text_document_did_change = textDocumentSync;
text_document_will_save = false;
text_document_will_save_wait_until = false;
text_document_save = false;
text_document_save_include_text = false;
}
elseif type(textDocumentSync) == 'table' then
text_document_sync_properties = {
text_document_open_close = ifnil(textDocumentSync.openClose, false);
text_document_did_change = ifnil(textDocumentSync.change, TextDocumentSyncKind.None);
text_document_will_save = ifnil(textDocumentSync.willSave, false);
text_document_will_save_wait_until = ifnil(textDocumentSync.willSaveWaitUntil, false);
text_document_save = ifnil(textDocumentSync.save, false);
text_document_save_include_text = ifnil(textDocumentSync.save and textDocumentSync.save.includeText, false);
}
else
return nil, string.format("Invalid type for textDocumentSync: %q", type(textDocumentSync))
end
end
general_properties.hover = server_capabilities.hoverProvider or false
general_properties.goto_definition = server_capabilities.definitionProvider or false
general_properties.find_references = server_capabilities.referencesProvider or false
general_properties.document_highlight = server_capabilities.documentHighlightProvider or false
general_properties.document_symbol = server_capabilities.documentSymbolProvider or false
general_properties.workspace_symbol = server_capabilities.workspaceSymbolProvider or false
general_properties.document_formatting = server_capabilities.documentFormattingProvider or false
general_properties.document_range_formatting = server_capabilities.documentRangeFormattingProvider or false
if server_capabilities.codeActionProvider == nil then
general_properties.code_action = false
elseif type(server_capabilities.codeActionProvider) == 'boolean' then
general_properties.code_action = server_capabilities.codeActionProvider
elseif type(server_capabilities.codeActionProvider) == 'table' then
-- TODO(ashkan) support CodeActionKind
general_properties.code_action = false
else
error("The server sent invalid codeActionProvider")
end
if server_capabilities.implementationProvider == nil then
general_properties.implementation = false
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
else
error("The server sent invalid implementationProvider")
end
local signature_help_properties
if server_capabilities.signatureHelpProvider == nil then
signature_help_properties = {
signature_help = false;
signature_help_trigger_characters = {};
}
elseif type(server_capabilities.signatureHelpProvider) == 'table' then
signature_help_properties = {
signature_help = true;
-- The characters that trigger signature help automatically.
signature_help_trigger_characters = server_capabilities.signatureHelpProvider.triggerCharacters or {};
}
else
error("The server sent invalid signatureHelpProvider")
end
return vim.tbl_extend("error"
, text_document_sync_properties
, signature_help_properties
, general_properties
)
end
return protocol
-- vim:sw=2 ts=2 et

452
runtime/lua/vim/lsp/rpc.lua Normal file
View File

@ -0,0 +1,452 @@
local uv = vim.loop
local log = require('vim.lsp.log')
local protocol = require('vim.lsp.protocol')
local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap
-- TODO replace with a better implementation.
local function json_encode(data)
local status, result = pcall(vim.fn.json_encode, data)
if status then
return result
else
return nil, result
end
end
local function json_decode(data)
local status, result = pcall(vim.fn.json_decode, data)
if status then
return result
else
return nil, result
end
end
local function is_dir(filename)
local stat = vim.loop.fs_stat(filename)
return stat and stat.type == 'directory' or false
end
local NIL = vim.NIL
local function convert_NIL(v)
if v == NIL then return nil end
return v
end
-- If a dictionary is passed in, turn it into a list of string of "k=v"
-- Accepts a table which can be composed of k=v strings or map-like
-- specification, such as:
--
-- ```
-- {
-- "PRODUCTION=false";
-- "PATH=/usr/bin/";
-- PORT = 123;
-- HOST = "0.0.0.0";
-- }
-- ```
--
-- Non-string values will be cast with `tostring`
local function force_env_list(final_env)
if final_env then
local env = final_env
final_env = {}
for k,v in pairs(env) do
-- If it's passed in as a dict, then convert to list of "k=v"
if type(k) == "string" then
table.insert(final_env, k..'='..tostring(v))
elseif type(v) == 'string' then
table.insert(final_env, v)
else
-- TODO is this right or should I exception here?
-- Try to coerce other values to string.
table.insert(final_env, tostring(v))
end
end
return final_env
end
end
local function format_message_with_content_length(encoded_message)
return table.concat {
'Content-Length: '; tostring(#encoded_message); '\r\n\r\n';
encoded_message;
}
end
--- Parse an LSP Message's header
-- @param header: The header to parse.
local function parse_headers(header)
if type(header) ~= 'string' then
return nil
end
local headers = {}
for line in vim.gsplit(header, '\r\n', true) do
if line == '' then
break
end
local key, value = line:match("^%s*(%S+)%s*:%s*(.+)%s*$")
if key then
key = key:lower():gsub('%-', '_')
headers[key] = value
else
local _ = log.error() and log.error("invalid header line %q", line)
error(string.format("invalid header line %q", line))
end
end
headers.content_length = tonumber(headers.content_length)
or error(string.format("Content-Length not found in headers. %q", header))
return headers
end
-- This is the start of any possible header patterns. The gsub converts it to a
-- case insensitive pattern.
local header_start_pattern = ("content"):gsub("%w", function(c) return "["..c..c:upper().."]" end)
local function request_parser_loop()
local buffer = ''
while true do
-- A message can only be complete if it has a double CRLF and also the full
-- payload, so first let's check for the CRLFs
local start, finish = buffer:find('\r\n\r\n', 1, true)
-- Start parsing the headers
if start then
-- This is a workaround for servers sending initial garbage before
-- sending headers, such as if a bash script sends stdout. It assumes
-- that we know all of the headers ahead of time. At this moment, the
-- only valid headers start with "Content-*", so that's the thing we will
-- be searching for.
-- TODO(ashkan) I'd like to remove this, but it seems permanent :(
local buffer_start = buffer:find(header_start_pattern)
local headers = parse_headers(buffer:sub(buffer_start, start-1))
buffer = buffer:sub(finish+1)
local content_length = headers.content_length
-- Keep waiting for data until we have enough.
while #buffer < content_length do
buffer = buffer..(coroutine.yield()
or error("Expected more data for the body. The server may have died.")) -- TODO hmm.
end
local body = buffer:sub(1, content_length)
buffer = buffer:sub(content_length + 1)
-- Yield our data.
buffer = buffer..(coroutine.yield(headers, body)
or error("Expected more data for the body. The server may have died.")) -- TODO hmm.
else
-- Get more data since we don't have enough.
buffer = buffer..(coroutine.yield()
or error("Expected more data for the header. The server may have died.")) -- TODO hmm.
end
end
end
local client_errors = vim.tbl_add_reverse_lookup {
INVALID_SERVER_MESSAGE = 1;
INVALID_SERVER_JSON = 2;
NO_RESULT_CALLBACK_FOUND = 3;
READ_ERROR = 4;
NOTIFICATION_HANDLER_ERROR = 5;
SERVER_REQUEST_HANDLER_ERROR = 6;
SERVER_RESULT_CALLBACK_ERROR = 7;
}
local function format_rpc_error(err)
validate {
err = { err, 't' };
}
local code_name = assert(protocol.ErrorCodes[err.code], "err.code is invalid")
local message_parts = {"RPC", code_name}
if err.message then
table.insert(message_parts, "message = ")
table.insert(message_parts, string.format("%q", err.message))
end
if err.data then
table.insert(message_parts, "data = ")
table.insert(message_parts, vim.inspect(err.data))
end
return table.concat(message_parts, ' ')
end
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')
return setmetatable({
code = code;
message = message or code_name;
data = data;
}, {
__tostring = format_rpc_error;
})
end
local default_handlers = {}
function default_handlers.notification(method, params)
local _ = log.debug() and log.debug('notification', method, params)
end
function default_handlers.server_request(method, params)
local _ = log.debug() and log.debug('server_request', method, params)
return nil, rpc_response_error(protocol.ErrorCodes.MethodNotFound)
end
function default_handlers.on_exit(code, signal)
local _ = log.info() and log.info("client exit", { code = code, signal = signal })
end
function default_handlers.on_error(code, err)
local _ = log.error() and log.error('client_error:', client_errors[code], err)
end
--- Create and start an RPC client.
-- @param cmd [
local function create_and_start_client(cmd, cmd_args, handlers, extra_spawn_params)
local _ = log.info() and log.info("Starting RPC client", {cmd = cmd, args = cmd_args, extra = extra_spawn_params})
validate {
cmd = { cmd, 's' };
cmd_args = { cmd_args, 't' };
handlers = { handlers, 't', true };
}
if not (vim.fn.executable(cmd) == 1) then
error(string.format("The given command %q is not executable.", cmd))
end
if handlers then
local user_handlers = handlers
handlers = {}
for handle_name, default_handler in pairs(default_handlers) do
local user_handler = user_handlers[handle_name]
if user_handler then
if type(user_handler) ~= 'function' then
error(string.format("handler.%s must be a function", handle_name))
end
-- server_request is wrapped elsewhere.
if not (handle_name == 'server_request'
or handle_name == 'on_exit') -- TODO this blocks the loop exiting for some reason.
then
user_handler = schedule_wrap(user_handler)
end
handlers[handle_name] = user_handler
else
handlers[handle_name] = default_handler
end
end
else
handlers = default_handlers
end
local stdin = uv.new_pipe(false)
local stdout = uv.new_pipe(false)
local stderr = uv.new_pipe(false)
local message_index = 0
local message_callbacks = {}
local handle, pid
do
local function onexit(code, signal)
stdin:close()
stdout:close()
stderr:close()
handle:close()
-- Make sure that message_callbacks can be gc'd.
message_callbacks = nil
handlers.on_exit(code, signal)
end
local spawn_params = {
args = cmd_args;
stdio = {stdin, stdout, stderr};
}
if extra_spawn_params then
spawn_params.cwd = extra_spawn_params.cwd
if spawn_params.cwd then
assert(is_dir(spawn_params.cwd), "cwd must be a directory")
end
spawn_params.env = force_env_list(extra_spawn_params.env)
end
handle, pid = uv.spawn(cmd, spawn_params, onexit)
end
local function encode_and_send(payload)
local _ = log.debug() and log.debug("rpc.send.payload", payload)
if handle:is_closing() then return false end
-- TODO(ashkan) remove this once we have a Lua json_encode
schedule(function()
local encoded = assert(json_encode(payload))
stdin:write(format_message_with_content_length(encoded))
end)
return true
end
local function send_notification(method, params)
local _ = log.debug() and log.debug("rpc.notify", method, params)
return encode_and_send {
jsonrpc = "2.0";
method = method;
params = params;
}
end
local function send_response(request_id, err, result)
return encode_and_send {
id = request_id;
jsonrpc = "2.0";
error = err;
result = result;
}
end
local function send_request(method, params, callback)
validate {
callback = { callback, 'f' };
}
message_index = message_index + 1
local message_id = message_index
local result = encode_and_send {
id = message_id;
jsonrpc = "2.0";
method = method;
params = params;
}
if result then
message_callbacks[message_id] = schedule_wrap(callback)
return result, message_id
else
return false
end
end
stderr:read_start(function(_err, chunk)
if chunk then
local _ = log.error() and log.error("rpc", cmd, "stderr", chunk)
end
end)
local function on_error(errkind, ...)
assert(client_errors[errkind])
-- TODO what to do if this fails?
pcall(handlers.on_error, errkind, ...)
end
local function pcall_handler(errkind, status, head, ...)
if not status then
on_error(errkind, head, ...)
return status, head
end
return status, head, ...
end
local function try_call(errkind, fn, ...)
return pcall_handler(errkind, pcall(fn, ...))
end
-- TODO periodically check message_callbacks for old requests past a certain
-- time and log them. This would require storing the timestamp. I could call
-- them with an error then, perhaps.
local function handle_body(body)
local decoded, err = json_decode(body)
if not decoded then
on_error(client_errors.INVALID_SERVER_JSON, err)
return
end
local _ = log.debug() and log.debug("decoded", decoded)
if type(decoded.method) == 'string' and decoded.id then
-- Server Request
decoded.params = convert_NIL(decoded.params)
-- Schedule here so that the users functions don't trigger an error and
-- we can still use the result.
schedule(function()
local status, result
status, result, err = try_call(client_errors.SERVER_REQUEST_HANDLER_ERROR,
handlers.server_request, decoded.method, decoded.params)
local _ = log.debug() and log.debug("server_request: callback result", { status = status, result = result, err = err })
if status then
if not (result or err) then
-- TODO this can be a problem if `null` is sent for result. needs vim.NIL
error(string.format("method %q: either a result or an error must be sent to the server in response", decoded.method))
end
if err then
assert(type(err) == 'table', "err must be a table. Use rpc_response_error to help format errors.")
local code_name = assert(protocol.ErrorCodes[err.code], "Errors must use protocol.ErrorCodes. Use rpc_response_error to help format errors.")
err.message = err.message or code_name
end
else
-- On an exception, result will contain the error message.
err = rpc_response_error(protocol.ErrorCodes.InternalError, result)
result = nil
end
send_response(decoded.id, err, result)
end)
-- This works because we are expecting vim.NIL here
elseif decoded.id and (decoded.result or decoded.error) then
-- Server Result
decoded.error = convert_NIL(decoded.error)
decoded.result = convert_NIL(decoded.result)
-- We sent a number, so we expect a number.
local result_id = tonumber(decoded.id)
local callback = message_callbacks[result_id]
if callback then
message_callbacks[result_id] = nil
validate {
callback = { callback, 'f' };
}
if decoded.error then
decoded.error = setmetatable(decoded.error, {
__tostring = format_rpc_error;
})
end
try_call(client_errors.SERVER_RESULT_CALLBACK_ERROR,
callback, decoded.error, decoded.result)
else
on_error(client_errors.NO_RESULT_CALLBACK_FOUND, decoded)
local _ = log.error() and log.error("No callback found for server response id "..result_id)
end
elseif type(decoded.method) == 'string' then
-- Notification
decoded.params = convert_NIL(decoded.params)
try_call(client_errors.NOTIFICATION_HANDLER_ERROR,
handlers.notification, decoded.method, decoded.params)
else
-- Invalid server message
on_error(client_errors.INVALID_SERVER_MESSAGE, decoded)
end
end
-- TODO(ashkan) remove this once we have a Lua json_decode
handle_body = schedule_wrap(handle_body)
local request_parser = coroutine.wrap(request_parser_loop)
request_parser()
stdout:read_start(function(err, chunk)
if err then
-- TODO better handling. Can these be intermittent errors?
on_error(client_errors.READ_ERROR, err)
return
end
-- This should signal that we are done reading from the client.
if not chunk then return end
-- Flush anything in the parser by looping until we don't get a result
-- anymore.
while true do
local headers, body = request_parser(chunk)
-- If we successfully parsed, then handle the response.
if headers then
handle_body(body)
-- Set chunk to empty so that we can call request_parser to get
-- anything existing in the parser to flush.
chunk = ''
else
break
end
end
end)
return {
pid = pid;
handle = handle;
request = send_request;
notify = send_notification;
}
end
return {
start = create_and_start_client;
rpc_response_error = rpc_response_error;
format_rpc_error = format_rpc_error;
client_errors = client_errors;
}
-- vim:sw=2 ts=2 et

View File

@ -0,0 +1,727 @@
local protocol = require 'vim.lsp.protocol'
local vim = vim
local validate = vim.validate
local api = vim.api
local list_extend = vim.list_extend
local M = {}
local split = vim.split
local function split_lines(value)
return split(value, '\n', true)
end
local function ok_or_nil(status, ...)
if not status then return end
return ...
end
local function npcall(fn, ...)
return ok_or_nil(pcall(fn, ...))
end
--- Find the longest shared prefix between prefix and word.
-- e.g. remove_prefix("123tes", "testing") == "ting"
local function remove_prefix(prefix, word)
local max_prefix_length = math.min(#prefix, #word)
local prefix_length = 0
for i = 1, max_prefix_length do
local current_line_suffix = prefix:sub(-i)
local word_prefix = word:sub(1, i)
if current_line_suffix == word_prefix then
prefix_length = i
end
end
return word:sub(prefix_length + 1)
end
-- TODO(ashkan) @performance this could do less copying.
function M.set_lines(lines, A, B, new_lines)
-- 0-indexing to 1-indexing
local i_0 = A[1] + 1
local i_n = B[1] + 1
if not (i_0 >= 1 and i_0 <= #lines and i_n >= 1 and i_n <= #lines) then
error("Invalid range: "..vim.inspect{A = A; B = B; #lines, new_lines})
end
local prefix = ""
local suffix = lines[i_n]:sub(B[2]+1)
if A[2] > 0 then
prefix = lines[i_0]:sub(1, A[2])
end
local n = i_n - i_0 + 1
if n ~= #new_lines then
for _ = 1, n - #new_lines do table.remove(lines, i_0) end
for _ = 1, #new_lines - n do table.insert(lines, i_0, '') end
end
for i = 1, #new_lines do
lines[i - 1 + i_0] = new_lines[i]
end
if #suffix > 0 then
local i = i_0 + #new_lines - 1
lines[i] = lines[i]..suffix
end
if #prefix > 0 then
lines[i_0] = prefix..lines[i_0]
end
return lines
end
local function sort_by_key(fn)
return function(a,b)
local ka, kb = fn(a), fn(b)
assert(#ka == #kb)
for i = 1, #ka do
if ka[i] ~= kb[i] then
return ka[i] < kb[i]
end
end
-- every value must have been equal here, which means it's not less than.
return false
end
end
local edit_sort_key = sort_by_key(function(e)
return {e.A[1], e.A[2], e.i}
end)
function M.apply_text_edits(text_edits, bufnr)
if not next(text_edits) then return end
local start_line, finish_line = math.huge, -1
local cleaned = {}
for i, e in ipairs(text_edits) do
start_line = math.min(e.range.start.line, start_line)
finish_line = math.max(e.range["end"].line, finish_line)
-- TODO(ashkan) sanity check ranges for overlap.
table.insert(cleaned, {
i = i;
A = {e.range.start.line; e.range.start.character};
B = {e.range["end"].line; e.range["end"].character};
lines = vim.split(e.newText, '\n', true);
})
end
-- Reverse sort the orders so we can apply them without interfering with
-- eachother. Also add i as a sort key to mimic a stable sort.
table.sort(cleaned, edit_sort_key)
local lines = api.nvim_buf_get_lines(bufnr, start_line, finish_line + 1, false)
local fix_eol = api.nvim_buf_get_option(bufnr, 'fixeol')
local set_eol = fix_eol and api.nvim_buf_line_count(bufnr) == finish_line + 1
if set_eol and #lines[#lines] ~= 0 then
table.insert(lines, '')
end
for i = #cleaned, 1, -1 do
local e = cleaned[i]
local A = {e.A[1] - start_line, e.A[2]}
local B = {e.B[1] - start_line, e.B[2]}
lines = M.set_lines(lines, A, B, e.lines)
end
if set_eol and #lines[#lines] == 0 then
table.remove(lines)
end
api.nvim_buf_set_lines(bufnr, start_line, finish_line + 1, false, lines)
end
-- local valid_windows_path_characters = "[^<>:\"/\\|?*]"
-- local valid_unix_path_characters = "[^/]"
-- https://github.com/davidm/lua-glob-pattern
-- https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names
-- function M.glob_to_regex(glob)
-- end
-- textDocument/completion response returns one of CompletionItem[], CompletionList or null.
-- https://microsoft.github.io/language-server-protocol/specification#textDocument_completion
function M.extract_completion_items(result)
if type(result) == 'table' and result.items then
return result.items
elseif result ~= nil then
return result
else
return {}
end
end
--- Apply the TextDocumentEdit response.
-- @params TextDocumentEdit [table] see https://microsoft.github.io/language-server-protocol/specification
function M.apply_text_document_edit(text_document_edit)
local text_document = text_document_edit.textDocument
local bufnr = vim.uri_to_bufnr(text_document.uri)
-- TODO(ashkan) check this is correct.
if api.nvim_buf_get_changedtick(bufnr) > text_document.version then
print("Buffer ", text_document.uri, " newer than edits.")
return
end
M.apply_text_edits(text_document_edit.edits, bufnr)
end
function M.get_current_line_to_cursor()
local pos = api.nvim_win_get_cursor(0)
local line = assert(api.nvim_buf_get_lines(0, pos[1]-1, pos[1], false)[1])
return line:sub(pos[2]+1)
end
--- Getting vim complete-items with incomplete flag.
-- @params CompletionItem[], CompletionList or nil (https://microsoft.github.io/language-server-protocol/specification#textDocument_completion)
-- @return { matches = complete-items table, incomplete = boolean }
function M.text_document_completion_list_to_complete_items(result, line_prefix)
local items = M.extract_completion_items(result)
if vim.tbl_isempty(items) then
return {}
end
-- Only initialize if we have some items.
if not line_prefix then
line_prefix = M.get_current_line_to_cursor()
end
local matches = {}
for _, completion_item in ipairs(items) do
local info = ' '
local documentation = completion_item.documentation
if documentation then
if type(documentation) == 'string' and documentation ~= '' then
info = documentation
elseif type(documentation) == 'table' and type(documentation.value) == 'string' then
info = documentation.value
-- else
-- TODO(ashkan) Validation handling here?
end
end
local word = completion_item.insertText or completion_item.label
-- Ref: `:h complete-items`
table.insert(matches, {
word = remove_prefix(line_prefix, word),
abbr = completion_item.label,
kind = protocol.CompletionItemKind[completion_item.kind] or '',
menu = completion_item.detail or '',
info = info,
icase = 1,
dup = 0,
empty = 1,
})
end
return matches
end
-- @params WorkspaceEdit [table] see https://microsoft.github.io/language-server-protocol/specification
function M.apply_workspace_edit(workspace_edit)
if workspace_edit.documentChanges then
for _, change in ipairs(workspace_edit.documentChanges) do
if change.kind then
-- TODO(ashkan) handle CreateFile/RenameFile/DeleteFile
error(string.format("Unsupported change: %q", vim.inspect(change)))
else
M.apply_text_document_edit(change)
end
end
return
end
local all_changes = workspace_edit.changes
if not (all_changes and not vim.tbl_isempty(all_changes)) then
return
end
for uri, changes in pairs(all_changes) do
local bufnr = vim.uri_to_bufnr(uri)
M.apply_text_edits(changes, bufnr)
end
end
--- Convert any of MarkedString | MarkedString[] | MarkupContent into markdown text lines
-- see https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#textDocument_hover
-- Useful for textDocument/hover, textDocument/signatureHelp, and potentially others.
function M.convert_input_to_markdown_lines(input, contents)
contents = contents or {}
-- MarkedString variation 1
if type(input) == 'string' then
list_extend(contents, split_lines(input))
else
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.
-- 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 ''))
-- MarkupString variation 2
elseif input.language then
-- Some servers send input.value as empty, so let's ignore this :(
-- assert(type(input.value) == 'string')
table.insert(contents, "```"..input.language)
list_extend(contents, split_lines(input.value or ''))
table.insert(contents, "```")
-- By deduction, this must be MarkedString[]
else
-- Use our existing logic to handle MarkedString
for _, marked_string in ipairs(input) do
M.convert_input_to_markdown_lines(marked_string, contents)
end
end
end
if contents[1] == '' or contents[1] == nil then
return {}
end
return contents
end
function M.make_floating_popup_options(width, height, opts)
validate {
opts = { opts, 't', true };
}
opts = opts or {}
validate {
["opts.offset_x"] = { opts.offset_x, 'n', true };
["opts.offset_y"] = { opts.offset_y, 'n', true };
}
local anchor = ''
local row, col
if vim.fn.winline() <= height then
anchor = anchor..'N'
row = 1
else
anchor = anchor..'S'
row = 0
end
if vim.fn.wincol() + width <= api.nvim_get_option('columns') then
anchor = anchor..'W'
col = 0
else
anchor = anchor..'E'
col = 1
end
return {
anchor = anchor,
col = col + (opts.offset_x or 0),
height = height,
relative = 'cursor',
row = row + (opts.offset_y or 0),
style = 'minimal',
width = width,
}
end
function M.jump_to_location(location)
if location.uri == nil then return end
local bufnr = vim.uri_to_bufnr(location.uri)
-- Save position in jumplist
vim.cmd "normal! m'"
-- TODO(ashkan) use tagfunc here to update tagstack.
api.nvim_set_current_buf(bufnr)
local row = location.range.start.line
local col = location.range.start.character
local line = api.nvim_buf_get_lines(0, row, row+1, true)[1]
col = vim.str_byteindex(line, col)
api.nvim_win_set_cursor(0, {row + 1, col})
return true
end
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
return win
end
end
end
-- Check if a window with `unique_name` tagged is associated with the current
-- buffer. If not, make a new preview.
--
-- fn()'s return values will be passed directly to open_floating_preview in the
-- case that a new floating window should be created.
function M.focusable_preview(unique_name, fn)
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 then
api.nvim_set_current_win(win)
api.nvim_command("stopinsert")
return
end
end
local pbufnr, pwinnr = M.open_floating_preview(fn())
api.nvim_win_set_var(pwinnr, unique_name, bufnr)
return pbufnr, pwinnr
end
function M.open_floating_preview(contents, filetype, opts)
validate {
contents = { contents, 't' };
filetype = { filetype, 's', true };
opts = { opts, 't', true };
}
opts = opts or {}
-- Trim empty lines from the end.
contents = M.trim_empty_lines(contents)
local width = opts.width
local height = opts.height or #contents
if not width then
width = 0
for i, line in ipairs(contents) do
-- Clean up the input and add left pad.
line = " "..line:gsub("\r", "")
-- TODO(ashkan) use nvim_strdisplaywidth if/when that is introduced.
local line_width = vim.fn.strdisplaywidth(line)
width = math.max(line_width, width)
contents[i] = line
end
-- Add right padding of 1 each.
width = width + 1
end
local floating_bufnr = api.nvim_create_buf(false, true)
if filetype then
api.nvim_buf_set_option(floating_bufnr, 'filetype', filetype)
end
local float_option = M.make_floating_popup_options(width, height, opts)
local floating_winnr = api.nvim_open_win(floating_bufnr, false, float_option)
if filetype == 'markdown' then
api.nvim_win_set_option(floating_winnr, 'conceallevel', 2)
end
api.nvim_buf_set_lines(floating_bufnr, 0, -1, true, contents)
api.nvim_buf_set_option(floating_bufnr, 'modifiable', false)
-- TODO make InsertCharPre disappearing optional?
api.nvim_command("autocmd CursorMoved,BufHidden,InsertCharPre <buffer> ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)")
return floating_bufnr, floating_winnr
end
local function validate_lsp_position(pos)
validate { pos = {pos, 't'} }
validate {
line = {pos.line, 'n'};
character = {pos.character, 'n'};
}
return true
end
function M.open_floating_peek_preview(bufnr, start, finish, opts)
validate {
bufnr = {bufnr, 'n'};
start = {start, validate_lsp_position, 'valid start Position'};
finish = {finish, validate_lsp_position, 'valid finish Position'};
opts = { opts, 't', true };
}
local width = math.max(finish.character - start.character + 1, 1)
local height = math.max(finish.line - start.line + 1, 1)
local floating_winnr = api.nvim_open_win(bufnr, false, M.make_floating_popup_options(width, height, opts))
api.nvim_win_set_cursor(floating_winnr, {start.line+1, start.character})
api.nvim_command("autocmd CursorMoved * ++once lua pcall(vim.api.nvim_win_close, "..floating_winnr..", true)")
return floating_winnr
end
local function highlight_range(bufnr, ns, hiname, start, finish)
if start[1] == finish[1] then
-- TODO care about encoding here since this is in byte index?
api.nvim_buf_add_highlight(bufnr, ns, hiname, start[1], start[2], finish[2])
else
api.nvim_buf_add_highlight(bufnr, ns, hiname, start[1], start[2], -1)
for line = start[1] + 1, finish[1] - 1 do
api.nvim_buf_add_highlight(bufnr, ns, hiname, line, 0, -1)
end
api.nvim_buf_add_highlight(bufnr, ns, hiname, finish[1], 0, finish[2])
end
end
do
local all_buffer_diagnostics = {}
local diagnostic_ns = api.nvim_create_namespace("vim_lsp_diagnostics")
local underline_highlight_name = "LspDiagnosticsUnderline"
api.nvim_command(string.format("highlight default %s gui=underline cterm=underline", underline_highlight_name))
local severity_highlights = {}
local default_severity_highlight = {
[protocol.DiagnosticSeverity.Error] = { guifg = "Red" };
[protocol.DiagnosticSeverity.Warning] = { guifg = "Orange" };
[protocol.DiagnosticSeverity.Information] = { guifg = "LightBlue" };
[protocol.DiagnosticSeverity.Hint] = { guifg = "LightGrey" };
}
-- Initialize default severity highlights
for severity, hi_info in pairs(default_severity_highlight) do
local severity_name = protocol.DiagnosticSeverity[severity]
local highlight_name = "LspDiagnostics"..severity_name
-- Try to fill in the foreground color with a sane default.
local cmd_parts = {"highlight", "default", highlight_name}
for k, v in pairs(hi_info) do
table.insert(cmd_parts, k.."="..v)
end
api.nvim_command(table.concat(cmd_parts, ' '))
severity_highlights[severity] = highlight_name
end
function M.buf_clear_diagnostics(bufnr)
validate { bufnr = {bufnr, 'n', true} }
bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr
api.nvim_buf_clear_namespace(bufnr, diagnostic_ns, 0, -1)
end
function M.get_severity_highlight_name(severity)
return severity_highlights[severity]
end
function M.show_line_diagnostics()
local bufnr = api.nvim_get_current_buf()
local line = api.nvim_win_get_cursor(0)[1] - 1
-- local marks = api.nvim_buf_get_extmarks(bufnr, diagnostic_ns, {line, 0}, {line, -1}, {})
-- if #marks == 0 then
-- return
-- end
-- local buffer_diagnostics = all_buffer_diagnostics[bufnr]
local lines = {"Diagnostics:"}
local highlights = {{0, "Bold"}}
local buffer_diagnostics = all_buffer_diagnostics[bufnr]
if not buffer_diagnostics then return end
local line_diagnostics = buffer_diagnostics[line]
if not line_diagnostics then return end
for i, diagnostic in ipairs(line_diagnostics) do
-- for i, mark in ipairs(marks) do
-- local mark_id = mark[1]
-- local diagnostic = buffer_diagnostics[mark_id]
-- TODO(ashkan) make format configurable?
local prefix = string.format("%d. ", i)
local hiname = severity_highlights[diagnostic.severity]
local message_lines = split_lines(diagnostic.message)
table.insert(lines, prefix..message_lines[1])
table.insert(highlights, {#prefix + 1, hiname})
for j = 2, #message_lines do
table.insert(lines, message_lines[j])
table.insert(highlights, {0, hiname})
end
end
local popup_bufnr, winnr = M.open_floating_preview(lines, 'plaintext')
for i, hi in ipairs(highlights) do
local prefixlen, hiname = unpack(hi)
-- Start highlight after the prefix
api.nvim_buf_add_highlight(popup_bufnr, -1, hiname, i-1, prefixlen, -1)
end
return popup_bufnr, winnr
end
function M.buf_diagnostics_save_positions(bufnr, diagnostics)
validate {
bufnr = {bufnr, 'n', true};
diagnostics = {diagnostics, 't', true};
}
if not diagnostics then return end
bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr
if not all_buffer_diagnostics[bufnr] then
-- Clean up our data when the buffer unloads.
api.nvim_buf_attach(bufnr, false, {
on_detach = function(b)
all_buffer_diagnostics[b] = nil
end
})
end
all_buffer_diagnostics[bufnr] = {}
local buffer_diagnostics = all_buffer_diagnostics[bufnr]
for _, diagnostic in ipairs(diagnostics) do
local start = diagnostic.range.start
-- local mark_id = api.nvim_buf_set_extmark(bufnr, diagnostic_ns, 0, start.line, 0, {})
-- buffer_diagnostics[mark_id] = diagnostic
local line_diagnostics = buffer_diagnostics[start.line]
if not line_diagnostics then
line_diagnostics = {}
buffer_diagnostics[start.line] = line_diagnostics
end
table.insert(line_diagnostics, diagnostic)
end
end
function M.buf_diagnostics_underline(bufnr, diagnostics)
for _, diagnostic in ipairs(diagnostics) do
local start = diagnostic.range.start
local finish = diagnostic.range["end"]
-- TODO care about encoding here since this is in byte index?
highlight_range(bufnr, diagnostic_ns, underline_highlight_name,
{start.line, start.character},
{finish.line, finish.character}
)
end
end
function M.buf_diagnostics_virtual_text(bufnr, diagnostics)
local buffer_line_diagnostics = all_buffer_diagnostics[bufnr]
if not buffer_line_diagnostics then
M.buf_diagnostics_save_positions(bufnr, diagnostics)
end
buffer_line_diagnostics = all_buffer_diagnostics[bufnr]
if not buffer_line_diagnostics then
return
end
for line, line_diags in pairs(buffer_line_diagnostics) do
local virt_texts = {}
for i = 1, #line_diags - 1 do
table.insert(virt_texts, {"", severity_highlights[line_diags[i].severity]})
end
local last = line_diags[#line_diags]
-- TODO(ashkan) use first line instead of subbing 2 spaces?
table.insert(virt_texts, {""..last.message:gsub("\r", ""):gsub("\n", " "), severity_highlights[last.severity]})
api.nvim_buf_set_virtual_text(bufnr, diagnostic_ns, line, virt_texts, {})
end
end
end
local position_sort = sort_by_key(function(v)
return {v.line, v.character}
end)
-- Returns the items with the byte position calculated correctly and in sorted
-- order.
function M.locations_to_items(locations)
local items = {}
local grouped = setmetatable({}, {
__index = function(t, k)
local v = {}
rawset(t, k, v)
return v
end;
})
for _, d in ipairs(locations) do
local start = d.range.start
local fname = assert(vim.uri_to_fname(d.uri))
table.insert(grouped[fname], start)
end
local keys = vim.tbl_keys(grouped)
table.sort(keys)
-- TODO(ashkan) I wish we could do this lazily.
for _, fname in ipairs(keys) do
local rows = grouped[fname]
table.sort(rows, position_sort)
local i = 0
for line in io.lines(fname) do
for _, pos in ipairs(rows) do
local row = pos.line
if i == row then
local col
if pos.character > #line then
col = #line
else
col = vim.str_byteindex(line, pos.character)
end
table.insert(items, {
filename = fname,
lnum = row + 1,
col = col + 1;
text = line;
})
end
end
i = i + 1
end
end
return items
end
-- locations is Location[]
-- Only sets for the current window.
function M.set_loclist(locations)
vim.fn.setloclist(0, {}, ' ', {
title = 'Language Server';
items = M.locations_to_items(locations);
})
end
-- locations is Location[]
function M.set_qflist(locations)
vim.fn.setqflist({}, ' ', {
title = 'Language Server';
items = M.locations_to_items(locations);
})
end
-- Remove empty lines from the beginning and end.
function M.trim_empty_lines(lines)
local start = 1
for i = 1, #lines do
if #lines[i] > 0 then
start = i
break
end
end
local finish = 1
for i = #lines, 1, -1 do
if #lines[i] > 0 then
finish = i
break
end
end
return vim.list_extend({}, lines, start, finish)
end
-- Accepts markdown lines and tries to reduce it to a filetype if it is
-- just a single code block.
-- Note: This modifies the input.
--
-- Returns: 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
local has_inner_code_fence = false
for i = 2, (#lines - 1) do
local line = lines[i]
if line:sub(1,3) == '```' then
has_inner_code_fence = true
break
end
end
-- No inner code fences + starting with code fence = hooray.
if not has_inner_code_fence then
table.remove(lines, 1)
table.remove(lines)
return language_id
end
end
return 'markdown'
end
local str_utfindex = vim.str_utfindex
function M.make_position_params()
local row, col = unpack(api.nvim_win_get_cursor(0))
row = row - 1
local line = api.nvim_buf_get_lines(0, row, row+1, true)[1]
col = str_utfindex(line, col)
return {
textDocument = { uri = vim.uri_from_bufnr(0) };
position = { line = row; character = col; }
}
end
-- @param buf buffer handle or 0 for current.
-- @param row 0-indexed line
-- @param col 0-indexed byte offset in line
function M.character_offset(buf, row, col)
local line = api.nvim_buf_get_lines(buf, row, row+1, true)[1]
-- If the col is past the EOL, use the line length.
if col > #line then
return str_utfindex(line)
end
return str_utfindex(line, col)
end
return M
-- vim:sw=2 ts=2 et

View File

@ -4,34 +4,37 @@
-- test-suite. If, in the future, Nvim itself is used to run the test-suite
-- instead of "vanilla Lua", these functions could move to src/nvim/lua/vim.lua
local vim = vim or {}
--- Returns a deep copy of the given object. Non-table objects are copied as
--- in a typical Lua assignment, whereas table objects are copied recursively.
---
--@param orig Table to copy
--@returns New table of copied keys and (nested) values.
local function deepcopy(orig)
error(orig)
end
local function _id(v)
return v
end
local deepcopy_funcs = {
table = function(orig)
local copy = {}
for k, v in pairs(orig) do
copy[deepcopy(k)] = deepcopy(v)
end
return copy
end,
number = _id,
string = _id,
['nil'] = _id,
boolean = _id,
}
deepcopy = function(orig)
return deepcopy_funcs[type(orig)](orig)
end
function vim.deepcopy(orig) end -- luacheck: no unused
vim.deepcopy = (function()
local function _id(v)
return v
end
local deepcopy_funcs = {
table = function(orig)
local copy = {}
for k, v in pairs(orig) do
copy[vim.deepcopy(k)] = vim.deepcopy(v)
end
return copy
end,
number = _id,
string = _id,
['nil'] = _id,
boolean = _id,
}
return function(orig)
return deepcopy_funcs[type(orig)](orig)
end
end)()
--- Splits a string at each instance of a separator.
---
@ -43,10 +46,8 @@ end
--@param sep Separator string or pattern
--@param plain If `true` use `sep` literally (passed to String.find)
--@returns Iterator over the split components
local function gsplit(s, sep, plain)
assert(type(s) == "string")
assert(type(sep) == "string")
assert(type(plain) == "boolean" or type(plain) == "nil")
function vim.gsplit(s, sep, plain)
vim.validate{s={s,'s'},sep={sep,'s'},plain={plain,'b',true}}
local start = 1
local done = false
@ -92,20 +93,51 @@ end
--@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.
local function split(s,sep,plain)
local t={} for c in gsplit(s, sep, plain) do table.insert(t,c) end
function vim.split(s,sep,plain)
local t={} for c in vim.gsplit(s, sep, plain) do table.insert(t,c) end
return t
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
---
--@param t Table
--@returns list of keys
function vim.tbl_keys(t)
assert(type(t) == 'table', string.format("Expected table, got %s", type(t)))
local keys = {}
for k, _ in pairs(t) do
table.insert(keys, k)
end
return keys
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
function vim.tbl_values(t)
assert(type(t) == 'table', string.format("Expected table, got %s", type(t)))
local values = {}
for _, v in pairs(t) do
table.insert(values, v)
end
return values
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`
local function tbl_contains(t, value)
if type(t) ~= 'table' then
error('t must be a table')
end
function vim.tbl_contains(t, value)
vim.validate{t={t,'t'}}
for _,v in ipairs(t) do
if v == value then
return true
@ -114,6 +146,16 @@ local function tbl_contains(t, value)
return false
end
-- Returns true if the table is empty, and contains no indexed or keyed values.
--
--@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua
--
--@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
--- Merges two or more map-like tables.
---
--@see |extend()|
@ -123,7 +165,7 @@ end
--- - "keep": use value from the leftmost map
--- - "force": use value from the rightmost map
--@param ... Two or more map-like tables.
local function tbl_extend(behavior, ...)
function vim.tbl_extend(behavior, ...)
if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then
error('invalid "behavior": '..tostring(behavior))
end
@ -145,13 +187,77 @@ local function tbl_extend(behavior, ...)
return ret
end
--- Deep compare values for equality
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
return false
end
end
return true
end
return false
end
--- Add the reverse lookup values to an existing table.
--- For example:
--- `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.
function vim.tbl_add_reverse_lookup(o)
local keys = vim.tbl_keys(o)
for _, k in ipairs(keys) do
local v = o[k]
if o[v] then
error(string.format("The reverse lookup found an existing value for %q while processing key %q", tostring(v), tostring(k)))
end
o[v] = k
end
return o
end
--- Extends a list-like table with the values of another list-like table.
---
--- NOTE: This mutates dst!
---
--@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
function vim.list_extend(dst, src, start, finish)
vim.validate {
dst = {dst, 't'};
src = {src, 't'};
start = {start, 'n', true};
finish = {finish, 'n', true};
}
for i = start or 1, finish or #src do
table.insert(dst, src[i])
end
return dst
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
---
--@param t List-like table
--@returns Flattened copy of the given list-like table.
local function tbl_flatten(t)
-- From https://github.com/premake/premake-core/blob/master/src/base/table.lua
function vim.tbl_flatten(t)
local result = {}
local function _tbl_flatten(_t)
local n = #_t
@ -168,13 +274,39 @@ local function tbl_flatten(t)
return result
end
-- Determine whether a Lua table can be treated as an array.
---
--@params Table
--@returns true: A non-empty array, false: A non-empty table, nil: An empty table
function vim.tbl_islist(t)
if type(t) ~= 'table' then
return false
end
local count = 0
for k, _ in pairs(t) do
if type(k) == "number" then
count = count + 1
else
return false
end
end
if count > 0 then
return true
else
return nil
end
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
local function trim(s)
assert(type(s) == 'string', 'Only strings can be trimmed')
function vim.trim(s)
vim.validate{s={s,'s'}}
return s:match('^%s*(.*%S)') or ''
end
@ -183,19 +315,120 @@ end
--@see https://github.com/rxi/lume
--@param s String to escape
--@returns %-escaped pattern string
local function pesc(s)
assert(type(s) == 'string')
function vim.pesc(s)
vim.validate{s={s,'s'}}
return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1')
end
local module = {
deepcopy = deepcopy,
gsplit = gsplit,
pesc = pesc,
split = split,
tbl_contains = tbl_contains,
tbl_extend = tbl_extend,
tbl_flatten = tbl_flatten,
trim = trim,
}
return module
--- Test if `prefix` is a prefix of `s` for strings.
--
-- @param s String to check
-- @param prefix Potential 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
end
--- Test if `suffix` is a suffix of `s` for strings.
--
-- @param s String to check
-- @param suffix Potential 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
end
--- Validates a parameter specification (types and values).
---
--- Usage example:
--- <pre>
--- function user.new(name, age, hobbies)
--- vim.validate{
--- name={name, 'string'},
--- age={age, 'number'},
--- hobbies={hobbies, 'table'},
--- }
--- ...
--- end
--- </pre>
---
--- Examples with explicit argument values (can be run directly):
--- <pre>
--- 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')
--- </pre>
---
--@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
--- - type_name: string type name, one of: ("table", "t", "string",
--- "s", "number", "n", "boolean", "b", "function", "f", "nil",
--- "thread", "userdata")
--- - optional: (optional) boolean, if true, `nil` is valid
--- 2. (arg_value, fn, msg)
--- - arg_value: argument value
--- - fn: any function accepting one argument, returns true if and
--- only if the argument is valid
--- - msg: (optional) error string if validation fails
function vim.validate(opt) end -- luacheck: no unused
vim.validate = (function()
local type_names = {
t='table', s='string', n='number', b='boolean', f='function', c='callable',
['table']='table', ['string']='string', ['number']='number',
['boolean']='boolean', ['function']='function', ['callable']='callable',
['nil']='nil', ['thread']='thread', ['userdata']='userdata',
}
local function _type_name(t)
local tname = type_names[t]
if tname == nil then
error(string.format('invalid type name: %s', tostring(t)))
end
return tname
end
local function _is_type(val, t)
return t == 'callable' and vim.is_callable(val) or type(val) == t
end
return function(opt)
assert(type(opt) == 'table', string.format('opt: expected table, got %s', type(opt)))
for param_name, spec in pairs(opt) do
assert(type(spec) == 'table', string.format('%s: expected table, got %s', param_name, type(spec)))
local val = spec[1] -- Argument value.
local t = spec[2] -- Type name, or callable.
local optional = (true == spec[3])
if not vim.is_callable(t) then -- Check type name.
if (not optional or val ~= nil) and not _is_type(val, _type_name(t)) then
error(string.format("%s: expected %s, got %s", param_name, _type_name(t), type(val)))
end
elseif not t(val) then -- Check user-provided validation function.
error(string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), val))
end
end
return true
end
end)()
--- Returns true if object `f` can be called as a function.
---
--@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)
if m == nil then return false end
return type(m.__call) == 'function'
end
return vim
-- vim:sw=2 ts=2 et

View File

@ -0,0 +1,73 @@
local a = vim.api
-- TODO(bfredl): currently we retain parsers for the lifetime of the buffer.
-- Consider use weak references to release parser if all plugins are done with
-- it.
local parsers = {}
local Parser = {}
Parser.__index = Parser
function Parser:parse()
if self.valid then
return self.tree
end
self.tree = self._parser:parse_buf(self.bufnr)
self.valid = true
return self.tree
end
function Parser:_on_lines(bufnr, _, start_row, old_stop_row, stop_row, old_byte_size)
local start_byte = a.nvim_buf_get_offset(bufnr,start_row)
local stop_byte = a.nvim_buf_get_offset(bufnr,stop_row)
local old_stop_byte = start_byte + old_byte_size
self._parser:edit(start_byte,old_stop_byte,stop_byte,
start_row,0,old_stop_row,0,stop_row,0)
self.valid = false
end
local module = {
add_language=vim._ts_add_language,
inspect_language=vim._ts_inspect_language,
}
function module.create_parser(bufnr, ft, id)
if bufnr == 0 then
bufnr = a.nvim_get_current_buf()
end
local self = setmetatable({bufnr=bufnr, valid=false}, Parser)
self._parser = vim._create_ts_parser(ft)
self:parse()
-- TODO(bfredl): use weakref to self, so that the parser is free'd is no plugin is
-- using it.
local function lines_cb(_, ...)
return self:_on_lines(...)
end
local detach_cb = nil
if id ~= nil then
detach_cb = function()
if parsers[id] == self then
parsers[id] = nil
end
end
end
a.nvim_buf_attach(self.bufnr, false, {on_lines=lines_cb, on_detach=detach_cb})
return self
end
function module.get_parser(bufnr, ft)
if bufnr == nil or bufnr == 0 then
bufnr = a.nvim_get_current_buf()
end
if ft == nil then
ft = a.nvim_buf_get_option(bufnr, "filetype")
end
local id = tostring(bufnr)..'_'..ft
if parsers[id] == nil then
parsers[id] = module.create_parser(bufnr, ft, id)
end
return parsers[id]
end
return module

94
runtime/lua/vim/uri.lua Normal file
View File

@ -0,0 +1,94 @@
--- TODO: This is implemented only for files now.
-- https://tools.ietf.org/html/rfc3986
-- https://tools.ietf.org/html/rfc2732
-- https://tools.ietf.org/html/rfc2396
local uri_decode
do
local schar = string.char
local function hex_to_char(hex)
return schar(tonumber(hex, 16))
end
uri_decode = function(str)
return str:gsub("%%([a-fA-F0-9][a-fA-F0-9])", hex_to_char)
end
end
local uri_encode
do
local PATTERNS = {
--- RFC 2396
-- https://tools.ietf.org/html/rfc2396#section-2.2
rfc2396 = "^A-Za-z0-9%-_.!~*'()";
--- RFC 2732
-- https://tools.ietf.org/html/rfc2732
rfc2732 = "^A-Za-z0-9%-_.!~*'()[]";
--- RFC 3986
-- https://tools.ietf.org/html/rfc3986#section-2.2
rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/";
}
local sbyte, tohex = string.byte
if jit then
tohex = require'bit'.tohex
else
tohex = function(b) return string.format("%02x", b) end
end
local function percent_encode_char(char)
return "%"..tohex(sbyte(char), 2)
end
uri_encode = function(text, rfc)
if not text then return end
local pattern = PATTERNS[rfc] or PATTERNS.rfc3986
return text:gsub("(["..pattern.."])", percent_encode_char)
end
end
local function is_windows_file_uri(uri)
return uri:match('^file:///[a-zA-Z]:') ~= nil
end
local function uri_from_fname(path)
local volume_path, fname = path:match("^([a-zA-Z]:)(.*)")
local is_windows = volume_path ~= nil
if is_windows then
path = volume_path..uri_encode(fname:gsub("\\", "/"))
else
path = uri_encode(path)
end
local uri_parts = {"file://"}
if is_windows then
table.insert(uri_parts, "/")
end
table.insert(uri_parts, path)
return table.concat(uri_parts)
end
local function uri_from_bufnr(bufnr)
return uri_from_fname(vim.api.nvim_buf_get_name(bufnr))
end
local function uri_to_fname(uri)
-- TODO improve this.
if is_windows_file_uri(uri) then
uri = uri:gsub('^file:///', '')
uri = uri:gsub('/', '\\')
else
uri = uri:gsub('^file://', '')
end
return uri_decode(uri)
end
-- Return or create a buffer for a uri.
local function uri_to_bufnr(uri)
return vim.fn.bufadd((uri_to_fname(uri)))
end
return {
uri_from_fname = uri_from_fname,
uri_from_bufnr = uri_from_bufnr,
uri_to_fname = uri_to_fname,
uri_to_bufnr = uri_to_bufnr,
}
-- vim:sw=2 ts=2 et

View File

@ -1,6 +1,6 @@
" Script to define the syntax menu in synmenu.vim
" Maintainer: Bram Moolenaar <Bram@vim.org>
" Last Change: 2018 May 17
" Last Change: 2019 Dec 07
" This is used by "make menu" in the src directory.
edit <sfile>:p:h/synmenu.vim
@ -101,6 +101,7 @@ SynMenu AB.AYacc:ayacc
SynMenu AB.B:b
SynMenu AB.Baan:baan
SynMenu AB.Bash:bash
SynMenu AB.Basic.FreeBasic:freebasic
SynMenu AB.Basic.IBasic:ibasic
SynMenu AB.Basic.QBasic:basic
@ -128,8 +129,9 @@ SynMenu C.Century\ Term:cterm
SynMenu C.CH\ script:ch
SynMenu C.ChaiScript:chaiscript
SynMenu C.ChangeLog:changelog
SynMenu C.Cheetah\ template:cheetah
SynMenu C.CHILL:chill
SynMenu C.Cheetah\ template:cheetah
SynMenu C.Chicken:chicken
SynMenu C.ChordPro:chordpro
SynMenu C.Clean:clean
SynMenu C.Clever:cl
@ -160,6 +162,7 @@ SynMenu C.Cyn++:cynpp
SynMenu C.Cynlib:cynlib
SynMenu DE.D:d
SynMenu DE.Dart:dart
SynMenu DE.Datascript:datascript
SynMenu DE.Debian.Debian\ ChangeLog:debchangelog
SynMenu DE.Debian.Debian\ Control:debcontrol
@ -192,12 +195,14 @@ SynMenu DE.DTD:dtd
SynMenu DE.DTML\ (Zope):dtml
SynMenu DE.DTrace:dtrace
SynMenu DE.Dts/dtsi:dts
SynMenu DE.Dune:dune
SynMenu DE.Dylan.Dylan:dylan
SynMenu DE.Dylan.Dylan\ interface:dylanintr
SynMenu DE.Dylan.Dylan\ lid:dylanlid
SynMenu DE.EDIF:edif
SynMenu DE.Eiffel:eiffel
SynMenu DE.Eight:8th
SynMenu DE.Elinks\ config:elinks
SynMenu DE.Elm\ filter\ rules:elmfilt
SynMenu DE.Embedix\ Component\ Description:ecd
@ -307,6 +312,7 @@ SynMenu HIJK.Java.JavaCC:javacc
SynMenu HIJK.Java.Java\ Server\ Pages:jsp
SynMenu HIJK.Java.Java\ Properties:jproperties
SynMenu HIJK.JavaScript:javascript
SynMenu HIJK.JavaScriptReact:javascriptreact
SynMenu HIJK.Jess:jess
SynMenu HIJK.Jgraph:jgraph
SynMenu HIJK.Jovial:jovial
@ -365,6 +371,7 @@ SynMenu M.Mathematica:mma
SynMenu M.Matlab:matlab
SynMenu M.Maxima:maxima
SynMenu M.MEL\ (for\ Maya):mel
SynMenu M.Meson:meson
SynMenu M.Messages\ (/var/log):messages
SynMenu M.Metafont:mf
SynMenu M.MetaPost:mp
@ -467,6 +474,7 @@ SynMenu R.R.R\ help:rhelp
SynMenu R.R.R\ noweb:rnoweb
SynMenu R.Racc\ input:racc
SynMenu R.Radiance:radiance
SynMenu R.Raml:raml
SynMenu R.Ratpoison:ratpoison
SynMenu R.RCS.RCS\ log\ output:rcslog
SynMenu R.RCS.RCS\ file:rcs
@ -609,6 +617,8 @@ SynMenu T.Trustees:trustees
SynMenu T.TSS.Command\ Line:tsscl
SynMenu T.TSS.Geometry:tssgm
SynMenu T.TSS.Optics:tssop
SynMenu T.Typescript:typescript
SynMenu T.TypescriptReact:typescriptreact
SynMenu UV.Udev\ config:udevconf
SynMenu UV.Udev\ permissions:udevperm
@ -637,6 +647,7 @@ SynMenu UV.VSE\ JCL:vsejcl
SynMenu WXYZ.WEB.CWEB:cweb
SynMenu WXYZ.WEB.WEB:web
SynMenu WXYZ.WEB.WEB\ Changes:change
SynMenu WXYZ.WebAssembly:wast
SynMenu WXYZ.Webmacro:webmacro
SynMenu WXYZ.Website\ MetaLanguage:wml
SynMenu WXYZ.wDiff:wdiff

View File

@ -26,6 +26,10 @@
</screenshots>
<releases>
<release date="2019-11-06" version="0.4.3"/>
<release date="2019-09-15" version="0.4.2"/>
<release date="2019-09-15" version="0.4.1"/>
<release date="2019-09-15" version="0.4.0"/>
<release date="2019-07-03" version="0.3.8"/>
<release date="2019-04-29" version="0.3.5"/>
<release date="2019-01-13" version="0.3.4"/>

View File

@ -300,6 +300,11 @@ call append("$", "tagstack\ta :tag command will use the tagstack")
call <SID>BinOptionG("tgst", &tgst)
call append("$", "showfulltag\twhen completing tags in Insert mode show more info")
call <SID>BinOptionG("sft", &sft)
if has("eval")
call append("$", "tagfunc\ta function to be used to perform tag searches")
call append("$", "\t(local to buffer)")
call <SID>OptionL("tfu")
endif
if has("cscope")
call append("$", "cscopeprg\tcommand for executing cscope")
call <SID>OptionG("csprg", &csprg)

View File

@ -67,8 +67,8 @@ command -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-ar
command -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>)
" Name of the gdb command, defaults to "gdb".
if !exists('termdebugger')
let termdebugger = 'gdb'
if !exists('g:termdebugger')
let g:termdebugger = 'gdb'
endif
let s:pc_id = 12
@ -106,9 +106,14 @@ endfunc
func s:StartDebug_internal(dict)
if exists('s:gdbwin')
echoerr 'Terminal debugger already running'
echoerr 'Terminal debugger already running, cannot run two'
return
endif
if !executable(g:termdebugger)
echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"'
return
endif
let s:ptywin = 0
let s:pid = 0
@ -578,6 +583,7 @@ func s:HandleEvaluate(msg)
endif
let s:evalFromBalloonExprResult = split(s:evalFromBalloonExprResult, '\\n')
call s:OpenHoverPreview(s:evalFromBalloonExprResult, v:null)
let s:evalFromBalloonExprResult = ''
else
echomsg '"' . s:evalexpr . '": ' . value
endif

View File

@ -1,4 +1,4 @@
" Maintainer: Anmol Sethi <anmol@aubble.com>
" Maintainer: Anmol Sethi <hi@nhooyr.io>
if exists('g:loaded_man')
finish

View File

@ -87,19 +87,20 @@ an 50.10.580 &Syntax.AB.Awk :cal SetSyn("awk")<CR>
an 50.10.590 &Syntax.AB.AYacc :cal SetSyn("ayacc")<CR>
an 50.10.610 &Syntax.AB.B :cal SetSyn("b")<CR>
an 50.10.620 &Syntax.AB.Baan :cal SetSyn("baan")<CR>
an 50.10.630 &Syntax.AB.Basic.FreeBasic :cal SetSyn("freebasic")<CR>
an 50.10.640 &Syntax.AB.Basic.IBasic :cal SetSyn("ibasic")<CR>
an 50.10.650 &Syntax.AB.Basic.QBasic :cal SetSyn("basic")<CR>
an 50.10.660 &Syntax.AB.Basic.Visual\ Basic :cal SetSyn("vb")<CR>
an 50.10.670 &Syntax.AB.Bazaar\ commit\ file :cal SetSyn("bzr")<CR>
an 50.10.680 &Syntax.AB.Bazel :cal SetSyn("bzl")<CR>
an 50.10.690 &Syntax.AB.BC\ calculator :cal SetSyn("bc")<CR>
an 50.10.700 &Syntax.AB.BDF\ font :cal SetSyn("bdf")<CR>
an 50.10.710 &Syntax.AB.BibTeX.Bibliography\ database :cal SetSyn("bib")<CR>
an 50.10.720 &Syntax.AB.BibTeX.Bibliography\ Style :cal SetSyn("bst")<CR>
an 50.10.730 &Syntax.AB.BIND.BIND\ config :cal SetSyn("named")<CR>
an 50.10.740 &Syntax.AB.BIND.BIND\ zone :cal SetSyn("bindzone")<CR>
an 50.10.750 &Syntax.AB.Blank :cal SetSyn("blank")<CR>
an 50.10.630 &Syntax.AB.Bash :cal SetSyn("bash")<CR>
an 50.10.640 &Syntax.AB.Basic.FreeBasic :cal SetSyn("freebasic")<CR>
an 50.10.650 &Syntax.AB.Basic.IBasic :cal SetSyn("ibasic")<CR>
an 50.10.660 &Syntax.AB.Basic.QBasic :cal SetSyn("basic")<CR>
an 50.10.670 &Syntax.AB.Basic.Visual\ Basic :cal SetSyn("vb")<CR>
an 50.10.680 &Syntax.AB.Bazaar\ commit\ file :cal SetSyn("bzr")<CR>
an 50.10.690 &Syntax.AB.Bazel :cal SetSyn("bzl")<CR>
an 50.10.700 &Syntax.AB.BC\ calculator :cal SetSyn("bc")<CR>
an 50.10.710 &Syntax.AB.BDF\ font :cal SetSyn("bdf")<CR>
an 50.10.720 &Syntax.AB.BibTeX.Bibliography\ database :cal SetSyn("bib")<CR>
an 50.10.730 &Syntax.AB.BibTeX.Bibliography\ Style :cal SetSyn("bst")<CR>
an 50.10.740 &Syntax.AB.BIND.BIND\ config :cal SetSyn("named")<CR>
an 50.10.750 &Syntax.AB.BIND.BIND\ zone :cal SetSyn("bindzone")<CR>
an 50.10.760 &Syntax.AB.Blank :cal SetSyn("blank")<CR>
an 50.20.100 &Syntax.C.C :cal SetSyn("c")<CR>
an 50.20.110 &Syntax.C.C++ :cal SetSyn("cpp")<CR>
an 50.20.120 &Syntax.C.C# :cal SetSyn("cs")<CR>
@ -113,89 +114,93 @@ an 50.20.190 &Syntax.C.Century\ Term :cal SetSyn("cterm")<CR>
an 50.20.200 &Syntax.C.CH\ script :cal SetSyn("ch")<CR>
an 50.20.210 &Syntax.C.ChaiScript :cal SetSyn("chaiscript")<CR>
an 50.20.220 &Syntax.C.ChangeLog :cal SetSyn("changelog")<CR>
an 50.20.230 &Syntax.C.Cheetah\ template :cal SetSyn("cheetah")<CR>
an 50.20.240 &Syntax.C.CHILL :cal SetSyn("chill")<CR>
an 50.20.250 &Syntax.C.ChordPro :cal SetSyn("chordpro")<CR>
an 50.20.260 &Syntax.C.Clean :cal SetSyn("clean")<CR>
an 50.20.270 &Syntax.C.Clever :cal SetSyn("cl")<CR>
an 50.20.280 &Syntax.C.Clipper :cal SetSyn("clipper")<CR>
an 50.20.290 &Syntax.C.Clojure :cal SetSyn("clojure")<CR>
an 50.20.300 &Syntax.C.Cmake :cal SetSyn("cmake")<CR>
an 50.20.310 &Syntax.C.Cmod :cal SetSyn("cmod")<CR>
an 50.20.320 &Syntax.C.Cmusrc :cal SetSyn("cmusrc")<CR>
an 50.20.330 &Syntax.C.Cobol :cal SetSyn("cobol")<CR>
an 50.20.340 &Syntax.C.Coco/R :cal SetSyn("coco")<CR>
an 50.20.350 &Syntax.C.Cold\ Fusion :cal SetSyn("cf")<CR>
an 50.20.360 &Syntax.C.Conary\ Recipe :cal SetSyn("conaryrecipe")<CR>
an 50.20.370 &Syntax.C.Config.Cfg\ Config\ file :cal SetSyn("cfg")<CR>
an 50.20.380 &Syntax.C.Config.Configure\.in :cal SetSyn("config")<CR>
an 50.20.390 &Syntax.C.Config.Generic\ Config\ file :cal SetSyn("conf")<CR>
an 50.20.400 &Syntax.C.CRM114 :cal SetSyn("crm")<CR>
an 50.20.410 &Syntax.C.Crontab :cal SetSyn("crontab")<CR>
an 50.20.420 &Syntax.C.CSDL :cal SetSyn("csdl")<CR>
an 50.20.430 &Syntax.C.CSP :cal SetSyn("csp")<CR>
an 50.20.440 &Syntax.C.Ctrl-H :cal SetSyn("ctrlh")<CR>
an 50.20.450 &Syntax.C.Cucumber :cal SetSyn("cucumber")<CR>
an 50.20.460 &Syntax.C.CUDA :cal SetSyn("cuda")<CR>
an 50.20.470 &Syntax.C.CUPL.CUPL :cal SetSyn("cupl")<CR>
an 50.20.480 &Syntax.C.CUPL.Simulation :cal SetSyn("cuplsim")<CR>
an 50.20.490 &Syntax.C.CVS.commit\ file :cal SetSyn("cvs")<CR>
an 50.20.500 &Syntax.C.CVS.cvsrc :cal SetSyn("cvsrc")<CR>
an 50.20.510 &Syntax.C.Cyn++ :cal SetSyn("cynpp")<CR>
an 50.20.520 &Syntax.C.Cynlib :cal SetSyn("cynlib")<CR>
an 50.20.230 &Syntax.C.CHILL :cal SetSyn("chill")<CR>
an 50.20.240 &Syntax.C.Cheetah\ template :cal SetSyn("cheetah")<CR>
an 50.20.250 &Syntax.C.Chicken :cal SetSyn("chicken")<CR>
an 50.20.260 &Syntax.C.ChordPro :cal SetSyn("chordpro")<CR>
an 50.20.270 &Syntax.C.Clean :cal SetSyn("clean")<CR>
an 50.20.280 &Syntax.C.Clever :cal SetSyn("cl")<CR>
an 50.20.290 &Syntax.C.Clipper :cal SetSyn("clipper")<CR>
an 50.20.300 &Syntax.C.Clojure :cal SetSyn("clojure")<CR>
an 50.20.310 &Syntax.C.Cmake :cal SetSyn("cmake")<CR>
an 50.20.320 &Syntax.C.Cmod :cal SetSyn("cmod")<CR>
an 50.20.330 &Syntax.C.Cmusrc :cal SetSyn("cmusrc")<CR>
an 50.20.340 &Syntax.C.Cobol :cal SetSyn("cobol")<CR>
an 50.20.350 &Syntax.C.Coco/R :cal SetSyn("coco")<CR>
an 50.20.360 &Syntax.C.Cold\ Fusion :cal SetSyn("cf")<CR>
an 50.20.370 &Syntax.C.Conary\ Recipe :cal SetSyn("conaryrecipe")<CR>
an 50.20.380 &Syntax.C.Config.Cfg\ Config\ file :cal SetSyn("cfg")<CR>
an 50.20.390 &Syntax.C.Config.Configure\.in :cal SetSyn("config")<CR>
an 50.20.400 &Syntax.C.Config.Generic\ Config\ file :cal SetSyn("conf")<CR>
an 50.20.410 &Syntax.C.CRM114 :cal SetSyn("crm")<CR>
an 50.20.420 &Syntax.C.Crontab :cal SetSyn("crontab")<CR>
an 50.20.430 &Syntax.C.CSDL :cal SetSyn("csdl")<CR>
an 50.20.440 &Syntax.C.CSP :cal SetSyn("csp")<CR>
an 50.20.450 &Syntax.C.Ctrl-H :cal SetSyn("ctrlh")<CR>
an 50.20.460 &Syntax.C.Cucumber :cal SetSyn("cucumber")<CR>
an 50.20.470 &Syntax.C.CUDA :cal SetSyn("cuda")<CR>
an 50.20.480 &Syntax.C.CUPL.CUPL :cal SetSyn("cupl")<CR>
an 50.20.490 &Syntax.C.CUPL.Simulation :cal SetSyn("cuplsim")<CR>
an 50.20.500 &Syntax.C.CVS.commit\ file :cal SetSyn("cvs")<CR>
an 50.20.510 &Syntax.C.CVS.cvsrc :cal SetSyn("cvsrc")<CR>
an 50.20.520 &Syntax.C.Cyn++ :cal SetSyn("cynpp")<CR>
an 50.20.530 &Syntax.C.Cynlib :cal SetSyn("cynlib")<CR>
an 50.30.100 &Syntax.DE.D :cal SetSyn("d")<CR>
an 50.30.110 &Syntax.DE.Datascript :cal SetSyn("datascript")<CR>
an 50.30.120 &Syntax.DE.Debian.Debian\ ChangeLog :cal SetSyn("debchangelog")<CR>
an 50.30.130 &Syntax.DE.Debian.Debian\ Control :cal SetSyn("debcontrol")<CR>
an 50.30.140 &Syntax.DE.Debian.Debian\ Copyright :cal SetSyn("debcopyright")<CR>
an 50.30.150 &Syntax.DE.Debian.Debian\ Sources\.list :cal SetSyn("debsources")<CR>
an 50.30.160 &Syntax.DE.Denyhosts :cal SetSyn("denyhosts")<CR>
an 50.30.170 &Syntax.DE.Desktop :cal SetSyn("desktop")<CR>
an 50.30.180 &Syntax.DE.Dict\ config :cal SetSyn("dictconf")<CR>
an 50.30.190 &Syntax.DE.Dictd\ config :cal SetSyn("dictdconf")<CR>
an 50.30.200 &Syntax.DE.Diff :cal SetSyn("diff")<CR>
an 50.30.210 &Syntax.DE.Digital\ Command\ Lang :cal SetSyn("dcl")<CR>
an 50.30.220 &Syntax.DE.Dircolors :cal SetSyn("dircolors")<CR>
an 50.30.230 &Syntax.DE.Dirpager :cal SetSyn("dirpager")<CR>
an 50.30.240 &Syntax.DE.Django\ template :cal SetSyn("django")<CR>
an 50.30.250 &Syntax.DE.DNS/BIND\ zone :cal SetSyn("bindzone")<CR>
an 50.30.260 &Syntax.DE.Dnsmasq\ config :cal SetSyn("dnsmasq")<CR>
an 50.30.270 &Syntax.DE.DocBook.auto-detect :cal SetSyn("docbk")<CR>
an 50.30.280 &Syntax.DE.DocBook.SGML :cal SetSyn("docbksgml")<CR>
an 50.30.290 &Syntax.DE.DocBook.XML :cal SetSyn("docbkxml")<CR>
an 50.30.300 &Syntax.DE.Dockerfile :cal SetSyn("dockerfile")<CR>
an 50.30.310 &Syntax.DE.Dot :cal SetSyn("dot")<CR>
an 50.30.320 &Syntax.DE.Doxygen.C\ with\ doxygen :cal SetSyn("c.doxygen")<CR>
an 50.30.330 &Syntax.DE.Doxygen.C++\ with\ doxygen :cal SetSyn("cpp.doxygen")<CR>
an 50.30.340 &Syntax.DE.Doxygen.IDL\ with\ doxygen :cal SetSyn("idl.doxygen")<CR>
an 50.30.350 &Syntax.DE.Doxygen.Java\ with\ doxygen :cal SetSyn("java.doxygen")<CR>
an 50.30.360 &Syntax.DE.Doxygen.DataScript\ with\ doxygen :cal SetSyn("datascript.doxygen")<CR>
an 50.30.370 &Syntax.DE.Dracula :cal SetSyn("dracula")<CR>
an 50.30.380 &Syntax.DE.DSSSL :cal SetSyn("dsl")<CR>
an 50.30.390 &Syntax.DE.DTD :cal SetSyn("dtd")<CR>
an 50.30.400 &Syntax.DE.DTML\ (Zope) :cal SetSyn("dtml")<CR>
an 50.30.410 &Syntax.DE.DTrace :cal SetSyn("dtrace")<CR>
an 50.30.420 &Syntax.DE.Dts/dtsi :cal SetSyn("dts")<CR>
an 50.30.430 &Syntax.DE.Dylan.Dylan :cal SetSyn("dylan")<CR>
an 50.30.440 &Syntax.DE.Dylan.Dylan\ interface :cal SetSyn("dylanintr")<CR>
an 50.30.450 &Syntax.DE.Dylan.Dylan\ lid :cal SetSyn("dylanlid")<CR>
an 50.30.470 &Syntax.DE.EDIF :cal SetSyn("edif")<CR>
an 50.30.480 &Syntax.DE.Eiffel :cal SetSyn("eiffel")<CR>
an 50.30.490 &Syntax.DE.Elinks\ config :cal SetSyn("elinks")<CR>
an 50.30.500 &Syntax.DE.Elm\ filter\ rules :cal SetSyn("elmfilt")<CR>
an 50.30.510 &Syntax.DE.Embedix\ Component\ Description :cal SetSyn("ecd")<CR>
an 50.30.520 &Syntax.DE.ERicsson\ LANGuage :cal SetSyn("erlang")<CR>
an 50.30.530 &Syntax.DE.ESMTP\ rc :cal SetSyn("esmtprc")<CR>
an 50.30.540 &Syntax.DE.ESQL-C :cal SetSyn("esqlc")<CR>
an 50.30.550 &Syntax.DE.Essbase\ script :cal SetSyn("csc")<CR>
an 50.30.560 &Syntax.DE.Esterel :cal SetSyn("esterel")<CR>
an 50.30.570 &Syntax.DE.Eterm\ config :cal SetSyn("eterm")<CR>
an 50.30.580 &Syntax.DE.Euphoria\ 3 :cal SetSyn("euphoria3")<CR>
an 50.30.590 &Syntax.DE.Euphoria\ 4 :cal SetSyn("euphoria4")<CR>
an 50.30.600 &Syntax.DE.Eviews :cal SetSyn("eviews")<CR>
an 50.30.610 &Syntax.DE.Exim\ conf :cal SetSyn("exim")<CR>
an 50.30.620 &Syntax.DE.Expect :cal SetSyn("expect")<CR>
an 50.30.630 &Syntax.DE.Exports :cal SetSyn("exports")<CR>
an 50.30.110 &Syntax.DE.Dart :cal SetSyn("dart")<CR>
an 50.30.120 &Syntax.DE.Datascript :cal SetSyn("datascript")<CR>
an 50.30.130 &Syntax.DE.Debian.Debian\ ChangeLog :cal SetSyn("debchangelog")<CR>
an 50.30.140 &Syntax.DE.Debian.Debian\ Control :cal SetSyn("debcontrol")<CR>
an 50.30.150 &Syntax.DE.Debian.Debian\ Copyright :cal SetSyn("debcopyright")<CR>
an 50.30.160 &Syntax.DE.Debian.Debian\ Sources\.list :cal SetSyn("debsources")<CR>
an 50.30.170 &Syntax.DE.Denyhosts :cal SetSyn("denyhosts")<CR>
an 50.30.180 &Syntax.DE.Desktop :cal SetSyn("desktop")<CR>
an 50.30.190 &Syntax.DE.Dict\ config :cal SetSyn("dictconf")<CR>
an 50.30.200 &Syntax.DE.Dictd\ config :cal SetSyn("dictdconf")<CR>
an 50.30.210 &Syntax.DE.Diff :cal SetSyn("diff")<CR>
an 50.30.220 &Syntax.DE.Digital\ Command\ Lang :cal SetSyn("dcl")<CR>
an 50.30.230 &Syntax.DE.Dircolors :cal SetSyn("dircolors")<CR>
an 50.30.240 &Syntax.DE.Dirpager :cal SetSyn("dirpager")<CR>
an 50.30.250 &Syntax.DE.Django\ template :cal SetSyn("django")<CR>
an 50.30.260 &Syntax.DE.DNS/BIND\ zone :cal SetSyn("bindzone")<CR>
an 50.30.270 &Syntax.DE.Dnsmasq\ config :cal SetSyn("dnsmasq")<CR>
an 50.30.280 &Syntax.DE.DocBook.auto-detect :cal SetSyn("docbk")<CR>
an 50.30.290 &Syntax.DE.DocBook.SGML :cal SetSyn("docbksgml")<CR>
an 50.30.300 &Syntax.DE.DocBook.XML :cal SetSyn("docbkxml")<CR>
an 50.30.310 &Syntax.DE.Dockerfile :cal SetSyn("dockerfile")<CR>
an 50.30.320 &Syntax.DE.Dot :cal SetSyn("dot")<CR>
an 50.30.330 &Syntax.DE.Doxygen.C\ with\ doxygen :cal SetSyn("c.doxygen")<CR>
an 50.30.340 &Syntax.DE.Doxygen.C++\ with\ doxygen :cal SetSyn("cpp.doxygen")<CR>
an 50.30.350 &Syntax.DE.Doxygen.IDL\ with\ doxygen :cal SetSyn("idl.doxygen")<CR>
an 50.30.360 &Syntax.DE.Doxygen.Java\ with\ doxygen :cal SetSyn("java.doxygen")<CR>
an 50.30.370 &Syntax.DE.Doxygen.DataScript\ with\ doxygen :cal SetSyn("datascript.doxygen")<CR>
an 50.30.380 &Syntax.DE.Dracula :cal SetSyn("dracula")<CR>
an 50.30.390 &Syntax.DE.DSSSL :cal SetSyn("dsl")<CR>
an 50.30.400 &Syntax.DE.DTD :cal SetSyn("dtd")<CR>
an 50.30.410 &Syntax.DE.DTML\ (Zope) :cal SetSyn("dtml")<CR>
an 50.30.420 &Syntax.DE.DTrace :cal SetSyn("dtrace")<CR>
an 50.30.430 &Syntax.DE.Dts/dtsi :cal SetSyn("dts")<CR>
an 50.30.440 &Syntax.DE.Dune :cal SetSyn("dune")<CR>
an 50.30.450 &Syntax.DE.Dylan.Dylan :cal SetSyn("dylan")<CR>
an 50.30.460 &Syntax.DE.Dylan.Dylan\ interface :cal SetSyn("dylanintr")<CR>
an 50.30.470 &Syntax.DE.Dylan.Dylan\ lid :cal SetSyn("dylanlid")<CR>
an 50.30.490 &Syntax.DE.EDIF :cal SetSyn("edif")<CR>
an 50.30.500 &Syntax.DE.Eiffel :cal SetSyn("eiffel")<CR>
an 50.30.510 &Syntax.DE.Eight :cal SetSyn("8th")<CR>
an 50.30.520 &Syntax.DE.Elinks\ config :cal SetSyn("elinks")<CR>
an 50.30.530 &Syntax.DE.Elm\ filter\ rules :cal SetSyn("elmfilt")<CR>
an 50.30.540 &Syntax.DE.Embedix\ Component\ Description :cal SetSyn("ecd")<CR>
an 50.30.550 &Syntax.DE.ERicsson\ LANGuage :cal SetSyn("erlang")<CR>
an 50.30.560 &Syntax.DE.ESMTP\ rc :cal SetSyn("esmtprc")<CR>
an 50.30.570 &Syntax.DE.ESQL-C :cal SetSyn("esqlc")<CR>
an 50.30.580 &Syntax.DE.Essbase\ script :cal SetSyn("csc")<CR>
an 50.30.590 &Syntax.DE.Esterel :cal SetSyn("esterel")<CR>
an 50.30.600 &Syntax.DE.Eterm\ config :cal SetSyn("eterm")<CR>
an 50.30.610 &Syntax.DE.Euphoria\ 3 :cal SetSyn("euphoria3")<CR>
an 50.30.620 &Syntax.DE.Euphoria\ 4 :cal SetSyn("euphoria4")<CR>
an 50.30.630 &Syntax.DE.Eviews :cal SetSyn("eviews")<CR>
an 50.30.640 &Syntax.DE.Exim\ conf :cal SetSyn("exim")<CR>
an 50.30.650 &Syntax.DE.Expect :cal SetSyn("expect")<CR>
an 50.30.660 &Syntax.DE.Exports :cal SetSyn("exports")<CR>
an 50.40.100 &Syntax.FG.Falcon :cal SetSyn("falcon")<CR>
an 50.40.110 &Syntax.FG.Fantom :cal SetSyn("fan")<CR>
an 50.40.120 &Syntax.FG.Fetchmail :cal SetSyn("fetchmail")<CR>
@ -259,43 +264,44 @@ an 50.50.290 &Syntax.HIJK.HTML.XHTML :cal SetSyn("xhtml")<CR>
an 50.50.300 &Syntax.HIJK.Host\.conf :cal SetSyn("hostconf")<CR>
an 50.50.310 &Syntax.HIJK.Hosts\ access :cal SetSyn("hostsaccess")<CR>
an 50.50.320 &Syntax.HIJK.Hyper\ Builder :cal SetSyn("hb")<CR>
an 50.50.330 &Syntax.HIJK.Icewm\ menu :cal SetSyn("icemenu")<CR>
an 50.50.340 &Syntax.HIJK.Icon :cal SetSyn("icon")<CR>
an 50.50.350 &Syntax.HIJK.IDL\Generic\ IDL :cal SetSyn("idl")<CR>
an 50.50.360 &Syntax.HIJK.IDL\Microsoft\ IDL :cal SetSyn("msidl")<CR>
an 50.50.370 &Syntax.HIJK.Indent\ profile :cal SetSyn("indent")<CR>
an 50.50.380 &Syntax.HIJK.Inform :cal SetSyn("inform")<CR>
an 50.50.390 &Syntax.HIJK.Informix\ 4GL :cal SetSyn("fgl")<CR>
an 50.50.400 &Syntax.HIJK.Initng :cal SetSyn("initng")<CR>
an 50.50.410 &Syntax.HIJK.Inittab :cal SetSyn("inittab")<CR>
an 50.50.420 &Syntax.HIJK.Inno\ setup :cal SetSyn("iss")<CR>
an 50.50.430 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ dat :cal SetSyn("upstreamdat")<CR>
an 50.50.440 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ log :cal SetSyn("upstreamlog")<CR>
an 50.50.450 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ rpt :cal SetSyn("upstreamrpt")<CR>
an 50.50.460 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ Install\ log :cal SetSyn("upstreaminstalllog")<CR>
an 50.50.470 &Syntax.HIJK.Innovation\ Data\ Processing.Usserver\ log :cal SetSyn("usserverlog")<CR>
an 50.50.480 &Syntax.HIJK.Innovation\ Data\ Processing.USW2KAgt\ log :cal SetSyn("usw2kagtlog")<CR>
an 50.50.490 &Syntax.HIJK.InstallShield\ script :cal SetSyn("ishd")<CR>
an 50.50.500 &Syntax.HIJK.Interactive\ Data\ Lang :cal SetSyn("idlang")<CR>
an 50.50.510 &Syntax.HIJK.IPfilter :cal SetSyn("ipfilter")<CR>
an 50.50.530 &Syntax.HIJK.J :cal SetSyn("j")<CR>
an 50.50.540 &Syntax.HIJK.JAL :cal SetSyn("jal")<CR>
an 50.50.550 &Syntax.HIJK.JAM :cal SetSyn("jam")<CR>
an 50.50.560 &Syntax.HIJK.Jargon :cal SetSyn("jargon")<CR>
an 50.50.570 &Syntax.HIJK.Java.Java :cal SetSyn("java")<CR>
an 50.50.580 &Syntax.HIJK.Java.JavaCC :cal SetSyn("javacc")<CR>
an 50.50.590 &Syntax.HIJK.Java.Java\ Server\ Pages :cal SetSyn("jsp")<CR>
an 50.50.600 &Syntax.HIJK.Java.Java\ Properties :cal SetSyn("jproperties")<CR>
an 50.50.610 &Syntax.HIJK.JavaScript :cal SetSyn("javascript")<CR>
an 50.50.620 &Syntax.HIJK.Jess :cal SetSyn("jess")<CR>
an 50.50.630 &Syntax.HIJK.Jgraph :cal SetSyn("jgraph")<CR>
an 50.50.640 &Syntax.HIJK.Jovial :cal SetSyn("jovial")<CR>
an 50.50.650 &Syntax.HIJK.JSON :cal SetSyn("json")<CR>
an 50.50.670 &Syntax.HIJK.Kconfig :cal SetSyn("kconfig")<CR>
an 50.50.680 &Syntax.HIJK.KDE\ script :cal SetSyn("kscript")<CR>
an 50.50.690 &Syntax.HIJK.Kimwitu++ :cal SetSyn("kwt")<CR>
an 50.50.700 &Syntax.HIJK.Kivy :cal SetSyn("kivy")<CR>
an 50.50.710 &Syntax.HIJK.KixTart :cal SetSyn("kix")<CR>
an 50.50.340 &Syntax.HIJK.Icewm\ menu :cal SetSyn("icemenu")<CR>
an 50.50.350 &Syntax.HIJK.Icon :cal SetSyn("icon")<CR>
an 50.50.360 &Syntax.HIJK.IDL\Generic\ IDL :cal SetSyn("idl")<CR>
an 50.50.370 &Syntax.HIJK.IDL\Microsoft\ IDL :cal SetSyn("msidl")<CR>
an 50.50.380 &Syntax.HIJK.Indent\ profile :cal SetSyn("indent")<CR>
an 50.50.390 &Syntax.HIJK.Inform :cal SetSyn("inform")<CR>
an 50.50.400 &Syntax.HIJK.Informix\ 4GL :cal SetSyn("fgl")<CR>
an 50.50.410 &Syntax.HIJK.Initng :cal SetSyn("initng")<CR>
an 50.50.420 &Syntax.HIJK.Inittab :cal SetSyn("inittab")<CR>
an 50.50.430 &Syntax.HIJK.Inno\ setup :cal SetSyn("iss")<CR>
an 50.50.440 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ dat :cal SetSyn("upstreamdat")<CR>
an 50.50.450 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ log :cal SetSyn("upstreamlog")<CR>
an 50.50.460 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ rpt :cal SetSyn("upstreamrpt")<CR>
an 50.50.470 &Syntax.HIJK.Innovation\ Data\ Processing.Upstream\ Install\ log :cal SetSyn("upstreaminstalllog")<CR>
an 50.50.480 &Syntax.HIJK.Innovation\ Data\ Processing.Usserver\ log :cal SetSyn("usserverlog")<CR>
an 50.50.490 &Syntax.HIJK.Innovation\ Data\ Processing.USW2KAgt\ log :cal SetSyn("usw2kagtlog")<CR>
an 50.50.500 &Syntax.HIJK.InstallShield\ script :cal SetSyn("ishd")<CR>
an 50.50.510 &Syntax.HIJK.Interactive\ Data\ Lang :cal SetSyn("idlang")<CR>
an 50.50.520 &Syntax.HIJK.IPfilter :cal SetSyn("ipfilter")<CR>
an 50.50.540 &Syntax.HIJK.J :cal SetSyn("j")<CR>
an 50.50.550 &Syntax.HIJK.JAL :cal SetSyn("jal")<CR>
an 50.50.560 &Syntax.HIJK.JAM :cal SetSyn("jam")<CR>
an 50.50.570 &Syntax.HIJK.Jargon :cal SetSyn("jargon")<CR>
an 50.50.580 &Syntax.HIJK.Java.Java :cal SetSyn("java")<CR>
an 50.50.590 &Syntax.HIJK.Java.JavaCC :cal SetSyn("javacc")<CR>
an 50.50.600 &Syntax.HIJK.Java.Java\ Server\ Pages :cal SetSyn("jsp")<CR>
an 50.50.610 &Syntax.HIJK.Java.Java\ Properties :cal SetSyn("jproperties")<CR>
an 50.50.620 &Syntax.HIJK.JavaScript :cal SetSyn("javascript")<CR>
an 50.50.630 &Syntax.HIJK.JavaScriptReact :cal SetSyn("javascriptreact")<CR>
an 50.50.640 &Syntax.HIJK.Jess :cal SetSyn("jess")<CR>
an 50.50.650 &Syntax.HIJK.Jgraph :cal SetSyn("jgraph")<CR>
an 50.50.660 &Syntax.HIJK.Jovial :cal SetSyn("jovial")<CR>
an 50.50.670 &Syntax.HIJK.JSON :cal SetSyn("json")<CR>
an 50.50.690 &Syntax.HIJK.Kconfig :cal SetSyn("kconfig")<CR>
an 50.50.700 &Syntax.HIJK.KDE\ script :cal SetSyn("kscript")<CR>
an 50.50.710 &Syntax.HIJK.Kimwitu++ :cal SetSyn("kwt")<CR>
an 50.50.720 &Syntax.HIJK.Kivy :cal SetSyn("kivy")<CR>
an 50.50.730 &Syntax.HIJK.KixTart :cal SetSyn("kix")<CR>
an 50.60.100 &Syntax.L.Lace :cal SetSyn("lace")<CR>
an 50.60.110 &Syntax.L.LamdaProlog :cal SetSyn("lprolog")<CR>
an 50.60.120 &Syntax.L.Latte :cal SetSyn("latte")<CR>
@ -343,34 +349,35 @@ an 50.70.240 &Syntax.M.Mathematica :cal SetSyn("mma")<CR>
an 50.70.250 &Syntax.M.Matlab :cal SetSyn("matlab")<CR>
an 50.70.260 &Syntax.M.Maxima :cal SetSyn("maxima")<CR>
an 50.70.270 &Syntax.M.MEL\ (for\ Maya) :cal SetSyn("mel")<CR>
an 50.70.280 &Syntax.M.Messages\ (/var/log) :cal SetSyn("messages")<CR>
an 50.70.290 &Syntax.M.Metafont :cal SetSyn("mf")<CR>
an 50.70.300 &Syntax.M.MetaPost :cal SetSyn("mp")<CR>
an 50.70.310 &Syntax.M.MGL :cal SetSyn("mgl")<CR>
an 50.70.320 &Syntax.M.MIX :cal SetSyn("mix")<CR>
an 50.70.330 &Syntax.M.MMIX :cal SetSyn("mmix")<CR>
an 50.70.340 &Syntax.M.Modconf :cal SetSyn("modconf")<CR>
an 50.70.350 &Syntax.M.Model :cal SetSyn("model")<CR>
an 50.70.360 &Syntax.M.Modsim\ III :cal SetSyn("modsim3")<CR>
an 50.70.370 &Syntax.M.Modula\ 2 :cal SetSyn("modula2")<CR>
an 50.70.380 &Syntax.M.Modula\ 3 :cal SetSyn("modula3")<CR>
an 50.70.390 &Syntax.M.Monk :cal SetSyn("monk")<CR>
an 50.70.400 &Syntax.M.Motorola\ S-Record :cal SetSyn("srec")<CR>
an 50.70.410 &Syntax.M.Mplayer\ config :cal SetSyn("mplayerconf")<CR>
an 50.70.420 &Syntax.M.MOO :cal SetSyn("moo")<CR>
an 50.70.430 &Syntax.M.Mrxvtrc :cal SetSyn("mrxvtrc")<CR>
an 50.70.440 &Syntax.M.MS-DOS/Windows.4DOS\ \.bat\ file :cal SetSyn("btm")<CR>
an 50.70.450 &Syntax.M.MS-DOS/Windows.\.bat\/\.cmd\ file :cal SetSyn("dosbatch")<CR>
an 50.70.460 &Syntax.M.MS-DOS/Windows.\.ini\ file :cal SetSyn("dosini")<CR>
an 50.70.470 &Syntax.M.MS-DOS/Windows.Message\ text :cal SetSyn("msmessages")<CR>
an 50.70.480 &Syntax.M.MS-DOS/Windows.Module\ Definition :cal SetSyn("def")<CR>
an 50.70.490 &Syntax.M.MS-DOS/Windows.Registry :cal SetSyn("registry")<CR>
an 50.70.500 &Syntax.M.MS-DOS/Windows.Resource\ file :cal SetSyn("rc")<CR>
an 50.70.510 &Syntax.M.Msql :cal SetSyn("msql")<CR>
an 50.70.520 &Syntax.M.MuPAD :cal SetSyn("mupad")<CR>
an 50.70.530 &Syntax.M.Murphi :cal SetSyn("murphi")<CR>
an 50.70.540 &Syntax.M.MUSHcode :cal SetSyn("mush")<CR>
an 50.70.550 &Syntax.M.Muttrc :cal SetSyn("muttrc")<CR>
an 50.70.280 &Syntax.M.Meson :cal SetSyn("meson")<CR>
an 50.70.290 &Syntax.M.Messages\ (/var/log) :cal SetSyn("messages")<CR>
an 50.70.300 &Syntax.M.Metafont :cal SetSyn("mf")<CR>
an 50.70.310 &Syntax.M.MetaPost :cal SetSyn("mp")<CR>
an 50.70.320 &Syntax.M.MGL :cal SetSyn("mgl")<CR>
an 50.70.330 &Syntax.M.MIX :cal SetSyn("mix")<CR>
an 50.70.340 &Syntax.M.MMIX :cal SetSyn("mmix")<CR>
an 50.70.350 &Syntax.M.Modconf :cal SetSyn("modconf")<CR>
an 50.70.360 &Syntax.M.Model :cal SetSyn("model")<CR>
an 50.70.370 &Syntax.M.Modsim\ III :cal SetSyn("modsim3")<CR>
an 50.70.380 &Syntax.M.Modula\ 2 :cal SetSyn("modula2")<CR>
an 50.70.390 &Syntax.M.Modula\ 3 :cal SetSyn("modula3")<CR>
an 50.70.400 &Syntax.M.Monk :cal SetSyn("monk")<CR>
an 50.70.410 &Syntax.M.Motorola\ S-Record :cal SetSyn("srec")<CR>
an 50.70.420 &Syntax.M.Mplayer\ config :cal SetSyn("mplayerconf")<CR>
an 50.70.430 &Syntax.M.MOO :cal SetSyn("moo")<CR>
an 50.70.440 &Syntax.M.Mrxvtrc :cal SetSyn("mrxvtrc")<CR>
an 50.70.450 &Syntax.M.MS-DOS/Windows.4DOS\ \.bat\ file :cal SetSyn("btm")<CR>
an 50.70.460 &Syntax.M.MS-DOS/Windows.\.bat\/\.cmd\ file :cal SetSyn("dosbatch")<CR>
an 50.70.470 &Syntax.M.MS-DOS/Windows.\.ini\ file :cal SetSyn("dosini")<CR>
an 50.70.480 &Syntax.M.MS-DOS/Windows.Message\ text :cal SetSyn("msmessages")<CR>
an 50.70.490 &Syntax.M.MS-DOS/Windows.Module\ Definition :cal SetSyn("def")<CR>
an 50.70.500 &Syntax.M.MS-DOS/Windows.Registry :cal SetSyn("registry")<CR>
an 50.70.510 &Syntax.M.MS-DOS/Windows.Resource\ file :cal SetSyn("rc")<CR>
an 50.70.520 &Syntax.M.Msql :cal SetSyn("msql")<CR>
an 50.70.530 &Syntax.M.MuPAD :cal SetSyn("mupad")<CR>
an 50.70.540 &Syntax.M.Murphi :cal SetSyn("murphi")<CR>
an 50.70.550 &Syntax.M.MUSHcode :cal SetSyn("mush")<CR>
an 50.70.560 &Syntax.M.Muttrc :cal SetSyn("muttrc")<CR>
an 50.80.100 &Syntax.NO.N1QL :cal SetSyn("n1ql")<CR>
an 50.80.110 &Syntax.NO.Nanorc :cal SetSyn("nanorc")<CR>
an 50.80.120 &Syntax.NO.Nastran\ input/DMAP :cal SetSyn("nastran")<CR>
@ -442,25 +449,26 @@ an 50.100.110 &Syntax.R.R.R\ help :cal SetSyn("rhelp")<CR>
an 50.100.120 &Syntax.R.R.R\ noweb :cal SetSyn("rnoweb")<CR>
an 50.100.130 &Syntax.R.Racc\ input :cal SetSyn("racc")<CR>
an 50.100.140 &Syntax.R.Radiance :cal SetSyn("radiance")<CR>
an 50.100.150 &Syntax.R.Ratpoison :cal SetSyn("ratpoison")<CR>
an 50.100.160 &Syntax.R.RCS.RCS\ log\ output :cal SetSyn("rcslog")<CR>
an 50.100.170 &Syntax.R.RCS.RCS\ file :cal SetSyn("rcs")<CR>
an 50.100.180 &Syntax.R.Readline\ config :cal SetSyn("readline")<CR>
an 50.100.190 &Syntax.R.Rebol :cal SetSyn("rebol")<CR>
an 50.100.200 &Syntax.R.ReDIF :cal SetSyn("redif")<CR>
an 50.100.210 &Syntax.R.Relax\ NG :cal SetSyn("rng")<CR>
an 50.100.220 &Syntax.R.Remind :cal SetSyn("remind")<CR>
an 50.100.230 &Syntax.R.Relax\ NG\ compact :cal SetSyn("rnc")<CR>
an 50.100.240 &Syntax.R.Renderman.Renderman\ Shader\ Lang :cal SetSyn("sl")<CR>
an 50.100.250 &Syntax.R.Renderman.Renderman\ Interface\ Bytestream :cal SetSyn("rib")<CR>
an 50.100.260 &Syntax.R.Resolv\.conf :cal SetSyn("resolv")<CR>
an 50.100.270 &Syntax.R.Reva\ Forth :cal SetSyn("reva")<CR>
an 50.100.280 &Syntax.R.Rexx :cal SetSyn("rexx")<CR>
an 50.100.290 &Syntax.R.Robots\.txt :cal SetSyn("robots")<CR>
an 50.100.300 &Syntax.R.RockLinux\ package\ desc\. :cal SetSyn("desc")<CR>
an 50.100.310 &Syntax.R.Rpcgen :cal SetSyn("rpcgen")<CR>
an 50.100.320 &Syntax.R.RPL/2 :cal SetSyn("rpl")<CR>
an 50.100.330 &Syntax.R.ReStructuredText :cal SetSyn("rst")<CR>
an 50.100.150 &Syntax.R.Raml :cal SetSyn("raml")<CR>
an 50.100.160 &Syntax.R.Ratpoison :cal SetSyn("ratpoison")<CR>
an 50.100.170 &Syntax.R.RCS.RCS\ log\ output :cal SetSyn("rcslog")<CR>
an 50.100.180 &Syntax.R.RCS.RCS\ file :cal SetSyn("rcs")<CR>
an 50.100.190 &Syntax.R.Readline\ config :cal SetSyn("readline")<CR>
an 50.100.200 &Syntax.R.Rebol :cal SetSyn("rebol")<CR>
an 50.100.210 &Syntax.R.ReDIF :cal SetSyn("redif")<CR>
an 50.100.220 &Syntax.R.Relax\ NG :cal SetSyn("rng")<CR>
an 50.100.230 &Syntax.R.Remind :cal SetSyn("remind")<CR>
an 50.100.240 &Syntax.R.Relax\ NG\ compact :cal SetSyn("rnc")<CR>
an 50.100.250 &Syntax.R.Renderman.Renderman\ Shader\ Lang :cal SetSyn("sl")<CR>
an 50.100.260 &Syntax.R.Renderman.Renderman\ Interface\ Bytestream :cal SetSyn("rib")<CR>
an 50.100.270 &Syntax.R.Resolv\.conf :cal SetSyn("resolv")<CR>
an 50.100.280 &Syntax.R.Reva\ Forth :cal SetSyn("reva")<CR>
an 50.100.290 &Syntax.R.Rexx :cal SetSyn("rexx")<CR>
an 50.100.300 &Syntax.R.Robots\.txt :cal SetSyn("robots")<CR>
an 50.100.310 &Syntax.R.RockLinux\ package\ desc\. :cal SetSyn("desc")<CR>
an 50.100.320 &Syntax.R.Rpcgen :cal SetSyn("rpcgen")<CR>
an 50.100.330 &Syntax.R.RPL/2 :cal SetSyn("rpl")<CR>
an 50.100.340 &Syntax.R.ReStructuredText :cal SetSyn("rst")<CR>
an 50.110.100 &Syntax.M.ReStructuredText\ with\ R\ statements :cal SetSyn("rrst")<CR>
an 50.120.100 &Syntax.R.RTF :cal SetSyn("rtf")<CR>
an 50.120.110 &Syntax.R.Ruby :cal SetSyn("ruby")<CR>
@ -581,6 +589,8 @@ an 50.150.370 &Syntax.T.Trustees :cal SetSyn("trustees")<CR>
an 50.150.380 &Syntax.T.TSS.Command\ Line :cal SetSyn("tsscl")<CR>
an 50.150.390 &Syntax.T.TSS.Geometry :cal SetSyn("tssgm")<CR>
an 50.150.400 &Syntax.T.TSS.Optics :cal SetSyn("tssop")<CR>
an 50.150.410 &Syntax.T.Typescript :cal SetSyn("typescript")<CR>
an 50.150.420 &Syntax.T.TypescriptReact :cal SetSyn("typescriptreact")<CR>
an 50.160.100 &Syntax.UV.Udev\ config :cal SetSyn("udevconf")<CR>
an 50.160.110 &Syntax.UV.Udev\ permissions :cal SetSyn("udevperm")<CR>
an 50.160.120 &Syntax.UV.Udev\ rules :cal SetSyn("udevrules")<CR>
@ -607,32 +617,33 @@ an 50.160.330 &Syntax.UV.VSE\ JCL :cal SetSyn("vsejcl")<CR>
an 50.170.100 &Syntax.WXYZ.WEB.CWEB :cal SetSyn("cweb")<CR>
an 50.170.110 &Syntax.WXYZ.WEB.WEB :cal SetSyn("web")<CR>
an 50.170.120 &Syntax.WXYZ.WEB.WEB\ Changes :cal SetSyn("change")<CR>
an 50.170.130 &Syntax.WXYZ.Webmacro :cal SetSyn("webmacro")<CR>
an 50.170.140 &Syntax.WXYZ.Website\ MetaLanguage :cal SetSyn("wml")<CR>
an 50.170.160 &Syntax.WXYZ.wDiff :cal SetSyn("wdiff")<CR>
an 50.170.180 &Syntax.WXYZ.Wget\ config :cal SetSyn("wget")<CR>
an 50.170.190 &Syntax.WXYZ.Whitespace\ (add) :cal SetSyn("whitespace")<CR>
an 50.170.200 &Syntax.WXYZ.WildPackets\ EtherPeek\ Decoder :cal SetSyn("dcd")<CR>
an 50.170.210 &Syntax.WXYZ.WinBatch/Webbatch :cal SetSyn("winbatch")<CR>
an 50.170.220 &Syntax.WXYZ.Windows\ Scripting\ Host :cal SetSyn("wsh")<CR>
an 50.170.230 &Syntax.WXYZ.WSML :cal SetSyn("wsml")<CR>
an 50.170.240 &Syntax.WXYZ.WvDial :cal SetSyn("wvdial")<CR>
an 50.170.260 &Syntax.WXYZ.X\ Keyboard\ Extension :cal SetSyn("xkb")<CR>
an 50.170.270 &Syntax.WXYZ.X\ Pixmap :cal SetSyn("xpm")<CR>
an 50.170.280 &Syntax.WXYZ.X\ Pixmap\ (2) :cal SetSyn("xpm2")<CR>
an 50.170.290 &Syntax.WXYZ.X\ resources :cal SetSyn("xdefaults")<CR>
an 50.170.300 &Syntax.WXYZ.XBL :cal SetSyn("xbl")<CR>
an 50.170.310 &Syntax.WXYZ.Xinetd\.conf :cal SetSyn("xinetd")<CR>
an 50.170.320 &Syntax.WXYZ.Xmodmap :cal SetSyn("xmodmap")<CR>
an 50.170.330 &Syntax.WXYZ.Xmath :cal SetSyn("xmath")<CR>
an 50.170.340 &Syntax.WXYZ.XML :cal SetSyn("xml")<CR>
an 50.170.350 &Syntax.WXYZ.XML\ Schema\ (XSD) :cal SetSyn("xsd")<CR>
an 50.170.360 &Syntax.WXYZ.XQuery :cal SetSyn("xquery")<CR>
an 50.170.370 &Syntax.WXYZ.Xslt :cal SetSyn("xslt")<CR>
an 50.170.380 &Syntax.WXYZ.XFree86\ Config :cal SetSyn("xf86conf")<CR>
an 50.170.400 &Syntax.WXYZ.YAML :cal SetSyn("yaml")<CR>
an 50.170.410 &Syntax.WXYZ.Yacc :cal SetSyn("yacc")<CR>
an 50.170.430 &Syntax.WXYZ.Zimbu :cal SetSyn("zimbu")<CR>
an 50.170.130 &Syntax.WXYZ.WebAssembly :cal SetSyn("wast")<CR>
an 50.170.140 &Syntax.WXYZ.Webmacro :cal SetSyn("webmacro")<CR>
an 50.170.150 &Syntax.WXYZ.Website\ MetaLanguage :cal SetSyn("wml")<CR>
an 50.170.170 &Syntax.WXYZ.wDiff :cal SetSyn("wdiff")<CR>
an 50.170.190 &Syntax.WXYZ.Wget\ config :cal SetSyn("wget")<CR>
an 50.170.200 &Syntax.WXYZ.Whitespace\ (add) :cal SetSyn("whitespace")<CR>
an 50.170.210 &Syntax.WXYZ.WildPackets\ EtherPeek\ Decoder :cal SetSyn("dcd")<CR>
an 50.170.220 &Syntax.WXYZ.WinBatch/Webbatch :cal SetSyn("winbatch")<CR>
an 50.170.230 &Syntax.WXYZ.Windows\ Scripting\ Host :cal SetSyn("wsh")<CR>
an 50.170.240 &Syntax.WXYZ.WSML :cal SetSyn("wsml")<CR>
an 50.170.250 &Syntax.WXYZ.WvDial :cal SetSyn("wvdial")<CR>
an 50.170.270 &Syntax.WXYZ.X\ Keyboard\ Extension :cal SetSyn("xkb")<CR>
an 50.170.280 &Syntax.WXYZ.X\ Pixmap :cal SetSyn("xpm")<CR>
an 50.170.290 &Syntax.WXYZ.X\ Pixmap\ (2) :cal SetSyn("xpm2")<CR>
an 50.170.300 &Syntax.WXYZ.X\ resources :cal SetSyn("xdefaults")<CR>
an 50.170.310 &Syntax.WXYZ.XBL :cal SetSyn("xbl")<CR>
an 50.170.320 &Syntax.WXYZ.Xinetd\.conf :cal SetSyn("xinetd")<CR>
an 50.170.330 &Syntax.WXYZ.Xmodmap :cal SetSyn("xmodmap")<CR>
an 50.170.340 &Syntax.WXYZ.Xmath :cal SetSyn("xmath")<CR>
an 50.170.350 &Syntax.WXYZ.XML :cal SetSyn("xml")<CR>
an 50.170.360 &Syntax.WXYZ.XML\ Schema\ (XSD) :cal SetSyn("xsd")<CR>
an 50.170.370 &Syntax.WXYZ.XQuery :cal SetSyn("xquery")<CR>
an 50.170.380 &Syntax.WXYZ.Xslt :cal SetSyn("xslt")<CR>
an 50.170.390 &Syntax.WXYZ.XFree86\ Config :cal SetSyn("xf86conf")<CR>
an 50.170.410 &Syntax.WXYZ.YAML :cal SetSyn("yaml")<CR>
an 50.170.420 &Syntax.WXYZ.Yacc :cal SetSyn("yacc")<CR>
an 50.170.440 &Syntax.WXYZ.Zimbu :cal SetSyn("zimbu")<CR>
" The End Of The Syntax Menu

View File

@ -1,4 +1,4 @@
" Maintainer: Anmol Sethi <anmol@aubble.com>
" Maintainer: Anmol Sethi <hi@nhooyr.io>
" Previous Maintainer: SungHyun Nam <goweol@gmail.com>
if exists('b:current_syntax')
@ -30,6 +30,7 @@ endif
if !exists('b:man_sect')
call man#init_pager()
endif
if b:man_sect =~# '^[023]'
syntax case match
syntax include @c $VIMRUNTIME/syntax/c.vim

View File

@ -558,7 +558,7 @@ syn match vimHiGuiFontname contained "'[a-zA-Z\-* ]\+'"
syn match vimHiGuiRgb contained "#\x\{6}"
" Highlighting: hi group key=arg ... {{{2
syn cluster vimHiCluster contains=vimGroup,vimHiGroup,vimHiTerm,vimHiCTerm,vimHiStartStop,vimHiCtermFgBg,vimHiGui,vimHiGuiFont,vimHiGuiFgBg,vimHiKeyError,vimNotation
syn cluster vimHiCluster contains=vimGroup,vimHiBlend,vimHiGroup,vimHiTerm,vimHiCTerm,vimHiStartStop,vimHiCtermFgBg,vimHiGui,vimHiGuiFont,vimHiGuiFgBg,vimHiKeyError,vimNotation
syn region vimHiKeyList contained oneline start="\i\+" skip="\\\\\|\\|" end="$\||" contains=@vimHiCluster
if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_vimhikeyerror")
syn match vimHiKeyError contained "\i\+="he=e-1
@ -571,6 +571,7 @@ syn match vimHiGui contained "\cgui="he=e-1 nextgroup=vimHiAttribList
syn match vimHiGuiFont contained "\cfont="he=e-1 nextgroup=vimHiFontname
syn match vimHiGuiFgBg contained "\cgui\%([fb]g\|sp\)="he=e-1 nextgroup=vimHiGroup,vimHiGuiFontname,vimHiGuiRgb,vimFgBgAttrib
syn match vimHiTermcap contained "\S\+" contains=vimNotation
syn match vimHiBlend contained "\cblend="he=e-1 nextgroup=vimHiNmbr
syn match vimHiNmbr contained '\d\+'
" Highlight: clear {{{2
@ -850,6 +851,7 @@ if !exists("skip_vim_syntax_inits")
hi def link vimGroupSpecial Special
hi def link vimGroup Type
hi def link vimHiAttrib PreProc
hi def link vimHiBlend vimHiTerm
hi def link vimHiClear vimHighlight
hi def link vimHiCtermFgBg vimHiTerm
hi def link vimHiCTerm vimHiTerm

View File

@ -91,7 +91,7 @@ NOTE: [:q!](:q) <Enter> discards any changes you made. In a few lessons you
** Press `x`{normal} to delete the character under the cursor. **
1. Move the cursor to the line below marked --->.
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.
@ -111,7 +111,7 @@ NOTE: As you go through this tutor, do not try to memorize, learn by
** Press `i`{normal} to insert text. **
1. Move the cursor to the first line below marked --->.
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.
@ -130,7 +130,7 @@ There is some text missing from this line.
** Press `A`{normal} to append text. **
1. Move the cursor to the first line below marked --->.
1. Move the cursor to the first line below marked .
It does not matter on what character the cursor is in that line.
2. Press [A](A) and type in the necessary additions.
@ -138,7 +138,7 @@ There is some text missing from this line.
3. As the text has been appended press `<Esc>`{normal} to return to Normal
mode.
4. Move the cursor to the second line marked ---> and repeat
4. Move the cursor to the second line marked and repeat
steps 2 and 3 to correct this sentence.
There is some text missing from th
@ -211,7 +211,7 @@ Now continue with Lesson 2.
1. Press `<Esc>`{normal} to make sure you are in Normal mode.
2. Move the cursor to the line below marked --->.
2. Move the cursor to the line below marked .
3. Move the cursor to the beginning of a word that needs to be deleted.
@ -227,7 +227,7 @@ There are a some words fun that don't belong paper in this sentence.
1. Press `<Esc>`{normal} to make sure you are in Normal mode.
2. Move the cursor to the line below marked --->.
2. Move the cursor to the line below marked .
3. Move the cursor to the end of the correct line (AFTER the first . ).
@ -263,7 +263,7 @@ NOTE: Pressing just the motion while in Normal mode without an operator
** Typing a number before a motion repeats it that many times. **
1. Move the cursor to the start of the line marked ---> below.
1. Move the cursor to the start of the line marked below.
2. Type `2w`{normal} to move the cursor two words forward.
@ -285,7 +285,7 @@ In the combination of the delete operator and a motion mentioned above you
insert a count before the motion to delete more:
d number motion
1. Move the cursor to the first UPPER CASE word in the line marked --->.
1. Move the cursor to the first UPPER CASE word in the line marked .
2. Type `d2w`{normal} to delete the two UPPER CASE words
@ -318,7 +318,7 @@ 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
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.
@ -359,7 +359,7 @@ Fiix the errors oon thhis line and reeplace them witth undo.
** Type `p`{normal} to put previously deleted text after the cursor. **
1. Move the cursor to the first ---> line below.
1. Move the cursor to the first line below.
2. Type `dd`{normal} to delete the line and store it in a Vim register.
@ -378,7 +378,7 @@ a) Roses are red,
** Type `rx`{normal} to replace the character at the cursor with x. **
1. Move the cursor to the first line below marked --->.
1. Move the cursor to the first line below marked .
2. Move the cursor so that it is on top of the first error.
@ -397,7 +397,7 @@ NOTE: Remember that you should be learning by doing, not memorization.
** To change until the end of a word, type `ce`{normal}. **
1. Move the cursor to the first line below marked --->.
1. Move the cursor to the first line below marked .
2. Place the cursor on the "u" in "lubw".
@ -423,7 +423,7 @@ Notice that [c](c)e deletes the word and places you in Insert mode.
2. The motions are the same, such as `w`{normal} (word) and `$`{normal} (end of line).
3. Move to the first line below marked --->.
3. Move to the first line below marked .
4. Move the cursor to the first error.
@ -503,7 +503,7 @@ NOTE: When the search reaches the end of the file it will continue at the
** Type `%`{normal} to find a matching ),], or }. **
1. Place the cursor on any (, [, or { in the line below marked --->.
1. Place the cursor on any (, [, or { in the line below marked .
2. Now type the [%](%) character.
@ -521,7 +521,7 @@ NOTE: This is very useful in debugging a program with unmatched parentheses!
** Type `:s/old/new/g` to substitute "new" for "old". **
1. Move the cursor to the line below marked --->.
1. Move the cursor to the line below marked .
2. Type
~~~ cmd
@ -725,7 +725,7 @@ NOTE: You can also read the output of an external command. For example,
** Type `o`{normal} to open a line below the cursor and place you in Insert mode. **
1. Move the cursor to the line below marked --->.
1. Move the cursor to the line below marked .
2. Type the lowercase letter `o`{normal} to [open](o) up a line BELOW the
cursor and place you in Insert mode.
@ -743,7 +743,7 @@ Open up a line above this by typing O while the cursor is on this line.
** Type `a`{normal} to insert text AFTER the cursor. **
1. Move the cursor to the start of the line below marked --->.
1. Move the cursor to the start of the line below marked .
2. Press `e`{normal} until the cursor is on the end of "li".
@ -766,7 +766,7 @@ 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
1. Move the cursor to the first line below marked . Move the cursor to
the beginning of the first "xxx".
2. Now press `R`{normal} ([capital R](R)) and type the number below it in the
@ -787,7 +787,7 @@ NOTE: Replace mode is like Insert mode, but every typed character deletes an
** Use the `y`{normal} operator to copy text and `p`{normal} to paste it. **
1. Go to the line marked with ---> below and place the cursor after "a)".
1. Go to the line marked with below and place the cursor after "a)".
2. Start Visual mode with `v`{normal} and move the cursor to just before
"first".
@ -805,7 +805,7 @@ NOTE: Replace mode is like Insert mode, but every typed character deletes an
end of the next line with `j$`{normal} and put the text there with `p`{normal}
a) This is the first item.
b)
b)
NOTE: you can use `y`{normal} as an operator: `yw`{normal} yanks one word.

View File

@ -1,43 +1,45 @@
{
"expect": {
"24": -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.",
"145": "There is some text missing from this line.",
"146": "There is also some text missing here.",
"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.",
"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.",
"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."
}
"expect": {
"24": -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.",
"145": "There is some text missing from this line.",
"146": "There is also some text missing here.",
"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.",
"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."
}
}

View File

@ -118,7 +118,7 @@ and are hidden by default. Links to them look like
\[label\]\(\*anchor\*\)
6. Add the appropiate link:
6. Add the appropriate link:
A link to the Links section
A link to the [Links](*links*) section

View File

@ -36,11 +36,12 @@ import shutil
import textwrap
import subprocess
import collections
import msgpack
from xml.dom import minidom
if sys.version_info[0] < 3:
print("use Python 3")
if sys.version_info[0] < 3 or sys.version_info[1] < 5:
print("requires Python 3.5+")
sys.exit(1)
DEBUG = ('DEBUG' in os.environ)
@ -84,7 +85,7 @@ CONFIG = {
'append_only': [],
},
'lua': {
'filename': 'if_lua.txt',
'filename': 'lua.txt',
'section_start_token': '*lua-vim*',
'section_order': [
'vim.lua',
@ -453,7 +454,7 @@ def parse_source_xml(filename, mode):
"""
global xrefs
xrefs = set()
functions = []
functions = {} # Map of func_name:docstring.
deprecated_functions = []
dom = minidom.parse(filename)
@ -577,11 +578,13 @@ def parse_source_xml(filename, mode):
if 'Deprecated' in xrefs:
deprecated_functions.append(func_doc)
elif name.startswith(CONFIG[mode]['func_name_prefix']):
functions.append(func_doc)
functions[name] = func_doc
xrefs.clear()
return '\n\n'.join(functions), '\n\n'.join(deprecated_functions)
return ('\n\n'.join(list(functions.values())),
'\n\n'.join(deprecated_functions),
functions)
def delete_lines_below(filename, tokenstr):
@ -604,6 +607,13 @@ def gen_docs(config):
Doxygen is called and configured through stdin.
"""
for mode in CONFIG:
functions = {} # Map of func_name:docstring.
mpack_file = os.path.join(
base_dir, 'runtime', 'doc',
CONFIG[mode]['filename'].replace('.txt', '.mpack'))
if os.path.exists(mpack_file):
os.remove(mpack_file)
output_dir = out_dir.format(mode=mode)
p = subprocess.Popen(['doxygen', '-'], stdin=subprocess.PIPE)
p.communicate(
@ -645,14 +655,15 @@ def gen_docs(config):
filename = get_text(find_first(compound, 'name'))
if filename.endswith('.c') or filename.endswith('.lua'):
functions, deprecated = parse_source_xml(
os.path.join(base, '%s.xml' %
compound.getAttribute('refid')), mode)
functions_text, deprecated_text, fns = parse_source_xml(
os.path.join(base, '{}.xml'.format(
compound.getAttribute('refid'))), mode)
# Collect functions from all modules (for the current `mode`).
functions = {**functions, **fns}
if not functions and not deprecated:
if not functions_text and not deprecated_text:
continue
if functions or deprecated:
else:
name = os.path.splitext(os.path.basename(filename))[0]
if name == 'ui':
name = name.upper()
@ -665,12 +676,12 @@ def gen_docs(config):
if intro:
doc += '\n\n' + intro
if functions:
doc += '\n\n' + functions
if functions_text:
doc += '\n\n' + functions_text
if INCLUDE_DEPRECATED and deprecated:
if INCLUDE_DEPRECATED and deprecated_text:
doc += '\n\n\nDeprecated %s Functions: ~\n\n' % name
doc += deprecated
doc += deprecated_text
if doc:
filename = os.path.basename(filename)
@ -713,6 +724,8 @@ def gen_docs(config):
delete_lines_below(doc_file, CONFIG[mode]['section_start_token'])
with open(doc_file, 'ab') as fp:
fp.write(docs.encode('utf8'))
with open(mpack_file, 'wb') as fp:
fp.write(msgpack.packb(functions, use_bin_type=True))
shutil.rmtree(output_dir)

View File

@ -65,7 +65,7 @@ FILTER_PATTERNS = *.lua=lua2dox_filter
Either add them to the end or find the appropriate entry in Doxyfile.
There are other lines that you might like to alter, but see futher documentation for details.
There are other lines that you might like to alter, but see further documentation for details.
<li> When Doxyfile is edited run "doxygen"
@ -543,7 +543,6 @@ function TLua2DoX_filter.readfile(this,AppStamp,Filename)
local fn = TString_removeCommentFromLine(string_trim(string.sub(line,pos_fn+8)))
if fn_magic then
fn = fn_magic
fn_magic = nil
end
if string.sub(fn,1,1)=='(' then
@ -554,49 +553,20 @@ function TLua2DoX_filter.readfile(this,AppStamp,Filename)
-- want to fix for iffy declarations
local open_paren = string.find(fn,'[%({]')
local fn0 = fn
if open_paren then
fn0 = string.sub(fn,1,open_paren-1)
-- we might have a missing close paren
if not string.find(fn,'%)') then
fn = fn .. ' ___MissingCloseParenHere___)'
end
end
local dot = string.find(fn0,'[%.:]')
if dot then -- it's a method
local klass = string.sub(fn,1,dot-1)
local method = string.sub(fn,dot+1)
--TCore_IO_writeln('function ' .. klass .. '::' .. method .. ftail .. '{}')
--TCore_IO_writeln(klass .. '::' .. method .. ftail .. '{}')
outStream:writeln(
'/*! \\memberof ' .. klass .. ' */ '
.. method .. '{}'
)
else
-- add vanilla function
outStream:writeln(fn_type .. 'function ' .. fn .. '{}')
end
-- add vanilla function
outStream:writeln(fn_type .. 'function ' .. fn .. '{}')
end
else
this:warning(inStream:getLineNo(),'something weird here')
end
fn_magic = nil -- mustn't indavertently use it again
elseif string.find(line,'=%s*class%(') then
state = 'in_class' -- it's a class declaration
local tailComment
line,tailComment = TString_removeCommentFromLine(line)
local equals = string.find(line,'=')
local klass = string_trim(string.sub(line,1,equals-1))
local tail = string_trim(string.sub(line,equals+1))
-- class(wibble wibble)
-- ....v.
local parent = string.sub(tail,7,-2)
if #parent>0 then
parent = ' :public ' .. parent
end
outStream:writeln('class ' .. klass .. parent .. '{};')
else
state = '' -- unknown
if #line>0 then -- we don't know what this line means, so just comment it out

Some files were not shown because too many files have changed in this diff Show More