aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml4
-rw-r--r--CMakeLists.txt80
-rw-r--r--CONTRIBUTING.md3
-rw-r--r--MAINTAIN.md33
-rw-r--r--Makefile30
-rw-r--r--README.md30
-rw-r--r--cmake/FindJeMalloc.cmake50
-rw-r--r--codecov.yml5
-rw-r--r--config/CMakeLists.txt4
-rw-r--r--config/config.h.in2
-rw-r--r--contrib/local.mk.example5
-rw-r--r--runtime/autoload/health.vim2
-rw-r--r--runtime/autoload/health/nvim.vim9
-rw-r--r--runtime/doc/api.txt151
-rw-r--r--runtime/doc/autocmd.txt6
-rw-r--r--runtime/doc/develop.txt14
-rw-r--r--runtime/doc/eval.txt17
-rw-r--r--runtime/doc/gui.txt31
-rw-r--r--runtime/doc/if_lua.txt287
-rw-r--r--runtime/doc/if_pyth.txt141
-rw-r--r--runtime/doc/options.txt62
-rw-r--r--runtime/doc/provider.txt4
-rw-r--r--runtime/doc/starting.txt44
-rw-r--r--runtime/doc/term.txt116
-rw-r--r--runtime/doc/ui.txt78
-rw-r--r--runtime/doc/vim_diff.txt11
-rw-r--r--runtime/doc/windows.txt1
-rw-r--r--runtime/ftplugin/man.vim26
-rw-r--r--runtime/pack/dist/opt/termdebug/plugin/termdebug.vim109
-rwxr-xr-xscripts/gen_api_vimdoc.py9
-rwxr-xr-xscripts/pvscheck.sh2
-rwxr-xr-xscripts/release.sh6
-rw-r--r--src/coverity-model.c26
-rw-r--r--src/nvim/CMakeLists.txt8
-rw-r--r--src/nvim/api/buffer.c2
-rw-r--r--src/nvim/api/private/helpers.c25
-rw-r--r--src/nvim/api/ui.c24
-rw-r--r--src/nvim/api/ui_events.in.h39
-rw-r--r--src/nvim/api/vim.c199
-rw-r--r--src/nvim/assert.h30
-rw-r--r--src/nvim/aucmd.c1
-rw-r--r--src/nvim/auevents.lua2
-rw-r--r--src/nvim/buffer.c30
-rw-r--r--src/nvim/buffer.h2
-rw-r--r--src/nvim/buffer_defs.h36
-rw-r--r--src/nvim/charset.c32
-rw-r--r--src/nvim/cursor.c8
-rw-r--r--src/nvim/diff.c2
-rw-r--r--src/nvim/edit.c65
-rw-r--r--src/nvim/eval.c53
-rw-r--r--src/nvim/eval/decode.c1
-rw-r--r--src/nvim/eval/encode.c10
-rw-r--r--src/nvim/ex_cmds.c14
-rw-r--r--src/nvim/ex_docmd.c58
-rw-r--r--src/nvim/ex_getln.c68
-rw-r--r--src/nvim/fileio.c1
-rw-r--r--src/nvim/garray.c4
-rw-r--r--src/nvim/garray.h1
-rw-r--r--src/nvim/generators/c_grammar.lua2
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua1
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua30
-rw-r--r--src/nvim/getchar.c17
-rw-r--r--src/nvim/globals.h50
-rw-r--r--src/nvim/grid_defs.h15
-rw-r--r--src/nvim/highlight.c204
-rw-r--r--src/nvim/highlight.h8
-rw-r--r--src/nvim/highlight_defs.h2
-rw-r--r--src/nvim/indent.c18
-rw-r--r--src/nvim/log.c1
-rw-r--r--src/nvim/log.h2
-rw-r--r--src/nvim/lua/executor.c2
-rw-r--r--src/nvim/macros.h21
-rw-r--r--src/nvim/main.c4
-rw-r--r--src/nvim/math.c42
-rw-r--r--src/nvim/math.h7
-rw-r--r--src/nvim/memline.c4
-rw-r--r--src/nvim/memline_defs.h71
-rw-r--r--src/nvim/memory.c20
-rw-r--r--src/nvim/menu.c55
-rw-r--r--src/nvim/message.c403
-rw-r--r--src/nvim/message.h5
-rw-r--r--src/nvim/misc1.c24
-rw-r--r--src/nvim/mouse.c73
-rw-r--r--src/nvim/move.c134
-rw-r--r--src/nvim/normal.c84
-rw-r--r--src/nvim/ops.c8
-rw-r--r--src/nvim/option.c163
-rw-r--r--src/nvim/option_defs.h5
-rw-r--r--src/nvim/options.lua30
-rw-r--r--src/nvim/os/dl.c1
-rw-r--r--src/nvim/os/input.c109
-rw-r--r--src/nvim/os/lang.c1
-rw-r--r--src/nvim/os/signal.c23
-rw-r--r--src/nvim/os_unix.c2
-rw-r--r--src/nvim/path.c13
-rw-r--r--src/nvim/popupmnu.c122
-rw-r--r--src/nvim/quickfix.c4
-rw-r--r--src/nvim/regexp.c1
-rw-r--r--src/nvim/screen.c699
-rw-r--r--src/nvim/screen.h3
-rw-r--r--src/nvim/search.c2
-rw-r--r--src/nvim/strings.c16
-rw-r--r--src/nvim/syntax.c2
-rw-r--r--src/nvim/terminal.c60
-rw-r--r--src/nvim/testdir/load.vim30
-rw-r--r--src/nvim/testdir/setup.vim3
-rw-r--r--src/nvim/testdir/test_autocmd.vim6
-rw-r--r--src/nvim/testdir/test_findfile.vim184
-rw-r--r--src/nvim/testdir/test_help_tagjump.vim4
-rw-r--r--src/nvim/testdir/test_lambda.vim16
-rw-r--r--src/nvim/testdir/test_listchars.vim32
-rw-r--r--src/nvim/testdir/test_menu.vim23
-rw-r--r--src/nvim/testdir/test_mksession.vim14
-rw-r--r--src/nvim/testdir/test_timers.vim13
-rw-r--r--src/nvim/tui/tui.c37
-rw-r--r--src/nvim/ui.c111
-rw-r--r--src/nvim/ui.h22
-rw-r--r--src/nvim/ui_bridge.c6
-rw-r--r--src/nvim/ui_compositor.c442
-rw-r--r--src/nvim/ui_compositor.h12
-rw-r--r--src/nvim/version.c6
-rw-r--r--src/nvim/window.c167
-rw-r--r--test/functional/api/vim_spec.lua88
-rw-r--r--test/functional/autocmd/signal_spec.lua38
-rw-r--r--test/functional/eval/timer_spec.lua34
-rw-r--r--test/functional/ex_cmds/menu_spec.lua65
-rw-r--r--test/functional/fixtures/tty-test.c1
-rw-r--r--test/functional/helpers.lua40
-rw-r--r--test/functional/lua/commands_spec.lua62
-rw-r--r--test/functional/options/chars_spec.lua (renamed from test/functional/options/fillchars_spec.lua)48
-rw-r--r--test/functional/options/num_options_spec.lua4
-rw-r--r--test/functional/terminal/altscreen_spec.lua2
-rw-r--r--test/functional/terminal/buffer_spec.lua2
-rw-r--r--test/functional/terminal/cursor_spec.lua2
-rw-r--r--test/functional/terminal/helpers.lua6
-rw-r--r--test/functional/terminal/highlight_spec.lua2
-rw-r--r--test/functional/terminal/mouse_spec.lua81
-rw-r--r--test/functional/terminal/scrollback_spec.lua56
-rw-r--r--test/functional/terminal/tui_spec.lua12
-rw-r--r--test/functional/terminal/window_spec.lua123
-rw-r--r--test/functional/terminal/window_split_tab_spec.lua2
-rw-r--r--test/functional/ui/inccommand_spec.lua33
-rw-r--r--test/functional/ui/messages_spec.lua632
-rw-r--r--test/functional/ui/mouse_spec.lua139
-rw-r--r--test/functional/ui/multigrid_spec.lua337
-rw-r--r--test/functional/ui/options_spec.lua3
-rw-r--r--test/functional/ui/popupmenu_spec.lua882
-rw-r--r--test/functional/ui/screen.lua57
-rw-r--r--test/functional/ui/screen_basic_spec.lua43
-rw-r--r--test/helpers.lua1
-rw-r--r--test/unit/api/helpers.lua3
-rw-r--r--third-party/CMakeLists.txt12
-rw-r--r--third-party/cmake/BuildJeMalloc.cmake24
153 files changed, 6438 insertions, 2200 deletions
diff --git a/.travis.yml b/.travis.yml
index e6b9516b6e..bcc5c11930 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -70,10 +70,10 @@ jobs:
env: BUILD_32BIT=ON
- os: osx
compiler: clang
- osx_image: xcode9.4 # macOS 10.13
+ osx_image: xcode10.1 # macOS 10.13
- os: osx
compiler: gcc
- osx_image: xcode9.4 # macOS 10.13
+ osx_image: xcode10.1 # macOS 10.13
- if: branch = master
os: linux
env: CI_TARGET=lint
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8272a1a469..988c69cd2d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -97,14 +97,22 @@ else()
option(ENABLE_LTO "enable link time optimization" ON)
endif()
-# Set default build type.
+message(STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}")
+
+# Build type.
if(NOT CMAKE_BUILD_TYPE)
- message(STATUS "CMAKE_BUILD_TYPE not given, defaulting to 'Debug'")
+ message(STATUS "CMAKE_BUILD_TYPE not specified, default is 'Debug'")
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build" FORCE)
+else()
+ message(STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
+endif()
+if(CMAKE_BUILD_TYPE MATCHES Debug)
+ set(DEBUG 1)
+else()
+ set(DEBUG 0)
endif()
-
# Set available build types for CMake GUIs.
-# A different build type can still be set by -DCMAKE_BUILD_TYPE=...
+# Other build types can still be set by -DCMAKE_BUILD_TYPE=...
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
@@ -138,12 +146,6 @@ set(NVIM_VERSION_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-# Default to -O2 on release builds.
-if(CMAKE_C_FLAGS_RELEASE MATCHES "-O3")
- message(STATUS "Replacing -O3 in CMAKE_C_FLAGS_RELEASE with -O2")
- string(REPLACE "-O3" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
-endif()
-
# 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")
@@ -155,6 +157,22 @@ 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 0 (DEBUG)")
+else()
+ if(NOT MIN_LOG_LEVEL MATCHES "^[0-3]$")
+ message(FATAL_ERROR "invalid MIN_LOG_LEVEL: " ${MIN_LOG_LEVEL})
+ endif()
+ message(STATUS "MIN_LOG_LEVEL=${MIN_LOG_LEVEL}")
+endif()
+
+# Default to -O2 on release builds.
+if(CMAKE_C_FLAGS_RELEASE MATCHES "-O3")
+ message(STATUS "Replacing -O3 in CMAKE_C_FLAGS_RELEASE with -O2")
+ string(REPLACE "-O3" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
+endif()
+
if(CMAKE_COMPILER_IS_GNUCC)
check_c_compiler_flag(-Og HAS_OG_FLAG)
else()
@@ -172,11 +190,6 @@ if(CMAKE_C_FLAGS_RELWITHDEBINFO MATCHES DNDEBUG)
string(REPLACE "-DNDEBUG" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
endif()
-# Enable -Wconversion.
-if(NOT MSVC)
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow -Wconversion")
-endif()
-
# gcc 4.0+ sets _FORTIFY_SOURCE=2 automatically. This currently
# does not work with Neovim due to some uses of dynamically-sized structures.
# https://github.com/neovim/neovim/issues/223
@@ -264,7 +277,8 @@ if(MSVC)
add_definitions(-DWIN32)
else()
add_definitions(-Wall -Wextra -pedantic -Wno-unused-parameter
- -Wstrict-prototypes -std=gnu99)
+ -Wstrict-prototypes -std=gnu99 -Wshadow -Wconversion
+ -Wmissing-prototypes)
check_c_compiler_flag(-Wimplicit-fallthrough HAS_WIMPLICIT_FALLTHROUGH_FLAG)
if(HAS_WIMPLICIT_FALLTHROUGH_FLAG)
@@ -333,12 +347,6 @@ if(TRAVIS_CI_BUILD)
endif()
endif()
-if(CMAKE_BUILD_TYPE MATCHES Debug)
- set(DEBUG 1)
-else()
- set(DEBUG 0)
-endif()
-
option(LOG_LIST_ACTIONS "Add list actions logging" OFF)
add_definitions(-DINCLUDE_GENERATED_DECLARATIONS)
@@ -456,22 +464,6 @@ if((CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN) AND NOT CMAKE_C_COMPILER_ID MA
message(FATAL_ERROR "Sanitizers are only supported for Clang")
endif()
-if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD|FreeBSD|Windows") # see #5318
- message(STATUS "skipping jemalloc on this system: ${CMAKE_SYSTEM_NAME}")
- option(ENABLE_JEMALLOC "enable jemalloc" OFF)
-else()
- option(ENABLE_JEMALLOC "enable jemalloc" ON)
-endif()
-
-if(ENABLE_JEMALLOC)
- if(CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN)
- message(STATUS "Sanitizers enabled; disabling jemalloc")
- else()
- find_package(JeMalloc REQUIRED)
- include_directories(SYSTEM ${JEMALLOC_INCLUDE_DIRS})
- endif()
-endif()
-
if(ENABLE_LIBINTL)
# LibIntl (not Intl) selects our FindLibIntl.cmake script. #8464
find_package(LibIntl REQUIRED)
@@ -545,17 +537,9 @@ install_helper(
FILES ${MANPAGES}
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
-# MIN_LOG_LEVEL for log.h
-if("${MIN_LOG_LEVEL}" MATCHES "^$")
- message(STATUS "MIN_LOG_LEVEL not specified")
-else()
- if(NOT MIN_LOG_LEVEL MATCHES "^[0-3]$")
- message(FATAL_ERROR "invalid MIN_LOG_LEVEL: " ${MIN_LOG_LEVEL})
- endif()
- message(STATUS "MIN_LOG_LEVEL set to ${MIN_LOG_LEVEL}")
-endif()
-
+#
# Go down the tree.
+#
add_subdirectory(src/nvim)
# Read compilation flags from src/nvim, used in config subdirectory below.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b7392eaf0c..630324e289 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -36,10 +36,11 @@ Reporting problems
- Check `$NVIM_LOG_FILE`, if it exists.
- Include `cmake --system-information` for build-related issues.
-Pull requests ("PRs")
+Pull requests (PRs)
---------------------
- To avoid duplicate work, create a `[WIP]` pull request as soon as possible.
+- Your PR must include **test coverage.** See [test/README.md][run-tests].
- Avoid cosmetic changes to unrelated files in the same commit.
- Use a [feature branch][git-feature-branch] instead of the master branch.
- Use a **rebase workflow** for small PRs.
diff --git a/MAINTAIN.md b/MAINTAIN.md
index ed0df76e36..55f4e7afc2 100644
--- a/MAINTAIN.md
+++ b/MAINTAIN.md
@@ -5,6 +5,15 @@ Notes on maintaining the Neovim project.
See also: https://github.com/git/git/blob/master/Documentation/howto/maintain-git.txt
+General guidelines
+------------------
+
+* Decide by cost-benefit
+* Write down what was decided
+* Constraints are good
+* Use automation to solve problems
+* Never break the API
+
Ticket Triage
-------------
@@ -19,9 +28,9 @@ The forecasting problem might be solved with an explicit priority system (like
Bram's todo.txt). Meanwhile the Neovim priority system is defined by:
- PRs nearing completion (RDY).
-- Issue labels. E.g. the +plan label increases the ticket's priority merely for
- having a plan written down: it is _closer to completion_ than tickets without
- a plan.
+- Issue labels. E.g. the `+plan` label increases the ticket's priority merely
+ for having a plan written down: it is _closer to completion_ than tickets
+ without a plan.
- Comment activity or new information.
Anything that isn't in the next milestone, and doesn't have a RDY PR ... is
@@ -32,17 +41,17 @@ full :)
Release Policy
--------------
-The goal is "early and often".
+Release "often", but not "early".
-Up to now we use only one branch, the `master` branch.
+The (unreleased) `master` branch is the "early" channel; it should not be
+released if it's not stable. Medium-risk changes may be merged to `master` if
+the next feature-release is not imminent.
-- If `master` is unstable we don't release.
-- If the last release has a major bug, we:
- 1. Fix the bug on `master`.
- 2. Disable or remove any known risks present on `master`.
- 3. Cut a release from `master`.
+For maintenance releases, create a `release-x.y` branch. If the current stable
+release has a major bug:
-This is a bit silly, but it works ok. And it keeps `master` from biting off
-more feature-creep than it can chew.
+1. Fix the bug on `master`.
+2. Cherry-pick the fix to `release-x.y`.
+3. Cut a release from `release-x.y` (run `scripts/release.sh`).
See also: https://github.com/neovim/neovim/issues/862
diff --git a/Makefile b/Makefile
index ec7f5c7bce..ba10a156b0 100644
--- a/Makefile
+++ b/Makefile
@@ -7,13 +7,19 @@ filter-true = $(strip $(filter-out 1 on ON true TRUE,$1))
CMAKE_PRG ?= $(shell (command -v cmake3 || echo cmake))
CMAKE_BUILD_TYPE ?= Debug
-
CMAKE_FLAGS := -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE)
+# Extra CMake flags which extend the default set
+CMAKE_EXTRA_FLAGS ?=
+
+# CMAKE_INSTALL_PREFIX
+# - May be passed directly or as part of CMAKE_EXTRA_FLAGS.
+# - `checkprefix` target checks that it matches the CMake-cached value. #9615
+CMAKE_INSTALL_PREFIX ?= $(shell echo $(CMAKE_EXTRA_FLAGS) | 2>/dev/null \
+ grep -o 'CMAKE_INSTALL_PREFIX=[^ ]\+' | cut -d '=' -f2)
BUILD_TYPE ?= $(shell (type ninja > /dev/null 2>&1 && echo "Ninja") || \
echo "Unix Makefiles")
DEPS_BUILD_DIR ?= .deps
-
ifneq (1,$(words [$(DEPS_BUILD_DIR)]))
$(error DEPS_BUILD_DIR must not contain whitespace)
endif
@@ -41,8 +47,6 @@ endif
BUILD_CMD = $(BUILD_TOOL) $(VERBOSE_FLAG)
-# Extra CMake flags which extend the default set
-CMAKE_EXTRA_FLAGS ?=
DEPS_CMAKE_FLAGS ?=
# Back-compat: USE_BUNDLED_DEPS was the old name.
USE_BUNDLED ?= $(USE_BUNDLED_DEPS)
@@ -62,7 +66,7 @@ SINGLE_MAKE = export MAKEFLAGS= ; $(MAKE)
all: nvim
-nvim: build/.ran-cmake deps
+nvim: checkprefix build/.ran-cmake deps
+$(BUILD_CMD) -C build
libnvim: build/.ran-cmake deps
@@ -153,11 +157,21 @@ generated-sources: build/.ran-cmake
appimage:
bash scripts/genappimage.sh
-# Build an appimage with embedded update information appimage-nightly for
-# nightly builds or appimage-latest for a release
+# Build an appimage with embedded update information.
+# appimage-nightly: for nightly builds
+# appimage-latest: for a release
appimage-%:
bash scripts/genappimage.sh $*
lint: check-single-includes clint testlint lualint
-.PHONY: test testlint lualint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage
+checkprefix:
+ @cached_prefix=$$($(CMAKE_PRG) -L -N build | 2>/dev/null grep 'CMAKE_INSTALL_PREFIX' | cut -d '=' -f2); \
+ if [ -n "$(CMAKE_INSTALL_PREFIX)" ] && ! [ "$(CMAKE_INSTALL_PREFIX)" = "$$cached_prefix" ]; then \
+ printf "\nerror: CMAKE_INSTALL_PREFIX '$(CMAKE_INSTALL_PREFIX)' does not match cached value '%s'\n" "$$cached_prefix"; \
+ printf " Run this command, then try again:\n"; \
+ printf " cmake build -DCMAKE_INSTALL_PREFIX=$(CMAKE_INSTALL_PREFIX)\n"; \
+ exit 1; \
+ fi;
+
+.PHONY: test testlint lualint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage checkprefix
diff --git a/README.md b/README.md
index 39d809002e..4979ab53ef 100644
--- a/README.md
+++ b/README.md
@@ -33,9 +33,10 @@ Features
--------
- Modern [GUIs](https://github.com/neovim/neovim/wiki/Related-projects#gui)
-- [API](https://github.com/neovim/neovim/wiki/Related-projects#api-clients)
- access from any language including Clojure, Lisp, Go, Haskell, Lua,
- JavaScript, Perl, Python, Ruby, and Rust
+- [API access](https://github.com/neovim/neovim/wiki/Related-projects#api-clients)
+ from any language including C/C++, C#, Clojure, D, Elixir, Lisp, Go,
+ Haskell, Java, JavaScript/Node.js, Julia, Lisp, Lua, Perl, Python, Racket,
+ Ruby, Rust
- Embedded, scriptable [terminal emulator](https://neovim.io/doc/user/nvim_terminal_emulator.html)
- Asynchronous [job control](https://github.com/neovim/neovim/pull/2247)
- [Shared data (shada)](https://github.com/neovim/neovim/pull/2506) among multiple editor instances
@@ -56,19 +57,21 @@ and [more](https://github.com/neovim/neovim/wiki/Installing-Neovim)!
Install from source
-------------------
+The build is CMake-based, but a Makefile is provided as a convenience.
+
make CMAKE_BUILD_TYPE=RelWithDebInfo
sudo make install
-To install to a non-default location, set `CMAKE_INSTALL_PREFIX`:
+To install to a non-default location:
- make CMAKE_EXTRA_FLAGS="-DCMAKE_INSTALL_PREFIX=/full/path/"
+ make CMAKE_INSTALL_PREFIX=/full/path/
make install
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 libjemalloc-dev
+ sudo apt install gperf luajit luarocks libuv1-dev libluajit-5.1-dev libunibilium-dev libmsgpack-dev libtermkey-dev libvterm-dev
sudo luarocks build mpack
sudo luarocks build lpeg
sudo luarocks build inspect
@@ -79,17 +82,10 @@ To skip bundled (`third-party/*`) dependencies:
sudo make install
```
-CMake features:
-
-- List all build targets:
- ```
- cmake --build build --target help
- ```
-- Print all variable definitions:
- ```
- cmake -LAH
- ```
-- `build/CMakeCache.txt` contains the resolved values of all CMake variables.
+To inspect the build, these CMake features are useful:
+
+- `cmake --build build --target help` lists all build targets.
+- `build/CMakeCache.txt` (or `cmake -LAH build/`) contains the resolved values of all CMake variables.
- `build/compile_commands.json` shows the full compiler invocations for each translation unit.
See the [Building Neovim](https://github.com/neovim/neovim/wiki/Building-Neovim) wiki page for details.
diff --git a/cmake/FindJeMalloc.cmake b/cmake/FindJeMalloc.cmake
deleted file mode 100644
index f139196a38..0000000000
--- a/cmake/FindJeMalloc.cmake
+++ /dev/null
@@ -1,50 +0,0 @@
-# - Try to find jemalloc
-# Once done this will define
-# JEMALLOC_FOUND - System has jemalloc
-# JEMALLOC_INCLUDE_DIRS - The jemalloc include directories
-# JEMALLOC_LIBRARIES - The libraries needed to use jemalloc
-
-if(NOT USE_BUNDLED_JEMALLOC)
- find_package(PkgConfig)
- if (PKG_CONFIG_FOUND)
- pkg_check_modules(PC_JEMALLOC QUIET jemalloc)
- endif()
-else()
- set(PC_JEMALLOC_INCLUDEDIR)
- set(PC_JEMALLOC_INCLUDE_DIRS)
- set(PC_JEMALLOC_LIBDIR)
- set(PC_JEMALLOC_LIBRARY_DIRS)
- set(LIMIT_SEARCH NO_DEFAULT_PATH)
-endif()
-
-set(JEMALLOC_DEFINITIONS ${PC_JEMALLOC_CFLAGS_OTHER})
-
-find_path(JEMALLOC_INCLUDE_DIR jemalloc/jemalloc.h
- PATHS ${PC_JEMALLOC_INCLUDEDIR} ${PC_JEMALLOC_INCLUDE_DIRS}
- ${LIMIT_SEARCH})
-
-# If we're asked to use static linkage, add libjemalloc.a as a preferred library name.
-if(JEMALLOC_USE_STATIC)
- list(APPEND JEMALLOC_NAMES
- "${CMAKE_STATIC_LIBRARY_PREFIX}jemalloc${CMAKE_STATIC_LIBRARY_SUFFIX}")
-elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
- list(INSERT JEMALLOC_NAMES 0
- "${CMAKE_STATIC_LIBRARY_PREFIX}jemalloc${CMAKE_STATIC_LIBRARY_SUFFIX}")
-endif()
-
-list(APPEND JEMALLOC_NAMES jemalloc)
-
-find_library(JEMALLOC_LIBRARY NAMES ${JEMALLOC_NAMES}
- HINTS ${PC_JEMALLOC_LIBDIR} ${PC_JEMALLOC_LIBRARY_DIRS}
- ${LIMIT_SEARCH})
-
-set(JEMALLOC_LIBRARIES ${JEMALLOC_LIBRARY})
-set(JEMALLOC_INCLUDE_DIRS ${JEMALLOC_INCLUDE_DIR})
-
-include(FindPackageHandleStandardArgs)
-# handle the QUIETLY and REQUIRED arguments and set JEMALLOC_FOUND to TRUE
-# if all listed variables are TRUE
-find_package_handle_standard_args(JeMalloc DEFAULT_MSG
- JEMALLOC_LIBRARY JEMALLOC_INCLUDE_DIR)
-
-mark_as_advanced(JEMALLOC_INCLUDE_DIR JEMALLOC_LIBRARY)
diff --git a/codecov.yml b/codecov.yml
index 8ce9a2bb38..5acc64f756 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -1,3 +1,6 @@
+# To validate:
+# cat codecov.yml | curl --data-binary @- https://codecov.io/validate
+
codecov:
notify:
require_ci_to_pass: no
@@ -12,7 +15,7 @@ coverage:
range: "70...100"
status:
- project: yes
+ project:
default:
threshold: 1
patch:
diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt
index 63cb3cc0d6..442d91524b 100644
--- a/config/CMakeLists.txt
+++ b/config/CMakeLists.txt
@@ -47,10 +47,6 @@ if(Iconv_FOUND)
set(HAVE_ICONV 1)
endif()
-if(JEMALLOC_FOUND)
- set(HAVE_JEMALLOC 1)
-endif()
-
check_function_exists(_putenv_s HAVE_PUTENV_S)
if(WIN32 AND NOT HAVE_PUTENV_S)
message(SEND_ERROR "_putenv_s() function not found on your system.")
diff --git a/config/config.h.in b/config/config.h.in
index 106013425d..56d46e9f14 100644
--- a/config/config.h.in
+++ b/config/config.h.in
@@ -62,7 +62,6 @@
#cmakedefine FEAT_TUI
#ifndef UNIT_TESTING
-#cmakedefine HAVE_JEMALLOC
#cmakedefine LOG_LIST_ACTIONS
#endif
@@ -71,5 +70,6 @@
#define ENDIAN_INCLUDE_FILE <@ENDIAN_INCLUDE_FILE@>
#cmakedefine HAVE_EXECINFO_BACKTRACE
+#cmakedefine HAVE_BUILTIN_ADD_OVERFLOW
#endif // AUTO_CONFIG_H
diff --git a/contrib/local.mk.example b/contrib/local.mk.example
index c347eb9e0d..52a5505399 100644
--- a/contrib/local.mk.example
+++ b/contrib/local.mk.example
@@ -7,10 +7,6 @@
# These CFLAGS can be used in addition to those specified in CMakeLists.txt:
# CMAKE_EXTRA_FLAGS="-DCMAKE_C_FLAGS=-ftrapv -Wlogical-op"
-# By default, the jemalloc family of memory allocation functions are used.
-# Uncomment the following to instead use libc memory allocation functions.
-# CMAKE_EXTRA_FLAGS += -DENABLE_JEMALLOC=OFF
-
# Sets the build type; defaults to Debug. Valid values:
#
# - Debug: Disables optimizations (-O0), enables debug information.
@@ -36,7 +32,6 @@
# them.
#
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_BUSTED=OFF
-# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_JEMALLOC=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LIBTERMKEY=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LIBUV=OFF
# DEPS_CMAKE_FLAGS += -DUSE_BUNDLED_LIBVTERM=OFF
diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim
index 56ae2071e9..0f7983f175 100644
--- a/runtime/autoload/health.vim
+++ b/runtime/autoload/health.vim
@@ -30,7 +30,7 @@ function! health#check(plugin_names) abort
\ : s:to_fn_names(a:plugin_names)
tabnew
- setlocal wrap breakindent
+ setlocal wrap breakindent linebreak
setlocal filetype=markdown
setlocal conceallevel=2 concealcursor=nc
setlocal keywordprg=:help
diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim
index 93660d05dc..efa3292801 100644
--- a/runtime/autoload/health/nvim.vim
+++ b/runtime/autoload/health/nvim.vim
@@ -25,6 +25,15 @@ function! s:check_config() abort
\ 'https://github.com/neovim/neovim/wiki/Following-HEAD#20170402' ])
endif
+ if v:ctype ==# 'C'
+ let ok = v:false
+ call health#report_error('Locale does not support UTF-8. Unicode characters may not display correctly.'
+ \ .printf("\n$LANG=%s $LC_ALL=%s $LC_CTYPE=%s", $LANG, $LC_ALL, $LC_CTYPE),
+ \ [ 'If using tmux, try the -u option.',
+ \ 'Ensure that your terminal/shell/tmux/etc inherits the environment, or set $LANG explicitly.' ,
+ \ 'Configure your system locale.' ])
+ endif
+
if &paste
let ok = v:false
call health#report_error("'paste' is enabled. This option is only for pasting text.\nIt should not be set in your config.",
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 2520a15890..c9d526d9aa 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -40,9 +40,9 @@ API metadata *api-metadata*
Nvim exposes API metadata as a Dictionary. Some items are described below:
version Nvim version, API level/compatibility
-version.api_level Current API level
+version.api_level API version integer *api-level*
version.api_compatible API is backwards-compatible with this level
-version.api_prerelease Declares the current API level as unstable >
+version.api_prerelease Declares the API as unstable/unreleased >
(version.api_prerelease && fn.since == version.api_level)
functions API function signatures
ui_events UI event signatures |ui|
@@ -164,7 +164,7 @@ nvim_buf_detach_event[{buf}] *nvim_buf_detach_event*
EXAMPLE ~
Calling |nvim_buf_attach()| with send_buffer=true on an empty buffer, emits: >
- nvim_buf_lines_event[{buf}, {changedtick}, 0, 0, [""], v:false]
+ nvim_buf_lines_event[{buf}, {changedtick}, 0, -1, [""], v:false]
User adds two lines to the buffer, emits: >
nvim_buf_lines_event[{buf}, {changedtick}, 0, 0, ["line1", "line2"], v:false]
@@ -289,6 +289,11 @@ nvim_input({keys}) *nvim_input()*
|keycodes| like <CR> are translated, so "<" is special. To
input a literal "<", send <LT>.
+ Note:
+ For mouse events use |nvim_input_mouse()|. The pseudokey
+ form "<LeftMouse><col,row>" is deprecated since
+ |api-level| 6.
+
Attributes: ~
{async}
@@ -299,6 +304,43 @@ nvim_input({keys}) *nvim_input()*
Number of bytes actually written (can be fewer than
requested if the buffer becomes full).
+ *nvim_input_mouse()*
+nvim_input_mouse({button}, {action}, {modifier}, {grid}, {row}, {col})
+ Send mouse event from GUI.
+
+ The call is non-blocking. It doesn't wait on any resulting
+ action, but queues the event to be processed soon by the event
+ loop.
+
+ Note:
+ Currently this doesn't support "scripting" multiple mouse
+ events by calling it multiple times in a loop: the
+ intermediate mouse positions will be ignored. It should be
+ used to implement real-time mouse input in a GUI. The
+ deprecated pseudokey form ("<LeftMouse><col,row>") of
+ |nvim_input()| has the same limitiation.
+
+ Attributes: ~
+ {async}
+
+ Parameters: ~
+ {button} Mouse button: one of "left", "right",
+ "middle", "wheel".
+ {action} For ordinary buttons, one of "press", "drag",
+ "release". For the wheel, one of "up", "down",
+ "left", "right".
+ {modifier} String of modifiers each represented by a
+ single char. The same specifiers are used as
+ for a key press, except that the "-" separator
+ is optional, so "C-A-", "c-a" and "CA" can all
+ be used to specify Ctrl+Alt+click.
+ {grid} Grid number if the client uses |ui-multigrid|,
+ else 0.
+ {row} Mouse row-position (zero-based, like redraw
+ events)
+ {col} Mouse column-position (zero-based, like redraw
+ events)
+
*nvim_replace_termcodes()*
nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a
@@ -400,7 +442,7 @@ nvim_set_current_dir({dir}) *nvim_set_current_dir()*
{dir} Directory path
nvim_get_current_line() *nvim_get_current_line()*
- Gets the current line
+ Gets the current line.
Parameters: ~
@@ -408,18 +450,18 @@ nvim_get_current_line() *nvim_get_current_line()*
Current line string
nvim_set_current_line({line}) *nvim_set_current_line()*
- Sets the current line
+ Sets the current line.
Parameters: ~
{line} Line contents
nvim_del_current_line() *nvim_del_current_line()*
- Deletes the current line
+ Deletes the current line.
Parameters: ~
nvim_get_var({name}) *nvim_get_var()*
- Gets a global (g:) variable
+ Gets a global (g:) variable.
Parameters: ~
{name} Variable name
@@ -428,20 +470,20 @@ nvim_get_var({name}) *nvim_get_var()*
Variable value
nvim_set_var({name}, {value}) *nvim_set_var()*
- Sets a global (g:) variable
+ Sets a global (g:) variable.
Parameters: ~
{name} Variable name
{value} Variable value
nvim_del_var({name}) *nvim_del_var()*
- Removes a global (g:) variable
+ Removes a global (g:) variable.
Parameters: ~
{name} Variable name
nvim_get_vvar({name}) *nvim_get_vvar()*
- Gets a v: variable
+ Gets a v: variable.
Parameters: ~
{name} Variable name
@@ -449,8 +491,15 @@ nvim_get_vvar({name}) *nvim_get_vvar()*
Return: ~
Variable value
+nvim_set_vvar({name}, {value}) *nvim_set_vvar()*
+ Sets a v: variable, if it is not readonly.
+
+ Parameters: ~
+ {name} Variable name
+ {value} Variable value
+
nvim_get_option({name}) *nvim_get_option()*
- Gets an option value string
+ Gets an option value string.
Parameters: ~
{name} Option name
@@ -459,7 +508,7 @@ nvim_get_option({name}) *nvim_get_option()*
Option value (global)
nvim_set_option({name}, {value}) *nvim_set_option()*
- Sets an option value
+ Sets an option value.
Parameters: ~
{name} Option name
@@ -498,55 +547,55 @@ nvim_list_bufs() *nvim_list_bufs()*
List of buffer handles
nvim_get_current_buf() *nvim_get_current_buf()*
- Gets the current buffer
+ Gets the current buffer.
Return: ~
Buffer handle
nvim_set_current_buf({buffer}) *nvim_set_current_buf()*
- Sets the current buffer
+ Sets the current buffer.
Parameters: ~
{buffer} Buffer handle
nvim_list_wins() *nvim_list_wins()*
- Gets the current list of window handles
+ Gets the current list of window handles.
Return: ~
List of window handles
nvim_get_current_win() *nvim_get_current_win()*
- Gets the current window
+ Gets the current window.
Return: ~
Window handle
nvim_set_current_win({window}) *nvim_set_current_win()*
- Sets the current window
+ Sets the current window.
Parameters: ~
{window} Window handle
nvim_list_tabpages() *nvim_list_tabpages()*
- Gets the current list of tabpage handles
+ Gets the current list of tabpage handles.
Return: ~
List of tabpage handles
nvim_get_current_tabpage() *nvim_get_current_tabpage()*
- Gets the current tabpage
+ Gets the current tabpage.
Return: ~
Tabpage handle
nvim_set_current_tabpage({tabpage}) *nvim_set_current_tabpage()*
- Sets the current tabpage
+ Sets the current tabpage.
Parameters: ~
{tabpage} Tabpage handle
nvim_create_namespace({name}) *nvim_create_namespace()*
- Creates a new namespace, or gets an existing one
+ Creates a new namespace, or gets an existing one.
Namespaces are used for buffer highlights and virtual text,
see |nvim_buf_add_highlight()| and
@@ -563,19 +612,19 @@ nvim_create_namespace({name}) *nvim_create_namespace()*
Namespace id
nvim_get_namespaces() *nvim_get_namespaces()*
- Gets existing, non-anonymous namespaces
+ Gets existing, non-anonymous namespaces.
Return: ~
dict that maps from names to namespace ids.
nvim_subscribe({event}) *nvim_subscribe()*
- Subscribes to event broadcasts
+ Subscribes to event broadcasts.
Parameters: ~
{event} Event type string
nvim_unsubscribe({event}) *nvim_unsubscribe()*
- Unsubscribes to event broadcasts
+ Unsubscribes to event broadcasts.
Parameters: ~
{event} Event type string
@@ -724,7 +773,7 @@ nvim_call_atomic({calls}) *nvim_call_atomic()*
*nvim_parse_expression()*
nvim_parse_expression({expr}, {flags}, {highlight})
- Parse a VimL expression
+ Parse a VimL expression.
Attributes: ~
{async}
@@ -801,7 +850,7 @@ nvim_parse_expression({expr}, {flags}, {highlight})
"SingleQuotedString" and "DoubleQuotedString" nodes.
nvim__id({obj}) *nvim__id()*
- Returns object given as argument
+ Returns object given as argument.
This API function is used for testing. One should not rely on
its presence in plugins.
@@ -813,7 +862,7 @@ nvim__id({obj}) *nvim__id()*
its argument.
nvim__id_array({arr}) *nvim__id_array()*
- Returns array given as argument
+ Returns array given as argument.
This API function is used for testing. One should not rely on
its presence in plugins.
@@ -825,7 +874,7 @@ nvim__id_array({arr}) *nvim__id_array()*
its argument.
nvim__id_dictionary({dct}) *nvim__id_dictionary()*
- Returns dictionary given as argument
+ Returns dictionary given as argument.
This API function is used for testing. One should not rely on
its presence in plugins.
@@ -837,7 +886,7 @@ nvim__id_dictionary({dct}) *nvim__id_dictionary()*
its argument.
nvim__id_float({flt}) *nvim__id_float()*
- Returns floating-point value given as argument
+ Returns floating-point value given as argument.
This API function is used for testing. One should not rely on
its presence in plugins.
@@ -874,6 +923,26 @@ nvim_get_proc({pid}) *nvim_get_proc()*
Return: ~
Map of process properties, or NIL if process not found.
+ *nvim_select_popupmenu_item()*
+nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts})
+ Selects an item in the completion popupmenu.
+
+ If |ins-completion| is not active this API call is silently
+ ignored. Useful for an external UI using |ui-popupmenu| to
+ control the popupmenu with the mouse. Can also be used in a
+ mapping; use <cmd> |:map-cmd| to ensure the mapping doesn't
+ end completion mode.
+
+ Parameters: ~
+ {item} Index (zero-based) of the item to select. Value
+ of -1 selects nothing and restores the original
+ text.
+ {insert} Whether the selection should be inserted in the
+ buffer.
+ {finish} Finish the completion and dismiss the popupmenu.
+ Implies `insert`.
+ {opts} Optional parameters. Reserved for future use.
+
nvim__inspect_cell({row}, {col}) *nvim__inspect_cell()*
TODO: Documentation
@@ -914,7 +983,8 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
`nvim_buf_lines_event`. Otherwise, the
first notification will be a
`nvim_buf_changedtick_event`
- {opts} Optional parameters. Currently not used.
+ {opts} Optional parameters. Reserved for future
+ use.
Return: ~
False when updates couldn't be enabled because the buffer
@@ -1184,8 +1254,8 @@ nvim_buf_set_virtual_text({buffer}, {ns_id}, {line}, {chunks}, {opts})
By default (and currently the only option) the text will be
placed after the buffer text. Virtual text will never cause
reflow, rather virtual text will be truncated at the end of
- the screen line. The virtual text will begin one cell (|lcs-
- eol| or space) after the ordinary text.
+ the screen line. The virtual text will begin one cell
+ (|lcs-eol| or space) after the ordinary text.
Namespaces are used to support batch deletion/updating of
virtual text. To create a namespace, use
@@ -1204,8 +1274,8 @@ nvim_buf_set_virtual_text({buffer}, {ns_id}, {line}, {chunks}, {opts})
{buffer} Buffer handle
{ns_id} Namespace to use or 0 to create a namespace, or
-1 for a ungrouped annotation
- {line} Line to annotate with virtual text (zero-
- indexed)
+ {line} Line to annotate with virtual text
+ (zero-indexed)
{chunks} A list of [text, hl_group] arrays, each
representing a text chunk with specified
highlight. `hl_group` element can be omitted for
@@ -1447,4 +1517,17 @@ nvim_ui_try_resize({width}, {height}) *nvim_ui_try_resize()*
nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()*
TODO: Documentation
+ *nvim_ui_try_resize_grid()*
+nvim_ui_try_resize_grid({grid}, {width}, {height})
+ Tell Nvim to resize a grid. Triggers a grid_resize event with
+ the requested grid size or the maximum size if it exceeds size
+ limits.
+
+ On invalid grid handle, fails with error.
+
+ Parameters: ~
+ {grid} The handle of the grid to be changed.
+ {width} The new requested width.
+ {height} The new requested height.
+
vim:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index d7b74f99c2..6423939b83 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -353,6 +353,8 @@ Name triggered by ~
|CompleteDone| after Insert mode completion is done
|User| to be used in combination with ":doautocmd"
+|Signal| after the nvim process received a signal
+
The alphabetical list of autocommand events: *autocmd-events-abc*
@@ -914,6 +916,10 @@ ShellCmdPost After executing a shell command with |:!cmd|,
any changed files.
For non-blocking shell commands, see
|job-control|.
+ *Signal*
+Signal After the nvim process received a signal.
+ The pattern is matched against the name of the
+ received signal. Only "SIGUSR1" is supported.
*ShellFilterPost*
ShellFilterPost After executing a shell command with
":{range}!cmd", ":w !cmd" or ":r !cmd".
diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt
index e244072c66..4b1cbe2cce 100644
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -4,7 +4,7 @@
NVIM REFERENCE MANUAL
-Development of Nvim *development*
+Development of Nvim *development* *dev*
This reference describes design constraints and guidelines, for developing
Nvim applications or Nvim itself.
@@ -79,7 +79,7 @@ include the kitchen sink... but it's good for plumbing."
==============================================================================
-Developer guidelines *dev*
+Developer guidelines *dev-guidelines*
PROVIDERS *dev-provider*
@@ -209,10 +209,12 @@ Examples of API-client package names:
Implementation ~
-Consider using libmpack instead of the msgpack.org C/C++ library. libmpack is
-small (can be inlined into your C/C++ project) and efficient (no allocations).
-It also implements msgpack-RPC.
-https://github.com/libmpack/libmpack/
+For C/C++ projects, consider libmpack instead of the msgpack.org library.
+ https://github.com/libmpack/libmpack/
+libmpack is small (no dependencies, can inline into your C/C++ project) and
+efficient (no allocations). It also implements msgpack-RPC, the protocol
+required by Nvim.
+ https://github.com/msgpack-rpc/msgpack-rpc
EXTERNAL UI *dev-ui*
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 425dcede8b..5dc282f6ba 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -5666,23 +5666,24 @@ max({expr}) Return the maximum value of all items in {expr}.
menu_get({path}, {modes}) *menu_get()*
Returns a |List| of |Dictionaries| describing |menus| (defined
- by |:menu|, |:amenu|, etc.).
- {path} limits the result to a subtree of the menu hierarchy
- (empty string matches all menus). E.g. to get items in the
- "File" menu subtree: >
+ by |:menu|, |:amenu|, …), including |hidden-menus|.
+
+ {path} matches a menu by name, or all menus if {path} is an
+ empty string. Example: >
:echo menu_get('File','')
+ :echo menu_get('')
<
{modes} is a string of zero or more modes (see |maparg()| or
|creating-menus| for the list of modes). "a" means "all".
- For example: >
+ Example: >
nnoremenu &Test.Test inormal
inoremenu Test.Test insert
vnoremenu Test.Test x
echo menu_get("")
-<
- returns something like this:
->
+
+< returns something like this: >
+
[ {
"hidden": 0,
"name": "Test",
diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt
index 06609a77e1..f8c7693d45 100644
--- a/runtime/doc/gui.txt
+++ b/runtime/doc/gui.txt
@@ -29,25 +29,6 @@ When the GUI starts up initializations are carried out, in this order:
The path names are truncated to 35 characters. You can truncate them at a
different length, for example 50, like this: >
:let bmenu_max_pathlen = 50
-- If the "-U {gvimrc}" command-line option has been used when starting Vim,
- the {gvimrc} file will be read for initializations. The following
- initializations are skipped. When {gvimrc} is "NONE" no file will be read
- for initializations.
-- For Unix and MS-Windows, if the system gvimrc exists, it is sourced. The
- name of this file is normally "$VIM/ginit.vim". You can check this with
- ":version". Also see |$VIM|.
-- The following are tried, and only the first one that exists is used:
- - If the GVIMINIT environment variable exists and is not empty, it is
- executed as an Ex command.
- - If the user gvimrc file exists, it is sourced. The name of this file is
- normally "$XDG_CONFIG_HOME/nvim/ginit.vim" ($XDG_CONFIG_HOME defaults to
- ~/.config).
- The name of the first file found is stored in $MYGVIMRC, unless it was
- already set.
-
-NOTE: All but the first one are not carried out if Vim was started with
-"-u NONE" or "-u DEFAULTS" and no "-U" argument was given, or when started
-with "-U NONE".
All this happens AFTER the normal Vim initializations, like reading your
vimrc file. See |initialization|.
@@ -55,17 +36,7 @@ But the GUI window is only opened after all the initializations have been
carried out. If you want some commands to be executed just after opening the
GUI window, use the |GUIEnter| autocommand event. Example: >
:autocmd GUIEnter * winpos 100 50
-
-You can use the gvimrc files to set up your own customized menus (see |:menu|)
-and initialize other things that you may want to set up differently from the
-terminal version.
-
-Recommended place for your personal GUI initializations:
- Unix $XDG_CONFIG_HOME/.config/nvim/ginit.vim
- (default for $XDG_CONFIG_HOME is ~/.config)
-
-The personal initialization files are searched in the order specified above
-and only the first one that is found is read.
+<
*:winp* *:winpos* *E188*
:winp[os]
diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt
index 97bbb34078..c36aeffa1a 100644
--- a/runtime/doc/if_lua.txt
+++ b/runtime/doc/if_lua.txt
@@ -4,11 +4,32 @@
NVIM REFERENCE MANUAL
-Lua Interface to Nvim *lua* *Lua*
+Lua engine *lua* *Lua*
Type |gO| to see the table of contents.
==============================================================================
+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. This library
+complements the Nvim editor |functions| and Ex commands (the editor "stdlib"),
+which can also be used from Lua code.
+
+Nvim resolves module conflicts 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 modules *lua-require*
Nvim automatically adjusts `package.path` and `package.cpath` according to
@@ -54,25 +75,27 @@ The result will look like this:
= `/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 keep up with '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.
+Note:
-Note 2: even though adjustments happens automatically Nvim does not track
-current values of `package.path` or `package.cpath`. If you happened to
-delete some paths from there you need to reset 'runtimepath' to make them
-readded. Just running `let &runtimepath = &runtimepath` should work.
+- 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.
-Note 3: skipping paths from 'runtimepath' which contain semicolons applies
-both to `package.path` and `package.cpath`. Given that there is a number of
-badly written plugins using shell which will not work with paths containing
-semicolons it is better to not have them in 'runtimepath' at all.
+- 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.
------------------------------------------------------------------------------
-Example of a plugin that uses lua modules *lua-require-example*
+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
@@ -83,70 +106,70 @@ 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
+ 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
+ if exists('g:charblob_loaded')
+ finish
+ endif
+ let g:charblob_loaded = 1
- command MakeCharBlob :call charblob#encode_buffer()
+ 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,
- }
+ 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*
@@ -157,13 +180,13 @@ Commands *lua-commands*
Examples:
>
- :lua vim.api.nvim_command('echo "Hello, Nvim!"')
+ :lua vim.api.nvim_command('echo "Hello, Nvim!"')
<
To see the Lua version: >
- :lua print(_VERSION)
+ :lua print(_VERSION)
To see the LuaJIT version: >
- :lua print(jit.version)
+ :lua print(jit.version)
<
:[range]lua << {endmarker}
@@ -179,15 +202,15 @@ in Vim scripts.
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
+ 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.
@@ -203,12 +226,12 @@ not the case for globals.
Examples:
>
- :luado return string.format("%s\t%d", line:reverse(), #line)
+ :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
+ :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*
@@ -218,8 +241,8 @@ Examples:
Examples:
>
- :luafile script.lua
- :luafile %
+ :luafile script.lua
+ :luafile %
<
All these commands execute a Lua chunk from either the command line (:lua and
@@ -235,21 +258,48 @@ position are restricted when the command is executed in the |sandbox|.
==============================================================================
-The vim module *lua-vim*
+vim.* *lua-vim* *lua-stdlib*
+
+The "standard library" (stdlib) of Nvim Lua is the `vim` module, which exposes
+various functions and sub-modules. The module is implicitly loaded, thus
+require("vim") is unnecessary.
-Lua interfaces Nvim through the "vim" module. Currently it has the `api`
-submodule and some Nvim-specific utilities.
+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: Underscore-prefixed functions (e.g. "_os_proc_children") are
+internal/private and must not be used by plugins.
------------------------------------------------------------------------------
vim.api.* functions
-`vim.api` exposes the Nvim |API| as a table of Lua functions. All functions
-are available.
+`vim.api` exposes the full Nvim |API| as a table of Lua functions.
For 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()))
+ print(tostring(vim.api.nvim_get_current_line()))
------------------------------------------------------------------------------
vim.* builtin functions
@@ -311,10 +361,10 @@ vim.type_idx *lua-vim.type_idx*
vim.val_idx *lua-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,
- }
+ {
+ [vim.type_idx] = vim.types.float,
+ [vim.val_idx] = 1.0,
+ }
< See also |lua-vim.type_idx| and |lua-special-tbl|.
vim.types *lua-vim.types*
@@ -346,8 +396,9 @@ vim.inspect({object}, {options}) *vim.inspect*
Return a human-readable representation of the passed object. See
https://github.com/kikito/inspect.lua
for details and possible options.
+
==============================================================================
-The luaeval function *lua-luaeval* *lua-eval*
+luaeval() *lua-luaeval* *lua-eval*
*luaeval()*
The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is
@@ -355,11 +406,11 @@ The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is
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
+ 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
@@ -368,10 +419,10 @@ 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
+ :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.
@@ -405,11 +456,11 @@ cases there is the following agreement:
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)
+ :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
@@ -417,4 +468,4 @@ value is also always converted. When converting, |msgpack-special-dict|s are
treated specially.
==============================================================================
- vim:tw=78:ts=8:noet:ft=help:norl:
+ vim:tw=78:ts=8:et:ft=help:norl:
diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt
index df4b54ef76..ac725a9b5d 100644
--- a/runtime/doc/if_pyth.txt
+++ b/runtime/doc/if_pyth.txt
@@ -11,7 +11,7 @@ See |provider-python| for more information.
Type |gO| to see the table of contents.
==============================================================================
-1. Commands *python-commands*
+Commands *python-commands*
*:python* *:py* *E263* *E264* *E887*
:[range]py[thon] {stmt}
@@ -44,10 +44,10 @@ Example: >
To see what version of Python you have: >
:python print(sys.version)
-There is no need to import sys, it's done by default.
+There is no need to "import sys", it's done by default.
-Note: Python is very sensitive to the indenting. Make sure the "class" line
-and "EOF" do not have any indent.
+Note: Python is very sensitive to indenting. Make sure the "class" line and
+"EOF" do not have any indent.
*:pydo*
:[range]pydo {body} Execute Python function "def _vim_pydo(line, linenr):
@@ -103,8 +103,8 @@ Here are some examples *python-examples* >
:python print "Hello"
:python str = current.buffer[42]
-(Note that changes - like the imports - persist from one command to the next,
-just like in the Python interpreter.)
+Note that changes (such as the "import" statements) persist from one command
+to the next, just like the Python REPL.
*script-here*
When using a script language in-line, you might want to skip this when the
@@ -130,7 +130,7 @@ Instead, put the Python command in a function and call that function:
Note that "EOF" must be at the start of the line.
==============================================================================
-2. The vim module *python-vim* *python2*
+The vim module *python-vim* *python2*
Python code gets all of its access to vim (with one exception - see
|python-output| below) via the "vim" module. The vim module implements two
@@ -189,11 +189,6 @@ vim.eval(str) *python-eval*
# string.atoi() to convert to
# a number.
-vim.bindeval(str) *python-bindeval*
- Like |python-eval|, but returns special objects described in
- |python-bindeval-objects|. These python objects let you modify (|List|
- or |Dictionary|) or call (|Funcref|) vim objects.
-
vim.strwidth(str) *python-strwidth*
Like |strwidth()|: returns number of display cells str occupies, tab
is counted as one cell.
@@ -291,8 +286,7 @@ vim.current *python-current*
vim.vars *python-vars*
vim.vvars *python-vvars*
Dictionary-like objects holding dictionaries with global (|g:|) and
- vim (|v:|) variables respectively. Identical to `vim.bindeval("g:")`,
- but faster.
+ vim (|v:|) variables respectively.
vim.options *python-options*
Object partly supporting mapping protocol (supports setting and
@@ -407,7 +401,7 @@ vim._get_paths *python-_get_paths*
{rtp}/pythonx directories for each {rtp} in 'runtimepath'.
==============================================================================
-3. Buffer objects *python-buffer*
+Buffer objects *python-buffer*
Buffer objects represent vim buffers. You can obtain them in a number of ways:
- via vim.current.buffer (|python-current|)
@@ -485,7 +479,7 @@ Examples (assume b is the current buffer) >
:py del b.options["ar"] # same as :set autoread<
==============================================================================
-4. Range objects *python-range*
+Range objects *python-range*
Range objects represent a part of a vim buffer. You can obtain them in a
number of ways:
@@ -517,7 +511,7 @@ Example (assume r is the current range):
vim.command("%d,%dhardcopy!" % (r.start+1,r.end+1))
==============================================================================
-5. Window objects *python-window*
+Window objects *python-window*
Window objects represent vim windows. You can obtain them in a number of ways:
- via vim.current.window (|python-current|)
@@ -561,7 +555,7 @@ The width attribute is writable only if the screen is split vertically.
Window object type is available using "Window" attribute of vim module.
==============================================================================
-6. Tab page objects *python-tabpage*
+Tab page objects *python-tabpage*
Tab page objects represent vim tab pages. You can obtain them in a number of
ways:
@@ -583,119 +577,14 @@ Tab page attributes are:
TabPage object type is available using "TabPage" attribute of vim module.
==============================================================================
-7. vim.bindeval objects *python-bindeval-objects*
-
-vim.Dictionary object *python-Dictionary*
- Dictionary-like object providing access to vim |Dictionary| type.
- Attributes:
- Attribute Description ~
- locked One of *python-.locked*
- Value Description ~
- zero Variable is not locked
- vim.VAR_LOCKED Variable is locked, but can be unlocked
- vim.VAR_FIXED Variable is locked and can't be unlocked
- Read-write. You can unlock locked variable by assigning
- `True` or `False` to this attribute. No recursive locking
- is supported.
- scope One of
- Value Description ~
- zero Dictionary is not a scope one
- vim.VAR_DEF_SCOPE |g:| or |l:| dictionary
- vim.VAR_SCOPE Other scope dictionary,
- see |internal-variables|
- Methods (note: methods do not support keyword arguments):
- Method Description ~
- keys() Returns a list with dictionary keys.
- values() Returns a list with dictionary values.
- items() Returns a list of 2-tuples with dictionary contents.
- update(iterable), update(dictionary), update(**kwargs)
- Adds keys to dictionary.
- get(key[, default=None])
- Obtain key from dictionary, returning the default if it is
- not present.
- pop(key[, default])
- Remove specified key from dictionary and return
- corresponding value. If key is not found and default is
- given returns the default, otherwise raises KeyError.
- popitem()
- Remove random key from dictionary and return (key, value)
- pair.
- has_key(key)
- Check whether dictionary contains specified key, similar
- to `key in dict`.
-
- __new__(), __new__(iterable), __new__(dictionary), __new__(update)
- You can use `vim.Dictionary()` to create new vim
- dictionaries. `d=vim.Dictionary(arg)` is the same as
- `d=vim.bindeval('{}');d.update(arg)`. Without arguments
- constructs empty dictionary.
-
- Examples: >
- d = vim.Dictionary(food="bar") # Constructor
- d['a'] = 'b' # Item assignment
- print d['a'] # getting item
- d.update({'c': 'd'}) # .update(dictionary)
- d.update(e='f') # .update(**kwargs)
- d.update((('g', 'h'), ('i', 'j'))) # .update(iterable)
- for key in d.keys(): # .keys()
- for val in d.values(): # .values()
- for key, val in d.items(): # .items()
- print isinstance(d, vim.Dictionary) # True
- for key in d: # Iteration over keys
- class Dict(vim.Dictionary): # Subclassing
-<
- Note: when iterating over keys you should not modify dictionary.
-
-vim.List object *python-List*
- Sequence-like object providing access to vim |List| type.
- Supports `.locked` attribute, see |python-.locked|. Also supports the
- following methods:
- Method Description ~
- extend(item) Add items to the list.
-
- __new__(), __new__(iterable)
- You can use `vim.List()` to create new vim lists.
- `l=vim.List(iterable)` is the same as
- `l=vim.bindeval('[]');l.extend(iterable)`. Without
- arguments constructs empty list.
- Examples: >
- l = vim.List("abc") # Constructor, result: ['a', 'b', 'c']
- l.extend(['abc', 'def']) # .extend() method
- print l[1:] # slicing
- l[:0] = ['ghi', 'jkl'] # slice assignment
- print l[0] # getting item
- l[0] = 'mno' # assignment
- for i in l: # iteration
- print isinstance(l, vim.List) # True
- class List(vim.List): # Subclassing
-
-vim.Function object *python-Function*
- Function-like object, acting like vim |Funcref| object. Supports `.name`
- attribute and is callable. Accepts special keyword argument `self`, see
- |Dictionary-function|. You can also use `vim.Function(name)` constructor,
- it is the same as `vim.bindeval('function(%s)'%json.dumps(name))`.
-
- Examples: >
- f = vim.Function('tr') # Constructor
- print f('abc', 'a', 'b') # Calls tr('abc', 'a', 'b')
- vim.command('''
- function DictFun() dict
- return self
- endfunction
- ''')
- f = vim.bindeval('function("DictFun")')
- print f(self={}) # Like call('DictFun', [], {})
- print isinstance(f, vim.Function) # True
-
-==============================================================================
-8. pyeval() and py3eval() Vim functions *python-pyeval*
+pyeval() and py3eval() Vim functions *python-pyeval*
To facilitate bi-directional interface, you can use |pyeval()| and |py3eval()|
functions to evaluate Python expressions and pass their values to Vim script.
|pyxeval()| is also available.
==============================================================================
-9. Python 3 *python3*
+Python 3 *python3*
*:py3* *:python3*
The `:py3` and `:python3` commands work similar to `:python`. A simple check
@@ -724,7 +613,7 @@ You can test what Python version is available with: >
endif
==============================================================================
-10. Python X *python_x* *pythonx*
+Python X *python_x* *pythonx*
Because most python code can be written so that it works with Python 2.6+ and
Python 3, the pyx* functions and commands have been written. They work the
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index bcefa1f56b..4177268780 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -2351,7 +2351,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'fillchars'* *'fcs'*
'fillchars' 'fcs' string (default "")
- global
+ local to window
Characters to fill the statuslines and vertical separators.
It is a comma separated list of items:
@@ -3134,7 +3134,7 @@ A jump table for the options with a short description can be found at |Q_op|.
may change in later releases.
*'iminsert'* *'imi'*
-'iminsert' 'imi' number (default 0, 2 when an input method is supported)
+'iminsert' 'imi' number (default 0)
local to buffer
Specifies whether :lmap or an Input Method (IM) is to be used in
Insert mode. Valid values:
@@ -3154,7 +3154,7 @@ A jump table for the options with a short description can be found at |Q_op|.
It is also used for the argument of commands like "r" and "f".
*'imsearch'* *'ims'*
-'imsearch' 'ims' number (default 0, 2 when an input method is supported)
+'imsearch' 'ims' number (default -1)
local to buffer
Specifies whether :lmap or an Input Method (IM) is to be used when
entering a search pattern. Valid values:
@@ -3653,7 +3653,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:$")
- global
+ local to window
Strings to use in 'list' mode and for the |:list| command. It is a
comma separated list of string settings.
@@ -3662,11 +3662,26 @@ A jump table for the options with a short description can be found at |Q_op|.
omitted, there is no extra character at the end of the
line.
*lcs-tab*
- tab:xy Two characters to be used to show a tab. The first
- char is used once. The second char is repeated to
- fill the space that the tab normally occupies.
- "tab:>-" will show a tab that takes four spaces as
- ">---". When omitted, a tab is show as ^I.
+ tab:xy[z] Two or three characters to be used to show a tab.
+ The third character is optional.
+
+ tab:xy The 'x' is always used, then 'y' as many times as will
+ fit. Thus "tab:>-" displays:
+ >
+ >-
+ >--
+ etc.
+
+ tab:xyz The 'z' is always used, then 'x' is prepended, and
+ then 'y' is used as many times as will fit. Thus
+ "tab:<->" displays:
+ >
+ <>
+ <->
+ <-->
+ etc.
+
+ When "tab:" is omitted, a tab is shown as ^I.
*lcs-space*
space:c Character to show for a space. When omitted, spaces
are left blank.
@@ -4471,6 +4486,15 @@ A jump table for the options with a short description can be found at |Q_op|.
Insert mode completion. When zero as much space as available is used.
|ins-completion-menu|.
+ *'pumblend'* *'pb'*
+'pumblend' 'pb' number (default 0)
+ global
+ Enables pseudo-transparency for the |popup-menu|. Valid values are in
+ the range of 0 for fully opaque popupmenu (disabled) to 100 for fully
+ transparent background. Values between 0-30 are typically most useful.
+
+ UI-dependent. Works best with RGB colors. 'termguicolors'
+
*'pyxversion'* *'pyx'*
'pyxversion' 'pyx' number (default depends on the build)
global
@@ -4746,14 +4770,12 @@ A jump table for the options with a short description can be found at |Q_op|.
height with ":set scroll=0".
*'scrollback'* *'scbk'*
-'scrollback' 'scbk' number (default: 10000
- in normal buffers: -1)
+'scrollback' 'scbk' number (default: 10000)
local to buffer
Maximum number of lines kept beyond the visible screen. Lines at the
top are deleted if new lines exceed this limit.
+ Minimum is 1, maximum is 100000.
Only in |terminal| buffers.
- -1 means "unlimited" for normal buffers, 100000 otherwise.
- Minimum is 1.
*'scrollbind'* *'scb'* *'noscrollbind'* *'noscb'*
'scrollbind' 'scb' boolean (default off)
@@ -6170,10 +6192,16 @@ A jump table for the options with a short description can be found at |Q_op|.
'ttimeout' boolean (default on)
global
This option and 'ttimeoutlen' determine the behavior when part of a
- key code sequence has been received by the terminal UI. For example,
- if the \x1b byte is received and 'ttimeout' is set, Nvim will wait
- 'ttimeoutlen' milliseconds for the terminal to complete a byte
- sequence that represents a key that starts with \x1b.
+ key code sequence has been received by the |TUI|.
+
+ For example if <Esc> (the \x1b byte) is received and 'ttimeout' is
+ set, Nvim waits 'ttimeoutlen' milliseconds for the terminal to
+ complete a key code sequence. If no input arrives before the timeout,
+ a single <Esc> is assumed. Many TUI cursor key codes start with <Esc>.
+
+ On very slow systems this may fail, causing cursor keys not to work
+ sometimes. If you discover this problem you can ":set ttimeout=9999".
+ Nvim will wait for the next character to arrive after an <Esc>.
*'timeoutlen'* *'tm'*
'timeoutlen' 'tm' number (default 1000)
diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt
index 6ed3c230b9..09fdc6872b 100644
--- a/runtime/doc/provider.txt
+++ b/runtime/doc/provider.txt
@@ -22,7 +22,9 @@ Python integration *provider-python*
Nvim supports Python |remote-plugin|s and the Vim legacy |python2| and
|python3| interfaces (which are implemented as remote-plugins).
-Note: Only the Vim 7.3 API is supported; bindeval (Vim 7.4) is not.
+
+Note: Only the Vim 7.3 legacy interface is supported, not later features such
+as |python-bindeval| (Vim 7.4); use the Nvim API instead.
PYTHON QUICKSTART ~
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index 12794b6cc0..291f4b2086 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -409,8 +409,8 @@ accordingly. Vim proceeds in this order:
useful for debugging the initializations.
3. Execute Ex commands, from environment variables and/or files
- An environment variable is read as one Ex command line, where multiple
- commands must be separated with '|' or <NL>.
+ An environment variable (e.g. $VIMINIT) is read as one Ex command
+ line, where multiple commands must be separated with '|' or <NL>.
*config* *init.vim* *vimrc* *exrc*
A file that contains initialization commands is generically called
a "vimrc" or config file. Each line in a vimrc file is executed as an
@@ -422,20 +422,15 @@ accordingly. Vim proceeds in this order:
Or if |$XDG_CONFIG_HOME| is defined:
$XDG_CONFIG_HOME/nvim/init.vim
- RECOMMENDATION: Put all your Vim configuration stuff in the
- $HOME/.config/nvim/ directory. That makes it easy to copy it to
- another system.
-
- If Vim was started with "-u filename", the file "filename" is used.
+ If Nvim was started with "-u filename", the file "filename" is used.
All following initializations until 4. are skipped. $MYVIMRC is not
set.
- "vim -u NORC" can be used to skip these initializations without
- reading a file. "vim -u NONE" also skips plugins and syntax
+ "nvim -u NORC" can be used to skip these initializations without
+ reading a file. "nvim -u NONE" also skips plugins and syntax
highlighting. |-u|
- If Vim was started in Ex mode with the "-s" argument, all following
- initializations until 4. are skipped. Only the "-u" option is
- interpreted.
+ If Nvim was started with |-es|, all following initializations until 4.
+ are skipped.
*system-vimrc* *sysinit.vim*
a. The system vimrc file is read for initializations. If
nvim/sysinit.vim file exists in one of $XDG_CONFIG_DIRS, it will be
@@ -447,13 +442,11 @@ accordingly. Vim proceeds in this order:
is used, the others are ignored. The $MYVIMRC environment variable is
set to the file that was first found, unless $MYVIMRC was already set
and when using VIMINIT.
- - The environment variable VIMINIT
- The value of $VIMINIT is used as an Ex command line.
- - The user vimrc file: $XDG_CONFIG_HOME/nvim/init.vim.
- - Other vimrc file: {xdg_config_dir}/nvim/init.vim where
+ - Environment variable $VIMINIT, used as an Ex command line.
+ - User |config| file: $XDG_CONFIG_HOME/nvim/init.vim.
+ - Other config file: {xdg_config_dir}/nvim/init.vim where
{xdg_config_dir} is one of the directories in $XDG_CONFIG_DIRS.
- - The environment variable EXINIT.
- The value of $EXINIT is used as an Ex command line.
+ - Environment variable $EXINIT, used as an Ex command line.
c. If the 'exrc' option is on (which is NOT the default), the current
directory is searched for three files. The first that exists is used,
@@ -519,18 +512,14 @@ accordingly. Vim proceeds in this order:
If the "-b" flag was given to Vim, the options for binary editing will
be set now. See |-b|.
-10. Perform GUI initializations
- Only when starting "gvim", the GUI initializations will be done. See
- |gui-init|.
-
-11. Read the ShaDa file
+10. Read the ShaDa file
See |shada-file|.
-12. Read the quickfix file
+11. Read the quickfix file
If the "-q" flag was given to Vim, the quickfix file is read. If this
fails, Vim exits.
-13. Open all windows
+12. Open all windows
When the |-o| flag was given, windows will be opened (but not
displayed yet).
When the |-p| flag was given, tab pages will be created (but not
@@ -539,7 +528,7 @@ accordingly. Vim proceeds in this order:
If the "-q" flag was given to Vim, the first error is jumped to.
Buffers for all windows will be loaded.
-14. Execute startup commands
+13. Execute startup commands
If a "-t" flag was given to Vim, the tag is jumped to.
The commands given with the |-c| and |+cmd| arguments are executed.
If the 'insertmode' option is set, Insert mode is entered.
@@ -591,9 +580,6 @@ On Windows systems Vim assumes that all the vimrc files have <CR> <NL> pairs
as line separators. This will give problems if you have a file with only
<NL>s and have a line like ":map xx yy^M". The trailing ^M will be ignored.
-The $MYVIMRC or $MYGVIMRC file will be set to the first found vimrc and/or
-gvimrc file.
-
Avoiding trojan horses ~
*trojan-horse*
diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt
index 9de5745e92..978f50dd55 100644
--- a/runtime/doc/term.txt
+++ b/runtime/doc/term.txt
@@ -6,17 +6,18 @@
Terminal UI *TUI* *tui*
-Nvim (except in |--headless| mode) uses information about the terminal you are
-using to present a built-in UI. If that information is not correct, the
-screen may be messed up or keys may not be recognized.
+Nvim uses a list of terminal capabilities to display its user interface
+(except in |--embed| and |--headless| modes). If that information is wrong,
+the screen may be messed up or keys may not be recognized.
Type |gO| to see the table of contents.
==============================================================================
Startup *startup-terminal*
-Nvim (except in |--headless| mode) guesses a terminal type when it starts.
-|$TERM| is the primary hint that determines the terminal type.
+Nvim guesses the terminal type when it starts (except in |--embed| and
+|--headless| modes). The |$TERM| environment variable is the primary hint that
+determines the terminal type.
*terminfo* *E557* *E558* *E559*
The terminfo database is used if available.
@@ -37,39 +38,39 @@ The $TERM environment variable must match the terminal you are using!
Otherwise Nvim cannot know what sequences your terminal expects, and weird
or sub-optimal behavior will result (scrolling quirks, wrong colors, etc.).
-$TERM is also important because it is mirrored by SSH to the remote session,
+$TERM is also important because it is forwarded by SSH to the remote session,
unlike most other environment variables.
For this terminal Set $TERM to |builtin-terms|
-------------------------------------------------------------------------
+ anything libvte-based vte, vte-256color Y
+ (e.g. GNOME Terminal) (aliases: gnome, gnome-256color)
iTerm (original) iterm, iTerm.app N
iTerm2 (new capabilities) iterm2, iTerm2.app Y
Konsole konsole-256color N
- anything libvte-based vte, vte-256color Y
- (e.g. GNOME Terminal) (aliases: gnome, gnome-256color)
- tmux tmux, tmux-256color Y
- screen screen, screen-256color Y
+ Linux virtual terminal linux, linux-256color Y
PuTTY putty, putty-256color Y
+ rxvt rxvt, rxvt-256color Y
+ screen screen, screen-256color Y
+ simple terminal (st) st, st-256color Y
Terminal.app nsterm N
- Linux virtual terminal linux, linux-256color Y
+ tmux tmux, tmux-256color Y
+ Windows/ConEmu conemu Y
+ Windows/Cygwin-built Nvim cygwin Y
+ Windows/Interix interix Y
+ Windows/VTP console vtpcon Y
+ Windows/legacy console win32con Y
+ xterm or compatible xterm, xterm-256color Y
*builtin-terms* *builtin_terms*
-If a |terminfo| database is not available, or no entry for the terminal type is
-found in that database, Nvim will use a compiled-in mini-database of terminfo
-entries for "xterm", "putty", "screen", "tmux", "rxvt", "iterm", "interix",
-"linux", "st", "vte", "gnome", and "ansi".
-
-The lookup matches the initial portion of the terminal type, so (for example)
-"putty-256color" and "putty" will both be mapped to the built-in "putty"
-entry. The built-in terminfo entries describe the terminal as 256-colour
-capable if possible. See |tui-colors|.
-
-If no built-in terminfo record matches the terminal type, the built-in "ansi"
-terminfo record is used as a final fallback.
-
-The built-in mini-database is not combined with an external terminfo database,
-nor can it be used in preference to one. You can thus entirely override any
-omissions or out-of-date information in the built-in terminfo database by
+If a |terminfo| database is not available or there is no entry for the current
+terminal, Nvim will map |$TERM| to a builtin entry according to the above
+table, or "ansi" if there is no match. For example "TERM=putty-256color" will
+be mapped to the builtin "putty" entry. See also |tui-colors|.
+
+The builtin terminfo is not combined with any external terminfo database, nor
+can it be used in preference to one. You can thus entirely override any
+omissions or out-of-date information in the builtin terminfo database by
supplying an external one with entries for the terminal type.
Settings depending on terminal *term-dependent-settings*
@@ -107,12 +108,6 @@ and right scroll margins as well. If Nvim detects that the terminal is Xterm,
it will make use of this ability to speed up scrolling that is not the full
width of the terminal.
-This ability is only present in genuine Xterm, not in the many terminal
-emulators that incorrectly describe themselves as xterm. Nvim's detection of
-genuine Xterm will not work over an SSH connection, because the environment
-variable, set by genuine Xterm, that it looks for is not automatically
-replicated over an SSH login session.
-
*tui-colors*
Nvim uses 256 colours by default, ignoring |terminfo| for most terminal types,
including "linux" (whose virtual terminals have had 256-colour support since
@@ -149,7 +144,7 @@ extension pioneered by dtterm. |terminfo| does not have a flag for this
extension. So Nvim simply assumes that (all) "dtterm", "xterm", "teraterm",
"rxvt" terminal types, and Konsole, are capable of this.
- *tui-cursor-shape*
+ *tui-cursor-shape*
Nvim will adjust the shape of the cursor from a block to a line when in insert
mode (or as specified by the 'guicursor' option), on terminals that support
it. It uses the same |terminfo| extensions that were pioneered by tmux for
@@ -162,55 +157,22 @@ environment variables. For the "rxvt", "putty", "linux", "screen",
terminal emulator, or genuine Xterm are detected, it will add constructed
"Ss" and "Se" capabilities.
-Note: Sometimes it will appear that Nvim when run within tmux is not changing
-the cursor, but in fact it is tmux receiving instructions from Nvim to change
-the cursor and not knowing what to do in turn. tmux has to translate what it
-receives from Nvim into whatever control sequence is appropriate for the
-terminal that it is outputting to. It shares a common mechanism with Nvim, of
-using the "Ss" and "Se" capabilities from terminfo (for the output terminal)
-if they are present. Unlike Nvim, if they are not present in terminfo you
-must add them by setting "terminal-overrides" in ~/.tmux.conf .
+ *tui-cursor-tmux*
+Within tmux it may appear that Nvim is not changing the cursor, but in fact it
+is tmux receiving instructions from Nvim to change the cursor and not knowing
+what to do in turn. tmux must translate what it receives from Nvim into
+whatever control sequence is appropriate for the host terminal. It shares
+a common mechanism with Nvim, of using the "Ss" and "Se" capabilities from
+terminfo (for the output terminal) if they are present. Unlike Nvim, if they
+are not in terminfo you must add them by setting "terminal-overrides" in
+~/.tmux.conf .
See the tmux(1) manual page for the details of how and what to do in the tmux
configuration file. It will look something like: >
set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q'
-<or (alas!) for Konsole specifically, something more complex like: >
+<or (alas!) for Konsole 18.07.70 or older, something more complex like: >
set -ga terminal-overrides 'xterm*:\E]50;CursorShape=%?%p1%{3}%<%t%{0}%e%{1}%;%d\007'
<
- *cs7-problem*
-Note: If the terminal settings are changed after running Vim, you might have
-an illegal combination of settings. This has been reported on Solaris 2.5
-with "stty cs8 parenb", which is restored as "stty cs7 parenb". Use
-"stty cs8 -parenb -istrip" instead, this is restored correctly.
-
-Many cursor key codes start with an <Esc>. Vim must find out if this is a
-single hit of the <Esc> key or the start of a cursor key sequence. It waits
-for a next character to arrive. If it does not arrive within one second a
-single <Esc> is assumed. On very slow systems this may fail, causing cursor
-keys not to work sometimes. If you discover this problem reset the 'timeout'
-option. Vim will wait for the next character to arrive after an <Esc>. If
-you want to enter a single <Esc> you must type it twice.
-
-Some terminals have confusing codes for the cursor keys. The televideo 925 is
-such a terminal. It sends a CTRL-H for cursor-left. This would make it
-impossible to distinguish a backspace and cursor-left. To avoid this problem
-CTRL-H is never recognized as cursor-left.
-
- *vt100-cursor-keys* *xterm-cursor-keys*
-Other terminals (e.g., vt100 and xterm) have cursor keys that send <Esc>OA,
-<Esc>OB, etc. Unfortunately these are valid commands in insert mode: Stop
-insert, Open a new line above the new one, start inserting 'A', 'B', etc.
-Instead of performing these commands Vim will erroneously recognize this typed
-key sequence as a cursor key movement. To avoid this and make Vim do what you
-want in either case you could use these settings: >
- :set notimeout " don't timeout on mappings
- :set ttimeout " do timeout on terminal key codes
- :set timeoutlen=100 " timeout after 100 msec
-This requires the key-codes to be sent within 100 msec in order to recognize
-them as a cursor key. When you type you normally are not that fast, so they
-are recognized as individual typed commands, even though Vim receives the same
-sequence of bytes.
-
==============================================================================
Window size *window-size*
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index 9acd8b2c5a..77a829b150 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -32,9 +32,11 @@ a dictionary with these (optional) keys:
`ext_tabline` Externalize the tabline. |ui-tabline|
`ext_cmdline` Externalize the cmdline. |ui-cmdline|
`ext_wildmenu` Externalize the wildmenu. |ui-wildmenu|
+ `ext_messages` Externalize messages. |ui-messages|
`ext_linegrid` Use new revision of the grid events. |ui-linegrid|
`ext_multigrid` Use per-window grid based events. |ui-multigrid|
`ext_hlstate` Use detailed highlight state. |ui-hlstate|
+ `ext_termcolors` Use external default colors.
Specifying a non-existent option is an error. UIs can check the |api-metadata|
`ui_options` key for supported options. Additionally Nvim (currently) requires
@@ -158,6 +160,7 @@ the editor.
'guifontset'
'guifontwide'
'linespace'
+ 'pumblend'
'showtabline'
'termguicolors'
"ext_*" (all |ui-ext-options|)
@@ -239,6 +242,13 @@ numerical highlight `id`:s to the actual attributes.
special colors respectively. `cterm_fg` and `cterm_bg` specifies the
default color codes to use in a 256-color terminal.
+ The rgb values will always be valid colors, by default. If no
+ colors have been set, they will default to black and white, depending
+ on 'background'. By setting the `ext_termcolors` option, instead
+ -1 will be used for unset colors. This is mostly useful for a
+ TUI implementation, where using the terminal emulators builitn
+ defaults are expected.
+
Note: unlike the corresponding events in the first revision, the
screen is not always cleared after sending this event. The GUI has to
repaint the screen with changed background color itself.
@@ -525,6 +535,7 @@ tabs.
See |ui-linegrid| for grid events.
See |nvim_ui_try_resize_grid| in |api-ui| to request changing the grid size.
+See |nvim_input_mouse| for sending mouse events to Nvim.
==============================================================================
Popupmenu Events *ui-popupmenu*
@@ -563,7 +574,7 @@ Only sent if `ext_tabline` option is set in |ui-options|
==============================================================================
Cmdline Events *ui-cmdline*
-Only sent if `ext_cmdline` option is set in |ui-options|
+Only sent if `ext_cmdline` option is set in |ui-options|.
["cmdline_show", content, pos, firstc, prompt, indent, level]
content: List of [attrs, string]
@@ -636,4 +647,69 @@ Only sent if `ext_wildmenu` option is set in |ui-options|
Hide the wildmenu.
==============================================================================
+Message Events *ui-messages*
+
+Only sent if `ext_messages` option is set in |ui-options|. This option implies
+`ext_linegrid` and `ext_cmdline` also being set. |ui-linegrid| and |ui-cmdline| events
+will thus also be sent.
+
+This extension allows the UI to control the display of messages that otherwise
+would have been displayed in the message/cmdline area in the bottom of the
+screen.
+
+Activating this extension means that Nvim will allocate no screen space for
+the cmdline or messages, and 'cmdheight' will be set to zero. Attempting to
+change 'cmdheight' will silently be ignored. |ui-cmdline| events will be used
+to represent the state of the cmdline.
+
+["msg_show", kind, content, replace_last]
+ Display a message to the user.
+
+ `kind` will be one of the following
+ `emsg`: Internal error message
+ `echo`: temporary message from plugin (|:echo|)
+ `echomsg`: ordinary message from plugin (|:echomsg|)
+ `echoerr`: error message from plugin (|:echoerr|)
+ `return_prompt`: |press-enter| prompt after a group of messages
+ `quickfix`: Quickfix navigation message
+ `kind` can also be the empty string. The message is then some internal
+ informative or warning message, that hasn't yet been assigned a kind.
+ New message kinds can be added in later versions; clients should
+ handle messages of an unknown kind just like empty kind.
+
+ `content` will be an array of `[attr_id, text_chunk]` tuples,
+ building up the message text of chunks of different highlights.
+ No extra spacing should be added between chunks, the `text_chunk` by
+ itself should contain any necessary whitespace. Messages can contain
+ line breaks `"\n"`.
+
+ `replace_last` controls how multiple messages should be displayed.
+ If `replace_last` is false, this message should be displayed together
+ with all previous messages that are still visible. If `replace_last`
+ is true, this message should replace the message in the most recent
+ `msg_show` call, but any other visible message should still remain.
+
+["msg_clear"]
+ Clear all messages currently displayed by "msg_show". (Messages sent
+ by other "msg_" events below will not be affected).
+
+["msg_showmode", content]
+ Shows 'showmode' and |recording| messages. `content` has the same
+ format as in "msg_show". This event is sent with empty `content` to
+ hide the last message.
+
+["msg_showcmd", content]
+ Shows 'showcmd' messages. `content` has the same format as in "msg_show".
+ This event is sent with empty `content` to hide the last message.
+
+["msg_ruler", content]
+ Used to display 'ruler' when there is no space for the ruler in a
+ statusline. `content` has the same format as in "msg_show". This event is
+ sent with empty `content` to hide the last message.
+
+["msg_history_show", entries]
+ Sent when |:messages| command is invoked. History is sent as a list of
+ entries, where each entry is a `[kind, content]` tuple.
+
+==============================================================================
vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index db856ceb65..25517e506b 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -37,8 +37,8 @@ a complete and centralized reference of those differences.
- 'display' defaults to "lastline,msgsep"
- 'encoding' is UTF-8 (cf. 'fileencoding' for file-content encoding)
- 'fillchars' defaults (in effect) to "vert:│,fold:·"
-- 'fsync' is disabled
- 'formatoptions' defaults to "tcqj"
+- 'fsync' is disabled
- 'history' defaults to 10000 (the maximum)
- 'hlsearch' is set by default
- 'incsearch' is set by default
@@ -142,6 +142,7 @@ Commands:
Events:
|DirChanged|
+ |Signal|
|TabNewEntered|
|TermClose|
|TermOpen|
@@ -184,9 +185,11 @@ Options:
'cpoptions' flags: |cpo-_|
'display' flag `msgsep` to minimize scrolling when showing messages
'guicursor' works in the terminal
- 'fillchars' flags: `msgsep` (see 'display' above)
- and `eob` for |hl-EndOfBuffer| marker
+ 'fillchars' local to window. flags: `msgsep` (see 'display' above) and `eob`
+ for |hl-EndOfBuffer| marker
'inccommand' shows interactive results for |:substitute|-like commands
+ 'listchars' local to window
+ 'pumblend' pseudo-transparent popupmenu
'scrollback'
'statusline' supports unlimited alignment sections
'tabline' %@Func@foo%X can call any function on mouse-click
@@ -371,7 +374,7 @@ VimL (Vim script) compatibility:
Some legacy Vim features are not implemented:
-- |if_py|: vim.bindeval() and vim.Function() are not supported
+- |if_py|: *python-bindeval* *python-Function* are not supported
- |if_lua|: the `vim` object is missing some legacy methods
- *if_perl*
- *if_mzscheme*
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index 44464c1cef..2e4b6f6e76 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -1256,6 +1256,7 @@ directory Displays directory contents. Can be used by a file explorer
< The buffer name is the name of the directory and is adjusted
when using the |:cd| command.
+ *scratch-buffer*
scratch Contains text that can be discarded at any time. It is kept
when closing the window, it must be deleted explicitly.
Settings: >
diff --git a/runtime/ftplugin/man.vim b/runtime/ftplugin/man.vim
index 26b51186a1..6c7f095f62 100644
--- a/runtime/ftplugin/man.vim
+++ b/runtime/ftplugin/man.vim
@@ -12,25 +12,13 @@ if s:pager
call man#init_pager()
endif
-setlocal buftype=nofile
-setlocal noswapfile
-setlocal bufhidden=hide
-setlocal nomodified
-setlocal readonly
-setlocal nomodifiable
-setlocal noexpandtab
-setlocal tabstop=8
-setlocal softtabstop=8
-setlocal shiftwidth=8
-setlocal wrap
-setlocal breakindent
-
-setlocal nonumber
-setlocal norelativenumber
-setlocal foldcolumn=0
-setlocal colorcolumn=0
-setlocal nolist
-setlocal nofoldenable
+setlocal noswapfile buftype=nofile bufhidden=hide
+setlocal nomodified readonly nomodifiable
+setlocal noexpandtab tabstop=8 softtabstop=8 shiftwidth=8
+setlocal wrap breakindent linebreak
+
+setlocal nonumber norelativenumber
+setlocal foldcolumn=0 colorcolumn=0 nolist nofoldenable
if !exists('g:no_plugin_maps') && !exists('g:no_man_maps')
nnoremap <silent> <buffer> j gj
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index b002cad4c6..506179297a 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -1,40 +1,133 @@
-" Debugger commands.
+" Debugger plugin using gdb.
"
" WORK IN PROGRESS - much doesn't work yet
"
-" Open two terminal windows:
+" Open two visible terminal windows:
" 1. run a pty, as with ":term NONE"
" 2. run gdb, passing the pty
-" The current window is used to edit source code and follows gdb.
+" The current window is used to view source code and follows gdb.
+"
+" A third terminal window is hidden, it is used for communication with gdb.
+"
+" The communication with gdb uses GDB/MI. See:
+" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html
"
" Author: Bram Moolenaar
-" Copyright: Vim license applies
+" Copyright: Vim license applies, see ":help license"
" In case this gets loaded twice.
if exists(':Termdebug')
finish
endif
+" The command that starts debugging, e.g. ":Termdebug vim".
+" To end type "quit" in the gdb window.
command -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>)
+" Name of the gdb command, defaults to "gdb".
if !exists('debugger')
let debugger = 'gdb'
endif
+" Sign used to highlight the line where the program has stopped.
+sign define debugPC linehl=debugPC
+if &background == 'light'
+ hi debugPC term=reverse ctermbg=lightblue guibg=lightblue
+else
+ hi debugPC term=reverse ctermbg=darkblue guibg=darkblue
+endif
+let s:pc_id = 12
+
func s:StartDebug(cmd)
+ let s:startwin = win_getid(winnr())
+ let s:startsigncolumn = &signcolumn
+
" Open a terminal window without a job, to run the debugged program
- let s:ptybuf = term_start('NONE', {})
- let pty = job_info(term_getjob(s:ptybuf))['tty']
+ let s:ptybuf = term_start('NONE', {
+ \ 'term_name': 'gdb program',
+ \ })
+ if s:ptybuf == 0
+ echoerr 'Failed to open the program terminal window'
+ return
+ endif
+ let pty = job_info(term_getjob(s:ptybuf))['tty_out']
+
+ " Create a hidden terminal window to communicate with gdb
+ let s:commbuf = term_start('NONE', {
+ \ 'term_name': 'gdb communication',
+ \ 'out_cb': function('s:CommOutput'),
+ \ 'hidden': 1,
+ \ })
+ if s:commbuf == 0
+ echoerr 'Failed to open the communication terminal window'
+ exe 'bwipe! ' . s:ptybuf
+ return
+ endif
+ let commpty = job_info(term_getjob(s:commbuf))['tty_out']
" Open a terminal window to run the debugger.
let cmd = [g:debugger, '-tty', pty, a:cmd]
echomsg 'executing "' . join(cmd) . '"'
let gdbbuf = term_start(cmd, {
\ 'exit_cb': function('s:EndDebug'),
- \ 'term_finish': 'close'
+ \ 'term_finish': 'close',
\ })
+ if gdbbuf == 0
+ echoerr 'Failed to open the gdb terminal window'
+ exe 'bwipe! ' . s:ptybuf
+ exe 'bwipe! ' . s:commbuf
+ return
+ endif
+
+ " Connect gdb to the communication pty, using the GDB/MI interface
+ call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r")
endfunc
func s:EndDebug(job, status)
- exe 'bwipe! ' . s:ptybuf
+ exe 'bwipe! ' . s:ptybuf
+ exe 'bwipe! ' . s:commbuf
+ call setwinvar(s:startwin, '&signcolumn', s:startsigncolumn)
+endfunc
+
+" Handle a message received from gdb on the GDB/MI interface.
+func s:CommOutput(chan, msg)
+ let msgs = split(a:msg, "\r")
+
+ for msg in msgs
+ " remove prefixed NL
+ if msg[0] == "\n"
+ let msg = msg[1:]
+ endif
+ if msg != ''
+ if msg =~ '^\*\(stopped\|running\)'
+ let wid = win_getid(winnr())
+
+ if win_gotoid(s:startwin)
+ if msg =~ '^\*stopped'
+ " TODO: proper parsing
+ let fname = substitute(msg, '.*fullname="\([^"]*\)".*', '\1', '')
+ let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
+ if lnum =~ '^[0-9]*$'
+ if expand('%:h') != fname
+ if &modified
+ " TODO: find existing window
+ exe 'split ' . fnameescape(fname)
+ let s:startwin = win_getid(winnr())
+ else
+ exe 'edit ' . fnameescape(fname)
+ endif
+ endif
+ exe lnum
+ exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname)
+ setlocal signcolumn=yes
+ endif
+ else
+ exe 'sign unplace ' . s:pc_id
+ endif
+
+ call win_gotoid(wid)
+ endif
+ endif
+ endif
+ endfor
endfunc
diff --git a/scripts/gen_api_vimdoc.py b/scripts/gen_api_vimdoc.py
index 4e86f15b37..515964bfe8 100755
--- a/scripts/gen_api_vimdoc.py
+++ b/scripts/gen_api_vimdoc.py
@@ -158,9 +158,12 @@ def doc_wrap(text, prefix='', width=70, func=False):
lines[-1] += part
return '\n'.join(x.rstrip() for x in lines).rstrip()
- return '\n'.join(textwrap.wrap(text.strip(), width=width,
- initial_indent=prefix,
- subsequent_indent=indent_space))
+ tw = textwrap.TextWrapper(break_long_words = False,
+ break_on_hyphens = False,
+ width=width,
+ initial_indent=prefix,
+ subsequent_indent=indent_space)
+ return '\n'.join(tw.wrap(text.strip()))
def parse_params(parent, width=62):
diff --git a/scripts/pvscheck.sh b/scripts/pvscheck.sh
index 371af7c7e5..fed50dde53 100755
--- a/scripts/pvscheck.sh
+++ b/scripts/pvscheck.sh
@@ -473,8 +473,6 @@ main() {
return 0
fi
- # set -x
-
if test -n "$patch" ; then
patch_sources "$tgt" "$only_build"
elif test -n "$pvs_install" ; then
diff --git a/scripts/release.sh b/scripts/release.sh
index fb266ad154..a51a6666f5 100755
--- a/scripts/release.sh
+++ b/scripts/release.sh
@@ -93,7 +93,7 @@ Next steps:
- Double-check NVIM_VERSION_* in CMakeLists.txt
- Push the tag:
git push --follow-tags
- - Empty-merge (if this is a maintenance release):
- git checkout upstream/master
- git merge -s ours upstream/release-x.y
+ - Update the 'stable' tag:
+ git push --force upstream HEAD^:refs/tags/stable
+ git fetch --tags
- Update website: index.html"
diff --git a/src/coverity-model.c b/src/coverity-model.c
index 3c38e4ae4d..2fd55c332c 100644
--- a/src/coverity-model.c
+++ b/src/coverity-model.c
@@ -34,32 +34,6 @@ int uv_pipe_open(struct uv_pipe_s *handle, int fd)
return result;
}
-// Issue 2422
-//
-// Teach coverity about jemalloc functions, so that it understands
-// they are equivalent to malloc ones.
-
-void *je_malloc(size_t size)
-{
- return __coverity_alloc__(size);
-}
-
-void je_free(void *ptr)
-{
- __coverity_free__(ptr);
-}
-
-void *je_calloc(size_t count, size_t size)
-{
- return je_malloc(count * size);
-}
-
-void *je_realloc(void *ptr, size_t size)
-{
- je_free(ptr);
- return je_malloc(size);
-}
-
// Hint Coverity that adding item to d avoids losing track
// of the memory allocated for item.
typedef struct {} dictitem_T;
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index a2c4e677d4..8abc43c2aa 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -23,6 +23,7 @@ set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators)
set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)
set(API_DISPATCH_GENERATOR ${GENERATOR_DIR}/gen_api_dispatch.lua)
set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua)
+set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua)
set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack)
set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack)
set(MSGPACK_LUA_C_BINDINGS ${GENERATED_DIR}/msgpack_lua_c_bindings.generated.c)
@@ -269,6 +270,7 @@ add_custom_command(
${API_HEADERS}
${MSGPACK_RPC_HEADERS}
${API_DISPATCH_GENERATOR}
+ ${GENERATOR_C_GRAMMAR}
${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua
)
@@ -300,6 +302,7 @@ add_custom_command(
${GENERATED_UI_EVENTS_METADATA}
DEPENDS
${API_UI_EVENTS_GENERATOR}
+ ${GENERATOR_C_GRAMMAR}
${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h
)
@@ -397,11 +400,6 @@ endif()
set(NVIM_EXEC_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUA_PREFERRED_LIBRARIES})
-# Don't use jemalloc in the unit test library.
-if(JEMALLOC_FOUND)
- list(APPEND NVIM_EXEC_LINK_LIBRARIES ${JEMALLOC_LIBRARIES})
-endif()
-
if(POLICY CMP0069)
cmake_policy(SET CMP0069 NEW)
endif()
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 5df0f0bb47..9cd178eaeb 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -104,7 +104,7 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
/// 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`
-/// @param opts Optional parameters. Currently not used.
+/// @param opts Optional parameters. Reserved for future use.
/// @param[out] err Details of an error that may have occurred
/// @return False when updates couldn't be enabled because the buffer isn't
/// loaded or `opts` contained an invalid key; otherwise True.
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 82c9a1da67..19a3368c1c 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -27,6 +27,7 @@
#include "nvim/version.h"
#include "nvim/lib/kvec.h"
#include "nvim/getchar.h"
+#include "nvim/fileio.h"
#include "nvim/ui.h"
/// Helper structure for vim_to_object
@@ -711,6 +712,12 @@ String cbuf_to_string(const char *buf, size_t size)
};
}
+String cstrn_to_string(const char *str, size_t maxsize)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return cbuf_to_string(str, strnlen(str, maxsize));
+}
+
/// Creates a String using the given C string. Unlike
/// cstr_to_string this function DOES NOT copy the C string.
///
@@ -725,6 +732,18 @@ String cstr_as_string(char *str) FUNC_ATTR_PURE
return (String){ .data = str, .size = strlen(str) };
}
+/// Return the owned memory of a ga as a String
+///
+/// Reinitializes the ga to a valid empty state.
+String ga_take_string(garray_T *ga)
+{
+ String str = { .data = (char *)ga->ga_data, .size = (size_t)ga->ga_len };
+ ga->ga_data = NULL;
+ ga->ga_len = 0;
+ ga->ga_maxlen = 0;
+ return str;
+}
+
/// Collects `n` buffer lines into array `l`, optionally replacing newlines
/// with NUL.
///
@@ -1094,7 +1113,7 @@ static void set_option_value_for(char *key,
{
win_T *save_curwin = NULL;
tabpage_T *save_curtab = NULL;
- bufref_T save_curbuf = { NULL, 0, 0 };
+ aco_save_T aco;
try_start();
switch (opt_type)
@@ -1115,9 +1134,9 @@ static void set_option_value_for(char *key,
restore_win(save_curwin, save_curtab, true);
break;
case SREQ_BUF:
- switch_buffer(&save_curbuf, (buf_T *)from);
+ aucmd_prepbuf(&aco, (buf_T *)from);
set_option_value_err(key, numval, stringval, opt_flags, err);
- restore_buffer(&save_curbuf);
+ aucmd_restbuf(&aco);
break;
case SREQ_GLOBAL:
set_option_value_err(key, numval, stringval, opt_flags, err);
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 7ba5251c60..9e9be588e3 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -113,6 +113,8 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->set_title = remote_ui_set_title;
ui->set_icon = remote_ui_set_icon;
ui->option_set = remote_ui_option_set;
+ ui->win_scroll_over_start = remote_ui_win_scroll_over_start;
+ ui->win_scroll_over_reset = remote_ui_win_scroll_over_reset;
ui->event = remote_ui_event;
ui->inspect = remote_ui_inspect;
@@ -130,6 +132,13 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
ui->ui_ext[kUILinegrid] = true;
}
+ if (ui->ui_ext[kUIMessages]) {
+ // This uses attribute indicies, so ext_linegrid is needed.
+ ui->ui_ext[kUILinegrid] = true;
+ // Cmdline uses the messages area, so it should be externalized too.
+ ui->ui_ext[kUICmdline] = true;
+ }
+
UIData *data = xmalloc(sizeof(UIData));
data->channel_id = channel_id;
data->buffer = (Array)ARRAY_DICT_INIT;
@@ -208,8 +217,9 @@ static void ui_set_option(UI *ui, bool init, String name, Object value,
return;
}
ui->rgb = value.data.boolean;
- // A little drastic, but only legacy uis need to use this option
- if (!init) {
+ // A little drastic, but only takes effect for legacy uis. For linegrid UI
+ // only changes metadata for nvim_list_uis(), no refresh needed.
+ if (!init && !ui->ui_ext[kUILinegrid]) {
ui_refresh();
}
return;
@@ -245,9 +255,8 @@ static void ui_set_option(UI *ui, bool init, String name, Object value,
name.data);
}
-/// Tell nvim to resize a grid. Nvim sends grid_resize event with the
-/// requested grid size is within size limits and with maximum allowed size
-/// otherwise.
+/// Tell Nvim to resize a grid. Triggers a grid_resize event with the requested
+/// grid size or the maximum size if it exceeds size limits.
///
/// On invalid grid handle, fails with error.
///
@@ -354,6 +363,9 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg,
Integer rgb_bg, Integer rgb_sp,
Integer cterm_fg, Integer cterm_bg)
{
+ if (!ui->ui_ext[kUITermColors]) {
+ HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp);
+ }
Array args = ARRAY_DICT_INIT;
ADD(args, INTEGER_OBJ(rgb_fg));
ADD(args, INTEGER_OBJ(rgb_bg));
@@ -460,7 +472,7 @@ static void remote_ui_put(UI *ui, const char *cell)
static void remote_ui_raw_line(UI *ui, Integer grid, Integer row,
Integer startcol, Integer endcol,
Integer clearcol, Integer clearattr,
- Boolean wrap, const schar_T *chunk,
+ LineFlags flags, const schar_T *chunk,
const sattr_T *attrs)
{
UIData *data = ui->data;
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 59a7780651..b57cf8d3ef 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -38,6 +38,9 @@ void set_icon(String icon)
FUNC_API_SINCE(3);
void option_set(String name, Object value)
FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL;
+// Stop event is not exported as such, represented by EOF in the msgpack stream.
+void stop(void)
+ FUNC_API_NOEXPORT;
// First revison of the grid protocol, used by default
void update_fg(Integer fg)
@@ -71,28 +74,39 @@ void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
Array info)
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
void grid_resize(Integer grid, Integer width, Integer height)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
void grid_clear(Integer grid)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
void grid_cursor_goto(Integer grid, Integer row, Integer col)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
void grid_line(Integer grid, Integer row, Integer col_start, Array data)
FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY;
void grid_scroll(Integer grid, Integer top, Integer bot,
Integer left, Integer right, Integer rows, Integer cols)
- FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
+ FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
void grid_destroy(Integer grid)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+// For perfomance and simplicity, we use the dense screen representation
+// in internal code, such as compositor and TUI. The remote_ui module will
+// translate this in to the public grid_line format.
+void raw_line(Integer grid, Integer row, Integer startcol,
+ Integer endcol, Integer clearcol, Integer clearattr,
+ LineFlags flags, const schar_T *chunk, const sattr_T *attrs)
+ FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL;
+
+void event(char *name, Array args, bool *args_consumed)
+ FUNC_API_NOEXPORT;
+
void win_pos(Integer grid, Integer win, Integer startrow,
Integer startcol, Integer width, Integer height)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_hide(Integer grid)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void win_scroll_over_start(void)
- FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
void win_scroll_over_reset(void)
- FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+ FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
void popupmenu_show(Array items, Integer selected,
Integer row, Integer col, Integer grid)
@@ -127,4 +141,17 @@ void wildmenu_select(Integer selected)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void wildmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
+
+void msg_show(String kind, Array content, Boolean replace_last)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_clear(void)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_showcmd(Array content)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_showmode(Array content)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_ruler(Array content)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
+void msg_history_show(Array entries)
+ FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
#endif // NVIM_API_UI_EVENTS_IN_H
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index ce7ef681ef..5a4d0a11e7 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -198,6 +198,9 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi)
/// @note |keycodes| like <CR> are translated, so "<" is special.
/// To input a literal "<", send <LT>.
///
+/// @note For mouse events use |nvim_input_mouse()|. The pseudokey form
+/// "<LeftMouse><col,row>" is deprecated since |api-level| 6.
+///
/// @param keys to be typed
/// @return Number of bytes actually written (can be fewer than
/// requested if the buffer becomes full).
@@ -207,6 +210,96 @@ Integer nvim_input(String keys)
return (Integer)input_enqueue(keys);
}
+/// Send mouse event from GUI.
+///
+/// The call is non-blocking. It doesn't wait on any resulting action, but
+/// queues the event to be processed soon by the event loop.
+///
+/// @note Currently this doesn't support "scripting" multiple mouse events
+/// by calling it multiple times in a loop: the intermediate mouse
+/// positions will be ignored. It should be used to implement real-time
+/// mouse input in a GUI. The deprecated pseudokey form
+/// ("<LeftMouse><col,row>") of |nvim_input()| has the same limitiation.
+///
+/// @param button Mouse button: one of "left", "right", "middle", "wheel".
+/// @param action For ordinary buttons, one of "press", "drag", "release".
+/// For the wheel, one of "up", "down", "left", "right".
+/// @param modifier String of modifiers each represented by a single char.
+/// The same specifiers are used as for a key press, except
+/// that the "-" separator is optional, so "C-A-", "c-a"
+/// and "CA" can all be used to specify Ctrl+Alt+click.
+/// @param grid Grid number if the client uses |ui-multigrid|, else 0.
+/// @param row Mouse row-position (zero-based, like redraw events)
+/// @param col Mouse column-position (zero-based, like redraw events)
+void nvim_input_mouse(String button, String action, String modifier,
+ Integer grid, Integer row, Integer col, Error *err)
+ FUNC_API_SINCE(6) FUNC_API_ASYNC
+{
+ if (button.data == NULL || action.data == NULL) {
+ goto error;
+ }
+
+ int code = 0;
+
+ if (strequal(button.data, "left")) {
+ code = KE_LEFTMOUSE;
+ } else if (strequal(button.data, "middle")) {
+ code = KE_MIDDLEMOUSE;
+ } else if (strequal(button.data, "right")) {
+ code = KE_RIGHTMOUSE;
+ } else if (strequal(button.data, "wheel")) {
+ code = KE_MOUSEDOWN;
+ } else {
+ goto error;
+ }
+
+ if (code == KE_MOUSEDOWN) {
+ if (strequal(action.data, "down")) {
+ code = KE_MOUSEUP;
+ } else if (strequal(action.data, "up")) {
+ code = KE_MOUSEDOWN;
+ } else if (strequal(action.data, "left")) {
+ code = KE_MOUSERIGHT;
+ } else if (strequal(action.data, "right")) {
+ code = KE_MOUSELEFT;
+ } else {
+ goto error;
+ }
+ } else {
+ if (strequal(action.data, "press")) {
+ // pass
+ } else if (strequal(action.data, "drag")) {
+ code += KE_LEFTDRAG - KE_LEFTMOUSE;
+ } else if (strequal(action.data, "release")) {
+ code += KE_LEFTRELEASE - KE_LEFTMOUSE;
+ } else {
+ goto error;
+ }
+ }
+
+ int modmask = 0;
+ for (size_t i = 0; i < modifier.size; i++) {
+ char byte = modifier.data[i];
+ if (byte == '-') {
+ continue;
+ }
+ int mod = name_to_mod_mask(byte);
+ if (mod == 0) {
+ api_set_error(err, kErrorTypeValidation,
+ "invalid modifier %c", byte);
+ return;
+ }
+ modmask |= mod;
+ }
+
+ input_enqueue_mouse(code, (uint8_t)modmask, (int)grid, (int)row, (int)col);
+ return;
+
+error:
+ api_set_error(err, kErrorTypeValidation,
+ "invalid button or action");
+}
+
/// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with
/// the internal representation.
///
@@ -598,7 +691,7 @@ void nvim_set_current_dir(String dir, Error *err)
try_end(err);
}
-/// Gets the current line
+/// Gets the current line.
///
/// @param[out] err Error details, if any
/// @return Current line string
@@ -608,7 +701,7 @@ String nvim_get_current_line(Error *err)
return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
}
-/// Sets the current line
+/// Sets the current line.
///
/// @param line Line contents
/// @param[out] err Error details, if any
@@ -618,7 +711,7 @@ void nvim_set_current_line(String line, Error *err)
buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err);
}
-/// Deletes the current line
+/// Deletes the current line.
///
/// @param[out] err Error details, if any
void nvim_del_current_line(Error *err)
@@ -627,7 +720,7 @@ void nvim_del_current_line(Error *err)
buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
}
-/// Gets a global (g:) variable
+/// Gets a global (g:) variable.
///
/// @param name Variable name
/// @param[out] err Error details, if any
@@ -638,7 +731,7 @@ Object nvim_get_var(String name, Error *err)
return dict_get_value(&globvardict, name, err);
}
-/// Sets a global (g:) variable
+/// Sets a global (g:) variable.
///
/// @param name Variable name
/// @param value Variable value
@@ -649,7 +742,7 @@ void nvim_set_var(String name, Object value, Error *err)
dict_set_var(&globvardict, name, value, false, false, err);
}
-/// Removes a global (g:) variable
+/// Removes a global (g:) variable.
///
/// @param name Variable name
/// @param[out] err Error details, if any
@@ -676,7 +769,7 @@ Object vim_del_var(String name, Error *err)
return dict_set_var(&globvardict, name, NIL, true, true, err);
}
-/// Gets a v: variable
+/// Gets a v: variable.
///
/// @param name Variable name
/// @param[out] err Error details, if any
@@ -687,7 +780,7 @@ Object nvim_get_vvar(String name, Error *err)
return dict_get_value(&vimvardict, name, err);
}
-/// Sets a v: variable, if it is not readonly
+/// Sets a v: variable, if it is not readonly.
///
/// @param name Variable name
/// @param value Variable value
@@ -698,7 +791,7 @@ void nvim_set_vvar(String name, Object value, Error *err)
dict_set_var(&vimvardict, name, value, false, false, err);
}
-/// Gets an option value string
+/// Gets an option value string.
///
/// @param name Option name
/// @param[out] err Error details, if any
@@ -709,7 +802,7 @@ Object nvim_get_option(String name, Error *err)
return get_option_from(NULL, SREQ_GLOBAL, name, err);
}
-/// Sets an option value
+/// Sets an option value.
///
/// @param name Option name
/// @param value New option value
@@ -777,7 +870,7 @@ ArrayOf(Buffer) nvim_list_bufs(void)
return rv;
}
-/// Gets the current buffer
+/// Gets the current buffer.
///
/// @return Buffer handle
Buffer nvim_get_current_buf(void)
@@ -786,7 +879,7 @@ Buffer nvim_get_current_buf(void)
return curbuf->handle;
}
-/// Sets the current buffer
+/// Sets the current buffer.
///
/// @param buffer Buffer handle
/// @param[out] err Error details, if any
@@ -809,7 +902,7 @@ void nvim_set_current_buf(Buffer buffer, Error *err)
}
}
-/// Gets the current list of window handles
+/// Gets the current list of window handles.
///
/// @return List of window handles
ArrayOf(Window) nvim_list_wins(void)
@@ -831,7 +924,7 @@ ArrayOf(Window) nvim_list_wins(void)
return rv;
}
-/// Gets the current window
+/// Gets the current window.
///
/// @return Window handle
Window nvim_get_current_win(void)
@@ -840,7 +933,7 @@ Window nvim_get_current_win(void)
return curwin->handle;
}
-/// Sets the current window
+/// Sets the current window.
///
/// @param window Window handle
void nvim_set_current_win(Window window, Error *err)
@@ -862,7 +955,39 @@ void nvim_set_current_win(Window window, Error *err)
}
}
-/// Gets the current list of tabpage handles
+/// Creates a new, empty, unnamed buffer.
+///
+/// @param listed Controls 'buflisted'
+/// @param scratch Creates a "throwaway" |scratch-buffer| for temporary work
+/// (always 'nomodified')
+/// @param[out] err Error details, if any
+/// @return Buffer handle, or 0 on error
+///
+/// @see buf_open_scratch
+Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
+ FUNC_API_SINCE(6)
+{
+ try_start();
+ buf_T *buf = buflist_new(NULL, NULL, (linenr_T)0,
+ BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
+ try_end(err);
+ if (buf == NULL) {
+ if (!ERROR_SET(err)) {
+ api_set_error(err, kErrorTypeException, "Failed to create buffer");
+ }
+ return 0;
+ }
+ if (scratch) {
+ WITH_BUFFER(buf, {
+ set_option_value("bh", 0L, "hide", OPT_LOCAL);
+ set_option_value("bt", 0L, "nofile", OPT_LOCAL);
+ set_option_value("swf", 0L, NULL, OPT_LOCAL);
+ });
+ }
+ return buf->b_fnum;
+}
+
+/// Gets the current list of tabpage handles.
///
/// @return List of tabpage handles
ArrayOf(Tabpage) nvim_list_tabpages(void)
@@ -884,7 +1009,7 @@ ArrayOf(Tabpage) nvim_list_tabpages(void)
return rv;
}
-/// Gets the current tabpage
+/// Gets the current tabpage.
///
/// @return Tabpage handle
Tabpage nvim_get_current_tabpage(void)
@@ -893,7 +1018,7 @@ Tabpage nvim_get_current_tabpage(void)
return curtab->handle;
}
-/// Sets the current tabpage
+/// Sets the current tabpage.
///
/// @param tabpage Tabpage handle
/// @param[out] err Error details, if any
@@ -916,7 +1041,7 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
}
}
-/// Creates a new namespace, or gets an existing one
+/// Creates a new namespace, or gets an existing one.
///
/// Namespaces are used for buffer highlights and virtual text, see
/// |nvim_buf_add_highlight()| and |nvim_buf_set_virtual_text()|.
@@ -942,7 +1067,7 @@ Integer nvim_create_namespace(String name)
return (Integer)id;
}
-/// Gets existing, non-anonymous namespaces
+/// Gets existing, non-anonymous namespaces.
///
/// @return dict that maps from names to namespace ids.
Dictionary nvim_get_namespaces(void)
@@ -959,7 +1084,7 @@ Dictionary nvim_get_namespaces(void)
return retval;
}
-/// Subscribes to event broadcasts
+/// Subscribes to event broadcasts.
///
/// @param channel_id Channel id (passed automatically by the dispatcher)
/// @param event Event type string
@@ -973,7 +1098,7 @@ void nvim_subscribe(uint64_t channel_id, String event)
rpc_subscribe(channel_id, e);
}
-/// Unsubscribes to event broadcasts
+/// Unsubscribes to event broadcasts.
///
/// @param channel_id Channel id (passed automatically by the dispatcher)
/// @param event Event type string
@@ -1283,7 +1408,7 @@ typedef struct {
typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// @endcond
-/// Parse a VimL expression
+/// Parse a VimL expression.
///
/// @param[in] expr Expression to parse. Is always treated as a single line.
/// @param[in] flags Flags:
@@ -1764,7 +1889,7 @@ static void write_msg(String message, bool to_err)
// Functions used for testing purposes
-/// Returns object given as argument
+/// Returns object given as argument.
///
/// This API function is used for testing. One should not rely on its presence
/// in plugins.
@@ -1777,7 +1902,7 @@ Object nvim__id(Object obj)
return copy_object(obj);
}
-/// Returns array given as argument
+/// Returns array given as argument.
///
/// This API function is used for testing. One should not rely on its presence
/// in plugins.
@@ -1790,7 +1915,7 @@ Array nvim__id_array(Array arr)
return copy_object(ARRAY_OBJ(arr)).data.array;
}
-/// Returns dictionary given as argument
+/// Returns dictionary given as argument.
///
/// This API function is used for testing. One should not rely on its presence
/// in plugins.
@@ -1803,7 +1928,7 @@ Dictionary nvim__id_dictionary(Dictionary dct)
return copy_object(DICTIONARY_OBJ(dct)).data.dictionary;
}
-/// Returns floating-point value given as argument
+/// Returns floating-point value given as argument.
///
/// This API function is used for testing. One should not rely on its presence
/// in plugins.
@@ -1927,19 +2052,19 @@ Object nvim_get_proc(Integer pid, Error *err)
return rvobj;
}
-/// Selects an item in the completion popupmenu
+/// Selects an item in the completion popupmenu.
///
-/// When insert completion is not active, this API call is silently ignored.
-/// It is mostly useful for an external UI using |ui-popupmenu| for instance
-/// to control the popupmenu with the mouse. But it can also be used in an
-/// insert mode mapping, use <cmd> mapping |:map-cmd| to ensure the mapping
-/// doesn't end completion mode.
+/// If |ins-completion| is not active this API call is silently ignored.
+/// Useful for an external UI using |ui-popupmenu| to control the popupmenu
+/// with the mouse. Can also be used in a mapping; use <cmd> |:map-cmd| to
+/// ensure the mapping doesn't end completion mode.
///
-/// @param item Index of the item to select, starting with zero. Pass in "-1"
-/// to select no item (restore original text).
+/// @param item Index (zero-based) of the item to select. Value of -1 selects
+/// nothing and restores the original text.
/// @param insert Whether the selection should be inserted in the buffer.
-/// @param finish If true, completion will be finished with this item, and the
-/// popupmenu dissmissed. Implies `insert`.
+/// @param finish Finish the completion and dismiss the popupmenu. Implies
+/// `insert`.
+/// @param opts Optional parameters. Reserved for future use.
void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish,
Dictionary opts, Error *err)
FUNC_API_SINCE(6)
diff --git a/src/nvim/assert.h b/src/nvim/assert.h
index 29195a49dc..34734f294d 100644
--- a/src/nvim/assert.h
+++ b/src/nvim/assert.h
@@ -1,6 +1,8 @@
#ifndef NVIM_ASSERT_H
#define NVIM_ASSERT_H
+#include "auto/config.h"
+
// support static asserts (aka compile-time asserts)
// some compilers don't properly support short-circuiting apparently, giving
@@ -78,7 +80,7 @@
# undef STATIC_ASSERT_PRAGMA_END
# define STATIC_ASSERT_PRAGMA_END \
- _Pragma("GCC diagnostic pop") \
+ _Pragma("GCC diagnostic pop")
// the same goes for clang in C99 mode, but we suppress a different warning
#elif defined(__clang__) && __has_extension(c_static_assert)
@@ -88,11 +90,11 @@
# undef STATIC_ASSERT_PRAGMA_START
# define STATIC_ASSERT_PRAGMA_START \
_Pragma("clang diagnostic push") \
- _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \
+ _Pragma("clang diagnostic ignored \"-Wc11-extensions\"")
# undef STATIC_ASSERT_PRAGMA_END
# define STATIC_ASSERT_PRAGMA_END \
- _Pragma("clang diagnostic pop") \
+ _Pragma("clang diagnostic pop")
// TODO(aktau): verify that this works, don't have MSVC on hand.
#elif _MSC_VER >= 1600
@@ -133,22 +135,32 @@
///
/// @param MAX Maximum value of the narrowest type of operand.
/// Not used if compiler supports __builtin_add_overflow.
-#if HAVE_BUILTIN_ADD_OVERFLOW
+#ifdef HAVE_BUILTIN_ADD_OVERFLOW
# define STRICT_ADD(a, b, c, t) \
- do { if (__builtin_add_overflow(a, b, c)) { abort(); } } while (0)
+ do { \
+ if (__builtin_add_overflow(a, b, c)) { \
+ ELOG("STRICT_ADD overflow"); \
+ abort(); \
+ } \
+ } while (0)
#else
# define STRICT_ADD(a, b, c, t) \
- do { *(c) = (t)(a + b); } while (0)
+ do { *(c) = (t)((a) + (b)); } while (0)
#endif
/// @def STRICT_SUB
/// @brief Subtracts (a - b) and stores result in `c`. Aborts on overflow.
-#if HAVE_BUILTIN_ADD_OVERFLOW
+#ifdef HAVE_BUILTIN_ADD_OVERFLOW
# define STRICT_SUB(a, b, c, t) \
- do { if (__builtin_sub_overflow(a, b, c)) { abort(); } } while (0)
+ do { \
+ if (__builtin_sub_overflow(a, b, c)) { \
+ ELOG("STRICT_SUB overflow"); \
+ abort(); \
+ } \
+ } while (0)
#else
# define STRICT_SUB(a, b, c, t) \
- do { *(c) = (t)(a - b); } while (0)
+ do { *(c) = (t)((a) - (b)); } while (0)
#endif
#endif // NVIM_ASSERT_H
diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c
index 9ad3414b79..3bb0fcec3b 100644
--- a/src/nvim/aucmd.c
+++ b/src/nvim/aucmd.c
@@ -6,6 +6,7 @@
#include "nvim/vim.h"
#include "nvim/main.h"
#include "nvim/ui.h"
+#include "nvim/aucmd.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "aucmd.c.generated.h"
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index 3cffd66dee..cc0ed0f587 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -74,6 +74,7 @@ return {
'SessionLoadPost', -- after loading a session file
'ShellCmdPost', -- after ":!cmd"
'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd".
+ 'Signal', -- after nvim process received a signal
'SourceCmd', -- sourcing a Vim script using command
'SourcePre', -- before sourcing a Vim script
'SpellFileMissing', -- spell file missing
@@ -115,6 +116,7 @@ return {
-- syntax file
nvim_specific = {
DirChanged=true,
+ Signal=true,
TabClosed=true,
TabNew=true,
TabNewEntered=true,
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index c15a6f1330..7fd4326914 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1472,6 +1472,10 @@ void set_curbuf(buf_T *buf, int action)
if (old_tw != curbuf->b_p_tw)
check_colorcolumn(curwin);
}
+
+ if (bufref_valid(&prevbufref) && prevbuf->terminal != NULL) {
+ terminal_check_size(prevbuf->terminal);
+ }
}
/*
@@ -1625,7 +1629,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags)
FileID file_id;
bool file_id_valid = (sfname != NULL
&& os_fileid((char *)sfname, &file_id));
- if (ffname != NULL && !(flags & BLN_DUMMY)
+ if (ffname != NULL && !(flags & (BLN_DUMMY | BLN_NEW))
&& (buf = buflist_findname_file_id(ffname, &file_id,
file_id_valid)) != NULL) {
xfree(ffname);
@@ -3220,6 +3224,9 @@ int build_stl_str_hl(
#define TMPLEN 70
char_u tmp[TMPLEN];
char_u *usefmt = fmt;
+ const int save_must_redraw = must_redraw;
+ const int save_redr_type = curwin->w_redr_type;
+ const int save_highlight_shcnaged = need_highlight_changed;
// When the format starts with "%!" then evaluate it as an expression and
// use the result as the actual format string.
@@ -3628,16 +3635,16 @@ int build_stl_str_hl(
vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum);
set_internal_string_var((char_u *)"g:actual_curbuf", tmp);
- buf_T *o_curbuf = curbuf;
- win_T *o_curwin = curwin;
+ buf_T *const save_curbuf = curbuf;
+ win_T *const save_curwin = curwin;
curwin = wp;
curbuf = wp->w_buffer;
// Note: The result stored in `t` is unused.
str = eval_to_string_safe(out_p, &t, use_sandbox);
- curwin = o_curwin;
- curbuf = o_curbuf;
+ curwin = save_curwin;
+ curbuf = save_curbuf;
// Remove the variable we just stored
do_unlet(S_LEN("g:actual_curbuf"), true);
@@ -3676,10 +3683,10 @@ int build_stl_str_hl(
{
// In list mode virtcol needs to be recomputed
colnr_T virtcol = wp->w_virtcol;
- if (wp->w_p_list && lcs_tab1 == NUL) {
- wp->w_p_list = FALSE;
+ if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) {
+ wp->w_p_list = false;
getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
- wp->w_p_list = TRUE;
+ wp->w_p_list = true;
}
++virtcol;
// Don't display %V if it's the same as %c.
@@ -4258,6 +4265,13 @@ int build_stl_str_hl(
cur_tab_rec->def.func = NULL;
}
+ // We do not want redrawing a stausline, ruler, title, etc. to trigger
+ // another redraw, it may cause an endless loop. This happens when a
+ // statusline changes a highlight group.
+ must_redraw = save_must_redraw;
+ curwin->w_redr_type = save_redr_type;
+ need_highlight_changed = save_highlight_shcnaged;
+
return width;
}
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index e61c312fb1..79bed049ea 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -31,7 +31,7 @@ enum bln_values {
BLN_CURBUF = 1, // May re-use curbuf for new buffer
BLN_LISTED = 2, // Put new buffer in buffer list
BLN_DUMMY = 4, // Allocating dummy buffer
- // TODO(mhinz): merge patch that introduces BLN_NEW
+ BLN_NEW = 8, // create a new buffer
BLN_NOOPT = 16, // Don't copy options to existing buffer
};
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 05688790c2..07d8ab2e0c 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -248,6 +248,10 @@ typedef struct {
# define w_p_scl w_onebuf_opt.wo_scl // 'signcolumn'
char_u *wo_winhl;
# define w_p_winhl w_onebuf_opt.wo_winhl // 'winhighlight'
+ char_u *wo_fcs;
+# define w_p_fcs w_onebuf_opt.wo_fcs // 'fillchars'
+ char_u *wo_lcs;
+# define w_p_lcs w_onebuf_opt.wo_lcs // 'listchars'
LastSet wo_scriptID[WV_COUNT]; // SIDs for window-local options
# define w_p_scriptID w_onebuf_opt.wo_scriptID
@@ -1003,6 +1007,31 @@ struct window_S {
colnr_T w_old_visual_col; ///< last known start of visual part
colnr_T w_old_curswant; ///< last known value of Curswant
+ // 'listchars' characters. Defaults set in set_chars_option().
+ struct {
+ int eol;
+ int ext;
+ int prec;
+ int nbsp;
+ int space;
+ int tab1; ///< first tab character
+ int tab2; ///< second tab character
+ int tab3; ///< third tab character
+ int trail;
+ int conceal;
+ } w_p_lcs_chars;
+
+ // 'fillchars' characters. Defaults set in set_chars_option().
+ struct {
+ int stl;
+ int stlnc;
+ int vert;
+ int fold;
+ int diff;
+ int msgsep;
+ int eob;
+ } w_p_fcs_chars;
+
/*
* "w_topline", "w_leftcol" and "w_skipcol" specify the offsets for
* displaying the buffer.
@@ -1034,6 +1063,13 @@ struct window_S {
int w_width; /* Width of window, excluding separation. */
int w_vsep_width; /* Number of separator columns (0 or 1). */
+ // inner size of window, which can be overridden by external UI
+ int w_height_inner;
+ int w_width_inner;
+ // external UI request. If non-zero, the inner size will use this.
+ int w_height_request;
+ int w_width_request;
+
/*
* === start of cached values ====
*/
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index c220c4e347..08ecff149c 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -749,8 +749,8 @@ int vim_strnsize(char_u *s, int len)
///
/// @return Number of characters.
#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
- if (*(p) == TAB && (!(wp)->w_p_list || lcs_tab1)) { \
- const int ts = (int) (buf)->b_p_ts; \
+ if (*(p) == TAB && (!(wp)->w_p_list || wp->w_p_lcs_chars.tab1)) { \
+ const int ts = (int)(buf)->b_p_ts; \
return (ts - (int)(col % ts)); \
} else { \
return ptr2cells(p); \
@@ -1022,12 +1022,12 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
&& vim_isbreak(c)
&& !vim_isbreak((int)s[1])
&& wp->w_p_wrap
- && (wp->w_grid.Columns != 0)) {
+ && (wp->w_width_inner != 0)) {
// Count all characters from first non-blank after a blank up to next
// non-blank after a blank.
numberextra = win_col_off(wp);
col2 = col;
- colmax = (colnr_T)(wp->w_grid.Columns - numberextra - col_adj);
+ colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj);
if (col >= colmax) {
colmax += col_adj;
@@ -1076,9 +1076,9 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
numberextra = numberwidth;
col += numberextra + mb_added;
- if (col >= (colnr_T)wp->w_grid.Columns) {
- col -= wp->w_grid.Columns;
- numberextra = wp->w_grid.Columns - (numberextra - win_col_off2(wp));
+ if (col >= (colnr_T)wp->w_width_inner) {
+ col -= wp->w_width_inner;
+ numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp));
if (col >= numberextra && numberextra > 0) {
col %= numberextra;
}
@@ -1097,17 +1097,17 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
numberwidth -= win_col_off2(wp);
}
- if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_grid.Columns)) {
+ if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_width_inner)) {
added = 0;
if (*p_sbr != NUL) {
- if (size + sbrlen + numberwidth > (colnr_T)wp->w_grid.Columns) {
+ if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) {
// Calculate effective window width.
- int width = (colnr_T)wp->w_grid.Columns - sbrlen - numberwidth;
- int prev_width = col ? ((colnr_T)wp->w_grid.Columns - (sbrlen + col))
+ int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth;
+ int prev_width = col ? ((colnr_T)wp->w_width_inner - (sbrlen + col))
: 0;
if (width == 0) {
- width = (colnr_T)wp->w_grid.Columns;
+ width = (colnr_T)wp->w_width_inner;
}
added += ((size - prev_width) / width) * vim_strsize(p_sbr);
if ((size - prev_width) % width) {
@@ -1149,7 +1149,7 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
{
int n;
- if ((*s == TAB) && (!wp->w_p_list || lcs_tab1)) {
+ if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
n = (int)wp->w_buffer->b_p_ts;
return n - (col % n);
}
@@ -1176,11 +1176,11 @@ bool in_win_border(win_T *wp, colnr_T vcol)
int width1; // width of first line (after line number)
int width2; // width of further lines
- if (wp->w_grid.Columns == 0) {
+ if (wp->w_width_inner == 0) {
// there is no border
return false;
}
- width1 = wp->w_grid.Columns - win_col_off(wp);
+ width1 = wp->w_width_inner - win_col_off(wp);
if ((int)vcol < width1 - 1) {
return false;
@@ -1241,7 +1241,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor,
// When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set
// use a simple loop.
// Also use this when 'list' is set but tabs take their normal size.
- if ((!wp->w_p_list || (lcs_tab1 != NUL))
+ if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL))
&& !wp->w_p_lbr
&& (*p_sbr == NUL)
&& !wp->w_p_bri ) {
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 6c1bd01ff5..bc14761877 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -121,11 +121,11 @@ static int coladvance2(
--curwin->w_curswant;
}
} else {
- int width = curwin->w_grid.Columns - win_col_off(curwin);
+ int width = curwin->w_width_inner - win_col_off(curwin);
if (finetune
&& curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
&& wcol >= (colnr_T)width) {
csize = linetabsize(line);
if (csize > 0)
@@ -230,7 +230,7 @@ static int coladvance2(
int b = (int)wcol - (int)col;
// The difference between wcol and col is used to set coladd.
- if (b > 0 && b < (MAXCOL - 2 * curwin->w_grid.Columns)) {
+ if (b > 0 && b < (MAXCOL - 2 * curwin->w_width_inner)) {
pos->coladd = b;
}
@@ -444,7 +444,7 @@ bool leftcol_changed(void)
bool retval = false;
changed_cline_bef_curs();
- lastcol = curwin->w_leftcol + curwin->w_grid.Columns - curwin_col_off() - 1;
+ lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1;
validate_virtcol();
/*
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 866161e5cf..ee4a48ff5d 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -1541,7 +1541,7 @@ static void diff_read(int idx_orig, int idx_new, diffout_T *dout)
diffstyle = DIFF_ED;
} else if ((STRNCMP(line, "@@ ", 3) == 0)) {
diffstyle = DIFF_UNIFIED;
- } else if ((STRNCMP(line, "--- ", 4) == 0)
+ } else if ((STRNCMP(line, "--- ", 4) == 0) // -V501
&& (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
&& (STRNCMP(line, "+++ ", 4) == 0)
&& (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index bb3c0ec196..62b35fa708 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -473,6 +473,8 @@ static void insert_enter(InsertState *s)
o_lnum = curwin->w_cursor.lnum;
}
+ pum_check_clear();
+
foldUpdateAfterInsert();
// When CTRL-C was typed got_int will be set, with the result
// that the autocommands won't be executed. When mapped got_int
@@ -566,7 +568,7 @@ static int insert_check(VimState *state)
if (curwin->w_wcol < s->mincol - curbuf->b_p_ts
&& curwin->w_wrow == curwin->w_winrow
- + curwin->w_grid.Rows - 1 - p_so
+ + curwin->w_height_inner - 1 - p_so
&& (curwin->w_cursor.lnum != curwin->w_topline
|| curwin->w_topfill > 0)) {
if (curwin->w_topfill > 0) {
@@ -1447,6 +1449,7 @@ ins_redraw (
redrawWinline(curwin, curwin->w_cursor.lnum);
}
+ pum_check_clear();
if (must_redraw) {
update_screen(0);
} else if (clear_cmdline || redraw_cmdline) {
@@ -2490,20 +2493,6 @@ void set_completion(colnr_T startcol, list_T *list)
static pumitem_T *compl_match_array = NULL;
static int compl_match_arraysize;
-/*
- * Update the screen and when there is any scrolling remove the popup menu.
- */
-static void ins_compl_upd_pum(void)
-{
- int h;
-
- if (compl_match_array != NULL) {
- h = curwin->w_cline_height;
- update_screen(0);
- if (h != curwin->w_cline_height)
- ins_compl_del_pum();
- }
-}
/*
* Remove any popup menu.
@@ -2511,7 +2500,7 @@ static void ins_compl_upd_pum(void)
static void ins_compl_del_pum(void)
{
if (compl_match_array != NULL) {
- pum_undisplay();
+ pum_undisplay(false);
xfree(compl_match_array);
compl_match_array = NULL;
}
@@ -4305,17 +4294,14 @@ ins_compl_next (
}
if (!allow_get_expansion) {
- /* may undisplay the popup menu first */
- ins_compl_upd_pum();
-
- /* redraw to show the user what was inserted */
+ // redraw to show the user what was inserted
update_screen(0);
- /* display the updated popup menu */
+ // display the updated popup menu
ins_compl_show_pum();
- /* Delete old text to be replaced, since we're still searching and
- * don't want to match ourselves! */
+ // Delete old text to be replaced, since we're still searching and
+ // don't want to match ourselves!
ins_compl_delete();
}
@@ -4862,8 +4848,6 @@ static int ins_complete(int c, bool enable_pum)
save_w_leftcol = curwin->w_leftcol;
n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false);
- /* may undisplay the popup menu */
- ins_compl_upd_pum();
if (n > 1) /* all matches have been found */
compl_matches = n;
@@ -5868,7 +5852,7 @@ static void check_auto_format(
/*
* Find out textwidth to be used for formatting:
* if 'textwidth' option is set, use it
- * else if 'wrapmargin' option is set, use curwin->w_grid.Columns-'wrapmargin'
+ * else if 'wrapmargin' option is set, use curwin->w_width_inner-'wrapmargin'
* if invalid value, use 0.
* Set default to window width (maximum 79) for "gq" operator.
*/
@@ -5883,7 +5867,7 @@ comp_textwidth (
if (textwidth == 0 && curbuf->b_p_wm) {
/* The width is the window width minus 'wrapmargin' minus all the
* things that add to the margin. */
- textwidth = curwin->w_grid.Columns - curbuf->b_p_wm;
+ textwidth = curwin->w_width_inner - curbuf->b_p_wm;
if (cmdwin_type != 0) {
textwidth -= 1;
}
@@ -5899,7 +5883,7 @@ comp_textwidth (
if (textwidth < 0)
textwidth = 0;
if (ff && textwidth == 0) {
- textwidth = curwin->w_grid.Columns - 1;
+ textwidth = curwin->w_width_inner - 1;
if (textwidth > 79) {
textwidth = 79;
}
@@ -7144,11 +7128,17 @@ static void ins_reg(void)
* message for it. Only call it explicitly. */
++no_u_sync;
if (regname == '=') {
- /* Sync undo when evaluating the expression calls setline() or
- * append(), so that it can be undone separately. */
+ pos_T curpos = curwin->w_cursor;
+
+ // Sync undo when evaluating the expression calls setline() or
+ // append(), so that it can be undone separately.
u_sync_once = 2;
regname = get_expr_register();
+
+ // Cursor may be moved back a column.
+ curwin->w_cursor = curpos;
+ check_cursor();
}
if (regname == NUL || !valid_yank_reg(regname, false)) {
vim_beep(BO_REG);
@@ -7939,15 +7929,13 @@ static void ins_mouse(int c)
static void ins_mousescroll(int dir)
{
win_T *const old_curwin = curwin;
- bool did_scroll = false;
pos_T tpos = curwin->w_cursor;
if (mouse_row >= 0 && mouse_col >= 0) {
- int row = mouse_row;
- int col = mouse_col;
+ int row = mouse_row, col = mouse_col, grid = mouse_grid;
// find the window at the pointer coordinates
- win_T *const wp = mouse_find_win(&row, &col);
+ win_T *wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return;
}
@@ -7970,7 +7958,6 @@ static void ins_mousescroll(int dir)
} else {
mouse_scroll_horiz(dir);
}
- did_scroll = true;
}
curwin->w_redr_status = TRUE;
@@ -7978,14 +7965,6 @@ static void ins_mousescroll(int dir)
curwin = old_curwin;
curbuf = curwin->w_buffer;
- /* The popup menu may overlay the window, need to redraw it.
- * TODO: Would be more efficient to only redraw the windows that are
- * overlapped by the popup menu. */
- if (pum_visible() && did_scroll) {
- redraw_all_later(NOT_VALID);
- ins_compl_show_pum();
- }
-
if (!equalpos(curwin->w_cursor, tpos)) {
start_arrow(&tpos);
can_cindent = true;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 4cf8a01ddb..d63e45d3c7 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -5958,7 +5958,9 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate)
#pragma function (floor)
#endif
+PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES
# include "funcs.generated.h"
+PRAGMA_DIAG_POP
#endif
/*
@@ -9521,6 +9523,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (is_mouse_key(n)) {
int row = mouse_row;
int col = mouse_col;
+ int grid = mouse_grid;
win_T *win;
linenr_T lnum;
win_T *wp;
@@ -9529,7 +9532,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (row >= 0 && col >= 0) {
/* Find the window at the mouse coordinates and compute the
* text position. */
- win = mouse_find_win(&row, &col);
+ win = mouse_find_win(&grid, &row, &col);
if (win == NULL) {
return;
}
@@ -11264,7 +11267,7 @@ void get_user_input(const typval_T *const argvars,
// Only the part of the message after the last NL is considered as
// prompt for the command line, unlsess cmdline is externalized
const char *p = prompt;
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
const char *lastnl = strrchr(prompt, '\n');
if (lastnl != NULL) {
p = lastnl+1;
@@ -16424,7 +16427,9 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) {
cchar = syn_get_sub_char();
if (cchar == NUL && curwin->w_p_cole == 1) {
- cchar = (lcs_conceal == NUL) ? ' ' : lcs_conceal;
+ cchar = (curwin->w_p_lcs_chars.conceal == NUL)
+ ? ' '
+ : curwin->w_p_lcs_chars.conceal;
}
if (cchar != NUL) {
utf_char2bytes(cchar, str);
@@ -16795,10 +16800,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- uint16_t term_width = MAX(0, curwin->w_grid.Columns - win_col_off(curwin));
+ uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin));
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
true, false, false, cwd,
- term_width, curwin->w_grid.Rows,
+ term_width, curwin->w_height_inner,
xstrdup("xterm-256color"),
&rettv->vval.v_number);
if (rettv->vval.v_number <= 0) {
@@ -19592,23 +19597,9 @@ void ex_echo(exarg_T *eap)
msg_puts_attr(" ", echo_attr);
}
char *tofree = encode_tv2echo(&rettv, NULL);
- const char *p = tofree;
- if (p != NULL) {
- for (; *p != NUL && !got_int; ++p) {
- if (*p == '\n' || *p == '\r' || *p == TAB) {
- if (*p != TAB && needclr) {
- /* remove any text still there from the command */
- msg_clr_eos();
- needclr = false;
- }
- msg_putchar_attr((uint8_t)(*p), echo_attr);
- } else {
- int i = (*mb_ptr2len)((const char_u *)p);
-
- (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr);
- p += i - 1;
- }
- }
+ if (*tofree != NUL) {
+ msg_ext_set_kind("echo");
+ msg_multiline_attr(tofree, echo_attr);
}
xfree(tofree);
}
@@ -19701,11 +19692,13 @@ void ex_execute(exarg_T *eap)
}
if (eap->cmdidx == CMD_echomsg) {
+ msg_ext_set_kind("echomsg");
MSG_ATTR(ga.ga_data, echo_attr);
ui_flush();
} else if (eap->cmdidx == CMD_echoerr) {
/* We don't want to abort following commands, restore did_emsg. */
save_did_emsg = did_emsg;
+ msg_ext_set_kind("echoerr");
EMSG((char_u *)ga.ga_data);
if (!force_abort)
did_emsg = save_did_emsg;
@@ -19902,13 +19895,15 @@ void ex_function(exarg_T *eap)
if (FUNCLINE(fp, j) == NULL)
continue;
msg_putchar('\n');
- msg_outnum((long)(j + 1));
- if (j < 9)
+ msg_outnum((long)j + 1);
+ if (j < 9) {
msg_putchar(' ');
- if (j < 99)
+ }
+ if (j < 99) {
msg_putchar(' ');
- msg_prt_line(FUNCLINE(fp, j), FALSE);
- ui_flush(); /* show a line at a time */
+ }
+ msg_prt_line(FUNCLINE(fp, j), false);
+ ui_flush(); // show a line at a time
os_breakcheck();
}
if (!got_int) {
@@ -19963,7 +19958,7 @@ void ex_function(exarg_T *eap)
goto errret_2;
}
- if (KeyTyped && ui_is_external(kUICmdline)) {
+ if (KeyTyped && ui_has(kUICmdline)) {
show_block = true;
ui_ext_cmdline_block_append(0, (const char *)eap->cmd);
}
@@ -20019,7 +20014,7 @@ void ex_function(exarg_T *eap)
if (!eap->skip && did_emsg)
goto erret;
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
msg_putchar('\n'); // don't overwrite the function name
}
cmdline_row = msg_row;
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 4d75c7bda1..42999ddd62 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -7,6 +7,7 @@
#include "nvim/eval/typval.h"
#include "nvim/eval.h"
+#include "nvim/eval/decode.h"
#include "nvim/eval/encode.h"
#include "nvim/ascii.h"
#include "nvim/macros.h"
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 64658b52d9..6074e4ee69 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -19,6 +19,7 @@
#include "nvim/eval/typval.h"
#include "nvim/garray.h"
#include "nvim/mbyte.h"
+#include "nvim/math.h"
#include "nvim/message.h"
#include "nvim/memory.h"
#include "nvim/charset.h" // vim_isprintc()
@@ -28,11 +29,6 @@
#include "nvim/lib/kvec.h"
#include "nvim/eval/typval_encode.h"
-#ifdef __MINGW32__
-# undef fpclassify
-# define fpclassify __fpclassify
-#endif
-
#define ga_concat(a, b) ga_concat(a, (char_u *)b)
#define utf_ptr2char(b) utf_ptr2char((char_u *)b)
#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
@@ -327,7 +323,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
const float_T flt_ = (flt); \
- switch (fpclassify(flt_)) { \
+ switch (xfpclassify(flt_)) { \
case FP_NAN: { \
ga_concat(gap, (char_u *) "str2float('nan')"); \
break; \
@@ -531,7 +527,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
const float_T flt_ = (flt); \
- switch (fpclassify(flt_)) { \
+ switch (xfpclassify(flt_)) { \
case FP_NAN: { \
EMSG(_("E474: Unable to represent NaN value in JSON")); \
return FAIL; \
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 85844c37bd..b3ac456979 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -257,7 +257,7 @@ void ex_align(exarg_T *eap)
if (width <= 0)
width = curbuf->b_p_tw;
if (width == 0 && curbuf->b_p_wm > 0) {
- width = curwin->w_grid.Columns - curbuf->b_p_wm;
+ width = curwin->w_width_inner - curbuf->b_p_wm;
}
if (width <= 0) {
width = 80;
@@ -2678,6 +2678,10 @@ int do_ecmd(
theend:
+ if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->terminal != NULL) {
+ terminal_check_size(old_curbuf.br_buf->terminal);
+ }
+
if (did_inc_redrawing_disabled) {
RedrawingDisabled--;
}
@@ -2872,11 +2876,11 @@ void ex_z(exarg_T *eap)
// Vi compatible: ":z!" uses display height, without a count uses
// 'scroll'
if (eap->forceit) {
- bigness = curwin->w_grid.Rows;
+ bigness = curwin->w_height_inner;
} else if (ONE_WINDOW) {
bigness = curwin->w_p_scr * 2;
} else {
- bigness = curwin->w_grid.Rows - 3;
+ bigness = curwin->w_height_inner - 3;
}
if (bigness < 1) {
bigness = 1;
@@ -4131,7 +4135,7 @@ skip:
buf_T *preview_buf = NULL;
size_t subsize = preview_lines.subresults.size;
if (preview && !aborting()) {
- if (got_quit) { // Substitution is too slow, disable 'inccommand'.
+ if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable.
set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE,
SID_NONE);
} else if (*p_icm != NUL && pat != NULL) {
@@ -4895,7 +4899,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches,
}
}
- *matches = (char_u **)"";
+ *matches = NULL;
*num_matches = 0;
int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE;
if (keep_lang) {
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index f5c16d883a..cda80dad39 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -9191,33 +9191,38 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin)
if (restore_size && (ssop_flags & SSOP_WINSIZE)) {
for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) {
- if (!ses_do_win(wp))
+ if (!ses_do_win(wp)) {
continue;
- ++n;
+ }
+ n++;
- /* restore height when not full height */
+ // restore height when not full height
if (wp->w_height + wp->w_status_height < topframe->fr_height
&& (fprintf(fd,
"exe '%dresize ' . ((&lines * %" PRId64
" + %" PRId64 ") / %" PRId64 ")",
- n, (int64_t)wp->w_grid.Rows,
- (int64_t)(Rows / 2), (int64_t)Rows) < 0
- || put_eol(fd) == FAIL))
+ n, (int64_t)wp->w_height,
+ (int64_t)Rows / 2, (int64_t)Rows) < 0
+ || put_eol(fd) == FAIL)) {
return FAIL;
+ }
- /* restore width when not full width */
+ // restore width when not full width
if (wp->w_width < Columns
- && (fprintf(fd, "exe 'vert %dresize ' . ((&columns * %" PRId64
- " + %" PRId64 ") / %" PRId64 ")",
- n, (int64_t)wp->w_width, (int64_t)(Columns / 2),
+ && (fprintf(fd,
+ "exe 'vert %dresize ' . ((&columns * %" PRId64
+ " + %" PRId64 ") / %" PRId64 ")",
+ n, (int64_t)wp->w_width, (int64_t)Columns / 2,
(int64_t)Columns) < 0
- || put_eol(fd) == FAIL))
+ || put_eol(fd) == FAIL)) {
return FAIL;
+ }
}
} else {
- /* Just equalise window sizes */
- if (put_line(fd, "wincmd =") == FAIL)
+ // Just equalise window sizes
+ if (put_line(fd, "wincmd =") == FAIL) {
return FAIL;
+ }
}
return OK;
}
@@ -9365,10 +9370,11 @@ put_view(
* arguments may have been deleted, check if the index is valid. */
if (wp->w_arg_idx != current_arg_idx && wp->w_arg_idx < WARGCOUNT(wp)
&& flagp == &ssop_flags) {
- if (fprintf(fd, "%" PRId64 "argu", (int64_t)(wp->w_arg_idx + 1)) < 0
- || put_eol(fd) == FAIL)
+ if (fprintf(fd, "%" PRId64 "argu", (int64_t)wp->w_arg_idx + 1) < 0
+ || put_eol(fd) == FAIL) {
return FAIL;
- did_next = TRUE;
+ }
+ did_next = true;
}
/* Edit the file. Skip this when ":next" already did it. */
@@ -9385,9 +9391,9 @@ put_view(
// Note, if a buffer for that file already exists, use :badd to
// edit that buffer, to not lose folding information (:edit resets
// folds in other buffers)
- if (fputs("if bufexists('", fd) < 0
+ if (fputs("if bufexists(\"", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
- || fputs("') | buffer ", fd) < 0
+ || fputs("\") | buffer ", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
|| fputs(" | else | edit ", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, false) == FAIL
@@ -9463,8 +9469,8 @@ put_view(
" * winheight(0) + %" PRId64 ") / %" PRId64 ")",
(int64_t)wp->w_cursor.lnum,
(int64_t)(wp->w_cursor.lnum - wp->w_topline),
- (int64_t)(wp->w_grid.Rows / 2),
- (int64_t)wp->w_grid.Rows) < 0
+ (int64_t)(wp->w_height_inner / 2),
+ (int64_t)wp->w_height_inner) < 0
|| put_eol(fd) == FAIL
|| put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL
|| put_line(fd, "exe s:l") == FAIL
@@ -9478,21 +9484,23 @@ put_view(
return FAIL;
} else {
if (!wp->w_p_wrap && wp->w_leftcol > 0 && wp->w_width > 0) {
- if (fprintf(fd, "let s:c = %" PRId64 " - ((%" PRId64
- " * winwidth(0) + %" PRId64 ") / %" PRId64 ")",
- (int64_t)(wp->w_virtcol + 1),
+ if (fprintf(fd,
+ "let s:c = %" PRId64 " - ((%" PRId64
+ " * winwidth(0) + %" PRId64 ") / %" PRId64 ")",
+ (int64_t)wp->w_virtcol + 1,
(int64_t)(wp->w_virtcol - wp->w_leftcol),
(int64_t)(wp->w_width / 2),
(int64_t)wp->w_width) < 0
|| put_eol(fd) == FAIL
|| put_line(fd, "if s:c > 0") == FAIL
|| fprintf(fd, " exe 'normal! ' . s:c . '|zs' . %" PRId64 " . '|'",
- (int64_t)(wp->w_virtcol + 1)) < 0
+ (int64_t)wp->w_virtcol + 1) < 0
|| put_eol(fd) == FAIL
|| put_line(fd, "else") == FAIL
|| put_view_curpos(fd, wp, " ") == FAIL
- || put_line(fd, "endif") == FAIL)
+ || put_line(fd, "endif") == FAIL) {
return FAIL;
+ }
} else if (put_view_curpos(fd, wp, "") == FAIL) {
return FAIL;
}
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 8efb027575..d70b81409d 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -51,6 +51,7 @@
#include "nvim/option.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/popupmnu.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"
@@ -307,6 +308,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
gotocmdline(true);
redrawcmdprompt(); // draw prompt or indent
set_cmdspos();
+ if (!msg_scroll) {
+ msg_ext_clear(false);
+ }
}
s->xpc.xp_context = EXPAND_NOTHING;
s->xpc.xp_backslash = XP_BS_NONE;
@@ -493,8 +497,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
char_u *p = ccline.cmdbuff;
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
ui_call_cmdline_hide(ccline.level);
+ if (msg_ext_is_visible()) {
+ msg_ext_did_cmdline = true;
+ if (must_redraw < VALID) {
+ must_redraw = VALID;
+ }
+ }
}
cmdline_level--;
@@ -613,7 +623,7 @@ static int command_line_execute(VimState *state, int key)
if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm
&& s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A
&& s->c != Ctrl_L) {
- if (ui_is_external(kUIWildmenu)) {
+ if (ui_has(kUIWildmenu)) {
ui_call_wildmenu_hide();
}
if (s->xpc.xp_numfiles != -1) {
@@ -895,7 +905,7 @@ static int command_line_execute(VimState *state, int key)
}
if (!cmd_silent) {
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
ui_cursor_goto(msg_row, 0);
}
ui_flush();
@@ -1253,7 +1263,7 @@ static int command_line_handle_key(CommandLineState *s)
xfree(ccline.cmdbuff); // no commandline to return
ccline.cmdbuff = NULL;
- if (!cmd_silent && !ui_is_external(kUICmdline)) {
+ if (!cmd_silent && !ui_has(kUICmdline)) {
if (cmdmsg_rl) {
msg_col = Columns;
} else {
@@ -1703,7 +1713,7 @@ static int command_line_handle_key(CommandLineState *s)
s->do_abbr = false; // don't do abbreviation now
// may need to remove ^ when composing char was typed
if (enc_utf8 && utf_iscomposing(s->c) && !cmd_silent) {
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
// TODO(bfredl): why not make unputcmdline also work with true?
unputcmdline();
} else {
@@ -1967,7 +1977,7 @@ static int command_line_changed(CommandLineState *s)
// Do it only when there are no characters left to read
// to avoid useless intermediate redraws.
// if cmdline is external the ui handles shaping, no redraw needed.
- if (!ui_is_external(kUICmdline) && vpeekc() == NUL) {
+ if (!ui_has(kUICmdline) && vpeekc() == NUL) {
redrawcmd();
}
}
@@ -2834,7 +2844,7 @@ static void draw_cmdline(int start, int len)
return;
}
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
ccline.special_char = NUL;
ccline.redraw_state = kCmdRedrawAll;
return;
@@ -3027,7 +3037,7 @@ void ui_ext_cmdline_block_leave(void)
/// assumes "redrawcmdline()" will already be invoked
void cmdline_screen_cleared(void)
{
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
return;
}
@@ -3052,7 +3062,7 @@ void cmdline_screen_cleared(void)
/// called by ui_flush, do what redraws neccessary to keep cmdline updated.
void cmdline_ui_flush(void)
{
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
return;
}
int level = ccline.level;
@@ -3081,7 +3091,7 @@ void putcmdline(int c, int shift)
if (cmd_silent) {
return;
}
- if (!ui_is_external(kUICmdline)) {
+ if (!ui_has(kUICmdline)) {
msg_no_more = true;
msg_putchar(c);
if (shift) {
@@ -3107,7 +3117,7 @@ void unputcmdline(void)
return;
}
msg_no_more = true;
- if (ccline.cmdlen == ccline.cmdpos && !ui_is_external(kUICmdline)) {
+ if (ccline.cmdlen == ccline.cmdpos && !ui_has(kUICmdline)) {
msg_putchar(' ');
} else {
draw_cmdline(ccline.cmdpos, mb_ptr2len(ccline.cmdbuff + ccline.cmdpos));
@@ -3393,14 +3403,11 @@ void cmdline_paste_str(char_u *s, int literally)
}
}
-/*
- * Delete characters on the command line, from "from" to the current
- * position.
- */
+/// Delete characters on the command line, from "from" to the current position.
static void cmdline_del(int from)
{
memmove(ccline.cmdbuff + from, ccline.cmdbuff + ccline.cmdpos,
- (size_t)(ccline.cmdlen - ccline.cmdpos + 1));
+ (size_t)ccline.cmdlen - ccline.cmdpos + 1);
ccline.cmdlen -= ccline.cmdpos - from;
ccline.cmdpos = from;
}
@@ -3425,7 +3432,7 @@ static void redrawcmdprompt(void)
if (cmd_silent)
return;
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
ccline.redraw_state = kCmdRedrawAll;
return;
}
@@ -3454,7 +3461,7 @@ void redrawcmd(void)
if (cmd_silent)
return;
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
draw_cmdline(0, ccline.cmdlen);
return;
}
@@ -3502,7 +3509,7 @@ static void cursorcmd(void)
if (cmd_silent)
return;
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
if (ccline.redraw_state < kCmdRedrawPos) {
ccline.redraw_state = kCmdRedrawPos;
}
@@ -3527,7 +3534,7 @@ static void cursorcmd(void)
void gotocmdline(int clr)
{
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
return;
}
msg_start();
@@ -3615,7 +3622,7 @@ nextwild (
return FAIL;
}
- if (!ui_is_external(kUIWildmenu)) {
+ if (!(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
MSG_PUTS("..."); // show that we are busy
ui_flush();
}
@@ -3661,8 +3668,8 @@ nextwild (
xp->xp_pattern = ccline.cmdbuff + i;
}
memmove(&ccline.cmdbuff[ccline.cmdpos + difflen],
- &ccline.cmdbuff[ccline.cmdpos],
- (size_t)(ccline.cmdlen - ccline.cmdpos + 1));
+ &ccline.cmdbuff[ccline.cmdpos],
+ (size_t)ccline.cmdlen - ccline.cmdpos + 1);
memmove(&ccline.cmdbuff[i], p2, STRLEN(p2));
ccline.cmdlen += difflen;
ccline.cmdpos += difflen;
@@ -3765,7 +3772,7 @@ ExpandOne (
findex = -1;
}
if (p_wmnu) {
- if (ui_is_external(kUIWildmenu)) {
+ if (ui_has(kUIWildmenu)) {
ui_call_wildmenu_select(findex);
} else {
win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files,
@@ -4120,7 +4127,7 @@ static int showmatches(expand_T *xp, int wildmenu)
showtail = cmd_showtail;
}
- if (ui_is_external(kUIWildmenu)) {
+ if (ui_has(kUIWildmenu)) {
Array args = ARRAY_DICT_INIT;
for (i = 0; i < num_files; i++) {
ADD(args, STRING_OBJ(cstr_to_string((char *)files_found[i])));
@@ -4705,7 +4712,7 @@ ExpandFromContext (
return ret;
}
- *file = (char_u **)"";
+ *file = NULL;
*num_file = 0;
if (xp->xp_context == EXPAND_HELP) {
/* With an empty argument we would get all the help tags, which is
@@ -6054,7 +6061,12 @@ static int open_cmdwin(void)
/* Don't execute autocommands while creating the window. */
block_autocmds();
- /* don't use a new tab page */
+
+ // When using completion in Insert mode with <C-R>=<C-F> one can open the
+ // command line window, but we don't want the popup menu then.
+ pum_undisplay(true);
+
+ // don't use a new tab page
cmdmod.tab = 0;
cmdmod.noswapfile = 1;
@@ -6121,7 +6133,7 @@ static int open_cmdwin(void)
curwin->w_cursor.col = ccline.cmdpos;
changed_line_abv_curs();
invalidate_botline();
- if (ui_is_external(kUICmdline)) {
+ if (ui_has(kUICmdline)) {
ccline.redraw_state = kCmdRedrawNone;
ui_call_cmdline_hide(ccline.level);
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index ba154ea36a..6356290b9c 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -6917,6 +6917,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
|| event == EVENT_REMOTEREPLY
|| event == EVENT_SPELLFILEMISSING
|| event == EVENT_SYNTAX
+ || event == EVENT_SIGNAL
|| event == EVENT_TABCLOSED) {
fname = vim_strsave(fname);
} else {
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index 2d2af54c95..74fd9d89cb 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -91,8 +91,8 @@ void ga_grow(garray_T *gap, int n)
}
int new_maxlen = gap->ga_len + n;
- size_t new_size = (size_t)(gap->ga_itemsize * new_maxlen);
- size_t old_size = (size_t)(gap->ga_itemsize * gap->ga_maxlen);
+ size_t new_size = (size_t)gap->ga_itemsize * (size_t)new_maxlen;
+ size_t old_size = (size_t)gap->ga_itemsize * (size_t)gap->ga_maxlen;
// reallocate and clear the new memory
char *pp = xrealloc(gap->ga_data, new_size);
diff --git a/src/nvim/garray.h b/src/nvim/garray.h
index 58738df691..e2cbdd4eab 100644
--- a/src/nvim/garray.h
+++ b/src/nvim/garray.h
@@ -18,6 +18,7 @@ typedef struct growarray {
} garray_T;
#define GA_EMPTY_INIT_VALUE { 0, 0, 0, 1, NULL }
+#define GA_INIT(itemsize, growsize) { 0, 0, (itemsize), (growsize), NULL }
#define GA_EMPTY(ga_ptr) ((ga_ptr)->ga_len <= 0)
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index d3047e1a9c..40bdc3e30c 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -25,6 +25,7 @@ local c_id = (
local c_void = P('void')
local c_param_type = (
((P('Error') * fill * P('*') * fill) * Cc('error')) +
+ C((P('const ') ^ -1) * (c_id) * (ws ^ 1) * P('*')) +
(C(c_id) * (ws ^ 1))
)
local c_type = (C(c_void) * (ws ^ 1)) + c_param_type
@@ -43,6 +44,7 @@ local c_proto = Ct(
(fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1) *
(fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) *
(fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) *
+ (fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) *
fill * P(';')
)
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index bd9650e4d1..3703b76973 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -448,6 +448,7 @@ for _, fn in ipairs(functions) do
end
output:write(string.format([[
+void nlua_add_api_functions(lua_State *lstate); // silence -Wmissing-prototypes
void nlua_add_api_functions(lua_State *lstate)
FUNC_ATTR_NONNULL_ALL
{
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index e76b601d8a..c8ab310b02 100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -54,7 +54,7 @@ for i = 1, #events do
ev = events[i]
assert(ev.return_type == 'void')
- if ev.since == nil then
+ if ev.since == nil and not ev.noexport then
print("Ui event "..ev.name.." lacks since field.\n")
os.exit(1)
end
@@ -65,7 +65,7 @@ for i = 1, #events do
write_signature(proto_output, ev, 'UI *ui')
proto_output:write(';\n')
- if not ev.remote_impl then
+ if not ev.remote_impl and not ev.noexport then
remote_output:write('static void remote_ui_'..ev.name)
write_signature(remote_output, ev, 'UI *ui')
remote_output:write('\n{\n')
@@ -74,8 +74,7 @@ for i = 1, #events do
remote_output:write('}\n\n')
end
- if not ev.bridge_impl then
-
+ if not ev.bridge_impl and not ev.noexport then
send, argv, recv, recv_argv, recv_cleanup = '', '', '', '', ''
argc = 1
for j = 1, #ev.parameters do
@@ -138,21 +137,36 @@ for i = 1, #events do
call_output:write('\n{\n')
if ev.remote_only then
write_arglist(call_output, ev, false)
- call_output:write(' UI_LOG('..ev.name..', 0);\n')
+ call_output:write(' UI_LOG('..ev.name..');\n')
call_output:write(' ui_event("'..ev.name..'", args);\n')
+ elseif ev.compositor_impl then
+ call_output:write(' UI_CALL')
+ write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true)
+ call_output:write(";\n")
else
call_output:write(' UI_CALL')
- write_signature(call_output, ev, ev.name, true)
+ write_signature(call_output, ev, 'true, '..ev.name..', ui', true)
call_output:write(";\n")
end
call_output:write("}\n\n")
end
+ if ev.compositor_impl then
+ call_output:write('void ui_composed_call_'..ev.name)
+ write_signature(call_output, ev, '')
+ call_output:write('\n{\n')
+ call_output:write(' UI_CALL')
+ write_signature(call_output, ev, 'ui->composed, '..ev.name..', ui', true)
+ call_output:write(";\n")
+ call_output:write("}\n\n")
+ end
+
end
proto_output:close()
call_output:close()
remote_output:close()
+bridge_output:close()
-- don't expose internal attributes like "impl_name" in public metadata
exported_attributes = {'name', 'parameters',
@@ -168,7 +182,9 @@ for _,ev in ipairs(events) do
p[1] = 'Dictionary'
end
end
- exported_events[#exported_events+1] = ev_exported
+ if not ev.noexport then
+ exported_events[#exported_events+1] = ev_exported
+ end
end
packed = mpack.pack(exported_events)
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 53e9846c2d..b24acb5ebb 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1187,10 +1187,11 @@ void save_typebuf(void)
alloc_typebuf();
}
-static int old_char = -1; /* character put back by vungetc() */
-static int old_mod_mask; /* mod_mask for ungotten character */
-static int old_mouse_row; /* mouse_row related to old_char */
-static int old_mouse_col; /* mouse_col related to old_char */
+static int old_char = -1; // character put back by vungetc()
+static int old_mod_mask; // mod_mask for ungotten character
+static int old_mouse_grid; // mouse_grid related to old_char
+static int old_mouse_row; // mouse_row related to old_char
+static int old_mouse_col; // mouse_col related to old_char
/*
@@ -1391,6 +1392,7 @@ int vgetc(void)
c = old_char;
old_char = -1;
mod_mask = old_mod_mask;
+ mouse_grid = old_mouse_grid;
mouse_row = old_mouse_row;
mouse_col = old_mouse_col;
} else {
@@ -1585,6 +1587,7 @@ vungetc ( /* unget one character (can only be done once!) */
{
old_char = c;
old_mod_mask = mod_mask;
+ old_mouse_grid = mouse_grid;
old_mouse_row = mouse_row;
old_mouse_col = mouse_col;
}
@@ -2120,8 +2123,8 @@ static int vgetorpeek(int advance)
++col;
}
curwin->w_wrow = curwin->w_cline_row
- + curwin->w_wcol / curwin->w_grid.Columns;
- curwin->w_wcol %= curwin->w_grid.Columns;
+ + curwin->w_wcol / curwin->w_width_inner;
+ curwin->w_wcol %= curwin->w_width_inner;
curwin->w_wcol += curwin_col_off();
col = 0; /* no correction needed */
} else {
@@ -2130,7 +2133,7 @@ static int vgetorpeek(int advance)
}
} else if (curwin->w_p_wrap && curwin->w_wrow) {
curwin->w_wrow--;
- curwin->w_wcol = curwin->w_grid.Columns - 1;
+ curwin->w_wcol = curwin->w_width_inner - 1;
col = curwin->w_cursor.col - 1;
}
if (col > 0 && curwin->w_wcol > 0) {
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 2550fb8163..f47697b190 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -194,12 +194,15 @@ EXTERN int compl_cont_status INIT(= 0);
EXTERN int cmdmsg_rl INIT(= false); // cmdline is drawn right to left
EXTERN int msg_col;
EXTERN int msg_row;
-EXTERN int msg_scrolled; /* Number of screen lines that windows have
- * scrolled because of printing messages. */
-EXTERN int msg_scrolled_ign INIT(= FALSE);
-/* when TRUE don't set need_wait_return in
- msg_puts_attr() when msg_scrolled is
- non-zero */
+EXTERN int msg_scrolled; // Number of screen lines that windows have
+ // scrolled because of printing messages.
+// when true don't set need_wait_return in msg_puts_attr()
+// when msg_scrolled is non-zero
+EXTERN bool msg_scrolled_ign INIT(= false);
+// Whether the screen is damaged due to scrolling. Sometimes msg_scrolled
+// is reset before the screen is redrawn, so we need to keep track of this.
+EXTERN bool msg_did_scroll INIT(= false);
+
EXTERN char_u *keep_msg INIT(= NULL); /* msg to be shown after redraw */
EXTERN int keep_msg_attr INIT(= 0); /* highlight attr for keep_msg */
@@ -394,9 +397,8 @@ EXTERN bufref_T au_new_curbuf INIT(= { NULL, 0, 0 });
EXTERN buf_T *au_pending_free_buf INIT(= NULL);
EXTERN win_T *au_pending_free_win INIT(= NULL);
-/*
- * Mouse coordinates, set by check_termcode()
- */
+// Mouse coordinates, set by handle_mouse_event()
+EXTERN int mouse_grid;
EXTERN int mouse_row;
EXTERN int mouse_col;
EXTERN bool mouse_past_bottom INIT(= false); /* mouse below last line */
@@ -843,26 +845,6 @@ extern char_u *compiled_sys;
* directory is not a local directory, globaldir is NULL. */
EXTERN char_u *globaldir INIT(= NULL);
-// 'listchars' characters. Defaults are overridden in set_chars_option().
-EXTERN int lcs_eol INIT(= '$');
-EXTERN int lcs_ext INIT(= NUL);
-EXTERN int lcs_prec INIT(= NUL);
-EXTERN int lcs_nbsp INIT(= NUL);
-EXTERN int lcs_space INIT(= NUL);
-EXTERN int lcs_tab1 INIT(= NUL);
-EXTERN int lcs_tab2 INIT(= NUL);
-EXTERN int lcs_trail INIT(= NUL);
-EXTERN int lcs_conceal INIT(= ' ');
-
-// 'fillchars' characters. Defaults are overridden in set_chars_option().
-EXTERN int fill_stl INIT(= ' ');
-EXTERN int fill_stlnc INIT(= ' ');
-EXTERN int fill_vert INIT(= 9474); // │
-EXTERN int fill_fold INIT(= 183); // ·
-EXTERN int fill_diff INIT(= '-');
-EXTERN int fill_msgsep INIT(= ' ');
-EXTERN int fill_eob INIT(= '~');
-
/* Whether 'keymodel' contains "stopsel" and "startsel". */
EXTERN int km_stopsel INIT(= FALSE);
EXTERN int km_startsel INIT(= FALSE);
@@ -1072,16 +1054,14 @@ EXTERN char_u e_cmdmap_key[] INIT(=N_(
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
EXTERN char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP"));
-/* For undo we need to know the lowest time possible. */
+// For undo we need to know the lowest time possible.
EXTERN time_t starttime;
EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */
-/*
- * Some compilers warn for not using a return value, but in some situations we
- * can't do anything useful with the value. Assign to this variable to avoid
- * the warning.
- */
+// Some compilers warn for not using a return value, but in some situations we
+// can't do anything useful with the value. Assign to this variable to avoid
+// the warning.
EXTERN int vim_ignored;
// Start a msgpack-rpc channel over stdin/stdout.
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 37d85ead0c..d9ccdbbdab 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -1,6 +1,8 @@
#ifndef NVIM_GRID_DEFS_H
#define NVIM_GRID_DEFS_H
+#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include "nvim/types.h"
@@ -49,11 +51,14 @@ typedef struct {
int row_offset;
int col_offset;
- // grid size requested by the UI. Used for window grids only.
- int requested_rows;
- int requested_cols;
-
- int was_resized;
+ // state owned by the compositor.
+ int comp_row;
+ int comp_col;
+ size_t comp_index;
+ bool comp_disabled;
} ScreenGrid;
+#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, \
+ false }
+
#endif // NVIM_GRID_DEFS_H
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 41d60fa3ea..4c5fca6d39 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -23,11 +23,15 @@ static kvec_t(HlEntry) attr_entries = KV_INITIAL_VALUE;
static Map(HlEntry, int) *attr_entry_ids;
static Map(int, int) *combine_attr_entries;
+static Map(int, int) *blend_attr_entries;
+static Map(int, int) *blendthrough_attr_entries;
void highlight_init(void)
{
attr_entry_ids = map_new(HlEntry, int)();
combine_attr_entries = map_new(int, int)();
+ blend_attr_entries = map_new(int, int)();
+ blendthrough_attr_entries = map_new(int, int)();
// index 0 is no attribute, add dummy entry:
kv_push(attr_entries, ((HlEntry){ .attr = HLATTRS_INIT, .kind = kHlUnknown,
@@ -101,6 +105,9 @@ static int get_attr_entry(HlEntry entry)
/// When a UI connects, we need to send it the table of highlights used so far.
void ui_send_all_hls(UI *ui)
{
+ if (!ui->hl_attr_define) {
+ return;
+ }
for (size_t i = 1; i < kv_size(attr_entries); i++) {
Array inspect = hl_inspect((int)i);
ui->hl_attr_define(ui, (Integer)i, kv_A(attr_entries, i).attr,
@@ -210,6 +217,8 @@ void clear_hl_tables(bool reinit)
kv_size(attr_entries) = 1;
map_clear(HlEntry, int)(attr_entry_ids);
map_clear(int, int)(combine_attr_entries);
+ map_clear(int, int)(blend_attr_entries);
+ map_clear(int, int)(blendthrough_attr_entries);
highlight_attr_set_all();
highlight_changed();
screen_invalidate_highlights();
@@ -217,15 +226,22 @@ void clear_hl_tables(bool reinit)
kv_destroy(attr_entries);
map_free(HlEntry, int)(attr_entry_ids);
map_free(int, int)(combine_attr_entries);
+ map_free(int, int)(blend_attr_entries);
+ map_free(int, int)(blendthrough_attr_entries);
}
}
+void hl_invalidate_blends(void)
+{
+ map_clear(int, int)(blend_attr_entries);
+ map_clear(int, int)(blendthrough_attr_entries);
+}
+
// Combine special attributes (e.g., for spelling) with other attributes
// (e.g., for syntax highlighting).
// "prim_attr" overrules "char_attr".
// This creates a new group when required.
-// Since we expect there to be few spelling mistakes we don't cache the
-// result.
+// Since we expect there to be a lot of spelling mistakes we cache the result.
// Return the resulting attributes.
int hl_combine_attr(int char_attr, int prim_attr)
{
@@ -280,6 +296,188 @@ int hl_combine_attr(int char_attr, int prim_attr)
return id;
}
+/// Get the used rgb colors for an attr group.
+///
+/// If colors are unset, use builtin default colors. Never returns -1
+/// Cterm colors are unchanged.
+static HlAttrs get_colors_force(int attr)
+{
+ HlAttrs attrs = syn_attr2entry(attr);
+ if (attrs.rgb_bg_color == -1) {
+ attrs.rgb_bg_color = normal_bg;
+ }
+ if (attrs.rgb_fg_color == -1) {
+ attrs.rgb_fg_color = normal_fg;
+ }
+ if (attrs.rgb_sp_color == -1) {
+ attrs.rgb_sp_color = normal_sp;
+ }
+ HL_SET_DEFAULT_COLORS(attrs.rgb_fg_color, attrs.rgb_bg_color,
+ attrs.rgb_sp_color);
+
+ if (attrs.rgb_ae_attr & HL_INVERSE) {
+ int temp = attrs.rgb_bg_color;
+ attrs.rgb_bg_color = attrs.rgb_fg_color;
+ attrs.rgb_fg_color = temp;
+ attrs.rgb_ae_attr &= ~HL_INVERSE;
+ }
+
+ return attrs;
+}
+
+/// Blend overlay attributes (for popupmenu) with other attributes
+///
+/// This creates a new group when required.
+/// This is called per-cell, so cache the result.
+///
+/// @return the resulting attributes.
+int hl_blend_attrs(int back_attr, int front_attr, bool through)
+{
+ int combine_tag = (back_attr << 16) + front_attr;
+ Map(int, int) *map = through ? blendthrough_attr_entries : blend_attr_entries;
+ int id = map_get(int, int)(map, combine_tag);
+ if (id > 0) {
+ return id;
+ }
+
+ HlAttrs battrs = get_colors_force(back_attr);
+ HlAttrs fattrs = get_colors_force(front_attr);
+ HlAttrs cattrs;
+ if (through) {
+ cattrs = battrs;
+ cattrs.rgb_fg_color = rgb_blend((int)p_pb, battrs.rgb_fg_color,
+ fattrs.rgb_bg_color);
+ if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) {
+ cattrs.rgb_sp_color = rgb_blend((int)p_pb, battrs.rgb_sp_color,
+ fattrs.rgb_bg_color);
+ } else {
+ cattrs.rgb_sp_color = -1;
+ }
+
+ cattrs.cterm_bg_color = fattrs.cterm_bg_color;
+ cattrs.cterm_fg_color = cterm_blend((int)p_pb, battrs.cterm_fg_color,
+ fattrs.cterm_bg_color);
+ } else {
+ cattrs = fattrs;
+ if (p_pb >= 50) {
+ cattrs.rgb_ae_attr |= battrs.rgb_ae_attr;
+ }
+ cattrs.rgb_fg_color = rgb_blend((int)p_pb/2, battrs.rgb_fg_color,
+ fattrs.rgb_fg_color);
+ if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) {
+ cattrs.rgb_sp_color = rgb_blend((int)p_pb/2, battrs.rgb_bg_color,
+ fattrs.rgb_sp_color);
+ } else {
+ cattrs.rgb_sp_color = -1;
+ }
+ }
+ cattrs.rgb_bg_color = rgb_blend((int)p_pb, battrs.rgb_bg_color,
+ fattrs.rgb_bg_color);
+
+ HlKind kind = through ? kHlBlendThrough : kHlBlend;
+ id = get_attr_entry((HlEntry){ .attr = cattrs, .kind = kind,
+ .id1 = back_attr, .id2 = front_attr });
+ if (id > 0) {
+ map_put(int, int)(map, combine_tag, id);
+ }
+ return id;
+}
+
+static int rgb_blend(int ratio, int rgb1, int rgb2)
+{
+ int a = ratio, b = 100-ratio;
+ int r1 = (rgb1 & 0xFF0000) >> 16;
+ int g1 = (rgb1 & 0x00FF00) >> 8;
+ int b1 = (rgb1 & 0x0000FF) >> 0;
+ int r2 = (rgb2 & 0xFF0000) >> 16;
+ int g2 = (rgb2 & 0x00FF00) >> 8;
+ int b2 = (rgb2 & 0x0000FF) >> 0;
+ int mr = (a * r1 + b * r2)/100;
+ int mg = (a * g1 + b * g2)/100;
+ int mb = (a * b1 + b * b2)/100;
+ return (mr << 16) + (mg << 8) + mb;
+}
+
+static int cterm_blend(int ratio, int c1, int c2)
+{
+ // 1. Convert cterm color numbers to RGB.
+ // 2. Blend the RGB colors.
+ // 3. Convert the RGB result to a cterm color.
+ int rgb1 = hl_cterm2rgb_color(c1);
+ int rgb2 = hl_cterm2rgb_color(c2);
+ int rgb_blended = rgb_blend(ratio, rgb1, rgb2);
+ return hl_rgb2cterm_color(rgb_blended);
+}
+
+/// Converts RGB color to 8-bit color (0-255).
+static int hl_rgb2cterm_color(int rgb)
+{
+ int r = (rgb & 0xFF0000) >> 16;
+ int g = (rgb & 0x00FF00) >> 8;
+ int b = (rgb & 0x0000FF) >> 0;
+
+ return (r * 6 / 256) * 36 + (g * 6 / 256) * 6 + (b * 6 / 256);
+}
+
+/// Converts 8-bit color (0-255) to RGB color.
+/// This is compatible with xterm.
+static int hl_cterm2rgb_color(int nr)
+{
+ static int cube_value[] = {
+ 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF
+ };
+ static int grey_ramp[] = {
+ 0x08, 0x12, 0x1C, 0x26, 0x30, 0x3A, 0x44, 0x4E, 0x58, 0x62, 0x6C, 0x76,
+ 0x80, 0x8A, 0x94, 0x9E, 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE
+ };
+ static char_u ansi_table[16][4] = {
+ // R G B idx
+ { 0, 0, 0, 1 } , // black
+ { 224, 0, 0, 2 } , // dark red
+ { 0, 224, 0, 3 } , // dark green
+ { 224, 224, 0, 4 } , // dark yellow / brown
+ { 0, 0, 224, 5 } , // dark blue
+ { 224, 0, 224, 6 } , // dark magenta
+ { 0, 224, 224, 7 } , // dark cyan
+ { 224, 224, 224, 8 } , // light grey
+
+ { 128, 128, 128, 9 } , // dark grey
+ { 255, 64, 64, 10 } , // light red
+ { 64, 255, 64, 11 } , // light green
+ { 255, 255, 64, 12 } , // yellow
+ { 64, 64, 255, 13 } , // light blue
+ { 255, 64, 255, 14 } , // light magenta
+ { 64, 255, 255, 15 } , // light cyan
+ { 255, 255, 255, 16 } , // white
+ };
+
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ int idx;
+ // *ansi_idx = 0;
+
+ if (nr < 16) {
+ r = ansi_table[nr][0];
+ g = ansi_table[nr][1];
+ b = ansi_table[nr][2];
+ // *ansi_idx = ansi_table[nr][3];
+ } else if (nr < 232) { // 216 color-cube
+ idx = nr - 16;
+ r = cube_value[idx / 36 % 6];
+ g = cube_value[idx / 6 % 6];
+ b = cube_value[idx % 6];
+ // *ansi_idx = -1;
+ } else if (nr < 256) { // 24 greyscale ramp
+ idx = nr - 232;
+ r = grey_ramp[idx];
+ g = grey_ramp[idx];
+ b = grey_ramp[idx];
+ // *ansi_idx = -1;
+ }
+ return (r << 16) + (g << 8) + b;
+}
+
/// Get highlight attributes for a attribute code
HlAttrs syn_attr2entry(int attr)
{
@@ -403,6 +601,8 @@ static void hl_inspect_impl(Array *arr, int attr)
break;
case kHlCombine:
+ case kHlBlend:
+ case kHlBlendThrough:
// attribute combination is associative, so flatten to an array
hl_inspect_impl(arr, e.id1);
hl_inspect_impl(arr, e.id2);
diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h
index 6be0d6200b..a237ddbc34 100644
--- a/src/nvim/highlight.h
+++ b/src/nvim/highlight.h
@@ -10,4 +10,12 @@
# include "highlight.h.generated.h"
#endif
+# define HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp) \
+ do { \
+ bool dark_ = (*p_bg == 'd'); \
+ rgb_fg = rgb_fg != -1 ? rgb_fg : (dark_ ? 0xFFFFFF : 0x000000); \
+ rgb_bg = rgb_bg != -1 ? rgb_bg : (dark_ ? 0x000000 : 0xFFFFFF); \
+ rgb_sp = rgb_sp != -1 ? rgb_sp : 0xFF0000; \
+ } while (0);
+
#endif // NVIM_HIGHLIGHT_H
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index 40025fcbbb..1da33bfea5 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -160,6 +160,8 @@ typedef enum {
kHlSyntax,
kHlTerminal,
kHlCombine,
+ kHlBlend,
+ kHlBlendThrough,
} HlKind;
typedef struct {
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 13534ac1a9..8e20aa5be4 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -61,7 +61,8 @@ int get_indent_str(char_u *ptr, int ts, int list)
for (; *ptr; ++ptr) {
// Count a tab for what it is worth.
if (*ptr == TAB) {
- if (!list || lcs_tab1) { // count a tab for what it is worth
+ if (!list || curwin->w_p_lcs_chars.tab1) {
+ // count a tab for what it is worth
count += ts - (count % ts);
} else {
// In list mode, when tab is not set, count screen char width
@@ -204,13 +205,12 @@ int set_indent(int size, int flags)
// characters and allocate accordingly. We will fill the rest with spaces
// after the if (!curbuf->b_p_et) below.
if (orig_char_len != -1) {
- assert(orig_char_len + size - ind_done + line_len >= 0);
- size_t n; // = orig_char_len + size - ind_done + line_len
- size_t n2;
- STRICT_ADD(orig_char_len, size, &n, size_t);
- STRICT_ADD(ind_done, line_len, &n2, size_t);
- STRICT_SUB(n, n2, &n, size_t);
- newline = xmalloc(n);
+ int newline_size; // = orig_char_len + size - ind_done + line_len
+ STRICT_ADD(orig_char_len, size, &newline_size, int);
+ STRICT_SUB(newline_size, ind_done, &newline_size, int);
+ STRICT_ADD(newline_size, line_len, &newline_size, int);
+ assert(newline_size >= 0);
+ newline = xmalloc((size_t)newline_size);
todo = size - ind_done;
// Set total length of indent in characters, which may have been
@@ -474,7 +474,7 @@ int get_breakindent_win(win_T *wp, char_u *line)
static varnumber_T prev_tick = 0; // Changedtick of cached value.
int bri = 0;
// window width minus window margin space, i.e. what rests for text
- const int eff_wwidth = wp->w_grid.Columns
+ const int eff_wwidth = wp->w_width_inner
- ((wp->w_p_nu || wp->w_p_rnu)
&& (vim_strchr(p_cpo, CPO_NUMCOL) == NULL)
? number_width(wp) + 1 : 0);
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 4d912c452b..8066b6e828 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -12,6 +12,7 @@
#endif
#include <uv.h>
+#include "auto/config.h"
#include "nvim/log.h"
#include "nvim/types.h"
#include "nvim/os/os.h"
diff --git a/src/nvim/log.h b/src/nvim/log.h
index 7d4c033565..17ff095473 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -4,6 +4,8 @@
#include <stdio.h>
#include <stdbool.h>
+#include "auto/config.h"
+
#define DEBUG_LOG_LEVEL 0
#define INFO_LOG_LEVEL 1
#define WARN_LOG_LEVEL 2
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index a7bda9d037..93069893cf 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -50,7 +50,7 @@ static void nlua_error(lua_State *const lstate, const char *const msg)
size_t len;
const char *const str = lua_tolstring(lstate, -1, &len);
- emsgf(msg, (int)len, str);
+ emsgf_multiline(msg, (int)len, str);
lua_pop(lstate, 1);
}
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index d447bff765..61009528a8 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -198,4 +198,25 @@
# define IO_COUNT(x) (x)
#endif
+///
+/// PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES
+///
+#if defined(__clang__) && __clang__ == 1
+# define PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wmissing-prototypes\"")
+# define PRAGMA_DIAG_POP \
+ _Pragma("clang diagnostic pop")
+#elif defined(__GNUC__)
+# define PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"")
+# define PRAGMA_DIAG_POP \
+ _Pragma("GCC diagnostic pop")
+#else
+# define PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES
+# define PRAGMA_DIAG_POP
+#endif
+
+
#endif // NVIM_MACROS_H
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 17f3a894d4..c0dea196fb 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -154,6 +154,7 @@ void event_init(void)
remote_ui_init();
api_vim_init();
terminal_init();
+ ui_init();
}
/// @returns false if main_loop could not be closed gracefully
@@ -221,6 +222,7 @@ void early_init(void)
}
#ifdef MAKE_LIB
+int nvim_main(int argc, char **argv); // silence -Wmissing-prototypes
int nvim_main(int argc, char **argv)
#elif defined(WIN32)
int wmain(int argc, wchar_t **argv_w) // multibyte args on Windows. #7060
@@ -298,7 +300,7 @@ int main(int argc, char **argv)
assert(p_ch >= 0 && Rows >= p_ch && Rows - p_ch <= INT_MAX);
cmdline_row = (int)(Rows - p_ch);
msg_row = cmdline_row;
- screenalloc(false); // allocate screen buffers
+ screenalloc(); // allocate screen buffers
set_init_2(headless_mode);
TIME_MSG("inits 2");
diff --git a/src/nvim/math.c b/src/nvim/math.c
new file mode 100644
index 0000000000..b51f335ed7
--- /dev/null
+++ b/src/nvim/math.c
@@ -0,0 +1,42 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include <math.h>
+
+#include "nvim/math.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "math.c.generated.h"
+#endif
+
+#if defined(__clang__) && __clang__ == 1 && __clang_major__ >= 6
+// Workaround glibc + Clang 6+ bug. #8274
+// https://bugzilla.redhat.com/show_bug.cgi?id=1472437
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wconversion"
+#endif
+int xfpclassify(double d)
+{
+#if defined(__MINGW32__)
+ // Workaround mingw warning. #7863
+ return __fpclassify(d);
+#else
+ return fpclassify(d);
+#endif
+}
+int xisinf(double d)
+{
+ return isinf(d);
+}
+int xisnan(double d)
+{
+#if defined(__MINGW32__)
+ // Workaround mingw warning. #7863
+ return _isnan(d);
+#else
+ return isnan(d);
+#endif
+}
+#if defined(__clang__) && __clang__ == 1 && __clang_major__ >= 6
+# pragma clang diagnostic pop
+#endif
diff --git a/src/nvim/math.h b/src/nvim/math.h
new file mode 100644
index 0000000000..7969323905
--- /dev/null
+++ b/src/nvim/math.h
@@ -0,0 +1,7 @@
+#ifndef NVIM_MATH_H
+#define NVIM_MATH_H
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "math.h.generated.h"
+#endif
+#endif // NVIM_MATH_H
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 4c4f7d65bd..662eda3c7c 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -3412,7 +3412,9 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
# endif
xfree(name);
- /* pretend screen didn't scroll, need redraw anyway */
+ // pretend screen didn't scroll, need redraw anyway
+ // TODO(bfredl): when doing the message grid refactor,
+ // simplify this special case.
msg_scrolled = 0;
redraw_all_later(NOT_VALID);
}
diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h
index 34a002af5d..edd933b2cd 100644
--- a/src/nvim/memline_defs.h
+++ b/src/nvim/memline_defs.h
@@ -3,54 +3,65 @@
#include "nvim/memfile_defs.h"
-/*
- * When searching for a specific line, we remember what blocks in the tree
- * are the branches leading to that block. This is stored in ml_stack. Each
- * entry is a pointer to info in a block (may be data block or pointer block)
- */
+///
+/// When searching for a specific line, we remember what blocks in the tree
+/// are the branches leading to that block. This is stored in ml_stack. Each
+/// entry is a pointer to info in a block (may be data block or pointer block)
+///
typedef struct info_pointer {
- blocknr_T ip_bnum; /* block number */
- linenr_T ip_low; /* lowest lnum in this block */
- linenr_T ip_high; /* highest lnum in this block */
- int ip_index; /* index for block with current lnum */
-} infoptr_T; /* block/index pair */
+ blocknr_T ip_bnum; // block number
+ linenr_T ip_low; // lowest lnum in this block
+ linenr_T ip_high; // highest lnum in this block
+ int ip_index; // index for block with current lnum
+} infoptr_T; // block/index pair
typedef struct ml_chunksize {
int mlcs_numlines;
long mlcs_totalsize;
} chunksize_T;
-/* Flags when calling ml_updatechunk() */
-
+// Flags when calling ml_updatechunk()
#define ML_CHNK_ADDLINE 1
#define ML_CHNK_DELLINE 2
#define ML_CHNK_UPDLINE 3
-/*
- * the memline structure holds all the information about a memline
- */
+/// memline structure: the contents of a buffer.
+/// Essentially a tree with a branch factor of 128.
+/// Lines are stored at leaf nodes.
+/// Nodes are stored on ml_mfp (memfile_T):
+/// pointer_block: internal nodes
+/// data_block: leaf nodes
+///
+/// Memline also has "chunks" of 800 lines that are separate from the 128-tree
+/// structure, primarily used to speed up line2byte() and byte2line().
+///
+/// Motivation: If you have a file that is 10000 lines long, and you insert
+/// a line at linenr 1000, you don't want to move 9000 lines in
+/// memory. With this structure it is roughly (N * 128) pointer
+/// moves, where N is the height (typically 1-3).
+///
typedef struct memline {
- linenr_T ml_line_count; /* number of lines in the buffer */
+ linenr_T ml_line_count; // number of lines in the buffer
- memfile_T *ml_mfp; /* pointer to associated memfile */
+ memfile_T *ml_mfp; // pointer to associated memfile
-#define ML_EMPTY 1 /* empty buffer */
-#define ML_LINE_DIRTY 2 /* cached line was changed and allocated */
-#define ML_LOCKED_DIRTY 4 /* ml_locked was changed */
-#define ML_LOCKED_POS 8 /* ml_locked needs positive block number */
+#define ML_EMPTY 1 // empty buffer
+#define ML_LINE_DIRTY 2 // cached line was changed and allocated
+#define ML_LOCKED_DIRTY 4 // ml_locked was changed
+#define ML_LOCKED_POS 8 // ml_locked needs positive block number
int ml_flags;
- infoptr_T *ml_stack; /* stack of pointer blocks (array of IPTRs) */
- int ml_stack_top; /* current top of ml_stack */
- int ml_stack_size; /* total number of entries in ml_stack */
+ infoptr_T *ml_stack; // stack of pointer blocks (array of IPTRs)
+ int ml_stack_top; // current top of ml_stack
+ int ml_stack_size; // total number of entries in ml_stack
- linenr_T ml_line_lnum; /* line number of cached line, 0 if not valid */
- char_u *ml_line_ptr; /* pointer to cached line */
+ linenr_T ml_line_lnum; // line number of cached line, 0 if not valid
+ char_u *ml_line_ptr; // pointer to cached line
- bhdr_T *ml_locked; /* block used by last ml_get */
- linenr_T ml_locked_low; /* first line in ml_locked */
- linenr_T ml_locked_high; /* last line in ml_locked */
- int ml_locked_lineadd; /* number of lines inserted in ml_locked */
+ bhdr_T *ml_locked; // block used by last ml_get
+ linenr_T ml_locked_low; // first line in ml_locked
+ linenr_T ml_locked_high; // last line in ml_locked
+ int ml_locked_lineadd; // number of lines inserted in ml_locked
chunksize_T *ml_chunksize;
int ml_numchunks;
int ml_usedchunks;
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index d38079ca72..b49b521bc9 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -18,35 +18,15 @@
#include "nvim/ui.h"
#include "nvim/api/vim.h"
-#ifdef HAVE_JEMALLOC
-// Force je_ prefix on jemalloc functions.
-# define JEMALLOC_NO_DEMANGLE
-# include <jemalloc/jemalloc.h>
-#endif
-
#ifdef UNIT_TESTING
# define malloc(size) mem_malloc(size)
# define calloc(count, size) mem_calloc(count, size)
# define realloc(ptr, size) mem_realloc(ptr, size)
# define free(ptr) mem_free(ptr)
-# ifdef HAVE_JEMALLOC
-MemMalloc mem_malloc = &je_malloc;
-MemFree mem_free = &je_free;
-MemCalloc mem_calloc = &je_calloc;
-MemRealloc mem_realloc = &je_realloc;
-# else
MemMalloc mem_malloc = &malloc;
MemFree mem_free = &free;
MemCalloc mem_calloc = &calloc;
MemRealloc mem_realloc = &realloc;
-# endif
-#else
-# ifdef HAVE_JEMALLOC
-# define malloc(size) je_malloc(size)
-# define calloc(count, size) je_calloc(count, size)
-# define realloc(ptr, size) je_realloc(ptr, size)
-# define free(ptr) je_free(ptr)
-# endif
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 1c54db10eb..aea297fce2 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -660,7 +660,8 @@ static void free_menu_string(vimmenu_T *menu, int idx)
///
/// @param[in] menu if null, starts from root_menu
/// @param modes, a choice of \ref MENU_MODES
-/// @return a dict with name/commands
+/// @return dict with name/commands
+/// @see show_menus_recursive
/// @see menu_get
static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
{
@@ -715,10 +716,10 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
// visit recursively all children
list_T *const children_list = tv_list_alloc(kListLenMayKnow);
for (menu = menu->children; menu != NULL; menu = menu->next) {
- dict_T *dic = menu_get_recursive(menu, modes);
- if (tv_dict_len(dict) > 0) {
- tv_list_append_dict(children_list, dic);
- }
+ dict_T *d = menu_get_recursive(menu, modes);
+ if (tv_dict_len(d) > 0) {
+ tv_list_append_dict(children_list, d);
+ }
}
tv_dict_add_list(dict, S_LEN("submenus"), children_list);
}
@@ -734,42 +735,51 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
/// @return false if could not find path_name
bool menu_get(char_u *const path_name, int modes, list_T *list)
{
- vimmenu_T *menu;
- menu = find_menu(root_menu, path_name, modes);
+ vimmenu_T *menu = find_menu(root_menu, path_name, modes);
if (!menu) {
return false;
}
for (; menu != NULL; menu = menu->next) {
- dict_T *dict = menu_get_recursive(menu, modes);
- if (dict && tv_dict_len(dict) > 0) {
- tv_list_append_dict(list, dict);
+ dict_T *d = menu_get_recursive(menu, modes);
+ if (d && tv_dict_len(d) > 0) {
+ tv_list_append_dict(list, d);
+ }
+ if (*path_name != NUL) {
+ // If a (non-empty) path query was given, only the first node in the
+ // find_menu() result is relevant. Else we want all nodes.
+ break;
}
}
return true;
}
-/// Find menu matching required name and modes
+/// Find menu matching `name` and `modes`.
///
/// @param menu top menu to start looking from
/// @param name path towards the menu
/// @return menu if \p name is null, found menu or NULL
-vimmenu_T *
-find_menu(vimmenu_T *menu, char_u * name, int modes)
+static vimmenu_T *find_menu(vimmenu_T *menu, char_u *name, int modes)
{
char_u *p;
while (*name) {
+ // find the end of one dot-separated name and put a NUL at the dot
p = menu_name_skip(name);
while (menu != NULL) {
if (menu_name_equal(name, menu)) {
- /* Found menu */
+ // Found menu
if (*p != NUL && menu->children == NULL) {
- EMSG(_(e_notsubmenu));
- return NULL;
- } else if ((menu->modes & modes) == 0x0) {
- EMSG(_(e_othermode));
- return NULL;
+ if (*p != NUL) {
+ EMSG(_(e_notsubmenu));
+ return NULL;
+ } else if ((menu->modes & modes) == 0x0) {
+ EMSG(_(e_othermode));
+ return NULL;
+ }
+ }
+ if (*p == NUL) { // found a full match
+ return menu;
}
break;
}
@@ -780,6 +790,7 @@ find_menu(vimmenu_T *menu, char_u * name, int modes)
EMSG2(_(e_nomenu), name);
return NULL;
}
+ // Found a match, search the sub-menu.
name = p;
menu = menu->children;
}
@@ -1235,7 +1246,7 @@ static char_u *popup_mode_name(char_u *name, int idx)
///
/// @return a pointer to allocated memory.
static char_u *menu_text(const char_u *str, int *mnemonic, char_u **actext)
- FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
FUNC_ATTR_NONNULL_ARG(1)
{
char_u *p;
@@ -1520,7 +1531,7 @@ static char_u *menutrans_lookup(char_u *name, int len)
char_u *dname;
for (int i = 0; i < menutrans_ga.ga_len; i++) {
- if (STRNCMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL) {
+ if (STRNICMP(name, tp[i].from, len) == 0 && tp[i].from[len] == NUL) {
return tp[i].to;
}
}
@@ -1531,7 +1542,7 @@ static char_u *menutrans_lookup(char_u *name, int len)
dname = menu_text(name, NULL, NULL);
name[len] = c;
for (int i = 0; i < menutrans_ga.ga_len; i++) {
- if (STRCMP(dname, tp[i].from_noamp) == 0) {
+ if (STRICMP(dname, tp[i].from_noamp) == 0) {
xfree(dname);
return tp[i].to;
}
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 37e40c3cc1..b22508c23f 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -34,11 +34,13 @@
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
+#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/mouse.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
#include "nvim/os/time.h"
+#include "nvim/api/private/helpers.h"
/*
* To be able to scroll back at the "more" and "hit-enter" prompts we need to
@@ -108,6 +110,19 @@ static int verbose_did_open = FALSE;
* This is an allocated string or NULL when not used.
*/
+
+// Extended msg state, currently used for external UIs with ext_messages
+static const char *msg_ext_kind = NULL;
+static Array msg_ext_chunks = ARRAY_DICT_INIT;
+static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
+static sattr_T msg_ext_last_attr = -1;
+
+static bool msg_ext_overwrite = false; ///< will overwrite last message
+static int msg_ext_visible = 0; ///< number of messages currently visible
+
+/// Shouldn't clear message after leaving cmdline
+static bool msg_ext_keep_after_cmdline = false;
+
/*
* msg(s) - displays the string 's' on the status line
* When terminal not initialized (yet) mch_errmsg(..) is used.
@@ -115,7 +130,7 @@ static int verbose_did_open = FALSE;
*/
int msg(char_u *s)
{
- return msg_attr_keep(s, 0, FALSE);
+ return msg_attr_keep(s, 0, false, false);
}
/*
@@ -126,29 +141,62 @@ int verb_msg(char_u *s)
int n;
verbose_enter();
- n = msg_attr_keep(s, 0, FALSE);
+ n = msg_attr_keep(s, 0, false, false);
verbose_leave();
return n;
}
-int msg_attr(const char *s, const int attr) FUNC_ATTR_NONNULL_ARG(1)
+int msg_attr(const char *s, const int attr)
+ FUNC_ATTR_NONNULL_ARG(1)
{
- return msg_attr_keep((char_u *)s, attr, false);
+ return msg_attr_keep((char_u *)s, attr, false, false);
}
-int
-msg_attr_keep (
- char_u *s,
- int attr,
- int keep /* TRUE: set keep_msg if it doesn't scroll */
-)
- FUNC_ATTR_NONNULL_ARG(1)
+/// similar to msg_outtrans_attr, but support newlines and tabs.
+void msg_multiline_attr(const char *s, int attr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const char *next_spec = s;
+
+ while (next_spec != NULL) {
+ next_spec = strpbrk(s, "\t\n\r");
+
+ if (next_spec != NULL) {
+ // Printing all char that are before the char found by strpbrk
+ msg_outtrans_len_attr((char_u *)s, next_spec - s, attr);
+
+ if (*next_spec != TAB) {
+ msg_clr_eos();
+ }
+ msg_putchar_attr((uint8_t)(*next_spec), attr);
+ s = next_spec + 1;
+ }
+ }
+
+ // Print the rest of the message. We know there is no special
+ // character because strpbrk returned NULL
+ if (*s != NUL) {
+ msg_outtrans_attr((char_u *)s, attr);
+ }
+}
+
+
+/// @param keep set keep_msg if it doesn't scroll
+bool msg_attr_keep(char_u *s, int attr, bool keep, bool multiline)
+ FUNC_ATTR_NONNULL_ALL
{
static int entered = 0;
int retval;
char_u *buf = NULL;
+ if (keep && multiline) {
+ // Not implemented. 'multiline' is only used by nvim-added messages,
+ // which should avoid 'keep' behavior (just show the message at
+ // the correct time already).
+ abort();
+ }
+
// Skip messages not match ":filter pattern".
// Don't filter when there is an error.
if (!emsg_on_display && message_filtered(s)) {
@@ -175,7 +223,7 @@ msg_attr_keep (
&& last_msg_hist != NULL
&& last_msg_hist->msg != NULL
&& STRCMP(s, last_msg_hist->msg))) {
- add_msg_hist((const char *)s, -1, attr);
+ add_msg_hist((const char *)s, -1, attr, multiline);
}
/* When displaying keep_msg, don't let msg_start() free it, caller must do
@@ -189,13 +237,18 @@ msg_attr_keep (
if (buf != NULL)
s = buf;
- msg_outtrans_attr(s, attr);
+ if (multiline) {
+ msg_multiline_attr((char *)s, attr);
+ } else {
+ msg_outtrans_attr(s, attr);
+ }
msg_clr_eos();
retval = msg_end();
if (keep && retval && vim_strsize(s) < (int)(Rows - cmdline_row - 1)
- * Columns + sc_col)
+ * Columns + sc_col) {
set_keep_msg(s, 0);
+ }
xfree(buf);
--entered;
@@ -218,7 +271,8 @@ msg_strtrunc (
/* May truncate message to avoid a hit-return prompt */
if ((!msg_scroll && !need_wait_return && shortmess(SHM_TRUNCALL)
- && !exmode_active && msg_silent == 0) || force) {
+ && !exmode_active && msg_silent == 0 && !ui_has(kUIMessages))
+ || force) {
len = vim_strsize(s);
if (msg_scrolled != 0)
/* Use all the columns. */
@@ -468,17 +522,8 @@ int emsg_not_now(void)
return FALSE;
}
-/*
- * emsg() - display an error message
- *
- * Rings the bell, if appropriate, and calls message() to do the real work
- * When terminal not initialized (yet) mch_errmsg(..) is used.
- *
- * return TRUE if wait_return not called
- */
-int emsg(const char_u *s_)
+static bool emsg_multiline(const char *s, bool multiline)
{
- const char *s = (const char *)s_;
int attr;
int ignore = false;
int severe;
@@ -565,6 +610,9 @@ int emsg(const char_u *s_)
} // wait_return has reset need_wait_return
// and a redraw is expected because
// msg_scrolled is non-zero
+ if (msg_ext_kind == NULL) {
+ msg_ext_set_kind("emsg");
+ }
/*
* Display name and line number for the source of the error.
@@ -573,7 +621,18 @@ int emsg(const char_u *s_)
// Display the error message itself.
msg_nowait = false; // Wait for this msg.
- return msg_attr(s, attr);
+ return msg_attr_keep((char_u *)s, attr, false, multiline);
+}
+
+/// emsg() - display an error message
+///
+/// Rings the bell, if appropriate, and calls message() to do the real work
+/// When terminal not initialized (yet) mch_errmsg(..) is used.
+///
+/// @return true if wait_return not called
+bool emsg(const char_u *s)
+{
+ return emsg_multiline((const char *)s, false);
}
void emsg_invreg(int name)
@@ -595,6 +654,28 @@ bool emsgf(const char *const fmt, ...)
return ret;
}
+#define MULTILINE_BUFSIZE 8192
+
+bool emsgf_multiline(const char *const fmt, ...)
+{
+ bool ret;
+ va_list ap;
+
+
+ static char errbuf[MULTILINE_BUFSIZE];
+ if (emsg_not_now()) {
+ return true;
+ }
+
+ va_start(ap, fmt);
+ vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL);
+ va_end(ap);
+
+ ret = emsg_multiline(errbuf, true);
+
+ return ret;
+}
+
/// Print an error message with unknown number of arguments
static bool emsgfv(const char *fmt, va_list ap)
{
@@ -669,7 +750,7 @@ char_u *msg_trunc_attr(char_u *s, int force, int attr)
int n;
// Add message to history before truncating.
- add_msg_hist((const char *)s, -1, attr);
+ add_msg_hist((const char *)s, -1, attr, false);
s = msg_may_trunc(force, s);
@@ -715,7 +796,7 @@ char_u *msg_may_trunc(int force, char_u *s)
}
/// @param[in] len Length of s or -1.
-static void add_msg_hist(const char *s, int len, int attr)
+static void add_msg_hist(const char *s, int len, int attr, bool multiline)
{
if (msg_hist_off || msg_silent != 0)
return;
@@ -739,12 +820,16 @@ static void add_msg_hist(const char *s, int len, int attr)
p->msg = (char_u *)xmemdupz(s, (size_t)len);
p->next = NULL;
p->attr = attr;
- if (last_msg_hist != NULL)
+ p->multiline = multiline;
+ p->kind = msg_ext_kind;
+ if (last_msg_hist != NULL) {
last_msg_hist->next = p;
+ }
last_msg_hist = p;
- if (first_msg_hist == NULL)
+ if (first_msg_hist == NULL) {
first_msg_hist = last_msg_hist;
- ++msg_hist_len;
+ }
+ msg_hist_len++;
}
/*
@@ -791,7 +876,6 @@ void ex_messages(void *const eap_p)
return;
}
- msg_hist_off = true;
p = first_msg_hist;
@@ -809,13 +893,31 @@ void ex_messages(void *const eap_p)
}
// Display what was not skipped.
- for (; p != NULL && !got_int; p = p->next) {
- if (p->msg != NULL) {
- msg_attr((const char *)p->msg, p->attr);
+ if (ui_has(kUIMessages)) {
+ Array entries = ARRAY_DICT_INIT;
+ for (; p != NULL; p = p->next) {
+ if (p->msg != NULL && p->msg[0] != NUL) {
+ Array entry = ARRAY_DICT_INIT;
+ ADD(entry, STRING_OBJ(cstr_to_string(p->kind)));
+ Array content_entry = ARRAY_DICT_INIT;
+ ADD(content_entry, INTEGER_OBJ(p->attr));
+ ADD(content_entry, STRING_OBJ(cstr_to_string((char *)(p->msg))));
+ Array content = ARRAY_DICT_INIT;
+ ADD(content, ARRAY_OBJ(content_entry));
+ ADD(entry, ARRAY_OBJ(content));
+ ADD(entries, ARRAY_OBJ(entry));
+ }
+ }
+ ui_call_msg_history_show(entries);
+ } else {
+ msg_hist_off = true;
+ for (; p != NULL && !got_int; p = p->next) {
+ if (p->msg != NULL) {
+ msg_attr_keep(p->msg, p->attr, false, p->multiline);
+ }
}
+ msg_hist_off = false;
}
-
- msg_hist_off = false;
}
/*
@@ -881,9 +983,9 @@ void wait_return(int redraw)
c = CAR; /* no need for a return in ex mode */
got_int = FALSE;
} else {
- /* Make sure the hit-return prompt is on screen when 'guioptions' was
- * just changed. */
- screenalloc(false);
+ // Make sure the hit-return prompt is on screen when 'guioptions' was
+ // just changed.
+ screenalloc();
State = HITRETURN;
setmouse();
@@ -993,8 +1095,9 @@ void wait_return(int redraw)
if (c == ':' || c == '?' || c == '/') {
if (!exmode_active)
cmdline_row = msg_row;
- skip_redraw = TRUE; /* skip redraw once */
- do_redraw = FALSE;
+ skip_redraw = true; // skip redraw once
+ do_redraw = false;
+ msg_ext_keep_after_cmdline = true;
}
/*
@@ -1019,9 +1122,13 @@ void wait_return(int redraw)
if (tmpState == SETWSIZE) { /* got resize event while in vgetc() */
ui_refresh();
- } else if (!skip_redraw
- && (redraw == TRUE || (msg_scrolled != 0 && redraw != -1))) {
- redraw_later(VALID);
+ } else if (!skip_redraw) {
+ if (redraw == true || (msg_scrolled != 0 && redraw != -1)) {
+ redraw_later(VALID);
+ }
+ if (ui_has(kUIMessages)) {
+ msg_ext_clear(true);
+ }
}
}
@@ -1035,8 +1142,10 @@ static void hit_return_msg(void)
p_more = FALSE; /* don't want see this message when scrolling back */
if (msg_didout) /* start on a new line */
msg_putchar('\n');
- if (got_int)
+ msg_ext_set_kind("return_prompt");
+ if (got_int) {
MSG_PUTS(_("Interrupt: "));
+ }
MSG_PUTS_ATTR(_("Press ENTER or type command to continue"), HL_ATTR(HLF_R));
if (!msg_use_printf()) {
@@ -1059,6 +1168,17 @@ void set_keep_msg(char_u *s, int attr)
keep_msg_attr = attr;
}
+void msg_ext_set_kind(const char *msg_kind)
+{
+ // Don't change the label of an existing batch:
+ msg_ext_ui_flush();
+
+ // TODO(bfredl): would be nice to avoid dynamic scoping, but that would
+ // need refactoring the msg_ interface to not be "please pretend nvim is
+ // a terminal for a moment"
+ msg_ext_kind = msg_kind;
+}
+
/*
* Prepare for outputting characters in the command line.
*/
@@ -1095,6 +1215,14 @@ void msg_start(void)
msg_didout = FALSE; /* no output on current line yet */
}
+ if (ui_has(kUIMessages)) {
+ msg_ext_ui_flush();
+ if (!msg_scroll && msg_ext_visible) {
+ // Will overwrite last message.
+ msg_ext_overwrite = true;
+ }
+ }
+
// When redirecting, may need to start a new line.
if (!did_return) {
redir_write("\n", 1);
@@ -1205,7 +1333,7 @@ int msg_outtrans_len_attr(char_u *msgstr, int len, int attr)
/* if MSG_HIST flag set, add message to history */
if (attr & MSG_HIST) {
- add_msg_hist(str, len, attr);
+ add_msg_hist(str, len, attr, false);
attr &= ~MSG_HIST;
}
@@ -1452,31 +1580,36 @@ void msg_prt_line(char_u *s, int list)
int col = 0;
int n_extra = 0;
int c_extra = 0;
- char_u *p_extra = NULL; /* init to make SASC shut up */
+ int c_final = 0;
+ char_u *p_extra = NULL; // init to make SASC shut up
int n;
int attr = 0;
- char_u *trail = NULL;
+ char_u *trail = NULL;
int l;
- if (curwin->w_p_list)
- list = TRUE;
+ if (curwin->w_p_list) {
+ list = true;
+ }
- /* find start of trailing whitespace */
- if (list && lcs_trail) {
+ // find start of trailing whitespace
+ if (list && curwin->w_p_lcs_chars.trail) {
trail = s + STRLEN(s);
- while (trail > s && ascii_iswhite(trail[-1]))
- --trail;
+ while (trail > s && ascii_iswhite(trail[-1])) {
+ trail--;
+ }
}
- /* output a space for an empty line, otherwise the line will be
- * overwritten */
- if (*s == NUL && !(list && lcs_eol != NUL))
+ // output a space for an empty line, otherwise the line will be overwritten
+ if (*s == NUL && !(list && curwin->w_p_lcs_chars.eol != NUL)) {
msg_putchar(' ');
+ }
while (!got_int) {
if (n_extra > 0) {
n_extra--;
- if (c_extra) {
+ if (n_extra == 0 && c_final) {
+ c = c_final;
+ } else if (c_extra) {
c = c_extra;
} else {
assert(p_extra != NULL);
@@ -1485,9 +1618,9 @@ void msg_prt_line(char_u *s, int list)
} else if ((l = utfc_ptr2len(s)) > 1) {
col += utf_ptr2cells(s);
char buf[MB_MAXBYTES + 1];
- if (lcs_nbsp != NUL && list
+ if (curwin->w_p_lcs_chars.nbsp != NUL && list
&& (utf_ptr2char(s) == 160 || utf_ptr2char(s) == 0x202f)) {
- utf_char2bytes(lcs_nbsp, (char_u *)buf);
+ utf_char2bytes(curwin->w_p_lcs_chars.nbsp, (char_u *)buf);
buf[utfc_ptr2len((char_u *)buf)] = NUL;
} else {
memmove(buf, s, (size_t)l);
@@ -1499,40 +1632,46 @@ void msg_prt_line(char_u *s, int list)
} else {
attr = 0;
c = *s++;
- if (c == TAB && (!list || lcs_tab1)) {
- /* tab amount depends on current column */
+ if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) {
+ // tab amount depends on current column
n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
if (!list) {
c = ' ';
c_extra = ' ';
+ c_final = NUL;
} else {
- c = lcs_tab1;
- c_extra = lcs_tab2;
+ c = (n_extra == 0 && curwin->w_p_lcs_chars.tab3)
+ ? curwin->w_p_lcs_chars.tab3
+ : curwin->w_p_lcs_chars.tab1;
+ c_extra = curwin->w_p_lcs_chars.tab2;
+ c_final = curwin->w_p_lcs_chars.tab3;
attr = HL_ATTR(HLF_8);
}
- } else if (c == 160 && list && lcs_nbsp != NUL) {
- c = lcs_nbsp;
+ } else if (c == 160 && list && curwin->w_p_lcs_chars.nbsp != NUL) {
+ c = curwin->w_p_lcs_chars.nbsp;
attr = HL_ATTR(HLF_8);
- } else if (c == NUL && list && lcs_eol != NUL) {
+ } else if (c == NUL && list && curwin->w_p_lcs_chars.eol != NUL) {
p_extra = (char_u *)"";
c_extra = NUL;
+ c_final = NUL;
n_extra = 1;
- c = lcs_eol;
+ c = curwin->w_p_lcs_chars.eol;
attr = HL_ATTR(HLF_AT);
s--;
} else if (c != NUL && (n = byte2cells(c)) > 1) {
n_extra = n - 1;
p_extra = transchar_byte(c);
c_extra = NUL;
+ c_final = NUL;
c = *p_extra++;
/* Use special coloring to be able to distinguish <hex> from
* the same in plain text. */
attr = HL_ATTR(HLF_8);
} else if (c == ' ' && trail != NULL && s > trail) {
- c = lcs_trail;
+ c = curwin->w_p_lcs_chars.trail;
attr = HL_ATTR(HLF_8);
- } else if (c == ' ' && list && lcs_space != NUL) {
- c = lcs_space;
+ } else if (c == ' ' && list && curwin->w_p_lcs_chars.space != NUL) {
+ c = curwin->w_p_lcs_chars.space;
attr = HL_ATTR(HLF_8);
}
}
@@ -1643,7 +1782,7 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
// if MSG_HIST flag set, add message to history
if (attr & MSG_HIST) {
- add_msg_hist(str, (int)len, attr);
+ add_msg_hist(str, (int)len, attr, false);
attr &= ~MSG_HIST;
}
@@ -1651,7 +1790,18 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr)
// wait-return prompt later. Needed when scrolling, resetting
// need_wait_return after some prompt, and then outputting something
// without scrolling
- if (msg_scrolled != 0 && !msg_scrolled_ign) {
+ bool overflow = false;
+ if (ui_has(kUIMessages)) {
+ int count = msg_ext_visible + (msg_ext_overwrite ? 0 : 1);
+ // TODO(bfredl): possible extension point, let external UI control this
+ if (count > 1) {
+ overflow = true;
+ }
+ } else {
+ overflow = msg_scrolled != 0;
+ }
+
+ if (overflow && !msg_scrolled_ign) {
need_wait_return = true;
}
msg_didany = true; // remember that something was outputted
@@ -1689,6 +1839,20 @@ void msg_printf_attr(const int attr, const char *const fmt, ...)
msg_puts_attr_len(msgbuf, (ptrdiff_t)len, attr);
}
+static void msg_ext_emit_chunk(void)
+{
+ // Color was changed or a message flushed, end current chunk.
+ if (msg_ext_last_attr == -1) {
+ return; // no chunk
+ }
+ Array chunk = ARRAY_DICT_INIT;
+ ADD(chunk, INTEGER_OBJ(msg_ext_last_attr));
+ msg_ext_last_attr = -1;
+ String text = ga_take_string(&msg_ext_last_chunk);
+ ADD(chunk, STRING_OBJ(text));
+ ADD(msg_ext_chunks, ARRAY_OBJ(chunk));
+}
+
/*
* The display part of msg_puts_attr_len().
* May be called recursively to display scroll-back text.
@@ -1707,6 +1871,18 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr,
int did_last_char;
did_wait_return = false;
+
+ if (ui_has(kUIMessages)) {
+ if (attr != msg_ext_last_attr) {
+ msg_ext_emit_chunk();
+ msg_ext_last_attr = attr;
+ }
+ // Concat pieces with the same highlight
+ ga_concat_len(&msg_ext_last_chunk, (char *)str,
+ strnlen((char *)str, maxlen));
+ return;
+ }
+
while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) {
// We are at the end of the screen line when:
// - When outputting a newline.
@@ -1884,13 +2060,15 @@ int msg_scrollsize(void)
*/
static void msg_scroll_up(void)
{
- if (msg_scrolled == 0) {
+ if (!msg_did_scroll) {
ui_call_win_scroll_over_start();
+ msg_did_scroll = true;
}
if (dy_flags & DY_MSGSEP) {
if (msg_scrolled == 0) {
grid_fill(&default_grid, Rows-p_ch-1, Rows-p_ch, 0, (int)Columns,
- fill_msgsep, fill_msgsep, HL_ATTR(HLF_MSGSEP));
+ curwin->w_p_fcs_chars.msgsep, curwin->w_p_fcs_chars.msgsep,
+ HL_ATTR(HLF_MSGSEP));
}
int nscroll = MIN(msg_scrollsize()+1, Rows);
grid_del_lines(&default_grid, Rows-nscroll, 1, Rows, 0, Columns);
@@ -2314,9 +2492,8 @@ static int do_more_prompt(int typed_char)
mp_last = msg_sb_start(mp_last->sb_prev);
}
- if (toscroll == -1
- && grid_ins_lines(&default_grid, 0, 1, (int)Rows,
- 0, (int)Columns) == OK) {
+ if (toscroll == -1) {
+ grid_ins_lines(&default_grid, 0, 1, (int)Rows, 0, (int)Columns);
grid_fill(&default_grid, 0, 1, 0, (int)Columns, ' ', ' ', 0);
// display line at top
(void)disp_sb_line(0, mp);
@@ -2530,6 +2707,9 @@ void msg_clr_eos(void)
*/
void msg_clr_eos_force(void)
{
+ if (ui_has(kUIMessages)) {
+ return;
+ }
int msg_startcol = (cmdmsg_rl) ? 0 : msg_col;
int msg_endcol = (cmdmsg_rl) ? msg_col + 1 : (int)Columns;
@@ -2566,8 +2746,66 @@ int msg_end(void)
wait_return(FALSE);
return FALSE;
}
- ui_flush();
- return TRUE;
+
+ // @TODO(bfredl): calling flush here inhibits substantial performance
+ // improvements. Caller should call ui_flush before waiting on user input or
+ // CPU busywork.
+ ui_flush(); // calls msg_ext_ui_flush
+ return true;
+}
+
+void msg_ext_ui_flush(void)
+{
+ if (!ui_has(kUIMessages)) {
+ return;
+ }
+
+ msg_ext_emit_chunk();
+ if (msg_ext_chunks.size > 0) {
+ ui_call_msg_show(cstr_to_string(msg_ext_kind),
+ msg_ext_chunks, msg_ext_overwrite);
+ if (!msg_ext_overwrite) {
+ msg_ext_visible++;
+ }
+ msg_ext_kind = NULL;
+ msg_ext_chunks = (Array)ARRAY_DICT_INIT;
+ msg_ext_overwrite = false;
+ }
+}
+
+void msg_ext_flush_showmode(void)
+{
+ // Showmode messages doesn't interrupt normal message flow, so we use
+ // separate event. Still reuse the same chunking logic, for simplicity.
+ msg_ext_emit_chunk();
+ ui_call_msg_showmode(msg_ext_chunks);
+ msg_ext_chunks = (Array)ARRAY_DICT_INIT;
+}
+
+void msg_ext_clear(bool force)
+{
+ if (msg_ext_visible && (!msg_ext_keep_after_cmdline || force)) {
+ ui_call_msg_clear();
+ msg_ext_visible = 0;
+ msg_ext_overwrite = false; // nothing to overwrite
+ }
+
+ // Only keep once.
+ msg_ext_keep_after_cmdline = false;
+}
+
+void msg_ext_check_prompt(void)
+{
+ // Redraw after cmdline is expected to clear messages.
+ if (msg_ext_did_cmdline) {
+ msg_ext_clear(true);
+ msg_ext_did_cmdline = false;
+ }
+}
+
+bool msg_ext_is_visible(void)
+{
+ return ui_has(kUIMessages) && msg_ext_visible > 0;
}
/*
@@ -2576,6 +2814,9 @@ int msg_end(void)
*/
void msg_check(void)
{
+ if (ui_has(kUIMessages)) {
+ return;
+ }
if (msg_row == Rows - 1 && msg_col >= sc_col) {
need_wait_return = TRUE;
redraw_cmdline = TRUE;
diff --git a/src/nvim/message.h b/src/nvim/message.h
index 82935a36a9..7938fd91d3 100644
--- a/src/nvim/message.h
+++ b/src/nvim/message.h
@@ -5,6 +5,7 @@
#include <stdarg.h>
#include <stddef.h>
+#include "nvim/macros.h"
#include "nvim/types.h"
/*
@@ -77,7 +78,9 @@
typedef struct msg_hist {
struct msg_hist *next; ///< Next message.
char_u *msg; ///< Message text.
+ const char *kind; ///< Message kind (for msg_ext)
int attr; ///< Message highlighting.
+ bool multiline; ///< Multiline message.
} MessageHistoryEntry;
/// First message
@@ -85,6 +88,8 @@ extern MessageHistoryEntry *first_msg_hist;
/// Last message
extern MessageHistoryEntry *last_msg_hist;
+EXTERN bool msg_ext_did_cmdline INIT(= false);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "message.h.generated.h"
#endif
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index a66ded13f1..e752910a4b 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -1251,7 +1251,7 @@ int plines_win_nofill(
return 1;
}
- if (wp->w_grid.Columns == 0) {
+ if (wp->w_width_inner == 0) {
return 1;
}
@@ -1261,8 +1261,8 @@ int plines_win_nofill(
}
const int lines = plines_win_nofold(wp, lnum);
- if (winheight && lines > wp->w_grid.Rows) {
- return wp->w_grid.Rows;
+ if (winheight && lines > wp->w_height_inner) {
+ return wp->w_height_inner;
}
return lines;
}
@@ -1282,17 +1282,16 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
return 1;
col = win_linetabsize(wp, s, (colnr_T)MAXCOL);
- /*
- * If list mode is on, then the '$' at the end of the line may take up one
- * extra column.
- */
- if (wp->w_p_list && lcs_eol != NUL)
+ // If list mode is on, then the '$' at the end of the line may take up one
+ // extra column.
+ if (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL) {
col += 1;
+ }
/*
* Add column offset for 'number', 'relativenumber' and 'foldcolumn'.
*/
- width = wp->w_grid.Columns - win_col_off(wp);
+ width = wp->w_width_inner - win_col_off(wp);
if (width <= 0 || col > 32000) {
return 32000; // bigger than the number of screen columns
}
@@ -1318,7 +1317,7 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
if (!wp->w_p_wrap)
return lines + 1;
- if (wp->w_grid.Columns == 0) {
+ if (wp->w_width_inner == 0) {
return lines + 1;
}
@@ -1336,12 +1335,13 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
// screen position of the TAB. This only fixes an error when the TAB wraps
// from one screen line to the next (when 'columns' is not a multiple of
// 'ts') -- webb.
- if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1)) {
+ if (*s == TAB && (State & NORMAL)
+ && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1;
}
// Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
- int width = wp->w_grid.Columns - win_col_off(wp);
+ int width = wp->w_width_inner - win_col_off(wp);
if (width <= 0) {
return 9999;
}
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 887cbde921..50dba92eca 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -70,6 +70,7 @@ int jump_to_mouse(int flags,
bool first;
int row = mouse_row;
int col = mouse_col;
+ int grid = mouse_grid;
int mouse_char;
mouse_past_bottom = false;
@@ -125,20 +126,22 @@ retnomove:
return IN_UNKNOWN;
// find the window where the row is in
- wp = mouse_find_win(&row, &col);
+ wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return IN_UNKNOWN;
}
dragwin = NULL;
// winpos and height may change in win_enter()!
- if (row >= wp->w_height) { // In (or below) status line
+ if (grid == DEFAULT_GRID_HANDLE && row >= wp->w_height) {
+ // In (or below) status line
on_status_line = row - wp->w_height + 1;
dragwin = wp;
} else {
on_status_line = 0;
}
- if (col >= wp->w_width) { // In separator line
+ if (grid == DEFAULT_GRID_HANDLE && col >= wp->w_width) {
+ // In separator line
on_sep_line = col - wp->w_width + 1;
dragwin = wp;
} else {
@@ -160,12 +163,10 @@ retnomove:
&& (wp->w_buffer != curwin->w_buffer
|| (!on_status_line
&& !on_sep_line
- && (
- wp->w_p_rl ? col < wp->w_width - wp->w_p_fdc :
- col >= wp->w_p_fdc
- + (cmdwin_type == 0 && wp ==
- curwin ? 0 : 1)
- )
+ && (wp->w_p_rl
+ ? col < wp->w_width_inner - wp->w_p_fdc
+ : col >= wp->w_p_fdc + (cmdwin_type == 0 && wp == curwin
+ ? 0 : 1))
&& (flags & MOUSE_MAY_STOP_VIS)))) {
end_visual_mode();
redraw_curbuf_later(INVERTED); // delete the inversion
@@ -257,7 +258,7 @@ retnomove:
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
redraw_later(VALID);
row = 0;
- } else if (row >= curwin->w_height) {
+ } else if (row >= curwin->w_height_inner) {
count = 0;
for (first = true; curwin->w_topline < curbuf->b_ml.ml_line_count; ) {
if (curwin->w_topfill > 0) {
@@ -266,7 +267,7 @@ retnomove:
count += plines(curwin->w_topline);
}
- if (!first && count > row - curwin->w_height + 1) {
+ if (!first && count > row - curwin->w_height_inner + 1) {
break;
}
first = false;
@@ -288,7 +289,7 @@ retnomove:
redraw_later(VALID);
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
- row = curwin->w_height - 1;
+ row = curwin->w_height_inner - 1;
} else if (row == 0) {
// When dragging the mouse, while the text has been scrolled up as
// far as it goes, moving the mouse in the top line should scroll
@@ -303,7 +304,7 @@ retnomove:
}
// Check for position outside of the fold column.
- if (curwin->w_p_rl ? col < curwin->w_width - curwin->w_p_fdc :
+ if (curwin->w_p_rl ? col < curwin->w_width_inner - curwin->w_p_fdc :
col >= curwin->w_p_fdc + (cmdwin_type == 0 ? 0 : 1)) {
mouse_char = ' ';
}
@@ -369,8 +370,9 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
int off;
int count;
- if (win->w_p_rl)
- col = win->w_width - 1 - col;
+ if (win->w_p_rl) {
+ col = win->w_width_inner - 1 - col;
+ }
lnum = win->w_topline;
@@ -407,7 +409,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
off = win_col_off(win) - win_col_off2(win);
if (col < off)
col = off;
- col += row * (win->w_width - off);
+ col += row * (win->w_width_inner - off);
// add skip column (for long wrapping line)
col += win->w_skipcol;
}
@@ -428,11 +430,23 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
return retval;
}
-// Find the window at screen position "*rowp" and "*colp". The positions are
-// updated to become relative to the top-left of the window.
-// Returns NULL when something is wrong.
-win_T *mouse_find_win(int *rowp, int *colp)
+/// Find the window at "grid" position "*rowp" and "*colp". The positions are
+/// updated to become relative to the top-left of the window.
+///
+/// @return NULL when something is wrong.
+win_T *mouse_find_win(int *gridp, int *rowp, int *colp)
{
+ win_T *wp_grid = mouse_find_grid_win(gridp, rowp, colp);
+ if (wp_grid) {
+ return wp_grid;
+ }
+
+ // TODO(bfredl): grid zero will have floats displayed on it, and will
+ // be adjusted to float grids.
+ if (*gridp == 0) {
+ *gridp = DEFAULT_GRID_HANDLE;
+ }
+
frame_T *fp;
fp = topframe;
@@ -464,6 +478,19 @@ win_T *mouse_find_win(int *rowp, int *colp)
return NULL;
}
+static win_T *mouse_find_grid_win(int *grid, int *rowp, int *colp)
+{
+ if (*grid > 1) {
+ win_T *wp = get_win_by_grid_handle(*grid);
+ if (wp && wp->w_grid.chars) {
+ *rowp = MIN(*rowp, wp->w_grid.Rows-1);
+ *colp = MIN(*colp, wp->w_grid.Columns-1);
+ return wp;
+ }
+ }
+ return NULL;
+}
+
/*
* setmouse() - switch mouse on/off depending on current mode and 'mouse'
*/
@@ -595,7 +622,7 @@ bool mouse_scroll_horiz(int dir)
int step = 6;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
- step = curwin->w_width;
+ step = curwin->w_width_inner;
}
int leftcol = curwin->w_leftcol + (dir == MSCR_RIGHT ? -step : +step);
@@ -647,7 +674,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
// Find the offset where scanning should begin.
int offset = wp->w_leftcol;
if (row > 0) {
- offset += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) -
+ offset += row * (wp->w_width_inner - win_col_off(wp) - win_col_off2(wp) -
wp->w_leftcol + wp->w_skipcol);
}
@@ -704,7 +731,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
} else {
if (!(row > 0 && ptr == ptr_row_offset)
&& (wp->w_p_cole == 1 || (wp->w_p_cole == 2
- && (lcs_conceal != NUL
+ && (wp->w_p_lcs_chars.conceal != NUL
|| syn_get_sub_char() != NUL)))) {
// At least one placeholder character will be displayed.
decr();
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 07b355e603..7aa7f922c1 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -83,7 +83,7 @@ static void comp_botline(win_T *wp)
redraw_for_cursorline(wp);
wp->w_valid |= (VALID_CROW|VALID_CHEIGHT);
}
- if (done + n > wp->w_grid.Rows) {
+ if (done + n > wp->w_height_inner) {
break;
}
done += n;
@@ -150,12 +150,9 @@ void update_topline(void)
bool check_botline = false;
long save_so = p_so;
- // need to have w_grid.Rows/Columns updated
- win_grid_alloc(curwin);
-
// If there is no valid screen and when the window height is zero just use
// the cursor line.
- if (!screen_valid(true) || curwin->w_grid.Rows == 0) {
+ if (!default_grid.chars || curwin->w_height_inner == 0) {
curwin->w_topline = curwin->w_cursor.lnum;
curwin->w_botline = curwin->w_topline;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
@@ -204,7 +201,7 @@ void update_topline(void)
check_topline = true;
if (check_topline) {
- int halfheight = curwin->w_grid.Rows / 2 - 1;
+ int halfheight = curwin->w_height_inner / 2 - 1;
if (halfheight < 2) {
halfheight = 2;
}
@@ -297,7 +294,7 @@ void update_topline(void)
lnum >= curwin->w_botline - p_so; lnum--) {
line_count++;
// stop at end of file or when we know we are far off
- if (lnum <= 0 || line_count > curwin->w_grid.Rows + 1) {
+ if (lnum <= 0 || line_count > curwin->w_height_inner + 1) {
break;
}
(void)hasFolding(lnum, &lnum, NULL);
@@ -305,7 +302,7 @@ void update_topline(void)
} else
line_count = curwin->w_cursor.lnum - curwin->w_botline
+ 1 + p_so;
- if (line_count <= curwin->w_grid.Rows + 1) {
+ if (line_count <= curwin->w_height_inner + 1) {
scroll_cursor_bot(scrolljump_value(), false);
} else {
scroll_cursor_halfway(false);
@@ -353,7 +350,7 @@ void update_topline_win(win_T* win)
*/
static int scrolljump_value(void)
{
- long result = p_sj >= 0 ? p_sj : (curwin->w_grid.Rows * -p_sj) / 100;
+ long result = p_sj >= 0 ? p_sj : (curwin->w_height_inner * -p_sj) / 100;
assert(result <= INT_MAX);
return (int)result;
}
@@ -529,7 +526,6 @@ int cursor_valid(void)
*/
void validate_cursor(void)
{
- win_grid_alloc(curwin); // we need to have w_grid.Rows/Columns updated
check_cursor_moved(curwin);
if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
curs_columns(true);
@@ -668,13 +664,13 @@ void validate_cursor_col(void)
colnr_T col = curwin->w_virtcol;
colnr_T off = curwin_col_off();
col += off;
- int width = curwin->w_grid.Columns - off + curwin_col_off2();
+ int width = curwin->w_width_inner - off + curwin_col_off2();
// long line wrapping, adjust curwin->w_wrow
- if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_grid.Columns
+ if (curwin->w_p_wrap && col >= (colnr_T)curwin->w_width_inner
&& width > 0) {
// use same formula as what is used in curs_columns()
- col -= ((col - curwin->w_grid.Columns) / width + 1) * width;
+ col -= ((col - curwin->w_width_inner) / width + 1) * width;
}
if (col > (int)curwin->w_leftcol) {
col -= curwin->w_leftcol;
@@ -769,20 +765,20 @@ void curs_columns(
*/
curwin->w_wrow = curwin->w_cline_row;
- int textwidth = curwin->w_grid.Columns - extra;
+ int textwidth = curwin->w_width_inner - extra;
if (textwidth <= 0) {
// No room for text, put cursor in last char of window.
- curwin->w_wcol = curwin->w_grid.Columns - 1;
- curwin->w_wrow = curwin->w_grid.Rows - 1;
+ curwin->w_wcol = curwin->w_width_inner - 1;
+ curwin->w_wrow = curwin->w_height_inner - 1;
} else if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
width = textwidth + curwin_col_off2();
// long line wrapping, adjust curwin->w_wrow
- if (curwin->w_wcol >= curwin->w_grid.Columns) {
+ if (curwin->w_wcol >= curwin->w_width_inner) {
// this same formula is used in validate_cursor_col()
- n = (curwin->w_wcol - curwin->w_grid.Columns) / width + 1;
+ n = (curwin->w_wcol - curwin->w_width_inner) / width + 1;
curwin->w_wcol -= n * width;
curwin->w_wrow += n;
@@ -809,7 +805,7 @@ void curs_columns(
assert(p_siso <= INT_MAX);
int off_left = startcol - curwin->w_leftcol - (int)p_siso;
int off_right =
- endcol - curwin->w_leftcol - curwin->w_grid.Columns + (int)p_siso + 1;
+ endcol - curwin->w_leftcol - curwin->w_width_inner + (int)p_siso + 1;
if (off_left < 0 || off_right > 0) {
int diff = (off_left < 0) ? -off_left: off_right;
@@ -852,16 +848,16 @@ void curs_columns(
prev_skipcol = curwin->w_skipcol;
int p_lines = 0;
- if ((curwin->w_wrow >= curwin->w_grid.Rows
+ if ((curwin->w_wrow >= curwin->w_height_inner
|| ((prev_skipcol > 0
- || curwin->w_wrow + p_so >= curwin->w_grid.Rows)
+ || curwin->w_wrow + p_so >= curwin->w_height_inner)
&& (p_lines =
plines_win_nofill(curwin, curwin->w_cursor.lnum, false)) - 1
- >= curwin->w_grid.Rows))
- && curwin->w_grid.Rows != 0
+ >= curwin->w_height_inner))
+ && curwin->w_height_inner != 0
&& curwin->w_cursor.lnum == curwin->w_topline
&& width > 0
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
/* Cursor past end of screen. Happens with a single line that does
* not fit on screen. Find a skipcol to show the text around the
@@ -883,21 +879,21 @@ void curs_columns(
}
else
n = p_lines;
- if ((colnr_T)n >= curwin->w_grid.Rows + curwin->w_skipcol / width) {
+ if ((colnr_T)n >= curwin->w_height_inner + curwin->w_skipcol / width) {
extra += 2;
}
if (extra == 3 || p_lines < p_so * 2) {
// not enough room for 'scrolloff', put cursor in the middle
n = curwin->w_virtcol / width;
- if (n > curwin->w_grid.Rows / 2) {
- n -= curwin->w_grid.Rows / 2;
+ if (n > curwin->w_height_inner / 2) {
+ n -= curwin->w_height_inner / 2;
} else {
n = 0;
}
// don't skip more than necessary
- if (n > p_lines - curwin->w_grid.Rows + 1) {
- n = p_lines - curwin->w_grid.Rows + 1;
+ if (n > p_lines - curwin->w_height_inner + 1) {
+ n = p_lines - curwin->w_height_inner + 1;
}
curwin->w_skipcol = n * width;
} else if (extra == 1) {
@@ -912,7 +908,7 @@ void curs_columns(
}
} else if (extra == 2) {
// less then 'scrolloff' lines below, increase skipcol
- endcol = (n - curwin->w_grid.Rows + 1) * width;
+ endcol = (n - curwin->w_height_inner + 1) * width;
while (endcol > curwin->w_virtcol) {
endcol -= width;
}
@@ -922,19 +918,16 @@ void curs_columns(
}
curwin->w_wrow -= curwin->w_skipcol / width;
- if (curwin->w_wrow >= curwin->w_grid.Rows) {
+ if (curwin->w_wrow >= curwin->w_height_inner) {
// small window, make sure cursor is in it
- extra = curwin->w_wrow - curwin->w_grid.Rows + 1;
+ extra = curwin->w_wrow - curwin->w_height_inner + 1;
curwin->w_skipcol += extra * width;
curwin->w_wrow -= extra;
}
+ // extra could be either positive or negative
extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width;
- if (extra > 0) {
- win_ins_lines(curwin, 0, extra);
- } else if (extra < 0) {
- win_del_lines(curwin, 0, -extra);
- }
+ win_scroll_lines(curwin, 0, extra);
} else {
curwin->w_skipcol = 0;
}
@@ -966,7 +959,7 @@ scrolldown (
validate_cursor(); /* w_wrow needs to be valid */
while (line_count-- > 0) {
if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)
- && curwin->w_topfill < curwin->w_grid.Rows - 1) {
+ && curwin->w_topfill < curwin->w_height_inner - 1) {
curwin->w_topfill++;
done++;
} else {
@@ -1001,15 +994,15 @@ scrolldown (
*/
int wrow = curwin->w_wrow;
if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
validate_virtcol();
validate_cheight();
wrow += curwin->w_cline_height - 1 -
- curwin->w_virtcol / curwin->w_grid.Columns;
+ curwin->w_virtcol / curwin->w_width_inner;
}
bool moved = false;
- while (wrow >= curwin->w_grid.Rows && curwin->w_cursor.lnum > 1) {
+ while (wrow >= curwin->w_height_inner && curwin->w_cursor.lnum > 1) {
linenr_T first;
if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) {
--wrow;
@@ -1094,12 +1087,12 @@ check_topfill (
{
if (wp->w_topfill > 0) {
int n = plines_win_nofill(wp, wp->w_topline, true);
- if (wp->w_topfill + n > wp->w_grid.Rows) {
+ if (wp->w_topfill + n > wp->w_height_inner) {
if (down && wp->w_topline > 1) {
--wp->w_topline;
wp->w_topfill = 0;
} else {
- wp->w_topfill = wp->w_grid.Rows - n;
+ wp->w_topfill = wp->w_height_inner - n;
if (wp->w_topfill < 0) {
wp->w_topfill = 0;
}
@@ -1115,12 +1108,12 @@ check_topfill (
static void max_topfill(void)
{
int n = plines_nofill(curwin->w_topline);
- if (n >= curwin->w_grid.Rows) {
+ if (n >= curwin->w_height_inner) {
curwin->w_topfill = 0;
} else {
curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline);
- if (curwin->w_topfill + n > curwin->w_grid.Rows) {
- curwin->w_topfill = curwin->w_grid.Rows - n;
+ if (curwin->w_topfill + n > curwin->w_height_inner) {
+ curwin->w_topfill = curwin->w_height_inner - n;
}
}
}
@@ -1152,14 +1145,14 @@ void scrolldown_clamp(void)
else
end_row += plines_nofill(curwin->w_topline - 1);
if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
validate_cheight();
validate_virtcol();
end_row += curwin->w_cline_height - 1 -
- curwin->w_virtcol / curwin->w_grid.Columns;
+ curwin->w_virtcol / curwin->w_width_inner;
}
- if (end_row < curwin->w_grid.Rows - p_so) {
+ if (end_row < curwin->w_height_inner - p_so) {
if (can_fill) {
++curwin->w_topfill;
check_topfill(curwin, true);
@@ -1194,10 +1187,10 @@ void scrollup_clamp(void)
int start_row = curwin->w_wrow - plines_nofill(curwin->w_topline)
- curwin->w_topfill;
if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
validate_virtcol();
- start_row -= curwin->w_virtcol / curwin->w_grid.Columns;
+ start_row -= curwin->w_virtcol / curwin->w_width_inner;
}
if (start_row >= p_so) {
if (curwin->w_topfill > 0)
@@ -1351,7 +1344,7 @@ void scroll_cursor_top(int min_scroll, int always)
else
used += plines(bot);
}
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
break;
}
if (top < curwin->w_topline) {
@@ -1376,7 +1369,7 @@ void scroll_cursor_top(int min_scroll, int always)
* This makes sure we get the same position when using "k" and "j"
* in a small window.
*/
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
scroll_cursor_halfway(false);
} else {
/*
@@ -1413,7 +1406,7 @@ void set_empty_rows(win_T *wp, int used)
if (used == 0) {
wp->w_empty_rows = 0; // single line that doesn't fit
} else {
- wp->w_empty_rows = wp->w_grid.Rows - used;
+ wp->w_empty_rows = wp->w_height_inner - used;
if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) {
wp->w_filler_rows = diff_check_fill(wp, wp->w_botline);
if (wp->w_empty_rows > wp->w_filler_rows)
@@ -1456,7 +1449,8 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
curwin->w_topline = loff.lnum) {
loff.lnum = curwin->w_topline;
topline_back(&loff);
- if (loff.height == MAXCOL || used + loff.height > curwin->w_grid.Rows) {
+ if (loff.height == MAXCOL
+ || used + loff.height > curwin->w_height_inner) {
break;
}
used += loff.height;
@@ -1520,7 +1514,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
} else {
used += loff.height;
}
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
break;
}
if (loff.lnum >= curwin->w_botline
@@ -1539,7 +1533,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
/* Add one line below */
botline_forw(&boff);
used += boff.height;
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
break;
}
if (extra < (
@@ -1566,7 +1560,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
if (scrolled <= 0) {
line_count = 0;
// more than a screenfull, don't scroll but redraw
- } else if (used > curwin->w_grid.Rows) {
+ } else if (used > curwin->w_height_inner) {
line_count = used;
// scroll minimal number of lines
} else {
@@ -1587,7 +1581,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
* Scroll up if the cursor is off the bottom of the screen a bit.
* Otherwise put it at 1/2 of the screen.
*/
- if (line_count >= curwin->w_grid.Rows && line_count > min_scroll) {
+ if (line_count >= curwin->w_height_inner && line_count > min_scroll) {
scroll_cursor_halfway(false);
} else {
scrollup(line_count, true);
@@ -1630,7 +1624,7 @@ void scroll_cursor_halfway(int atend)
if (boff.lnum < curbuf->b_ml.ml_line_count) {
botline_forw(&boff);
used += boff.height;
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
break;
}
below += boff.height;
@@ -1647,7 +1641,7 @@ void scroll_cursor_halfway(int atend)
used = MAXCOL;
else
used += loff.height;
- if (used > curwin->w_grid.Rows) {
+ if (used > curwin->w_height_inner) {
break;
}
above += loff.height;
@@ -1658,7 +1652,7 @@ void scroll_cursor_halfway(int atend)
if (!hasFolding(topline, &curwin->w_topline, NULL))
curwin->w_topline = topline;
curwin->w_topfill = topfill;
- if (old_topline > curwin->w_topline + curwin->w_grid.Rows) {
+ if (old_topline > curwin->w_topline + curwin->w_height_inner) {
curwin->w_botfill = false;
}
check_topfill(curwin, false);
@@ -1687,7 +1681,7 @@ void cursor_correct(void)
}
if (curwin->w_topline == 1) {
above_wanted = 0;
- int max_off = curwin->w_grid.Rows / 2;
+ int max_off = curwin->w_height_inner / 2;
if (below_wanted > max_off) {
below_wanted = max_off;
}
@@ -1697,7 +1691,7 @@ void cursor_correct(void)
&& mouse_dragging == 0
) {
below_wanted = 0;
- int max_off = (curwin->w_grid.Rows - 1) / 2;
+ int max_off = (curwin->w_height_inner - 1) / 2;
if (above_wanted > max_off) {
above_wanted = max_off;
}
@@ -1871,7 +1865,7 @@ int onepage(Direction dir, long count)
/* Find the line just above the new topline to get the right line
* at the bottom of the window. */
n = 0;
- while (n <= curwin->w_grid.Rows && loff.lnum >= 1) {
+ while (n <= curwin->w_height_inner && loff.lnum >= 1) {
topline_back(&loff);
if (loff.height == MAXCOL)
n = MAXCOL;
@@ -1962,7 +1956,7 @@ int onepage(Direction dir, long count)
*/
static void get_scroll_overlap(lineoff_T *lp, int dir)
{
- int min_height = curwin->w_grid.Rows - 2;
+ int min_height = curwin->w_height_inner - 2;
if (lp->fill > 0)
lp->height = 1;
@@ -2017,12 +2011,12 @@ void halfpage(bool flag, linenr_T Prenum)
int i;
if (Prenum) {
- curwin->w_p_scr = (Prenum > curwin->w_grid.Rows) ? curwin->w_grid.Rows
+ curwin->w_p_scr = (Prenum > curwin->w_height_inner) ? curwin->w_height_inner
: Prenum;
}
assert(curwin->w_p_scr <= INT_MAX);
- int n = curwin->w_p_scr <= curwin->w_grid.Rows ? (int)curwin->w_p_scr
- : curwin->w_grid.Rows;
+ int n = curwin->w_p_scr <= curwin->w_height_inner ? (int)curwin->w_p_scr
+ : curwin->w_height_inner;
update_topline();
validate_botline();
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 0e3946740a..d361d81ac7 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -61,6 +61,7 @@
#include "nvim/event/loop.h"
#include "nvim/os/time.h"
#include "nvim/os/input.h"
+#include "nvim/api/private/helpers.h"
typedef struct normal_state {
VimState state;
@@ -1258,8 +1259,9 @@ static void normal_redraw(NormalState *s)
maketitle();
}
- // display message after redraw
- if (keep_msg != NULL) {
+ // Display message after redraw. If an external message is still visible,
+ // it contains the kept message already.
+ if (keep_msg != NULL && !msg_ext_is_visible()) {
// msg_attr_keep() will set keep_msg to NULL, must free the string here.
// Don't reset keep_msg, msg_attr_keep() uses it to check for duplicates.
char *p = (char *)keep_msg;
@@ -3284,11 +3286,11 @@ void clear_showcmd(void)
p_sbr = empty_option;
getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
p_sbr = saved_sbr;
- sprintf((char *)showcmd_buf, "%" PRId64 "x%" PRId64,
- (int64_t)lines, (int64_t)(rightcol - leftcol + 1));
- } else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum)
- sprintf((char *)showcmd_buf, "%" PRId64, (int64_t)lines);
- else {
+ snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64 "x%" PRId64,
+ (int64_t)lines, (int64_t)rightcol - leftcol + 1);
+ } else if (VIsual_mode == 'V' || VIsual.lnum != curwin->w_cursor.lnum) {
+ snprintf((char *)showcmd_buf, SHOWCMD_BUFLEN, "%" PRId64, (int64_t)lines);
+ } else {
char_u *s, *e;
int l;
int bytes = 0;
@@ -3317,7 +3319,8 @@ void clear_showcmd(void)
else
sprintf((char *)showcmd_buf, "%d-%d", chars, bytes);
}
- showcmd_buf[SHOWCMD_COLS] = NUL; /* truncate */
+ int limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS;
+ showcmd_buf[limit] = NUL; // truncate
showcmd_visual = true;
} else {
showcmd_buf[0] = NUL;
@@ -3370,8 +3373,9 @@ bool add_to_showcmd(int c)
STRCPY(p, "<20>");
size_t old_len = STRLEN(showcmd_buf);
size_t extra_len = STRLEN(p);
- if (old_len + extra_len > SHOWCMD_COLS) {
- size_t overflow = old_len + extra_len - SHOWCMD_COLS;
+ size_t limit = ui_has(kUIMessages) ? SHOWCMD_BUFLEN-1 : SHOWCMD_COLS;
+ if (old_len + extra_len > limit) {
+ size_t overflow = old_len + extra_len - limit;
memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1);
}
STRCAT(showcmd_buf, p);
@@ -3432,13 +3436,24 @@ void pop_showcmd(void)
static void display_showcmd(void)
{
int len;
-
len = (int)STRLEN(showcmd_buf);
- if (len == 0) {
- showcmd_is_clear = true;
- } else {
+ showcmd_is_clear = (len == 0);
+
+ if (ui_has(kUIMessages)) {
+ Array content = ARRAY_DICT_INIT;
+ if (len > 0) {
+ Array chunk = ARRAY_DICT_INIT;
+ // placeholder for future highlight support
+ ADD(chunk, INTEGER_OBJ(0));
+ ADD(chunk, STRING_OBJ(cstr_to_string((char *)showcmd_buf)));
+ ADD(content, ARRAY_OBJ(chunk));
+ }
+ ui_call_msg_showcmd(content);
+ return;
+ }
+
+ if (!showcmd_is_clear) {
grid_puts(&default_grid, showcmd_buf, (int)Rows - 1, sc_col, 0);
- showcmd_is_clear = false;
}
/*
@@ -3875,14 +3890,14 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist)
col_off1 = curwin_col_off();
col_off2 = col_off1 - curwin_col_off2();
- width1 = curwin->w_grid.Columns - col_off1;
- width2 = curwin->w_grid.Columns - col_off2;
+ width1 = curwin->w_width_inner - col_off1;
+ width2 = curwin->w_width_inner - col_off2;
if (width2 == 0) {
width2 = 1; // Avoid divide by zero.
}
- if (curwin->w_grid.Columns != 0) {
+ if (curwin->w_width_inner != 0) {
// Instead of sticking at the last character of the buffer line we
// try to stick in the last column of the screen.
if (curwin->w_curswant == MAXCOL) {
@@ -3997,13 +4012,14 @@ static void nv_mousescroll(cmdarg_T *cap)
win_T *old_curwin = curwin;
if (mouse_row >= 0 && mouse_col >= 0) {
- int row, col;
+ int grid, row, col;
+ grid = mouse_grid;
row = mouse_row;
col = mouse_col;
// find the window at the pointer coordinates
- win_T *const wp = mouse_find_win(&row, &col);
+ win_T *wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return;
}
@@ -4225,7 +4241,7 @@ dozet:
/* "zH" - scroll screen right half-page */
case 'H':
- cap->count1 *= curwin->w_grid.Columns / 2;
+ cap->count1 *= curwin->w_width_inner / 2;
FALLTHROUGH;
/* "zh" - scroll screen to the right */
@@ -4241,7 +4257,7 @@ dozet:
break;
// "zL" - scroll screen left half-page
- case 'L': cap->count1 *= curwin->w_grid.Columns / 2;
+ case 'L': cap->count1 *= curwin->w_width_inner / 2;
FALLTHROUGH;
/* "zl" - scroll screen to the left */
@@ -4277,7 +4293,7 @@ dozet:
col = 0; /* like the cursor is in col 0 */
else
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
- n = curwin->w_grid.Columns - curwin_col_off();
+ n = curwin->w_width_inner - curwin_col_off();
if (col + l_p_siso < n) {
col = 0;
} else {
@@ -4930,10 +4946,10 @@ get_visual_text (
} else {
if (lt(curwin->w_cursor, VIsual)) {
*pp = ml_get_pos(&curwin->w_cursor);
- *lenp = (size_t)(VIsual.col - curwin->w_cursor.col + 1);
+ *lenp = (size_t)VIsual.col - (size_t)curwin->w_cursor.col + 1;
} else {
*pp = ml_get_pos(&VIsual);
- *lenp = (size_t)(curwin->w_cursor.col - VIsual.col + 1);
+ *lenp = (size_t)curwin->w_cursor.col - (size_t)VIsual.col + 1;
}
if (has_mbyte)
/* Correct the length to include the whole last character. */
@@ -4988,7 +5004,7 @@ static void nv_scroll(cmdarg_T *cap)
used -= diff_check_fill(curwin, curwin->w_topline)
- curwin->w_topfill;
validate_botline(); // make sure w_empty_rows is valid
- half = (curwin->w_grid.Rows - curwin->w_empty_rows + 1) / 2;
+ half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2;
for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) {
// Count half he number of filler lines to be "below this
// line" and half to be "above the next line".
@@ -5003,7 +5019,7 @@ static void nv_scroll(cmdarg_T *cap)
if (hasFolding(curwin->w_topline + n, NULL, &lnum))
n = lnum - curwin->w_topline;
}
- if (n > 0 && used > curwin->w_grid.Rows) {
+ if (n > 0 && used > curwin->w_height_inner) {
n--;
}
} else { // (cap->cmdchar == 'H')
@@ -6715,9 +6731,9 @@ static void nv_g_cmd(cmdarg_T *cap)
oap->motion_type = kMTCharWise;
oap->inclusive = false;
if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
- int width1 = curwin->w_grid.Columns - curwin_col_off();
+ int width1 = curwin->w_width_inner - curwin_col_off();
int width2 = width1 + curwin_col_off2();
validate_virtcol();
@@ -6730,7 +6746,7 @@ static void nv_g_cmd(cmdarg_T *cap)
* 'relativenumber' is on and lines are wrapping the middle can be more
* to the left. */
if (cap->nchar == 'm') {
- i += (curwin->w_grid.Columns - curwin_col_off()
+ i += (curwin->w_width_inner - curwin_col_off()
+ ((curwin->w_p_wrap && i > 0)
? curwin_col_off2() : 0)) / 2;
}
@@ -6777,11 +6793,11 @@ static void nv_g_cmd(cmdarg_T *cap)
oap->motion_type = kMTCharWise;
oap->inclusive = true;
if (curwin->w_p_wrap
- && curwin->w_grid.Columns != 0
+ && curwin->w_width_inner != 0
) {
curwin->w_curswant = MAXCOL; /* so we stay at the end */
if (cap->count1 == 1) {
- int width1 = curwin->w_grid.Columns - col_off;
+ int width1 = curwin->w_width_inner - col_off;
int width2 = width1 + curwin_col_off2();
validate_virtcol();
@@ -6807,7 +6823,7 @@ static void nv_g_cmd(cmdarg_T *cap)
} else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false)
clearopbeep(oap);
} else {
- i = curwin->w_leftcol + curwin->w_grid.Columns - col_off - 1;
+ i = curwin->w_leftcol + curwin->w_width_inner - col_off - 1;
coladvance((colnr_T)i);
/* Make sure we stick in this column. */
@@ -7917,7 +7933,7 @@ static void get_op_vcol(
colnr_T end;
if (VIsual_mode != Ctrl_V
- || (!initial && oap->end.col < curwin->w_grid.Columns)) {
+ || (!initial && oap->end.col < curwin->w_width_inner)) {
return;
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index e9cb480647..674a9244f0 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -865,8 +865,12 @@ int do_record(int c)
* needs to be removed again to put it in a register. exec_reg then
* adds the escaping back later.
*/
- Recording = FALSE;
- MSG("");
+ Recording = false;
+ if (ui_has(kUIMessages)) {
+ showmode();
+ } else {
+ MSG("");
+ }
p = get_recorded();
if (p == NULL)
retval = FAIL;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index b8f5957c09..b85ec6dbcc 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -50,6 +50,7 @@
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/hardcopy.h"
+#include "nvim/highlight.h"
#include "nvim/indent_c.h"
#include "nvim/mbyte.h"
#include "nvim/memfile.h"
@@ -65,6 +66,7 @@
#include "nvim/normal.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/popupmnu.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/spell.h"
@@ -2121,10 +2123,10 @@ static void didset_options2(void)
(void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
// Parse default for 'fillchars'.
- (void)set_chars_option(&p_fcs);
+ (void)set_chars_option(curwin, &curwin->w_p_fcs);
// Parse default for 'listchars'.
- (void)set_chars_option(&p_lcs);
+ (void)set_chars_option(curwin, &curwin->w_p_lcs);
// Parse default for 'wildmode'.
check_opt_wim();
@@ -2567,10 +2569,19 @@ did_set_string_option (
// 'ambiwidth'
if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
errmsg = e_invarg;
- } else if (set_chars_option(&p_lcs) != NULL) {
- errmsg = (char_u *)_("E834: Conflicts with value of 'listchars'");
- } else if (set_chars_option(&p_fcs) != NULL) {
- errmsg = (char_u *)_("E835: Conflicts with value of 'fillchars'");
+ } else {
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (set_chars_option(wp, &wp->w_p_lcs) != NULL) {
+ errmsg = (char_u *)_("E834: Conflicts with value of 'listchars'");
+ goto ambw_end;
+ }
+ if (set_chars_option(wp, &wp->w_p_fcs) != NULL) {
+ errmsg = (char_u *)_("E835: Conflicts with value of 'fillchars'");
+ goto ambw_end;
+ }
+ }
+ambw_end:
+ {} // clint prefers {} over ; as an empty statement
}
}
/* 'background' */
@@ -2756,14 +2767,10 @@ did_set_string_option (
}
s = skip_to_option_part(s);
}
- }
- /* 'listchars' */
- else if (varp == &p_lcs) {
- errmsg = set_chars_option(varp);
- }
- /* 'fillchars' */
- else if (varp == &p_fcs) {
- errmsg = set_chars_option(varp);
+ } else if (varp == &curwin->w_p_lcs) { // 'listchars'
+ errmsg = set_chars_option(curwin, varp);
+ } else if (varp == &curwin->w_p_fcs) { // 'fillchars'
+ errmsg = set_chars_option(curwin, varp);
}
/* 'cedit' */
else if (varp == &p_cedit) {
@@ -3394,53 +3401,55 @@ skip:
/// Handle setting 'listchars' or 'fillchars'.
/// Assume monocell characters
///
-/// @param varp either &p_lcs ('listchars') or &p_fcs ('fillchar')
+/// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs
/// @return error message, NULL if it's OK.
-static char_u *set_chars_option(char_u **varp)
+static char_u *set_chars_option(win_T *wp, char_u **varp)
{
int round, i, len, entries;
- char_u *p, *s;
- int c1, c2 = 0;
- struct charstab {
+ char_u *p, *s;
+ int c1 = 0, c2 = 0, c3 = 0;
+
+ struct chars_tab {
int *cp; ///< char value
char *name; ///< char id
int def; ///< default value
};
- static struct charstab filltab[] = {
- { &fill_stl, "stl" , ' ' },
- { &fill_stlnc, "stlnc", ' ' },
- { &fill_vert, "vert" , 9474 }, // │
- { &fill_fold, "fold" , 183 }, // ·
- { &fill_diff, "diff" , '-' },
- { &fill_msgsep, "msgsep", ' ' },
- { &fill_eob, "eob", '~' },
+ struct chars_tab *tab;
+
+ struct chars_tab fcs_tab[] = {
+ { &wp->w_p_fcs_chars.stl, "stl", ' ' },
+ { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
+ { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │
+ { &wp->w_p_fcs_chars.fold, "fold", 183 }, // ·
+ { &wp->w_p_fcs_chars.diff, "diff", '-' },
+ { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
+ { &wp->w_p_fcs_chars.eob, "eob", '~' },
};
- static struct charstab lcstab[] = {
- { &lcs_eol, "eol", NUL },
- { &lcs_ext, "extends", NUL },
- { &lcs_nbsp, "nbsp", NUL },
- { &lcs_prec, "precedes", NUL },
- { &lcs_space, "space", NUL },
- { &lcs_tab2, "tab", NUL },
- { &lcs_trail, "trail", NUL },
- { &lcs_conceal, "conceal", NUL },
+ struct chars_tab lcs_tab[] = {
+ { &wp->w_p_lcs_chars.eol, "eol", NUL },
+ { &wp->w_p_lcs_chars.ext, "extends", NUL },
+ { &wp->w_p_lcs_chars.nbsp, "nbsp", NUL },
+ { &wp->w_p_lcs_chars.prec, "precedes", NUL },
+ { &wp->w_p_lcs_chars.space, "space", NUL },
+ { &wp->w_p_lcs_chars.tab2, "tab", NUL },
+ { &wp->w_p_lcs_chars.trail, "trail", NUL },
+ { &wp->w_p_lcs_chars.conceal, "conceal", NUL },
};
- struct charstab *tab;
- if (varp == &p_lcs) {
- tab = lcstab;
- entries = ARRAY_SIZE(lcstab);
+ if (varp == &wp->w_p_lcs) {
+ tab = lcs_tab;
+ entries = ARRAY_SIZE(lcs_tab);
} else {
- tab = filltab;
- entries = ARRAY_SIZE(filltab);
+ tab = fcs_tab;
+ entries = ARRAY_SIZE(fcs_tab);
if (*p_ambw == 'd') {
// XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is
// forbidden (TUI limitation?). Set old defaults.
- filltab[2].def = '|';
- filltab[3].def = '-';
+ fcs_tab[2].def = '|';
+ fcs_tab[3].def = '-';
} else {
- filltab[2].def = 9474; // │
- filltab[3].def = 183; // ·
+ fcs_tab[2].def = 9474; // │
+ fcs_tab[3].def = 183; // ·
}
}
@@ -3453,8 +3462,9 @@ static char_u *set_chars_option(char_u **varp)
*(tab[i].cp) = tab[i].def;
}
}
- if (varp == &p_lcs) {
- lcs_tab1 = NUL;
+ if (varp == &wp->w_p_lcs) {
+ wp->w_p_lcs_chars.tab1 = NUL;
+ wp->w_p_lcs_chars.tab3 = NUL;
}
}
p = *varp;
@@ -3464,6 +3474,7 @@ static char_u *set_chars_option(char_u **varp)
if (STRNCMP(p, tab[i].name, len) == 0
&& p[len] == ':'
&& p[len + 1] != NUL) {
+ c1 = c2 = c3 = 0;
s = p + len + 1;
// TODO(bfredl): use schar_T representation and utfc_ptr2len
@@ -3472,7 +3483,7 @@ static char_u *set_chars_option(char_u **varp)
if (mb_char2cells(c1) > 1 || (c1len == 1 && c1 > 127)) {
continue;
}
- if (tab[i].cp == &lcs_tab2) {
+ if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
if (*s == NUL) {
continue;
}
@@ -3481,15 +3492,23 @@ static char_u *set_chars_option(char_u **varp)
if (mb_char2cells(c2) > 1 || (c2len == 1 && c2 > 127)) {
continue;
}
+ if (!(*s == ',' || *s == NUL)) {
+ int c3len = utf_ptr2len(s);
+ c3 = mb_cptr2char_adv((const char_u **)&s);
+ if (mb_char2cells(c3) > 1 || (c3len == 1 && c3 > 127)) {
+ continue;
+ }
+ }
}
if (*s == ',' || *s == NUL) {
if (round) {
- if (tab[i].cp == &lcs_tab2) {
- lcs_tab1 = c1;
- lcs_tab2 = c2;
- } else if (tab[i].cp != NULL)
+ if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
+ wp->w_p_lcs_chars.tab1 = c1;
+ wp->w_p_lcs_chars.tab2 = c2;
+ wp->w_p_lcs_chars.tab3 = c3;
+ } else if (tab[i].cp != NULL) {
*(tab[i].cp) = c1;
-
+ }
}
p = s;
break;
@@ -4149,7 +4168,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
errmsg = e_positive;
}
} else if (pp == &p_ch) {
- if (value < 1) {
+ int minval = ui_has(kUIMessages) ? 0 : 1;
+ if (value < minval) {
errmsg = e_positive;
}
} else if (pp == &p_tm) {
@@ -4223,8 +4243,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
} else if (pp == &curbuf->b_p_channel || pp == &p_channel) {
errmsg = e_invarg;
} else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) {
- if (value < -1 || value > SB_MAX
- || (value != -1 && opt_flags == OPT_LOCAL && !curbuf->terminal)) {
+ if (value < -1 || value > SB_MAX) {
errmsg = e_invarg;
}
} else if (pp == &curbuf->b_p_sw || pp == &p_sw) {
@@ -4258,6 +4277,9 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
p_window = Rows - 1;
}
} else if (pp == &p_ch) {
+ if (ui_has(kUIMessages)) {
+ p_ch = 0;
+ }
if (p_ch > Rows - min_rows() + 1) {
p_ch = Rows - min_rows() + 1;
}
@@ -4322,6 +4344,14 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
if (p_uc && !old_value) {
ml_open_files();
}
+ } else if (pp == &p_pb) {
+ p_pb = MAX(MIN(p_pb, 100), 0);
+ if (old_value != 0) {
+ hl_invalidate_blends();
+ }
+ if (pum_drawn()) {
+ pum_recompose();
+ }
} else if (pp == &p_pyx) {
if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) {
errmsg = e_invarg;
@@ -4339,7 +4369,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
} else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) {
if (curbuf->terminal) {
// Force the scrollback to take effect.
- terminal_resize(curbuf->terminal, UINT16_MAX, UINT16_MAX);
+ terminal_check_size(curbuf->terminal);
}
} else if (pp == &curwin->w_p_nuw) {
curwin->w_nrwidth_line_count = 0;
@@ -4418,11 +4448,6 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
*(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = *pp;
}
- if (pp == &curbuf->b_p_scbk && !curbuf->terminal) {
- // Normal buffer: reset local 'scrollback' after updating the global value.
- curbuf->b_p_scbk = -1;
- }
-
options[opt_idx].flags |= P_WAS_SET;
// Don't do this while starting up, failure or recursively.
@@ -5613,6 +5638,8 @@ static char_u *get_varp(vimoption_T *p)
case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap);
case PV_SCL: return (char_u *)&(curwin->w_p_scl);
case PV_WINHL: return (char_u *)&(curwin->w_p_winhl);
+ case PV_FCS: return (char_u *)&(curwin->w_p_fcs);
+ case PV_LCS: return (char_u *)&(curwin->w_p_lcs);
default: IEMSG(_("E356: get_varp ERROR"));
}
/* always return a valid pointer to avoid a crash! */
@@ -5691,6 +5718,8 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_fmr = vim_strsave(from->wo_fmr);
to->wo_scl = vim_strsave(from->wo_scl);
to->wo_winhl = vim_strsave(from->wo_winhl);
+ to->wo_fcs = vim_strsave(from->wo_fcs);
+ to->wo_lcs = vim_strsave(from->wo_lcs);
check_winopt(to); // don't want NULL pointers
}
@@ -5721,6 +5750,8 @@ static void check_winopt(winopt_T *wop)
check_string_option(&wop->wo_cocu);
check_string_option(&wop->wo_briopt);
check_string_option(&wop->wo_winhl);
+ check_string_option(&wop->wo_fcs);
+ check_string_option(&wop->wo_lcs);
}
/*
@@ -5741,12 +5772,16 @@ void clear_winopt(winopt_T *wop)
clear_string_option(&wop->wo_cocu);
clear_string_option(&wop->wo_briopt);
clear_string_option(&wop->wo_winhl);
+ clear_string_option(&wop->wo_fcs);
+ clear_string_option(&wop->wo_lcs);
}
void didset_window_options(win_T *wp)
{
check_colorcolumn(wp);
briopt_check(wp);
+ set_chars_option(wp, &wp->w_p_fcs);
+ set_chars_option(wp, &wp->w_p_lcs);
parse_winhl_opt(wp);
}
@@ -5835,7 +5870,7 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_ai = p_ai;
buf->b_p_ai_nopaste = p_ai_nopaste;
buf->b_p_sw = p_sw;
- buf->b_p_scbk = -1;
+ buf->b_p_scbk = p_scbk;
buf->b_p_tw = p_tw;
buf->b_p_tw_nopaste = p_tw_nopaste;
buf->b_p_tw_nobin = p_tw_nobin;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 7b99b6f266..ccb0496495 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -375,6 +375,7 @@ EXTERN int p_confirm; // 'confirm'
EXTERN int p_cp; // 'compatible'
EXTERN char_u *p_cot; // 'completeopt'
EXTERN long p_ph; // 'pumheight'
+EXTERN long p_pb; // 'pumblend'
EXTERN char_u *p_cpo; // 'cpoptions'
EXTERN char_u *p_csprg; // 'cscopeprg'
EXTERN int p_csre; // 'cscoperelative'
@@ -486,7 +487,6 @@ EXTERN long *p_linespace; // 'linespace'
EXTERN char_u *p_lispwords; // 'lispwords'
EXTERN long p_ls; // 'laststatus'
EXTERN long p_stal; // 'showtabline'
-EXTERN char_u *p_lcs; // 'listchars'
EXTERN int p_lz; // 'lazyredraw'
EXTERN int p_lpl; // 'loadplugins'
@@ -638,7 +638,6 @@ EXTERN long p_ul; ///< 'undolevels'
EXTERN long p_ur; ///< 'undoreload'
EXTERN long p_uc; ///< 'updatecount'
EXTERN long p_ut; ///< 'updatetime'
-EXTERN char_u *p_fcs; ///< 'fillchar'
EXTERN char_u *p_shada; ///< 'shada'
EXTERN char_u *p_vdir; ///< 'viewdir'
EXTERN char_u *p_vop; ///< 'viewoptions'
@@ -814,6 +813,8 @@ enum {
, WV_WRAP
, WV_SCL
, WV_WINHL
+ , WV_FCS
+ , WV_LCS
, WV_COUNT // must be the last one
};
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index aba8f8b893..b8f128103c 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -806,11 +806,11 @@ return {
},
{
full_name='fillchars', abbreviation='fcs',
- type='string', list='onecomma', scope={'global'},
+ type='string', list='onecomma', scope={'window'},
deny_duplicates=true,
vi_def=true,
- redraw={'all_windows'},
- varname='p_fcs',
+ alloced=true,
+ redraw={'current_window'},
defaults={if_true={vi=''}}
},
{
@@ -1173,9 +1173,7 @@ return {
vi_def=true,
varname='p_iminsert', pv_name='p_imi',
defaults={
- condition='B_IMODE_IM',
- if_true={vi=macros('B_IMODE_IM')},
- if_false={vi=macros('B_IMODE_NONE')},
+ if_true={vi=macros('B_IMODE_NONE')},
}
},
{
@@ -1184,9 +1182,7 @@ return {
vi_def=true,
varname='p_imsearch', pv_name='p_ims',
defaults={
- condition='B_IMODE_IM',
- if_true={vi=macros('B_IMODE_IM')},
- if_false={vi=macros('B_IMODE_NONE')},
+ if_true={vi=macros('B_IMODE_USE_INSERT')},
}
},
{
@@ -1425,11 +1421,11 @@ return {
},
{
full_name='listchars', abbreviation='lcs',
- type='string', list='onecomma', scope={'global'},
+ type='string', list='onecomma', scope={'window'},
deny_duplicates=true,
vim=true,
- redraw={'all_windows'},
- varname='p_lcs',
+ alloced=true,
+ redraw={'current_window'},
defaults={if_true={vi="eol:$", vim="tab:> ,trail:-,nbsp:+"}}
},
{
@@ -1810,6 +1806,14 @@ return {
defaults={if_true={vi=0}}
},
{
+ full_name='pumblend', abbreviation='pb',
+ type='number', scope={'global'},
+ vi_def=true,
+ redraw={'ui_option'},
+ varname='p_pb',
+ defaults={if_true={vi=0}}
+ },
+ {
full_name='pyxversion', abbreviation='pyx',
type='number', scope={'global'},
secure=true,
@@ -1934,7 +1938,7 @@ return {
vi_def=true,
varname='p_scbk',
redraw={'current_buffer'},
- defaults={if_true={vi=10000}}
+ defaults={if_true={vi=-1}}
},
{
full_name='scrollbind', abbreviation='scb',
diff --git a/src/nvim/os/dl.c b/src/nvim/os/dl.c
index 267cf5ae4b..bbd0424a82 100644
--- a/src/nvim/os/dl.c
+++ b/src/nvim/os/dl.c
@@ -7,6 +7,7 @@
#include <stdint.h>
#include <uv.h>
+#include "nvim/os/dl.h"
#include "nvim/os/os.h"
#include "nvim/memory.h"
#include "nvim/message.h"
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index f62253cbce..5e2c9ecb36 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -236,6 +236,53 @@ size_t input_enqueue(String keys)
return rv;
}
+static uint8_t check_multiclick(int code, int grid, int row, int col)
+{
+ static int orig_num_clicks = 0;
+ static int orig_mouse_code = 0;
+ static int orig_mouse_grid = 0;
+ static int orig_mouse_col = 0;
+ static int orig_mouse_row = 0;
+ static uint64_t orig_mouse_time = 0; // time of previous mouse click
+
+ if (code == KE_LEFTRELEASE || code == KE_RIGHTRELEASE
+ || code == KE_MIDDLERELEASE) {
+ return 0;
+ }
+ uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
+
+ // compute the time elapsed since the previous mouse click and
+ // convert p_mouse from ms to ns
+ uint64_t timediff = mouse_time - orig_mouse_time;
+ uint64_t mouset = (uint64_t)p_mouset * 1000000;
+ if (code == orig_mouse_code
+ && timediff < mouset
+ && orig_num_clicks != 4
+ && orig_mouse_grid == grid
+ && orig_mouse_col == col
+ && orig_mouse_row == row) {
+ orig_num_clicks++;
+ } else {
+ orig_num_clicks = 1;
+ }
+ orig_mouse_code = code;
+ orig_mouse_grid = grid;
+ orig_mouse_col = col;
+ orig_mouse_row = row;
+ orig_mouse_time = mouse_time;
+
+ uint8_t modifiers = 0;
+ if (orig_num_clicks == 2) {
+ modifiers |= MOD_MASK_2CLICK;
+ } else if (orig_num_clicks == 3) {
+ modifiers |= MOD_MASK_3CLICK;
+ } else if (orig_num_clicks == 4) {
+ modifiers |= MOD_MASK_4CLICK;
+ }
+ return modifiers;
+}
+
+
// Mouse event handling code(Extract row/col if available and detect multiple
// clicks)
static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
@@ -274,48 +321,16 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
if (row >= Rows) {
row = (int)Rows - 1;
}
+ mouse_grid = 0;
mouse_row = row;
mouse_col = col;
}
*ptr += advance;
}
- static int orig_num_clicks = 0;
- if (mouse_code != KE_LEFTRELEASE && mouse_code != KE_RIGHTRELEASE
- && mouse_code != KE_MIDDLERELEASE) {
- static int orig_mouse_code = 0;
- static int orig_mouse_col = 0;
- static int orig_mouse_row = 0;
- static uint64_t orig_mouse_time = 0; // time of previous mouse click
- uint64_t mouse_time = os_hrtime(); // time of current mouse click (ns)
-
- // compute the time elapsed since the previous mouse click and
- // convert p_mouse from ms to ns
- uint64_t timediff = mouse_time - orig_mouse_time;
- uint64_t mouset = (uint64_t)p_mouset * 1000000;
- if (mouse_code == orig_mouse_code
- && timediff < mouset
- && orig_num_clicks != 4
- && orig_mouse_col == mouse_col
- && orig_mouse_row == mouse_row) {
- orig_num_clicks++;
- } else {
- orig_num_clicks = 1;
- }
- orig_mouse_code = mouse_code;
- orig_mouse_col = mouse_col;
- orig_mouse_row = mouse_row;
- orig_mouse_time = mouse_time;
- }
+ uint8_t modifiers = check_multiclick(mouse_code, mouse_grid,
+ mouse_row, mouse_col);
- uint8_t modifiers = 0;
- if (orig_num_clicks == 2) {
- modifiers |= MOD_MASK_2CLICK;
- } else if (orig_num_clicks == 3) {
- modifiers |= MOD_MASK_3CLICK;
- } else if (orig_num_clicks == 4) {
- modifiers |= MOD_MASK_4CLICK;
- }
if (modifiers) {
if (buf[1] != KS_MODIFIER) {
@@ -334,6 +349,30 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
return bufsize;
}
+size_t input_enqueue_mouse(int code, uint8_t modifier,
+ int grid, int row, int col)
+{
+ modifier |= check_multiclick(code, grid, row, col);
+ uint8_t buf[7], *p = buf;
+ if (modifier) {
+ p[0] = K_SPECIAL;
+ p[1] = KS_MODIFIER;
+ p[2] = modifier;
+ p += 3;
+ }
+ p[0] = K_SPECIAL;
+ p[1] = KS_EXTRA;
+ p[2] = (uint8_t)code;
+
+ mouse_grid = grid;
+ mouse_row = row;
+ mouse_col = col;
+
+ size_t written = 3 + (size_t)(p-buf);
+ rbuffer_write(input_buffer, (char *)buf, written);
+ return written;
+}
+
/// @return true if the main loop is blocked and waiting for input.
bool input_blocking(void)
{
diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c
index 108a9c6c39..fe2d7986bf 100644
--- a/src/nvim/os/lang.c
+++ b/src/nvim/os/lang.c
@@ -11,6 +11,7 @@
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
+#include "nvim/os/lang.h"
#include "nvim/os/os.h"
void lang_init(void)
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index fc7f9cefd1..20f68233e7 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -15,6 +15,7 @@
#include "nvim/globals.h"
#include "nvim/memline.h"
#include "nvim/eval.h"
+#include "nvim/fileio.h"
#include "nvim/main.h"
#include "nvim/memory.h"
#include "nvim/misc1.h"
@@ -22,7 +23,7 @@
#include "nvim/os/signal.h"
#include "nvim/event/loop.h"
-static SignalWatcher spipe, shup, squit, sterm;
+static SignalWatcher spipe, shup, squit, sterm, susr1;
#ifdef SIGPWR
static SignalWatcher spwr;
#endif
@@ -61,6 +62,10 @@ void signal_init(void)
signal_watcher_init(&main_loop, &spwr, NULL);
signal_watcher_start(&spwr, on_signal, SIGPWR);
#endif
+#ifdef SIGUSR1
+ signal_watcher_init(&main_loop, &susr1, NULL);
+ signal_watcher_start(&susr1, on_signal, SIGUSR1);
+#endif
}
void signal_teardown(void)
@@ -73,6 +78,9 @@ void signal_teardown(void)
#ifdef SIGPWR
signal_watcher_close(&spwr, NULL);
#endif
+#ifdef SIGUSR1
+ signal_watcher_close(&susr1, NULL);
+#endif
}
void signal_stop(void)
@@ -84,6 +92,9 @@ void signal_stop(void)
#ifdef SIGPWR
signal_watcher_stop(&spwr);
#endif
+#ifdef SIGUSR1
+ signal_watcher_stop(&susr1);
+#endif
}
void signal_reject_deadly(void)
@@ -115,6 +126,10 @@ static char * signal_name(int signum)
#endif
case SIGHUP:
return "SIGHUP";
+#ifdef SIGUSR1
+ case SIGUSR1:
+ return "SIGUSR1";
+#endif
default:
return "Unknown";
}
@@ -162,6 +177,12 @@ static void on_signal(SignalWatcher *handle, int signum, void *data)
deadly_signal(signum);
}
break;
+#ifdef SIGUSR1
+ case SIGUSR1:
+ apply_autocmds(EVENT_SIGNAL, (char_u *)"SIGUSR1", curbuf->b_fname, true,
+ curbuf);
+ break;
+#endif
default:
ELOG("invalid signal: %d", signum);
break;
diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c
index 09ba718302..351350d939 100644
--- a/src/nvim/os_unix.c
+++ b/src/nvim/os_unix.c
@@ -139,7 +139,7 @@ void mch_exit(int r)
exiting = true;
ui_flush();
- ui_builtin_stop();
+ ui_call_stop();
ml_close_all(true); // remove all memfiles
if (!event_teardown() && r == 0) {
diff --git a/src/nvim/path.c b/src/nvim/path.c
index de697642c7..7903e3f4f4 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -1267,7 +1267,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file,
}
*num_file = ga.ga_len;
- *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data : (char_u **)"";
+ *file = (ga.ga_data != NULL) ? (char_u **)ga.ga_data : NULL;
recursive = false;
@@ -2039,6 +2039,7 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files,
char_u *ffname;
// check all files in (*files)[]
+ assert(*num_files == 0 || *files != NULL);
for (i = 0; i < *num_files; i++) {
ffname = (char_u *)FullName_save((char *)(*files)[i], false);
assert((*files)[i] != NULL);
@@ -2056,16 +2057,16 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files,
}
}
- /*
- * Move the names where 'suffixes' match to the end.
- */
+ //
+ // Move the names where 'suffixes' match to the end.
+ //
+ assert(*num_files == 0 || *files != NULL);
if (*num_files > 1) {
non_suf_match = 0;
for (i = 0; i < *num_files; i++) {
if (!match_suffix((*files)[i])) {
//
- // Move the name without matching suffix to the front
- // of the list.
+ // Move the name without matching suffix to the front of the list.
//
p = (*files)[i];
for (j = i; j > non_suf_match; j--) {
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c
index 056770f2c0..3c10b7ae0f 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmnu.c
@@ -19,6 +19,7 @@
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/screen.h"
+#include "nvim/ui_compositor.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/memory.h"
@@ -41,7 +42,11 @@ static int pum_row; // top row of pum
static int pum_col; // left column of pum
static bool pum_is_visible = false;
+static bool pum_is_drawn = false;
static bool pum_external = false;
+static bool pum_invalid = false; // the screen was just cleared
+
+static ScreenGrid pum_grid = SCREEN_GRID_INIT;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "popupmnu.c.generated.h"
@@ -78,13 +83,14 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
if (!pum_is_visible) {
// To keep the code simple, we only allow changing the
// draw mode when the popup menu is not being displayed
- pum_external = ui_is_external(kUIPopupmenu);
+ pum_external = ui_has(kUIPopupmenu);
}
do {
// Mark the pum as visible already here,
// to avoid that must_redraw is set when 'cursorcolumn' is on.
pum_is_visible = true;
+ pum_is_drawn = true;
validate_cursor_col();
above_row = 0;
below_row = cmdline_row;
@@ -98,7 +104,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
}
int grid = (int)curwin->w_grid.handle;
- if (!ui_is_external(kUIMultigrid)) {
+ if (!ui_has(kUIMultigrid)) {
grid = (int)default_grid.handle;
row += curwin->w_winrow;
col += curwin->w_wincol;
@@ -317,7 +323,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
/// Redraw the popup menu, using "pum_first" and "pum_selected".
void pum_redraw(void)
{
- int row = pum_row;
+ int row = 0;
int col;
int attr_norm = win_hl_attr(curwin, HLF_PNI);
int attr_select = win_hl_attr(curwin, HLF_PSI);
@@ -334,6 +340,39 @@ void pum_redraw(void)
int round;
int n;
+ int grid_width = pum_width;
+ int col_off = 0;
+ bool extra_space = false;
+ if (curwin->w_p_rl) {
+ col_off = pum_width;
+ if (pum_col < curwin->w_wincol + curwin->w_width - 1) {
+ grid_width += 1;
+ extra_space = true;
+ }
+ } else if (pum_col > 0) {
+ grid_width += 1;
+ col_off = 1;
+ extra_space = true;
+ }
+ if (pum_scrollbar > 0) {
+ grid_width++;
+ }
+
+ grid_assign_handle(&pum_grid);
+ bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col-col_off,
+ pum_height, grid_width);
+ bool invalid_grid = moved || pum_invalid;
+ pum_invalid = false;
+
+ if (!pum_grid.chars
+ || pum_grid.Rows != pum_height || pum_grid.Columns != grid_width) {
+ grid_alloc(&pum_grid, pum_height, grid_width, !invalid_grid, false);
+ ui_call_grid_resize(pum_grid.handle, pum_grid.Columns, pum_grid.Rows);
+ } else if (invalid_grid) {
+ grid_invalidate(&pum_grid);
+ }
+
+
// Never display more than we have
if (pum_first > pum_size - pum_height) {
pum_first = pum_size - pum_height;
@@ -356,17 +395,17 @@ void pum_redraw(void)
screen_puts_line_start(row);
// prepend a space if there is room
- if (curwin->w_p_rl) {
- if (pum_col < curwin->w_wincol + curwin->w_width - 1) {
- grid_putchar(&default_grid, ' ', row, pum_col + 1, attr);
+ if (extra_space) {
+ if (curwin->w_p_rl) {
+ grid_putchar(&pum_grid, ' ', row, col_off + 1, attr);
+ } else {
+ grid_putchar(&pum_grid, ' ', row, col_off - 1, attr);
}
- } else if (pum_col > 0) {
- grid_putchar(&default_grid, ' ', row, pum_col - 1, attr);
}
// Display each entry, use two spaces for a Tab.
// Do this 3 times: For the main text, kind and extra info
- col = pum_col;
+ col = col_off;
totwidth = 0;
for (round = 1; round <= 3; ++round) {
@@ -423,13 +462,13 @@ void pum_redraw(void)
size++;
}
}
- grid_puts_len(&default_grid, rt, (int)STRLEN(rt), row,
+ grid_puts_len(&pum_grid, rt, (int)STRLEN(rt), row,
col - size + 1, attr);
xfree(rt_start);
xfree(st);
col -= width;
} else {
- grid_puts_len(&default_grid, st, (int)STRLEN(st), row, col, attr);
+ grid_puts_len(&pum_grid, st, (int)STRLEN(st), row, col, attr);
xfree(st);
col += width;
}
@@ -440,11 +479,11 @@ void pum_redraw(void)
// Display two spaces for a Tab.
if (curwin->w_p_rl) {
- grid_puts_len(&default_grid, (char_u *)" ", 2, row, col - 1,
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col - 1,
attr);
col -= 2;
} else {
- grid_puts_len(&default_grid, (char_u *)" ", 2, row, col, attr);
+ grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col, attr);
col += 2;
}
totwidth += 2;
@@ -475,37 +514,37 @@ void pum_redraw(void)
}
if (curwin->w_p_rl) {
- grid_fill(&default_grid, row, row + 1, pum_col - pum_base_width - n + 1,
+ grid_fill(&pum_grid, row, row + 1, col_off - pum_base_width - n + 1,
col + 1, ' ', ' ', attr);
- col = pum_col - pum_base_width - n + 1;
+ col = col_off - pum_base_width - n + 1;
} else {
- grid_fill(&default_grid, row, row + 1, col,
- pum_col + pum_base_width + n, ' ', ' ', attr);
- col = pum_col + pum_base_width + n;
+ grid_fill(&pum_grid, row, row + 1, col,
+ col_off + pum_base_width + n, ' ', ' ', attr);
+ col = col_off + pum_base_width + n;
}
totwidth = pum_base_width + n;
}
if (curwin->w_p_rl) {
- grid_fill(&default_grid, row, row + 1, pum_col - pum_width + 1, col + 1,
+ grid_fill(&pum_grid, row, row + 1, col_off - pum_width + 1, col + 1,
' ', ' ', attr);
} else {
- grid_fill(&default_grid, row, row + 1, col, pum_col + pum_width, ' ', ' ',
+ grid_fill(&pum_grid, row, row + 1, col, col_off + pum_width, ' ', ' ',
attr);
}
if (pum_scrollbar > 0) {
if (curwin->w_p_rl) {
- grid_putchar(&default_grid, ' ', row, pum_col - pum_width,
+ grid_putchar(&pum_grid, ' ', row, col_off - pum_width,
i >= thumb_pos && i < thumb_pos + thumb_heigth
? attr_thumb : attr_scroll);
} else {
- grid_putchar(&default_grid, ' ', row, pum_col + pum_width,
+ grid_putchar(&pum_grid, ' ', row, col_off + pum_width,
i >= thumb_pos && i < thumb_pos + thumb_heigth
? attr_thumb : attr_scroll);
}
}
- grid_puts_line_flush(&default_grid, false);
+ grid_puts_line_flush(&pum_grid, false);
row++;
}
}
@@ -696,6 +735,8 @@ static int pum_set_selected(int n, int repeat)
// Update the screen before drawing the popup menu.
// Enable updating the status lines.
+ // TODO(bfredl): can simplify, get rid of the flag munging?
+ // or at least eliminate extra redraw before win_enter()?
pum_is_visible = false;
update_screen(0);
pum_is_visible = true;
@@ -725,17 +766,27 @@ static int pum_set_selected(int n, int repeat)
}
/// Undisplay the popup menu (later).
-void pum_undisplay(void)
+void pum_undisplay(bool immediate)
{
pum_is_visible = false;
pum_array = NULL;
- if (pum_external) {
- ui_call_popupmenu_hide();
- } else {
- redraw_all_later(SOME_VALID);
- redraw_tabline = true;
- status_redraw_all();
+ if (immediate) {
+ pum_check_clear();
+ }
+}
+
+void pum_check_clear(void)
+{
+ if (!pum_is_visible && pum_is_drawn) {
+ if (pum_external) {
+ ui_call_popupmenu_hide();
+ } else {
+ ui_comp_remove_grid(&pum_grid);
+ // TODO(bfredl): consider keeping float grids allocated.
+ grid_free(&pum_grid);
+ }
+ pum_is_drawn = false;
}
}
@@ -758,6 +809,17 @@ bool pum_drawn(void)
return pum_visible() && !pum_external;
}
+/// Screen was cleared, need to redraw next time
+void pum_invalidate(void)
+{
+ pum_invalid = true;
+}
+
+void pum_recompose(void)
+{
+ ui_comp_compose_grid(&pum_grid);
+}
+
/// Gets the height of the menu.
///
/// @return the height of the popup menu, the number of entries visible.
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 4eeddf1d5a..f0c37c0e38 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -44,6 +44,7 @@
#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
+#include "nvim/api/private/helpers.h"
struct dir_stack_T {
@@ -2155,7 +2156,8 @@ win_found:
} else if (!msg_scrolled && shortmess(SHM_OVERALL)) {
msg_scroll = false;
}
- msg_attr_keep(IObuff, 0, true);
+ msg_ext_set_kind("quickfix");
+ msg_attr_keep(IObuff, 0, true, false);
msg_scroll = (int)i;
}
} else {
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 0b9e1cfdec..a70b150e9b 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -230,6 +230,7 @@
#define UPPER 47 /* Match uppercase char */
#define NUPPER 48 /* Match non-uppercase char */
#define LAST_NL NUPPER + ADD_NL
+// -V:WITH_NL:560
#define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL)
#define MOPEN 80 // -89 Mark this point in input as start of
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 2467cf192f..5ac90ab601 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -110,6 +110,7 @@
#include "nvim/syntax.h"
#include "nvim/terminal.h"
#include "nvim/ui.h"
+#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/window.h"
@@ -153,6 +154,8 @@ static bool highlights_invalid = false;
static bool conceal_cursor_used = false;
+static bool redraw_popupmenu = false;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.c.generated.h"
#endif
@@ -271,9 +274,10 @@ void update_screen(int type)
static int did_intro = FALSE;
int did_one;
- /* Don't do anything if the screen structures are (not yet) valid. */
- if (!screen_valid(TRUE))
+ // Don't do anything if the screen structures are (not yet) valid.
+ if (!default_grid.chars) {
return;
+ }
if (must_redraw) {
if (type < must_redraw) /* use maximal type */
@@ -304,11 +308,15 @@ void update_screen(int type)
++display_tick; /* let syntax code know we're in a next round of
* display updating */
- /*
- * if the screen was scrolled up when displaying a message, scroll it down
- */
- if (msg_scrolled) {
+ // Tricky: vim code can reset msg_scrolled behind our back, so need
+ // separate bookkeeping for now.
+ if (msg_did_scroll) {
ui_call_win_scroll_over_reset();
+ msg_did_scroll = false;
+ }
+
+ // if the screen was scrolled up when displaying a message, scroll it down
+ if (msg_scrolled) {
clear_cmdline = true;
if (dy_flags & DY_MSGSEP) {
int valid = MAX(Rows - msg_scrollsize(), 0);
@@ -324,14 +332,12 @@ void update_screen(int type)
wp->w_redr_status = true;
}
}
- } else if (msg_scrolled > default_grid.Rows - 5) { // clearing is faster
+ } else if (msg_scrolled > Rows - 5) { // clearing is faster
type = CLEAR;
} else if (type != CLEAR) {
check_for_delay(false);
- if (grid_ins_lines(&default_grid, 0, msg_scrolled, (int)Rows,
- 0, (int)Columns) == FAIL) {
- type = CLEAR;
- }
+ grid_ins_lines(&default_grid, 0, msg_scrolled, (int)Rows,
+ 0, (int)Columns);
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_winrow < msg_scrolled) {
if (W_ENDROW(wp) > msg_scrolled
@@ -356,6 +362,8 @@ void update_screen(int type)
need_wait_return = FALSE;
}
+ msg_ext_check_prompt();
+
/* reset cmdline_row now (may have been changed temporarily) */
compute_cmdrow();
@@ -453,17 +461,20 @@ void update_screen(int type)
/* redraw status line after the window to minimize cursor movement */
if (wp->w_redr_status) {
- win_redr_status(wp, true); // any popup menu will be redrawn below
+ win_redr_status(wp);
}
}
- send_grid_resize = false;
- highlights_invalid = false;
+
end_search_hl();
// May need to redraw the popup menu.
- if (pum_drawn()) {
+ if (pum_drawn() && redraw_popupmenu) {
pum_redraw();
}
+ send_grid_resize = false;
+ highlights_invalid = false;
+ redraw_popupmenu = false;
+
/* Reset b_mod_set flags. Going through all windows is probably faster
* than going through all buffers (there could be many buffers). */
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
@@ -474,8 +485,9 @@ void update_screen(int type)
/* Clear or redraw the command line. Done last, because scrolling may
* mess up the command line. */
- if (clear_cmdline || redraw_cmdline)
+ if (clear_cmdline || redraw_cmdline) {
showmode();
+ }
/* May put up an introductory message when not editing a file */
if (!did_intro)
@@ -629,9 +641,7 @@ static void win_update(win_T *wp)
wp->w_nrwidth = i;
if (buf->terminal) {
- terminal_resize(buf->terminal,
- (uint16_t)(MAX(0, wp->w_grid.Columns - win_col_off(wp))),
- (uint16_t)wp->w_grid.Rows);
+ terminal_check_size(buf->terminal);
}
} else if (buf->b_mod_set
&& buf->b_mod_xlines != 0
@@ -820,31 +830,31 @@ static void win_update(win_T *wp)
// Try to insert the correct number of lines.
// If not the last window, delete the lines at the bottom.
// win_ins_lines may fail when the terminal can't do it.
- if (win_ins_lines(wp, 0, i) == OK) {
- if (wp->w_lines_valid != 0) {
- /* Need to update rows that are new, stop at the
- * first one that scrolled down. */
- top_end = i;
- scrolled_down = TRUE;
-
- /* Move the entries that were scrolled, disable
- * the entries for the lines to be redrawn. */
- if ((wp->w_lines_valid += j) > wp->w_grid.Rows) {
- wp->w_lines_valid = wp->w_grid.Rows;
- }
- for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
- wp->w_lines[idx] = wp->w_lines[idx - j];
- }
- while (idx >= 0) {
- wp->w_lines[idx--].wl_valid = false;
- }
+ win_scroll_lines(wp, 0, i);
+ if (wp->w_lines_valid != 0) {
+ // Need to update rows that are new, stop at the
+ // first one that scrolled down.
+ top_end = i;
+ scrolled_down = true;
+
+ // Move the entries that were scrolled, disable
+ // the entries for the lines to be redrawn.
+ if ((wp->w_lines_valid += j) > wp->w_grid.Rows) {
+ wp->w_lines_valid = wp->w_grid.Rows;
}
- } else
- mid_start = 0; /* redraw all lines */
- } else
- mid_start = 0; /* redraw all lines */
- } else
- mid_start = 0; /* redraw all lines */
+ for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
+ wp->w_lines[idx] = wp->w_lines[idx - j];
+ }
+ while (idx >= 0) {
+ wp->w_lines[idx--].wl_valid = false;
+ }
+ }
+ } else {
+ mid_start = 0; // redraw all lines
+ }
+ } else {
+ mid_start = 0; // redraw all lines
+ }
} else {
/*
* New topline is at or below old topline: May scroll up.
@@ -881,11 +891,8 @@ static void win_update(win_T *wp)
/* ... but don't delete new filler lines. */
row -= wp->w_topfill;
if (row > 0) {
- if (win_del_lines(wp, 0, row) == OK) {
- bot_start = wp->w_grid.Rows - row;
- } else {
- mid_start = 0; // redraw all lines
- }
+ win_scroll_lines(wp, 0, -row);
+ bot_start = wp->w_grid.Rows - row;
}
if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) {
/*
@@ -1140,7 +1147,7 @@ static void win_update(win_T *wp)
// Update a line when it is in an area that needs updating, when it
// has changes or w_lines[idx] is invalid.
// "bot_start" may be halfway a wrapped line after using
- // win_del_lines(), check if the current line includes it.
+ // win_scroll_lines(), check if the current line includes it.
// When syntax folding is being used, the saved syntax states will
// already have been updated, we can't see where the syntax state is
// the same again, just update until the end of the window.
@@ -1236,11 +1243,8 @@ static void win_update(win_T *wp)
if (row - xtra_rows >= wp->w_grid.Rows - 2) {
mod_bot = MAXLNUM;
} else {
- if (win_del_lines(wp, row, -xtra_rows) == FAIL) {
- mod_bot = MAXLNUM;
- } else {
- bot_start = wp->w_grid.Rows + xtra_rows;
- }
+ win_scroll_lines(wp, row, xtra_rows);
+ bot_start = wp->w_grid.Rows + xtra_rows;
}
} else if (xtra_rows > 0) {
/* May scroll text down. If there is not enough
@@ -1249,9 +1253,8 @@ static void win_update(win_T *wp)
if (row + xtra_rows >= wp->w_grid.Rows - 2) {
mod_bot = MAXLNUM;
} else {
- if (win_ins_lines(wp, row + old_rows, xtra_rows) == FAIL) {
- mod_bot = MAXLNUM;
- } else if (top_end > row + old_rows) {
+ win_scroll_lines(wp, row + old_rows, xtra_rows);
+ if (top_end > row + old_rows) {
// Scrolled the part at the top that requires
// updating down.
top_end += xtra_rows;
@@ -1448,13 +1451,12 @@ static void win_update(win_T *wp)
wp->w_botline = buf->b_ml.ml_line_count + 1;
j = diff_check_fill(wp, wp->w_botline);
if (j > 0 && !wp->w_botfill) {
- /*
- * Display filler lines at the end of the file
- */
- if (char2cells(fill_diff) > 1)
+ // display filler lines at the end of the file
+ if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
i = '-';
- else
- i = fill_diff;
+ } else {
+ i = wp->w_p_fcs_chars.diff;
+ }
if (row + j > wp->w_grid.Rows) {
j = wp->w_grid.Rows - row;
}
@@ -1465,8 +1467,8 @@ static void win_update(win_T *wp)
wp->w_botline = lnum;
// make sure the rest of the screen is blank
- // write the 'fill_eob' character to rows that aren't part of the file.
- win_draw_end(wp, fill_eob, ' ', row, wp->w_grid.Rows, HLF_EOB);
+ // write the 'eob' character to rows that aren't part of the file.
+ win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', row, wp->w_grid.Rows, HLF_EOB);
}
if (wp->w_redr_type >= REDRAW_TOP) {
@@ -1817,7 +1819,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
txtcol = col; /* remember where text starts */
- // 5. move the text to linebuf_char[off]. Fill up with "fill_fold".
+ // 5. move the text to linebuf_char[off]. Fill up with "fold".
// Right-left text is put in columns 0 - number-col, normal text is put
// in columns number-col - window-width.
int idx;
@@ -1849,7 +1851,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
col -= txtcol;
schar_T sc;
- schar_from_char(sc, fill_fold);
+ schar_from_char(sc, wp->w_p_fcs_chars.fold);
while (col < wp->w_grid.Columns
- (wp->w_p_rl ? txtcol : 0)
) {
@@ -2042,30 +2044,33 @@ win_line (
bool number_only // only update the number column
)
{
- int c = 0; // init for GCC
- long vcol = 0; // virtual column (for tabs)
- long vcol_sbr = -1; // virtual column after showbreak
- long vcol_prev = -1; // "vcol" of previous character
- char_u *line; // current line
- char_u *ptr; // current position in "line"
- int row; // row in the window, excl w_winrow
- ScreenGrid *grid = &wp->w_grid; // grid specfic to the window
-
- char_u extra[18]; /* line number and 'fdc' must fit in here */
- int n_extra = 0; /* number of extra chars */
- char_u *p_extra = NULL; /* string of extra chars, plus NUL */
- char_u *p_extra_free = NULL; /* p_extra needs to be freed */
- int c_extra = NUL; /* extra chars, all the same */
- int extra_attr = 0; /* attributes when n_extra != 0 */
- static char_u *at_end_str = (char_u *)""; /* used for p_extra when
- displaying lcs_eol at end-of-line */
- int lcs_eol_one = lcs_eol; /* lcs_eol until it's been used */
- int lcs_prec_todo = lcs_prec; /* lcs_prec until it's been used */
+ int c = 0; // init for GCC
+ long vcol = 0; // virtual column (for tabs)
+ long vcol_sbr = -1; // virtual column after showbreak
+ long vcol_prev = -1; // "vcol" of previous character
+ char_u *line; // current line
+ char_u *ptr; // current position in "line"
+ int row; // row in the window, excl w_winrow
+ ScreenGrid *grid = &wp->w_grid; // grid specfic to the window
+
+ char_u extra[18]; // line number and 'fdc' must fit in here
+ int n_extra = 0; // number of extra chars
+ char_u *p_extra = NULL; // string of extra chars, plus NUL
+ char_u *p_extra_free = NULL; // p_extra needs to be freed
+ int c_extra = NUL; // extra chars, all the same
+ int c_final = NUL; // final char, mandatory if set
+ int extra_attr = 0; // attributes when n_extra != 0
+ static char_u *at_end_str = (char_u *)""; // used for p_extra when displaying
+ // curwin->w_p_lcs_chars.eol at
+ // end-of-line
+ int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used
+ int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used
/* saved "extra" items for when draw_state becomes WL_LINE (again) */
int saved_n_extra = 0;
char_u *saved_p_extra = NULL;
int saved_c_extra = 0;
+ int saved_c_final = 0;
int saved_char_attr = 0;
int n_attr = 0; /* chars with special attr */
@@ -2432,11 +2437,11 @@ win_line (
}
if (wp->w_p_list) {
- if (lcs_space || lcs_trail) {
+ if (curwin->w_p_lcs_chars.space || wp->w_p_lcs_chars.trail) {
extra_check = true;
}
// find start of trailing whitespace
- if (lcs_trail) {
+ if (wp->w_p_lcs_chars.trail) {
trailcol = (colnr_T)STRLEN(ptr);
while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) {
trailcol--;
@@ -2644,6 +2649,7 @@ win_line (
/* Draw the cmdline character. */
n_extra = 1;
c_extra = cmdwin_type;
+ c_final = NUL;
char_attr = win_hl_attr(wp, HLF_AT);
}
}
@@ -2662,6 +2668,7 @@ win_line (
p_extra_free[n_extra] = NUL;
p_extra = p_extra_free;
c_extra = NUL;
+ c_final = NUL;
char_attr = win_hl_attr(wp, HLF_FC);
}
}
@@ -2675,6 +2682,7 @@ win_line (
int text_sign;
// Draw cells with the sign value or blank.
c_extra = ' ';
+ c_final = NUL;
char_attr = win_hl_attr(wp, HLF_SC);
n_extra = win_signcol_width(wp);
@@ -2685,6 +2693,7 @@ win_line (
int symbol_blen = (int)STRLEN(p_extra);
if (p_extra != NULL) {
c_extra = NUL;
+ c_final = NUL;
// symbol(s) bytes + (filling spaces) (one byte each)
n_extra = symbol_blen +
(win_signcol_width(wp) - mb_string2cells(p_extra));
@@ -2736,8 +2745,11 @@ win_line (
rl_mirror(extra);
p_extra = extra;
c_extra = NUL;
- } else
+ c_final = NUL;
+ } else {
c_extra = ' ';
+ c_final = NUL;
+ }
n_extra = number_width(wp) + 1;
char_attr = win_hl_attr(wp, HLF_N);
@@ -2792,11 +2804,14 @@ win_line (
if (draw_state == WL_SBR - 1 && n_extra == 0) {
draw_state = WL_SBR;
if (filler_todo > 0) {
- /* Draw "deleted" diff line(s). */
- if (char2cells(fill_diff) > 1)
+ // draw "deleted" diff line(s)
+ if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
c_extra = '-';
- else
- c_extra = fill_diff;
+ c_final = NUL;
+ } else {
+ c_extra = wp->w_p_fcs_chars.diff;
+ c_final = NUL;
+ }
if (wp->w_p_rl) {
n_extra = col + 1;
} else {
@@ -2808,6 +2823,7 @@ win_line (
/* Draw 'showbreak' at the start of each broken line. */
p_extra = p_sbr;
c_extra = NUL;
+ c_final = NUL;
n_extra = (int)STRLEN(p_sbr);
char_attr = win_hl_attr(wp, HLF_AT);
need_showbreak = false;
@@ -2829,6 +2845,7 @@ win_line (
/* Continue item from end of wrapped line. */
n_extra = saved_n_extra;
c_extra = saved_c_extra;
+ c_final = saved_c_final;
p_extra = saved_p_extra;
char_attr = saved_char_attr;
} else {
@@ -3026,20 +3043,18 @@ win_line (
}
}
- /*
- * Get the next character to put on the screen.
- */
- /*
- * The "p_extra" points to the extra stuff that is inserted to
- * represent special characters (non-printable stuff) and other
- * things. When all characters are the same, c_extra is used.
- * "p_extra" must end in a NUL to avoid mb_ptr2len() reads past
- * "p_extra[n_extra]".
- * For the '$' of the 'list' option, n_extra == 1, p_extra == "".
- */
+ // Get the next character to put on the screen.
+ //
+ // The "p_extra" points to the extra stuff that is inserted to
+ // represent special characters (non-printable stuff) and other
+ // things. When all characters are the same, c_extra is used.
+ // If c_final is set, it will compulsorily be used at the end.
+ // "p_extra" must end in a NUL to avoid mb_ptr2len() reads past
+ // "p_extra[n_extra]".
+ // For the '$' of the 'list' option, n_extra == 1, p_extra == "".
if (n_extra > 0) {
- if (c_extra != NUL) {
- c = c_extra;
+ if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) {
+ c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra;
mb_c = c; // doesn't handle non-utf-8 multi-byte!
if (enc_utf8 && utf_char2len(c) > 1) {
mb_utf8 = true;
@@ -3146,6 +3161,7 @@ win_line (
mb_utf8 = (c >= 0x80);
n_extra = (int)STRLEN(p_extra);
c_extra = NUL;
+ c_final = NUL;
if (area_attr == 0 && search_attr == 0) {
n_attr = n_extra + 1;
extra_attr = win_hl_attr(wp, HLF_8);
@@ -3198,6 +3214,7 @@ win_line (
p_extra = extra;
n_extra = (int)STRLEN(extra) - 1;
c_extra = NUL;
+ c_final = NUL;
c = *p_extra++;
if (area_attr == 0 && search_attr == 0) {
n_attr = n_extra + 1;
@@ -3232,6 +3249,7 @@ win_line (
if (n_skip > 0 && mb_l > 1 && n_extra == 0) {
n_extra = 1;
c_extra = MB_FILLER_CHAR;
+ c_final = NUL;
c = ' ';
if (area_attr == 0 && search_attr == 0) {
n_attr = n_extra + 1;
@@ -3391,6 +3409,7 @@ win_line (
- vcol % (int)wp->w_buffer->b_p_ts - 1;
}
c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
+ c_final = NUL;
if (ascii_iswhite(c)) {
if (c == TAB)
/* See "Tab alignment" below. */
@@ -3401,13 +3420,14 @@ win_line (
}
}
- // 'list': change char 160 to lcs_nbsp and space to lcs_space.
+ // 'list': change char 160 to 'nbsp' and space to 'space'.
if (wp->w_p_list
&& (((c == 160
|| (mb_utf8 && (mb_c == 160 || mb_c == 0x202f)))
- && lcs_nbsp)
- || (c == ' ' && lcs_space && ptr - line <= trailcol))) {
- c = (c == ' ') ? lcs_space : lcs_nbsp;
+ && curwin->w_p_lcs_chars.nbsp)
+ || (c == ' ' && curwin->w_p_lcs_chars.space
+ && ptr - line <= trailcol))) {
+ c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
n_attr = 1;
extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = char_attr; // save current attr
@@ -3422,7 +3442,7 @@ win_line (
}
if (trailcol != MAXCOL && ptr > line + trailcol && c == ' ') {
- c = lcs_trail;
+ c = wp->w_p_lcs_chars.trail;
n_attr = 1;
extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = char_attr; // save current attr
@@ -3443,7 +3463,7 @@ win_line (
if (!vim_isprintc(c)) {
// when getting a character from the file, we may have to
// turn it into something else on the way to putting it on the screen.
- if (c == TAB && (!wp->w_p_list || lcs_tab1)) {
+ if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
int tab_len = 0;
long vcol_adjusted = vcol; // removed showbreak length
// Only adjust the tab_len, when at the first column after the
@@ -3467,26 +3487,28 @@ win_line (
tab_len += vcol_off;
}
// boguscols before FIX_FOR_BOGUSCOLS macro from above.
- if (lcs_tab1 && old_boguscols > 0 && n_extra > tab_len) {
+ if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0
+ && n_extra > tab_len) {
tab_len += n_extra - tab_len;
}
/* if n_extra > 0, it gives the number of chars to use for
* a tab, else we need to calculate the width for a tab */
- int len = (tab_len * mb_char2len(lcs_tab2));
+ int len = (tab_len * mb_char2len(wp->w_p_lcs_chars.tab2));
if (n_extra > 0) {
len += n_extra - tab_len;
}
- c = lcs_tab1;
+ c = wp->w_p_lcs_chars.tab1;
p = xmalloc(len + 1);
memset(p, ' ', len);
p[len] = NUL;
xfree(p_extra_free);
p_extra_free = p;
for (i = 0; i < tab_len; i++) {
- utf_char2bytes(lcs_tab2, p);
- p += mb_char2len(lcs_tab2);
- n_extra += mb_char2len(lcs_tab2) - (saved_nextra > 0 ? 1: 0);
+ utf_char2bytes(wp->w_p_lcs_chars.tab2, p);
+ p += mb_char2len(wp->w_p_lcs_chars.tab2);
+ n_extra += mb_char2len(wp->w_p_lcs_chars.tab2)
+ - (saved_nextra > 0 ? 1: 0);
}
p_extra = p_extra_free;
@@ -3511,19 +3533,23 @@ win_line (
// Make sure, the highlighting for the tab char will be
// correctly set further below (effectively reverts the
// FIX_FOR_BOGSUCOLS macro.
- if (n_extra == tab_len + vc_saved && wp->w_p_list && lcs_tab1) {
+ if (n_extra == tab_len + vc_saved && wp->w_p_list
+ && wp->w_p_lcs_chars.tab1) {
tab_len += vc_saved;
}
}
mb_utf8 = false; // don't draw as UTF-8
if (wp->w_p_list) {
- c = lcs_tab1;
+ c = (n_extra == 0 && wp->w_p_lcs_chars.tab3)
+ ? wp->w_p_lcs_chars.tab3
+ : wp->w_p_lcs_chars.tab1;
if (wp->w_p_lbr) {
c_extra = NUL; /* using p_extra from above */
} else {
- c_extra = lcs_tab2;
+ c_extra = wp->w_p_lcs_chars.tab2;
}
+ c_final = wp->w_p_lcs_chars.tab3;
n_attr = tab_len + 1;
extra_attr = win_hl_attr(wp, HLF_0);
saved_attr2 = char_attr; // save current attr
@@ -3534,6 +3560,7 @@ win_line (
c = 0xc0;
}
} else {
+ c_final = NUL;
c_extra = ' ';
c = ' ';
}
@@ -3561,10 +3588,11 @@ win_line (
p_extra = at_end_str;
n_extra = 1;
c_extra = NUL;
+ c_final = NUL;
}
}
- if (wp->w_p_list && lcs_eol > 0) {
- c = lcs_eol;
+ if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) {
+ c = wp->w_p_lcs_chars.eol;
} else {
c = ' ';
}
@@ -3588,6 +3616,7 @@ win_line (
if ((dy_flags & DY_UHEX) && wp->w_p_rl)
rl_mirror(p_extra); /* reverse "<12>" */
c_extra = NUL;
+ c_final = NUL;
if (wp->w_p_lbr) {
char_u *p;
@@ -3634,8 +3663,8 @@ win_line (
c = match_conc;
} else if (syn_get_sub_char() != NUL) {
c = syn_get_sub_char();
- } else if (lcs_conceal != NUL) {
- c = lcs_conceal;
+ } else if (wp->w_p_lcs_chars.conceal != NUL) {
+ c = wp->w_p_lcs_chars.conceal;
} else {
c = ' ';
}
@@ -3705,12 +3734,13 @@ win_line (
&& filler_todo <= 0
&& draw_state > WL_NR
&& c != NUL) {
- c = lcs_prec;
+ c = wp->w_p_lcs_chars.prec;
lcs_prec_todo = NUL;
if (has_mbyte && (*mb_char2cells)(mb_c) > 1) {
/* Double-width character being overwritten by the "precedes"
* character, need to fill up half the character. */
c_extra = MB_FILLER_CHAR;
+ c_final = NUL;
n_extra = 1;
n_attr = 2;
extra_attr = win_hl_attr(wp, HLF_AT);
@@ -3755,7 +3785,7 @@ win_line (
cur = cur->next;
}
}
- if (lcs_eol == lcs_eol_one
+ if (wp->w_p_lcs_chars.eol == lcs_eol_one
&& ((area_attr != 0 && vcol == fromcol
&& (VIsual_mode != Ctrl_V
|| lnum == VIsual.lnum
@@ -3858,7 +3888,8 @@ win_line (
// Make sure alignment is the same regardless
// if listchars=eol:X is used or not.
- bool delay_virttext = lcs_eol == lcs_eol_one && eol_hl_off == 0;
+ bool delay_virttext = wp->w_p_lcs_chars.eol == lcs_eol_one
+ && eol_hl_off == 0;
if (wp->w_p_cuc) {
rightmost_vcol = wp->w_virtcol;
@@ -3978,15 +4009,15 @@ win_line (
break;
}
- /* line continues beyond line end */
- if (lcs_ext
+ // line continues beyond line end
+ if (wp->w_p_lcs_chars.ext
&& !wp->w_p_wrap
&& filler_todo <= 0
&& (wp->w_p_rl ? col == 0 : col == grid->Columns - 1)
&& (*ptr != NUL
|| (wp->w_p_list && lcs_eol_one > 0)
|| (n_extra && (c_extra != NUL || *p_extra != NUL)))) {
- c = lcs_ext;
+ c = wp->w_p_lcs_chars.ext;
char_attr = win_hl_attr(wp, HLF_AT);
mb_c = c;
if (enc_utf8 && utf_char2len(c) > 1) {
@@ -4164,7 +4195,8 @@ win_line (
if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns))
&& (*ptr != NUL
|| filler_todo > 0
- || (wp->w_p_list && lcs_eol != NUL && p_extra != at_end_str)
+ || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
+ && p_extra != at_end_str)
|| (n_extra != 0 && (c_extra != NUL || *p_extra != NUL)))
) {
bool wrap = wp->w_p_wrap // Wrapping enabled.
@@ -4172,7 +4204,7 @@ win_line (
&& lcs_eol_one != -1 // Haven't printed the lcs_eol character.
&& row != endrow - 1 // Not the last line being displayed.
&& (grid->Columns == Columns // Window spans the width of the screen,
- || ui_is_external(kUIMultigrid)) // or has dedicated grid.
+ || ui_has(kUIMultigrid)) // or has dedicated grid.
&& !wp->w_p_rl; // Not right-to-left.
grid_put_linebuf(grid, row, 0, col - boguscols, grid->Columns, wp->w_p_rl,
wp, wp->w_hl_attr_normal, wrap);
@@ -4224,16 +4256,19 @@ win_line (
saved_n_extra = n_extra;
saved_p_extra = p_extra;
saved_c_extra = c_extra;
+ saved_c_final = c_final;
saved_char_attr = char_attr;
n_extra = 0;
- lcs_prec_todo = lcs_prec;
- if (filler_todo <= 0)
- need_showbreak = TRUE;
- --filler_todo;
- /* When the filler lines are actually below the last line of the
- * file, don't draw the line itself, break here. */
- if (filler_todo == 0 && wp->w_botfill)
+ lcs_prec_todo = wp->w_p_lcs_chars.prec;
+ if (filler_todo <= 0) {
+ need_showbreak = true;
+ }
+ filler_todo--;
+ // When the filler lines are actually below the last line of the
+ // file, don't draw the line itself, break here.
+ if (filler_todo == 0 && wp->w_botfill) {
break;
+ }
}
} /* for every character in the line */
@@ -4257,7 +4292,7 @@ win_line (
/// screen positions.
static void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
{
- if (!ui_is_external(kUIMultigrid) && *grid != &default_grid) {
+ if (!(*grid)->chars && *grid != &default_grid) {
*row_off += (*grid)->row_offset;
*col_off += (*grid)->col_offset;
*grid = &default_grid;
@@ -4507,7 +4542,7 @@ void redraw_statuslines(void)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_redr_status) {
- win_redr_status(wp, false);
+ win_redr_status(wp);
}
}
if (redraw_tabline)
@@ -4629,8 +4664,7 @@ win_redr_status_matches (
if (matches == NULL) /* interrupted completion? */
return;
- buf = xmalloc(has_mbyte ? default_grid.Columns * MB_MAXBYTES + 1
- : default_grid.Columns + 1);
+ buf = xmalloc(Columns * MB_MAXBYTES + 1);
if (match == -1) { /* don't show match but original text */
match = 0;
@@ -4651,13 +4685,13 @@ win_redr_status_matches (
if (first_match > 0)
clen += 2;
// jumping right, put match at the left
- if ((long)clen > default_grid.Columns) {
+ if ((long)clen > Columns) {
first_match = match;
/* if showing the last match, we can add some on the left */
clen = 2;
for (i = match; i < num_matches; ++i) {
clen += status_match_len(xp, L_MATCH(i)) + 2;
- if ((long)clen >= default_grid.Columns) {
+ if ((long)clen >= Columns) {
break;
}
}
@@ -4668,7 +4702,7 @@ win_redr_status_matches (
if (add_left)
while (first_match > 0) {
clen += status_match_len(xp, L_MATCH(first_match - 1)) + 2;
- if ((long)clen >= default_grid.Columns) {
+ if ((long)clen >= Columns) {
break;
}
first_match--;
@@ -4686,8 +4720,7 @@ win_redr_status_matches (
clen = len;
i = first_match;
- while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2)
- < default_grid.Columns) {
+ while ((long)(clen + status_match_len(xp, L_MATCH(i)) + 2) < Columns) {
if (i == match) {
selstart = buf + len;
selstart_col = clen;
@@ -4738,7 +4771,7 @@ win_redr_status_matches (
if (msg_scrolled > 0) {
/* Put the wildmenu just above the command line. If there is
* no room, scroll the screen one line up. */
- if (cmdline_row == default_grid.Rows - 1) {
+ if (cmdline_row == Rows - 1) {
grid_del_lines(&default_grid, 0, 1, (int)Rows, 0, (int)Columns);
msg_scrolled++;
} else {
@@ -4767,7 +4800,7 @@ win_redr_status_matches (
grid_puts(&default_grid, selstart, row, selstart_col, HL_ATTR(HLF_WM));
}
- grid_fill(&default_grid, row, row + 1, clen, (int)default_grid.Columns,
+ grid_fill(&default_grid, row, row + 1, clen, (int)Columns,
fillchar, fillchar, attr);
}
@@ -4778,9 +4811,7 @@ win_redr_status_matches (
/// Redraw the status line of window `wp`.
///
/// If inversion is possible we use it. Else '=' characters are used.
-/// If "ignore_pum" is true, also redraw statusline when the popup menu is
-/// displayed.
-static void win_redr_status(win_T *wp, int ignore_pum)
+static void win_redr_status(win_T *wp)
{
int row;
char_u *p;
@@ -4794,7 +4825,7 @@ static void win_redr_status(win_T *wp, int ignore_pum)
// invokes ":redrawstatus". Simply ignore the call then.
if (busy
// Also ignore if wildmenu is showing.
- || (wild_menu_showing != 0 && !ui_is_external(kUIWildmenu))) {
+ || (wild_menu_showing != 0 && !ui_has(kUIWildmenu))) {
return;
}
busy = true;
@@ -4803,7 +4834,7 @@ static void win_redr_status(win_T *wp, int ignore_pum)
if (wp->w_status_height == 0) {
// no status line, can only be last window
redraw_cmdline = true;
- } else if (!redrawing() || (!ignore_pum && pum_drawn())) {
+ } else if (!redrawing()) {
// Don't redraw right now, do it later. Don't update status line when
// popup menu is visible and may be drawn over it
wp->w_redr_status = true;
@@ -5033,7 +5064,7 @@ win_redr_custom (
row = 0;
fillchar = ' ';
attr = HL_ATTR(HLF_TPF);
- maxwidth = default_grid.Columns;
+ maxwidth = Columns;
use_sandbox = was_set_insecurely((char_u *)"tabline", 0);
} else {
row = W_ENDROW(wp);
@@ -5052,13 +5083,13 @@ win_redr_custom (
if (*stl++ != '(')
stl = p_ruf;
}
- col = ru_col - (default_grid.Columns - wp->w_width);
+ col = ru_col - (Columns - wp->w_width);
if (col < (wp->w_width + 1) / 2) {
col = (wp->w_width + 1) / 2;
}
maxwidth = wp->w_width - col;
if (!wp->w_status_height) {
- row = default_grid.Rows - 1;
+ row = Rows - 1;
maxwidth--; // writing in last column may cause scrolling
fillchar = ' ';
attr = 0;
@@ -5152,7 +5183,7 @@ win_redr_custom (
p = (char_u *) tabtab[n].start;
cur_click_def = tabtab[n].def;
}
- while (col < default_grid.Columns) {
+ while (col < Columns) {
tab_page_click_defs[col++] = cur_click_def;
}
}
@@ -5282,7 +5313,7 @@ void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr)
}
static int put_dirty_row = -1;
-static int put_dirty_first = -1;
+static int put_dirty_first = INT_MAX;
static int put_dirty_last = 0;
/// Start a group of screen_puts_len calls that builds a single screen line.
@@ -5313,8 +5344,6 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
int prev_c = 0; /* previous Arabic character */
int pc, nc, nc1;
int pcc[MAX_MCO];
- int force_redraw_this;
- int force_redraw_next = FALSE;
int need_redraw;
bool do_flush = false;
@@ -5337,16 +5366,10 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
/* When drawing over the right halve of a double-wide char clear out the
* left halve. Only needed in a terminal. */
- if (col > 0 && col < grid->Columns && grid_fix_col(grid, col, row) != col) {
- schar_from_ascii(grid->chars[off - 1], ' ');
- grid->attrs[off - 1] = 0;
+ if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) {
// redraw the previous cell, make it empty
- if (put_dirty_first == -1) {
- put_dirty_first = col-1;
- }
- put_dirty_last = col+1;
- // force the cell at "col" to be redrawn
- force_redraw_next = true;
+ put_dirty_first = -1;
+ put_dirty_last = MAX(put_dirty_last, 1);
}
max_off = grid->line_offset[row] + grid->Columns;
@@ -5394,15 +5417,12 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
schar_from_cc(buf, u8c, u8cc);
- force_redraw_this = force_redraw_next;
- force_redraw_next = FALSE;
-
need_redraw = schar_cmp(grid->chars[off], buf)
|| (mbyte_cells == 2 && grid->chars[off + 1][0] != 0)
|| grid->attrs[off] != attr
|| exmode_active;
- if (need_redraw || force_redraw_this) {
+ if (need_redraw) {
// When at the end of the text and overwriting a two-cell
// character with a one-cell character, need to clear the next
// cell. Also when overwriting the left halve of a two-cell char
@@ -5426,10 +5446,8 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
grid->chars[off + 1][0] = 0;
grid->attrs[off + 1] = attr;
}
- if (put_dirty_first == -1) {
- put_dirty_first = col;
- }
- put_dirty_last = col+mbyte_cells;
+ put_dirty_first = MIN(put_dirty_first, col);
+ put_dirty_last = MAX(put_dirty_last, col+mbyte_cells);
}
off += mbyte_cells;
@@ -5457,14 +5475,14 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
void grid_puts_line_flush(ScreenGrid *grid, bool set_cursor)
{
assert(put_dirty_row != -1);
- if (put_dirty_first != -1) {
+ if (put_dirty_first < put_dirty_last) {
if (set_cursor) {
ui_grid_cursor_goto(grid->handle, put_dirty_row,
MIN(put_dirty_last, grid->Columns-1));
}
ui_line(grid, put_dirty_row, put_dirty_first, put_dirty_last,
put_dirty_last, 0, false);
- put_dirty_first = -1;
+ put_dirty_first = INT_MAX;
put_dirty_last = 0;
}
put_dirty_row = -1;
@@ -5790,7 +5808,7 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col,
}
// nothing to do
- if (grid->chars == NULL || start_row >= end_row || start_col >= end_col) {
+ if (start_row >= end_row || start_col >= end_col) {
return;
}
@@ -5836,9 +5854,7 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col,
if (dirty_last > dirty_first) {
// TODO(bfredl): support a cleared suffix even with a batched line?
if (put_dirty_row == row) {
- if (put_dirty_first == -1) {
- put_dirty_first = dirty_first;
- }
+ put_dirty_first = MIN(put_dirty_first, dirty_first);
put_dirty_last = MAX(put_dirty_last, dirty_last);
} else {
int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' ');
@@ -5851,7 +5867,8 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col,
}
// TODO(bfredl): The relevant caller should do this
- if (row == default_grid.Rows - 1) { // overwritten the command line
+ if (row == Rows - 1 && !ui_has(kUIMessages)) {
+ // overwritten the command line
redraw_cmdline = true;
if (start_col == 0 && end_col == Columns
&& c1 == ' ' && c2 == ' ' && attr == 0) {
@@ -5881,18 +5898,6 @@ void check_for_delay(int check_msg_scroll)
}
}
-/*
- * screen_valid - allocate screen buffers if size changed
- * If "doclear" is TRUE: clear screen if it has been resized.
- * Returns TRUE if there is a valid screen to write to.
- * Returns FALSE when starting up and screen not initialized yet.
- */
-int screen_valid(int doclear)
-{
- screenalloc(doclear); // allocate screen buffers if size changed
- return default_grid.chars != NULL;
-}
-
/// (Re)allocates a window grid if size changed while in ext_multigrid mode.
/// Updates size, offsets and handle for the grid regardless.
///
@@ -5902,38 +5907,35 @@ void win_grid_alloc(win_T *wp)
{
ScreenGrid *grid = &wp->w_grid;
- int rows = grid->requested_rows;
- if (rows == 0) {
- rows = wp->w_height;
- }
-
- int columns = grid->requested_cols;
- if (columns == 0) {
- columns = wp->w_width;
- }
+ int rows = wp->w_height_inner;
+ int cols = wp->w_width_inner;
// TODO(bfredl): floating windows should force this to true
- bool want_allocation = ui_is_external(kUIMultigrid);
+ bool want_allocation = ui_has(kUIMultigrid);
bool has_allocation = (grid->chars != NULL);
if (want_allocation && has_allocation && highlights_invalid) {
grid_invalidate(grid);
}
+ if (grid->Rows != rows) {
+ wp->w_lines_valid = 0;
+ xfree(wp->w_lines);
+ wp->w_lines = xcalloc(rows+1, sizeof(wline_T));
+ }
+
int was_resized = false;
if ((has_allocation != want_allocation)
|| grid->Rows != rows
- || grid->Columns != columns) {
+ || grid->Columns != cols) {
if (want_allocation) {
- grid_alloc(grid, rows, columns, true);
- win_free_lsize(wp);
- win_alloc_lines(wp);
+ grid_alloc(grid, rows, cols, true, true);
} else {
// Single grid mode, all rendering will be redirected to default_grid.
// Only keep track of the size and offset of the window.
grid_free(grid);
grid->Rows = rows;
- grid->Columns = columns;
+ grid->Columns = cols;
}
was_resized = true;
}
@@ -5945,7 +5947,7 @@ void win_grid_alloc(win_T *wp)
// - a grid was just resized
// - screen_resize was called and all grid sizes must be sent
// - the UI wants multigrid event (necessary)
- if ((send_grid_resize || was_resized) && ui_is_external(kUIMultigrid)) {
+ if ((send_grid_resize || was_resized) && ui_has(kUIMultigrid)) {
ui_call_grid_resize(grid->handle, grid->Columns, grid->Rows);
}
}
@@ -5971,7 +5973,7 @@ void grid_assign_handle(ScreenGrid *grid)
/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
/// and Columns for positioning text etc. where the final size of the shell is
/// needed.
-void screenalloc(bool doclear)
+void screenalloc(void)
{
static bool entered = false; // avoid recursiveness
int retry_count = 0;
@@ -6005,6 +6007,10 @@ retry:
*/
++RedrawingDisabled;
+ // win_new_shellsize will recompute floats position, but tell the
+ // compositor to not redraw them yet
+ ui_comp_invalidate_screen();
+
win_new_shellsize(); /* fit the windows in the new sized shell */
comp_col(); /* recompute columns for shown command and ruler */
@@ -6018,37 +6024,22 @@ retry:
// If anything fails, make grid arrays NULL, so we don't do anything!
// Continuing with the old arrays may result in a crash, because the
// size is wrong.
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- win_free_lsize(wp);
- }
- if (aucmd_win != NULL)
- win_free_lsize(aucmd_win);
- grid_alloc(&default_grid, Rows, Columns, !doclear);
+ grid_alloc(&default_grid, Rows, Columns, true, true);
StlClickDefinition *new_tab_page_click_defs = xcalloc(
(size_t)Columns, sizeof(*new_tab_page_click_defs));
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- win_alloc_lines(wp);
- }
- if (aucmd_win != NULL && aucmd_win->w_lines == NULL) {
- win_alloc_lines(aucmd_win);
- }
-
clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
xfree(tab_page_click_defs);
tab_page_click_defs = new_tab_page_click_defs;
- tab_page_click_defs_size = default_grid.Columns;
+ tab_page_click_defs_size = Columns;
default_grid.row_offset = 0;
default_grid.col_offset = 0;
default_grid.handle = DEFAULT_GRID_HANDLE;
- must_redraw = CLEAR; /* need to clear the screen later */
- if (doclear)
- screenclear2();
-
+ must_redraw = CLEAR; // need to clear the screen later
entered = FALSE;
--RedrawingDisabled;
@@ -6065,7 +6056,7 @@ retry:
}
}
-void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy)
+void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid)
{
int new_row;
ScreenGrid new = *grid;
@@ -6083,7 +6074,7 @@ void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy)
new.line_offset[new_row] = new_row * new.Columns;
new.line_wraps[new_row] = false;
- grid_clear_line(&new, new.line_offset[new_row], columns, true);
+ grid_clear_line(&new, new.line_offset[new_row], columns, valid);
if (copy) {
// If the screen is not going to be cleared, copy as much as
@@ -6155,13 +6146,9 @@ void clear_tab_page_click_defs(StlClickDefinition *const tpcd,
void screenclear(void)
{
- check_for_delay(FALSE);
- screenalloc(false); /* allocate screen buffers if size changed */
- screenclear2(); /* clear the screen */
-}
+ check_for_delay(false);
+ screenalloc(); // allocate screen buffers if size changed
-static void screenclear2(void)
-{
int i;
if (starting == NO_SCREEN || default_grid.chars == NULL) {
@@ -6182,6 +6169,8 @@ static void screenclear2(void)
redraw_all_later(NOT_VALID);
redraw_cmdline = true;
redraw_tabline = true;
+ redraw_popupmenu = true;
+ pum_invalidate();
if (must_redraw == CLEAR) {
must_redraw = NOT_VALID; // no need to clear again
}
@@ -6205,11 +6194,17 @@ static void grid_clear_line(ScreenGrid *grid, unsigned off, int width,
(void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T));
}
-static void grid_invalidate(ScreenGrid *grid)
+void grid_invalidate(ScreenGrid *grid)
{
(void)memset(grid->attrs, -1, grid->Rows * grid->Columns * sizeof(sattr_T));
}
+bool grid_invalid_row(ScreenGrid *grid, int row)
+{
+ return grid->attrs[grid->line_offset[row]] < 0;
+}
+
+
/// Copy part of a grid line for vertically split window.
static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
@@ -6237,7 +6232,7 @@ void setcursor(void)
if (curwin->w_p_rl) {
// With 'rightleft' set and the cursor on a double-wide character,
// position it on the leftmost column.
- col = curwin->w_grid.Columns - curwin->w_wcol
+ col = curwin->w_width_inner - curwin->w_wcol
- ((utf_ptr2cells(get_cursor_pos_ptr()) == 2
&& vim_isprintc(gchar_cursor())) ? 2 : 1);
}
@@ -6247,42 +6242,28 @@ void setcursor(void)
}
}
-/// Insert 'line_count' lines at 'row' in window 'wp'.
-/// Returns FAIL if the lines are not inserted, OK for success.
-int win_ins_lines(win_T *wp, int row, int line_count)
-{
- return win_do_lines(wp, row, line_count, false);
-}
-
-/// Delete "line_count" window lines at "row" in window "wp".
-/// Return OK for success, FAIL if the lines are not deleted.
-int win_del_lines(win_T *wp, int row, int line_count)
-{
- return win_do_lines(wp, row, line_count, true);
-}
-
-// Common code for win_ins_lines() and win_del_lines().
-// Returns OK or FAIL when the work has been done.
-static int win_do_lines(win_T *wp, int row, int line_count, int del)
+/// Scroll 'line_count' lines at 'row' in window 'wp'.
+///
+/// Positive `line_count' means scrolling down, so that more space is available
+/// at 'row'. Negative `line_count` implies deleting lines at `row`.
+void win_scroll_lines(win_T *wp, int row, int line_count)
{
- if (!redrawing() || line_count <= 0) {
- return FAIL;
+ if (!redrawing() || line_count == 0) {
+ return;
}
// No lines are being moved, just draw over the entire area
- if (row + line_count >= wp->w_grid.Rows) {
- return OK;
+ if (row + abs(line_count) >= wp->w_grid.Rows) {
+ return;
}
- int retval;
- if (del) {
- retval = grid_del_lines(&wp->w_grid, row, line_count,
- wp->w_grid.Rows, 0, wp->w_grid.Columns);
+ if (line_count < 0) {
+ grid_del_lines(&wp->w_grid, row, -line_count,
+ wp->w_grid.Rows, 0, wp->w_grid.Columns);
} else {
- retval = grid_ins_lines(&wp->w_grid, row, line_count,
- wp->w_grid.Rows, 0, wp->w_grid.Columns);
+ grid_ins_lines(&wp->w_grid, row, line_count,
+ wp->w_grid.Rows, 0, wp->w_grid.Columns);
}
- return retval;
}
/*
@@ -6302,10 +6283,8 @@ static int win_do_lines(win_T *wp, int row, int line_count, int del)
/// 'col' is the column from with we start inserting.
//
/// 'row', 'col' and 'end' are relative to the start of the region.
-///
-/// @return FAIL for failure, OK for success.
-int grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
- int width)
+void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
+ int width)
{
int i;
int j;
@@ -6316,8 +6295,8 @@ int grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
row += row_off;
end += row_off;
- if (!screen_valid(TRUE) || line_count <= 0) {
- return FAIL;
+ if (line_count <= 0) {
+ return;
}
// Shift line_offset[] line_count down to reflect the inserted lines.
@@ -6347,17 +6326,15 @@ int grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
ui_call_grid_scroll(grid->handle, row, end, col, col+width, -line_count, 0);
- return OK;
+ return;
}
/// delete lines on the screen and move lines up.
/// 'end' is the line after the scrolled part. Normally it is Rows.
/// When scrolling region used 'off' is the offset from the top for the region.
/// 'row' and 'end' are relative to the start of the region.
-///
-/// Return OK for success, FAIL if the lines are not deleted.
-int grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
- int width)
+void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
+ int width)
{
int j;
int i;
@@ -6368,8 +6345,8 @@ int grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
row += row_off;
end += row_off;
- if (!screen_valid(TRUE) || line_count <= 0) {
- return FAIL;
+ if (line_count <= 0) {
+ return;
}
// Now shift line_offset[] line_count up to reflect the deleted lines.
@@ -6400,7 +6377,7 @@ int grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col,
ui_call_grid_scroll(grid->handle, row, end, col, col+width, line_count, 0);
- return OK;
+ return;
}
@@ -6421,6 +6398,13 @@ int showmode(void)
int nwr_save;
int sub_attr;
+ if (ui_has(kUIMessages) && clear_cmdline) {
+ msg_ext_clear(true);
+ }
+
+ // don't make non-flushed message part of the showmode
+ msg_ext_ui_flush();
+
do_mode = ((p_smd && msg_silent == 0)
&& ((State & TERM_FOCUS)
|| (State & INSERT)
@@ -6445,7 +6429,7 @@ int showmode(void)
/* if the cmdline is more than one line high, erase top lines */
need_clear = clear_cmdline;
- if (clear_cmdline && cmdline_row < default_grid.Rows - 1) {
+ if (clear_cmdline && cmdline_row < Rows - 1) {
msg_clr_cmdline(); // will reset clear_cmdline
}
@@ -6463,9 +6447,14 @@ int showmode(void)
MSG_PUTS_ATTR("--", attr);
// CTRL-X in Insert mode
if (edit_submode != NULL && !shortmess(SHM_COMPLETIONMENU)) {
- /* These messages can get long, avoid a wrap in a narrow
- * window. Prefer showing edit_submode_extra. */
- length = (default_grid.Rows - msg_row) * default_grid.Columns - 3;
+ // These messages can get long, avoid a wrap in a narrow window.
+ // Prefer showing edit_submode_extra. With external messages there
+ // is no imposed limit.
+ if (ui_has(kUIMessages)) {
+ length = INT_MAX;
+ } else {
+ length = (Rows - msg_row) * Columns - 3;
+ }
if (edit_submode_extra != NULL) {
length -= vim_strsize(edit_submode_extra);
}
@@ -6567,6 +6556,9 @@ int showmode(void)
msg_clr_cmdline();
}
+ // NB: also handles clearing the showmode if it was emtpy or disabled
+ msg_ext_flush_showmode();
+
/* In Visual mode the size of the selected area must be redrawn. */
if (VIsual_active)
clear_showcmd();
@@ -6589,7 +6581,7 @@ int showmode(void)
static void msg_pos_mode(void)
{
msg_col = 0;
- msg_row = default_grid.Rows - 1;
+ msg_row = Rows - 1;
}
/// Delete mode message. Used when ESC is typed which is expected to end
@@ -6608,11 +6600,13 @@ void unshowmode(bool force)
// Clear the mode message.
void clearmode(void)
{
- msg_pos_mode();
- if (Recording) {
- recording_mode(HL_ATTR(HLF_CM));
- }
- msg_clr_eos();
+ msg_ext_ui_flush();
+ msg_pos_mode();
+ if (Recording) {
+ recording_mode(HL_ATTR(HLF_CM));
+ }
+ msg_clr_eos();
+ msg_ext_flush_showmode();
}
static void recording_mode(int attr)
@@ -6653,7 +6647,7 @@ static void draw_tabline(void)
}
redraw_tabline = false;
- if (ui_is_external(kUITabline)) {
+ if (ui_has(kUITabline)) {
ui_ext_tabline_update();
return;
}
@@ -6663,7 +6657,7 @@ static void draw_tabline(void)
// Init TabPageIdxs[] to zero: Clicking outside of tabs has no effect.
- assert(default_grid.Columns == tab_page_click_defs_size);
+ assert(Columns == tab_page_click_defs_size);
clear_tab_page_click_defs(tab_page_click_defs, tab_page_click_defs_size);
/* Use the 'tabline' option if it's set. */
@@ -6685,7 +6679,7 @@ static void draw_tabline(void)
}
if (tabcount > 0) {
- tabwidth = (default_grid.Columns - 1 + tabcount / 2) / tabcount;
+ tabwidth = (Columns - 1 + tabcount / 2) / tabcount;
}
if (tabwidth < 6) {
@@ -6696,7 +6690,7 @@ static void draw_tabline(void)
tabcount = 0;
FOR_ALL_TABS(tp) {
- if (col >= default_grid.Columns - 4) {
+ if (col >= Columns - 4) {
break;
}
@@ -6737,7 +6731,7 @@ static void draw_tabline(void)
if (wincount > 1) {
vim_snprintf((char *)NameBuff, MAXPATHL, "%d", wincount);
len = (int)STRLEN(NameBuff);
- if (col + len >= default_grid.Columns - 3) {
+ if (col + len >= Columns - 3) {
break;
}
grid_puts_len(&default_grid, NameBuff, len, 0, col,
@@ -6766,8 +6760,8 @@ static void draw_tabline(void)
p += len - room;
len = room;
}
- if (len > default_grid.Columns - col - 1) {
- len = default_grid.Columns - col - 1;
+ if (len > Columns - col - 1) {
+ len = Columns - col - 1;
}
grid_puts_len(&default_grid, p, (int)STRLEN(p), 0, col, attr);
@@ -6791,14 +6785,14 @@ static void draw_tabline(void)
c = '_';
else
c = ' ';
- grid_fill(&default_grid, 0, 1, col, (int)default_grid.Columns, c, c,
+ grid_fill(&default_grid, 0, 1, col, (int)Columns, c, c,
attr_fill);
/* Put an "X" for closing the current tab if there are several. */
if (first_tabpage->tp_next != NULL) {
- grid_putchar(&default_grid, 'X', 0, (int)default_grid.Columns - 1,
+ grid_putchar(&default_grid, 'X', 0, (int)Columns - 1,
attr_nosel);
- tab_page_click_defs[default_grid.Columns - 1] = (StlClickDefinition) {
+ tab_page_click_defs[Columns - 1] = (StlClickDefinition) {
.type = kStlClickTabClose,
.tabnr = 999,
.func = NULL,
@@ -6849,17 +6843,17 @@ static int fillchar_status(int *attr, win_T *wp)
bool is_curwin = (wp == curwin);
if (is_curwin) {
*attr = win_hl_attr(wp, HLF_S);
- fill = fill_stl;
+ fill = wp->w_p_fcs_chars.stl;
} else {
*attr = win_hl_attr(wp, HLF_SNC);
- fill = fill_stlnc;
+ fill = wp->w_p_fcs_chars.stlnc;
}
/* Use fill when there is highlighting, and highlighting of current
* window differs, or the fillchars differ, or this is not the
* current window */
if (*attr != 0 && ((win_hl_attr(wp, HLF_S) != win_hl_attr(wp, HLF_SNC)
|| !is_curwin || ONE_WINDOW)
- || (fill_stl != fill_stlnc))) {
+ || (wp->w_p_fcs_chars.stl != wp->w_p_fcs_chars.stlnc))) {
return fill;
}
if (is_curwin) {
@@ -6875,7 +6869,7 @@ static int fillchar_status(int *attr, win_T *wp)
static int fillchar_vsep(win_T *wp, int *attr)
{
*attr = win_hl_attr(wp, HLF_C);
- return fill_vert;
+ return wp->w_p_fcs_chars.vert;
}
/*
@@ -6903,11 +6897,6 @@ void showruler(int always)
{
if (!always && !redrawing())
return;
- if (pum_drawn()) {
- // Don't redraw right now, do it later.
- curwin->w_redr_status = true;
- return;
- }
if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) {
redraw_custom_statusline(curwin);
} else {
@@ -6926,9 +6915,12 @@ void showruler(int always)
static void win_redr_ruler(win_T *wp, int always)
{
- /* If 'ruler' off or redrawing disabled, don't do anything */
- if (!p_ru)
+ static bool did_show_ext_ruler = false;
+
+ // If 'ruler' off or redrawing disabled, don't do anything
+ if (!p_ru) {
return;
+ }
/*
* Check if cursor.lnum is valid, since win_redr_ruler() may be called
@@ -6942,10 +6934,6 @@ static void win_redr_ruler(win_T *wp, int always)
if (wp == lastwin && lastwin->w_status_height == 0)
if (edit_submode != NULL)
return;
- // Don't draw the ruler when the popup menu is visible, it may overlap.
- if (pum_drawn()) {
- return;
- }
if (*p_ruf) {
int save_called_emsg = called_emsg;
@@ -6987,26 +6975,28 @@ static void win_redr_ruler(win_T *wp, int always)
int fillchar;
int attr;
int off;
+ bool part_of_status = false;
if (wp->w_status_height) {
row = W_ENDROW(wp);
fillchar = fillchar_status(&attr, wp);
off = wp->w_wincol;
width = wp->w_width;
+ part_of_status = true;
} else {
- row = default_grid.Rows - 1;
+ row = Rows - 1;
fillchar = ' ';
attr = 0;
- width = default_grid.Columns;
+ width = Columns;
off = 0;
}
- /* In list mode virtcol needs to be recomputed */
+ // In list mode virtcol needs to be recomputed
colnr_T virtcol = wp->w_virtcol;
- if (wp->w_p_list && lcs_tab1 == NUL) {
- wp->w_p_list = FALSE;
+ if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) {
+ wp->w_p_list = false;
getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
- wp->w_p_list = TRUE;
+ wp->w_p_list = true;
}
#define RULER_BUF_LEN 70
@@ -7035,7 +7025,7 @@ static void win_redr_ruler(win_T *wp, int always)
if (wp->w_status_height == 0) { // can't use last char of screen
o++;
}
- int this_ru_col = ru_col - (default_grid.Columns - width);
+ int this_ru_col = ru_col - (Columns - width);
if (this_ru_col < 0) {
this_ru_col = 0;
}
@@ -7052,23 +7042,39 @@ static void win_redr_ruler(win_T *wp, int always)
}
get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
}
- // Truncate at window boundary.
- o = 0;
- for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
- o += utf_ptr2cells(buffer + i);
- if (this_ru_col + o > width) {
- buffer[i] = NUL;
- break;
+
+ if (ui_has(kUIMessages) && !part_of_status) {
+ Array content = ARRAY_DICT_INIT;
+ Array chunk = ARRAY_DICT_INIT;
+ ADD(chunk, INTEGER_OBJ(attr));
+ ADD(chunk, STRING_OBJ(cstr_to_string((char *)buffer)));
+ ADD(content, ARRAY_OBJ(chunk));
+ ui_call_msg_ruler(content);
+ did_show_ext_ruler = true;
+ } else {
+ if (did_show_ext_ruler) {
+ ui_call_msg_ruler((Array)ARRAY_DICT_INIT);
+ did_show_ext_ruler = false;
+ }
+ // Truncate at window boundary.
+ o = 0;
+ for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
+ o += utf_ptr2cells(buffer + i);
+ if (this_ru_col + o > width) {
+ buffer[i] = NUL;
+ break;
+ }
}
+
+ grid_puts(&default_grid, buffer, row, this_ru_col + off, attr);
+ i = redraw_cmdline;
+ grid_fill(&default_grid, row, row + 1,
+ this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
+ fillchar, attr);
+ // don't redraw the cmdline because of showing the ruler
+ redraw_cmdline = i;
}
- grid_puts(&default_grid, buffer, row, this_ru_col + off, attr);
- i = redraw_cmdline;
- grid_fill(&default_grid, row, row + 1,
- this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
- fillchar, attr);
- // don't redraw the cmdline because of showing the ruler
- redraw_cmdline = i;
wp->w_ru_cursor = wp->w_cursor;
wp->w_ru_virtcol = wp->w_virtcol;
wp->w_ru_empty = empty_line;
@@ -7090,7 +7096,7 @@ int number_width(win_T *wp)
if (wp->w_p_rnu && !wp->w_p_nu) {
// cursor line shows "0"
- lnum = wp->w_grid.Rows;
+ lnum = wp->w_height_inner;
} else {
// cursor line shows absolute line number
lnum = wp->w_buffer->b_ml.ml_line_count;
@@ -7177,7 +7183,7 @@ void screen_resize(int width, int height)
*/
if (State == ASKMORE || State == EXTERNCMD || State == CONFIRM
|| exmode_active) {
- screenalloc(false);
+ screenalloc();
repeat_message();
} else {
if (curwin->w_p_scb)
@@ -7188,7 +7194,10 @@ void screen_resize(int width, int height)
} else {
update_topline();
if (pum_drawn()) {
- redraw_later(NOT_VALID);
+ // TODO(bfredl): ins_compl_show_pum wants to redraw the screen first.
+ // For now make sure the nested update_screen(0) won't redraw the
+ // pum at the old position. Try to untangle this later.
+ redraw_popupmenu = false;
ins_compl_show_pum();
}
update_screen(NOT_VALID);
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index 109541ef07..61ed98247d 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -28,8 +28,7 @@
///
/// Note: before the screen is initialized and when out of memory these can be
/// NULL.
-EXTERN ScreenGrid default_grid INIT(= { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0,
- 0, 0, 0 });
+EXTERN ScreenGrid default_grid INIT(= SCREEN_GRID_INIT);
#define DEFAULT_GRID_HANDLE 1 // handle for the default_grid
diff --git a/src/nvim/search.c b/src/nvim/search.c
index cf0f1ea287..5f0ff96f95 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -2168,7 +2168,7 @@ showmatch(
}
if (curwin->w_p_wrap
|| (vcol >= curwin->w_leftcol
- && vcol < curwin->w_leftcol + curwin->w_grid.Columns)) {
+ && vcol < curwin->w_leftcol + curwin->w_width_inner)) {
mpos = *lpos; // save the pos, update_screen() may change it
save_cursor = curwin->w_cursor;
save_so = p_so;
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 4921824316..96a8dfd295 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -27,6 +27,7 @@
#include "nvim/func_attr.h"
#include "nvim/getchar.h"
#include "nvim/mark.h"
+#include "nvim/math.h"
#include "nvim/mbyte.h"
#include "nvim/memfile.h"
#include "nvim/memline.h"
@@ -50,16 +51,7 @@
#include "nvim/os/shell.h"
#include "nvim/eval/encode.h"
-#ifdef __MINGW32__
-# undef fpclassify
-# define fpclassify __fpclassify
-# undef isnan
-# define isnan _isnan
-#endif
-
-/*
- * Copy "string" into newly allocated memory.
- */
+/// Copy "string" into newly allocated memory.
char_u *vim_strsave(const char_u *string)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
@@ -1214,14 +1206,14 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap,
remove_trailing_zeroes = true;
}
- if (isinf(f)
+ if (xisinf(f)
|| (strchr("fF", fmt_spec) != NULL && abs_f > 1.0e307)) {
xstrlcpy(tmp, infinity_str(f > 0.0, fmt_spec,
force_sign, space_for_positive),
sizeof(tmp));
str_arg_l = strlen(tmp);
zero_padding = 0;
- } else if (isnan(f)) {
+ } else if (xisnan(f)) {
// Not a number: nan or NAN
memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4);
str_arg_l = 3;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 81c78ca6a9..b6b7dfff11 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -6893,7 +6893,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// "fg", which have been changed now.
highlight_attr_set_all();
- if (!ui_is_external(kUILinegrid) && starting == 0) {
+ if (!ui_has(kUILinegrid) && starting == 0) {
// Older UIs assume that we clear the screen after normal group is
// changed
ui_refresh();
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 51bf22b31c..8b4ad4d3af 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -236,7 +236,8 @@ Terminal *terminal_open(TerminalOptions opts)
// Default settings for terminal buffers
curbuf->b_p_ma = false; // 'nomodifiable'
curbuf->b_p_ul = -1; // 'undolevels'
- curbuf->b_p_scbk = p_scbk; // 'scrollback'
+ curbuf->b_p_scbk = // 'scrollback' (initialize local from global)
+ (p_scbk < 0) ? 10000 : MAX(1, p_scbk);
curbuf->b_p_tw = 0; // 'textwidth'
set_option_value("wrap", false, NULL, OPT_LOCAL);
set_option_value("list", false, NULL, OPT_LOCAL);
@@ -244,13 +245,13 @@ Terminal *terminal_open(TerminalOptions opts)
RESET_BINDING(curwin);
// Reset cursor in current window.
curwin->w_cursor = (pos_T){ .lnum = 1, .col = 0, .coladd = 0 };
-
// Apply TermOpen autocmds _before_ configuring the scrollback buffer.
apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf);
+ // Local 'scrollback' _after_ autocmds.
+ curbuf->b_p_scbk = (curbuf->b_p_scbk < 1) ? SB_MAX : curbuf->b_p_scbk;
// Configure the scrollback buffer.
- rv->sb_size = curbuf->b_p_scbk < 0
- ? SB_MAX : (size_t)MAX(1, curbuf->b_p_scbk);
+ rv->sb_size = (size_t)curbuf->b_p_scbk;
rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size);
if (!true_color) {
@@ -334,41 +335,32 @@ void terminal_close(Terminal *term, char *msg)
}
}
-void terminal_resize(Terminal *term, uint16_t width, uint16_t height)
+void terminal_check_size(Terminal *term)
{
if (term->closed) {
- // If two windows display the same terminal and one is closed by keypress.
return;
}
- bool force = width == UINT16_MAX || height == UINT16_MAX;
+
int curwidth, curheight;
vterm_get_size(term->vt, &curheight, &curwidth);
+ uint16_t width = 0, height = 0;
- if (force || !width) {
- width = (uint16_t)curwidth;
- }
-
- if (force || !height) {
- height = (uint16_t)curheight;
- }
-
- if (!force && curheight == height && curwidth == width) {
- return;
- }
-
- if (height == 0 || width == 0) {
- return;
- }
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer && wp->w_buffer->terminal == term) {
const uint16_t win_width =
- (uint16_t)(MAX(0, wp->w_width - win_col_off(wp)));
+ (uint16_t)(MAX(0, wp->w_width_inner - win_col_off(wp)));
width = MAX(width, win_width);
- height = (uint16_t)MAX(height, wp->w_height);
+ height = (uint16_t)MAX(height, wp->w_height_inner);
}
}
+ // if no window displays the terminal, or such all windows are zero-height,
+ // don't resize the terminal.
+ if ((curheight == height && curwidth == width) || height == 0 || width == 0) {
+ return;
+ }
+
vterm_set_size(term->vt, height, width);
vterm_screen_flush_damage(term->vts);
term->pending_resize = true;
@@ -383,8 +375,10 @@ void terminal_enter(void)
memset(s, 0, sizeof(TerminalState));
s->term = buf->terminal;
- // Ensure the terminal is properly sized.
- terminal_resize(s->term, 0, 0);
+ // Ensure the terminal is properly sized. Ideally window size management
+ // code should always have resized the terminal already, but check here to
+ // be sure.
+ terminal_check_size(s->term);
int save_state = State;
s->save_rd = RedrawingDisabled;
@@ -981,8 +975,8 @@ static void mouse_action(Terminal *term, int button, int row, int col,
// terminal should lose focus
static bool send_mouse_event(Terminal *term, int c)
{
- int row = mouse_row, col = mouse_col;
- win_T *mouse_win = mouse_find_win(&row, &col);
+ int row = mouse_row, col = mouse_col, grid = mouse_grid;
+ win_T *mouse_win = mouse_find_win(&grid, &row, &col);
if (term->forward_mouse && mouse_win->w_buffer->terminal == term) {
// event in the terminal window and mouse events was enabled by the
@@ -1169,8 +1163,10 @@ static void refresh_size(Terminal *term, buf_T *buf)
/// Adjusts scrollback storage after 'scrollback' option changed.
static void on_scrollback_option_changed(Terminal *term, buf_T *buf)
{
- const size_t scbk = curbuf->b_p_scbk < 0
- ? SB_MAX : (size_t)MAX(1, curbuf->b_p_scbk);
+ if (buf->b_p_scbk < 1) { // Local 'scrollback' was set to -1.
+ buf->b_p_scbk = SB_MAX;
+ }
+ const size_t scbk = (size_t)buf->b_p_scbk;
assert(term->sb_current < SIZE_MAX);
if (term->sb_pending > 0) { // Pending rows must be processed first.
abort();
@@ -1318,8 +1314,6 @@ static void redraw(bool restore_cursor)
static void adjust_topline(Terminal *term, buf_T *buf, long added)
{
- int height, width;
- vterm_get_size(term->vt, &height, &width);
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == buf) {
linenr_T ml_end = buf->b_ml.ml_line_count;
@@ -1328,7 +1322,7 @@ static void adjust_topline(Terminal *term, buf_T *buf, long added)
if (following || (wp == curwin && is_focused(term))) {
// "Follow" the terminal output
wp->w_cursor.lnum = ml_end;
- set_topline(wp, MAX(wp->w_cursor.lnum - height + 1, 1));
+ set_topline(wp, MAX(wp->w_cursor.lnum - wp->w_height_inner + 1, 1));
} else {
// Ensure valid cursor for each window displaying this terminal.
wp->w_cursor.lnum = MIN(wp->w_cursor.lnum, ml_end);
diff --git a/src/nvim/testdir/load.vim b/src/nvim/testdir/load.vim
new file mode 100644
index 0000000000..2e01338dd0
--- /dev/null
+++ b/src/nvim/testdir/load.vim
@@ -0,0 +1,30 @@
+function! s:load_factor() abort
+ let timeout = 200
+ let times = []
+
+ for _ in range(5)
+ let g:val = 0
+ call timer_start(timeout, {-> nvim_set_var('val', 1)})
+ let start = reltime()
+ while 1
+ sleep 10m
+ if g:val == 1
+ let g:waited_in_ms = float2nr(reltimefloat(reltime(start)) * 1000)
+ break
+ endif
+ endwhile
+ call insert(times, g:waited_in_ms, 0)
+ endfor
+
+ let longest = max(times)
+ let factor = (longest + 50.0) / timeout
+
+ return factor
+endfunction
+
+" Compute load factor only once.
+let s:load_factor = s:load_factor()
+
+function! LoadAdjust(num) abort
+ return float2nr(ceil(a:num * s:load_factor))
+endfunction
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index c7c3b378cc..011433f19e 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -24,6 +24,9 @@ let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after
let &packpath = &rtp
+" Avoid storing shell history.
+let $HISTFILE = ""
+
" Make sure $HOME does not get read or written.
let $HOME = expand(getcwd() . '/XfakeHOME')
if !isdirectory($HOME)
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 253d6750ed..f1fb8e67b9 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -18,6 +18,8 @@ func Test_vim_did_enter()
endfunc
if has('timers')
+ source load.vim
+
func ExitInsertMode(id)
call feedkeys("\<Esc>")
endfunc
@@ -29,7 +31,7 @@ if has('timers')
let g:triggered = 0
au CursorHoldI * let g:triggered += 1
set updatetime=20
- call timer_start(100, 'ExitInsertMode')
+ call timer_start(LoadAdjust(100), 'ExitInsertMode')
call feedkeys('a', 'x!')
call assert_equal(1, g:triggered)
au! CursorHoldI
@@ -40,7 +42,7 @@ if has('timers')
let g:triggered = 0
au CursorHoldI * let g:triggered += 1
set updatetime=20
- call timer_start(100, 'ExitInsertMode')
+ call timer_start(LoadAdjust(100), 'ExitInsertMode')
" CursorHoldI does not trigger after CTRL-X
call feedkeys("a\<C-X>", 'x!')
call assert_equal(0, g:triggered)
diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim
index d9a89801ea..78e51ed836 100644
--- a/src/nvim/testdir/test_findfile.vim
+++ b/src/nvim/testdir/test_findfile.vim
@@ -1,25 +1,169 @@
-" Test for findfile()
-"
+" Test findfile() and finddir()
+
+let s:files = [ 'Xdir1/foo',
+ \ 'Xdir1/bar',
+ \ 'Xdir1/Xdir2/foo',
+ \ 'Xdir1/Xdir2/foobar',
+ \ 'Xdir1/Xdir2/Xdir3/bar',
+ \ 'Xdir1/Xdir2/Xdir3/barfoo' ]
+
+func CreateFiles()
+ call mkdir('Xdir1/Xdir2/Xdir3/Xdir2', 'p')
+ for f in s:files
+ call writefile([], f)
+ endfor
+endfunc
+
+func CleanFiles()
+ " Safer to delete each file even if it's more verbose
+ " than doing a recursive delete('Xdir1', 'rf').
+ for f in s:files
+ call delete(f)
+ endfor
+
+ call delete('Xdir1/Xdir2/Xdir3/Xdir2', 'd')
+ call delete('Xdir1/Xdir2/Xdir3', 'd')
+ call delete('Xdir1/Xdir2', 'd')
+ call delete('Xdir1', 'd')
+endfunc
+
+" Test findfile({name} [, {path} [, {count}]])
func Test_findfile()
- new
- let cwd=getcwd()
- cd ..
+ let save_path = &path
+ let save_shellslash = &shellslash
+ let save_dir = getcwd()
+ set shellslash
+ call CreateFiles()
+ cd Xdir1
+ e Xdir2/foo
+
+ " With ,, in path, findfile() searches in current directory.
+ set path=,,
+ call assert_equal('foo', findfile('foo'))
+ call assert_equal('bar', findfile('bar'))
+ call assert_equal('', findfile('foobar'))
+
+ " Directories should not be found (finddir() finds them).
+ call assert_equal('', findfile('Xdir2'))
+
+ " With . in 'path', findfile() searches relatively to current file.
+ set path=.
+ call assert_equal('Xdir2/foo', findfile('foo'))
+ call assert_equal('', findfile('bar'))
+ call assert_equal('Xdir2/foobar', findfile('foobar'))
+
+ " Empty {path} 2nd argument is the same as no 2nd argument.
+ call assert_equal('Xdir2/foo', findfile('foo', ''))
+ call assert_equal('', findfile('bar', ''))
+
+ " Test with *
+ call assert_equal('Xdir2/foo', findfile('foo', '*'))
+ call assert_equal('', findfile('bar', '*'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', '*/*'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', 'Xdir2/*'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', 'Xdir*/Xdir3'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', '*2/*3'))
+
+ " Test with **
+ call assert_equal('bar', findfile('bar', '**'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', '**/Xdir3'))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', 'Xdir2/**'))
+
+ call assert_equal('Xdir2/Xdir3/barfoo', findfile('barfoo', '**2'))
+ call assert_equal('', findfile('barfoo', '**1'))
+ call assert_equal('Xdir2/foobar', findfile('foobar', '**1'))
+
+ " Test with {count} 3rd argument.
+ call assert_equal('bar', findfile('bar', '**', 0))
+ call assert_equal('bar', findfile('bar', '**', 1))
+ call assert_equal('Xdir2/Xdir3/bar', findfile('bar', '**', 2))
+ call assert_equal('', findfile('bar', '**', 3))
+ call assert_equal(['bar', 'Xdir2/Xdir3/bar'], findfile('bar', '**', -1))
+
+ " Test upwards search.
+ cd Xdir2/Xdir3
+ call assert_equal('bar', findfile('bar', ';'))
+ call assert_match('.*/Xdir1/Xdir2/foo', findfile('foo', ';'))
+ call assert_match('.*/Xdir1/Xdir2/foo', findfile('foo', ';', 1))
+ call assert_match('.*/Xdir1/foo', findfile('foo', ';', 2))
+ call assert_match('.*/Xdir1/foo', findfile('foo', ';', 2))
+ call assert_match('.*/Xdir1/Xdir2/foo', findfile('foo', 'Xdir2;', 1))
+ call assert_equal('', findfile('foo', 'Xdir2;', 2))
+
+ " List l should have at least 2 values (possibly more if foo file
+ " happens to be found upwards above Xdir1).
+ let l = findfile('foo', ';', -1)
+ call assert_match('.*/Xdir1/Xdir2/foo', l[0])
+ call assert_match('.*/Xdir1/foo', l[1])
+
+ " Test upwards search with stop-directory.
+ cd Xdir2
+ let l = findfile('bar', ';' . save_dir . '/Xdir1/Xdir2/', -1)
+ call assert_equal(1, len(l))
+ call assert_match('.*/Xdir1/Xdir2/Xdir3/bar', l[0])
+
+ let l = findfile('bar', ';' . save_dir . '/Xdir1/', -1)
+ call assert_equal(2, len(l))
+ call assert_match('.*/Xdir1/Xdir2/Xdir3/bar', l[0])
+ call assert_match('.*/Xdir1/bar', l[1])
+
+ " Test combined downwards and upwards search from Xdir2/.
+ cd ../..
+ call assert_equal('Xdir3/bar', findfile('bar', '**;', 1))
+ call assert_match('.*/Xdir1/bar', findfile('bar', '**;', 2))
+
+ bwipe!
+ exe 'cd ' . save_dir
+ call CleanFiles()
+ let &path = save_path
+ let &shellslash = save_shellslash
+endfunc
+
+" Test finddir({name} [, {path} [, {count}]])
+func Test_finddir()
+ let save_path = &path
+ let save_shellslash = &shellslash
+ let save_dir = getcwd()
+ set path=,,
+ call CreateFiles()
+ cd Xdir1
+
+ call assert_equal('Xdir2', finddir('Xdir2'))
+ call assert_equal('', finddir('Xdir3'))
+
+ " Files should not be found (findfile() finds them).
+ call assert_equal('', finddir('foo'))
+
+ call assert_equal('Xdir2', finddir('Xdir2', '**'))
+ call assert_equal('Xdir2/Xdir3', finddir('Xdir3', '**'))
+
+ call assert_equal('Xdir2', finddir('Xdir2', '**', 1))
+ call assert_equal('Xdir2/Xdir3/Xdir2', finddir('Xdir2', '**', 2))
+ call assert_equal(['Xdir2',
+ \ 'Xdir2/Xdir3/Xdir2'], finddir('Xdir2', '**', -1))
+
+ call assert_equal('Xdir2', finddir('Xdir2', '**1'))
+ call assert_equal('Xdir2', finddir('Xdir2', '**0'))
+ call assert_equal('Xdir2/Xdir3', finddir('Xdir3', '**1'))
+ call assert_equal('', finddir('Xdir3', '**0'))
+
+ " Test upwards dir search.
+ cd Xdir2/Xdir3
+ call assert_match('.*/Xdir1', finddir('Xdir1', ';'))
+
+ " Test upwards search with stop-directory.
+ call assert_match('.*/Xdir1', finddir('Xdir1', ';' . save_dir . '/'))
+ call assert_equal('', finddir('Xdir1', ';' . save_dir . '/Xdir1/'))
- " Tests may be run from a shadow directory, so an extra cd needs to be done to
- " get above src/
- if fnamemodify(getcwd(), ':t') != 'src'
- cd ../..
- else
- cd ..
- endif
- set ssl
-
- call assert_equal('src/nvim/testdir/test_findfile.vim', findfile('test_findfile.vim','src/nvim/test*'))
- exe "cd" cwd
+ " Test combined downwards and upwards dir search from Xdir2/.
cd ..
- call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','test*'))
- call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','testdir'))
+ call assert_match('.*/Xdir1', finddir('Xdir1', '**;', 1))
+ call assert_equal('Xdir3/Xdir2', finddir('Xdir2', '**;', 1))
+ call assert_match('.*/Xdir1/Xdir2', finddir('Xdir2', '**;', 2))
+ call assert_equal('Xdir3', finddir('Xdir3', '**;', 1))
- exe "cd" cwd
- q!
+ exe 'cd ' . save_dir
+ call CleanFiles()
+ let &path = save_path
+ let &shellslash = save_shellslash
endfunc
diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim
index c873487b92..a6494c531c 100644
--- a/src/nvim/testdir/test_help_tagjump.vim
+++ b/src/nvim/testdir/test_help_tagjump.vim
@@ -28,9 +28,9 @@ func Test_help_tagjump()
call assert_true(getline('.') =~ '\*quotestar\*')
helpclose
- help sp?it
+ help ch?ckhealth
call assert_equal("help", &filetype)
- call assert_true(getline('.') =~ '\*'.(has('win32') ? 'split()' : ':split').'\*')
+ call assert_true(getline('.') =~ '\*:checkhealth\*')
helpclose
help :?
diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim
index 6e07c874b4..ada25da4a8 100644
--- a/src/nvim/testdir/test_lambda.vim
+++ b/src/nvim/testdir/test_lambda.vim
@@ -23,6 +23,8 @@ function! Test_lambda_with_timer()
return
endif
+ source load.vim
+
let s:n = 0
let s:timer_id = 0
function! s:Foo()
@@ -31,15 +33,19 @@ function! Test_lambda_with_timer()
endfunction
call s:Foo()
- sleep 210ms
+ sleep 210m
" do not collect lambda
call test_garbagecollect_now()
- let m = s:n
- sleep 230ms
+ let m = LoadAdjust(s:n)
+ sleep 230m
call timer_stop(s:timer_id)
+
+ let n = LoadAdjust(s:n)
+ let nine = LoadAdjust(9)
+
call assert_true(m > 1)
- call assert_true(s:n > m + 1)
- call assert_true(s:n < 9)
+ call assert_true(n > m + 1)
+ call assert_true(n < nine)
endfunction
function! Test_lambda_with_partial()
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index 57ea7ca5a9..4899f59910 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -42,6 +42,38 @@ func Test_listchars()
call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
endfor
+ " tab with 3rd character.
+ set listchars-=tab:>-
+ set listchars+=tab:<=>,trail:-
+ let expected = [
+ \ '<======>aa<====>$',
+ \ '..bb<==>--$',
+ \ '...cccc>-$',
+ \ 'dd........ee--<>$',
+ \ '-$'
+ \ ]
+ redraw!
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+ endfor
+
+ set listchars-=trail:-
+ let expected = [
+ \ '<======>aa<====>$',
+ \ '..bb<==>..$',
+ \ '...cccc>.$',
+ \ 'dd........ee..<>$',
+ \ '.$'
+ \ ]
+ redraw!
+ for i in range(1, 5)
+ call cursor(i, 1)
+ call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$')))
+ endfor
+
+ set listchars-=tab:<=>
+ set listchars+=tab:>-
set listchars+=trail:<
set nolist
normal ggdG
diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim
index af18760065..055d944b15 100644
--- a/src/nvim/testdir/test_menu.vim
+++ b/src/nvim/testdir/test_menu.vim
@@ -1,9 +1,32 @@
" Test that the system menu can be loaded.
+if !has('menu')
+ finish
+endif
+
func Test_load_menu()
try
source $VIMRUNTIME/menu.vim
catch
call assert_report('error while loading menus: ' . v:exception)
endtry
+ call assert_match('browse confirm w', execute(':menu File.Save'))
+ source $VIMRUNTIME/delmenu.vim
+endfunc
+
+func Test_translate_menu()
+ if !has('multi_lang')
+ return
+ endif
+ if !filereadable($VIMRUNTIME . '/lang/menu_de_de.latin1.vim')
+ throw 'Skipped: translated menu not found'
+ endif
+
+ " First delete any English menus.
+ source $VIMRUNTIME/delmenu.vim
+ set langmenu=de_de
+ source $VIMRUNTIME/menu.vim
+ call assert_match('browse confirm w', execute(':menu Datei.Speichern'))
+
+ source $VIMRUNTIME/delmenu.vim
endfunc
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index 9ba264deb6..c2e7bb7bf9 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -238,4 +238,18 @@ func Test_mkview_no_file_name()
%bwipe
endfunc
+func Test_mksession_quote_in_filename()
+ let v:errmsg = ''
+ %bwipe!
+ split another
+ split x'y\"z
+ mksession! Xtest_mks_quoted.out
+ %bwipe!
+ source Xtest_mks_quoted.out
+ call assert_true(bufexists("x'y\"z"))
+
+ %bwipe!
+ call delete('Xtest_mks_quoted.out')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index cdfdd473b9..62ddad5dce 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -5,6 +5,7 @@ if !has('timers')
endif
source shared.vim
+source load.vim
func MyHandler(timer)
let g:val += 1
@@ -14,13 +15,17 @@ func MyHandlerWithLists(lists, timer)
let x = string(a:lists)
endfunc
+func s:assert_inrange(lower, upper, actual)
+ return assert_inrange(a:lower, LoadAdjust(a:upper), a:actual)
+endfunc
+
func Test_oneshot()
let g:val = 0
let timer = timer_start(50, 'MyHandler')
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call assert_inrange(40, 120, slept)
+ call s:assert_inrange(40, 120, slept)
else
call assert_inrange(20, 120, slept)
endif
@@ -32,7 +37,7 @@ func Test_repeat_three()
let slept = WaitFor('g:val == 3')
call assert_equal(3, g:val)
if has('reltime')
- call assert_inrange(120, 250, slept)
+ call s:assert_inrange(120, 250, slept)
else
call assert_inrange(80, 200, slept)
endif
@@ -58,7 +63,7 @@ func Test_with_partial_callback()
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call assert_inrange(40, 130, slept)
+ call s:assert_inrange(40, 130, slept)
else
call assert_inrange(20, 100, slept)
endif
@@ -121,7 +126,7 @@ func Test_paused()
let slept = WaitFor('g:val == 1')
call assert_equal(1, g:val)
if has('reltime')
- call assert_inrange(0, 140, slept)
+ call s:assert_inrange(0, 140, slept)
else
call assert_inrange(0, 10, slept)
endif
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index dbffabd707..0fc153e8ce 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -240,6 +240,8 @@ static void terminfo_start(UI *ui)
const char *vte_version_env = os_getenv("VTE_VERSION");
long vtev = vte_version_env ? strtol(vte_version_env, NULL, 10) : 0;
bool iterm_env = termprg && strstr(termprg, "iTerm.app");
+ bool nsterm = (termprg && strstr(termprg, "Apple_Terminal"))
+ || terminfo_is_term_family(term, "nsterm");
bool konsole = terminfo_is_term_family(term, "konsole")
|| os_getenv("KONSOLE_PROFILE_NAME")
|| os_getenv("KONSOLE_DBUS_SESSION");
@@ -247,8 +249,8 @@ static void terminfo_start(UI *ui)
long konsolev = konsolev_env ? strtol(konsolev_env, NULL, 10)
: (konsole ? 1 : 0);
- patch_terminfo_bugs(data, term, colorterm, vtev, konsolev, iterm_env);
- augment_terminfo(data, term, colorterm, vtev, konsolev, iterm_env);
+ patch_terminfo_bugs(data, term, colorterm, vtev, konsolev, iterm_env, nsterm);
+ augment_terminfo(data, term, colorterm, vtev, konsolev, iterm_env, nsterm);
data->can_change_scroll_region =
!!unibi_get_str(data->ut, unibi_change_scroll_region);
data->can_set_lr_margin =
@@ -1277,7 +1279,7 @@ static void tui_option_set(UI *ui, String name, Object value)
static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
Integer endcol, Integer clearcol, Integer clearattr,
- Boolean wrap, const schar_T *chunk,
+ LineFlags flags, const schar_T *chunk,
const sattr_T *attrs)
{
TUIData *data = ui->data;
@@ -1299,7 +1301,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
(int)clearattr);
}
- if (wrap && ui->width == grid->width && linerow + 1 < grid->height) {
+ if (flags & kLineFlagWrap && ui->width == grid->width
+ && linerow + 1 < grid->height) {
// Only do line wrapping if the grid width is equal to the terminal
// width and the line continuation is within the grid.
@@ -1490,7 +1493,7 @@ static int unibi_find_ext_bool(unibi_term *ut, const char *name)
/// and several terminal emulators falsely announce incorrect terminal types.
static void patch_terminfo_bugs(TUIData *data, const char *term,
const char *colorterm, long vte_version,
- long konsolev, bool iterm_env)
+ long konsolev, bool iterm_env, bool nsterm)
{
unibi_term *ut = data->ut;
const char *xterm_version = os_getenv("XTERM_VERSION");
@@ -1499,7 +1502,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
#endif
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
- || terminfo_is_term_family(term, "nsterm");
+ || nsterm;
bool kitty = terminfo_is_term_family(term, "xterm-kitty");
bool linuxvt = terminfo_is_term_family(term, "linux");
bool bsdvt = terminfo_is_bsd_console(term);
@@ -1518,7 +1521,6 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
bool alacritty = terminfo_is_term_family(term, "alacritty");
// None of the following work over SSH; see :help TERM .
bool iterm_pretending_xterm = xterm && iterm_env;
- bool konsole_pretending_xterm = xterm && konsolev;
bool gnome_pretending_xterm = xterm && colorterm
&& strstr(colorterm, "gnome-terminal");
bool mate_pretending_xterm = xterm && colorterm
@@ -1572,11 +1574,12 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
// claim to be xterm. Or they would mimic xterm properly enough to be
// treatable as xterm.
- // 2017-04 terminfo.src lacks these. genuine Xterm has them, as have
- // the false claimants.
+ // 2017-04 terminfo.src lacks these. Xterm-likes have them.
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;");
unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr");
+ unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
+ unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
if (true_xterm) {
// 2017-04 terminfo.src lacks these. genuine Xterm has them.
@@ -1584,15 +1587,6 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
unibi_set_if_empty(ut, unibi_set_left_margin_parm, "\x1b[%i%p1%ds");
unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds");
}
- if (true_xterm
- || iterm_pretending_xterm
- || gnome_pretending_xterm
- || konsole_pretending_xterm) {
- // Apple's outdated copy of terminfo.src for MacOS lacks these.
- // genuine Xterm and three false claimants have them.
- unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
- unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
- }
} else if (rxvt) {
// 2017-04 terminfo.src lacks these. Unicode rxvt has them.
unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
@@ -1609,11 +1603,12 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
} else if (tmux) {
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_");
unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\");
+ unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m");
+ unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m");
} else if (terminfo_is_term_family(term, "interix")) {
// 2017-04 terminfo.src lacks this.
unibi_set_if_empty(ut, unibi_carriage_return, "\x0d");
} else if (linuxvt) {
- // Apple's outdated copy of terminfo.src for MacOS lacks these.
unibi_set_if_empty(ut, unibi_parm_up_cursor, "\x1b[%p1%dA");
unibi_set_if_empty(ut, unibi_parm_down_cursor, "\x1b[%p1%dB");
unibi_set_if_empty(ut, unibi_parm_right_cursor, "\x1b[%p1%dC");
@@ -1781,12 +1776,12 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
/// capabilities.
static void augment_terminfo(TUIData *data, const char *term,
const char *colorterm, long vte_version,
- long konsolev, bool iterm_env)
+ long konsolev, bool iterm_env, bool nsterm)
{
unibi_term *ut = data->ut;
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
- || terminfo_is_term_family(term, "nsterm");
+ || nsterm;
bool bsdvt = terminfo_is_bsd_console(term);
bool dtterm = terminfo_is_term_family(term, "dtterm");
bool rxvt = terminfo_is_term_family(term, "rxvt");
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 96232ab223..16370f2d10 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -33,6 +33,7 @@
#include "nvim/popupmnu.h"
#include "nvim/screen.h"
#include "nvim/highlight.h"
+#include "nvim/ui_compositor.h"
#include "nvim/window.h"
#include "nvim/cursor_shape.h"
#ifdef FEAT_TUI
@@ -60,11 +61,11 @@ static bool pending_mode_update = false;
static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE;
#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
-# define UI_LOG(funname, ...)
+# define UI_LOG(funname)
#else
static size_t uilog_seen = 0;
static char uilog_last_event[1024] = { 0 };
-# define UI_LOG(funname, ...) \
+# define UI_LOG(funname) \
do { \
if (strequal(uilog_last_event, STR(funname))) { \
uilog_seen++; \
@@ -80,42 +81,34 @@ static char uilog_last_event[1024] = { 0 };
} while (0)
#endif
-// UI_CALL invokes a function on all registered UI instances. The functions can
-// have 0-10 arguments (configurable by SELECT_NTH).
-//
-// See http://stackoverflow.com/a/11172679 for how it works.
-#ifdef _MSC_VER
-# define UI_CALL(funname, ...) \
- do { \
- UI_LOG(funname, 0); \
- for (size_t i = 0; i < ui_count; i++) { \
- UI *ui = uis[i]; \
- UI_CALL_MORE(funname, __VA_ARGS__); \
- } \
- } while (0)
-#else
-# define UI_CALL(...) \
- do { \
- UI_LOG(__VA_ARGS__, 0); \
- for (size_t i = 0; i < ui_count; i++) { \
- UI *ui = uis[i]; \
- UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
+// UI_CALL invokes a function on all registered UI instances.
+// This is called by code generated by generators/gen_api_ui_events.lua
+// C code should use ui_call_{funname} instead.
+# define UI_CALL(cond, funname, ...) \
+ do { \
+ bool any_call = false; \
+ for (size_t i = 0; i < ui_count; i++) { \
+ UI *ui = uis[i]; \
+ if (ui->funname && (cond)) { \
+ ui->funname(__VA_ARGS__); \
+ any_call = true; \
} \
- } while (0)
-#endif
-#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, MORE, \
- MORE, MORE, MORE, MORE, ZERO, ignore)
-#define SELECT_NTH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, ...) a11
-#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
-// Resolves to UI_CALL_MORE or UI_CALL_ZERO.
-#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
-#define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__)
-#define UI_CALL_ZERO(method) if (ui->method) ui->method(ui)
+ } \
+ if (any_call) { \
+ UI_LOG(funname); \
+ } \
+ } while (0)
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui_events_call.generated.h"
#endif
+void ui_init(void)
+{
+ default_grid.handle = 1;
+ ui_comp_init();
+}
+
void ui_builtin_start(void)
{
#ifdef FEAT_TUI
@@ -135,17 +128,12 @@ void ui_builtin_start(void)
#endif
}
-void ui_builtin_stop(void)
-{
- UI_CALL(stop);
-}
-
bool ui_rgb_attached(void)
{
if (!headless_mode && p_tgc) {
return true;
}
- for (size_t i = 0; i < ui_count; i++) {
+ for (size_t i = 1; i < ui_count; i++) {
if (uis[i]->rgb) {
return true;
}
@@ -155,13 +143,13 @@ bool ui_rgb_attached(void)
bool ui_active(void)
{
- return ui_count != 0;
+ return ui_count > 1;
}
void ui_event(char *name, Array args)
{
bool args_consumed = false;
- UI_CALL(event, name, args, &args_consumed);
+ ui_call_event(name, args, &args_consumed);
if (!args_consumed) {
api_free_array(args);
}
@@ -212,6 +200,10 @@ void ui_refresh(void)
screen_resize(width, height);
p_lz = save_p_lz;
+ if (ext_widgets[kUIMessages]) {
+ p_ch = 0;
+ command_height();
+ }
ui_mode_info_set();
pending_mode_update = true;
ui_cursor_shape();
@@ -257,6 +249,9 @@ void ui_attach_impl(UI *ui)
if (ui_count == MAX_UI_COUNT) {
abort();
}
+ if (!ui->ui_ext[kUIMultigrid]) {
+ ui_comp_attach(ui);
+ }
uis[ui_count++] = ui;
ui_refresh_options();
@@ -303,6 +298,10 @@ void ui_detach_impl(UI *ui)
&& !exiting) {
ui_schedule_refresh();
}
+
+ if (!ui->ui_ext[kUIMultigrid]) {
+ ui_comp_detach(ui);
+ }
}
void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
@@ -315,16 +314,25 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
ui->option_set(ui, cstr_as_string((char *)ui_ext_names[ext]),
BOOLEAN_OBJ(active));
}
+ if (ext == kUITermColors) {
+ ui_default_colors_set();
+ }
}
void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol,
int clearattr, bool wrap)
{
+ LineFlags flags = wrap ? kLineFlagWrap : 0;
+ if (startcol == -1) {
+ startcol = 0;
+ flags |= kLineFlagInvalid;
+ }
+
size_t off = grid->line_offset[row] + (size_t)startcol;
- UI_CALL(raw_line, grid->handle, row, startcol, endcol, clearcol, clearattr,
- wrap, (const schar_T *)grid->chars + off,
- (const sattr_T *)grid->attrs + off);
+ ui_call_raw_line(grid->handle, row, startcol, endcol, clearcol, clearattr,
+ flags, (const schar_T *)grid->chars + off,
+ (const sattr_T *)grid->attrs + off);
if (p_wd) { // 'writedelay': flush & delay each time.
int old_row = cursor_row, old_col = cursor_col;
@@ -376,6 +384,8 @@ void ui_flush(void)
{
cmdline_ui_flush();
win_ui_flush();
+ msg_ext_ui_flush();
+
if (pending_cursor_update) {
ui_call_grid_cursor_goto(cursor_grid_handle, cursor_row, cursor_col);
pending_cursor_update = false;
@@ -412,16 +422,16 @@ void ui_cursor_shape(void)
conceal_check_cursor_line();
}
-/// Returns true if `widget` is externalized.
-bool ui_is_external(UIExtension widget)
+/// Returns true if the given UI extension is enabled.
+bool ui_has(UIExtension ext)
{
- return ui_ext[widget];
+ return ui_ext[ext];
}
Array ui_array(void)
{
Array all_uis = ARRAY_DICT_INIT;
- for (size_t i = 0; i < ui_count; i++) {
+ for (size_t i = 1; i < ui_count; i++) {
UI *ui = uis[i];
Dictionary info = ARRAY_DICT_INIT;
PUT(info, "width", INTEGER_OBJ(ui->width));
@@ -452,7 +462,8 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
return;
}
- wp->w_grid.requested_rows = (int)height;
- wp->w_grid.requested_cols = (int)width;
- redraw_win_later(wp, SOME_VALID);
+ // non-positive indicates no request
+ wp->w_height_request = (int)MAX(height, 0);
+ wp->w_width_request = (int)MAX(width, 0);
+ win_set_inner_size(wp);
}
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 16237214cb..490cc930b1 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -14,10 +14,12 @@ typedef enum {
kUIPopupmenu,
kUITabline,
kUIWildmenu,
-#define kUIGlobalCount (kUIWildmenu+1)
+ kUIMessages,
+#define kUIGlobalCount kUILinegrid
kUILinegrid,
kUIMultigrid,
kUIHlState,
+ kUITermColors,
kUIExtCount,
} UIExtension;
@@ -26,16 +28,26 @@ EXTERN const char *ui_ext_names[] INIT(= {
"ext_popupmenu",
"ext_tabline",
"ext_wildmenu",
+ "ext_messages",
"ext_linegrid",
"ext_multigrid",
"ext_hlstate",
+ "ext_termcolors",
});
typedef struct ui_t UI;
+enum {
+ kLineFlagWrap = 1,
+ kLineFlagInvalid = 2,
+};
+
+typedef int LineFlags;
+
struct ui_t {
bool rgb;
+ bool composed;
bool ui_ext[kUIExtCount]; ///< Externalized widgets
int width, height;
void *data;
@@ -44,14 +56,6 @@ struct ui_t {
# include "ui_events.generated.h"
#endif
- // For perfomance and simplicity, we use the dense screen representation
- // in the bridge and the TUI. The remote_ui module will translate this
- // in to the public grid_line format.
- void (*raw_line)(UI *ui, Integer grid, Integer row, Integer startcol,
- Integer endcol, Integer clearcol, Integer clearattr,
- Boolean wrap, const schar_T *chunk, const sattr_T *attrs);
- void (*event)(UI *ui, char *name, Array args, bool *args_consumed);
- void (*stop)(UI *ui);
void (*inspect)(UI *ui, Dictionary *info);
};
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index bd5d37be73..91cd458702 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -153,14 +153,14 @@ static void ui_bridge_raw_line_event(void **argv)
UI *ui = UI(argv[0]);
ui->raw_line(ui, PTR2INT(argv[1]), PTR2INT(argv[2]), PTR2INT(argv[3]),
PTR2INT(argv[4]), PTR2INT(argv[5]), PTR2INT(argv[6]),
- PTR2INT(argv[7]), argv[8], argv[9]);
+ (LineFlags)PTR2INT(argv[7]), argv[8], argv[9]);
xfree(argv[8]);
xfree(argv[9]);
}
static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row,
Integer startcol, Integer endcol,
Integer clearcol, Integer clearattr,
- Boolean wrap, const schar_T *chunk,
+ LineFlags flags, const schar_T *chunk,
const sattr_T *attrs)
{
size_t ncol = (size_t)(endcol-startcol);
@@ -168,7 +168,7 @@ static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row,
sattr_T *hl = xmemdup(attrs, ncol * sizeof(sattr_T));
UI_BRIDGE_CALL(ui, raw_line, 10, ui, INT2PTR(grid), INT2PTR(row),
INT2PTR(startcol), INT2PTR(endcol), INT2PTR(clearcol),
- INT2PTR(clearattr), INT2PTR(wrap), c, hl);
+ INT2PTR(clearattr), INT2PTR(flags), c, hl);
}
static void ui_bridge_suspend(UI *b)
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
new file mode 100644
index 0000000000..c7ba0306e4
--- /dev/null
+++ b/src/nvim/ui_compositor.c
@@ -0,0 +1,442 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// Compositor: merge floating grids with the main grid for display in
+// TUI and non-multigrid UIs.
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include "nvim/lib/kvec.h"
+#include "nvim/log.h"
+#include "nvim/main.h"
+#include "nvim/ascii.h"
+#include "nvim/vim.h"
+#include "nvim/ui.h"
+#include "nvim/highlight.h"
+#include "nvim/memory.h"
+#include "nvim/ui_compositor.h"
+#include "nvim/ugrid.h"
+#include "nvim/screen.h"
+#include "nvim/syntax.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/os/os.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "ui_compositor.c.generated.h"
+#endif
+
+static UI *compositor = NULL;
+static int composed_uis = 0;
+kvec_t(ScreenGrid *) layers = KV_INITIAL_VALUE;
+
+static size_t bufsize = 0;
+static schar_T *linebuf;
+static sattr_T *attrbuf;
+
+#ifndef NDEBUG
+static int chk_width = 0, chk_height = 0;
+#endif
+
+static ScreenGrid *curgrid;
+
+static bool valid_screen = true;
+static bool msg_scroll_mode = false;
+static int msg_first_invalid = 0;
+
+void ui_comp_init(void)
+{
+ if (compositor != NULL) {
+ return;
+ }
+ compositor = xcalloc(1, sizeof(UI));
+
+ compositor->rgb = true;
+ compositor->grid_resize = ui_comp_grid_resize;
+ compositor->grid_clear = ui_comp_grid_clear;
+ compositor->grid_scroll = ui_comp_grid_scroll;
+ compositor->grid_cursor_goto = ui_comp_grid_cursor_goto;
+ compositor->raw_line = ui_comp_raw_line;
+ compositor->win_scroll_over_start = ui_comp_win_scroll_over_start;
+ compositor->win_scroll_over_reset = ui_comp_win_scroll_over_reset;
+
+ // Be unopinionated: will be attached together with a "real" ui anyway
+ compositor->width = INT_MAX;
+ compositor->height = INT_MAX;
+ for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
+ compositor->ui_ext[i] = true;
+ }
+
+ // TODO(bfredl): this will be more complicated if we implement
+ // hlstate per UI (i e reduce hl ids for non-hlstate UIs)
+ compositor->ui_ext[kUIHlState] = false;
+
+ kv_push(layers, &default_grid);
+ curgrid = &default_grid;
+
+ ui_attach_impl(compositor);
+}
+
+
+void ui_comp_attach(UI *ui)
+{
+ composed_uis++;
+ ui->composed = true;
+}
+
+void ui_comp_detach(UI *ui)
+{
+ composed_uis--;
+ if (composed_uis == 0) {
+ xfree(linebuf);
+ xfree(attrbuf);
+ linebuf = NULL;
+ attrbuf = NULL;
+ bufsize = 0;
+ }
+ ui->composed = false;
+}
+
+bool ui_comp_should_draw(void)
+{
+ return composed_uis != 0 && valid_screen;
+}
+
+/// TODO(bfredl): later on the compositor should just use win_float_pos events,
+/// though that will require slight event order adjustment: emit the win_pos
+/// events in the beginning of update_screen(0), rather than in ui_flush()
+bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width)
+{
+ if (grid->comp_index != 0) {
+ bool moved = (row != grid->comp_row) || (col != grid->comp_col);
+ if (ui_comp_should_draw()) {
+ // Redraw the area covered by the old position, and is not covered
+ // by the new position. Disable the grid so that compose_area() will not
+ // use it.
+ grid->comp_disabled = true;
+ compose_area(grid->comp_row, row,
+ grid->comp_col, grid->comp_col + grid->Columns);
+ if (grid->comp_col < col) {
+ compose_area(MAX(row, grid->comp_row),
+ MIN(row+height, grid->comp_row+grid->Rows),
+ grid->comp_col, col);
+ }
+ if (col+width < grid->comp_col+grid->Columns) {
+ compose_area(MAX(row, grid->comp_row),
+ MIN(row+height, grid->comp_row+grid->Rows),
+ col+width, grid->comp_col+grid->Columns);
+ }
+ compose_area(row+height, grid->comp_row+grid->Rows,
+ grid->comp_col, grid->comp_col + grid->Columns);
+ grid->comp_disabled = false;
+ }
+ grid->comp_row = row;
+ grid->comp_col = col;
+ return moved;
+ }
+#ifndef NDEBUG
+ for (size_t i = 0; i < kv_size(layers); i++) {
+ if (kv_A(layers, i) == grid) {
+ assert(false);
+ }
+ }
+#endif
+ // not found: new grid
+ kv_push(layers, grid);
+ grid->comp_row = row;
+ grid->comp_col = col;
+ grid->comp_index = kv_size(layers)-1;
+ return true;
+}
+
+void ui_comp_remove_grid(ScreenGrid *grid)
+{
+ assert(grid != &default_grid);
+ if (grid->comp_index == 0) {
+ // grid wasn't present
+ return;
+ }
+
+ if (curgrid == grid) {
+ curgrid = &default_grid;
+ }
+
+ for (size_t i = grid->comp_index; i < kv_size(layers)-1; i++) {
+ kv_A(layers, i) = kv_A(layers, i+1);
+ kv_A(layers, i)->comp_index = i;
+ }
+ (void)kv_pop(layers);
+ grid->comp_index = 0;
+
+ // recompose the area under the grid
+ // inefficent when being overlapped: only draw up to grid->comp_index
+ ui_comp_compose_grid(grid);
+}
+
+bool ui_comp_set_grid(handle_T handle)
+{
+ if (curgrid->handle == handle) {
+ return true;
+ }
+ ScreenGrid *grid = NULL;
+ for (size_t i = 0; i < kv_size(layers); i++) {
+ if (kv_A(layers, i)->handle == handle) {
+ grid = kv_A(layers, i);
+ break;
+ }
+ }
+ if (grid != NULL) {
+ curgrid = grid;
+ return true;
+ }
+ return false;
+}
+
+static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle,
+ Integer r, Integer c)
+{
+ if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid_handle)) {
+ return;
+ }
+ int cursor_row = curgrid->comp_row+(int)r;
+ int cursor_col = curgrid->comp_col+(int)c;
+
+ if (cursor_col >= default_grid.Columns || cursor_row >= default_grid.Rows) {
+ // TODO(bfredl): this happens with 'writedelay', refactor?
+ // abort();
+ return;
+ }
+ ui_composed_call_grid_cursor_goto(1, cursor_row, cursor_col);
+}
+
+
+/// Baseline implementation. This is always correct, but we can sometimes
+/// do something more efficient (where efficiency means smaller deltas to
+/// the downstream UI.)
+static void compose_line(Integer row, Integer startcol, Integer endcol,
+ LineFlags flags)
+{
+ // in case we start on the right half of a double-width char, we need to
+ // check the left half. But skip it in output if it wasn't doublewidth.
+ int skip = 0;
+ if (startcol > 0 && (flags & kLineFlagInvalid)) {
+ startcol--;
+ skip = 1;
+ }
+
+ int col = (int)startcol;
+ ScreenGrid *grid = NULL;
+ schar_T *bg_line = &default_grid.chars[default_grid.line_offset[row]
+ +(size_t)startcol];
+ sattr_T *bg_attrs = &default_grid.attrs[default_grid.line_offset[row]
+ +(size_t)startcol];
+
+ while (col < endcol) {
+ int until = 0;
+ for (size_t i = 0; i < kv_size(layers); i++) {
+ ScreenGrid *g = kv_A(layers, i);
+ if (g->comp_row > row || row >= g->comp_row + g->Rows
+ || g->comp_disabled) {
+ continue;
+ }
+ if (g->comp_col <= col && col < g->comp_col+g->Columns) {
+ grid = g;
+ until = g->comp_col+g->Columns;
+ } else if (g->comp_col > col) {
+ until = MIN(until, g->comp_col);
+ }
+ }
+ until = MIN(until, (int)endcol);
+
+ assert(grid != NULL);
+ assert(until > col);
+ assert(until <= default_grid.Columns);
+ size_t n = (size_t)(until-col);
+ size_t off = grid->line_offset[row-grid->comp_row]
+ + (size_t)(col-grid->comp_col);
+ memcpy(linebuf+(col-startcol), grid->chars+off, n * sizeof(*linebuf));
+ memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf));
+
+ // 'pumblend'
+ if (grid != &default_grid && p_pb) {
+ for (int i = col-(int)startcol; i < until-startcol; i++) {
+ bool thru = strequal((char *)linebuf[i], " "); // negative space
+ attrbuf[i] = (sattr_T)hl_blend_attrs(bg_attrs[i], attrbuf[i], thru);
+ if (thru) {
+ memcpy(linebuf[i], bg_line[i], sizeof(linebuf[i]));
+ }
+ }
+ }
+
+ // Tricky: if overlap caused a doublewidth char to get cut-off, must
+ // replace the visible half with a space.
+ if (linebuf[col-startcol][0] == NUL) {
+ linebuf[col-startcol][0] = ' ';
+ linebuf[col-startcol][1] = NUL;
+ } else if (n > 1 && linebuf[col-startcol+1][0] == NUL) {
+ skip = 0;
+ }
+ if (grid->comp_col+grid->Columns > until
+ && grid->chars[off+n][0] == NUL) {
+ linebuf[until-1-startcol][0] = ' ';
+ linebuf[until-1-startcol][1] = '\0';
+ if (col == startcol && n == 1) {
+ skip = 0;
+ }
+ }
+
+ col = until;
+ }
+ assert(endcol <= chk_width);
+ assert(row < chk_height);
+
+ if (!(grid && grid == &default_grid)) {
+ // TODO(bfredl): too conservative, need check
+ // grid->line_wraps if grid->Width == Width
+ flags = flags & ~kLineFlagWrap;
+ }
+
+ ui_composed_call_raw_line(1, row, startcol+skip, endcol, endcol, 0, flags,
+ (const schar_T *)linebuf+skip,
+ (const sattr_T *)attrbuf+skip);
+}
+
+static void compose_area(Integer startrow, Integer endrow,
+ Integer startcol, Integer endcol)
+{
+ endrow = MIN(endrow, default_grid.Rows);
+ endcol = MIN(endcol, default_grid.Columns);
+ if (endcol <= startcol) {
+ return;
+ }
+ for (int r = (int)startrow; r < endrow; r++) {
+ compose_line(r, startcol, endcol, kLineFlagInvalid);
+ }
+}
+
+/// compose the area under the grid.
+///
+/// This is needed when some option affecting composition is changed,
+/// such as 'pumblend' for popupmenu grid.
+void ui_comp_compose_grid(ScreenGrid *grid)
+{
+ if (ui_comp_should_draw()) {
+ compose_area(grid->comp_row, grid->comp_row+grid->Rows,
+ grid->comp_col, grid->comp_col+grid->Columns);
+ }
+}
+
+static void ui_comp_raw_line(UI *ui, Integer grid, Integer row,
+ Integer startcol, Integer endcol,
+ Integer clearcol, Integer clearattr,
+ LineFlags flags, const schar_T *chunk,
+ const sattr_T *attrs)
+{
+ if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid)) {
+ return;
+ }
+
+ row += curgrid->comp_row;
+ startcol += curgrid->comp_col;
+ endcol += curgrid->comp_col;
+ clearcol += curgrid->comp_col;
+ if (curgrid != &default_grid) {
+ flags = flags & ~kLineFlagWrap;
+ }
+ assert(row < default_grid.Rows);
+ assert(clearcol <= default_grid.Columns);
+ if (flags & kLineFlagInvalid
+ || kv_size(layers) > (p_pb ? 1 : curgrid->comp_index+1)) {
+ compose_line(row, startcol, clearcol, flags);
+ } else {
+ ui_composed_call_raw_line(1, row, startcol, endcol, clearcol, clearattr,
+ flags, chunk, attrs);
+ }
+}
+
+/// The screen is invalid and will soon be cleared
+///
+/// Don't redraw floats until screen is cleared
+void ui_comp_invalidate_screen(void)
+{
+ valid_screen = false;
+}
+
+static void ui_comp_grid_clear(UI *ui, Integer grid)
+{
+ // By design, only first grid uses clearing.
+ assert(grid == 1);
+ ui_composed_call_grid_clear(1);
+ valid_screen = true;
+}
+
+// TODO(bfredl): These events are somewhat of a hack. multiline messages
+// should later on be a separate grid, then this would just be ordinary
+// ui_comp_put_grid and ui_comp_remove_grid calls.
+static void ui_comp_win_scroll_over_start(UI *ui)
+{
+ msg_scroll_mode = true;
+ msg_first_invalid = ui->height;
+}
+
+static void ui_comp_win_scroll_over_reset(UI *ui)
+{
+ msg_scroll_mode = false;
+ for (size_t i = 1; i < kv_size(layers); i++) {
+ ScreenGrid *grid = kv_A(layers, i);
+ if (grid->comp_row+grid->Rows > msg_first_invalid) {
+ compose_area(msg_first_invalid, grid->comp_row+grid->Rows,
+ grid->comp_col, grid->comp_col+grid->Columns);
+ }
+ }
+}
+
+static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top,
+ Integer bot, Integer left, Integer right,
+ Integer rows, Integer cols)
+{
+ if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid)) {
+ return;
+ }
+ top += curgrid->comp_row;
+ bot += curgrid->comp_row;
+ left += curgrid->comp_col;
+ right += curgrid->comp_col;
+ if (!msg_scroll_mode && kv_size(layers) > curgrid->comp_index+1) {
+ // TODO(bfredl):
+ // 1. check if rectangles actually overlap
+ // 2. calulate subareas that can scroll.
+ if (rows > 0) {
+ bot -= rows;
+ } else {
+ top += (-rows);
+ }
+ compose_area(top, bot, left, right);
+ } else {
+ msg_first_invalid = MIN(msg_first_invalid, (int)top);
+ ui_composed_call_grid_scroll(1, top, bot, left, right, rows, cols);
+ }
+}
+
+static void ui_comp_grid_resize(UI *ui, Integer grid,
+ Integer width, Integer height)
+{
+ if (grid == 1) {
+ ui_composed_call_grid_resize(1, width, height);
+#ifndef NDEBUG
+ chk_width = (int)width;
+ chk_height = (int)height;
+#endif
+ size_t new_bufsize = (size_t)width;
+ if (bufsize != new_bufsize) {
+ xfree(linebuf);
+ xfree(attrbuf);
+ linebuf = xmalloc(new_bufsize * sizeof(*linebuf));
+ attrbuf = xmalloc(new_bufsize * sizeof(*attrbuf));
+ bufsize = new_bufsize;
+ }
+ }
+}
+
diff --git a/src/nvim/ui_compositor.h b/src/nvim/ui_compositor.h
new file mode 100644
index 0000000000..b3780db532
--- /dev/null
+++ b/src/nvim/ui_compositor.h
@@ -0,0 +1,12 @@
+// Bridge for communication between a UI thread and nvim core.
+// Used by the built-in TUI and libnvim-based UIs.
+#ifndef NVIM_UI_COMPOSITOR_H
+#define NVIM_UI_COMPOSITOR_H
+
+#include "nvim/ui.h"
+#include "nvim/event/defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "ui_compositor.h.generated.h"
+#endif
+#endif // NVIM_UI_COMPOSITOR_H
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 20b71ab724..b7c9140b7f 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -62,12 +62,6 @@ static char *features[] = {
"-iconv",
#endif
-#ifdef HAVE_JEMALLOC
-"+jemalloc",
-#else
-"-jemalloc",
-#endif
-
#ifdef FEAT_TUI
"+tui",
#else
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 3cadfe612a..24fe529fd6 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -1300,6 +1300,7 @@ static void win_rotate(int upwards, int count)
if (upwards) { /* first window becomes last window */
/* remove first window/frame from the list */
frp = curwin->w_frame->fr_parent->fr_child;
+ assert(frp != NULL);
wp1 = frp->fr_win;
win_remove(wp1, NULL);
frame_remove(frp);
@@ -3025,8 +3026,10 @@ static void new_frame(win_T *wp)
void win_init_size(void)
{
firstwin->w_height = ROWS_AVAIL;
+ firstwin->w_height_inner = firstwin->w_height;
topframe->fr_height = ROWS_AVAIL;
firstwin->w_width = Columns;
+ firstwin->w_width_inner = firstwin->w_width;
topframe->fr_width = Columns;
}
@@ -3118,7 +3121,7 @@ int win_new_tabpage(int after, char_u *filename)
redraw_all_later(NOT_VALID);
- if (ui_is_external(kUIMultigrid)) {
+ if (ui_has(kUIMultigrid)) {
tabpage_check_windows(tp);
}
@@ -3319,7 +3322,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au
lastwin = tp->tp_lastwin;
topframe = tp->tp_topframe;
- if (old_curtab != curtab && ui_is_external(kUIMultigrid)) {
+ if (old_curtab != curtab && ui_has(kUIMultigrid)) {
tabpage_check_windows(old_curtab);
}
@@ -3889,7 +3892,6 @@ static win_T *win_alloc(win_T *after, int hidden)
// allocate window structure and linesizes arrays
win_T *new_wp = xcalloc(1, sizeof(win_T));
- win_alloc_lines(new_wp);
new_wp->handle = ++last_win_id;
handle_register_window(new_wp);
@@ -3970,7 +3972,7 @@ win_free (
}
}
- win_free_lsize(wp);
+ xfree(wp->w_lines);
for (i = 0; i < wp->w_tagstacklen; ++i)
xfree(wp->w_tagstack[i].tagname);
@@ -4010,7 +4012,7 @@ win_free (
void win_free_grid(win_T *wp, bool reinit)
{
- if (wp->w_grid.handle != 0 && ui_is_external(kUIMultigrid)) {
+ if (wp->w_grid.handle != 0 && ui_has(kUIMultigrid)) {
ui_call_grid_destroy(wp->w_grid.handle);
wp->w_grid.handle = 0;
}
@@ -4118,28 +4120,6 @@ static void frame_remove(frame_T *frp)
/*
- * Allocate w_lines[] for window "wp".
- */
-void win_alloc_lines(win_T *wp)
-{
- wp->w_lines_valid = 0;
- assert(wp->w_grid.Rows >= 0);
- wp->w_lines = xcalloc(MAX(wp->w_grid.Rows + 1, Rows), sizeof(wline_T));
-}
-
-/*
- * free lsize arrays for a window
- */
-void win_free_lsize(win_T *wp)
-{
- // TODO: why would wp be NULL here?
- if (wp != NULL) {
- xfree(wp->w_lines);
- wp->w_lines = NULL;
- }
-}
-
-/*
* Called from win_new_shellsize() after Rows changed.
* This only does the current tab page, others must be done when made active.
*/
@@ -4876,9 +4856,9 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
// Has no effect when the window is less than two lines.
void set_fraction(win_T *wp)
{
- if (wp->w_height > 1) {
- wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height / 2)
- / (long)wp->w_height;
+ if (wp->w_height_inner > 1) {
+ wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height_inner / 2)
+ / (long)wp->w_height_inner;
}
}
@@ -4889,46 +4869,25 @@ void set_fraction(win_T *wp)
*/
void win_new_height(win_T *wp, int height)
{
- int prev_height = wp->w_height;
-
- /* Don't want a negative height. Happens when splitting a tiny window.
- * Will equalize heights soon to fix it. */
- if (height < 0)
+ // Don't want a negative height. Happens when splitting a tiny window.
+ // Will equalize heights soon to fix it.
+ if (height < 0) {
height = 0;
- if (wp->w_height == height)
- return; /* nothing to do */
-
- if (wp->w_height > 0) {
- if (wp == curwin) {
- // w_wrow needs to be valid. When setting 'laststatus' this may
- // call win_new_height() recursively.
- validate_cursor();
- }
- if (wp->w_height != prev_height) { // -V547
- return; // Recursive call already changed the size, bail out.
- }
- if (wp->w_wrow != wp->w_prev_fraction_row) {
- set_fraction(wp);
- }
}
-
- wp->w_height = height;
- wp->w_skipcol = 0;
-
- // There is no point in adjusting the scroll position when exiting. Some
- // values might be invalid.
- if (!exiting) {
- scroll_to_fraction(wp, prev_height);
+ if (wp->w_height == height) {
+ return; // nothing to do
}
+ wp->w_height = height;
wp->w_pos_changed = true;
+ win_set_inner_size(wp);
}
void scroll_to_fraction(win_T *wp, int prev_height)
{
linenr_T lnum;
int sline, line_size;
- int height = wp->w_height;
+ int height = wp->w_height_inner;
/* Don't change w_topline when height is zero. Don't set w_topline when
* 'scrollbind' is set and this isn't the current window. */
@@ -4951,8 +4910,8 @@ void scroll_to_fraction(win_T *wp, int prev_height)
// Make sure the whole cursor line is visible, if possible.
const int rows = plines_win(wp, lnum, false);
- if (sline > wp->w_height - rows) {
- sline = wp->w_height - rows;
+ if (sline > wp->w_height_inner - rows) {
+ sline = wp->w_height_inner - rows;
wp->w_wrow -= rows - line_size;
}
}
@@ -4964,14 +4923,14 @@ void scroll_to_fraction(win_T *wp, int prev_height)
* room use w_skipcol;
*/
wp->w_wrow = line_size;
- if (wp->w_wrow >= wp->w_height
- && (wp->w_width - win_col_off(wp)) > 0) {
- wp->w_skipcol += wp->w_width - win_col_off(wp);
- --wp->w_wrow;
- while (wp->w_wrow >= wp->w_height) {
- wp->w_skipcol += wp->w_width - win_col_off(wp)
+ if (wp->w_wrow >= wp->w_height_inner
+ && (wp->w_width_inner - win_col_off(wp)) > 0) {
+ wp->w_skipcol += wp->w_width_inner - win_col_off(wp);
+ wp->w_wrow--;
+ while (wp->w_wrow >= wp->w_height_inner) {
+ wp->w_skipcol += wp->w_width_inner - win_col_off(wp)
+ win_col_off2(wp);
- --wp->w_wrow;
+ wp->w_wrow--;
}
}
set_topline(wp, lnum);
@@ -5024,10 +4983,58 @@ void scroll_to_fraction(win_T *wp, int prev_height)
redraw_win_later(wp, SOME_VALID);
wp->w_redr_status = TRUE;
invalidate_botline_win(wp);
+}
+
+void win_set_inner_size(win_T *wp)
+{
+ int width = wp->w_width_request;
+ if (width == 0) {
+ width = wp->w_width;
+ }
+
+ int prev_height = wp->w_height_inner;
+ int height = wp->w_height_request;
+ if (height == 0) {
+ height = wp->w_height;
+ }
+
+ if (height != prev_height) {
+ if (height > 0) {
+ if (wp == curwin) {
+ // w_wrow needs to be valid. When setting 'laststatus' this may
+ // call win_new_height() recursively.
+ validate_cursor();
+ }
+ if (wp->w_height_inner != prev_height) { // -V547
+ return; // Recursive call already changed the size, bail out.
+ }
+ if (wp->w_wrow != wp->w_prev_fraction_row) {
+ set_fraction(wp);
+ }
+ }
+ wp->w_height_inner = height;
+ wp->w_skipcol = 0;
+
+ // There is no point in adjusting the scroll position when exiting. Some
+ // values might be invalid.
+ if (!exiting) {
+ scroll_to_fraction(wp, prev_height);
+ }
+ }
+
+ if (width != wp->w_width_inner) {
+ wp->w_width_inner = width;
+ wp->w_lines_valid = 0;
+ changed_line_abv_curs_win(wp);
+ invalidate_botline_win(wp);
+ if (wp == curwin) {
+ update_topline();
+ curs_columns(true); // validate w_wrow
+ }
+ }
if (wp->w_buffer->terminal) {
- terminal_resize(wp->w_buffer->terminal, 0, wp->w_height);
- redraw_win_later(wp, NOT_VALID);
+ terminal_check_size(wp->w_buffer->terminal);
}
}
@@ -5035,23 +5042,11 @@ void scroll_to_fraction(win_T *wp, int prev_height)
void win_new_width(win_T *wp, int width)
{
wp->w_width = width;
- wp->w_lines_valid = 0;
- changed_line_abv_curs_win(wp);
- invalidate_botline_win(wp);
- if (wp == curwin) {
- update_topline();
- curs_columns(TRUE); /* validate w_wrow */
- }
+ win_set_inner_size(wp);
+
redraw_win_later(wp, NOT_VALID);
wp->w_redr_status = TRUE;
- if (wp->w_buffer->terminal) {
- if (wp->w_height != 0) {
- terminal_resize(wp->w_buffer->terminal,
- (uint16_t)(MAX(0, wp->w_width - win_col_off(wp))),
- 0);
- }
- }
wp->w_pos_changed = true;
}
@@ -5355,7 +5350,7 @@ static void last_status_rec(frame_T *fr, int statusline)
*/
int tabline_height(void)
{
- if (ui_is_external(kUITabline)) {
+ if (ui_has(kUITabline)) {
return 0;
}
assert(first_tabpage);
@@ -6092,7 +6087,7 @@ void win_findbuf(typval_T *argvars, list_T *list)
void win_ui_flush(void)
{
- if (!ui_is_external(kUIMultigrid)) {
+ if (!ui_has(kUIMultigrid)) {
return;
}
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 0e06e48a35..b10076c6da 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -318,6 +318,10 @@ describe('API', function()
eq({false, 'Error executing lua: [string "<nvim>"]:1: '..
"attempt to call global 'bork' (a nil value)"},
meth_pcall(meths.execute_lua, 'bork()', {}))
+
+ eq({false, 'Error executing lua: [string "<nvim>"]:1: '..
+ "did\nthe\nfail"},
+ meth_pcall(meths.execute_lua, 'error("did\\nthe\\nfail")', {}))
end)
end)
@@ -1273,7 +1277,9 @@ describe('API', function()
ext_wildmenu = false,
ext_linegrid = screen._options.ext_linegrid or false,
ext_multigrid = false,
- ext_hlstate=false,
+ ext_hlstate = false,
+ ext_termcolors = false,
+ ext_messages = false,
height = 4,
rgb = true,
width = 20,
@@ -1303,4 +1309,84 @@ describe('API', function()
eq({["ns-1"]=1, ["ns-2"]=2}, meths.get_namespaces())
end)
end)
+
+ describe('nvim_create_buf', function()
+ it('works', function()
+ eq({id=2}, meths.create_buf(true, false))
+ eq({id=3}, meths.create_buf(false, false))
+ eq(' 1 %a "[No Name]" line 1\n'..
+ ' 2 "[No Name]" line 0',
+ meths.command_output("ls"))
+ -- current buffer didn't change
+ eq({id=1}, meths.get_current_buf())
+
+ local screen = Screen.new(20, 4)
+ screen:attach()
+ meths.buf_set_lines(2, 0, -1, true, {"some text"})
+ meths.set_current_buf(2)
+ screen:expect([[
+ ^some text |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], {
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ })
+ end)
+
+ it('can change buftype before visiting', function()
+ meths.set_option("hidden", false)
+ eq({id=2}, meths.create_buf(true, false))
+ meths.buf_set_option(2, "buftype", "nofile")
+ meths.buf_set_lines(2, 0, -1, true, {"test text"})
+ command("split | buffer 2")
+ eq({id=2}, meths.get_current_buf())
+ -- if the buf_set_option("buftype") didn't work, this would error out.
+ command("close")
+ eq({id=1}, meths.get_current_buf())
+ end)
+
+ it('|scratch-buffer|', function()
+ eq({id=2}, meths.create_buf(false, true))
+ eq({id=3}, meths.create_buf(true, true))
+ eq({id=4}, meths.create_buf(true, true))
+ local scratch_bufs = { 2, 3, 4 }
+ eq(' 1 %a "[No Name]" line 1\n'..
+ ' 3 "[Scratch]" line 0\n'..
+ ' 4 "[Scratch]" line 0',
+ meths.command_output("ls"))
+ -- current buffer didn't change
+ eq({id=1}, meths.get_current_buf())
+
+ local screen = Screen.new(20, 4)
+ screen:attach()
+
+ --
+ -- Editing a scratch-buffer does NOT change its properties.
+ --
+ local edited_buf = 2
+ meths.buf_set_lines(edited_buf, 0, -1, true, {"some text"})
+ for _,b in ipairs(scratch_bufs) do
+ eq('nofile', meths.buf_get_option(b, 'buftype'))
+ eq('hide', meths.buf_get_option(b, 'bufhidden'))
+ eq(false, meths.buf_get_option(b, 'swapfile'))
+ end
+
+ --
+ -- Visiting a scratch-buffer DOES change its properties.
+ --
+ meths.set_current_buf(edited_buf)
+ screen:expect([[
+ ^some text |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], {
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ })
+ eq('', meths.buf_get_option(edited_buf, 'buftype'))
+ eq('', meths.buf_get_option(edited_buf, 'bufhidden'))
+ eq(false, meths.buf_get_option(edited_buf, 'swapfile'))
+ end)
+ end)
end)
diff --git a/test/functional/autocmd/signal_spec.lua b/test/functional/autocmd/signal_spec.lua
new file mode 100644
index 0000000000..719adeaf1b
--- /dev/null
+++ b/test/functional/autocmd/signal_spec.lua
@@ -0,0 +1,38 @@
+local helpers = require('test.functional.helpers')(after_each)
+
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local funcs = helpers.funcs
+local next_msg = helpers.next_msg
+
+if helpers.pending_win32(pending) then
+ -- Only applies to POSIX systems.
+ return
+end
+
+local function posix_kill(signame, pid)
+ os.execute('kill -s '..signame..' -- '..pid..' >/dev/null')
+end
+
+describe('autocmd Signal', function()
+ before_each(clear)
+
+ it('matches *', function()
+ command('autocmd Signal * call rpcnotify(1, "foo")')
+ posix_kill('USR1', funcs.getpid())
+ eq({'notification', 'foo', {}}, next_msg())
+ end)
+
+ it('matches SIGUSR1', function()
+ command('autocmd Signal SIGUSR1 call rpcnotify(1, "foo")')
+ posix_kill('USR1', funcs.getpid())
+ eq({'notification', 'foo', {}}, next_msg())
+ end)
+
+ it('does not match unknown patterns', function()
+ command('autocmd Signal SIGUSR2 call rpcnotify(1, "foo")')
+ posix_kill('USR1', funcs.getpid())
+ eq(nil, next_msg(500))
+ end)
+end)
diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua
index 124b9625c3..14f3c29fb7 100644
--- a/test/functional/eval/timer_spec.lua
+++ b/test/functional/eval/timer_spec.lua
@@ -4,6 +4,7 @@ local feed, eq, eval = helpers.feed, helpers.eq, helpers.eval
local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run
local clear, command, funcs = helpers.clear, helpers.command, helpers.funcs
local curbufmeths = helpers.curbufmeths
+local load_adjust = helpers.load_adjust
describe('timers', function()
before_each(function()
@@ -19,14 +20,14 @@ describe('timers', function()
it('works one-shot', function()
command("call timer_start(50, 'MyHandler')")
eq(0,eval("g:val"))
- run(nil, nil, nil, 200)
+ run(nil, nil, nil, load_adjust(200))
eq(1,eval("g:val"))
end)
it('works one-shot when repeat=0', function()
command("call timer_start(50, 'MyHandler', {'repeat': 0})")
eq(0,eval("g:val"))
- run(nil, nil, nil, 200)
+ run(nil, nil, nil, load_adjust(200))
eq(1,eval("g:val"))
end)
@@ -34,7 +35,7 @@ describe('timers', function()
it('works with repeat two', function()
command("call timer_start(50, 'MyHandler', {'repeat': 2})")
eq(0,eval("g:val"))
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, load_adjust(300))
eq(2,eval("g:val"))
end)
@@ -42,14 +43,14 @@ describe('timers', function()
command("call timer_start(50, 'MyHandler', {'repeat': 2})")
nvim_async("command", "sleep 10")
eq(0,eval("g:val"))
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, load_adjust(300))
eq(2,eval("g:val"))
end)
it('works with zero timeout', function()
-- timer_start does still not invoke the callback immediately
eq(0,eval("[timer_start(0, 'MyHandler', {'repeat': 1000}), g:val][1]"))
- run(nil, nil, nil, 400)
+ run(nil, nil, nil, load_adjust(400))
eq(1000,eval("g:val"))
end)
@@ -58,18 +59,18 @@ describe('timers', function()
-- this also tests that remote requests works during sleep
eval("timer_start(50, 'MyHandler', {'repeat': 2})")
eq(0,eval("g:val"))
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, load_adjust(300))
eq(2,eval("g:val"))
end)
it('are paused when event processing is disabled', function()
command("call timer_start(50, 'MyHandler', {'repeat': -1})")
- run(nil, nil, nil, 100)
+ run(nil, nil, nil, load_adjust(100))
local count = eval("g:val")
-- shows two line error message and thus invokes the return prompt.
-- if we start to allow event processing here, we need to change this test.
feed(':throw "fatal error"<CR>')
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, load_adjust(300))
feed("<cr>")
local diff = eval("g:val") - count
assert(0 <= diff and diff <= 4,
@@ -79,7 +80,7 @@ describe('timers', function()
it('are triggered in blocking getchar() call', function()
command("call timer_start(50, 'MyHandler', {'repeat': -1})")
nvim_async("command", "let g:c = getchar()")
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, load_adjust(300))
feed("c")
local count = eval("g:val")
assert(count >= 3, 'expected count >= 3, got: '..tostring(count))
@@ -137,15 +138,16 @@ describe('timers', function()
it('can be stopped', function()
local t = eval("timer_start(50, 'MyHandler', {'repeat': -1})")
eq(0,eval("g:val"))
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, load_adjust(300))
funcs.timer_stop(t)
local count = eval("g:val")
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, load_adjust(300))
local count2 = eval("g:val")
-- when count is eval:ed after timer_stop this should be non-racy
eq(count, count2)
- assert(3 <= count and count <= 7,
- 'expected (3 <= count <= 7), got: '..tostring(count))
+ assert((3 <= count and count <= load_adjust(7)),
+ string.format('expected (3 <= count <= %s), got: %s',
+ load_adjust(7), tostring(count)))
end)
it('can be stopped from the handler', function()
@@ -161,7 +163,7 @@ describe('timers', function()
]])
command("call timer_start(50, 'MyHandler', {'repeat': -1})")
eq(0,eval("g:val"))
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, load_adjust(300))
eq(3,eval("g:val"))
end)
@@ -174,7 +176,7 @@ describe('timers', function()
]])
command("call timer_start(20, 'MyHandler', {'repeat': 3})")
command("call timer_start(40, 'MyHandler2', {'repeat': 2})")
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, load_adjust(300))
eq(3,eval("g:val"))
eq(2,eval("g:val2"))
end)
@@ -189,7 +191,7 @@ describe('timers', function()
endfunc
]])
command("call timer_start(5, 'MyHandler', {'repeat': 1})")
- run(nil, nil, nil, 300)
+ run(nil, nil, nil, load_adjust(300))
eq(1,eval("g:val"))
end)
diff --git a/test/functional/ex_cmds/menu_spec.lua b/test/functional/ex_cmds/menu_spec.lua
index 2c0535acda..2309e949c0 100644
--- a/test/functional/ex_cmds/menu_spec.lua
+++ b/test/functional/ex_cmds/menu_spec.lua
@@ -63,25 +63,27 @@ describe('menu_get', function()
before_each(function()
clear()
- command('nnoremenu &Test.Test inormal<ESC>')
- command('inoremenu Test.Test insert')
- command('vnoremenu Test.Test x')
- command('cnoremenu Test.Test cmdmode')
- command('menu Test.Nested.test level1')
- command('menu Test.Nested.Nested2 level2')
+ command([=[
+ nnoremenu &Test.Test inormal<ESC>
+ inoremenu Test.Test insert
+ vnoremenu Test.Test x
+ cnoremenu Test.Test cmdmode
+ menu Test.Nested.test level1
+ menu Test.Nested.Nested2 level2
- command('nnoremenu <script> Export.Script p')
- command('tmenu Export.Script This is the tooltip')
- command('menu ]Export.hidden thisoneshouldbehidden')
+ nnoremenu <script> Export.Script p
+ tmenu Export.Script This is the tooltip
+ menu ]Export.hidden thisoneshouldbehidden
- command('nnoremenu Edit.Paste p')
- command('cnoremenu Edit.Paste <C-R>"')
+ nnoremenu Edit.Paste p
+ cnoremenu Edit.Paste <C-R>"
+ ]=])
end)
it("path='', modes='a'", function()
local m = funcs.menu_get("","a");
-- HINT: To print the expected table and regenerate the tests:
- -- print(require('pl.pretty').dump(m))
+ -- print(require('inspect')(m))
local expected = {
{
shortcut = "T",
@@ -306,10 +308,13 @@ describe('menu_get', function()
eq(expected, m)
end)
- it('matching path, default modes', function()
+ it('matching path, all modes', function()
local m = funcs.menu_get("Export", "a")
- local expected = {
- {
+ local expected = { {
+ hidden = 0,
+ name = "Export",
+ priority = 500,
+ submenus = { {
tooltip = "This is the tooltip",
hidden = 0,
name = "Script",
@@ -323,8 +328,8 @@ describe('menu_get', function()
silent = 0
}
}
- }
- }
+ } }
+ } }
eq(expected, m)
end)
@@ -349,8 +354,6 @@ describe('menu_get', function()
name = "Test",
hidden = 0
},
- {
- }
},
priority = 500,
name = "Test"
@@ -363,14 +366,22 @@ describe('menu_get', function()
local m = funcs.menu_get("Test","i")
local expected = {
{
- mappings = {
- i = {
- sid = 1,
- noremap = 1,
- enabled = 1,
- rhs = "insert",
- silent = 0
- }
+ shortcut = "T",
+ submenus = {
+ {
+ mappings = {
+ i = {
+ sid = 1,
+ noremap = 1,
+ enabled = 1,
+ rhs = "insert",
+ silent = 0
+ },
+ },
+ priority = 500,
+ name = "Test",
+ hidden = 0
+ },
},
priority = 500,
name = "Test",
diff --git a/test/functional/fixtures/tty-test.c b/test/functional/fixtures/tty-test.c
index 5f1f5cb91c..e2a78a594b 100644
--- a/test/functional/fixtures/tty-test.c
+++ b/test/functional/fixtures/tty-test.c
@@ -20,6 +20,7 @@
uv_tty_t tty;
uv_tty_t tty_out;
+bool owns_tty(void); // silence -Wmissing-prototypes
bool owns_tty(void)
{
#ifdef _WIN32
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 851f3e720e..3e72ba6f98 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -1,4 +1,3 @@
-require('vim.compat')
require('coxpcall')
local luv = require('luv')
local lfs = require('lfs')
@@ -679,6 +678,44 @@ local function alter_slashes(obj)
end
end
+local function compute_load_factor()
+ local timeout = 200
+ local times = {}
+
+ clear()
+
+ for _ = 1, 5 do
+ source([[
+ let g:val = 0
+ call timer_start(200, {-> nvim_set_var('val', 1)})
+ let start = reltime()
+ while 1
+ sleep 10m
+ if g:val == 1
+ let g:waited_in_ms = float2nr(reltimefloat(reltime(start)) * 1000)
+ break
+ endif
+ endwhile
+ ]])
+ table.insert(times, nvim_eval('g:waited_in_ms'))
+ end
+
+ session:close()
+ session = nil
+
+ local longest = math.max(unpack(times))
+ local factor = (longest + 50.0) / timeout
+
+ return factor
+end
+
+-- Compute load factor only once.
+local load_factor = compute_load_factor()
+
+local function load_adjust(num)
+ return math.ceil(num * load_factor)
+end
+
local module = {
NIL = mpack.NIL,
alter_slashes = alter_slashes,
@@ -720,6 +757,7 @@ local module = {
meths = meths,
missing_provider = missing_provider,
mkdir = lfs.mkdir,
+ load_adjust = load_adjust,
near = near,
neq = neq,
new_pipename = new_pipename,
diff --git a/test/functional/lua/commands_spec.lua b/test/functional/lua/commands_spec.lua
index 017ee55729..26dcbe0534 100644
--- a/test/functional/lua/commands_spec.lua
+++ b/test/functional/lua/commands_spec.lua
@@ -1,13 +1,17 @@
-- Test suite for checking :lua* commands
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
local eq = helpers.eq
local NIL = helpers.NIL
+local eval = helpers.eval
+local feed = helpers.feed
local clear = helpers.clear
local meths = helpers.meths
local funcs = helpers.funcs
local source = helpers.source
local dedent = helpers.dedent
+local command = helpers.command
local exc_exec = helpers.exc_exec
local write_file = helpers.write_file
local redir_exec = helpers.redir_exec
@@ -76,6 +80,64 @@ describe(':lua command', function()
eq('', redir_exec(('lua vim.api.nvim_buf_set_lines(1, 1, 2, false, {"%s"})'):format(s)))
eq({'', s}, curbufmeths.get_lines(0, -1, false))
end)
+
+ it('can show multiline error messages', function()
+ local screen = Screen.new(50,10)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {bold = true, reverse = true},
+ [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [4] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ })
+
+ feed(':lua error("fail\\nmuch error\\nsuch details")<cr>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ {3:E5105: Error while calling lua chunk: [string "<Vi}|
+ {3:mL compiled string>"]:1: fail} |
+ {3:much error} |
+ {3:such details} |
+ {4:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ eq('E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: fail\nmuch error\nsuch details', eval('v:errmsg'))
+
+ local status, err = pcall(command,'lua error("some error\\nin a\\nAPI command")')
+ local expected = 'Vim(lua):E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: some error\nin a\nAPI command'
+ eq(false, status)
+ eq(expected, string.sub(err, -string.len(expected)))
+
+ feed(':messages<cr>')
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2: }|
+ {3:E5105: Error while calling lua chunk: [string "<Vi}|
+ {3:mL compiled string>"]:1: fail} |
+ {3:much error} |
+ {3:such details} |
+ {4:Press ENTER or type command to continue}^ |
+ ]])
+ end)
end)
describe(':luado command', function()
diff --git a/test/functional/options/fillchars_spec.lua b/test/functional/options/chars_spec.lua
index 99177a11b4..1330c29e61 100644
--- a/test/functional/options/fillchars_spec.lua
+++ b/test/functional/options/chars_spec.lua
@@ -4,6 +4,8 @@ local clear, command = helpers.clear, helpers.command
local eval = helpers.eval
local eq = helpers.eq
local exc_exec = helpers.exc_exec
+local insert = helpers.insert
+local feed = helpers.feed
describe("'fillchars'", function()
local screen
@@ -69,5 +71,51 @@ describe("'fillchars'", function()
shouldfail('eob:xy') -- two ascii chars
shouldfail('eob:\255', 'eob:<ff>') -- invalid UTF-8
end)
+ it('is local to window', function()
+ clear()
+ screen = Screen.new(50, 5)
+ screen:attach()
+ insert("foo\nbar")
+ command('set laststatus=0')
+ command('1,2fold')
+ command('vsplit')
+ command('set fillchars=fold:x')
+ screen:expect([[
+ ^+-- 2 lines: fooxxxxxxxx│+-- 2 lines: foo·······|
+ ~ │~ |
+ ~ │~ |
+ ~ │~ |
+ |
+ ]])
+ end)
+ end)
+end)
+
+describe("'listchars'", function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(50, 5)
+ screen:attach()
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('is local to window', function()
+ feed('i<tab><tab><tab><esc>')
+ command('set laststatus=0')
+ command('set list listchars=tab:<->')
+ command('vsplit')
+ command('set listchars&')
+ screen:expect([[
+ > > ^> │<------><------><------>|
+ ~ │~ |
+ ~ │~ |
+ ~ │~ |
+ |
+ ]])
end)
end)
diff --git a/test/functional/options/num_options_spec.lua b/test/functional/options/num_options_spec.lua
index fb0559054d..88e554c86f 100644
--- a/test/functional/options/num_options_spec.lua
+++ b/test/functional/options/num_options_spec.lua
@@ -32,9 +32,9 @@ describe(':setlocal', function()
eq(0, meths.get_option('iminsert'))
feed_command('setlocal iminsert=1')
eq(0, meths.get_option('iminsert'))
- eq(0, meths.get_option('imsearch'))
+ eq(-1, meths.get_option('imsearch'))
feed_command('setlocal imsearch=1')
- eq(0, meths.get_option('imsearch'))
+ eq(-1, meths.get_option('imsearch'))
end)
end)
diff --git a/test/functional/terminal/altscreen_spec.lua b/test/functional/terminal/altscreen_spec.lua
index 4526037808..155a156d15 100644
--- a/test/functional/terminal/altscreen_spec.lua
+++ b/test/functional/terminal/altscreen_spec.lua
@@ -8,7 +8,7 @@ local exit_altscreen = thelpers.exit_altscreen
if helpers.pending_win32(pending) then return end
-describe('terminal altscreen', function()
+describe(':terminal altscreen', function()
local screen
before_each(function()
diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua
index 4d6b125f9f..e598c325a8 100644
--- a/test/functional/terminal/buffer_spec.lua
+++ b/test/functional/terminal/buffer_spec.lua
@@ -6,7 +6,7 @@ local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.s
local eq, neq = helpers.eq, helpers.neq
local write_file = helpers.write_file
-describe('terminal buffer', function()
+describe(':terminal buffer', function()
local screen
before_each(function()
diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua
index d942723d02..ef12438ecc 100644
--- a/test/functional/terminal/cursor_spec.lua
+++ b/test/functional/terminal/cursor_spec.lua
@@ -7,7 +7,7 @@ local feed_command = helpers.feed_command
local hide_cursor = thelpers.hide_cursor
local show_cursor = thelpers.show_cursor
-describe('terminal cursor', function()
+describe(':terminal cursor', function()
local screen
before_each(function()
diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua
index 7de0152de0..18f0b9e4c1 100644
--- a/test/functional/terminal/helpers.lua
+++ b/test/functional/terminal/helpers.lua
@@ -33,7 +33,7 @@ local function disable_mouse() feed_termcode('[?1002l') end
local default_command = '["'..nvim_dir..'/tty-test'..'"]'
-local function screen_setup(extra_rows, command, cols)
+local function screen_setup(extra_rows, command, cols, opts)
extra_rows = extra_rows and extra_rows or 0
command = command and command or default_command
cols = cols and cols or 50
@@ -55,7 +55,7 @@ local function screen_setup(extra_rows, command, cols)
[10] = {foreground = 121}, -- "Press ENTER" in embedded :terminal session.
})
- screen:attach({rgb=false})
+ screen:attach(opts or {rgb=false})
feed_command('enew | call termopen('..command..')')
nvim('input', '<CR>')
@@ -69,7 +69,7 @@ local function screen_setup(extra_rows, command, cols)
-- tty-test puts the terminal into raw mode and echoes input. Tests work by
-- feeding termcodes to control the display and asserting by screen:expect.
- if command == default_command then
+ if command == default_command and opts == nil then
-- Wait for "tty ready" to be printed before each test or the terminal may
-- still be in canonical mode (will echo characters for example).
local empty_line = (' '):rep(cols)
diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua
index f33959c58d..9579e0ea0b 100644
--- a/test/functional/terminal/highlight_spec.lua
+++ b/test/functional/terminal/highlight_spec.lua
@@ -5,7 +5,7 @@ local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim
local nvim_dir, command = helpers.nvim_dir, helpers.command
local eq, eval = helpers.eq, helpers.eval
-describe('terminal window highlighting', function()
+describe(':terminal window highlighting', function()
local screen
before_each(function()
diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua
index 263a5ce79d..64f437f206 100644
--- a/test/functional/terminal/mouse_spec.lua
+++ b/test/functional/terminal/mouse_spec.lua
@@ -4,7 +4,7 @@ local clear, eq, eval = helpers.clear, helpers.eq, helpers.eval
local feed, nvim = helpers.feed, helpers.nvim
local feed_data = thelpers.feed_data
-describe('terminal mouse', function()
+describe(':terminal mouse', function()
local screen
before_each(function()
@@ -108,41 +108,42 @@ describe('terminal mouse', function()
]])
feed(':enew | set number<cr>')
screen:expect([[
- {7: 1 }^ │line28 |
- {4:~ }│line29 |
+ {7: 1 }^ │line29 |
{4:~ }│line30 |
{4:~ }│rows: 5, cols: 25 |
+ {4:~ }│rows: 5, cols: 24 |
{4:~ }│{2: } |
========== ========== |
:enew | set number |
]])
feed('30iline\n<esc>')
screen:expect([[
- {7: 27 }line │line28 |
- {7: 28 }line │line29 |
- {7: 29 }line │line30 |
- {7: 30 }line │rows: 5, cols: 25 |
+ {7: 27 }line │line29 |
+ {7: 28 }line │line30 |
+ {7: 29 }line │rows: 5, cols: 25 |
+ {7: 30 }line │rows: 5, cols: 24 |
{7: 31 }^ │{2: } |
========== ========== |
|
]])
feed('<c-w>li')
screen:expect([[
- {7: 27 }line │line28 |
- {7: 28 }line │line29 |
- {7: 29 }line │line30 |
- {7: 30 }line │rows: 5, cols: 25 |
+ {7: 27 }line │line29 |
+ {7: 28 }line │line30 |
+ {7: 29 }line │rows: 5, cols: 25 |
+ {7: 30 }line │rows: 5, cols: 24 |
{7: 31 } │{1: } |
========== ========== |
{3:-- TERMINAL --} |
]])
+
-- enabling mouse won't affect interaction with other windows
thelpers.enable_mouse()
thelpers.feed_data('mouse enabled\n')
screen:expect([[
- {7: 27 }line │line29 |
- {7: 28 }line │line30 |
- {7: 29 }line │rows: 5, cols: 25 |
+ {7: 27 }line │line30 |
+ {7: 28 }line │rows: 5, cols: 25 |
+ {7: 29 }line │rows: 5, cols: 24 |
{7: 30 }line │mouse enabled |
{7: 31 } │{1: } |
========== ========== |
@@ -153,9 +154,9 @@ describe('terminal mouse', function()
it('wont lose focus if another window is scrolled', function()
feed('<ScrollWheelUp><0,0><ScrollWheelUp><0,0>')
screen:expect([[
- {7: 21 }line │line29 |
- {7: 22 }line │line30 |
- {7: 23 }line │rows: 5, cols: 25 |
+ {7: 21 }line │line30 |
+ {7: 22 }line │rows: 5, cols: 25 |
+ {7: 23 }line │rows: 5, cols: 24 |
{7: 24 }line │mouse enabled |
{7: 25 }line │{1: } |
========== ========== |
@@ -163,9 +164,9 @@ describe('terminal mouse', function()
]])
feed('<S-ScrollWheelDown><0,0>')
screen:expect([[
- {7: 26 }line │line29 |
- {7: 27 }line │line30 |
- {7: 28 }line │rows: 5, cols: 25 |
+ {7: 26 }line │line30 |
+ {7: 27 }line │rows: 5, cols: 25 |
+ {7: 28 }line │rows: 5, cols: 24 |
{7: 29 }line │mouse enabled |
{7: 30 }line │{1: } |
========== ========== |
@@ -176,15 +177,49 @@ describe('terminal mouse', function()
it('will lose focus if another window is clicked', function()
feed('<LeftMouse><5,1>')
screen:expect([[
- {7: 27 }line │line29 |
- {7: 28 }l^ine │line30 |
- {7: 29 }line │rows: 5, cols: 25 |
+ {7: 27 }line │line30 |
+ {7: 28 }l^ine │rows: 5, cols: 25 |
+ {7: 29 }line │rows: 5, cols: 24 |
{7: 30 }line │mouse enabled |
{7: 31 } │{2: } |
========== ========== |
|
]])
end)
+
+ it('handles terminal size when switching buffers', function()
+ nvim('set_option', 'hidden', true)
+ feed('<c-\\><c-n><c-w><c-w>')
+ screen:expect([[
+ {7: 27 }line │line30 |
+ {7: 28 }line │rows: 5, cols: 25 |
+ {7: 29 }line │rows: 5, cols: 24 |
+ {7: 30 }line │mouse enabled |
+ {7: 31 }^ │{2: } |
+ ========== ========== |
+ |
+ ]])
+ feed(':bn<cr>')
+ screen:expect([[
+ rows: 5, cols: 25 │rows: 5, cols: 25 |
+ rows: 5, cols: 24 │rows: 5, cols: 24 |
+ mouse enabled │mouse enabled |
+ rows: 5, cols: 25 │rows: 5, cols: 25 |
+ {2:^ } │{2: } |
+ ========== ========== |
+ :bn |
+ ]])
+ feed(':bn<cr>')
+ screen:expect([[
+ {7: 27 }line │rows: 5, cols: 24 |
+ {7: 28 }line │mouse enabled |
+ {7: 29 }line │rows: 5, cols: 25 |
+ {7: 30 }line │rows: 5, cols: 24 |
+ {7: 31 }^ │{2: } |
+ ========== ========== |
+ :bn |
+ ]])
+ end)
end)
end)
end)
diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua
index 1c97441213..75bb89a1ab 100644
--- a/test/functional/terminal/scrollback_spec.lua
+++ b/test/functional/terminal/scrollback_spec.lua
@@ -10,9 +10,10 @@ local wait = helpers.wait
local retry = helpers.retry
local curbufmeths = helpers.curbufmeths
local nvim = helpers.nvim
+local expect_err = helpers.expect_err
local feed_data = thelpers.feed_data
-describe('terminal scrollback', function()
+describe(':terminal scrollback', function()
local screen
before_each(function()
@@ -344,7 +345,7 @@ describe('terminal scrollback', function()
end)
end)
-describe('terminal prints more lines than the screen height and exits', function()
+describe(':terminal prints more lines than the screen height and exits', function()
it('will push extra lines to scrollback', function()
clear()
local screen = Screen.new(30, 7)
@@ -460,20 +461,15 @@ describe("'scrollback' option", function()
screen:detach()
end)
- it('defaults to 10000 in terminal buffers', function()
+ it('defaults to 10000 in :terminal buffers', function()
set_fake_shell()
command('terminal')
eq(10000, curbufmeths.get_option('scrollback'))
end)
it('error if set to invalid value', function()
- local status, rv = pcall(command, 'set scrollback=-2')
- eq(false, status) -- assert failure
- eq('E474:', string.match(rv, "E%d*:"))
-
- status, rv = pcall(command, 'set scrollback=100001')
- eq(false, status) -- assert failure
- eq('E474:', string.match(rv, "E%d*:"))
+ expect_err('E474:', command, 'set scrollback=-2')
+ expect_err('E474:', command, 'set scrollback=100001')
end)
it('defaults to -1 on normal buffers', function()
@@ -481,25 +477,41 @@ describe("'scrollback' option", function()
eq(-1, curbufmeths.get_option('scrollback'))
end)
- it(':setlocal in a normal buffer is an error', function()
- command('new')
+ it(':setlocal in a :terminal buffer', function()
+ set_fake_shell()
- -- :setlocal to -1 is NOT an error.
- feed_command('setlocal scrollback=-1')
- eq(nil, string.match(eval("v:errmsg"), "E%d*:"))
- feed('<CR>')
+ -- _Global_ scrollback=-1 defaults :terminal to 10_000.
+ command('setglobal scrollback=-1')
+ command('terminal')
+ eq(10000, curbufmeths.get_option('scrollback'))
+
+ -- _Local_ scrollback=-1 in :terminal forces the _maximum_.
+ command('setlocal scrollback=-1')
+ retry(nil, nil, function() -- Fixup happens on refresh, not immediately.
+ eq(100000, curbufmeths.get_option('scrollback'))
+ end)
- -- :setlocal to anything except -1 is an error.
- feed_command('setlocal scrollback=42')
- feed('<CR>')
- eq('E474:', string.match(eval("v:errmsg"), "E%d*:"))
+ -- _Local_ scrollback=-1 during TermOpen forces the maximum. #9605
+ command('setglobal scrollback=-1')
+ command('autocmd TermOpen * setlocal scrollback=-1')
+ command('terminal')
+ eq(100000, curbufmeths.get_option('scrollback'))
+ end)
+
+ it(':setlocal in a normal buffer', function()
+ command('new')
+ -- :setlocal to -1.
+ command('setlocal scrollback=-1')
eq(-1, curbufmeths.get_option('scrollback'))
+ -- :setlocal to anything except -1. Currently, this just has no effect.
+ command('setlocal scrollback=42')
+ eq(42, curbufmeths.get_option('scrollback'))
end)
it(':set updates local value and global default', function()
set_fake_shell()
- command('set scrollback=42') -- set global and (attempt) local
- eq(-1, curbufmeths.get_option('scrollback')) -- normal buffer: -1
+ command('set scrollback=42') -- set global value
+ eq(42, curbufmeths.get_option('scrollback'))
command('terminal')
eq(42, curbufmeths.get_option('scrollback')) -- inherits global default
command('setlocal scrollback=99')
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index 1bc1334a6a..70356b7e2d 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -256,13 +256,13 @@ describe('TUI', function()
end)
it('shows up in nvim_list_uis', function()
- feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013')
+ feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(filter(v, {k,v -> k[:3] !=# "ext_" })))})\013')
screen:expect([=[
- [[['ext_cmdline', v:false], ['ext_hlstate', v:fals|
- e], ['ext_linegrid', v:true], ['ext_multigrid', v:|
- false], ['ext_popupmenu', v:false], ['ext_tabline'|
- , v:false], ['ext_wildmenu', v:false], ['height', |
- 6], ['rgb', v:false], ['width', 50]]] |
+ |
+ {4:~ }|
+ {5: }|
+ [[['height', 6], ['rgb', v:false], ['width', 50]]]|
+ |
{10:Press ENTER or type command to continue}{1: } |
{3:-- TERMINAL --} |
]=])
diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua
index 842a81872e..4f62c75433 100644
--- a/test/functional/terminal/window_spec.lua
+++ b/test/functional/terminal/window_spec.lua
@@ -1,10 +1,15 @@
local helpers = require('test.functional.helpers')(after_each)
local thelpers = require('test.functional.terminal.helpers')
+local feed_data = thelpers.feed_data
local feed, clear = helpers.feed, helpers.clear
local wait = helpers.wait
local iswin = helpers.iswin
+local command = helpers.command
+local retry = helpers.retry
+local eq = helpers.eq
+local eval = helpers.eval
-describe('terminal window', function()
+describe(':terminal window', function()
local screen
before_each(function()
@@ -12,6 +17,18 @@ describe('terminal window', function()
screen = thelpers.screen_setup()
end)
+ it('sets topline correctly #8556', function()
+ -- Test has hardcoded assumptions of dimensions.
+ eq(7, eval('&lines'))
+ feed_data('\n\n\n') -- Add blank lines.
+ -- Terminal/shell contents must exceed the height of this window.
+ command('topleft 1split')
+ eq('terminal', eval('&buftype'))
+ feed([[i<cr>]])
+ -- Check topline _while_ in terminal-mode.
+ retry(nil, nil, function() eq(6, eval('winsaveview()["topline"]')) end)
+ end)
+
describe("with 'number'", function()
it('wraps text', function()
feed([[<C-\><C-N>]])
@@ -25,7 +42,7 @@ describe('terminal window', function()
{7:6 } |
{3:-- TERMINAL --} |
]])
- thelpers.feed_data({'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'})
+ feed_data({'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'})
screen:expect([[
{7:1 }tty ready |
{7:2 }rows: 6, cols: 48 |
@@ -52,7 +69,7 @@ describe('terminal window', function()
{7: 6 } |
{3:-- TERMINAL --} |
]])
- thelpers.feed_data({' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'})
+ feed_data({' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'})
screen:expect([[
{7: 1 }tty ready |
{7: 2 }rows: 6, cols: 48 |
@@ -96,7 +113,7 @@ describe('terminal window', function()
describe('with fold set', function()
before_each(function()
feed([[<C-\><C-N>:set foldenable foldmethod=manual<CR>i]])
- thelpers.feed_data({'line1', 'line2', 'line3', 'line4', ''})
+ feed_data({'line1', 'line2', 'line3', 'line4', ''})
screen:expect([[
tty ready |
line1 |
@@ -124,3 +141,101 @@ describe('terminal window', function()
end)
end)
+describe(':terminal with multigrid', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = thelpers.screen_setup(0,nil,50,{ext_multigrid=true})
+ end)
+
+ it('resizes to requested size', function()
+ screen:expect([[
+ ## grid 1
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ {3:-- TERMINAL --} |
+ ## grid 2
+ tty ready |
+ {1: } |
+ |
+ |
+ |
+ |
+ ]])
+
+ screen:try_resize_grid(2, 20, 10)
+ if iswin() then
+ screen:expect{any="rows: 10, cols: 20"}
+ else
+ screen:expect([[
+ ## grid 1
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ {3:-- TERMINAL --} |
+ ## grid 2
+ tty ready |
+ rows: 10, cols: 20 |
+ {1: } |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ ]])
+ end
+
+ screen:try_resize_grid(2, 70, 3)
+ if iswin() then
+ screen:expect{any="rows: 3, cols: 70"}
+ else
+ screen:expect([[
+ ## grid 1
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ {3:-- TERMINAL --} |
+ ## grid 2
+ rows: 10, cols: 20 |
+ rows: 3, cols: 70 |
+ {1: } |
+ ]])
+ end
+
+ screen:try_resize_grid(2, 0, 0)
+ if iswin() then
+ screen:expect{any="rows: 6, cols: 50"}
+ else
+ screen:expect([[
+ ## grid 1
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ [2:--------------------------------------------------]|
+ {3:-- TERMINAL --} |
+ ## grid 2
+ tty ready |
+ rows: 10, cols: 20 |
+ rows: 3, cols: 70 |
+ rows: 6, cols: 50 |
+ {1: } |
+ |
+ ]])
+ end
+ end)
+end)
diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua
index fecffe3295..f3d0b45d09 100644
--- a/test/functional/terminal/window_split_tab_spec.lua
+++ b/test/functional/terminal/window_split_tab_spec.lua
@@ -9,7 +9,7 @@ local eval = helpers.eval
local iswin = helpers.iswin
local retry = helpers.retry
-describe('terminal', function()
+describe(':terminal', function()
local screen
before_each(function()
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index 3228d6b7fc..536264019c 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -1151,6 +1151,39 @@ describe(":substitute, inccommand=split", function()
eq("split", eval("&inccommand"))
end)
+ it("deactivates if 'foldexpr' is slow #9557", function()
+ insert([[
+ a
+ a
+ a
+ a
+ a
+ a
+ a
+ a
+ ]])
+ source([[
+ function! Slowfold(lnum)
+ sleep 5m
+ return a:lnum % 3
+ endfun
+ ]])
+ command('set redrawtime=1 inccommand=split')
+ command('set foldmethod=expr foldexpr=Slowfold(v:lnum)')
+ feed(':%s/a/bcdef')
+
+ -- Assert that 'inccommand' is DISABLED in cmdline mode.
+ retry(nil, nil, function()
+ eq('', eval('&inccommand'))
+ end)
+
+ -- Assert that 'inccommand' is again ENABLED after leaving cmdline mode.
+ feed([[<C-\><C-N>]])
+ retry(nil, nil, function()
+ eq('split', eval('&inccommand'))
+ end)
+ end)
+
it("clears preview if non-previewable command is edited #5585", function()
-- Put a non-previewable command in history.
feed_command("echo 'foo'")
diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua
new file mode 100644
index 0000000000..388c6b3e95
--- /dev/null
+++ b/test/functional/ui/messages_spec.lua
@@ -0,0 +1,632 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, feed = helpers.clear, helpers.feed
+local eval = helpers.eval
+local eq = helpers.eq
+local command = helpers.command
+
+
+describe('ui/ext_messages', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(25, 5)
+ screen:attach({rgb=true, ext_messages=true, ext_popupmenu=true})
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [3] = {bold = true},
+ [4] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [5] = {foreground = Screen.colors.Blue1},
+ [6] = {bold = true, reverse = true},
+ })
+ end)
+
+ it('supports :echoerr', function()
+ feed(':echoerr "raa"<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={{
+ content = {{"raa", 2}},
+ kind = "echoerr",
+ }}}
+
+ -- cmdline in a later input cycle clears error message
+ feed(':')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], cmdline={{
+ firstc = ":",
+ content = {{ "" }},
+ pos = 0,
+ }}}
+
+
+ feed('echoerr "bork" | echoerr "fail"<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={{
+ content = {{ "bork", 2 }},
+ kind = "echoerr"
+ }, {
+ content = {{ "fail", 2 }},
+ kind = "echoerr"
+ }, {
+ content = {{ "Press ENTER or type command to continue", 4 }},
+ kind = "return_prompt"
+ }}}
+
+ feed(':echoerr "extrafail"<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={{
+ content = { { "bork", 2 } },
+ kind = "echoerr"
+ }, {
+ content = { { "fail", 2 } },
+ kind = "echoerr"
+ }, {
+ content = { { "extrafail", 2 } },
+ kind = "echoerr"
+ }, {
+ content = { { "Press ENTER or type command to continue", 4 } },
+ kind = "return_prompt"
+ }}}
+
+ feed('<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]]}
+
+ -- cmdline without interleaving wait/display keeps the error message
+ feed(':echoerr "problem" | let x = input("foo> ")<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={{
+ content = {{ "problem", 2 }},
+ kind = "echoerr"
+ }}, cmdline={{
+ prompt = "foo> ",
+ content = {{ "" }},
+ pos = 0,
+ }}}
+
+ feed('solution<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]]}
+ eq('solution', eval('x'))
+
+ feed(":messages<cr>")
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={
+ {kind="echoerr", content={{"raa", 2}}},
+ {kind="echoerr", content={{"bork", 2}}},
+ {kind="echoerr", content={{"fail", 2}}},
+ {kind="echoerr", content={{"extrafail", 2}}},
+ {kind="echoerr", content={{"problem", 2}}}
+ }}
+ end)
+
+ it('supports showmode', function()
+ command('imap <f2> <cmd>echomsg "stuff"<cr>')
+ feed('i')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showmode={{"-- INSERT --", 3}}}
+
+ feed('alphpabet<cr>alphanum<cr>')
+ screen:expect{grid=[[
+ alphpabet |
+ alphanum |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ ]], showmode={ { "-- INSERT --", 3 } }}
+
+ feed('<c-x>')
+ screen:expect{grid=[[
+ alphpabet |
+ alphanum |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ ]], showmode={ { "-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)", 3 } }}
+
+ feed('<c-p>')
+ screen:expect{grid=[[
+ alphpabet |
+ alphanum |
+ alphanum^ |
+ {1:~ }|
+ {1:~ }|
+ ]], popupmenu={
+ anchor = { 2, 0 },
+ items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } },
+ pos = 1
+ }, showmode={ { "-- Keyword Local completion (^N^P) ", 3 }, { "match 1 of 2", 4 } }}
+
+ -- echomsg and showmode don't overwrite each other, this is the same
+ -- as the TUI behavior with cmdheight=2 or larger.
+ feed('<f2>')
+ screen:expect{grid=[[
+ alphpabet |
+ alphanum |
+ alphanum^ |
+ {1:~ }|
+ {1:~ }|
+ ]], popupmenu={
+ anchor = { 2, 0 },
+ items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } },
+ pos = 1
+ }, messages={ {
+ content = { { "stuff" } },
+ kind = "echomsg"
+ } }, showmode={ { "-- Keyword Local completion (^N^P) ", 3 }, { "match 1 of 2", 4 } }}
+
+ feed('<c-p>')
+ screen:expect{grid=[[
+ alphpabet |
+ alphanum |
+ alphpabet^ |
+ {1:~ }|
+ {1:~ }|
+ ]], popupmenu={
+ anchor = { 2, 0 },
+ items = { { "alphpabet", "", "", "" }, { "alphanum", "", "", "" } },
+ pos = 0
+ }, messages={ {
+ content = { { "stuff" } },
+ kind = "echomsg"
+ } }, showmode={ { "-- Keyword Local completion (^N^P) ", 3 }, { "match 2 of 2", 4 } }}
+
+ feed("<esc>:messages<cr>")
+ screen:expect{grid=[[
+ alphpabet |
+ alphanum |
+ alphpabe^t |
+ {1:~ }|
+ {1:~ }|
+ ]], messages={
+ {kind="echomsg", content={{"stuff"}}},
+ }}
+ end)
+
+ it('supports showmode with recording message', function()
+ feed('qq')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showmode={ { "recording @q", 3 } }}
+
+ feed('i')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showmode={ { "-- INSERT --recording @q", 3 } }}
+
+ feed('<esc>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showmode={ { "recording @q", 3 } }}
+
+ feed('q')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+
+ it('shows recording message with noshowmode', function()
+ command("set noshowmode")
+ feed('qq')
+ -- also check mode to avoid immediate success
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showmode={ { "recording @q", 3 } }, mode="normal"}
+
+ feed('i')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showmode={ { "recording @q", 3 } }, mode="insert"}
+
+ feed('<esc>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showmode={ { "recording @q", 3 } }, mode="normal"}
+
+ feed('q')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], mode="normal"}
+ end)
+
+ it('supports showcmd and ruler', function()
+ command('set showcmd ruler')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], ruler={ { "0,0-1 All" } }}
+ feed('i')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showmode={ { "-- INSERT --", 3 } }, ruler={ { "0,1 All" } }}
+ feed('abcde<cr>12345<esc>')
+ screen:expect{grid=[[
+ abcde |
+ 1234^5 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], ruler={ { "2,5 All" } }}
+ feed('d')
+ screen:expect{grid=[[
+ abcde |
+ 1234^5 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showcmd={ { "d" } }, ruler={ { "2,5 All" } }}
+ feed('<esc>^')
+ screen:expect{grid=[[
+ abcde |
+ ^12345 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], ruler={ { "2,1 All" } }}
+ feed('d')
+ screen:expect{grid=[[
+ abcde |
+ ^12345 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showcmd={ { "d" } }, ruler={ { "2,1 All" } }}
+ feed('i')
+ screen:expect{grid=[[
+ abcde |
+ ^12345 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], showcmd={ { "di" } }, ruler={ { "2,1 All" } }}
+ feed('w')
+ screen:expect{grid=[[
+ abcde |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], ruler={ { "2,0-1 All" } }}
+
+ -- when ruler is part of statusline it is not externalized.
+ -- this will be added as part of future ext_statusline support
+ command("set laststatus=2")
+ screen:expect([[
+ abcde |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {6:<o Name] [+] 2,0-1 All}|
+ ]])
+ end)
+
+ it('keeps history of message of different kinds', function()
+ feed(':echomsg "howdy"<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={{
+ content = {{ "howdy" }}, kind = "echomsg"}
+ }}
+
+ -- always test a message without kind. If this one gets promoted to a
+ -- category, add a new message without kind.
+ feed('<c-c>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={{
+ content = {{ "Type :qa! and press <Enter> to abandon all changes and exit Nvim" }},
+ kind = ""}
+ }}
+
+ feed(':echoerr "bork"<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={{
+ content = {{ "bork", 2 }}, kind = "echoerr"}
+ }}
+
+ feed(':echo "xyz"<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={{
+ content = {{ "xyz" }}, kind = "echo"}
+ }}
+
+ feed(':call nosuchfunction()<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={{
+ content = {{ "E117: Unknown function: nosuchfunction", 2 }},
+ kind = "emsg"}
+ }}
+
+ feed(':messages<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={
+ {kind="echomsg", content={{"howdy"}}},
+ {kind="", content={{"Type :qa! and press <Enter> to abandon all changes and exit Nvim"}}},
+ {kind="echoerr", content={{"bork", 2}}},
+ {kind="emsg", content={{"E117: Unknown function: nosuchfunction", 2}}}
+ }}
+ end)
+
+ it('implies ext_cmdline and ignores cmdheight', function()
+ eq(0, eval('&cmdheight'))
+ feed(':set cmdheight=1')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], cmdline={{
+ content = { { "set cmdheight=1" } },
+ firstc = ":",
+ pos = 15 }
+ }}
+
+ feed('<cr>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ eq(0, eval('&cmdheight'))
+
+ -- normally this would be an error
+ feed(':set cmdheight=0')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], cmdline={{
+ content = { { "set cmdheight=0" } },
+ firstc = ":",
+ pos = 15 }
+ }}
+ feed('<cr>')
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ eq(0, eval('&cmdheight'))
+ end)
+
+ it('supports multiline messages', function()
+ feed(':lua error("such\\nmultiline\\nerror")<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], messages={{
+ content = {{'E5105: Error while calling lua chunk: [string "<VimL compiled string>"]:1: such\nmultiline\nerror', 2}},
+ kind = "emsg"
+ }}}
+ end)
+end)
+
+describe('ui/ext_messages', function()
+ local screen
+
+ before_each(function()
+ clear{headless=false, args={"--cmd", "set shortmess-=I"}}
+ screen = Screen.new(80, 24)
+ screen:attach({rgb=true, ext_messages=true, ext_popupmenu=true})
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [3] = {bold = true},
+ [4] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [5] = {foreground = Screen.colors.Blue1},
+ })
+ end)
+
+ it('supports intro screen', function()
+ -- intro message is not externalized. But check that it still works.
+ -- Note parts of it depends on version or is indeterministic. We ignore those parts.
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {IGNORE}|
+ {1:~ }|
+ {1:~ }Nvim is open source and freely distributable{1: }|
+ {1:~ }https://neovim.io/#chat{1: }|
+ {1:~ }|
+ {1:~ }type :help nvim{5:<Enter>} if you are new! {1: }|
+ {1:~ }type :checkhealth{5:<Enter>} to optimize Nvim{1: }|
+ {1:~ }type :q{5:<Enter>} to exit {1: }|
+ {1:~ }type :help{5:<Enter>} for help {1: }|
+ {1:~ }|
+ {IGNORE}|
+ {IGNORE}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ feed("<c-l>")
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ feed(":intro<cr>")
+ screen:expect{grid=[[
+ |
+ |
+ |
+ |
+ |
+ |
+ {IGNORE}|
+ |
+ Nvim is open source and freely distributable |
+ https://neovim.io/#chat |
+ |
+ type :help nvim{5:<Enter>} if you are new! |
+ type :checkhealth{5:<Enter>} to optimize Nvim |
+ type :q{5:<Enter>} to exit |
+ type :help{5:<Enter>} for help |
+ |
+ {IGNORE}|
+ {IGNORE}|
+ |
+ |
+ |
+ |
+ |
+ |
+ ]], messages={
+ {content = { { "Press ENTER or type command to continue", 4 } }, kind = "return_prompt" }
+ }}
+ end)
+end)
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index 8d35df6f48..7805ed3cb9 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -11,6 +11,7 @@ describe('ui/mouse/input', function()
before_each(function()
clear()
meths.set_option('mouse', 'a')
+ meths.set_option('list', true)
meths.set_option('listchars', 'eol:$')
screen = Screen.new(25, 5)
screen:attach()
@@ -82,7 +83,7 @@ describe('ui/mouse/input', function()
feed('<LeftMouse><0,0>')
feed('<LeftRelease><0,0>')
screen:expect([[
- ^t{1:esting}{3: } |
+ ^t{1:esting} |
mouse |
support and selection |
{0:~ }|
@@ -125,7 +126,7 @@ describe('ui/mouse/input', function()
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -162,7 +163,7 @@ describe('ui/mouse/input', function()
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -170,7 +171,7 @@ describe('ui/mouse/input', function()
feed('<LeftMouse><11,0>')
screen:expect{grid=[[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -178,7 +179,7 @@ describe('ui/mouse/input', function()
feed('<LeftDrag><6,0>')
screen:expect([[
{sel: + bar }{tab: + foo }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -192,7 +193,7 @@ describe('ui/mouse/input', function()
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -222,7 +223,7 @@ describe('ui/mouse/input', function()
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -260,7 +261,7 @@ describe('ui/mouse/input', function()
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -268,7 +269,7 @@ describe('ui/mouse/input', function()
feed('<LeftMouse><11,0>')
screen:expect{grid=[[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -276,7 +277,7 @@ describe('ui/mouse/input', function()
feed('<LeftDrag><11,1>')
screen:expect{grid=[[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -284,7 +285,7 @@ describe('ui/mouse/input', function()
feed('<LeftDrag><6,1>')
screen:expect([[
{sel: + bar }{tab: + foo }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -298,7 +299,7 @@ describe('ui/mouse/input', function()
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -347,7 +348,7 @@ describe('ui/mouse/input', function()
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -370,7 +371,7 @@ describe('ui/mouse/input', function()
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -393,7 +394,7 @@ describe('ui/mouse/input', function()
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- this is ba^r |
+ this is ba^r{0:$} |
{0:~ }|
{0:~ }|
|
@@ -401,7 +402,7 @@ describe('ui/mouse/input', function()
feed('<2-LeftMouse><4,0>')
screen:expect([[
{sel: Name] }{tab: + foo + bar }{fill: }{tab:X}|
- ^ |
+ {0:^$} |
{0:~ }|
{0:~ }|
|
@@ -440,16 +441,34 @@ describe('ui/mouse/input', function()
local test_click = function(name, click_str, click_num, mouse_button,
modifiers)
- it(name .. ' works', function()
+
+ local function doit(do_click)
eq(1, funcs.has('tablineat'))
- feed(click_str .. '<3,0>')
+ do_click(0,3)
check_reply({0, click_num, mouse_button, modifiers})
- feed(click_str .. '<4,0>')
+ do_click(0,4)
check_reply({})
- feed(click_str .. '<6,0>')
+ do_click(0,6)
check_reply({5, click_num, mouse_button, modifiers, 2})
- feed(click_str .. '<13,0>')
+ do_click(0,13)
check_reply({5, click_num, mouse_button, modifiers, 2})
+ end
+
+ it(name .. ' works (pseudokey)', function()
+ doit(function (row,col)
+ feed(click_str .. '<' .. col .. ',' .. row .. '>')
+ end)
+ end)
+
+ it(name .. ' works (nvim_input_mouse)', function()
+ doit(function (row,col)
+ local buttons = {l='left',m='middle',r='right'}
+ local modstr = (click_num > 1) and tostring(click_num) or ''
+ for char in string.gmatch(modifiers, '%w') do
+ modstr = modstr .. char .. '-' -- - not needed but should be accepted
+ end
+ meths.input_mouse(buttons[mouse_button], 'press', modstr, 0, row, col)
+ end)
end)
end
@@ -499,14 +518,14 @@ describe('ui/mouse/input', function()
feed('<LeftDrag><2,2>')
screen:expect([[
testing |
- mo{1:use}{3: } |
+ mo{1:use} |
{1:su}^pport and selection |
{0:~ }|
{2:-- VISUAL --} |
]])
feed('<LeftDrag><0,0>')
screen:expect([[
- ^t{1:esting}{3: } |
+ ^t{1:esting} |
{1:mou}se |
support and selection |
{0:~ }|
@@ -537,7 +556,7 @@ describe('ui/mouse/input', function()
feed('<LeftMouse><0,1>')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- ^this is bar |
+ ^this is bar{0:$} |
{0:~ }|
{0:~ }|
:tabprevious |
@@ -545,7 +564,7 @@ describe('ui/mouse/input', function()
feed('<LeftDrag><4,1>')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
- {vis:this}^ is bar |
+ {vis:this}^ is bar{0:$} |
{0:~ }|
{0:~ }|
{sel:-- VISUAL --} |
@@ -568,7 +587,7 @@ describe('ui/mouse/input', function()
screen:expect([[
testing |
mouse |
- {1:su}^p{1:port and selection}{3: } |
+ {1:su}^p{1:port and selection} |
{0:~ }|
{2:-- VISUAL LINE --} |
]])
@@ -596,8 +615,8 @@ describe('ui/mouse/input', function()
]])
feed('<RightMouse><2,2>')
screen:expect([[
- {1:testing}{3: } |
- {1:mouse}{3: } |
+ {1:testing} |
+ {1:mouse} |
{1:su}^pport and selection |
{0:~ }|
{2:-- VISUAL --} |
@@ -617,7 +636,7 @@ describe('ui/mouse/input', function()
feed('<cr>')
end)
- it('mouse whell will target the hovered window', function()
+ local function wheel(use_api)
feed('ggdG')
insert([[
Inserting
@@ -647,7 +666,11 @@ describe('ui/mouse/input', function()
{4:[No Name] [+] }|
:vsp |
]])
- feed('<ScrollWheelDown><0,0>')
+ if use_api then
+ meths.input_mouse('wheel', 'down', '', 0, 0, 0)
+ else
+ feed('<ScrollWheelDown><0,0>')
+ end
screen:expect([[
mouse scrolling {4:│}lines |
^ {4:│}to |
@@ -664,7 +687,11 @@ describe('ui/mouse/input', function()
{4:[No Name] [+] }|
:vsp |
]])
- feed('<ScrollWheelUp><27,0>')
+ if use_api then
+ meths.input_mouse('wheel', 'up', '', 0, 0, 27)
+ else
+ feed('<ScrollWheelUp><27,0>')
+ end
screen:expect([[
mouse scrolling {4:│}text |
^ {4:│}with |
@@ -681,7 +708,12 @@ describe('ui/mouse/input', function()
{4:[No Name] [+] }|
:vsp |
]])
- feed('<ScrollWheelUp><27,7><ScrollWheelUp>')
+ if use_api then
+ meths.input_mouse('wheel', 'up', '', 0, 7, 27)
+ meths.input_mouse('wheel', 'up', '', 0, 7, 27)
+ else
+ feed('<ScrollWheelUp><27,7><ScrollWheelUp>')
+ end
screen:expect([[
mouse scrolling {4:│}text |
^ {4:│}with |
@@ -698,9 +730,17 @@ describe('ui/mouse/input', function()
{4:[No Name] [+] }|
:vsp |
]])
+ end
+
+ it('mouse wheel will target the hovered window (pseudokey)', function()
+ wheel(false)
end)
- it('horizontal scrolling', function()
+ it('mouse wheel will target the hovered window (nvim_input_mouse)', function()
+ wheel(true)
+ end)
+
+ it('horizontal scrolling (pseudokey)', function()
command('set sidescroll=0')
feed("<esc>:set nowrap<cr>")
@@ -732,6 +772,39 @@ describe('ui/mouse/input', function()
]])
end)
+ it('horizontal scrolling (nvim_input_mouse)', function()
+ command('set sidescroll=0')
+ feed("<esc>:set nowrap<cr>")
+
+ feed("a <esc>20Ab<esc>")
+ screen:expect([[
+ |
+ |
+ bbbbbbbbbbbbbbb^b |
+ {0:~ }|
+ |
+ ]])
+
+ meths.input_mouse('wheel', 'left', '', 0, 0, 27)
+ screen:expect([[
+ |
+ |
+ n bbbbbbbbbbbbbbbbbbb^b |
+ {0:~ }|
+ |
+ ]])
+
+ feed("^")
+ meths.input_mouse('wheel', 'right', '', 0, 0, 0)
+ screen:expect([[
+ g |
+ |
+ ^t and selection bbbbbbbbb|
+ {0:~ }|
+ |
+ ]])
+ end)
+
describe('on concealed text', function()
-- Helpful for reading the test expectations:
-- :match Error /\^/
diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua
index a5d4e34000..c54d608ec4 100644
--- a/test/functional/ui/multigrid_spec.lua
+++ b/test/functional/ui/multigrid_spec.lua
@@ -3,9 +3,11 @@ local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local feed, command, insert = helpers.feed, helpers.command, helpers.insert
local eq = helpers.eq
+local meths = helpers.meths
+local wait = helpers.wait
-describe('multigrid screen', function()
+describe('ext_multigrid', function()
local screen
before_each(function()
@@ -1521,4 +1523,337 @@ describe('multigrid screen', function()
{1:~ }|
]])
end)
+
+ it('supports mouse', function()
+ insert('some text\nto be clicked')
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicke^d |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 2, 0, 5)
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ |
+ ## grid 2
+ some ^text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ feed(':new<cr>')
+ insert('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmo')
+
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing el|
+ it, sed do eiusm^o |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 2, 1, 6)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be ^clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing el|
+ it, sed do eiusmo |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 3, 1, 4)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing el|
+ it, ^sed do eiusmo |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ screen:try_resize_grid(3, 80, 2)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, ^sed do eiusmo |
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 3, 0, 64)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do ^eiusmo |
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 1,6, 20)
+ -- TODO(bfredl): "batching" input_mouse is formally not supported yet.
+ -- Normally it should work fine in async context when nvim is not blocked,
+ -- but add a wait be sure.
+ wait()
+ meths.input_mouse('left', 'drag', '', 1, 4, 20)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do ^eiusmo |
+ {1:~ }|
+ ]])
+
+ feed('<c-w><c-w><c-w>v')
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ {11:[No Name] [+] }{12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmo |
+ {1:~ }|
+ ## grid 4
+ some text |
+ to be ^clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ meths.input_mouse('left', 'press', '', 1,8, 26)
+ wait()
+ meths.input_mouse('left', 'drag', '', 1, 6, 30)
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ [4:------------------------------]{12:│}[2:----------------------]|
+ {11:[No Name] [+] }{12:[No Name] [+] }|
+ |
+ ## grid 2
+ some text |
+ to be clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmo |
+ {1:~ }|
+ ## grid 4
+ some text |
+ to be ^clicked |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
end)
diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua
index c26fa5e29b..ed630259be 100644
--- a/test/functional/ui/options_spec.lua
+++ b/test/functional/ui/options_spec.lua
@@ -18,6 +18,7 @@ describe('ui receives option updates', function()
guifontset='',
guifontwide='',
linespace=0,
+ pumblend=0,
showtabline=1,
termguicolors=false,
ext_cmdline=false,
@@ -27,6 +28,8 @@ describe('ui receives option updates', function()
ext_linegrid=false,
ext_hlstate=false,
ext_multigrid=false,
+ ext_messages=false,
+ ext_termcolors=false,
}
clear(...)
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
index 9424931de4..9a8c5a5789 100644
--- a/test/functional/ui/popupmenu_spec.lua
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -5,6 +5,7 @@ local source = helpers.source
local insert = helpers.insert
local meths = helpers.meths
local command = helpers.command
+local funcs = helpers.funcs
describe('ui/ext_popupmenu', function()
local screen
@@ -368,7 +369,7 @@ describe('ui/ext_popupmenu', function()
end)
-describe('popup placement', function()
+describe('builtin popupmenu', function()
local screen
before_each(function()
clear()
@@ -385,7 +386,8 @@ describe('popup placement', function()
[2] = {bold = true},
[3] = {reverse = true},
[4] = {bold = true, reverse = true},
- [5] = {bold = true, foreground = Screen.colors.SeaGreen}
+ [5] = {bold = true, foreground = Screen.colors.SeaGreen},
+ [6] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
})
end)
@@ -607,4 +609,880 @@ describe('popup placement', function()
{2:-- }{5:match 1 of 3} |
]])
end)
+
+ it('works with split and scroll', function()
+ screen:try_resize(60,14)
+ command("split")
+ command("set completeopt+=noinsert")
+ command("set mouse=a")
+ insert([[
+ Lorem ipsum dolor sit amet, consectetur
+ adipisicing elit, sed do eiusmod tempor
+ incididunt ut labore et dolore magna aliqua.
+ Ut enim ad minim veniam, quis nostrud
+ exercitation ullamco laboris nisi ut aliquip ex
+ ea commodo consequat. Duis aute irure dolor in
+ reprehenderit in voluptate velit esse cillum
+ dolore eu fugiat nulla pariatur. Excepteur sint
+ occaecat cupidatat non proident, sunt in culpa
+ qui officia deserunt mollit anim id est
+ laborum.
+ ]])
+
+ screen:expect([[
+ reprehenderit in voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ occaecat cupidatat non proident, sunt in culpa |
+ qui officia deserunt mollit anim id est |
+ laborum. |
+ ^ |
+ {4:[No Name] [+] }|
+ Lorem ipsum dolor sit amet, consectetur |
+ adipisicing elit, sed do eiusmod tempor |
+ incididunt ut labore et dolore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ {3:[No Name] [+] }|
+ |
+ ]])
+
+ feed('ggOEst <c-x><c-p>')
+ screen:expect([[
+ Est ^ |
+ L{n: sunt }{s: }sit amet, consectetur |
+ a{n: in }{s: }sed do eiusmod tempor |
+ i{n: culpa }{s: }re et dolore magna aliqua. |
+ U{n: qui }{s: }eniam, quis nostrud |
+ e{n: officia }{s: }co laboris nisi ut aliquip ex |
+ {4:[No}{n: deserunt }{s: }{4: }|
+ L{n: mollit }{s: }sit amet, consectetur |
+ a{n: anim }{s: }sed do eiusmod tempor |
+ i{n: id }{s: }re et dolore magna aliqua. |
+ U{n: est }{s: }eniam, quis nostrud |
+ e{n: laborum }{c: }co laboris nisi ut aliquip ex |
+ {3:[No}{s: Est }{c: }{3: }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
+ ]])
+
+ meths.input_mouse('wheel', 'down', '', 0, 9, 40)
+ screen:expect([[
+ Est ^ |
+ L{n: sunt }{s: }sit amet, consectetur |
+ a{n: in }{s: }sed do eiusmod tempor |
+ i{n: culpa }{s: }re et dolore magna aliqua. |
+ U{n: qui }{s: }eniam, quis nostrud |
+ e{n: officia }{s: }co laboris nisi ut aliquip ex |
+ {4:[No}{n: deserunt }{s: }{4: }|
+ U{n: mollit }{s: }eniam, quis nostrud |
+ e{n: anim }{s: }co laboris nisi ut aliquip ex |
+ e{n: id }{s: }at. Duis aute irure dolor in |
+ r{n: est }{s: }oluptate velit esse cillum |
+ d{n: laborum }{c: }ulla pariatur. Excepteur sint |
+ {3:[No}{s: Est }{c: }{3: }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
+ ]])
+
+ feed('e')
+ screen:expect([[
+ Est e^ |
+ L{n: elit } sit amet, consectetur |
+ a{n: eiusmod } sed do eiusmod tempor |
+ i{n: et }ore et dolore magna aliqua. |
+ U{n: enim }veniam, quis nostrud |
+ e{n: exercitation }mco laboris nisi ut aliquip ex |
+ {4:[No}{n: ex }{4: }|
+ U{n: ea }veniam, quis nostrud |
+ e{n: esse }mco laboris nisi ut aliquip ex |
+ e{n: eu }uat. Duis aute irure dolor in |
+ r{s: est }voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ {3:[No Name] [+] }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
+ ]])
+
+ meths.input_mouse('wheel', 'up', '', 0, 9, 40)
+ screen:expect([[
+ Est e^ |
+ L{n: elit } sit amet, consectetur |
+ a{n: eiusmod } sed do eiusmod tempor |
+ i{n: et }ore et dolore magna aliqua. |
+ U{n: enim }veniam, quis nostrud |
+ e{n: exercitation }mco laboris nisi ut aliquip ex |
+ {4:[No}{n: ex }{4: }|
+ L{n: ea } sit amet, consectetur |
+ a{n: esse } sed do eiusmod tempor |
+ i{n: eu }ore et dolore magna aliqua. |
+ U{s: est }veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ {3:[No Name] [+] }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
+ ]])
+
+ feed('s')
+ screen:expect([[
+ Est es^ |
+ L{n: esse } sit amet, consectetur |
+ a{s: est } sed do eiusmod tempor |
+ incididunt ut labore et dolore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ {4:[No Name] [+] }|
+ Lorem ipsum dolor sit amet, consectetur |
+ adipisicing elit, sed do eiusmod tempor |
+ incididunt ut labore et dolore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ {3:[No Name] [+] }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
+ ]])
+
+ meths.input_mouse('wheel', 'down', '', 0, 9, 40)
+ screen:expect([[
+ Est es^ |
+ L{n: esse } sit amet, consectetur |
+ a{s: est } sed do eiusmod tempor |
+ incididunt ut labore et dolore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ {4:[No Name] [+] }|
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ ea commodo consequat. Duis aute irure dolor in |
+ reprehenderit in voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ {3:[No Name] [+] }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
+ ]])
+
+ feed('<bs>')
+ screen:expect([[
+ Est e^ |
+ L{n: elit } sit amet, consectetur |
+ a{n: eiusmod } sed do eiusmod tempor |
+ i{n: et }ore et dolore magna aliqua. |
+ U{n: enim }veniam, quis nostrud |
+ e{n: exercitation }mco laboris nisi ut aliquip ex |
+ {4:[No}{n: ex }{4: }|
+ U{n: ea }veniam, quis nostrud |
+ e{n: esse }mco laboris nisi ut aliquip ex |
+ e{n: eu }uat. Duis aute irure dolor in |
+ r{s: est }voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ {3:[No Name] [+] }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
+ ]])
+
+ feed('<c-p>')
+ screen:expect([[
+ Est eu^ |
+ L{n: elit } sit amet, consectetur |
+ a{n: eiusmod } sed do eiusmod tempor |
+ i{n: et }ore et dolore magna aliqua. |
+ U{n: enim }veniam, quis nostrud |
+ e{n: exercitation }mco laboris nisi ut aliquip ex |
+ {4:[No}{n: ex }{4: }|
+ U{n: ea }veniam, quis nostrud |
+ e{n: esse }mco laboris nisi ut aliquip ex |
+ e{s: eu }uat. Duis aute irure dolor in |
+ r{n: est }voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ {3:[No Name] [+] }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 22 of 65} |
+ ]])
+
+ meths.input_mouse('wheel', 'down', '', 0, 9, 40)
+ screen:expect([[
+ Est eu^ |
+ L{n: elit } sit amet, consectetur |
+ a{n: eiusmod } sed do eiusmod tempor |
+ i{n: et }ore et dolore magna aliqua. |
+ U{n: enim }veniam, quis nostrud |
+ e{n: exercitation }mco laboris nisi ut aliquip ex |
+ {4:[No}{n: ex }{4: }|
+ r{n: ea }voluptate velit esse cillum |
+ d{n: esse }nulla pariatur. Excepteur sint |
+ o{s: eu }t non proident, sunt in culpa |
+ q{n: est }unt mollit anim id est |
+ laborum. |
+ {3:[No Name] [+] }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 22 of 65} |
+ ]])
+
+
+ funcs.complete(4, {'ea', 'eeeeeeeeeeeeeeeeee', 'ei', 'eo', 'eu', 'ey', 'eå', 'eä', 'eö'})
+ screen:expect([[
+ Est eu^ |
+ {s: ea }t amet, consectetur |
+ {n: eeeeeeeeeeeeeeeeee }d do eiusmod tempor |
+ {n: ei } et dolore magna aliqua. |
+ {n: eo }iam, quis nostrud |
+ {n: eu } laboris nisi ut aliquip ex |
+ {4:[N}{n: ey }{4: }|
+ {n: eå }uptate velit esse cillum |
+ {n: eä }la pariatur. Excepteur sint |
+ {n: eö }on proident, sunt in culpa |
+ qui officia deserunt mollit anim id est |
+ laborum. |
+ {3:[No Name] [+] }|
+ {2:-- Keyword Local completion (^N^P) }{5:match 1 of 9} |
+ ]])
+
+ funcs.complete(4, {'ea', 'eee', 'ei', 'eo', 'eu', 'ey', 'eå', 'eä', 'eö'})
+ screen:expect([[
+ Est eu^ |
+ {s: ea }r sit amet, consectetur |
+ {n: eee }, sed do eiusmod tempor |
+ {n: ei }bore et dolore magna aliqua. |
+ {n: eo } veniam, quis nostrud |
+ {n: eu }amco laboris nisi ut aliquip ex |
+ {4:[N}{n: ey }{4: }|
+ {n: eå } voluptate velit esse cillum |
+ {n: eä } nulla pariatur. Excepteur sint |
+ {n: eö }at non proident, sunt in culpa |
+ qui officia deserunt mollit anim id est |
+ laborum. |
+ {3:[No Name] [+] }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<c-n>')
+ screen:expect([[
+ Esteee^ |
+ {n: ea }r sit amet, consectetur |
+ {s: eee }, sed do eiusmod tempor |
+ {n: ei }bore et dolore magna aliqua. |
+ {n: eo } veniam, quis nostrud |
+ {n: eu }amco laboris nisi ut aliquip ex |
+ {4:[N}{n: ey }{4: }|
+ {n: eå } voluptate velit esse cillum |
+ {n: eä } nulla pariatur. Excepteur sint |
+ {n: eö }at non proident, sunt in culpa |
+ qui officia deserunt mollit anim id est |
+ laborum. |
+ {3:[No Name] [+] }|
+ {2:-- INSERT --} |
+ ]])
+
+ funcs.complete(6, {'foo', 'bar'})
+ screen:expect([[
+ Esteee^ |
+ Lo{s: foo }sit amet, consectetur |
+ ad{n: bar }sed do eiusmod tempor |
+ incididunt ut labore et dolore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ {4:[No Name] [+] }|
+ reprehenderit in voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ occaecat cupidatat non proident, sunt in culpa |
+ qui officia deserunt mollit anim id est |
+ laborum. |
+ {3:[No Name] [+] }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<c-y>')
+ screen:expect([[
+ Esteefoo^ |
+ Lorem ipsum dolor sit amet, consectetur |
+ adipisicing elit, sed do eiusmod tempor |
+ incididunt ut labore et dolore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ {4:[No Name] [+] }|
+ reprehenderit in voluptate velit esse cillum |
+ dolore eu fugiat nulla pariatur. Excepteur sint |
+ occaecat cupidatat non proident, sunt in culpa |
+ qui officia deserunt mollit anim id est |
+ laborum. |
+ {3:[No Name] [+] }|
+ {2:-- INSERT --} |
+ ]])
+ end)
+
+ it('can be moved due to wrap or resize', function()
+ feed('isome long prefix before the ')
+ command("set completeopt+=noinsert,noselect")
+ command("set linebreak")
+ funcs.complete(29, {'word', 'choice', 'text', 'thing'})
+ screen:expect([[
+ some long prefix before the ^ |
+ {1:~ }{n: word }|
+ {1:~ }{n: choice}|
+ {1:~ }{n: text }|
+ {1:~ }{n: thing }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<c-p>')
+ screen:expect([[
+ some long prefix before the |
+ thing^ |
+ {n:word }{1: }|
+ {n:choice }{1: }|
+ {n:text }{1: }|
+ {s:thing }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<c-p>')
+ screen:expect([[
+ some long prefix before the text|
+ {1:^~ }{n: word }|
+ {1:~ }{n: choice}|
+ {1:~ }{s: text }|
+ {1:~ }{n: thing }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ screen:try_resize(30,8)
+ screen:expect([[
+ some long prefix before the |
+ text^ |
+ {n:word }{1: }|
+ {n:choice }{1: }|
+ {s:text }{1: }|
+ {n:thing }{1: }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ screen:try_resize(50,8)
+ screen:expect([[
+ some long prefix before the text^ |
+ {1:~ }{n: word }{1: }|
+ {1:~ }{n: choice }{1: }|
+ {1:~ }{s: text }{1: }|
+ {1:~ }{n: thing }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ screen:try_resize(25,10)
+ screen:expect([[
+ some long prefix before |
+ the text^ |
+ {1:~ }{n: word }{1: }|
+ {1:~ }{n: choice }{1: }|
+ {1:~ }{s: text }{1: }|
+ {1:~ }{n: thing }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ screen:try_resize(12,5)
+ screen:expect([[
+ some long |
+ prefix |
+ bef{n: word } |
+ tex{n: }^ |
+ {2:-- }{s: text } |
+ ]])
+
+ -- can't draw the pum, but check we don't crash
+ screen:try_resize(12,2)
+ screen:expect([[
+ text^ |
+ {2:-- INSERT -} |
+ ]])
+
+ -- but state is preserved, pum reappears
+ screen:try_resize(20,8)
+ screen:expect([[
+ some long prefix |
+ before the text^ |
+ {1:~ }{n: word }|
+ {1:~ }{n: choice }|
+ {1:~ }{s: text }|
+ {1:~ }{n: thing }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+ end)
+
+ it('works with rightleft window', function()
+ command("set rl")
+ feed('isome rightleft ')
+ screen:expect([[
+ ^ tfelthgir emos|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {2:-- INSERT --} |
+ ]])
+
+ command("set completeopt+=noinsert,noselect")
+ funcs.complete(16, {'word', 'choice', 'text', 'thing'})
+ screen:expect([[
+ ^ tfelthgir emos|
+ {1: }{n: drow}{1: ~}|
+ {1: }{n: eciohc}{1: ~}|
+ {1: }{n: txet}{1: ~}|
+ {1: }{n: gniht}{1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<c-n>')
+ screen:expect([[
+ ^ drow tfelthgir emos|
+ {1: }{s: drow}{1: ~}|
+ {1: }{n: eciohc}{1: ~}|
+ {1: }{n: txet}{1: ~}|
+ {1: }{n: gniht}{1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<c-y>')
+ screen:expect([[
+ ^ drow tfelthgir emos|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {2:-- INSERT --} |
+ ]])
+ end)
+
+ it('works with multiline messages', function()
+ screen:try_resize(40,8)
+ feed('ixx<cr>')
+ command('imap <f2> <cmd>echoerr "very"\\|echoerr "much"\\|echoerr "error"<cr>')
+ funcs.complete(1, {'word', 'choice', 'text', 'thing'})
+ screen:expect([[
+ xx |
+ word^ |
+ {s:word }{1: }|
+ {n:choice }{1: }|
+ {n:text }{1: }|
+ {n:thing }{1: }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<f2>')
+ screen:expect([[
+ xx |
+ word |
+ {s:word }{1: }|
+ {4: }|
+ {6:very} |
+ {6:much} |
+ {6:error} |
+ {5:Press ENTER or type command to continue}^ |
+ ]])
+
+ feed('<cr>')
+ screen:expect([[
+ xx |
+ word^ |
+ {s:word }{1: }|
+ {n:choice }{1: }|
+ {n:text }{1: }|
+ {n:thing }{1: }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<c-n>')
+ screen:expect([[
+ xx |
+ choice^ |
+ {n:word }{1: }|
+ {s:choice }{1: }|
+ {n:text }{1: }|
+ {n:thing }{1: }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ command("split")
+ screen:expect([[
+ xx |
+ choice^ |
+ {n:word }{1: }|
+ {s:choice }{4: }|
+ {n:text } |
+ {n:thing } |
+ {3:[No Name] [+] }|
+ {2:-- INSERT --} |
+ ]])
+
+ meths.input_mouse('wheel', 'down', '', 0, 6, 15)
+ screen:expect([[
+ xx |
+ choice^ |
+ {n:word }{1: }|
+ {s:choice }{4: }|
+ {n:text } |
+ {n:thing }{1: }|
+ {3:[No Name] [+] }|
+ {2:-- INSERT --} |
+ ]])
+ end)
+
+ it('works with kind, menu and abbr attributes', function()
+ screen:try_resize(40,8)
+ feed('ixx ')
+ funcs.complete(4, {{word='wordey', kind= 'x', menu='extrainfo'}, 'thing', {word='secret', abbr='sneaky', menu='bar'}})
+ screen:expect([[
+ xx wordey^ |
+ {1:~ }{s: wordey x extrainfo }{1: }|
+ {1:~ }{n: thing }{1: }|
+ {1:~ }{n: sneaky bar }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<c-p>')
+ screen:expect([[
+ xx ^ |
+ {1:~ }{n: wordey x extrainfo }{1: }|
+ {1:~ }{n: thing }{1: }|
+ {1:~ }{n: sneaky bar }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<c-p>')
+ screen:expect([[
+ xx secret^ |
+ {1:~ }{n: wordey x extrainfo }{1: }|
+ {1:~ }{n: thing }{1: }|
+ {1:~ }{s: sneaky bar }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<esc>')
+ screen:expect([[
+ xx secre^t |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it("'pumblend' RGB-color", function()
+ screen:try_resize(60,14)
+ screen:set_default_attr_ids({
+ [1] = {background = Screen.colors.Yellow},
+ [2] = {bold = true, reverse = true},
+ [3] = {bold = true, foreground = Screen.colors.Brown},
+ [4] = {foreground = Screen.colors.Blue1},
+ [5] = {reverse = true},
+ [6] = {background = Screen.colors.Gray55, foreground = Screen.colors.Grey45},
+ [7] = {background = Screen.colors.Gray55, foreground = Screen.colors.Grey0},
+ [8] = {background = tonumber('0x191919'), foreground = Screen.colors.Grey0},
+ [9] = {background = tonumber('0xffc1ff'), foreground = tonumber('0xe5a8e5')},
+ [10] = {background = tonumber('0xffc1ff'), foreground = Screen.colors.Grey0},
+ [11] = {foreground = tonumber('0xffc1ff'), background = tonumber('0xe5a8e5'), bold = true},
+ [12] = {foreground = Screen.colors.Grey55, background = Screen.colors.Gray45, bold = true},
+ [13] = {background = tonumber('0xffc1e5'), foreground = Screen.colors.Grey0},
+ [14] = {background = tonumber('0xffc1e5'), foreground = tonumber('0xe5a8e5')},
+ [15] = {background = tonumber('0xffc1ff'), foreground = tonumber('0x080202')},
+ [16] = {background = tonumber('0xffc1ff'), bold = true, foreground = tonumber('0xf6ace9')},
+ [17] = {background = tonumber('0xffc1ff'), foreground = tonumber('0xe5a8ff')},
+ [18] = {background = tonumber('0xe5a8e5'), foreground = tonumber('0xffc1ff')},
+ [19] = {background = Screen.colors.Gray45, foreground = Screen.colors.Grey55},
+ [20] = {bold = true},
+ [21] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [22] = {background = Screen.colors.WebGray},
+ [23] = {background = Screen.colors.Grey0},
+ [24] = {background = Screen.colors.LightMagenta},
+ [25] = {background = Screen.colors.Gray75, foreground = Screen.colors.Grey25},
+ [26] = {background = Screen.colors.Gray75, foreground = Screen.colors.Grey0},
+ [27] = {background = Screen.colors.Gray50, foreground = Screen.colors.Grey0},
+ [28] = {background = tonumber('0xffddff'), foreground = tonumber('0x7f5d7f')},
+ [29] = {background = tonumber('0xffddff'), foreground = Screen.colors.Grey0},
+ [30] = {foreground = tonumber('0xffddff'), background = tonumber('0x7f5d7f'), bold = true},
+ [31] = {foreground = tonumber('0xffddff'), background = Screen.colors.Grey0, bold = true},
+ [32] = {foreground = Screen.colors.Gray75, background = Screen.colors.Grey25, bold = true},
+ [33] = {background = tonumber('0xffdd7f'), foreground = Screen.colors.Grey0},
+ [34] = {background = tonumber('0xffdd7f'), foreground = tonumber('0x7f5d7f')},
+ [35] = {background = tonumber('0xffddff'), bold = true, foreground = tonumber('0x290a0a')},
+ [36] = {background = tonumber('0xffddff'), bold = true, foreground = tonumber('0xd27294')},
+ [37] = {background = tonumber('0xffddff'), foreground = tonumber('0x7f5dff')},
+ [38] = {background = tonumber('0x7f5d7f'), foreground = tonumber('0xffddff')},
+ [39] = {background = Screen.colors.Grey0, foreground = tonumber('0xffddff')},
+ [40] = {background = Screen.colors.Gray25, foreground = Screen.colors.Grey75},
+ [41] = {background = tonumber('0xffddff'), foreground = tonumber('0x00003f')},
+ [42] = {foreground = tonumber('0x0c0c0c'), background = tonumber('0xe5a8e5')},
+ [43] = {background = tonumber('0x7f5d7f'), bold = true, foreground = tonumber('0x3f3f3f')},
+ [44] = {foreground = tonumber('0x3f3f3f'), background = tonumber('0x7f5d7f')},
+ })
+ command('syntax on')
+ command('set mouse=a')
+ command('set pumblend=10')
+ insert([[
+ Lorem ipsum dolor sit amet, consectetur
+ adipisicing elit, sed do eiusmod tempor
+ incididunt ut labore et dolore magna aliqua.
+ Ut enim ad minim veniam, quis nostrud
+ exercitation ullamco laboris nisi ut aliquip ex
+ ea commodo consequat. Duis aute irure dolor in
+ reprehenderit in voluptate velit esse cillum
+ dolore eu fugiat nulla pariatur. Excepteur sint
+ occaecat cupidatat non proident, sunt in culpa
+ qui officia deserunt mollit anim id est
+ laborum.]])
+ command('match Statement /el/')
+ command('2match Comment /ut/')
+ command('1')
+ command('split')
+ command('/ol')
+ screen:expect([[
+ Lorem ipsum d{1:ol}or sit amet, consectetur |
+ adipisicing elit, sed do eiusmod tempor |
+ ^incididunt ut labore et d{1:ol}ore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ ea commodo consequat. Duis aute irure d{1:ol}or in |
+ {2:[No Name] [+] }|
+ Lorem ipsum d{1:ol}or sit amet, consectetur |
+ adipisicing {3:el}it, sed do eiusmod tempor |
+ incididunt {4:ut} labore et d{1:ol}ore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi {4:ut} aliquip ex |
+ {5:[No Name] [+] }|
+ |
+ ]])
+
+ feed('Obla bla <c-x><c-n>')
+ screen:expect([[
+ Lorem ipsum d{1:ol}or sit amet, consectetur |
+ adipisicing elit, sed do eiusmod tempor |
+ bla bla incididunt^ |
+ incidid{6:u}{7:incididunt}{6:re et}{8: }d{1:ol}ore magna aliqua. |
+ Ut enim{9: }{10:ut}{9: minim veniam}{6:,} quis nostrud |
+ exercit{9:a}{10:labore}{9:llamco la}{6:b}oris nisi ut aliquip ex |
+ {2:[No Nam}{11:e}{42:et}{11:[+] }{12: }{2: }|
+ Lorem i{9:p}{10:dolor}{13:e}{14:l}{9:or sit a}{6:m}et, consectetur |
+ adipisi{9:c}{10:magn}{15:a}{16:l}{9:it, sed d}{6:o} eiusmod tempor |
+ bla bla{9: }{10:aliqua}{9:dunt }{6: } |
+ incidid{9:u}{10:Ut}{9: }{17:ut}{9: labore et}{6: }d{1:ol}ore magna aliqua. |
+ Ut enim{9: }{10:enim}{9:inim veniam}{6:,} quis nostrud |
+ {5:[No Nam}{18:e}{42:ad}{18:[+] }{19: }{5: }|
+ {20:-- Keyword Local completion (^N^P) }{21:match 1 of 65} |
+ ]])
+
+ command('set pumblend=0')
+ screen:expect([[
+ Lorem ipsum d{1:ol}or sit amet, consectetur |
+ adipisicing elit, sed do eiusmod tempor |
+ bla bla incididunt^ |
+ incidid{22: incididunt }{23: }d{1:ol}ore magna aliqua. |
+ Ut enim{24: ut }{22: } quis nostrud |
+ exercit{24: labore }{22: }oris nisi ut aliquip ex |
+ {2:[No Nam}{24: et }{22: }{2: }|
+ Lorem i{24: dolore }{22: }et, consectetur |
+ adipisi{24: magna }{22: } eiusmod tempor |
+ bla bla{24: aliqua }{22: } |
+ incidid{24: Ut }{22: }d{1:ol}ore magna aliqua. |
+ Ut enim{24: enim }{22: } quis nostrud |
+ {5:[No Nam}{24: ad }{22: }{5: }|
+ {20:-- Keyword Local completion (^N^P) }{21:match 1 of 65} |
+ ]])
+
+ command('set pumblend=50')
+ screen:expect([[
+ Lorem ipsum d{1:ol}or sit amet, consectetur |
+ adipisicing elit, sed do eiusmod tempor |
+ bla bla incididunt^ |
+ incidid{25:u}{26:incididunt}{25:re et}{27: }d{1:ol}ore magna aliqua. |
+ Ut enim{28: }{29:ut}{28: minim veniam}{25:,} quis nostrud |
+ exercit{28:a}{29:labore}{28:llamco la}{25:b}oris nisi ut aliquip ex |
+ {2:[No Nam}{30:e}{43:et}{30:[+] }{32: }{2: }|
+ Lorem i{28:p}{29:dolor}{33:e}{34:l}{28:or sit a}{25:m}et, consectetur |
+ adipisi{28:c}{29:magn}{35:a}{36:l}{28:it, sed d}{25:o} eiusmod tempor |
+ bla bla{28: }{29:aliqua}{28:dunt }{25: } |
+ incidid{28:u}{29:Ut}{28: }{37:ut}{28: labore et}{25: }d{1:ol}ore magna aliqua. |
+ Ut enim{28: }{29:enim}{28:inim veniam}{25:,} quis nostrud |
+ {5:[No Nam}{38:e}{44:ad}{38:[+] }{40: }{5: }|
+ {20:-- Keyword Local completion (^N^P) }{21:match 1 of 65} |
+ ]])
+
+ meths.input_mouse('wheel', 'down', '', 0, 9, 40)
+ screen:expect([[
+ Lorem ipsum d{1:ol}or sit amet, consectetur |
+ adipisicing elit, sed do eiusmod tempor |
+ bla bla incididunt^ |
+ incidid{25:u}{26:incididunt}{25:re et}{27: }d{1:ol}ore magna aliqua. |
+ Ut enim{28: }{29:ut}{28: minim veniam}{25:,} quis nostrud |
+ exercit{28:a}{29:labore}{28:llamco la}{25:b}oris nisi ut aliquip ex |
+ {2:[No Nam}{30:e}{43:et}{30:[+] }{32: }{2: }|
+ incidid{28:u}{29:dol}{41:or}{29:e}{28:labore et}{25: }d{1:ol}ore magna aliqua. |
+ Ut enim{28: }{29:magna}{28:nim veniam}{25:,} quis nostrud |
+ exercit{28:a}{29:aliqua}{28:llamco la}{25:b}oris nisi {4:ut} aliquip ex |
+ ea comm{28:o}{29:Ut}{28: consequat. D}{25:u}is a{4:ut}e irure d{1:ol}or in |
+ reprehe{28:n}{29:enim}{28:t in v}{34:ol}{28:upt}{25:a}te v{3:el}it esse cillum |
+ {5:[No Nam}{38:e}{44:ad}{38:[+] }{40: }{5: }|
+ {20:-- Keyword Local completion (^N^P) }{21:match 1 of 65} |
+ ]])
+
+ feed('<c-e>')
+ screen:expect([[
+ Lorem ipsum d{1:ol}or sit amet, consectetur |
+ adipisicing elit, sed do eiusmod tempor |
+ bla bla ^ |
+ incididunt ut labore et d{1:ol}ore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi ut aliquip ex |
+ {2:[No Name] [+] }|
+ incididunt {4:ut} labore et d{1:ol}ore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ exercitation ullamco laboris nisi {4:ut} aliquip ex |
+ ea commodo consequat. Duis a{4:ut}e irure d{1:ol}or in |
+ reprehenderit in v{1:ol}uptate v{3:el}it esse cillum |
+ {5:[No Name] [+] }|
+ {20:-- INSERT --} |
+ ]])
+ end)
+
+ it("'pumblend' 256-color (non-RGB)", function()
+ screen:detach()
+ screen = Screen.new(60, 8)
+ screen:attach({rgb=false, ext_popupmenu=false})
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.Grey0, background = tonumber('0x000007')},
+ [2] = {foreground = tonumber('0x000055'), background = tonumber('0x000007')},
+ [3] = {foreground = tonumber('0x00008f'), background = Screen.colors.Grey0},
+ [4] = {foreground = Screen.colors.Grey0, background = tonumber('0x0000e1')},
+ [5] = {foreground = tonumber('0x0000d1'), background = tonumber('0x0000e1')},
+ [6] = {foreground = Screen.colors.NavyBlue, background = tonumber('0x0000f8')},
+ [7] = {foreground = tonumber('0x0000a5'), background = tonumber('0x0000f8')},
+ [8] = {foreground = tonumber('0x00000c')},
+ [9] = {bold = true},
+ [10] = {foreground = tonumber('0x000002')},
+ })
+ command('set notermguicolors pumblend=10')
+ insert([[
+ Lorem ipsum dolor sit amet, consectetur
+ adipisicing elit, sed do eiusmod tempor
+ incididunt ut labore et dolore magna aliqua.
+ Ut enim ad minim veniam, quis nostrud
+ laborum.]])
+
+ feed('ggOdo<c-x><c-n>')
+ screen:expect([[
+ dolor^ |
+ {1:dolor}{2: ipsum dol}or sit amet, consectetur |
+ {4:do}{5:ipisicing eli}t, sed do eiusmod tempor |
+ {4:dolore}{5:dunt ut l}abore et dolore magna aliqua. |
+ Ut enim ad minim veniam, quis nostrud |
+ laborum. |
+ {8:~ }|
+ {9:-- Keyword Local completion (^N^P) }{10:match 1 of 3} |
+ ]])
+ end)
end)
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 69f4a44dd8..2eae549ebd 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -159,6 +159,11 @@ function Screen.new(width, height)
wildmenu_selected = nil,
win_position = {},
_session = nil,
+ messages = {},
+ msg_history = {},
+ showmode = {},
+ showcmd = {},
+ ruler = {},
_default_attr_ids = nil,
_default_attr_ignore = nil,
_mouse_enabled = true,
@@ -250,7 +255,8 @@ end
-- canonical order of ext keys, used to generate asserts
local ext_keys = {
- 'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos'
+ 'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos',
+ 'messages', 'showmode', 'showcmd', 'ruler',
}
-- Asserts that the screen state eventually matches an expected state
@@ -392,7 +398,7 @@ function Screen:expect(expected, attr_ids, attr_ignore)
.. ') differs from configured height(' .. #actual_rows .. ') of Screen.'
end
for i = 1, #actual_rows do
- if expected_rows[i] ~= actual_rows[i] then
+ if expected_rows[i] ~= actual_rows[i] and expected_rows[i] ~= "{IGNORE}|" then
local msg_expected_rows = {}
for j = 1, #expected_rows do
msg_expected_rows[j] = expected_rows[j]
@@ -917,7 +923,7 @@ function Screen:_handle_option_set(name, value)
end
function Screen:_handle_popupmenu_show(items, selected, row, col)
- self.popupmenu = {items=items,pos=selected, anchor={row, col}}
+ self.popupmenu = {items=items, pos=selected, anchor={row, col}}
end
function Screen:_handle_popupmenu_select(selected)
@@ -973,6 +979,34 @@ function Screen:_handle_wildmenu_hide()
self.wildmenu_items, self.wildmenu_pos = nil, nil
end
+function Screen:_handle_msg_show(kind, chunks, replace_last)
+ local pos = #self.messages
+ if not replace_last or pos == 0 then
+ pos = pos + 1
+ end
+ self.messages[pos] = {kind=kind, content=chunks}
+end
+
+function Screen:_handle_msg_clear()
+ self.messages = {}
+end
+
+function Screen:_handle_msg_showcmd(msg)
+ self.showcmd = msg
+end
+
+function Screen:_handle_msg_showmode(msg)
+ self.showmode = msg
+end
+
+function Screen:_handle_msg_ruler(msg)
+ self.ruler = msg
+end
+
+function Screen:_handle_msg_history_show(entries)
+ self.msg_history = entries
+end
+
function Screen:_clear_block(grid, top, bot, left, right)
for i = top, bot do
self:_clear_row_section(grid, i, left, right)
@@ -1057,12 +1091,27 @@ function Screen:_extstate_repr(attr_state)
cmdline_block[i] = self:_chunks_repr(entry, attr_state)
end
+ local messages = {}
+ for i, entry in ipairs(self.messages) do
+ messages[i] = {kind=entry.kind, content=self:_chunks_repr(entry.content, attr_state)}
+ end
+
+ local msg_history = {}
+ for i, entry in ipairs(self.msg_history) do
+ messages[i] = {kind=entry[1], content=self:_chunks_repr(entry[2], attr_state)}
+ end
+
return {
popupmenu=self.popupmenu,
cmdline=cmdline,
cmdline_block=cmdline_block,
wildmenu_items=self.wildmenu_items,
wildmenu_pos=self.wildmenu_pos,
+ messages=messages,
+ showmode=self:_chunks_repr(self.showmode, attr_state),
+ showcmd=self:_chunks_repr(self.showcmd, attr_state),
+ ruler=self:_chunks_repr(self.ruler, attr_state),
+ msg_history=msg_history,
}
end
@@ -1303,6 +1352,8 @@ function Screen:_pprint_attrs(attrs)
if f == "foreground" or f == "background" or f == "special" then
if Screen.colornames[v] ~= nil then
desc = "Screen.colors."..Screen.colornames[v]
+ else
+ desc = string.format("tonumber('0x%06x')",v)
end
end
table.insert(items, f.." = "..desc)
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index 04d532f6e1..46f0b5060c 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -960,3 +960,46 @@ end)
describe("Screen (line-based)", function()
screen_tests(true)
end)
+
+describe('Screen default colors', function()
+ local screen
+ local function startup(light, termcolors)
+ local extra = (light and ' background=light') or ''
+
+ local nvim_argv = {helpers.nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N',
+ '--cmd', 'set shortmess+=I noswapfile belloff= noshowcmd noruler'..extra,
+ '--embed'}
+ local screen_nvim = spawn(nvim_argv)
+ set_session(screen_nvim)
+ screen = Screen.new()
+ screen:attach(termcolors and {rgb=true,ext_termcolors=true} or {rgb=true})
+ end
+
+ it('are dark per default', function()
+ startup(false, false)
+ screen:expect{condition=function()
+ eq({rgb_bg=0, rgb_fg=Screen.colors.White, rgb_sp=Screen.colors.Red,
+ cterm_bg=0, cterm_fg=0}, screen.default_colors)
+ end}
+ end)
+
+ it('can be set to light', function()
+ startup(true, false)
+ screen:expect{condition=function()
+ eq({rgb_bg=Screen.colors.White, rgb_fg=0, rgb_sp=Screen.colors.Red,
+ cterm_bg=0, cterm_fg=0}, screen.default_colors)
+ end}
+ end)
+
+ it('can be handled by external terminal', function()
+ startup(false, true)
+ screen:expect{condition=function()
+ eq({rgb_bg=-1, rgb_fg=-1, rgb_sp=-1, cterm_bg=0, cterm_fg=0}, screen.default_colors)
+ end}
+
+ startup(true, true)
+ screen:expect{condition=function()
+ eq({rgb_bg=-1, rgb_fg=-1, rgb_sp=-1, cterm_bg=0, cterm_fg=0}, screen.default_colors)
+ end}
+ end)
+end)
diff --git a/test/helpers.lua b/test/helpers.lua
index a6ed312213..59da274e87 100644
--- a/test/helpers.lua
+++ b/test/helpers.lua
@@ -1,3 +1,4 @@
+require('vim.compat')
local assert = require('luassert')
local luv = require('luv')
local lfs = require('lfs')
diff --git a/test/unit/api/helpers.lua b/test/unit/api/helpers.lua
index 4fb1cee4b3..3d306d2b1b 100644
--- a/test/unit/api/helpers.lua
+++ b/test/unit/api/helpers.lua
@@ -114,8 +114,7 @@ local lua2obj_type_tab = {
api.xmalloc(len * ffi.sizeof('KeyValuePair'))),
}})
for i = 1, len do
- local table_unpack = table.unpack or unpack -- luacheck: compat
- local key, val = table_unpack(kvs[i])
+ local key, val = unpack(kvs[i])
dct.data.dictionary.items[i - 1] = ffi.new(
'KeyValuePair', {key=ffi.gc(lua2obj(key), nil).data.string,
value=ffi.gc(lua2obj(val), nil)})
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index 015b3ecbd4..f316e5bd0d 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -30,7 +30,6 @@ set(DEPS_DOWNLOAD_DIR "${DEPS_BUILD_DIR}/downloads" CACHE PATH "Dependencies dow
option(USE_BUNDLED "Use bundled dependencies." ON)
option(USE_BUNDLED_GPERF "Use the bundled version of gperf." ${USE_BUNDLED})
-option(USE_BUNDLED_JEMALLOC "Use the bundled jemalloc." ${USE_BUNDLED})
option(USE_BUNDLED_UNIBILIUM "Use the bundled unibilium." ${USE_BUNDLED})
option(USE_BUNDLED_LIBTERMKEY "Use the bundled libtermkey." ${USE_BUNDLED})
option(USE_BUNDLED_LIBVTERM "Use the bundled libvterm." ${USE_BUNDLED})
@@ -147,11 +146,8 @@ set(UNIBILIUM_SHA256 78997d38d4c8177c60d3d0c1aa8c53fd0806eb21825b7b335b1768d7116
set(LIBTERMKEY_URL http://www.leonerd.org.uk/code/libtermkey/libtermkey-0.20.tar.gz)
set(LIBTERMKEY_SHA256 6c0d87c94ab9915e76ecd313baec08dedf3bd56de83743d9aa923a081935d2f5)
-set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/3f62ac6b7bdffda39d68f723fb1806dfd6d6382d.tar.gz)
-set(LIBVTERM_SHA256 1c8b318370f00f831f43e3ec86a48984250e3ee5c76beb106a421c9a42286ac5)
-
-set(JEMALLOC_URL https://github.com/jemalloc/jemalloc/releases/download/4.5.0/jemalloc-4.5.0.tar.bz2)
-set(JEMALLOC_SHA256 9409d85664b4f135b77518b0b118c549009dc10f6cba14557d170476611f6780)
+set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/b45b648cab73f9667bde7c0c6045b285e22b3ecd.tar.gz)
+set(LIBVTERM_SHA256 37cc123deff29327efa654358c2ebaaf8589da03754ca5adb8ec47be386a0433)
set(LUV_URL https://github.com/luvit/luv/archive/1.9.1-1.tar.gz)
set(LUV_SHA256 562b9efaad30aa051a40eac9ade0c3df48bb8186763769abe47ec3fb3edb1268)
@@ -212,10 +208,6 @@ if(USE_BUNDLED_LUAROCKS)
include(BuildLuarocks)
endif()
-if(USE_BUNDLED_JEMALLOC)
- include(BuildJeMalloc)
-endif()
-
if(USE_BUNDLED_LUV)
include(BuildLuv)
endif()
diff --git a/third-party/cmake/BuildJeMalloc.cmake b/third-party/cmake/BuildJeMalloc.cmake
deleted file mode 100644
index 637aadaad9..0000000000
--- a/third-party/cmake/BuildJeMalloc.cmake
+++ /dev/null
@@ -1,24 +0,0 @@
-if(WIN32)
- message(STATUS "Building jemalloc in Windows is not supported (skipping)")
- return()
-endif()
-
-ExternalProject_Add(jemalloc
- PREFIX ${DEPS_BUILD_DIR}
- URL ${JEMALLOC_URL}
- DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/jemalloc
- DOWNLOAD_COMMAND ${CMAKE_COMMAND}
- -DPREFIX=${DEPS_BUILD_DIR}
- -DDOWNLOAD_DIR=${DEPS_DOWNLOAD_DIR}/jemalloc
- -DURL=${JEMALLOC_URL}
- -DEXPECTED_SHA256=${JEMALLOC_SHA256}
- -DTARGET=jemalloc
- -DUSE_EXISTING_SRC_DIR=${USE_EXISTING_SRC_DIR}
- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/DownloadAndExtractFile.cmake
- BUILD_IN_SOURCE 1
- CONFIGURE_COMMAND ${DEPS_BUILD_DIR}/src/jemalloc/configure
- CC=${DEPS_C_COMPILER} --prefix=${DEPS_INSTALL_DIR}
- BUILD_COMMAND ""
- INSTALL_COMMAND ${MAKE_PRG} install_include install_lib_static)
-
-list(APPEND THIRD_PARTY_DEPS jemalloc)