mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge remote-tracking branch 'upstream/master' into libcall
This commit is contained in:
commit
6566251d14
44
.builds/freebsd.yml
Normal file
44
.builds/freebsd.yml
Normal 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
|
@ -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
1
.gitattributes
vendored
@ -1 +1,2 @@
|
||||
*.h linguist-language=C
|
||||
src/nvim/testdir/test42.in diff
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -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
|
||||
|
||||
|
@ -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
22
.luacov
Normal 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
|
57
.travis.yml
57
.travis.yml
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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/
|
||||
|
24
Makefile
24
Makefile
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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}"
|
||||
|
53
ci/build.ps1
53
ci/build.ps1
@ -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.
|
||||
|
@ -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}"
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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 \
|
||||
|
@ -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
14
ci/snap/after_success.sh
Executable 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
21
ci/snap/deploy.sh
Executable 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
10
ci/snap/install.sh
Executable 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
8
ci/snap/script.sh
Executable 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
|
||||
|
BIN
ci/snap/travis_snapcraft.cfg
Normal file
BIN
ci/snap/travis_snapcraft.cfg
Normal file
Binary file not shown.
16
cmake/FindUTF8PROC.cmake
Normal file
16
cmake/FindUTF8PROC.cmake
Normal 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)
|
@ -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}"
|
||||
|
@ -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()
|
@ -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()
|
@ -25,3 +25,6 @@ coverage:
|
||||
changes: no
|
||||
|
||||
comment: off
|
||||
|
||||
ignore:
|
||||
- "src/tree_sitter"
|
||||
|
@ -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@
|
||||
|
@ -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.
|
||||
#
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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>"
|
||||
|
@ -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))',
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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|
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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*
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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*
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 it’s 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:
|
||||
|
@ -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
|
||||
|
@ -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
575
runtime/doc/lsp.txt
Normal 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
1001
runtime/doc/lua.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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*
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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"]
|
||||
|
@ -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*
|
||||
|
@ -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 other’s 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 other’s 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:
|
||||
|
@ -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".
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
14
runtime/indent/Makefile
Normal 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
|
@ -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.
|
||||
|
97
runtime/indent/testdir/README.txt
Normal file
97
runtime/indent/testdir/README.txt
Normal 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.
|
132
runtime/indent/testdir/runtest.vim
Normal file
132
runtime/indent/testdir/runtest.vim
Normal 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!
|
46
runtime/indent/testdir/vim.in
Normal file
46
runtime/indent/testdir/vim.in
Normal 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
|
46
runtime/indent/testdir/vim.ok
Normal file
46
runtime/indent/testdir/vim.ok
Normal 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
|
112
runtime/keymap/russian-jcukenwintype.vim
Normal file
112
runtime/keymap/russian-jcukenwintype.vim
Normal 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
|
||||
_ =
|
||||
+ \\
|
||||
\\ )
|
||||
\| (
|
@ -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
958
runtime/lua/vim/lsp.lua
Normal 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
136
runtime/lua/vim/lsp/buf.lua
Normal 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
|
223
runtime/lua/vim/lsp/callbacks.lua
Normal file
223
runtime/lua/vim/lsp/callbacks.lua
Normal 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
|
95
runtime/lua/vim/lsp/log.lua
Normal file
95
runtime/lua/vim/lsp/log.lua
Normal 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
|
922
runtime/lua/vim/lsp/protocol.lua
Normal file
922
runtime/lua/vim/lsp/protocol.lua
Normal 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
452
runtime/lua/vim/lsp/rpc.lua
Normal 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
|
727
runtime/lua/vim/lsp/util.lua
Normal file
727
runtime/lua/vim/lsp/util.lua
Normal 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
|
@ -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
|
||||
|
73
runtime/lua/vim/treesitter.lua
Normal file
73
runtime/lua/vim/treesitter.lua
Normal 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
94
runtime/lua/vim/uri.lua
Normal 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
|
@ -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
|
||||
|
@ -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"/>
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
" Maintainer: Anmol Sethi <anmol@aubble.com>
|
||||
" Maintainer: Anmol Sethi <hi@nhooyr.io>
|
||||
|
||||
if exists('g:loaded_man')
|
||||
finish
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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."
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user